- Install with
pnpm install --frozen-lockfile;pnpm-lock.yamlis canonical. Bun is still required because build, setup, and dashboard scripts run through it—do not substitutebun 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. Thedomproject is the only happy-dom/browser-conditions suite; all other tests use thenodeproject and abun:sqliteshim. pnpm typecheckcoverssrc/, not tests or scripts. ESLint also ignores tests and generated dashboard files, so run the relevant Vitest project after changing them.
- This is one package with three published entrypoints: server plugin
src/index.ts, TUI pluginsrc/tui.tsx, and installer CLIsrc/install/cli.ts. src/index.tsis the server composition root. Core boundaries aresrc/loop/(runtime/state machine),src/storage/(database and repositories),src/tools/(OpenCode tools), andsrc/agents/(agent definitions).pnpm dashboardruns the standalone read-only dashboard throughscripts/dashboard.ts; it is not a package export.
pnpm buildrewritessrc/version.ts,src/dashboard/marked-source.ts, andsrc/dashboard/app-bundle.ts. Editpackage.json,src/dashboard/marked.min.js, orsrc/dashboard/app/respectively, never the generated files.- After changing
src/dashboard/app/, runpnpm buildbefore tests;test/dashboard/app-bundle.test.tsrejects a stale source hash. - 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 inskills/. 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.mdsynchronized with the constants insrc/utils/section-summary.ts. MAX_TOTAL_SECTIONSinsrc/constants/loop.tsis the single section cap; the decomposer, section bootstrap, plan structure summary, TUI inline plan preview andplan-adjustall read it, and the architect system reminder insrc/index.tsinterpolates it.src/prompts/agents/architect.mdis prose and repeats the number literally — update it when the cap changes.PLAN_AUTHORING_TOOL_NAMESinsrc/constants/loop.tsis the single list of plan-authoring tools; thecode,auditor, andfeature-splittertool-exclude lists and both permission rulesets derive their deny entries from it.LoopService.resolveActiveLoopForSessionis the only correct "is this session inside a running loop" check.resolveLoopNamematches terminated loops too, so using it as an activity guard blocks a session forever after its loop ends.resolveForgeDbPathinsrc/utils/opencode-paths.tsis the only place<dataDir>/forge.dbis built; every entry point must route through it so a configureddataDiris honoured uniformly.
- The dashboard browser app uses
solid-js/html, not JSX. Do not use<${Show}>or<${For}>; use reactive thunks/memos and.map(). Every template needs a real root element, reactive regions must be functions such as${() => ...}, and the root component returns one wrapper element.test/dashboard/app-dom.test.tsenforces these constraints. - Storage migrations are registered explicitly, in execution order, in the lowercase
migrationsarray insrc/storage/migrations/index.ts; they are not discovered from filenames. Inline migrations are valid, so not every migration needs a SQL file.
- Logging is disabled by default. When enabled, logs default to
$XDG_DATA_HOME/opencode/forge/logs/forge.log(falling back to~/.local/share/opencode/forge/logs/forge.log);logging.fileinforge-config.jsoncoverrides it.