claude-atone
v0.1.5
Published
When Claude Code admits a mistake, a little guy does penance squats in a popup terminal — a macOS Stop hook
Maintainers
Readme
claude-atone 🙇
When Claude Code admits a mistake, a little guy holds his ears and does up-down squats as punishment in a popup terminal.
Unofficial, community-maintained — not affiliated with Anthropic.
macOS only. Pure Node + ANSI — no dependencies, no GIF, no Homebrew. The
animation is real pixel graphics painted as colored Unicode half-blocks (the
chafa technique, built in). When Claude's reply matches a "mistake" phrase, a
native dialog asks "Do you want me to atone? 🙏" — and only on Yes does the
animation play.
Install
npx claude-atone
npx claude-atone --test # pop the "atone?" dialog right now to verify itThat's it. The installer:
- Adds a
Stophook to~/.claude/settings.json(appending — any existing hooks, e.g.claude-nudge, are preserved). - Copies the runner (
on-stop.mjs) + animation (updown.mjs) to~/.claude/claude-atone/(a stable path that survivesnpmcache cleanup). - Snapshots your prior
settings.jsonto~/.claude/.claude-atone-backups/(mode0600, 5 most recent kept), then writes atomically (temp file +rename) so an interrupted write can never corrupt your config.
It takes effect on your next interaction with Claude Code.
Update
npx caches packages, so a bare npx claude-atone may run a stale version. Pin
@latest to force a fresh fetch and re-copy the runner:
npx claude-atone@latestThen npx claude-atone --doctor confirms it — it reports the installed runner's
version and warns if it's stale relative to the package.
Uninstall
npx claude-atone --uninstallRemoves the Stop hook and the runner directory. Settings backups are kept.
Commands
| Command | Purpose |
|---|---|
| npx claude-atone | Install the Stop hook + runner |
| npx claude-atone --test | Pop the "atone?" dialog now (bypasses the cooldown) |
| npx claude-atone --doctor | Diagnose install health (platform, settings.json, runner, version, osascript) |
| npx claude-atone --dry-run | Show proposed changes without writing anything |
| npx claude-atone --uninstall | Remove the hook and the runner directory |
| npx claude-atone --help | Show help |
| npx claude-atone --version | Print version |
How it works
Claude finishes a reply
└─► Stop hook runs on-stop.mjs
└─► reads Claude's last_assistant_message from the payload
└─► if it matches a "mistake" phrase
(you're right / my mistake / I apologize / sorry /
I was wrong / good catch / my bad / oops …)
└─► native dialog: "Do you want me to atone? 🙏" [No] [Yes]
├─ Yes → open a small Terminal window, play
│ updown.mjs --once (~5s), then auto-close
└─ No / ignore (30s timeout) → nothingWhy a popup window? Claude Code owns its own terminal screen (it's a TUI), so an animation can't be painted into it without clashing. A separate window is its own TTY, so it plays cleanly.
A 20-second cooldown keeps a burst of apologetic replies from stacking dialogs.
Opens as a tab instead of a window? That's the macOS "Prefer tabs when opening documents" setting (System Settings → Desktop & Dock) — Terminal gives AppleScript no way to override it. Set it to "Manually" for a clean popup window. (Either way it still plays and closes itself afterward.)
macOS permission
Opening that popup uses Apple Events to control Terminal, which macOS gates behind the Automation permission. The first time the hook fires, macOS asks "…wants to control Terminal" — click OK.
Run it up front so the first real one isn't missed:
npx claude-atone --testIf you dismissed the prompt and the animation never appears, re-enable it in System Settings → Privacy & Security → Automation. (If permission is denied the hook just does nothing — it never blocks or errors Claude Code.)
What it writes to settings.json
{
"theme": "...",
"permissions": { ... },
+ "hooks": {
+ "Stop": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "node /Users/<you>/.claude/claude-atone/on-stop.mjs"
+ }
+ ]
+ }
+ ]
+ }
}If you already have other Stop hooks, they are preserved — claude-atone
appends its own entry alongside them and removes only its own on uninstall.
Try the animation directly
node ~/.claude/claude-atone/updown.mjs # loops until Ctrl+C
node ~/.claude/claude-atone/updown.mjs --once # ~5s, then exitsTuning
| What | Where |
| ---- | ----- |
| Trigger phrases | TRIGGERS array in bin/on-stop.mjs |
| Cooldown window | COOLDOWN_MS in bin/on-stop.mjs (default 20s) |
| Figure size | W / H at the top of bin/updown.mjs |
| Squat depth | the hipY formula in drawFigure |
| Speed | setInterval(frame, 90) (frame period, ms) + the t * 0.5 ease factor |
| Run length (--once) | ONCE_TICKS in bin/updown.mjs (frames at 90ms; 55 ≈ 5s) |
Debug toggles (env vars): CLAUDE_ATONE_DRYRUN=1 prints what would play instead
of opening a window; CLAUDE_ATONE_DEBUG=1 logs to ~/.claude/claude-atone/run.log.
Security & privacy
- All processing is local. Zero network calls. Zero telemetry. Zero runtime dependencies. Zero npm lifecycle scripts.
- The hook reads Claude's last message (and, as a fallback, the tail of your transcript) only for case-insensitive substring matching — it is never transmitted anywhere, and never reaches an executed command.
osascriptis invoked withargv(no shell), so there's no AppleScript/shell injection from LLM text.- The animation runs only after you click "Yes".
settings.jsonis snapshotted and written atomically.
Full threat model and load-bearing invariants: SECURITY.md.
Verifying the publish
npm audit signatures claude-atoneThis package is published with npm provenance — attestations linking the tarball back to the GitHub build that produced it.
Requirements
- macOS (Darwin) — uses
osascriptto open the popup window - Node.js ≥ 18
- A 256-color terminal — every modern terminal qualifies, including the built-in Apple Terminal the popup opens in; 24-bit truecolor is used automatically where it's actually supported (iTerm2, VS Code)
- Claude Code CLI installed
Roadmap
- [ ]
--enable/--disabletoggle without full uninstall - [ ] Configurable trigger phrases via the
envblock - [ ] Linux/Windows window-spawning
Contributing
See CONTRIBUTING.md. Issues and PRs welcome at aashutosh-prakash/claude-atone.
Security
See SECURITY.md for disclosure policy and security properties.
Report vulnerabilities privately to [email protected].
License
MIT — see LICENSE.
Unofficial, community-maintained. Not affiliated with Anthropic.
