Skip to main content
Every LLM-powered step (analyze’s LLM rules, and the whole Spec → Guard pipeline: spec scan, guard setup, guard generate) reaches the model through a pluggable transport: The choice between Claude Code and API is a saved, per-user setting; agent is a per-run mode for an orchestrating agent. All three send identical prompts and parse identical schema-validated JSON; only the delivery differs.

First run

The very first truecourse command you run, whichever it is, asks once and saves the answer:
Claude Code saves the choice and continues into your command. API walks provider → model → API key → optional fallback model and base URL, then makes one live call to prove the configuration works; a configuration that fails its probe is never saved. In a non-interactive shell (CI, scripts, git hooks) nothing is asked and nothing is written: Claude Code stays the default.

truecourse config llm

setup takes flags for non-interactive use (CI, dotfiles); passing --transport skips every prompt:

Where the selection lives

The selection lives in ~/.truecourse/config.json: per-user, written 0600 inside a 0700 directory, deliberately not the committable per-repo .truecourse/config.json. TRUECOURSE_HOME relocates the whole directory.
The api block persists even while transport is claude-code, so flipping between the two never re-asks for credentials. Where the key comes from, in order: llm.api.apiKey, then the variable named by llm.api.apiKeyEnv, then the provider’s standard variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, or COPILOT_API_KEY). Bedrock has none of these: omitted credentials fall through to the ambient AWS chain. Store no key at all and TrueCourse reads it from the environment on every run. TRUECOURSE_LLM_TRANSPORT=claude-code|api overrides the saved selection for a single run or a CI job. In API mode nothing shells out to claude, so its login preflight is skipped; instead an unusable provider configuration aborts up front, before any pipeline work or cost, with a pointer to truecourse config llm setup. Credentials are entered through the CLI or the config file only: the dashboard reads the selection but never edits it.

Per-run override: —llm-transport

--llm-transport <cli|agent|api> overrides the saved selection for one command: cli forces Claude Code, api forces the configured provider, agent uses the mailbox. Accepted by analyze, spec scan, guard setup, and guard generate.
On analyze, --llm / --no-llm is a separate flag: it decides whether LLM rules run; --llm-transport decides how to reach the model.

The agent mailbox

In agent mode the tool doesn’t call the model itself: for each prompt it writes requests/<id>.json ({ stage, system, user, schema, … }) into the --io directory and waits for a matching responses/<id>.json ({ text }). An orchestrating agent that is itself an LLM (e.g. a Claude Code routine) watches that directory and answers each prompt. This lets guard generation and analyze’s LLM rules run inside a headless cloud session with no claude binary and no API key.

Next steps

Models & environment

Per-stage model selection, concurrency, and timeouts.

Storage

Where the per-user config lives, and everything else on disk.