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.
Files changed (34) hide show
  1. package/dist/templates/_shared/.claude/commands/handoff.md +44 -78
  2. package/dist/templates/_shared/hooks-ts/session_end.ts +16 -11
  3. package/dist/templates/_shared/hooks-ts/session_start.ts +4 -1
  4. package/dist/templates/_shared/lib-ts/base/inference.ts +72 -23
  5. package/dist/templates/_shared/lib-ts/base/state-io.ts +12 -7
  6. package/dist/templates/_shared/lib-ts/context/context-store.ts +35 -74
  7. package/dist/templates/_shared/lib-ts/types.ts +64 -63
  8. package/dist/templates/_shared/scripts/resolve_context.ts +14 -5
  9. package/dist/templates/_shared/scripts/resume_handoff.ts +16 -13
  10. package/dist/templates/_shared/scripts/save_handoff.ts +30 -31
  11. package/dist/templates/_shared/workflows/handoff.md +28 -6
  12. package/dist/templates/cc-native/.claude/commands/rlm/ask.md +136 -0
  13. package/dist/templates/cc-native/.claude/commands/rlm/index.md +21 -0
  14. package/dist/templates/cc-native/.claude/commands/rlm/overview.md +56 -0
  15. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +4 -4
  16. package/dist/templates/cc-native/_cc-native/{plan-review.config.json → cc-native.config.json} +12 -0
  17. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1 -1
  18. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +3 -3
  19. package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +26 -4
  20. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/CLAUDE.md +480 -0
  21. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/embedding-indexer.ts +287 -0
  22. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/hyde.ts +148 -0
  23. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/index.ts +54 -0
  24. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +58 -0
  25. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/ollama-client.ts +208 -0
  26. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +460 -0
  27. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-indexer.ts +447 -0
  28. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-loader.ts +280 -0
  29. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-searcher.ts +274 -0
  30. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +201 -0
  31. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/vector-store.ts +278 -0
  32. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +2 -1
  33. package/oclif.manifest.json +1 -1
  34. 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 as _logDebug, logError, logInfo, logWarn, setContextPath } from "../base/logger.js";
24
- import { dictToState as _dictToState, readStateJson, toDict as _toDict, writeStateJson } from "../base/state-io.js";
25
- import { generateContextId, nowIso } from "../base/utils.js";
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, "utf8");
37
+ const raw = fs.readFileSync(indexPath, "utf-8");
39
38
  return JSON.parse(raw) as IndexFile;
40
- } catch (error: any) {
41
- logWarn("context_store", `Failed to read index, recreating: ${error}`);
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, "utf8"));
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 (error: any) {
111
- logWarn("context_store", `Failed to migrate context.json for '${contextId}': ${error}`);
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, null | string] {
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: null | string,
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, "method" | "summary" | "tags">>,
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 (error: any) {
523
- logError("context_store", `Failed to move context to archive: ${error}`);
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, "utf8"));
643
- } catch (error_: any) {
644
- logWarn("context_store", `Failed to read archive index, recreating: ${error_}`);
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 (error: any) {
673
- logError("context_store", `Failed to restore context from archive: ${error}`);
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, "utf8")) as IndexFile;
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 (error: any) {
704
- logWarn("context_store", `Failed to read archive index: ${error}`);
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 = "active" | "has_handoff" | "has_plan" | "idle";
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
- last_active: string;
16
- last_session: LastSession | null;
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
- plan_hash: null | string;
22
- plan_id: null | string;
23
- plan_path: null | string;
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
- status: "active" | "completed";
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
- completed_at: null | string;
56
+ status: "pending" | "in_progress" | "completed" | "blocked";
53
57
  created_at: string;
54
- description: string;
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
- component?: string;
81
- data?: any;
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
- file_path: null | string;
154
- important_notes: string[];
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
- completedWork: null | string;
165
- context: null | string;
166
- deadEnds: null | string;
167
- decisions: null | string;
168
- index: null | string;
169
- pending: null | string;
170
- plan: null | string;
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
- new_context_desc: null | string;
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 contextId = findActiveContextId(projectRoot);
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 (!contextId) {
20
- eprint("No active context found. Handoffs require an active context.");
28
+ if (!context) {
29
+ eprint(`No context found for session: ${sessionId}`);
21
30
  process.exit(1);
22
31
  }
23
32
 
24
- console.log(contextId);
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 active context
121
- const discoveredId = findActiveContextId(projectRoot);
122
- if (discoveredId) {
123
- const folder = findLatestHandoff(discoveredId, projectRoot);
124
- if (!folder) {
125
- eprint(`No handoff folders found for context: ${discoveredId} (auto-discovered)`);
126
- process.exit(1);
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 active context found.\n\n" +
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
  );