wogiflow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,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
|
+
}
|