295 lines
8.6 KiB
Markdown
295 lines
8.6 KiB
Markdown
# OpenCode Support Design
|
|
|
|
**Date:** 2025-11-22
|
|
**Author:** Bot & Jesse
|
|
**Status:** Design Complete, Awaiting Implementation
|
|
|
|
## Overview
|
|
|
|
Add full superpowers support for OpenCode.ai using a native OpenCode plugin architecture that shares core functionality with the existing Codex implementation.
|
|
|
|
## Background
|
|
|
|
OpenCode.ai is a coding agent similar to Claude Code and Codex. Previous attempts to port superpowers to OpenCode (PR #93, PR #116) used file-copying approaches. This design takes a different approach: building a native OpenCode plugin using their JavaScript/TypeScript plugin system while sharing code with the Codex implementation.
|
|
|
|
### Key Differences Between Platforms
|
|
|
|
- **Claude Code**: Native Anthropic plugin system + file-based skills
|
|
- **Codex**: No plugin system → bootstrap markdown + CLI script
|
|
- **OpenCode**: JavaScript/TypeScript plugins with event hooks and custom tools API
|
|
|
|
### OpenCode's Agent System
|
|
|
|
- **Primary agents**: Build (default, full access) and Plan (restricted, read-only)
|
|
- **Subagents**: General (research, searching, multi-step tasks)
|
|
- **Invocation**: Automatic dispatch by primary agents OR manual `@mention` syntax
|
|
- **Configuration**: Custom agents in `opencode.json` or `~/.config/opencode/agent/`
|
|
|
|
## Architecture
|
|
|
|
### High-Level Structure
|
|
|
|
1. **Shared Core Module** (`lib/skills-core.js`)
|
|
- Common skill discovery and parsing logic
|
|
- Used by both Codex and OpenCode implementations
|
|
|
|
2. **Platform-Specific Wrappers**
|
|
- Codex: CLI script (`.codex/superpowers-codex`)
|
|
- OpenCode: Plugin module (`.opencode/plugin/superpowers.js`)
|
|
|
|
3. **Skill Directories**
|
|
- Core: `~/.config/opencode/superpowers/skills/` (or installed location)
|
|
- Personal: `~/.config/opencode/skills/` (shadows core skills)
|
|
|
|
### Code Reuse Strategy
|
|
|
|
Extract common functionality from `.codex/superpowers-codex` into shared module:
|
|
|
|
```javascript
|
|
// lib/skills-core.js
|
|
module.exports = {
|
|
extractFrontmatter(filePath), // Parse name + description from YAML
|
|
findSkillsInDir(dir, maxDepth), // Recursive SKILL.md discovery
|
|
findAllSkills(dirs), // Scan multiple directories
|
|
resolveSkillPath(skillName, dirs), // Handle shadowing (personal > core)
|
|
checkForUpdates(repoDir) // Git fetch/status check
|
|
};
|
|
```
|
|
|
|
### Skill Frontmatter Format
|
|
|
|
Current format (no `when_to_use` field):
|
|
|
|
```yaml
|
|
---
|
|
name: skill-name
|
|
description: Use when [condition] - [what it does]; [additional context]
|
|
---
|
|
```
|
|
|
|
## OpenCode Plugin Implementation
|
|
|
|
### Custom Tools
|
|
|
|
**Tool 1: `use_skill`**
|
|
|
|
Loads a specific skill's content into the conversation (equivalent to Claude's Skill tool).
|
|
|
|
```javascript
|
|
{
|
|
name: 'use_skill',
|
|
description: 'Load and read a specific skill to guide your work',
|
|
schema: z.object({
|
|
skill_name: z.string().describe('Name of skill (e.g., "superpowers:brainstorming")')
|
|
}),
|
|
execute: async ({ skill_name }) => {
|
|
const { skillPath, content, frontmatter } = resolveAndReadSkill(skill_name);
|
|
const skillDir = path.dirname(skillPath);
|
|
|
|
return `# ${frontmatter.name}
|
|
# ${frontmatter.description}
|
|
# Supporting tools and docs are in ${skillDir}
|
|
# ============================================
|
|
|
|
${content}`;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Tool 2: `find_skills`**
|
|
|
|
Lists all available skills with metadata.
|
|
|
|
```javascript
|
|
{
|
|
name: 'find_skills',
|
|
description: 'List all available skills',
|
|
schema: z.object({}),
|
|
execute: async () => {
|
|
const skills = discoverAllSkills();
|
|
return skills.map(s =>
|
|
`${s.namespace}:${s.name}
|
|
${s.description}
|
|
Directory: ${s.directory}
|
|
`).join('\n');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Session Startup Hook
|
|
|
|
When a new session starts (`session.started` event):
|
|
|
|
1. **Inject using-superpowers content**
|
|
- Full content of the using-superpowers skill
|
|
- Establishes mandatory workflows
|
|
|
|
2. **Run find_skills automatically**
|
|
- Display full list of available skills upfront
|
|
- Include skill directories for each
|
|
|
|
3. **Inject tool mapping instructions**
|
|
```markdown
|
|
**Tool Mapping for OpenCode:**
|
|
When skills reference tools you don't have, substitute:
|
|
- `TodoWrite` → `update_plan`
|
|
- `Task` with subagents → Use OpenCode subagent system (@mention)
|
|
- `Skill` tool → `use_skill` custom tool
|
|
- Read, Write, Edit, Bash → Your native equivalents
|
|
|
|
**Skill directories contain:**
|
|
- Supporting scripts (run with bash)
|
|
- Additional documentation (read with read tool)
|
|
- Utilities specific to that skill
|
|
```
|
|
|
|
4. **Check for updates** (non-blocking)
|
|
- Quick git fetch with timeout
|
|
- Notify if updates available
|
|
|
|
### Plugin Structure
|
|
|
|
```javascript
|
|
// .opencode/plugin/superpowers.js
|
|
const skillsCore = require('../../lib/skills-core');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const { z } = require('zod');
|
|
|
|
export const SuperpowersPlugin = async ({ client, directory, $ }) => {
|
|
const superpowersDir = path.join(process.env.HOME, '.config/opencode/superpowers');
|
|
const personalDir = path.join(process.env.HOME, '.config/opencode/skills');
|
|
|
|
return {
|
|
'session.started': async () => {
|
|
const usingSuperpowers = await readSkill('using-superpowers');
|
|
const skillsList = await findAllSkills();
|
|
const toolMapping = getToolMappingInstructions();
|
|
|
|
return {
|
|
context: `${usingSuperpowers}\n\n${skillsList}\n\n${toolMapping}`
|
|
};
|
|
},
|
|
|
|
tools: [
|
|
{
|
|
name: 'use_skill',
|
|
description: 'Load and read a specific skill',
|
|
schema: z.object({
|
|
skill_name: z.string()
|
|
}),
|
|
execute: async ({ skill_name }) => {
|
|
// Implementation using skillsCore
|
|
}
|
|
},
|
|
{
|
|
name: 'find_skills',
|
|
description: 'List all available skills',
|
|
schema: z.object({}),
|
|
execute: async () => {
|
|
// Implementation using skillsCore
|
|
}
|
|
}
|
|
]
|
|
};
|
|
};
|
|
```
|
|
|
|
## File Structure
|
|
|
|
```
|
|
superpowers/
|
|
├── lib/
|
|
│ └── skills-core.js # NEW: Shared skill logic
|
|
├── .codex/
|
|
│ ├── superpowers-codex # UPDATED: Use skills-core
|
|
│ ├── superpowers-bootstrap.md
|
|
│ └── INSTALL.md
|
|
├── .opencode/
|
|
│ ├── plugin/
|
|
│ │ └── superpowers.js # NEW: OpenCode plugin
|
|
│ └── INSTALL.md # NEW: Installation guide
|
|
└── skills/ # Unchanged
|
|
```
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Refactor Shared Core
|
|
|
|
1. Create `lib/skills-core.js`
|
|
- Extract frontmatter parsing from `.codex/superpowers-codex`
|
|
- Extract skill discovery logic
|
|
- Extract path resolution (with shadowing)
|
|
- Update to use only `name` and `description` (no `when_to_use`)
|
|
|
|
2. Update `.codex/superpowers-codex` to use shared core
|
|
- Import from `../lib/skills-core.js`
|
|
- Remove duplicated code
|
|
- Keep CLI wrapper logic
|
|
|
|
3. Test Codex implementation still works
|
|
- Verify bootstrap command
|
|
- Verify use-skill command
|
|
- Verify find-skills command
|
|
|
|
### Phase 2: Build OpenCode Plugin
|
|
|
|
1. Create `.opencode/plugin/superpowers.js`
|
|
- Import shared core from `../../lib/skills-core.js`
|
|
- Implement plugin function
|
|
- Define custom tools (use_skill, find_skills)
|
|
- Implement session.started hook
|
|
|
|
2. Create `.opencode/INSTALL.md`
|
|
- Installation instructions
|
|
- Directory setup
|
|
- Configuration guidance
|
|
|
|
3. Test OpenCode implementation
|
|
- Verify session startup bootstrap
|
|
- Verify use_skill tool works
|
|
- Verify find_skills tool works
|
|
- Verify skill directories are accessible
|
|
|
|
### Phase 3: Documentation & Polish
|
|
|
|
1. Update README with OpenCode support
|
|
2. Add OpenCode installation to main docs
|
|
3. Update RELEASE-NOTES
|
|
4. Test both Codex and OpenCode work correctly
|
|
|
|
## Next Steps
|
|
|
|
1. **Create isolated workspace** (using git worktrees)
|
|
- Branch: `feature/opencode-support`
|
|
|
|
2. **Follow TDD where applicable**
|
|
- Test shared core functions
|
|
- Test skill discovery and parsing
|
|
- Integration tests for both platforms
|
|
|
|
3. **Incremental implementation**
|
|
- Phase 1: Refactor shared core + update Codex
|
|
- Verify Codex still works before moving on
|
|
- Phase 2: Build OpenCode plugin
|
|
- Phase 3: Documentation and polish
|
|
|
|
4. **Testing strategy**
|
|
- Manual testing with real OpenCode installation
|
|
- Verify skill loading, directories, scripts work
|
|
- Test both Codex and OpenCode side-by-side
|
|
- Verify tool mappings work correctly
|
|
|
|
5. **PR and merge**
|
|
- Create PR with complete implementation
|
|
- Test in clean environment
|
|
- Merge to main
|
|
|
|
## Benefits
|
|
|
|
- **Code reuse**: Single source of truth for skill discovery/parsing
|
|
- **Maintainability**: Bug fixes apply to both platforms
|
|
- **Extensibility**: Easy to add future platforms (Cursor, Windsurf, etc.)
|
|
- **Native integration**: Uses OpenCode's plugin system properly
|
|
- **Consistency**: Same skill experience across all platforms
|