CLI flags
| Flag | Default | Description |
|---|---|---|
--graph | graphify-out/graph.json | Path to the graphify output graph.json |
--docs | docs/ | Directory containing markdown source files |
--passes | A,B,C | Comma-separated passes: A extract, B anchor, C cross-doc relate, D cross-doc concept resolution (opt-in) |
--backend | (auto) | Backend selection (see LLM backend) |
--progress | auto | Progress reporting mode: auto (live line on TTY, heartbeat when piped), plain (heartbeat lines only), json (NDJSON events on stderr), none (silent). stdout stays results only; warnings and progress go to stderr. |
--min-confidence | 0.75 | Minimum confidence_score for Pass C and Pass D edges |
--max-concurrency | 4 | Maximum number of concurrent LLM requests |
--max-tokens | 500000 | Estimated-token ceiling for the whole run (all passes, summed). Enforced three ways (a pre-flight prompt, a mid-Pass-A breaker, and a post-Pass-A checkpoint before Pass B), so a run stays within the estimate it showed you. Nothing is written on an abort; --yes skips only the pre-flight prompt, never the checkpoint. 0 = unlimited. Full semantics in AGENTS.md |
--max-cost-usd | 2.00 | Cost guard for API backends: prompts if the pre-flight estimate exceeds this (the subscription backend has no per-token bill to guard, so it skips this check; --max-tokens is the guard that applies there). Pre-flight only; the post-Pass-A checkpoint gates tokens, not cost |
--yes | false | Skip the pre-flight ceiling prompt (non-interactive / CI); does not skip the post-Pass-A whole-run checkpoint |
--dry-run | false | Project calls/tokens/cost/time as JSON and exit: no extraction (one tiny backend liveness ping only). JSON shape documented in AGENTS.md |
--no-cache | false | Bypass the Pass A cache entirely: no reads, no writes; every section re-extracts. See Caching & resume |
--doctor [--json] | false | Check the environment (runtime, backend auth, graph/docs shape, version drift) and exit; --json for machine output. Version drift is advisory only; a stale editable install no longer fails the run |
--version | n/a | Print the installed version and exit |
--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:
| Code | Meaning |
|---|---|
0 | Success |
1 | Unexpected error (unhandled exception; never raised deliberately) |
2 | Bad input: invalid --passes, graph.json missing, no .md docs found |
3 | Token/cost ceiling: pre-flight decline, non-interactive over-ceiling, the Pass A budget breaker, or the post-Pass-A whole-run checkpoint before Pass B |
4 | Backend unusable: none configured, the graphify runtime missing, or all Pass A sections failed |
5 | Policy gate: changed --fail-on-constraint (a changed file is governed by a constraint), intent-diff --gate (constraint-governed code changed with no superseding record in the PR), or check (a compiled constraint in intent-rules.json is violated) |
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 (subscription backend)
On the subscription (claude-cli) backend, each pass defaults to a different model rather
than one blanket choice, which kills the old Opus-for-everything default:
| Pass | Default model |
|---|---|
| A (extract) | sonnet |
| B (anchor) | haiku |
| C (cross-doc relate) | haiku |
| D (concept resolution) | haiku |
haiku
carries them at a fraction of the cost. Set GRAPHIFY_CLAUDE_CLI_MODEL to override the
model for every pass, e.g. GRAPHIFY_CLAUDE_CLI_MODEL=haiku if your corpus is
code-heavy enough that even Pass A doesn’t need sonnet-level reasoning.
--max-concurrency changes wall-clock time only, not cost, and only on the API backend.
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, there. On the subscription backend, claude-cli calls serialize
upstream in graphify (a lock around the CLI subprocess), so --max-concurrency has little
effect: raising it does not make subscription runs faster.
Outputs
All outputs are written to the same directory asgraph.json:
| File | Contents |
|---|---|
.whyfile.json | Sidecar: all intent nodes and edges added across runs |
graph.enriched.json | Original graph merged with the intent layer (graphify node-link format) |
enrichment_report.md | Before/after metrics: node/link/isolated counts, intent breakdown by kind, anchored vs orphaned, grounding coverage |
.whyfile_progress.jsonl | Progress stream (NDJSON, one event per line): run_start, pass_start, tick, unit_fail, pass_end events for agents to tail or poll. Written for any --progress mode except none. |
intent_run.json | Per-run manifest: status/error, metrics, artifacts, and per-pass records (passes[], including s_per_call, cache_hits, and cache_misses; the cache fields are nonzero only for Pass A). Written on every run (independent of --progress mode). At run end, an estimated-vs-actual runtime/duration reconciliation line is printed to stderr. |
intent_runs.jsonl | History: the same manifest object appended as one line per run, oldest first: a run-over-run log without snapshotting intent_run.json yourself. |
.whyfile.prev.json | Snapshot of the sidecar as it was before this run’s merge. Written every run (skipped on the first run). Powers digest --since last-run. Git-ignored under graphify-out/. |