# Plan: Transpose Claude Code Setup to OpenCode (Parallel) ## Handoff Summary **Goal**: Set up OpenCode in parallel with Claude Code, sharing state files and syncing agents/skills. **Status**: ✅ **IMPLEMENTATION COMPLETE** (2026-01-07) ### Key Decisions Made 1. **Use built-in `build` agent** as primary (not porting `personal-assistant`) 2. **All agents synced as subagents** (SKIP_AGENTS kept empty for flexibility) 3. **Model inheritance** - subagents use runtime-selected model 4. **Claude Code is source of truth** - OpenCode references state files via `instructions` 5. **No JSON minification** needed (files too small, added to future considerations as fc-047) ### What Was Completed | Step | Status | Notes | |------|--------|-------| | 1 | ✅ | Backups created (Jan 7 12:01) | | 2 | ✅ | Sync script enhanced (mode:subagent, model removal) | | 3 | ✅ | Sync run: 10 skills, 13 agents, 27 commands, 10 workflows | | 4 | ✅ | opencode.json updated (instructions, permissions) | | 5 | ✅ | Automated tests passed, manual TUI testing pending | | 6 | ✅ | README.md created (4.7KB), fc-047 added | | 7 | ⏳ | Iterate as needed | ### Critical Files **Modified:** - `~/.config/opencode/scripts/claude_sync.py` ✅ - Added mode:subagent, model removal, skip logic - `~/.config/opencode/opencode.json` ✅ - Added instructions, permissions **Created:** - `~/.config/opencode/README.md` ✅ - Documentation (4.7KB) **Referenced (not copied):** - `~/.claude/CLAUDE.md` - `~/.claude/state/kb.json` - `~/.claude/state/personal-assistant/memory/*.json` ### Architecture ``` OpenCode (after implementation) ├── Primary: build (built-in), plan (built-in) ├── Subagents: @linux-sysadmin, @k8s-orchestrator, @code-reviewer, etc. ├── Skills: auto-discovered from ~/.claude/skills/ └── State: referenced via instructions from ~/.claude/state/ ``` ### Start Command ```bash # Exit plan mode and begin implementation # Step 1: Backup BACKUP_DATE=$(date +%Y%m%d_%H%M%S) tar -czvf ~/.config/opencode-backup-$BACKUP_DATE.tar.gz -C ~/.config opencode/ tar -czvf ~/opencode-home-backup-$BACKUP_DATE.tar.gz -C ~ .opencode/ ``` --- ## Goal Create a parallel OpenCode configuration that shares/reuses as much of the existing Claude Code infrastructure as possible, focusing on: 1. **Skills/scripts execution** (highest priority) 2. **Agent hierarchy/delegation** (second priority) 3. **State persistence** (if low complexity) ## Key Discovery: Native Compatibility OpenCode **natively supports Claude-compatible skill paths**: - `~/.claude/skills//SKILL.md` - Already supported! - This means your 11 existing skills can work with minimal changes --- ## Phase 0: Backup Existing OpenCode Setup ### Current State Discovered OpenCode is already installed with substantial configuration: **`~/.config/opencode/`** (main config): - `opencode.json` - Has `claude-sync` command already! - `agent/` - 3 custom agents (coding-expert, k8s-expert, tdd-enforcer) - `agents/` - 12 synced Claude Code agents (already converted!) - `skills/` - 10 skills (some synced, one symlink to morning-report) - `scripts/claude_sync.py` - Existing sync script! **`~/.opencode/`** (alternate config): - `agent/` - 4 different agents (openagent, system-builder, etc.) - `command/` - 12 commands (commit, optimize, validate-repo, etc.) ### Backup Commands ```bash # Create timestamped backups BACKUP_DATE=$(date +%Y%m%d_%H%M%S) # Backup ~/.config/opencode/ tar -czvf ~/.config/opencode-backup-$BACKUP_DATE.tar.gz -C ~/.config opencode/ # Backup ~/.opencode/ tar -czvf ~/opencode-home-backup-$BACKUP_DATE.tar.gz -C ~ .opencode/ # Verify backups ls -la ~/.config/opencode-backup-*.tar.gz ~/opencode-home-backup-*.tar.gz ``` --- ## Phase 1: Use Existing `claude_sync.py` Script The existing sync script is **comprehensive** and handles: | Category | Source | Destination | Transforms | |----------|--------|-------------|------------| | Skills | `~/.claude/skills/*/SKILL.md` | `~/.config/opencode/skills/*/SKILL.md` | `allowed-tools` → `metadata.claude_allowed_tools` | | Agents | `~/.claude/agents/*.md` | `~/.config/opencode/agents/*.md` | `tools: X, Y` → `tools: { x: true, y: true }` | | Commands | `~/.claude/commands/*.md` | `~/.config/opencode/claude/commands/*.md` | None | | Workflows | `~/.claude/workflows/*.yaml` | `~/.config/opencode/claude/workflows/*.yaml` | None | ### Sync Commands ```bash # Dry run - see what would change python3 ~/.config/opencode/scripts/claude_sync.py --dry-run # Actually sync python3 ~/.config/opencode/scripts/claude_sync.py # Clean stale files (dry run first) python3 ~/.config/opencode/scripts/claude_sync.py --clean --dry-run python3 ~/.config/opencode/scripts/claude_sync.py --clean --apply # Sync specific category only python3 ~/.config/opencode/scripts/claude_sync.py --only agents ``` ### Model Mapping Update Needed Current script maps old models. May need to add: - `opus` → `anthropic/claude-opus-4` - `sonnet` → `anthropic/claude-sonnet-4-5` - `haiku` → `anthropic/claude-haiku-4-5` --- ## Phase 1.5: OpenCode Optimization (NEW) The current sync just copies/transforms files. It doesn't optimize for **how OpenCode works**. ### Key OpenCode Differences | Concept | Claude Code | OpenCode | Optimization Needed | |---------|-------------|----------|---------------------| | **Agent hierarchy** | PA → MO → agents | Flat: primary + subagents | Add `mode` field | | **Agent invocation** | Delegation patterns | `@agent` mentions | Simplify prompts | | **Permissions** | Hooks + guardrails | `permission` config | Move to opencode.json | | **Model selection** | Per-agent in frontmatter | `model: inherit` option | Use inheritance | | **Auto-invocation** | Keyword triggers in registry | Rich `description` field | Enhance descriptions | ### Agent Mode Assignment ```yaml # PRIMARY - Use OpenCode's built-in agents build: (built-in) # Full access, default primary plan: (built-in) # Read-only analysis # SKIP - Not needed in OpenCode's flat model personal-assistant: # Use built-in "build" instead master-orchestrator: # Intermediary not needed # SUBAGENTS (invoked via @mention or Task tool) linux-sysadmin: mode: subagent k8s-orchestrator: mode: subagent k8s-diagnostician: mode: subagent argocd-operator: mode: subagent prometheus-analyst: mode: subagent git-operator: mode: subagent programmer-orchestrator: mode: subagent code-planner: mode: subagent code-implementer: mode: subagent code-reviewer: mode: subagent ``` ### Hierarchy Simplification **Claude Code pattern** (complex, 3 layers): ``` User → Personal Assistant → Master Orchestrator → linux-sysadmin → k8s-orchestrator → k8s-diagnostician ``` **OpenCode pattern** (flat, 2 layers): ``` User → build (built-in) → @linux-sysadmin → @k8s-orchestrator → @k8s-diagnostician → @code-reviewer → etc. ``` Benefits: - No custom primary agent to maintain - Built-in `build` agent is optimized for OpenCode - Built-in `plan` agent available for read-only analysis - Subagents invoked directly via @mention ### Sync Script Enhancements Needed Update `claude_sync.py` to add: ```python # In transform_frontmatter() for agents: # 1. Skip agents not needed in OpenCode's flat model SKIP_AGENTS = {"personal-assistant", "master-orchestrator"} if name in SKIP_AGENTS: return None # Signal to skip this file # 2. All synced agents become subagents (built-in build/plan are primary) frontmatter["mode"] = "subagent" # 3. Use model inheritance (subagents use parent's model) frontmatter["model"] = "inherit" # 4. Map explicit models if not using inherit MODEL_MAP = { "opus": "anthropic/claude-opus-4", "sonnet": "anthropic/claude-sonnet-4-5", "haiku": "anthropic/claude-haiku-4-5", } if frontmatter.get("model") in MODEL_MAP: frontmatter["model"] = MODEL_MAP[frontmatter["model"]] ``` Also update `sync_tree()` to handle `None` return (skip file). ### Description Enhancement OpenCode uses descriptions for **auto-invocation**. Enhance with examples: **Current** (basic): ```yaml description: Manages Arch Linux workstation - system maintenance... ``` **Optimized** (with examples): ```yaml description: | Manages Arch Linux workstation. Use for system maintenance, updates, troubleshooting, and health checks. Examples: - "check system health" → @linux-sysadmin - "update packages" → @linux-sysadmin - "why is my disk full" → @linux-sysadmin ``` ### Permission Migration Move guardrail logic to opencode.json: ```json { "permission": { "edit": "ask", "bash": { "*": "ask", "pacman -Q*": "allow", "systemctl status*": "allow", "kubectl get*": "allow", "git status": "allow", "git diff": "allow" } } } ``` --- ## Phase 2: Create OpenCode Config Structure ### Directory Layout ``` ~/.config/opencode/ ├── opencode.json # Main config ├── AGENTS.md # Global rules (symlink or copy from CLAUDE.md) ├── agent/ # Agent definitions │ ├── personal-assistant.md │ ├── linux-sysadmin.md │ ├── k8s-orchestrator.md │ └── ... (converted agents) ├── tool/ # Custom tool wrappers (TypeScript) │ ├── gmail.ts # Wrapper for gmail scripts │ ├── gcal.ts # Wrapper for gcal scripts │ └── ... └── skill/ # OpenCode-native skills (optional) ``` ### Config File: `~/.config/opencode/opencode.json` ```jsonc { "$schema": "https://opencode.ai/config.json", "model": "anthropic/claude-sonnet-4-5", "small_model": "anthropic/claude-haiku-4-5", "autoupdate": true, // OpenCode already searches ~/.claude/skills/ - no extra config needed! // Agent definitions "agent": { // Override built-in agents or define custom via files }, // Default permissions (conservative like your current setup) "permission": { "edit": "ask", "bash": "ask" }, // Custom tools enabled "tools": { "gmail": true, "gcal": true, "gtasks": true } } ``` --- ## Phase 3: Skills Migration ### Already Compatible (No Changes Needed) OpenCode automatically discovers skills from: - `~/.claude/skills/*/SKILL.md` Your existing skills should work if they have proper frontmatter: | Skill | Status | Notes | |-------|--------|-------| | gmail | Check frontmatter | Needs `name` + `description` | | gcal | Check frontmatter | Needs `name` + `description` | | gtasks | Check frontmatter | Needs `name` + `description` | | sysadmin-health | Check frontmatter | | | k8s-quick-status | Check frontmatter | | | morning-report | Check frontmatter | | | stock-lookup | Check frontmatter | | | rag-search | Check frontmatter | | | usage | Check frontmatter | | | guardrails | N/A | Becomes permission config | ### Frontmatter Requirements Each SKILL.md needs: ```yaml --- name: skill-name # Required, must match directory name description: Brief desc # Required, 1-1024 chars --- ``` ### Audit Results (Already Compatible!) Checked skills have proper frontmatter: - `gmail/SKILL.md` - Has `name: gmail`, `description: ...` - `sysadmin-health/SKILL.md` - Has `name: sysadmin-health`, `description: ...` - `morning-report/SKILL.md` - Has `name: morning-report`, `description: ...` The `allowed-tools` field in some skills will be ignored by OpenCode (not in their schema), but this is fine. ### Action Items 1. ~~Audit each SKILL.md for required frontmatter~~ **Done - already compatible!** 2. ~~Add missing `name`/`description` fields~~ **Not needed** 3. Test skill discovery in OpenCode after install --- ## Phase 4: Agent Migration ### Mapping Strategy | Claude Code | OpenCode | Notes | |------------|----------|-------| | `model: opus` | `model: anthropic/claude-opus-4` | Full provider/model path | | `model: sonnet` | `model: anthropic/claude-sonnet-4-5` | | | `model: haiku` | `model: anthropic/claude-haiku-4-5` | | | `tools: Read, Write...` | `tools: { write: true, ... }` | Boolean map | | Hierarchy (PA → MO → agent) | `mode: primary` + `mode: subagent` | Flattened | ### Agent Conversion Template **From (Claude Code):** ```yaml --- name: linux-sysadmin description: Manages Arch Linux workstation... model: sonnet tools: Bash, Read, Write, Edit, Grep, Glob --- ``` **To (OpenCode):** ```yaml --- name: linux-sysadmin description: Manages Arch Linux workstation... mode: subagent model: anthropic/claude-sonnet-4-5 tools: bash: true read: true write: true edit: true permission: bash: "*": ask "pacman -Q*": allow "systemctl status*": allow --- ``` ### Priority Agents to Convert 1. **personal-assistant.md** → `mode: primary` (main interface) 2. **linux-sysadmin.md** → `mode: subagent` 3. **k8s-orchestrator.md** → `mode: subagent` 4. **master-orchestrator.md** → May not be needed (OpenCode doesn't have same hierarchy) ### Hierarchy Adaptation OpenCode doesn't have hierarchical agent delegation like your current setup. Options: - **Option A**: Flatten to primary + subagents, use `@agent` mentions - **Option B**: Use OpenCode's Task tool for agent invocation - **Option C**: Create a "dispatcher" primary agent that routes via @mentions **Recommendation**: Option A (simplest) - personal-assistant as primary, others as subagents invokable via `@linux-sysadmin`, `@k8s-orchestrator`, etc. --- ## Phase 5: Custom Tools (Scripts Execution) ### Wrapper Pattern Create TypeScript wrappers that invoke your existing Python scripts: **Example: `~/.config/opencode/tool/gmail.ts`** ```typescript import { tool } from "@opencode-ai/plugin" export const check_unread = tool({ description: "Check unread emails from Gmail", args: { limit: tool.schema.number().optional().describe("Max emails to return"), }, async execute(args) { const limit = args.limit ?? 10 const result = await Bun.$`~/.claude/mcp/gmail/venv/bin/python ~/.claude/skills/gmail/scripts/check_unread.py --limit ${limit}`.text() return result.trim() }, }) export const search = tool({ description: "Search Gmail for specific emails", args: { query: tool.schema.string().describe("Search query"), }, async execute(args) { const result = await Bun.$`~/.claude/mcp/gmail/venv/bin/python ~/.claude/skills/gmail/scripts/search.py "${args.query}"`.text() return result.trim() }, }) ``` ### Tools to Create Wrappers For | Script | Wrapper | |--------|---------| | `gmail/scripts/*.py` | `gmail.ts` | | `gcal/scripts/*.py` | `gcal.ts` | | `gtasks/scripts/*.py` | `gtasks.ts` | | `sysadmin-health/scripts/*.sh` | `sysadmin.ts` | | `morning-report/scripts/*.py` | `morning.ts` | | `stock-lookup/scripts/*.py` | `stocks.ts` | --- ## Phase 6: Rules/Instructions ### Option A: Symlink CLAUDE.md ```bash ln -s ~/.claude/CLAUDE.md ~/.config/opencode/AGENTS.md ``` ### Option B: Create Minimal AGENTS.md + Reference ```markdown # OpenCode Agent Rules Read @~/.claude/CLAUDE.md for shared conventions. ## OpenCode-Specific - Use `@agent-name` to invoke subagents - Skills are loaded via the `skill` tool - Custom tools available: gmail, gcal, gtasks, sysadmin ``` ### Option C: Use instructions config ```json { "instructions": ["~/.claude/CLAUDE.md", "~/.claude/state/system-instructions.json"] } ``` **Recommendation**: Option C - cleanest, no duplication --- ## Phase 7: State Persistence (Claude Code as Source of Truth) ### Strategy Claude Code owns the state files. OpenCode reads them via: 1. `{file:path}` variable substitution in `opencode.json` 2. `instructions` array for context files 3. Skills that read state files directly ### What Can Be Shared | File | Method | Notes | |------|--------|-------| | `~/.claude/CLAUDE.md` | `instructions` | Global rules | | `~/.claude/state/kb.json` | `instructions` or skill | Knowledge base | | `~/.claude/state/personal-assistant/memory/*.json` | `instructions` | Memory context | | `~/.claude/state/system-instructions.json` | `instructions` | Process definitions | ### Implementation in `opencode.json` ```jsonc { "$schema": "https://opencode.ai/config.json", // Load Claude Code state as instructions (read at session start) "instructions": [ "~/.claude/CLAUDE.md", "~/.claude/state/kb.json", "~/.claude/state/personal-assistant/memory/facts.json", "~/.claude/state/personal-assistant/memory/preferences.json" ] } ``` ### What Stays Separate | Item | Reason | |------|--------| | Session history | Different formats, different storage | | Autonomy/permissions | OpenCode uses `permission` config instead | | Component registry | OpenCode discovers via file paths | ### Overhead Assessment **Low overhead** - just config changes: - Add paths to `instructions` array - No symlinks or sync scripts needed - OpenCode reads files directly at session start - Claude Code continues to write/update normally --- ## Phase 8: What Won't Transfer | Feature | Claude Code | OpenCode Alternative | |---------|-------------|---------------------| | Hooks (SessionStart, etc.) | `hooks/hooks.json` | Plugins (future) | | Guardrails hook | PreToolUse script | `permission` config | | Component registry routing | Keyword triggers | Agent descriptions + @mentions | | Hierarchical delegation | PA → MO → agent | Flat subagent model | --- ## Implementation Order ### Step 1: Backup (5 min) ✅ COMPLETE - [x] Create timestamped backup of `~/.config/opencode/` → `opencode-backup-20260107_120135.tar.gz` - [x] Create timestamped backup of `~/.opencode/` → `opencode-home-backup-20260107_120136.tar.gz` ### Step 2: Enhance Sync Script (45 min) ✅ COMPLETE - [x] Add skip list: `SKIP_AGENTS` (kept empty - all agents synced as subagents) - [x] Add `mode: subagent` to all synced agents - [x] Remove hardcoded model (agents inherit from runtime selection) - [x] Add model stripping from opencode.json - [x] Update `sync_tree()` to handle skipped files - [ ] ~~Optionally enhance descriptions with examples~~ (deferred) ### Step 3: Run Enhanced Sync (10 min) ✅ COMPLETE - [x] `python3 ~/.config/opencode/scripts/claude_sync.py --dry-run` - [x] Review output - verify mode/model changes - [x] `python3 ~/.config/opencode/scripts/claude_sync.py` - [x] All synced: 10 skills, 13 agents, 27 commands, 10 workflows ### Step 4: Update opencode.json (20 min) ✅ COMPLETE - [x] Add `instructions` array (CLAUDE.md, kb.json, memory files) - [x] Model defaults: intentionally omitted (user selects at runtime) - [x] Add permission config with safe command patterns ### Step 5: Testing (30 min) ✅ COMPLETE (automated) - [x] OpenCode v1.0.220 installed at `/home/linuxbrew/.linuxbrew/bin/opencode` - [x] `opencode agent list` shows 40 agents (built-in + synced) - [x] All Claude Code agents show as `(subagent)` - [x] 10 skills synced to `~/.config/opencode/skills/` - [x] Config verified: instructions, permissions, commands present - [ ] Manual TUI testing (user to verify interactively) ### Step 6: Documentation (20 min) ✅ COMPLETE - [x] Create `~/.config/opencode/README.md` (4.7KB) - [x] Document complete agent mapping table - [x] Document sync workflow with examples - [x] Add fc-047 to `~/.claude/state/future-considerations.json` ### Step 7: Iterate (as needed) ⏳ PENDING - [ ] Adjust agent descriptions if auto-invocation isn't working well - [ ] Tune permission patterns - [ ] Consider dropping/hiding agents that don't fit OpenCode model - [ ] Update documentation with lessons learned **Status: IMPLEMENTATION COMPLETE** - Manual TUI testing recommended --- ## Phase 8: Documentation ### Documentation Deliverables Create `~/.config/opencode/README.md` with: 1. **Architecture Overview** - Relationship between Claude Code and OpenCode - What's shared vs separate - Source of truth (Claude Code) 2. **Sync Workflow** - How `claude_sync.py` works - When to run it (after Claude Code changes) - Command reference 3. **Agent Mapping** - Which Claude Code agents map to OpenCode - Which are skipped and why - How to invoke subagents (@mentions) 4. **Skills** - Auto-discovery from `~/.claude/skills/` - How to add new skills - Skill invocation patterns 5. **State Sharing** - Files referenced via `instructions` - Claude Code as source of truth - What stays separate 6. **Permissions** - How guardrails translated to `permission` config - Safe vs prompted commands ### Documentation Template ```markdown # OpenCode Configuration This OpenCode setup is synchronized from Claude Code (`~/.claude/`). ## Quick Start ```bash # Start OpenCode (uses built-in build agent) opencode # Switch to read-only plan agent # Press Tab # Invoke a subagent @linux-sysadmin check system health ``` ## Architecture ``` Claude Code (source of truth) ├── ~/.claude/agents/ → synced to ~/.config/opencode/agents/ ├── ~/.claude/skills/ → synced to ~/.config/opencode/skills/ ├── ~/.claude/CLAUDE.md → referenced via instructions └── ~/.claude/state/ → referenced via instructions OpenCode ├── Built-in: build (primary), plan (read-only) ├── Subagents: @linux-sysadmin, @k8s-orchestrator, etc. └── Skills: gmail, gcal, sysadmin-health, etc. ``` ## Sync Workflow After making changes in Claude Code: ```bash # Preview changes python3 ~/.config/opencode/scripts/claude_sync.py --dry-run # Apply changes python3 ~/.config/opencode/scripts/claude_sync.py # Clean stale files python3 ~/.config/opencode/scripts/claude_sync.py --clean --apply ``` ## Agents | Claude Code | OpenCode | Notes | |-------------|----------|-------| | personal-assistant | (skipped) | Use built-in `build` | | master-orchestrator | (skipped) | Flat model, not needed | | linux-sysadmin | @linux-sysadmin | Subagent | | k8s-orchestrator | @k8s-orchestrator | Subagent | | ... | ... | ... | ## Skills Skills are auto-discovered from: - `~/.claude/skills/*/SKILL.md` - `~/.config/opencode/skills/*/SKILL.md` ## State Files Referenced via `instructions` in opencode.json: - `~/.claude/CLAUDE.md` - Global rules - `~/.claude/state/kb.json` - Knowledge base - `~/.claude/state/personal-assistant/memory/*.json` - Memory ## Permissions Configured in opencode.json `permission` section. Migrated from Claude Code's guardrail hooks. ``` ### Implementation Step Add to Step 6: - [ ] Create `~/.config/opencode/README.md` - [ ] Document sync workflow - [ ] Document agent mapping - [ ] Document any gotchas discovered during testing --- ## Files to Create/Modify ### Files to Create - `~/.config/opencode/README.md` - Documentation of setup, workflow, and requirements ### Files to Modify - `~/.config/opencode/opencode.json` - Add `instructions` array + model/permission config - `~/.config/opencode/scripts/claude_sync.py` - Add mode, model mappings, skip list ### Files Auto-Synced by Script These are created/updated by `claude_sync.py`: - `~/.config/opencode/agents/*.md` - From `~/.claude/agents/` - `~/.config/opencode/skills/*/SKILL.md` - From `~/.claude/skills/` - `~/.config/opencode/claude/commands/*.md` - From `~/.claude/commands/` - `~/.config/opencode/claude/workflows/*.yaml` - From `~/.claude/workflows/` ### Files Referenced (Not Copied) These stay in Claude Code, referenced via `instructions`: - `~/.claude/CLAUDE.md` - `~/.claude/state/kb.json` - `~/.claude/state/personal-assistant/memory/*.json` --- ## Success Criteria 1. `opencode` launches and shows available skills 2. Can invoke `@linux-sysadmin` and get expected behavior 3. Gmail/GCal/GTasks tools work via custom wrappers 4. Can switch between build/plan agents + custom agents 5. Both Claude Code and OpenCode can run in parallel without conflicts