Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions .opencode/plugins/superpowers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* Skills are discovered via OpenCode's native skill tool from symlinked directory.
*/

import path from 'path';
import fs from 'fs';
import os from 'os';
import { fileURLToPath } from 'url';
import fs from "fs";
import os from "os";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Expand All @@ -21,11 +21,14 @@ const extractAndStripFrontmatter = (content) => {
const body = match[2];
const frontmatter = {};

for (const line of frontmatterStr.split('\n')) {
const colonIdx = line.indexOf(':');
for (const line of frontmatterStr.split("\n")) {
const colonIdx = line.indexOf(":");
if (colonIdx > 0) {
const key = line.slice(0, colonIdx).trim();
const value = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
const value = line
.slice(colonIdx + 1)
.trim()
.replace(/^["']|["']$/g, "");
frontmatter[key] = value;
}
}
Expand All @@ -35,30 +38,30 @@ const extractAndStripFrontmatter = (content) => {

// Normalize a path: trim whitespace, expand ~, resolve to absolute
const normalizePath = (p, homeDir) => {
if (!p || typeof p !== 'string') return null;
if (!p || typeof p !== "string") return null;
let normalized = p.trim();
if (!normalized) return null;
if (normalized.startsWith('~/')) {
if (normalized.startsWith("~/")) {
normalized = path.join(homeDir, normalized.slice(2));
} else if (normalized === '~') {
} else if (normalized === "~") {
normalized = homeDir;
}
return path.resolve(normalized);
};

export const SuperpowersPlugin = async ({ client, directory }) => {
const homeDir = os.homedir();
const superpowersSkillsDir = path.resolve(__dirname, '../../skills');
const superpowersSkillsDir = path.resolve(__dirname, "../../skills");
const envConfigDir = normalizePath(process.env.OPENCODE_CONFIG_DIR, homeDir);
const configDir = envConfigDir || path.join(homeDir, '.config/opencode');
const configDir = envConfigDir || path.join(homeDir, ".config/opencode");

// Helper to generate bootstrap content
const getBootstrapContent = () => {
// Try to load using-superpowers skill
const skillPath = path.join(superpowersSkillsDir, 'using-superpowers', 'SKILL.md');
const skillPath = path.join(superpowersSkillsDir, "using-superpowers", "SKILL.md");
if (!fs.existsSync(skillPath)) return null;

const fullContent = fs.readFileSync(skillPath, 'utf8');
const fullContent = fs.readFileSync(skillPath, "utf8");
const { content } = extractAndStripFrontmatter(fullContent);

const toolMapping = `**Tool Mapping for OpenCode:**
Expand All @@ -85,11 +88,12 @@ ${toolMapping}

return {
// Use system prompt transform to inject bootstrap (fixes #226 agent reset bug)
'experimental.chat.system.transform': async (_input, output) => {
"experimental.chat.system.transform": async (_input, output) => {
if (process.env.SUPERPOWERS_SKIP_BOOTSTRAP === "1") return;
const bootstrap = getBootstrapContent();
if (bootstrap) {
(output.system ||= []).push(bootstrap);
}
}
},
};
};