aiwcli 0.12.0 → 0.12.1

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 (37) hide show
  1. package/dist/lib/template-installer.js +3 -3
  2. package/dist/lib/version.js +2 -2
  3. package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
  4. package/dist/templates/_shared/hooks-ts/session_start.ts +14 -11
  5. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +8 -8
  6. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +45 -29
  7. package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
  8. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +1 -1
  9. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +14 -13
  10. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +3 -2
  11. package/dist/templates/_shared/scripts/resume_handoff.ts +4 -4
  12. package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
  13. package/dist/templates/_shared/scripts/status_line.ts +103 -70
  14. package/dist/templates/cc-native/.claude/settings.json +11 -12
  15. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +62 -65
  16. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +24 -8
  17. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +3 -2
  18. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +5 -5
  19. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +4 -4
  20. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +6 -5
  21. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +19 -18
  22. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +67 -18
  23. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +6 -6
  24. package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +3 -2
  25. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +14 -11
  26. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +108 -108
  27. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +2 -2
  28. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +18 -18
  29. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +75 -74
  30. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +8 -8
  31. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +33 -33
  32. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +4 -2
  33. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +17 -16
  34. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +10 -2
  35. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
  36. package/oclif.manifest.json +1 -1
  37. package/package.json +1 -1
@@ -75,14 +75,14 @@ export async function checkTemplateStatus(templatePath, targetDir, ides, templat
75
75
  * Patterns to exclude when copying template directories.
76
76
  * These are development/test artifacts that shouldn't be packaged.
77
77
  */
78
- const EXCLUDED_PATTERNS = [
78
+ const EXCLUDED_PATTERNS = new Set([
79
79
  '_output',
80
- ];
80
+ ]);
81
81
  /**
82
82
  * Check if a filename should be excluded from copying
83
83
  */
84
84
  export function shouldExclude(name) {
85
- return EXCLUDED_PATTERNS.includes(name);
85
+ return EXCLUDED_PATTERNS.has(name);
86
86
  }
87
87
  /**
88
88
  * Copy directory recursively with proper error handling.
@@ -37,7 +37,7 @@ const MIN_CLAUDE_CODE_VERSION = '0.1.0';
37
37
  * Known incompatible Claude Code versions.
38
38
  * These versions have confirmed issues with AI Workflow CLI.
39
39
  */
40
- const INCOMPATIBLE_VERSIONS = ['0.0.9'];
40
+ const INCOMPATIBLE_VERSIONS = new Set(['0.0.9']);
41
41
  /**
42
42
  * Detects Claude Code version by executing `claude --version`.
43
43
  *
@@ -109,7 +109,7 @@ export function checkVersionCompatibility(version) {
109
109
  };
110
110
  }
111
111
  // Check known incompatible versions first
112
- if (INCOMPATIBLE_VERSIONS.includes(version)) {
112
+ if (INCOMPATIBLE_VERSIONS.has(version)) {
113
113
  return {
114
114
  compatible: false,
115
115
  version,
@@ -5,18 +5,70 @@
5
5
  */
6
6
  import * as crypto from "node:crypto";
7
7
  import * as fs from "node:fs";
8
+ import * as path from "node:path";
8
9
 
9
- import { getProjectRoot } from "../lib-ts/base/constants.js";
10
+ import { getProjectRoot, getContextDir } from "../lib-ts/base/constants.js";
10
11
  import { getGitState } from "../lib-ts/base/git-state.js";
11
12
  import {
12
- loadHookInput, logDebug, logDiagnostic, logError, logInfo, logWarn as _logWarn, runHook,
13
+ loadHookInput, runHook, logDebug, logInfo, logWarn, logError, logDiagnostic,
13
14
  } from "../lib-ts/base/hook-utils.js";
14
15
  import { nowIso } from "../lib-ts/base/utils.js";
15
16
  import { getContextBySessionId, saveState } from "../lib-ts/context/context-store.js";
16
17
  import {
17
- extractPlanAnchors, findLatestPlan, generatePlanId, normalizePlanContent,
18
+ findLatestPlan, normalizePlanContent, generatePlanId, extractPlanAnchors,
18
19
  } from "../lib-ts/context/plan-manager.js";
19
20
 
21
+ /**
22
+ * Archive session transcript to context's session-transcripts/ folder.
23
+ * Returns archived path on success, null if skipped or failed.
24
+ */
25
+ function archiveTranscript(
26
+ transcriptPath: string,
27
+ contextId: string,
28
+ sessionId: string,
29
+ projectRoot: string,
30
+ ): string | null {
31
+ // 1. Validate inputs
32
+ if (!transcriptPath || !fs.existsSync(transcriptPath)) {
33
+ logDebug("session_end", `Transcript not found: ${transcriptPath}`);
34
+ return null;
35
+ }
36
+
37
+ // 2. Ensure session-transcripts directory exists
38
+ const contextDir = getContextDir(contextId, projectRoot);
39
+ const transcriptsDir = path.join(contextDir, "session-transcripts");
40
+ fs.mkdirSync(transcriptsDir, { recursive: true });
41
+
42
+ // 3. Generate archive filename: YYYY-MM-DD-HHMM-{session_id}.jsonl
43
+ const now = new Date();
44
+ // Format: 2026-02-14-1400 (year-month-day-hourminute)
45
+ // Note: Hours and minutes are concatenated without separator (HHMM)
46
+ const timestamp =
47
+ `${now.getFullYear()}-` +
48
+ `${String(now.getMonth() + 1).padStart(2, "0")}-` +
49
+ `${String(now.getDate()).padStart(2, "0")}-` +
50
+ `${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
51
+
52
+ // 4. Handle collisions (rare, but possible with rapid session churn)
53
+ let archiveName = `${timestamp}-${sessionId}.jsonl`;
54
+ let archivePath = path.join(transcriptsDir, archiveName);
55
+ let counter = 2;
56
+ while (fs.existsSync(archivePath)) {
57
+ archiveName = `${timestamp}-${sessionId}-${counter}.jsonl`;
58
+ archivePath = path.join(transcriptsDir, archiveName);
59
+ counter++;
60
+ }
61
+
62
+ // 5. Copy transcript file
63
+ try {
64
+ fs.copyFileSync(transcriptPath, archivePath);
65
+ return archivePath;
66
+ } catch (error) {
67
+ logError("session_end", `Failed to copy transcript: ${error}`);
68
+ return null;
69
+ }
70
+ }
71
+
20
72
  function main(): void {
21
73
  const payload = loadHookInput();
22
74
  if (!payload) return;
@@ -50,6 +102,25 @@ function main(): void {
50
102
  };
51
103
  state.last_active = nowIso();
52
104
 
105
+ // Archive transcript (NEW)
106
+ // Note: state is a ContextState object (from getContextBySessionId on line 33)
107
+ // state.id is the context ID used to construct paths like _output/contexts/{context_id}/
108
+ if (payload.transcript_path) {
109
+ try {
110
+ const archived = archiveTranscript(
111
+ payload.transcript_path,
112
+ state.id, // Context ID, verified by existing code on line 98: saveState(state.id, ...)
113
+ sessionId,
114
+ projectRoot,
115
+ );
116
+ if (archived) {
117
+ logInfo("session_end", `Archived transcript: ${path.basename(archived)}`);
118
+ }
119
+ } catch (error) {
120
+ logError("session_end", `Transcript archival failed: ${error}`);
121
+ }
122
+ }
123
+
53
124
  // Plan fallback assignment (skip in plan mode — rejected plans shouldn't stage)
54
125
  if (permissionMode !== "plan") {
55
126
  // Step 1: Assign plan fields if missing
@@ -57,7 +128,7 @@ function main(): void {
57
128
  const latestPlanPath = findLatestPlan(state.id, projectRoot);
58
129
  if (latestPlanPath) {
59
130
  try {
60
- const content = fs.readFileSync(latestPlanPath, "utf8");
131
+ const content = fs.readFileSync(latestPlanPath, "utf-8");
61
132
  const normalized = normalizePlanContent(content);
62
133
  const planHash = crypto.createHash("sha256")
63
134
  .update(normalized, "utf-8")
@@ -3,17 +3,17 @@
3
3
  * SessionStart hook: Restore context after /clear (plan/handoff) or compaction.
4
4
  * Routes by source field to appropriate handler.
5
5
  */
6
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
6
7
  import {
7
8
  loadHookInput, emitContext, runHook, runHookAsync,
8
9
  logDebug, logInfo, logError, logDiagnostic,
9
10
  } from "../lib-ts/base/hook-utils.js";
10
- import { getProjectRoot } from "../lib-ts/base/constants.js";
11
- import {
12
- getContextBySessionId, getAllContexts, bindSession, updateMode,
13
- } from "../lib-ts/context/context-store.js";
14
11
  import {
15
12
  buildRestoreSections, formatHandoffContinuation, getModeDisplay,
16
13
  } from "../lib-ts/context/context-formatter.js";
14
+ import {
15
+ getContextBySessionId, getAllContexts, bindSession, updateMode,
16
+ } from "../lib-ts/context/context-store.js";
17
17
  import type { ContextState } from "../lib-ts/types.js";
18
18
 
19
19
  /**
@@ -124,15 +124,18 @@ async function main(): Promise<void> {
124
124
  logDiagnostic("session_start", "entry", `source=${source}, session=${sessionId}`);
125
125
 
126
126
  switch (source) {
127
- case "compact":
128
- handleCompactRestore(sessionId, projectRoot);
129
- break;
130
- case "clear":
127
+ case "clear": {
131
128
  await handleClearRestore(sessionId, projectRoot);
132
- break;
133
- default:
129
+ break;
130
+ }
131
+ case "compact": {
132
+ handleCompactRestore(sessionId, projectRoot);
133
+ break;
134
+ }
135
+ default: {
134
136
  logDebug("session_start", `Unhandled source: ${source}`);
135
- break;
137
+ break;
138
+ }
136
139
  }
137
140
  }
138
141
 
@@ -6,14 +6,14 @@
6
6
  * Uses emitContext() for output — context text is passed via hookSpecificOutput JSON.
7
7
  * Catches BlockRequest and uses emitBlock() to block the prompt.
8
8
  */
9
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
9
10
  import {
10
11
  loadHookInput, runHookAsync, logDebug, logInfo, logWarn, logBlocking, logDiagnostic, hookLog, emitContext, emitBlock,
11
12
  } from "../lib-ts/base/hook-utils.js";
12
- import { getProjectRoot } from "../lib-ts/base/constants.js";
13
+ import { determineContext, BlockRequest } from "../lib-ts/context/context-selector.js";
13
14
  import {
14
15
  getContextBySessionId, bindSession, maybeActivate, saveState,
15
16
  } from "../lib-ts/context/context-store.js";
16
- import { determineContext, BlockRequest } from "../lib-ts/context/context-selector.js";
17
17
 
18
18
  async function asyncMain(): Promise<void> {
19
19
  const payload = loadHookInput();
@@ -38,8 +38,8 @@ async function asyncMain(): Promise<void> {
38
38
  // Returning user — context already bound (stderr: false to avoid "hook error" display)
39
39
  try {
40
40
  maybeActivate(existingCtx.id, permissionMode, projectRoot, "user_prompt_submit");
41
- } catch (e) {
42
- hookLog("warn", "user_prompt_submit", `maybeActivate failed (non-critical): ${e}`, { stderr: false });
41
+ } catch (error) {
42
+ hookLog("warn", "user_prompt_submit", `maybeActivate failed (non-critical): ${error}`, { stderr: false });
43
43
  }
44
44
  hookLog("debug", "user_prompt_submit", `Session bound to ${existingCtx.id}`, { stderr: false });
45
45
  } else if (prompt) {
@@ -64,12 +64,12 @@ async function asyncMain(): Promise<void> {
64
64
  if (outputText) {
65
65
  outputs.push(outputText);
66
66
  }
67
- } catch (e) {
68
- if (e instanceof BlockRequest) {
69
- emitBlock((e as Error).message);
67
+ } catch (error) {
68
+ if (error instanceof BlockRequest) {
69
+ emitBlock((error as Error).message);
70
70
  return;
71
71
  }
72
- throw e; // Re-throw unexpected errors
72
+ throw error; // Re-throw unexpected errors
73
73
  }
74
74
  }
75
75
 
@@ -4,14 +4,15 @@
4
4
  * See SPEC.md §5
5
5
  */
6
6
 
7
- import * as fs from "node:fs";
8
- import { logDebug, logInfo, logWarn, logError, logBlocking, logHookError, logDiagnostic, hookLog, setSessionId, setContextPath, getContextPath as _getContextPath } from "./logger.js";
7
+ import * as fs from "node:fs";
8
+
9
9
  import { getProjectRoot } from "./constants.js";
10
+ import { logDebug, logWarn, hookLog, setSessionId, getContextPath as _getContextPath } from "./logger.js";
10
11
  import { getContextBySessionId } from "../context/context-store.js";
11
12
  import type { HookInput, HookOutput, PermissionRequestOutput } from "../types.js";
12
13
 
13
14
  // Re-export logger functions for convenience (matches Python hook_utils re-exports)
14
- export { logDebug, logInfo, logWarn, logError, logBlocking, logHookError, logDiagnostic, hookLog, setSessionId, setContextPath };
15
+ export { setSessionId };
15
16
 
16
17
  // Context window baseline: tokens not visible in hook data §5.9
17
18
  export const CONTEXT_BASELINE_TOKENS = 22_600;
@@ -93,7 +94,7 @@ export function checkSkipPersistence(
93
94
  const toolInput = getToolInput(payload);
94
95
  if (!toolInput) return false;
95
96
 
96
- const metadata = toolInput.metadata;
97
+ const {metadata} = toolInput;
97
98
  if (metadata && typeof metadata === "object" && metadata.skip_persistence) {
98
99
  logDebug(hookName, "Skipping persistence (skip_persistence flag set)");
99
100
  return true;
@@ -105,11 +106,23 @@ export function checkSkipPersistence(
105
106
  * Emit hookSpecificOutput with additionalContext to stdout.
106
107
  * hookEventName is required by Claude Code's Zod validator (discriminated union).
107
108
  * Auto-detected from stdin payload (set by loadHookInput/runHook).
109
+ *
110
+ * SubagentStop and Stop events use top-level systemMessage field instead of hookSpecificOutput.
108
111
  * See SPEC.md §5.5
109
112
  */
110
113
  export function emitContext(additionalContext: string): void {
111
114
  const eventName = _lastHookEvent ?? undefined;
112
115
  const tool = _lastToolName;
116
+
117
+ // SubagentStop and Stop use top-level systemMessage field
118
+ if (eventName === "SubagentStop" || eventName === "Stop") {
119
+ const out = { systemMessage: additionalContext };
120
+ process.stdout.write(JSON.stringify(out) + "\n");
121
+ _logEmit("systemMessage", additionalContext.length, { event: eventName ?? "unknown", systemMessage: additionalContext });
122
+ return;
123
+ }
124
+
125
+ // All other events use hookSpecificOutput
113
126
  const out: HookOutput = {
114
127
  hookSpecificOutput: {
115
128
  ...(eventName ? { hookEventName: eventName } : {}),
@@ -153,8 +166,8 @@ export function emitContextAndBlock(
153
166
  process.stdout.write(json + "\n");
154
167
  }
155
168
 
156
- /** Log hook output (context or block) to hook-log.jsonl for visibility. */
157
- function _logEmit(type: "context" | "block", chars: number, payload: Record<string, any>): void {
169
+ /** Log hook output (context, systemMessage, or block) to hook-log.jsonl for visibility. */
170
+ function _logEmit(type: "context" | "systemMessage" | "block", chars: number, payload: Record<string, any>): void {
158
171
  const hook = _cachedHookName ?? "unknown";
159
172
  const event = payload.event ?? "unknown";
160
173
  const mechanism = payload.mechanism ? ` via ${payload.mechanism}` : "";
@@ -268,27 +281,28 @@ export function emitPermissionDecision(
268
281
  export function emitBlock(reason: string, context?: string): void {
269
282
  const event = _lastHookEvent;
270
283
  switch (event) {
271
- case "PreToolUse":
272
- emitContextAndBlock(context ?? reason, reason);
273
- break;
274
- case "UserPromptSubmit":
275
- emitBlockPrompt(reason, context);
284
+ case "PermissionRequest":
285
+ emitPermissionDecision("deny", { message: reason });
276
286
  break;
277
287
  case "PostToolUse":
278
288
  case "PostToolUseFailure":
279
289
  emitBlockViaExit(reason, context);
280
290
  break;
291
+ case "PreToolUse":
292
+ emitContextAndBlock(context ?? reason, reason);
293
+ break;
281
294
  case "Stop":
282
295
  case "SubagentStop":
283
296
  emitBlockTopLevel(reason);
284
297
  break;
285
- case "PermissionRequest":
286
- emitPermissionDecision("deny", { message: reason });
298
+ case "UserPromptSubmit":
299
+ emitBlockPrompt(reason, context);
287
300
  break;
288
- default:
301
+ default: {
289
302
  logWarn(_cachedHookName ?? "unknown",
290
303
  `emitBlock() called from ${event ?? "unknown"} — no blocking mechanism exists for this event type, ignoring`);
291
- break;
304
+ break;
305
+ }
292
306
  }
293
307
  }
294
308
 
@@ -296,7 +310,7 @@ export function emitBlock(reason: string, context?: string): void {
296
310
  * Auto-detect template origin from the hook script path.
297
311
  */
298
312
  function detectTemplate(scriptPath = ""): string {
299
- const p = (scriptPath || (process.argv[1] ?? "")).replace(/\\/g, "/");
313
+ const p = (scriptPath || (process.argv[1] ?? "")).replaceAll('\\', "/");
300
314
  if (p.includes("/_shared/hooks/") || p.startsWith("_shared/hooks/")) {
301
315
  return "shared";
302
316
  }
@@ -438,16 +452,16 @@ export function runHook(
438
452
  const result = mainFunc();
439
453
  exitCode = typeof result === "number" ? result : 0;
440
454
  status = exitCode !== 0 ? "blocked" : "success";
441
- } catch (e: any) {
442
- if (e instanceof Error && e.message.startsWith("SystemExit:")) {
443
- const code = parseInt(e.message.slice(11), 10);
444
- exitCode = isNaN(code) ? (e.message.slice(11) ? 1 : 0) : code;
455
+ } catch (error: any) {
456
+ if (error instanceof Error && error.message.startsWith("SystemExit:")) {
457
+ const code = parseInt(error.message.slice(11), 10);
458
+ exitCode = isNaN(code) ? (error.message.slice(11) ? 1 : 0) : code;
445
459
  status = exitCode !== 0 ? "blocked" : "success";
446
460
  } else {
447
461
  exitCode = 0; // Non-blocking
448
462
  status = "error";
449
- const stack = e instanceof Error ? e.stack ?? "" : "";
450
- errorInfo = [e instanceof Error ? e : new Error(String(e)), stack];
463
+ const stack = error instanceof Error ? error.stack ?? "" : "";
464
+ errorInfo = [error instanceof Error ? error : new Error(String(error)), stack];
451
465
  }
452
466
  }
453
467
 
@@ -487,19 +501,19 @@ export function runHookAsync(
487
501
  _emitHookEnd(hookName, startTime, exitCode, exitCode !== 0 ? "blocked" : "success", null, startData, event, tool, template);
488
502
  _drainAndExit(exitCode);
489
503
  })
490
- .catch((e: any) => {
504
+ .catch((error: any) => {
491
505
  let exitCode = 0;
492
506
  let status = "error";
493
507
  let errorInfo: [Error, string] | null = null;
494
508
 
495
- if (e instanceof Error && e.message.startsWith("SystemExit:")) {
496
- const code = parseInt(e.message.slice(11), 10);
497
- exitCode = isNaN(code) ? (e.message.slice(11) ? 1 : 0) : code;
509
+ if (error instanceof Error && error.message.startsWith("SystemExit:")) {
510
+ const code = parseInt(error.message.slice(11), 10);
511
+ exitCode = isNaN(code) ? (error.message.slice(11) ? 1 : 0) : code;
498
512
  status = exitCode !== 0 ? "blocked" : "success";
499
513
  } else {
500
514
  exitCode = 0; // Non-blocking (fail open)
501
- const stack = e instanceof Error ? e.stack ?? "" : "";
502
- errorInfo = [e instanceof Error ? e : new Error(String(e)), stack];
515
+ const stack = error instanceof Error ? error.stack ?? "" : "";
516
+ errorInfo = [error instanceof Error ? error : new Error(String(error)), stack];
503
517
  }
504
518
 
505
519
  _emitHookEnd(hookName, startTime, exitCode, status, errorInfo, startData, event, tool, template);
@@ -541,7 +555,7 @@ function _emitHookEnd(
541
555
  if (errorInfo) {
542
556
  const [err, tb] = errorInfo;
543
557
  endData.error_type = err.constructor.name;
544
- hookLog("error", hookName, `[${endEvent}] ${err.constructor.name}: ${String(err).replace(/[\n\r]/g, " ").slice(0, 200)}`, { traceback_str: tb });
558
+ hookLog("error", hookName, `[${endEvent}] ${err.constructor.name}: ${String(err).replaceAll(/[\n\r]/g, " ").slice(0, 200)}`, { traceback_str: tb });
545
559
  hookLog("error", hookName, `HOOK_END: ${err}`, { data: endData, traceback_str: tb });
546
560
  } else if (status === "blocked") {
547
561
  hookLog("warn", hookName, "HOOK_END", { data: endData });
@@ -568,3 +582,5 @@ function _drainAndExit(code: number): void {
568
582
  process.exit(code);
569
583
  });
570
584
  }
585
+
586
+ export {logInfo, logError, logBlocking, logHookError, logDiagnostic, setContextPath, hookLog, logDebug, logWarn} from "./logger.js";
@@ -235,7 +235,7 @@ export function logHookError(
235
235
  tracebackStr = "",
236
236
  ): void {
237
237
  const errStr = typeof error === "string" ? error : String(error);
238
- const msg = errStr.replace(/[\n\r]/g, " ").slice(0, 200);
238
+ const msg = errStr.replaceAll(/[\n\r]/g, " ").slice(0, 200);
239
239
  const errType =
240
240
  typeof error === "object" && error !== null
241
241
  ? error.constructor.name
@@ -92,7 +92,7 @@ export function isExecSyncError(e: unknown): e is ExecSyncError {
92
92
  */
93
93
  export function shellQuoteWin(arg: string): string {
94
94
  if (process.platform !== "win32") return arg;
95
- return '"' + arg.replace(/"/g, '""') + '"';
95
+ return '"' + arg.replaceAll('"', '""') + '"';
96
96
  }
97
97
 
98
98
  // ---------------------------------------------------------------------------
@@ -8,11 +8,12 @@
8
8
  * - extractPlanPathFromResult: parse plan path from ExitPlanMode output
9
9
  */
10
10
 
11
- import * as fs from "node:fs";
12
- import * as path from "node:path";
13
11
  import * as crypto from "node:crypto";
14
- import { getContextDir, getContextPlansDir, sanitizeTitle } from "../base/constants.js";
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+
15
15
  import { atomicWrite } from "../base/atomic-write.js";
16
+ import { getContextDir, getContextPlansDir, sanitizeTitle } from "../base/constants.js";
16
17
  import { logDebug, logInfo, logWarn, logError } from "../base/logger.js";
17
18
  import { generateSlug } from "../base/utils.js";
18
19
  import type { ContextState } from "../types.js";
@@ -43,8 +44,8 @@ export function archivePlan(
43
44
  let content: string;
44
45
  try {
45
46
  content = fs.readFileSync(planPath, "utf-8");
46
- } catch (e: any) {
47
- logError("plan_manager", `Failed to read plan: ${e}`);
47
+ } catch (error_: any) {
48
+ logError("plan_manager", `Failed to read plan: ${error_}`);
48
49
  return [null, null, null];
49
50
  }
50
51
 
@@ -148,8 +149,8 @@ export function findLatestPlan(
148
149
  if (state?.plan_path && fs.existsSync(state.plan_path)) {
149
150
  return state.plan_path;
150
151
  }
151
- } catch (e: any) {
152
- logWarn("plan_manager", `Failed to check state.json plan_path: ${e}`);
152
+ } catch (error: any) {
153
+ logWarn("plan_manager", `Failed to check state.json plan_path: ${error}`);
153
154
  }
154
155
 
155
156
  // 2. Fall back to most recent .md in plans/ dir
@@ -182,7 +183,7 @@ export function findLatestPlan(
182
183
  * See SPEC.md §9.4
183
184
  */
184
185
  export function generatePlanId(): string {
185
- return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
186
+ return crypto.randomUUID().replaceAll('-', "").slice(0, 8);
186
187
  }
187
188
 
188
189
  /**
@@ -191,8 +192,8 @@ export function generatePlanId(): string {
191
192
  * See SPEC.md §9.5
192
193
  */
193
194
  export function normalizePlanContent(text: string): string {
194
- let result = text.replace(/<[^>]+>/g, "");
195
- result = result.replace(/\s+/g, " ").trim();
195
+ let result = text.replaceAll(/<[^>]+>/g, "");
196
+ result = result.replaceAll(/\s+/g, " ").trim();
196
197
  return result;
197
198
  }
198
199
 
@@ -249,8 +250,8 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
249
250
  let lines: string[];
250
251
  try {
251
252
  lines = fs.readFileSync(transcriptPath, "utf-8").split(/\r?\n/);
252
- } catch (e: any) {
253
- logWarn("plan_manager", `Failed to read transcript: ${e}`);
253
+ } catch (error: any) {
254
+ logWarn("plan_manager", `Failed to read transcript: ${error}`);
254
255
  return null;
255
256
  }
256
257
 
@@ -282,7 +283,7 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
282
283
  if (!filePath) continue;
283
284
 
284
285
  // Check if path contains .claude/plans/ as consecutive parts
285
- const parts = filePath.replace(/\\/g, "/").split("/");
286
+ const parts = filePath.replaceAll('\\', "/").split("/");
286
287
  for (let j = 0; j < parts.length - 1; j++) {
287
288
  if (parts[j] === ".claude" && parts[j + 1] === "plans") {
288
289
  logInfo("plan_manager", `Extracted plan path from transcript: ${filePath}`);
@@ -7,7 +7,8 @@
7
7
  */
8
8
 
9
9
  import * as fs from "node:fs";
10
- import * as path from "node:path";
10
+ import * as path from "node:path";
11
+
11
12
  import { getContextHandoffsDir } from "../base/constants.js";
12
13
  import { getContext } from "../context/context-store.js";
13
14
  import type { HandoffSections } from "../types.js";
@@ -29,7 +30,7 @@ export function findLatestHandoff(contextId: string, projectRoot?: string): stri
29
30
  .sort();
30
31
 
31
32
  if (entries.length === 0) return null;
32
- return path.join(handoffsDir, entries[entries.length - 1]!);
33
+ return path.join(handoffsDir, entries.at(-1)!);
33
34
  } catch {
34
35
  return null;
35
36
  }
@@ -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";
18
22
  import {
19
23
  findLatestHandoff,
20
24
  readHandoffSections,
21
25
  getHandoffTimestamp,
22
26
  getHandoffPlanReference,
23
27
  } from "../lib-ts/handoff/handoff-reader.js";
24
- import { getProjectRoot } from "../lib-ts/base/constants.js";
25
- import { findActiveContextId } 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
@@ -22,12 +22,12 @@
22
22
  import * as fs from "node:fs";
23
23
  import * as path from "node:path";
24
24
 
25
- import { getContext, saveState } from "../lib-ts/context/context-store.js";
26
- import { getHandoffFolderPath, getProjectRoot } from "../lib-ts/base/constants.js";
27
25
  import { atomicWrite } from "../lib-ts/base/atomic-write.js";
28
- import { logInfo, logWarn, logError } from "../lib-ts/base/logger.js";
26
+ import { getHandoffFolderPath, getProjectRoot } from "../lib-ts/base/constants.js";
29
27
  import { getGitStatusShort } from "../lib-ts/base/git-state.js";
28
+ import { logInfo, logWarn, logError } from "../lib-ts/base/logger.js";
30
29
  import { eprint } from "../lib-ts/base/utils.js";
30
+ import { getContext, saveState } from "../lib-ts/context/context-store.js";
31
31
 
32
32
  // ---------------------------------------------------------------------------
33
33
  // Parsing helpers
@@ -250,8 +250,8 @@ function main(): void {
250
250
  } else {
251
251
  logWarn("save_handoff", `Failed to copy plan: ${error}`);
252
252
  }
253
- } catch (e) {
254
- logWarn("save_handoff", `Failed to read plan: ${e}`);
253
+ } catch (error) {
254
+ logWarn("save_handoff", `Failed to read plan: ${error}`);
255
255
  }
256
256
  }
257
257
 
@@ -339,8 +339,8 @@ function main(): void {
339
339
  } else {
340
340
  logWarn("save_handoff", `Could not load context state for ${contextId}`);
341
341
  }
342
- } catch (e) {
343
- logWarn("save_handoff", `Handoff saved but auto-resume won't work (context update failed): ${e}`);
342
+ } catch (error) {
343
+ logWarn("save_handoff", `Handoff saved but auto-resume won't work (context update failed): ${error}`);
344
344
  }
345
345
 
346
346
  // Output success message