Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,8 @@

- Install with `pnpm install --frozen-lockfile`; `pnpm-lock.yaml` is canonical. Bun is still required because build, setup, and dashboard scripts run through it—do not substitute `bun install`.
- Full source verification: `pnpm typecheck && pnpm lint && pnpm test && pnpm build`.
- Focus a Node test with `pnpm test -- --project node test/path.test.ts`; add `-t "test name"` for one case.
- Run dashboard DOM tests with `pnpm test -- --project dom test/dashboard/app-dom.test.ts`. The `dom` project is the only happy-dom/browser-conditions suite; all other tests use the `node` project and a `bun:sqlite` shim.
- Focus a Node test with `pnpm test --project node test/path.test.ts`; add `-t "test name"` for one case.
- Run dashboard DOM tests with `pnpm test --project dom test/dashboard/app-dom.test.ts`. The `dom` project is the only happy-dom/browser-conditions suite; all other tests use the `node` project and a `bun:sqlite` shim.
- `pnpm typecheck` covers `src/`, not tests or scripts. ESLint also ignores tests and generated dashboard files, so run the relevant Vitest project after changing them.

## Runtime boundaries
Expand All@@ -21,6 +21,10 @@
- The build does not clean `dist/`; remove stale output when deleting or renaming source modules before validating package contents.
- Bundled prompts live in `src/prompts/`; bundled skills live in `skills/`. They sync on every plugin load, preserving user edits and never deleting files. The standalone installer handles conflicts and orphan pruning.
- Keep the section-summary markers in `src/prompts/agents/auditor-loop-addendum.md` synchronized with the constants in `src/utils/section-summary.ts`.
- `MAX_TOTAL_SECTIONS` in `src/constants/loop.ts` is the single section cap; the decomposer, section bootstrap, plan structure summary, TUI inline plan preview and `plan-adjust` all read it, and the architect system reminder in `src/index.ts` interpolates it. `src/prompts/agents/architect.md` is prose and repeats the number literally — update it when the cap changes.
- `PLAN_AUTHORING_TOOL_NAMES` in `src/constants/loop.ts` is the single list of plan-authoring tools; the `code`, `auditor`, and `feature-splitter` tool-exclude lists and both permission rulesets derive their deny entries from it.
- `LoopService.resolveActiveLoopForSession` is the only correct "is this session inside a running loop" check. `resolveLoopName` matches terminated loops too, so using it as an activity guard blocks a session forever after its loop ends.
- `resolveForgeDbPath` in `src/utils/opencode-paths.ts` is the only place `<dataDir>/forge.db` is built; every entry point must route through it so a configured `dataDir` is honoured uniformly.

## Dashboard and storage gotchas

Expand Down
34 changes: 24 additions & 10 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -70,7 +70,19 @@ Forge includes a read-only observability Dashboard — a standalone Bun HTTP ser

### Views

The dashboard shows a **Loops view** by default — groups loops by project with filterable project/loop lists, loop detail (plan, sections, findings, usage, audit results, completion summary), and live polled state (5 s interval). Supports `#<projectId>/<loopName>` deep linking.
The dashboard is a **repo shell**: pick a repository, then move between its **Loops**, **Groups**, **Findings**, and **Plans** sections. Loop detail opens as tabs (overview, timeline, sections, findings, plan, usage), with live polled state on a 5 s interval.

Deep links use the hash:

| Hash | Opens |
| --- | --- |
| `#<projectId>` | The repo's Loops section |
| `#<projectId>/loops` \| `/groups` \| `/findings` \| `/plans` | That repo section |
| `#<projectId>/groups/<groupId>` | A feature group's detail view |
| `#<projectId>/loop/<loopName>[/<tab>]` | A loop, optionally on a given tab |
| `?status=running,errored&q=<text>` | Appended to any of the above to preserve filters |

`loops`, `groups`, `findings`, and `plans` are reserved as the second segment, so a loop with one of those names must be addressed as `#<projectId>/loop/<loopName>`.

### API Endpoints

Expand All@@ -79,7 +91,9 @@ All endpoints are read-only (non-GET requests return 404):
| Endpoint | Description |
|----------|-------------|
| `GET /` | HTML page (inlined SolidJS app) |
| `GET /api/data` | JSON snapshot of Forge loop/project state |
| `GET /api/data` | JSON snapshot of Forge loop/project state. Accepts optional `project` and `loop` query parameters (`/api/data?project=<projectId>&loop=<loopName>`) to scope the payload: per-loop text (`plan`, `goal`, `lastAuditResult`, `postActionReport`, `sections`, `amendments`) is materialised only for the scoped loop, and `findings` rows, `usage`, and `transitions` only for the scoped project. `duration`, `hasPlan`, `sectionCount`, and `bugCount` are always populated so the repo index, tab set, and section/bug counts render correctly while detail is in flight. |

In the browser, the loop table, repo findings list, plans list, and loop picker cap their rendered rows behind a "Showing N of M" affordance (a "Show all" toggle expands the loop table, findings, and plans lists).

## Screenshots

Expand All@@ -89,7 +103,7 @@ Execution flow dialog with mode and model selection:

## Features

- **Plans** — architect produces marked plans that are auto-captured to SQL storage
- **Plans** — architect authors plans directly into SQL storage with `plan-write`/`plan-edit`; marked plans emitted in chat are still auto-captured
- **Execution** — approved-plan launch paths plus direct `/execute-goal` loops in dedicated worktree sessions; plan loops can also target a configured remote opencode server (see [Configuration](docs/configuration.md#remotes))
- **Loops** — iterative coding/auditing with isolated git worktree and optional Docker sandbox
- **Review Findings** — persistent, loop-scoped review findings across loop sessions
Expand All@@ -111,7 +125,7 @@ The auditor agent is a read-only subagent that cannot edit source files or execu

**Tool restrictions:** The auditor cannot use file-editing tools, planning tools, or loop-management tools. See [Auditor restrictions](docs/agents-and-commands.md#auditor-restrictions).

The architect agent operates as a read-only planner with message-level reinforcement via the `experimental.chat.messages.transform` hook. Final plans are rendered once in the assistant response between `<!-- forge-plan:start -->` and `<!-- forge-plan:end -->` markers, then auto-captured into SQL before execution approval. After user approval via the question tool, execution is dispatched programmatically — no additional LLM calls are needed. The user can view and edit the cached plan from the sidebar or command palette before or during execution.
The architect agent operates as a read-only planner with message-level reinforcement via the `experimental.chat.messages.transform` hook. Final plans are authored straight into SQL storage with the `plan-write` and `plan-edit` tools, which return a structure report the architect uses to fix warnings before asking for approval. A plan emitted in the assistant response between `<!-- forge-plan:start -->` and `<!-- forge-plan:end -->` markers is still auto-captured into the same row, so marker-only architect prompts keep working. After user approval via the question tool, execution is dispatched programmatically for New session and Execute here modes — no additional LLM calls are needed. Loop mode remains an exception: the architect must invoke `execute-plan`. The user can view and edit the stored plan from the sidebar or command palette before or during execution.


## Tools
Expand All@@ -120,7 +134,7 @@ See [Tools reference](docs/tools.md) for full arguments, section-scoping behavio

Forge provides these tool groups:

- **Plan tools** — `plan-read`, `section-read`
- **Plan tools** — `plan-write`, `plan-edit`, `plan-read`, `section-read`, `plan-adjust`
- **Review tools** — `review-write`, `review-read`, `review-delete`
- **Loop tools** — `execute-plan`, `execute-goal`, `loop-cancel`, `loop-status`
- **Sandbox shell** — `sh` when a sandbox manager is available
Expand DownExpand Up@@ -209,7 +223,7 @@ The sidebar shows Forge's connection status and version. Captured plans live on

### Execution Dialog

Open the dialog from the command palette as `Execute plan` (default keybind `<leader>f`). The plan is sourced from the most recent architect message in the current session the marked `<!-- forge-plan:start --> ... <!-- forge-plan:end -->` block is parsed out of the assistant's reply. If no marked plan exists in the session, the dialog will not open and you'll see a toast asking the architect to produce one first.
Open the dialog from the command palette as `Execute plan` (default keybind `<leader>f`). The plan is sourced from the stored plan for the current session, so the dialog shows exactly what `execute-plan` would run. When no stored row exists, the most recent architect message is scanned for a marked `<!-- forge-plan:start --> ... <!-- forge-plan:end -->` block as a fallback. If neither is present, the dialog will not open and you'll see a toast asking the architect to produce a plan first.

The dialog provides full control over execution parameters:

Expand DownExpand Up@@ -311,9 +325,9 @@ Plan with a smart model, execute with a fast model. The architect agent research

### How Plans Work

The architect is read-only and must output exactly one final planbetween `<!-- forge-plan:start -->` and `<!-- forge-plan:end -->` markers. Forge auto-captures that marked plan into SQL storage for the current session.
The architect is read-only and authors the plan into SQL storage for the current session with `plan-write`, appending further phases with `plan-write { append: true }` and revising with `plan-edit`. Every write returns a structure report — line/character counts, the detected `Loop Name:`, the sections the decomposer would emit, and warnings — so structural problems surface before approval. When no stored plan was written, a plan emitted once between `<!-- forge-plan:start -->` and `<!-- forge-plan:end -->` markers is auto-captured into the same row.

The captured plan is the source of truth for execution. The architect's own message in the chat history is the human-readable view; programmatic access is via the `plan-read` tool.
The stored plan is the source of truth for execution: `execute-plan`, the approval hook, and the TUI dialog all read it, and a marker-free assistant message can never replay an older chat plan over a newer tool-authored one. Programmatic access is via the `plan-read` tool.

### Execution

Expand DownExpand Up@@ -363,7 +377,7 @@ Model and variant selection follows this priority order:

### Troubleshooting

- **No plan found** — Ensure the architect output included the `<!-- forge-plan:start -->` / `<!-- forge-plan:end -->` markers; the capture hook only stores plans wrapped in those markers.
- **No plan found** — Ensure the architect called `plan-write`, or that its response wrapped the plan in `<!-- forge-plan:start -->` / `<!-- forge-plan:end -->` markers; a plan emitted in chat without those markers is not captured.
- **TUI shows no plan** — Plans are session-scoped on the server; switch to the session where the architect produced the plan.
- **Need logs** — Set `logging.enabled` to `true`, and optionally `logging.debug` for verbose output.

Expand DownExpand Up@@ -391,7 +405,7 @@ Audit findings survive session rotation via the **review store**. The auditor st

### Usage Tracking

Loop sessions rotate between code and auditor work, so Forge persists per-session usage rows in `loop_session_usage` and merges them for `loop-status`. Detailed status includes cumulative cost, input/output/reasoning/cache token totals, per-model breakdowns, and live active-session output when available.
Loop sessions rotate between code and auditor work, so Forge persists per-session usage rows in `loop_session_usage` and merges them for `loop-status`. Detailed status includes cumulative cost, input/output/reasoning/cache token totals, per-model breakdowns, a per-role (`code`/`auditor`/`unknown`) breakdown, and live active-session output when available.

### Worktree Isolation

Expand Down
4 changes: 3 additions & 1 deletion docs/agents-and-commands.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ See also: [Tools](tools.md), [Configuration](configuration.md), [Loop System](lo
| Agent | Mode | Description |
|---|---|---|
| `code` | `all` | Primary implementation agent. |
| `architect` | `primary` | Read-only planning agent. Produces marked plans for approval and execution. |
| `architect` | `primary` | Read-only planning agent. Authors the stored plan with `plan-write`/`plan-edit` for approval and execution; marked plans in chat are still captured. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
printf'\n== Files ==\n'
git ls-files 'docs/agents-and-commands.md''docs/api/_media/agents-and-commands.md'| cat
printf'\n== Relevant mentions in docs ==\n'
rg -n 'plan-write|plan-edit|plan_exit|plan\b|AUDITOR_TOOL_EXCLUDES|PLAN_AUTHORING_TOOL_NAMES|auditor' docs/agents-and-commands.md docs/api/_media/agents-and-commands.md
printf'\n== Search repo for tool name constants ==\n'
rg -n 'PLAN_AUTHORING_TOOL_NAMES|AUDITOR_TOOL_EXCLUDES|plan-write|plan-edit|plan_exit|plan'. --glob '!docs/api/_media/agents-and-commands.md' --glob '!docs/agents-and-commands.md'

Repository: chriswritescode-dev/opencode-forge

Length of output: 50391


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo'== docs/agents-and-commands.md =='
cat -n docs/agents-and-commands.md | sed -n '1,40p'echoecho'== docs/api/_media/agents-and-commands.md =='
cat -n docs/api/_media/agents-and-commands.md | sed -n '1,40p'echoecho'== targeted source search =='
rg -n 'AUDITOR_TOOL_EXCLUDES|PLAN_AUTHORING_TOOL_NAMES|plan-adjust|plan-write|plan-edit|plan_exit|plan_enter' src test docs -g '!docs/api/_media/agents-and-commands.md' -g '!docs/agents-and-commands.md'

Repository: chriswritescode-dev/opencode-forge

Length of output: 36724


Document plan-write and plan-edit in the auditor exclusions.
Both docs still list only plan and plan_exit, but the shared auditor deny list also excludes plan-write and plan-edit via PLAN_AUTHORING_TOOL_NAMES; update the mirrored tables to match.

📍 Affects 2 files
  • docs/agents-and-commands.md#L12-L12 (this comment)
  • docs/api/_media/agents-and-commands.md#L12-L12
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/agents-and-commands.md` at line 12, Update the auditor exclusion entries
in both docs/agents-and-commands.md:12-12 and
docs/api/_media/agents-and-commands.md:12-12 to include plan-write and plan-edit
alongside plan and plan_exit, matching the shared PLAN_AUTHORING_TOOL_NAMES deny
list. Keep the mirrored tables consistent.

Source: Coding guidelines

| `auditor` | `subagent` | Read-only code review agent for convention-aware reviews. |
| `auditor-loop` | `primary`, hidden | Internal auditor used by loop audit sessions. |

Expand All@@ -27,6 +27,8 @@ Excluded tools:
- `multiedit`
- `plan`
- `plan_exit`
- `plan-write`
- `plan-edit`
- `execute-plan`
- `execute-goal`
- `loop-cancel`
Expand Down
Loading