The layout is mandatory. New files go where this scheme says; if a deviation is needed, update this document first (CLAUDE.md rule 1).
src/
core/ PURE translation core: no I/O, no imports from server/providers.
request.ts Anthropic request -> provider mapping (SPEC-TRANSLATION §1-3)
response.ts non-streaming response mapping (§4)
stream.ts SSE state machine (§5)
dialect.ts dialect normalization (§5bis)
errors.ts error mapping (§6)
tokens.ts count_tokens estimate (§7)
quirks.ts centralized quirk implementation (the single place)
providers/ Declarative registry: provider definitions, default profiles, config schema.
server/ HTTP ingress, passthrough/translate routing, daemon, logging.
config/ Config load/save, keychain/credentials, schema validation.
cli/ Commands (init, use, run, doctor, list, status, stop, logs). Orchestration only:
the logic lives elsewhere.
doctor/ Agentic benchmark (SPEC-CLI §3), added in M4.
plan.ts PURE: workspace, prompt, scoring from artefacts, preflight, honest
reading of the headless result, cache_control probe classification
run.ts I/O: ephemeral server, headless Claude Code session, artefact collection
scripts/ Utilities not shipped with the package (for example ci-config.ts for the doctor
workflow). No product logic: if it is needed elsewhere, it belongs in src/.
test/
*.test.ts unit (pure core, fixture runner) and integration (server against the fake provider); run in CI
fixtures/ see TESTING.md
helpers/ fake provider server, request builders
e2e/ doctor and tests against real providers (excluded from CI by default)
core/imports fromcore/only. It is a pure library: functions (input to output), synchronous or generators for streaming. All fixture testing depends on this.providers/is declarative: data (endpoints, quirk flags, profiles), not logic. Quirk logic lives incore/quirks.ts.server/is the only place with HTTP, sockets and runtime filesystem access. It composescore/,providers/andconfig/.cli/implements no business logic: it callsserver/,config/anddoctor/.doctor/splits in two:plan.tsis pure likecore/(input to output, testable with no network and no disk) and imports nothing outside itself;run.tsis the I/O side and may composeserver/andconfig/. The split is not cosmetic: everything that decides a verdict lives in the pure half, so a judgement about a model never depends on how the network behaved. Nothing incore/may import fromdoctor/.
A note on plan.ts being imported by cli/init.ts: the harness context floor (DOCTOR_MIN_CONTEXT) is one single measurement and must hold identically in the wizard and in the doctor. Duplicating it would mean letting the two drift apart.
Request flow (translate mode):
Claude Code -> server/ingress -> [local auth] -> providers/resolve(profile, slot)
-> core/request.map -> HTTP to the provider -> core/stream (or response)
-> core/dialect.normalize -> SSE back to Claude Code
In passthrough the core branch shrinks to: rewrite URL, auth and model, then pipe.
Minimal and justified: a lightweight HTTP framework (hono or fastify), tiktoken-js (count_tokens), jsonrepair (the looseJsonArguments quirk), @napi-rs/keyring (optional, with the 600 file fallback, ADR-26). Every new dependency must be justified in DECISIONS.md.