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,630 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Loop Retry Learning
5
+ *
6
+ * Analyzes completed sessions that took >3 iterations to identify
7
+ * patterns and suggest improvements to prevent similar issues.
8
+ *
9
+ * Integration:
10
+ * - Hooks into archiveDurableSession() after task completion
11
+ * - Categorizes root causes from step failure history
12
+ * - Stores learnings in adaptive-learning.json
13
+ * - Suggests updates to decisions.md / patterns
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { getConfig, getProjectRoot, colors } = require('./flow-utils');
19
+ const { FailureCategory, detectCategory } = require('../.workflow/lib/failure-categories');
20
+
21
+ const PROJECT_ROOT = getProjectRoot();
22
+ const LEARNING_LOG_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'adaptive-learning.json');
23
+
24
+ // ============================================================================
25
+ // Root Cause Categories
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Root cause categories for high-iteration tasks
30
+ *
31
+ * Uses centralized FailureCategory for patterns, adds suggestion/targetFile
32
+ * for workflow improvement recommendations.
33
+ */
34
+ const ROOT_CAUSE_CATEGORIES = {
35
+ MISSING_CONTEXT: {
36
+ // Use centralized patterns
37
+ ...FailureCategory.MISSING_CONTEXT,
38
+ // Add loop-retry specific fields
39
+ suggestion: 'Load more context files before implementation',
40
+ targetFile: 'decisions.md'
41
+ },
42
+ VALIDATION_FAILURES: {
43
+ // Combines TYPE_ERROR and PATTERN_VIOLATION patterns
44
+ patterns: [
45
+ ...FailureCategory.TYPE_ERROR.patterns,
46
+ ...FailureCategory.PATTERN_VIOLATION.patterns
47
+ ],
48
+ description: 'Repeated lint/type errors',
49
+ severity: FailureCategory.TYPE_ERROR.severity,
50
+ escalate: false,
51
+ strategy: 'type_fix',
52
+ suggestion: 'Check type definitions before editing',
53
+ targetFile: 'decisions.md'
54
+ },
55
+ INCOMPLETE_REQUIREMENTS: {
56
+ ...FailureCategory.INCOMPLETE_REQUIREMENTS,
57
+ suggestion: 'Decompose story into more specific criteria',
58
+ targetFile: 'agents/story-writer.md'
59
+ },
60
+ COMPONENT_REUSE_MISS: {
61
+ ...FailureCategory.COMPONENT_REUSE_MISS,
62
+ suggestion: 'Always check app-map.md before creating components',
63
+ targetFile: 'decisions.md'
64
+ },
65
+ PATTERN_VIOLATION: {
66
+ ...FailureCategory.PATTERN_VIOLATION,
67
+ suggestion: 'Check decisions.md for coding patterns',
68
+ targetFile: 'decisions.md'
69
+ },
70
+ EXTERNAL_DEPENDENCY: {
71
+ // Combines EXTERNAL_DEPENDENCY, RATE_LIMIT, and API_ERROR
72
+ patterns: [
73
+ ...FailureCategory.EXTERNAL_DEPENDENCY.patterns,
74
+ ...FailureCategory.RATE_LIMIT.patterns,
75
+ ...FailureCategory.API_ERROR.patterns
76
+ ],
77
+ description: 'Waiting on CI/tests/external systems',
78
+ severity: FailureCategory.EXTERNAL_DEPENDENCY.severity,
79
+ escalate: false,
80
+ strategy: 'wait_retry',
81
+ suggestion: 'Consider async suspension for external dependencies',
82
+ targetFile: 'config.json'
83
+ },
84
+ SYNTAX_ISSUES: {
85
+ ...FailureCategory.SYNTAX_ERROR,
86
+ suggestion: 'Validate code before saving',
87
+ targetFile: 'decisions.md'
88
+ },
89
+ // Additional categories from centralized module
90
+ IMPORT_ERROR: {
91
+ ...FailureCategory.IMPORT_ERROR,
92
+ suggestion: 'Check import paths and available exports',
93
+ targetFile: 'decisions.md'
94
+ },
95
+ HALLUCINATION: {
96
+ ...FailureCategory.HALLUCINATION,
97
+ suggestion: 'Use only explicitly provided context',
98
+ targetFile: 'decisions.md'
99
+ },
100
+ CONTEXT_OVERFLOW: {
101
+ ...FailureCategory.CONTEXT_OVERFLOW,
102
+ suggestion: 'Use /wogi-compact before large tasks',
103
+ targetFile: 'config.json'
104
+ },
105
+ CAPABILITY_MISMATCH: {
106
+ ...FailureCategory.CAPABILITY_MISMATCH,
107
+ suggestion: 'Consider using a more capable model for complex tasks',
108
+ targetFile: 'config.json'
109
+ }
110
+ };
111
+
112
+ // ============================================================================
113
+ // Core Analysis Functions
114
+ // ============================================================================
115
+
116
+ /**
117
+ * Check if learning should be triggered for this session
118
+ * @param {Object} session - Completed durable session
119
+ * @returns {boolean}
120
+ */
121
+ function shouldTriggerLearning(session) {
122
+ if (!session) return false;
123
+
124
+ const config = getConfig();
125
+ const threshold = config.skillLearning?.loopRetryThreshold || 3;
126
+
127
+ // Check iteration count
128
+ const iterations = session.execution?.iteration || 0;
129
+ if (iterations <= threshold) {
130
+ return false;
131
+ }
132
+
133
+ // Check if learning is enabled
134
+ if (config.skillLearning?.learnFromLoopRetries === false) {
135
+ return false;
136
+ }
137
+
138
+ // Only analyze completed sessions
139
+ if (session.status !== 'completed') {
140
+ return false;
141
+ }
142
+
143
+ return true;
144
+ }
145
+
146
+ /**
147
+ * Extract failure patterns from session steps
148
+ * @param {Array} steps - Session steps with attempt history
149
+ * @returns {Object} Grouped failure patterns
150
+ */
151
+ function extractFailurePatterns(steps) {
152
+ const patterns = {
153
+ byCategory: {},
154
+ errors: [],
155
+ stepFailures: []
156
+ };
157
+
158
+ for (const step of steps) {
159
+ // Check step error
160
+ if (step.error) {
161
+ patterns.errors.push({
162
+ stepId: step.id,
163
+ error: step.error,
164
+ attempts: step.attempts
165
+ });
166
+ }
167
+
168
+ // Track steps that needed multiple attempts
169
+ if (step.attempts > 1) {
170
+ patterns.stepFailures.push({
171
+ stepId: step.id,
172
+ description: step.description,
173
+ attempts: step.attempts,
174
+ error: step.error
175
+ });
176
+ }
177
+ }
178
+
179
+ return patterns;
180
+ }
181
+
182
+ /**
183
+ * Categorize the root cause of high iterations
184
+ * @param {Object} patterns - Extracted failure patterns
185
+ * @param {Object} session - Full session data
186
+ * @returns {Object} Root cause analysis
187
+ */
188
+ function categorizeRootCause(patterns, session) {
189
+ const allErrors = patterns.errors
190
+ .map(e => typeof e.error === 'string' ? e.error : JSON.stringify(e.error))
191
+ .join('\n');
192
+
193
+ const analysis = {
194
+ categories: [],
195
+ primaryCategory: null,
196
+ confidence: 0,
197
+ details: {}
198
+ };
199
+
200
+ // Check each category
201
+ for (const [category, config] of Object.entries(ROOT_CAUSE_CATEGORIES)) {
202
+ for (const pattern of config.patterns) {
203
+ if (pattern.test(allErrors)) {
204
+ analysis.categories.push({
205
+ category,
206
+ description: config.description,
207
+ suggestion: config.suggestion,
208
+ targetFile: config.targetFile,
209
+ matchCount: (allErrors.match(new RegExp(pattern.source, 'gi')) || []).length
210
+ });
211
+ break;
212
+ }
213
+ }
214
+ }
215
+
216
+ // Set primary category (highest match count)
217
+ if (analysis.categories.length > 0) {
218
+ analysis.categories.sort((a, b) => b.matchCount - a.matchCount);
219
+ analysis.primaryCategory = analysis.categories[0].category;
220
+ analysis.confidence = Math.min(analysis.categories[0].matchCount / 3, 1);
221
+ } else {
222
+ // Default to validation failures if we had retries but couldn't categorize
223
+ if (session.execution.totalRetries > 0) {
224
+ analysis.primaryCategory = 'VALIDATION_FAILURES';
225
+ analysis.confidence = 0.3;
226
+ analysis.categories.push({
227
+ category: 'VALIDATION_FAILURES',
228
+ description: ROOT_CAUSE_CATEGORIES.VALIDATION_FAILURES.description,
229
+ suggestion: ROOT_CAUSE_CATEGORIES.VALIDATION_FAILURES.suggestion,
230
+ targetFile: ROOT_CAUSE_CATEGORIES.VALIDATION_FAILURES.targetFile,
231
+ matchCount: 0
232
+ });
233
+ }
234
+ }
235
+
236
+ // Add session-level details
237
+ analysis.details = {
238
+ totalIterations: session.execution.iteration,
239
+ totalRetries: session.execution.totalRetries,
240
+ failedSteps: patterns.stepFailures.length,
241
+ errorCount: patterns.errors.length
242
+ };
243
+
244
+ return analysis;
245
+ }
246
+
247
+ /**
248
+ * Generate learning entry from analysis
249
+ * @param {Object} rootCause - Root cause analysis
250
+ * @param {Object} session - Session data
251
+ * @returns {Object} Learning entry
252
+ */
253
+ function generateLearning(rootCause, session) {
254
+ if (!rootCause.primaryCategory) {
255
+ return null;
256
+ }
257
+
258
+ const categoryConfig = ROOT_CAUSE_CATEGORIES[rootCause.primaryCategory];
259
+ const date = new Date().toISOString().split('T')[0];
260
+
261
+ return {
262
+ timestamp: new Date().toISOString(),
263
+ date,
264
+ taskId: session.taskId,
265
+ iterations: session.execution.iteration,
266
+ retries: session.execution.totalRetries,
267
+ rootCause: rootCause.primaryCategory,
268
+ confidence: rootCause.confidence,
269
+ pattern: categoryConfig.description,
270
+ suggestion: categoryConfig.suggestion,
271
+ targetFile: categoryConfig.targetFile,
272
+ applied: false,
273
+ details: rootCause.details
274
+ };
275
+ }
276
+
277
+ /**
278
+ * Check if similar learning exists (deduplication)
279
+ * @param {string} rootCause - Root cause category
280
+ * @param {string} taskId - Task ID
281
+ * @returns {boolean}
282
+ */
283
+ function isDuplicateLearning(rootCause, taskId) {
284
+ if (!fs.existsSync(LEARNING_LOG_PATH)) {
285
+ return false;
286
+ }
287
+
288
+ try {
289
+ const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
290
+ const loopLearnings = log.loopRetryLearnings || [];
291
+
292
+ // Check for same root cause in last 7 days
293
+ const sevenDaysAgo = new Date();
294
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
295
+ const cutoffDate = sevenDaysAgo.toISOString();
296
+
297
+ return loopLearnings.some(l =>
298
+ l.rootCause === rootCause &&
299
+ l.timestamp >= cutoffDate
300
+ );
301
+ } catch {
302
+ return false;
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Store learning to adaptive-learning.json
308
+ * @param {Object} learning - Learning entry
309
+ */
310
+ function storeLearning(learning) {
311
+ let log = { entries: [], loopRetryLearnings: [] };
312
+
313
+ if (fs.existsSync(LEARNING_LOG_PATH)) {
314
+ try {
315
+ log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
316
+ if (!log.loopRetryLearnings) {
317
+ log.loopRetryLearnings = [];
318
+ }
319
+ } catch {
320
+ log = { entries: [], loopRetryLearnings: [] };
321
+ }
322
+ }
323
+
324
+ log.loopRetryLearnings.push(learning);
325
+
326
+ // Keep last 50 learnings
327
+ if (log.loopRetryLearnings.length > 50) {
328
+ log.loopRetryLearnings = log.loopRetryLearnings.slice(-50);
329
+ }
330
+
331
+ // Ensure directory exists
332
+ const dir = path.dirname(LEARNING_LOG_PATH);
333
+ if (!fs.existsSync(dir)) {
334
+ fs.mkdirSync(dir, { recursive: true });
335
+ }
336
+
337
+ fs.writeFileSync(LEARNING_LOG_PATH, JSON.stringify(log, null, 2));
338
+ }
339
+
340
+ // ============================================================================
341
+ // Main Entry Point
342
+ // ============================================================================
343
+
344
+ /**
345
+ * Analyze a completed session for learnings
346
+ * @param {Object} session - Completed durable session
347
+ * @returns {Object} Analysis result
348
+ */
349
+ async function analyzeCompletedSession(session) {
350
+ // Check if we should analyze
351
+ if (!shouldTriggerLearning(session)) {
352
+ return { analyzed: false, reason: 'threshold-not-met' };
353
+ }
354
+
355
+ // Extract failure patterns
356
+ const patterns = extractFailurePatterns(session.steps);
357
+
358
+ // Categorize root cause
359
+ const rootCause = categorizeRootCause(patterns, session);
360
+
361
+ if (!rootCause.primaryCategory) {
362
+ return { analyzed: false, reason: 'no-root-cause-identified' };
363
+ }
364
+
365
+ // Check for duplicate
366
+ if (isDuplicateLearning(rootCause.primaryCategory, session.taskId)) {
367
+ return {
368
+ analyzed: false,
369
+ reason: 'duplicate-learning',
370
+ rootCause: rootCause.primaryCategory
371
+ };
372
+ }
373
+
374
+ // Generate and store learning
375
+ const learning = generateLearning(rootCause, session);
376
+ if (learning) {
377
+ storeLearning(learning);
378
+
379
+ // Log to console
380
+ console.log(`${colors.cyan} ๐Ÿ“š Loop Retry Learning${colors.reset}`);
381
+ console.log(` Task took ${session.execution.iteration} iterations`);
382
+ console.log(` Root cause: ${rootCause.primaryCategory}`);
383
+ console.log(` Suggestion: ${learning.suggestion}`);
384
+
385
+ return {
386
+ analyzed: true,
387
+ learning,
388
+ suggestion: formatSuggestion(learning)
389
+ };
390
+ }
391
+
392
+ return { analyzed: false, reason: 'no-learning-generated' };
393
+ }
394
+
395
+ /**
396
+ * Format suggestion for display
397
+ * @param {Object} learning - Learning entry
398
+ * @returns {string} Formatted suggestion
399
+ */
400
+ function formatSuggestion(learning) {
401
+ const categoryConfig = ROOT_CAUSE_CATEGORIES[learning.rootCause];
402
+ if (!categoryConfig) return '';
403
+
404
+ let suggestion = `\n๐Ÿ’ก **Learning from ${learning.taskId}**\n`;
405
+ suggestion += ` Problem: ${categoryConfig.description}\n`;
406
+ suggestion += ` Suggestion: ${categoryConfig.suggestion}\n`;
407
+
408
+ if (learning.targetFile) {
409
+ suggestion += ` Update: Consider adding to ${learning.targetFile}\n`;
410
+ }
411
+
412
+ return suggestion;
413
+ }
414
+
415
+ /**
416
+ * Get learning statistics
417
+ * @returns {Object} Stats
418
+ */
419
+ function getLearningStats() {
420
+ if (!fs.existsSync(LEARNING_LOG_PATH)) {
421
+ return { total: 0, byCategory: {}, recentLearnings: [], avgIterations: 0 };
422
+ }
423
+
424
+ try {
425
+ const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
426
+ const learnings = log.loopRetryLearnings || [];
427
+
428
+ const byCategory = {};
429
+ for (const l of learnings) {
430
+ byCategory[l.rootCause] = (byCategory[l.rootCause] || 0) + 1;
431
+ }
432
+
433
+ return {
434
+ total: learnings.length,
435
+ byCategory,
436
+ recentLearnings: learnings.slice(-5),
437
+ avgIterations: learnings.length > 0
438
+ ? Math.round(learnings.reduce((sum, l) => sum + l.iterations, 0) / learnings.length * 10) / 10
439
+ : 0
440
+ };
441
+ } catch {
442
+ return { total: 0, byCategory: {}, recentLearnings: [], avgIterations: 0 };
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Get unapplied learning suggestions
448
+ * @returns {Array} Suggestions that haven't been applied
449
+ */
450
+ function getUnappliedSuggestions() {
451
+ if (!fs.existsSync(LEARNING_LOG_PATH)) {
452
+ return [];
453
+ }
454
+
455
+ try {
456
+ const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
457
+ const learnings = log.loopRetryLearnings || [];
458
+
459
+ return learnings
460
+ .filter(l => !l.applied)
461
+ .map(l => ({
462
+ taskId: l.taskId,
463
+ rootCause: l.rootCause,
464
+ suggestion: l.suggestion,
465
+ targetFile: l.targetFile,
466
+ date: l.date
467
+ }));
468
+ } catch {
469
+ return [];
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Mark a learning as applied
475
+ * @param {string} taskId - Task ID of the learning to mark
476
+ * @returns {boolean} Success
477
+ */
478
+ function markLearningApplied(taskId) {
479
+ if (!fs.existsSync(LEARNING_LOG_PATH)) {
480
+ return false;
481
+ }
482
+
483
+ try {
484
+ const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
485
+ const learning = (log.loopRetryLearnings || []).find(l => l.taskId === taskId);
486
+
487
+ if (learning) {
488
+ learning.applied = true;
489
+ learning.appliedAt = new Date().toISOString();
490
+ fs.writeFileSync(LEARNING_LOG_PATH, JSON.stringify(log, null, 2));
491
+ return true;
492
+ }
493
+
494
+ return false;
495
+ } catch {
496
+ return false;
497
+ }
498
+ }
499
+
500
+ // ============================================================================
501
+ // Exports
502
+ // ============================================================================
503
+
504
+ module.exports = {
505
+ // Core functions
506
+ analyzeCompletedSession,
507
+ shouldTriggerLearning,
508
+ extractFailurePatterns,
509
+ categorizeRootCause,
510
+ generateLearning,
511
+
512
+ // Storage
513
+ storeLearning,
514
+ isDuplicateLearning,
515
+
516
+ // Utilities
517
+ formatSuggestion,
518
+ getLearningStats,
519
+ getUnappliedSuggestions,
520
+ markLearningApplied,
521
+
522
+ // Constants
523
+ ROOT_CAUSE_CATEGORIES,
524
+
525
+ // Re-export centralized failure categories
526
+ FailureCategory,
527
+ detectCategory
528
+ };
529
+
530
+ // ============================================================================
531
+ // CLI Interface
532
+ // ============================================================================
533
+
534
+ if (require.main === module) {
535
+ const args = process.argv.slice(2);
536
+ const command = args[0];
537
+
538
+ switch (command) {
539
+ case 'stats': {
540
+ const stats = getLearningStats();
541
+ console.log('\n๐Ÿ“Š Loop Retry Learning Statistics');
542
+ console.log('โ”€'.repeat(40));
543
+ console.log(`Total learnings: ${stats.total}`);
544
+ console.log(`Avg iterations when triggered: ${stats.avgIterations}`);
545
+ console.log('');
546
+ console.log('By category:');
547
+ for (const [cat, count] of Object.entries(stats.byCategory)) {
548
+ const config = ROOT_CAUSE_CATEGORIES[cat];
549
+ console.log(` ${cat}: ${count}`);
550
+ if (config) {
551
+ console.log(` โ””โ”€ ${config.description}`);
552
+ }
553
+ }
554
+ console.log('โ”€'.repeat(40));
555
+ break;
556
+ }
557
+
558
+ case 'suggestions': {
559
+ const suggestions = getUnappliedSuggestions();
560
+ if (suggestions.length === 0) {
561
+ console.log('\nโœ… No unapplied suggestions');
562
+ break;
563
+ }
564
+
565
+ console.log('\n๐Ÿ’ก Unapplied Learning Suggestions');
566
+ console.log('โ”€'.repeat(40));
567
+ for (const s of suggestions) {
568
+ console.log(`\n๐Ÿ“ ${s.taskId} (${s.date})`);
569
+ console.log(` Root cause: ${s.rootCause}`);
570
+ console.log(` Suggestion: ${s.suggestion}`);
571
+ console.log(` Update: ${s.targetFile}`);
572
+ }
573
+ console.log('โ”€'.repeat(40));
574
+ break;
575
+ }
576
+
577
+ case 'test': {
578
+ // Test with mock session
579
+ const mockSession = {
580
+ taskId: 'TASK-TEST',
581
+ status: 'completed',
582
+ execution: {
583
+ iteration: 5,
584
+ totalRetries: 8
585
+ },
586
+ steps: [
587
+ { id: 'step-001', error: 'Type error: property does not exist', attempts: 3 },
588
+ { id: 'step-002', error: 'TypeScript error TS2339', attempts: 2 },
589
+ { id: 'step-003', error: null, attempts: 1 }
590
+ ]
591
+ };
592
+
593
+ console.log('\n๐Ÿงช Testing with mock session');
594
+ console.log('โ”€'.repeat(40));
595
+ console.log(`Task: ${mockSession.taskId}`);
596
+ console.log(`Iterations: ${mockSession.execution.iteration}`);
597
+ console.log(`Retries: ${mockSession.execution.totalRetries}`);
598
+ console.log('');
599
+
600
+ const patterns = extractFailurePatterns(mockSession.steps);
601
+ console.log('Extracted patterns:', JSON.stringify(patterns, null, 2));
602
+
603
+ const rootCause = categorizeRootCause(patterns, mockSession);
604
+ console.log('\nRoot cause analysis:', JSON.stringify(rootCause, null, 2));
605
+
606
+ const learning = generateLearning(rootCause, mockSession);
607
+ console.log('\nGenerated learning:', JSON.stringify(learning, null, 2));
608
+ console.log('โ”€'.repeat(40));
609
+ break;
610
+ }
611
+
612
+ default:
613
+ console.log(`
614
+ Wogi Flow - Loop Retry Learning
615
+
616
+ Usage:
617
+ node flow-loop-retry-learning.js <command>
618
+
619
+ Commands:
620
+ stats Show learning statistics
621
+ suggestions Show unapplied learning suggestions
622
+ test Test with mock session data
623
+
624
+ This module analyzes tasks that take >3 iterations and:
625
+ 1. Identifies root causes (validation failures, missing context, etc.)
626
+ 2. Stores learnings for pattern improvement
627
+ 3. Suggests updates to decisions.md or other config files
628
+ `);
629
+ }
630
+ }