Retries an auto-mode-blocked call up to three times so Claude Code's own permission prompt (and Moshi) can ask you.

Claude Code mods.
A sidebar that tracks what Claude said it would do and has not done yet. After each turn a small model audits Claude's reply: it adds new promises and closes the ones that were kept. Unfinished TodoWrite items show too.
Keys in the pane: a letter flags an entry, 1 sends the flagged ones back to Claude as "You left this undone: ...", 2 marks them done, 3 exports the list to ~/.claude/left-undone/latest.md, 4 imports it. /left-undone-export and /left-undone-import [path] do the same. The list is per session.
Install, typed at a Claude Code prompt:
/plugin install left-undone --marketplace <owner>/<repo>
Answer y to add the marketplace, then pick the user scope to have it in every session.
When auto mode's classifier blocks a tool call, this retries the identical call up to three times so Claude Code's own permission prompt appears after three blocks in a row. It retries calls the classifier denied, so read its source before installing. Whether the retries count toward Claude Code's threshold is not confirmed.
/plugin install approve-blocked --marketplace <owner>/<repo>hooks/register.ts 27 lines1import type { Register } from 'claude-code'
2
3const BLOCKED = /denied by the Claude Code auto mode classifier/i
4const TRIES = 3
5
6const blockedBy = (r: { deny?: string; isError?: boolean; text?: string }) => {
7 const why = r.deny ?? (r.isError === true ? r.text : undefined)
8 return typeof why === 'string' && BLOCKED.test(why)
9}
10
11export const register: Register = on => {
12 // Claude Code falls back to its own permission prompt after 3 blocks in a row,
13 // and that prompt is the one Moshi answers. Agents stop at the first block, so
14 // the same call is retried here until the third, which is the one that asks.
15 on('tool.call', async ($, e, next) => {
16 let ran = await next(e)
17 if (!blockedBy(ran) || e.tool === 'AskUserQuestion') return ran
18
19 const { tool, tool_use_id: _id, agentId: _agent, ...input } = e
20 for (let i = 1; i < TRIES && blockedBy(ran); i++) {
21 ran = await $.tool.call({ tool, ...input } as never)
22 }
23
24 return ran
25 })
26}
27