Self-calibrating AI task estimates for Claude Code
Claude estimates in its own units. The hook records actuals. Next time, it calibrates.
Most AI assistants default to human-calendar estimates — "this'll take 30 minutes" — for tasks that, for the AI, actually take ~90 seconds and ~40k tokens. The estimates are useless, and they never get better because there's no feedback loop.
WallClock flips the default. It's a skill + hook pair that teaches Claude Code to estimate in AI-native units (tokens + wall-clock seconds), records actuals after every turn, and has the agent consult the log before making its next estimate. No telemetry. No servers. Just a skill file, a bash hook, and an append-only NDJSON log at ~/.wallclock/log.ndjson.
The goal: over a few days of real use, Claude's estimates become grounded in its own history rather than a vague prior trained into the model.
| Feature | Details | |
|---|---|---|
| ⏱️ | Per-turn wall-clock | UserPromptSubmit + Stop hooks bracket each turn |
| 🎟️ | Token accounting | Pulls input_tokens, output_tokens, cache reads/creates from the session transcript |
| 🧠 | Self-calibrating | The skill reads recent log entries before estimating the next task |
| 📓 | Append-only NDJSON | Simple to grep, jq, pipe into sqlite, or plot |
| 🚫 | Zero telemetry | Everything stays in ~/.wallclock/ on your machine |
| 🪶 | ~100 lines of bash | Auditable end-to-end; no dependencies beyond jq |
git clone https://github.com/AnthonyDavidAdams/wallclock.git
cd wallclock
./install.shCopies
hook.shto~/.wallclock/, installs the skill to~/.claude/skills/wallclock/, and registers both hooks in~/.claude/settings.json(backing up the existing file first).
- Claude Code
jq(brew install jq)- macOS or Linux with bash
- Restart Claude Code after install so the new hooks load
- Give Claude a non-trivial task
- It'll print an estimate up front:
Estimate: ~1 human-hour · ~45k tokens · ~90 s AI wall-clock
- Your hook logs actuals silently in the background
- Repeat — estimates sharpen as the log grows
There's nothing to run. The skill loads automatically when Claude Code starts, and the hooks fire on every turn. You can inspect or aggregate the log anytime:
# last 5 turns with token totals
tail -5 ~/.wallclock/log.ndjson | jq .
# total tokens spent this week
jq -s 'map(select(.event=="turn_end").usage.output_tokens) | add' ~/.wallclock/log.ndjson
# grep your estimates vs reality for a task
jq 'select(.task|test("refactor"))' ~/.wallclock/log.ndjson| Event | Written by | Fields |
|---|---|---|
turn_start |
UserPromptSubmit hook |
ts, session_id, cwd |
turn_end |
Stop hook |
ts, session_id, cwd, usage {input/output/cache} |
estimate |
Claude (via skill) | ts, estimate {tokens, seconds, human_hours}, task |
reflection |
Claude (via skill) | ts, note |
Wall-clock for a turn = turn_end.ts − turn_start.ts (nearest preceding start in same session).
wallclock/
├── install.sh # registers hooks + copies files
├── hook.sh # UserPromptSubmit + Stop handler
├── skill/
│ └── SKILL.md # estimate → record → reflect protocol
├── LICENSE # MIT
└── README.md
~/.wallclock/
├── hook.sh # copy of hook
└── log.ndjson # append-only log
~/.claude/skills/wallclock/
└── SKILL.md # loaded by Claude Code on startup
~/.claude/settings.json # two hook entries registered under .hooks.Stop and .hooks.UserPromptSubmit
┌───────────────────────────────┐
│ User gives Claude a task │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ UserPromptSubmit hook logs │
│ turn_start { ts, session_id } │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Skill has Claude read the log, │
│ output an estimate + log it │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Claude does the work │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Stop hook logs turn_end with │
│ actual token usage │
└───────────────────────────────┘
mv ~/.claude/settings.json.wallclock.bak ~/.claude/settings.json
rm -rf ~/.wallclock ~/.claude/skills/wallclockMIT — do whatever, attribution appreciated.
Provided as a public utility by Earth Pilot — Mission Support for Spaceship Earth.