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.
Files changed (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. package/templates/trace.md +69 -0
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * flow-bridge.js - CLI Bridge Management
5
+ *
6
+ * Commands:
7
+ * flow bridge sync - Sync .workflow/ to CLI-specific folder
8
+ * flow bridge status - Show current bridge configuration
9
+ * flow bridge list - List available CLI bridges
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ // Colors
16
+ const colors = {
17
+ green: '\x1b[32m',
18
+ yellow: '\x1b[33m',
19
+ cyan: '\x1b[36m',
20
+ red: '\x1b[31m',
21
+ bold: '\x1b[1m',
22
+ reset: '\x1b[0m'
23
+ };
24
+
25
+ const PROJECT_ROOT = process.cwd();
26
+ const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
27
+ const BRIDGES_DIR = path.join(WORKFLOW_DIR, 'bridges');
28
+ const CONFIG_PATH = path.join(WORKFLOW_DIR, 'config.json');
29
+
30
+ /**
31
+ * Read config file
32
+ */
33
+ function getConfig() {
34
+ if (!fs.existsSync(CONFIG_PATH)) {
35
+ console.error(`${colors.red}Error:${colors.reset} Config not found. Run 'flow install' first.`);
36
+ process.exit(1);
37
+ }
38
+ try {
39
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
40
+ } catch (err) {
41
+ console.error(`${colors.red}Error:${colors.reset} Invalid JSON in config.json: ${err.message}`);
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Get CLI type from config
48
+ */
49
+ function getCliType() {
50
+ const config = getConfig();
51
+ return config.cli?.type || 'claude-code';
52
+ }
53
+
54
+ /**
55
+ * List available bridges
56
+ */
57
+ function listBridges() {
58
+ console.log(`${colors.bold}Available CLI Bridges:${colors.reset}`);
59
+ console.log('');
60
+
61
+ const availableBridges = [
62
+ {
63
+ id: 'claude-code',
64
+ name: 'Claude Code',
65
+ status: 'implemented',
66
+ folder: '.claude',
67
+ rulesFile: 'CLAUDE.md'
68
+ },
69
+ {
70
+ id: 'gemini-cli',
71
+ name: 'Gemini CLI',
72
+ status: 'planned',
73
+ folder: '.gemini',
74
+ rulesFile: 'GEMINI.md'
75
+ },
76
+ {
77
+ id: 'opencode',
78
+ name: 'OpenCode',
79
+ status: 'planned',
80
+ folder: '.opencode',
81
+ rulesFile: 'OPENCODE.md'
82
+ },
83
+ {
84
+ id: 'other',
85
+ name: 'Other / Manual',
86
+ status: 'manual',
87
+ folder: 'N/A',
88
+ rulesFile: 'N/A'
89
+ }
90
+ ];
91
+
92
+ const currentCli = getCliType();
93
+
94
+ for (const bridge of availableBridges) {
95
+ const isCurrent = bridge.id === currentCli;
96
+ const statusColor = bridge.status === 'implemented' ? colors.green :
97
+ bridge.status === 'planned' ? colors.yellow : colors.cyan;
98
+ const indicator = isCurrent ? `${colors.green}→${colors.reset}` : ' ';
99
+
100
+ console.log(` ${indicator} ${colors.bold}${bridge.name}${colors.reset} (${bridge.id})`);
101
+ console.log(` Status: ${statusColor}${bridge.status}${colors.reset}`);
102
+ console.log(` Folder: ${bridge.folder}`);
103
+ console.log(` Rules: ${bridge.rulesFile}`);
104
+ console.log('');
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Show bridge status
110
+ */
111
+ function showStatus() {
112
+ const config = getConfig();
113
+ const cliType = config.cli?.type || 'claude-code';
114
+ const bridgeConfig = config.cli?.bridge || {};
115
+
116
+ console.log(`${colors.bold}CLI Bridge Status${colors.reset}`);
117
+ console.log('');
118
+ console.log(` CLI Type: ${colors.cyan}${cliType}${colors.reset}`);
119
+ console.log(` Auto Sync: ${bridgeConfig.autoSync ? colors.green + 'enabled' : colors.yellow + 'disabled'}${colors.reset}`);
120
+ console.log(` Sync on Change: ${bridgeConfig.syncOnConfigChange ? colors.green + 'enabled' : colors.yellow + 'disabled'}${colors.reset}`);
121
+ console.log('');
122
+
123
+ // Check if bridge file exists
124
+ const bridgePath = path.join(BRIDGES_DIR, `${cliType.replace('-', '-')}-bridge.js`);
125
+ const bridgeFileMap = {
126
+ 'claude-code': 'claude-bridge.js',
127
+ 'gemini-cli': 'gemini-bridge.js',
128
+ 'opencode': 'opencode-bridge.js'
129
+ };
130
+
131
+ const bridgeFile = bridgeFileMap[cliType];
132
+ if (bridgeFile) {
133
+ const bridgeExists = fs.existsSync(path.join(BRIDGES_DIR, bridgeFile));
134
+ console.log(` Bridge File: ${bridgeExists ? colors.green + '✓ ' + bridgeFile : colors.yellow + '○ not implemented'}${colors.reset}`);
135
+ }
136
+
137
+ // Check CLI folder status
138
+ const cliFolders = {
139
+ 'claude-code': '.claude',
140
+ 'gemini-cli': '.gemini',
141
+ 'opencode': '.opencode'
142
+ };
143
+
144
+ const cliFolder = cliFolders[cliType];
145
+ if (cliFolder) {
146
+ const folderExists = fs.existsSync(path.join(PROJECT_ROOT, cliFolder));
147
+ console.log(` CLI Folder: ${folderExists ? colors.green + '✓ ' + cliFolder + '/' : colors.yellow + '○ ' + cliFolder + '/ (not created)'}${colors.reset}`);
148
+ }
149
+
150
+ console.log('');
151
+ }
152
+
153
+ /**
154
+ * Sync bridge
155
+ */
156
+ async function syncBridge(options = {}) {
157
+ const verbose = options.verbose || process.argv.includes('--verbose') || process.argv.includes('-v');
158
+
159
+ console.log(`${colors.cyan}Syncing CLI bridge...${colors.reset}`);
160
+ console.log('');
161
+
162
+ try {
163
+ // Try to load the bridges module
164
+ let bridges;
165
+ try {
166
+ bridges = require(path.join(BRIDGES_DIR, 'index.js'));
167
+ } catch (err) {
168
+ console.error(`${colors.red}Error:${colors.reset} Bridges module not found.`);
169
+ console.error('Make sure .workflow/bridges/index.js exists.');
170
+ process.exit(1);
171
+ }
172
+
173
+ const result = await bridges.syncBridge({ verbose, projectDir: PROJECT_ROOT });
174
+
175
+ if (result.success) {
176
+ console.log(`${colors.green}✓ Bridge sync complete${colors.reset}`);
177
+ console.log('');
178
+ console.log(` CLI Type: ${result.cliType}`);
179
+ console.log(` Folder: ${result.cliFolder}`);
180
+ console.log(` Synced: ${result.synced.join(', ')}`);
181
+ console.log(` Duration: ${result.duration}ms`);
182
+ } else {
183
+ console.log(`${colors.yellow}⚠ Bridge sync completed with issues${colors.reset}`);
184
+ console.log('');
185
+ if (result.error) {
186
+ console.log(` Error: ${result.error}`);
187
+ }
188
+ if (result.errors && result.errors.length > 0) {
189
+ for (const err of result.errors) {
190
+ console.log(` ${colors.yellow}○${colors.reset} ${err.step}: ${err.error}`);
191
+ }
192
+ }
193
+ }
194
+ } catch (error) {
195
+ console.error(`${colors.red}Error:${colors.reset} ${error.message}`);
196
+ process.exit(1);
197
+ }
198
+
199
+ console.log('');
200
+ }
201
+
202
+ // Main
203
+ const command = process.argv[2] || 'status';
204
+
205
+ switch (command) {
206
+ case 'sync':
207
+ syncBridge();
208
+ break;
209
+ case 'status':
210
+ showStatus();
211
+ break;
212
+ case 'list':
213
+ listBridges();
214
+ break;
215
+ default:
216
+ console.log('Usage: flow bridge [sync|status|list]');
217
+ console.log('');
218
+ console.log('Commands:');
219
+ console.log(' sync Sync .workflow/ config to CLI-specific folder');
220
+ console.log(' status Show current bridge configuration');
221
+ console.log(' list List available CLI bridges');
222
+ process.exit(1);
223
+ }
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Browser Test Suggestion
5
+ *
6
+ * Detects UI tasks and suggests relevant browser test flows.
7
+ * Integrates with /wogi-test-browser command.
8
+ *
9
+ * Usage:
10
+ * Called automatically after task completion in flow-done.js
11
+ * Or manually: flow browser-suggest <task-id>
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { getProjectRoot, colors, getConfig } = require('./flow-utils');
17
+
18
+ const PROJECT_ROOT = getProjectRoot();
19
+ const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
20
+ const FLOWS_DIR = path.join(WORKFLOW_DIR, 'tests', 'flows');
21
+ const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
22
+
23
+ function log(color, ...args) {
24
+ console.log(colors[color] + args.join(' ') + colors.reset);
25
+ }
26
+
27
+ /**
28
+ * Check if a task is a UI task based on files it modified
29
+ */
30
+ function isUITask(taskData) {
31
+ const files = taskData?.files || [];
32
+
33
+ // UI file patterns
34
+ const uiPatterns = [
35
+ /\.tsx$/,
36
+ /\.jsx$/,
37
+ /\/components\//,
38
+ /\/pages\//,
39
+ /\/views\//,
40
+ /\/screens\//,
41
+ /\/ui\//,
42
+ /\.css$/,
43
+ /\.scss$/,
44
+ /\.styled\./,
45
+ ];
46
+
47
+ return files.some(file =>
48
+ uiPatterns.some(pattern => pattern.test(file))
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Get files changed by a task from various sources
54
+ */
55
+ function getTaskFiles(taskId, taskData) {
56
+ const files = new Set(taskData?.files || []);
57
+
58
+ // Try to get files from request-log
59
+ const logPath = path.join(STATE_DIR, 'request-log.md');
60
+ if (fs.existsSync(logPath)) {
61
+ try {
62
+ const logContent = fs.readFileSync(logPath, 'utf8');
63
+ const escapedTaskId = taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
64
+ const taskPattern = new RegExp(`### R-\\d+.*${escapedTaskId}[\\s\\S]*?Files:\\s*([^\\n]+)`, 'gi');
65
+
66
+ let match;
67
+ while ((match = taskPattern.exec(logContent)) !== null) {
68
+ const mentionedFiles = match[1].split(',').map(f => f.trim().replace(/`/g, ''));
69
+ mentionedFiles.forEach(f => files.add(f));
70
+ }
71
+ } catch (err) {
72
+ // Ignore errors reading log
73
+ }
74
+ }
75
+
76
+ // Try to get from git diff if task has branch info
77
+ if (taskData?.branch) {
78
+ try {
79
+ const { execSync } = require('child_process');
80
+ const diff = execSync(`git diff --name-only main...${taskData.branch}`, {
81
+ cwd: PROJECT_ROOT,
82
+ encoding: 'utf8',
83
+ stdio: ['pipe', 'pipe', 'pipe']
84
+ });
85
+ diff.split('\n').filter(Boolean).forEach(f => files.add(f));
86
+ } catch (err) {
87
+ // Git command failed, ignore
88
+ }
89
+ }
90
+
91
+ return Array.from(files);
92
+ }
93
+
94
+ /**
95
+ * Find browser test flows that match the task
96
+ */
97
+ function findMatchingFlows(taskId, taskData) {
98
+ if (!fs.existsSync(FLOWS_DIR)) {
99
+ return [];
100
+ }
101
+
102
+ const matchingFlows = [];
103
+ const files = getTaskFiles(taskId, taskData);
104
+ const taskTitle = (taskData?.title || taskData?.name || '').toLowerCase();
105
+
106
+ try {
107
+ const flowFiles = fs.readdirSync(FLOWS_DIR).filter(f => f.endsWith('.json'));
108
+
109
+ for (const flowFile of flowFiles) {
110
+ const flowPath = path.join(FLOWS_DIR, flowFile);
111
+ try {
112
+ const flow = JSON.parse(fs.readFileSync(flowPath, 'utf8'));
113
+ const flowName = flowFile.replace('.json', '');
114
+
115
+ // Check if flow explicitly references this task
116
+ if (flow.relatedTasks?.includes(taskId)) {
117
+ matchingFlows.push(flowName);
118
+ continue;
119
+ }
120
+
121
+ // Check if flow name matches any component in task files
122
+ const flowNameLower = flowName.toLowerCase();
123
+ for (const file of files) {
124
+ const fileName = path.basename(file, path.extname(file)).toLowerCase();
125
+ if (flowNameLower.includes(fileName) || fileName.includes(flowNameLower)) {
126
+ matchingFlows.push(flowName);
127
+ break;
128
+ }
129
+ }
130
+
131
+ // Check if flow name matches task title keywords
132
+ if (taskTitle) {
133
+ const titleWords = taskTitle.split(/\s+/).filter(w => w.length > 3);
134
+ if (titleWords.some(word => flowNameLower.includes(word))) {
135
+ if (!matchingFlows.includes(flowName)) {
136
+ matchingFlows.push(flowName);
137
+ }
138
+ }
139
+ }
140
+
141
+ // Check if flow tests components that were modified
142
+ if (flow.components) {
143
+ const flowComponents = flow.components.map(c => c.toLowerCase());
144
+ for (const file of files) {
145
+ const fileName = path.basename(file, path.extname(file)).toLowerCase();
146
+ if (flowComponents.some(c => c.includes(fileName) || fileName.includes(c))) {
147
+ if (!matchingFlows.includes(flowName)) {
148
+ matchingFlows.push(flowName);
149
+ }
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ } catch (err) {
155
+ // Invalid flow file, skip
156
+ }
157
+ }
158
+ } catch (err) {
159
+ // Can't read flows directory
160
+ }
161
+
162
+ return matchingFlows;
163
+ }
164
+
165
+ /**
166
+ * Main function to suggest browser tests for a task
167
+ */
168
+ function suggestBrowserTests(taskId, taskData = {}) {
169
+ const config = getConfig();
170
+ const browserConfig = config.browserTesting || {};
171
+
172
+ // Check if browser testing is enabled
173
+ if (!browserConfig.enabled) {
174
+ return { suggested: false, reason: 'Browser testing disabled' };
175
+ }
176
+
177
+ // Get task files and check if UI task
178
+ const files = getTaskFiles(taskId, taskData);
179
+ taskData = { ...taskData, files };
180
+
181
+ const isUI = isUITask(taskData);
182
+
183
+ // If runForUITasks is true, only suggest for UI tasks
184
+ if (browserConfig.runForUITasks && !isUI) {
185
+ return { suggested: false, reason: 'Not a UI task', isUITask: false };
186
+ }
187
+
188
+ // Find matching flows
189
+ const flows = findMatchingFlows(taskId, taskData);
190
+
191
+ if (flows.length === 0) {
192
+ return {
193
+ suggested: false,
194
+ reason: 'No matching test flows found',
195
+ isUITask: isUI,
196
+ hint: isUI ? 'Consider creating a test flow for this component' : null
197
+ };
198
+ }
199
+
200
+ return {
201
+ suggested: true,
202
+ flows,
203
+ isUITask: isUI,
204
+ autoRun: browserConfig.autoRun || false
205
+ };
206
+ }
207
+
208
+ /**
209
+ * List all available browser test flows
210
+ */
211
+ function listFlows() {
212
+ if (!fs.existsSync(FLOWS_DIR)) {
213
+ return [];
214
+ }
215
+
216
+ try {
217
+ return fs.readdirSync(FLOWS_DIR)
218
+ .filter(f => f.endsWith('.json'))
219
+ .map(f => f.replace('.json', ''));
220
+ } catch (err) {
221
+ return [];
222
+ }
223
+ }
224
+
225
+ // CLI handling
226
+ if (require.main === module) {
227
+ const args = process.argv.slice(2);
228
+
229
+ if (args.includes('--help') || args.includes('-h')) {
230
+ console.log(`
231
+ Wogi Flow - Browser Test Suggestion
232
+
233
+ Usage:
234
+ flow browser-suggest <task-id> Suggest browser tests for task
235
+ flow browser-suggest --list List all available test flows
236
+
237
+ Configuration (config.json):
238
+ "browserTesting": {
239
+ "enabled": true,
240
+ "runOnTaskComplete": true,
241
+ "runForUITasks": true,
242
+ "autoRun": false,
243
+ "timeout": 30000,
244
+ "screenshotOnFailure": true
245
+ }
246
+
247
+ Test flows are stored in: .workflow/tests/flows/*.json
248
+ `);
249
+ process.exit(0);
250
+ }
251
+
252
+ if (args.includes('--list')) {
253
+ const flows = listFlows();
254
+ if (flows.length === 0) {
255
+ log('yellow', 'No browser test flows found');
256
+ log('dim', `Create flows in: ${FLOWS_DIR}`);
257
+ } else {
258
+ log('cyan', 'Available browser test flows:');
259
+ flows.forEach(f => log('white', ` - ${f}`));
260
+ }
261
+ process.exit(0);
262
+ }
263
+
264
+ const taskId = args[0];
265
+ if (!taskId) {
266
+ console.log('Usage: flow browser-suggest <task-id>');
267
+ process.exit(1);
268
+ }
269
+
270
+ // Try to load task data from ready.json
271
+ let taskData = {};
272
+ const readyPath = path.join(STATE_DIR, 'ready.json');
273
+ if (fs.existsSync(readyPath)) {
274
+ try {
275
+ const ready = JSON.parse(fs.readFileSync(readyPath, 'utf8'));
276
+ const allTasks = [
277
+ ...(ready.ready || []),
278
+ ...(ready.inProgress || []),
279
+ ...(ready.recentlyCompleted || []),
280
+ ...(ready.blocked || [])
281
+ ];
282
+ const task = allTasks.find(t => t.id === taskId);
283
+ if (task) taskData = task;
284
+ } catch (err) {
285
+ // Ignore
286
+ }
287
+ }
288
+
289
+ const result = suggestBrowserTests(taskId, taskData);
290
+
291
+ console.log('');
292
+ if (result.suggested) {
293
+ log('green', `✓ Browser tests suggested for ${taskId}`);
294
+ log('cyan', '\nMatching test flows:');
295
+ result.flows.forEach(f => log('white', ` - ${f}`));
296
+ console.log('');
297
+ log('dim', `Run: /wogi-test-browser ${result.flows[0]}`);
298
+ if (result.flows.length > 1) {
299
+ log('dim', `Or run all: /wogi-test-browser all`);
300
+ }
301
+ } else {
302
+ log('yellow', `No browser tests suggested: ${result.reason}`);
303
+ if (result.hint) {
304
+ log('dim', result.hint);
305
+ }
306
+ }
307
+ }
308
+
309
+ // Export for use by other modules
310
+ module.exports = {
311
+ suggestBrowserTests,
312
+ isUITask,
313
+ findMatchingFlows,
314
+ getTaskFiles,
315
+ listFlows
316
+ };