wogiflow 2.4.2 → 2.4.3

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 (196) hide show
  1. package/.claude/docs/claude-code-compatibility.md +27 -0
  2. package/.claude/settings.json +1 -1
  3. package/.workflow/models/registry.json +1 -1
  4. package/package.json +4 -4
  5. package/scripts/MEMORY-ARCHITECTURE.md +1 -1
  6. package/scripts/base-workflow-step.js +136 -0
  7. package/scripts/flow-adaptive-learning.js +8 -9
  8. package/scripts/flow-aggregate.js +11 -6
  9. package/scripts/flow-api-index.js +4 -6
  10. package/scripts/flow-assumption-detector.js +0 -2
  11. package/scripts/flow-audit.js +15 -2
  12. package/scripts/flow-auto-context.js +8 -12
  13. package/scripts/flow-auto-learn.js +49 -49
  14. package/scripts/flow-background.js +5 -6
  15. package/scripts/flow-bridge-state.js +8 -10
  16. package/scripts/flow-bulk-loop.js +1 -3
  17. package/scripts/flow-bulk-orchestrator.js +1 -3
  18. package/scripts/flow-cascade-completion.js +0 -2
  19. package/scripts/flow-cascade.js +4 -4
  20. package/scripts/flow-checkpoint.js +10 -13
  21. package/scripts/flow-code-intelligence.js +10 -12
  22. package/scripts/flow-community-sync.js +4 -4
  23. package/scripts/flow-community.js +12 -20
  24. package/scripts/flow-config-defaults.js +0 -2
  25. package/scripts/flow-config-interactive.js +9 -5
  26. package/scripts/flow-config-loader.js +49 -92
  27. package/scripts/flow-config-substitution.js +0 -2
  28. package/scripts/flow-context-estimator.js +4 -4
  29. package/scripts/flow-context-init.js +10 -12
  30. package/scripts/flow-context-manager.js +0 -2
  31. package/scripts/flow-context-scoring.js +2 -2
  32. package/scripts/flow-contract-scan.js +6 -9
  33. package/scripts/flow-correct.js +29 -27
  34. package/scripts/flow-correction-detector.js +5 -1
  35. package/scripts/flow-damage-control.js +47 -54
  36. package/scripts/flow-decisions-merge.js +4 -14
  37. package/scripts/flow-diff.js +5 -8
  38. package/scripts/flow-done-gates.js +786 -0
  39. package/scripts/flow-done-report.js +123 -0
  40. package/scripts/flow-done.js +71 -717
  41. package/scripts/flow-entropy-monitor.js +1 -3
  42. package/scripts/flow-eval.js +5 -5
  43. package/scripts/flow-extraction-review.js +1 -0
  44. package/scripts/flow-failure-categories.js +0 -2
  45. package/scripts/flow-figma-confirm.js +5 -9
  46. package/scripts/flow-figma-generate.js +8 -10
  47. package/scripts/flow-figma-index.js +8 -10
  48. package/scripts/flow-figma-match.js +3 -5
  49. package/scripts/flow-figma-mcp-server.js +2 -4
  50. package/scripts/flow-figma-orchestrator.js +2 -3
  51. package/scripts/flow-figma-registry.js +2 -3
  52. package/scripts/flow-framework-resolver.js +0 -2
  53. package/scripts/flow-function-index.js +4 -6
  54. package/scripts/flow-gate-confidence.js +2 -2
  55. package/scripts/flow-gitignore.js +0 -2
  56. package/scripts/flow-guided-edit.js +5 -6
  57. package/scripts/flow-health.js +5 -6
  58. package/scripts/flow-hook-errors.js +6 -0
  59. package/scripts/flow-hook-status.js +263 -0
  60. package/scripts/flow-hooks.js +17 -29
  61. package/scripts/flow-http-client.js +9 -8
  62. package/scripts/flow-hybrid-interactive.js +7 -12
  63. package/scripts/flow-hybrid-test.js +12 -13
  64. package/scripts/flow-instruction-richness.js +1 -1
  65. package/scripts/flow-io.js +21 -4
  66. package/scripts/flow-knowledge-router.js +9 -3
  67. package/scripts/flow-learning-orchestrator.js +318 -13
  68. package/scripts/flow-links.js +5 -7
  69. package/scripts/flow-long-input-association.js +275 -0
  70. package/scripts/flow-long-input-chunking.js +1 -0
  71. package/scripts/flow-long-input-cli.js +0 -2
  72. package/scripts/flow-long-input-complexity.js +0 -2
  73. package/scripts/flow-long-input-constants.js +0 -2
  74. package/scripts/flow-long-input-contradictions.js +351 -0
  75. package/scripts/flow-long-input-detection.js +0 -2
  76. package/scripts/flow-long-input-passes.js +885 -0
  77. package/scripts/flow-long-input-stories.js +1 -1
  78. package/scripts/flow-long-input-voice.js +0 -2
  79. package/scripts/flow-long-input.js +425 -3005
  80. package/scripts/flow-loop-retry-learning.js +2 -3
  81. package/scripts/flow-lsp.js +3 -3
  82. package/scripts/flow-mcp-docs.js +3 -4
  83. package/scripts/flow-memory-db.js +6 -8
  84. package/scripts/flow-memory-sync.js +18 -11
  85. package/scripts/flow-metrics.js +1 -2
  86. package/scripts/flow-model-adapter.js +2 -3
  87. package/scripts/flow-model-config.js +72 -104
  88. package/scripts/flow-model-router.js +2 -2
  89. package/scripts/flow-model-types.js +0 -2
  90. package/scripts/flow-multi-approach.js +5 -6
  91. package/scripts/flow-orchestrate-context.js +3 -7
  92. package/scripts/flow-orchestrate-rollback.js +3 -8
  93. package/scripts/flow-orchestrate-state.js +8 -14
  94. package/scripts/flow-orchestrate-templates.js +2 -6
  95. package/scripts/flow-orchestrate-validator.js +5 -9
  96. package/scripts/flow-orchestrate.js +126 -103
  97. package/scripts/flow-output.js +0 -2
  98. package/scripts/flow-parallel.js +1 -1
  99. package/scripts/flow-paths.js +23 -2
  100. package/scripts/flow-pattern-enforcer.js +30 -28
  101. package/scripts/flow-pattern-extractor.js +3 -4
  102. package/scripts/flow-pending.js +0 -2
  103. package/scripts/flow-permissions.js +2 -3
  104. package/scripts/flow-plugin-registry.js +10 -12
  105. package/scripts/flow-prd-manager.js +1 -1
  106. package/scripts/flow-progress.js +7 -9
  107. package/scripts/flow-prompt-composer.js +3 -3
  108. package/scripts/flow-prompt-template.js +2 -2
  109. package/scripts/flow-providers.js +7 -4
  110. package/scripts/flow-registry-manager.js +7 -12
  111. package/scripts/flow-regression.js +9 -11
  112. package/scripts/flow-roadmap.js +2 -2
  113. package/scripts/flow-run-trace.js +16 -15
  114. package/scripts/flow-safety.js +2 -5
  115. package/scripts/flow-scanner-base.js +5 -7
  116. package/scripts/flow-scenario-engine.js +1 -5
  117. package/scripts/flow-security.js +29 -0
  118. package/scripts/flow-session-end.js +32 -41
  119. package/scripts/flow-session-learning.js +53 -49
  120. package/scripts/flow-setup-hooks.js +2 -3
  121. package/scripts/flow-skill-create.js +7 -12
  122. package/scripts/flow-skill-generator.js +12 -16
  123. package/scripts/flow-skill-learn.js +17 -8
  124. package/scripts/flow-skill-matcher.js +1 -2
  125. package/scripts/flow-spec-generator.js +2 -4
  126. package/scripts/flow-stack-wizard.js +5 -7
  127. package/scripts/flow-standards-learner.js +35 -16
  128. package/scripts/flow-start.js +2 -0
  129. package/scripts/flow-stats-collector.js +2 -2
  130. package/scripts/flow-status.js +10 -10
  131. package/scripts/flow-statusline-setup.js +2 -2
  132. package/scripts/flow-step-changelog.js +2 -3
  133. package/scripts/flow-step-comments.js +66 -81
  134. package/scripts/flow-step-complexity.js +50 -70
  135. package/scripts/flow-step-coverage.js +3 -5
  136. package/scripts/flow-step-knowledge.js +2 -3
  137. package/scripts/flow-step-pr-tests.js +64 -74
  138. package/scripts/flow-step-regression.js +3 -5
  139. package/scripts/flow-step-review.js +86 -103
  140. package/scripts/flow-step-security.js +111 -121
  141. package/scripts/flow-step-silent-failures.js +56 -83
  142. package/scripts/flow-step-simplifier.js +52 -70
  143. package/scripts/flow-story.js +4 -7
  144. package/scripts/flow-strict-adherence.js +3 -4
  145. package/scripts/flow-task-checkpoint.js +36 -5
  146. package/scripts/flow-task-enforcer.js +2 -24
  147. package/scripts/flow-tech-debt.js +1 -1
  148. package/scripts/flow-template-extractor.js +1 -0
  149. package/scripts/flow-templates.js +11 -13
  150. package/scripts/flow-test-api.js +9 -13
  151. package/scripts/flow-test-discovery.js +1 -1
  152. package/scripts/flow-test-generate.js +5 -9
  153. package/scripts/flow-test-integrity.js +3 -7
  154. package/scripts/flow-test-ui.js +5 -9
  155. package/scripts/flow-testing-deps.js +1 -3
  156. package/scripts/flow-tiered-learning.js +4 -4
  157. package/scripts/flow-todowrite-sync.js +1 -1
  158. package/scripts/flow-tokens.js +0 -2
  159. package/scripts/flow-verification-profile.js +6 -10
  160. package/scripts/flow-verify.js +12 -16
  161. package/scripts/flow-version-check.js +4 -12
  162. package/scripts/flow-webmcp-generator.js +3 -5
  163. package/scripts/flow-workflow-steps.js +0 -2
  164. package/scripts/flow-workflow.js +9 -11
  165. package/scripts/hooks/adapters/claude-code.js +2 -0
  166. package/scripts/hooks/core/config-change.js +1 -0
  167. package/scripts/hooks/core/extension-registry.js +0 -2
  168. package/scripts/hooks/core/instructions-loaded.js +1 -1
  169. package/scripts/hooks/core/observation-capture.js +5 -5
  170. package/scripts/hooks/core/phase-gate.js +5 -0
  171. package/scripts/hooks/core/post-compact.js +1 -12
  172. package/scripts/hooks/core/research-gate.js +2 -12
  173. package/scripts/hooks/core/routing-gate.js +6 -0
  174. package/scripts/hooks/core/task-completed.js +12 -0
  175. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  176. package/scripts/hooks/entry/claude-code/config-change.js +6 -29
  177. package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
  178. package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
  179. package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
  180. package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
  181. package/scripts/hooks/entry/claude-code/session-end.js +4 -28
  182. package/scripts/hooks/entry/claude-code/session-start.js +205 -243
  183. package/scripts/hooks/entry/claude-code/setup.js +8 -49
  184. package/scripts/hooks/entry/claude-code/stop.js +40 -72
  185. package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
  186. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
  187. package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
  188. package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
  189. package/scripts/hooks/entry/shared/hook-runner.js +99 -0
  190. package/scripts/hooks/entry/shared/read-stdin.js +0 -2
  191. package/scripts/registries/api-registry.js +0 -2
  192. package/scripts/registries/component-registry.js +5 -9
  193. package/scripts/registries/contract-scanner.js +2 -9
  194. package/scripts/registries/function-registry.js +0 -2
  195. package/scripts/registries/schema-registry.js +14 -18
  196. package/scripts/registries/service-registry.js +23 -27
@@ -15,11 +15,10 @@
15
15
 
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
- const { getConfig, getProjectRoot, colors, readJson, info, success } = require('./flow-utils');
18
+ const { getConfig, getProjectRoot, colors, readJson, info, success, PATHS } = require('./flow-utils');
19
19
  const { FailureCategory, detectCategory } = require('./flow-failure-categories');
20
20
 
21
- const PROJECT_ROOT = getProjectRoot();
22
- const LEARNING_LOG_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'adaptive-learning.json');
21
+ const LEARNING_LOG_PATH = path.join(PATHS.state, 'adaptive-learning.json');
23
22
 
24
23
  // ============================================================================
25
24
  // Root Cause Categories
@@ -327,7 +327,7 @@ class LSPClient {
327
327
  });
328
328
 
329
329
  // Wait a bit for the server to process
330
- await new Promise(r => setTimeout(r, 100));
330
+ await require('node:timers/promises').setTimeout(100);
331
331
 
332
332
  return uri;
333
333
  }
@@ -419,7 +419,7 @@ class LSPClient {
419
419
  const uri = await this.openDocument(filePath);
420
420
 
421
421
  // Wait for diagnostics to be pushed
422
- await new Promise(r => setTimeout(r, 500));
422
+ await require('node:timers/promises').setTimeout(500);
423
423
 
424
424
  const diagnostics = this.diagnosticsCache.get(uri) || [];
425
425
 
@@ -916,7 +916,7 @@ module.exports = {
916
916
  };
917
917
 
918
918
  if (require.main === module) {
919
- main().catch(e => {
919
+ main().catch(err => {
920
920
  error(err.message);
921
921
  process.exit(1);
922
922
  });
@@ -27,9 +27,8 @@ const { color, printHeader, printSection, success } = require('./flow-output');;
27
27
  // Configuration
28
28
  // ============================================================
29
29
 
30
- const PROJECT_ROOT = getProjectRoot();
31
- const MCP_DOCS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'mcp-tools.json');
32
- const DOCS_OUTPUT_PATH = path.join(PROJECT_ROOT, '.claude', 'docs', 'knowledge-base', '05-development-tools', 'mcp-tools-generated.md');
30
+ const MCP_DOCS_PATH = path.join(PATHS.state, 'mcp-tools.json');
31
+ const DOCS_OUTPUT_PATH = path.join(PATHS.root, '.claude', 'docs', 'knowledge-base', '05-development-tools', 'mcp-tools-generated.md');
33
32
 
34
33
  // Known MCP server locations in this project
35
34
  const MCP_SERVER_PATHS = [
@@ -46,7 +45,7 @@ const MCP_SERVER_PATHS = [
46
45
  * @returns {Object[]} Array of tool definitions
47
46
  */
48
47
  function extractToolsFromFile(filePath) {
49
- const fullPath = path.join(PROJECT_ROOT, filePath);
48
+ const fullPath = path.join(PATHS.root, filePath);
50
49
 
51
50
  if (!fileExists(fullPath)) {
52
51
  return [];
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Features:
12
12
  * - SQLite database with sql.js
13
- * - Embeddings via @xenova/transformers
13
+ * - Embeddings via @huggingface/transformers
14
14
  * - Facts, proposals, and PRD storage
15
15
  * - Semantic similarity search
16
16
  *
@@ -54,10 +54,8 @@ const DEFAULTS = {
54
54
  // Configuration
55
55
  // ============================================================
56
56
 
57
- const { getProjectRoot } = require('./flow-paths');
58
- const PROJECT_ROOT = getProjectRoot();
59
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
60
- const MEMORY_DIR = path.join(WORKFLOW_DIR, 'memory');
57
+ const { getProjectRoot, PATHS } = require('./flow-paths');
58
+ const MEMORY_DIR = path.join(PATHS.workflow, 'memory');
61
59
  const DB_PATH = path.join(MEMORY_DIR, 'local.db');
62
60
 
63
61
  // ============================================================
@@ -424,21 +422,21 @@ let embeddingsAvailable = null; // null = unknown, true/false after first check
424
422
 
425
423
  /**
426
424
  * Get embedder (lazy load)
427
- * Returns null if @xenova/transformers is not installed
425
+ * Returns null if @huggingface/transformers is not installed
428
426
  */
429
427
  async function getEmbedder() {
430
428
  if (embeddingsAvailable === false) return null;
431
429
 
432
430
  if (!embedder) {
433
431
  try {
434
- const { pipeline } = await import('@xenova/transformers');
432
+ const { pipeline } = await import('@huggingface/transformers');
435
433
  embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
436
434
  embeddingsAvailable = true;
437
435
  } catch (err) {
438
436
  if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND') {
439
437
  embeddingsAvailable = false;
440
438
  if (process.env.DEBUG) {
441
- console.warn('[DEBUG] @xenova/transformers not installed - semantic search disabled');
439
+ console.warn('[DEBUG] @huggingface/transformers not installed - semantic search disabled');
442
440
  }
443
441
  return null;
444
442
  }
@@ -28,6 +28,7 @@ let _syncDecisionsToRules = null;
28
28
  function syncDecisionsToRules() {
29
29
  if (!_syncDecisionsToRules) {
30
30
  _syncDecisionsToRules = require('./flow-rules-sync').syncDecisionsToRules;
31
+ const { PATHS } = require('./flow-utils');
31
32
  }
32
33
  return _syncDecisionsToRules();
33
34
  }
@@ -36,7 +37,7 @@ function syncDecisionsToRules() {
36
37
  // Configuration
37
38
  // ============================================================
38
39
 
39
- const DECISIONS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'decisions.md');
40
+ const DECISIONS_PATH = PATHS.decisions;
40
41
 
41
42
  // loadConfig() removed — getConfig() already handles errors gracefully
42
43
 
@@ -249,12 +250,15 @@ async function promoteFact(factId, dryRun = false) {
249
250
  return true;
250
251
  }
251
252
 
252
- // Update decisions.md
253
+ // Update decisions.md through orchestrator
253
254
  const newContent = appendToDecisions(formatted, decisionsContent);
254
- fs.writeFileSync(DECISIONS_PATH, newContent);
255
-
256
- // Sync to .claude/rules/ for Claude Code integration
257
- syncDecisionsToRules();
255
+ try {
256
+ const { writeToDecisions } = require('./flow-learning-orchestrator');
257
+ await writeToDecisions({ content: newContent, entryText: fact.fact, caller: 'flow-memory-sync/promote', syncRules: true });
258
+ } catch (_err) {
259
+ fs.writeFileSync(DECISIONS_PATH, newContent);
260
+ syncDecisionsToRules();
261
+ }
258
262
 
259
263
  // Mark fact as promoted
260
264
  await memoryDb.markFactPromoted(factId, 'decisions.md');
@@ -324,15 +328,18 @@ async function autoPromote(config) {
324
328
  }
325
329
 
326
330
  if (promoted > 0) {
327
- // Write decisions.md FIRST, then mark as promoted in DB
328
- // This prevents data loss if process crashes between the two operations
329
- fs.writeFileSync(DECISIONS_PATH, currentContent);
331
+ // Write decisions.md through orchestrator, then mark as promoted in DB
332
+ try {
333
+ const { writeToDecisions } = require('./flow-learning-orchestrator');
334
+ await writeToDecisions({ content: currentContent, caller: 'flow-memory-sync/autoPromote', skipDedup: true, syncRules: true });
335
+ } catch (_err) {
336
+ fs.writeFileSync(DECISIONS_PATH, currentContent);
337
+ syncDecisionsToRules();
338
+ }
330
339
  // Now safe to mark as promoted in DB
331
340
  for (const id of promotedIds) {
332
341
  await memoryDb.markFactPromoted(id, 'decisions.md');
333
342
  }
334
- // Sync to .claude/rules/ for Claude Code integration
335
- syncDecisionsToRules();
336
343
  await memoryDb.recordMemoryMetric('auto_promote');
337
344
  }
338
345
 
@@ -21,8 +21,7 @@ const path = require('node:path');
21
21
  const { getProjectRoot, getConfig, PATHS, colors, showHelp: showHelpGeneric, readJson } = require('./flow-utils');
22
22
  const { success, error: errorMsg } = require('./flow-output');
23
23
 
24
- const PROJECT_ROOT = getProjectRoot();
25
- const METRICS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'command-metrics.json');
24
+ const METRICS_PATH = PATHS.commandMetrics;
26
25
 
27
26
  // ============================================================
28
27
  // Metrics Data Structure
@@ -29,9 +29,8 @@ const fs = require('node:fs');
29
29
  const path = require('node:path');
30
30
  const { getProjectRoot, getConfig, PATHS, colors, readJson } = require('./flow-utils');
31
31
 
32
- const PROJECT_ROOT = getProjectRoot();
33
- const ADAPTERS_DIR = path.join(PROJECT_ROOT, '.workflow', 'model-adapters');
34
- const MODEL_STATS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'model-stats.json');
32
+ const ADAPTERS_DIR = PATHS.modelAdapters;
33
+ const MODEL_STATS_PATH = PATHS.modelStatsLegacy;
35
34
 
36
35
  // ============================================================
37
36
  // Model Detection
@@ -31,13 +31,11 @@
31
31
 
32
32
  const fs = require('node:fs');
33
33
  const path = require('node:path');
34
- const { getProjectRoot, safeJsonParse, colors: c, error, success } = require('./flow-utils');
34
+ const { getProjectRoot, safeJsonParse, safeJsonParseString, colors: c, error, success, PATHS } = require('./flow-utils');
35
35
 
36
- const PROJECT_ROOT = getProjectRoot();
37
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
38
- const CONFIG_PATH = path.join(WORKFLOW_DIR, 'config.json');
39
- const ENV_PATH = path.join(PROJECT_ROOT, '.env');
40
- const SESSION_STATE_PATH = path.join(WORKFLOW_DIR, 'state', 'session-state.json');
36
+ const CONFIG_PATH = path.join(PATHS.workflow, 'config.json');
37
+ const ENV_PATH = path.join(PATHS.root, '.env');
38
+ const SESSION_STATE_PATH = path.join(PATHS.workflow, 'state', 'session-state.json');
41
39
 
42
40
  /**
43
41
  * Known provider configurations
@@ -106,7 +104,7 @@ function readConfig() {
106
104
  * @param {Object} config - Config to write
107
105
  */
108
106
  function writeConfig(config) {
109
- fs.mkdirSync(WORKFLOW_DIR, { recursive: true });
107
+ fs.mkdirSync(PATHS.workflow, { recursive: true });
110
108
  fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
111
109
  }
112
110
 
@@ -374,8 +372,12 @@ async function testLocalProvider(endpoint) {
374
372
  res.on('data', chunk => data += chunk);
375
373
  res.on('end', () => {
376
374
  try {
377
- const parsed = JSON.parse(data);
378
- const models = parsed.models?.map(m => m.name) || [];
375
+ const parsed = safeJsonParseString(data, null);
376
+ if (!parsed) {
377
+ resolve({ success: false, message: 'Invalid JSON response from Ollama' });
378
+ return;
379
+ }
380
+ const models = parsed.models?.map(m => m.name) ?? [];
379
381
  resolve({
380
382
  success: true,
381
383
  message: `Connected to Ollama. Found ${models.length} models.`,
@@ -405,115 +407,81 @@ async function testLocalProvider(endpoint) {
405
407
  * Test LM Studio provider
406
408
  */
407
409
  async function testLMStudio(baseEndpoint) {
408
- const http = require('node:http');
409
-
410
- return new Promise((resolve) => {
410
+ try {
411
411
  // LM Studio uses OpenAI-compatible endpoint
412
412
  const endpoint = baseEndpoint.replace(':11434', ':1234');
413
413
  const url = new URL('/v1/models', endpoint);
414
414
 
415
- const req = http.request(url, { method: 'GET', timeout: 5000 }, (res) => {
416
- let data = '';
417
- res.on('data', chunk => data += chunk);
418
- res.on('end', () => {
419
- try {
420
- const parsed = JSON.parse(data);
421
- const models = parsed.data?.map(m => m.id) || [];
422
- resolve({
423
- success: true,
424
- message: `Connected to LM Studio. Found ${models.length} models.`,
425
- models,
426
- provider: 'lmstudio'
427
- });
428
- } catch (err) {
429
- resolve({ success: false, message: 'No local LLM detected' });
430
- }
431
- });
432
- });
433
-
434
- req.on('error', () => {
435
- resolve({ success: false, message: 'No local LLM detected at localhost:11434 or :1234' });
436
- });
437
-
438
- req.end();
439
- });
415
+ const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(5000) });
416
+ const parsed = await res.json();
417
+ const models = parsed.data?.map(m => m.id) || [];
418
+ return {
419
+ success: true,
420
+ message: `Connected to LM Studio. Found ${models.length} models.`,
421
+ models,
422
+ provider: 'lmstudio'
423
+ };
424
+ } catch (_err) {
425
+ return { success: false, message: 'No local LLM detected at localhost:11434 or :1234' };
426
+ }
440
427
  }
441
428
 
442
429
  /**
443
430
  * Test cloud provider connection
444
431
  */
445
432
  async function testCloudProvider(providerName, endpoint, apiKey) {
446
- const https = require('node:https');
433
+ let url, headers;
447
434
 
448
- return new Promise((resolve, reject) => {
449
- let url, headers;
450
-
451
- switch (providerName) {
452
- case 'openai':
453
- url = new URL('/v1/models', endpoint);
454
- headers = { 'Authorization': `Bearer ${apiKey}` };
455
- break;
456
- case 'google':
457
- url = new URL('/v1beta/models', endpoint);
458
- headers = { 'x-goog-api-key': apiKey };
459
- break;
460
- case 'anthropic':
461
- // Anthropic doesn't have a models list endpoint, just test with a simple check
462
- resolve({ success: true, message: 'API key format valid (Anthropic)' });
463
- return;
464
- default:
465
- reject(new Error(`Unknown cloud provider: ${providerName}`));
466
- return;
467
- }
435
+ switch (providerName) {
436
+ case 'openai':
437
+ url = new URL('/v1/models', endpoint);
438
+ headers = { 'Authorization': `Bearer ${apiKey}` };
439
+ break;
440
+ case 'google':
441
+ url = new URL('/v1beta/models', endpoint);
442
+ headers = { 'x-goog-api-key': apiKey };
443
+ break;
444
+ case 'anthropic':
445
+ // Anthropic doesn't have a models list endpoint, just test with a simple check
446
+ return { success: true, message: 'API key format valid (Anthropic)' };
447
+ default:
448
+ throw new Error(`Unknown cloud provider: ${providerName}`);
449
+ }
468
450
 
469
- const req = https.request(url, { method: 'GET', headers, timeout: 10000 }, (res) => {
470
- let data = '';
471
- res.on('data', chunk => data += chunk);
472
- res.on('end', () => {
473
- if (res.statusCode === 200) {
474
- try {
475
- const parsed = JSON.parse(data);
476
-
477
- // Check for prototype pollution in API response
478
- if (hasDangerousKeys(parsed)) {
479
- resolve({ success: false, message: 'Invalid API response (security check failed)' });
480
- return;
481
- }
482
-
483
- let models = [];
484
-
485
- if (providerName === 'openai') {
486
- models = parsed.data?.map(m => m.id).filter(id =>
487
- id.startsWith('gpt-4') || id.startsWith('o1')
488
- ).slice(0, 10) || [];
489
- } else if (providerName === 'google') {
490
- models = parsed.models?.map(m => m.name.replace('models/', '')) || [];
491
- }
492
-
493
- resolve({
494
- success: true,
495
- message: `Connected to ${providerName}. Found ${models.length} models.`,
496
- models
497
- });
498
- } catch (err) {
499
- resolve({ success: true, message: `Connected to ${providerName}` });
500
- }
501
- } else if (res.statusCode === 401) {
502
- resolve({ success: false, message: 'Invalid API key' });
503
- } else {
504
- resolve({ success: false, message: `API error: ${res.statusCode}` });
505
- }
506
- });
507
- });
451
+ const res = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(10000) });
508
452
 
509
- req.on('error', (err) => reject(err));
510
- req.on('timeout', () => {
511
- req.destroy();
512
- reject(new Error('Connection timeout'));
513
- });
453
+ if (res.status === 200) {
454
+ try {
455
+ const parsed = await res.json();
514
456
 
515
- req.end();
516
- });
457
+ // Check for prototype pollution in API response
458
+ if (hasDangerousKeys(parsed)) {
459
+ return { success: false, message: 'Invalid API response (security check failed)' };
460
+ }
461
+
462
+ let models = [];
463
+
464
+ if (providerName === 'openai') {
465
+ models = parsed.data?.map(m => m.id).filter(id =>
466
+ id.startsWith('gpt-4') || id.startsWith('o1')
467
+ ).slice(0, 10) || [];
468
+ } else if (providerName === 'google') {
469
+ models = parsed.models?.map(m => m.name.replace('models/', '')) || [];
470
+ }
471
+
472
+ return {
473
+ success: true,
474
+ message: `Connected to ${providerName}. Found ${models.length} models.`,
475
+ models
476
+ };
477
+ } catch (_err) {
478
+ return { success: true, message: `Connected to ${providerName}` };
479
+ }
480
+ } else if (res.status === 401) {
481
+ return { success: false, message: 'Invalid API key' };
482
+ } else {
483
+ return { success: false, message: `API error: ${res.status}` };
484
+ }
517
485
  }
518
486
 
519
487
  /**
@@ -29,7 +29,7 @@ const {
29
29
  getConfig,
30
30
  printHeader,
31
31
  printSection,
32
- estimateTokens
32
+ estimateTokens, PATHS
33
33
  } = require('./flow-utils');
34
34
 
35
35
  const { analyzeTask } = require('./flow-task-analyzer');
@@ -61,7 +61,7 @@ try {
61
61
  // Constants
62
62
  // ============================================================
63
63
 
64
- const CONFIG_PATH = path.join(PROJECT_ROOT, '.workflow', 'config.json');
64
+ const CONFIG_PATH = path.join(PATHS.workflow, 'config.json');
65
65
 
66
66
  const ROUTING_STRATEGIES = {
67
67
  'quality-first': 'Select highest-capability model matching requirements',
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Wogi Flow - Model Types & Shared Registry Access
5
3
  *
@@ -27,11 +27,10 @@
27
27
 
28
28
  const fs = require('node:fs');
29
29
  const path = require('node:path');
30
- const { getProjectRoot, getConfig, PATHS, colors, writeJson, ensureDir, readJson } = require('./flow-utils');
30
+ const { getProjectRoot, getConfig, PATHS, colors, writeJson, ensureDir, readJson, safeJsonParse } = require('./flow-utils');
31
31
  const { success, error: errorMsg } = require('./flow-output');
32
32
 
33
- const PROJECT_ROOT = getProjectRoot();
34
- const APPROACHES_DIR = path.join(PROJECT_ROOT, '.workflow', 'state', 'approaches');
33
+ const APPROACHES_DIR = path.join(PATHS.state, 'approaches');
35
34
 
36
35
  // ============================================================
37
36
  // Configuration
@@ -351,9 +350,8 @@ function listSessions(limit = 10) {
351
350
  return fs.readdirSync(APPROACHES_DIR)
352
351
  .filter(f => f.endsWith('.json'))
353
352
  .map(f => {
354
- const session = JSON.parse(
355
- fs.readFileSync(path.join(APPROACHES_DIR, f), 'utf-8')
356
- );
353
+ const session = safeJsonParse(path.join(APPROACHES_DIR, f), null);
354
+ if (!session) return null;
357
355
  return {
358
356
  id: session.id,
359
357
  task: session.task?.description?.slice(0, 50) + '...',
@@ -362,6 +360,7 @@ function listSessions(limit = 10) {
362
360
  createdAt: session.createdAt
363
361
  };
364
362
  })
363
+ .filter(Boolean)
365
364
  .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
366
365
  .slice(0, limit);
367
366
  }
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
2
  /**
5
3
  * Project Context Generator - Extracted from flow-orchestrate.js
6
4
  *
@@ -17,7 +15,7 @@
17
15
 
18
16
  const fs = require('node:fs');
19
17
  const path = require('node:path');
20
- const { getProjectRoot, colors, getConfig } = require('./flow-utils');
18
+ const { getProjectRoot, colors, getConfig, PATHS } = require('./flow-utils');
21
19
  const {
22
20
  buildExportMap,
23
21
  loadCachedExportMap,
@@ -26,17 +24,15 @@ const {
26
24
  setProjectRoot: setExportScannerRoot
27
25
  } = require('./flow-export-scanner');
28
26
 
29
- const PROJECT_ROOT = getProjectRoot();
30
-
31
27
  // Set export scanner project root to match
32
- setExportScannerRoot(PROJECT_ROOT);
28
+ setExportScannerRoot(PATHS.root);
33
29
 
34
30
  function log(color, ...args) {
35
31
  console.log(colors[color] + args.join(' ') + colors.reset);
36
32
  }
37
33
 
38
34
  class ProjectContextGenerator {
39
- constructor(projectRoot = PROJECT_ROOT) {
35
+ constructor(projectRoot = PATHS.root) {
40
36
  this.projectRoot = projectRoot;
41
37
  this.contextPath = path.join(projectRoot, '.workflow/state/hybrid-context.md');
42
38
  this.cacheMaxAge = 60 * 60 * 1000; // 1 hour
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
2
  /**
5
3
  * Rollback Manager - Extracted from flow-orchestrate.js
6
4
  *
@@ -10,12 +8,9 @@
10
8
 
11
9
  const fs = require('node:fs');
12
10
  const path = require('node:path');
13
- const { getProjectRoot, colors } = require('./flow-utils');
11
+ const { getProjectRoot, colors, PATHS } = require('./flow-utils');
14
12
  const { readJson } = require('./flow-io');
15
13
 
16
- const PROJECT_ROOT = getProjectRoot();
17
- const STATE_DIR = path.join(PROJECT_ROOT, '.workflow', 'state');
18
-
19
14
  function log(color, ...args) {
20
15
  console.log(colors[color] + args.join(' ') + colors.reset);
21
16
  }
@@ -24,7 +19,7 @@ class RollbackManager {
24
19
  constructor() {
25
20
  this.createdFiles = [];
26
21
  this.modifiedFiles = [];
27
- this.checkpointPath = path.join(STATE_DIR, 'rollback-checkpoint.json');
22
+ this.checkpointPath = path.join(PATHS.state, 'rollback-checkpoint.json');
28
23
  }
29
24
 
30
25
  trackCreation(filePath) {
@@ -68,7 +63,7 @@ class RollbackManager {
68
63
  log('dim', ` 🗑️ Deleted: ${filePath}`);
69
64
 
70
65
  let dir = path.dirname(filePath);
71
- while (dir !== PROJECT_ROOT && fs.existsSync(dir)) {
66
+ while (dir !== PATHS.root && fs.existsSync(dir)) {
72
67
  const files = fs.readdirSync(dir);
73
68
  if (files.length === 0) {
74
69
  fs.rmdirSync(dir);
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
2
  /**
5
3
  * State Manager - Extracted from flow-orchestrate.js
6
4
  *
@@ -10,22 +8,18 @@
10
8
 
11
9
  const fs = require('node:fs');
12
10
  const path = require('node:path');
13
- const { getProjectRoot, colors, getConfig, writeJson } = require('./flow-utils');
11
+ const { getProjectRoot, colors, getConfig, writeJson, PATHS } = require('./flow-utils');
14
12
  const { readJson } = require('./flow-io');
15
13
  const { loadCachedExportMap } = require('./flow-export-scanner');
16
14
  const durableSession = require('./flow-durable-session');
17
15
 
18
- const PROJECT_ROOT = getProjectRoot();
19
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
20
- const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
21
-
22
16
  function log(color, ...args) {
23
17
  console.log(colors[color] + args.join(' ') + colors.reset);
24
18
  }
25
19
 
26
20
  class StateManager {
27
21
  updateRequestLog(step, status, mode = 'hybrid', executor = '') {
28
- const logPath = path.join(STATE_DIR, 'request-log.md');
22
+ const logPath = PATHS.requestLog;
29
23
  const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 16);
30
24
 
31
25
  const entry = `
@@ -49,7 +43,7 @@ ${step.description || ''}
49
43
  updateAppMap(update) {
50
44
  if (!update) return;
51
45
 
52
- const mapPath = path.join(STATE_DIR, 'app-map.md');
46
+ const mapPath = PATHS.appMap;
53
47
  if (!fs.existsSync(mapPath)) return;
54
48
 
55
49
  let content = fs.readFileSync(mapPath, 'utf-8');
@@ -111,7 +105,7 @@ ${step.description || ''}
111
105
  // DEPRECATED: This path is kept for backward compatibility but will be removed
112
106
  // Enable durableSteps in config.json to use the modern session management
113
107
  console.warn('[DEPRECATED] Using legacy hybrid-session.json - enable durableSteps.enabled in config.json');
114
- const sessionPath = path.join(STATE_DIR, 'hybrid-session.json');
108
+ const sessionPath = path.join(PATHS.state, 'hybrid-session.json');
115
109
 
116
110
  let session = {
117
111
  sessionId: `sess-${Date.now()}`,
@@ -150,7 +144,7 @@ ${step.description || ''}
150
144
  }
151
145
 
152
146
  // Legacy fallback - DEPRECATED
153
- const sessionPath = path.join(STATE_DIR, 'hybrid-session.json');
147
+ const sessionPath = path.join(PATHS.state, 'hybrid-session.json');
154
148
  const legacySession = readJson(sessionPath, null);
155
149
  if (legacySession) {
156
150
  console.warn('[DEPRECATED] Reading legacy hybrid-session.json - enable durableSteps.enabled in config.json');
@@ -160,7 +154,7 @@ ${step.description || ''}
160
154
  }
161
155
 
162
156
  saveResults(results) {
163
- const resultsPath = path.join(STATE_DIR, 'hybrid-results.json');
157
+ const resultsPath = path.join(PATHS.state, 'hybrid-results.json');
164
158
  fs.writeFileSync(resultsPath, JSON.stringify(results, null, 2));
165
159
  }
166
160
 
@@ -192,7 +186,7 @@ ${step.description || ''}
192
186
  };
193
187
 
194
188
  // Try to load from config (primary source)
195
- const configPath = path.join(WORKFLOW_DIR, 'config.json');
189
+ const configPath = path.join(PATHS.workflow, 'config.json');
196
190
  const config = readJson(configPath, null);
197
191
  if (config) {
198
192
  const projectCtx = config.hybrid?.projectContext || {};
@@ -295,7 +289,7 @@ ${step.description || ''}
295
289
  }
296
290
 
297
291
  // Supplement with app-map.md if no exports found
298
- const appMapPath = path.join(STATE_DIR, 'app-map.md');
292
+ const appMapPath = PATHS.appMap;
299
293
  if (fs.existsSync(appMapPath) && !context.availableComponents) {
300
294
  try {
301
295
  const appMap = fs.readFileSync(appMapPath, 'utf-8');