Skip to content
ƒtsforgev0.57.1
38

Subsystems

8 min read

Derived from packages/core/src: 32 subsystems, 748 files, 146k lines, 154 cross-subsystem edges.

This page is the exhaustive record: every subsystem, every cross-subsystem edge, and a file:line witness for each claim. For the shape of the system rather than its inventory, see the hand-drawn map on Internals.

SubsystemPurposeTierFilesLinesFan-inFan-out
loopThe drive-to-green engine: turns, tools, gate settling, steering, adapterscore17749k824
rule-packsThe ESLint rule packs the gate enforces, grouped by stackcore19123k33
renderTerminal UI — status bar, menus, wizards, markdown, diffs, spinnerscore4511k77
cliArgument parsing, the interactive REPL, and per-mode wiringcore2910k220
agentTool schemas, the model-as-agent wrapper, and the malformed-tool-call repair laddercore115k99
site-pluginsBuilt-in site experts (Reddit…) that read a site’s structured data through the Chrome bridgeoptional344k14
inferenceOpenAI-compatible provider: streaming, tool calls, reasoning, token usagecore123k104
configtsforge.config.json, profiles, recipes, agent specs, and external pluginscore143k109
meta-rulesGate rules that need no AST — config shape, CI wiring, supply chaincore423k12
self-harnessLets the harness propose, trial, and keep edits to its own prompts and rulesoptional153k29
scaffoldStands up a new project from an archetype and configures its gateoptional203k33
(root)CLI entry, model registry, session persistence — the loose files in src/core63k616
gateComposes and runs the deterministic gate: linter, stages, tool pathscore163k58
editorThe terminal input-line editor behind the REPL promptcore102k22
reviewersIndependent review panel that grades a change before it is trustedoptional92k13
evalRun scoring, failure classification, and the quality judgeoptional102k44
libShared primitives — fs, json, guards, scope globs, SSRF checks, clipboardcore212k260
filesReading, creating, and hash-anchored editing of workspace filescore92k51
policyDecides which actions are allowed in the current mode before they runcore52k53
mcpModel Context Protocol client that exposes external servers as toolsoptional112k41
chrome-bridgeLocalhost WebSocket bridge to the tsforge Chrome extension — read-only research in the user’s browseroptional91k41
architecture ⚠️Derives this map from source so the docs cannot drift from the codeoptional81k00
lspTypeScript language service powering navigation and write-time diagnosticsoptional3<1k50
browserHeadless Chromium oracle that render-checks a page as a gate stageoptional3<1k11
infer-rulesScans a repo for its conventions and turns them into rule overridescore7<1k52
validateRuns the gate command and parses tool output into structured errorscore7<1k72
specTask and spec shapes, spec parsing, and test generation from intentcore6<1k66
stack-detectionDetects the project’s stack and picks which rule packs applycore4<1k81
setupOnboarding wizard that writes a project’s initial tsforge configoptional4<1k25
codebaseStructural workspace map and hub ranking used to seed prompt contextcore6<1k24
proptest ⚠️Derives property-based test inputs from TypeScript typesoptional3<1k00
constitution ⚠️Baseline system-role text and the reference ESLint constitutionoptional1<1k00

⚠️ marks a subsystem with no imports in either direction inside src/. That does not make it dead — it may be reached only from tests/ or scripts/ — but it does mean the running harness never pulls it in.

Pairs that import each other, with the line proving each direction. Only mutual pairs are listed: longer cycles exist in a graph this dense, and enumerating them buries the ones someone can actually go and break.

PairOne wayThe other
(root) ↔ clicli.ts:30 → ./cli/argscli/config-menu.ts:10 → ../models-config
(root) ↔ gatecli.ts:32 → ./gate/gate-runnergate/core-gate.ts:3 → ../update-check
(root) ↔ inferenceclassify.ts:1 → ./inferenceinference/image-gen.ts:4 → ../models-config
(root) ↔ loopcli.ts:13 → ./looploop/expert-handoff.ts:21 → ../models-config
agent ↔ inferenceagent/agent-runner.ts:18 → ../inferenceinference/wire.ts:9 → ../agent
agent ↔ loopagent/agent-runner.ts:21 → ../loop/loop.typesloop/context-hygiene.ts:12 → ../agent/agent.constants
agent ↔ policyagent/agent-runner.ts:20 → ../policypolicy/classify.ts:1 → ../agent
agent ↔ specagent/agent.types.ts:1 → ../specspec/generate-tests.ts:5 → ../agent
cli ↔ rendercli/banner.ts:9 → ../renderrender/command-menu.ts:2 → ../cli/commands
config ↔ rule-packsconfig/external-plugins.ts:5 → ../rule-packsrule-packs/index.ts:135 → ../config/plugin-fingerprint
editor ↔ rendereditor/completion.ts:8 → ../render/file-menurender/frame/input-seq.ts:1 → ../../editor/segments
eval ↔ loopeval/failure-class.ts:1 → ../loop/loop.typesloop/loop.types.ts:7 → ../eval/failure-class
inference ↔ loopinference/wire.ts:10 → ../loop/context-hygieneloop/assistant-message.ts:1 → ../inference
loop ↔ renderloop/gate/panel.ts:3 → ../../render/frame/ansi-plainrender/agent-tree.ts:8 → ../loop/loop.types
loop ↔ self-harnessloop/feedback/rule-docs.ts:3 → ../../self-harness/overlayself-harness/build-evidence.ts:3 → ../loop
loop ↔ specloop/feedback/feedback.ts:2 → ../../specspec/generate-tests.ts:4 → ../loop
spec ↔ validatespec/generate-tests.ts:8 → ../validatevalidate/accept.ts:1 → ../spec

The interfaces an adapter fills in. The core loop may not import an adapter — @typescript-eslint/no-restricted-imports in eslint.config.js enforces it — so everything stack-specific arrives through one of these.

SeamDeclaredFiles referencing it
IStackAdapterloop/planning/stack-adapter.ts:146
IConventionProviderloop/conventions-provider.ts:1017
IPlanSchemaloop/planning/plan-types.ts:458
IGategate/gate-runner.ts:2522
IProductPlanloop/planning/plan-types.ts:3210

Async functions returning an exit code, declared under the CLI — the commands.

FunctionDeclared
agentsModecli.ts:384
greenfieldModecli.ts:685
harnessDiagnoseModecli/harness-diagnose-mode.ts:211
harnessReviewModecli/harness-review-mode.ts:700
maincli.ts:801
mapModecli.ts:520
recipesModecli.ts:539
replcli/repl.ts:1175
reviewModecli.ts:203
runOncecli.ts:105
runTraceCommandcli/repl-commands.ts:160
scaffoldModecli.ts:767
setupModecli.ts:528
traceModecli.ts:590

src/ reaching into scripts/ inverts the house rule that scripts orchestrate and src/ decides — logic imported this way sits where tests are not looking.

FromImportTarget
scaffoldscaffold/io.ts:11scripts/boot-check.ts

Stated so a gap is not mistaken for a clean result:

  • Dynamic import() and createRequire are not followed — only static imports.
  • tests/ and scripts/ are not scanned; only imports into them are noted above.
  • Coupling through shared global state or the filesystem is invisible here.
  • Only mutual dependency pairs are reported, not longer cycles.