aiwcli 0.12.2 → 0.12.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/templates/_shared/.claude/commands/handoff.md +44 -78
- package/dist/templates/_shared/hooks-ts/session_end.ts +16 -11
- package/dist/templates/_shared/hooks-ts/session_start.ts +4 -1
- package/dist/templates/_shared/lib-ts/base/inference.ts +72 -23
- package/dist/templates/_shared/lib-ts/base/state-io.ts +12 -7
- package/dist/templates/_shared/lib-ts/context/context-store.ts +35 -74
- package/dist/templates/_shared/lib-ts/types.ts +64 -63
- package/dist/templates/_shared/scripts/resolve_context.ts +14 -5
- package/dist/templates/_shared/scripts/resume_handoff.ts +16 -13
- package/dist/templates/_shared/scripts/save_handoff.ts +30 -31
- package/dist/templates/_shared/workflows/handoff.md +28 -6
- package/dist/templates/cc-native/.claude/commands/rlm/ask.md +136 -0
- package/dist/templates/cc-native/.claude/commands/rlm/index.md +21 -0
- package/dist/templates/cc-native/.claude/commands/rlm/overview.md +56 -0
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +4 -4
- package/dist/templates/cc-native/_cc-native/{plan-review.config.json → cc-native.config.json} +12 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +3 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +26 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/CLAUDE.md +480 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/embedding-indexer.ts +287 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/hyde.ts +148 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/index.ts +54 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +58 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/ollama-client.ts +208 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +460 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-indexer.ts +447 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-loader.ts +280 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-searcher.ts +274 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +201 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/vector-store.ts +278 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +2 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -9,21 +9,20 @@
|
|
|
9
9
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import * as path from "node:path";
|
|
12
|
-
|
|
12
|
+
import { readStateJson, writeStateJson, toDict, dictToState } from "../base/state-io.js";
|
|
13
13
|
import { atomicWrite } from "../base/atomic-write.js";
|
|
14
14
|
import {
|
|
15
|
-
getArchiveContextDir,
|
|
16
|
-
getArchiveDir,
|
|
17
|
-
getArchiveIndexPath,
|
|
18
15
|
getContextDir,
|
|
19
16
|
getContextsDir,
|
|
20
17
|
getIndexPath,
|
|
18
|
+
getArchiveDir,
|
|
19
|
+
getArchiveContextDir,
|
|
20
|
+
getArchiveIndexPath,
|
|
21
21
|
validateContextId,
|
|
22
22
|
} from "../base/constants.js";
|
|
23
|
-
import { logDebug
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import type { ContextState, IndexEntry, IndexFile, Mode } from "../types.js";
|
|
23
|
+
import { logDebug, logInfo, logWarn, logError, setContextPath } from "../base/logger.js";
|
|
24
|
+
import { nowIso, generateContextId } from "../base/utils.js";
|
|
25
|
+
import type { ContextState, IndexFile, IndexEntry, Mode } from "../types.js";
|
|
27
26
|
|
|
28
27
|
const INDEX_VERSION = "3.0";
|
|
29
28
|
|
|
@@ -35,13 +34,12 @@ function loadIndex(projectRoot?: string): IndexFile {
|
|
|
35
34
|
const indexPath = getIndexPath(projectRoot);
|
|
36
35
|
if (fs.existsSync(indexPath)) {
|
|
37
36
|
try {
|
|
38
|
-
const raw = fs.readFileSync(indexPath, "
|
|
37
|
+
const raw = fs.readFileSync(indexPath, "utf-8");
|
|
39
38
|
return JSON.parse(raw) as IndexFile;
|
|
40
|
-
} catch (
|
|
41
|
-
logWarn("context_store", `Failed to read index, recreating: ${
|
|
39
|
+
} catch (e: any) {
|
|
40
|
+
logWarn("context_store", `Failed to read index, recreating: ${e}`);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
|
-
|
|
45
43
|
return { version: INDEX_VERSION, updated_at: nowIso(), sessions: {}, contexts: {} };
|
|
46
44
|
}
|
|
47
45
|
|
|
@@ -52,7 +50,6 @@ function saveIndex(index: IndexFile, projectRoot?: string): boolean {
|
|
|
52
50
|
if (!success) {
|
|
53
51
|
logWarn("context_store", `Failed to write index: ${error}`);
|
|
54
52
|
}
|
|
55
|
-
|
|
56
53
|
return success;
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -72,7 +69,7 @@ function migrateContextJson(contextId: string, projectRoot?: string): ContextSta
|
|
|
72
69
|
if (!fs.existsSync(legacyPath)) return null;
|
|
73
70
|
|
|
74
71
|
try {
|
|
75
|
-
const data = JSON.parse(fs.readFileSync(legacyPath, "
|
|
72
|
+
const data = JSON.parse(fs.readFileSync(legacyPath, "utf-8"));
|
|
76
73
|
const inFlight = data.in_flight ?? {};
|
|
77
74
|
const oldMode = inFlight.mode ?? "none";
|
|
78
75
|
const MODE_MIGRATION: Record<string, string> = {
|
|
@@ -107,8 +104,8 @@ function migrateContextJson(contextId: string, projectRoot?: string): ContextSta
|
|
|
107
104
|
last_session: null,
|
|
108
105
|
tasks: [],
|
|
109
106
|
};
|
|
110
|
-
} catch (
|
|
111
|
-
logWarn("context_store", `Failed to migrate context.json for '${contextId}': ${
|
|
107
|
+
} catch (e: any) {
|
|
108
|
+
logWarn("context_store", `Failed to migrate context.json for '${contextId}': ${e}`);
|
|
112
109
|
return null;
|
|
113
110
|
}
|
|
114
111
|
}
|
|
@@ -137,7 +134,7 @@ export function saveState(
|
|
|
137
134
|
contextId: string,
|
|
138
135
|
state: ContextState,
|
|
139
136
|
projectRoot?: string,
|
|
140
|
-
): [boolean,
|
|
137
|
+
): [boolean, string | null] {
|
|
141
138
|
// Ensure the state ID matches
|
|
142
139
|
state.id = contextId;
|
|
143
140
|
|
|
@@ -155,12 +152,10 @@ export function saveState(
|
|
|
155
152
|
if (!index.sessions) index.sessions = {} as Record<string, string>;
|
|
156
153
|
index.sessions[sid] = contextId;
|
|
157
154
|
}
|
|
158
|
-
|
|
159
155
|
const indexOk = saveIndex(index, projectRoot);
|
|
160
156
|
if (!indexOk) {
|
|
161
157
|
return [true, "state.json saved but index.json update failed"];
|
|
162
158
|
}
|
|
163
|
-
|
|
164
159
|
return [true, null];
|
|
165
160
|
}
|
|
166
161
|
|
|
@@ -170,7 +165,7 @@ export function saveState(
|
|
|
170
165
|
* See SPEC.md §7.4
|
|
171
166
|
*/
|
|
172
167
|
export function createContext(
|
|
173
|
-
contextId:
|
|
168
|
+
contextId: string | null,
|
|
174
169
|
summary: string,
|
|
175
170
|
method = "",
|
|
176
171
|
projectRoot?: string,
|
|
@@ -190,7 +185,6 @@ export function createContext(
|
|
|
190
185
|
} catch { /* ignore */ }
|
|
191
186
|
}
|
|
192
187
|
}
|
|
193
|
-
|
|
194
188
|
contextId = generateContextId(summary, existingIds);
|
|
195
189
|
}
|
|
196
190
|
|
|
@@ -219,6 +213,7 @@ export function createContext(
|
|
|
219
213
|
plan_id: null,
|
|
220
214
|
plan_anchors: [],
|
|
221
215
|
plan_consumed: false,
|
|
216
|
+
plan_hash_consumed: null,
|
|
222
217
|
handoff_path: null,
|
|
223
218
|
handoff_consumed: false,
|
|
224
219
|
session_ids: [],
|
|
@@ -241,7 +236,6 @@ export function getContext(contextId: string, projectRoot?: string): ContextStat
|
|
|
241
236
|
} catch {
|
|
242
237
|
return null;
|
|
243
238
|
}
|
|
244
|
-
|
|
245
239
|
return loadState(contextId, projectRoot);
|
|
246
240
|
}
|
|
247
241
|
|
|
@@ -279,7 +273,6 @@ export function getAllContexts(
|
|
|
279
273
|
try {
|
|
280
274
|
if (!fs.statSync(fullPath).isDirectory()) continue;
|
|
281
275
|
} catch { continue; }
|
|
282
|
-
|
|
283
276
|
const state = loadState(entry, projectRoot);
|
|
284
277
|
if (state && (!status || state.status === status)) {
|
|
285
278
|
results.push(state);
|
|
@@ -298,7 +291,7 @@ export function getAllContexts(
|
|
|
298
291
|
*/
|
|
299
292
|
export function updateContext(
|
|
300
293
|
contextId: string,
|
|
301
|
-
updates: Partial<Pick<ContextState, "
|
|
294
|
+
updates: Partial<Pick<ContextState, "summary" | "tags" | "method">>,
|
|
302
295
|
projectRoot?: string,
|
|
303
296
|
): ContextState | null {
|
|
304
297
|
const state = getContext(contextId, projectRoot);
|
|
@@ -348,7 +341,6 @@ export function getContextBySessionId(
|
|
|
348
341
|
return state;
|
|
349
342
|
}
|
|
350
343
|
}
|
|
351
|
-
|
|
352
344
|
return null;
|
|
353
345
|
}
|
|
354
346
|
|
|
@@ -380,7 +372,6 @@ export function bindSession(
|
|
|
380
372
|
if (!state.session_ids.includes(sessionId)) {
|
|
381
373
|
state.session_ids.push(sessionId);
|
|
382
374
|
}
|
|
383
|
-
|
|
384
375
|
state.last_active = nowIso();
|
|
385
376
|
|
|
386
377
|
const [success] = saveState(contextId, state, projectRoot);
|
|
@@ -396,13 +387,14 @@ export function updateMode(
|
|
|
396
387
|
mode: Mode,
|
|
397
388
|
projectRoot?: string,
|
|
398
389
|
opts?: {
|
|
399
|
-
handoff_consumed?: boolean;
|
|
400
|
-
plan_anchors?: string[];
|
|
401
|
-
plan_consumed?: boolean;
|
|
402
|
-
plan_hash?: string;
|
|
403
|
-
plan_id?: string;
|
|
404
390
|
plan_path?: string;
|
|
391
|
+
plan_hash?: string;
|
|
405
392
|
plan_signature?: string;
|
|
393
|
+
plan_id?: string;
|
|
394
|
+
plan_anchors?: string[];
|
|
395
|
+
plan_consumed?: boolean;
|
|
396
|
+
plan_hash_consumed?: string;
|
|
397
|
+
handoff_consumed?: boolean;
|
|
406
398
|
},
|
|
407
399
|
): ContextState | null {
|
|
408
400
|
const state = getContext(contextId, projectRoot);
|
|
@@ -418,6 +410,7 @@ export function updateMode(
|
|
|
418
410
|
if (opts.plan_id !== undefined) state.plan_id = opts.plan_id;
|
|
419
411
|
if (opts.plan_anchors !== undefined) state.plan_anchors = opts.plan_anchors;
|
|
420
412
|
if (opts.plan_consumed !== undefined) state.plan_consumed = opts.plan_consumed;
|
|
413
|
+
if (opts.plan_hash_consumed !== undefined) state.plan_hash_consumed = opts.plan_hash_consumed;
|
|
421
414
|
if (opts.handoff_consumed !== undefined) state.handoff_consumed = opts.handoff_consumed;
|
|
422
415
|
}
|
|
423
416
|
|
|
@@ -429,6 +422,7 @@ export function updateMode(
|
|
|
429
422
|
state.plan_id = null;
|
|
430
423
|
state.plan_anchors = [];
|
|
431
424
|
state.plan_consumed = false;
|
|
425
|
+
state.plan_hash_consumed = null;
|
|
432
426
|
state.handoff_consumed = false;
|
|
433
427
|
}
|
|
434
428
|
|
|
@@ -500,7 +494,6 @@ export function archiveContext(contextId: string, projectRoot?: string): Context
|
|
|
500
494
|
logWarn("context_store", `Cannot archive: context '${contextId}' not found`);
|
|
501
495
|
return null;
|
|
502
496
|
}
|
|
503
|
-
|
|
504
497
|
if (state.status !== "completed") {
|
|
505
498
|
logWarn("context_store", `Cannot archive: context '${contextId}' not completed`);
|
|
506
499
|
return null;
|
|
@@ -519,8 +512,8 @@ export function archiveContext(contextId: string, projectRoot?: string): Context
|
|
|
519
512
|
|
|
520
513
|
try {
|
|
521
514
|
fs.renameSync(sourceDir, archiveDest);
|
|
522
|
-
} catch (
|
|
523
|
-
logError("context_store", `Failed to move context to archive: ${
|
|
515
|
+
} catch (e: any) {
|
|
516
|
+
logError("context_store", `Failed to move context to archive: ${e}`);
|
|
524
517
|
return null;
|
|
525
518
|
}
|
|
526
519
|
|
|
@@ -531,7 +524,6 @@ export function archiveContext(contextId: string, projectRoot?: string): Context
|
|
|
531
524
|
for (const [sid, cid] of Object.entries(sessions)) {
|
|
532
525
|
if (cid === contextId) delete sessions[sid];
|
|
533
526
|
}
|
|
534
|
-
|
|
535
527
|
saveIndex(index, projectRoot);
|
|
536
528
|
|
|
537
529
|
// Add to archive index
|
|
@@ -551,7 +543,6 @@ export function reopenContext(contextId: string, projectRoot?: string): ContextS
|
|
|
551
543
|
if (!state) {
|
|
552
544
|
state = restoreFromArchive(contextId, projectRoot);
|
|
553
545
|
}
|
|
554
|
-
|
|
555
546
|
if (!state) return null;
|
|
556
547
|
|
|
557
548
|
if (state.status === "active") {
|
|
@@ -592,34 +583,6 @@ export function createContextFromPrompt(
|
|
|
592
583
|
);
|
|
593
584
|
}
|
|
594
585
|
|
|
595
|
-
/**
|
|
596
|
-
* Find the active context ID programmatically.
|
|
597
|
-
* Checks CONTEXT_ID env var first, then searches for the single active context.
|
|
598
|
-
* Returns null if no active context or multiple active contexts found.
|
|
599
|
-
*/
|
|
600
|
-
export function findActiveContextId(projectRoot?: string): null | string {
|
|
601
|
-
// Env var takes priority
|
|
602
|
-
const envId = process.env.CONTEXT_ID;
|
|
603
|
-
if (envId) {
|
|
604
|
-
const ctx = getContext(envId, projectRoot);
|
|
605
|
-
if (ctx) return ctx.id;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// Search for active contexts
|
|
609
|
-
const active = getAllContexts("active", projectRoot)
|
|
610
|
-
.filter(c => c.mode === "active" || c.mode === "has_plan" || c.mode === "has_handoff");
|
|
611
|
-
|
|
612
|
-
if (active.length === 1) return active[0]!.id;
|
|
613
|
-
if (active.length > 1) {
|
|
614
|
-
// Multiple active — try to find the most recently active
|
|
615
|
-
const sorted = active.sort((a, b) =>
|
|
616
|
-
(b.last_active ?? "").localeCompare(a.last_active ?? ""),
|
|
617
|
-
);
|
|
618
|
-
return sorted[0]!.id;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
586
|
|
|
624
587
|
// ---------------------------------------------------------------------------
|
|
625
588
|
// Archive helpers
|
|
@@ -639,9 +602,9 @@ function updateArchiveIndex(state: ContextState, projectRoot?: string): boolean
|
|
|
639
602
|
|
|
640
603
|
if (fs.existsSync(archiveIndexPath)) {
|
|
641
604
|
try {
|
|
642
|
-
archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "
|
|
643
|
-
} catch (
|
|
644
|
-
logWarn("context_store", `Failed to read archive index, recreating: ${
|
|
605
|
+
archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf-8"));
|
|
606
|
+
} catch (e: any) {
|
|
607
|
+
logWarn("context_store", `Failed to read archive index, recreating: ${e}`);
|
|
645
608
|
}
|
|
646
609
|
}
|
|
647
610
|
|
|
@@ -653,7 +616,6 @@ function updateArchiveIndex(state: ContextState, projectRoot?: string): boolean
|
|
|
653
616
|
if (!success) {
|
|
654
617
|
logWarn("context_store", `Failed to write archive index: ${error}`);
|
|
655
618
|
}
|
|
656
|
-
|
|
657
619
|
return success;
|
|
658
620
|
}
|
|
659
621
|
|
|
@@ -669,8 +631,8 @@ function restoreFromArchive(contextId: string, projectRoot?: string): ContextSta
|
|
|
669
631
|
|
|
670
632
|
try {
|
|
671
633
|
fs.renameSync(archiveDir, activeDir);
|
|
672
|
-
} catch (
|
|
673
|
-
logError("context_store", `Failed to restore context from archive: ${
|
|
634
|
+
} catch (e: any) {
|
|
635
|
+
logError("context_store", `Failed to restore context from archive: ${e}`);
|
|
674
636
|
return null;
|
|
675
637
|
}
|
|
676
638
|
|
|
@@ -687,7 +649,7 @@ function removeFromArchiveIndex(contextId: string, projectRoot?: string): boolea
|
|
|
687
649
|
if (!fs.existsSync(archiveIndexPath)) return true;
|
|
688
650
|
|
|
689
651
|
try {
|
|
690
|
-
const archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "
|
|
652
|
+
const archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf-8")) as IndexFile;
|
|
691
653
|
if (archiveIndex.contexts[contextId]) {
|
|
692
654
|
delete archiveIndex.contexts[contextId];
|
|
693
655
|
archiveIndex.updated_at = nowIso();
|
|
@@ -698,10 +660,9 @@ function removeFromArchiveIndex(contextId: string, projectRoot?: string): boolea
|
|
|
698
660
|
return false;
|
|
699
661
|
}
|
|
700
662
|
}
|
|
701
|
-
|
|
702
663
|
return true;
|
|
703
|
-
} catch (
|
|
704
|
-
logWarn("context_store", `Failed to read archive index: ${
|
|
664
|
+
} catch (e: any) {
|
|
665
|
+
logWarn("context_store", `Failed to read archive index: ${e}`);
|
|
705
666
|
return false;
|
|
706
667
|
}
|
|
707
668
|
}
|
|
@@ -5,115 +5,116 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// §1.1
|
|
8
|
-
export type Mode = "
|
|
8
|
+
export type Mode = "idle" | "has_plan" | "has_handoff" | "active";
|
|
9
9
|
|
|
10
10
|
export interface ContextState {
|
|
11
|
-
created_at: string;
|
|
12
|
-
handoff_consumed: boolean;
|
|
13
|
-
handoff_path: null | string;
|
|
14
11
|
id: string;
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
status: "active" | "completed";
|
|
13
|
+
summary: string;
|
|
17
14
|
method: string;
|
|
15
|
+
tags: string[];
|
|
16
|
+
created_at: string;
|
|
17
|
+
last_active: string;
|
|
18
18
|
mode: Mode;
|
|
19
|
+
plan_path: string | null;
|
|
20
|
+
plan_hash: string | null;
|
|
21
|
+
plan_signature: string | null;
|
|
22
|
+
plan_id: string | null;
|
|
19
23
|
plan_anchors: string[];
|
|
20
24
|
plan_consumed: boolean;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
plan_signature: null | string;
|
|
25
|
+
plan_hash_consumed: string | null;
|
|
26
|
+
handoff_path: string | null;
|
|
27
|
+
handoff_consumed: boolean;
|
|
25
28
|
session_ids: string[];
|
|
26
|
-
|
|
27
|
-
summary: string;
|
|
28
|
-
tags: string[];
|
|
29
|
+
last_session: LastSession | null;
|
|
29
30
|
tasks: Task[];
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
// §1.2
|
|
33
34
|
export interface GitState {
|
|
34
35
|
branch?: string;
|
|
35
|
-
last_commit_short?: string;
|
|
36
36
|
uncommitted_files?: string[];
|
|
37
|
+
last_commit_short?: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface LastSession {
|
|
41
|
+
session_id?: string;
|
|
42
|
+
saved_at?: string;
|
|
43
|
+
save_reason?: string;
|
|
44
|
+
transcript_path?: string;
|
|
40
45
|
context_remaining_pct?: number;
|
|
41
46
|
context_warnings_fired?: number[];
|
|
42
47
|
git_state?: GitState;
|
|
43
|
-
save_reason?: string;
|
|
44
|
-
saved_at?: string;
|
|
45
|
-
session_id?: string;
|
|
46
|
-
transcript_path?: string;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
// §1.3
|
|
50
51
|
export interface Task {
|
|
52
|
+
id: string;
|
|
53
|
+
subject: string;
|
|
54
|
+
description: string;
|
|
51
55
|
active_form: string;
|
|
52
|
-
|
|
56
|
+
status: "pending" | "in_progress" | "completed" | "blocked";
|
|
53
57
|
created_at: string;
|
|
54
|
-
|
|
58
|
+
completed_at: string | null;
|
|
55
59
|
evidence: string;
|
|
60
|
+
work_summary: string;
|
|
56
61
|
files_changed: string[];
|
|
57
|
-
id: string;
|
|
58
62
|
session_id?: string;
|
|
59
|
-
status: "blocked" | "completed" | "in_progress" | "pending";
|
|
60
|
-
subject: string;
|
|
61
|
-
work_summary: string;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
// §1.4
|
|
65
66
|
export interface IndexEntry {
|
|
66
|
-
last_active: string;
|
|
67
|
-
mode: string;
|
|
68
67
|
summary: string;
|
|
68
|
+
mode: string;
|
|
69
|
+
last_active: string;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export interface IndexFile {
|
|
72
|
-
contexts: Record<string, IndexEntry>;
|
|
73
|
-
sessions: Record<string, string>;
|
|
74
|
-
updated_at: string;
|
|
75
73
|
version: "3.0";
|
|
74
|
+
updated_at: string;
|
|
75
|
+
sessions: Record<string, string>;
|
|
76
|
+
contexts: Record<string, IndexEntry>;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
// §1.5
|
|
79
80
|
export interface LogEntry {
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
ts: string;
|
|
82
|
+
level: "debug" | "info" | "warn" | "error";
|
|
82
83
|
hook: string;
|
|
83
|
-
level: "debug" | "error" | "info" | "warn";
|
|
84
84
|
msg: string;
|
|
85
|
+
component?: string;
|
|
86
|
+
data?: any;
|
|
85
87
|
tb?: string;
|
|
86
|
-
ts: string;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
// §1.6
|
|
90
91
|
export interface HookInput {
|
|
92
|
+
hook_event_name: string;
|
|
93
|
+
tool_name?: string;
|
|
94
|
+
tool_input?: Record<string, any>;
|
|
95
|
+
tool_result?: string;
|
|
96
|
+
session_id?: string;
|
|
97
|
+
cwd?: string;
|
|
98
|
+
transcript_path?: string;
|
|
91
99
|
context_window?: {
|
|
92
|
-
context_window_size?: number;
|
|
93
100
|
current_usage?: {
|
|
94
|
-
cache_creation_input_tokens?: number;
|
|
95
101
|
cache_read_input_tokens?: number;
|
|
96
102
|
input_tokens?: number;
|
|
103
|
+
cache_creation_input_tokens?: number;
|
|
97
104
|
output_tokens?: number;
|
|
98
105
|
};
|
|
106
|
+
context_window_size?: number;
|
|
99
107
|
};
|
|
100
|
-
cwd?: string;
|
|
101
|
-
hook_event_name: string;
|
|
102
108
|
permission_mode?: string;
|
|
103
|
-
session_id?: string;
|
|
104
109
|
source?: string;
|
|
105
|
-
tool_input?: Record<string, any>;
|
|
106
|
-
tool_name?: string;
|
|
107
|
-
tool_result?: string;
|
|
108
|
-
transcript_path?: string;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
// §1.7 — Three hook output patterns (see hook-utils.ts for emit functions)
|
|
112
113
|
export interface HookOutput {
|
|
113
114
|
// Pattern 1: hookSpecificOutput (PreToolUse, PostToolUse, UserPromptSubmit, etc.)
|
|
114
115
|
hookSpecificOutput?: {
|
|
115
|
-
additionalContext?: string;
|
|
116
116
|
hookEventName?: string;
|
|
117
|
+
additionalContext?: string;
|
|
117
118
|
permissionDecision?: "allow" | "deny" | "ask";
|
|
118
119
|
permissionDecisionReason?: string;
|
|
119
120
|
updatedInput?: Record<string, unknown>;
|
|
@@ -135,45 +136,45 @@ export interface PermissionRequestOutput {
|
|
|
135
136
|
|
|
136
137
|
// §1.8
|
|
137
138
|
export interface InferenceResult {
|
|
139
|
+
success: boolean;
|
|
140
|
+
output: string;
|
|
138
141
|
error?: string;
|
|
139
142
|
latency_ms: number;
|
|
140
|
-
output: string;
|
|
141
|
-
success: boolean;
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
// §1.9
|
|
145
146
|
export interface HandoffDocument {
|
|
146
|
-
active_tasks: Task[];
|
|
147
|
-
completed_tasks_this_session: Array<{ subject: string }>;
|
|
148
|
-
context_folder: string;
|
|
149
147
|
context_id: string;
|
|
150
148
|
context_summary: string;
|
|
149
|
+
session_id: string;
|
|
150
|
+
reason: string;
|
|
151
151
|
created_at: string;
|
|
152
|
+
plan_path: string | null;
|
|
153
|
+
context_folder: string;
|
|
152
154
|
events_log_path: string;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
next_steps: string[];
|
|
156
|
-
plan_path: null | string;
|
|
157
|
-
reason: string;
|
|
158
|
-
session_id: string;
|
|
155
|
+
active_tasks: Task[];
|
|
156
|
+
completed_tasks_this_session: Array<{ subject: string }>;
|
|
159
157
|
work_summary: string;
|
|
158
|
+
next_steps: string[];
|
|
159
|
+
important_notes: string[];
|
|
160
|
+
file_path: string | null;
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
// §1.10
|
|
163
164
|
export interface HandoffSections {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
index: string | null;
|
|
166
|
+
deadEnds: string | null;
|
|
167
|
+
pending: string | null;
|
|
168
|
+
plan: string | null;
|
|
169
|
+
decisions: string | null;
|
|
170
|
+
completedWork: string | null;
|
|
171
|
+
context: string | null;
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
// §1.11
|
|
174
175
|
export interface CaretCommand {
|
|
175
176
|
ends: string[];
|
|
176
|
-
|
|
177
|
+
select: string | null;
|
|
178
|
+
new_context_desc: string | null;
|
|
177
179
|
remaining_prompt: string;
|
|
178
|
-
select: null | string;
|
|
179
180
|
}
|
|
@@ -8,17 +8,26 @@
|
|
|
8
8
|
* Prints the context ID to stdout. Exits 1 if no active context found.
|
|
9
9
|
* Used by command templates (/handoff, /handoff-resume) to programmatically
|
|
10
10
|
* get the context ID instead of parsing system reminders.
|
|
11
|
+
*
|
|
12
|
+
* Requires CLAUDE_SESSION_ID environment variable (set by Claude Code).
|
|
11
13
|
*/
|
|
14
|
+
import { getContextBySessionId } from "../lib-ts/context/context-store.js";
|
|
12
15
|
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
13
16
|
import { eprint } from "../lib-ts/base/utils.js";
|
|
14
|
-
import { findActiveContextId } from "../lib-ts/context/context-store.js";
|
|
15
17
|
|
|
16
18
|
const projectRoot = getProjectRoot(process.cwd());
|
|
17
|
-
const
|
|
19
|
+
const sessionId = process.env.CLAUDE_SESSION_ID;
|
|
20
|
+
|
|
21
|
+
if (!sessionId) {
|
|
22
|
+
eprint("CLAUDE_SESSION_ID not set. This script must be run from within a Claude Code session.");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const context = getContextBySessionId(sessionId, projectRoot);
|
|
18
27
|
|
|
19
|
-
if (!
|
|
20
|
-
eprint(
|
|
28
|
+
if (!context) {
|
|
29
|
+
eprint(`No context found for session: ${sessionId}`);
|
|
21
30
|
process.exit(1);
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
console.log(
|
|
33
|
+
console.log(context.id);
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
|
|
18
|
-
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
19
|
-
import { getGitStatusShort } from "../lib-ts/base/git-state.js";
|
|
20
|
-
import { eprint } from "../lib-ts/base/utils.js";
|
|
21
|
-
import { findActiveContextId } from "../lib-ts/context/context-store.js";
|
|
22
18
|
import {
|
|
23
19
|
findLatestHandoff,
|
|
24
20
|
readHandoffSections,
|
|
25
21
|
getHandoffTimestamp,
|
|
26
22
|
getHandoffPlanReference,
|
|
27
23
|
} from "../lib-ts/handoff/handoff-reader.js";
|
|
24
|
+
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
25
|
+
import { getContextBySessionId } from "../lib-ts/context/context-store.js";
|
|
26
|
+
import { getGitStatusShort } from "../lib-ts/base/git-state.js";
|
|
27
|
+
import { eprint } from "../lib-ts/base/utils.js";
|
|
28
28
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Helpers
|
|
@@ -117,19 +117,22 @@ function resolveHandoffFolder(args: string[]): [string, string | null] {
|
|
|
117
117
|
return [target, null];
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// Auto-discover
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
// Auto-discover via session ID (when running from Claude Code)
|
|
121
|
+
const sessionId = process.env.CLAUDE_SESSION_ID;
|
|
122
|
+
if (sessionId) {
|
|
123
|
+
const context = getContextBySessionId(sessionId, projectRoot);
|
|
124
|
+
if (context) {
|
|
125
|
+
const folder = findLatestHandoff(context.id, projectRoot);
|
|
126
|
+
if (!folder) {
|
|
127
|
+
eprint(`No handoff folders found for context: ${context.id} (from session ${sessionId})`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
return [folder, context.id];
|
|
127
131
|
}
|
|
128
|
-
return [folder, discoveredId];
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
eprint(
|
|
132
|
-
"No
|
|
135
|
+
"No context found for current session.\n\n" +
|
|
133
136
|
"Usage: bun resume_handoff.ts <handoff_folder_or_index>\n" +
|
|
134
137
|
" bun resume_handoff.ts --context <context_id>",
|
|
135
138
|
);
|