Преглед изворни кода

Add Codex superpowers integration

- Single unified script: superpowers-codex with bootstrap/use-skill/find-skills commands
- Cross-platform Node.js implementation (works on Windows, macOS, Linux)
- Namespaced skills: superpowers:skill-name for superpowers, skill-name for personal
- Personal skills override superpowers skills when names match
- Clean skill display: shows name/description without raw frontmatter
- Helpful context: shows supporting files directory for each skill
- Tool mapping for Codex: TodoWrite→update_plan, subagents→manual fallback, etc.
- Updated terminology: "Superpowers skills" instead of "Core skills"
- Bootstrap integration with AGENTS.md for automatic startup
- Installation guide and bootstrap instructions specific to Codex
Jesse Vincent пре 10 месеци
родитељ
комит
26487902f8
3 измењених фајлова са 442 додато и 0 уклоњено
  1. 62 0
      codex/INSTALL.md
  2. 29 0
      codex/superpowers-bootstrap.md
  3. 351 0
      scripts/superpowers-codex

+ 62 - 0
codex/INSTALL.md

@@ -0,0 +1,62 @@
+# Installing Superpowers for Codex
+
+Quick setup to enable superpowers skills in Codex.
+
+## Installation
+
+1. **Clone superpowers repository**:
+   ```bash
+   mkdir -p ~/.codex/superpowers
+   cd ~/.codex/superpowers
+   git clone https://github.com/obra/superpowers.git .
+   ```
+
+2. **Create personal skills directory**:
+   ```bash
+   mkdir -p ~/.codex/skills
+   ```
+
+3. **Update ~/.codex/AGENTS.md** to include this superpowers section:
+   ```markdown
+   ## Superpowers System
+
+   <EXTREMELY_IMPORTANT>
+   You have superpowers.
+
+   **RIGHT NOW, immediately run the bootstrap script:**
+
+   ```bash
+   ~/.codex/superpowers/scripts/superpowers-codex bootstrap
+   ```
+
+   This single command will:
+   - Show you the complete bootstrap instructions
+   - List all available skills
+   - Auto-load the using-superpowers skill
+
+   **Tool paths for ongoing use:**
+   - superpowers-codex: ~/.codex/superpowers/scripts/superpowers-codex
+
+   **Critical Rules:**
+   - Before ANY task, review the skills list (already shown in bootstrap)
+   - If a relevant skill exists, you MUST use superpowers-codex use-skill to load it
+   - Announce: "I've read the [Skill Name] skill and I'm using it to [purpose]"
+   - Skills with checklists require update_plan todos for each item
+   - NEVER skip mandatory workflows (brainstorming before coding, TDD, systematic debugging)
+
+   **Skills location:**
+   - Superpowers skills: ~/.codex/superpowers/skills/
+   - Personal skills: ~/.codex/skills/ (override superpowers when names match)
+
+   IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
+   </EXTREMELY_IMPORTANT>
+   ```
+
+## Verification
+
+Test the installation:
+```bash
+~/.codex/superpowers/scripts/superpowers-codex bootstrap
+```
+
+You should see skill listings and bootstrap instructions. The system is now ready for use.

+ 29 - 0
codex/superpowers-bootstrap.md

@@ -0,0 +1,29 @@
+# Superpowers Bootstrap for Codex
+
+<EXTREMELY_IMPORTANT>
+You have superpowers.
+
+**Tool for running skills:**
+- `~/.codex/superpowers/scripts/superpowers-codex use-skill <skill-name>`
+
+**Tool Mapping for Codex:**
+When skills reference tools you don't have, substitute your equivalent tools:
+- `TodoWrite` → `update_plan` (your planning/task tracking tool)
+- `Task` tool with subagents → Tell the user that subagents aren't available in Codex yet and you'll do the work the subagent would do
+- `Skill` tool → `superpowers-codex use-skill` command (already available)
+- `Read`, `Write`, `Edit`, `Bash` → Use your native tools with similar functions
+
+**Skills naming:**
+- Superpowers skills: `superpowers:skill-name` (from ~/.codex/superpowers/skills/)
+- Personal skills: `skill-name` (from ~/.codex/skills/)
+- Personal skills override superpowers skills when names match
+
+**Critical Rules:**
+- Before ANY task, check the skills list shown below
+- If a relevant skill exists, you MUST use `superpowers-codex use-skill` to load it
+- Announce: "I've read the [Skill Name] skill and I'm using it to [purpose]"
+- Skills with checklists require `update_plan` todos for each item
+- NEVER skip mandatory workflows (brainstorming before coding, TDD, systematic debugging)
+
+IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
+</EXTREMELY_IMPORTANT>

+ 351 - 0
scripts/superpowers-codex

@@ -0,0 +1,351 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+
+// Paths
+const homeDir = os.homedir();
+const superpowersSkillsDir = path.join(homeDir, '.codex', 'superpowers', 'skills');
+const personalSkillsDir = path.join(homeDir, '.codex', 'skills');
+const bootstrapFile = path.join(homeDir, '.codex', 'superpowers', 'codex', 'superpowers-bootstrap.md');
+
+// Utility functions
+function extractFrontmatter(filePath) {
+    try {
+        const content = fs.readFileSync(filePath, 'utf8');
+        const lines = content.split('\n');
+
+        let inFrontmatter = false;
+        let name = '';
+        let description = '';
+        let whenToUse = '';
+
+        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;
+                        case 'when_to_use': whenToUse = value.trim(); break;
+                    }
+                }
+            }
+        }
+
+        return { name, description, whenToUse };
+    } catch (error) {
+        return { name: '', description: '', whenToUse: '' };
+    }
+}
+
+function printSkill(skillPath, sourceType) {
+    const skillFile = path.join(skillPath, 'SKILL.md');
+    const relPath = sourceType === 'personal'
+        ? path.relative(personalSkillsDir, skillPath)
+        : path.relative(superpowersSkillsDir, skillPath);
+
+    // Print skill name with namespace
+    if (sourceType === 'personal') {
+        console.log(relPath.replace(/\\/g, '/')); // Personal skills are not namespaced
+    } else {
+        console.log(`superpowers:${relPath.replace(/\\/g, '/')}`); // Superpowers skills get superpowers namespace
+    }
+
+    // Extract and print metadata
+    const { name, description, whenToUse } = extractFrontmatter(skillFile);
+
+    if (description) console.log(`  ${description}`);
+    if (whenToUse) console.log(`  When to use: ${whenToUse}`);
+    console.log('');
+}
+
+function findSkillsInDir(dir, sourceType, maxDepth = 1) {
+    const skills = [];
+
+    if (!fs.existsSync(dir)) return skills;
+
+    function searchDir(currentDir, currentDepth) {
+        if (currentDepth > maxDepth) return;
+
+        try {
+            const entries = fs.readdirSync(currentDir, { withFileTypes: true });
+
+            for (const entry of entries) {
+                if (entry.isDirectory()) {
+                    const skillDir = path.join(currentDir, entry.name);
+                    const skillFile = path.join(skillDir, 'SKILL.md');
+
+                    if (fs.existsSync(skillFile)) {
+                        skills.push(skillDir);
+                    }
+
+                    // For personal skills, search deeper (category/skill structure)
+                    if (sourceType === 'personal' && currentDepth < maxDepth) {
+                        searchDir(skillDir, currentDepth + 1);
+                    }
+                }
+            }
+        } catch (error) {
+            // Ignore permission errors or other issues
+        }
+    }
+
+    searchDir(dir, 0);
+    return skills;
+}
+
+// Commands
+function runFindSkills() {
+    console.log('Available skills:');
+    console.log('==================');
+    console.log('');
+
+    const foundSkills = new Set();
+
+    // Find personal skills first (these take precedence)
+    const personalSkills = findSkillsInDir(personalSkillsDir, 'personal', 2);
+    for (const skillPath of personalSkills) {
+        const relPath = path.relative(personalSkillsDir, skillPath);
+        foundSkills.add(relPath);
+        printSkill(skillPath, 'personal');
+    }
+
+    // Find superpowers skills (only if not already found in personal)
+    const superpowersSkills = findSkillsInDir(superpowersSkillsDir, 'superpowers', 1);
+    for (const skillPath of superpowersSkills) {
+        const relPath = path.relative(superpowersSkillsDir, skillPath);
+        if (!foundSkills.has(relPath)) {
+            printSkill(skillPath, 'superpowers');
+        }
+    }
+
+    console.log('Usage:');
+    console.log('  superpowers-codex use-skill <skill-name>   # Load a specific skill');
+    console.log('');
+    console.log('Skill naming:');
+    console.log('  Superpowers skills: superpowers:skill-name (from ~/.codex/superpowers/skills/)');
+    console.log('  Personal skills: skill-name (from ~/.codex/skills/)');
+    console.log('  Personal skills override superpowers skills when names match.');
+    console.log('');
+    console.log('Note: All skills are disclosed at session start via bootstrap.');
+}
+
+function runBootstrap() {
+    console.log('# Superpowers Bootstrap for Codex');
+    console.log('# ================================');
+    console.log('');
+
+    // Show the bootstrap instructions
+    if (fs.existsSync(bootstrapFile)) {
+        console.log('## Bootstrap Instructions:');
+        console.log('');
+        try {
+            const content = fs.readFileSync(bootstrapFile, 'utf8');
+            console.log(content);
+        } catch (error) {
+            console.log(`Error reading bootstrap file: ${error.message}`);
+        }
+        console.log('');
+        console.log('---');
+        console.log('');
+    }
+
+    // Run find-skills to show available skills
+    console.log('## Available Skills:');
+    console.log('');
+    runFindSkills();
+
+    console.log('');
+    console.log('---');
+    console.log('');
+
+    // Load the using-superpowers skill automatically
+    console.log('## Auto-loading superpowers:using-superpowers skill:');
+    console.log('');
+    runUseSkill('superpowers:using-superpowers');
+
+    console.log('');
+    console.log('---');
+    console.log('');
+    console.log('# Bootstrap Complete!');
+    console.log('# You now have access to all superpowers skills.');
+    console.log('# Use "superpowers-codex use-skill <skill>" to load and apply skills.');
+    console.log('# Remember: If a skill applies to your task, you MUST use it!');
+}
+
+function runUseSkill(skillName) {
+    if (!skillName) {
+        console.log('Usage: superpowers-codex use-skill <skill-name>');
+        console.log('Examples:');
+        console.log('  superpowers-codex use-skill superpowers:brainstorming  # Load superpowers skill');
+        console.log('  superpowers-codex use-skill brainstorming              # Load personal skill (or superpowers if not found)');
+        console.log('  superpowers-codex use-skill my-custom-skill            # Load personal skill');
+        return;
+    }
+
+    // Handle namespaced skill names
+    let actualSkillPath;
+    let forceSuperpowers = false;
+
+    if (skillName.startsWith('superpowers:')) {
+        // Remove the superpowers: namespace prefix
+        actualSkillPath = skillName.substring('superpowers:'.length);
+        forceSuperpowers = true;
+    } else {
+        actualSkillPath = skillName;
+    }
+
+    // Remove "skills/" prefix if present
+    if (actualSkillPath.startsWith('skills/')) {
+        actualSkillPath = actualSkillPath.substring('skills/'.length);
+    }
+
+    // Function to find skill file
+    function findSkillFile(searchPath) {
+        // Check for exact match with SKILL.md
+        const skillMdPath = path.join(searchPath, 'SKILL.md');
+        if (fs.existsSync(skillMdPath)) {
+            return skillMdPath;
+        }
+
+        // Check for direct SKILL.md file
+        if (searchPath.endsWith('SKILL.md') && fs.existsSync(searchPath)) {
+            return searchPath;
+        }
+
+        return null;
+    }
+
+    let skillFile = null;
+
+    // If superpowers: namespace was used, only check superpowers skills
+    if (forceSuperpowers) {
+        if (fs.existsSync(superpowersSkillsDir)) {
+            const superpowersPath = path.join(superpowersSkillsDir, actualSkillPath);
+            skillFile = findSkillFile(superpowersPath);
+        }
+    } else {
+        // First check personal skills directory (takes precedence)
+        if (fs.existsSync(personalSkillsDir)) {
+            const personalPath = path.join(personalSkillsDir, actualSkillPath);
+            skillFile = findSkillFile(personalPath);
+            if (skillFile) {
+                console.log(`# Loading personal skill: ${actualSkillPath}`);
+                console.log(`# Source: ${skillFile}`);
+                console.log('');
+            }
+        }
+
+        // If not found in personal, check superpowers skills
+        if (!skillFile && fs.existsSync(superpowersSkillsDir)) {
+            const superpowersPath = path.join(superpowersSkillsDir, actualSkillPath);
+            skillFile = findSkillFile(superpowersPath);
+            if (skillFile) {
+                console.log(`# Loading superpowers skill: superpowers:${actualSkillPath}`);
+                console.log(`# Source: ${skillFile}`);
+                console.log('');
+            }
+        }
+    }
+
+    // If still not found, error
+    if (!skillFile) {
+        console.log(`Error: Skill not found: ${actualSkillPath}`);
+        console.log('');
+        console.log('Available skills:');
+        runFindSkills();
+        return;
+    }
+
+    // Extract frontmatter and content
+    let content, frontmatter;
+    try {
+        const fullContent = fs.readFileSync(skillFile, 'utf8');
+        const { name, description, whenToUse } = extractFrontmatter(skillFile);
+
+        // Extract just the 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);
+            }
+        }
+
+        content = contentLines.join('\n').trim();
+        frontmatter = { name, description, whenToUse };
+    } catch (error) {
+        console.log(`Error reading skill file: ${error.message}`);
+        return;
+    }
+
+    // Display skill header with clean info
+    const displayName = forceSuperpowers ? `superpowers:${actualSkillPath}` :
+                       (skillFile.includes(personalSkillsDir) ? actualSkillPath : `superpowers:${actualSkillPath}`);
+
+    const skillDirectory = path.dirname(skillFile);
+
+    console.log(`# ${frontmatter.name || displayName}`);
+    if (frontmatter.description) {
+        console.log(`# ${frontmatter.description}`);
+    }
+    if (frontmatter.whenToUse) {
+        console.log(`# When to use: ${frontmatter.whenToUse}`);
+    }
+    console.log(`# Supporting tools and docs are in ${skillDirectory}`);
+    console.log('# ============================================');
+    console.log('');
+
+    // Display the skill content (without frontmatter)
+    console.log(content);
+
+}
+
+// Main CLI
+const command = process.argv[2];
+const arg = process.argv[3];
+
+switch (command) {
+    case 'bootstrap':
+        runBootstrap();
+        break;
+    case 'use-skill':
+        runUseSkill(arg);
+        break;
+    case 'find-skills':
+        runFindSkills();
+        break;
+    default:
+        console.log('Superpowers for Codex');
+        console.log('Usage:');
+        console.log('  superpowers-codex bootstrap              # Run complete bootstrap with all skills');
+        console.log('  superpowers-codex use-skill <skill-name> # Load a specific skill');
+        console.log('  superpowers-codex find-skills            # List all available skills');
+        console.log('');
+        console.log('Examples:');
+        console.log('  superpowers-codex bootstrap');
+        console.log('  superpowers-codex use-skill superpowers:brainstorming');
+        console.log('  superpowers-codex use-skill my-custom-skill');
+        break;
+}