cc-session-recover 0.1.0
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/.claude/auto-continue.md +30 -0
- package/.claude/hooks/inject-standing-instructions.sh +16 -0
- package/.claude/hooks/log-stop-failure.sh +39 -0
- package/.claude/loop.md +25 -0
- package/.claude/settings.example.json +26 -0
- package/.claude/standing-instructions.md +10 -0
- package/.claude/statusline-quota-cache.sh +50 -0
- package/CHANGELOG.md +17 -0
- package/HANDOFF.md +44 -0
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/SECURITY.md +55 -0
- package/bin/cli.js +88 -0
- package/docs/claude-code-auto-resume.md +242 -0
- package/docs/faq.md +37 -0
- package/docs/simple-flow.md +43 -0
- package/docs/verified-quota-resume-example.md +57 -0
- package/package.json +49 -0
- package/scripts/install-into-project.sh +69 -0
- package/scripts/quota-watcher.sh +97 -0
- package/scripts/test-fake-quota-flow.sh +109 -0
- package/scripts/verify-claude-loop-workflow.sh +102 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
You are resuming a task that was interrupted, most likely by a quota stop.
|
|
2
|
+
Act now. Do not describe these instructions, do not summarize them, and do not wait for a task.
|
|
3
|
+
This prompt is safe to fire at any time, so if there is nothing to do, that is a normal outcome.
|
|
4
|
+
|
|
5
|
+
Read `HANDOFF.md` now.
|
|
6
|
+
|
|
7
|
+
If the Goal in the handoff is complete:
|
|
8
|
+
|
|
9
|
+
1. Update `HANDOFF.md` so Current Status and the checklist show the goal is complete.
|
|
10
|
+
2. Cancel this recurring schedule if one exists.
|
|
11
|
+
3. Reply DONE and stop.
|
|
12
|
+
|
|
13
|
+
If the Goal is incomplete, do the work now:
|
|
14
|
+
|
|
15
|
+
1. Run `git status --short`.
|
|
16
|
+
2. Inspect the current diff only enough to understand the working tree.
|
|
17
|
+
3. Continue from `Next Exact Action`.
|
|
18
|
+
4. Keep working through the remaining checklist.
|
|
19
|
+
5. Run the narrowest relevant check after each step.
|
|
20
|
+
6. Update `HANDOFF.md` after every small safe step.
|
|
21
|
+
7. Stop only when the Goal is complete or you are blocked on the user.
|
|
22
|
+
|
|
23
|
+
Rules:
|
|
24
|
+
|
|
25
|
+
- Do not repeat completed work.
|
|
26
|
+
- Do not start unrelated work.
|
|
27
|
+
- Do not do broad refactors.
|
|
28
|
+
- Do not run destructive commands.
|
|
29
|
+
- Do not run expensive full test suites unless `HANDOFF.md` says they are needed.
|
|
30
|
+
- If the handoff is missing or unclear, update it with what you know and stop.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# SessionStart hook.
|
|
4
|
+
# Whatever this prints to stdout is added to Claude's context for the session,
|
|
5
|
+
# so the user does not have to paste the heartbeat instructions every time.
|
|
6
|
+
|
|
7
|
+
set -u
|
|
8
|
+
|
|
9
|
+
PROJECT_DIR=${CLAUDE_PROJECT_DIR:-$(pwd)}
|
|
10
|
+
INSTRUCTIONS="$PROJECT_DIR/.claude/standing-instructions.md"
|
|
11
|
+
|
|
12
|
+
[ -f "$INSTRUCTIONS" ] || exit 0
|
|
13
|
+
|
|
14
|
+
cat "$INSTRUCTIONS"
|
|
15
|
+
|
|
16
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -u
|
|
4
|
+
|
|
5
|
+
PROJECT_DIR=${CLAUDE_PROJECT_DIR:-$(pwd)}
|
|
6
|
+
CLAUDE_DIR="$PROJECT_DIR/.claude"
|
|
7
|
+
HANDOFF="$PROJECT_DIR/HANDOFF.md"
|
|
8
|
+
LOG="$CLAUDE_DIR/stop-failure-events.jsonl"
|
|
9
|
+
MARKER="$CLAUDE_DIR/quota-blocked.json"
|
|
10
|
+
STAMP=$(date '+%Y-%m-%d %H:%M:%S %Z')
|
|
11
|
+
INPUT=$(cat)
|
|
12
|
+
|
|
13
|
+
mkdir -p "$CLAUDE_DIR" || exit 0
|
|
14
|
+
|
|
15
|
+
printf '{"logged_at":"%s","hook_input":%s}\n' "$STAMP" "$INPUT" >> "$LOG" 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
# Marker for the optional unattended watcher. The watcher deletes it after a
|
|
18
|
+
# successful resume; a stale marker is harmless otherwise.
|
|
19
|
+
# If the status line cached the rate-limit reset time, stamp it in so the
|
|
20
|
+
# watcher can sleep until exactly then instead of knocking on an interval.
|
|
21
|
+
RATE_STATE='null'
|
|
22
|
+
if [ -f "$CLAUDE_DIR/rate-limit-state.json" ]; then
|
|
23
|
+
RATE_STATE=$(cat "$CLAUDE_DIR/rate-limit-state.json" 2>/dev/null) || RATE_STATE='null'
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
printf '{"logged_at":"%s","rate_limit_state":%s,"hook_input":%s}\n' "$STAMP" "${RATE_STATE:-null}" "$INPUT" > "$MARKER" 2>/dev/null || true
|
|
27
|
+
|
|
28
|
+
# Dedupe: during one outage every blocked retry fires this hook again, so
|
|
29
|
+
# only append a note when the handoff does not already end with one.
|
|
30
|
+
if [ -f "$HANDOFF" ] && ! tail -5 "$HANDOFF" | grep -Fq 'claude-code-stop-failure'; then
|
|
31
|
+
{
|
|
32
|
+
printf '\n<!-- claude-code-stop-failure: %s -->\n' "$STAMP"
|
|
33
|
+
printf '\nAutomatic note: Claude Code hit a rate limit at %s.\n' "$STAMP"
|
|
34
|
+
printf 'Raw hook input was saved to `.claude/stop-failure-events.jsonl`.\n'
|
|
35
|
+
printf 'This hook cannot schedule a same-session resume by itself.\n'
|
|
36
|
+
} >> "$HANDOFF" 2>/dev/null || true
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
exit 0
|
package/.claude/loop.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-standing-instructions.sh"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"StopFailure": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "rate_limit",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/log-stop-failure.sh"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Standing quota-recovery instructions for this project:
|
|
2
|
+
|
|
3
|
+
If the user gives a multi-step coding task in this session:
|
|
4
|
+
|
|
5
|
+
1. Keep `HANDOFF.md` updated after every small safe step.
|
|
6
|
+
2. Create a recurring schedule every 45 minutes with this prompt: "Read .claude/auto-continue.md and follow it."
|
|
7
|
+
3. Cancel that schedule when the task is fully complete.
|
|
8
|
+
|
|
9
|
+
Do not create the schedule for quick questions or single-step requests.
|
|
10
|
+
Do not create a second schedule if one already exists.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Status line wrapper that caches rate-limit state for the quota workflow.
|
|
4
|
+
#
|
|
5
|
+
# Claude Code feeds the status line JSON that includes
|
|
6
|
+
# rate_limits.five_hour.resets_at. This script saves those fields to
|
|
7
|
+
# .claude/rate-limit-state.json so the StopFailure hook can stamp the exact
|
|
8
|
+
# reset time into the quota marker when a quota stop happens.
|
|
9
|
+
#
|
|
10
|
+
# Display:
|
|
11
|
+
# - If CLAUDE_QUOTA_STATUSLINE_DELEGATE is set, the same JSON is passed to
|
|
12
|
+
# that command and its output is shown, so an existing status line keeps
|
|
13
|
+
# working unchanged.
|
|
14
|
+
# - Otherwise a minimal line with model and quota usage is printed.
|
|
15
|
+
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
20
|
+
DIR=$(printf '%s' "$INPUT" | jq -r '.workspace.project_dir // .cwd // empty' 2>/dev/null)
|
|
21
|
+
|
|
22
|
+
if [ -n "$DIR" ] && [ -d "$DIR/.claude" ]; then
|
|
23
|
+
STATE=$(printf '%s' "$INPUT" | jq -c '{
|
|
24
|
+
five_hour_used: (.rate_limits.five_hour.used_percentage // null),
|
|
25
|
+
five_hour_resets_at: (.rate_limits.five_hour.resets_at // null),
|
|
26
|
+
seven_day_used: (.rate_limits.seven_day.used_percentage // null),
|
|
27
|
+
seven_day_resets_at: (.rate_limits.seven_day.resets_at // null),
|
|
28
|
+
cached_at: now | floor
|
|
29
|
+
}' 2>/dev/null)
|
|
30
|
+
|
|
31
|
+
if [ -n "$STATE" ] && [ "$(printf '%s' "$STATE" | jq -r '.five_hour_resets_at')" != "null" ]; then
|
|
32
|
+
printf '%s\n' "$STATE" > "$DIR/.claude/rate-limit-state.json" 2>/dev/null || true
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if [ -n "${CLAUDE_QUOTA_STATUSLINE_DELEGATE:-}" ]; then
|
|
37
|
+
printf '%s' "$INPUT" | "$CLAUDE_QUOTA_STATUSLINE_DELEGATE"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
MODEL=$(printf '%s' "$INPUT" | jq -r '.model.display_name // "Claude"' 2>/dev/null)
|
|
42
|
+
FIVE=$(printf '%s' "$INPUT" | jq -r '.rate_limits.five_hour.used_percentage // empty' 2>/dev/null)
|
|
43
|
+
RESETS=$(printf '%s' "$INPUT" | jq -r '.rate_limits.five_hour.resets_at // empty' 2>/dev/null)
|
|
44
|
+
|
|
45
|
+
if [ -n "$FIVE" ] && [ -n "$RESETS" ]; then
|
|
46
|
+
RESETS_LOCAL=$(date -r "$RESETS" '+%H:%M' 2>/dev/null || date -d "@$RESETS" '+%H:%M' 2>/dev/null || echo '?')
|
|
47
|
+
printf '%s | 5h quota: %s%% (resets %s)' "$MODEL" "$FIVE" "$RESETS_LOCAL"
|
|
48
|
+
else
|
|
49
|
+
printf '%s' "$MODEL"
|
|
50
|
+
fi
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-06-12
|
|
4
|
+
|
|
5
|
+
Initial release of the quota-resume template.
|
|
6
|
+
|
|
7
|
+
### Included
|
|
8
|
+
|
|
9
|
+
- `HANDOFF.md` notebook and `.claude/auto-continue.md` heartbeat prompt for automatic continuation after quota resets.
|
|
10
|
+
- `SessionStart` hook that injects the standing instructions, so the setup never has to be typed.
|
|
11
|
+
- `StopFailure` hook that logs quota stops and writes a marker for the unattended watcher.
|
|
12
|
+
- Watcher script that resumes the exact recorded session headlessly once quota is back, sleeping until the known reset time plus a 15-minute buffer when the status line cache is configured.
|
|
13
|
+
- Installer, verify script, plain-English docs, and a self-test that proves the whole chain in a throwaway repo without using real quota.
|
|
14
|
+
|
|
15
|
+
### Verification
|
|
16
|
+
|
|
17
|
+
- Every link was verified against real Claude Code sessions, including one genuine quota stop: the hook captured it, the heartbeat retried while blocked, and the first fire after the reset resumed and completed the task with no human input.
|
package/HANDOFF.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Claude Code Handoff
|
|
2
|
+
|
|
3
|
+
This file lives in the project root on purpose.
|
|
4
|
+
Files under `.claude/` are treated as sensitive by Claude Code, so edits to them are blocked in unattended runs.
|
|
5
|
+
|
|
6
|
+
Use this file for the current task only.
|
|
7
|
+
Keep it fresh while work is active.
|
|
8
|
+
Update it after each small step and before any long or risky step.
|
|
9
|
+
|
|
10
|
+
## Goal
|
|
11
|
+
|
|
12
|
+
- Not set yet.
|
|
13
|
+
|
|
14
|
+
## Current Status
|
|
15
|
+
|
|
16
|
+
- No active task is recorded yet.
|
|
17
|
+
|
|
18
|
+
## Completed Work
|
|
19
|
+
|
|
20
|
+
- None yet.
|
|
21
|
+
|
|
22
|
+
## Remaining Checklist
|
|
23
|
+
|
|
24
|
+
- [ ] Add the next task steps here.
|
|
25
|
+
|
|
26
|
+
## Files Changed
|
|
27
|
+
|
|
28
|
+
- None yet.
|
|
29
|
+
|
|
30
|
+
## Commands Run
|
|
31
|
+
|
|
32
|
+
- None yet.
|
|
33
|
+
|
|
34
|
+
## Current Errors or Failing Tests
|
|
35
|
+
|
|
36
|
+
- None known.
|
|
37
|
+
|
|
38
|
+
## Known Risks
|
|
39
|
+
|
|
40
|
+
- None known.
|
|
41
|
+
|
|
42
|
+
## Next Exact Action
|
|
43
|
+
|
|
44
|
+
- Set the goal and first safe step when a task starts.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pradeep Singh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# cc-session-recover
|
|
2
|
+
|
|
3
|
+
Keep a long Claude Code task recoverable when quota or a rate limit stops it.
|
|
4
|
+
|
|
5
|
+
This project installs a small workflow into another repo. Claude keeps a handoff file and sets a slow in-session heartbeat. If the terminal closes after a quota stop is recorded, an optional watcher can resume the same Claude session from another shell.
|
|
6
|
+
|
|
7
|
+
## Why This Approach Is Stronger
|
|
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.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
Use `npx` from any project:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npx cc-session-recover init /path/to/project
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Enable the local Claude Code hooks during install:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npx cc-session-recover init --enable-local-hook /path/to/project
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
You can also install from a clone of this repo:
|
|
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:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
cd /path/to/project
|
|
56
|
+
claude
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then give Claude your real task.
|
|
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
|
+
```
|
|
121
|
+
|
|
122
|
+
If the status line cache has a reset time, the watcher sleeps until that time plus a 15-minute buffer before it tries. Without a reset time, it retries on an interval.
|
|
123
|
+
|
|
124
|
+
Close the interactive Claude Code session before you rely on the watcher. Otherwise the open session and the watcher may work on the same task.
|
|
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
|
+
```
|
|
147
|
+
|
|
148
|
+
When you run the fake test, the script creates a throwaway repo, installs the workflow, fakes a `SessionStart` event, fakes a `rate_limit` `StopFailure`, replaces `claude` with a stub that fails twice, then checks that the watcher resumes the recorded session.
|
|
149
|
+
|
|
150
|
+
## Limits
|
|
151
|
+
|
|
152
|
+
- This does not bypass quota.
|
|
153
|
+
- The heartbeat needs Claude Code to stay open and idle when the scheduled prompt fires.
|
|
154
|
+
- The watcher needs the local hook marker file. It only acts after a quota stop gets recorded.
|
|
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.
|
|
157
|
+
|
|
158
|
+
## More Docs
|
|
159
|
+
|
|
160
|
+
- [Simple flow](docs/simple-flow.md): the notebook, alarm, and watcher explained without code.
|
|
161
|
+
- [FAQ](docs/faq.md): reliability, hook approval, and what still needs a human.
|
|
162
|
+
- [Auto-resume details](docs/claude-code-auto-resume.md): full behavior and limits.
|
|
163
|
+
- [Verified quota resume example](docs/verified-quota-resume-example.md): a concrete one-time reminder example.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
`cc-session-recover` is a local Claude Code recovery workflow. The highest-risk areas are Claude Code hook behavior, session-id handling, local project files, status line data, and unattended headless resume.
|
|
4
|
+
|
|
5
|
+
## Supported Versions
|
|
6
|
+
|
|
7
|
+
Security fixes target the latest release only. Early preview versions before the current `latest` tag are not supported.
|
|
8
|
+
|
|
9
|
+
Check the current version:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm view cc-session-recover version
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Reporting a Vulnerability
|
|
16
|
+
|
|
17
|
+
Do not paste secrets, prompts, transcripts, tool output, local file contents, shell output, or exploit details into a public issue.
|
|
18
|
+
|
|
19
|
+
Preferred path:
|
|
20
|
+
|
|
21
|
+
1. Use GitHub private vulnerability reporting for this repository if it is available.
|
|
22
|
+
2. If private reporting is not available, open a public issue with only a short summary and ask for a private contact path. Do not include sensitive details.
|
|
23
|
+
|
|
24
|
+
Useful report details:
|
|
25
|
+
|
|
26
|
+
- `cc-session-recover` version and install method.
|
|
27
|
+
- Claude Code version, Node version, shell, and OS.
|
|
28
|
+
- Whether the issue affects install, hooks, status line cache, `HANDOFF.md`, scheduled resume, or `scripts/quota-watcher.sh`.
|
|
29
|
+
- A minimal reproduction that uses dummy paths and dummy data.
|
|
30
|
+
- Whether any prompt, transcript, shell output, file content, local path, API key, or raw Claude session id was exposed.
|
|
31
|
+
|
|
32
|
+
## Security-Sensitive Behavior
|
|
33
|
+
|
|
34
|
+
Please treat these as security-relevant:
|
|
35
|
+
|
|
36
|
+
- Raw prompts, assistant text, tool output, shell output, command arguments, file contents, local paths, transcript paths, workspace paths, API keys, or raw Claude session ids are printed or stored in an unsafe place.
|
|
37
|
+
- Install overwrites Claude Code settings without warning or without leaving the existing file intact.
|
|
38
|
+
- Hooks run unexpected commands, change unrelated files, or write files outside the target project.
|
|
39
|
+
- `scripts/quota-watcher.sh` resumes the wrong session id.
|
|
40
|
+
- `scripts/quota-watcher.sh` runs while the interactive session is still active and causes two sessions to work on the same task.
|
|
41
|
+
- Runtime marker files are created with broad permissions or are included in version control.
|
|
42
|
+
- Shell command construction can be changed by project paths, settings, or marker contents in a way that changes execution.
|
|
43
|
+
|
|
44
|
+
These are usually not `cc-session-recover` security bugs:
|
|
45
|
+
|
|
46
|
+
- Claude Code model behavior.
|
|
47
|
+
- Claude Code authentication, billing, or quota behavior.
|
|
48
|
+
- A local user intentionally reading files from their own account.
|
|
49
|
+
- A scheduled resume failing because quota is still blocked.
|
|
50
|
+
|
|
51
|
+
## Project Privacy Invariants
|
|
52
|
+
|
|
53
|
+
`cc-session-recover` should store only the minimum state needed for recovery: handoff notes written by the user or agent, hook event metadata, rate-limit reset time, and a session id needed to resume the recorded Claude Code session.
|
|
54
|
+
|
|
55
|
+
It must not store or print raw secrets, API keys, unrelated local file contents, unrelated shell output, or unrelated Claude Code transcript content.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// npx installer for the Claude Code quota-resume workflow.
|
|
5
|
+
// Pure Node so it also works where bash is absent; the installed runtime
|
|
6
|
+
// scripts themselves are bash and need a POSIX shell (macOS, Linux, WSL,
|
|
7
|
+
// Git Bash).
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const TEMPLATE_ROOT = path.join(__dirname, '..');
|
|
13
|
+
|
|
14
|
+
const FILES = [
|
|
15
|
+
'.claude/loop.md',
|
|
16
|
+
'.claude/auto-continue.md',
|
|
17
|
+
'.claude/standing-instructions.md',
|
|
18
|
+
'.claude/settings.example.json',
|
|
19
|
+
'.claude/statusline-quota-cache.sh',
|
|
20
|
+
'.claude/hooks/log-stop-failure.sh',
|
|
21
|
+
'.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
|
+
];
|
|
31
|
+
|
|
32
|
+
const COPY_IF_MISSING = ['HANDOFF.md', 'README.md'];
|
|
33
|
+
|
|
34
|
+
function usage() {
|
|
35
|
+
console.error('Usage: cc-session-recover init [--enable-local-hook] [target-dir]');
|
|
36
|
+
process.exit(2);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function main() {
|
|
40
|
+
const args = process.argv.slice(2);
|
|
41
|
+
if (args[0] !== 'init') usage();
|
|
42
|
+
|
|
43
|
+
let enableHook = false;
|
|
44
|
+
let target = '.';
|
|
45
|
+
for (const arg of args.slice(1)) {
|
|
46
|
+
if (arg === '--enable-local-hook') enableHook = true;
|
|
47
|
+
else if (arg.startsWith('-')) usage();
|
|
48
|
+
else target = arg;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
target = path.resolve(target);
|
|
52
|
+
if (!fs.existsSync(target) || !fs.statSync(target).isDirectory()) {
|
|
53
|
+
console.error(`Target directory does not exist: ${target}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const rel of FILES) {
|
|
58
|
+
const src = path.join(TEMPLATE_ROOT, rel);
|
|
59
|
+
const dest = path.join(target, rel);
|
|
60
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
61
|
+
fs.copyFileSync(src, dest);
|
|
62
|
+
if (rel.endsWith('.sh')) {
|
|
63
|
+
try { fs.chmodSync(dest, 0o755); } catch (_) { /* no-op on Windows */ }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const rel of COPY_IF_MISSING) {
|
|
68
|
+
const dest = path.join(target, rel);
|
|
69
|
+
if (!fs.existsSync(dest)) {
|
|
70
|
+
fs.copyFileSync(path.join(TEMPLATE_ROOT, rel), dest);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (enableHook) {
|
|
75
|
+
const local = path.join(target, '.claude', 'settings.local.json');
|
|
76
|
+
if (fs.existsSync(local)) {
|
|
77
|
+
console.error('Skipped hook enablement: .claude/settings.local.json already exists.');
|
|
78
|
+
console.error('Merge .claude/settings.example.json into it manually if wanted.');
|
|
79
|
+
} else {
|
|
80
|
+
fs.copyFileSync(path.join(TEMPLATE_ROOT, '.claude', 'settings.example.json'), local);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log(`Installed Claude Code session recovery workflow into ${target}`);
|
|
85
|
+
console.log('Next: start `claude` there and approve the hooks once when asked.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
main();
|