wogiflow 1.9.7 → 1.9.8

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 (47) hide show
  1. package/lib/utils.js +1 -1
  2. package/package.json +2 -5
  3. package/scripts/flow-consistency-check.js +0 -4
  4. package/scripts/flow-context-estimator.js +4 -20
  5. package/scripts/flow-correct.js +2 -1
  6. package/scripts/flow-decision-tracker.js +2 -2
  7. package/scripts/flow-done.js +4 -4
  8. package/scripts/flow-entropy-monitor.js +3 -24
  9. package/scripts/flow-export-scanner.js +3 -3
  10. package/scripts/flow-health.js +3 -2
  11. package/scripts/flow-hybrid-test.js +2 -3
  12. package/scripts/flow-long-input-stories.js +4 -6
  13. package/scripts/flow-long-input.js +5 -8
  14. package/scripts/flow-memory-compactor.js +1 -13
  15. package/scripts/flow-memory-db.js +1 -1
  16. package/scripts/flow-memory-sync.js +1 -17
  17. package/scripts/flow-orchestrate-llm.js +3 -7
  18. package/scripts/flow-orchestrate.js +2 -13
  19. package/scripts/flow-pattern-extractor.js +3 -17
  20. package/scripts/flow-peer-review.js +4 -12
  21. package/scripts/flow-plugin-registry.js +2 -2
  22. package/scripts/flow-project-analyzer.js +4 -4
  23. package/scripts/flow-providers.js +2 -7
  24. package/scripts/flow-safety.js +6 -10
  25. package/scripts/flow-script-resolver.js +4 -16
  26. package/scripts/flow-skill-freshness.js +3 -12
  27. package/scripts/flow-skill-generator.js +14 -24
  28. package/scripts/flow-strict-adherence.js +2 -2
  29. package/scripts/flow-task-analyzer.js +3 -3
  30. package/scripts/flow-test-discovery.js +3 -3
  31. package/scripts/flow-utils.js +1 -0
  32. package/scripts/flow-webmcp-generator.js +1 -1
  33. package/scripts/flow-workflow-steps.js +31 -23
  34. package/scripts/hooks/core/component-check.js +22 -7
  35. package/scripts/hooks/core/observation-capture.js +7 -4
  36. package/scripts/hooks/core/task-gate.js +2 -2
  37. package/scripts/hooks/entry/claude-code/post-tool-use.js +10 -8
  38. package/scripts/hooks/entry/claude-code/pre-tool-use.js +6 -40
  39. package/scripts/hooks/entry/claude-code/session-start.js +4 -12
  40. package/scripts/hooks/entry/shared/read-stdin.js +33 -0
  41. package/scripts/flow-done +0 -151
  42. package/scripts/flow-file-ops.js +0 -307
  43. package/scripts/flow-health +0 -185
  44. package/scripts/flow-ready +0 -82
  45. package/scripts/flow-start +0 -74
  46. package/scripts/flow-status +0 -110
  47. package/scripts/flow-story +0 -105
package/lib/utils.js CHANGED
@@ -40,7 +40,7 @@ function findProjectRoot() {
40
40
  /**
41
41
  * Safely parse JSON content with prototype pollution protection
42
42
  *
43
- * Note: For parsing JSON files, use safeJsonParseFile from flow-file-ops.js
43
+ * Note: For parsing JSON files, use safeJsonParse from flow-utils.js
44
44
  * or safeReadJson from this module instead.
45
45
  *
46
46
  * @param {string} content - JSON string to parse
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.9.7",
3
+ "version": "1.9.8",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -9,8 +9,6 @@
9
9
  },
10
10
  "scripts": {
11
11
  "flow": "./scripts/flow",
12
- "test": "node mcp-memory-server/test.js",
13
- "memory-server": "node mcp-memory-server/index.js",
14
12
  "postinstall": "node scripts/postinstall.js",
15
13
  "preuninstall": "node scripts/preuninstall.js"
16
14
  },
@@ -58,10 +56,9 @@
58
56
  },
59
57
  "homepage": "https://github.com/Tomer-Wogi/WogiFlow#readme",
60
58
  "dependencies": {
61
- "sql.js": "^1.10.0"
59
+ "sql.js": "^1.14.1"
62
60
  },
63
61
  "optionalDependencies": {
64
- "@modelcontextprotocol/sdk": "^1.0.0",
65
62
  "@xenova/transformers": "^2.15.0"
66
63
  },
67
64
  "engines": {
@@ -511,10 +511,6 @@ function runConsistencyCheck(options = {}) {
511
511
  }
512
512
  }
513
513
 
514
- // TODO: Implement crossMapConsistency check (config key exists but check is not yet implemented)
515
- // This would verify that components referenced in one map exist in others
516
- // (e.g., a function used by a component is also in function-map)
517
-
518
514
  // Determine overall status
519
515
  const mode = consistencyConfig.mode || 'warn';
520
516
  const orphanMode = consistencyConfig.orphanMode || 'warn';
@@ -13,7 +13,7 @@
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
- const { getConfig, PATHS, safeJsonParse } = require('./flow-utils');
16
+ const { getConfig, PATHS, safeJsonParse, validateTaskId } = require('./flow-utils');
17
17
 
18
18
  /**
19
19
  * Default estimation config (can be overridden in config.json)
@@ -33,20 +33,6 @@ const DEFAULT_REFACTOR_KEYWORDS = [
33
33
  'restructure', 'rearchitect', 'modernize', 'upgrade'
34
34
  ];
35
35
 
36
- // Valid task ID pattern — enforces wf-[8 hex] format and prevents path traversal
37
- // Also accepts legacy TASK-NNN/BUG-NNN and sub-tasks wf-XXXXXXXX-NN
38
- const VALID_TASK_ID_PATTERN = /^(wf-[a-f0-9]{8}(-\d{2})?|(TASK|BUG)-\d{3,})$/i;
39
-
40
- /**
41
- * Validate task ID format — must be wf-[8 hex chars] or legacy TASK-NNN/BUG-NNN.
42
- * Also prevents path traversal attacks.
43
- * @param {string} taskId - Task ID to validate
44
- * @returns {boolean} True if valid
45
- */
46
- function isValidTaskId(taskId) {
47
- return typeof taskId === 'string' && VALID_TASK_ID_PATTERN.test(taskId);
48
- }
49
-
50
36
  /**
51
37
  * Get smart compaction config from config.json
52
38
  * @returns {Object} Smart compaction configuration
@@ -90,7 +76,7 @@ function getSmartCompactionConfig() {
90
76
  */
91
77
  function readSpecFile(taskId) {
92
78
  // Validate taskId to prevent path traversal (Security Rule)
93
- if (!isValidTaskId(taskId)) {
79
+ if (!validateTaskId(taskId).valid) {
94
80
  return null;
95
81
  }
96
82
 
@@ -476,7 +462,7 @@ if (require.main === module) {
476
462
  const taskId = args[1];
477
463
 
478
464
  // Validate taskId to prevent path traversal
479
- if (!isValidTaskId(taskId)) {
465
+ if (!validateTaskId(taskId).valid) {
480
466
  console.error(`Invalid task ID format: ${taskId}`);
481
467
  console.error('Task IDs must contain only alphanumeric characters, hyphens, and underscores.');
482
468
  process.exit(1);
@@ -521,7 +507,7 @@ if (require.main === module) {
521
507
  const taskId = args[1];
522
508
 
523
509
  // Validate taskId to prevent path traversal
524
- if (!isValidTaskId(taskId)) {
510
+ if (!validateTaskId(taskId).valid) {
525
511
  console.error(`Invalid task ID format: ${taskId}`);
526
512
  console.error('Task IDs must contain only alphanumeric characters, hyphens, and underscores.');
527
513
  process.exit(1);
@@ -775,8 +761,6 @@ module.exports = {
775
761
  formatEstimationResult,
776
762
  extractCriteriaCount,
777
763
  extractFileCount,
778
- isValidTaskId,
779
- VALID_TASK_ID_PATTERN,
780
764
  // Finding-level estimation (for review-fix sessions)
781
765
  estimateFindingContextCost,
782
766
  calculateDynamicBatchSize,
@@ -23,6 +23,7 @@ const {
23
23
  dirExists,
24
24
  readFile,
25
25
  writeFile,
26
+ getConfig,
26
27
  color,
27
28
  success,
28
29
  warn,
@@ -35,7 +36,7 @@ const {
35
36
 
36
37
  function getCorrectionsDir() {
37
38
  try {
38
- const config = JSON.parse(fs.readFileSync(PATHS.config, 'utf-8'));
39
+ const config = getConfig();
39
40
  const detailPath = config?.corrections?.detailPath;
40
41
  if (detailPath) {
41
42
  return path.isAbsolute(detailPath) ? detailPath : path.join(PROJECT_ROOT, detailPath);
@@ -22,10 +22,10 @@
22
22
 
23
23
  const fs = require('fs');
24
24
  const path = require('path');
25
- const crypto = require('crypto');
26
25
  const {
27
26
  PATHS,
28
27
  getConfig,
28
+ generateHashId,
29
29
  success,
30
30
  warn,
31
31
  error,
@@ -163,7 +163,7 @@ function recordAmendment(params) {
163
163
  }
164
164
 
165
165
  // Generate amendment record
166
- const id = `amend-${crypto.randomBytes(4).toString('hex')}`;
166
+ const id = generateHashId('amend', '', '');
167
167
  const amendment = {
168
168
  id,
169
169
  timestamp: new Date().toISOString(),
@@ -210,7 +210,7 @@ function checkOutstandingFindings() {
210
210
  */
211
211
  function runQualityGates(taskId, taskType) {
212
212
  // Validate taskId before using in any path construction
213
- if (taskId && !validateTaskId(taskId)) {
213
+ if (taskId && !validateTaskId(taskId).valid) {
214
214
  console.log(color('red', `Invalid task ID format: ${String(taskId).slice(0, 30)}`));
215
215
  return { passed: false, failed: ['invalidTaskId'], errors: { invalidTaskId: 'Task ID failed validation' } };
216
216
  }
@@ -670,7 +670,7 @@ function runQualityGates(taskId, taskType) {
670
670
  }
671
671
  } else if (gate === 'generatedTestsPass') {
672
672
  if (config.testing?.enabled && config.testing?.generation?.autoGenerate) {
673
- if (!validateTaskId(taskId)) {
673
+ if (!validateTaskId(taskId).valid) {
674
674
  console.log(` ${color('yellow', '⚠')} generatedTestsPass (invalid task ID)`);
675
675
  } else {
676
676
  const testDir = path.join(PATHS.workflow, 'tests', 'generated', taskId);
@@ -727,7 +727,7 @@ function runQualityGates(taskId, taskType) {
727
727
  const gateModes = isUI ? ['ui', 'full', 'auto'] : ['api', 'full', 'auto'];
728
728
  const testingMode = config.testing?.mode || 'off';
729
729
  if (config.testing?.enabled && gateModes.includes(testingMode)) {
730
- if (!validateTaskId(taskId)) {
730
+ if (!validateTaskId(taskId).valid) {
731
731
  console.log(` ${color('yellow', '⚠')} ${gate} (invalid task ID)`);
732
732
  } else {
733
733
  try {
@@ -778,7 +778,7 @@ function runQualityGates(taskId, taskType) {
778
778
  }
779
779
 
780
780
  // Also check scenario verification results if available
781
- if (!isUI && validateTaskId(taskId)) {
781
+ if (!isUI && validateTaskId(taskId).valid) {
782
782
  const scenarioReportPath = path.join(PATHS.workflow, 'verifications', `${taskId}-scenarios.json`);
783
783
  if (fs.existsSync(scenarioReportPath)) {
784
784
  try {
@@ -17,40 +17,19 @@
17
17
  const fs = require('fs');
18
18
  const path = require('path');
19
19
  const memoryDb = require('./flow-memory-db');
20
+ const { getConfig } = require('./flow-config-loader');
21
+ const { color } = require('./flow-output');
20
22
 
21
23
  // ============================================================
22
24
  // Configuration
23
25
  // ============================================================
24
26
 
25
27
  const PROJECT_ROOT = process.env.WOGI_PROJECT_ROOT || process.cwd();
26
- const CONFIG_PATH = path.join(PROJECT_ROOT, '.workflow', 'config.json');
27
-
28
- function loadConfig() {
29
- try {
30
- if (fs.existsSync(CONFIG_PATH)) {
31
- return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
32
- }
33
- } catch {}
34
- return {};
35
- }
36
28
 
37
29
  // ============================================================
38
30
  // Output Formatting
39
31
  // ============================================================
40
32
 
41
- function color(c, text) {
42
- const colors = {
43
- red: '\x1b[31m',
44
- green: '\x1b[32m',
45
- yellow: '\x1b[33m',
46
- blue: '\x1b[34m',
47
- cyan: '\x1b[36m',
48
- gray: '\x1b[90m',
49
- reset: '\x1b[0m'
50
- };
51
- return `${colors[c] || ''}${text}${colors.reset}`;
52
- }
53
-
54
33
  function formatEntropy(entropy) {
55
34
  if (entropy < 0.4) return color('green', `${entropy} (healthy)`);
56
35
  if (entropy < 0.7) return color('yellow', `${entropy} (moderate)`);
@@ -299,7 +278,7 @@ async function showPromotionCandidates(config) {
299
278
 
300
279
  async function main() {
301
280
  const args = process.argv.slice(2);
302
- const config = loadConfig();
281
+ const config = getConfig();
303
282
 
304
283
  try {
305
284
  if (args.includes('--auto')) {
@@ -16,10 +16,10 @@
16
16
 
17
17
  const fs = require('fs');
18
18
  const path = require('path');
19
- const { getProjectRoot: getProjectRootFromUtils, getConfig } = require('./flow-utils');
19
+ const { PATHS, getConfig } = require('./flow-utils');
20
20
 
21
- // Default to getProjectRoot from utils, can be overridden via setProjectRoot() or CLI arg
22
- let PROJECT_ROOT = getProjectRootFromUtils();
21
+ // Default to PATHS.root from flow-utils, can be overridden via setProjectRoot() or CLI arg
22
+ let PROJECT_ROOT = PATHS.root;
23
23
  let CONFIG_PATH = path.join(PROJECT_ROOT, '.workflow/config.json');
24
24
  let CACHE_PATH = path.join(PROJECT_ROOT, '.workflow/state/export-map.json');
25
25
  const CACHE_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
@@ -38,7 +38,8 @@ const {
38
38
  checkSpecMigration,
39
39
  safeJsonParse,
40
40
  meetsVersion,
41
- getFdCommand
41
+ getFdCommand,
42
+ getConfig
42
43
  } = require('./flow-utils');
43
44
 
44
45
  const { execSync, execFileSync } = require('child_process');
@@ -128,7 +129,7 @@ function main() {
128
129
  let cliType = 'claude-code'; // default
129
130
  if (fileExists(PATHS.config)) {
130
131
  try {
131
- const config = require(PATHS.config);
132
+ const config = getConfig();
132
133
  cliType = config.cli?.type || 'claude-code';
133
134
  } catch {}
134
135
  }
@@ -9,7 +9,7 @@
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
  const { spawnSync } = require('child_process');
12
- const { getProjectRoot } = require('./flow-utils');
12
+ const { getProjectRoot, getConfig } = require('./flow-utils');
13
13
 
14
14
  const PROJECT_ROOT = getProjectRoot();
15
15
  const TESTS = [];
@@ -52,8 +52,7 @@ test('Config file exists', () => {
52
52
  });
53
53
 
54
54
  test('Config has hybrid section', () => {
55
- const configPath = path.join(PROJECT_ROOT, '.workflow', 'config.json');
56
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
55
+ const config = getConfig();
57
56
  if (!config.hybrid) {
58
57
  throw new Error('hybrid section missing from config');
59
58
  }
@@ -13,10 +13,8 @@
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
- const crypto = require('crypto');
17
-
18
16
  // Import safe utilities
19
- const { safeJsonParse, writeJson, generateTaskId, withLock, PATHS } = require('./flow-utils');
17
+ const { safeJsonParse, writeJson, generateTaskId, generateHashId, withLock, PATHS } = require('./flow-utils');
20
18
 
21
19
  // Utility: ISO timestamp
22
20
  function now() {
@@ -95,7 +93,7 @@ const SCENARIO_PATTERNS = [
95
93
  * Generate unique story ID
96
94
  */
97
95
  function generateStoryId() {
98
- return 'story-' + crypto.randomBytes(4).toString('hex');
96
+ return generateHashId('story', '', '');
99
97
  }
100
98
 
101
99
  /**
@@ -1063,14 +1061,14 @@ function resetPresentation() {
1063
1061
  * Generate unique edit session ID
1064
1062
  */
1065
1063
  function generateEditSessionId() {
1066
- return 'edit-' + crypto.randomBytes(4).toString('hex');
1064
+ return generateHashId('edit', '', '');
1067
1065
  }
1068
1066
 
1069
1067
  /**
1070
1068
  * Generate unique change ID
1071
1069
  */
1072
1070
  function generateChangeId() {
1073
- return 'change-' + crypto.randomBytes(3).toString('hex');
1071
+ return generateHashId('change', '', '');
1074
1072
  }
1075
1073
 
1076
1074
  /**
@@ -15,8 +15,7 @@
15
15
 
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
- const crypto = require('crypto');
19
- const { estimateTokens } = require('./flow-utils');
18
+ const { estimateTokens, generateHashId, getConfig } = require('./flow-utils');
20
19
 
21
20
  // Import extracted modules (renamed from transcript-* to long-input-*)
22
21
  const transcriptParsing = require('./flow-long-input-parsing');
@@ -106,8 +105,6 @@ const {
106
105
  const TMP_DIR = path.join(process.cwd(), '.workflow', 'tmp', 'long-input');
107
106
  const STATE_DIR = TMP_DIR; // Alias for backward compatibility during migration
108
107
  const ACTIVE_DIGEST_FILE = path.join(TMP_DIR, 'active-digest.json');
109
- const CONFIG_FILE = path.join(process.cwd(), '.workflow', 'config.json');
110
-
111
108
  // Colors for CLI output
112
109
  const c = {
113
110
  reset: '\x1b[0m',
@@ -124,7 +121,7 @@ const c = {
124
121
  */
125
122
  function loadConfig() {
126
123
  try {
127
- const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
124
+ const config = getConfig();
128
125
  return config.longInputGate || config.transcriptDigestion || {};
129
126
  } catch (_err) {
130
127
  return {};
@@ -135,7 +132,7 @@ function loadConfig() {
135
132
  * Generate unique digest ID
136
133
  */
137
134
  function generateDigestId() {
138
- return 'digest-' + crypto.randomBytes(4).toString('hex');
135
+ return generateHashId('digest', '', '');
139
136
  }
140
137
 
141
138
  /**
@@ -860,7 +857,7 @@ function resolveOrphan(orphan, topics) {
860
857
  function createTopicFromOrphans(orphans, _existingTopics) {
861
858
  // Guard against empty orphans array
862
859
  if (!orphans || orphans.length === 0) {
863
- const topicId = 't-auto-' + crypto.randomBytes(3).toString('hex');
860
+ const topicId = generateHashId('t-auto', '', '');
864
861
  return {
865
862
  id: topicId,
866
863
  title: 'Miscellaneous',
@@ -875,7 +872,7 @@ function createTopicFromOrphans(orphans, _existingTopics) {
875
872
  };
876
873
  }
877
874
 
878
- const topicId = 't-auto-' + crypto.randomBytes(3).toString('hex');
875
+ const topicId = generateHashId('t-auto', '', '');
879
876
 
880
877
  // Extract common keywords from orphans
881
878
  const allWords = orphans.flatMap(o =>
@@ -20,6 +20,7 @@ const fs = require('fs');
20
20
  const path = require('path');
21
21
  const memoryDb = require('./flow-memory-db');
22
22
  const { getConfig, PROJECT_ROOT } = require('./flow-config-loader');
23
+ const { color } = require('./flow-output');
23
24
 
24
25
  // ============================================================
25
26
  // Configuration
@@ -37,19 +38,6 @@ function loadConfig() {
37
38
  // Output Formatting
38
39
  // ============================================================
39
40
 
40
- function color(c, text) {
41
- const colors = {
42
- red: '\x1b[31m',
43
- green: '\x1b[32m',
44
- yellow: '\x1b[33m',
45
- blue: '\x1b[34m',
46
- cyan: '\x1b[36m',
47
- gray: '\x1b[90m',
48
- reset: '\x1b[0m'
49
- };
50
- return `${colors[c] || ''}${text}${colors.reset}`;
51
- }
52
-
53
41
  function formatEntropy(entropy) {
54
42
  if (entropy < 0.4) return color('green', `${entropy}`);
55
43
  if (entropy < 0.7) return color('yellow', `${entropy}`);
@@ -141,7 +141,7 @@ let db = null;
141
141
  let embedder = null;
142
142
  let initPromise = null;
143
143
 
144
- // ensureDir imported from flow-file-ops
144
+ // ensureDir imported from flow-io
145
145
 
146
146
  /**
147
147
  * Initialize database (singleton)
@@ -21,6 +21,7 @@ const fs = require('fs');
21
21
  const path = require('path');
22
22
  const memoryDb = require('./flow-memory-db');
23
23
  const { getConfig, PROJECT_ROOT } = require('./flow-config-loader');
24
+ const { color } = require('./flow-output');
24
25
 
25
26
  // Lazy-load to avoid circular dependency
26
27
  let _syncDecisionsToRules = null;
@@ -48,23 +49,6 @@ function loadConfig() {
48
49
  }
49
50
  }
50
51
 
51
- // ============================================================
52
- // Output Formatting
53
- // ============================================================
54
-
55
- function color(c, text) {
56
- const colors = {
57
- red: '\x1b[31m',
58
- green: '\x1b[32m',
59
- yellow: '\x1b[33m',
60
- blue: '\x1b[34m',
61
- cyan: '\x1b[36m',
62
- gray: '\x1b[90m',
63
- reset: '\x1b[0m'
64
- };
65
- return `${colors[c] || ''}${text}${colors.reset}`;
66
- }
67
-
68
52
  // ============================================================
69
53
  // Pattern Analysis
70
54
  // ============================================================
@@ -21,14 +21,10 @@ const {
21
21
  // Logging Helper
22
22
  // ============================================================
23
23
 
24
- const colors = {
25
- reset: '\x1b[0m',
26
- cyan: '\x1b[36m',
27
- dim: '\x1b[2m'
28
- };
24
+ const { colors } = require('./flow-output');
29
25
 
30
- function log(color, ...args) {
31
- console.log(colors[color] + args.join(' ') + colors.reset);
26
+ function log(colorName, ...args) {
27
+ console.log((colors[colorName] || '') + args.join(' ') + colors.reset);
32
28
  }
33
29
 
34
30
  // ============================================================
@@ -6,14 +6,9 @@
6
6
  * Executes plans created by Claude using a local LLM.
7
7
  * Updates all Wogi Flow state files after each step.
8
8
  *
9
- * LIMITATION: The --resume flag is documented but not yet implemented.
10
- * Checkpoint data is saved but recovery logic needs completion.
11
- * TODO: Implement resume from checkpoint functionality.
12
- *
13
9
  * Usage:
14
- * flow-orchestrate <plan.json> # Execute a plan
15
- * flow-orchestrate --resume # Resume from checkpoint (NOT YET IMPLEMENTED)
16
- * flow-orchestrate --rollback # Rollback last execution
10
+ * flow-orchestrate <plan.json> # Execute a plan
11
+ * flow-orchestrate --rollback # Rollback last execution
17
12
  */
18
13
 
19
14
  const fs = require('fs');
@@ -3146,7 +3141,6 @@ Wogi Flow Hybrid Orchestrator
3146
3141
 
3147
3142
  Usage:
3148
3143
  flow-orchestrate <plan.json> Execute a plan file
3149
- flow-orchestrate --resume Resume from checkpoint
3150
3144
  flow-orchestrate --rollback Rollback last execution
3151
3145
  flow-orchestrate --help Show this help
3152
3146
 
@@ -3167,11 +3161,6 @@ Examples:
3167
3161
  process.exit(0);
3168
3162
  }
3169
3163
 
3170
- if (args.includes('--resume')) {
3171
- log('yellow', 'Resume not yet implemented');
3172
- process.exit(1);
3173
- }
3174
-
3175
3164
  const planPath = args[0];
3176
3165
  if (!planPath) {
3177
3166
  console.error('Usage: flow-orchestrate <plan.json>');
@@ -37,8 +37,8 @@
37
37
  const fs = require('fs');
38
38
  const path = require('path');
39
39
  const { execSync, execFileSync } = require('child_process');
40
- const crypto = require('crypto');
41
40
  const { resolvePatterns } = require('./flow-framework-resolver');
41
+ const { getProjectRoot, generateHashId } = require('./flow-utils');
42
42
 
43
43
  // ============================================================================
44
44
  // Constants
@@ -107,25 +107,11 @@ const c = {
107
107
  // Utility Functions
108
108
  // ============================================================================
109
109
 
110
- /**
111
- * Get project root directory
112
- */
113
- function getProjectRoot() {
114
- try {
115
- return execSync('git rev-parse --show-toplevel', {
116
- encoding: 'utf-8',
117
- stdio: ['pipe', 'pipe', 'pipe']
118
- }).trim();
119
- } catch {
120
- return process.cwd();
121
- }
122
- }
123
-
124
110
  /**
125
111
  * Generate unique pattern ID
126
112
  */
127
113
  function generatePatternId() {
128
- return 'pat-' + crypto.randomBytes(4).toString('hex');
114
+ return generateHashId('pat', '', '');
129
115
  }
130
116
 
131
117
  /**
@@ -420,7 +406,7 @@ function createPattern(category, subcategory, name, options = {}) {
420
406
  */
421
407
  function createConflict(patternA, patternB, options = {}) {
422
408
  return {
423
- id: 'conf-' + crypto.randomBytes(4).toString('hex'),
409
+ id: generateHashId('conf', '', ''),
424
410
  category: patternA.category,
425
411
  subcategory: patternA.subcategory,
426
412
  description: options.description || `Conflicting ${patternA.subcategory} patterns`,
@@ -22,7 +22,8 @@ const {
22
22
  success,
23
23
  warn,
24
24
  error,
25
- info
25
+ info,
26
+ validateTaskId
26
27
  } = require('./flow-utils');
27
28
 
28
29
  const {
@@ -91,14 +92,6 @@ function getStagedChanges() {
91
92
  }
92
93
  }
93
94
 
94
- /**
95
- * Validate taskId format — must be wf-[8 hex chars] or legacy TASK-NNN/BUG-NNN.
96
- * Also prevents command injection and path traversal.
97
- */
98
- function isValidTaskId(taskId) {
99
- return /^(wf-[a-f0-9]{8}(-\d{2})?|(TASK|BUG)-\d{3,})$/i.test(taskId);
100
- }
101
-
102
95
  /**
103
96
  * Validate commit hash format
104
97
  */
@@ -111,7 +104,7 @@ function isValidCommitHash(hash) {
111
104
  */
112
105
  function getTaskChanges(taskId) {
113
106
  // Validate taskId to prevent command injection
114
- if (!isValidTaskId(taskId)) {
107
+ if (!validateTaskId(taskId).valid) {
115
108
  warn(`Invalid task ID format: ${taskId}`);
116
109
  return getStagedChanges();
117
110
  }
@@ -504,8 +497,7 @@ module.exports = {
504
497
  compareFindings,
505
498
  formatResults,
506
499
  IMPROVEMENT_PROMPT,
507
- // Security validation functions
508
- isValidTaskId,
500
+ // Security validation functions (validateTaskId imported from flow-utils)
509
501
  isValidCommitHash,
510
502
  isValidGlob,
511
503
  isValidFilePath
@@ -21,6 +21,7 @@ const path = require('path');
21
21
  const {
22
22
  getProjectRoot,
23
23
  safeJsonParse,
24
+ getConfig,
24
25
  writeJson,
25
26
  fileExists,
26
27
  isPathWithinProject,
@@ -47,8 +48,7 @@ const PROJECT_ROOT = getProjectRoot();
47
48
  * @returns {Object} Plugin configuration
48
49
  */
49
50
  function getPluginConfig() {
50
- const configPath = path.join(PROJECT_ROOT, '.workflow', 'config.json');
51
- const config = safeJsonParse(configPath, {});
51
+ const config = getConfig();
52
52
  return config.plugins || {
53
53
  enabled: true,
54
54
  registryPath: '.workflow/state/plugin-registry.json',
@@ -12,7 +12,7 @@
12
12
 
13
13
  const fs = require('fs');
14
14
  const path = require('path');
15
- const { getProjectRoot, safeJsonParse } = require('./flow-utils');
15
+ const { getProjectRoot, safeJsonParse, getConfig } = require('./flow-utils');
16
16
 
17
17
  const PROJECT_ROOT = process.argv[2] || getProjectRoot();
18
18
 
@@ -802,8 +802,8 @@ function updateConfig(analysis) {
802
802
  }
803
803
 
804
804
  try {
805
- const config = safeJsonParse(CONFIG_PATH, null);
806
- if (!config) return false;
805
+ const config = getConfig();
806
+ if (!config || Object.keys(config).length === 0) return false;
807
807
 
808
808
  // Ensure hybrid section exists
809
809
  if (!config.hybrid) config.hybrid = {};
@@ -1227,7 +1227,7 @@ if (require.main === module) {
1227
1227
  clearContextCache();
1228
1228
 
1229
1229
  // Also generate codebase insights during full analysis
1230
- const config = safeJsonParse(CONFIG_PATH, {});
1230
+ const config = getConfig();
1231
1231
  if (config.codebaseInsights?.enabled !== false) {
1232
1232
  saveCodebaseInsights();
1233
1233
  }