CLI flags
Set
--backend to subscription, api, or an explicit provider name (see LLM backend).
stdout is strictly results-only. The pre-flight projection line (Pass A: ~… tokens (whole run) …), the Pass A: gated N section(s) prefilter notice, and every
ceiling/prompt/abort message print to stderr; stdout carries only the per-pass result lines,
the sidecar path, and Done..
Exit codes
Stable across releases (src/whyfile/errors.py), safe for scripts/agents to branch on:
See
AGENTS.md for the full contract (per-code agent actions, --dry-run/--doctor
JSON shapes, progress events, and manifest fields) for driving this tool non-interactively.
Per-pass models
On both backends, each pass defaults to a different model rather than one blanket choice, which kills the old Opus-for-everything default (and, since #55, the API path’s old everything-on-sonnet behavior — measured at ~2.3x the tiered cost):
Pass A keeps the stronger model because it does the actual reasoning (deriving a claim,
rationale, and alternatives from prose); B/C/D are comparatively mechanical, so
haiku
carries them at a fraction of the cost. On the subscription backend these are the CLI’s
model aliases; on the claude API backend they are real model ids
(claude-sonnet-4-6 for A, claude-haiku-4-5-20251001 for B/C/D) threaded into each
call. Other API backends (bedrock, gemini, …) have no whyfile tiering,
but since #77 whyfile resolves the one model id graphify itself will use (the backend’s
model env var, e.g. GRAPHIFY_BEDROCK_MODEL, else graphify’s registry default) and
threads it into every pass — so the manifest records the real id and cost is priced by
the id’s recognized family (bedrock’s …claude-haiku… ids price at haiku rates).
To override the model for every pass: GRAPHIFY_CLAUDE_CLI_MODEL on the subscription
backend (e.g. =haiku if your corpus is code-heavy enough that even Pass A doesn’t need
sonnet-level reasoning), ANTHROPIC_MODEL on the claude API backend, and the
backend’s own GRAPHIFY_<BACKEND>_MODEL env var elsewhere. Explicit env overrides always
beat the per-pass defaults.
To override a specific pass without an env var (and without every teammate repeating
it), commit an intent_models map to whyfile.config.json — see
Configuring whyfile
(#82). It sits between the env override and the tier defaults above: env override > intent_models[pass] > built-in tier > backend default, applies to any backend (not just
claude-cli/claude), and a partial map only touches the passes it lists.
--max-concurrency changes wall-clock time only, not cost — on both backends.
The actual cost levers are the model (per-pass tiering above), the call count (Pass A’s
structural gate on trivial sections), and per-call context; raising concurrency just runs the
same calls faster in parallel. Concurrent claude -p subprocesses parallelize under one
subscription login (measured: 4 concurrent realistic calls at ~3.3× effective concurrency —
an earlier claim that graphify serialized them behind a lock is obsolete; current graphify
has no such lock). One subscription caveat the meter now makes visible: each claude-cli call
carries the CLI’s own context scaffold on its input side (measured at tens of thousands of
input tokens per call, mostly cache reads), so the per-pass tokens_in on the subscription
backend reads far higher than a lean API call for the same prompt — that is the true figure,
not a bug.
Outputs
All outputs are written to the same directory asgraph.json: