# OpenCode Support Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Add full superpowers support for OpenCode.ai with a native JavaScript plugin that shares core functionality with the existing Codex implementation. **Architecture:** Extract common skill discovery/parsing logic into `lib/skills-core.js`, refactor Codex to use it, then build OpenCode plugin using their native plugin API with custom tools and session hooks. **Tech Stack:** Node.js, JavaScript, OpenCode Plugin API, Git worktrees --- ## Phase 1: Create Shared Core Module ### Task 1: Extract Frontmatter Parsing **Files:** - Create: `lib/skills-core.js` - Reference: `.codex/superpowers-codex` (lines 40-74) **Step 1: Create lib/skills-core.js with extractFrontmatter function** ```javascript #!/usr/bin/env node const fs = require('fs'); const path = require('path'); /** * Extract YAML frontmatter from a skill file. * Current format: * --- * name: skill-name * description: Use when [condition] - [what it does] * --- * * @param {string} filePath - Path to SKILL.md file * @returns {{name: string, description: string}} */ function extractFrontmatter(filePath) { try { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.split('\n'); let inFrontmatter = false; let name = ''; let description = ''; for (const line of lines) { if (line.trim() === '---') { if (inFrontmatter) break; inFrontmatter = true; continue; } if (inFrontmatter) { const match = line.match(/^(\w+):\s*(.*)$/); if (match) { const [, key, value] = match; switch (key) { case 'name': name = value.trim(); break; case 'description': description = value.trim(); break; } } } } return { name, description }; } catch (error) { return { name: '', description: '' }; } } module.exports = { extractFrontmatter }; ``` **Step 2: Verify file was created** Run: `ls -l lib/skills-core.js` Expected: File exists **Step 3: Commit** ```bash git add lib/skills-core.js git commit -m "feat: create shared skills core module with frontmatter parser" ``` --- ### Task 2: Extract Skill Discovery Logic **Files:** - Modify: `lib/skills-core.js` - Reference: `.codex/superpowers-codex` (lines 97-136) **Step 1: Add findSkillsInDir function to skills-core.js** Add before `module.exports`: ```javascript /** * Find all SKILL.md files in a directory recursively. * * @param {string} dir - Directory to search * @param {string} sourceType - 'personal' or 'superpowers' for namespacing * @param {number} maxDepth - Maximum recursion depth (default: 3) * @returns {Array<{path: string, name: string, description: string, sourceType: string}>} */ function findSkillsInDir(dir, sourceType, maxDepth = 3) { const skills = []; if (!fs.existsSync(dir)) return skills; function recurse(currentDir, depth) { if (depth > maxDepth) return; const entries = fs.readdirSync(currentDir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentDir, entry.name); if (entry.isDirectory()) { // Check for SKILL.md in this directory const skillFile = path.join(fullPath, 'SKILL.md'); if (fs.existsSync(skillFile)) { const { name, description } = extractFrontmatter(skillFile); skills.push({ path: fullPath, skillFile: skillFile, name: name || entry.name, description: description || '', sourceType: sourceType }); } // Recurse into subdirectories recurse(fullPath, depth + 1); } } } recurse(dir, 0); return skills; } ``` **Step 2: Update module.exports** Replace the exports line with: ```javascript module.exports = { extractFrontmatter, findSkillsInDir }; ``` **Step 3: Verify syntax** Run: `node -c lib/skills-core.js` Expected: No output (success) **Step 4: Commit** ```bash git add lib/skills-core.js git commit -m "feat: add skill discovery function to core module" ``` --- ### Task 3: Extract Skill Resolution Logic **Files:** - Modify: `lib/skills-core.js` - Reference: `.codex/superpowers-codex` (lines 212-280) **Step 1: Add resolveSkillPath function** Add before `module.exports`: ```javascript /** * Resolve a skill name to its file path, handling shadowing * (personal skills override superpowers skills). * * @param {string} skillName - Name like "superpowers:brainstorming" or "my-skill" * @param {string} superpowersDir - Path to superpowers skills directory * @param {string} personalDir - Path to personal skills directory * @returns {{skillFile: string, sourceType: string, skillPath: string} | null} */ function resolveSkillPath(skillName, superpowersDir, personalDir) { // Strip superpowers: prefix if present const forceSuperpowers = skillName.startsWith('superpowers:'); const actualSkillName = forceSuperpowers ? skillName.replace(/^superpowers:/, '') : skillName; // Try personal skills first (unless explicitly superpowers:) if (!forceSuperpowers && personalDir) { const personalPath = path.join(personalDir, actualSkillName); const personalSkillFile = path.join(personalPath, 'SKILL.md'); if (fs.existsSync(personalSkillFile)) { return { skillFile: personalSkillFile, sourceType: 'personal', skillPath: actualSkillName }; } } // Try superpowers skills if (superpowersDir) { const superpowersPath = path.join(superpowersDir, actualSkillName); const superpowersSkillFile = path.join(superpowersPath, 'SKILL.md'); if (fs.existsSync(superpowersSkillFile)) { return { skillFile: superpowersSkillFile, sourceType: 'superpowers', skillPath: actualSkillName }; } } return null; } ``` **Step 2: Update module.exports** ```javascript module.exports = { extractFrontmatter, findSkillsInDir, resolveSkillPath }; ``` **Step 3: Verify syntax** Run: `node -c lib/skills-core.js` Expected: No output **Step 4: Commit** ```bash git add lib/skills-core.js git commit -m "feat: add skill path resolution with shadowing support" ``` --- ### Task 4: Extract Update Check Logic **Files:** - Modify: `lib/skills-core.js` - Reference: `.codex/superpowers-codex` (lines 16-38) **Step 1: Add checkForUpdates function** Add at top after requires: ```javascript const { execSync } = require('child_process'); ``` Add before `module.exports`: ```javascript /** * Check if a git repository has updates available. * * @param {string} repoDir - Path to git repository * @returns {boolean} - True if updates are available */ function checkForUpdates(repoDir) { try { // Quick check with 3 second timeout to avoid delays if network is down const output = execSync('git fetch origin && git status --porcelain=v1 --branch', { cwd: repoDir, timeout: 3000, encoding: 'utf8', stdio: 'pipe' }); // Parse git status output to see if we're behind const statusLines = output.split('\n'); for (const line of statusLines) { if (line.startsWith('## ') && line.includes('[behind ')) { return true; // We're behind remote } } return false; // Up to date } catch (error) { // Network down, git error, timeout, etc. - don't block bootstrap return false; } } ``` **Step 2: Update module.exports** ```javascript module.exports = { extractFrontmatter, findSkillsInDir, resolveSkillPath, checkForUpdates }; ``` **Step 3: Verify syntax** Run: `node -c lib/skills-core.js` Expected: No output **Step 4: Commit** ```bash git add lib/skills-core.js git commit -m "feat: add git update checking to core module" ``` --- ## Phase 2: Refactor Codex to Use Shared Core ### Task 5: Update Codex to Import Shared Core **Files:** - Modify: `.codex/superpowers-codex` (add import at top) **Step 1: Add import statement** After the existing requires at top of file (around line 6), add: ```javascript const skillsCore = require('../lib/skills-core'); ``` **Step 2: Verify syntax** Run: `node -c .codex/superpowers-codex` Expected: No output **Step 3: Commit** ```bash git add .codex/superpowers-codex git commit -m "refactor: import shared skills core in codex" ``` --- ### Task 6: Replace extractFrontmatter with Core Version **Files:** - Modify: `.codex/superpowers-codex` (lines 40-74) **Step 1: Remove local extractFrontmatter function** Delete lines 40-74 (the entire extractFrontmatter function definition). **Step 2: Update all extractFrontmatter calls** Find and replace all calls from `extractFrontmatter(` to `skillsCore.extractFrontmatter(` Affected lines approximately: 90, 310 **Step 3: Verify script still works** Run: `.codex/superpowers-codex find-skills | head -20` Expected: Shows list of skills **Step 4: Commit** ```bash git add .codex/superpowers-codex git commit -m "refactor: use shared extractFrontmatter in codex" ``` --- ### Task 7: Replace findSkillsInDir with Core Version **Files:** - Modify: `.codex/superpowers-codex` (lines 97-136, approximately) **Step 1: Remove local findSkillsInDir function** Delete the entire `findSkillsInDir` function definition (approximately lines 97-136). **Step 2: Update all findSkillsInDir calls** Replace calls from `findSkillsInDir(` to `skillsCore.findSkillsInDir(` **Step 3: Verify script still works** Run: `.codex/superpowers-codex find-skills | head -20` Expected: Shows list of skills **Step 4: Commit** ```bash git add .codex/superpowers-codex git commit -m "refactor: use shared findSkillsInDir in codex" ``` --- ### Task 8: Replace checkForUpdates with Core Version **Files:** - Modify: `.codex/superpowers-codex` (lines 16-38, approximately) **Step 1: Remove local checkForUpdates function** Delete the entire `checkForUpdates` function definition. **Step 2: Update all checkForUpdates calls** Replace calls from `checkForUpdates(` to `skillsCore.checkForUpdates(` **Step 3: Verify script still works** Run: `.codex/superpowers-codex bootstrap | head -50` Expected: Shows bootstrap content **Step 4: Commit** ```bash git add .codex/superpowers-codex git commit -m "refactor: use shared checkForUpdates in codex" ``` --- ## Phase 3: Build OpenCode Plugin ### Task 9: Create OpenCode Plugin Directory Structure **Files:** - Create: `.opencode/plugin/superpowers.js` **Step 1: Create directory** Run: `mkdir -p .opencode/plugin` **Step 2: Create basic plugin file** ```javascript #!/usr/bin/env node /** * 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'); /** * OpenCode plugin entry point */ export const SuperpowersPlugin = async ({ project, client, $, directory, worktree }) => { return { // Custom tools and hooks will go here }; }; ``` **Step 3: Verify file was created** Run: `ls -l .opencode/plugin/superpowers.js` Expected: File exists **Step 4: Commit** ```bash git add .opencode/plugin/superpowers.js git commit -m "feat: create opencode plugin scaffold" ``` --- ### Task 10: Implement use_skill Tool **Files:** - Modify: `.opencode/plugin/superpowers.js` **Step 1: Add use_skill tool implementation** Replace the plugin return statement with: ```javascript export const SuperpowersPlugin = async ({ project, client, $, directory, worktree }) => { // Import zod for schema validation 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 }) => { // Resolve skill path (handles shadowing: personal > superpowers) 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.`; } // Read skill content const fullContent = fs.readFileSync(resolved.skillFile, 'utf8'); const { name, description } = skillsCore.extractFrontmatter(resolved.skillFile); // Extract content after frontmatter const lines = fullContent.split('\n'); let inFrontmatter = false; let frontmatterEnded = false; const contentLines = []; for (const line of lines) { if (line.trim() === '---') { if (inFrontmatter) { frontmatterEnded = true; continue; } inFrontmatter = true; continue; } if (frontmatterEnded || !inFrontmatter) { contentLines.push(line); } } const content = contentLines.join('\n').trim(); const skillDirectory = path.dirname(resolved.skillFile); // Format output similar to Claude Code's Skill tool return `# ${name || skill_name} # ${description || ''} # Supporting tools and docs are in ${skillDirectory} # ============================================ ${content}`; } } ] }; }; ``` **Step 2: Verify syntax** Run: `node -c .opencode/plugin/superpowers.js` Expected: No output **Step 3: Commit** ```bash git add .opencode/plugin/superpowers.js git commit -m "feat: implement use_skill tool for opencode" ``` --- ### Task 11: Implement find_skills Tool **Files:** - Modify: `.opencode/plugin/superpowers.js` **Step 1: Add find_skills tool to tools array** Add after the use_skill tool definition, before closing the tools array: ```javascript { name: 'find_skills', description: 'List all available skills in the superpowers and personal skill libraries.', schema: z.object({}), execute: async () => { // Find skills in both directories const superpowersSkills = skillsCore.findSkillsInDir( superpowersSkillsDir, 'superpowers', 3 ); const personalSkills = skillsCore.findSkillsInDir( personalSkillsDir, 'personal', 3 ); // Combine and format skills list 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; } } ``` **Step 2: Verify syntax** Run: `node -c .opencode/plugin/superpowers.js` Expected: No output **Step 3: Commit** ```bash git add .opencode/plugin/superpowers.js git commit -m "feat: implement find_skills tool for opencode" ``` --- ### Task 12: Implement Session Start Hook **Files:** - Modify: `.opencode/plugin/superpowers.js` **Step 1: Add session.started hook** After the tools array, add: ```javascript 'session.started': async () => { // Read using-superpowers skill content const usingSuperpowersPath = skillsCore.resolveSkillPath( 'using-superpowers', superpowersSkillsDir, personalSkillsDir ); let usingSuperpowersContent = ''; if (usingSuperpowersPath) { const fullContent = fs.readFileSync(usingSuperpowersPath.skillFile, 'utf8'); // Strip frontmatter const lines = fullContent.split('\n'); let inFrontmatter = false; let frontmatterEnded = false; const contentLines = []; for (const line of lines) { if (line.trim() === '---') { if (inFrontmatter) { frontmatterEnded = true; continue; } inFrontmatter = true; continue; } if (frontmatterEnded || !inFrontmatter) { contentLines.push(line); } } usingSuperpowersContent = contentLines.join('\n').trim(); } // Tool mapping instructions 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 `; // Check for updates (non-blocking) 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 to inject into session return { context: ` 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} ` }; } ``` **Step 2: Verify syntax** Run: `node -c .opencode/plugin/superpowers.js` Expected: No output **Step 3: Commit** ```bash git add .opencode/plugin/superpowers.js git commit -m "feat: implement session.started hook for opencode" ``` --- ## Phase 4: Documentation ### Task 13: Create OpenCode Installation Guide **Files:** - Create: `.opencode/INSTALL.md` **Step 1: Create installation guide** ```markdown # Installing Superpowers for OpenCode ## Prerequisites - [OpenCode.ai](https://opencode.ai) installed - Node.js installed - Git installed ## Installation Steps ### 1. Install Superpowers Skills ```bash # Clone superpowers skills to OpenCode config directory mkdir -p ~/.config/opencode/superpowers git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers ``` ### 2. Install the Plugin The plugin is included in the superpowers repository you just cloned. OpenCode will automatically discover it from: - `~/.config/opencode/superpowers/.opencode/plugin/superpowers.js` Or you can link it to the project-local plugin directory: ```bash # In your OpenCode project mkdir -p .opencode/plugin ln -s ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js .opencode/plugin/superpowers.js ``` ### 3. Restart OpenCode Restart OpenCode to load the plugin. On the next session, you should see: ``` You have superpowers. ``` ## Usage ### Finding Skills Use the `find_skills` tool to list all available skills: ``` use find_skills tool ``` ### Loading a Skill Use the `use_skill` tool to load a specific skill: ``` use use_skill tool with skill_name: "superpowers:brainstorming" ``` ### Personal Skills Create your own skills in `~/.config/opencode/skills/`: ```bash mkdir -p ~/.config/opencode/skills/my-skill ``` Create `~/.config/opencode/skills/my-skill/SKILL.md`: ```markdown --- name: my-skill description: Use when [condition] - [what it does] --- # My Skill [Your skill content here] ``` Personal skills override superpowers skills with the same name. ## Updating ```bash cd ~/.config/opencode/superpowers git pull ``` ## Troubleshooting ### Plugin not loading 1. Check plugin file exists: `ls ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js` 2. Check OpenCode logs for errors 3. Verify Node.js is installed: `node --version` ### Skills not found 1. Verify skills directory exists: `ls ~/.config/opencode/superpowers/skills` 2. Use `find_skills` tool to see what's discovered 3. Check file structure: each skill should have a `SKILL.md` file ### Tool mapping issues When a skill references a Claude Code tool you don't have: - `TodoWrite` → use `update_plan` - `Task` with subagents → use `@mention` syntax to invoke OpenCode subagents - `Skill` → use `use_skill` tool - File operations → use your native tools ## Getting Help - Report issues: https://github.com/obra/superpowers/issues - Documentation: https://github.com/obra/superpowers ``` **Step 2: Verify file created** Run: `ls -l .opencode/INSTALL.md` Expected: File exists **Step 3: Commit** ```bash git add .opencode/INSTALL.md git commit -m "docs: add opencode installation guide" ``` --- ### Task 14: Update Main README **Files:** - Modify: `README.md` **Step 1: Add OpenCode section** Find the section about supported platforms (search for "Codex" in the file), and add after it: ```markdown ### OpenCode Superpowers works with [OpenCode.ai](https://opencode.ai) through a native JavaScript plugin. **Installation:** See [.opencode/INSTALL.md](.opencode/INSTALL.md) **Features:** - Custom tools: `use_skill` and `find_skills` - Automatic session bootstrap - Personal skills with shadowing - Supporting files and scripts access ``` **Step 2: Verify formatting** Run: `grep -A 10 "### OpenCode" README.md` Expected: Shows the section you added **Step 3: Commit** ```bash git add README.md git commit -m "docs: add opencode support to readme" ``` --- ### Task 15: Update Release Notes **Files:** - Modify: `RELEASE-NOTES.md` **Step 1: Add entry for OpenCode support** At the top of the file (after the header), add: ```markdown ## [Unreleased] ### Added - **OpenCode Support**: Native JavaScript plugin for OpenCode.ai - Custom tools: `use_skill` and `find_skills` - Automatic session bootstrap with tool mapping instructions - Shared core module (`lib/skills-core.js`) for code reuse - Installation guide in `.opencode/INSTALL.md` ### Changed - **Refactored Codex Implementation**: Now uses shared `lib/skills-core.js` module - Eliminates code duplication between Codex and OpenCode - Single source of truth for skill discovery and parsing --- ``` **Step 2: Verify formatting** Run: `head -30 RELEASE-NOTES.md` Expected: Shows your new section **Step 3: Commit** ```bash git add RELEASE-NOTES.md git commit -m "docs: add opencode support to release notes" ``` --- ## Phase 5: Final Verification ### Task 16: Test Codex Still Works **Files:** - Test: `.codex/superpowers-codex` **Step 1: Test find-skills command** Run: `.codex/superpowers-codex find-skills | head -20` Expected: Shows list of skills with names and descriptions **Step 2: Test use-skill command** Run: `.codex/superpowers-codex use-skill superpowers:brainstorming | head -20` Expected: Shows brainstorming skill content **Step 3: Test bootstrap command** Run: `.codex/superpowers-codex bootstrap | head -30` Expected: Shows bootstrap content with instructions **Step 4: If all tests pass, record success** No commit needed - this is verification only. --- ### Task 17: Verify File Structure **Files:** - Check: All new files exist **Step 1: Verify all files created** Run: ```bash ls -l lib/skills-core.js ls -l .opencode/plugin/superpowers.js ls -l .opencode/INSTALL.md ``` Expected: All files exist **Step 2: Verify directory structure** Run: `tree -L 2 .opencode/` (or `find .opencode -type f` if tree not available) Expected: ``` .opencode/ ├── INSTALL.md └── plugin/ └── superpowers.js ``` **Step 3: If structure correct, proceed** No commit needed - this is verification only. --- ### Task 18: Final Commit and Summary **Files:** - Check: `git status` **Step 1: Check git status** Run: `git status` Expected: Working tree clean, all changes committed **Step 2: Review commit log** Run: `git log --oneline -20` Expected: Shows all commits from this implementation **Step 3: Create summary document** Create a completion summary showing: - Total commits made - Files created: `lib/skills-core.js`, `.opencode/plugin/superpowers.js`, `.opencode/INSTALL.md` - Files modified: `.codex/superpowers-codex`, `README.md`, `RELEASE-NOTES.md` - Testing performed: Codex commands verified - Ready for: Testing with actual OpenCode installation **Step 4: Report completion** Present summary to user and offer to: 1. Push to remote 2. Create pull request 3. Test with real OpenCode installation (requires OpenCode installed) --- ## Testing Guide (Manual - Requires OpenCode) These steps require OpenCode to be installed and are not part of the automated implementation: 1. **Install skills**: Follow `.opencode/INSTALL.md` 2. **Start OpenCode session**: Verify bootstrap appears 3. **Test find_skills**: Should list all available skills 4. **Test use_skill**: Load a skill and verify content appears 5. **Test supporting files**: Verify skill directory paths are accessible 6. **Test personal skills**: Create a personal skill and verify it shadows core 7. **Test tool mapping**: Verify TodoWrite → update_plan mapping works ## Success Criteria - [ ] `lib/skills-core.js` created with all core functions - [ ] `.codex/superpowers-codex` refactored to use shared core - [ ] Codex commands still work (find-skills, use-skill, bootstrap) - [ ] `.opencode/plugin/superpowers.js` created with tools and hooks - [ ] Installation guide created - [ ] README and RELEASE-NOTES updated - [ ] All changes committed - [ ] Working tree clean