{"slug": "i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes", "title": "I used Claude Code's hooks to pause a terminal game the moment my agent finishes", "summary": "A developer built Agent Arcade, a set of terminal games that pause and display a banner the moment a coding agent finishes its turn or requests permission, using Claude Code hooks that write a single word to a signal file (~/.agent-arcade/signal) which the game polls via its modified timestamp. The developer notes that the PermissionRequest hook should be used instead of Notification for \"agent needs you\" alerts, since the permission-prompt notification only fires after roughly six seconds of inactivity, and the tool is now distributed as a Claude Code plugin that registers an immediate /arcade command without editing settings.json.", "body_md": "Claude Code and Codex often take a few minutes on a task. In that time I either stare at the spinner, or switch to another tab and forget to come back for ten minutes.\n\nSo I built **Agent Arcade**: tiny terminal games to play during the wait. The useful part isn't the game. It's that the game **pauses and tells you the moment your agent is done or needs you**, so the break never runs long.\n\nThis post is about how that works, because the same trick (an agent hook that writes a file, something else that watches it) is handy for a lot more than games.\n\nClaude Code can run a command at certain moments of a session. Three of those moments matter here:\n\n| Hook | Fires when | Agent Arcade says | \n|---|---|---|\n| `Stop` | Claude finishes its turn | \"done\" | \n| `PermissionRequest` | Claude shows a permission prompt | \"ask\" | \n| `Notification` | other \"waiting for you\" moments, like an idle prompt | \"ask\" | \n\nEach hook runs one tiny command, `agent-arcade notify` or `agent-arcade notify ask`. All it does is write a word to a file:\n\n```\n// bin/agent-arcade.mjs\nelse if (cmd === 'notify') {\n  fs.mkdirSync(DIR, { recursive: true });\n  fs.writeFileSync(SIGNAL, arg === 'ask' ? 'ask\\n' : 'done\\n'); // ~/.agent-arcade/signal\n}\n```\n\nNo server, no socket, no daemon. The game, running in another terminal, checks that file's modified time on every tick. A new time means a new event:\n\n``` js\n// src/shell.mjs\nconst stamp = () => { try { return fs.statSync(SIGNAL).mtimeMs; } catch { return 0; } };\nlet seen = stamp();\n\nfunction checkAgent() {\n  const t = stamp();\n  if (t === seen) return;          // nothing new\n  seen = t;\n  let kind = 'done';\n  try { kind = fs.readFileSync(SIGNAL, 'utf8').trim() === 'ask' ? 'ask' : 'done'; } catch {}\n  agent = kind;                    // shows the banner and pauses the game\n  out.write('\\x07');               // terminal bell\n}\n```\n\nUsing the file's modified time instead of its contents means two \"done\" events in a row still count as two events, and a game started after an event doesn't react to an old one.\n\nThe same file works for Codex, whose `notify` setting runs a program after each turn. Any agent that can run a command when it finishes can use it.\n\nMy first version used `Notification` for \"Claude needs you\". On my Show HN post, a commenter pointed out something I'd missed in the docs: the permission-prompt notification only fires **after you haven't typed for about six seconds**. Mid-game, you're pressing keys constantly, so the \"Claude is waiting\" banner could arrive late.\n\n`PermissionRequest` fires **as soon as the prompt appears**. The fix was one line in the setup, and the plugin now listens to it directly. The hook never answers the prompt: it only writes \"ask\", and you approve or deny as usual.\n\nIf you write hooks for \"the agent needs me\" alerts, use `PermissionRequest`, not `Notification`.\n\nTwo small things make the pause feel right:\n\n`\\x1b[?1004h`). When you click away, the terminal sends `\\x1b[O` and the game pauses. When you come back, it sends `\\x1b[I`.\nKeys typed in the first moment after a banner appears are ignored, because they were meant for the game, not the banner.\n\nThis was the fiddly part. `/arcade` tries, in order:\n\n`tmux display-popup -E -w 70% -h 70% agent-arcade`, a floating window over your session` agent-arcade`\nAt first, setup was `npm install -g agent-arcade` and then `agent-arcade setup`, which edits `~/.claude/settings.json`. That's two steps and a settings file edit.\n\nNow it's a Claude Code plugin, installed from the GitHub repo, which doubles as a plugin marketplace:\n\n```\n/plugin install agent-arcade --marketplace Kuldeep-045/agent-arcade\n```\n\nThe plugin registers `/arcade` as an immediate command, so it runs without a model turn. It opens the game instantly, even while Claude is in the middle of a task, and doesn't add anything to the conversation. It also listens to the same hook events, so there's no settings file to edit:\n\n``` js\non('classic.Stop', async ($, e, next) => {\n  arcade($, ['notify'], 5000).catch(() => {})       // fire and forget\n  return next(e)                                    // never change what Claude does\n})\n\non('classic.PermissionRequest', async ($, e, next) => {\n  arcade($, ['notify', 'ask'], 5000).catch(() => {})\n  return next(e)\n})\n```\n\nThe repo only needs two extra files to be installable: the plugin's manifest and a `.claude-plugin/marketplace.json` listing it. If `agent-arcade` isn't installed globally, the plugin falls back to `npx`.\n\nEach game is a plain object: `start`, `tick`, `key`, `draw`, `score`. The shell handles the screen, input, pausing, banners and best scores. Snake is about 60 lines. If you have a favourite game for a two-minute wait, I'd love a pull request.\n\n`/plugin install agent-arcade --marketplace Kuldeep-045/agent-arcade`, then `/arcade`\n`npm install -g agent-arcade`, then `agent-arcade setup`\nCode (MIT, no dependencies, no tracking): [https://github.com/Kuldeep-045/agent-arcade](https://github.com/Kuldeep-045/agent-arcade)\n\nWhat game would you want next?", "url": "https://wpnews.pro/news/i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes", "canonical_source": "https://dev.to/kuldeepmahour/i-used-claude-codes-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes-53k2", "published_at": "2026-11-01 10:18:13+00:00", "updated_at": "2026-11-01 10:25:06.722400+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Claude Code", "Agent Arcade", "Codex", "Anthropic", "Kuldeep-045", "Hacker News"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes", "markdown": "https://wpnews.pro/news/i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes.md", "text": "https://wpnews.pro/news/i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes.txt", "jsonld": "https://wpnews.pro/news/i-used-claude-code-s-hooks-to-pause-a-terminal-game-the-moment-my-agent-finishes.jsonld"}}