|
|
@@ -0,0 +1,1095 @@
|
|
|
+# 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: `<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>`
|
|
|
+ };
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+**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
|