How the plugin is designed, why it's built this way, and how all the pieces connect.
Bring Ix Memory's graph-first reasoning into OpenCode as a native cognitive layer — not a thin CLI wrapper. The plugin should change how OpenCode thinks, not just give it new commands.
Skills are cognitive capabilities, not command aliases. They reason in phases, use cheap signals before expensive ones, and stop early when the question is answered.
Ix Graph = structured memory (code relationships, history, decisions)
OpenCode = reasoning engine (infers, synthesizes, decides)
Skills/Agents = cognition layer (task abstractions over the graph)
OpenCode is not a command wrapper. It uses the Ix graph as memory and synthesizes answers. The graph provides facts; OpenCode provides understanding.
ix-opencode-plugin/
opencode.json # plugin manifest — wires everything together
AGENTS.md # always-on context — injected into every session
plugins/
ix-plugin.ts # plugin entry: registers tools and hooks
tools/
ix-query.ts # graph entity lookup (locate + explain)
ix-neighbors.ts # neighborhood traversal (callers, callees, depends)
ix-impact.ts # blast radius analysis
ix-map.ts # architectural map and subsystem overview
ix-ingest.ts # ingest status and graph refresh trigger
ix-history.ts # revision, decisions, bugs (Ix Pro)
ix-docs-tool.ts # condensed context summary for injection
commands/
ix-understand.md # /ix-understand — architectural mental model
ix-investigate.md # /ix-investigate — symbol deep dive
ix-impact.md # /ix-impact — blast radius analysis
ix-plan.md # /ix-plan — risk-ordered change plan
ix-debug.md # /ix-debug — root cause analysis
ix-architecture.md # /ix-architecture — structural health audit
ix-docs.md # /ix-docs — narrative-first documentation
agents/
ix-explorer.json # general-purpose exploration
ix-system-explorer.json # full architectural model building
ix-bug-investigator.json # autonomous debugging
ix-safe-refactor-planner.json # blast radius + safe change sequencing
ix-architecture-auditor.json # structural health audit
Wires the plugin together. References:
plugin→plugins/ix-plugin.ts(tool and hook registration)agent→ agent JSON files (custom agent definitions)command→ command markdown files (slash command skill files)instructions→AGENTS.md(always-on context injection)
Injected into every OpenCode session via the instructions field. Contains:
- The three-layer cognitive model
- Behavioral rules (always/never)
- The seven-step reasoning strategy
- Token budget rules
- Quick reference tables for skills, agents, and ix CLI commands
This is the primary mechanism for changing OpenCode's default behavior. It doesn't require the user to invoke anything — it works passively.
Registers all 7 tools and 5 hooks with the OpenCode runtime. Also runs onInit to check graph availability and trigger initial ingest if the graph is empty.
Key design decisions:
- Tools are imported from
tools/and wrapped with the OpenCode tool interface - All tool
executefunctions are async and return strings (see Tool contract below) - Hooks are advisory, not blocking — they inject context but always allow the action
- The post-edit hook fires
ix map --silentas fire-and-forget (non-blocking)
Each tool calls the ix CLI using Bun's $ shell API and returns a formatted markdown string. Tools are the primitive operations; skills and agents compose them.
See TOOL_CONTRACT.md for the full API contract.
Markdown files that define phased reasoning protocols. When a user types /ix-understand, OpenCode loads this file and follows the instructions.
Each skill:
- Starts with cheap graph signals (subsystems, explain, locate)
- Escalates to more expensive operations only if needed
- Has explicit stop conditions at each phase
- Produces structured output (summary, evidence, next step)
These are ported from ix-claude-plugin/skills/ with adaptation for OpenCode's model (no Claude-specific Agent tool references).
JSON files defining OpenCode custom agents. Each agent has:
name— identifier used to invoke the agentdescription— shown in the agent pickerpermission— tool access (allow,ask,deny)prompt— full system prompt defining the agent's reasoning loop
Agents are higher-level than slash commands — they operate autonomously over multiple tool calls. Slash commands guide the current context; agents are delegated subagents for complex tasks.
Five hooks are registered in ix-plugin.ts:
| Hook | Event | Trigger | Behavior |
|---|---|---|---|
ix-pre-edit |
tool.execute.before |
write, edit | Impact check on target file; injects risk note for medium/high/critical |
ix-read |
tool.execute.before |
read | Hints to use ix-query first for broad (non-targeted) reads |
ix-intercept |
tool.execute.before |
bash | Suggests ix text when bash command looks like grep/rg |
ix-ingest |
tool.execute.after |
write, edit | Fires ix map --silent in background after source edits |
ix-errors |
tool.execute.after |
ix-* tools | Detects stale graph signals in tool output and suggests refresh |
Design principle: advisory, not blocking. All hooks return { action: "allow" }. The goal is to inject context that nudges behavior, not to block actions. Blocking would make the plugin feel hostile.
Known limitation: tool.execute.before may not reliably intercept subagent tool calls (open OpenCode issue). The hooks provide best-effort coverage for top-level agent actions.
All tools follow this contract:
- Input: structured JSON parameters (validated by OpenCode's parameter schema)
- Output: formatted markdown string — never raw JSON, never structured objects
- Fallback: if
ixis unavailable, return a helpful error message with recovery steps - Depth scaling: heavier analysis phases only run when lighter phases indicate it's needed
Example output shape:
## ix-impact: UserService
**Risk level:** HIGH
**Verdict:** NEEDS CHANGE PLAN
**Blast radius:**
- Direct dependents: 14
- Transitive (depth 2): 31
- Subsystems affected: auth, api, models
**Key callers:**
- `AuthController` [auth]
- `SessionManager` [auth]
- `UserRouter` [api]
...
This string output constraint comes from the OpenCode runtime — returning objects or non-strings from tools has caused runtime issues.
Skills are phased reasoning protocols, not CLI aliases. Each command file follows this structure:
Phase 1 — Cheap orient (subsystems, stats, locate)
↓ stop if answer is clear
Phase 2 — Explain / overview
↓ stop if sufficient
Phase 3 — Connections (callers, callees) — only if needed
↓ stop if sufficient
Phase 4 — Trace — only if execution flow unclear
↓ stop if sufficient
Phase 5 — Code read — last resort, hard limit 2 reads
Stop conditions at every phase prevent over-querying. The skill succeeds when it answers the question — not when it exhausts all available data.
Agents are autonomous reasoning loops. Each agent:
- Builds its own context from graph data before acting
- Has explicit stop conditions ("stop when you have 1–3 candidates with evidence")
- Is scoped to appropriate tools (
bash,read,grep,glob) - Follows the graph-before-code principle throughout
The five agents cover distinct use cases:
| Agent | Use case | Stop condition |
|---|---|---|
ix-explorer |
Open-ended questions | Can answer the question |
ix-system-explorer |
Full architectural model | All major systems documented |
ix-bug-investigator |
Root cause analysis | 1–3 candidates with evidence |
ix-safe-refactor-planner |
Change sequencing | Full plan with test checkpoints |
ix-architecture-auditor |
Structural health | Ranked list with metric evidence |
OpenCode V1 has no clean pre-task hidden context injection hook. The plugin uses two mechanisms instead:
-
AGENTS.mdviainstructionsfield — always-on, injected into every session. Covers behavioral rules, reasoning strategy, and reference tables. -
Hook
contextreturn field — hooks can return{ action: "allow", context: "..." }to inject context into the current tool call. Used byix-pre-edit(risk notes) andix-intercept(search hints).
When OpenCode adds a proper pre-task hook, the briefing logic can move there. For now, AGENTS.md handles the always-on case and hooks handle the event-driven case.
| Constraint | Workaround |
|---|---|
| Tool returns must be strings | Format all ix JSON output as structured markdown before returning |
| No pre-task context injection hook | Behavioral rules in AGENTS.md + advisory hooks |
| No rich UI (no tables, cards, trees) | Return structured markdown with headers, bullets, code blocks |
tool.execute.before may miss subagent tool calls |
Accept partial coverage; document the gap |
| No first-class plugin KV store | Use .opencode/ix-cache/ for any file-based state |
When OpenCode adds the following capabilities, the plugin will upgrade:
| Capability | Planned improvement |
|---|---|
| Pre-task hook | Move briefing from AGENTS.md to per-task injected context |
| Reliable subagent hook interception | Enforce ix-pre-edit and ix-intercept for all agents |
| Structured tool return types | Return typed JSON from tools instead of markdown strings |
| Localhost HTTP service | Replace CLI subprocess calls with direct Ix service HTTP calls |
| Post-task summary hook | Add ix-map and ix-report hooks |