wogiflow 2.4.2 → 2.4.4

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 (210) hide show
  1. package/.claude/commands/wogi-start.md +124 -0
  2. package/.claude/docs/claude-code-compatibility.md +51 -0
  3. package/.claude/docs/explore-agents.md +11 -0
  4. package/.claude/settings.json +12 -1
  5. package/.workflow/models/registry.json +1 -1
  6. package/bin/flow +11 -1
  7. package/lib/workspace-contracts.js +599 -0
  8. package/lib/workspace-intelligence.js +600 -0
  9. package/lib/workspace-messages.js +441 -0
  10. package/lib/workspace-routing.js +485 -0
  11. package/lib/workspace-sync.js +339 -0
  12. package/lib/workspace.js +1073 -0
  13. package/package.json +4 -4
  14. package/scripts/MEMORY-ARCHITECTURE.md +1 -1
  15. package/scripts/base-workflow-step.js +136 -0
  16. package/scripts/flow-adaptive-learning.js +8 -9
  17. package/scripts/flow-aggregate.js +11 -6
  18. package/scripts/flow-api-index.js +4 -6
  19. package/scripts/flow-assumption-detector.js +0 -2
  20. package/scripts/flow-audit.js +15 -2
  21. package/scripts/flow-auto-context.js +8 -12
  22. package/scripts/flow-auto-learn.js +49 -49
  23. package/scripts/flow-background.js +5 -6
  24. package/scripts/flow-bridge-state.js +8 -10
  25. package/scripts/flow-bulk-loop.js +1 -3
  26. package/scripts/flow-bulk-orchestrator.js +1 -3
  27. package/scripts/flow-cascade-completion.js +0 -2
  28. package/scripts/flow-cascade.js +4 -4
  29. package/scripts/flow-checkpoint.js +10 -13
  30. package/scripts/flow-code-intelligence.js +10 -12
  31. package/scripts/flow-community-sync.js +4 -4
  32. package/scripts/flow-community.js +12 -20
  33. package/scripts/flow-config-defaults.js +28 -2
  34. package/scripts/flow-config-interactive.js +9 -5
  35. package/scripts/flow-config-loader.js +49 -92
  36. package/scripts/flow-config-substitution.js +0 -2
  37. package/scripts/flow-context-estimator.js +4 -4
  38. package/scripts/flow-context-init.js +10 -12
  39. package/scripts/flow-context-manager.js +0 -2
  40. package/scripts/flow-context-scoring.js +2 -2
  41. package/scripts/flow-contract-scan.js +6 -9
  42. package/scripts/flow-correct.js +29 -27
  43. package/scripts/flow-correction-detector.js +5 -1
  44. package/scripts/flow-damage-control.js +47 -54
  45. package/scripts/flow-decisions-merge.js +4 -14
  46. package/scripts/flow-diff.js +5 -8
  47. package/scripts/flow-done-gates.js +786 -0
  48. package/scripts/flow-done-report.js +123 -0
  49. package/scripts/flow-done.js +71 -717
  50. package/scripts/flow-entropy-monitor.js +1 -3
  51. package/scripts/flow-eval-calibration.js +257 -0
  52. package/scripts/flow-eval-judge.js +10 -1
  53. package/scripts/flow-eval.js +14 -5
  54. package/scripts/flow-extraction-review.js +1 -0
  55. package/scripts/flow-failure-categories.js +0 -2
  56. package/scripts/flow-figma-confirm.js +5 -9
  57. package/scripts/flow-figma-generate.js +8 -10
  58. package/scripts/flow-figma-index.js +8 -10
  59. package/scripts/flow-figma-match.js +3 -5
  60. package/scripts/flow-figma-mcp-server.js +2 -4
  61. package/scripts/flow-figma-orchestrator.js +2 -3
  62. package/scripts/flow-figma-registry.js +2 -3
  63. package/scripts/flow-framework-resolver.js +0 -2
  64. package/scripts/flow-function-index.js +4 -6
  65. package/scripts/flow-gate-confidence.js +2 -2
  66. package/scripts/flow-gitignore.js +0 -2
  67. package/scripts/flow-guided-edit.js +5 -6
  68. package/scripts/flow-health.js +5 -6
  69. package/scripts/flow-hook-errors.js +6 -0
  70. package/scripts/flow-hook-status.js +263 -0
  71. package/scripts/flow-hooks.js +17 -29
  72. package/scripts/flow-http-client.js +9 -8
  73. package/scripts/flow-hybrid-interactive.js +7 -12
  74. package/scripts/flow-hybrid-test.js +12 -13
  75. package/scripts/flow-instruction-richness.js +1 -1
  76. package/scripts/flow-io.js +21 -4
  77. package/scripts/flow-knowledge-router.js +9 -3
  78. package/scripts/flow-learning-orchestrator.js +318 -13
  79. package/scripts/flow-links.js +5 -7
  80. package/scripts/flow-long-input-association.js +275 -0
  81. package/scripts/flow-long-input-chunking.js +1 -0
  82. package/scripts/flow-long-input-cli.js +0 -2
  83. package/scripts/flow-long-input-complexity.js +0 -2
  84. package/scripts/flow-long-input-constants.js +0 -2
  85. package/scripts/flow-long-input-contradictions.js +351 -0
  86. package/scripts/flow-long-input-detection.js +0 -2
  87. package/scripts/flow-long-input-passes.js +885 -0
  88. package/scripts/flow-long-input-stories.js +1 -1
  89. package/scripts/flow-long-input-voice.js +0 -2
  90. package/scripts/flow-long-input.js +425 -3005
  91. package/scripts/flow-loop-retry-learning.js +2 -3
  92. package/scripts/flow-lsp.js +3 -3
  93. package/scripts/flow-mcp-docs.js +3 -4
  94. package/scripts/flow-memory-db.js +6 -8
  95. package/scripts/flow-memory-sync.js +18 -11
  96. package/scripts/flow-metrics.js +1 -2
  97. package/scripts/flow-model-adapter.js +2 -3
  98. package/scripts/flow-model-config.js +72 -104
  99. package/scripts/flow-model-router.js +2 -2
  100. package/scripts/flow-model-types.js +0 -2
  101. package/scripts/flow-multi-approach.js +5 -6
  102. package/scripts/flow-orchestrate-context.js +3 -7
  103. package/scripts/flow-orchestrate-rollback.js +3 -8
  104. package/scripts/flow-orchestrate-state.js +8 -14
  105. package/scripts/flow-orchestrate-templates.js +2 -6
  106. package/scripts/flow-orchestrate-validator.js +5 -9
  107. package/scripts/flow-orchestrate.js +126 -103
  108. package/scripts/flow-output.js +0 -2
  109. package/scripts/flow-parallel.js +1 -1
  110. package/scripts/flow-paths.js +23 -2
  111. package/scripts/flow-pattern-enforcer.js +30 -28
  112. package/scripts/flow-pattern-extractor.js +3 -4
  113. package/scripts/flow-pending.js +0 -2
  114. package/scripts/flow-permissions.js +2 -3
  115. package/scripts/flow-plugin-registry.js +10 -12
  116. package/scripts/flow-prd-manager.js +1 -1
  117. package/scripts/flow-progress.js +7 -9
  118. package/scripts/flow-prompt-composer.js +3 -3
  119. package/scripts/flow-prompt-template.js +2 -2
  120. package/scripts/flow-providers.js +7 -4
  121. package/scripts/flow-registry-manager.js +7 -12
  122. package/scripts/flow-regression.js +9 -11
  123. package/scripts/flow-roadmap.js +2 -2
  124. package/scripts/flow-run-trace.js +16 -15
  125. package/scripts/flow-safety.js +2 -5
  126. package/scripts/flow-scanner-base.js +5 -7
  127. package/scripts/flow-scenario-engine.js +1 -5
  128. package/scripts/flow-security.js +29 -0
  129. package/scripts/flow-session-end.js +32 -41
  130. package/scripts/flow-session-learning.js +53 -49
  131. package/scripts/flow-setup-hooks.js +2 -3
  132. package/scripts/flow-skill-create.js +7 -12
  133. package/scripts/flow-skill-generator.js +12 -16
  134. package/scripts/flow-skill-learn.js +17 -8
  135. package/scripts/flow-skill-matcher.js +1 -2
  136. package/scripts/flow-spec-generator.js +2 -4
  137. package/scripts/flow-stack-wizard.js +5 -7
  138. package/scripts/flow-standards-learner.js +35 -16
  139. package/scripts/flow-start.js +2 -0
  140. package/scripts/flow-stats-collector.js +2 -2
  141. package/scripts/flow-status.js +10 -10
  142. package/scripts/flow-statusline-setup.js +2 -2
  143. package/scripts/flow-step-changelog.js +2 -3
  144. package/scripts/flow-step-comments.js +66 -81
  145. package/scripts/flow-step-complexity.js +50 -70
  146. package/scripts/flow-step-coverage.js +3 -5
  147. package/scripts/flow-step-knowledge.js +2 -3
  148. package/scripts/flow-step-pr-tests.js +64 -74
  149. package/scripts/flow-step-regression.js +3 -5
  150. package/scripts/flow-step-review.js +86 -103
  151. package/scripts/flow-step-security.js +111 -121
  152. package/scripts/flow-step-silent-failures.js +56 -83
  153. package/scripts/flow-step-simplifier.js +52 -70
  154. package/scripts/flow-story.js +4 -7
  155. package/scripts/flow-strict-adherence.js +3 -4
  156. package/scripts/flow-task-checkpoint.js +36 -5
  157. package/scripts/flow-task-enforcer.js +2 -24
  158. package/scripts/flow-tech-debt.js +1 -1
  159. package/scripts/flow-template-extractor.js +1 -0
  160. package/scripts/flow-templates.js +11 -13
  161. package/scripts/flow-test-api.js +9 -13
  162. package/scripts/flow-test-discovery.js +1 -1
  163. package/scripts/flow-test-generate.js +5 -9
  164. package/scripts/flow-test-integrity.js +3 -7
  165. package/scripts/flow-test-ui.js +5 -9
  166. package/scripts/flow-testing-deps.js +1 -3
  167. package/scripts/flow-tiered-learning.js +4 -4
  168. package/scripts/flow-todowrite-sync.js +1 -1
  169. package/scripts/flow-tokens.js +0 -2
  170. package/scripts/flow-verification-profile.js +6 -10
  171. package/scripts/flow-verify.js +12 -16
  172. package/scripts/flow-version-check.js +4 -12
  173. package/scripts/flow-webmcp-generator.js +3 -5
  174. package/scripts/flow-workflow-steps.js +0 -2
  175. package/scripts/flow-workflow.js +9 -11
  176. package/scripts/hooks/adapters/claude-code.js +31 -0
  177. package/scripts/hooks/core/config-change.js +1 -0
  178. package/scripts/hooks/core/extension-registry.js +0 -2
  179. package/scripts/hooks/core/instructions-loaded.js +1 -1
  180. package/scripts/hooks/core/observation-capture.js +5 -5
  181. package/scripts/hooks/core/phase-gate.js +5 -0
  182. package/scripts/hooks/core/post-compact.js +1 -12
  183. package/scripts/hooks/core/research-gate.js +2 -12
  184. package/scripts/hooks/core/routing-gate.js +6 -0
  185. package/scripts/hooks/core/task-completed.js +12 -0
  186. package/scripts/hooks/core/task-created.js +83 -0
  187. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  188. package/scripts/hooks/entry/claude-code/config-change.js +6 -29
  189. package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
  190. package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
  191. package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
  192. package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
  193. package/scripts/hooks/entry/claude-code/session-end.js +4 -28
  194. package/scripts/hooks/entry/claude-code/session-start.js +205 -243
  195. package/scripts/hooks/entry/claude-code/setup.js +8 -49
  196. package/scripts/hooks/entry/claude-code/stop.js +40 -72
  197. package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
  198. package/scripts/hooks/entry/claude-code/task-created.js +15 -0
  199. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
  200. package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
  201. package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
  202. package/scripts/hooks/entry/shared/hook-runner.js +99 -0
  203. package/scripts/hooks/entry/shared/read-stdin.js +0 -2
  204. package/scripts/postinstall.js +2 -0
  205. package/scripts/registries/api-registry.js +0 -2
  206. package/scripts/registries/component-registry.js +5 -9
  207. package/scripts/registries/contract-scanner.js +2 -9
  208. package/scripts/registries/function-registry.js +0 -2
  209. package/scripts/registries/schema-registry.js +14 -18
  210. package/scripts/registries/service-registry.js +23 -27
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
2
  /**
5
3
  * Template Engine - Extracted from flow-orchestrate.js
6
4
  *
@@ -11,7 +9,7 @@
11
9
 
12
10
  const fs = require('node:fs');
13
11
  const path = require('node:path');
14
- const { getProjectRoot, colors, getConfig } = require('./flow-utils');
12
+ const { getProjectRoot, colors, getConfig, PATHS } = require('./flow-utils');
15
13
  const {
16
14
  getVerbosityGuidance,
17
15
  loadPatterns,
@@ -19,8 +17,6 @@ const {
19
17
  loadRelatedCode
20
18
  } = require('./flow-instruction-richness');
21
19
 
22
- const PROJECT_ROOT = getProjectRoot();
23
-
24
20
  function log(color, ...args) {
25
21
  console.log(colors[color] + args.join(' ') + colors.reset);
26
22
  }
@@ -30,7 +26,7 @@ class TemplateEngine {
30
26
  this.templatesDir = templatesDir;
31
27
  this.cache = new Map();
32
28
  this.richness = null; // Instruction richness settings
33
- this.projectRoot = PROJECT_ROOT;
29
+ this.projectRoot = PATHS.root;
34
30
  this.projectContext = this.loadProjectContext();
35
31
  }
36
32
 
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
2
  /**
5
3
  * Validator - Extracted from flow-orchestrate.js
6
4
  *
@@ -11,11 +9,9 @@
11
9
  const fs = require('node:fs');
12
10
  const path = require('node:path');
13
11
  const { execFileSync } = require('node:child_process');
14
- const { getProjectRoot, colors } = require('./flow-utils');
12
+ const { getProjectRoot, colors, PATHS } = require('./flow-utils');
15
13
  const { getExecParts } = require('./flow-script-resolver');
16
14
 
17
- const PROJECT_ROOT = getProjectRoot();
18
-
19
15
  function log(color, ...args) {
20
16
  console.log(colors[color] + args.join(' ') + colors.reset);
21
17
  }
@@ -34,7 +30,7 @@ class Validator {
34
30
  * Essential for monorepos where tsconfig is in apps/web/, apps/api/, etc.
35
31
  */
36
32
  static findTsConfigDir(filePath) {
37
- if (!filePath) return PROJECT_ROOT;
33
+ if (!filePath) return PATHS.root;
38
34
 
39
35
  let dir = path.dirname(filePath);
40
36
  while (dir && dir !== path.dirname(dir)) { // Stop at filesystem root
@@ -52,7 +48,7 @@ class Validator {
52
48
  }
53
49
  dir = path.dirname(dir);
54
50
  }
55
- return PROJECT_ROOT;
51
+ return PATHS.root;
56
52
  }
57
53
 
58
54
  static typescriptCheck(filePath) {
@@ -67,8 +63,8 @@ class Validator {
67
63
  return { success: true, message: 'TypeScript check skipped (no tsconfig.json)' };
68
64
  }
69
65
 
70
- if (cwd !== PROJECT_ROOT) {
71
- log('dim', ` 📁 Running tsc from: ${path.relative(PROJECT_ROOT, cwd) || '.'}`);
66
+ if (cwd !== PATHS.root) {
67
+ log('dim', ` 📁 Running tsc from: ${path.relative(PATHS.root, cwd) || '.'}`);
72
68
  }
73
69
 
74
70
  // Use execFileSync with array args for safety
@@ -59,7 +59,7 @@ const {
59
59
  } = require('./flow-export-scanner');
60
60
 
61
61
  // Import utilities for consistent project root, colors, and config
62
- const { getProjectRoot, colors, getConfig, writeJson, estimateTokens, error } = require('./flow-utils');
62
+ const { getProjectRoot, colors, getConfig, writeJson, estimateTokens, error, PATHS } = require('./flow-utils');
63
63
  const { getPromptAdjustments, recordModelResult } = require('./flow-model-adapter');
64
64
 
65
65
  // Import provider infrastructure for cloud executors
@@ -121,13 +121,9 @@ const {
121
121
  // Configuration
122
122
  // ============================================================
123
123
 
124
- const PROJECT_ROOT = getProjectRoot();
125
-
126
124
  // Set export scanner project root to match orchestrator's
127
- setExportScannerRoot(PROJECT_ROOT);
128
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
129
- const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
130
- const TEMPLATES_DIR = path.join(PROJECT_ROOT, 'templates', 'hybrid');
125
+ setExportScannerRoot(PATHS.root);
126
+ const TEMPLATES_DIR = path.join(PATHS.root, 'templates', 'hybrid');
131
127
 
132
128
  function log(color, ...args) {
133
129
  console.log(colors[color] + args.join(' ') + colors.reset);
@@ -142,7 +138,7 @@ function log(color, ...args) {
142
138
  * This helps the AI understand what failed and how to fix it
143
139
  */
144
140
  function saveStructuredFailure(step, errorHistory, attempts, config) {
145
- const failurePath = path.join(STATE_DIR, 'last-failure.json');
141
+ const failurePath = path.join(PATHS.state, 'last-failure.json');
146
142
 
147
143
  const failureInfo = {
148
144
  timestamp: new Date().toISOString(),
@@ -183,7 +179,7 @@ function generateFixSuggestion(errorHistory) {
183
179
  const errorCounts = {};
184
180
 
185
181
  for (const e of errorHistory) {
186
- errorCounts[e.category] = (errorCounts[e.category] || 0) + 1;
182
+ errorCounts[e.category] = (errorCounts[e.category] ?? 0) + 1;
187
183
  }
188
184
 
189
185
  const mostCommon = Object.entries(errorCounts)
@@ -206,7 +202,7 @@ function generateFixSuggestion(errorHistory) {
206
202
 
207
203
  function loadHybridConfig() {
208
204
  const config = getConfig();
209
- const hybrid = config.hybrid || {};
205
+ const hybrid = config.hybrid ?? {};
210
206
 
211
207
  if (!hybrid.enabled) {
212
208
  throw new Error('Hybrid mode is not enabled. Run /wogi-hybrid first.');
@@ -217,9 +213,9 @@ function loadHybridConfig() {
217
213
  const isLocal = executorConfig.type !== 'cloud';
218
214
 
219
215
  // Context window: config override > executor config > auto-detect later
220
- const contextWindow = hybrid.executor?.contextWindow ||
221
- hybrid.settings?.contextWindow ||
222
- executorConfig.contextWindow ||
216
+ const contextWindow = hybrid.executor?.contextWindow ??
217
+ hybrid.settings?.contextWindow ??
218
+ executorConfig.contextWindow ??
223
219
  null;
224
220
 
225
221
  // For local LLMs: use full context (they're free!)
@@ -272,10 +268,10 @@ function loadHybridConfig() {
272
268
  outputReserveMax,
273
269
 
274
270
  // Instruction richness settings
275
- instructionRichness: hybrid.settings?.instructionRichness || {},
271
+ instructionRichness: hybrid.settings?.instructionRichness ?? {},
276
272
 
277
273
  // Cloud provider reference (for model selection in setup wizard)
278
- cloudProviders: hybrid.cloudProviders || config.hybrid?.cloudProviders || {}
274
+ cloudProviders: hybrid.cloudProviders ?? config.hybrid?.cloudProviders ?? {}
279
275
  };
280
276
  }
281
277
 
@@ -293,7 +289,7 @@ function loadHybridConfig() {
293
289
  function getProjectContext() {
294
290
  try {
295
291
  const config = getConfig();
296
- return config.hybrid?.projectContext || {};
292
+ return config.hybrid?.projectContext ?? {};
297
293
  } catch (err) {
298
294
  return {};
299
295
  }
@@ -312,7 +308,7 @@ function autoCorrectCode(code, filePath, projectConfig = null) {
312
308
  }
313
309
 
314
310
  // Load project context from config if not provided
315
- const ctx = projectConfig?.projectContext || getProjectContext();
311
+ const ctx = projectConfig?.projectContext ?? getProjectContext();
316
312
 
317
313
  let corrected = code;
318
314
  const corrections = [];
@@ -343,7 +339,7 @@ function autoCorrectCode(code, filePath, projectConfig = null) {
343
339
  }
344
340
 
345
341
  // 2. Fix component paths based on config mappings
346
- const componentPaths = ctx.componentPaths || {};
342
+ const componentPaths = ctx.componentPaths ?? {};
347
343
 
348
344
  // Build reverse mapping from shadcn-style to project paths
349
345
  // @/components/ui/button → project's Button path
@@ -430,7 +426,7 @@ function autoCorrectCode(code, filePath, projectConfig = null) {
430
426
  * @param {string} projectRoot - Root directory of the project
431
427
  * @returns {string} - Framework name: 'styled-components', 'shadcn', 'mui', 'chakra', 'antd', or 'react'
432
428
  */
433
- function detectUIFramework(projectRoot = PROJECT_ROOT) {
429
+ function detectUIFramework(projectRoot = PATHS.root) {
434
430
  try {
435
431
  const pkgJsonPath = path.join(projectRoot, 'package.json');
436
432
  const pkgJson = readJson(pkgJsonPath, null);
@@ -459,7 +455,7 @@ function detectUIFramework(projectRoot = PROJECT_ROOT) {
459
455
  * @param {string[]} componentDirs - Directories to scan (relative to projectRoot)
460
456
  * @returns {Object} - Mapping of ComponentName → import path
461
457
  */
462
- function scanComponentPaths(projectRoot = PROJECT_ROOT, componentDirs = ['src/components']) {
458
+ function scanComponentPaths(projectRoot = PATHS.root, componentDirs = ['src/components']) {
463
459
  const componentPaths = {};
464
460
 
465
461
  for (const dir of componentDirs) {
@@ -527,7 +523,7 @@ function scanComponentPaths(projectRoot = PROJECT_ROOT, componentDirs = ['src/co
527
523
  * @param {string} projectRoot - Root directory of the project
528
524
  * @returns {Object} - projectContext configuration
529
525
  */
530
- function generateProjectContext(projectRoot = PROJECT_ROOT) {
526
+ function generateProjectContext(projectRoot = PATHS.root) {
531
527
  const uiFramework = detectUIFramework(projectRoot);
532
528
 
533
529
  // Scan standard component directories
@@ -588,7 +584,7 @@ function logTokenMetrics(plan, executionResult, complexity) {
588
584
 
589
585
  if (!logMetrics) return;
590
586
 
591
- const metricsPath = path.join(STATE_DIR, 'hybrid-metrics.json');
587
+ const metricsPath = path.join(PATHS.state, 'hybrid-metrics.json');
592
588
 
593
589
  // Load existing metrics or create new array
594
590
  const metrics = readJson(metricsPath, []);
@@ -599,16 +595,16 @@ function logTokenMetrics(plan, executionResult, complexity) {
599
595
  planId: plan.planId || 'unknown',
600
596
  task: plan.task || 'unknown',
601
597
  complexity: {
602
- level: complexity?.level || 'unknown',
603
- estimatedTokens: complexity?.estimatedTokens || 0,
604
- reasoning: complexity?.reasoning || ''
598
+ level: complexity?.level ?? 'unknown',
599
+ estimatedTokens: complexity?.estimatedTokens ?? 0,
600
+ reasoning: complexity?.reasoning ?? ''
605
601
  },
606
602
  execution: {
607
603
  success: executionResult.success,
608
- stepsCompleted: executionResult.steps?.filter(s => s.success).length || 0,
609
- stepsTotal: executionResult.steps?.length || 0,
604
+ stepsCompleted: executionResult.steps?.filter(s => s.success).length ?? 0,
605
+ stepsTotal: executionResult.steps?.length ?? 0,
610
606
  escalated: executionResult.escalateToCloud?.length > 0,
611
- escalatedSteps: executionResult.escalateToCloud?.map(s => s.id) || []
607
+ escalatedSteps: executionResult.escalateToCloud?.map(s => s.id) ?? []
612
608
  }
613
609
  };
614
610
 
@@ -794,6 +790,45 @@ const compactionStrategies = {
794
790
  return trimmed;
795
791
  },
796
792
 
793
+ /**
794
+ * Truncate code blocks (``` markers) to maxLines per block
795
+ */
796
+ truncateCodeBlocks(text, maxLines = 100) {
797
+ const codeBlockRegex = /```[\s\S]*?```/g;
798
+ return text.replace(codeBlockRegex, (match) => {
799
+ const content = match.slice(3, -3); // Remove ``` markers
800
+ if (content.split('\n').length > maxLines) {
801
+ const truncated = compactionStrategies.truncateFileContent(content, maxLines);
802
+ return '```' + truncated + '```';
803
+ }
804
+ return match;
805
+ });
806
+ },
807
+
808
+ /**
809
+ * Truncate {{currentContent}} template blocks exceeding charLimit
810
+ */
811
+ truncateCurrentContent(text, charLimit = 5000, maxLines = 150) {
812
+ const currentContentMatch = text.match(/{{currentContent}}[\s\S]*?(?=##|$)/);
813
+ if (currentContentMatch && currentContentMatch[0].length > charLimit) {
814
+ const lines = currentContentMatch[0].split('\n');
815
+ const truncated = compactionStrategies.truncateFileContent(lines.slice(1).join('\n'), maxLines);
816
+ return text.replace(currentContentMatch[0], '{{currentContent}}\n' + truncated + '\n\n');
817
+ }
818
+ return text;
819
+ },
820
+
821
+ /**
822
+ * Aggressive last-resort truncation to fit within token budget
823
+ */
824
+ aggressiveTruncate(text, availableTokens, estimateTokensFn) {
825
+ const tokens = estimateTokensFn(text);
826
+ if (tokens <= availableTokens) return text;
827
+ const ratio = availableTokens / tokens;
828
+ const targetLength = Math.floor(text.length * ratio * 0.9); // 10% safety margin
829
+ return text.slice(0, targetLength) + '\n\n[Content truncated to fit context window]';
830
+ },
831
+
797
832
  /**
798
833
  * Truncate search results array to prevent context overflow
799
834
  * @param {Array} results - Array of search results with optional content
@@ -833,99 +868,84 @@ const compactionStrategies = {
833
868
  };
834
869
 
835
870
  /**
836
- * Auto-compacts a prompt to fit within context window.
837
- * Returns { prompt, wasCompacted, originalTokens, finalTokens }
871
+ * Calculates available token budget after reserving output tokens.
872
+ * Caps reserve at 50% of context window to prevent zero-budget bugs.
838
873
  */
839
- function autoCompactPrompt(prompt, contextWindow, reserveForOutput = 2048) {
840
- // Sanity check: never reserve more than 50% of context window
841
- // This prevents the bug where maxTokens == contextWindow causing availableTokens = 0
874
+ function calcAvailableTokens(contextWindow, reserveForOutput) {
842
875
  const maxReserve = Math.floor(contextWindow / 2);
843
876
  if (reserveForOutput > maxReserve) {
844
877
  log('dim', ` 📊 Capping output reserve from ${reserveForOutput} to ${maxReserve} tokens`);
845
878
  reserveForOutput = maxReserve;
846
879
  }
880
+ const available = contextWindow - reserveForOutput;
881
+ if (available < 1024) {
882
+ log('yellow', ` ⚠️ Warning: Very low available tokens (${available}). Context: ${contextWindow}, Reserve: ${reserveForOutput}`);
883
+ }
884
+ return available;
885
+ }
847
886
 
848
- const availableTokens = contextWindow - reserveForOutput;
887
+ /**
888
+ * Builds a compaction result object.
889
+ */
890
+ function compactionResult(prompt, wasCompacted, originalTokens, contextWindow) {
891
+ const finalTokens = estimateTokens(prompt);
892
+ return {
893
+ prompt,
894
+ wasCompacted,
895
+ originalTokens,
896
+ finalTokens,
897
+ usage: getContextUsage(finalTokens, contextWindow)
898
+ };
899
+ }
849
900
 
850
- // Another sanity check: ensure we have at least 1024 tokens for the prompt
851
- if (availableTokens < 1024) {
852
- log('yellow', ` ⚠️ Warning: Very low available tokens (${availableTokens}). Context: ${contextWindow}, Reserve: ${reserveForOutput}`);
853
- }
901
+ /**
902
+ * Ordered compaction pipeline — each step is tried in sequence.
903
+ * Each entry: { name, apply(text) text, logLabel }
904
+ */
905
+ const COMPACTION_PIPELINE = [
906
+ { name: 'trimRetryErrors', apply: (t) => compactionStrategies.trimRetryErrors(t), logLabel: 'Trimmed retry errors' },
907
+ { name: 'trimTemplateVerbosity', apply: (t) => compactionStrategies.trimTemplateVerbosity(t), logLabel: 'Trimmed template verbosity' },
908
+ { name: 'truncateCodeBlocks', apply: (t) => compactionStrategies.truncateCodeBlocks(t, 100), logLabel: 'Truncated code blocks' },
909
+ { name: 'truncateCurrentContent', apply: (t) => compactionStrategies.truncateCurrentContent(t), logLabel: 'Truncated currentContent blocks' },
910
+ ];
854
911
 
912
+ /**
913
+ * Auto-compacts a prompt to fit within context window.
914
+ * Applies strategies in order: retry errors, template verbosity,
915
+ * code block truncation, currentContent truncation, aggressive truncation.
916
+ * Returns { prompt, wasCompacted, originalTokens, finalTokens, usage }
917
+ */
918
+ function autoCompactPrompt(prompt, contextWindow, reserveForOutput = 2048) {
919
+ const availableTokens = calcAvailableTokens(contextWindow, reserveForOutput);
855
920
  const originalTokens = estimateTokens(prompt);
856
921
 
857
922
  if (originalTokens <= availableTokens) {
858
- return {
859
- prompt,
860
- wasCompacted: false,
861
- originalTokens,
862
- finalTokens: originalTokens,
863
- usage: getContextUsage(originalTokens, contextWindow)
864
- };
923
+ return compactionResult(prompt, false, originalTokens, contextWindow);
865
924
  }
866
925
 
867
926
  log('yellow', ` ⚠️ Prompt too large (${originalTokens.toLocaleString()} tokens), compacting...`);
868
927
 
869
928
  let compacted = prompt;
870
929
 
871
- // Strategy 1: Trim retry errors
872
- compacted = compactionStrategies.trimRetryErrors(compacted);
873
- let tokens = estimateTokens(compacted);
874
- if (tokens <= availableTokens) {
875
- log('dim', ` 📦 Trimmed retry errors: ${tokens.toLocaleString()} tokens`);
876
- return { prompt: compacted, wasCompacted: true, originalTokens, finalTokens: tokens, usage: getContextUsage(tokens, contextWindow) };
877
- }
878
-
879
- // Strategy 2: Trim template verbosity
880
- compacted = compactionStrategies.trimTemplateVerbosity(compacted);
881
- tokens = estimateTokens(compacted);
882
- if (tokens <= availableTokens) {
883
- log('dim', ` 📦 Trimmed template verbosity: ${tokens.toLocaleString()} tokens`);
884
- return { prompt: compacted, wasCompacted: true, originalTokens, finalTokens: tokens, usage: getContextUsage(tokens, contextWindow) };
885
- }
886
-
887
- // Strategy 3: Truncate file content in the prompt
888
- // Find content between ``` markers and truncate
889
- const codeBlockRegex = /```[\s\S]*?```/g;
890
- compacted = compacted.replace(codeBlockRegex, (match) => {
891
- const content = match.slice(3, -3); // Remove ``` markers
892
- if (content.split('\n').length > 100) {
893
- const truncated = compactionStrategies.truncateFileContent(content, 100);
894
- return '```' + truncated + '```';
930
+ // Apply pipeline strategies in order, exit early if prompt fits
931
+ for (const step of COMPACTION_PIPELINE) {
932
+ compacted = step.apply(compacted);
933
+ const tokens = estimateTokens(compacted);
934
+ if (tokens <= availableTokens) {
935
+ log('dim', ` 📦 ${step.logLabel}: ${tokens.toLocaleString()} tokens`);
936
+ return compactionResult(compacted, true, originalTokens, contextWindow);
895
937
  }
896
- return match;
897
- });
898
-
899
- // Also check for {{currentContent}} style blocks
900
- const currentContentMatch = compacted.match(/{{currentContent}}[\s\S]*?(?=##|$)/);
901
- if (currentContentMatch && currentContentMatch[0].length > 5000) {
902
- const lines = currentContentMatch[0].split('\n');
903
- const truncated = compactionStrategies.truncateFileContent(lines.slice(1).join('\n'), 150);
904
- compacted = compacted.replace(currentContentMatch[0], '{{currentContent}}\n' + truncated + '\n\n');
905
938
  }
906
939
 
907
- tokens = estimateTokens(compacted);
908
- log('dim', ` 📦 Truncated file content: ${tokens.toLocaleString()} tokens`);
940
+ log('dim', ` 📦 Truncated file content: ${estimateTokens(compacted).toLocaleString()} tokens`);
909
941
 
910
- // If still too large, do aggressive truncation
911
- if (tokens > availableTokens) {
912
- const ratio = availableTokens / tokens;
913
- const targetLength = Math.floor(compacted.length * ratio * 0.9); // 10% safety margin
914
- compacted = compacted.slice(0, targetLength) + '\n\n[Content truncated to fit context window]';
915
- tokens = estimateTokens(compacted);
916
- log('yellow', ` ⚠️ Aggressive truncation: ${tokens.toLocaleString()} tokens`);
917
- }
942
+ // Last resort: aggressive truncation
943
+ compacted = compactionStrategies.aggressiveTruncate(compacted, availableTokens, estimateTokens);
944
+ log('yellow', ` ⚠️ Aggressive truncation: ${estimateTokens(compacted).toLocaleString()} tokens`);
918
945
 
919
- return {
920
- prompt: compacted,
921
- wasCompacted: true,
922
- originalTokens,
923
- finalTokens: tokens,
924
- usage: getContextUsage(tokens, contextWindow)
925
- };
946
+ return compactionResult(compacted, true, originalTokens, contextWindow);
926
947
  }
927
948
 
928
-
929
949
  // TemplateEngine extracted to ./flow-orchestrate-templates.js
930
950
  // Validator extracted to ./flow-orchestrate-validator.js
931
951
  // RollbackManager extracted to ./flow-orchestrate-rollback.js
@@ -946,7 +966,7 @@ class Orchestrator {
946
966
  this.completedSteps = new Set();
947
967
 
948
968
  // Project context generator - generates once, reuses for all steps
949
- this.contextGenerator = new ProjectContextGenerator(PROJECT_ROOT);
969
+ this.contextGenerator = new ProjectContextGenerator(PATHS.root);
950
970
  this.projectContext = null;
951
971
 
952
972
  // Complexity assessment for the current plan
@@ -1161,7 +1181,7 @@ class Orchestrator {
1161
1181
 
1162
1182
  // Load model profile for intelligent context loading
1163
1183
  const modelProfile = getModelProfile(this.config.model, taskType);
1164
- const profileRichness = getProfileBasedRichness(this.config.model, taskType, this.config.maxTokens || 8192);
1184
+ const profileRichness = getProfileBasedRichness(this.config.model, taskType, this.config.maxTokens ?? 8192);
1165
1185
 
1166
1186
  // Store for use during retries
1167
1187
  result.taskType = taskType;
@@ -1199,7 +1219,7 @@ class Orchestrator {
1199
1219
  file: step.params?.path || step.file || '',
1200
1220
  action: step.action || templateName
1201
1221
  };
1202
- prompt = injectPatterns(prompt, taskContext, PROJECT_ROOT);
1222
+ prompt = injectPatterns(prompt, taskContext, PATHS.root);
1203
1223
 
1204
1224
  // PREPEND PROJECT CONTEXT - Local LLM tokens are FREE
1205
1225
  // This gives the LLM comprehensive knowledge about types, theme, patterns
@@ -1279,10 +1299,10 @@ class Orchestrator {
1279
1299
  try {
1280
1300
  // Auto-compact prompt if needed
1281
1301
  // Use config override, or LLM's detected context window, or conservative fallback
1282
- const contextWindow = this.config.contextWindow || this.llm.contextWindow || 4096;
1302
+ const contextWindow = this.config.contextWindow ?? this.llm.contextWindow ?? 4096;
1283
1303
  // Reserve configurable % of context for output, with configurable max
1284
- const reserveRatio = this.config.outputReserveRatio || 0.3;
1285
- const reserveMax = this.config.outputReserveMax || 4096;
1304
+ const reserveRatio = this.config.outputReserveRatio ?? 0.3;
1305
+ const reserveMax = this.config.outputReserveMax ?? 4096;
1286
1306
  const reserveForOutput = Math.min(reserveMax, Math.floor(contextWindow * reserveRatio));
1287
1307
  const { prompt: compactedPrompt, wasCompacted, usage } = autoCompactPrompt(
1288
1308
  prompt,
@@ -1657,7 +1677,10 @@ if (process.env.NODE_ENV === 'test') {
1657
1677
  module.exports._test = {
1658
1678
  autoCompactPrompt,
1659
1679
  getContextUsage,
1660
- compactionStrategies
1680
+ compactionStrategies,
1681
+ calcAvailableTokens,
1682
+ compactionResult,
1683
+ COMPACTION_PIPELINE
1661
1684
  };
1662
1685
  } else {
1663
1686
  main().catch(err => {
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Wogi Flow - Output Utilities
5
3
  *
@@ -36,7 +36,7 @@
36
36
 
37
37
  const fs = require('node:fs');
38
38
  const path = require('node:path');
39
- const { getProjectRoot, getConfig, readJson, info } = require('./flow-utils');
39
+ const { getProjectRoot, getConfig, readJson, info, PATHS } = require('./flow-utils');
40
40
 
41
41
  // ============================================================
42
42
  // Configuration (uses centralized getConfig from flow-utils)
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Wogi Flow - Path Constants and Utilities
5
3
  *
@@ -152,6 +150,29 @@ const PATHS = {
152
150
  modelsDir: path.join(WORKFLOW_DIR, 'models'),
153
151
  modelRegistry: path.join(WORKFLOW_DIR, 'models', 'registry.json'),
154
152
  modelStats: path.join(WORKFLOW_DIR, 'models', 'stats.json'),
153
+ modelStatsArchive: path.join(WORKFLOW_DIR, 'models', 'stats-archive'),
154
+ modelCapabilities: path.join(WORKFLOW_DIR, 'models', 'capabilities'),
155
+ communityScores: path.join(WORKFLOW_DIR, 'models', 'community-scores.json'),
156
+ communityRouting: path.join(WORKFLOW_DIR, 'models', 'community-routing.json'),
157
+ // Additional workflow directories (v1.9.8)
158
+ verifications: path.join(WORKFLOW_DIR, 'verifications'),
159
+ logs: path.join(WORKFLOW_DIR, 'logs'),
160
+ bridges: path.join(WORKFLOW_DIR, 'bridges'),
161
+ evals: path.join(WORKFLOW_DIR, 'evals'),
162
+ reviews: path.join(WORKFLOW_DIR, 'reviews'),
163
+ completed: path.join(WORKFLOW_DIR, 'completed'),
164
+ failureLearnings: path.join(WORKFLOW_DIR, 'failure-learnings'),
165
+ templates: path.join(WORKFLOW_DIR, 'templates'),
166
+ templatesExtracted: path.join(WORKFLOW_DIR, 'templates', 'extracted'),
167
+ templatesPrompts: path.join(WORKFLOW_DIR, 'templates', 'prompts'),
168
+ promptsFragments: path.join(WORKFLOW_DIR, 'prompts', 'fragments'),
169
+ promptsComposed: path.join(WORKFLOW_DIR, 'prompts', 'composed'),
170
+ testsGenerated: path.join(WORKFLOW_DIR, 'tests', 'generated'),
171
+ testsApiFixtures: path.join(WORKFLOW_DIR, 'tests', 'api-fixtures.json'),
172
+ longInputTmp: path.join(WORKFLOW_DIR, 'tmp', 'long-input'),
173
+ // State files (v1.9.8)
174
+ roadmap: path.join(STATE_DIR, 'roadmap.md'),
175
+ memoryDb: path.join(WORKFLOW_DIR, 'memory', 'local.db'),
155
176
  };
156
177
 
157
178
  // ============================================================