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,1259 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Adaptive Learning for Hybrid Mode
|
|
5
|
+
*
|
|
6
|
+
* When a task fails with an executor model, this module:
|
|
7
|
+
* 1. Analyzes WHY it failed
|
|
8
|
+
* 2. Refines the prompt based on the failure
|
|
9
|
+
* 3. Tracks what refinements were needed
|
|
10
|
+
* 4. On success, updates the model adapter with learnings
|
|
11
|
+
*
|
|
12
|
+
* This creates a feedback loop that improves prompts over time.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { execFileSync } = require('child_process');
|
|
18
|
+
const { getProjectRoot, colors } = require('./flow-utils');
|
|
19
|
+
const { storeSingleLearning, getAdapterPath } = require('./flow-model-adapter');
|
|
20
|
+
const {
|
|
21
|
+
FailureCategory,
|
|
22
|
+
detectCategory,
|
|
23
|
+
shouldEscalate: checkShouldEscalate
|
|
24
|
+
} = require('../.workflow/lib/failure-categories');
|
|
25
|
+
const { validateRepoFormat, safeGitCommand, sanitizeCommitMessage } = require('./flow-security');
|
|
26
|
+
|
|
27
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
28
|
+
const LEARNING_LOG_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'adaptive-learning.json');
|
|
29
|
+
const STRATEGY_STATS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'strategy-effectiveness.json');
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// Failure Analysis
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Error categories with detection patterns and fix strategies
|
|
37
|
+
* Now uses centralized FailureCategory from .workflow/lib/failure-categories.js
|
|
38
|
+
*
|
|
39
|
+
* Legacy mapping for backward compatibility - maps to centralized categories
|
|
40
|
+
*/
|
|
41
|
+
const ERROR_CATEGORIES = {
|
|
42
|
+
IMPORT_ERROR: FailureCategory.IMPORT_ERROR,
|
|
43
|
+
TYPE_ERROR: FailureCategory.TYPE_ERROR,
|
|
44
|
+
SYNTAX_ERROR: FailureCategory.SYNTAX_ERROR,
|
|
45
|
+
MARKDOWN_POLLUTION: FailureCategory.MARKDOWN_POLLUTION,
|
|
46
|
+
INCOMPLETE_OUTPUT: FailureCategory.INCOMPLETE_OUTPUT,
|
|
47
|
+
HALLUCINATION: FailureCategory.HALLUCINATION,
|
|
48
|
+
// Additional categories from centralized module
|
|
49
|
+
PARSE_ERROR: FailureCategory.PARSE_ERROR,
|
|
50
|
+
RUNTIME_ERROR: FailureCategory.RUNTIME_ERROR,
|
|
51
|
+
RATE_LIMIT: FailureCategory.RATE_LIMIT,
|
|
52
|
+
API_ERROR: FailureCategory.API_ERROR,
|
|
53
|
+
CONTEXT_OVERFLOW: FailureCategory.CONTEXT_OVERFLOW,
|
|
54
|
+
CAPABILITY_MISMATCH: FailureCategory.CAPABILITY_MISMATCH,
|
|
55
|
+
MISSING_CONTEXT: FailureCategory.MISSING_CONTEXT,
|
|
56
|
+
PATTERN_VIOLATION: FailureCategory.PATTERN_VIOLATION,
|
|
57
|
+
UNKNOWN: FailureCategory.UNKNOWN
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Analyze a failure and categorize it
|
|
62
|
+
* Uses centralized detectCategory for consistent categorization
|
|
63
|
+
* @param {string} error - Error message or output
|
|
64
|
+
* @param {string} output - Model's output
|
|
65
|
+
* @param {object} context - Task context
|
|
66
|
+
* @returns {object} Failure analysis
|
|
67
|
+
*/
|
|
68
|
+
function analyzeFailure(error, output, context = {}) {
|
|
69
|
+
const errorStr = String(error);
|
|
70
|
+
const outputStr = String(output || '');
|
|
71
|
+
|
|
72
|
+
// Use centralized detection
|
|
73
|
+
const detection = detectCategory(errorStr, outputStr);
|
|
74
|
+
|
|
75
|
+
const analysis = {
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
categories: detection.all.map(match => ({
|
|
78
|
+
category: match.category,
|
|
79
|
+
strategy: match.strategy,
|
|
80
|
+
description: match.description,
|
|
81
|
+
severity: match.severity,
|
|
82
|
+
escalate: match.escalate,
|
|
83
|
+
matchedPattern: match.matchedPattern
|
|
84
|
+
})),
|
|
85
|
+
primaryCategory: detection.primary.category,
|
|
86
|
+
strategy: detection.primary.strategy,
|
|
87
|
+
severity: detection.primary.severity,
|
|
88
|
+
shouldEscalate: detection.shouldEscalate,
|
|
89
|
+
details: {},
|
|
90
|
+
rawError: errorStr.slice(0, 500),
|
|
91
|
+
outputSample: outputStr.slice(0, 300)
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Extract specific details based on category
|
|
95
|
+
analysis.details = extractErrorDetails(errorStr, analysis.primaryCategory);
|
|
96
|
+
|
|
97
|
+
return analysis;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extract specific details from error based on category
|
|
102
|
+
*/
|
|
103
|
+
function extractErrorDetails(error, category) {
|
|
104
|
+
const details = {};
|
|
105
|
+
|
|
106
|
+
switch (category) {
|
|
107
|
+
case 'IMPORT_ERROR': {
|
|
108
|
+
// Extract module name
|
|
109
|
+
const moduleMatch = error.match(/(?:module|from) ['"]([^'"]+)['"]/i);
|
|
110
|
+
if (moduleMatch) details.moduleName = moduleMatch[1];
|
|
111
|
+
|
|
112
|
+
// Extract export name
|
|
113
|
+
const exportMatch = error.match(/(?:member|export) ['"]?(\w+)['"]?/i);
|
|
114
|
+
if (exportMatch) details.exportName = exportMatch[1];
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'TYPE_ERROR': {
|
|
118
|
+
// Extract type names
|
|
119
|
+
const typeMatch = error.match(/type ['"]?([^'"]+)['"]? is not assignable to ['"]?([^'"]+)['"]?/i);
|
|
120
|
+
if (typeMatch) {
|
|
121
|
+
details.actualType = typeMatch[1];
|
|
122
|
+
details.expectedType = typeMatch[2];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Extract property name
|
|
126
|
+
const propMatch = error.match(/property ['"]?(\w+)['"]?/i);
|
|
127
|
+
if (propMatch) details.propertyName = propMatch[1];
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'SYNTAX_ERROR': {
|
|
131
|
+
// Extract line number if present
|
|
132
|
+
const lineMatch = error.match(/line (\d+)/i);
|
|
133
|
+
if (lineMatch) details.line = parseInt(lineMatch[1]);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return details;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================================
|
|
142
|
+
// Prompt Refinement
|
|
143
|
+
// ============================================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Refinement strategies for different error types
|
|
147
|
+
*/
|
|
148
|
+
const REFINEMENT_STRATEGIES = {
|
|
149
|
+
import_fix: {
|
|
150
|
+
prefix: `CRITICAL: Your previous output had IMPORT ERRORS.
|
|
151
|
+
Pay close attention to the "Available Imports" section below.
|
|
152
|
+
Use EXACT import paths and export names as shown.
|
|
153
|
+
Do NOT guess or create new import paths.`,
|
|
154
|
+
suffix: `Remember: Use ONLY imports from the "Available Imports" section. Copy them exactly.`
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
type_fix: {
|
|
158
|
+
prefix: `CRITICAL: Your previous output had TYPE ERRORS.
|
|
159
|
+
Pay close attention to the type definitions provided.
|
|
160
|
+
Match prop types EXACTLY as defined.
|
|
161
|
+
Do NOT add optional properties that aren't in the type.`,
|
|
162
|
+
suffix: `Remember: Match types exactly. If unsure, use the simplest valid type.`
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
syntax_fix: {
|
|
166
|
+
prefix: `CRITICAL: Your previous output had SYNTAX ERRORS.
|
|
167
|
+
Output ONLY valid TypeScript/JavaScript code.
|
|
168
|
+
NO markdown fences. NO explanatory text. NO preamble.
|
|
169
|
+
Start directly with the code (import statements or first line of code).`,
|
|
170
|
+
suffix: `Remember: Pure code only. The output will be saved directly to a file.`
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
format_fix: {
|
|
174
|
+
prefix: `CRITICAL: Your previous output included MARKDOWN or EXPLANATIONS.
|
|
175
|
+
Output ONLY the raw code. NO markdown fences (\`\`\`).
|
|
176
|
+
NO "Here's the code" text. NO explanations before or after.
|
|
177
|
+
Start IMMEDIATELY with the first line of code.`,
|
|
178
|
+
suffix: `CRITICAL: Start your response with actual code (import or first statement). Nothing else.`
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
completion_fix: {
|
|
182
|
+
prefix: `CRITICAL: Your previous output was INCOMPLETE.
|
|
183
|
+
You MUST output the COMPLETE file content.
|
|
184
|
+
Do NOT use "..." or "// rest of code" placeholders.
|
|
185
|
+
Do NOT truncate. Include EVERY line.`,
|
|
186
|
+
suffix: `Remember: Complete code only. No placeholders, no truncation.`
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
context_fix: {
|
|
190
|
+
prefix: `CRITICAL: Your previous output referenced NON-EXISTENT code.
|
|
191
|
+
Use ONLY components, hooks, and utilities that exist in the project.
|
|
192
|
+
Check the "Available Components" and "Available Imports" sections.
|
|
193
|
+
Do NOT invent or hallucinate imports or function names.`,
|
|
194
|
+
suffix: `Remember: Only use what's explicitly listed in the context. When unsure, keep it simple.`
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
generic_fix: {
|
|
198
|
+
prefix: `CRITICAL: Your previous attempt failed. Please try again more carefully.
|
|
199
|
+
Follow the instructions exactly. Output only what is requested.`,
|
|
200
|
+
suffix: `Take your time and ensure the output is correct.`
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Additional strategies for new centralized categories
|
|
204
|
+
wait_retry: {
|
|
205
|
+
prefix: `NOTE: The previous attempt encountered a temporary issue (rate limit or timeout).
|
|
206
|
+
This is a retry - the same approach should work.`,
|
|
207
|
+
suffix: `Proceed normally - the temporary issue should be resolved.`
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
retry: {
|
|
211
|
+
prefix: `NOTE: The previous attempt failed due to a transient error.
|
|
212
|
+
Please try again with the same approach.`,
|
|
213
|
+
suffix: `This should work on retry.`
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
context_reduction: {
|
|
217
|
+
prefix: `CRITICAL: Your previous output exceeded context limits.
|
|
218
|
+
Please provide a MORE CONCISE response.
|
|
219
|
+
Focus only on the essential code - no explanations.
|
|
220
|
+
If the task is complex, break it into smaller parts.`,
|
|
221
|
+
suffix: `Keep your response as short as possible while still being complete.`
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
escalate: {
|
|
225
|
+
prefix: `NOTE: This task requires capabilities that may need escalation to a more advanced model.
|
|
226
|
+
Please attempt the task anyway.`,
|
|
227
|
+
suffix: `If unable to complete, indicate what capability is missing.`
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
context_load: {
|
|
231
|
+
prefix: `CRITICAL: Your previous attempt was missing necessary context.
|
|
232
|
+
The required components/files are now provided below.
|
|
233
|
+
Use ONLY what is explicitly available.`,
|
|
234
|
+
suffix: `Remember: Use the context provided, don't assume or invent.`
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
clarify: {
|
|
238
|
+
prefix: `NOTE: The requirements were unclear in the previous attempt.
|
|
239
|
+
Please focus on the CORE functionality described.
|
|
240
|
+
If unsure, implement the simplest interpretation.`,
|
|
241
|
+
suffix: `When in doubt, keep it simple and focused on the main requirement.`
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
pattern_fix: {
|
|
245
|
+
prefix: `CRITICAL: Your previous output didn't follow project patterns.
|
|
246
|
+
Review the patterns and conventions described below.
|
|
247
|
+
Match the existing code style exactly.`,
|
|
248
|
+
suffix: `Remember: Follow project conventions - check naming, structure, and patterns.`
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Refine prompt based on failure analysis
|
|
254
|
+
* @param {string} originalPrompt - Original prompt
|
|
255
|
+
* @param {object} failure - Failure analysis from analyzeFailure()
|
|
256
|
+
* @param {array} previousAttempts - Previous failure analyses
|
|
257
|
+
* @returns {object} Refined prompt and metadata
|
|
258
|
+
*/
|
|
259
|
+
function refinePromptForRetry(originalPrompt, failure, previousAttempts = []) {
|
|
260
|
+
const strategy = failure.strategy || 'generic_fix';
|
|
261
|
+
const refinements = REFINEMENT_STRATEGIES[strategy] || REFINEMENT_STRATEGIES.generic_fix;
|
|
262
|
+
|
|
263
|
+
// Build refinement context
|
|
264
|
+
let refinementContext = '';
|
|
265
|
+
|
|
266
|
+
// Add specific error details
|
|
267
|
+
if (failure.details && Object.keys(failure.details).length > 0) {
|
|
268
|
+
refinementContext += '\n\nSPECIFIC ISSUE:\n';
|
|
269
|
+
for (const [key, value] of Object.entries(failure.details)) {
|
|
270
|
+
refinementContext += `- ${key}: ${value}\n`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Add learnings from previous attempts
|
|
275
|
+
if (previousAttempts.length > 0) {
|
|
276
|
+
refinementContext += '\n\nPREVIOUS ERRORS TO AVOID:\n';
|
|
277
|
+
const uniqueCategories = [...new Set(previousAttempts.map(a => a.primaryCategory))];
|
|
278
|
+
for (const cat of uniqueCategories) {
|
|
279
|
+
const catConfig = ERROR_CATEGORIES[cat];
|
|
280
|
+
if (catConfig) {
|
|
281
|
+
refinementContext += `- ${catConfig.description}\n`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Construct refined prompt
|
|
287
|
+
const refinedPrompt = `${refinements.prefix}
|
|
288
|
+
${refinementContext}
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
${originalPrompt}
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
${refinements.suffix}`;
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
prompt: refinedPrompt,
|
|
298
|
+
strategy,
|
|
299
|
+
addedInstructions: {
|
|
300
|
+
prefix: refinements.prefix,
|
|
301
|
+
suffix: refinements.suffix,
|
|
302
|
+
context: refinementContext
|
|
303
|
+
},
|
|
304
|
+
attemptNumber: previousAttempts.length + 2 // +2 because first attempt was 1
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ============================================================
|
|
309
|
+
// Success Recording
|
|
310
|
+
// ============================================================
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Record a successful recovery to the model adapter
|
|
314
|
+
* @param {string} modelName - Model identifier
|
|
315
|
+
* @param {array} failures - Array of failure analyses that led to success
|
|
316
|
+
* @param {object} successContext - What finally worked
|
|
317
|
+
*/
|
|
318
|
+
function recordSuccessfulRecovery(modelName, failures, successContext = {}) {
|
|
319
|
+
if (!failures || failures.length === 0) return;
|
|
320
|
+
|
|
321
|
+
// Track strategy effectiveness for all strategies used
|
|
322
|
+
const strategiesUsed = [...new Set(failures.map(f => f.strategy))];
|
|
323
|
+
for (const strategy of strategiesUsed) {
|
|
324
|
+
trackStrategyEffectiveness(modelName, strategy, true);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Group failures by category
|
|
328
|
+
const categoryCounts = {};
|
|
329
|
+
for (const failure of failures) {
|
|
330
|
+
const cat = failure.primaryCategory;
|
|
331
|
+
if (!categoryCounts[cat]) {
|
|
332
|
+
categoryCounts[cat] = { count: 0, details: [], strategy: failure.strategy };
|
|
333
|
+
}
|
|
334
|
+
categoryCounts[cat].count++;
|
|
335
|
+
if (failure.details && Object.keys(failure.details).length > 0) {
|
|
336
|
+
categoryCounts[cat].details.push(failure.details);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Generate learning entry (with deduplication)
|
|
341
|
+
const learnings = [];
|
|
342
|
+
const date = new Date().toISOString().split('T')[0];
|
|
343
|
+
let newLearningsCount = 0;
|
|
344
|
+
|
|
345
|
+
for (const [category, data] of Object.entries(categoryCounts)) {
|
|
346
|
+
const config = ERROR_CATEGORIES[category];
|
|
347
|
+
if (!config) continue;
|
|
348
|
+
|
|
349
|
+
// Skip if we already have a recent learning for this category
|
|
350
|
+
if (isDuplicateLearning(modelName, category, data.details)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
newLearningsCount++;
|
|
355
|
+
learnings.push(`### ${date} - Learned from ${data.count} ${category} failures`);
|
|
356
|
+
learnings.push('');
|
|
357
|
+
learnings.push(`**Issue**: ${config.description}`);
|
|
358
|
+
learnings.push(`**Strategy**: ${config.strategy}`);
|
|
359
|
+
|
|
360
|
+
// Add specific guidance based on category
|
|
361
|
+
const guidance = generateGuidanceFromFailures(category, data.details);
|
|
362
|
+
if (guidance) {
|
|
363
|
+
learnings.push(`**Do**: ${guidance.do}`);
|
|
364
|
+
learnings.push(`**Don't**: ${guidance.dont}`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
learnings.push('');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Always log to adaptive learning log (for stats)
|
|
371
|
+
logLearning(modelName, failures, successContext);
|
|
372
|
+
|
|
373
|
+
if (learnings.length > 0) {
|
|
374
|
+
// Add to model adapter file
|
|
375
|
+
const learningText = learnings.join('\n');
|
|
376
|
+
storeSingleLearning(modelName, learningText, {
|
|
377
|
+
taskId: successContext.taskId || 'adaptive-recovery',
|
|
378
|
+
trigger: 'adaptive-learning',
|
|
379
|
+
sourceContext: `Recovered after ${failures.length} failures`
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
console.log(`${colors.green} ✅ ${newLearningsCount} new learning(s) recorded to ${modelName} adapter${colors.reset}`);
|
|
383
|
+
} else {
|
|
384
|
+
console.log(`${colors.dim} ℹ️ No new learnings (duplicates skipped)${colors.reset}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Generate specific do/don't guidance from failure details
|
|
390
|
+
*/
|
|
391
|
+
function generateGuidanceFromFailures(category, details) {
|
|
392
|
+
switch (category) {
|
|
393
|
+
case 'IMPORT_ERROR': {
|
|
394
|
+
const modules = details.map(d => d.moduleName).filter(Boolean);
|
|
395
|
+
const exports = details.map(d => d.exportName).filter(Boolean);
|
|
396
|
+
return {
|
|
397
|
+
do: 'Copy import paths exactly from the "Available Imports" section',
|
|
398
|
+
dont: modules.length > 0
|
|
399
|
+
? `Don't use these incorrect paths: ${modules.slice(0, 3).join(', ')}`
|
|
400
|
+
: 'Don\'t guess import paths'
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
case 'TYPE_ERROR': {
|
|
404
|
+
return {
|
|
405
|
+
do: 'Match prop types exactly as defined in the interface',
|
|
406
|
+
dont: 'Don\'t add extra properties or change required/optional status'
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
case 'MARKDOWN_POLLUTION': {
|
|
410
|
+
return {
|
|
411
|
+
do: 'Start response immediately with code (import statement or first line)',
|
|
412
|
+
dont: 'Don\'t include markdown fences, "Here\'s the code", or explanations'
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
case 'INCOMPLETE_OUTPUT': {
|
|
416
|
+
return {
|
|
417
|
+
do: 'Output complete, working code with all functions implemented',
|
|
418
|
+
dont: 'Don\'t use "...", "// rest of code", or other placeholders'
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
default:
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Log learning to persistent file for analysis
|
|
428
|
+
*/
|
|
429
|
+
function logLearning(modelName, failures, context) {
|
|
430
|
+
let log = { entries: [] };
|
|
431
|
+
|
|
432
|
+
if (fs.existsSync(LEARNING_LOG_PATH)) {
|
|
433
|
+
try {
|
|
434
|
+
log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
|
|
435
|
+
} catch (err) {
|
|
436
|
+
log = { entries: [] };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
log.entries.push({
|
|
441
|
+
timestamp: new Date().toISOString(),
|
|
442
|
+
model: modelName,
|
|
443
|
+
failureCount: failures.length,
|
|
444
|
+
categories: failures.map(f => f.primaryCategory),
|
|
445
|
+
strategies: failures.map(f => f.strategy),
|
|
446
|
+
context: context.taskId || 'unknown',
|
|
447
|
+
recovered: true
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Keep last 100 entries
|
|
451
|
+
if (log.entries.length > 100) {
|
|
452
|
+
log.entries = log.entries.slice(-100);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const dir = path.dirname(LEARNING_LOG_PATH);
|
|
456
|
+
if (!fs.existsSync(dir)) {
|
|
457
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
fs.writeFileSync(LEARNING_LOG_PATH, JSON.stringify(log, null, 2));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ============================================================
|
|
464
|
+
// Strategy Effectiveness Tracking
|
|
465
|
+
// ============================================================
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Track which strategies lead to successful recovery
|
|
469
|
+
* This helps us learn which refinement strategies work best per model
|
|
470
|
+
*/
|
|
471
|
+
function trackStrategyEffectiveness(modelName, strategy, succeeded) {
|
|
472
|
+
let stats = {};
|
|
473
|
+
|
|
474
|
+
if (fs.existsSync(STRATEGY_STATS_PATH)) {
|
|
475
|
+
try {
|
|
476
|
+
stats = JSON.parse(fs.readFileSync(STRATEGY_STATS_PATH, 'utf-8'));
|
|
477
|
+
} catch (err) {
|
|
478
|
+
stats = {};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (!stats[modelName]) {
|
|
483
|
+
stats[modelName] = {};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (!stats[modelName][strategy]) {
|
|
487
|
+
stats[modelName][strategy] = { successes: 0, failures: 0 };
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (succeeded) {
|
|
491
|
+
stats[modelName][strategy].successes++;
|
|
492
|
+
} else {
|
|
493
|
+
stats[modelName][strategy].failures++;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Calculate effectiveness rate (guard against division by zero)
|
|
497
|
+
const s = stats[modelName][strategy];
|
|
498
|
+
const total = s.successes + s.failures;
|
|
499
|
+
s.rate = total > 0 ? s.successes / total : 0;
|
|
500
|
+
|
|
501
|
+
const dir = path.dirname(STRATEGY_STATS_PATH);
|
|
502
|
+
if (!fs.existsSync(dir)) {
|
|
503
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
fs.writeFileSync(STRATEGY_STATS_PATH, JSON.stringify(stats, null, 2));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get strategy effectiveness for a model
|
|
511
|
+
*/
|
|
512
|
+
function getStrategyEffectiveness(modelName) {
|
|
513
|
+
if (!fs.existsSync(STRATEGY_STATS_PATH)) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
const stats = JSON.parse(fs.readFileSync(STRATEGY_STATS_PATH, 'utf-8'));
|
|
519
|
+
return stats[modelName] || null;
|
|
520
|
+
} catch (err) {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Get the best strategy for a model and error category
|
|
527
|
+
*/
|
|
528
|
+
function getBestStrategy(modelName, category) {
|
|
529
|
+
const effectiveness = getStrategyEffectiveness(modelName);
|
|
530
|
+
if (!effectiveness) return null;
|
|
531
|
+
|
|
532
|
+
const categoryStrategy = ERROR_CATEGORIES[category]?.strategy;
|
|
533
|
+
if (categoryStrategy && effectiveness[categoryStrategy]) {
|
|
534
|
+
const rate = effectiveness[categoryStrategy].rate;
|
|
535
|
+
if (rate < 0.5) {
|
|
536
|
+
// This strategy isn't working well, try generic
|
|
537
|
+
return 'generic_fix';
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return categoryStrategy;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// ============================================================
|
|
545
|
+
// Learning Deduplication
|
|
546
|
+
// ============================================================
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Check if a similar learning already exists
|
|
550
|
+
*/
|
|
551
|
+
function isDuplicateLearning(modelName, category, details) {
|
|
552
|
+
const adapterPath = getAdapterPath(modelName);
|
|
553
|
+
if (!fs.existsSync(adapterPath)) return false;
|
|
554
|
+
|
|
555
|
+
const content = fs.readFileSync(adapterPath, 'utf-8');
|
|
556
|
+
const learningsMatch = content.match(/## Learnings\n([\s\S]*?)(?=\n## |$)/);
|
|
557
|
+
if (!learningsMatch) return false;
|
|
558
|
+
|
|
559
|
+
const learnings = learningsMatch[1];
|
|
560
|
+
|
|
561
|
+
// Check if we have a learning for this category in the last 7 days
|
|
562
|
+
const sevenDaysAgo = new Date();
|
|
563
|
+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
|
564
|
+
const dateStr = sevenDaysAgo.toISOString().split('T')[0];
|
|
565
|
+
|
|
566
|
+
// Find entries with both a recent date AND the category on the same line or nearby
|
|
567
|
+
// Pattern: ### YYYY-MM-DD followed by category on same line
|
|
568
|
+
const categoryEscaped = category.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
569
|
+
const recentCategoryPattern = new RegExp(`### (\\d{4}-\\d{2}-\\d{2}).*?${categoryEscaped}`, 'gi');
|
|
570
|
+
const recentMatches = learnings.match(recentCategoryPattern);
|
|
571
|
+
|
|
572
|
+
if (recentMatches) {
|
|
573
|
+
for (const match of recentMatches) {
|
|
574
|
+
const dateMatch = match.match(/(\d{4}-\d{2}-\d{2})/);
|
|
575
|
+
if (dateMatch && dateMatch[1] >= dateStr) {
|
|
576
|
+
return true; // Recent duplicate found - same date+category header
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ============================================================
|
|
585
|
+
// Adaptive Retry Loop
|
|
586
|
+
// ============================================================
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Adaptive retry wrapper for executor calls
|
|
590
|
+
* @param {function} executeFn - Function that executes the task: (prompt) => output
|
|
591
|
+
* @param {function} validateFn - Function that validates output: (output) => { success, error }
|
|
592
|
+
* @param {string} originalPrompt - Original prompt
|
|
593
|
+
* @param {object} options - Options: maxRetries, modelName, taskContext
|
|
594
|
+
* @returns {object} Result with output, success, failures, learningsRecorded
|
|
595
|
+
*/
|
|
596
|
+
async function adaptiveRetry(executeFn, validateFn, originalPrompt, options = {}) {
|
|
597
|
+
const {
|
|
598
|
+
maxRetries = 5,
|
|
599
|
+
modelName = 'unknown',
|
|
600
|
+
taskContext = {}
|
|
601
|
+
} = options;
|
|
602
|
+
|
|
603
|
+
const failures = [];
|
|
604
|
+
let currentPrompt = originalPrompt;
|
|
605
|
+
let lastOutput = null;
|
|
606
|
+
let lastError = null;
|
|
607
|
+
|
|
608
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
609
|
+
try {
|
|
610
|
+
// Execute with current prompt
|
|
611
|
+
const output = await executeFn(currentPrompt);
|
|
612
|
+
lastOutput = output;
|
|
613
|
+
|
|
614
|
+
// Validate output
|
|
615
|
+
const validation = await validateFn(output);
|
|
616
|
+
|
|
617
|
+
if (validation.success) {
|
|
618
|
+
// Success! Record learnings if we had failures
|
|
619
|
+
if (failures.length > 0) {
|
|
620
|
+
recordSuccessfulRecovery(modelName, failures, {
|
|
621
|
+
taskId: taskContext.taskId,
|
|
622
|
+
attemptsTaken: attempt,
|
|
623
|
+
finalPromptLength: currentPrompt.length
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
success: true,
|
|
629
|
+
output,
|
|
630
|
+
attempts: attempt,
|
|
631
|
+
failures,
|
|
632
|
+
learningsRecorded: failures.length > 0
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Failed validation
|
|
637
|
+
lastError = validation.error;
|
|
638
|
+
|
|
639
|
+
} catch (error) {
|
|
640
|
+
lastError = error.message || String(error);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Analyze failure
|
|
644
|
+
const failure = analyzeFailure(lastError, lastOutput, taskContext);
|
|
645
|
+
failures.push(failure);
|
|
646
|
+
|
|
647
|
+
console.log(`${colors.yellow} ⚠️ Attempt ${attempt} failed: ${failure.primaryCategory}${colors.reset}`);
|
|
648
|
+
|
|
649
|
+
// Refine prompt for next attempt
|
|
650
|
+
if (attempt < maxRetries) {
|
|
651
|
+
const refined = refinePromptForRetry(originalPrompt, failure, failures.slice(0, -1));
|
|
652
|
+
currentPrompt = refined.prompt;
|
|
653
|
+
console.log(`${colors.dim} 📝 Refining prompt with ${refined.strategy} strategy${colors.reset}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// All attempts failed
|
|
658
|
+
return {
|
|
659
|
+
success: false,
|
|
660
|
+
output: lastOutput,
|
|
661
|
+
error: lastError,
|
|
662
|
+
attempts: maxRetries,
|
|
663
|
+
failures,
|
|
664
|
+
learningsRecorded: false
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ============================================================
|
|
669
|
+
// Community Sharing - Export Learnings
|
|
670
|
+
// ============================================================
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Export aggregated learnings for contribution to wogi-flow
|
|
674
|
+
* This allows users to share their model-specific learnings with the community
|
|
675
|
+
*/
|
|
676
|
+
function exportLearningsForSharing() {
|
|
677
|
+
const exportData = {
|
|
678
|
+
exportedAt: new Date().toISOString(),
|
|
679
|
+
version: '1.0',
|
|
680
|
+
models: {}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// Load learning log
|
|
684
|
+
if (!fs.existsSync(LEARNING_LOG_PATH)) {
|
|
685
|
+
return { success: false, error: 'No learning data found' };
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
|
|
689
|
+
|
|
690
|
+
// Aggregate by model
|
|
691
|
+
for (const entry of log.entries) {
|
|
692
|
+
const model = entry.model;
|
|
693
|
+
if (!exportData.models[model]) {
|
|
694
|
+
exportData.models[model] = {
|
|
695
|
+
totalRecoveries: 0,
|
|
696
|
+
categoryFrequency: {},
|
|
697
|
+
learnings: []
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
exportData.models[model].totalRecoveries++;
|
|
702
|
+
|
|
703
|
+
for (const cat of entry.categories) {
|
|
704
|
+
exportData.models[model].categoryFrequency[cat] =
|
|
705
|
+
(exportData.models[model].categoryFrequency[cat] || 0) + 1;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Load model adapter files and extract learnings sections
|
|
710
|
+
const adaptersDir = path.join(PROJECT_ROOT, '.workflow', 'model-adapters');
|
|
711
|
+
if (fs.existsSync(adaptersDir)) {
|
|
712
|
+
const adapterFiles = fs.readdirSync(adaptersDir).filter(f => f.endsWith('.md') && !f.startsWith('_'));
|
|
713
|
+
|
|
714
|
+
for (const file of adapterFiles) {
|
|
715
|
+
const modelName = file.replace('.md', '');
|
|
716
|
+
const content = fs.readFileSync(path.join(adaptersDir, file), 'utf-8');
|
|
717
|
+
|
|
718
|
+
// Extract learnings section
|
|
719
|
+
const learningsMatch = content.match(/## Learnings\n([\s\S]*?)(?=\n## |$)/);
|
|
720
|
+
if (learningsMatch && learningsMatch[1].trim()) {
|
|
721
|
+
if (!exportData.models[modelName]) {
|
|
722
|
+
exportData.models[modelName] = {
|
|
723
|
+
totalRecoveries: 0,
|
|
724
|
+
categoryFrequency: {},
|
|
725
|
+
learnings: []
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Parse individual learnings
|
|
730
|
+
const learningBlocks = learningsMatch[1].split(/\n### /).filter(Boolean);
|
|
731
|
+
for (const block of learningBlocks) {
|
|
732
|
+
if (block.includes('Auto-learned') || block.includes('adaptive-learning')) {
|
|
733
|
+
exportData.models[modelName].learnings.push(block.trim());
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Generate summary
|
|
741
|
+
exportData.summary = {
|
|
742
|
+
totalModels: Object.keys(exportData.models).length,
|
|
743
|
+
totalLearnings: Object.values(exportData.models).reduce((sum, m) => sum + m.learnings.length, 0),
|
|
744
|
+
topIssues: getTopIssues(exportData.models)
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
return { success: true, data: exportData };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Get top issues across all models
|
|
752
|
+
*/
|
|
753
|
+
function getTopIssues(models) {
|
|
754
|
+
const allCategories = {};
|
|
755
|
+
|
|
756
|
+
for (const model of Object.values(models)) {
|
|
757
|
+
for (const [cat, count] of Object.entries(model.categoryFrequency)) {
|
|
758
|
+
allCategories[cat] = (allCategories[cat] || 0) + count;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return Object.entries(allCategories)
|
|
763
|
+
.sort((a, b) => b[1] - a[1])
|
|
764
|
+
.slice(0, 5)
|
|
765
|
+
.map(([category, count]) => ({
|
|
766
|
+
category,
|
|
767
|
+
count,
|
|
768
|
+
description: ERROR_CATEGORIES[category]?.description || 'Unknown'
|
|
769
|
+
}));
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Format export for PR submission
|
|
774
|
+
*/
|
|
775
|
+
function formatExportForPR(exportData) {
|
|
776
|
+
const lines = [
|
|
777
|
+
'# Model Adapter Learnings Contribution',
|
|
778
|
+
'',
|
|
779
|
+
`Exported: ${exportData.exportedAt}`,
|
|
780
|
+
`Models: ${exportData.summary.totalModels}`,
|
|
781
|
+
`Learnings: ${exportData.summary.totalLearnings}`,
|
|
782
|
+
'',
|
|
783
|
+
'## Top Issues Encountered',
|
|
784
|
+
''
|
|
785
|
+
];
|
|
786
|
+
|
|
787
|
+
for (const issue of exportData.summary.topIssues) {
|
|
788
|
+
lines.push(`- **${issue.category}** (${issue.count}x): ${issue.description}`);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
lines.push('', '## Per-Model Learnings', '');
|
|
792
|
+
|
|
793
|
+
for (const [model, data] of Object.entries(exportData.models)) {
|
|
794
|
+
if (data.learnings.length === 0) continue;
|
|
795
|
+
|
|
796
|
+
lines.push(`### ${model}`, '');
|
|
797
|
+
lines.push(`Recoveries: ${data.totalRecoveries}`, '');
|
|
798
|
+
|
|
799
|
+
for (const learning of data.learnings.slice(0, 5)) {
|
|
800
|
+
lines.push('```');
|
|
801
|
+
lines.push(learning);
|
|
802
|
+
lines.push('```');
|
|
803
|
+
lines.push('');
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return lines.join('\n');
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ============================================================
|
|
811
|
+
// Automatic PR Contribution
|
|
812
|
+
// ============================================================
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Check if gh CLI is available and authenticated
|
|
816
|
+
*/
|
|
817
|
+
function checkGitHubCLI() {
|
|
818
|
+
try {
|
|
819
|
+
execFileSync('gh', ['auth', 'status'], { stdio: 'pipe' });
|
|
820
|
+
return { available: true };
|
|
821
|
+
} catch (err) {
|
|
822
|
+
return { available: false, error: 'gh CLI not authenticated. Run: gh auth login' };
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Create a PR with learnings to the wogi-flow repository
|
|
828
|
+
* @param {string} upstreamRepo - The upstream repo (e.g., 'owner/wogi-flow')
|
|
829
|
+
* @param {object} options - Options
|
|
830
|
+
*/
|
|
831
|
+
async function contributeLearnings(upstreamRepo = 'your-org/wogi-flow', options = {}) {
|
|
832
|
+
const { dryRun = false } = options;
|
|
833
|
+
|
|
834
|
+
// Check prerequisites
|
|
835
|
+
const ghCheck = checkGitHubCLI();
|
|
836
|
+
if (!ghCheck.available) {
|
|
837
|
+
return { success: false, error: ghCheck.error };
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Export learnings
|
|
841
|
+
const exportResult = exportLearningsForSharing();
|
|
842
|
+
if (!exportResult.success) {
|
|
843
|
+
return { success: false, error: 'No learnings to contribute' };
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const { data } = exportResult;
|
|
847
|
+
if (data.summary.totalLearnings === 0) {
|
|
848
|
+
return { success: false, error: 'No new learnings to contribute' };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Generate unique branch name
|
|
852
|
+
const timestamp = Date.now();
|
|
853
|
+
const branchName = `learnings-contribution-${timestamp}`;
|
|
854
|
+
|
|
855
|
+
if (dryRun) {
|
|
856
|
+
console.log(`${colors.cyan}[DRY RUN] Would create PR with:${colors.reset}`);
|
|
857
|
+
console.log(` Branch: ${branchName}`);
|
|
858
|
+
console.log(` Models: ${data.summary.totalModels}`);
|
|
859
|
+
console.log(` Learnings: ${data.summary.totalLearnings}`);
|
|
860
|
+
return { success: true, dryRun: true };
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
try {
|
|
864
|
+
// Create contribution file
|
|
865
|
+
const contributionDir = path.join(PROJECT_ROOT, '.workflow', 'contributions');
|
|
866
|
+
if (!fs.existsSync(contributionDir)) {
|
|
867
|
+
fs.mkdirSync(contributionDir, { recursive: true });
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const contributionFile = path.join(contributionDir, `contribution-${timestamp}.md`);
|
|
871
|
+
fs.writeFileSync(contributionFile, formatExportForPR(data));
|
|
872
|
+
|
|
873
|
+
// Also create/update model adapter patches
|
|
874
|
+
const patchesDir = path.join(contributionDir, 'patches');
|
|
875
|
+
if (!fs.existsSync(patchesDir)) {
|
|
876
|
+
fs.mkdirSync(patchesDir, { recursive: true });
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
for (const [model, modelData] of Object.entries(data.models)) {
|
|
880
|
+
if (modelData.learnings.length === 0) continue;
|
|
881
|
+
|
|
882
|
+
const patchContent = [
|
|
883
|
+
`# Learnings for ${model}`,
|
|
884
|
+
'',
|
|
885
|
+
`Contributed: ${data.exportedAt}`,
|
|
886
|
+
`Recoveries: ${modelData.totalRecoveries}`,
|
|
887
|
+
'',
|
|
888
|
+
'## New Learnings',
|
|
889
|
+
'',
|
|
890
|
+
...modelData.learnings.map(l => `### ${l}`),
|
|
891
|
+
''
|
|
892
|
+
].join('\n');
|
|
893
|
+
|
|
894
|
+
fs.writeFileSync(path.join(patchesDir, `${model}.md`), patchContent);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
console.log(`${colors.green}✅ Contribution files created in ${contributionDir}${colors.reset}`);
|
|
898
|
+
console.log('');
|
|
899
|
+
console.log(`${colors.cyan}To submit a PR:${colors.reset}`);
|
|
900
|
+
console.log(` 1. Fork ${upstreamRepo} on GitHub`);
|
|
901
|
+
console.log(` 2. Clone your fork`);
|
|
902
|
+
console.log(` 3. Copy files from ${contributionDir} to .workflow/model-adapters/`);
|
|
903
|
+
console.log(` 4. Commit and push`);
|
|
904
|
+
console.log(` 5. Create PR with title: "model-adapters: community learnings"`);
|
|
905
|
+
console.log('');
|
|
906
|
+
console.log(`${colors.dim}Or run with --auto-pr to attempt automatic PR creation (requires fork)${colors.reset}`);
|
|
907
|
+
|
|
908
|
+
return {
|
|
909
|
+
success: true,
|
|
910
|
+
contributionDir,
|
|
911
|
+
models: Object.keys(data.models),
|
|
912
|
+
learnings: data.summary.totalLearnings
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
} catch (err) {
|
|
916
|
+
return { success: false, error: err.message };
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Attempt to create PR automatically using gh CLI
|
|
922
|
+
* This requires the user to have forked the wogi-flow repo
|
|
923
|
+
*/
|
|
924
|
+
async function createAutoPR(upstreamRepo, options = {}) {
|
|
925
|
+
const { forkRepo } = options;
|
|
926
|
+
|
|
927
|
+
if (!forkRepo) {
|
|
928
|
+
return { success: false, error: 'Fork repo required. Use --fork=username/wogi-flow' };
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Validate repository formats to prevent injection
|
|
932
|
+
if (!validateRepoFormat(forkRepo)) {
|
|
933
|
+
return { success: false, error: 'Invalid fork repo format. Expected: owner/repo' };
|
|
934
|
+
}
|
|
935
|
+
if (!validateRepoFormat(upstreamRepo)) {
|
|
936
|
+
return { success: false, error: 'Invalid upstream repo format. Expected: owner/repo' };
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const ghCheck = checkGitHubCLI();
|
|
940
|
+
if (!ghCheck.available) {
|
|
941
|
+
return { success: false, error: ghCheck.error };
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Export and prepare
|
|
945
|
+
const contribution = await contributeLearnings(upstreamRepo, { dryRun: false });
|
|
946
|
+
if (!contribution.success) {
|
|
947
|
+
return contribution;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const timestamp = Date.now();
|
|
951
|
+
const branchName = `learnings-${timestamp}`;
|
|
952
|
+
|
|
953
|
+
try {
|
|
954
|
+
// Clone fork, create branch, add files, push, create PR
|
|
955
|
+
const tempDir = path.join(PROJECT_ROOT, '.workflow', 'temp-pr');
|
|
956
|
+
|
|
957
|
+
console.log(`${colors.cyan}Creating PR automatically...${colors.reset}`);
|
|
958
|
+
|
|
959
|
+
// Clone the fork using safe git command (array arguments prevent injection)
|
|
960
|
+
execFileSync('git', ['clone', '--depth', '1', `https://github.com/${forkRepo}.git`, tempDir], { stdio: 'pipe' });
|
|
961
|
+
|
|
962
|
+
// Create branch
|
|
963
|
+
safeGitCommand(['checkout', '-b', branchName], { cwd: tempDir });
|
|
964
|
+
|
|
965
|
+
// Copy contribution files
|
|
966
|
+
const patchesDir = path.join(contribution.contributionDir, 'patches');
|
|
967
|
+
const targetDir = path.join(tempDir, '.workflow', 'model-adapters');
|
|
968
|
+
|
|
969
|
+
if (fs.existsSync(patchesDir)) {
|
|
970
|
+
const patches = fs.readdirSync(patchesDir);
|
|
971
|
+
for (const patch of patches) {
|
|
972
|
+
const src = path.join(patchesDir, patch);
|
|
973
|
+
const dest = path.join(targetDir, patch);
|
|
974
|
+
|
|
975
|
+
// Append learnings to existing adapter or create new
|
|
976
|
+
if (fs.existsSync(dest)) {
|
|
977
|
+
const existing = fs.readFileSync(dest, 'utf-8');
|
|
978
|
+
const addition = fs.readFileSync(src, 'utf-8');
|
|
979
|
+
// Append new learnings section
|
|
980
|
+
fs.writeFileSync(dest, existing + '\n' + addition);
|
|
981
|
+
} else {
|
|
982
|
+
fs.copyFileSync(src, dest);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// Commit using safe git commands
|
|
988
|
+
safeGitCommand(['add', '.'], { cwd: tempDir });
|
|
989
|
+
safeGitCommand(['commit', '-m', 'model-adapters: community learnings from adaptive learning'], { cwd: tempDir });
|
|
990
|
+
|
|
991
|
+
// Push
|
|
992
|
+
safeGitCommand(['push', 'origin', branchName], { cwd: tempDir });
|
|
993
|
+
|
|
994
|
+
// Create PR using gh with proper argument handling
|
|
995
|
+
const prTitle = 'model-adapters: community learnings contribution';
|
|
996
|
+
const prBody = `## Community Learnings Contribution
|
|
997
|
+
|
|
998
|
+
This PR adds learnings from adaptive learning sessions.
|
|
999
|
+
|
|
1000
|
+
**Models:** ${contribution.models.join(', ')}
|
|
1001
|
+
**New Learnings:** ${contribution.learnings}
|
|
1002
|
+
|
|
1003
|
+
These learnings help improve prompt refinement for all wogi-flow users.
|
|
1004
|
+
|
|
1005
|
+
---
|
|
1006
|
+
*Auto-generated by wogi-flow adaptive learning system*`;
|
|
1007
|
+
|
|
1008
|
+
const forkOwner = forkRepo.split('/')[0];
|
|
1009
|
+
execFileSync('gh', [
|
|
1010
|
+
'pr', 'create',
|
|
1011
|
+
'--repo', upstreamRepo,
|
|
1012
|
+
'--head', `${forkOwner}:${branchName}`,
|
|
1013
|
+
'--title', prTitle,
|
|
1014
|
+
'--body', prBody
|
|
1015
|
+
], { cwd: tempDir, stdio: 'inherit' });
|
|
1016
|
+
|
|
1017
|
+
// Cleanup
|
|
1018
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
1019
|
+
|
|
1020
|
+
console.log(`${colors.green}✅ PR created successfully!${colors.reset}`);
|
|
1021
|
+
return { success: true };
|
|
1022
|
+
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
return { success: false, error: err.message };
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// ============================================================
|
|
1029
|
+
// Exports
|
|
1030
|
+
// ============================================================
|
|
1031
|
+
|
|
1032
|
+
module.exports = {
|
|
1033
|
+
// Core functions
|
|
1034
|
+
analyzeFailure,
|
|
1035
|
+
refinePromptForRetry,
|
|
1036
|
+
recordSuccessfulRecovery,
|
|
1037
|
+
adaptiveRetry,
|
|
1038
|
+
|
|
1039
|
+
// Constants for external use
|
|
1040
|
+
ERROR_CATEGORIES,
|
|
1041
|
+
REFINEMENT_STRATEGIES,
|
|
1042
|
+
|
|
1043
|
+
// Re-export centralized failure categories for convenience
|
|
1044
|
+
FailureCategory,
|
|
1045
|
+
detectCategory,
|
|
1046
|
+
shouldEscalate: checkShouldEscalate,
|
|
1047
|
+
|
|
1048
|
+
// Utilities
|
|
1049
|
+
extractErrorDetails,
|
|
1050
|
+
generateGuidanceFromFailures,
|
|
1051
|
+
logLearning,
|
|
1052
|
+
|
|
1053
|
+
// Strategy effectiveness
|
|
1054
|
+
trackStrategyEffectiveness,
|
|
1055
|
+
getStrategyEffectiveness,
|
|
1056
|
+
getBestStrategy,
|
|
1057
|
+
|
|
1058
|
+
// Deduplication
|
|
1059
|
+
isDuplicateLearning,
|
|
1060
|
+
|
|
1061
|
+
// Community sharing
|
|
1062
|
+
exportLearningsForSharing,
|
|
1063
|
+
formatExportForPR,
|
|
1064
|
+
contributeLearnings,
|
|
1065
|
+
createAutoPR,
|
|
1066
|
+
checkGitHubCLI
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
// ============================================================
|
|
1070
|
+
// CLI
|
|
1071
|
+
// ============================================================
|
|
1072
|
+
|
|
1073
|
+
if (require.main === module) {
|
|
1074
|
+
(async () => {
|
|
1075
|
+
const args = process.argv.slice(2);
|
|
1076
|
+
const command = args[0];
|
|
1077
|
+
|
|
1078
|
+
switch (command) {
|
|
1079
|
+
case 'stats': {
|
|
1080
|
+
// Show learning statistics
|
|
1081
|
+
if (fs.existsSync(LEARNING_LOG_PATH)) {
|
|
1082
|
+
const log = JSON.parse(fs.readFileSync(LEARNING_LOG_PATH, 'utf-8'));
|
|
1083
|
+
console.log('\n📊 Adaptive Learning Statistics\n');
|
|
1084
|
+
console.log(`Total recoveries: ${log.entries.length}`);
|
|
1085
|
+
|
|
1086
|
+
// Group by model
|
|
1087
|
+
const byModel = {};
|
|
1088
|
+
for (const entry of log.entries) {
|
|
1089
|
+
if (!byModel[entry.model]) {
|
|
1090
|
+
byModel[entry.model] = { count: 0, categories: {} };
|
|
1091
|
+
}
|
|
1092
|
+
byModel[entry.model].count++;
|
|
1093
|
+
for (const cat of entry.categories) {
|
|
1094
|
+
byModel[entry.model].categories[cat] = (byModel[entry.model].categories[cat] || 0) + 1;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
console.log('\nBy model:');
|
|
1099
|
+
for (const [model, data] of Object.entries(byModel)) {
|
|
1100
|
+
console.log(`\n ${model}: ${data.count} recoveries`);
|
|
1101
|
+
for (const [cat, count] of Object.entries(data.categories)) {
|
|
1102
|
+
console.log(` - ${cat}: ${count}`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
} else {
|
|
1106
|
+
console.log('No adaptive learning data yet.');
|
|
1107
|
+
}
|
|
1108
|
+
break;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
case 'test': {
|
|
1112
|
+
// Test failure analysis
|
|
1113
|
+
const testErrors = [
|
|
1114
|
+
"Cannot find module '@/components/Button'",
|
|
1115
|
+
"Type 'string' is not assignable to type 'number'",
|
|
1116
|
+
"Unexpected token, expected '}'",
|
|
1117
|
+
"```typescript\nimport React from 'react'",
|
|
1118
|
+
"// ... rest of component"
|
|
1119
|
+
];
|
|
1120
|
+
|
|
1121
|
+
console.log('\n🧪 Testing Failure Analysis\n');
|
|
1122
|
+
for (const error of testErrors) {
|
|
1123
|
+
const analysis = analyzeFailure(error, '');
|
|
1124
|
+
console.log(`Error: "${error.slice(0, 50)}..."`);
|
|
1125
|
+
console.log(` Category: ${analysis.primaryCategory}`);
|
|
1126
|
+
console.log(` Strategy: ${analysis.strategy}`);
|
|
1127
|
+
console.log('');
|
|
1128
|
+
}
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
case 'export': {
|
|
1133
|
+
// Export learnings for community contribution
|
|
1134
|
+
console.log('\n📤 Exporting Learnings for Community Contribution\n');
|
|
1135
|
+
|
|
1136
|
+
const result = exportLearningsForSharing();
|
|
1137
|
+
|
|
1138
|
+
if (!result.success) {
|
|
1139
|
+
console.log(`${colors.yellow}No learning data to export yet.${colors.reset}`);
|
|
1140
|
+
console.log('Run some hybrid mode tasks first to generate learnings.');
|
|
1141
|
+
process.exit(1);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const { data } = result;
|
|
1145
|
+
console.log(`Models with learnings: ${data.summary.totalModels}`);
|
|
1146
|
+
console.log(`Total learnings: ${data.summary.totalLearnings}`);
|
|
1147
|
+
|
|
1148
|
+
if (data.summary.topIssues.length > 0) {
|
|
1149
|
+
console.log('\nTop issues encountered:');
|
|
1150
|
+
for (const issue of data.summary.topIssues) {
|
|
1151
|
+
console.log(` - ${issue.category} (${issue.count}x): ${issue.description}`);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Save export file
|
|
1156
|
+
const exportPath = path.join(PROJECT_ROOT, '.workflow', 'learnings-export.json');
|
|
1157
|
+
fs.writeFileSync(exportPath, JSON.stringify(data, null, 2));
|
|
1158
|
+
console.log(`\n${colors.green}✅ Exported to: ${exportPath}${colors.reset}`);
|
|
1159
|
+
|
|
1160
|
+
// Also create PR-ready markdown
|
|
1161
|
+
const prPath = path.join(PROJECT_ROOT, '.workflow', 'learnings-contribution.md');
|
|
1162
|
+
fs.writeFileSync(prPath, formatExportForPR(data));
|
|
1163
|
+
console.log(`${colors.green}✅ PR-ready format: ${prPath}${colors.reset}`);
|
|
1164
|
+
|
|
1165
|
+
console.log(`
|
|
1166
|
+
${colors.cyan}To contribute these learnings to wogi-flow:${colors.reset}
|
|
1167
|
+
1. Fork https://github.com/your-org/wogi-flow
|
|
1168
|
+
2. Copy ${prPath} content to your PR
|
|
1169
|
+
3. Submit PR with title: "model-adapters: community learnings"
|
|
1170
|
+
|
|
1171
|
+
Or use: flow hybrid learning contribute --auto-pr --fork=yourusername/wogi-flow
|
|
1172
|
+
`);
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
case 'contribute': {
|
|
1177
|
+
// Contribute learnings via PR
|
|
1178
|
+
console.log('\n🤝 Contributing Learnings to Community\n');
|
|
1179
|
+
|
|
1180
|
+
const autoPR = args.includes('--auto-pr');
|
|
1181
|
+
const forkArg = args.find(a => a.startsWith('--fork='));
|
|
1182
|
+
const forkRepo = forkArg ? forkArg.split('=')[1] : null;
|
|
1183
|
+
const upstreamArg = args.find(a => a.startsWith('--upstream='));
|
|
1184
|
+
const upstreamRepo = upstreamArg ? upstreamArg.split('=')[1] : 'your-org/wogi-flow';
|
|
1185
|
+
|
|
1186
|
+
if (autoPR) {
|
|
1187
|
+
if (!forkRepo) {
|
|
1188
|
+
console.log(`${colors.red}Error: --fork=username/wogi-flow required for auto-PR${colors.reset}`);
|
|
1189
|
+
console.log('Example: flow hybrid learning contribute --auto-pr --fork=myuser/wogi-flow');
|
|
1190
|
+
process.exit(1);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const result = await createAutoPR(upstreamRepo, { forkRepo });
|
|
1194
|
+
if (!result.success) {
|
|
1195
|
+
console.log(`${colors.red}Error: ${result.error}${colors.reset}`);
|
|
1196
|
+
process.exit(1);
|
|
1197
|
+
}
|
|
1198
|
+
} else {
|
|
1199
|
+
const result = await contributeLearnings(upstreamRepo);
|
|
1200
|
+
if (!result.success) {
|
|
1201
|
+
console.log(`${colors.red}Error: ${result.error}${colors.reset}`);
|
|
1202
|
+
process.exit(1);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
case 'effectiveness': {
|
|
1209
|
+
// Show strategy effectiveness stats
|
|
1210
|
+
console.log('\n📈 Strategy Effectiveness\n');
|
|
1211
|
+
|
|
1212
|
+
if (!fs.existsSync(STRATEGY_STATS_PATH)) {
|
|
1213
|
+
console.log('No strategy effectiveness data yet.');
|
|
1214
|
+
console.log('Run some hybrid mode tasks first to generate data.');
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const stats = JSON.parse(fs.readFileSync(STRATEGY_STATS_PATH, 'utf-8'));
|
|
1219
|
+
|
|
1220
|
+
for (const [model, strategies] of Object.entries(stats)) {
|
|
1221
|
+
console.log(`\n${colors.cyan}${model}${colors.reset}`);
|
|
1222
|
+
for (const [strategy, data] of Object.entries(strategies)) {
|
|
1223
|
+
const rate = (data.rate * 100).toFixed(0);
|
|
1224
|
+
const color = data.rate > 0.7 ? colors.green : data.rate > 0.5 ? colors.yellow : colors.red;
|
|
1225
|
+
console.log(` ${strategy}: ${color}${rate}%${colors.reset} (${data.successes}/${data.successes + data.failures})`);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
default:
|
|
1232
|
+
console.log(`
|
|
1233
|
+
Wogi Flow - Adaptive Learning
|
|
1234
|
+
|
|
1235
|
+
Usage:
|
|
1236
|
+
node flow-adaptive-learning.js <command>
|
|
1237
|
+
|
|
1238
|
+
Commands:
|
|
1239
|
+
stats Show learning statistics
|
|
1240
|
+
test Test failure analysis with sample errors
|
|
1241
|
+
export Export learnings for community contribution
|
|
1242
|
+
contribute Create contribution files for PR
|
|
1243
|
+
effectiveness Show strategy effectiveness per model
|
|
1244
|
+
|
|
1245
|
+
Options for contribute:
|
|
1246
|
+
--auto-pr Automatically create GitHub PR
|
|
1247
|
+
--fork=user/repo Your fork of wogi-flow (required for --auto-pr)
|
|
1248
|
+
--upstream=user/repo Upstream repo (default: your-org/wogi-flow)
|
|
1249
|
+
|
|
1250
|
+
This module enables hybrid mode to learn from failures:
|
|
1251
|
+
1. When executor fails, analyze WHY
|
|
1252
|
+
2. Refine prompt based on failure category
|
|
1253
|
+
3. Retry with improved instructions
|
|
1254
|
+
4. On success, update model adapter with learnings
|
|
1255
|
+
5. Share learnings with the community via PR
|
|
1256
|
+
`);
|
|
1257
|
+
}
|
|
1258
|
+
})();
|
|
1259
|
+
}
|