2025-11-22 12:29:50 -08:00
/**
* Superpowers plugin for OpenCode.ai
*
* Provides custom tools for loading and discovering skills,
* with automatic bootstrap on session start.
*/
2025-11-22 12:53:26 -08:00
import path from 'path' ;
import fs from 'fs' ;
import os from 'os' ;
2025-11-22 13:22:17 -08:00
import { fileURLToPath } from 'url' ;
2025-11-22 13:23:05 -08:00
import * as skillsCore from '../../lib/skills-core.js' ;
2025-11-22 12:53:26 -08:00
2025-11-22 13:22:17 -08:00
const __dirname = path . dirname ( fileURLToPath ( import . meta . url ));
2025-11-22 12:29:50 -08:00
2025-11-22 12:53:26 -08:00
export const SuperpowersPlugin = async ({ project , client , $ , directory , worktree }) => {
2025-11-22 12:30:20 -08:00
const { z } = await import ( 'zod' );
2025-11-22 13:22:17 -08:00
const homeDir = os . homedir ();
const superpowersSkillsDir = path . join ( homeDir , '.config/opencode/superpowers/skills' );
const personalSkillsDir = path . join ( homeDir , '.config/opencode/skills' );
2025-11-22 12:29:50 -08:00
return {
2025-11-22 12:30:20 -08:00
tools : [
{
name : 'use_skill' ,
description : 'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques.' ,
schema : z . object ({
skill_name : z . string (). describe ( 'Name of the skill to load (e.g., "superpowers:brainstorming" or "my-custom-skill")' )
}),
execute : async ({ skill_name }) => {
const resolved = skillsCore . resolveSkillPath ( skill_name , superpowersSkillsDir , personalSkillsDir );
if ( ! resolved ) {
return `Error: Skill " ${ skill_name } " not found.\n\nRun find_skills to see available skills.` ;
}
const fullContent = fs . readFileSync ( resolved . skillFile , 'utf8' );
const { name , description } = skillsCore . extractFrontmatter ( resolved . skillFile );
2025-11-22 12:41:58 -08:00
const content = skillsCore . stripFrontmatter ( fullContent );
2025-11-22 12:30:20 -08:00
const skillDirectory = path . dirname ( resolved . skillFile );
return `# ${ name || skill_name }
# ${ description || '' }
# Supporting tools and docs are in ${ skillDirectory }
# ============================================
${ content } ` ;
}
2025-11-22 12:30:48 -08:00
},
{
name : 'find_skills' ,
description : 'List all available skills in the superpowers and personal skill libraries.' ,
schema : z . object ({}),
execute : async () => {
const superpowersSkills = skillsCore . findSkillsInDir ( superpowersSkillsDir , 'superpowers' , 3 );
const personalSkills = skillsCore . findSkillsInDir ( personalSkillsDir , 'personal' , 3 );
const allSkills = [... personalSkills , ... superpowersSkills ];
if ( allSkills . length === 0 ) {
return 'No skills found. Install superpowers skills to ~/.config/opencode/superpowers/skills/' ;
}
let output = 'Available skills:\n\n' ;
for ( const skill of allSkills ) {
const namespace = skill . sourceType === 'personal' ? '' : 'superpowers:' ;
const skillName = skill . name || path . basename ( skill . path );
output += ` ${ namespace }${ skillName } \n` ;
if ( skill . description ) {
output += ` ${ skill . description } \n` ;
}
output += ` Directory: ${ skill . path } \n\n` ;
}
return output ;
}
2025-11-22 12:30:20 -08:00
}
2025-11-22 12:31:23 -08:00
],
2025-11-22 15:47:38 -08:00
event : async ({ event }) => {
if ( event . type === 'session.created' ) {
const usingSuperpowersPath = skillsCore . resolveSkillPath ( 'using-superpowers' , superpowersSkillsDir , personalSkillsDir );
let usingSuperpowersContent = '' ;
if ( usingSuperpowersPath ) {
const fullContent = fs . readFileSync ( usingSuperpowersPath . skillFile , 'utf8' );
usingSuperpowersContent = skillsCore . stripFrontmatter ( fullContent );
}
2025-11-22 12:31:23 -08:00
2025-11-22 15:47:38 -08:00
const toolMapping = `
2025-11-22 12:31:23 -08:00
**Tool Mapping for OpenCode:**
When skills reference tools you don't have, substitute OpenCode equivalents:
- \`TodoWrite\` → \`update_plan\` (your planning/task tracking tool)
- \`Task\` tool with subagents → Use OpenCode's subagent system (@mention syntax or automatic dispatch)
- \`Skill\` tool → \`use_skill\` custom tool (already available)
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Use your native tools
**Skill directories contain supporting files:**
- Scripts you can run with bash tool
- Additional documentation you can read
- Utilities and helpers specific to that skill
**Skills naming:**
- Superpowers skills: \`superpowers:skill-name\` (from ~/.config/opencode/superpowers/skills/)
- Personal skills: \`skill-name\` (from ~/.config/opencode/skills/)
- Personal skills override superpowers skills when names match
` ;
2025-11-22 15:47:38 -08:00
const hasUpdates = skillsCore . checkForUpdates ( path . join ( homeDir , '.config/opencode/superpowers' ));
2025-11-22 12:31:23 -08:00
2025-11-22 15:47:38 -08:00
const updateNotice = hasUpdates ?
'\n\n**Updates available!** Run `cd ~/.config/opencode/superpowers && git pull` to update superpowers.' :
'' ;
2025-11-22 12:31:23 -08:00
2025-11-22 15:47:38 -08:00
return {
context : `<EXTREMELY_IMPORTANT>
2025-11-22 12:31:23 -08:00
You have superpowers.
**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'use_skill' tool:**
${ usingSuperpowersContent }
${ toolMapping }${ updateNotice }
</EXTREMELY_IMPORTANT>`
2025-11-22 15:47:38 -08:00
};
}
2025-11-22 12:31:23 -08:00
}
2025-11-22 12:29:50 -08:00
};
};