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.

  1. 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.

  2. You log the CLI in

    whyline login checks the key against the server, then saves the URL and key in ~/.config/whyline/config.json. You can use the WHYLINE_URL and WHYLINE_KEY environment variables instead.

  3. Hooks capture events as you work

    Two hook types feed the same API:

    • Claude Code plugin. On every prompt you submit, a prompt event is sent. After every Edit, Write, MultiEdit or NotebookEdit, an edit event is sent with the file path.
    • Cursor, Codex CLI and Gemini CLI hooks. The same prompt and edit events, from each agent's own hook system (setup).
    • Git post-commit hook (installed by whyline init). After every commit, a commit event is sent with the hash, author email, message and changed files. After a rebase, a post-rewrite hook records the new commits with the prompts of the commits they replace.
  4. 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: or Assisted-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 as none.

  5. You ask why

    whyline blame src/orders.ts:2 runs git blame on 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 cents

    That output comes from a real run against a local demo repository: one Claude Code prompt, one edit, one commit.

  6. You read the rest in the dashboard or CLI

    whyline events lists 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.