cc-session-recover 0.1.2 → 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 +7 -0
- package/README.md +11 -5
- package/bin/cli.js +27 -8
- package/package.json +1 -2
- package/scripts/install-into-project.sh +8 -15
- package/scripts/test-fake-quota-flow.sh +3 -1
- package/scripts/verify-claude-loop-workflow.sh +3 -16
- package/.claude/loop.md +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
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
|
+
|
|
3
10
|
## 0.1.2 — 2026-06-12
|
|
4
11
|
|
|
5
12
|
- Stop installing the package docs into target projects.
|
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@ Every part of this has been verified against a genuine quota stop, end to end.
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npx cc-session-recover init
|
|
12
|
+
npx cc-session-recover init /path/to/project
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
(Or from a clone: `bash scripts/install-into-project.sh
|
|
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.)
|
|
16
16
|
|
|
17
17
|
Approve the hooks once when Claude Code asks on the next start. That's the whole setup.
|
|
18
18
|
|
|
@@ -27,15 +27,21 @@ Give Claude your task, normally. Nothing extra to type — the injected standing
|
|
|
27
27
|
|
|
28
28
|
If quota dies mid-task, work resumes automatically after the reset.
|
|
29
29
|
|
|
30
|
+
## Why This Approach Is Stronger
|
|
31
|
+
|
|
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.
|
|
36
|
+
|
|
30
37
|
## Limits
|
|
31
38
|
|
|
32
39
|
- It does not bypass quota. It only waits for the reset.
|
|
33
40
|
- The basic flow needs the terminal to stay open. A closed-terminal recovery mode exists; see the docs.
|
|
34
|
-
- Worst case is never lost work: the recovery note is always on disk, and "Read HANDOFF.md and continue" restores any session by hand.
|
|
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.
|
|
35
42
|
|
|
36
43
|
## Docs
|
|
37
44
|
|
|
38
45
|
- [Simple flow](docs/simple-flow.md) — how it works, told as a story (notebook, alarm, watchman).
|
|
39
46
|
- [FAQ](docs/faq.md) — reliability, hook approval, what still needs a human.
|
|
40
|
-
- [Full details](docs/claude-code-auto-resume.md) — closed-terminal watcher, precise reset-time resume,
|
|
41
|
-
- [Worked example](docs/verified-quota-resume-example.md) — a concrete one-time reminder example.
|
|
47
|
+
- [Full details](docs/claude-code-auto-resume.md) — closed-terminal watcher, precise reset-time resume, all limits.
|
package/bin/cli.js
CHANGED
|
@@ -5,23 +5,24 @@
|
|
|
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
|
-
'scripts/install-into-project.sh',
|
|
23
|
-
'scripts/quota-watcher.sh',
|
|
24
|
-
'scripts/test-fake-quota-flow.sh',
|
|
25
26
|
];
|
|
26
27
|
|
|
27
28
|
const COPY_IF_MISSING = ['HANDOFF.md'];
|
|
@@ -39,18 +40,31 @@ const IGNORE_ENTRIES = [
|
|
|
39
40
|
const IGNORE_HEADER = '# Claude Code session-recovery runtime state';
|
|
40
41
|
|
|
41
42
|
function usage() {
|
|
42
|
-
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.');
|
|
43
46
|
process.exit(2);
|
|
44
47
|
}
|
|
45
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
|
+
|
|
46
59
|
function main() {
|
|
47
60
|
const args = process.argv.slice(2);
|
|
48
|
-
if (args[0] !== 'init') usage();
|
|
61
|
+
if (args[0] !== 'init' && args[0] !== 'watch') usage();
|
|
49
62
|
|
|
50
|
-
let enableHook =
|
|
63
|
+
let enableHook = true;
|
|
51
64
|
let target = '.';
|
|
52
65
|
for (const arg of args.slice(1)) {
|
|
53
|
-
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
|
|
54
68
|
else if (arg.startsWith('-')) usage();
|
|
55
69
|
else target = arg;
|
|
56
70
|
}
|
|
@@ -61,6 +75,11 @@ function main() {
|
|
|
61
75
|
process.exit(1);
|
|
62
76
|
}
|
|
63
77
|
|
|
78
|
+
if (args[0] === 'watch') {
|
|
79
|
+
watch(target);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
for (const rel of FILES) {
|
|
65
84
|
const src = path.join(TEMPLATE_ROOT, rel);
|
|
66
85
|
const dest = path.join(target, rel);
|
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,7 +12,6 @@
|
|
|
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",
|
|
@@ -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,19 +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
|
-
cp "$TEMPLATE_ROOT/.claude/loop.md" "$TARGET/.claude/loop.md"
|
|
32
32
|
cp "$TEMPLATE_ROOT/.claude/auto-continue.md" "$TARGET/.claude/auto-continue.md"
|
|
33
33
|
cp "$TEMPLATE_ROOT/.claude/standing-instructions.md" "$TARGET/.claude/standing-instructions.md"
|
|
34
34
|
cp "$TEMPLATE_ROOT/.claude/statusline-quota-cache.sh" "$TARGET/.claude/statusline-quota-cache.sh"
|
|
35
35
|
cp "$TEMPLATE_ROOT/.claude/hooks/inject-standing-instructions.sh" "$TARGET/.claude/hooks/inject-standing-instructions.sh"
|
|
36
36
|
cp "$TEMPLATE_ROOT/.claude/settings.example.json" "$TARGET/.claude/settings.example.json"
|
|
37
37
|
cp "$TEMPLATE_ROOT/.claude/hooks/log-stop-failure.sh" "$TARGET/.claude/hooks/log-stop-failure.sh"
|
|
38
|
-
cp "$TEMPLATE_ROOT/scripts/install-into-project.sh" "$TARGET/scripts/install-into-project.sh"
|
|
39
|
-
cp "$TEMPLATE_ROOT/scripts/quota-watcher.sh" "$TARGET/scripts/quota-watcher.sh"
|
|
40
|
-
cp "$TEMPLATE_ROOT/scripts/test-fake-quota-flow.sh" "$TARGET/scripts/test-fake-quota-flow.sh"
|
|
41
|
-
|
|
42
38
|
if [ ! -f "$TARGET/HANDOFF.md" ]; then
|
|
43
39
|
cp "$TEMPLATE_ROOT/HANDOFF.md" "$TARGET/HANDOFF.md"
|
|
44
40
|
fi
|
|
@@ -46,9 +42,6 @@ fi
|
|
|
46
42
|
chmod +x "$TARGET/.claude/hooks/log-stop-failure.sh"
|
|
47
43
|
chmod +x "$TARGET/.claude/hooks/inject-standing-instructions.sh"
|
|
48
44
|
chmod +x "$TARGET/.claude/statusline-quota-cache.sh"
|
|
49
|
-
chmod +x "$TARGET/scripts/test-fake-quota-flow.sh"
|
|
50
|
-
chmod +x "$TARGET/scripts/install-into-project.sh"
|
|
51
|
-
chmod +x "$TARGET/scripts/quota-watcher.sh"
|
|
52
45
|
|
|
53
46
|
# Keep runtime state out of the target's git history. HANDOFF.md must stay in
|
|
54
47
|
# the project root (Claude Code blocks unattended edits inside .claude/), so
|
|
@@ -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,12 +54,6 @@ 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
58
|
require_text "README.md" "npx cc-session-recover init"
|
|
72
59
|
require_text "README.md" "not bypass quota"
|
|
@@ -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.
|