Forge (loops, plans, sandbox, graph) + the ForgeCode harness (summary-frame compaction, doom-loop, pending-todos, truncation, undo snapshots) for OpenCode.
opencode-forgecode is a single plugin that combines two toolchains:
- opencode-forge — iterative development loops, session-scoped plan storage, review-finding persistence, code-structure graph indexing, Docker sandbox, and a TUI sidebar.
- ForgeCode harness — a port of forgecode's runtime: summary-frame compaction, output truncation (shell / search / fetch), doom-loop detection, pending-todo reminders, undo snapshots on mutating tools, and the unified
forge/muse/sageagent trinity.
Both sides are wired through the same plugin entrypoint, so you do not need to stack two plugins that would fight over experimental.session.compacting, tool.execute.*, or event.
npm add opencode-forgecodeAdd to your opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-forgecode@latest"]
}Add to your ~/.config/opencode/tui.json to enable the sidebar:
{
"$schema": "https://opencode.ai/tui.json",
"plugin": ["opencode-forgecode@latest"]
}On first run the plugin auto-creates ~/.config/opencode/forge-config.jsonc from the bundled template.
Runtime notes:
- Server/backend plugin — packaged
dist/index.jsis built for Node-compatible loading. - TUI plugin — still depends on the OpenTUI/Bun runtime stack (
@opentui/core/bun:ffi), so treat the sidebar as Bun/OpenTUI-bound for now. - If the backend still fails before the host can reach its local plugin URL, enable logging in
~/.config/opencode/forge-config.jsonc:
- Iterative Development Loops — Autonomous coding/auditing loop with optional worktree isolation, session rotation, stall detection, and review-finding persistence.
- Session Plan Storage — Session-scoped plan storage with 7-day TTL; plans viewable and editable from the TUI sidebar.
- Review Finding Persistence — Store and retrieve audit findings across session rotations.
- Graph Indexing — Code-structure graph with file watching, auto-scanning, and symbol tracking.
- Docker Sandbox — Run loops inside isolated Docker containers with a bind-mounted project directory.
- Bundled TS agent trinity —
forge,muse,sagepreconfigured for graph-aware workflows (TypeScriptsrc/agents/*.ts). Registered automatically when the plugin loads — no separate install step. - CLI —
oc-forgecode loop …,oc-forgecode graph …,oc-forgecode upgrade.
- Summary-frame compaction — Overrides
experimental.session.compactingwith the portedforge-partial-summary-frame.md. Falls back to forge's custom compaction prompt when no cached messages are available. - Output truncation —
tool.execute.aftertrimsbash/shelloutput (head+tail with long-line clipping), caps lines forgrep/glob/search tools, and caps characters forwebfetch. - Doom-loop detection — Per-session tool-signature tracker. On threshold-length repeating patterns (identical or cyclic, defaults to 3), appends a reminder via
tui.appendPromptasking the agent to change strategy. - Pending-todos reminder — Tracks
todo.updatedevents. When a session goes idle withpendingorin_progresstodos, appends a reminder with the outstanding items. - Undo snapshots — Before every
write/edit/multi_patchcall, the prior file contents are snapshotted under<dataDir>/snapshots/<session>/<ts>-<tag>.bak. Restore with the bundledfs_undotool.
The plugin ships a unified TS-backed agent trinity. All three are registered automatically when the plugin loads — no setup step required, no markdown agents installed into ~/.config/opencode/agents/.
| Agent | Mode | Description |
|---|---|---|
| forge | primary | Primary coding agent with graph-first code discovery and harness tone. Read/edit/bash access. review-delete, plan-execute, plan-write, plan-edit, and loop are excluded. |
| muse | primary | Strategic planning agent. Builds plans incrementally in the KV store, caches them for user approval, and dispatches execution programmatically. All edits denied. Enforces two-step approval (pre-plan checkpoint + execution checkpoint with four canonical options). |
| sage | subagent | Dual-mode research + code review agent. Mode is selected from the request: review mode on diffs/commits/PRs/loop iterations, research mode on architectural or cross-file investigation. Read-only. Temperature 0.0. plan-execute, plan-write, plan-edit, and loop are excluded. |
The muse agent runs with all edits denied via message-level enforcement in the experimental.chat.messages.transform hook.
Example agent model overrides in opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-forgecode@latest"],
"compaction": {
"auto": true,
"prune": true
},
"agent": {
"forge": { "model": "anthropic/claude-opus-4.7" },
"muse": {
"model": "openai/gpt-5.4",
"reasoningEffort": "high",
"textVerbosity": "medium",
"reasoningSummary": "auto"
},
"sage": {
"model": "openai/gpt-5.4-mini",
"reasoningEffort": "high",
"textVerbosity": "low",
"reasoningSummary": "auto"
}
}
}Suggested model split:
- Strong model for
forge/muse. - Cheaper / faster model for
sage. - Keep OpenCode
compaction.auto=trueandcompaction.prune=true; forgecode's summary-frame compaction builds on top of the native safety net rather than replacing it.
For long tool-heavy sessions, @tarquinen/opencode-dcp can complement forgecode well if forgecode remains the owner of final session compaction and DCP is used only for proactive pruning/compression.
Recommended opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-forgecode@latest", "@tarquinen/opencode-dcp@latest"],
"compaction": {
"auto": true,
"prune": true
},
"agent": {
"forge": { "model": "anthropic/claude-opus-4.7" },
"muse": {
"model": "openai/gpt-5.4",
"reasoningEffort": "high",
"textVerbosity": "medium",
"reasoningSummary": "auto"
},
"sage": {
"model": "openai/gpt-5.4-mini",
"reasoningEffort": "high",
"textVerbosity": "low",
"reasoningSummary": "auto"
}
}
}Recommended ~/.config/opencode/dcp.jsonc:
{
"$schema": "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.schema.json",
"enabled": true,
"experimental": {
"allowSubAgents": false,
},
"compress": {
"permission": "allow",
"protectedTools": [
"graph-query",
"graph-symbols",
"graph-analyze",
"plan-write",
"plan-read",
"plan-edit",
"review-write",
"review-read",
"review-delete",
"apply_patch",
"multi_patch",
"sem_search",
],
},
"strategies": {
"deduplication": {
"enabled": true,
"protectedTools": [
"graph-query",
"graph-symbols",
"graph-analyze",
"plan-write",
"plan-read",
"plan-edit",
"review-write",
"review-read",
"review-delete",
"apply_patch",
"multi_patch",
"sem_search",
],
},
"purgeErrors": {
"enabled": true,
"protectedTools": [
"graph-query",
"graph-symbols",
"graph-analyze",
"plan-write",
"plan-read",
"plan-edit",
"review-write",
"review-read",
"review-delete",
"apply_patch",
"multi_patch",
"sem_search",
],
},
},
}Use DCP when:
- you keep long-lived sessions open,
- graph/search/bash outputs accumulate heavily,
- you want proactive cleanup before OpenCode hits hard compaction.
Skip DCP when:
- you usually start fresh sessions,
- your model has a very large context window and token cost is not a concern,
- you want the simplest possible setup.
| Tool | Description |
|---|---|
plan-write |
Store the entire plan content. Auto-resolves key to plan:{sessionID}. |
plan-edit |
Edit the plan by finding old_string and replacing with new_string. |
plan-read |
Retrieve the plan. Supports pagination with offset/limit and pattern search. |
plan-execute |
Create a new forge session and send an approved plan as the first prompt. |
| Tool | Description |
|---|---|
review-write |
Store a review finding with file, line, severity, and description. Auto-injects branch field. |
review-read |
Retrieve review findings. Filter by file path or search by regex pattern. |
review-delete |
Delete a review finding by file and line. |
| Tool | Description |
|---|---|
loop |
Execute a plan using an iterative development loop. Default runs in current directory. Set worktree: true for isolated git worktree. |
loop-cancel |
Cancel an active loop by worktree name. |
loop-status |
List all active loops or get detailed status by worktree name. Supports restart to resume inactive loops. |
| Tool | Description |
|---|---|
graph-status |
Check graph indexing status or trigger re-scan. Actions: status, scan. |
graph-query |
File-level queries: top_files, file_deps, file_dependents, cochanges, blast_radius, packages, file_symbols. |
graph-symbols |
Symbol-level queries: find, search, signature, callers, callees. |
graph-analyze |
Code-quality analysis: unused_exports, duplication, near_duplicates. |
| Tool | Description |
|---|---|
multi_patch |
Apply multiple text replacements to a single file atomically (tempfile + rename). |
fs_undo |
Restore a file from the snapshot history written by the harness on mutating tool calls. |
| Command | Description | Agent |
|---|---|---|
/review |
Run a code review on current changes | sage (subtask) |
/loop |
Start an iterative development loop in a worktree | forge |
/loop-status |
Check status of all active loops | forge |
/loop-cancel |
Cancel the active loop | forge |
Manage loops and graph using the oc-forgecode CLI. The CLI auto-detects the project ID from git.
oc-forgecode <command> [options]Check for plugin updates and install the latest version.
oc-forgecode upgradeoc-forgecode loop status
oc-forgecode loop cancel <worktree>
oc-forgecode loop restart <worktree> [--force] [--server http://localhost:5551]oc-forgecode graph status
oc-forgecode graph scan
oc-forgecode graph list
oc-forgecode graph remove <target>
oc-forgecode graph cleanup --days <n> [--yes]Global flags: --project, -p <name>, --dir, -d <path>, --db-path <path>, --help, -h.
On first run, the plugin copies the bundled config to ~/.config/opencode/forge-config.jsonc. The file is JSONC (supports // and /* */ comments).
{
"dataDir": "",
"logging": { "enabled": false, "debug": false, "file": "" },
"compaction": { "customPrompt": true, "maxContextTokens": 0 },
"messagesTransform": { "enabled": true, "debug": false },
"executionModel": "",
"auditorModel": "",
"loop": {
"enabled": true,
"defaultMaxIterations": 15,
"cleanupWorktree": false,
"defaultAudit": true,
"model": "",
"minAudits": 1,
"stallTimeoutMs": 60000,
},
"sandbox": { "mode": "off", "image": "oc-forge-sandbox:latest" },
"graph": { "enabled": true, "autoScan": true, "watch": true, "debounceMs": 100 },
"tui": { "sidebar": true, "showLoops": true, "showVersion": true },
"defaultKvTtlMs": 604800000,
// Harness block — ForgeCode runtime behavior
"harness": {
"enabled": true, // Master switch. false disables all harness hooks.
"doomLoopThreshold": 3, // Consecutive tool repetitions before firing a reminder
"pendingTodosReminder": true, // Remind about pending todos on session.idle
"snapshots": true, // Capture .bak snapshots before write/edit/multi_patch
"compaction": true, // Use summary-frame compaction (overrides output.prompt)
"truncation": { "enabled": true }, // Trim long shell/search/fetch outputs
},
}harness.enabled— Master switch. Set tofalseto disable every harness hook (doom-loop, pending-todos, snapshots, compaction override, truncation). Defaulttrue.harness.doomLoopThreshold— Number of consecutive identical/cyclic tool invocations before the doom-loop reminder fires. Default3.harness.pendingTodosReminder— Whentrue, a reminder with outstanding todos is appended to the TUI prompt onsession.idleorsession.completed. Defaulttrue.harness.snapshots— Capture<dataDir>/snapshots/<session>/<ts>-<tag>.bakbefore everywrite/edit/multi_patch. Used byfs_undo. Defaulttrue.harness.compaction— Overrideexperimental.session.compacting.output.promptwith the ported summary-frame. When harness does not set a prompt (e.g. session has no cached messages), the plugin falls back to forge'sbuildCustomCompactionPrompt. Defaulttrue.harness.truncation.enabled— Trim long tool outputs intool.execute.after. Caps: shell 200 lines head + 200 tail + 2000 chars/line; search 500 lines; fetch 40 000 chars. Defaulttrue.
dataDir— Data dir for plugin storage (graph.db, KV store, logs, harness snapshots). Empty →~/.local/share/opencode/forge.defaultKvTtlMs— Default TTL for KV entries (default604800000/ 7 days).executionModel—provider/modeloverride for plan-execute sessions.auditorModel—provider/modeloverride for the sage agent (review-mode invocations). Kept asauditorModelfor backwards compatibility.agents— Per-agent temperature overrides keyed by display name.
logging.enabled,logging.debug,logging.file.
compaction.customPrompt— Use forge's custom compaction prompt. Used as a fallback whenharness.compaction=truehas no cached messages.compaction.maxContextTokens— Token budget (0= unlimited).
messagesTransform.enabled,messagesTransform.debug.
loop.enabled,loop.defaultMaxIterations,loop.cleanupWorktree,loop.defaultAudit,loop.model,loop.stallTimeoutMs,loop.minAudits.
sandbox.mode("off"|"docker"),sandbox.image.
graph.enabled,graph.autoScan,graph.watch,graph.debounceMs.
tui.sidebar,tui.showLoops,tui.showVersion.
| Plugin | Recommendation | Why |
|---|---|---|
opencode-forgecode |
Use | This plugin. Owns compaction, doom-loop, pending-todos, loops, plans, graph, sandbox, and the agent set. |
@plannotator/opencode |
Usually safe | Planning / todo layer complements the forge plan store. |
opencode-forge (upstream) |
Do not stack | opencode-forgecode already contains everything from opencode-forge. Running both doubles loop hooks and graph scans. |
@tarquinen/opencode-dcp |
Optional for long sessions | Safe when used as proactive pruning/compression with protected forgecode tools. Keep OpenCode compaction enabled so forgecode still owns the final summary-frame compaction step. |
Main collision points:
experimental.session.compactingtool.execute.before/tool.execute.afterexperimental.chat.messages.transformeventhandlers touching todos / sessionstui.appendPrompt
Rule: let opencode-forgecode own the final compaction prompt (experimental.session.compacting). If you also enable DCP, treat it as an upstream pruning layer, not a replacement for forgecode compaction.
Harness snapshots live at <dataDir>/snapshots/<sessionId>/<ts>-<fileTag>.bak. The file tag is the workspace-relative path with non-alphanumerics replaced by underscores. To restore:
fs_undo file="src/foo.ts" # newest snapshot
fs_undo file="src/foo.ts" steps=3 # 3rd newest
Snapshots are never deleted automatically. Clean old data-dir sessions manually if they grow large.
See the tui config block above. The sidebar shows all loops for the current project, a 📋 Plan link when a session plan is cached, and a command-palette entry Memory: Show loops.
The execution dialog lets you choose a launch mode (New session, Execute here, Loop (worktree), Loop) and pre-fills model selections based on last-used values (30-day TTL per project).
bun install
bun run typecheck
bun run lint
bun test --max-concurrency=1
bun run build # emits dist/The test suite covers 844 cases across forge features and the 52 harness-specific unit and integration tests.
MIT
{ "logging": { "enabled": true, "debug": true, }, }