@infinitedusky/indusk-mcp 1.1.1 → 1.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/dist/bin/commands/init.js +9 -2
- package/dist/tools/graph-tools.js +186 -8
- package/extensions/falkordb/manifest.json +4 -0
- package/hooks/check-catchup.js +77 -0
- package/package.json +1 -1
- package/skills/catchup.md +16 -2
- package/skills/handoff.md +11 -0
|
@@ -176,7 +176,12 @@ export async function init(projectRoot, options = {}) {
|
|
|
176
176
|
console.info("\n[Hooks]");
|
|
177
177
|
const hooksSource = join(packageRoot, "hooks");
|
|
178
178
|
const hooksTarget = join(projectRoot, ".claude/hooks");
|
|
179
|
-
const hookFiles = [
|
|
179
|
+
const hookFiles = [
|
|
180
|
+
"check-gates.js",
|
|
181
|
+
"gate-reminder.js",
|
|
182
|
+
"validate-impl-structure.js",
|
|
183
|
+
"check-catchup.js",
|
|
184
|
+
];
|
|
180
185
|
if (existsSync(hooksSource)) {
|
|
181
186
|
mkdirSync(hooksTarget, { recursive: true });
|
|
182
187
|
for (const file of hookFiles) {
|
|
@@ -202,6 +207,7 @@ export async function init(projectRoot, options = {}) {
|
|
|
202
207
|
hooks: [
|
|
203
208
|
{ type: "command", command: "node .claude/hooks/check-gates.js" },
|
|
204
209
|
{ type: "command", command: "node .claude/hooks/validate-impl-structure.js" },
|
|
210
|
+
{ type: "command", command: "node .claude/hooks/check-catchup.js" },
|
|
205
211
|
],
|
|
206
212
|
},
|
|
207
213
|
],
|
|
@@ -221,7 +227,8 @@ export async function init(projectRoot, options = {}) {
|
|
|
221
227
|
// Check if our hook is already present
|
|
222
228
|
const hasOurHook = existingEntries.some((e) => e.hooks?.some((h) => h.command?.includes("check-gates") ||
|
|
223
229
|
h.command?.includes("gate-reminder") ||
|
|
224
|
-
h.command?.includes("validate-impl")
|
|
230
|
+
h.command?.includes("validate-impl") ||
|
|
231
|
+
h.command?.includes("check-catchup")));
|
|
225
232
|
if (!hasOurHook || force) {
|
|
226
233
|
// Remove old entries if force, then add
|
|
227
234
|
if (force) {
|
|
@@ -6,22 +6,58 @@ function cgcPath() {
|
|
|
6
6
|
const paths = [join(process.env.HOME ?? "", ".local/bin/cgc"), "/usr/local/bin/cgc"];
|
|
7
7
|
return paths.find((p) => existsSync(p)) ?? null;
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function getFalkorHost() {
|
|
10
|
+
if (process.env.FALKORDB_HOST)
|
|
11
|
+
return process.env.FALKORDB_HOST;
|
|
12
|
+
// Try OrbStack hostname first, fall back to localhost
|
|
13
|
+
try {
|
|
14
|
+
execSync("ping -c 1 -W 1 falkordb.orb.local", {
|
|
15
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
16
|
+
timeout: 2000,
|
|
17
|
+
});
|
|
18
|
+
return "falkordb.orb.local";
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return "localhost";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function checkFalkorConnection(host) {
|
|
25
|
+
try {
|
|
26
|
+
// Fast TCP check — try to connect to Redis port
|
|
27
|
+
execSync(`node -e "const s=require('net').connect(6379,'${host}');s.setTimeout(2000);s.on('connect',()=>{s.end();process.exit(0)});s.on('error',()=>process.exit(1));s.on('timeout',()=>process.exit(1))"`, { timeout: 3000, stdio: ["ignore", "ignore", "ignore"] });
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function runCgc(args, projectRoot, options) {
|
|
10
35
|
const cgc = cgcPath();
|
|
11
36
|
if (!cgc) {
|
|
12
37
|
return JSON.stringify({ error: "CGC not installed — run: pipx install codegraphcontext" });
|
|
13
38
|
}
|
|
39
|
+
const host = getFalkorHost();
|
|
40
|
+
const timeout = options?.timeout ?? 15000;
|
|
41
|
+
// Fast pre-check: is FalkorDB reachable?
|
|
42
|
+
if (!options?.skipConnectionCheck && !checkFalkorConnection(host)) {
|
|
43
|
+
return JSON.stringify({
|
|
44
|
+
error: `FalkorDB not reachable at ${host}:6379. Is the container running? Try: docker start falkordb`,
|
|
45
|
+
host,
|
|
46
|
+
suggestion: host === "localhost"
|
|
47
|
+
? "OrbStack not detected. If using OrbStack, ensure it's running."
|
|
48
|
+
: "Try: docker start falkordb — or check if OrbStack is running.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
14
51
|
try {
|
|
15
52
|
return execSync(`${cgc} ${args}`, {
|
|
16
53
|
encoding: "utf-8",
|
|
17
|
-
timeout
|
|
54
|
+
timeout,
|
|
18
55
|
stdio: ["ignore", "pipe", "pipe"],
|
|
19
56
|
cwd: projectRoot,
|
|
20
57
|
env: {
|
|
21
58
|
...process.env,
|
|
22
59
|
DATABASE_TYPE: "falkordb-remote",
|
|
23
|
-
FALKORDB_HOST:
|
|
24
|
-
FALKORDB_PORT: process.env.FALKORDB_PORT ?? "6379",
|
|
60
|
+
FALKORDB_HOST: host,
|
|
25
61
|
FALKORDB_GRAPH_NAME: process.env.FALKORDB_GRAPH_NAME ?? basename(projectRoot),
|
|
26
62
|
},
|
|
27
63
|
}).trim();
|
|
@@ -38,10 +74,16 @@ export function indexProject(projectRoot) {
|
|
|
38
74
|
if (!cgc) {
|
|
39
75
|
return { success: false, output: "CGC not installed — run: pipx install codegraphcontext" };
|
|
40
76
|
}
|
|
77
|
+
const host = getFalkorHost();
|
|
78
|
+
if (!checkFalkorConnection(host)) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
output: `FalkorDB not reachable at ${host}:6379. Is the container running? Try: docker start falkordb`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
41
84
|
const graphName = process.env.FALKORDB_GRAPH_NAME ?? basename(projectRoot);
|
|
85
|
+
const hasIgnore = existsSync(join(projectRoot, ".cgcignore"));
|
|
42
86
|
try {
|
|
43
|
-
// Check if .cgcignore exists
|
|
44
|
-
const hasIgnore = existsSync(join(projectRoot, ".cgcignore"));
|
|
45
87
|
const output = execSync(`${cgc} index ${projectRoot}`, {
|
|
46
88
|
encoding: "utf-8",
|
|
47
89
|
timeout: 120000,
|
|
@@ -49,8 +91,7 @@ export function indexProject(projectRoot) {
|
|
|
49
91
|
env: {
|
|
50
92
|
...process.env,
|
|
51
93
|
DATABASE_TYPE: "falkordb-remote",
|
|
52
|
-
FALKORDB_HOST:
|
|
53
|
-
FALKORDB_PORT: process.env.FALKORDB_PORT ?? "6379",
|
|
94
|
+
FALKORDB_HOST: host,
|
|
54
95
|
FALKORDB_GRAPH_NAME: graphName,
|
|
55
96
|
},
|
|
56
97
|
}).trim();
|
|
@@ -268,4 +309,141 @@ export function registerGraphTools(server, projectRoot) {
|
|
|
268
309
|
content: [{ type: "text", text: output }],
|
|
269
310
|
};
|
|
270
311
|
});
|
|
312
|
+
server.registerTool("graph_ensure", {
|
|
313
|
+
description: "Validate and fix the entire code graph stack: FalkorDB container, CGC connection, repo indexing. Call this during catchup or when graph tools fail. Attempts auto-repair for common issues.",
|
|
314
|
+
}, async () => {
|
|
315
|
+
const steps = [];
|
|
316
|
+
// 1. Check CGC installed
|
|
317
|
+
const cgc = cgcPath();
|
|
318
|
+
if (!cgc) {
|
|
319
|
+
steps.push({
|
|
320
|
+
step: "cgc-installed",
|
|
321
|
+
status: "error",
|
|
322
|
+
detail: "CGC not installed — run: pipx install codegraphcontext",
|
|
323
|
+
});
|
|
324
|
+
return {
|
|
325
|
+
content: [{ type: "text", text: JSON.stringify({ steps }, null, 2) }],
|
|
326
|
+
isError: true,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
steps.push({ step: "cgc-installed", status: "ok", detail: cgc });
|
|
330
|
+
// 2. Check FalkorDB container exists and is running
|
|
331
|
+
try {
|
|
332
|
+
const status = execSync("docker ps --filter name=falkordb --format '{{.Status}}'", {
|
|
333
|
+
encoding: "utf-8",
|
|
334
|
+
timeout: 5000,
|
|
335
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
336
|
+
}).trim();
|
|
337
|
+
if (status) {
|
|
338
|
+
steps.push({ step: "falkordb-container", status: "ok", detail: status });
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Container exists but not running — try to start
|
|
342
|
+
try {
|
|
343
|
+
execSync("docker start falkordb", {
|
|
344
|
+
timeout: 10000,
|
|
345
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
346
|
+
});
|
|
347
|
+
steps.push({
|
|
348
|
+
step: "falkordb-container",
|
|
349
|
+
status: "fixed",
|
|
350
|
+
detail: "Started existing container",
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Container doesn't exist — create it
|
|
355
|
+
try {
|
|
356
|
+
execSync("docker run -d --name falkordb --restart unless-stopped -v falkordb-global:/var/lib/falkordb/data falkordb/falkordb:latest", { timeout: 30000, stdio: ["ignore", "pipe", "pipe"] });
|
|
357
|
+
steps.push({
|
|
358
|
+
step: "falkordb-container",
|
|
359
|
+
status: "fixed",
|
|
360
|
+
detail: "Created new container",
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
const err = e;
|
|
365
|
+
steps.push({
|
|
366
|
+
step: "falkordb-container",
|
|
367
|
+
status: "error",
|
|
368
|
+
detail: err.message ?? "Failed to create container",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
steps.push({
|
|
376
|
+
step: "falkordb-container",
|
|
377
|
+
status: "error",
|
|
378
|
+
detail: "Docker not available — is OrbStack running?",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
// 3. Check connectivity
|
|
382
|
+
const host = getFalkorHost();
|
|
383
|
+
if (checkFalkorConnection(host)) {
|
|
384
|
+
steps.push({
|
|
385
|
+
step: "falkordb-connection",
|
|
386
|
+
status: "ok",
|
|
387
|
+
detail: `Connected to ${host}:6379`,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// Wait a moment if we just started the container
|
|
392
|
+
const justStarted = steps.some((s) => s.step === "falkordb-container" && s.status === "fixed");
|
|
393
|
+
if (justStarted) {
|
|
394
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
395
|
+
if (checkFalkorConnection(host)) {
|
|
396
|
+
steps.push({
|
|
397
|
+
step: "falkordb-connection",
|
|
398
|
+
status: "ok",
|
|
399
|
+
detail: `Connected to ${host}:6379 (after wait)`,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
steps.push({
|
|
404
|
+
step: "falkordb-connection",
|
|
405
|
+
status: "error",
|
|
406
|
+
detail: `Cannot connect to ${host}:6379`,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
steps.push({
|
|
412
|
+
step: "falkordb-connection",
|
|
413
|
+
status: "error",
|
|
414
|
+
detail: `Cannot connect to ${host}:6379`,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// 4. Check if repo is indexed
|
|
419
|
+
if (steps.every((s) => s.status !== "error")) {
|
|
420
|
+
const listOutput = runCgc("list", projectRoot, { skipConnectionCheck: true });
|
|
421
|
+
if (listOutput.includes(projectRoot) || listOutput.includes(basename(projectRoot))) {
|
|
422
|
+
steps.push({ step: "repo-indexed", status: "ok", detail: "Repository is indexed" });
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
steps.push({
|
|
426
|
+
step: "repo-indexed",
|
|
427
|
+
status: "error",
|
|
428
|
+
detail: "Repository not indexed — call index_project to index",
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const hasErrors = steps.some((s) => s.status === "error");
|
|
433
|
+
const hasFixed = steps.some((s) => s.status === "fixed");
|
|
434
|
+
return {
|
|
435
|
+
content: [
|
|
436
|
+
{
|
|
437
|
+
type: "text",
|
|
438
|
+
text: JSON.stringify({
|
|
439
|
+
healthy: !hasErrors,
|
|
440
|
+
autoRepaired: hasFixed,
|
|
441
|
+
host,
|
|
442
|
+
steps,
|
|
443
|
+
}, null, 2),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
isError: hasErrors,
|
|
447
|
+
};
|
|
448
|
+
});
|
|
271
449
|
}
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
{
|
|
7
7
|
"name": "falkordb-container",
|
|
8
8
|
"command": "docker ps --filter name=falkordb --format '{{.Status}}'"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "falkordb-connection",
|
|
12
|
+
"command": "node -e \"const s=require('net').connect(6379,process.env.FALKORDB_HOST||'falkordb.orb.local');s.setTimeout(3000);s.on('connect',()=>{console.log('connected');s.end();process.exit(0)});s.on('error',e=>{console.error(e.message);process.exit(1)});s.on('timeout',()=>{console.error('timeout');process.exit(1)})\""
|
|
9
13
|
}
|
|
10
14
|
],
|
|
11
15
|
"env_vars": {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse hook: blocks Edit/Write on project files until /catchup is complete.
|
|
4
|
+
*
|
|
5
|
+
* Reads .claude/handoff.md and checks that all Catchup Status boxes are checked.
|
|
6
|
+
* Allows edits TO the handoff file itself (so catchup can check off boxes).
|
|
7
|
+
* Allows edits if no handoff file exists (first session, no enforcement).
|
|
8
|
+
*
|
|
9
|
+
* Exit 0 = allow
|
|
10
|
+
* Exit 2 = block (stderr sent to agent)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
|
|
16
|
+
const input = JSON.parse(readFileSync("/dev/stdin", "utf-8"));
|
|
17
|
+
const toolInput = input.tool_input ?? {};
|
|
18
|
+
const filePath = toolInput.file_path ?? "";
|
|
19
|
+
|
|
20
|
+
// Find project root by looking for .claude/ directory
|
|
21
|
+
function findProjectRoot() {
|
|
22
|
+
let dir = process.cwd();
|
|
23
|
+
for (let i = 0; i < 10; i++) {
|
|
24
|
+
if (existsSync(join(dir, ".claude"))) return dir;
|
|
25
|
+
const parent = resolve(dir, "..");
|
|
26
|
+
if (parent === dir) break;
|
|
27
|
+
dir = parent;
|
|
28
|
+
}
|
|
29
|
+
return process.cwd();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const projectRoot = findProjectRoot();
|
|
33
|
+
const handoffPath = join(projectRoot, ".claude", "handoff.md");
|
|
34
|
+
|
|
35
|
+
// Allow if no handoff exists (first session or handoff not yet created)
|
|
36
|
+
if (!existsSync(handoffPath)) {
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Always allow edits to the handoff file itself (catchup needs to check boxes)
|
|
41
|
+
if (filePath.endsWith("handoff.md")) {
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Read the handoff and check catchup status
|
|
46
|
+
const content = readFileSync(handoffPath, "utf-8");
|
|
47
|
+
|
|
48
|
+
// Look for the Catchup Status section
|
|
49
|
+
const statusMatch = content.match(/## Catchup Status\n([\s\S]*?)(?:\n##|\n$|$)/);
|
|
50
|
+
if (!statusMatch) {
|
|
51
|
+
// No catchup status section — allow (handoff was written before this feature)
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const statusSection = statusMatch[1];
|
|
56
|
+
const unchecked = [];
|
|
57
|
+
const checkboxes = statusSection.matchAll(/- \[( |x)\] (\w+)/g);
|
|
58
|
+
|
|
59
|
+
for (const match of checkboxes) {
|
|
60
|
+
if (match[1] === " ") {
|
|
61
|
+
unchecked.push(match[2]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (unchecked.length === 0) {
|
|
66
|
+
// All boxes checked — catchup is complete
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Block the edit
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
`Catchup incomplete. Run /catchup before editing project files.\n` +
|
|
73
|
+
`Missing steps: ${unchecked.join(", ")}\n` +
|
|
74
|
+
`The handoff file (.claude/handoff.md) has unchecked catchup boxes. ` +
|
|
75
|
+
`Complete each /catchup step to check them off.`,
|
|
76
|
+
);
|
|
77
|
+
process.exit(2);
|
package/package.json
CHANGED
package/skills/catchup.md
CHANGED
|
@@ -16,14 +16,20 @@ Check if `.claude/handoff.md` exists. If it does, read it first — this is the
|
|
|
16
16
|
|
|
17
17
|
If the handoff exists, present a brief summary to the user: "Last session was working on X, stopped at Y. Ready to pick up there?"
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**After reading, edit the handoff to check off:** `- [x] handoff`
|
|
20
|
+
|
|
21
|
+
If no handoff exists, create one with all catchup status boxes unchecked, then check off handoff.
|
|
20
22
|
|
|
21
23
|
### 2. Read Lessons
|
|
22
24
|
Call `list_lessons`. Read every lesson. These are rules learned from past mistakes — not suggestions. Internalize them before touching any code.
|
|
23
25
|
|
|
26
|
+
**After reading, edit the handoff to check off:** `- [x] lessons`
|
|
27
|
+
|
|
24
28
|
### 3. Check Infrastructure
|
|
25
29
|
Call `check_health`. Verify FalkorDB and CGC are running. If unhealthy, tell the user what's down and how to fix it.
|
|
26
30
|
|
|
31
|
+
**After checking, edit the handoff to check off:** `- [x] health`
|
|
32
|
+
|
|
27
33
|
### 4. Read Project Context
|
|
28
34
|
Call `get_context` to read CLAUDE.md. This contains:
|
|
29
35
|
- **Architecture** — what the project is, how it's structured
|
|
@@ -34,12 +40,16 @@ Call `get_context` to read CLAUDE.md. This contains:
|
|
|
34
40
|
|
|
35
41
|
Read it fully. Don't skim.
|
|
36
42
|
|
|
43
|
+
**After reading, edit the handoff to check off:** `- [x] context`
|
|
44
|
+
|
|
37
45
|
### 5. Check Active Plans
|
|
38
46
|
Call `list_plans`. This shows every plan and its status. Pay attention to:
|
|
39
47
|
- Plans with status `in-progress` — these are actively being worked on
|
|
40
48
|
- The current phase of each active plan — this is where `/work` will pick up
|
|
41
49
|
- Dependencies between plans — don't start a blocked plan
|
|
42
50
|
|
|
51
|
+
**After checking, edit the handoff to check off:** `- [x] plans`
|
|
52
|
+
|
|
43
53
|
### 6. Review Skills and Extensions
|
|
44
54
|
Call `extensions_status` to see what extensions are enabled and their capabilities.
|
|
45
55
|
|
|
@@ -51,8 +61,12 @@ Then read all installed skill files in `.claude/skills/*/SKILL.md`. These define
|
|
|
51
61
|
|
|
52
62
|
Understand what each skill does and when to use it. You should be able to answer: "What slash commands are available and what do they do?"
|
|
53
63
|
|
|
64
|
+
**After reviewing, edit the handoff to check off:** `- [x] skills` and `- [x] extensions`
|
|
65
|
+
|
|
54
66
|
### 7. Check Code Graph
|
|
55
|
-
Call `
|
|
67
|
+
Call `graph_ensure` to validate the entire code graph stack: FalkorDB container, CGC connection, repo indexing. This tool auto-repairs common issues (starts stopped containers, detects the right host). If it reports errors it couldn't fix, tell the user what's wrong and how to fix it. If the repo isn't indexed, call `index_project`.
|
|
68
|
+
|
|
69
|
+
**After checking, edit the handoff to check off:** `- [x] graph`
|
|
56
70
|
|
|
57
71
|
### 8. Summarize
|
|
58
72
|
|
package/skills/handoff.md
CHANGED
|
@@ -32,6 +32,16 @@ Create or overwrite `.claude/handoff.md` with:
|
|
|
32
32
|
|
|
33
33
|
## Watch Out For
|
|
34
34
|
{Gotchas the next agent should know. "The FalkorDB graph needs reindexing." "The hooks aren't published yet — version 1.0.3 has them." "Don't touch init.ts until the extension system PR is merged."}
|
|
35
|
+
|
|
36
|
+
## Catchup Status
|
|
37
|
+
- [ ] handoff
|
|
38
|
+
- [ ] lessons
|
|
39
|
+
- [ ] skills
|
|
40
|
+
- [ ] health
|
|
41
|
+
- [ ] context
|
|
42
|
+
- [ ] plans
|
|
43
|
+
- [ ] extensions
|
|
44
|
+
- [ ] graph
|
|
35
45
|
```
|
|
36
46
|
|
|
37
47
|
## When to Write a Handoff
|
|
@@ -48,3 +58,4 @@ Create or overwrite `.claude/handoff.md` with:
|
|
|
48
58
|
- **Decisions that aren't saved anywhere else MUST go here.** They'll be lost otherwise.
|
|
49
59
|
- **Overwrite the previous handoff.** There's only one — the most recent session's. Old handoffs are consumed by /catchup and don't need to persist.
|
|
50
60
|
- **Keep it short.** This isn't a retrospective. It's a sticky note for the next person.
|
|
61
|
+
- **Always include the Catchup Status section** with all boxes unchecked. This is enforced by a hook — the next session cannot edit or write code until `/catchup` checks off every box. Each step in catchup marks its box when completed.
|