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
@@ -9,7 +9,7 @@
9
9
 
10
10
  const { execSync } = require('node:child_process');
11
11
  const path = require('node:path');
12
- const { getProjectRoot, PATHS } = require('./flow-utils');
12
+ const { PATHS } = require('./flow-utils');
13
13
 
14
14
  /**
15
15
  * Run regression tests as a workflow step
@@ -37,7 +37,7 @@ async function run(options = {}) {
37
37
  let parsed;
38
38
  try {
39
39
  parsed = JSON.parse(result);
40
- } catch (err) {
40
+ } catch (_err) {
41
41
  // Non-JSON output means success with no issues
42
42
  return { passed: true, message: 'Regression tests passed' };
43
43
  }
@@ -67,7 +67,7 @@ async function run(options = {}) {
67
67
  details: parsed.failures,
68
68
  };
69
69
  }
70
- } catch (err) {
70
+ } catch (_err) {
71
71
  // Not JSON, treat as error
72
72
  }
73
73
  }
@@ -142,7 +142,7 @@ function printIssue(issue) {
142
142
  /**
143
143
  * Run multi-agent review (3 perspectives)
144
144
  */
145
- async function runMultiAgentReview(files, config) {
145
+ async function runMultiAgentReview(files, _config) {
146
146
  const allIssues = [];
147
147
 
148
148
  for (const file of files) {
@@ -165,7 +165,7 @@ async function runMultiAgentReview(files, config) {
165
165
  ]);
166
166
 
167
167
  allIssues.push(...fileIssues);
168
- } catch (err) {
168
+ } catch (_err) {
169
169
  // Skip unreadable files
170
170
  }
171
171
  }
@@ -176,7 +176,7 @@ async function runMultiAgentReview(files, config) {
176
176
  /**
177
177
  * Run simple review (single pass)
178
178
  */
179
- async function runSimpleReview(files, config) {
179
+ async function runSimpleReview(files, _config) {
180
180
  const allIssues = [];
181
181
 
182
182
  for (const file of files) {
@@ -187,7 +187,7 @@ async function runSimpleReview(files, config) {
187
187
  const content = fs.readFileSync(filePath, 'utf8');
188
188
  const fileIssues = runBasicChecks(content, file);
189
189
  allIssues.push(...fileIssues);
190
- } catch (err) {
190
+ } catch (_err) {
191
191
  // Skip unreadable files
192
192
  }
193
193
  }
@@ -200,7 +200,7 @@ async function runSimpleReview(files, config) {
200
200
  */
201
201
  function reviewArchitecture(content, fileName) {
202
202
  const issues = [];
203
- const lines = content.split('\n');
203
+ const _lines = content.split('\n');
204
204
 
205
205
  // Check for god objects (too many methods/properties)
206
206
  const methodCount = (content.match(/(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s*)?\()/g) || []).length;
@@ -15,7 +15,7 @@
15
15
  const fs = require('node:fs');
16
16
  const path = require('node:path');
17
17
  const {
18
- getProjectRoot,
18
+ getProjectRoot: _getProjectRoot,
19
19
  colors,
20
20
  getConfig,
21
21
  getConfigValue,
@@ -26,7 +26,7 @@ const {
26
26
  safeJsonParse,
27
27
  isPathWithinProject, PATHS
28
28
  } = require('./flow-utils');
29
- const { success, warn, error, info, print, printHeader } = require('./flow-output');
29
+ const { success, warn, error, info } = require('./flow-output');
30
30
 
31
31
  // Import context orchestrator for product context
32
32
  let contextOrchestrator = null;
@@ -20,7 +20,7 @@
20
20
  const fs = require('node:fs');
21
21
  const path = require('node:path');
22
22
  const {
23
- getProjectRoot,
23
+ getProjectRoot: _getProjectRoot,
24
24
  safeJsonParse,
25
25
  getConfig,
26
26
  colors,
@@ -500,7 +500,7 @@ function reviewCode(options = {}) {
500
500
 
501
501
  if (!isEnabled()) return { warnings };
502
502
 
503
- const standards = loadStandards();
503
+ const _standards = loadStandards();
504
504
  const config = loadConfig();
505
505
 
506
506
  // Only run in user review mode
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow — Structure-Change Sensor
5
+ *
6
+ * Detects folder-restructure patterns in a git diff range so /wogi-finalize
7
+ * and cross-repo audit workflows can flag merges that will produce
8
+ * modify/delete conflicts rather than routine content conflicts.
9
+ *
10
+ * Patterns detected (all evaluated against `git diff --name-status <range>`):
11
+ *
12
+ * 1. FOLDER_PER_COMPONENT — `X.tsx` deleted + `X/X.tsx` added
13
+ * 2. SPLIT_INTO_SUBMODULE — `X.ts` deleted + one or more `<dir>/X-*.ts`
14
+ * or `<dir>/<other>.ts` added at deeper depth
15
+ * 3. BARREL_INTRODUCTION — `X.ts` deleted + `X/index.ts` added
16
+ * 4. RENAME_NEW_HOME — single file deletion + single file addition,
17
+ * same basename, different directory
18
+ *
19
+ * The sensor is intentionally pattern-based and bounded: no full-diff parsing,
20
+ * no AST walks. Downstream callers (wogi-finalize merge-plan gate) use the
21
+ * output to drive a percentage threshold — when restructure-matched files
22
+ * exceed `restructureThreshold` (default 20%) of the changed set, a structural
23
+ * warning is surfaced.
24
+ *
25
+ * Usage (CLI):
26
+ * flow-structure-sensor.js <base>..<branch>
27
+ * → emits one JSON object on stdout
28
+ *
29
+ * Usage (module):
30
+ * const { detectStructureChanges } = require('./flow-structure-sensor');
31
+ * const result = detectStructureChanges({ base: 'master', branch: 'feature/x' });
32
+ *
33
+ * Exit codes:
34
+ * 0 — success (patterns may or may not be present; see result.warn)
35
+ * 1 — git invocation failed or invalid range
36
+ */
37
+
38
+ 'use strict';
39
+
40
+ const { execFileSync } = require('node:child_process');
41
+ const path = require('node:path');
42
+
43
+ const DEFAULT_RESTRUCTURE_THRESHOLD = 0.20;
44
+
45
+ /**
46
+ * Run `git diff --name-status` and return the parsed entries.
47
+ * Each entry is { status: 'A'|'D'|'M'|'R100'|..., path: string, origPath?: string }.
48
+ */
49
+ function getDiffEntries({ range, cwd }) {
50
+ const args = ['diff', '--name-status', range];
51
+ let output;
52
+ try {
53
+ output = execFileSync('git', args, {
54
+ cwd: cwd || process.cwd(),
55
+ encoding: 'utf-8',
56
+ stdio: ['pipe', 'pipe', 'pipe'],
57
+ });
58
+ } catch (err) {
59
+ throw new Error(`git diff failed: ${err.message || err}`);
60
+ }
61
+ const entries = [];
62
+ for (const line of output.split('\n')) {
63
+ if (!line.trim()) continue;
64
+ const parts = line.split('\t');
65
+ const status = parts[0];
66
+ if (!status) continue;
67
+ // R/C statuses carry two paths: origPath, newPath
68
+ if (/^R/.test(status) || /^C/.test(status)) {
69
+ if (parts.length >= 3) {
70
+ entries.push({ status, origPath: parts[1], path: parts[2] });
71
+ }
72
+ } else {
73
+ entries.push({ status, path: parts[1] });
74
+ }
75
+ }
76
+ return entries;
77
+ }
78
+
79
+ /**
80
+ * Analyze entries for restructure patterns.
81
+ * Returns:
82
+ * {
83
+ * range, totalChanged, restructureCount, ratio, warn,
84
+ * patterns: [{ type, deleted, added, basename }]
85
+ * }
86
+ */
87
+ function classify(entries) {
88
+ const deletions = entries.filter((e) => e.status === 'D').map((e) => e.path);
89
+ const additions = entries.filter((e) => e.status === 'A').map((e) => e.path);
90
+ const renames = entries.filter((e) => /^R/.test(e.status));
91
+
92
+ const patterns = [];
93
+ const restructureFiles = new Set();
94
+
95
+ const deletedByBasename = new Map();
96
+ for (const p of deletions) {
97
+ const base = path.basename(p, path.extname(p));
98
+ if (!deletedByBasename.has(base)) deletedByBasename.set(base, []);
99
+ deletedByBasename.get(base).push(p);
100
+ }
101
+ const addedByBasename = new Map();
102
+ for (const p of additions) {
103
+ const base = path.basename(p, path.extname(p));
104
+ if (!addedByBasename.has(base)) addedByBasename.set(base, []);
105
+ addedByBasename.get(base).push(p);
106
+ }
107
+
108
+ // 1. FOLDER_PER_COMPONENT: X.tsx deleted AND X/X.tsx (same basename/ext) added
109
+ for (const [base, dels] of deletedByBasename) {
110
+ const adds = addedByBasename.get(base) || [];
111
+ for (const del of dels) {
112
+ for (const add of adds) {
113
+ const delDir = path.dirname(del);
114
+ const addDir = path.dirname(add);
115
+ const delExt = path.extname(del);
116
+ const addExt = path.extname(add);
117
+ if (delExt !== addExt) continue;
118
+ // Folder-per-component: addDir = delDir + '/' + base
119
+ if (addDir === path.join(delDir, base)) {
120
+ patterns.push({ type: 'FOLDER_PER_COMPONENT', deleted: del, added: add, basename: base });
121
+ restructureFiles.add(del);
122
+ restructureFiles.add(add);
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ // 2. SPLIT_INTO_SUBMODULE: X.ts deleted AND 2+ files added under a dir
129
+ // named X/ or at deeper depth carrying X's name as dir.
130
+ for (const [base, dels] of deletedByBasename) {
131
+ for (const del of dels) {
132
+ const delDir = path.dirname(del);
133
+ const delExt = path.extname(del);
134
+ const submoduleDir = path.join(delDir, base);
135
+ const adds = additions.filter(
136
+ (a) =>
137
+ path.extname(a) === delExt &&
138
+ (a.startsWith(submoduleDir + path.sep) || a.startsWith(submoduleDir + '/'))
139
+ );
140
+ // Need to see this as a split only if 2+ files land there — single file
141
+ // was already picked up as FOLDER_PER_COMPONENT above.
142
+ if (adds.length >= 2) {
143
+ patterns.push({ type: 'SPLIT_INTO_SUBMODULE', deleted: del, added: adds, basename: base });
144
+ restructureFiles.add(del);
145
+ for (const a of adds) restructureFiles.add(a);
146
+ }
147
+ }
148
+ }
149
+
150
+ // 3. BARREL_INTRODUCTION: X.ts deleted AND X/index.ts added (any extension match)
151
+ for (const [base, dels] of deletedByBasename) {
152
+ for (const del of dels) {
153
+ const delDir = path.dirname(del);
154
+ const delExt = path.extname(del);
155
+ const barrelPath = path.join(delDir, base, `index${delExt}`);
156
+ if (additions.includes(barrelPath)) {
157
+ patterns.push({ type: 'BARREL_INTRODUCTION', deleted: del, added: barrelPath, basename: base });
158
+ restructureFiles.add(del);
159
+ restructureFiles.add(barrelPath);
160
+ }
161
+ }
162
+ }
163
+
164
+ // 4. RENAME_NEW_HOME: git-reported renames across different directories
165
+ for (const r of renames) {
166
+ if (!r.origPath || !r.path) continue;
167
+ if (path.dirname(r.origPath) !== path.dirname(r.path)) {
168
+ patterns.push({
169
+ type: 'RENAME_NEW_HOME',
170
+ deleted: r.origPath,
171
+ added: r.path,
172
+ basename: path.basename(r.path, path.extname(r.path)),
173
+ });
174
+ restructureFiles.add(r.origPath);
175
+ restructureFiles.add(r.path);
176
+ }
177
+ }
178
+
179
+ const totalChanged = entries.length;
180
+ const ratio = totalChanged > 0 ? restructureFiles.size / totalChanged : 0;
181
+
182
+ return {
183
+ totalChanged,
184
+ restructureCount: restructureFiles.size,
185
+ ratio,
186
+ patterns,
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Public API — detect structure changes for a git range.
192
+ *
193
+ * @param {Object} opts
194
+ * @param {string} opts.range - Git range (e.g., "master..feature/foo" or a single SHA range).
195
+ * If opts.base+opts.branch given, they combine as "base..branch".
196
+ * @param {string} [opts.base]
197
+ * @param {string} [opts.branch]
198
+ * @param {string} [opts.cwd]
199
+ * @param {number} [opts.threshold=0.20] - Ratio above which warn=true.
200
+ * @returns {Object}
201
+ */
202
+ function detectStructureChanges(opts = {}) {
203
+ const range = opts.range || (opts.base && opts.branch ? `${opts.base}..${opts.branch}` : null);
204
+ if (!range || typeof range !== 'string') {
205
+ return {
206
+ ok: false,
207
+ reason: 'missing-range',
208
+ totalChanged: 0,
209
+ restructureCount: 0,
210
+ ratio: 0,
211
+ warn: false,
212
+ patterns: [],
213
+ };
214
+ }
215
+ const threshold = typeof opts.threshold === 'number' ? opts.threshold : DEFAULT_RESTRUCTURE_THRESHOLD;
216
+
217
+ let entries;
218
+ try {
219
+ entries = getDiffEntries({ range, cwd: opts.cwd });
220
+ } catch (err) {
221
+ return {
222
+ ok: false,
223
+ reason: `git-failed: ${err.message}`,
224
+ range,
225
+ totalChanged: 0,
226
+ restructureCount: 0,
227
+ ratio: 0,
228
+ warn: false,
229
+ patterns: [],
230
+ };
231
+ }
232
+
233
+ const classified = classify(entries);
234
+ const warn = classified.ratio >= threshold && classified.restructureCount >= 2;
235
+
236
+ return {
237
+ ok: true,
238
+ range,
239
+ threshold,
240
+ totalChanged: classified.totalChanged,
241
+ restructureCount: classified.restructureCount,
242
+ ratio: Number(classified.ratio.toFixed(4)),
243
+ warn,
244
+ patterns: classified.patterns,
245
+ };
246
+ }
247
+
248
+ // ============================================================
249
+ // CLI
250
+ // ============================================================
251
+
252
+ function main() {
253
+ const argv = process.argv.slice(2);
254
+ if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
255
+ console.log('Usage: flow-structure-sensor.js <range> [--threshold=N]');
256
+ console.log(' range — git diff range, e.g. "master..feature/x" or "<sha1>..<sha2>"');
257
+ console.log(' --threshold=N float 0–1, default 0.20');
258
+ process.exit(argv.length === 0 ? 1 : 0);
259
+ }
260
+ const range = argv[0];
261
+ let threshold = DEFAULT_RESTRUCTURE_THRESHOLD;
262
+ for (const a of argv.slice(1)) {
263
+ const m = a.match(/^--threshold=(.+)$/);
264
+ if (m) {
265
+ const n = Number(m[1]);
266
+ if (Number.isFinite(n)) threshold = n;
267
+ }
268
+ }
269
+ const result = detectStructureChanges({ range, threshold });
270
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
271
+ process.exit(result.ok ? 0 : 1);
272
+ }
273
+
274
+ if (require.main === module) {
275
+ main();
276
+ }
277
+
278
+ module.exports = {
279
+ detectStructureChanges,
280
+ classify,
281
+ getDiffEntries,
282
+ DEFAULT_RESTRUCTURE_THRESHOLD,
283
+ };
@@ -103,14 +103,14 @@ function anonymizeBatch(records) {
103
103
  */
104
104
  function createUploadPayload(params) {
105
105
  const { records, capabilityScores, routingEffectiveness } = params;
106
- const config = getConfig();
106
+ const _config = getConfig();
107
107
 
108
108
  // Get WogiFlow version from package.json
109
109
  let version = 'unknown';
110
110
  try {
111
111
  const pkg = require(path.join(PATHS.root, 'package.json'));
112
112
  version = pkg.version || 'unknown';
113
- } catch (err) {
113
+ } catch (_err) {
114
114
  // Non-critical
115
115
  }
116
116
 
@@ -209,7 +209,7 @@ function main() {
209
209
  const sample = [
210
210
  {
211
211
  taskId: 'wf-abc123',
212
- model: 'claude-opus-4-6',
212
+ model: 'claude-opus-4-7',
213
213
  taskType: 'feature',
214
214
  iterations: 2,
215
215
  firstAttemptPass: true,
@@ -14,10 +14,10 @@
14
14
  * Part of S1: Smart Context Compaction
15
15
  */
16
16
 
17
- const fs = require('node:fs');
17
+ const _fs = require('node:fs');
18
18
  const path = require('node:path');
19
19
  const {
20
- getConfig,
20
+ getConfig: _getConfig,
21
21
  STATE_DIR,
22
22
  readJson,
23
23
  writeJson,
@@ -25,7 +25,7 @@ const {
25
25
  PROJECT_ROOT,
26
26
  readFile,
27
27
  fileExists,
28
- info,
28
+ info: _info,
29
29
  warn,
30
30
  parseFlags,
31
31
  outputJson
@@ -113,7 +113,7 @@ const TASK_TYPE_PATTERNS = {
113
113
  * @param {Object} options - Classification options
114
114
  * @returns {Object} - Classification result with type and confidence
115
115
  */
116
- function classifyTask(taskDescription, affectedFiles = [], options = {}) {
116
+ function classifyTask(taskDescription, affectedFiles = [], _options = {}) {
117
117
  const descLower = taskDescription.toLowerCase();
118
118
  const scores = {};
119
119
 
@@ -12,7 +12,7 @@
12
12
  const path = require('node:path');
13
13
  const fs = require('node:fs');
14
14
  const { execFileSync } = require('node:child_process');
15
- const { getConfig, PATHS, safeJsonParse, readFile, writeFile, ensureDir, isPathWithinProject, validateTaskId } = require('./flow-utils');
15
+ const { getConfig, PATHS, safeJsonParse, writeFile, ensureDir, isPathWithinProject, validateTaskId } = require('./flow-utils');
16
16
 
17
17
  /**
18
18
  * Check if completion summaries are enabled in config
@@ -14,7 +14,7 @@
14
14
 
15
15
  const fs = require('node:fs');
16
16
  const path = require('node:path');
17
- const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse, PATHS } = require('./flow-utils');
17
+ const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse } = require('./flow-utils');
18
18
  const { getCommand } = require('./flow-script-resolver');
19
19
  const { sanitizeShellArg } = require('./flow-security');
20
20
 
@@ -84,7 +84,7 @@ function isRecheckEnabled() {
84
84
  * Returns { allowed: boolean, message: string }
85
85
  */
86
86
  function canSkipCriterion(criterionId, approvalGiven = false) {
87
- const config = getConfig();
87
+ const _config = getConfig();
88
88
  const session = getActiveLoop();
89
89
 
90
90
  if (!session) {
@@ -150,7 +150,7 @@ function startLoop(taskId, acceptanceCriteria) {
150
150
 
151
151
  // v2.0: Use durable session if enabled
152
152
  if (config.durableSteps?.enabled !== false) {
153
- const session = durableSession.createDurableSession(taskId, 'task', acceptanceCriteria);
153
+ const _session = durableSession.createDurableSession(taskId, 'task', acceptanceCriteria);
154
154
  // Return backward-compatible format
155
155
  return durableSession.getActiveLoop();
156
156
  }
@@ -753,7 +753,7 @@ function getLoopStats() {
753
753
  function verifyCriterion(criterion, context = {}) {
754
754
  const { execSync, execFileSync } = require('node:child_process');
755
755
  const { changedFiles = [], testResults = null, lintResults = null } = context;
756
- const config = getConfig();
756
+ const _config = getConfig();
757
757
  const taskConfig = getTaskConfig();
758
758
  const desc = criterion.description;
759
759
  const descLower = desc.toLowerCase();
@@ -848,7 +848,7 @@ function verifyCriterion(criterion, context = {}) {
848
848
  }
849
849
  }
850
850
  }
851
- } catch (err) { /* ignore permission errors */ }
851
+ } catch (_err) { /* ignore permission errors */ }
852
852
  return results;
853
853
  }
854
854
 
@@ -32,13 +32,13 @@ const {
32
32
  colors: c,
33
33
  safeJsonParse,
34
34
  writeJson,
35
- fileExists,
35
+ fileExists: _fileExists,
36
36
  getProjectRoot,
37
37
  ensureDir,
38
- generateTaskId, PATHS,
38
+ generateTaskId, PATHS: _PATHS,
39
39
  getTodayDate
40
40
  } = require('./flow-utils');
41
- const { success: printSuccess, warn: printWarn } = require('./flow-output');
41
+ const { warn: printWarn } = require('./flow-output');
42
42
 
43
43
  // ============================================================================
44
44
  // Constants
@@ -289,7 +289,7 @@ function generateTemplate(file, type) {
289
289
  const content = file.content;
290
290
  const lines = content.split('\n');
291
291
  const templateLines = [];
292
- let insideBody = false;
292
+ let _insideBody = false;
293
293
  let braceDepth = 0;
294
294
  let bodyStartDepth = 0;
295
295
  let skipUntilCloseBrace = false;
@@ -379,10 +379,10 @@ function generateTemplate(file, type) {
379
379
  /**
380
380
  * Check if a line is structural (should always be kept)
381
381
  */
382
- function isStructuralLine(trimmed, type) {
382
+ function isStructuralLine(trimmed, _type) {
383
383
  // Import statements
384
384
  if (trimmed.startsWith('import ') || trimmed.startsWith('const ') && trimmed.includes('require(')) return true;
385
- const { PATHS } = require('./flow-utils');
385
+ const { } = require('./flow-utils');
386
386
  if (trimmed.startsWith('from ')) return true;
387
387
 
388
388
  // Export statements
@@ -13,7 +13,7 @@
13
13
 
14
14
  const fs = require('node:fs');
15
15
  const path = require('node:path');
16
- const { getProjectRoot, readJson, info, PATHS } = require('./flow-utils');
16
+ const { readJson, info, PATHS } = require('./flow-utils');
17
17
 
18
18
  const TEMPLATES_DIR = path.join(PATHS.root, 'templates', 'hybrid');
19
19
 
@@ -19,14 +19,14 @@
19
19
  const fs = require('node:fs');
20
20
  const path = require('node:path');
21
21
  const { spawn } = require('node:child_process');
22
- const { getProjectRoot, PATHS, ensureDir, safeJsonParse, safeJsonParseString } = require('./flow-utils');
22
+ const { PATHS, ensureDir, safeJsonParse, safeJsonParseString } = require('./flow-utils');
23
23
  const { getConfig } = require('./flow-config-loader');
24
24
  const { loadProfile } = require('./flow-verification-profile');
25
25
 
26
26
  let scenarioEngine;
27
27
  try {
28
28
  scenarioEngine = require('./flow-scenario-engine');
29
- } catch (err) {
29
+ } catch (_err) {
30
30
  scenarioEngine = null;
31
31
  }
32
32
 
@@ -58,7 +58,7 @@ function parseAPIMap(apiMapPath) {
58
58
  let content;
59
59
  try {
60
60
  content = fs.readFileSync(apiMapPath, 'utf-8');
61
- } catch (err) {
61
+ } catch (_err) {
62
62
  return [];
63
63
  }
64
64
 
@@ -137,7 +137,7 @@ function parseOpenAPISpec(specPath) {
137
137
  let content;
138
138
  try {
139
139
  content = fs.readFileSync(specPath, 'utf-8');
140
- } catch (err) {
140
+ } catch (_err) {
141
141
  return { endpoints: [], raw: null };
142
142
  }
143
143
 
@@ -524,13 +524,13 @@ async function executeAPITest(testCase, baseUrl, options = {}) {
524
524
  if (contentType.includes('application/json')) {
525
525
  try {
526
526
  body = await response.json();
527
- } catch (err) {
527
+ } catch (_err) {
528
528
  body = null;
529
529
  }
530
530
  } else {
531
531
  try {
532
532
  body = await response.text();
533
- } catch (err) {
533
+ } catch (_err) {
534
534
  body = null;
535
535
  }
536
536
  }
@@ -665,7 +665,7 @@ async function startAPIServer(command, baseUrl, timeout = SERVER_READY_TIMEOUT)
665
665
  if (response.ok || response.status < 500) {
666
666
  return { process: serverProcess, ready: true, error: null };
667
667
  }
668
- } catch (err) {
668
+ } catch (_err) {
669
669
  // Server not ready yet — continue polling
670
670
  }
671
671
  }
@@ -693,7 +693,7 @@ function stopAPIServer(serverProcess) {
693
693
  if (serverProcess.pid) {
694
694
  try {
695
695
  process.kill(-serverProcess.pid, 'SIGTERM');
696
- } catch (err) {
696
+ } catch (_err) {
697
697
  // Process group kill failed — try direct kill
698
698
  try {
699
699
  serverProcess.kill('SIGTERM');
@@ -708,12 +708,12 @@ function stopAPIServer(serverProcess) {
708
708
  if (!serverProcess.killed) {
709
709
  process.kill(-serverProcess.pid, 'SIGKILL');
710
710
  }
711
- } catch (err) {
711
+ } catch (_err) {
712
712
  // Already dead
713
713
  }
714
714
  }, 5000);
715
715
  }
716
- } catch (err) {
716
+ } catch (_err) {
717
717
  // Process already terminated
718
718
  }
719
719
  }
@@ -1049,7 +1049,7 @@ function loadOrGenerateScenarios(taskId, options = {}) {
1049
1049
  let apiMapContent = null;
1050
1050
  try {
1051
1051
  apiMapContent = fs.readFileSync(apiMapPath, 'utf-8');
1052
- } catch (err) {
1052
+ } catch (_err) {
1053
1053
  // No API map available
1054
1054
  }
1055
1055
 
@@ -1105,7 +1105,7 @@ async function runScenarioTests(taskId, scenarios, options = {}) {
1105
1105
  try {
1106
1106
  fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
1107
1107
  report.reportPath = reportPath;
1108
- } catch (err) {
1108
+ } catch (_err) {
1109
1109
  // Non-fatal
1110
1110
  }
1111
1111
  }