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,615 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Auto Learning System
5
+ *
6
+ * Automatically captures learnings from:
7
+ * - Session reviews (code quality, security, architecture issues)
8
+ * - Bug fixes (patterns that were violated)
9
+ *
10
+ * Logs to feedback-patterns.md and suggests/auto-promotes to decisions.md
11
+ * when patterns reach the promotion threshold.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const {
17
+ PATHS,
18
+ PROJECT_ROOT,
19
+ fileExists,
20
+ getConfig,
21
+ color,
22
+ success,
23
+ info,
24
+ warn
25
+ } = require('./flow-utils');
26
+
27
+ // ============================================================
28
+ // Constants
29
+ // ============================================================
30
+
31
+ const FEEDBACK_PATTERNS_PATH = path.join(PATHS.state, 'feedback-patterns.md');
32
+ const DECISIONS_PATH = path.join(PATHS.state, 'decisions.md');
33
+
34
+ // Map issue types to normalized pattern names
35
+ const ISSUE_TO_PATTERN = {
36
+ // Security issues
37
+ 'security:eval_usage': 'no-eval',
38
+ 'security:innerhtml_xss': 'sanitize-html-output',
39
+ 'security:hardcoded_credentials': 'use-env-for-secrets',
40
+ 'security:missing_error_handling': 'handle-async-errors',
41
+ 'security:command_injection': 'sanitize-shell-commands',
42
+ 'security:sql_injection': 'use-parameterized-queries',
43
+
44
+ // Implementation issues
45
+ 'implementation:magic_numbers': 'use-named-constants',
46
+ 'implementation:deep_nesting': 'flatten-nested-logic',
47
+ 'implementation:duplicate_strings': 'extract-string-constants',
48
+ 'implementation:long_function': 'split-long-functions',
49
+ 'implementation:complex_condition': 'simplify-conditions',
50
+
51
+ // Architecture issues
52
+ 'architecture:god_object': 'split-large-files',
53
+ 'architecture:mixed_concerns': 'separate-concerns',
54
+ 'architecture:tight_coupling': 'reduce-coupling',
55
+ 'architecture:circular_dependency': 'break-circular-deps',
56
+
57
+ // Basic issues
58
+ 'basic:console_log': 'remove-console-logs',
59
+ 'basic:todo_fixme': 'resolve-todos-before-merge',
60
+ 'basic:empty_catch': 'handle-catch-blocks',
61
+ 'basic:debugger': 'remove-debugger-statements',
62
+
63
+ // From previous session reviews
64
+ 'file_read_no_try_catch': 'try-catch-file-reads',
65
+ 'json_parse_unsafe': 'use-safe-json-parse',
66
+ 'glob_path_separator': 'glob-no-path-separator',
67
+ 'template_prototype': 'template-prototype-protection',
68
+ 'json_no_validation': 'validate-json-structure'
69
+ };
70
+
71
+ // Default configuration
72
+ const DEFAULT_CONFIG = {
73
+ enabled: true,
74
+ captureFrom: {
75
+ sessionReview: true,
76
+ bugFix: true
77
+ },
78
+ confidenceThreshold: 80,
79
+ promotionThreshold: 3,
80
+ autoPromote: false
81
+ };
82
+
83
+ // Table format constants (DRY)
84
+ const TABLE_FORMAT = {
85
+ header: '| Date | Pattern | Source | Count | Confidence | Status |',
86
+ separator: '|------|---------|--------|-------|------------|--------|',
87
+ sectionHeader: '## Auto-Captured Patterns',
88
+ sectionRegex: /## Auto-Captured Patterns\s*\n\n\|[^\n]+\|\s*\n\|[-|\s]+\|\s*\n([\s\S]*?)(?=\n## |\n---|\n$)/
89
+ };
90
+
91
+ // Confidence update weight (0-1, higher means more weight to recent observations)
92
+ const CONFIDENCE_WEIGHT_RECENT = 0.7;
93
+
94
+ // ============================================================
95
+ // Configuration
96
+ // ============================================================
97
+
98
+ /**
99
+ * Get auto-learning configuration
100
+ * @returns {Object} Config with defaults applied
101
+ */
102
+ function getAutoLearnConfig() {
103
+ const config = getConfig();
104
+ return {
105
+ ...DEFAULT_CONFIG,
106
+ ...(config?.autoLearning || {})
107
+ };
108
+ }
109
+
110
+ // ============================================================
111
+ // Pattern Normalization
112
+ // ============================================================
113
+
114
+ /**
115
+ * Convert issue to normalized pattern name
116
+ * @param {Object} issue - Issue from session review
117
+ * @returns {string} Normalized pattern name
118
+ */
119
+ function normalizeIssueToPattern(issue) {
120
+ // Create key from perspective:type
121
+ const perspective = issue.perspective || issue.category || 'unknown';
122
+ const type = issue.type || slugify(issue.description || '');
123
+ const key = `${perspective}:${type}`;
124
+
125
+ // Check explicit mapping
126
+ if (ISSUE_TO_PATTERN[key]) {
127
+ return ISSUE_TO_PATTERN[key];
128
+ }
129
+
130
+ // Check if type alone matches
131
+ if (ISSUE_TO_PATTERN[type]) {
132
+ return ISSUE_TO_PATTERN[type];
133
+ }
134
+
135
+ // Generate from description (kebab-case, max 5 words)
136
+ const desc = issue.description || issue.type || 'unknown-pattern';
137
+ return slugify(desc.split(' ').slice(0, 5).join(' '));
138
+ }
139
+
140
+ /**
141
+ * Convert string to kebab-case
142
+ * @param {string} str - Input string
143
+ * @returns {string} Kebab-case string
144
+ */
145
+ function slugify(str) {
146
+ return str
147
+ .toLowerCase()
148
+ .replace(/[^a-z0-9\s-]/g, '')
149
+ .replace(/\s+/g, '-')
150
+ .replace(/-+/g, '-')
151
+ .replace(/^-|-$/g, '')
152
+ .slice(0, 50);
153
+ }
154
+
155
+ // ============================================================
156
+ // Feedback Patterns File Management
157
+ // ============================================================
158
+
159
+ /**
160
+ * Parse feedback-patterns.md to extract auto-captured patterns
161
+ * @returns {Array} Array of pattern objects
162
+ */
163
+ function loadAutoPatterns() {
164
+ if (!fileExists(FEEDBACK_PATTERNS_PATH)) {
165
+ return [];
166
+ }
167
+
168
+ try {
169
+ const content = fs.readFileSync(FEEDBACK_PATTERNS_PATH, 'utf-8');
170
+
171
+ // Find Auto-Captured Patterns section (using DRY constant)
172
+ const sectionMatch = content.match(TABLE_FORMAT.sectionRegex);
173
+ if (!sectionMatch) {
174
+ return [];
175
+ }
176
+
177
+ const tableContent = sectionMatch[1];
178
+ const patterns = [];
179
+
180
+ // Parse table rows
181
+ const rows = tableContent.trim().split('\n').filter(line => line.startsWith('|'));
182
+ for (const row of rows) {
183
+ const cells = row.split('|').map(c => c.trim()).filter(Boolean);
184
+ if (cells.length >= 5) {
185
+ patterns.push({
186
+ date: cells[0],
187
+ pattern: cells[1],
188
+ source: cells[2],
189
+ count: parseInt(cells[3], 10) || 1,
190
+ confidence: parseInt(cells[4], 10) || 80,
191
+ status: cells[5] || 'Monitor'
192
+ });
193
+ }
194
+ }
195
+
196
+ return patterns;
197
+ } catch (err) {
198
+ warn(`Could not parse feedback-patterns.md: ${err.message}`);
199
+ return [];
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Save auto-captured patterns back to feedback-patterns.md
205
+ * @param {Array} patterns - Array of pattern objects
206
+ */
207
+ function saveAutoPatterns(patterns) {
208
+ if (!fileExists(FEEDBACK_PATTERNS_PATH)) {
209
+ warn(`Could not save patterns: feedback-patterns.md not found`);
210
+ return;
211
+ }
212
+
213
+ try {
214
+ let content = fs.readFileSync(FEEDBACK_PATTERNS_PATH, 'utf-8');
215
+
216
+ // Build new table (using DRY constants)
217
+ const rows = patterns.map(p =>
218
+ `| ${p.date} | ${p.pattern} | ${p.source} | ${p.count} | ${p.confidence}% | ${p.status} |`
219
+ );
220
+
221
+ const newSection = `${TABLE_FORMAT.sectionHeader}\n\n${TABLE_FORMAT.header}\n${TABLE_FORMAT.separator}\n${rows.join('\n')}`;
222
+
223
+ // Replace or add section
224
+ if (content.includes('## Auto-Captured Patterns')) {
225
+ content = content.replace(
226
+ /## Auto-Captured Patterns[\s\S]*?(?=\n## |\n---\n\n## |$)/,
227
+ newSection + '\n\n'
228
+ );
229
+ } else {
230
+ // Add before "## Promotion History" or at the end
231
+ if (content.includes('## Promotion History')) {
232
+ content = content.replace('## Promotion History', newSection + '\n\n---\n\n## Promotion History');
233
+ } else {
234
+ content = content.trimEnd() + '\n\n---\n\n' + newSection + '\n';
235
+ }
236
+ }
237
+
238
+ fs.writeFileSync(FEEDBACK_PATTERNS_PATH, content, 'utf-8');
239
+ } catch (err) {
240
+ warn(`Could not save feedback-patterns.md: ${err.message}`);
241
+ }
242
+ }
243
+
244
+ // ============================================================
245
+ // Core Learning Functions
246
+ // ============================================================
247
+
248
+ /**
249
+ * Capture learnings from session review issues
250
+ * @param {Array} issues - Issues from session review
251
+ */
252
+ function captureFromSessionReview(issues) {
253
+ const config = getAutoLearnConfig();
254
+
255
+ if (!config.enabled || !config.captureFrom.sessionReview) {
256
+ return;
257
+ }
258
+
259
+ // Filter by confidence threshold
260
+ const validIssues = issues.filter(i =>
261
+ (i.confidence || 80) >= config.confidenceThreshold
262
+ );
263
+
264
+ if (validIssues.length === 0) {
265
+ return;
266
+ }
267
+
268
+ const patterns = loadAutoPatterns();
269
+ const today = new Date().toISOString().split('T')[0];
270
+ let capturedCount = 0;
271
+ const promotionCandidates = [];
272
+
273
+ for (const issue of validIssues) {
274
+ const patternName = normalizeIssueToPattern(issue);
275
+ const confidence = issue.confidence || 80;
276
+
277
+ // Check if pattern already exists
278
+ const existing = patterns.find(p => p.pattern === patternName);
279
+
280
+ if (existing) {
281
+ // Increment count and update confidence (weighted average favoring recent)
282
+ existing.count += 1;
283
+ existing.confidence = Math.round(
284
+ (1 - CONFIDENCE_WEIGHT_RECENT) * existing.confidence +
285
+ CONFIDENCE_WEIGHT_RECENT * confidence
286
+ );
287
+ existing.date = today;
288
+
289
+ // Check promotion threshold
290
+ if (existing.count >= config.promotionThreshold && existing.status === 'Monitor') {
291
+ existing.status = 'Ready';
292
+ promotionCandidates.push(existing);
293
+ }
294
+ } else {
295
+ // Add new pattern
296
+ patterns.push({
297
+ date: today,
298
+ pattern: patternName,
299
+ source: 'session-review',
300
+ count: 1,
301
+ confidence: confidence,
302
+ status: 'Monitor'
303
+ });
304
+ }
305
+
306
+ capturedCount++;
307
+ }
308
+
309
+ // Save updated patterns
310
+ saveAutoPatterns(patterns);
311
+
312
+ // Report
313
+ if (capturedCount > 0) {
314
+ info(`Auto-learned ${capturedCount} pattern(s) from session review`);
315
+ }
316
+
317
+ // Handle promotions
318
+ for (const candidate of promotionCandidates) {
319
+ handlePromotion(candidate, config);
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Capture learnings from bug fix completion
325
+ * @param {string} taskId - Task ID
326
+ * @param {Array} files - Modified files
327
+ * @param {string} description - Task description
328
+ */
329
+ function captureFromBugFix(taskId, files, description) {
330
+ const config = getAutoLearnConfig();
331
+
332
+ if (!config.enabled || !config.captureFrom.bugFix) {
333
+ return;
334
+ }
335
+
336
+ // Analyze the bug fix to detect patterns
337
+ const detectedPatterns = analyzeBugFix(files, description);
338
+
339
+ if (detectedPatterns.length === 0) {
340
+ return;
341
+ }
342
+
343
+ const patterns = loadAutoPatterns();
344
+ const today = new Date().toISOString().split('T')[0];
345
+ const promotionCandidates = [];
346
+
347
+ for (const detected of detectedPatterns) {
348
+ const existing = patterns.find(p => p.pattern === detected.pattern);
349
+
350
+ if (existing) {
351
+ existing.count += 1;
352
+ existing.date = today;
353
+
354
+ if (existing.count >= config.promotionThreshold && existing.status === 'Monitor') {
355
+ existing.status = 'Ready';
356
+ promotionCandidates.push(existing);
357
+ }
358
+ } else {
359
+ patterns.push({
360
+ date: today,
361
+ pattern: detected.pattern,
362
+ source: 'bug-fix',
363
+ count: 1,
364
+ confidence: detected.confidence || 85,
365
+ status: 'Monitor'
366
+ });
367
+ }
368
+ }
369
+
370
+ saveAutoPatterns(patterns);
371
+
372
+ if (detectedPatterns.length > 0) {
373
+ info(`Auto-learned ${detectedPatterns.length} pattern(s) from bug fix ${taskId}`);
374
+ }
375
+
376
+ for (const candidate of promotionCandidates) {
377
+ handlePromotion(candidate, config);
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Analyze bug fix files to detect violated patterns
383
+ * @param {Array} files - Modified files
384
+ * @param {string} description - Task description
385
+ * @returns {Array} Detected patterns
386
+ */
387
+ function analyzeBugFix(files, description) {
388
+ const detected = [];
389
+ const descLower = (description || '').toLowerCase();
390
+
391
+ // Keyword-based pattern detection from description
392
+ const keywordPatterns = [
393
+ { keywords: ['try-catch', 'error handling', 'exception'], pattern: 'handle-async-errors' },
394
+ { keywords: ['json parse', 'json.parse', 'parsing error'], pattern: 'use-safe-json-parse' },
395
+ { keywords: ['null check', 'undefined', 'cannot read property'], pattern: 'null-safety-checks' },
396
+ { keywords: ['path traversal', 'directory traversal'], pattern: 'validate-file-paths' },
397
+ { keywords: ['xss', 'innerhtml', 'script injection'], pattern: 'sanitize-html-output' },
398
+ { keywords: ['sql injection', 'query injection'], pattern: 'use-parameterized-queries' },
399
+ { keywords: ['race condition', 'concurrent', 'async race'], pattern: 'handle-race-conditions' },
400
+ { keywords: ['memory leak', 'cleanup', 'dispose'], pattern: 'cleanup-resources' },
401
+ { keywords: ['validation', 'invalid input', 'input validation'], pattern: 'validate-input' },
402
+ { keywords: ['timeout', 'deadline', 'hung'], pattern: 'add-timeouts' }
403
+ ];
404
+
405
+ for (const kp of keywordPatterns) {
406
+ if (kp.keywords.some(k => descLower.includes(k))) {
407
+ detected.push({ pattern: kp.pattern, confidence: 85 });
408
+ }
409
+ }
410
+
411
+ // If no keywords matched, try to extract from file changes
412
+ // (Could be enhanced to actually read the diff)
413
+ if (detected.length === 0 && files.length > 0) {
414
+ // Generic "bug fix" pattern based on file type
415
+ const jsFiles = files.filter(f => f.endsWith('.js') || f.endsWith('.ts'));
416
+ if (jsFiles.length > 0) {
417
+ detected.push({ pattern: 'review-similar-code', confidence: 70 });
418
+ }
419
+ }
420
+
421
+ return detected;
422
+ }
423
+
424
+ // ============================================================
425
+ // Promotion Handling
426
+ // ============================================================
427
+
428
+ /**
429
+ * Handle pattern promotion
430
+ * @param {Object} pattern - Pattern to promote
431
+ * @param {Object} config - Auto-learn config
432
+ */
433
+ function handlePromotion(pattern, config) {
434
+ if (config.autoPromote) {
435
+ // Auto-promote to decisions.md
436
+ promoteToDecisions(pattern);
437
+ success(`Auto-promoted pattern "${pattern.pattern}" to decisions.md`);
438
+ } else {
439
+ // Notify user
440
+ console.log('');
441
+ console.log(color('yellow', `Pattern ready for promotion: "${pattern.pattern}"`));
442
+ console.log(` Occurrences: ${pattern.count}`);
443
+ console.log(` Confidence: ${pattern.confidence}%`);
444
+ console.log(` Source: ${pattern.source}`);
445
+ console.log(color('dim', ' Run "flow aggregate" to promote, or edit decisions.md manually'));
446
+ console.log('');
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Promote pattern to decisions.md
452
+ * @param {Object} pattern - Pattern to promote
453
+ */
454
+ function promoteToDecisions(pattern) {
455
+ if (!fileExists(DECISIONS_PATH)) {
456
+ warn(`Could not promote pattern: decisions.md not found`);
457
+ return;
458
+ }
459
+
460
+ try {
461
+ let content = fs.readFileSync(DECISIONS_PATH, 'utf-8');
462
+
463
+ // Find Coding Standards section
464
+ const sectionHeader = '## Coding Standards';
465
+
466
+ if (!content.includes(sectionHeader)) {
467
+ warn(`Could not promote pattern: Coding Standards section not found in decisions.md`);
468
+ return;
469
+ }
470
+
471
+ // Generate rule entry (escape markdown special characters in pattern name)
472
+ const today = new Date().toISOString().split('T')[0];
473
+ const escapedPattern = pattern.pattern.replace(/[#*_\[\]()\\]/g, '\\$&');
474
+ const ruleEntry = `\n### ${escapedPattern} (${today})
475
+ **Source**: Auto-learned from ${pattern.count} occurrences (${pattern.source})
476
+ **Rule**: [Describe the pattern rule here]
477
+ `;
478
+
479
+ // Insert after Coding Standards header
480
+ const insertPoint = content.indexOf(sectionHeader) + sectionHeader.length;
481
+ const nextSection = content.indexOf('\n## ', insertPoint);
482
+
483
+ if (nextSection > insertPoint) {
484
+ // Insert before next section
485
+ content = content.slice(0, nextSection) + ruleEntry + content.slice(nextSection);
486
+ } else {
487
+ // Append to section
488
+ content = content.slice(0, insertPoint) + ruleEntry + content.slice(insertPoint);
489
+ }
490
+
491
+ fs.writeFileSync(DECISIONS_PATH, content, 'utf-8');
492
+
493
+ // Update pattern status
494
+ const patterns = loadAutoPatterns();
495
+ const updated = patterns.find(p => p.pattern === pattern.pattern);
496
+ if (updated) {
497
+ updated.status = 'Promoted';
498
+ saveAutoPatterns(patterns);
499
+ }
500
+
501
+ // Sync rules
502
+ try {
503
+ require('./flow-rules-sync');
504
+ } catch (syncErr) {
505
+ // Log the failure for debugging
506
+ info(`Note: Rules sync skipped - ${syncErr.code === 'MODULE_NOT_FOUND' ? 'module not found' : syncErr.message}`);
507
+ }
508
+ } catch (err) {
509
+ warn(`Could not promote to decisions.md: ${err.message}`);
510
+ }
511
+ }
512
+
513
+ // ============================================================
514
+ // CLI
515
+ // ============================================================
516
+
517
+ /**
518
+ * Show auto-learning status
519
+ */
520
+ function showStatus() {
521
+ const config = getAutoLearnConfig();
522
+ const patterns = loadAutoPatterns();
523
+
524
+ console.log('');
525
+ console.log(color('cyan', '='.repeat(50)));
526
+ console.log(color('cyan', ' AUTO-LEARNING STATUS'));
527
+ console.log(color('cyan', '='.repeat(50)));
528
+ console.log('');
529
+
530
+ // Config
531
+ console.log(color('cyan', 'Configuration'));
532
+ console.log(` Enabled: ${config.enabled ? 'Yes' : 'No'}`);
533
+ console.log(` Capture from: ${Object.entries(config.captureFrom).filter(([,v]) => v).map(([k]) => k).join(', ')}`);
534
+ console.log(` Confidence threshold: ${config.confidenceThreshold}%`);
535
+ console.log(` Promotion threshold: ${config.promotionThreshold} occurrences`);
536
+ console.log(` Auto-promote: ${config.autoPromote ? 'Yes' : 'No'}`);
537
+ console.log('');
538
+
539
+ // Patterns
540
+ console.log(color('cyan', 'Captured Patterns'));
541
+ if (patterns.length === 0) {
542
+ console.log(' No patterns captured yet');
543
+ } else {
544
+ const ready = patterns.filter(p => p.status === 'Ready');
545
+ const monitoring = patterns.filter(p => p.status === 'Monitor');
546
+ const promoted = patterns.filter(p => p.status === 'Promoted');
547
+
548
+ if (ready.length > 0) {
549
+ console.log(color('yellow', ` Ready for promotion (${ready.length}):`));
550
+ for (const p of ready) {
551
+ console.log(` - ${p.pattern} (${p.count}x, ${p.confidence}%)`);
552
+ }
553
+ }
554
+
555
+ if (monitoring.length > 0) {
556
+ console.log(` Monitoring (${monitoring.length}):`);
557
+ for (const p of monitoring) {
558
+ console.log(` - ${p.pattern} (${p.count}x, ${p.confidence}%)`);
559
+ }
560
+ }
561
+
562
+ if (promoted.length > 0) {
563
+ console.log(color('green', ` Promoted (${promoted.length}):`));
564
+ for (const p of promoted) {
565
+ console.log(` - ${p.pattern}`);
566
+ }
567
+ }
568
+ }
569
+
570
+ console.log('');
571
+ }
572
+
573
+ // ============================================================
574
+ // Main
575
+ // ============================================================
576
+
577
+ async function main() {
578
+ const args = process.argv.slice(2);
579
+ const command = args[0] || 'status';
580
+
581
+ switch (command) {
582
+ case 'status':
583
+ showStatus();
584
+ break;
585
+
586
+ case 'test':
587
+ // Test capture with mock issues
588
+ captureFromSessionReview([
589
+ { perspective: 'security', type: 'missing_error_handling', description: 'Missing try-catch', confidence: 85 },
590
+ { perspective: 'implementation', type: 'magic_numbers', description: 'Magic number 42', confidence: 82 }
591
+ ]);
592
+ success('Test capture completed');
593
+ break;
594
+
595
+ default:
596
+ console.log('Usage: flow auto-learn [status|test]');
597
+ }
598
+ }
599
+
600
+ // ============================================================
601
+ // Exports
602
+ // ============================================================
603
+
604
+ module.exports = {
605
+ captureFromSessionReview,
606
+ captureFromBugFix,
607
+ normalizeIssueToPattern,
608
+ getAutoLearnConfig,
609
+ loadAutoPatterns,
610
+ showStatus
611
+ };
612
+
613
+ if (require.main === module) {
614
+ main();
615
+ }