claude-code-handoff 1.8.1 → 2.0.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 +3 -3
- package/cli.js +118 -70
- package/commands/auto-handoff.md +7 -5
- package/hooks/context-monitor.sh +2 -2
- package/install.sh +134 -100
- package/package.json +1 -1
- package/uninstall.sh +1 -0
package/README.md
CHANGED
|
@@ -253,7 +253,7 @@ THRESHOLD_PERCENT=${CLAUDE_CONTEXT_THRESHOLD:-90} # change 90 to your value
|
|
|
253
253
|
|
|
254
254
|
- **One-shot trigger**: A flag file in `/tmp` (per session ID) prevents infinite loops — the hook triggers exactly once per session, even if Claude's handoff response pushes the transcript further
|
|
255
255
|
- **Session cleanup**: A `SessionStart` hook automatically cleans up stale flag files older than 24 hours
|
|
256
|
-
- **
|
|
256
|
+
- **Opt-in design**: Auto-handoff only runs when `.claude/hooks/.auto-handoff-enabled` exists (created by `/auto-handoff`). No file = disabled by default
|
|
257
257
|
- **Non-destructive**: The hook only blocks and instructs — it never modifies files directly. Claude performs the actual handoff save
|
|
258
258
|
|
|
259
259
|
### What Happens When It Triggers
|
|
@@ -311,7 +311,7 @@ your-project/
|
|
|
311
311
|
├── hooks/
|
|
312
312
|
│ ├── context-monitor.sh ← Stop hook (monitors context size)
|
|
313
313
|
│ ├── session-cleanup.sh ← SessionStart hook (cleans old flags)
|
|
314
|
-
│ └── .auto-handoff-
|
|
314
|
+
│ └── .auto-handoff-enabled ← Enable flag (created by /auto-handoff)
|
|
315
315
|
├── settings.json ← Hook configuration
|
|
316
316
|
└── handoffs/ ← Session state (gitignored)
|
|
317
317
|
├── _active.md ← Current workstream
|
|
@@ -604,7 +604,7 @@ A: Absolutely. They're plain markdown. You can add notes, reorder next steps, or
|
|
|
604
604
|
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.
|
|
605
605
|
|
|
606
606
|
**Q: Can I disable auto-handoff?**
|
|
607
|
-
A: Yes. Run `/auto-handoff` and select "Disable", or manually
|
|
607
|
+
A: Yes. Run `/auto-handoff` and select "Disable", or manually delete `.claude/hooks/.auto-handoff-enabled`. Without this file, auto-handoff is off.
|
|
608
608
|
|
|
609
609
|
**Q: What if auto-handoff triggers too early/late?**
|
|
610
610
|
A: Adjust the threshold. If it triggers too early, increase to 95%. If you're running out of context before it triggers, lower to 80% or 75%. Use `/auto-handoff` to change it interactively.
|
package/cli.js
CHANGED
|
@@ -3,24 +3,35 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
// Colors (RGB)
|
|
7
|
+
const AMBER = '\x1b[38;2;245;158;11m';
|
|
8
|
+
const GREEN = '\x1b[38;2;16;185;129m';
|
|
9
|
+
const RED = '\x1b[38;2;239;68;68m';
|
|
10
|
+
const CYAN = '\x1b[38;2;34;211;238m';
|
|
11
|
+
const WHITE = '\x1b[37m';
|
|
12
|
+
const GRAY = '\x1b[90m';
|
|
13
|
+
const BOLD = '\x1b[1m';
|
|
14
|
+
const DIM = '\x1b[2m';
|
|
9
15
|
const NC = '\x1b[0m';
|
|
10
16
|
|
|
17
|
+
const ok = (msg) => console.log(` ${GREEN}✓${NC} ${msg}`);
|
|
18
|
+
const fail = (msg) => console.log(` ${RED}✗${NC} ${msg}`);
|
|
19
|
+
const info = (msg) => console.log(` ${GRAY}${msg}${NC}`);
|
|
20
|
+
const head = (msg) => console.log(`\n ${AMBER}${BOLD}${msg}${NC}`);
|
|
21
|
+
|
|
11
22
|
const PROJECT_DIR = process.cwd();
|
|
12
23
|
const CLAUDE_DIR = path.join(PROJECT_DIR, '.claude');
|
|
13
24
|
const SCRIPT_DIR = __dirname;
|
|
14
25
|
|
|
26
|
+
// ─── Banner ───────────────────────────────────────────
|
|
15
27
|
console.log('');
|
|
16
|
-
console.log(
|
|
17
|
-
console.log(
|
|
18
|
-
console.log(
|
|
19
|
-
console.log(
|
|
20
|
-
console.log(` Project: ${GREEN}${PROJECT_DIR}${NC}`);
|
|
28
|
+
console.log(` ${AMBER}${BOLD}┌──────────────────────────────────────┐${NC}`);
|
|
29
|
+
console.log(` ${AMBER}${BOLD}│${NC} ${WHITE}${BOLD}claude-code-handoff${NC} ${DIM}v1.9${NC} ${AMBER}${BOLD}│${NC}`);
|
|
30
|
+
console.log(` ${AMBER}${BOLD}│${NC} ${GRAY}Session Continuity for Claude Code${NC} ${AMBER}${BOLD}│${NC}`);
|
|
31
|
+
console.log(` ${AMBER}${BOLD}└──────────────────────────────────────┘${NC}`);
|
|
21
32
|
console.log('');
|
|
33
|
+
info(`Project: ${WHITE}${PROJECT_DIR}${NC}`);
|
|
22
34
|
|
|
23
|
-
// Helper
|
|
24
35
|
function ensureDir(dir) {
|
|
25
36
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
26
37
|
}
|
|
@@ -30,48 +41,62 @@ function copyFile(src, dst) {
|
|
|
30
41
|
if (fs.existsSync(srcPath)) {
|
|
31
42
|
fs.copyFileSync(srcPath, dst);
|
|
32
43
|
} else {
|
|
33
|
-
|
|
44
|
+
fail(`${src} not found in package`);
|
|
34
45
|
process.exit(1);
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
//
|
|
39
|
-
|
|
49
|
+
// ─── Pre-flight ───────────────────────────────────────
|
|
50
|
+
head('Pre-flight');
|
|
51
|
+
|
|
52
|
+
const monitorPath = path.join(CLAUDE_DIR, 'hooks', 'context-monitor.sh');
|
|
53
|
+
let isReinstall = false;
|
|
54
|
+
let savedThreshold = '';
|
|
55
|
+
let savedMaxContext = '';
|
|
56
|
+
if (fs.existsSync(monitorPath)) {
|
|
57
|
+
isReinstall = true;
|
|
58
|
+
const oldContent = fs.readFileSync(monitorPath, 'utf-8');
|
|
59
|
+
const thresholdMatch = oldContent.match(/CLAUDE_CONTEXT_THRESHOLD:-(\d+)/);
|
|
60
|
+
const maxContextMatch = oldContent.match(/CLAUDE_MAX_CONTEXT:-(\d+)/);
|
|
61
|
+
if (thresholdMatch) savedThreshold = thresholdMatch[1];
|
|
62
|
+
if (maxContextMatch) savedMaxContext = maxContextMatch[1];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (isReinstall) {
|
|
66
|
+
info('Existing installation found. Upgrading...');
|
|
67
|
+
} else {
|
|
68
|
+
ok('Fresh install');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Directories ──────────────────────────────────────
|
|
72
|
+
head('Creating directories');
|
|
73
|
+
|
|
40
74
|
ensureDir(path.join(CLAUDE_DIR, 'commands'));
|
|
41
75
|
ensureDir(path.join(CLAUDE_DIR, 'rules'));
|
|
42
76
|
ensureDir(path.join(CLAUDE_DIR, 'hooks'));
|
|
43
77
|
ensureDir(path.join(CLAUDE_DIR, 'handoffs', 'archive'));
|
|
78
|
+
ok('Directory structure created');
|
|
79
|
+
|
|
80
|
+
// ─── Commands ─────────────────────────────────────────
|
|
81
|
+
head('Installing commands');
|
|
44
82
|
|
|
45
|
-
// 2. Copy commands
|
|
46
|
-
console.log(` ${YELLOW}[2/10]${NC} Installing commands...`);
|
|
47
83
|
copyFile('commands/resume.md', path.join(CLAUDE_DIR, 'commands', 'resume.md'));
|
|
48
84
|
copyFile('commands/save-handoff.md', path.join(CLAUDE_DIR, 'commands', 'save-handoff.md'));
|
|
49
85
|
copyFile('commands/switch-context.md', path.join(CLAUDE_DIR, 'commands', 'switch-context.md'));
|
|
50
86
|
copyFile('commands/handoff.md', path.join(CLAUDE_DIR, 'commands', 'handoff.md'));
|
|
51
87
|
copyFile('commands/delete-handoff.md', path.join(CLAUDE_DIR, 'commands', 'delete-handoff.md'));
|
|
52
88
|
copyFile('commands/auto-handoff.md', path.join(CLAUDE_DIR, 'commands', 'auto-handoff.md'));
|
|
89
|
+
ok('6 slash commands installed');
|
|
90
|
+
|
|
91
|
+
// ─── Rules ────────────────────────────────────────────
|
|
92
|
+
head('Installing rules');
|
|
53
93
|
|
|
54
|
-
// 3. Copy rules
|
|
55
|
-
console.log(` ${YELLOW}[3/10]${NC} Installing rules...`);
|
|
56
94
|
copyFile('rules/session-continuity.md', path.join(CLAUDE_DIR, 'rules', 'session-continuity.md'));
|
|
57
95
|
copyFile('rules/auto-handoff.md', path.join(CLAUDE_DIR, 'rules', 'auto-handoff.md'));
|
|
96
|
+
ok('Behavioral rules installed');
|
|
58
97
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Detect reinstall: save user's custom settings before overwriting
|
|
63
|
-
const monitorPath = path.join(CLAUDE_DIR, 'hooks', 'context-monitor.sh');
|
|
64
|
-
let isReinstall = false;
|
|
65
|
-
let savedThreshold = '';
|
|
66
|
-
let savedMaxContext = '';
|
|
67
|
-
if (fs.existsSync(monitorPath)) {
|
|
68
|
-
isReinstall = true;
|
|
69
|
-
const oldContent = fs.readFileSync(monitorPath, 'utf-8');
|
|
70
|
-
const thresholdMatch = oldContent.match(/CLAUDE_CONTEXT_THRESHOLD:-(\d+)/);
|
|
71
|
-
const maxContextMatch = oldContent.match(/CLAUDE_MAX_CONTEXT:-(\d+)/);
|
|
72
|
-
if (thresholdMatch) savedThreshold = thresholdMatch[1];
|
|
73
|
-
if (maxContextMatch) savedMaxContext = maxContextMatch[1];
|
|
74
|
-
}
|
|
98
|
+
// ─── Hooks ────────────────────────────────────────────
|
|
99
|
+
head('Installing hooks');
|
|
75
100
|
|
|
76
101
|
copyFile('hooks/context-monitor.sh', monitorPath);
|
|
77
102
|
copyFile('hooks/session-cleanup.sh', path.join(CLAUDE_DIR, 'hooks', 'session-cleanup.sh'));
|
|
@@ -79,25 +104,26 @@ fs.chmodSync(monitorPath, 0o755);
|
|
|
79
104
|
fs.chmodSync(path.join(CLAUDE_DIR, 'hooks', 'session-cleanup.sh'), 0o755);
|
|
80
105
|
|
|
81
106
|
if (isReinstall) {
|
|
82
|
-
// Restore user's custom settings
|
|
83
107
|
let content = fs.readFileSync(monitorPath, 'utf-8');
|
|
84
108
|
if (savedThreshold && savedThreshold !== '90') {
|
|
85
109
|
content = content.replace('CLAUDE_CONTEXT_THRESHOLD:-90', `CLAUDE_CONTEXT_THRESHOLD:-${savedThreshold}`);
|
|
86
|
-
|
|
110
|
+
ok(`Preserved threshold: ${CYAN}${savedThreshold}%${NC}`);
|
|
87
111
|
}
|
|
88
112
|
if (savedMaxContext && savedMaxContext !== '200000') {
|
|
89
113
|
content = content.replace('CLAUDE_MAX_CONTEXT:-200000', `CLAUDE_MAX_CONTEXT:-${savedMaxContext}`);
|
|
90
|
-
|
|
114
|
+
ok(`Preserved max context: ${CYAN}${savedMaxContext} tokens${NC}`);
|
|
91
115
|
}
|
|
92
116
|
fs.writeFileSync(monitorPath, content);
|
|
93
|
-
// Don't touch .auto-handoff-disabled — preserve user's on/off choice
|
|
94
|
-
} else {
|
|
95
|
-
// Fresh install: auto-handoff disabled by default (beta feature)
|
|
96
|
-
fs.writeFileSync(path.join(CLAUDE_DIR, 'hooks', '.auto-handoff-disabled'), '');
|
|
97
117
|
}
|
|
98
118
|
|
|
99
|
-
//
|
|
100
|
-
|
|
119
|
+
// Clean up legacy disabled flag
|
|
120
|
+
const legacyDisabled = path.join(CLAUDE_DIR, 'hooks', '.auto-handoff-disabled');
|
|
121
|
+
if (fs.existsSync(legacyDisabled)) fs.unlinkSync(legacyDisabled);
|
|
122
|
+
ok('Context monitor + session cleanup hooks');
|
|
123
|
+
|
|
124
|
+
// ─── Settings ─────────────────────────────────────────
|
|
125
|
+
head('Configuring settings.json');
|
|
126
|
+
|
|
101
127
|
const settingsPath = path.join(CLAUDE_DIR, 'settings.json');
|
|
102
128
|
const hooksConfig = {
|
|
103
129
|
Stop: [{ hooks: [{ type: 'command', command: '"$CLAUDE_PROJECT_DIR/.claude/hooks/context-monitor.sh"', timeout: 10 }] }],
|
|
@@ -108,15 +134,20 @@ if (fs.existsSync(settingsPath)) {
|
|
|
108
134
|
if (!JSON.stringify(settings).includes('context-monitor')) {
|
|
109
135
|
settings.hooks = hooksConfig;
|
|
110
136
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
137
|
+
ok('Hooks added to existing settings.json');
|
|
138
|
+
} else {
|
|
139
|
+
ok('Hooks already configured');
|
|
111
140
|
}
|
|
112
141
|
} else {
|
|
113
142
|
fs.writeFileSync(settingsPath, JSON.stringify({ hooks: hooksConfig }, null, 2) + '\n');
|
|
143
|
+
ok('settings.json created with hooks');
|
|
114
144
|
}
|
|
115
145
|
|
|
116
|
-
//
|
|
146
|
+
// ─── Handoff ──────────────────────────────────────────
|
|
147
|
+
head('Setting up handoff storage');
|
|
148
|
+
|
|
117
149
|
const activePath = path.join(CLAUDE_DIR, 'handoffs', '_active.md');
|
|
118
150
|
if (!fs.existsSync(activePath)) {
|
|
119
|
-
console.log(` ${YELLOW}[6/10]${NC} Creating initial handoff...`);
|
|
120
151
|
fs.writeFileSync(activePath, `# Session Handoff
|
|
121
152
|
|
|
122
153
|
> No active session yet. Use \`/handoff\` or \`/save-handoff\` to save your first session state.
|
|
@@ -142,24 +173,31 @@ if (!fs.existsSync(activePath)) {
|
|
|
142
173
|
## Decisions Registry
|
|
143
174
|
(none)
|
|
144
175
|
`);
|
|
176
|
+
ok('Initial handoff template created');
|
|
145
177
|
} else {
|
|
146
|
-
|
|
178
|
+
ok('Existing handoff preserved');
|
|
147
179
|
}
|
|
148
180
|
|
|
149
|
-
//
|
|
150
|
-
|
|
181
|
+
// ─── Gitignore ────────────────────────────────────────
|
|
182
|
+
head('Updating .gitignore');
|
|
183
|
+
|
|
151
184
|
const gitignorePath = path.join(PROJECT_DIR, '.gitignore');
|
|
152
185
|
if (fs.existsSync(gitignorePath)) {
|
|
153
186
|
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
154
187
|
if (!content.includes('.claude/handoffs/')) {
|
|
155
188
|
fs.appendFileSync(gitignorePath, '\n# claude-code-handoff (personal session state)\n.claude/handoffs/\n');
|
|
189
|
+
ok('Added .claude/handoffs/ to .gitignore');
|
|
190
|
+
} else {
|
|
191
|
+
ok('Already in .gitignore');
|
|
156
192
|
}
|
|
157
193
|
} else {
|
|
158
194
|
fs.writeFileSync(gitignorePath, '# claude-code-handoff (personal session state)\n.claude/handoffs/\n');
|
|
195
|
+
ok('.gitignore created');
|
|
159
196
|
}
|
|
160
197
|
|
|
161
|
-
//
|
|
162
|
-
|
|
198
|
+
// ─── CLAUDE.md ────────────────────────────────────────
|
|
199
|
+
head('Updating CLAUDE.md');
|
|
200
|
+
|
|
163
201
|
const claudeMdPath = path.join(CLAUDE_DIR, 'CLAUDE.md');
|
|
164
202
|
const continuityBlock = `## Session Continuity (MANDATORY)
|
|
165
203
|
|
|
@@ -178,13 +216,26 @@ if (fs.existsSync(claudeMdPath)) {
|
|
|
178
216
|
} else {
|
|
179
217
|
fs.appendFileSync(claudeMdPath, '\n' + continuityBlock + '\n');
|
|
180
218
|
}
|
|
219
|
+
ok('Session Continuity section added');
|
|
220
|
+
} else {
|
|
221
|
+
ok('Session Continuity already present');
|
|
181
222
|
}
|
|
182
223
|
} else {
|
|
183
224
|
fs.writeFileSync(claudeMdPath, `# Project Rules\n\n${continuityBlock}\n`);
|
|
225
|
+
ok('CLAUDE.md created');
|
|
184
226
|
}
|
|
185
227
|
|
|
186
|
-
//
|
|
187
|
-
|
|
228
|
+
// ─── Legacy cleanup ──────────────────────────────────
|
|
229
|
+
let cleaned = 0;
|
|
230
|
+
for (const f of ['retomar.md', 'salvar-handoff.md', 'trocar-contexto.md', 'auto-handoff-toggle.md']) {
|
|
231
|
+
const fp = path.join(CLAUDE_DIR, 'commands', f);
|
|
232
|
+
if (fs.existsSync(fp)) { fs.unlinkSync(fp); cleaned++; }
|
|
233
|
+
}
|
|
234
|
+
if (cleaned > 0) info(`Removed ${cleaned} legacy command(s)`);
|
|
235
|
+
|
|
236
|
+
// ─── Verify ───────────────────────────────────────────
|
|
237
|
+
head('Verifying');
|
|
238
|
+
|
|
188
239
|
let installed = 0;
|
|
189
240
|
for (const f of ['resume.md', 'save-handoff.md', 'switch-context.md', 'handoff.md', 'delete-handoff.md', 'auto-handoff.md']) {
|
|
190
241
|
if (fs.existsSync(path.join(CLAUDE_DIR, 'commands', f))) installed++;
|
|
@@ -193,31 +244,28 @@ let hooksOk = 0;
|
|
|
193
244
|
if (fs.existsSync(path.join(CLAUDE_DIR, 'hooks', 'context-monitor.sh'))) hooksOk++;
|
|
194
245
|
if (fs.existsSync(path.join(CLAUDE_DIR, 'hooks', 'session-cleanup.sh'))) hooksOk++;
|
|
195
246
|
|
|
196
|
-
console.log('');
|
|
197
|
-
console.log(` ${YELLOW}[10/10]${NC} Done!`);
|
|
198
|
-
console.log('');
|
|
199
247
|
if (installed === 6 && hooksOk === 2) {
|
|
200
|
-
|
|
248
|
+
ok(`${installed}/6 commands, ${hooksOk}/2 hooks`);
|
|
201
249
|
} else {
|
|
202
|
-
|
|
250
|
+
fail(`Partial: ${installed}/6 commands, ${hooksOk}/2 hooks`);
|
|
203
251
|
}
|
|
252
|
+
|
|
253
|
+
// ─── Done ─────────────────────────────────────────────
|
|
204
254
|
console.log('');
|
|
205
|
-
console.log(
|
|
206
|
-
console.log(`
|
|
207
|
-
console.log(`
|
|
208
|
-
console.log(` ${CYAN}/save-handoff${NC} Save session state (wizard)`);
|
|
209
|
-
console.log(` ${CYAN}/switch-context${NC} Switch workstream`);
|
|
210
|
-
console.log(` ${CYAN}/delete-handoff${NC} Delete handoff(s)`);
|
|
211
|
-
console.log(` ${CYAN}/auto-handoff${NC} Toggle auto-handoff on/off`);
|
|
255
|
+
console.log(` ${AMBER}${BOLD}════════════════════════════════════════${NC}`);
|
|
256
|
+
console.log(` ${GREEN}${BOLD} Installed successfully!${NC}`);
|
|
257
|
+
console.log(` ${AMBER}${BOLD}════════════════════════════════════════${NC}`);
|
|
212
258
|
console.log('');
|
|
213
|
-
console.log(`
|
|
214
|
-
console.log(`
|
|
259
|
+
console.log(` ${WHITE}${BOLD}Commands:${NC}`);
|
|
260
|
+
console.log(` ${CYAN}/handoff${NC} ${GRAY}Auto-save session${NC}`);
|
|
261
|
+
console.log(` ${CYAN}/resume${NC} ${GRAY}Resume with wizard${NC}`);
|
|
262
|
+
console.log(` ${CYAN}/save-handoff${NC} ${GRAY}Save with options${NC}`);
|
|
263
|
+
console.log(` ${CYAN}/switch-context${NC} ${GRAY}Switch workstream${NC}`);
|
|
264
|
+
console.log(` ${CYAN}/delete-handoff${NC} ${GRAY}Delete handoff(s)${NC}`);
|
|
265
|
+
console.log(` ${CYAN}/auto-handoff${NC} ${GRAY}Toggle auto-handoff${NC}`);
|
|
215
266
|
console.log('');
|
|
216
|
-
console.log(
|
|
217
|
-
console.log(
|
|
218
|
-
console.log(' .claude/rules/ session-continuity.md, auto-handoff.md');
|
|
219
|
-
console.log(' .claude/hooks/ context-monitor.sh, session-cleanup.sh');
|
|
220
|
-
console.log(' .claude/handoffs/ session state (gitignored)');
|
|
267
|
+
console.log(` ${WHITE}${BOLD}Auto-handoff:${NC} ${DIM}beta — disabled by default${NC}`);
|
|
268
|
+
console.log(` ${GRAY}Run ${CYAN}/auto-handoff${GRAY} inside Claude Code to enable${NC}`);
|
|
221
269
|
console.log('');
|
|
222
|
-
console.log(` ${
|
|
270
|
+
console.log(` ${DIM}Start Claude Code and use ${WHITE}/resume${DIM} to begin.${NC}`);
|
|
223
271
|
console.log('');
|
package/commands/auto-handoff.md
CHANGED
|
@@ -6,9 +6,9 @@ Toggle the automatic handoff context monitor on/off, configure threshold, and se
|
|
|
6
6
|
|
|
7
7
|
### Step 1: Check current state
|
|
8
8
|
|
|
9
|
-
Check if `.claude/hooks/.auto-handoff-
|
|
10
|
-
- If exists → currently
|
|
11
|
-
- If not exists → currently
|
|
9
|
+
Check if `.claude/hooks/.auto-handoff-enabled` exists:
|
|
10
|
+
- If exists → currently ENABLED
|
|
11
|
+
- If not exists → currently DISABLED (default)
|
|
12
12
|
|
|
13
13
|
Also read from `.claude/hooks/context-monitor.sh`:
|
|
14
14
|
- `THRESHOLD_PERCENT` value (the default in `THRESHOLD_PERCENT=${CLAUDE_CONTEXT_THRESHOLD:-XX}`)
|
|
@@ -30,7 +30,9 @@ Use AskUserQuestion:
|
|
|
30
30
|
### Step 3: Execute
|
|
31
31
|
|
|
32
32
|
#### Toggle (Ativar/Desativar):
|
|
33
|
-
-
|
|
33
|
+
- Ativar: create `.claude/hooks/.auto-handoff-enabled`
|
|
34
|
+
- Desativar: delete `.claude/hooks/.auto-handoff-enabled`
|
|
35
|
+
- Also delete legacy `.claude/hooks/.auto-handoff-disabled` if it exists
|
|
34
36
|
|
|
35
37
|
#### Ajustar threshold:
|
|
36
38
|
Ask with AskUserQuestion:
|
|
@@ -52,7 +54,7 @@ Ask with AskUserQuestion:
|
|
|
52
54
|
- Update the `MAX_CONTEXT_TOKENS` default value in `context-monitor.sh` by changing `MAX_CONTEXT_TOKENS=${CLAUDE_MAX_CONTEXT:-XXXXXX}` to the chosen value
|
|
53
55
|
|
|
54
56
|
#### Ativar com configuração customizada:
|
|
55
|
-
Run both "Alterar plano" and "Ajustar threshold" flows above, then
|
|
57
|
+
Run both "Alterar plano" and "Ajustar threshold" flows above, then create `.claude/hooks/.auto-handoff-enabled`
|
|
56
58
|
|
|
57
59
|
### Step 4: Confirm
|
|
58
60
|
|
package/hooks/context-monitor.sh
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
# Detecta quando o contexto está próximo do limite e força o salvamento do handoff.
|
|
4
4
|
# Usado como hook "Stop" do Claude Code.
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Auto-handoff is opt-in: only runs if explicitly enabled
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
if [ -f "$SCRIPT_DIR/.auto-handoff-
|
|
8
|
+
if [ ! -f "$SCRIPT_DIR/.auto-handoff-enabled" ]; then
|
|
9
9
|
exit 0
|
|
10
10
|
fi
|
|
11
11
|
|
package/install.sh
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# git clone https://github.com/eximIA-Ventures/claude-code-handoff.git /tmp/claude-code-handoff
|
|
7
|
-
# cd /your/project && /tmp/claude-code-handoff/install.sh
|
|
2
|
+
# ═══════════════════════════════════════════════════════
|
|
3
|
+
# claude-code-handoff — Installer
|
|
4
|
+
# Usage: curl -fsSL https://raw.githubusercontent.com/eximIA-Ventures/claude-code-handoff/main/install.sh | bash
|
|
5
|
+
# ═══════════════════════════════════════════════════════
|
|
8
6
|
|
|
9
7
|
set -e
|
|
10
8
|
|
|
11
|
-
# Colors
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
# Colors (RGB)
|
|
10
|
+
AMBER='\033[38;2;245;158;11m'
|
|
11
|
+
GREEN='\033[38;2;16;185;129m'
|
|
12
|
+
RED='\033[38;2;239;68;68m'
|
|
13
|
+
CYAN='\033[38;2;34;211;238m'
|
|
14
|
+
WHITE='\033[37m'
|
|
15
|
+
GRAY='\033[90m'
|
|
16
|
+
BOLD='\033[1m'
|
|
17
|
+
DIM='\033[2m'
|
|
18
|
+
RESET='\033[0m'
|
|
16
19
|
|
|
17
20
|
REPO="eximIA-Ventures/claude-code-handoff"
|
|
18
21
|
BRANCH="main"
|
|
@@ -20,13 +23,19 @@ RAW_BASE="https://raw.githubusercontent.com/$REPO/$BRANCH"
|
|
|
20
23
|
PROJECT_DIR="$(pwd)"
|
|
21
24
|
CLAUDE_DIR="$PROJECT_DIR/.claude"
|
|
22
25
|
|
|
26
|
+
ok() { echo -e " ${GREEN}✓${RESET} $1"; }
|
|
27
|
+
fail() { echo -e " ${RED}✗${RESET} $1"; }
|
|
28
|
+
info() { echo -e " ${GRAY}$1${RESET}"; }
|
|
29
|
+
head() { echo -e "\n ${AMBER}${BOLD}$1${RESET}"; }
|
|
30
|
+
|
|
31
|
+
# ─── Banner ───────────────────────────────────────────
|
|
23
32
|
echo ""
|
|
24
|
-
echo -e "${
|
|
25
|
-
echo -e "${
|
|
26
|
-
echo -e "${
|
|
27
|
-
echo ""
|
|
28
|
-
echo -e " Project: ${GREEN}$PROJECT_DIR${NC}"
|
|
33
|
+
echo -e " ${AMBER}${BOLD}┌──────────────────────────────────────┐${RESET}"
|
|
34
|
+
echo -e " ${AMBER}${BOLD}│${RESET} ${WHITE}${BOLD}claude-code-handoff${RESET} ${DIM}v1.9${RESET} ${AMBER}${BOLD}│${RESET}"
|
|
35
|
+
echo -e " ${AMBER}${BOLD}│${RESET} ${GRAY}Session Continuity for Claude Code${RESET} ${AMBER}${BOLD}│${RESET}"
|
|
36
|
+
echo -e " ${AMBER}${BOLD}└──────────────────────────────────────┘${RESET}"
|
|
29
37
|
echo ""
|
|
38
|
+
info "Project: ${WHITE}$PROJECT_DIR${RESET}"
|
|
30
39
|
|
|
31
40
|
# Detect if running from cloned repo or via curl
|
|
32
41
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" 2>/dev/null && pwd 2>/dev/null || echo "")"
|
|
@@ -34,49 +43,61 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" 2>/dev/null && pwd
|
|
|
34
43
|
download_file() {
|
|
35
44
|
local src="$1"
|
|
36
45
|
local dst="$2"
|
|
37
|
-
|
|
38
|
-
# If running from cloned repo, copy locally
|
|
39
46
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/$src" ]; then
|
|
40
47
|
cp "$SCRIPT_DIR/$src" "$dst"
|
|
41
48
|
else
|
|
42
|
-
# Download from GitHub
|
|
43
49
|
curl -fsSL "$RAW_BASE/$src" -o "$dst"
|
|
44
50
|
fi
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
#
|
|
48
|
-
|
|
53
|
+
# ─── Pre-flight ───────────────────────────────────────
|
|
54
|
+
head "Pre-flight"
|
|
55
|
+
|
|
56
|
+
# Detect reinstall
|
|
57
|
+
IS_REINSTALL=false
|
|
58
|
+
SAVED_THRESHOLD=""
|
|
59
|
+
SAVED_MAX_CONTEXT=""
|
|
60
|
+
if [ -f "$CLAUDE_DIR/hooks/context-monitor.sh" ]; then
|
|
61
|
+
IS_REINSTALL=true
|
|
62
|
+
SAVED_THRESHOLD=$(grep -oP 'CLAUDE_CONTEXT_THRESHOLD:-\K[0-9]+' "$CLAUDE_DIR/hooks/context-monitor.sh" 2>/dev/null || echo "")
|
|
63
|
+
SAVED_MAX_CONTEXT=$(grep -oP 'CLAUDE_MAX_CONTEXT:-\K[0-9]+' "$CLAUDE_DIR/hooks/context-monitor.sh" 2>/dev/null || echo "")
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if [ "$IS_REINSTALL" = true ]; then
|
|
67
|
+
info "Existing installation found. Upgrading..."
|
|
68
|
+
else
|
|
69
|
+
ok "Fresh install"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ─── Directories ──────────────────────────────────────
|
|
73
|
+
head "Creating directories"
|
|
74
|
+
|
|
49
75
|
mkdir -p "$CLAUDE_DIR/commands"
|
|
50
76
|
mkdir -p "$CLAUDE_DIR/rules"
|
|
51
77
|
mkdir -p "$CLAUDE_DIR/hooks"
|
|
52
78
|
mkdir -p "$CLAUDE_DIR/handoffs/archive"
|
|
79
|
+
ok "Directory structure created"
|
|
80
|
+
|
|
81
|
+
# ─── Commands ─────────────────────────────────────────
|
|
82
|
+
head "Installing commands"
|
|
53
83
|
|
|
54
|
-
# 2. Download/copy commands
|
|
55
|
-
echo -e " ${YELLOW}[2/10]${NC} Installing commands..."
|
|
56
84
|
download_file "commands/resume.md" "$CLAUDE_DIR/commands/resume.md"
|
|
57
85
|
download_file "commands/save-handoff.md" "$CLAUDE_DIR/commands/save-handoff.md"
|
|
58
86
|
download_file "commands/switch-context.md" "$CLAUDE_DIR/commands/switch-context.md"
|
|
59
87
|
download_file "commands/handoff.md" "$CLAUDE_DIR/commands/handoff.md"
|
|
60
88
|
download_file "commands/delete-handoff.md" "$CLAUDE_DIR/commands/delete-handoff.md"
|
|
61
89
|
download_file "commands/auto-handoff.md" "$CLAUDE_DIR/commands/auto-handoff.md"
|
|
90
|
+
ok "6 slash commands installed"
|
|
91
|
+
|
|
92
|
+
# ─── Rules ────────────────────────────────────────────
|
|
93
|
+
head "Installing rules"
|
|
62
94
|
|
|
63
|
-
# 3. Download/copy rules
|
|
64
|
-
echo -e " ${YELLOW}[3/10]${NC} Installing rules..."
|
|
65
95
|
download_file "rules/session-continuity.md" "$CLAUDE_DIR/rules/session-continuity.md"
|
|
66
96
|
download_file "rules/auto-handoff.md" "$CLAUDE_DIR/rules/auto-handoff.md"
|
|
97
|
+
ok "Behavioral rules installed"
|
|
67
98
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# Detect reinstall: save user's custom settings before overwriting
|
|
72
|
-
IS_REINSTALL=false
|
|
73
|
-
SAVED_THRESHOLD=""
|
|
74
|
-
SAVED_MAX_CONTEXT=""
|
|
75
|
-
if [ -f "$CLAUDE_DIR/hooks/context-monitor.sh" ]; then
|
|
76
|
-
IS_REINSTALL=true
|
|
77
|
-
SAVED_THRESHOLD=$(grep -oP 'CLAUDE_CONTEXT_THRESHOLD:-\K[0-9]+' "$CLAUDE_DIR/hooks/context-monitor.sh" 2>/dev/null || echo "")
|
|
78
|
-
SAVED_MAX_CONTEXT=$(grep -oP 'CLAUDE_MAX_CONTEXT:-\K[0-9]+' "$CLAUDE_DIR/hooks/context-monitor.sh" 2>/dev/null || echo "")
|
|
79
|
-
fi
|
|
99
|
+
# ─── Hooks ────────────────────────────────────────────
|
|
100
|
+
head "Installing hooks"
|
|
80
101
|
|
|
81
102
|
download_file "hooks/context-monitor.sh" "$CLAUDE_DIR/hooks/context-monitor.sh"
|
|
82
103
|
download_file "hooks/session-cleanup.sh" "$CLAUDE_DIR/hooks/session-cleanup.sh"
|
|
@@ -84,30 +105,28 @@ chmod +x "$CLAUDE_DIR/hooks/context-monitor.sh"
|
|
|
84
105
|
chmod +x "$CLAUDE_DIR/hooks/session-cleanup.sh"
|
|
85
106
|
|
|
86
107
|
if [ "$IS_REINSTALL" = true ]; then
|
|
87
|
-
# Restore user's custom settings
|
|
88
108
|
if [ -n "$SAVED_THRESHOLD" ] && [ "$SAVED_THRESHOLD" != "90" ]; then
|
|
89
109
|
sed -i.bak "s/CLAUDE_CONTEXT_THRESHOLD:-90/CLAUDE_CONTEXT_THRESHOLD:-${SAVED_THRESHOLD}/" "$CLAUDE_DIR/hooks/context-monitor.sh"
|
|
90
110
|
rm -f "$CLAUDE_DIR/hooks/context-monitor.sh.bak"
|
|
91
|
-
|
|
111
|
+
ok "Preserved threshold: ${CYAN}${SAVED_THRESHOLD}%${RESET}"
|
|
92
112
|
fi
|
|
93
113
|
if [ -n "$SAVED_MAX_CONTEXT" ] && [ "$SAVED_MAX_CONTEXT" != "200000" ]; then
|
|
94
114
|
sed -i.bak "s/CLAUDE_MAX_CONTEXT:-200000/CLAUDE_MAX_CONTEXT:-${SAVED_MAX_CONTEXT}/" "$CLAUDE_DIR/hooks/context-monitor.sh"
|
|
95
115
|
rm -f "$CLAUDE_DIR/hooks/context-monitor.sh.bak"
|
|
96
|
-
|
|
116
|
+
ok "Preserved max context: ${CYAN}${SAVED_MAX_CONTEXT} tokens${RESET}"
|
|
97
117
|
fi
|
|
98
|
-
# Don't touch .auto-handoff-disabled — preserve user's on/off choice
|
|
99
|
-
else
|
|
100
|
-
# Fresh install: auto-handoff disabled by default (beta feature)
|
|
101
|
-
touch "$CLAUDE_DIR/hooks/.auto-handoff-disabled"
|
|
102
118
|
fi
|
|
103
119
|
|
|
104
|
-
#
|
|
105
|
-
|
|
120
|
+
# Clean up legacy disabled flag
|
|
121
|
+
rm -f "$CLAUDE_DIR/hooks/.auto-handoff-disabled"
|
|
122
|
+
ok "Context monitor + session cleanup hooks"
|
|
123
|
+
|
|
124
|
+
# ─── Settings ─────────────────────────────────────────
|
|
125
|
+
head "Configuring settings.json"
|
|
126
|
+
|
|
106
127
|
SETTINGS_FILE="$CLAUDE_DIR/settings.json"
|
|
107
128
|
if [ -f "$SETTINGS_FILE" ]; then
|
|
108
|
-
# Check if hooks already configured
|
|
109
129
|
if ! grep -q "context-monitor" "$SETTINGS_FILE" 2>/dev/null; then
|
|
110
|
-
# Merge hooks into existing settings.json using jq if available
|
|
111
130
|
if command -v jq &>/dev/null; then
|
|
112
131
|
HOOKS_JSON='{
|
|
113
132
|
"hooks": {
|
|
@@ -116,22 +135,22 @@ if [ -f "$SETTINGS_FILE" ]; then
|
|
|
116
135
|
}
|
|
117
136
|
}'
|
|
118
137
|
jq --argjson hooks "$(echo "$HOOKS_JSON" | jq '.hooks')" '. + {hooks: $hooks}' "$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" && mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE"
|
|
138
|
+
ok "Hooks added to existing settings.json"
|
|
119
139
|
else
|
|
120
|
-
# Fallback: rewrite settings.json preserving existing keys
|
|
121
|
-
echo -e " ${YELLOW} ⚠ jq not found. Adding hooks config manually...${NC}"
|
|
122
|
-
# Read existing content, strip trailing brace, append hooks
|
|
123
140
|
EXISTING=$(cat "$SETTINGS_FILE")
|
|
124
|
-
# Remove trailing } and whitespace
|
|
125
141
|
EXISTING=$(echo "$EXISTING" | sed '$ s/}$//')
|
|
126
|
-
|
|
142
|
+
LANG_SETTING=$(echo "$EXISTING" | grep '"language"' | head -1 | sed 's/,$//')
|
|
143
|
+
if [ -n "$LANG_SETTING" ]; then
|
|
144
|
+
cat > "$SETTINGS_FILE" << SETTINGSEOF
|
|
127
145
|
{
|
|
146
|
+
${LANG_SETTING},
|
|
128
147
|
"hooks": {
|
|
129
148
|
"Stop": [
|
|
130
149
|
{
|
|
131
150
|
"hooks": [
|
|
132
151
|
{
|
|
133
152
|
"type": "command",
|
|
134
|
-
"command": "\"
|
|
153
|
+
"command": "\"\$CLAUDE_PROJECT_DIR/.claude/hooks/context-monitor.sh\"",
|
|
135
154
|
"timeout": 10
|
|
136
155
|
}
|
|
137
156
|
]
|
|
@@ -142,7 +161,7 @@ if [ -f "$SETTINGS_FILE" ]; then
|
|
|
142
161
|
"hooks": [
|
|
143
162
|
{
|
|
144
163
|
"type": "command",
|
|
145
|
-
"command": "\"
|
|
164
|
+
"command": "\"\$CLAUDE_PROJECT_DIR/.claude/hooks/session-cleanup.sh\"",
|
|
146
165
|
"timeout": 5
|
|
147
166
|
}
|
|
148
167
|
]
|
|
@@ -151,21 +170,17 @@ if [ -f "$SETTINGS_FILE" ]; then
|
|
|
151
170
|
}
|
|
152
171
|
}
|
|
153
172
|
SETTINGSEOF
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
LANG_SETTING=$(echo "$EXISTING" | grep '"language"' | head -1 | sed 's/,$//')
|
|
158
|
-
if [ -n "$LANG_SETTING" ]; then
|
|
159
|
-
cat > "$SETTINGS_FILE" << SETTINGSEOF
|
|
173
|
+
ok "Hooks added (preserved language setting)"
|
|
174
|
+
else
|
|
175
|
+
cat > "$SETTINGS_FILE" << 'SETTINGSEOF'
|
|
160
176
|
{
|
|
161
|
-
${LANG_SETTING},
|
|
162
177
|
"hooks": {
|
|
163
178
|
"Stop": [
|
|
164
179
|
{
|
|
165
180
|
"hooks": [
|
|
166
181
|
{
|
|
167
182
|
"type": "command",
|
|
168
|
-
"command": "\"
|
|
183
|
+
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/context-monitor.sh\"",
|
|
169
184
|
"timeout": 10
|
|
170
185
|
}
|
|
171
186
|
]
|
|
@@ -176,7 +191,7 @@ SETTINGSEOF
|
|
|
176
191
|
"hooks": [
|
|
177
192
|
{
|
|
178
193
|
"type": "command",
|
|
179
|
-
"command": "\"
|
|
194
|
+
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-cleanup.sh\"",
|
|
180
195
|
"timeout": 5
|
|
181
196
|
}
|
|
182
197
|
]
|
|
@@ -185,11 +200,13 @@ SETTINGSEOF
|
|
|
185
200
|
}
|
|
186
201
|
}
|
|
187
202
|
SETTINGSEOF
|
|
203
|
+
ok "Hooks config written"
|
|
188
204
|
fi
|
|
189
205
|
fi
|
|
206
|
+
else
|
|
207
|
+
ok "Hooks already configured"
|
|
190
208
|
fi
|
|
191
209
|
else
|
|
192
|
-
# Create new settings.json with hooks
|
|
193
210
|
cat > "$SETTINGS_FILE" << 'SETTINGSEOF'
|
|
194
211
|
{
|
|
195
212
|
"hooks": {
|
|
@@ -218,11 +235,13 @@ else
|
|
|
218
235
|
}
|
|
219
236
|
}
|
|
220
237
|
SETTINGSEOF
|
|
238
|
+
ok "settings.json created with hooks"
|
|
221
239
|
fi
|
|
222
240
|
|
|
223
|
-
#
|
|
241
|
+
# ─── Handoff ──────────────────────────────────────────
|
|
242
|
+
head "Setting up handoff storage"
|
|
243
|
+
|
|
224
244
|
if [ ! -f "$CLAUDE_DIR/handoffs/_active.md" ]; then
|
|
225
|
-
echo -e " ${YELLOW}[6/10]${NC} Creating initial handoff..."
|
|
226
245
|
cat > "$CLAUDE_DIR/handoffs/_active.md" << 'HANDOFF'
|
|
227
246
|
# Session Handoff
|
|
228
247
|
|
|
@@ -249,33 +268,34 @@ if [ ! -f "$CLAUDE_DIR/handoffs/_active.md" ]; then
|
|
|
249
268
|
## Decisions Registry
|
|
250
269
|
(none)
|
|
251
270
|
HANDOFF
|
|
271
|
+
ok "Initial handoff template created"
|
|
252
272
|
else
|
|
253
|
-
|
|
273
|
+
ok "Existing handoff preserved"
|
|
254
274
|
fi
|
|
255
275
|
|
|
256
|
-
#
|
|
257
|
-
|
|
276
|
+
# ─── Gitignore ────────────────────────────────────────
|
|
277
|
+
head "Updating .gitignore"
|
|
278
|
+
|
|
258
279
|
GITIGNORE="$PROJECT_DIR/.gitignore"
|
|
259
280
|
if [ -f "$GITIGNORE" ]; then
|
|
260
281
|
if ! grep -q ".claude/handoffs/" "$GITIGNORE" 2>/dev/null; then
|
|
261
282
|
echo "" >> "$GITIGNORE"
|
|
262
283
|
echo "# claude-code-handoff (personal session state)" >> "$GITIGNORE"
|
|
263
284
|
echo ".claude/handoffs/" >> "$GITIGNORE"
|
|
285
|
+
ok "Added .claude/handoffs/ to .gitignore"
|
|
286
|
+
else
|
|
287
|
+
ok "Already in .gitignore"
|
|
264
288
|
fi
|
|
265
289
|
else
|
|
266
290
|
echo "# claude-code-handoff (personal session state)" > "$GITIGNORE"
|
|
267
291
|
echo ".claude/handoffs/" >> "$GITIGNORE"
|
|
292
|
+
ok ".gitignore created"
|
|
268
293
|
fi
|
|
269
294
|
|
|
270
|
-
#
|
|
271
|
-
|
|
272
|
-
CLAUDE_MD="$CLAUDE_DIR/CLAUDE.md"
|
|
273
|
-
CONTINUITY_BLOCK='## Session Continuity (MANDATORY)
|
|
274
|
-
|
|
275
|
-
At the START of every session, read `.claude/handoffs/_active.md` to recover context from prior sessions.
|
|
276
|
-
During work, update the handoff proactively after significant milestones.
|
|
277
|
-
Use `/handoff` before `/clear`. Use `/resume` to pick up. Use `/switch-context <topic>` to switch workstreams.'
|
|
295
|
+
# ─── CLAUDE.md ────────────────────────────────────────
|
|
296
|
+
head "Updating CLAUDE.md"
|
|
278
297
|
|
|
298
|
+
CLAUDE_MD="$CLAUDE_DIR/CLAUDE.md"
|
|
279
299
|
if [ -f "$CLAUDE_MD" ]; then
|
|
280
300
|
if ! grep -q "Session Continuity" "$CLAUDE_MD" 2>/dev/null; then
|
|
281
301
|
TEMP_FILE=$(mktemp)
|
|
@@ -295,6 +315,9 @@ if [ -f "$CLAUDE_MD" ]; then
|
|
|
295
315
|
{ print }
|
|
296
316
|
' "$CLAUDE_MD" > "$TEMP_FILE"
|
|
297
317
|
mv "$TEMP_FILE" "$CLAUDE_MD"
|
|
318
|
+
ok "Session Continuity section added"
|
|
319
|
+
else
|
|
320
|
+
ok "Session Continuity already present"
|
|
298
321
|
fi
|
|
299
322
|
else
|
|
300
323
|
cat > "$CLAUDE_MD" << 'CLAUDEMD'
|
|
@@ -306,10 +329,24 @@ At the START of every session, read `.claude/handoffs/_active.md` to recover con
|
|
|
306
329
|
During work, update the handoff proactively after significant milestones.
|
|
307
330
|
Use `/handoff` before `/clear`. Use `/resume` to pick up. Use `/switch-context <topic>` to switch workstreams.
|
|
308
331
|
CLAUDEMD
|
|
332
|
+
ok "CLAUDE.md created"
|
|
309
333
|
fi
|
|
310
334
|
|
|
311
|
-
#
|
|
312
|
-
|
|
335
|
+
# ─── Legacy cleanup ──────────────────────────────────
|
|
336
|
+
CLEANED=0
|
|
337
|
+
for f in retomar.md salvar-handoff.md trocar-contexto.md auto-handoff-toggle.md; do
|
|
338
|
+
if [ -f "$CLAUDE_DIR/commands/$f" ]; then
|
|
339
|
+
rm -f "$CLAUDE_DIR/commands/$f"
|
|
340
|
+
CLEANED=$((CLEANED + 1))
|
|
341
|
+
fi
|
|
342
|
+
done
|
|
343
|
+
if [ "$CLEANED" -gt 0 ]; then
|
|
344
|
+
info "Removed $CLEANED legacy command(s)"
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
# ─── Verify ───────────────────────────────────────────
|
|
348
|
+
head "Verifying"
|
|
349
|
+
|
|
313
350
|
INSTALLED=0
|
|
314
351
|
for f in resume.md save-handoff.md switch-context.md handoff.md delete-handoff.md auto-handoff.md; do
|
|
315
352
|
[ -f "$CLAUDE_DIR/commands/$f" ] && INSTALLED=$((INSTALLED + 1))
|
|
@@ -318,31 +355,28 @@ HOOKS_OK=0
|
|
|
318
355
|
[ -f "$CLAUDE_DIR/hooks/context-monitor.sh" ] && HOOKS_OK=$((HOOKS_OK + 1))
|
|
319
356
|
[ -f "$CLAUDE_DIR/hooks/session-cleanup.sh" ] && HOOKS_OK=$((HOOKS_OK + 1))
|
|
320
357
|
|
|
321
|
-
echo ""
|
|
322
|
-
echo -e " ${YELLOW}[10/10]${NC} Done!"
|
|
323
|
-
echo ""
|
|
324
358
|
if [ "$INSTALLED" -eq 6 ] && [ "$HOOKS_OK" -eq 2 ]; then
|
|
325
|
-
|
|
359
|
+
ok "${INSTALLED}/6 commands, ${HOOKS_OK}/2 hooks"
|
|
326
360
|
else
|
|
327
|
-
|
|
361
|
+
fail "Partial: ${INSTALLED}/6 commands, ${HOOKS_OK}/2 hooks"
|
|
328
362
|
fi
|
|
363
|
+
|
|
364
|
+
# ─── Done ─────────────────────────────────────────────
|
|
329
365
|
echo ""
|
|
330
|
-
echo -e "
|
|
331
|
-
echo -e "
|
|
332
|
-
echo -e "
|
|
333
|
-
echo -e " ${CYAN}/save-handoff${NC} Save session state (wizard)"
|
|
334
|
-
echo -e " ${CYAN}/switch-context${NC} Switch workstream"
|
|
335
|
-
echo -e " ${CYAN}/delete-handoff${NC} Delete handoff(s)"
|
|
336
|
-
echo -e " ${CYAN}/auto-handoff${NC} Toggle auto-handoff on/off"
|
|
366
|
+
echo -e " ${AMBER}${BOLD}════════════════════════════════════════${RESET}"
|
|
367
|
+
echo -e " ${GREEN}${BOLD} Installed successfully!${RESET}"
|
|
368
|
+
echo -e " ${AMBER}${BOLD}════════════════════════════════════════${RESET}"
|
|
337
369
|
echo ""
|
|
338
|
-
echo -e "
|
|
339
|
-
echo -e "
|
|
370
|
+
echo -e " ${WHITE}${BOLD}Commands:${RESET}"
|
|
371
|
+
echo -e " ${CYAN}/handoff${RESET} ${GRAY}Auto-save session${RESET}"
|
|
372
|
+
echo -e " ${CYAN}/resume${RESET} ${GRAY}Resume with wizard${RESET}"
|
|
373
|
+
echo -e " ${CYAN}/save-handoff${RESET} ${GRAY}Save with options${RESET}"
|
|
374
|
+
echo -e " ${CYAN}/switch-context${RESET} ${GRAY}Switch workstream${RESET}"
|
|
375
|
+
echo -e " ${CYAN}/delete-handoff${RESET} ${GRAY}Delete handoff(s)${RESET}"
|
|
376
|
+
echo -e " ${CYAN}/auto-handoff${RESET} ${GRAY}Toggle auto-handoff${RESET}"
|
|
340
377
|
echo ""
|
|
341
|
-
echo -e "
|
|
342
|
-
echo -e "
|
|
343
|
-
echo -e " .claude/rules/ session-continuity.md, auto-handoff.md"
|
|
344
|
-
echo -e " .claude/hooks/ context-monitor.sh, session-cleanup.sh"
|
|
345
|
-
echo -e " .claude/handoffs/ session state (gitignored)"
|
|
378
|
+
echo -e " ${WHITE}${BOLD}Auto-handoff:${RESET} ${DIM}beta — disabled by default${RESET}"
|
|
379
|
+
echo -e " ${GRAY}Run ${CYAN}/auto-handoff${GRAY} inside Claude Code to enable${RESET}"
|
|
346
380
|
echo ""
|
|
347
|
-
echo -e " ${
|
|
381
|
+
echo -e " ${DIM}Start Claude Code and use ${WHITE}/resume${DIM} to begin.${RESET}"
|
|
348
382
|
echo ""
|
package/package.json
CHANGED
package/uninstall.sh
CHANGED
|
@@ -50,6 +50,7 @@ rm -f "$CLAUDE_DIR/rules/auto-handoff.md"
|
|
|
50
50
|
echo -e " ${YELLOW}[3/6]${NC} Removing hooks..."
|
|
51
51
|
rm -f "$CLAUDE_DIR/hooks/context-monitor.sh"
|
|
52
52
|
rm -f "$CLAUDE_DIR/hooks/session-cleanup.sh"
|
|
53
|
+
rm -f "$CLAUDE_DIR/hooks/.auto-handoff-enabled"
|
|
53
54
|
rm -f "$CLAUDE_DIR/hooks/.auto-handoff-disabled"
|
|
54
55
|
rmdir "$CLAUDE_DIR/hooks" 2>/dev/null || true
|
|
55
56
|
|