Guide

Install

Recommended for every agent:

npm install -g kankaku
kankaku setup

kankaku installs everything: the CLI, the pi extension and the Claude Code integration.

Requires Node.js 24 or later. kankaku setup opens a wizard for agents (pi, gentle-shell, Claude Code), a hub (existing, local, or skipped) and project roots. Then kankaku opens the dashboard. For scripts, kankaku setup --yes accepts defaults without questions; kankaku setup --dry-run prints the plan without writing. kankaku doctor reports the state read-only. See the CLI guide.

Only the pi extension:

Install with
shell
pi install npm:kankaku-pi

pi install npm:kankaku still works for existing installs, but it also brings the CLI’s dependencies into pi; never keep both sources in the same settings, or the extension loads twice and doubles every measurement.

Also: pi install git:github.com/soyunninja/kankaku (add @v1.3.0 to pin the current version; tags v0.9.0 through v0.12.0 cannot be loaded from git), or pi install /absolute/path/to/kankaku/packages/pi, no copy. Try without installing: pi -e /absolute/path/to/kankaku/packages/pi. With gentle-shell, these same install commands use gentle-shell install ….

Your first session

In pi the first prompt is measured from the next prompt on; in Claude Code the same holds once kankaku setup has wired it (see “Claude Code” below).

Nothing to turn on — kankaku measures from the next prompt. With a hub, opening a new project asks for the client and project; you can skip it, and saying yes to remembering means it won’t ask again.

In pi’s TUI, /kankaku opens a framed panel. Its title (>_ kankaku · <Screen>) follows the screen you open:

╭─ >_ kankaku ─────────────────────────────────────╮
│  Target   Client, project, hub task, label       │
│  Report   Summary, tasks, sessions, clients,     │
│           projects; today/all; pin to chat       │
│  Sync     Status, now, all, backfill, catalog    │
│  Export   csv/json, today/all, write file        │
│  Doctor   Orphan/uncertain subagent counts       │
│  About    Versions, directory, hub, settings     │
│  ↑↓ move · Enter open · Esc/← back · q close     │
╰──────────────────────────────────────────────────╯

Esc or ← closes the panel at the root; q closes it from anywhere. Footer hints and rows are clickable only in pi’s fullscreen mode, not regular mode. Subcommands still work unchanged and skip the panel in the TUI. Headless (print/RPC) mode uses subcommands too; there, bare /kankaku prints today’s totals.

Claude Code

Check Claude Code in kankaku setup. No git checkout or --plugin-dir flag is needed. Setup merges statusLine (which reports per-prompt cost) and the declared event hooks into ~/.claude/settings.json without disturbing other keys or hooks. It installs user commands under ~/.claude/commands/kankaku/. Re-run setup if the install path changes, such as after switching Node versions. Do not also launch Claude Code with --plugin-dir: hooks would run twice and records would be duplicated.

Each prompt produces a worklog record in the same format as pi: wall time, waiting on a question or permission dialog, work time (wall minus waiting), tokens and cost. The record identifies its measuring agent (claude-code), plugin (kankaku-claude), plugin version and the Claude Code version, even when the CLI syncs it. Client and project resolve from the project’s config.json first, then from repository paths in the cached hub catalog; hooks do not contact the network. An interrupted prompt closes at its last recorded activity. Cost is the difference of the session’s running total between two settles, and each prompt starts where the previous one ended, so what the session spends between two prompts goes to the next record. Tokens include subagent work, including tokens spent between prompts, which go to the next record. A headless run (claude -p) has no statusline; its cost is read from Claude Code’s own session transcript when the session ends.

kankaku reads those numbers from the session transcript Claude Code keeps on disk: token counts, the Claude Code version and whether the session is interactive or headless. It reads no prompt and no answer, and sends nothing anywhere because of it.

Command What it does Notes
/kankaku:report Recent work by day: wall, waiting and work time and cost per prompt. Last 7 days.
/kankaku:status Sessions with state in this project: live process, open prompt, last cost, resolved target: and task:, and recorded $X of $Y, with any difference labeled (this prompt so far $R), (pending $P, goes to the next prompt) or (never recorded $Z) (spend before a chained baseline that cannot be recovered).
/kankaku:task [number | id | text | clear] Links this session to an open or doing task in the resolved hub project. No argument lists numbered tasks; choose by last-list number, hub id or unique title fragment; clear unlinks. Hub required. Session-only, not saved in config.json; a task outside the resolved project is unlinked. Assignment on the hub happens only at row creation.
/kankaku:sync Syncs recent local records to the hub. No arguments; default 24-hour window.
/kankaku:sync-all Requests a full sync.
/kankaku:sync-status Shows local pending count and sync state. No network or credentials.
/kankaku:doctor Read-only diagnosis of plugin files, sessions, cost visibility, hub sync, resolved target and task. No hub contact or printed credentials.
/kankaku:setup Prints a statusLine snippet for ~/.claude/settings.json. Only needed without the kankaku CLI; kankaku setup already configures it.

Token counts, and with them cache hit, come from Claude Code’s own session transcript, an internal format that is not a documented interface: if a Claude Code update changes it, records are still written, without tokens (and, for headless runs, without cost), until kankaku is updated. Cost is a per-prompt share of the session total read from the statusline. Background subagent time is not measured separately; a permission wait ends at the next hook event, not the click. Client or project cannot yet be changed from inside Claude Code.

Your numbers

/kankaku all        # all-time summary (TUI subcommand)
/kankaku tasks      # one line per task
/kankaku sync       # push what's pending to the hub
/kankaku export     # csv or json

Report lines with recorded tokens end in cache hit NN%, the share of prompt input tokens served from the provider’s cache: cacheRead / (input + cacheRead + cacheWrite). If no tokens were recorded, the suffix is omitted, not shown as 0%. For example: orchestrator: work 41m00s, waiting 0m00s, 1 record(s), $0.82, cache hit 68%. This also applies to tasks, clients and projects lines in pi. From 1.3.0, Claude Code records carry token counts too, so their lines show cache hit as well; records written by an earlier version have none.

Connect the hub (optional)

Want to see it first? Open https://demo.kankaku.io and log in with demo@kankaku.io / demokankaku (read-only, fictional data). The hub is open source: github.com/soyunninja/kankaku_hub.

export KANKAKU_PB_URL="https://your-hub.example"
export KANKAKU_PB_EMAIL="service@your-hub.example"
export KANKAKU_PB_PASSWORD="…"

No hub yet? kankaku setup can install one here: choose “install locally” in the Hub step, or run kankaku hub install. See the CLI guide.

Use a service account, never your own. Without this, kankaku still works — the client is just free text. The hub client/project catalog refreshes at every session start (the picker waits briefly); /kankaku catalog refresh refreshes it manually.

Use /kankaku task (or /kankaku task pick) to link this session to an open/doing task in the current hub project; subsequent synced task rows carry that link. /kankaku task clear removes it. The link is session-only: it is not saved in config.json or requested at session start, and /kankaku target pick|clear drops it. Subagents do not inherit it. Create tasks in the hub, not in kankaku. Assignment happens only when a row is created, so changing the assignee in the hub web app is not undone by later syncs.

In Claude Code, /kankaku:task links a hub task to the session. If you switch hubs, kankaku uploads everything again: it never reuses the previous hub’s sync state. The agent shown on a hub row is the one that measured the work (pi or Claude Code), not the tool that synced it.

With Engram. If the hub has Engram configured, each session arrives with its goal and a summary of what got done, next to time and cost. Enable it with KANKAKU_ENGRAM_URL on the hub server. Without Engram, the hub looks exactly the same.

Subagents and gentle-ai

kankaku doesn’t need gentle-ai. The essentials come from pi’s own events: work vs. waiting, tokens, cost, model, crash recovery, and hub sync.

Where the mechanism does matter is subagents:

  • gentle-pi and pi-subagents mark their children. kankaku joins them to their orchestrator with certainty, cost included.
  • The pi subagents example marks nothing. kankaku joins it by process ancestry, checking process identity.
  • Another mechanism: declare it with KANKAKU_SUBAGENT_TOOLS and KANKAKU_SUBAGENT_CHILD_ENV. Otherwise a detected child stays uncertain: its time is reported separately and never counted as a new task.

The review tag comes ready for gentle-ai review. If you don’t use it, it never shows up. Change it with KANKAKU_SEGMENTS.

Settings

The panel’s About screen shows every environment-only setting read-only, alongside versions, the resolved KANKAKU_DIR and hub URL.

VariableDefaultMeaning
KANKAKU_DIR.kankakuDirectory for the work log (`worklog.jsonl`) and crash-recovery checkpoints, relative to the project cwd unless given as an absolute path.
KANKAKU_INTERACTIVE_TOOLSask_user_question,ask_user_choiceComma-separated list of tool names whose execution span counts as waiting time.
KANKAKU_SEGMENTSreview=bash:\bgentle-ai review\b";"-separated "tag=tool:regex" rules for tagged segments (defaults to the single "review" rule).
KANKAKU_CLIENT(unset)Default billing client for this project; lower precedence than "/kankaku client", higher than the project's "config.json".
KANKAKU_ROLE(unset)Overrides role detection ("orchestrator"/"subagent") for one invocation only — never export it globally: `KANKAKU_ROLE=orchestrator pi …`.
KANKAKU_PB_URL(unset)PocketBase hub URL. Must be HTTPS unless it points at localhost/127.0.0.1/::1.
KANKAKU_PB_EMAIL(unset)Hub service-account email.
KANKAKU_PB_PASSWORD(unset)Hub service-account password.
KANKAKU_MACHINEOS hostnameThis machine's display name, attached to every record as "machine" once the hub is configured.
KANKAKU_SYNC_PROMPTnonePrompt privacy for sync: "none" (omitted), "truncated" (first 120 characters), or "full".
KANKAKU_SYNC_WINDOW_HOURS24How many hours behind the sync watermark are re-evaluated on every run, to catch subagents that settle late.
KANKAKU_SYNC_RECORDS1 (enabled)"0" disables uploading "work_records" (raw detail); "task_entries" are always uploaded.
KANKAKU_SYNC_AUTO1 (enabled)"0" disables the automatic "session_start"/"agent_settled" sync; "/kankaku sync" still works.
KANKAKU_SYNC_MIN_INTERVAL_MINUTES5Minimum minutes between automatic syncs; "0" disables the throttle. Never applies to a manual sync.

Good to know

  • Prompt text never leaves your machine unless you turn it on.
  • A subagent running in parallel never doubles the time.
  • A crash doesn’t erase anything — it’s marked interrupted.
  • kankaku estimates cost; it never invoices.
  • /kankaku doctor diagnoses without touching the network.
  • kankaku doctor (terminal) and /kankaku:doctor (Claude Code) diagnose the wider installation.
Metric What it is
work time what it actually worked
waiting time what it waited on you
wall-clock time the total, start to finish