superpowers.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * Superpowers plugin for OpenCode.ai
  3. *
  4. * Provides custom tools for loading and discovering skills,
  5. * with automatic bootstrap on session start.
  6. */
  7. import path from 'path';
  8. import fs from 'fs';
  9. import os from 'os';
  10. import { fileURLToPath } from 'url';
  11. import * as skillsCore from '../../lib/skills-core.js';
  12. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  13. export const SuperpowersPlugin = async ({ project, client, $, directory, worktree }) => {
  14. const { z } = await import('zod');
  15. const homeDir = os.homedir();
  16. const superpowersSkillsDir = path.join(homeDir, '.config/opencode/superpowers/skills');
  17. const personalSkillsDir = path.join(homeDir, '.config/opencode/skills');
  18. return {
  19. tools: [
  20. {
  21. name: 'use_skill',
  22. description: 'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques.',
  23. schema: z.object({
  24. skill_name: z.string().describe('Name of the skill to load (e.g., "superpowers:brainstorming" or "my-custom-skill")')
  25. }),
  26. execute: async ({ skill_name }) => {
  27. const resolved = skillsCore.resolveSkillPath(skill_name, superpowersSkillsDir, personalSkillsDir);
  28. if (!resolved) {
  29. return `Error: Skill "${skill_name}" not found.\n\nRun find_skills to see available skills.`;
  30. }
  31. const fullContent = fs.readFileSync(resolved.skillFile, 'utf8');
  32. const { name, description } = skillsCore.extractFrontmatter(resolved.skillFile);
  33. const content = skillsCore.stripFrontmatter(fullContent);
  34. const skillDirectory = path.dirname(resolved.skillFile);
  35. return `# ${name || skill_name}
  36. # ${description || ''}
  37. # Supporting tools and docs are in ${skillDirectory}
  38. # ============================================
  39. ${content}`;
  40. }
  41. },
  42. {
  43. name: 'find_skills',
  44. description: 'List all available skills in the superpowers and personal skill libraries.',
  45. schema: z.object({}),
  46. execute: async () => {
  47. const superpowersSkills = skillsCore.findSkillsInDir(superpowersSkillsDir, 'superpowers', 3);
  48. const personalSkills = skillsCore.findSkillsInDir(personalSkillsDir, 'personal', 3);
  49. const allSkills = [...personalSkills, ...superpowersSkills];
  50. if (allSkills.length === 0) {
  51. return 'No skills found. Install superpowers skills to ~/.config/opencode/superpowers/skills/';
  52. }
  53. let output = 'Available skills:\n\n';
  54. for (const skill of allSkills) {
  55. const namespace = skill.sourceType === 'personal' ? '' : 'superpowers:';
  56. const skillName = skill.name || path.basename(skill.path);
  57. output += `${namespace}${skillName}\n`;
  58. if (skill.description) {
  59. output += ` ${skill.description}\n`;
  60. }
  61. output += ` Directory: ${skill.path}\n\n`;
  62. }
  63. return output;
  64. }
  65. }
  66. ],
  67. event: async ({ event }) => {
  68. if (event.type === 'session.created') {
  69. const usingSuperpowersPath = skillsCore.resolveSkillPath('using-superpowers', superpowersSkillsDir, personalSkillsDir);
  70. let usingSuperpowersContent = '';
  71. if (usingSuperpowersPath) {
  72. const fullContent = fs.readFileSync(usingSuperpowersPath.skillFile, 'utf8');
  73. usingSuperpowersContent = skillsCore.stripFrontmatter(fullContent);
  74. }
  75. const toolMapping = `
  76. **Tool Mapping for OpenCode:**
  77. When skills reference tools you don't have, substitute OpenCode equivalents:
  78. - \`TodoWrite\` → \`update_plan\` (your planning/task tracking tool)
  79. - \`Task\` tool with subagents → Use OpenCode's subagent system (@mention syntax or automatic dispatch)
  80. - \`Skill\` tool → \`use_skill\` custom tool (already available)
  81. - \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Use your native tools
  82. **Skill directories contain supporting files:**
  83. - Scripts you can run with bash tool
  84. - Additional documentation you can read
  85. - Utilities and helpers specific to that skill
  86. **Skills naming:**
  87. - Superpowers skills: \`superpowers:skill-name\` (from ~/.config/opencode/superpowers/skills/)
  88. - Personal skills: \`skill-name\` (from ~/.config/opencode/skills/)
  89. - Personal skills override superpowers skills when names match
  90. `;
  91. const hasUpdates = skillsCore.checkForUpdates(path.join(homeDir, '.config/opencode/superpowers'));
  92. const updateNotice = hasUpdates ?
  93. '\n\n**Updates available!** Run `cd ~/.config/opencode/superpowers && git pull` to update superpowers.' :
  94. '';
  95. return {
  96. context: `<EXTREMELY_IMPORTANT>
  97. You have superpowers.
  98. **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:**
  99. ${usingSuperpowersContent}
  100. ${toolMapping}${updateNotice}
  101. </EXTREMELY_IMPORTANT>`
  102. };
  103. }
  104. }
  105. };
  106. };