wogiflow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Output Utilities
|
|
5
|
+
*
|
|
6
|
+
* Terminal output formatting with colors and standard message types.
|
|
7
|
+
* Extracted from flow-utils.js for better modularity.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const { colors, color, success, warn, error, info } = require('./flow-output');
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================================
|
|
14
|
+
// Colors (ANSI escape codes)
|
|
15
|
+
// ============================================================
|
|
16
|
+
|
|
17
|
+
const colors = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
bold: '\x1b[1m',
|
|
20
|
+
dim: '\x1b[2m',
|
|
21
|
+
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
blue: '\x1b[34m',
|
|
26
|
+
magenta: '\x1b[35m',
|
|
27
|
+
cyan: '\x1b[36m',
|
|
28
|
+
white: '\x1b[37m',
|
|
29
|
+
|
|
30
|
+
bgRed: '\x1b[41m',
|
|
31
|
+
bgGreen: '\x1b[42m',
|
|
32
|
+
bgYellow: '\x1b[43m',
|
|
33
|
+
bgBlue: '\x1b[44m',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Colorize text for terminal output
|
|
38
|
+
*/
|
|
39
|
+
function color(colorName, text) {
|
|
40
|
+
if (process.env.DEBUG && !colors[colorName]) {
|
|
41
|
+
console.warn(`[DEBUG] Unknown color: "${colorName}"`);
|
|
42
|
+
}
|
|
43
|
+
return `${colors[colorName] || ''}${text}${colors.reset}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Print colored output
|
|
48
|
+
*/
|
|
49
|
+
function print(colorName, text) {
|
|
50
|
+
console.log(color(colorName, text));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Print a styled header
|
|
55
|
+
*/
|
|
56
|
+
function printHeader(title) {
|
|
57
|
+
console.log(color('cyan', '═'.repeat(50)));
|
|
58
|
+
console.log(color('cyan', ` ${title}`));
|
|
59
|
+
console.log(color('cyan', '═'.repeat(50)));
|
|
60
|
+
console.log('');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Print a section title
|
|
65
|
+
*/
|
|
66
|
+
function printSection(title) {
|
|
67
|
+
console.log(color('cyan', title));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================
|
|
71
|
+
// Standard Messaging Functions
|
|
72
|
+
// ============================================================
|
|
73
|
+
//
|
|
74
|
+
// STANDARD: All scripts should use these functions for consistent output:
|
|
75
|
+
// success(msg) - Green checkmark ✓ for successful operations
|
|
76
|
+
// warn(msg) - Yellow warning ⚠ for non-fatal issues
|
|
77
|
+
// error(msg) - Red X ✗ for errors (use before process.exit(1))
|
|
78
|
+
// info(msg) - Cyan info ℹ for informational messages
|
|
79
|
+
//
|
|
80
|
+
// Import with: const { success, warn, error, info } = require('./flow-output');
|
|
81
|
+
//
|
|
82
|
+
// AVOID: Direct console.log with color() for status messages.
|
|
83
|
+
// ============================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Print success message
|
|
87
|
+
*/
|
|
88
|
+
function success(message) {
|
|
89
|
+
console.log(`${color('green', '✓')} ${message}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Print warning message
|
|
94
|
+
*/
|
|
95
|
+
function warn(message) {
|
|
96
|
+
console.log(`${color('yellow', '⚠')} ${message}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Print error message
|
|
101
|
+
*/
|
|
102
|
+
function error(message) {
|
|
103
|
+
console.log(`${color('red', '✗')} ${message}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Print info message
|
|
108
|
+
*/
|
|
109
|
+
function info(message) {
|
|
110
|
+
console.log(`${color('cyan', 'ℹ')} ${message}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
colors,
|
|
115
|
+
color,
|
|
116
|
+
print,
|
|
117
|
+
printHeader,
|
|
118
|
+
printSection,
|
|
119
|
+
success,
|
|
120
|
+
warn,
|
|
121
|
+
error,
|
|
122
|
+
info,
|
|
123
|
+
};
|
|
@@ -0,0 +1,399 @@
|
|
|
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
|
+
}
|