How Whyline works
Four parts: a workspace, two kinds of hooks, a small API, and the places you read the results. This page follows one change from prompt to whyline blame.
You create a workspace and get a key
A workspace holds one team's events. Creating one in the dashboard returns an API key that starts with
wl_. The key is shown once; only its SHA-256 hash is stored, so it cannot be shown again.You log the CLI in
whyline loginchecks the key against the server, then saves the URL and key in~/.config/whyline/config.json. You can use theWHYLINE_URLandWHYLINE_KEYenvironment variables instead.Hooks capture events as you work
Two hook types feed the same API:
- Claude Code plugin. On every prompt you submit, a
promptevent is sent. After everyEdit,Write,MultiEditorNotebookEdit, aneditevent is sent with the file path. - Cursor, Codex CLI and Gemini CLI hooks. The same
promptandeditevents, from each agent's own hook system (setup). - Git post-commit hook (installed by
whyline init). After every commit, acommitevent is sent with the hash, author email, message and changed files. After a rebase, apost-rewritehook records the new commits with the prompts of the commits they replace.
- Claude Code plugin. On every prompt you submit, a
Commits pick up the prompts that produced them
Agent edits are also remembered locally in
~/.config/whyline/edits.jsonl. When a commit includes files from those edits, the commit event carries the prompts from the same session, and the commit is recorded as that agent even without a trailer. Local records older than a day are dropped.For other agents there is no prompt link. The agent comes from the commit message: a
Co-Authored-By:,AI-Agent:orAssisted-by:trailer that identifies claude-code (Claude), cursor, copilot, codex, gemini, aider, devin or windsurf itself, not a person who shares its name. A commit with no match is recorded asnone.You ask why
whyline blame src/orders.ts:2runsgit blameon that line to get the commit, then asks the API for the event recorded for that commit. It prints the agent, author, time, commit message and, if one was linked, the prompt.Example output$ whyline blame src/orders.ts:2 src/orders.ts:2 const amount_cents = Math.round(order.total * 100); a7394e0b · claude-code · dev@example.com · 10/7/2026, 11:10:20 AM add orders Prompt: store money as integer centsThat output comes from a real run against a local demo repository: one Claude Code prompt, one edit, one commit.
You read the rest in the dashboard or CLI
whyline eventslists recent events. The dashboard shows the newest 500 events in a timeline you can filter by file, prompt, agent, author or commit, and Export CSV downloads the full history.
When something goes wrong
You are offline or the server is down
The event is appended to ~/.config/whyline/queue.jsonl and sent with the next successful event. Events the server rejects as invalid (HTTP 400 or 413) are dropped, because retrying cannot succeed. So are events sent with a rejected API key (401 or 403), so a wrong key cannot fill the queue; events already queued wait until the key is fixed. Every event carries an ID, so one that is sent again is stored once.
A hook fails
Hooks print nothing to stdout and always exit 0, so they cannot fail your commit or interrupt the agent. Errors go to stderr.
A line has no record
whyline blame says so: the commit may predate Whyline, or have been made without the git hook. An uncommitted line is reported as "not committed yet".
You want prompts kept private
Log in with --no-prompts. Prompt events are still recorded, without the text. See data and privacy.