superpowers.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * Superpowers plugin for OpenCode.ai
  3. *
  4. * Provides custom tools for loading and discovering skills,
  5. * with prompt generation for agent configuration.
  6. */
  7. import path from 'path';
  8. import fs from 'fs';
  9. import os from 'os';
  10. import { fileURLToPath } from 'url';
  11. import { tool } from '@opencode-ai/plugin/tool';
  12. import * as skillsCore from '../../lib/skills-core.js';
  13. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  14. export const SuperpowersPlugin = async ({ project, client, $, directory, worktree }) => {
  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. const promptsDir = path.join(homeDir, '.config/opencode/prompts');
  19. const promptFile = path.join(promptsDir, 'superpowers.txt');
  20. return {
  21. tool: {
  22. use_skill: tool({
  23. description: 'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques.',
  24. args: {
  25. skill_name: tool.schema.string().describe('Name of the skill to load (e.g., "superpowers:brainstorming" or "my-custom-skill")')
  26. },
  27. execute: async (args, context) => {
  28. const { skill_name } = args;
  29. const resolved = skillsCore.resolveSkillPath(skill_name, superpowersSkillsDir, personalSkillsDir);
  30. if (!resolved) {
  31. return `Error: Skill "${skill_name}" not found.\n\nRun find_skills to see available skills.`;
  32. }
  33. const fullContent = fs.readFileSync(resolved.skillFile, 'utf8');
  34. const { name, description } = skillsCore.extractFrontmatter(resolved.skillFile);
  35. const content = skillsCore.stripFrontmatter(fullContent);
  36. const skillDirectory = path.dirname(resolved.skillFile);
  37. return `# ${name || skill_name}
  38. # ${description || ''}
  39. # Supporting tools and docs are in ${skillDirectory}
  40. # ============================================
  41. ${content}`;
  42. }
  43. }),
  44. find_skills: tool({
  45. description: 'List all available skills in the superpowers and personal skill libraries.',
  46. args: {},
  47. execute: async (args, context) => {
  48. const superpowersSkills = skillsCore.findSkillsInDir(superpowersSkillsDir, 'superpowers', 3);
  49. const personalSkills = skillsCore.findSkillsInDir(personalSkillsDir, 'personal', 3);
  50. const allSkills = [...personalSkills, ...superpowersSkills];
  51. if (allSkills.length === 0) {
  52. return 'No skills found. Install superpowers skills to ~/.config/opencode/superpowers/skills/';
  53. }
  54. let output = 'Available skills:\n\n';
  55. for (const skill of allSkills) {
  56. const namespace = skill.sourceType === 'personal' ? '' : 'superpowers:';
  57. const skillName = skill.name || path.basename(skill.path);
  58. output += `${namespace}${skillName}\n`;
  59. if (skill.description) {
  60. output += ` ${skill.description}\n`;
  61. }
  62. output += ` Directory: ${skill.path}\n\n`;
  63. }
  64. return output;
  65. }
  66. })
  67. },
  68. "chat.message": async (input, output) => {
  69. // Only inject on first message of session (or every message if needed)
  70. if (!output.message.system || output.message.system.length === 0) {
  71. const usingSuperpowersPath = skillsCore.resolveSkillPath('using-superpowers', superpowersSkillsDir, personalSkillsDir);
  72. if (usingSuperpowersPath) {
  73. const fullContent = fs.readFileSync(usingSuperpowersPath.skillFile, 'utf8');
  74. const usingSuperpowersContent = skillsCore.stripFrontmatter(fullContent);
  75. const toolMapping = `**Tool Mapping for OpenCode:**
  76. When skills reference tools you don't have, substitute OpenCode equivalents:
  77. - \`TodoWrite\` → \`update_plan\`
  78. - \`Task\` tool with subagents → Use OpenCode's subagent system (@mention)
  79. - \`Skill\` tool → \`use_skill\` custom tool
  80. - \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
  81. **Skills naming:**
  82. - Superpowers skills: \`superpowers:skill-name\`
  83. - Personal skills: \`skill-name\`
  84. - Personal skills override superpowers when names match`;
  85. output.message.system = `<EXTREMELY_IMPORTANT>
  86. You have superpowers.
  87. ${usingSuperpowersContent}
  88. ${toolMapping}
  89. </EXTREMELY_IMPORTANT>`;
  90. }
  91. }
  92. }
  93. };
  94. };