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,301 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Project Status Overview
5
+ *
6
+ * Shows task counts, features, bugs, components, and config summary.
7
+ *
8
+ * Usage:
9
+ * flow status Human-readable status
10
+ * flow status --json JSON output for programmatic access
11
+ */
12
+
13
+ const path = require('path');
14
+
15
+ const {
16
+ PATHS,
17
+ PROJECT_ROOT,
18
+ fileExists,
19
+ dirExists,
20
+ getTaskCounts,
21
+ getConfig,
22
+ getReadyData,
23
+ countRequestLogEntries,
24
+ countAppMapComponents,
25
+ getGitStatus,
26
+ listDirs,
27
+ listFiles,
28
+ parseFlags,
29
+ outputJson,
30
+ printHeader,
31
+ printSection,
32
+ color
33
+ } = require('./flow-utils');
34
+
35
+ /**
36
+ * Collect all status data
37
+ */
38
+ function collectStatus() {
39
+ const status = {
40
+ tasks: { ready: 0, inProgress: 0, blocked: 0, recentlyCompleted: 0 },
41
+ features: [],
42
+ bugs: [],
43
+ components: 0,
44
+ requestLog: 0,
45
+ git: { isRepo: false, branch: null, uncommitted: 0 },
46
+ cli: { type: 'claude-code', bridgeStatus: 'unknown' },
47
+ config: {},
48
+ recommendation: {}
49
+ };
50
+
51
+ // Task counts
52
+ if (fileExists(PATHS.ready)) {
53
+ status.tasks = getTaskCounts();
54
+ }
55
+
56
+ // Features
57
+ if (dirExists(PATHS.changes)) {
58
+ status.features = listDirs(PATHS.changes);
59
+ }
60
+
61
+ // Bugs
62
+ if (dirExists(PATHS.bugs)) {
63
+ status.bugs = listFiles(PATHS.bugs, '.md').filter(f => !f.startsWith('.'));
64
+ }
65
+
66
+ // Components
67
+ if (fileExists(PATHS.appMap)) {
68
+ status.components = countAppMapComponents();
69
+ }
70
+
71
+ // Request log
72
+ if (fileExists(PATHS.requestLog)) {
73
+ status.requestLog = countRequestLogEntries();
74
+ }
75
+
76
+ // Git
77
+ status.git = getGitStatus();
78
+
79
+ // CLI
80
+ if (fileExists(PATHS.config)) {
81
+ try {
82
+ const config = getConfig();
83
+ status.cli.type = config.cli?.type || 'claude-code';
84
+
85
+ // Check bridge status
86
+ const bridgesDir = path.join(PROJECT_ROOT, '.workflow', 'bridges');
87
+ const modelsDir = path.join(PROJECT_ROOT, '.workflow', 'models');
88
+
89
+ if (dirExists(bridgesDir) && dirExists(modelsDir)) {
90
+ status.cli.bridgeStatus = 'configured';
91
+ } else if (config.cli?.type) {
92
+ status.cli.bridgeStatus = 'partial';
93
+ } else {
94
+ status.cli.bridgeStatus = 'legacy';
95
+ }
96
+ } catch {
97
+ status.cli.bridgeStatus = 'error';
98
+ }
99
+ }
100
+
101
+ // Config
102
+ if (fileExists(PATHS.config)) {
103
+ const config = getConfig();
104
+ status.config = {
105
+ mandatoryAfterTask: config.mandatorySteps?.afterTask || [],
106
+ strictMode: config.enforcement?.strictMode || false,
107
+ priorities: config.priorities || {}
108
+ };
109
+ }
110
+
111
+ // Recommendation
112
+ status.recommendation = getRecommendation();
113
+
114
+ return status;
115
+ }
116
+
117
+ function main() {
118
+ const { flags } = parseFlags(process.argv.slice(2));
119
+
120
+ const status = collectStatus();
121
+
122
+ // JSON output - exit early to avoid human-readable output
123
+ if (flags.json) {
124
+ outputJson({
125
+ success: true,
126
+ ...status
127
+ });
128
+ return;
129
+ }
130
+
131
+ // Human-readable output - use already collected status data
132
+ printHeader('PROJECT STATUS');
133
+
134
+ // Task counts (use status.tasks from collectStatus)
135
+ if (status.tasks.ready > 0 || status.tasks.inProgress > 0 || status.tasks.blocked > 0 || status.tasks.recentlyCompleted > 0) {
136
+ printSection('Tasks');
137
+ console.log(` Ready: ${status.tasks.ready}`);
138
+ console.log(` In Progress: ${status.tasks.inProgress}`);
139
+ console.log(` Blocked: ${status.tasks.blocked}`);
140
+ console.log(` Recently Done: ${status.tasks.recentlyCompleted}`);
141
+ console.log('');
142
+ }
143
+
144
+ // Features (use status.features from collectStatus)
145
+ if (status.features.length > 0 || dirExists(PATHS.changes)) {
146
+ printSection('Features');
147
+ console.log(` Active: ${status.features.length}`);
148
+ if (status.features.length > 0) {
149
+ for (const feature of status.features) {
150
+ console.log(` • ${feature}`);
151
+ }
152
+ }
153
+ console.log('');
154
+ }
155
+
156
+ // Bugs (use status.bugs from collectStatus)
157
+ if (status.bugs.length > 0 || dirExists(PATHS.bugs)) {
158
+ printSection('Bugs');
159
+ console.log(` Open: ${status.bugs.length}`);
160
+ console.log('');
161
+ }
162
+
163
+ // Components (use status.components from collectStatus)
164
+ if (status.components > 0) {
165
+ printSection('Components');
166
+ console.log(` Mapped: ${status.components}`);
167
+ console.log('');
168
+ }
169
+
170
+ // Request log (use status.requestLog from collectStatus)
171
+ if (status.requestLog > 0) {
172
+ printSection('Request Log');
173
+ console.log(` Entries: ${status.requestLog}`);
174
+ console.log('');
175
+ }
176
+
177
+ // Git status (use status.git from collectStatus)
178
+ if (status.git.isRepo) {
179
+ printSection('Git');
180
+ console.log(` Branch: ${status.git.branch || 'unknown'}`);
181
+ console.log(` Uncommitted: ${status.git.uncommitted || 0} files`);
182
+ console.log('');
183
+ }
184
+
185
+ // CLI status
186
+ printSection('CLI');
187
+ const cliNames = {
188
+ 'claude-code': 'Claude Code',
189
+ 'gemini-cli': 'Gemini CLI',
190
+ 'opencode': 'OpenCode'
191
+ };
192
+ const bridgeColors = {
193
+ 'configured': 'green',
194
+ 'partial': 'yellow',
195
+ 'legacy': 'yellow',
196
+ 'error': 'red'
197
+ };
198
+ console.log(` Type: ${cliNames[status.cli.type] || status.cli.type}`);
199
+ console.log(` Bridge: ${color(bridgeColors[status.cli.bridgeStatus] || 'dim', status.cli.bridgeStatus)}`);
200
+ console.log('');
201
+
202
+ // Config summary (use status.config from collectStatus)
203
+ if (status.config.mandatoryAfterTask) {
204
+ printSection('Config');
205
+ const afterTask = status.config.mandatoryAfterTask;
206
+
207
+ if (afterTask.length > 0) {
208
+ console.log(` After task: ${afterTask.join(', ')}`);
209
+ } else {
210
+ console.log(' After task: (none)');
211
+ }
212
+ }
213
+
214
+ // Action-oriented recommendation (use status.recommendation from collectStatus)
215
+ printSection('📌 Recommended Next Action');
216
+ console.log(` ${status.recommendation.action}`);
217
+ if (status.recommendation.command) {
218
+ console.log(color('dim', ` Run: ${status.recommendation.command}`));
219
+ }
220
+
221
+ console.log('');
222
+ console.log(color('cyan', '═'.repeat(50)));
223
+ }
224
+
225
+ /**
226
+ * Get recommended next action based on current state
227
+ */
228
+ function getRecommendation() {
229
+ const config = getConfig();
230
+
231
+ // Check for uncommitted changes first
232
+ const git = getGitStatus();
233
+ if (git.isRepo && git.uncommitted > 0) {
234
+ return {
235
+ action: `Commit ${git.uncommitted} uncommitted file(s)`,
236
+ command: 'git add -A && git commit -m "message"'
237
+ };
238
+ }
239
+
240
+ // Check task state
241
+ if (!fileExists(PATHS.ready)) {
242
+ return {
243
+ action: 'Initialize workflow to start tracking tasks',
244
+ command: 'flow install'
245
+ };
246
+ }
247
+
248
+ const data = getReadyData();
249
+
250
+ // Check in-progress tasks first
251
+ const inProgress = data.inProgress || [];
252
+ if (inProgress.length > 0) {
253
+ const task = inProgress[0];
254
+ const taskId = typeof task === 'string' ? task : task.id;
255
+ const taskTitle = typeof task === 'object' ? (task.title || task.description || '') : '';
256
+ return {
257
+ action: `Continue working on ${taskId}${taskTitle ? `: ${taskTitle.slice(0, 40)}` : ''}`,
258
+ command: null
259
+ };
260
+ }
261
+
262
+ // Check ready tasks
263
+ const ready = data.ready || [];
264
+ if (ready.length > 0) {
265
+ // Find highest priority task (P0=critical, P1=high, P2=medium, P3=low, P4=lowest)
266
+ const priorityOrder = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
267
+ const sorted = [...ready].sort((a, b) => {
268
+ const aPriority = priorityOrder[a.priority] ?? 2;
269
+ const bPriority = priorityOrder[b.priority] ?? 2;
270
+ return aPriority - bPriority;
271
+ });
272
+
273
+ const task = sorted[0];
274
+ const taskId = typeof task === 'string' ? task : task.id;
275
+ const taskTitle = typeof task === 'object' ? (task.title || task.description || '') : '';
276
+ const priority = typeof task === 'object' && task.priority ? ` [${task.priority}]` : '';
277
+ return {
278
+ action: `Start ${taskId}${priority}${taskTitle ? `: ${taskTitle.slice(0, 35)}` : ''}`,
279
+ command: `flow start ${taskId}`
280
+ };
281
+ }
282
+
283
+ // Check blocked tasks
284
+ const blocked = data.blocked || [];
285
+ if (blocked.length > 0) {
286
+ return {
287
+ action: `${blocked.length} task(s) are blocked - resolve dependencies`,
288
+ command: 'flow ready'
289
+ };
290
+ }
291
+
292
+ // No tasks at all
293
+ return {
294
+ action: 'Create a new task to work on',
295
+ command: 'flow story "Your task title"'
296
+ };
297
+ }
298
+
299
+ if (require.main === module) {
300
+ main();
301
+ }
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Browser Test Step
5
+ *
6
+ * Workflow step wrapper for browser test suggestions.
7
+ * Suggests running browser tests for UI changes.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { getProjectRoot, colors, getConfig } = require('./flow-utils');
13
+
14
+ const PROJECT_ROOT = getProjectRoot();
15
+ const TESTS_DIR = path.join(PROJECT_ROOT, '.workflow', 'tests', 'flows');
16
+
17
+ /**
18
+ * Run browser test step
19
+ *
20
+ * @param {object} options
21
+ * @param {string[]} options.files - Files modified in this task
22
+ * @param {string} options.taskId - Current task ID
23
+ * @param {object} options.stepConfig - Step configuration
24
+ * @param {string} options.mode - Step mode (block/warn/prompt/auto)
25
+ * @returns {object} - { passed: boolean, message: string, suggestion?: string }
26
+ */
27
+ async function run(options = {}) {
28
+ const { files = [], mode, stepConfig = {} } = options;
29
+
30
+ // Check if we have any UI files
31
+ const uiExtensions = ['.tsx', '.jsx', '.vue', '.svelte'];
32
+ const uiFiles = files.filter(f => uiExtensions.some(ext => f.endsWith(ext)));
33
+
34
+ if (uiFiles.length === 0) {
35
+ return { passed: true, message: 'No UI files modified' };
36
+ }
37
+
38
+ // Check if browser tests exist
39
+ if (!fs.existsSync(TESTS_DIR)) {
40
+ return {
41
+ passed: true,
42
+ message: 'No browser test flows configured',
43
+ suggestion: 'Consider adding browser tests in .workflow/tests/flows/',
44
+ };
45
+ }
46
+
47
+ // Find available test flows
48
+ const testFlows = fs.readdirSync(TESTS_DIR)
49
+ .filter(f => f.endsWith('.json') || f.endsWith('.yaml') || f.endsWith('.yml'));
50
+
51
+ if (testFlows.length === 0) {
52
+ return {
53
+ passed: true,
54
+ message: 'No browser test flows found',
55
+ suggestion: 'Add test flows to .workflow/tests/flows/',
56
+ };
57
+ }
58
+
59
+ // In prompt mode, suggest tests
60
+ if (mode === 'prompt') {
61
+ console.log(colors.yellow + '\n UI files modified:' + colors.reset);
62
+ uiFiles.forEach(f => console.log(` - ${f}`));
63
+ console.log(colors.yellow + '\n Available browser tests:' + colors.reset);
64
+ testFlows.forEach(f => console.log(` - ${f.replace(/\.(json|ya?ml)$/, '')}`));
65
+ console.log(colors.cyan + '\n Run with: /wogi-test-browser <flow-name>' + colors.reset);
66
+
67
+ return {
68
+ passed: true,
69
+ message: 'Browser tests suggested',
70
+ suggestion: `/wogi-test-browser ${testFlows[0].replace(/\.(json|ya?ml)$/, '')}`,
71
+ };
72
+ }
73
+
74
+ // In auto mode, we could run tests automatically
75
+ // For now, just mark as passed with suggestion
76
+ return {
77
+ passed: true,
78
+ message: `${uiFiles.length} UI file(s) modified, browser tests available`,
79
+ suggestion: 'Run browser tests to verify UI changes',
80
+ };
81
+ }
82
+
83
+ module.exports = { run };
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Update Changelog Step
5
+ *
6
+ * Prompts or auto-generates changelog entries.
7
+ * Follows Keep a Changelog format.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { getProjectRoot, colors, getConfig } = require('./flow-utils');
13
+
14
+ const PROJECT_ROOT = getProjectRoot();
15
+ const CHANGELOG_PATH = path.join(PROJECT_ROOT, 'CHANGELOG.md');
16
+
17
+ /**
18
+ * Run update changelog step
19
+ *
20
+ * @param {object} options
21
+ * @param {string} options.taskId - Current task ID
22
+ * @param {string} options.taskTitle - Task title/description
23
+ * @param {string} options.taskType - Task type (feature/bugfix/refactor)
24
+ * @param {string[]} options.files - Files modified
25
+ * @param {object} options.stepConfig - Step configuration
26
+ * @param {string} options.mode - Step mode (block/warn/prompt/auto)
27
+ * @returns {object} - { passed: boolean, message: string, entry?: string }
28
+ */
29
+ async function run(options = {}) {
30
+ const { taskId, taskTitle, taskType, files = [], mode, stepConfig = {} } = options;
31
+
32
+ // Determine changelog category
33
+ const category = getChangelogCategory(taskType, taskTitle, files);
34
+
35
+ // Generate suggested entry
36
+ const entry = generateEntry(taskId, taskTitle, category, files);
37
+
38
+ // In prompt mode, show suggestion
39
+ if (mode === 'prompt') {
40
+ console.log(colors.yellow + '\n Suggested changelog entry:' + colors.reset);
41
+ console.log(colors.gray + ` ### ${category}` + colors.reset);
42
+ console.log(` - ${entry}`);
43
+
44
+ if (!fs.existsSync(CHANGELOG_PATH)) {
45
+ console.log(colors.yellow + '\n CHANGELOG.md does not exist - will create if approved' + colors.reset);
46
+ }
47
+
48
+ return {
49
+ passed: true,
50
+ message: 'Changelog entry suggested',
51
+ entry,
52
+ category,
53
+ suggestion: `Add to CHANGELOG.md under ${category}`,
54
+ };
55
+ }
56
+
57
+ // In auto mode, add the entry
58
+ if (mode === 'auto') {
59
+ const result = addToChangelog(entry, category);
60
+ if (result.success) {
61
+ return {
62
+ passed: true,
63
+ message: 'Changelog entry added',
64
+ entry,
65
+ category,
66
+ };
67
+ } else {
68
+ return {
69
+ passed: false,
70
+ message: result.error,
71
+ };
72
+ }
73
+ }
74
+
75
+ // In warn mode, just report
76
+ return {
77
+ passed: true,
78
+ message: `Changelog entry ready: ${entry}`,
79
+ entry,
80
+ category,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Determine changelog category from task type
86
+ */
87
+ function getChangelogCategory(taskType, taskTitle, files) {
88
+ // Explicit type mapping
89
+ const typeMap = {
90
+ feature: 'Added',
91
+ feat: 'Added',
92
+ add: 'Added',
93
+ bugfix: 'Fixed',
94
+ fix: 'Fixed',
95
+ refactor: 'Changed',
96
+ change: 'Changed',
97
+ update: 'Changed',
98
+ remove: 'Removed',
99
+ delete: 'Removed',
100
+ deprecate: 'Deprecated',
101
+ security: 'Security',
102
+ };
103
+
104
+ if (taskType && typeMap[taskType.toLowerCase()]) {
105
+ return typeMap[taskType.toLowerCase()];
106
+ }
107
+
108
+ // Infer from title
109
+ const titleLower = (taskTitle || '').toLowerCase();
110
+
111
+ if (titleLower.includes('add') || titleLower.includes('implement') || titleLower.includes('create')) {
112
+ return 'Added';
113
+ }
114
+ if (titleLower.includes('fix') || titleLower.includes('bug') || titleLower.includes('resolve')) {
115
+ return 'Fixed';
116
+ }
117
+ if (titleLower.includes('remove') || titleLower.includes('delete')) {
118
+ return 'Removed';
119
+ }
120
+ if (titleLower.includes('refactor') || titleLower.includes('update') || titleLower.includes('change')) {
121
+ return 'Changed';
122
+ }
123
+ if (titleLower.includes('deprecate')) {
124
+ return 'Deprecated';
125
+ }
126
+ if (titleLower.includes('security') || titleLower.includes('vulnerability')) {
127
+ return 'Security';
128
+ }
129
+
130
+ // Default to Changed
131
+ return 'Changed';
132
+ }
133
+
134
+ /**
135
+ * Generate a changelog entry
136
+ */
137
+ function generateEntry(taskId, taskTitle, category, files) {
138
+ // Clean up title
139
+ let entry = taskTitle || 'Update';
140
+
141
+ // Remove task ID prefix if present
142
+ entry = entry.replace(/^(TASK-\d+:?\s*)/i, '');
143
+
144
+ // Capitalize first letter
145
+ entry = entry.charAt(0).toUpperCase() + entry.slice(1);
146
+
147
+ // Add task reference
148
+ if (taskId) {
149
+ entry += ` (${taskId})`;
150
+ }
151
+
152
+ return entry;
153
+ }
154
+
155
+ /**
156
+ * Add entry to CHANGELOG.md
157
+ */
158
+ function addToChangelog(entry, category) {
159
+ try {
160
+ let content;
161
+
162
+ if (fs.existsSync(CHANGELOG_PATH)) {
163
+ content = fs.readFileSync(CHANGELOG_PATH, 'utf8');
164
+ } else {
165
+ // Create new changelog
166
+ content = `# Changelog
167
+
168
+ All notable changes to this project will be documented in this file.
169
+
170
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
171
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
172
+
173
+ ## [Unreleased]
174
+
175
+ `;
176
+ }
177
+
178
+ // Find or create [Unreleased] section
179
+ const unreleasedMatch = content.match(/## \[Unreleased\]\n/);
180
+ if (!unreleasedMatch) {
181
+ // Add unreleased section after header
182
+ const headerEnd = content.indexOf('\n\n') + 2;
183
+ content = content.slice(0, headerEnd) + '## [Unreleased]\n\n' + content.slice(headerEnd);
184
+ }
185
+
186
+ // Find or create category under Unreleased
187
+ const unreleasedIndex = content.indexOf('## [Unreleased]');
188
+ const nextVersionMatch = content.slice(unreleasedIndex + 15).match(/\n## \[/);
189
+ const unreleasedEnd = nextVersionMatch
190
+ ? unreleasedIndex + 15 + nextVersionMatch.index
191
+ : content.length;
192
+
193
+ const unreleasedSection = content.slice(unreleasedIndex, unreleasedEnd);
194
+
195
+ // Check if category exists
196
+ const categoryRegex = new RegExp(`### ${category}\\n`);
197
+ const categoryMatch = unreleasedSection.match(categoryRegex);
198
+
199
+ if (categoryMatch) {
200
+ // Add under existing category
201
+ const categoryIndex = unreleasedIndex + categoryMatch.index + categoryMatch[0].length;
202
+ content = content.slice(0, categoryIndex) + `- ${entry}\n` + content.slice(categoryIndex);
203
+ } else {
204
+ // Add new category section
205
+ const insertIndex = unreleasedIndex + 16; // After "## [Unreleased]\n"
206
+ content = content.slice(0, insertIndex) + `\n### ${category}\n- ${entry}\n` + content.slice(insertIndex);
207
+ }
208
+
209
+ fs.writeFileSync(CHANGELOG_PATH, content);
210
+ return { success: true };
211
+
212
+ } catch (error) {
213
+ return { success: false, error: error.message };
214
+ }
215
+ }
216
+
217
+ module.exports = { run, getChangelogCategory, generateEntry, addToChangelog };