aiwcli 0.10.3 → 0.11.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 (191) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.js +28 -131
  3. package/dist/commands/init/index.js +3 -3
  4. package/dist/lib/gitignore-manager.d.ts +32 -0
  5. package/dist/lib/gitignore-manager.js +141 -2
  6. package/dist/templates/CLAUDE.md +8 -8
  7. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  8. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  9. package/dist/templates/_shared/.claude/settings.json +7 -7
  10. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  11. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  12. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  13. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  14. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  15. package/dist/templates/_shared/hooks-ts/session_end.ts +107 -0
  16. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  17. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  18. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  19. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  20. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  21. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
  22. package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
  23. package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
  24. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
  25. package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
  26. package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
  27. package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
  28. package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
  29. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
  30. package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
  31. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
  32. package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
  33. package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
  34. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
  35. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
  36. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
  37. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
  38. package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
  39. package/dist/templates/_shared/lib-ts/types.ts +68 -55
  40. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  41. package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
  42. package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
  43. package/dist/templates/_shared/scripts/status_line.ts +687 -0
  44. package/dist/templates/cc-native/.claude/settings.json +175 -185
  45. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  46. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
  47. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  48. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  49. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
  50. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  51. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
  52. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
  53. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  54. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
  55. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  56. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  57. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
  58. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  59. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
  60. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
  61. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  62. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
  63. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
  64. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  65. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
  66. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  67. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  70. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
  71. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  72. package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
  73. package/oclif.manifest.json +1 -1
  74. package/package.json +2 -2
  75. package/dist/templates/_shared/hooks/__init__.py +0 -16
  76. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  77. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  78. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  79. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  80. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  81. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  82. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  83. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  84. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  85. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  86. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  87. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  88. package/dist/templates/_shared/hooks/archive_plan.py +0 -177
  89. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  90. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  91. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  92. package/dist/templates/_shared/hooks/session_end.py +0 -173
  93. package/dist/templates/_shared/hooks/session_start.py +0 -206
  94. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  95. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  96. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  97. package/dist/templates/_shared/lib/__init__.py +0 -1
  98. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  99. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  100. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  102. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  103. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  104. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  107. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  108. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  109. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  110. package/dist/templates/_shared/lib/base/constants.py +0 -358
  111. package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
  112. package/dist/templates/_shared/lib/base/inference.py +0 -307
  113. package/dist/templates/_shared/lib/base/logger.py +0 -305
  114. package/dist/templates/_shared/lib/base/stop_words.py +0 -221
  115. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  116. package/dist/templates/_shared/lib/base/utils.py +0 -263
  117. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  118. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  131. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  132. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  133. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  134. package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
  135. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  136. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  137. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  138. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  139. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  140. package/dist/templates/_shared/lib/templates/README.md +0 -206
  141. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  142. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  147. package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
  148. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  151. package/dist/templates/_shared/scripts/status_line.py +0 -716
  152. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  153. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  154. package/dist/templates/cc-native/MIGRATION.md +0 -86
  155. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  156. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  157. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  158. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  159. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  160. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  161. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  162. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
  163. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  164. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  165. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  166. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  167. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  171. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  172. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  174. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  175. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  176. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  177. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  178. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  179. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  180. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  181. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  187. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  188. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
  189. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  190. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  191. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { execFileSync } from "node:child_process";
8
+
8
9
  import { logDebug, logWarn } from "./logger.js";
9
10
  import { STOP_WORDS } from "./stop-words.js";
10
11
  import { cleanTextForSlug } from "./utils.js";
@@ -44,19 +45,25 @@ export function inference(
44
45
 
45
46
  try {
46
47
  const isWin = process.platform === "win32";
47
- let stdout: string;
48
+ // On Windows with shell:true, Node.js sets windowsVerbatimArguments —
49
+ // args are joined with spaces, NOT individually quoted. We must manually
50
+ // wrap multi-word/special-char args in "..." for cmd.exe parsing.
51
+ // Inside double quotes: "" = literal ", and |&<> are safe.
52
+ const empty = isWin ? '""' : "";
53
+ let promptArg = fullPrompt;
54
+ if (isWin) {
55
+ promptArg = '"' + fullPrompt.replaceAll(/\r?\n/g, " ").replaceAll('"', '""') + '"';
56
+ }
48
57
 
49
- // Use execFileSync with shell option on Windows for safe argument passing
50
- // (no string interpolation — avoids command injection)
51
- stdout = execFileSync(
58
+ const stdout = execFileSync(
52
59
  "claude",
53
- ["--model", model, "--print", "--setting-sources", "", "-p", fullPrompt],
60
+ ["--model", model, "--print", "--setting-sources", empty, "-p", promptArg],
54
61
  {
55
62
  timeout: timeoutSec * 1000,
56
63
  env,
57
64
  encoding: "utf-8",
58
65
  stdio: ["pipe", "pipe", "pipe"],
59
- shell: isWin, // Windows needs shell for command resolution
66
+ shell: isWin, // Windows needs shell for .cmd resolution
60
67
  },
61
68
  );
62
69
 
@@ -66,10 +73,10 @@ export function inference(
66
73
  output: stdout.trim(),
67
74
  latency_ms: latencyMs,
68
75
  };
69
- } catch (e: any) {
76
+ } catch (error: any) {
70
77
  const latencyMs = Date.now() - startTime;
71
78
 
72
- if (e.code === "ETIMEDOUT" || e.killed) {
79
+ if (error.code === "ETIMEDOUT" || error.killed) {
73
80
  return {
74
81
  success: false,
75
82
  output: "",
@@ -78,7 +85,7 @@ export function inference(
78
85
  };
79
86
  }
80
87
 
81
- if (e.code === "ENOENT") {
88
+ if (error.code === "ENOENT") {
82
89
  return {
83
90
  success: false,
84
91
  output: "",
@@ -88,11 +95,11 @@ export function inference(
88
95
  }
89
96
 
90
97
  // Non-zero exit code
91
- if (e.status !== undefined && e.status !== 0) {
98
+ if (error.status !== undefined && error.status !== 0) {
92
99
  return {
93
100
  success: false,
94
- output: (e.stdout ?? "").toString().trim(),
95
- error: (e.stderr ?? "").toString().trim() || `Exit code: ${e.status}`,
101
+ output: (error.stdout ?? "").toString().trim(),
102
+ error: (error.stderr ?? "").toString().trim() || `Exit code: ${error.status}`,
96
103
  latency_ms: latencyMs,
97
104
  };
98
105
  }
@@ -100,7 +107,7 @@ export function inference(
100
107
  return {
101
108
  success: false,
102
109
  output: "",
103
- error: String(e),
110
+ error: String(error),
104
111
  latency_ms: latencyMs,
105
112
  };
106
113
  }
@@ -126,13 +133,13 @@ Output ONLY the keywords separated by spaces, nothing else.`;
126
133
  export function generateSemanticSummary(
127
134
  prompt: string,
128
135
  timeout = 15,
129
- ): string | null {
136
+ ): null | string {
130
137
  const result = inference(CONTEXT_ID_SYSTEM_PROMPT, prompt, "standard", timeout);
131
138
 
132
139
  if (!result.success || !result.output) return null;
133
140
 
134
141
  let summary = result.output.trim();
135
- summary = summary.replace(/^["']+|["']+$/g, "");
142
+ summary = summary.replaceAll(/^["']+|["']+$/g, "");
136
143
  summary = summary.replace(/[.!?]+$/, "");
137
144
 
138
145
  // Filter stop words
@@ -187,7 +194,7 @@ Respond with ONLY a JSON object: {"slug": "your 8-12 word phrase here"}`;
187
194
  export function generateContextIdSlug(
188
195
  prompt: string,
189
196
  timeout = 3,
190
- ): string | null {
197
+ ): null | string {
191
198
  const truncated = prompt.slice(0, 500);
192
199
 
193
200
  const result = inference(CONTEXT_ID_SLUG_PROMPT, truncated, "fast", timeout);
@@ -200,7 +207,7 @@ export function generateContextIdSlug(
200
207
  const raw = result.output.trim();
201
208
 
202
209
  // Parse JSON response, fall back to raw text
203
- let slug: string | null = null;
210
+ let slug: null | string = null;
204
211
  try {
205
212
  const parsed = JSON.parse(raw);
206
213
  if (parsed && typeof parsed === "object" && "slug" in parsed) {
@@ -213,11 +220,11 @@ export function generateContextIdSlug(
213
220
  if (!slug) slug = raw;
214
221
 
215
222
  // Clean up
216
- slug = slug.replace(/^["'`]+|["'`]+$/g, "");
223
+ slug = slug.replaceAll(/^["'`]+|["'`]+$/g, "");
217
224
  slug = slug.replace(/[.!?]+$/, "");
218
- slug = slug.replace(/-/g, " ");
219
- slug = slug.replace(/[^a-zA-Z0-9 ]/g, "");
220
- slug = slug.replace(/\s+/g, " ").trim();
225
+ slug = slug.replaceAll('-', " ");
226
+ slug = slug.replaceAll(/[^a-zA-Z0-9 ]/g, "");
227
+ slug = slug.replaceAll(/\s+/g, " ").trim();
221
228
 
222
229
  const words = slug.split(" ");
223
230
 
@@ -5,6 +5,11 @@
5
5
  * Log location: _output/hook-log.jsonl (global, all sessions)
6
6
  * Filter by session using the "sid" field.
7
7
  *
8
+ * stderr is OPT-IN: convenience functions (logDebug, logInfo, logWarn, logError)
9
+ * write to file only by default. To also write to stderr (visible to Claude Code
10
+ * as "hook error"), pass { stderr: true } or use logBlocking().
11
+ * logHookError() always writes to stderr (unhandled errors must be visible).
12
+ *
8
13
  * Environment variables:
9
14
  * - HOOK_LOG_DISABLE=1: Disable all file logging
10
15
  * - HOOK_LOG_LEVEL=warn: Minimum level to log (default: debug)
@@ -94,7 +99,7 @@ export function hookLog(
94
99
  const levelNum = LEVELS[levelLower] ?? 0;
95
100
  const component = opts?.component ?? "";
96
101
  const tracebackStr = opts?.traceback_str ?? "";
97
- const stderrEnabled = opts?.stderr !== false;
102
+ const stderrEnabled = opts?.stderr === true;
98
103
 
99
104
  // Write to stderr
100
105
  if (stderrEnabled) {
@@ -147,7 +152,7 @@ export function hookLog(
147
152
  try {
148
153
  if (fs.existsSync(logPath)) {
149
154
  const content = fs.readFileSync(logPath, "utf-8");
150
- const lines = content.split("\n");
155
+ const lines = content.split(/\r?\n/);
151
156
  if (lines.length > MAX_LOG_LINES) {
152
157
  fs.writeFileSync(
153
158
  logPath,
@@ -182,6 +187,14 @@ export function logError(hookName: string, message: string, opts?: Record<string
182
187
  hookLog("error", hookName, message, opts);
183
188
  }
184
189
 
190
+ /**
191
+ * Log an error that SHOULD be visible to user/model via stderr.
192
+ * Use for real problems needing attention, not routine diagnostics.
193
+ */
194
+ export function logBlocking(hookName: string, message: string, opts?: Record<string, any>): void {
195
+ hookLog("error", hookName, message, { ...opts, stderr: true });
196
+ }
197
+
185
198
  /**
186
199
  * Log a structured diagnostic entry at a hook decision point.
187
200
  * See SPEC.md §3.8
@@ -5,9 +5,10 @@
5
5
  */
6
6
 
7
7
  import * as fs from "node:fs";
8
- import * as path from "node:path";
9
- import { getContextDir } from "./constants.js";
8
+ import * as path from "node:path";
9
+
10
10
  import { atomicWrite } from "./atomic-write.js";
11
+ import { getContextDir } from "./constants.js";
11
12
  import { logWarn } from "./logger.js";
12
13
  import type { ContextState, Mode } from "../types.js";
13
14
 
@@ -29,7 +30,8 @@ export function toDict(state: ContextState): Record<string, unknown> {
29
30
  if (value !== null && value !== undefined) {
30
31
  result[key] = value;
31
32
  }
32
- }
33
+ }
34
+
33
35
  return result;
34
36
  }
35
37
 
@@ -52,11 +54,11 @@ export function readStateJson(
52
54
  if (!fs.existsSync(sp)) return null;
53
55
 
54
56
  try {
55
- const raw = fs.readFileSync(sp, "utf-8");
57
+ const raw = fs.readFileSync(sp, "utf8");
56
58
  const data = JSON.parse(raw) as Record<string, any>;
57
59
  return dictToState(data);
58
- } catch (e: any) {
59
- logWarn("state_io", `Failed to read state.json for '${contextId}': ${e}`);
60
+ } catch (error: any) {
61
+ logWarn("state_io", `Failed to read state.json for '${contextId}': ${error}`);
60
62
  return null;
61
63
  }
62
64
  }
@@ -69,7 +71,7 @@ export function writeStateJson(
69
71
  contextId: string,
70
72
  state: ContextState,
71
73
  projectRoot?: string,
72
- ): [boolean, string | null] {
74
+ ): [boolean, null | string] {
73
75
  const sp = statePath(contextId, projectRoot);
74
76
  const dir = path.dirname(sp);
75
77
  fs.mkdirSync(dir, { recursive: true });
@@ -14,161 +14,161 @@
14
14
 
15
15
  export const STOP_WORDS: ReadonlySet<string> = new Set([
16
16
  // ARTICLES
17
- "a", "an", "the",
17
+ "a", "about", "above",
18
18
 
19
- // PREPOSITIONS
20
- "to", "for", "in", "on", "at", "by", "with", "from", "of", "about",
21
- "into", "over", "under", "between", "through", "during", "before", "after",
22
- "above", "below", "against", "among", "around", "behind", "beside", "besides",
23
- "beyond", "down", "inside", "outside", "near", "off", "onto", "out",
24
- "since", "toward", "towards", "until", "upon", "within", "without",
25
- "across", "along", "via", "per",
26
-
27
- // PRONOUNS - Personal
28
- "i", "you", "he", "she", "it", "we", "they",
29
- "me", "him", "her", "us", "them",
30
- "myself", "yourself", "himself", "herself", "itself", "ourselves", "themselves",
19
+ "absolutely", "accordingly", "across", "active", "actually", "after", "afterwards", "against", "ago", "ah",
20
+ "aiw", "all", "allow", "almost", "along", "already", "alright", "also",
21
+ "alternatively", "although", "always", // AUXILIARY/MODAL VERBS
22
+ "am", "among", "an", // CONJUNCTIONS
23
+ "and", "another",
24
+ "any", "anybody", "anyone", "anything", "anyway", "anyways", "apparently", "appear",
25
+ "are", "aren", "arent", "args", "around", "as", "ask",
26
+ "assert", "async", "at", // SINGLE LETTERS
27
+ "b",
31
28
 
32
- // PRONOUNS - Possessive
33
- "my", "your", "his", "her", "its", "our", "their",
34
- "mine", "yours", "hers", "ours", "theirs",
29
+ "based", "basic", "basically", "bat", "be", "because", "become",
30
+ "been", "before", "begin", "behind", "being",
31
+ "below", "below", "beside", "besides", "between", "beyond", "block",
35
32
 
36
- // PRONOUNS - Demonstrative
37
- "this", "that", "these", "those",
33
+ "both", "but", "by", "c", "can", "cant", "case",
34
+ "cases", "cause", "cc", "certainly", "chunk",
38
35
 
39
- // PRONOUNS - Relative
40
- "who", "whom", "whose", "which",
41
-
42
- // PRONOUNS - Indefinite
43
- "someone", "somebody", "something", "anyone", "anybody", "anything",
44
- "everyone", "everybody", "everything", "no one", "nobody", "nothing",
45
- "one", "ones",
46
-
47
- // AUXILIARY/MODAL VERBS
48
- "am", "is", "are", "was", "were", "be", "been", "being",
49
- "have", "has", "had", "having",
50
- "do", "does", "did", "doing", "done",
51
- "can", "could", "will", "would", "shall", "should", "may", "might", "must",
52
-
53
- // CONJUNCTIONS
54
- "and", "or", "but", "nor", "so", "yet",
55
- "if", "then", "else", "whether", "unless", "although", "though",
56
- "because", "while", "whereas", "whenever", "wherever",
57
-
58
- // QUESTION WORDS
59
- "what", "when", "where", "why", "how",
60
-
61
- // ADVERBS OF PLACE/TIME
62
- "here", "there", "now", "always", "never", "often", "sometimes",
63
- "already", "still", "soon", "later", "ago", "today", "tomorrow",
64
- "yesterday", "currently", "previously", "recently", "immediately",
65
- "finally", "eventually", "meanwhile", "afterwards",
66
-
67
- // NEGATION
68
- "no", "not", "none", "neither",
69
- "don", "doesn", "didn", "won", "wouldn", "couldn", "shouldn",
70
- "isn", "aren", "wasn", "weren", "hasn", "haven", "hadn",
71
-
72
- // QUANTIFIERS
73
- "some", "any", "all", "each", "every", "both", "few", "more", "most",
74
- "many", "much", "several", "other", "another", "enough", "less", "least",
75
- "either", "such",
36
+ "clarification", "class", "clearly", "come",
76
37
 
77
- // FILLER/HEDGE WORDS
78
- "just", "also", "only", "really", "actually", "basically", "simply",
79
- "very", "quite", "rather", "pretty", "somewhat", "almost", "nearly",
80
- "exactly", "completely", "entirely", "totally", "absolutely",
81
- "probably", "possibly", "maybe", "perhaps", "definitely", "certainly",
82
- "apparently", "obviously", "clearly", "literally", "essentially",
83
-
84
- // SPEECH-TO-TEXT FILLERS (STT artifacts from voice input)
85
- "um", "uh", "ah", "oh", "hmm", "hm", "er", "eh", "huh",
86
- "hey", "hi", "hello", "yeah", "yep", "yup", "nah", "nope",
87
- "gonna", "gotta", "wanna", "kinda", "sorta",
88
- "stuff", "anyway", "anyways", "alright", "right", "well",
89
-
90
- // COMMON REQUEST PHRASES
91
- "want", "need", "help", "please", "like", "let", "get",
92
- "think", "know", "see", "try", "make", "give", "take",
93
- "look", "looking", "trying", "going", "getting", "making",
38
+ "complete", "completely", "consequently", "consider",
94
39
 
95
- // COMMON NON-ACTION VERBS
96
- "go", "come", "put", "say", "tell", "ask", "find", "keep",
97
- "seem", "appear", "become", "remain", "stay", "feel", "show", "mean",
98
- "include", "provide", "require", "allow", "expect", "cause",
99
- "follow", "consider", "continue", "start", "begin", "end",
100
- "contain", "contains",
40
+ "const", "contain", "contains", "continue", "conversely", "correct",
41
+ "correctly", "could", "couldn", "couldnt", "critical", "current",
42
+ "currently", "d",
101
43
 
102
- // LINKING/TRANSITION WORDS
103
- "however", "therefore", "thus", "hence", "otherwise", "instead",
104
- "moreover", "furthermore", "nevertheless", "nonetheless", "accordingly",
105
- "consequently", "similarly", "likewise", "conversely", "alternatively",
44
+ "def", "definitely", "dict", "did", "didn", "didnt", "different", "directory",
45
+ "do", "does", "doesn", "doesn",
46
+ "doesnt", "doing", "don", "done", "dont",
47
+ "down", "during", "e", "each", "eh", "eight", "either", "elif", "else",
106
48
 
107
- // FILE EXTENSIONS
108
- "py", "md", "ts", "json", "js", "yaml", "toml", "exe", "bat",
49
+ "empty", "end", "enough", "entirely", "eprint", "er",
50
+ "essentially", // SHORT NOISE
51
+ "etc", "eventually", "every", "everybody", "everyone", "everything",
52
+ "exactly", "example", "examples", "except", "exe",
109
53
 
110
- // COMMON CODING TERMS
111
- "using", "used", "uses",
112
- "based", "following",
113
- "same", "different", "specific", "existing", "new", "current", "first",
114
- "full", "complete", "single", "multiple", "simple",
115
- "needed", "required", "provided", "expected", "correctly",
116
- "works", "working", "work",
54
+ "existing", "expect", "expected", "f", "false",
117
55
 
118
- // STRUCTURAL WORDS
119
- "step", "steps", "phase", "below",
56
+ "feature", "features", "feel", "few", "finally", "find", "first",
57
+ "five", "folder", "follow", "following", "footer", "for", "format",
58
+ "four", "from", "full", "furthermore", "g",
59
+ "general", "get", "getting", "give",
120
60
 
121
- // QUERY LANGUAGE
122
- "questions", "question", "clarification",
61
+ // COMMON NON-ACTION VERBS
62
+ "go", "going", "gonna", "gotta",
63
+ "group", "h", "had", "hadn", "hadnt", "has", "hasn",
64
+ "hasnt", "have", "haven", "havent", "having", "he", "header",
123
65
 
124
- // OVERLY GENERIC TERMS
125
- "thing", "things", "way", "ways", "kind", "type", "types",
126
- "example", "examples", "case", "cases",
127
- "part", "parts", "point", "points",
128
- "time", "times", "next", "last",
129
- "set", "list", "group", "item", "items",
66
+ "hello", "help", "hence", "her", "her", // ADVERBS OF PLACE/TIME
67
+ "here", "heres", "hers", "herself",
68
+ "hes", "hey", "hi", // GENERIC ADJECTIVES
69
+ "high", "him", "himself", "his", "hm",
70
+ "hmm", "how",
130
71
 
131
- // PROGRAMMING KEYWORDS
132
- "self", "def", "return", "import", "true", "false", "none", "str",
133
- "const", "async", "class", "assert", "except", "dict", "len", "args",
134
- "sys", "eprint", "elif", "lambda", "yield", "pass",
72
+ // LINKING/TRANSITION WORDS
73
+ "however", "huh", // PRONOUNS - Personal
74
+ "i", "id", "if", "ill", // CONTRACTED FORMS
75
+ "im",
76
+ "immediately", "import", "important", "in", "include", "index", // GENERIC TECHNICAL NOUNS
77
+ "information",
78
+ "inside", "instead", "into", "is", "isn",
79
+ "isnt", "issue", "it", "item", "items", "its",
80
+ "itself", "ive", "j", "js", "json",
135
81
 
136
- // GENERIC ADJECTIVES
137
- "high", "low", "important", "critical", "optional", "manual",
138
- "real", "empty", "stable", "active", "proper", "correct",
139
- "basic", "main", "primary", "secondary", "general", "overall",
82
+ // FILLER/HEDGE WORDS
83
+ "just", "k", "keep", "kind", "kinda", "know", "l", "lambda", "last",
84
+ "later", "least", "len", "less", "let", "lets", "level", "like",
85
+ "likewise", "line", "lines", "list", "literally",
86
+ "ll", "look", "looking", "low", "m", "main",
140
87
 
141
- // GENERIC TECHNICAL NOUNS
142
- "information", "format", "status", "method", "purpose", "result",
143
- "source", "value", "option", "options", "feature", "features", "issue",
144
- "process", "version", "mode", "state",
88
+ "make", "making", "manual", "many", "may", "maybe", "md",
89
+ "me", "mean", "meanwhile", "method", "might", "mine", "mode",
90
+ "more", "moreover", "most", "much", "multiple", "must",
145
91
 
146
- // DOCUMENT/CODE STRUCTURE
147
- "section", "lines", "line", "folder", "directory", "index",
148
- "level", "block", "chunk", "region", "header", "footer",
92
+ // PRONOUNS - Possessive
93
+ "my", "myself", "n", "nah", "near", "nearly", "need", "needed",
94
+ "neither", "never", "nevertheless", "new", "next", "nine", // NEGATION
95
+ "no", "nobody",
96
+ "none", "none", "nonetheless", "no one", "nope", "nor",
97
+ "not", "nothing", "now", "o", "obviously", "of",
98
+ "off", "often",
99
+
100
+ "oh", "ok", "okay", "on", "one", "ones",
101
+ "only", "onto", "option", "optional", "options",
102
+ "or", "other", "otherwise", "our", "ours",
103
+
104
+ "ourselves", "out", "outside", "over", "overall", "p", "part", "parts", "pass",
105
+
106
+ "per", "perhaps", "phase",
107
+ "pl", "please",
108
+ "point", "points", "possibly", "pretty", "previously", "primary", "probably",
109
+ "process", "proper", "provide", "provided", "purpose",
110
+ "put", // FILE EXTENSIONS
111
+ "py", "q", "question", // QUERY LANGUAGE
112
+ "questions",
113
+ "quite", "r", "rather",
149
114
 
150
115
  // FRAGMENT WORDS
151
- "re", "pl", "aiw", "ve", "ll", "doesn", "t", "s",
116
+ "re", "real", "really", "recently",
117
+
118
+ "region", "remain", "require",
152
119
 
153
- // CONTRACTED FORMS
154
- "im", "ive", "id", "ill", "youre", "youve", "youll",
155
- "hes", "shes", "weve", "theyre", "theyve", "dont", "doesnt",
156
- "didnt", "wont", "wouldnt", "cant", "couldnt", "shouldnt", "isnt",
157
- "arent", "wasnt", "werent", "hasnt", "havent", "hadnt", "lets",
158
- "thats", "whats", "heres", "theres", "whos",
120
+ "required", "result", "return", "right", "s", "same", "say",
121
+ "secondary", // DOCUMENT/CODE STRUCTURE
122
+ "section", "see", "seem",
123
+ // PROGRAMMING KEYWORDS
124
+ "self", "set", "seven", "several",
125
+ "shall", "she", "shes", "should",
126
+ "shouldn", "shouldnt", "show", "similarly", "simple",
127
+
128
+ "simply", "since", "single", "six", "so", // QUANTIFIERS
129
+ "some", "somebody", // PRONOUNS - Indefinite
130
+ "someone",
131
+ "something", "sometimes", "somewhat", "soon", "sorta", "source", "specific", "stable",
132
+ "start", "state", "status", "stay", // STRUCTURAL WORDS
133
+ "step", "steps",
134
+
135
+ "still", "str", "stuff", "such", "sys", "t",
136
+ "take", "tell", "ten", "that", "thats", "the",
137
+ "their", "theirs", "them", "themselves", "then", "there",
138
+
139
+ "therefore", "theres", "these", "they", "theyre", "theyve",
140
+ // OVERLY GENERIC TERMS
141
+ "thing", "things", "think", // PRONOUNS - Demonstrative
142
+ "this", "those", "though", "three",
143
+ "through", "thus", "time", "times",
159
144
 
160
- // SHORT NOISE
161
- "etc", "up", "as", "cc",
145
+ // PREPOSITIONS
146
+ "to", "today", "toml", "tomorrow", // SHORT FILLER
147
+ "too", "totally",
148
+ "toward", "towards", "true", "try", "trying", "ts",
162
149
 
163
150
  // NUMBER WORDS
164
- "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
151
+ "two", "type", "types", "u", "uh", // SPEECH-TO-TEXT FILLERS (STT artifacts from voice input)
152
+ "um", "under", "unless",
153
+
154
+ "until", "up", "upon", "us", "used", "uses", // COMMON CODING TERMS
155
+ "using",
156
+ "v", "value", "ve", "version", "very", "via", "w",
157
+ "wanna", // COMMON REQUEST PHRASES
158
+ "want", "was", "wasn", "wasnt", "way", "ways",
159
+ "we", "well", "were", "weren", "werent", "weve", // QUESTION WORDS
160
+ "what",
161
+ "whats", "when", "whenever", "where", "whereas",
162
+
163
+ "wherever", "whether", "which", "while",
164
+
165
+ // PRONOUNS - Relative
166
+ "who", "whom", "whos", "whose", "why", "will", "with", "within", "without",
165
167
 
166
- // SINGLE LETTERS
167
- "b", "c", "d", "e", "f", "g", "h", "j", "k", "l", "m", "n", "o", "p",
168
- "q", "r", "u", "v", "w", "x", "y", "z",
168
+ "won", "wont", "work", "working", "works", "would", "wouldn", "wouldnt", "x", "y", "yaml", "yeah", "yep", "yes",
169
+ "yesterday", "yet", "yield", "you", "youll", "your", "youre", "yours",
169
170
 
170
- // SHORT FILLER
171
- "too", "yes", "ok", "okay",
171
+ "yourself", "youve", "yup", "z",
172
172
  ]);
173
173
 
174
174
  /**