claude-code-handoff 1.5.1 → 1.6.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/README.md +12 -9
- package/hooks/context-monitor.sh +52 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -161,7 +161,7 @@ The biggest risk with handoffs is **forgetting to save**. You're deep in a task,
|
|
|
161
161
|
|
|
162
162
|
### How It Works
|
|
163
163
|
|
|
164
|
-
A [Claude Code hook](https://docs.anthropic.com/en/docs/claude-code/hooks) runs after every Claude response (Stop event). It
|
|
164
|
+
A [Claude Code hook](https://docs.anthropic.com/en/docs/claude-code/hooks) runs after every Claude response (Stop event). It reads the **actual token count** from Claude's API usage data in the JSONL transcript and compares it against the 200K token context window. When the threshold is exceeded, it **blocks** Claude's next action and forces an immediate handoff save.
|
|
165
165
|
|
|
166
166
|
```mermaid
|
|
167
167
|
flowchart TD
|
|
@@ -178,18 +178,21 @@ flowchart TD
|
|
|
178
178
|
|
|
179
179
|
### Threshold Configuration
|
|
180
180
|
|
|
181
|
-
The threshold is configured as a **percentage of the
|
|
181
|
+
The threshold is configured as a **percentage of the 200K token context window**. The hook reads the **actual token count** from Claude's API usage data in the transcript — no guesswork, no byte-to-token estimation.
|
|
182
182
|
|
|
183
183
|
| Preset | Value | Triggers at | Best for |
|
|
184
184
|
|--------|-------|-------------|----------|
|
|
185
|
-
| **90% (default)** | `THRESHOLD_PERCENT=90` |
|
|
186
|
-
| **80%** | `THRESHOLD_PERCENT=80` |
|
|
187
|
-
| **75%** | `THRESHOLD_PERCENT=75` |
|
|
185
|
+
| **90% (default)** | `THRESHOLD_PERCENT=90` | 180K tokens | Maximizing context usage |
|
|
186
|
+
| **80%** | `THRESHOLD_PERCENT=80` | 160K tokens | Balance between space and safety |
|
|
187
|
+
| **75%** | `THRESHOLD_PERCENT=75` | 150K tokens | Short sessions, early handoff |
|
|
188
188
|
|
|
189
|
-
The calculation
|
|
189
|
+
The calculation uses real data:
|
|
190
190
|
```
|
|
191
|
-
|
|
192
|
-
THRESHOLD =
|
|
191
|
+
MAX_CONTEXT_TOKENS = 200000 (Claude Code's context window)
|
|
192
|
+
THRESHOLD = MAX_CONTEXT_TOKENS × THRESHOLD_PERCENT / 100
|
|
193
|
+
|
|
194
|
+
# The hook reads input_tokens from the last assistant message in the JSONL
|
|
195
|
+
# This is the ACTUAL context size — not an estimate
|
|
193
196
|
```
|
|
194
197
|
|
|
195
198
|
### Three Ways to Configure
|
|
@@ -572,7 +575,7 @@ A: The commands automatically summarize older sessions into a "Prior Sessions Su
|
|
|
572
575
|
A: Absolutely. They're plain markdown. You can add notes, reorder next steps, or clean up history.
|
|
573
576
|
|
|
574
577
|
**Q: How does the auto-handoff threshold work?**
|
|
575
|
-
A: The threshold is a percentage of
|
|
578
|
+
A: The threshold is a percentage of Claude Code's 200K token context window. At 90% (default), the hook triggers at 180K tokens. The hook reads the **actual token count** from Claude's API usage data — not file size estimates. You can set any value from 1-100 via env var (`CLAUDE_CONTEXT_THRESHOLD=80`) or the `/auto-handoff` command.
|
|
576
579
|
|
|
577
580
|
**Q: Can I disable auto-handoff?**
|
|
578
581
|
A: Yes. Run `/auto-handoff` and select "Disable", or manually create the file `.claude/hooks/.auto-handoff-disabled`. Delete the file to re-enable.
|
package/hooks/context-monitor.sh
CHANGED
|
@@ -9,11 +9,11 @@ if [ -f "$SCRIPT_DIR/.auto-handoff-disabled" ]; then
|
|
|
9
9
|
exit 0
|
|
10
10
|
fi
|
|
11
11
|
|
|
12
|
-
# Contexto máximo
|
|
13
|
-
|
|
12
|
+
# Contexto máximo do Claude Code (tokens)
|
|
13
|
+
MAX_CONTEXT_TOKENS=200000
|
|
14
14
|
# Threshold configurável (% do contexto). 90% padrão — maximiza uso do contexto
|
|
15
15
|
THRESHOLD_PERCENT=${CLAUDE_CONTEXT_THRESHOLD:-90}
|
|
16
|
-
|
|
16
|
+
THRESHOLD_TOKENS=$((MAX_CONTEXT_TOKENS * THRESHOLD_PERCENT / 100))
|
|
17
17
|
|
|
18
18
|
INPUT=$(cat)
|
|
19
19
|
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
|
|
@@ -28,11 +28,52 @@ if [ -z "$SESSION_ID" ]; then
|
|
|
28
28
|
exit 0
|
|
29
29
|
fi
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
# Extrai o input_tokens da última mensagem do assistente no JSONL.
|
|
32
|
+
# Isso reflete o tamanho REAL do contexto que o Claude está usando.
|
|
33
|
+
# Campos: input_tokens + cache_read_input_tokens + cache_creation_input_tokens = total input
|
|
34
|
+
CURRENT_TOKENS=0
|
|
35
|
+
if command -v python3 &>/dev/null; then
|
|
36
|
+
CURRENT_TOKENS=$(python3 -c "
|
|
37
|
+
import json, sys
|
|
38
|
+
last = 0
|
|
39
|
+
with open('$TRANSCRIPT_PATH') as f:
|
|
40
|
+
for line in f:
|
|
41
|
+
try:
|
|
42
|
+
e = json.loads(line)
|
|
43
|
+
if e.get('type') == 'assistant':
|
|
44
|
+
u = e.get('message', {}).get('usage', {})
|
|
45
|
+
t = u.get('input_tokens', 0) + u.get('cache_read_input_tokens', 0) + u.get('cache_creation_input_tokens', 0)
|
|
46
|
+
if t > 0:
|
|
47
|
+
last = t
|
|
48
|
+
except:
|
|
49
|
+
pass
|
|
50
|
+
print(last)
|
|
51
|
+
" 2>/dev/null)
|
|
52
|
+
elif command -v node &>/dev/null; then
|
|
53
|
+
CURRENT_TOKENS=$(node -e "
|
|
54
|
+
const fs = require('fs');
|
|
55
|
+
const lines = fs.readFileSync('$TRANSCRIPT_PATH', 'utf-8').trim().split('\n');
|
|
56
|
+
let last = 0;
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
try {
|
|
59
|
+
const e = JSON.parse(line);
|
|
60
|
+
if (e.type === 'assistant' && e.message?.usage) {
|
|
61
|
+
const u = e.message.usage;
|
|
62
|
+
const t = (u.input_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
63
|
+
if (t > 0) last = t;
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
console.log(last);
|
|
68
|
+
" 2>/dev/null)
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
CURRENT_TOKENS=$(echo "$CURRENT_TOKENS" | tr -d ' \n')
|
|
72
|
+
if [ -z "$CURRENT_TOKENS" ] || [ "$CURRENT_TOKENS" -eq 0 ] 2>/dev/null; then
|
|
73
|
+
exit 0
|
|
74
|
+
fi
|
|
34
75
|
|
|
35
|
-
if [ "$
|
|
76
|
+
if [ "$CURRENT_TOKENS" -lt "$THRESHOLD_TOKENS" ]; then
|
|
36
77
|
exit 0
|
|
37
78
|
fi
|
|
38
79
|
|
|
@@ -43,10 +84,13 @@ if [ -f "$FLAG" ]; then
|
|
|
43
84
|
fi
|
|
44
85
|
touch "$FLAG"
|
|
45
86
|
|
|
87
|
+
# Calcula % atual
|
|
88
|
+
CURRENT_PERCENT=$((CURRENT_TOKENS * 100 / MAX_CONTEXT_TOKENS))
|
|
89
|
+
|
|
46
90
|
# Bloqueia e força handoff
|
|
47
91
|
cat <<HOOKEOF
|
|
48
92
|
{
|
|
49
93
|
"decision": "block",
|
|
50
|
-
"reason": "⚠️ AUTO-HANDOFF: O contexto atingiu ${
|
|
94
|
+
"reason": "⚠️ AUTO-HANDOFF: O contexto atingiu ${CURRENT_PERCENT}% do limite (${CURRENT_TOKENS}/${MAX_CONTEXT_TOKENS} tokens). Você DEVE salvar o handoff AGORA.\n\nSiga estes passos IMEDIATAMENTE:\n1. Analise a conversa inteira e extraia: o que foi feito, próximos passos, arquivos-chave, decisões\n2. Escreva o handoff em .claude/handoffs/_active.md seguindo o template padrão\n3. Diga ao usuário: 'Handoff salvo automaticamente. Use /clear e depois /resume para continuar.'\n\nNÃO continue com outro trabalho até o handoff estar salvo."
|
|
51
95
|
}
|
|
52
96
|
HOOKEOF
|
package/package.json
CHANGED