cc-session-recover 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +20 -136
- package/bin/cli.js +50 -14
- package/package.json +1 -3
- package/scripts/install-into-project.sh +36 -22
- package/scripts/test-fake-quota-flow.sh +3 -1
- package/scripts/verify-claude-loop-workflow.sh +4 -17
- package/.claude/loop.md +0 -25
- package/docs/claude-code-auto-resume.md +0 -242
- package/docs/faq.md +0 -37
- package/docs/simple-flow.md +0 -43
- package/docs/verified-quota-resume-example.md +0 -57
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3 — 2026-06-12
|
|
4
|
+
|
|
5
|
+
- Install only the runtime `.claude` files and `HANDOFF.md` into target projects; package tooling now stays in the npm package.
|
|
6
|
+
- Enable hooks by default, with `--no-hooks` as the opt-out flag.
|
|
7
|
+
- Add `cc-session-recover watch` for running the closed-terminal watcher through `npx`.
|
|
8
|
+
- Remove the old one-time reminder flow and its `loop.md` prompt from the packaged workflow.
|
|
9
|
+
|
|
10
|
+
## 0.1.2 — 2026-06-12
|
|
11
|
+
|
|
12
|
+
- Stop installing the package docs into target projects.
|
|
13
|
+
- Keep runtime files out of target git history by appending the session-recovery ignore block.
|
|
14
|
+
- Remove the fake-quota try-it flow from the README.
|
|
15
|
+
|
|
3
16
|
## 0.1.0 — 2026-06-12
|
|
4
17
|
|
|
5
18
|
Initial release of the quota-resume template.
|
package/README.md
CHANGED
|
@@ -1,163 +1,47 @@
|
|
|
1
1
|
# cc-session-recover
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your long Claude Code task survives quota stops — and continues by itself after the reset, with no prompt from you.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
When quota or a rate limit kills a session mid-task, Claude normally just stops until you come back and tell it to continue. This workflow removes that. Claude keeps a recovery note (`HANDOFF.md`) as it works, retries on a slow schedule while quota is blocked, and the first attempt after the reset picks the task up exactly where it stopped.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- It has two recovery paths. With the terminal open, the heartbeat resumes inside the active Claude Code session. With the terminal closed, the watcher can resume the saved session id.
|
|
10
|
-
- Claude Code hooks record the quota stop. The watcher uses `claude -p --resume` only for closed-terminal recovery, so it does not depend on terminal text.
|
|
11
|
-
- `HANDOFF.md` keeps the next step in the repo. A resume has project state, recent progress, and the exact next action.
|
|
12
|
-
- The heartbeat runs inside the active Claude Code session. It keeps the original context alive while quota is blocked.
|
|
13
|
-
- The watcher covers closed terminals. It reads the quota marker, waits for the reset time when available, and resumes the saved session id.
|
|
14
|
-
- The status line cache reduces noisy retries. When Claude Code exposes a reset time, the watcher sleeps until that time plus a buffer.
|
|
15
|
-
- The fake quota test checks the full path: install, hook injection, quota marker, retry loop, exact-session resume, and marker cleanup.
|
|
16
|
-
|
|
17
|
-
## What You Get
|
|
18
|
-
|
|
19
|
-
- `HANDOFF.md`: the recovery note for the current task.
|
|
20
|
-
- `.claude/auto-continue.md`: the prompt used by the recurring heartbeat.
|
|
21
|
-
- `.claude/loop.md`: a one-step resume prompt for a scheduled one-time reminder.
|
|
22
|
-
- A `SessionStart` hook that injects the standing instructions at the start of each Claude Code session.
|
|
23
|
-
- A `StopFailure` hook that records quota stops and writes `.claude/quota-blocked.json`.
|
|
24
|
-
- `scripts/quota-watcher.sh`: an optional watcher that uses the marker file to resume the recorded session with `claude -p --resume`.
|
|
25
|
-
- `scripts/test-fake-quota-flow.sh`: an end-to-end test that proves the install, hooks, marker, and watcher without using real quota.
|
|
7
|
+
Every part of this has been verified against a genuine quota stop, end to end.
|
|
26
8
|
|
|
27
9
|
## Install
|
|
28
10
|
|
|
29
|
-
Use `npx` from any project:
|
|
30
|
-
|
|
31
11
|
```sh
|
|
32
12
|
npx cc-session-recover init /path/to/project
|
|
33
13
|
```
|
|
34
14
|
|
|
35
|
-
|
|
15
|
+
(Or from a clone: `bash scripts/install-into-project.sh /path/to/project`. Pass `--no-hooks` with either to install the files without activating anything.)
|
|
36
16
|
|
|
37
|
-
|
|
38
|
-
npx cc-session-recover init --enable-local-hook /path/to/project
|
|
39
|
-
```
|
|
17
|
+
Approve the hooks once when Claude Code asks on the next start. That's the whole setup.
|
|
40
18
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
cd /path/to/cc-session-recover
|
|
45
|
-
bash scripts/install-into-project.sh --enable-local-hook /path/to/project
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Claude Code may ask you to approve the hooks the next time you start `claude` in that project. Approve them once.
|
|
49
|
-
|
|
50
|
-
## Use It
|
|
51
|
-
|
|
52
|
-
Start Claude Code in the target project:
|
|
19
|
+
## Use
|
|
53
20
|
|
|
54
21
|
```sh
|
|
55
22
|
cd /path/to/project
|
|
56
23
|
claude
|
|
57
24
|
```
|
|
58
25
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
With hooks enabled, the `SessionStart` hook prints `.claude/standing-instructions.md` into Claude's context. That tells Claude to:
|
|
62
|
-
|
|
63
|
-
1. Keep `HANDOFF.md` current after each small step.
|
|
64
|
-
2. Create a recurring schedule every 45 minutes.
|
|
65
|
-
3. Use this scheduled prompt: `Read .claude/auto-continue.md and follow it.`
|
|
66
|
-
4. Cancel the schedule when the task is complete.
|
|
67
|
-
|
|
68
|
-
If quota blocks the session, each heartbeat fails while quota remains blocked. The first heartbeat after the reset reads `HANDOFF.md` and continues from the recorded next step.
|
|
69
|
-
|
|
70
|
-
The terminal must stay open for this heartbeat. The schedule lives inside the running Claude Code session.
|
|
71
|
-
|
|
72
|
-
## Use Without Hooks
|
|
73
|
-
|
|
74
|
-
If you install without hooks, include this with your task:
|
|
75
|
-
|
|
76
|
-
```text
|
|
77
|
-
Keep HANDOFF.md updated after every small safe step.
|
|
78
|
-
|
|
79
|
-
Also create a recurring schedule every 45 minutes with this prompt:
|
|
80
|
-
|
|
81
|
-
Read .claude/auto-continue.md and follow it.
|
|
82
|
-
|
|
83
|
-
Cancel that schedule when the task is complete.
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## One-Time Resume
|
|
87
|
-
|
|
88
|
-
Use this when you know the quota reset time and want one future resume.
|
|
89
|
-
|
|
90
|
-
Look at the reset time in the Claude Code status line. Add 10 to 15 minutes. Then ask Claude Code to schedule one reminder.
|
|
91
|
-
|
|
92
|
-
Example:
|
|
93
|
-
|
|
94
|
-
```text
|
|
95
|
-
Update HANDOFF.md now.
|
|
96
|
-
|
|
97
|
-
Then set a one-time reminder for 02:25 local time with this prompt:
|
|
98
|
-
|
|
99
|
-
Read HANDOFF.md first. If the task is incomplete, run git status --short, inspect the current diff only enough to understand the working tree, continue exactly one small safe step from Next Exact Action, run the narrowest relevant check, update HANDOFF.md, and stop.
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Pick a time like `02:25`, rather than exactly `02:00` or `02:30`.
|
|
103
|
-
|
|
104
|
-
Use `/loop` for short polling jobs, such as checking CI. Avoid `/loop 1m` for overnight quota recovery.
|
|
105
|
-
|
|
106
|
-
## Watcher For Closed Terminals
|
|
107
|
-
|
|
108
|
-
The heartbeat dies when the terminal closes. If you need unattended recovery after that, enable the local hook and run the watcher from another shell:
|
|
109
|
-
|
|
110
|
-
```sh
|
|
111
|
-
bash scripts/quota-watcher.sh /path/to/project
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
The watcher needs `jq` and the `claude` CLI on `PATH`.
|
|
115
|
-
|
|
116
|
-
When quota stops a turn, the hook writes `.claude/quota-blocked.json`. The watcher reads the saved `session_id` and runs:
|
|
117
|
-
|
|
118
|
-
```sh
|
|
119
|
-
claude -p --resume <session_id> --permission-mode acceptEdits "<contents of .claude/auto-continue.md>"
|
|
120
|
-
```
|
|
26
|
+
Give Claude your task, normally. Nothing extra to type — the injected standing instructions make Claude keep the recovery note and set its own retry schedule. Leave the terminal open and walk away.
|
|
121
27
|
|
|
122
|
-
If
|
|
28
|
+
If quota dies mid-task, work resumes automatically after the reset.
|
|
123
29
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
## Status Line Cache
|
|
127
|
-
|
|
128
|
-
`.claude/statusline-quota-cache.sh` saves Claude Code rate-limit fields into `.claude/rate-limit-state.json`.
|
|
129
|
-
|
|
130
|
-
The `StopFailure` hook copies that reset time into `.claude/quota-blocked.json`. The watcher uses it to wait for the reset instead of retrying blind.
|
|
131
|
-
|
|
132
|
-
To keep your existing status line, set `CLAUDE_QUOTA_STATUSLINE_DELEGATE` to your current status line command. The wrapper passes the display through after it caches the quota data.
|
|
133
|
-
|
|
134
|
-
## Test The Workflow
|
|
135
|
-
|
|
136
|
-
Run the static project check:
|
|
137
|
-
|
|
138
|
-
```sh
|
|
139
|
-
bash scripts/verify-claude-loop-workflow.sh
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Run the fake quota flow:
|
|
143
|
-
|
|
144
|
-
```sh
|
|
145
|
-
bash scripts/test-fake-quota-flow.sh
|
|
146
|
-
```
|
|
30
|
+
## Why This Approach Is Stronger
|
|
147
31
|
|
|
148
|
-
|
|
32
|
+
- It avoids `tmux`, `screen`, and terminal-injection hacks. Headless `claude -p --resume` is used only by the optional closed-terminal watcher, targeting the exact recorded session.
|
|
33
|
+
- It gives you two recovery paths. With the terminal open, the heartbeat resumes inside the active Claude Code session. With the terminal closed, the watcher resumes the saved session id.
|
|
34
|
+
- `HANDOFF.md` keeps the next step in the repo, with project state, recent progress, and the exact next action.
|
|
35
|
+
- The heartbeat runs inside the active Claude Code session, so the original context stays alive while quota is blocked.
|
|
149
36
|
|
|
150
37
|
## Limits
|
|
151
38
|
|
|
152
|
-
-
|
|
153
|
-
- The
|
|
154
|
-
-
|
|
155
|
-
- Headless resume cannot ask for permission. Review `--permission-mode acceptEdits` before you use it.
|
|
156
|
-
- Scheduled tasks require Claude Code v2.1.72 or newer.
|
|
39
|
+
- It does not bypass quota. It only waits for the reset.
|
|
40
|
+
- The basic flow needs the terminal to stay open. A closed-terminal recovery mode exists; see the docs.
|
|
41
|
+
- Worst case is never lost work: the recovery note is always on disk, and "Read HANDOFF.md and continue" restores any session by hand.
|
|
157
42
|
|
|
158
|
-
##
|
|
43
|
+
## Docs
|
|
159
44
|
|
|
160
|
-
- [Simple flow](docs/simple-flow.md)
|
|
161
|
-
- [FAQ](docs/faq.md)
|
|
162
|
-
- [
|
|
163
|
-
- [Verified quota resume example](docs/verified-quota-resume-example.md): a concrete one-time reminder example.
|
|
45
|
+
- [Simple flow](docs/simple-flow.md) — how it works, told as a story (notebook, alarm, watchman).
|
|
46
|
+
- [FAQ](docs/faq.md) — reliability, hook approval, what still needs a human.
|
|
47
|
+
- [Full details](docs/claude-code-auto-resume.md) — closed-terminal watcher, precise reset-time resume, all limits.
|
package/bin/cli.js
CHANGED
|
@@ -5,45 +5,66 @@
|
|
|
5
5
|
// Pure Node so it also works where bash is absent; the installed runtime
|
|
6
6
|
// scripts themselves are bash and need a POSIX shell (macOS, Linux, WSL,
|
|
7
7
|
// Git Bash).
|
|
8
|
+
//
|
|
9
|
+
// Only files Claude Code must execute from inside the project are installed
|
|
10
|
+
// (the .claude prompts, hooks, and status line wrapper). Tooling like the
|
|
11
|
+
// watcher stays in this package and runs via `npx cc-session-recover watch`.
|
|
8
12
|
|
|
9
13
|
const fs = require('fs');
|
|
10
14
|
const path = require('path');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
11
16
|
|
|
12
17
|
const TEMPLATE_ROOT = path.join(__dirname, '..');
|
|
13
18
|
|
|
14
19
|
const FILES = [
|
|
15
|
-
'.claude/loop.md',
|
|
16
20
|
'.claude/auto-continue.md',
|
|
17
21
|
'.claude/standing-instructions.md',
|
|
18
22
|
'.claude/settings.example.json',
|
|
19
23
|
'.claude/statusline-quota-cache.sh',
|
|
20
24
|
'.claude/hooks/log-stop-failure.sh',
|
|
21
25
|
'.claude/hooks/inject-standing-instructions.sh',
|
|
22
|
-
'docs/claude-code-auto-resume.md',
|
|
23
|
-
'docs/verified-quota-resume-example.md',
|
|
24
|
-
'docs/simple-flow.md',
|
|
25
|
-
'docs/faq.md',
|
|
26
|
-
'scripts/install-into-project.sh',
|
|
27
|
-
'scripts/verify-claude-loop-workflow.sh',
|
|
28
|
-
'scripts/quota-watcher.sh',
|
|
29
|
-
'scripts/test-fake-quota-flow.sh',
|
|
30
26
|
];
|
|
31
27
|
|
|
32
|
-
const COPY_IF_MISSING = ['HANDOFF.md'
|
|
28
|
+
const COPY_IF_MISSING = ['HANDOFF.md'];
|
|
29
|
+
|
|
30
|
+
// HANDOFF.md must stay editable by unattended Claude runs, so it cannot live
|
|
31
|
+
// in .claude/ (Claude Code blocks edits there). Ignoring it in git gives the
|
|
32
|
+
// same "never committed" result without breaking recovery.
|
|
33
|
+
const IGNORE_ENTRIES = [
|
|
34
|
+
'HANDOFF.md',
|
|
35
|
+
'.claude/settings.local.json',
|
|
36
|
+
'.claude/rate-limit-state.json',
|
|
37
|
+
'.claude/stop-failure-events.jsonl',
|
|
38
|
+
'.claude/quota-blocked.json',
|
|
39
|
+
];
|
|
40
|
+
const IGNORE_HEADER = '# Claude Code session-recovery runtime state';
|
|
33
41
|
|
|
34
42
|
function usage() {
|
|
35
|
-
console.error('Usage: cc-session-recover init [--
|
|
43
|
+
console.error('Usage: cc-session-recover init [--no-hooks] [target-dir]');
|
|
44
|
+
console.error(' cc-session-recover watch [target-dir]');
|
|
45
|
+
console.error('Hooks are enabled by default; Claude Code still asks you to approve them once.');
|
|
36
46
|
process.exit(2);
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
function watch(target) {
|
|
50
|
+
const script = path.join(TEMPLATE_ROOT, 'scripts', 'quota-watcher.sh');
|
|
51
|
+
const child = spawn('bash', [script, target], { stdio: 'inherit' });
|
|
52
|
+
child.on('exit', (code) => process.exit(code === null ? 1 : code));
|
|
53
|
+
child.on('error', (err) => {
|
|
54
|
+
console.error(`Could not start watcher: ${err.message}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
39
59
|
function main() {
|
|
40
60
|
const args = process.argv.slice(2);
|
|
41
|
-
if (args[0] !== 'init') usage();
|
|
61
|
+
if (args[0] !== 'init' && args[0] !== 'watch') usage();
|
|
42
62
|
|
|
43
|
-
let enableHook =
|
|
63
|
+
let enableHook = true;
|
|
44
64
|
let target = '.';
|
|
45
65
|
for (const arg of args.slice(1)) {
|
|
46
|
-
if (arg === '--
|
|
66
|
+
if (arg === '--no-hooks') enableHook = false;
|
|
67
|
+
else if (arg === '--enable-local-hook') enableHook = true; // legacy no-op, was the old default-off flag
|
|
47
68
|
else if (arg.startsWith('-')) usage();
|
|
48
69
|
else target = arg;
|
|
49
70
|
}
|
|
@@ -54,6 +75,11 @@ function main() {
|
|
|
54
75
|
process.exit(1);
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
if (args[0] === 'watch') {
|
|
79
|
+
watch(target);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
for (const rel of FILES) {
|
|
58
84
|
const src = path.join(TEMPLATE_ROOT, rel);
|
|
59
85
|
const dest = path.join(target, rel);
|
|
@@ -71,6 +97,16 @@ function main() {
|
|
|
71
97
|
}
|
|
72
98
|
}
|
|
73
99
|
|
|
100
|
+
const giPath = path.join(target, '.gitignore');
|
|
101
|
+
const existing = fs.existsSync(giPath) ? fs.readFileSync(giPath, 'utf8') : '';
|
|
102
|
+
const lines = existing.split(/\r?\n/);
|
|
103
|
+
const missing = IGNORE_ENTRIES.filter((e) => !lines.includes(e));
|
|
104
|
+
if (missing.length) {
|
|
105
|
+
const lead = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
106
|
+
const header = lines.includes(IGNORE_HEADER) ? '' : `${IGNORE_HEADER}\n`;
|
|
107
|
+
fs.appendFileSync(giPath, `${lead}${header}${missing.join('\n')}\n`);
|
|
108
|
+
}
|
|
109
|
+
|
|
74
110
|
if (enableHook) {
|
|
75
111
|
const local = path.join(target, '.claude', 'settings.local.json');
|
|
76
112
|
if (fs.existsSync(local)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-session-recover",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Quota-resume workflow for Claude Code: handoff notebook, heartbeat auto-continue, StopFailure hooks, and an unattended watcher that resumes after quota resets.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"check": "node --check bin/cli.js && bash -n scripts/*.sh .claude/hooks/*.sh .claude/statusline-quota-cache.sh && bash scripts/verify-claude-loop-workflow.sh",
|
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"bin",
|
|
15
|
-
".claude/loop.md",
|
|
16
15
|
".claude/auto-continue.md",
|
|
17
16
|
".claude/standing-instructions.md",
|
|
18
17
|
".claude/settings.example.json",
|
|
19
18
|
".claude/statusline-quota-cache.sh",
|
|
20
19
|
".claude/hooks",
|
|
21
|
-
"docs",
|
|
22
20
|
"scripts",
|
|
23
21
|
"HANDOFF.md",
|
|
24
22
|
"CHANGELOG.md",
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
set -eu
|
|
4
4
|
|
|
5
5
|
usage() {
|
|
6
|
-
printf 'Usage: %s [--
|
|
6
|
+
printf 'Usage: %s [--no-hooks] /path/to/project\n' "$0" >&2
|
|
7
|
+
printf 'Hooks are enabled by default; Claude Code still asks you to approve them once.\n' >&2
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
ENABLE_LOCAL_HOOK=
|
|
10
|
+
ENABLE_LOCAL_HOOK=1
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
ENABLE_LOCAL_HOOK=
|
|
13
|
-
shift
|
|
14
|
-
|
|
12
|
+
case "${1:-}" in
|
|
13
|
+
--no-hooks) ENABLE_LOCAL_HOOK=0; shift ;;
|
|
14
|
+
--enable-local-hook) shift ;; # legacy no-op, was the old default-off flag
|
|
15
|
+
esac
|
|
15
16
|
|
|
16
17
|
if [ "$#" -ne 1 ]; then
|
|
17
18
|
usage
|
|
@@ -26,25 +27,14 @@ TEMPLATE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
|
|
26
27
|
exit 1
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
mkdir -p "$TARGET/.claude/hooks"
|
|
30
|
+
mkdir -p "$TARGET/.claude/hooks"
|
|
30
31
|
|
|
31
|
-
if [ ! -f "$TARGET/README.md" ]; then
|
|
32
|
-
cp "$TEMPLATE_ROOT/README.md" "$TARGET/README.md"
|
|
33
|
-
fi
|
|
34
|
-
cp "$TEMPLATE_ROOT/.claude/loop.md" "$TARGET/.claude/loop.md"
|
|
35
32
|
cp "$TEMPLATE_ROOT/.claude/auto-continue.md" "$TARGET/.claude/auto-continue.md"
|
|
36
33
|
cp "$TEMPLATE_ROOT/.claude/standing-instructions.md" "$TARGET/.claude/standing-instructions.md"
|
|
37
34
|
cp "$TEMPLATE_ROOT/.claude/statusline-quota-cache.sh" "$TARGET/.claude/statusline-quota-cache.sh"
|
|
38
35
|
cp "$TEMPLATE_ROOT/.claude/hooks/inject-standing-instructions.sh" "$TARGET/.claude/hooks/inject-standing-instructions.sh"
|
|
39
36
|
cp "$TEMPLATE_ROOT/.claude/settings.example.json" "$TARGET/.claude/settings.example.json"
|
|
40
37
|
cp "$TEMPLATE_ROOT/.claude/hooks/log-stop-failure.sh" "$TARGET/.claude/hooks/log-stop-failure.sh"
|
|
41
|
-
cp "$TEMPLATE_ROOT/docs/claude-code-auto-resume.md" "$TARGET/docs/claude-code-auto-resume.md"
|
|
42
|
-
cp "$TEMPLATE_ROOT/docs/verified-quota-resume-example.md" "$TARGET/docs/verified-quota-resume-example.md"
|
|
43
|
-
cp "$TEMPLATE_ROOT/scripts/install-into-project.sh" "$TARGET/scripts/install-into-project.sh"
|
|
44
|
-
cp "$TEMPLATE_ROOT/scripts/verify-claude-loop-workflow.sh" "$TARGET/scripts/verify-claude-loop-workflow.sh"
|
|
45
|
-
cp "$TEMPLATE_ROOT/scripts/quota-watcher.sh" "$TARGET/scripts/quota-watcher.sh"
|
|
46
|
-
cp "$TEMPLATE_ROOT/scripts/test-fake-quota-flow.sh" "$TARGET/scripts/test-fake-quota-flow.sh"
|
|
47
|
-
|
|
48
38
|
if [ ! -f "$TARGET/HANDOFF.md" ]; then
|
|
49
39
|
cp "$TEMPLATE_ROOT/HANDOFF.md" "$TARGET/HANDOFF.md"
|
|
50
40
|
fi
|
|
@@ -52,10 +42,34 @@ fi
|
|
|
52
42
|
chmod +x "$TARGET/.claude/hooks/log-stop-failure.sh"
|
|
53
43
|
chmod +x "$TARGET/.claude/hooks/inject-standing-instructions.sh"
|
|
54
44
|
chmod +x "$TARGET/.claude/statusline-quota-cache.sh"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
|
|
46
|
+
# Keep runtime state out of the target's git history. HANDOFF.md must stay in
|
|
47
|
+
# the project root (Claude Code blocks unattended edits inside .claude/), so
|
|
48
|
+
# ignoring it is how it stays uncommitted.
|
|
49
|
+
GITIGNORE="$TARGET/.gitignore"
|
|
50
|
+
GITIGNORE_HEADER='# Claude Code session-recovery runtime state'
|
|
51
|
+
NEEDS_HEADER=1
|
|
52
|
+
APPENDED_GITIGNORE=0
|
|
53
|
+
touch "$GITIGNORE"
|
|
54
|
+
if grep -qxF "$GITIGNORE_HEADER" "$GITIGNORE"; then
|
|
55
|
+
NEEDS_HEADER=0
|
|
56
|
+
fi
|
|
57
|
+
append_gitignore_line() {
|
|
58
|
+
if [ "$APPENDED_GITIGNORE" -eq 0 ] && [ -s "$GITIGNORE" ] && [ "$(tail -c 1 "$GITIGNORE")" != "" ]; then
|
|
59
|
+
printf '\n' >> "$GITIGNORE"
|
|
60
|
+
fi
|
|
61
|
+
APPENDED_GITIGNORE=1
|
|
62
|
+
printf '%s\n' "$1" >> "$GITIGNORE"
|
|
63
|
+
}
|
|
64
|
+
for entry in HANDOFF.md .claude/settings.local.json .claude/rate-limit-state.json .claude/stop-failure-events.jsonl .claude/quota-blocked.json; do
|
|
65
|
+
if ! grep -qxF "$entry" "$GITIGNORE"; then
|
|
66
|
+
if [ "$NEEDS_HEADER" -eq 1 ]; then
|
|
67
|
+
append_gitignore_line "$GITIGNORE_HEADER"
|
|
68
|
+
NEEDS_HEADER=0
|
|
69
|
+
fi
|
|
70
|
+
append_gitignore_line "$entry"
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
59
73
|
|
|
60
74
|
if [ "$ENABLE_LOCAL_HOOK" -eq 1 ]; then
|
|
61
75
|
if [ -f "$TARGET/.claude/settings.local.json" ]; then
|
|
@@ -42,6 +42,8 @@ mkdir -p "$DUMMY"
|
|
|
42
42
|
git -C "$DUMMY" init -q
|
|
43
43
|
bash "$TEMPLATE_ROOT/scripts/install-into-project.sh" --enable-local-hook "$DUMMY" >/dev/null
|
|
44
44
|
[ -f "$DUMMY/.claude/settings.local.json" ] || fail "hook settings not installed"
|
|
45
|
+
[ ! -d "$DUMMY/scripts" ] || fail "install must not create a scripts folder in the target"
|
|
46
|
+
[ ! -d "$DUMMY/docs" ] || fail "install must not create a docs folder in the target"
|
|
45
47
|
|
|
46
48
|
step "Fake SessionStart: standing instructions should be injected"
|
|
47
49
|
INJECTED=$(printf '{"session_id":"fake-session-123","hook_event_name":"SessionStart","source":"startup"}' \
|
|
@@ -89,7 +91,7 @@ chmod +x "$BIN/claude"
|
|
|
89
91
|
|
|
90
92
|
step "Run the watcher against the fake CLI"
|
|
91
93
|
QUOTA_WATCH_INTERVAL=1 QUOTA_RESUME_BUFFER=1 PATH="$BIN:$PATH" \
|
|
92
|
-
bash "$
|
|
94
|
+
bash "$TEMPLATE_ROOT/scripts/quota-watcher.sh" "$DUMMY" >"$WORK/watcher.log" 2>&1 &
|
|
93
95
|
WATCHER_PID=$!
|
|
94
96
|
disown "$WATCHER_PID" 2>/dev/null || true
|
|
95
97
|
|
|
@@ -20,7 +20,6 @@ require_text() {
|
|
|
20
20
|
grep -Fqi -- "$text" "$ROOT/$file" || fail "$file must mention: $text"
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
require_file ".claude/loop.md"
|
|
24
23
|
require_file ".claude/auto-continue.md"
|
|
25
24
|
require_file "HANDOFF.md"
|
|
26
25
|
require_file ".claude/settings.example.json"
|
|
@@ -30,26 +29,20 @@ require_file ".claude/standing-instructions.md"
|
|
|
30
29
|
require_file "scripts/test-fake-quota-flow.sh"
|
|
31
30
|
require_file "README.md"
|
|
32
31
|
require_file "docs/claude-code-auto-resume.md"
|
|
33
|
-
require_file "docs/
|
|
32
|
+
require_file "docs/simple-flow.md"
|
|
33
|
+
require_file "docs/faq.md"
|
|
34
34
|
require_file "scripts/install-into-project.sh"
|
|
35
35
|
require_file "scripts/quota-watcher.sh"
|
|
36
36
|
require_file "package.json"
|
|
37
37
|
require_file "bin/cli.js"
|
|
38
38
|
require_file "LICENSE"
|
|
39
39
|
|
|
40
|
-
require_text ".claude/loop.md" "HANDOFF.md"
|
|
41
|
-
require_text ".claude/loop.md" "git status --short"
|
|
42
|
-
require_text ".claude/loop.md" "one small safe step"
|
|
43
|
-
require_text ".claude/loop.md" "update HANDOFF.md"
|
|
44
|
-
require_text ".claude/loop.md" "stop"
|
|
45
|
-
|
|
46
40
|
require_text ".claude/auto-continue.md" "HANDOFF.md"
|
|
47
41
|
require_text ".claude/auto-continue.md" "git status --short"
|
|
48
42
|
require_text ".claude/auto-continue.md" "remaining checklist"
|
|
49
43
|
require_text ".claude/auto-continue.md" "safe to fire at any time"
|
|
50
44
|
require_text ".claude/auto-continue.md" "Cancel this recurring schedule"
|
|
51
45
|
|
|
52
|
-
require_text "docs/claude-code-auto-resume.md" "one-time reminder"
|
|
53
46
|
require_text "docs/claude-code-auto-resume.md" "auto-continue.md"
|
|
54
47
|
require_text "docs/claude-code-auto-resume.md" "heartbeat"
|
|
55
48
|
require_text "docs/claude-code-auto-resume.md" "claude"
|
|
@@ -61,14 +54,8 @@ require_text "docs/claude-code-auto-resume.md" "StopFailure"
|
|
|
61
54
|
require_text "docs/claude-code-auto-resume.md" "Do not use"
|
|
62
55
|
require_text "docs/claude-code-auto-resume.md" "/loop 1m"
|
|
63
56
|
|
|
64
|
-
require_text "docs/verified-quota-resume-example.md" "02:25"
|
|
65
|
-
require_text "docs/verified-quota-resume-example.md" "one-time reminder"
|
|
66
|
-
require_text "docs/verified-quota-resume-example.md" "git status --short"
|
|
67
|
-
require_text "docs/verified-quota-resume-example.md" "Do not use"
|
|
68
|
-
require_text "docs/verified-quota-resume-example.md" "/loop 1m"
|
|
69
|
-
|
|
70
57
|
require_text "README.md" "install-into-project.sh"
|
|
71
|
-
require_text "README.md" "
|
|
58
|
+
require_text "README.md" "npx cc-session-recover init"
|
|
72
59
|
require_text "README.md" "not bypass quota"
|
|
73
60
|
|
|
74
61
|
require_text ".claude/settings.example.json" "SessionStart"
|
|
@@ -94,7 +81,7 @@ require_text "scripts/quota-watcher.sh" "session_id"
|
|
|
94
81
|
[ -x "$ROOT/scripts/test-fake-quota-flow.sh" ] || fail "scripts/test-fake-quota-flow.sh must be executable"
|
|
95
82
|
|
|
96
83
|
for forbidden in "claude -p" "tmux" "screen" "expect" "TIOCSTI"; do
|
|
97
|
-
if grep -Fqi -- "$forbidden" "$ROOT/.claude/
|
|
84
|
+
if grep -Fqi -- "$forbidden" "$ROOT/.claude/auto-continue.md" "$ROOT/HANDOFF.md"; then
|
|
98
85
|
fail "runtime workflow must not require $forbidden"
|
|
99
86
|
fi
|
|
100
87
|
done
|
package/.claude/loop.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# Claude Code Resume Prompt
|
|
2
|
-
|
|
3
|
-
Use this prompt for a one-time scheduled resume or for a slow `/loop`.
|
|
4
|
-
|
|
5
|
-
Resume only if the task is incomplete.
|
|
6
|
-
|
|
7
|
-
First read `HANDOFF.md`.
|
|
8
|
-
|
|
9
|
-
Then:
|
|
10
|
-
|
|
11
|
-
1. Run `git status --short`.
|
|
12
|
-
2. Inspect the current diff only enough to understand the working tree.
|
|
13
|
-
3. Continue exactly one small safe step from `Next Exact Action`.
|
|
14
|
-
4. Run the narrowest relevant check for that step.
|
|
15
|
-
5. Update HANDOFF.md with the new state.
|
|
16
|
-
6. Stop after that small step.
|
|
17
|
-
|
|
18
|
-
Rules:
|
|
19
|
-
|
|
20
|
-
- Do not repeat completed work.
|
|
21
|
-
- Do not start unrelated work.
|
|
22
|
-
- Do not do broad refactors.
|
|
23
|
-
- Do not run destructive commands.
|
|
24
|
-
- Do not run expensive full test suites unless `HANDOFF.md` says they are needed.
|
|
25
|
-
- If the handoff is missing or unclear, update it with what you know and stop.
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
# Claude Code Quota Resume Workflow
|
|
2
|
-
|
|
3
|
-
This workflow helps Claude Code continue after a quota or rate limit pause without a new prompt from you.
|
|
4
|
-
|
|
5
|
-
The main mechanism is a fresh handoff file plus a recurring in-session heartbeat armed at task start.
|
|
6
|
-
A one-time scheduled resume after the reset time still works when you know the reset time.
|
|
7
|
-
Neither bypasses quota.
|
|
8
|
-
They only wait until quota should be available again.
|
|
9
|
-
|
|
10
|
-
## Automatic Injection Through Hooks
|
|
11
|
-
|
|
12
|
-
Hook events differ in what they are allowed to do:
|
|
13
|
-
|
|
14
|
-
- `SessionStart` can inject context: anything its script prints to stdout is added to Claude's context for the session.
|
|
15
|
-
- `UserPromptSubmit` can also inject context, alongside each prompt you send.
|
|
16
|
-
- `StopFailure` can only observe: its output is ignored, so it can log and write files but never inject a prompt or schedule anything.
|
|
17
|
-
|
|
18
|
-
This template uses `SessionStart` to inject `.claude/standing-instructions.md` at the start of every session.
|
|
19
|
-
That file tells Claude to keep the handoff fresh, arm the 45-minute heartbeat for any multi-step task, and cancel it when done.
|
|
20
|
-
With the local hook settings enabled, you never paste the setup message again.
|
|
21
|
-
|
|
22
|
-
One honest limit: injected instructions are still instructions.
|
|
23
|
-
The harness guarantees Claude receives them, and the heartbeat schedule itself is enforced by the harness once created, but creating it is something Claude does in response to the injected text.
|
|
24
|
-
|
|
25
|
-
## Recommended Flow: Heartbeat Armed At Task Start
|
|
26
|
-
|
|
27
|
-
With the hook enabled, just give Claude the task; the arming instructions are injected for you.
|
|
28
|
-
Without it, arm the heartbeat when you give the task, not when quota is about to run out.
|
|
29
|
-
You do not need to know the reset time.
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
Keep HANDOFF.md updated after every small safe step.
|
|
33
|
-
|
|
34
|
-
Also create a recurring schedule every 45 minutes with this prompt:
|
|
35
|
-
|
|
36
|
-
Read .claude/auto-continue.md and follow it.
|
|
37
|
-
|
|
38
|
-
Cancel that schedule when the task is fully complete.
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
How it behaves:
|
|
42
|
-
|
|
43
|
-
- While Claude is working, the session is busy, so heartbeat fires wait or pass quickly.
|
|
44
|
-
- If quota blocks a turn, the session goes idle and each heartbeat fire fails cheaply, about once per interval.
|
|
45
|
-
- The first fire after the quota reset reads `HANDOFF.md` and continues the task to completion.
|
|
46
|
-
- The prompt in `.claude/auto-continue.md` is safe to fire at any time, so a fire after completion is a no-op.
|
|
47
|
-
|
|
48
|
-
The terminal must stay open for the heartbeat.
|
|
49
|
-
Use an interval of 30 to 60 minutes.
|
|
50
|
-
Do not use a one-minute interval.
|
|
51
|
-
|
|
52
|
-
## Install In Any Project
|
|
53
|
-
|
|
54
|
-
From the template folder:
|
|
55
|
-
|
|
56
|
-
```sh
|
|
57
|
-
cd /path/to/cc-session-recover
|
|
58
|
-
bash scripts/install-into-project.sh /path/to/project
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
To also install the optional local quota logger hook:
|
|
62
|
-
|
|
63
|
-
```sh
|
|
64
|
-
bash scripts/install-into-project.sh --enable-local-hook /path/to/project
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The hook is a logger plus a marker writer.
|
|
68
|
-
It cannot schedule the same-session resume by itself.
|
|
69
|
-
The marker feeds the optional unattended watcher described below.
|
|
70
|
-
|
|
71
|
-
## What It Does
|
|
72
|
-
|
|
73
|
-
The one-time resume prompt in `.claude/loop.md` tells Claude Code to:
|
|
74
|
-
|
|
75
|
-
- Read `HANDOFF.md`.
|
|
76
|
-
- Check the current git state.
|
|
77
|
-
- Do one small safe step.
|
|
78
|
-
- Run the narrowest useful check.
|
|
79
|
-
- Update the handoff.
|
|
80
|
-
- Stop.
|
|
81
|
-
|
|
82
|
-
The handoff is the recovery file for the current task.
|
|
83
|
-
It should stay fresh while Claude works.
|
|
84
|
-
Do not wait until the quota is almost gone.
|
|
85
|
-
Claude may not always know that a quota stop is about to happen.
|
|
86
|
-
|
|
87
|
-
The heartbeat prompt in `.claude/auto-continue.md` is different.
|
|
88
|
-
It keeps working through the remaining checklist until the task is complete or blocked, because a one-step-per-fire prompt would never finish a real task.
|
|
89
|
-
|
|
90
|
-
## One-Time Resume Flow
|
|
91
|
-
|
|
92
|
-
1. Run `claude` in the repo.
|
|
93
|
-
2. Give Claude the main coding task.
|
|
94
|
-
3. Make sure Claude keeps `HANDOFF.md` updated.
|
|
95
|
-
4. Look at the reset time in your Claude Code status line.
|
|
96
|
-
5. Add a 10 to 15 minute buffer.
|
|
97
|
-
6. Ask Claude Code to schedule one resume at that time.
|
|
98
|
-
|
|
99
|
-
The terminal must stay open.
|
|
100
|
-
The Claude Code session must be idle when the scheduled resume fires.
|
|
101
|
-
|
|
102
|
-
Example:
|
|
103
|
-
|
|
104
|
-
```text
|
|
105
|
-
Update HANDOFF.md now.
|
|
106
|
-
|
|
107
|
-
Then set a one-time reminder for 02:25 local time with this prompt:
|
|
108
|
-
|
|
109
|
-
Read HANDOFF.md first. If the task is incomplete, run git status --short, inspect the current diff only enough to understand the working tree, continue exactly one small safe step from Next Exact Action, run the narrowest relevant check, update HANDOFF.md, and stop.
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Use a time like `02:25`, not exactly `02:00` or `02:30`.
|
|
113
|
-
Claude Code may add a small timing offset to one-shot tasks at the top or bottom of the hour.
|
|
114
|
-
|
|
115
|
-
## What Happens During Quota Or Rate Limit
|
|
116
|
-
|
|
117
|
-
If quota or rate limit blocks a turn, Claude Code cannot keep working at that moment.
|
|
118
|
-
The scheduled resume does not bypass quota.
|
|
119
|
-
|
|
120
|
-
After quota resets, the scheduled resume prompt runs.
|
|
121
|
-
Claude reads `HANDOFF.md`, checks the repo state, does one small safe step, updates the handoff, and stops.
|
|
122
|
-
|
|
123
|
-
This avoids sending a prompt every minute while quota is still blocked.
|
|
124
|
-
|
|
125
|
-
## When To Use `/loop`
|
|
126
|
-
|
|
127
|
-
Use `/loop` for short polling tasks.
|
|
128
|
-
For example, use it to check whether CI passed or whether a deployment finished.
|
|
129
|
-
|
|
130
|
-
Do not use `/loop 1m` for overnight quota recovery.
|
|
131
|
-
That can add repeated failed attempts to the session.
|
|
132
|
-
|
|
133
|
-
If you do not know the reset time, use a slow loop such as `/loop 1h`, not a one-minute loop.
|
|
134
|
-
|
|
135
|
-
## How To Stop The Loop
|
|
136
|
-
|
|
137
|
-
Press Esc while the loop is waiting.
|
|
138
|
-
|
|
139
|
-
If that does not stop it, use the normal Claude Code stop control in the same terminal.
|
|
140
|
-
|
|
141
|
-
One-time scheduled reminders are different.
|
|
142
|
-
Ask Claude Code to list or cancel scheduled tasks if you need to remove one.
|
|
143
|
-
|
|
144
|
-
## Hooks And Status Line
|
|
145
|
-
|
|
146
|
-
Claude Code status lines can receive rate-limit fields such as `rate_limits.five_hour.resets_at`.
|
|
147
|
-
That is how the reset time can be shown.
|
|
148
|
-
|
|
149
|
-
Claude Code hooks can detect a `rate_limit` stop through `StopFailure`.
|
|
150
|
-
But `StopFailure` hooks have no decision control.
|
|
151
|
-
So a hook can log or notify, but it should not be treated as a documented way to schedule a same-session resume.
|
|
152
|
-
|
|
153
|
-
This template includes an optional hook logger:
|
|
154
|
-
|
|
155
|
-
- `.claude/settings.example.json`
|
|
156
|
-
- `.claude/hooks/log-stop-failure.sh`
|
|
157
|
-
|
|
158
|
-
To enable it in one project, copy `.claude/settings.example.json` to `.claude/settings.local.json`.
|
|
159
|
-
If that file already exists, merge the hook settings manually.
|
|
160
|
-
|
|
161
|
-
When quota stops a turn, the hook appends a short note to `HANDOFF.md`.
|
|
162
|
-
It also writes raw hook input to `.claude/stop-failure-events.jsonl` and a marker to `.claude/quota-blocked.json`.
|
|
163
|
-
|
|
164
|
-
## Optional Unattended Watcher
|
|
165
|
-
|
|
166
|
-
The heartbeat needs the terminal open.
|
|
167
|
-
If the terminal might close, run the watcher from another shell:
|
|
168
|
-
|
|
169
|
-
```sh
|
|
170
|
-
bash scripts/quota-watcher.sh /path/to/project
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
It requires `jq`, the `claude` CLI on `PATH`, and the local hook enabled.
|
|
174
|
-
|
|
175
|
-
When the hook writes `.claude/quota-blocked.json`, the watcher reads the `session_id` from it and retries:
|
|
176
|
-
|
|
177
|
-
```sh
|
|
178
|
-
claude -p --resume <session_id> --permission-mode acceptEdits "<contents of .claude/auto-continue.md>"
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
While quota is blocked, each attempt fails and the watcher sleeps.
|
|
182
|
-
After the reset, the resume succeeds, the task continues headlessly, and the watcher clears the marker.
|
|
183
|
-
|
|
184
|
-
## Precise Resume Using The Status Line Cache
|
|
185
|
-
|
|
186
|
-
The status line wrapper `.claude/statusline-quota-cache.sh` saves the rate-limit fields, including `five_hour.resets_at`, to `.claude/rate-limit-state.json` while you work.
|
|
187
|
-
The StopFailure hook stamps that cached reset time into the quota marker.
|
|
188
|
-
When the marker has a reset time, the watcher does not knock on an interval.
|
|
189
|
-
It sleeps until the reset time plus `QUOTA_RESUME_BUFFER` seconds, default 900 (15 minutes), and knocks once.
|
|
190
|
-
The reset time is Unix epoch seconds, so the arithmetic is timezone-safe; local time appears only in printed messages.
|
|
191
|
-
The interval knocking remains only as the fallback when no reset time is known or the precise knock fails.
|
|
192
|
-
|
|
193
|
-
To keep an existing status line, set `CLAUDE_QUOTA_STATUSLINE_DELEGATE` to its command in the `statusLine` settings entry; the wrapper caches the fields and passes the display through unchanged.
|
|
194
|
-
|
|
195
|
-
Cautions:
|
|
196
|
-
|
|
197
|
-
- Exit the interactive Claude Code session before relying on the watcher, or both will work the same task in parallel.
|
|
198
|
-
- Headless mode cannot ask for permissions, so configure the project allowlist or accept the default `--permission-mode acceptEdits`, and understand what that allows.
|
|
199
|
-
- Set `QUOTA_WATCH_INTERVAL` and `QUOTA_WATCH_CLAUDE_ARGS` to tune the retry interval and claude flags.
|
|
200
|
-
|
|
201
|
-
## Testing The Flow Without Real Quota
|
|
202
|
-
|
|
203
|
-
Run:
|
|
204
|
-
|
|
205
|
-
```sh
|
|
206
|
-
bash scripts/test-fake-quota-flow.sh
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
It tests the full chain in a throwaway dummy repo:
|
|
210
|
-
|
|
211
|
-
1. Installs the template into the dummy repo with the hook enabled.
|
|
212
|
-
2. Pipes a fake `SessionStart` event into the injection hook and asserts the standing instructions come out.
|
|
213
|
-
3. Pipes a fake `rate_limit` `StopFailure` event into the logger hook and asserts the log line, the handoff note, and the `quota-blocked.json` marker with the right `session_id`.
|
|
214
|
-
4. Replaces the `claude` CLI with a stub that fails twice, simulating blocked quota, then succeeds, simulating the reset.
|
|
215
|
-
5. Runs the real watcher against the stub and asserts it retried, resumed the exact recorded session with the auto-continue prompt, and cleared the marker.
|
|
216
|
-
|
|
217
|
-
The one piece this cannot fake is the in-session heartbeat schedule, because that timer lives inside a real Claude Code session.
|
|
218
|
-
To check that piece live, start `claude` in a scratch repo, give it a tiny multi-step task, and confirm it creates the 45-minute schedule on its own from the injected instructions.
|
|
219
|
-
|
|
220
|
-
## Why The Interactive Flow Avoids Terminal Hacks
|
|
221
|
-
|
|
222
|
-
The in-session flow uses the original Claude Code terminal.
|
|
223
|
-
It avoids `tmux`, `screen`, `expect`, terminal-injection hacks, and `TIOCSTI`.
|
|
224
|
-
|
|
225
|
-
Those tools try to control a terminal from the outside.
|
|
226
|
-
That is fragile.
|
|
227
|
-
It can also continue the wrong session.
|
|
228
|
-
|
|
229
|
-
The built-in scheduler is simpler.
|
|
230
|
-
It runs inside the same Claude Code session that already has the task context.
|
|
231
|
-
|
|
232
|
-
The watcher's headless `claude -p --resume` is different from terminal injection.
|
|
233
|
-
It is a documented CLI mode, and it targets the exact session recorded by the hook.
|
|
234
|
-
|
|
235
|
-
## Verified Limits
|
|
236
|
-
|
|
237
|
-
- Scheduled tasks require Claude Code v2.1.72 or newer.
|
|
238
|
-
- Session scheduled tasks only fire while Claude Code is running and idle.
|
|
239
|
-
- Closing the terminal or ending the session stops them.
|
|
240
|
-
- A missed scheduled time does not catch up later unless Claude was only busy in the same open session.
|
|
241
|
-
- Starting a fresh conversation clears session scheduled tasks.
|
|
242
|
-
- Resuming with `claude --resume` or `claude --continue` can restore unexpired tasks.
|
package/docs/faq.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# FAQ
|
|
2
|
-
|
|
3
|
-
Honest answers, including the parts that are not proven.
|
|
4
|
-
|
|
5
|
-
## Where does the 45-minute alarm live?
|
|
6
|
-
|
|
7
|
-
Inside the running Claude Code session, in memory.
|
|
8
|
-
It is not a system cron job, not a launchd job, and not a file on disk.
|
|
9
|
-
The standing-instructions hook asks Claude to create it at task start, and Claude creates it with Claude Code's built-in scheduler.
|
|
10
|
-
Because it lives in the process, closing the terminal kills it.
|
|
11
|
-
|
|
12
|
-
## Is this 100% reliable?
|
|
13
|
-
|
|
14
|
-
No. Three layers, three confidence levels:
|
|
15
|
-
|
|
16
|
-
- The scripts and the watchman: tested end to end with real sessions. Strong.
|
|
17
|
-
- The harness behavior (hooks firing, schedules firing): documented by Claude Code, and the injection and resume paths were verified live.
|
|
18
|
-
- Claude obeying the standing orders (keeping the notebook fresh, arming the alarm): this is a model following instructions. It is very reliable, not guaranteed. This is the weakest link.
|
|
19
|
-
|
|
20
|
-
The first genuine quota stop is the final exam: afterwards, check `.claude/stop-failure-events.jsonl` to confirm the hook fired for real.
|
|
21
|
-
|
|
22
|
-
## What still needs a human?
|
|
23
|
-
|
|
24
|
-
- Opening the session and giving the task.
|
|
25
|
-
- Keeping the terminal open for the alarm, or starting the watchman if it cannot stay open.
|
|
26
|
-
- Approving the hooks once per project, the first time Claude Code sees them.
|
|
27
|
-
- Judging the finished work. The workflow recovers progress; it does not review quality.
|
|
28
|
-
|
|
29
|
-
## What happens if the terminal closes?
|
|
30
|
-
|
|
31
|
-
The alarm dies with the session, but nothing is lost:
|
|
32
|
-
|
|
33
|
-
- The notebook (`HANDOFF.md`) is still on disk with the exact next step.
|
|
34
|
-
- If the watchman is running and a quota stop was recorded, it resumes the session headlessly on its own.
|
|
35
|
-
- Otherwise, reopen the project, run `claude`, and say: "Read HANDOFF.md and continue."
|
|
36
|
-
|
|
37
|
-
Worst case equals one typed line, never lost work.
|
package/docs/simple-flow.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# The Simple Flow: Notebook, Alarm, Watchman
|
|
2
|
-
|
|
3
|
-
This is the plain-English story of how this workflow survives quota stops.
|
|
4
|
-
|
|
5
|
-
## The notebook
|
|
6
|
-
|
|
7
|
-
Claude keeps a notebook called `HANDOFF.md` in the project root.
|
|
8
|
-
After every small piece of work it writes down: the goal, what is done, and the exact next step.
|
|
9
|
-
If Claude is cut off at any moment, whoever reads the notebook can continue exactly where it stopped.
|
|
10
|
-
That "whoever" is usually Claude itself, later.
|
|
11
|
-
|
|
12
|
-
## The alarm
|
|
13
|
-
|
|
14
|
-
When you give Claude a big task, it sets a repeating alarm inside the session:
|
|
15
|
-
every 45 minutes, "read `.claude/auto-continue.md` and follow it."
|
|
16
|
-
|
|
17
|
-
- While Claude is working, the alarm is harmless.
|
|
18
|
-
- If the task is already done, the alarm fire reads the notebook, sees "done", and goes back to sleep.
|
|
19
|
-
- If quota cut Claude off, each alarm fire fails cheaply while quota is still blocked.
|
|
20
|
-
- The first fire after the quota reset reads the notebook and continues the work.
|
|
21
|
-
|
|
22
|
-
You never type anything. The one rule: the terminal must stay open, because the alarm lives inside the running session.
|
|
23
|
-
|
|
24
|
-
## The watchman
|
|
25
|
-
|
|
26
|
-
If the terminal cannot stay open, run the watchman in a second terminal:
|
|
27
|
-
|
|
28
|
-
```sh
|
|
29
|
-
bash scripts/quota-watcher.sh /path/to/project
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
When quota cuts Claude off, a hook drops a marker file with the session id.
|
|
33
|
-
The watchman sees the marker and knocks every 20 minutes: "can I resume that session yet?"
|
|
34
|
-
While quota is blocked: no, it waits. After the reset: yes, it resumes that exact session headlessly, hands it the notebook, and the work finishes without any window at all.
|
|
35
|
-
|
|
36
|
-
Use the alarm or the watchman, not both at once, or two Claudes will do the same work twice.
|
|
37
|
-
|
|
38
|
-
## A normal day with this installed
|
|
39
|
-
|
|
40
|
-
1. `cd` into the project and run `claude`.
|
|
41
|
-
2. Give your task normally. A hook already injected the standing orders, so Claude sets up the notebook and the alarm by itself.
|
|
42
|
-
3. Walk away. Quota stops are recovered automatically.
|
|
43
|
-
4. Worst case, everything fails: the notebook still means you lose nothing. Type "read HANDOFF.md and continue" and you are back.
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# Verified Quota Resume Example
|
|
2
|
-
|
|
3
|
-
This example uses only documented Claude Code behavior.
|
|
4
|
-
|
|
5
|
-
## Situation
|
|
6
|
-
|
|
7
|
-
You are working in `/path/to/my-app`.
|
|
8
|
-
Claude Code shows that the 5-hour quota resets at `02:10`.
|
|
9
|
-
You want work to continue overnight without sending a prompt every minute.
|
|
10
|
-
|
|
11
|
-
## What You Type
|
|
12
|
-
|
|
13
|
-
Inside the same Claude Code terminal, type:
|
|
14
|
-
|
|
15
|
-
```text
|
|
16
|
-
Update HANDOFF.md now.
|
|
17
|
-
|
|
18
|
-
Then set a one-time reminder for 02:25 local time with this prompt:
|
|
19
|
-
|
|
20
|
-
Read HANDOFF.md first. If the task is incomplete, run git status --short, inspect the current diff only enough to understand the working tree, continue exactly one small safe step from Next Exact Action, run the narrowest relevant check, update HANDOFF.md, and stop.
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Why `02:25`
|
|
24
|
-
|
|
25
|
-
The quota reset is `02:10`.
|
|
26
|
-
The extra 15 minutes gives a buffer.
|
|
27
|
-
The time is not exactly `02:00` or `02:30`, so it avoids the special timing offset Claude Code may add at those exact marks.
|
|
28
|
-
|
|
29
|
-
## What Should Happen
|
|
30
|
-
|
|
31
|
-
1. Claude updates `HANDOFF.md`.
|
|
32
|
-
2. Claude schedules one future prompt for `02:25` local time.
|
|
33
|
-
3. If quota blocks the current turn before then, nothing keeps retrying every minute.
|
|
34
|
-
4. At about `02:25`, if Claude Code is still open and idle, the scheduled prompt fires.
|
|
35
|
-
5. Claude reads the handoff.
|
|
36
|
-
6. Claude does one small safe step.
|
|
37
|
-
7. Claude updates the handoff again.
|
|
38
|
-
8. Claude stops.
|
|
39
|
-
|
|
40
|
-
## What This Does Not Do
|
|
41
|
-
|
|
42
|
-
- It does not bypass quota.
|
|
43
|
-
- It does not work if the terminal is closed.
|
|
44
|
-
- It does not work if Claude Code exits.
|
|
45
|
-
- It does not guarantee the whole task finishes.
|
|
46
|
-
- It does not use `tmux`, `screen`, `expect`, `claude -p`, or terminal injection.
|
|
47
|
-
|
|
48
|
-
## If The Reset Time Is Unknown
|
|
49
|
-
|
|
50
|
-
Use a slow loop only as a fallback:
|
|
51
|
-
|
|
52
|
-
```text
|
|
53
|
-
/loop 1h
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Do not use `/loop 1m` for quota recovery.
|
|
57
|
-
That can add many failed attempts to the session.
|