wogiflow 2.20.1 → 2.22.0

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 (255) hide show
  1. package/.claude/commands/wogi-finalize.md +83 -0
  2. package/.claude/rules/_internal/self-maintenance.md +1 -1
  3. package/.claude/settings.json +1 -1
  4. package/lib/commands/login.js +1 -1
  5. package/lib/installer.js +5 -5
  6. package/lib/release-channel.js +1 -1
  7. package/lib/skill-registry.js +3 -3
  8. package/lib/workspace-events.js +1 -1
  9. package/lib/workspace-gates.js +2 -2
  10. package/lib/workspace-intelligence.js +1 -1
  11. package/lib/workspace-routing.js +1 -1
  12. package/lib/workspace.js +16 -17
  13. package/package.json +2 -2
  14. package/scripts/base-workflow-step.js +2 -2
  15. package/scripts/flow-adaptive-learning.js +6 -6
  16. package/scripts/flow-api-index.js +2 -2
  17. package/scripts/flow-architect-pass.js +1 -1
  18. package/scripts/flow-ask.js +1 -1
  19. package/scripts/flow-assumption-detector.js +1 -1
  20. package/scripts/flow-audit-gates.js +38 -12
  21. package/scripts/flow-audit.js +4 -4
  22. package/scripts/flow-auto-context.js +3 -3
  23. package/scripts/flow-background.js +1 -1
  24. package/scripts/flow-best-of-n.js +7 -7
  25. package/scripts/flow-bridge.js +3 -3
  26. package/scripts/flow-bug.js +2 -2
  27. package/scripts/flow-bulk-loop.js +7 -7
  28. package/scripts/flow-cascade-completion.js +2 -2
  29. package/scripts/flow-cascade.js +1 -1
  30. package/scripts/flow-checkpoint.js +2 -2
  31. package/scripts/flow-clarifying-questions.js +2 -2
  32. package/scripts/flow-cli.js +2 -2
  33. package/scripts/flow-code-intelligence.js +4 -4
  34. package/scripts/flow-community-sync.js +6 -6
  35. package/scripts/flow-community.js +1 -1
  36. package/scripts/flow-completion-truth-gate.js +161 -5
  37. package/scripts/flow-complexity.js +1 -1
  38. package/scripts/flow-config-defaults.js +16 -4
  39. package/scripts/flow-config-interactive.js +2 -2
  40. package/scripts/flow-config-loader.js +1 -1
  41. package/scripts/flow-config-migrate.js +5 -6
  42. package/scripts/flow-consistency-check.js +5 -5
  43. package/scripts/flow-context-compact/expander.js +1 -1
  44. package/scripts/flow-context-compact/index.js +2 -2
  45. package/scripts/flow-context-compact/section-extractor.js +3 -3
  46. package/scripts/flow-context-compact/summary-tree.js +1 -1
  47. package/scripts/flow-context-estimator.js +1 -1
  48. package/scripts/flow-context-gatherer.js +6 -6
  49. package/scripts/flow-context-generator.js +6 -6
  50. package/scripts/flow-context-init.js +2 -2
  51. package/scripts/flow-context-manager.js +1 -1
  52. package/scripts/flow-context-manifest.js +1 -1
  53. package/scripts/flow-context-monitor.js +5 -5
  54. package/scripts/flow-context-orchestrator.js +2 -2
  55. package/scripts/flow-context-scoring.js +4 -4
  56. package/scripts/flow-contract-scan.js +1 -1
  57. package/scripts/flow-correct.js +3 -3
  58. package/scripts/flow-damage-control.js +2 -2
  59. package/scripts/flow-deploy-gate.js +2 -2
  60. package/scripts/flow-deploy-history.js +1 -1
  61. package/scripts/flow-diff.js +3 -3
  62. package/scripts/flow-done-gates.js +1 -1
  63. package/scripts/flow-done.js +7 -7
  64. package/scripts/flow-durable-session.js +1 -1
  65. package/scripts/flow-entropy-monitor.js +3 -3
  66. package/scripts/flow-epics.js +5 -5
  67. package/scripts/flow-error-recovery.js +4 -4
  68. package/scripts/flow-eval-judge.js +5 -5
  69. package/scripts/flow-eval.js +7 -7
  70. package/scripts/flow-export-scanner.js +5 -5
  71. package/scripts/flow-extraction-review.js +1 -1
  72. package/scripts/flow-failure-learning.js +9 -9
  73. package/scripts/flow-feature.js +5 -5
  74. package/scripts/flow-figma-confirm.js +1 -1
  75. package/scripts/flow-figma-extract.js +2 -2
  76. package/scripts/flow-figma-index.js +2 -2
  77. package/scripts/flow-figma-match.js +1 -1
  78. package/scripts/flow-figma-mcp-server.js +3 -3
  79. package/scripts/flow-figma-orchestrator.js +1 -1
  80. package/scripts/flow-figma-registry.js +2 -2
  81. package/scripts/flow-function-index.js +2 -2
  82. package/scripts/flow-gate-confidence.js +2 -2
  83. package/scripts/flow-gate-telemetry.js +1 -1
  84. package/scripts/flow-gitignore.js +1 -1
  85. package/scripts/flow-guided-edit.js +3 -3
  86. package/scripts/flow-health.js +95 -8
  87. package/scripts/flow-hooks.js +3 -3
  88. package/scripts/flow-hybrid-detect.js +2 -2
  89. package/scripts/flow-hybrid-interactive.js +1 -1
  90. package/scripts/flow-hybrid-test.js +1 -1
  91. package/scripts/flow-hypothesis-generator.js +4 -4
  92. package/scripts/flow-instruction-richness.js +11 -11
  93. package/scripts/flow-intent-bootstrap.js +1 -1
  94. package/scripts/flow-intent-framing.js +1 -1
  95. package/scripts/flow-item-link.js +2 -2
  96. package/scripts/flow-knowledge-router.js +7 -7
  97. package/scripts/flow-knowledge-sync.js +3 -3
  98. package/scripts/flow-learning-orchestrator.js +1 -1
  99. package/scripts/flow-links.js +2 -2
  100. package/scripts/flow-log-manager.js +2 -2
  101. package/scripts/flow-logic-adversary.js +5 -4
  102. package/scripts/flow-long-input-chunking.js +1 -1
  103. package/scripts/flow-long-input-cli.js +3 -3
  104. package/scripts/flow-long-input.js +18 -18
  105. package/scripts/flow-loop-retry-learning.js +2 -2
  106. package/scripts/flow-lsp.js +4 -4
  107. package/scripts/flow-mcp-docs.js +1 -1
  108. package/scripts/flow-memory-blocks.js +5 -5
  109. package/scripts/flow-memory-compactor.js +3 -3
  110. package/scripts/flow-memory-db.js +4 -4
  111. package/scripts/flow-memory-sync.js +3 -3
  112. package/scripts/flow-metrics.js +2 -2
  113. package/scripts/flow-migrate-igr.js +2 -2
  114. package/scripts/flow-migrate.js +2 -2
  115. package/scripts/flow-model-adapter.js +4 -4
  116. package/scripts/flow-model-caller.js +8 -8
  117. package/scripts/flow-model-config.js +5 -5
  118. package/scripts/flow-model-profile.js +7 -7
  119. package/scripts/flow-model-router.js +5 -5
  120. package/scripts/flow-model-types.js +3 -3
  121. package/scripts/flow-models.js +8 -8
  122. package/scripts/flow-morning.js +1 -1
  123. package/scripts/flow-multi-approach.js +1 -1
  124. package/scripts/flow-orchestrate-context.js +2 -2
  125. package/scripts/flow-orchestrate-llm.js +4 -4
  126. package/scripts/flow-orchestrate-rollback.js +1 -1
  127. package/scripts/flow-orchestrate-state.js +6 -6
  128. package/scripts/flow-orchestrate-templates.js +1 -1
  129. package/scripts/flow-orchestrate-validation.js +2 -2
  130. package/scripts/flow-orchestrate-validator.js +1 -1
  131. package/scripts/flow-orchestrate.js +25 -25
  132. package/scripts/flow-parallel.js +1 -1
  133. package/scripts/flow-pattern-enforcer.js +7 -7
  134. package/scripts/flow-pattern-extractor.js +3 -3
  135. package/scripts/flow-peer-review.js +8 -8
  136. package/scripts/flow-pending.js +1 -1
  137. package/scripts/flow-permissions.js +1 -1
  138. package/scripts/flow-phased-task.js +1 -1
  139. package/scripts/flow-plan.js +1 -1
  140. package/scripts/flow-prd-manager.js +2 -2
  141. package/scripts/flow-product-scanner.js +2 -2
  142. package/scripts/flow-progress-tracker.js +2 -2
  143. package/scripts/flow-progress.js +1 -1
  144. package/scripts/flow-project-analyzer.js +3 -3
  145. package/scripts/flow-prompt-capture.js +2 -2
  146. package/scripts/flow-prompt-composer.js +3 -3
  147. package/scripts/flow-prompt-template.js +4 -4
  148. package/scripts/flow-providers.js +31 -23
  149. package/scripts/flow-queue.js +1 -1
  150. package/scripts/flow-registry-manager.js +4 -4
  151. package/scripts/flow-regression.js +1 -1
  152. package/scripts/flow-response-parser.js +1 -1
  153. package/scripts/flow-resume.js +1 -1
  154. package/scripts/flow-review-passes/index.js +2 -2
  155. package/scripts/flow-review-passes/integration.js +3 -3
  156. package/scripts/flow-review-passes/logic.js +3 -3
  157. package/scripts/flow-review-passes/security.js +2 -2
  158. package/scripts/flow-review-passes/structure.js +1 -1
  159. package/scripts/flow-review.js +11 -11
  160. package/scripts/flow-revision-tracker.js +2 -2
  161. package/scripts/flow-roadmap.js +2 -2
  162. package/scripts/flow-run-trace.js +1 -1
  163. package/scripts/flow-safety.js +3 -3
  164. package/scripts/flow-scanner-base.js +1 -1
  165. package/scripts/flow-scenario-engine.js +7 -7
  166. package/scripts/flow-schema-drift.js +4 -3
  167. package/scripts/flow-section-index.js +2 -2
  168. package/scripts/flow-section-resolver.js +4 -4
  169. package/scripts/flow-semantic-match.js +3 -3
  170. package/scripts/flow-session-end.js +56 -0
  171. package/scripts/flow-session-learning.js +2 -2
  172. package/scripts/flow-setup-hooks.js +1 -1
  173. package/scripts/flow-skill-create.js +3 -3
  174. package/scripts/flow-skill-freshness.js +2 -2
  175. package/scripts/flow-skill-generator.js +6 -6
  176. package/scripts/flow-skill-learn.js +7 -7
  177. package/scripts/flow-skill-matcher.js +2 -2
  178. package/scripts/flow-solution-optimizer.js +1 -1
  179. package/scripts/flow-spec-generator.js +5 -5
  180. package/scripts/flow-spec-verifier.js +2 -2
  181. package/scripts/flow-stack-wizard.js +6 -6
  182. package/scripts/flow-standards-checker.js +8 -8
  183. package/scripts/flow-standards-gate.js +4 -4
  184. package/scripts/flow-standards-learner.js +2 -2
  185. package/scripts/flow-start.js +9 -9
  186. package/scripts/flow-stats-collector.js +2 -2
  187. package/scripts/flow-status.js +1 -1
  188. package/scripts/flow-step-changelog.js +3 -3
  189. package/scripts/flow-step-complexity.js +1 -1
  190. package/scripts/flow-step-coverage.js +3 -3
  191. package/scripts/flow-step-knowledge.js +2 -2
  192. package/scripts/flow-step-pr-tests.js +2 -2
  193. package/scripts/flow-step-regression.js +3 -3
  194. package/scripts/flow-step-review.js +5 -5
  195. package/scripts/flow-story.js +2 -2
  196. package/scripts/flow-strict-adherence.js +2 -2
  197. package/scripts/flow-structure-sensor.js +283 -0
  198. package/scripts/flow-sync-anonymizer.js +3 -3
  199. package/scripts/flow-task-checkpoint.js +2 -2
  200. package/scripts/flow-task-classifier.js +2 -2
  201. package/scripts/flow-task-completion-summary.js +1 -1
  202. package/scripts/flow-task-enforcer.js +5 -5
  203. package/scripts/flow-tech-debt.js +3 -3
  204. package/scripts/flow-template-extractor.js +3 -3
  205. package/scripts/flow-templates.js +1 -1
  206. package/scripts/flow-test-api.js +12 -12
  207. package/scripts/flow-test-discovery.js +9 -9
  208. package/scripts/flow-test-generate.js +5 -5
  209. package/scripts/flow-test-integrity.js +3 -3
  210. package/scripts/flow-test-ui.js +8 -8
  211. package/scripts/flow-testing-deps.js +4 -4
  212. package/scripts/flow-tiered-learning.js +3 -3
  213. package/scripts/flow-todowrite-sync.js +1 -1
  214. package/scripts/flow-trap-zone.js +1 -1
  215. package/scripts/flow-verification-profile.js +9 -9
  216. package/scripts/flow-verify.js +2 -2
  217. package/scripts/flow-version-check.js +2 -2
  218. package/scripts/flow-webmcp-generator.js +3 -3
  219. package/scripts/flow-wiring-verifier.js +13 -13
  220. package/scripts/flow-worker-question-classifier.js +256 -0
  221. package/scripts/flow-workflow-steps.js +3 -3
  222. package/scripts/flow-workflow.js +1 -1
  223. package/scripts/flow-worktree.js +1 -1
  224. package/scripts/hooks/adapters/base-adapter.js +2 -2
  225. package/scripts/hooks/core/commit-log-gate.js +2 -2
  226. package/scripts/hooks/core/component-check.js +3 -3
  227. package/scripts/hooks/core/config-change.js +1 -1
  228. package/scripts/hooks/core/deploy-gate.js +2 -1
  229. package/scripts/hooks/core/git-safety-gate.js +1 -1
  230. package/scripts/hooks/core/instructions-loaded.js +1 -1
  231. package/scripts/hooks/core/loop-check.js +1 -1
  232. package/scripts/hooks/core/manager-boundary-gate.js +3 -2
  233. package/scripts/hooks/core/observation-capture.js +6 -6
  234. package/scripts/hooks/core/phase-gate.js +4 -4
  235. package/scripts/hooks/core/pre-compact.js +1 -1
  236. package/scripts/hooks/core/pre-tool-orchestrator.js +1 -1
  237. package/scripts/hooks/core/routing-gate.js +2 -84
  238. package/scripts/hooks/core/session-context.js +1 -1
  239. package/scripts/hooks/core/session-end.js +3 -3
  240. package/scripts/hooks/core/session-history.js +1 -1
  241. package/scripts/hooks/core/setup-handler.js +1 -1
  242. package/scripts/hooks/core/task-boundary-reset.js +2 -4
  243. package/scripts/hooks/core/task-completed.js +13 -7
  244. package/scripts/hooks/core/task-created.js +1 -1
  245. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  246. package/scripts/hooks/entry/claude-code/permission-denied.js +4 -2
  247. package/scripts/hooks/entry/claude-code/stop.js +60 -0
  248. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +1 -1
  249. package/scripts/hooks/git/post-commit.js +1 -1
  250. package/scripts/postinstall.js +7 -7
  251. package/scripts/preuninstall.js +5 -5
  252. package/scripts/registries/component-registry.js +2 -2
  253. package/scripts/registries/contract-scanner.js +11 -11
  254. package/scripts/registries/schema-registry.js +5 -5
  255. package/scripts/registries/service-registry.js +9 -9
@@ -38,7 +38,7 @@ function getUncommittedCount() {
38
38
  stdio: ['pipe', 'pipe', 'pipe']
39
39
  });
40
40
  return output.trim().split('\n').filter(line => line.trim()).length;
41
- } catch (err) {
41
+ } catch (_err) {
42
42
  return 0;
43
43
  }
44
44
  }
@@ -131,7 +131,7 @@ function handleSessionEnd(input) {
131
131
  console.error(`[Session End] Community sync-up failed: ${err.message}`);
132
132
  }
133
133
  });
134
- } catch (err) {
134
+ } catch (_err) {
135
135
  // Non-critical — community sync module may not be available
136
136
  }
137
137
 
@@ -144,7 +144,7 @@ function handleSessionEnd(input) {
144
144
  // Non-critical - memory pipeline may not be available
145
145
  });
146
146
  }
147
- } catch (err) {
147
+ } catch (_err) {
148
148
  // Non-critical — memory DB may not be available
149
149
  }
150
150
 
@@ -31,7 +31,7 @@
31
31
  * `.workflow/archive/session-history-<date>.json`.
32
32
  */
33
33
 
34
- const fs = require('node:fs');
34
+ const _fs = require('node:fs');
35
35
  const path = require('node:path');
36
36
 
37
37
  const { PATHS, safeJsonParse, writeJson } = require('../../flow-utils');
@@ -93,7 +93,7 @@ function handleSetup(options = {}) {
93
93
  * @param {Object} options
94
94
  * @returns {Object} Result: { tasks, message, results }
95
95
  */
96
- function handleMaintenance(options = {}) {
96
+ function handleMaintenance(_options = {}) {
97
97
  if (!isSetupEnabled()) {
98
98
  return {
99
99
  tasks: [],
@@ -47,6 +47,7 @@ const fs = require('node:fs');
47
47
  const path = require('node:path');
48
48
 
49
49
  const { getConfig, PATHS } = require('../../flow-utils');
50
+ const { safeJsonParse } = require('../../flow-io');
50
51
 
51
52
  const PENDING_MARKER_FILE = 'task-just-completed';
52
53
 
@@ -157,10 +158,7 @@ function consumeAndTriggerRestart() {
157
158
  // Consume the marker. Do this BEFORE signaling so we never double-fire if
158
159
  // for some reason the signal delivery is delayed and a second Stop-hook
159
160
  // invocation races through.
160
- let markerPayload = null;
161
- try {
162
- markerPayload = JSON.parse(fs.readFileSync(markerPath, 'utf-8'));
163
- } catch (_err) { /* payload read is optional */ }
161
+ const markerPayload = safeJsonParse(markerPath, null);
164
162
  try {
165
163
  fs.unlinkSync(markerPath);
166
164
  } catch (err) {
@@ -168,7 +168,7 @@ async function handleTaskCompleted(input) {
168
168
  writeJson(readyPath, ready);
169
169
  result.completed = true;
170
170
  result.message = `Task ${completedTask.id} (${completedTask.title}) moved to completed`;
171
- } catch (err) {
171
+ } catch (_err) {
172
172
  result.message = 'Failed to update ready.json';
173
173
  }
174
174
  });
@@ -401,12 +401,18 @@ async function handleTaskCompleted(input) {
401
401
  { mode: 0o644 }
402
402
  );
403
403
  }
404
- } catch (_err) {
405
- // Non-critical — workspace message is defense-in-depth.
406
- // The Stop hook curl remains as fallback.
407
- if (process.env.DEBUG) {
408
- console.error(`[Task Completed] Workspace message write failed: ${_err.message}`);
409
- }
404
+ } catch (err) {
405
+ // Workspace message is the VERIFIED completion signal. A silent failure
406
+ // produces "workers stopped writing since <date>" incidents that are
407
+ // indistinguishable from "no tasks completed recently" — surface on
408
+ // stderr unconditionally so regressions are visible in worker logs.
409
+ // (diagnostic D1, 2026-04-16 honesty-infrastructure review.)
410
+ const reason = err && err.message ? err.message : 'unknown';
411
+ const root = process.env.WOGI_WORKSPACE_ROOT || '(unset)';
412
+ const repo = process.env.WOGI_REPO_NAME || '(unset)';
413
+ console.error(
414
+ `[Task Completed] workspace message write FAILED for ${completedTask.id}: ${reason} (root=${root}, repo=${repo})`
415
+ );
410
416
  }
411
417
  }
412
418
 
@@ -31,7 +31,7 @@ function isTaskCreatedEnabled() {
31
31
  * @param {Object} input - Parsed hook input from Claude Code
32
32
  * @returns {Object} Core result
33
33
  */
34
- async function handleTaskCreated(input) {
34
+ async function handleTaskCreated(_input) {
35
35
  if (!isTaskCreatedEnabled()) {
36
36
  return { enabled: false, message: null };
37
37
  }
@@ -25,7 +25,7 @@ const CORE_STATE_FILES = ['ready.json', 'decisions.md'];
25
25
 
26
26
  function getEssentialStateFiles() {
27
27
  try {
28
- const { getRegistryMapFiles, PATHS } = require('../../flow-utils');
28
+ const { getRegistryMapFiles } = require('../../flow-utils');
29
29
  return [...CORE_STATE_FILES, ...getRegistryMapFiles()];
30
30
  } catch (_err) {
31
31
  return [...CORE_STATE_FILES, 'app-map.md', 'function-map.md', 'api-map.md'];
@@ -31,13 +31,15 @@ runHook('PermissionDenied', async ({ input, parsedInput }) => {
31
31
  try {
32
32
  const fs = require('node:fs');
33
33
  const { PATHS } = require('../../../flow-utils');
34
+ const { safeJsonParseString } = require('../../../flow-io');
34
35
  const logPath = path.join(PATHS.state, 'permission-denials.json');
35
36
 
36
37
  let denials = [];
37
38
  try {
38
39
  if (fs.existsSync(logPath)) {
39
- denials = JSON.parse(fs.readFileSync(logPath, 'utf-8'));
40
- if (!Array.isArray(denials)) denials = [];
40
+ const raw = fs.readFileSync(logPath, 'utf-8');
41
+ const parsed = safeJsonParseString(raw, []);
42
+ denials = Array.isArray(parsed) ? parsed : [];
41
43
  }
42
44
  } catch (_err) {
43
45
  denials = [];
@@ -242,6 +242,66 @@ runHook('Stop', async ({ parsedInput }) => {
242
242
  }
243
243
  }
244
244
 
245
+ // G3 (v2.21.0) — AI-based worker-question classifier.
246
+ //
247
+ // If the worker ends a turn with a question to the user in free text (no tool
248
+ // call, just hedging), Gap B above won't fire when the queue is empty.
249
+ // Regex-based detection was rejected as brittle. Instead: a single Haiku call
250
+ // classifies the final assistant message. If it IS an open question to the
251
+ // user → block with escalation instructions.
252
+ //
253
+ // Fail-open throughout: missing API key, missing transcript, model errors,
254
+ // malformed responses all skip cleanly. Silent-stall false-negatives recover;
255
+ // blocking legitimate stops on classifier bugs does not.
256
+ try {
257
+ const isWorker = process.env.WOGI_WORKSPACE_ROOT &&
258
+ process.env.WOGI_REPO_NAME &&
259
+ process.env.WOGI_REPO_NAME !== 'manager';
260
+ if (isWorker) {
261
+ const { getConfig } = require('../../../flow-utils');
262
+ const config = getConfig();
263
+ const clf = config.workspace?.aiWorkerQuestionClassifier;
264
+ const enabled = clf?.enabled !== false; // default true
265
+ if (enabled && parsedInput?.transcriptPath) {
266
+ const { classifyWorkerQuestion } = require('../../../flow-worker-question-classifier');
267
+ const result = await classifyWorkerQuestion({
268
+ transcriptPath: parsedInput.transcriptPath,
269
+ minConfidence: Number.isFinite(clf?.minConfidence) ? clf.minConfidence : 70,
270
+ model: typeof clf?.model === 'string' ? clf.model : undefined
271
+ });
272
+ if (result?.blocked) {
273
+ const port = process.env.WOGI_MANAGER_PORT || '8800';
274
+ const repo = process.env.WOGI_REPO_NAME;
275
+ const msg = [
276
+ `WORKER→USER QUESTION DETECTED (confidence ${result.confidence}%, threshold ${result.minConfidence}%):`,
277
+ ` "${String(result.reason || '').slice(0, 200)}"`,
278
+ '',
279
+ 'In workspace mode, workers CANNOT ask the user directly — the user only sees',
280
+ 'the manager terminal. Your question will stall silently.',
281
+ '',
282
+ 'Channel-dispatch to the manager instead, THEN end the turn:',
283
+ '',
284
+ ` curl -s -X POST http://127.0.0.1:${port} \\`,
285
+ ` -H "X-Wogi-From: ${repo}" \\`,
286
+ ` --data-binary "## QUESTION: <your question>"`,
287
+ '',
288
+ 'The manager will relay to the user, capture the answer, and dispatch a',
289
+ 'follow-up task to you with the resolved context.',
290
+ '',
291
+ 'If you don\'t actually need the user — make a reasonable autonomous decision',
292
+ 'and note it in your ## Results reply to the manager. Then end the turn.'
293
+ ].join('\n');
294
+ return { __raw: true, continue: true, stopReason: msg };
295
+ }
296
+ }
297
+ }
298
+ } catch (err) {
299
+ // Fail-OPEN — classifier errors must not block legitimate stops.
300
+ if (process.env.DEBUG) {
301
+ console.error(`[Stop] Worker question classifier error (fail-open): ${err.message}`);
302
+ }
303
+ }
304
+
245
305
  // Check if loop can exit
246
306
  return await checkLoopExit();
247
307
  }, { failMode: 'warn', failOutput: { continue: false } });
@@ -55,7 +55,7 @@ runHook('UserPromptSubmit', async ({ input, parsedInput }) => {
55
55
  let hookConfig;
56
56
  try {
57
57
  hookConfig = getConfig();
58
- } catch (err) {
58
+ } catch (_err) {
59
59
  hookConfig = {};
60
60
  }
61
61
 
@@ -15,7 +15,7 @@ const path = require('node:path');
15
15
 
16
16
  // Resolve paths relative to this script's location
17
17
  const scriptsDir = path.resolve(__dirname, '../..');
18
- const { getReadyData, saveReadyData, info, color } = require(path.join(scriptsDir, 'flow-utils'));
18
+ const { getReadyData, saveReadyData, info } = require(path.join(scriptsDir, 'flow-utils'));
19
19
  const { success } = require('../../flow-output');
20
20
 
21
21
  /**
@@ -62,7 +62,7 @@ function safeJsonParseString(jsonString, defaultValue = null) {
62
62
  return defaultValue;
63
63
  }
64
64
  return parsed;
65
- } catch (err) {
65
+ } catch (_err) {
66
66
  return defaultValue;
67
67
  }
68
68
  }
@@ -93,7 +93,7 @@ function hasDangerousKeys(obj) {
93
93
  */
94
94
  function safeClose(fd) {
95
95
  if (fd !== null) {
96
- try { fs.closeSync(fd); } catch (err) { /* intentionally ignored */ }
96
+ try { fs.closeSync(fd); } catch (_err) { /* intentionally ignored */ }
97
97
  }
98
98
  }
99
99
 
@@ -520,7 +520,7 @@ function copyClaudeResources() {
520
520
  }
521
521
  try {
522
522
  fs.chmodSync(projectSettings, FILE_MODE);
523
- } catch (err) { /* non-critical */ }
523
+ } catch (_err) { /* non-critical */ }
524
524
  } catch (err) {
525
525
  if (process.env.DEBUG) {
526
526
  console.error(`[postinstall] settings.json initial copy failed: ${err.message}`);
@@ -608,7 +608,7 @@ function getPackageVersion() {
608
608
  const content = fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf-8');
609
609
  const pkg = safeJsonParseString(content, null);
610
610
  return (pkg && pkg.version) || 'unknown';
611
- } catch (err) {
611
+ } catch (_err) {
612
612
  return 'unknown';
613
613
  }
614
614
  }
@@ -644,7 +644,7 @@ function regenerateClaudeMd() {
644
644
  }
645
645
  return;
646
646
  }
647
- } catch (err) {
647
+ } catch (_err) {
648
648
  // No version file yet — proceed with regen
649
649
  }
650
650
 
@@ -688,7 +688,7 @@ function regenerateClaudeMd() {
688
688
  // Record successful regen version for skip gate
689
689
  try {
690
690
  fs.writeFileSync(versionFile, pkgVersion, { mode: FILE_MODE });
691
- } catch (err) {
691
+ } catch (_err) {
692
692
  // Non-critical — next install will just regen again
693
693
  }
694
694
 
@@ -1015,7 +1015,7 @@ function main() {
1015
1015
  // Combine access check and open into single try-catch to avoid TOCTOU
1016
1016
  ttyFd = fs.openSync('/dev/tty', 'w');
1017
1017
  output = { write: (msg) => fs.writeSync(ttyFd, msg) };
1018
- } catch (err) {
1018
+ } catch (_err) {
1019
1019
  // /dev/tty not available (no terminal, CI, etc.) - fallback to stderr
1020
1020
  ttyFd = null;
1021
1021
  }
@@ -61,7 +61,7 @@ function readManifest() {
61
61
  return manifest;
62
62
  }
63
63
  return null;
64
- } catch (err) {
64
+ } catch (_err) {
65
65
  return null;
66
66
  }
67
67
  }
@@ -112,7 +112,7 @@ function cleanupEmptyDirs(baseDir) {
112
112
  if (remaining.length === 0) {
113
113
  fs.rmdirSync(subDir);
114
114
  }
115
- } catch (err) {
115
+ } catch (_err) {
116
116
  // Dir already gone or inaccessible
117
117
  }
118
118
  }
@@ -156,7 +156,7 @@ function manifestBasedCleanup(manifest) {
156
156
  preserved.push(path.join(dir, f));
157
157
  }
158
158
  }
159
- } catch (err) {
159
+ } catch (_err) {
160
160
  // Dir doesn't exist or already removed
161
161
  }
162
162
  }
@@ -231,7 +231,7 @@ function removeClaudeMd() {
231
231
  return true;
232
232
  }
233
233
  return false;
234
- } catch (err) {
234
+ } catch (_err) {
235
235
  return false;
236
236
  }
237
237
  }
@@ -254,7 +254,7 @@ function cleanupClaudeDir() {
254
254
  return { removed: true, preserved: [] };
255
255
  }
256
256
  return { removed: false, preserved: userFiles };
257
- } catch (err) {
257
+ } catch (_err) {
258
258
  return { removed: false, preserved: [] };
259
259
  }
260
260
  }
@@ -14,7 +14,7 @@
14
14
 
15
15
  const fs = require('node:fs');
16
16
  const path = require('node:path');
17
- const { getProjectRoot, getConfig, color, success, warn, PATHS } = require('../flow-utils');
17
+ const { getConfig, color, success, warn, PATHS } = require('../flow-utils');
18
18
  const { RegistryPlugin } = require('../flow-registry-manager');
19
19
  const { BaseScanner, PROJECT_ROOT } = require('../flow-scanner-base');
20
20
  const { info } = require('../flow-output');
@@ -92,7 +92,7 @@ class ComponentScanner extends BaseScanner {
92
92
  } else {
93
93
  this._scanJSXFile(content, relativePath, category);
94
94
  }
95
- } catch (err) {
95
+ } catch (_err) {
96
96
  // Skip files that can't be read
97
97
  }
98
98
  }
@@ -57,7 +57,7 @@ function walkSourceFiles(dir, options = {}) {
57
57
  let entries;
58
58
  try {
59
59
  entries = fs.readdirSync(currentDir, { withFileTypes: true });
60
- } catch (err) {
60
+ } catch (_err) {
61
61
  return; // Skip unreadable directories
62
62
  }
63
63
 
@@ -120,12 +120,12 @@ function scanHttpClients(projectRoot, options = {}) {
120
120
  let content;
121
121
  try {
122
122
  content = fs.readFileSync(filePath, 'utf-8');
123
- } catch (err) {
123
+ } catch (_err) {
124
124
  continue;
125
125
  }
126
126
 
127
127
  const relPath = path.relative(projectRoot, filePath);
128
- const lines = content.split('\n');
128
+ const _lines = content.split('\n');
129
129
 
130
130
  for (const pattern of patterns) {
131
131
  // Reset lastIndex for global regex
@@ -192,7 +192,7 @@ function scanRouteDefinitions(projectRoot, options = {}) {
192
192
  let content;
193
193
  try {
194
194
  content = fs.readFileSync(filePath, 'utf-8');
195
- } catch (err) {
195
+ } catch (_err) {
196
196
  continue;
197
197
  }
198
198
 
@@ -286,7 +286,7 @@ function scanNextjsApiRoutes(projectRoot) {
286
286
  let content;
287
287
  try {
288
288
  content = fs.readFileSync(filePath, 'utf-8');
289
- } catch (err) {
289
+ } catch (_err) {
290
290
  continue;
291
291
  }
292
292
 
@@ -352,7 +352,7 @@ function scanEventBus(projectRoot, options = {}) {
352
352
  let content;
353
353
  try {
354
354
  content = fs.readFileSync(filePath, 'utf-8');
355
- } catch (err) {
355
+ } catch (_err) {
356
356
  continue;
357
357
  }
358
358
 
@@ -422,7 +422,7 @@ function scanSharedTypes(projectRoot, options = {}) {
422
422
  let content;
423
423
  try {
424
424
  content = fs.readFileSync(filePath, 'utf-8');
425
- } catch (err) {
425
+ } catch (_err) {
426
426
  continue;
427
427
  }
428
428
 
@@ -495,7 +495,7 @@ function scanEnvVars(projectRoot, options = {}) {
495
495
  let content;
496
496
  try {
497
497
  content = fs.readFileSync(filePath, 'utf-8');
498
- } catch (err) {
498
+ } catch (_err) {
499
499
  continue;
500
500
  }
501
501
 
@@ -531,7 +531,7 @@ function scanEnvVars(projectRoot, options = {}) {
531
531
  let content;
532
532
  try {
533
533
  content = fs.readFileSync(envPath, 'utf-8');
534
- } catch (err) {
534
+ } catch (_err) {
535
535
  continue;
536
536
  }
537
537
 
@@ -588,7 +588,7 @@ function detectProjectType(projectRoot) {
588
588
  try {
589
589
  const entries = fs.readdirSync(dir, { withFileTypes: true });
590
590
  packageCount += entries.filter(e => e.isDirectory()).length;
591
- } catch (err) {
591
+ } catch (_err) {
592
592
  // skip
593
593
  }
594
594
  }
@@ -651,7 +651,7 @@ function getCommitSha(projectRoot) {
651
651
  encoding: 'utf-8',
652
652
  stdio: ['pipe', 'pipe', 'pipe']
653
653
  }).trim();
654
- } catch (err) {
654
+ } catch (_err) {
655
655
  return null;
656
656
  }
657
657
  }
@@ -12,7 +12,7 @@
12
12
  const fs = require('node:fs');
13
13
  const path = require('node:path');
14
14
  const { RegistryPlugin } = require('../flow-registry-manager');
15
- const { getProjectRoot, safeJsonParse: safeJsonParseFile, PATHS, getTodayDate } = require('../flow-utils');
15
+ const { safeJsonParse: safeJsonParseFile, PATHS, getTodayDate } = require('../flow-utils');
16
16
 
17
17
  const INDEX_PATH = path.join(PATHS.state, 'schema-index.json');
18
18
  const MAP_PATH = path.join(PATHS.state, 'schema-map.md');
@@ -47,7 +47,7 @@ class SchemaRegistry extends RegistryPlugin {
47
47
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
48
48
  const ormPackages = ['prisma', '@prisma/client', 'typeorm', 'drizzle-orm', 'sequelize', 'mongoose'];
49
49
  if (ormPackages.some(p => allDeps[p])) return true;
50
- } catch (err) {
50
+ } catch (_err) {
51
51
  // ignore
52
52
  }
53
53
  }
@@ -197,7 +197,7 @@ class SchemaRegistry extends RegistryPlugin {
197
197
  const content = fs.readFileSync(file, 'utf-8');
198
198
  const relPath = path.relative(PATHS.root, file);
199
199
  this._parsePrismaFile(content, relPath);
200
- } catch (err) {
200
+ } catch (_err) {
201
201
  // Skip unreadable files
202
202
  }
203
203
  }
@@ -217,7 +217,7 @@ class SchemaRegistry extends RegistryPlugin {
217
217
  results.push(fullPath);
218
218
  }
219
219
  }
220
- } catch (err) {
220
+ } catch (_err) {
221
221
  // Skip unreadable directories
222
222
  }
223
223
  return results;
@@ -368,7 +368,7 @@ class SchemaRegistry extends RegistryPlugin {
368
368
  }
369
369
  }
370
370
  }
371
- } catch (err) {
371
+ } catch (_err) {
372
372
  // Skip unreadable directories
373
373
  }
374
374
  }
@@ -13,7 +13,7 @@
13
13
  const fs = require('node:fs');
14
14
  const path = require('node:path');
15
15
  const { RegistryPlugin } = require('../flow-registry-manager');
16
- const { getProjectRoot, safeJsonParse: safeJsonParseFile, PATHS, getTodayDate } = require('../flow-utils');
16
+ const { safeJsonParse: safeJsonParseFile, PATHS, getTodayDate } = require('../flow-utils');
17
17
 
18
18
  const INDEX_PATH = path.join(PATHS.state, 'service-index.json');
19
19
  const MAP_PATH = path.join(PATHS.state, 'service-map.md');
@@ -314,12 +314,12 @@ class ServiceRegistry extends RegistryPlugin {
314
314
  if (content.includes('NestMiddleware')) {
315
315
  this._parseNestJSMiddleware(content, relPath, 'middleware');
316
316
  }
317
- } catch (err) {
317
+ } catch (_err) {
318
318
  // Skip unreadable files
319
319
  }
320
320
  }
321
321
  }
322
- } catch (err) {
322
+ } catch (_err) {
323
323
  // Skip unreadable directories
324
324
  }
325
325
  }
@@ -466,12 +466,12 @@ class ServiceRegistry extends RegistryPlugin {
466
466
  file: relPath
467
467
  });
468
468
  }
469
- } catch (err) {
469
+ } catch (_err) {
470
470
  // Skip
471
471
  }
472
472
  }
473
473
  }
474
- } catch (err) {
474
+ } catch (_err) {
475
475
  // Skip
476
476
  }
477
477
  }
@@ -523,12 +523,12 @@ class ServiceRegistry extends RegistryPlugin {
523
523
  const content = fs.readFileSync(fullPath, 'utf-8');
524
524
  const relPath = path.relative(PATHS.root, fullPath);
525
525
  this._parseDjangoFile(content, relPath);
526
- } catch (err) {
526
+ } catch (_err) {
527
527
  // Skip
528
528
  }
529
529
  }
530
530
  }
531
- } catch (err) {
531
+ } catch (_err) {
532
532
  // Skip
533
533
  }
534
534
  }
@@ -589,12 +589,12 @@ class ServiceRegistry extends RegistryPlugin {
589
589
  const relPath = path.relative(PATHS.root, fullPath);
590
590
  this._parseGoFile(content, relPath);
591
591
  }
592
- } catch (err) {
592
+ } catch (_err) {
593
593
  // Skip
594
594
  }
595
595
  }
596
596
  }
597
- } catch (err) {
597
+ } catch (_err) {
598
598
  // Skip
599
599
  }
600
600
  }