| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- /**
- * Superpowers plugin for OpenCode.ai
- *
- * Provides custom tools for loading and discovering skills,
- * with automatic bootstrap on session start.
- */
- const skillsCore = require('../../lib/skills-core');
- const path = require('path');
- const fs = require('fs');
- const os = require('os');
- const homeDir = os.homedir();
- const superpowersSkillsDir = path.join(homeDir, '.config/opencode/superpowers/skills');
- const personalSkillsDir = path.join(homeDir, '.config/opencode/skills');
- module.exports = async ({ project, client, $, directory, worktree }) => {
- const { z } = await import('zod');
- return {
- 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);
- const content = skillsCore.stripFrontmatter(fullContent);
- const skillDirectory = path.dirname(resolved.skillFile);
- return `# ${name || skill_name}
- # ${description || ''}
- # Supporting tools and docs are in ${skillDirectory}
- # ============================================
- ${content}`;
- }
- },
- {
- 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;
- }
- }
- ],
- 'session.started': async () => {
- const usingSuperpowersPath = skillsCore.resolveSkillPath('using-superpowers', superpowersSkillsDir, personalSkillsDir);
- let usingSuperpowersContent = '';
- if (usingSuperpowersPath) {
- const fullContent = fs.readFileSync(usingSuperpowersPath.skillFile, 'utf8');
- usingSuperpowersContent = skillsCore.stripFrontmatter(fullContent);
- }
- const toolMapping = `
- **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
- `;
- const hasUpdates = skillsCore.checkForUpdates(path.join(homeDir, '.config/opencode/superpowers'));
- const updateNotice = hasUpdates ?
- '\n\n**Updates available!** Run `cd ~/.config/opencode/superpowers && git pull` to update superpowers.' :
- '';
- return {
- context: `<EXTREMELY_IMPORTANT>
- 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>`
- };
- }
- };
- };
|