claude-auto-continue
A minimal Claude Code plugin that auto-continues an idle session by blocking the Stop event and re-prompting the model.
How it works
Stop hook fires when the model finishes a turn and would otherwise idle.
- If enabled, the hook returns
{"decision":"block","reason":"..."}, which Claude Code treats as "keep going" and injects the reason as the next instruction.
UserPromptSubmit resets the per-session counter so a real user message always breaks the loop.
- A hard cap (
CLAUDE_AUTO_CONTINUE_MAX, default 10) prevents runaway loops.
Install
# 1. Clone / already have it at /home/junzhouh/claude-auto-continue
# 2. Register the plugin with Claude Code:
claude plugin install /home/junzhouh/claude-auto-continue
# or add it to your ~/.claude/settings.json plugins list
If your Claude Code version doesn't have plugin install, symlink it into your user plugins dir:
mkdir -p ~/.claude/plugins
ln -s /home/junzhouh/claude-auto-continue ~/.claude/plugins/claude-auto-continue
Configure (per project)
Drop a file at <project-root>/.claude/auto-continue.json:
{
"enabled": true,
"max": 10,
"message": "Continue the task. If fully done, say DONE and stop."
}
The hook resolves the project root from the cwd field Claude Code passes in the hook payload (i.e. wherever you launched the session). A session started outside a project with this file is simply not auto-continued.
Env var overrides (optional, for quick experiments)
export CLAUDE_AUTO_CONTINUE=1 # or 0 to force-disable, even if json says enabled
export CLAUDE_AUTO_CONTINUE_MAX=20
export CLAUDE_AUTO_CONTINUE_MESSAGE='Keep iterating until the tests pass; say DONE when finished.'
Precedence: env var → project .claude/auto-continue.json → built-in default (disabled, max 10).
Files
claude-auto-continue/
├── .claude-plugin/plugin.json # plugin manifest
├── hooks/hooks.json # registers Stop / UserPromptSubmit / SessionStart
├── scripts/auto-continue.mjs # hook script (handles all three events via argv)
└── README.md
State lives under ~/.claude-auto-continue/ (not per-project — keyed by session id):
state.json — per-session counters
log.txt — hook activity log
Testing
1. Unit-test the hook script directly (no Claude Code needed)
Set up a fake project with a config file and simulate the Stop payload:
mkdir -p /tmp/ac-test/.claude
cat > /tmp/ac-test/.claude/auto-continue.json <<'EOF'
{ "enabled": true, "max": 3, "message": "Keep going; say DONE when finished." }
EOF
HOOK=/home/junzhouh/claude-auto-continue/scripts/auto-continue.mjs
payload() { printf '{"session_id":"test-sess","cwd":"/tmp/ac-test","stop_hook_active":false}'; }
# 4 calls — first 3 emit block, the 4th is silent (cap hit)
for i in 1 2 3 4; do
echo "--- call $i ---"
payload | node "$HOOK"
echo
done
# user prompt resets counter
printf '{"session_id":"test-sess","cwd":"/tmp/ac-test"}' \
| node "$HOOK" --user-prompt
cat ~/.claude-auto-continue/state.json # session entry should be gone
tail ~/.claude-auto-continue/log.txt
Expected: three {"decision":"block",...} lines, then silence. After --user-prompt, state.json no longer has the session.
Safety rails:
# No project config + no env var -> disabled, no output
printf '{"session_id":"t","cwd":"/tmp","stop_hook_active":false}' | node "$HOOK"
# Project config says enabled:false -> still disabled
echo '{"enabled":false}' > /tmp/ac-test/.claude/auto-continue.json
payload | node "$HOOK"
# Env override forces enabled even if config says false
echo '{"enabled":false}' > /tmp/ac-test/.claude/auto-continue.json
CLAUDE_AUTO_CONTINUE=1 payload | node "$HOOK"
# stop_hook_active=true is ignored by design — our max counter is the guard.
# This call should still emit {"decision":"block", ...}
echo '{"enabled":true}' > /tmp/ac-test/.claude/auto-continue.json
printf '{"session_id":"t","cwd":"/tmp/ac-test","stop_hook_active":true}' | node "$HOOK"
2. End-to-end test in a real Claude Code session