wogiflow 1.0.11 → 1.0.13

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 (46) hide show
  1. package/.workflow/specs/architecture.md.template +24 -0
  2. package/.workflow/specs/stack.md.template +33 -0
  3. package/.workflow/specs/testing.md.template +36 -0
  4. package/README.md +90 -1
  5. package/lib/unified-wizard.js +569 -30
  6. package/package.json +1 -1
  7. package/scripts/MEMORY-ARCHITECTURE.md +150 -0
  8. package/scripts/flow +20 -19
  9. package/scripts/flow-auto-context.js +97 -3
  10. package/scripts/flow-conflict-resolver.js +735 -0
  11. package/scripts/flow-context-gatherer.js +520 -0
  12. package/scripts/flow-context-monitor.js +148 -19
  13. package/scripts/flow-damage-control.js +5 -1
  14. package/scripts/flow-export-profile +168 -1
  15. package/scripts/flow-import-profile +257 -6
  16. package/scripts/flow-instruction-richness.js +182 -18
  17. package/scripts/flow-knowledge-router.js +2 -0
  18. package/scripts/flow-knowledge-sync.js +2 -0
  19. package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
  20. package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
  21. package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
  22. package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
  23. package/scripts/flow-memory-db.js +386 -1
  24. package/scripts/flow-memory-sync.js +2 -0
  25. package/scripts/flow-model-adapter.js +53 -29
  26. package/scripts/flow-model-router.js +246 -1
  27. package/scripts/flow-morning.js +94 -0
  28. package/scripts/flow-onboard +223 -10
  29. package/scripts/flow-orchestrate-validation.js +539 -0
  30. package/scripts/flow-orchestrate.js +16 -507
  31. package/scripts/flow-pattern-extractor.js +1265 -0
  32. package/scripts/flow-prompt-composer.js +222 -2
  33. package/scripts/flow-quality-guard.js +594 -0
  34. package/scripts/flow-section-index.js +713 -0
  35. package/scripts/flow-section-resolver.js +484 -0
  36. package/scripts/flow-session-end.js +188 -2
  37. package/scripts/flow-skill-create.js +19 -3
  38. package/scripts/flow-skill-matcher.js +122 -7
  39. package/scripts/flow-statusline-setup.js +218 -0
  40. package/scripts/flow-step-review.js +19 -0
  41. package/scripts/flow-tech-debt.js +734 -0
  42. package/scripts/flow-utils.js +2 -0
  43. package/scripts/hooks/core/long-input-gate.js +293 -0
  44. package/scripts/flow-parallel-detector.js +0 -399
  45. package/scripts/flow-parallel-dispatch.js +0 -987
  46. /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
@@ -145,6 +145,8 @@ const PATHS = {
145
145
  skills: path.join(CLAUDE_DIR, 'skills'),
146
146
  rules: path.join(CLAUDE_DIR, 'rules'),
147
147
  commands: path.join(CLAUDE_DIR, 'commands'),
148
+ // Smart Context System (Phase 1)
149
+ sectionIndex: path.join(STATE_DIR, 'section-index.json'),
148
150
  // Knowledge files (Phase 0.4 - synced documentation)
149
151
  // NOTE: These are DEPRECATED - use specsStack, specsArchitecture, specsTesting instead
150
152
  // Kept for backward compatibility, will be removed in v2.0
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Long Input Gate (Core Module)
5
+ *
6
+ * Detects long/complex inputs and triggers appropriate processing.
7
+ * Uses smart defaults based on content type:
8
+ * - transcript/spec/requirements → full extraction
9
+ * - code → skip
10
+ * - other → quick scan
11
+ *
12
+ * Part of the Long Input Processing pipeline (formerly transcript-digestion).
13
+ */
14
+
15
+ const path = require('path');
16
+
17
+ // Import from parent scripts directory
18
+ const { getConfig, safeJsonParse } = require('../../flow-utils');
19
+
20
+ // Default configuration
21
+ const DEFAULTS = {
22
+ enabled: true,
23
+ charThreshold: 2000, // Characters to trigger gate
24
+ lineThreshold: 50, // Lines to trigger gate
25
+ smartDefault: true, // Use content-based defaults
26
+ contentRules: {
27
+ transcript: 'full', // Meeting notes, voice transcripts
28
+ spec: 'full', // Specifications, PRDs
29
+ requirements: 'full', // Feature requirements
30
+ code: 'skip', // Code snippets - no extraction needed
31
+ default: 'quick' // Unknown content - quick scan
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Content type patterns for classification
37
+ */
38
+ const CONTENT_PATTERNS = {
39
+ transcript: [
40
+ /\b(meeting|discussion|call|transcript)\b/i,
41
+ /\b(said|mentioned|asked|replied)\b/i,
42
+ /\b(speaker|participant)\s*[:\d]/i,
43
+ /\[\d{1,2}:\d{2}(:\d{2})?\]/, // Timestamps [00:00:00]
44
+ /^\s*-?\s*[A-Z][a-z]+:/m // Speaker: format
45
+ ],
46
+ spec: [
47
+ /\b(specification|spec|prd|requirement|feature)\b/i,
48
+ /\b(must|shall|should|will)\s+(be|have|support|allow)\b/i,
49
+ /\b(user story|acceptance criteria|given.+when.+then)\b/i,
50
+ /\b(functional|non-functional|technical)\s+requirement/i
51
+ ],
52
+ requirements: [
53
+ /\b(feature|functionality|capability)\b/i,
54
+ /\b(user wants|users need|allow users to)\b/i,
55
+ /\b(add|implement|create|build)\s+a?\s*(new|the)?\s*(feature|page|component|button)/i,
56
+ /as a .+, i want .+, so that/i // User story format
57
+ ],
58
+ code: [
59
+ /^(import|export|const|let|var|function|class|interface|type)\s/m,
60
+ /\b(async|await|return|throw|try|catch)\b/,
61
+ /[{}\[\]();=]\s*$/m, // Code endings
62
+ /^\s*(\/\/|\/\*|\*|#)/m, // Comments
63
+ /\.(ts|js|tsx|jsx|py|go|rs|java|rb)$/ // File extensions mentioned
64
+ ]
65
+ };
66
+
67
+ /**
68
+ * Get long input gate configuration
69
+ */
70
+ function getLongInputConfig() {
71
+ const config = getConfig();
72
+ return {
73
+ ...DEFAULTS,
74
+ ...(config.longInputGate || config.transcriptDigestion || {})
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Check if long input gate is enabled
80
+ */
81
+ function isLongInputGateEnabled() {
82
+ const config = getLongInputConfig();
83
+ return config.enabled !== false;
84
+ }
85
+
86
+ /**
87
+ * Check if input exceeds thresholds
88
+ */
89
+ function exceedsThresholds(input) {
90
+ if (!input || typeof input !== 'string') {
91
+ return { exceeds: false };
92
+ }
93
+
94
+ const config = getLongInputConfig();
95
+ const charCount = input.length;
96
+ const lineCount = input.split('\n').length;
97
+
98
+ const exceedsChars = charCount >= config.charThreshold;
99
+ const exceedsLines = lineCount >= config.lineThreshold;
100
+
101
+ return {
102
+ exceeds: exceedsChars || exceedsLines,
103
+ charCount,
104
+ lineCount,
105
+ charThreshold: config.charThreshold,
106
+ lineThreshold: config.lineThreshold,
107
+ reason: exceedsChars ? 'chars' : (exceedsLines ? 'lines' : null)
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Classify content type based on patterns
113
+ */
114
+ function classifyContent(input) {
115
+ if (!input || typeof input !== 'string') {
116
+ return { type: 'unknown', confidence: 0, matches: [] };
117
+ }
118
+
119
+ const scores = {};
120
+ const matches = {};
121
+
122
+ for (const [type, patterns] of Object.entries(CONTENT_PATTERNS)) {
123
+ scores[type] = 0;
124
+ matches[type] = [];
125
+
126
+ for (const pattern of patterns) {
127
+ const match = input.match(pattern);
128
+ if (match) {
129
+ scores[type]++;
130
+ matches[type].push(pattern.source.slice(0, 30) + '...');
131
+ }
132
+ }
133
+ }
134
+
135
+ // Find highest scoring type
136
+ let bestType = 'unknown';
137
+ let bestScore = 0;
138
+
139
+ for (const [type, score] of Object.entries(scores)) {
140
+ if (score > bestScore) {
141
+ bestScore = score;
142
+ bestType = type;
143
+ }
144
+ }
145
+
146
+ // Calculate confidence (0-1)
147
+ const maxPossible = CONTENT_PATTERNS[bestType]?.length || 1;
148
+ const confidence = Math.min(bestScore / maxPossible, 1);
149
+
150
+ return {
151
+ type: bestScore > 0 ? bestType : 'unknown',
152
+ confidence,
153
+ scores,
154
+ matches: matches[bestType] || []
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Get recommended action for content type
160
+ */
161
+ function getRecommendedAction(contentType) {
162
+ const config = getLongInputConfig();
163
+ const rules = config.contentRules || DEFAULTS.contentRules;
164
+
165
+ return rules[contentType] || rules.default || 'quick';
166
+ }
167
+
168
+ /**
169
+ * Check long input gate for a given input
170
+ *
171
+ * @param {Object} options
172
+ * @param {string} options.input - The input text to check
173
+ * @param {string} options.source - Source of input ('task', 'story', 'voice', 'paste')
174
+ * @returns {Object} Gate result
175
+ */
176
+ function checkLongInputGate(options = {}) {
177
+ const { input, source = 'unknown' } = options;
178
+
179
+ // Check if gate is enabled
180
+ if (!isLongInputGateEnabled()) {
181
+ return {
182
+ triggered: false,
183
+ action: 'skip',
184
+ reason: 'gate_disabled'
185
+ };
186
+ }
187
+
188
+ // Check thresholds
189
+ const thresholdResult = exceedsThresholds(input);
190
+
191
+ if (!thresholdResult.exceeds) {
192
+ return {
193
+ triggered: false,
194
+ action: 'skip',
195
+ reason: 'below_threshold',
196
+ metrics: thresholdResult
197
+ };
198
+ }
199
+
200
+ // Classify content
201
+ const classification = classifyContent(input);
202
+
203
+ // Get recommended action
204
+ const config = getLongInputConfig();
205
+ let action;
206
+
207
+ if (config.smartDefault) {
208
+ action = getRecommendedAction(classification.type);
209
+ } else {
210
+ action = 'ask'; // Always ask if smart defaults disabled
211
+ }
212
+
213
+ return {
214
+ triggered: true,
215
+ action,
216
+ reason: 'threshold_exceeded',
217
+ metrics: thresholdResult,
218
+ classification,
219
+ source,
220
+ message: generateGateMessage(thresholdResult, classification, action)
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Generate user-facing message for gate trigger
226
+ */
227
+ function generateGateMessage(metrics, classification, action) {
228
+ const sizeInfo = metrics.reason === 'chars'
229
+ ? `${metrics.charCount.toLocaleString()} characters`
230
+ : `${metrics.lineCount} lines`;
231
+
232
+ const typeInfo = classification.confidence > 0.5
233
+ ? `Detected as: ${classification.type}`
234
+ : 'Content type: unknown';
235
+
236
+ const actionInfo = {
237
+ full: 'Running full extraction (4-pass with clarifications)',
238
+ quick: 'Running quick scan (single-pass, no clarifications)',
239
+ skip: 'Skipping extraction (code content)',
240
+ ask: 'Please choose processing mode'
241
+ };
242
+
243
+ return `Long input detected: ${sizeInfo}
244
+ ${typeInfo}
245
+ Recommended action: ${actionInfo[action] || action}`;
246
+ }
247
+
248
+ /**
249
+ * Format gate result for display
250
+ */
251
+ function formatGateResult(result) {
252
+ if (!result.triggered) {
253
+ return null;
254
+ }
255
+
256
+ const lines = [
257
+ `Long Input Gate Triggered`,
258
+ ``,
259
+ `Size: ${result.metrics.charCount.toLocaleString()} chars, ${result.metrics.lineCount} lines`,
260
+ `Content Type: ${result.classification.type} (${Math.round(result.classification.confidence * 100)}% confidence)`,
261
+ `Recommended: ${result.action}`,
262
+ ``
263
+ ];
264
+
265
+ if (result.action === 'ask') {
266
+ lines.push(
267
+ `Options:`,
268
+ ` 1. full - Complete 4-pass extraction with clarifications`,
269
+ ` 2. quick - Fast single-pass scan`,
270
+ ` 3. skip - Proceed without extraction`
271
+ );
272
+ }
273
+
274
+ return lines.join('\n');
275
+ }
276
+
277
+ module.exports = {
278
+ // Configuration
279
+ getLongInputConfig,
280
+ isLongInputGateEnabled,
281
+ DEFAULTS,
282
+ CONTENT_PATTERNS,
283
+
284
+ // Core functions
285
+ exceedsThresholds,
286
+ classifyContent,
287
+ getRecommendedAction,
288
+ checkLongInputGate,
289
+
290
+ // Display
291
+ generateGateMessage,
292
+ formatGateResult
293
+ };
@@ -1,399 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Wogi Flow - Parallel Execution Detector
5
- *
6
- * Automatically detects when tasks can run in parallel and suggests/executes
7
- * parallel execution based on configuration.
8
- *
9
- * Features:
10
- * - Analyzes task dependencies and file overlaps
11
- * - Suggests parallel execution when beneficial
12
- * - Auto-executes parallel tasks when configured
13
- * - Provides clear explanations of parallelization decisions
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
- const { getConfig, getProjectRoot } = require('./flow-utils');
19
- const {
20
- detectDependencies,
21
- findParallelizable,
22
- canRunInParallel,
23
- getParallelConfig
24
- } = require('./flow-parallel');
25
-
26
- /**
27
- * Analyze tasks for parallel execution potential
28
- */
29
- function analyzeParallelPotential(tasks) {
30
- if (!tasks || tasks.length < 2) {
31
- return {
32
- canParallelize: false,
33
- reason: 'insufficient-tasks',
34
- message: 'Need at least 2 tasks to parallelize'
35
- };
36
- }
37
-
38
- const dependencies = detectDependencies(tasks);
39
- const parallelizable = findParallelizable(tasks, new Set(), dependencies);
40
- const config = getParallelConfig();
41
-
42
- // Check minimum threshold
43
- const minTasks = config.minTasksForParallel || 2;
44
- if (parallelizable.length < minTasks) {
45
- return {
46
- canParallelize: false,
47
- reason: 'below-threshold',
48
- message: `Only ${parallelizable.length} tasks can run in parallel (minimum: ${minTasks})`,
49
- parallelizable: parallelizable.map(t => t.id)
50
- };
51
- }
52
-
53
- // Analyze file overlap for safety warnings
54
- const fileOverlaps = analyzeFileOverlaps(tasks, dependencies);
55
-
56
- // Calculate efficiency gain
57
- const efficiencyGain = calculateEfficiencyGain(tasks, parallelizable);
58
-
59
- return {
60
- canParallelize: true,
61
- parallelizable: parallelizable.map(t => ({
62
- id: t.id,
63
- title: t.title || t.description,
64
- files: t.files || []
65
- })),
66
- totalTasks: tasks.length,
67
- parallelCount: parallelizable.length,
68
- dependencies,
69
- fileOverlaps,
70
- efficiencyGain,
71
- waves: calculateWaves(tasks, dependencies),
72
- recommendation: generateRecommendation(parallelizable, efficiencyGain, fileOverlaps)
73
- };
74
- }
75
-
76
- /**
77
- * Analyze file overlaps that might cause issues
78
- */
79
- function analyzeFileOverlaps(tasks, dependencies) {
80
- const overlaps = [];
81
- const fileToTasks = {};
82
-
83
- for (const task of tasks) {
84
- if (task.files && Array.isArray(task.files)) {
85
- for (const file of task.files) {
86
- if (!fileToTasks[file]) {
87
- fileToTasks[file] = [];
88
- }
89
- fileToTasks[file].push(task.id);
90
- }
91
- }
92
- }
93
-
94
- for (const [file, taskIds] of Object.entries(fileToTasks)) {
95
- if (taskIds.length > 1) {
96
- overlaps.push({
97
- file,
98
- tasks: taskIds,
99
- severity: taskIds.length > 2 ? 'high' : 'medium'
100
- });
101
- }
102
- }
103
-
104
- return overlaps;
105
- }
106
-
107
- /**
108
- * Calculate efficiency gain from parallel execution
109
- */
110
- function calculateEfficiencyGain(tasks, parallelizable) {
111
- // Simple estimation based on parallelizable ratio
112
- const sequentialTime = tasks.length; // 1 unit per task
113
- const parallelTime = Math.ceil(tasks.length / parallelizable.length);
114
-
115
- return {
116
- sequential: sequentialTime,
117
- parallel: parallelTime,
118
- savedTime: sequentialTime - parallelTime,
119
- percentageGain: Math.round((1 - parallelTime / sequentialTime) * 100)
120
- };
121
- }
122
-
123
- /**
124
- * Calculate execution waves (groups that can run together)
125
- */
126
- function calculateWaves(tasks, dependencies) {
127
- const waves = [];
128
- const completed = new Set();
129
-
130
- while (completed.size < tasks.length) {
131
- const wave = [];
132
-
133
- for (const task of tasks) {
134
- if (completed.has(task.id)) continue;
135
-
136
- const taskDeps = dependencies[task.id] || [];
137
- const allDepsComplete = taskDeps.every(d => completed.has(d));
138
-
139
- if (allDepsComplete) {
140
- wave.push(task.id);
141
- }
142
- }
143
-
144
- if (wave.length === 0) {
145
- // Circular dependency or stuck
146
- break;
147
- }
148
-
149
- waves.push(wave);
150
- wave.forEach(id => completed.add(id));
151
- }
152
-
153
- return waves;
154
- }
155
-
156
- /**
157
- * Generate a human-readable recommendation
158
- */
159
- function generateRecommendation(parallelizable, efficiencyGain, fileOverlaps) {
160
- const lines = [];
161
-
162
- if (parallelizable.length >= 3) {
163
- lines.push('✅ RECOMMENDED: High parallelization potential');
164
- } else if (parallelizable.length >= 2) {
165
- lines.push('⚠️ POSSIBLE: Moderate parallelization potential');
166
- }
167
-
168
- lines.push(` ${parallelizable.length} tasks can run simultaneously`);
169
- lines.push(` ~${efficiencyGain.percentageGain}% time savings expected`);
170
-
171
- if (fileOverlaps.length > 0) {
172
- const highSeverity = fileOverlaps.filter(o => o.severity === 'high');
173
- if (highSeverity.length > 0) {
174
- lines.push(` ⚠️ ${highSeverity.length} high-risk file overlaps detected`);
175
- lines.push(` Consider enabling worktree isolation`);
176
- }
177
- }
178
-
179
- return lines.join('\n');
180
- }
181
-
182
- /**
183
- * Generate suggestion message for user
184
- */
185
- function generateSuggestionMessage(analysis) {
186
- const lines = [
187
- '',
188
- '╔══════════════════════════════════════════════════════╗',
189
- '║ 🔀 PARALLEL EXECUTION AVAILABLE ║',
190
- '╠══════════════════════════════════════════════════════╣'
191
- ];
192
-
193
- lines.push(`║ ${analysis.parallelCount} of ${analysis.totalTasks} tasks can run in parallel`.padEnd(55) + '║');
194
- lines.push(`║ Estimated time savings: ~${analysis.efficiencyGain.percentageGain}%`.padEnd(55) + '║');
195
-
196
- lines.push('╠══════════════════════════════════════════════════════╣');
197
- lines.push('║ Parallelizable tasks:'.padEnd(55) + '║');
198
-
199
- for (const task of analysis.parallelizable.slice(0, 5)) {
200
- const title = task.title || task.id;
201
- const truncated = title.length > 45 ? title.substring(0, 42) + '...' : title;
202
- lines.push(`║ • ${truncated}`.padEnd(55) + '║');
203
- }
204
-
205
- if (analysis.parallelizable.length > 5) {
206
- lines.push(`║ ... and ${analysis.parallelizable.length - 5} more`.padEnd(55) + '║');
207
- }
208
-
209
- if (analysis.fileOverlaps.length > 0) {
210
- lines.push('╠══════════════════════════════════════════════════════╣');
211
- lines.push('║ ⚠️ File overlap warnings:'.padEnd(55) + '║');
212
- for (const overlap of analysis.fileOverlaps.slice(0, 3)) {
213
- const msg = `${overlap.file} → ${overlap.tasks.join(', ')}`;
214
- const truncated = msg.length > 47 ? msg.substring(0, 44) + '...' : msg;
215
- lines.push(`║ ${truncated}`.padEnd(55) + '║');
216
- }
217
- }
218
-
219
- lines.push('╠══════════════════════════════════════════════════════╣');
220
- lines.push('║ Options:'.padEnd(55) + '║');
221
- lines.push('║ [P] Run in parallel'.padEnd(55) + '║');
222
- lines.push('║ [S] Run sequentially'.padEnd(55) + '║');
223
- lines.push('║ [W] Run parallel with worktree isolation'.padEnd(55) + '║');
224
- lines.push('╚══════════════════════════════════════════════════════╝');
225
-
226
- return lines.join('\n');
227
- }
228
-
229
- /**
230
- * Check if parallel execution should be suggested
231
- */
232
- function shouldSuggestParallel(tasks) {
233
- const config = getConfig();
234
- const parallelConfig = config.parallel || {};
235
-
236
- if (!parallelConfig.enabled) {
237
- return { suggest: false, reason: 'parallel-disabled' };
238
- }
239
-
240
- if (!parallelConfig.autoDetect) {
241
- return { suggest: false, reason: 'auto-detect-disabled' };
242
- }
243
-
244
- const analysis = analyzeParallelPotential(tasks);
245
-
246
- if (!analysis.canParallelize) {
247
- return { suggest: false, reason: analysis.reason };
248
- }
249
-
250
- if (parallelConfig.autoSuggest) {
251
- return {
252
- suggest: true,
253
- analysis,
254
- message: generateSuggestionMessage(analysis)
255
- };
256
- }
257
-
258
- return { suggest: false, reason: 'auto-suggest-disabled' };
259
- }
260
-
261
- /**
262
- * Check if parallel execution should auto-execute
263
- * Uses autoExecute config option (not autoApprove which is for manual triggers)
264
- */
265
- function shouldAutoExecute(tasks) {
266
- const config = getConfig();
267
- const parallelConfig = config.parallel || {};
268
-
269
- if (!parallelConfig.enabled) return false;
270
- if (!parallelConfig.autoExecute) return false; // Use autoExecute, not autoApprove
271
- if (!parallelConfig.autoDetect) return false;
272
-
273
- const analysis = analyzeParallelPotential(tasks);
274
- return analysis.canParallelize;
275
- }
276
-
277
- /**
278
- * Load pending tasks from ready.json
279
- */
280
- function loadPendingTasks() {
281
- const projectRoot = getProjectRoot();
282
- const readyPath = path.join(projectRoot, '.workflow', 'state', 'ready.json');
283
-
284
- if (!fs.existsSync(readyPath)) {
285
- return [];
286
- }
287
-
288
- try {
289
- const ready = JSON.parse(fs.readFileSync(readyPath, 'utf-8'));
290
- return (ready.tasks || []).filter(t =>
291
- t.status === 'pending' || t.status === 'ready'
292
- );
293
- } catch {
294
- return [];
295
- }
296
- }
297
-
298
- // ============================================================
299
- // Exports
300
- // ============================================================
301
-
302
- module.exports = {
303
- analyzeParallelPotential,
304
- analyzeFileOverlaps,
305
- calculateEfficiencyGain,
306
- calculateWaves,
307
- generateRecommendation,
308
- generateSuggestionMessage,
309
- shouldSuggestParallel,
310
- shouldAutoExecute,
311
- loadPendingTasks
312
- };
313
-
314
- // ============================================================
315
- // CLI
316
- // ============================================================
317
-
318
- if (require.main === module) {
319
- const args = process.argv.slice(2);
320
- const command = args[0];
321
-
322
- switch (command) {
323
- case 'analyze': {
324
- const tasks = loadPendingTasks();
325
- if (tasks.length === 0) {
326
- console.log('No pending tasks found in ready.json');
327
- process.exit(0);
328
- }
329
-
330
- const analysis = analyzeParallelPotential(tasks);
331
- console.log('\n📊 Parallel Execution Analysis\n');
332
- console.log(`Total tasks: ${analysis.totalTasks || tasks.length}`);
333
-
334
- if (analysis.canParallelize) {
335
- console.log(`Can parallelize: ${analysis.parallelCount} tasks`);
336
- console.log(`\nExecution waves:`);
337
- analysis.waves.forEach((wave, i) => {
338
- console.log(` Wave ${i + 1}: ${wave.join(', ')}`);
339
- });
340
- console.log(`\nEfficiency:`);
341
- console.log(` Sequential time: ${analysis.efficiencyGain.sequential} units`);
342
- console.log(` Parallel time: ${analysis.efficiencyGain.parallel} units`);
343
- console.log(` Savings: ${analysis.efficiencyGain.percentageGain}%`);
344
-
345
- if (analysis.fileOverlaps.length > 0) {
346
- console.log(`\n⚠️ File overlaps:`);
347
- analysis.fileOverlaps.forEach(o => {
348
- console.log(` ${o.file}: ${o.tasks.join(', ')} (${o.severity})`);
349
- });
350
- }
351
-
352
- console.log('\n' + analysis.recommendation);
353
- } else {
354
- console.log(`Cannot parallelize: ${analysis.reason}`);
355
- console.log(analysis.message);
356
- }
357
- break;
358
- }
359
-
360
- case 'suggest': {
361
- const tasks = loadPendingTasks();
362
- const result = shouldSuggestParallel(tasks);
363
-
364
- if (result.suggest) {
365
- console.log(result.message);
366
- } else {
367
- console.log(`Parallel execution not suggested: ${result.reason}`);
368
- }
369
- break;
370
- }
371
-
372
- case 'config': {
373
- const config = getParallelConfig();
374
- console.log('\n⚙️ Parallel Execution Configuration\n');
375
- console.log(JSON.stringify(config, null, 2));
376
- break;
377
- }
378
-
379
- default:
380
- console.log(`
381
- Wogi Flow - Parallel Execution Detector
382
-
383
- Usage:
384
- node flow-parallel-detector.js <command>
385
-
386
- Commands:
387
- analyze Analyze pending tasks for parallel potential
388
- suggest Check if parallel execution should be suggested
389
- config Show parallel execution configuration
390
-
391
- Configuration (config.json):
392
- parallel.enabled: true Enable parallel execution
393
- parallel.autoDetect: true Auto-detect parallel opportunities
394
- parallel.autoSuggest: true Suggest parallel execution to user
395
- parallel.autoApprove: false Auto-execute without approval
396
- parallel.minTasksForParallel: 2 Minimum tasks to trigger
397
- `);
398
- }
399
- }