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,802 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Model-Specific Adapters
|
|
5
|
+
*
|
|
6
|
+
* Manages per-model learning and prompt adjustments.
|
|
7
|
+
* Different models (Claude Opus, Sonnet, Ollama, LM Studio) have different
|
|
8
|
+
* strengths and weaknesses. This module:
|
|
9
|
+
*
|
|
10
|
+
* 1. Detects current model from config/environment
|
|
11
|
+
* 2. Loads model-specific adapter with prompt adjustments
|
|
12
|
+
* 3. Records per-model success/failure patterns
|
|
13
|
+
* 4. Auto-learns from repeated mistakes (updates adapter file)
|
|
14
|
+
*
|
|
15
|
+
* Uses multi-model approach with per-model learning.
|
|
16
|
+
*
|
|
17
|
+
* Usage as module:
|
|
18
|
+
* const { getCurrentModel, getPromptAdjustments, recordModelResult } = require('./flow-model-adapter');
|
|
19
|
+
* const model = getCurrentModel();
|
|
20
|
+
* const adjustments = getPromptAdjustments(model);
|
|
21
|
+
*
|
|
22
|
+
* Usage as CLI:
|
|
23
|
+
* flow model-adapter # Show current model info
|
|
24
|
+
* flow model-adapter --list # List available adapters
|
|
25
|
+
* flow model-adapter --stats # Show per-model statistics
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const { getProjectRoot, getConfig, PATHS, colors } = require('./flow-utils');
|
|
31
|
+
|
|
32
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
33
|
+
const ADAPTERS_DIR = path.join(PROJECT_ROOT, '.workflow', 'model-adapters');
|
|
34
|
+
const MODEL_STATS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'model-stats.json');
|
|
35
|
+
|
|
36
|
+
// ============================================================
|
|
37
|
+
// Model Detection
|
|
38
|
+
// ============================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Known model patterns for identification
|
|
42
|
+
*/
|
|
43
|
+
const MODEL_PATTERNS = {
|
|
44
|
+
'claude-opus': ['claude-opus', 'opus', 'claude-3-opus', 'claude-opus-4'],
|
|
45
|
+
'claude-sonnet': ['claude-sonnet', 'sonnet', 'claude-3-sonnet', 'claude-sonnet-4'],
|
|
46
|
+
'claude-haiku': ['claude-haiku', 'haiku', 'claude-3-haiku'],
|
|
47
|
+
'gpt-4': ['gpt-4', 'gpt-4-turbo', 'gpt-4o'],
|
|
48
|
+
'gpt-3.5': ['gpt-3.5', 'gpt-3.5-turbo'],
|
|
49
|
+
'ollama-nemotron': ['nemotron', 'nvidia-nemotron'],
|
|
50
|
+
'ollama-qwen': ['qwen', 'qwen-coder', 'qwen3'],
|
|
51
|
+
'ollama-deepseek': ['deepseek', 'deepseek-coder'],
|
|
52
|
+
'ollama-codellama': ['codellama', 'code-llama'],
|
|
53
|
+
'ollama-mistral': ['mistral', 'mixtral'],
|
|
54
|
+
'lm-studio': ['lm-studio', 'lmstudio']
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get current model from config or environment
|
|
59
|
+
*/
|
|
60
|
+
function getCurrentModel() {
|
|
61
|
+
const config = getConfig();
|
|
62
|
+
|
|
63
|
+
// Check hybrid mode config first
|
|
64
|
+
if (config.hybrid?.enabled) {
|
|
65
|
+
// New config structure: hybrid.executor.model
|
|
66
|
+
if (config.hybrid.executor?.model) {
|
|
67
|
+
return normalizeModelName(config.hybrid.executor.model);
|
|
68
|
+
}
|
|
69
|
+
// Legacy config structure: hybrid.model directly
|
|
70
|
+
if (config.hybrid.model) {
|
|
71
|
+
return normalizeModelName(config.hybrid.model);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check environment variable
|
|
76
|
+
if (process.env.CLAUDE_MODEL) {
|
|
77
|
+
return normalizeModelName(process.env.CLAUDE_MODEL);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check modelAdapters config
|
|
81
|
+
if (config.modelAdapters?.currentModel) {
|
|
82
|
+
return normalizeModelName(config.modelAdapters.currentModel);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Default to claude-opus (most capable)
|
|
86
|
+
return 'claude-opus';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Normalize model name to standard format
|
|
91
|
+
*/
|
|
92
|
+
function normalizeModelName(modelName) {
|
|
93
|
+
const lower = modelName.toLowerCase();
|
|
94
|
+
|
|
95
|
+
for (const [standard, patterns] of Object.entries(MODEL_PATTERNS)) {
|
|
96
|
+
for (const pattern of patterns) {
|
|
97
|
+
if (lower.includes(pattern)) {
|
|
98
|
+
return standard;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Return as-is if no pattern matches
|
|
104
|
+
return lower.replace(/[^a-z0-9-]/g, '-');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get model family (claude, gpt, ollama, lm-studio)
|
|
109
|
+
*/
|
|
110
|
+
function getModelFamily(modelName) {
|
|
111
|
+
const model = normalizeModelName(modelName);
|
|
112
|
+
|
|
113
|
+
if (model.startsWith('claude-')) return 'claude';
|
|
114
|
+
if (model.startsWith('gpt-')) return 'openai';
|
|
115
|
+
if (model.startsWith('ollama-')) return 'ollama';
|
|
116
|
+
if (model.startsWith('lm-studio')) return 'lm-studio';
|
|
117
|
+
|
|
118
|
+
return 'unknown';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================
|
|
122
|
+
// Adapter Loading
|
|
123
|
+
// ============================================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get path to adapter file for a model
|
|
127
|
+
*/
|
|
128
|
+
function getAdapterPath(modelName) {
|
|
129
|
+
const normalized = normalizeModelName(modelName);
|
|
130
|
+
return path.join(ADAPTERS_DIR, `${normalized}.md`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Load adapter file content
|
|
135
|
+
*/
|
|
136
|
+
function loadAdapter(modelName) {
|
|
137
|
+
const adapterPath = getAdapterPath(modelName);
|
|
138
|
+
|
|
139
|
+
if (!fs.existsSync(adapterPath)) {
|
|
140
|
+
// Try loading family adapter
|
|
141
|
+
const family = getModelFamily(modelName);
|
|
142
|
+
const familyPath = path.join(ADAPTERS_DIR, `${family}-default.md`);
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(familyPath)) {
|
|
145
|
+
return {
|
|
146
|
+
path: familyPath,
|
|
147
|
+
content: fs.readFileSync(familyPath, 'utf-8'),
|
|
148
|
+
isDefault: true
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Load template as last resort
|
|
153
|
+
const templatePath = path.join(ADAPTERS_DIR, '_template.md');
|
|
154
|
+
if (fs.existsSync(templatePath)) {
|
|
155
|
+
return {
|
|
156
|
+
path: templatePath,
|
|
157
|
+
content: fs.readFileSync(templatePath, 'utf-8'),
|
|
158
|
+
isTemplate: true
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
path: adapterPath,
|
|
167
|
+
content: fs.readFileSync(adapterPath, 'utf-8'),
|
|
168
|
+
isDefault: false
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parse adapter file into structured data
|
|
174
|
+
*/
|
|
175
|
+
function parseAdapter(content) {
|
|
176
|
+
const adapter = {
|
|
177
|
+
name: '',
|
|
178
|
+
strengths: [],
|
|
179
|
+
weaknesses: [],
|
|
180
|
+
promptAdjustments: [],
|
|
181
|
+
antiPatterns: [],
|
|
182
|
+
knownIssues: [],
|
|
183
|
+
learnings: []
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (!content) return adapter;
|
|
187
|
+
|
|
188
|
+
// Extract name from first heading
|
|
189
|
+
const nameMatch = content.match(/^#\s+(.+)/m);
|
|
190
|
+
if (nameMatch) {
|
|
191
|
+
adapter.name = nameMatch[1].trim();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Extract sections
|
|
195
|
+
const sections = content.split(/^##\s+/m);
|
|
196
|
+
|
|
197
|
+
for (const section of sections) {
|
|
198
|
+
const lines = section.split('\n');
|
|
199
|
+
const title = lines[0]?.toLowerCase().trim() || '';
|
|
200
|
+
const body = lines.slice(1).join('\n');
|
|
201
|
+
|
|
202
|
+
if (title.includes('strength')) {
|
|
203
|
+
adapter.strengths = extractListItems(body);
|
|
204
|
+
} else if (title.includes('weakness')) {
|
|
205
|
+
adapter.weaknesses = extractListItems(body);
|
|
206
|
+
} else if (title.includes('prompt') || title.includes('adjustment')) {
|
|
207
|
+
adapter.promptAdjustments = extractListItems(body);
|
|
208
|
+
} else if (title.includes('anti-pattern') || title.includes('avoid')) {
|
|
209
|
+
adapter.antiPatterns = extractListItems(body);
|
|
210
|
+
} else if (title.includes('known issue') || title.includes('bug')) {
|
|
211
|
+
adapter.knownIssues = extractListItems(body);
|
|
212
|
+
} else if (title.includes('learning') || title.includes('mistake')) {
|
|
213
|
+
adapter.learnings = extractLearnings(body);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return adapter;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Extract bullet list items from markdown
|
|
222
|
+
*/
|
|
223
|
+
function extractListItems(text) {
|
|
224
|
+
const items = [];
|
|
225
|
+
const lines = text.split('\n');
|
|
226
|
+
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
const match = line.match(/^[-*]\s+(.+)/);
|
|
229
|
+
if (match) {
|
|
230
|
+
items.push(match[1].trim());
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return items;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Extract learning entries (date + content)
|
|
239
|
+
*/
|
|
240
|
+
function extractLearnings(text) {
|
|
241
|
+
const learnings = [];
|
|
242
|
+
const entries = text.split(/^###\s+/m);
|
|
243
|
+
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
if (!entry.trim()) continue;
|
|
246
|
+
|
|
247
|
+
const lines = entry.split('\n');
|
|
248
|
+
const title = lines[0]?.trim() || '';
|
|
249
|
+
const body = lines.slice(1).join('\n').trim();
|
|
250
|
+
|
|
251
|
+
if (title) {
|
|
252
|
+
learnings.push({
|
|
253
|
+
title,
|
|
254
|
+
body,
|
|
255
|
+
date: extractDateFromTitle(title)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return learnings;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extract date from learning title (e.g., "2024-01-15 - Fixed import issue")
|
|
265
|
+
*/
|
|
266
|
+
function extractDateFromTitle(title) {
|
|
267
|
+
const match = title.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
268
|
+
return match ? match[1] : null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================
|
|
272
|
+
// Prompt Adjustments
|
|
273
|
+
// ============================================================
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get prompt adjustments for current model
|
|
277
|
+
* Returns guidance to prepend to prompts
|
|
278
|
+
*/
|
|
279
|
+
function getPromptAdjustments(modelName = null) {
|
|
280
|
+
const model = modelName || getCurrentModel();
|
|
281
|
+
const adapterData = loadAdapter(model);
|
|
282
|
+
|
|
283
|
+
if (!adapterData) {
|
|
284
|
+
return {
|
|
285
|
+
model,
|
|
286
|
+
adjustments: [],
|
|
287
|
+
guidance: ''
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const adapter = parseAdapter(adapterData.content);
|
|
292
|
+
const guidance = [];
|
|
293
|
+
|
|
294
|
+
// Add weakness-based guidance
|
|
295
|
+
if (adapter.weaknesses.length > 0) {
|
|
296
|
+
guidance.push('Be especially careful with:');
|
|
297
|
+
for (const weakness of adapter.weaknesses.slice(0, 3)) {
|
|
298
|
+
guidance.push(`- ${weakness}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Add anti-patterns
|
|
303
|
+
if (adapter.antiPatterns.length > 0) {
|
|
304
|
+
guidance.push('');
|
|
305
|
+
guidance.push('Avoid these patterns:');
|
|
306
|
+
for (const pattern of adapter.antiPatterns.slice(0, 3)) {
|
|
307
|
+
guidance.push(`- ${pattern}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Add recent learnings (last 3)
|
|
312
|
+
const recentLearnings = adapter.learnings.slice(-3);
|
|
313
|
+
if (recentLearnings.length > 0) {
|
|
314
|
+
guidance.push('');
|
|
315
|
+
guidance.push('Recent learnings to remember:');
|
|
316
|
+
for (const learning of recentLearnings) {
|
|
317
|
+
guidance.push(`- ${learning.title}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
model,
|
|
323
|
+
adapterPath: adapterData.path,
|
|
324
|
+
isDefault: adapterData.isDefault || false,
|
|
325
|
+
isTemplate: adapterData.isTemplate || false,
|
|
326
|
+
adjustments: adapter.promptAdjustments,
|
|
327
|
+
guidance: guidance.join('\n'),
|
|
328
|
+
strengths: adapter.strengths,
|
|
329
|
+
weaknesses: adapter.weaknesses
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ============================================================
|
|
334
|
+
// Model Statistics & Learning
|
|
335
|
+
// ============================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Load model statistics
|
|
339
|
+
*/
|
|
340
|
+
function loadModelStats() {
|
|
341
|
+
try {
|
|
342
|
+
if (fs.existsSync(MODEL_STATS_PATH)) {
|
|
343
|
+
return JSON.parse(fs.readFileSync(MODEL_STATS_PATH, 'utf-8'));
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
// Ignore errors
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
version: '1.0.0',
|
|
351
|
+
lastUpdated: new Date().toISOString(),
|
|
352
|
+
models: {}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Save model statistics
|
|
358
|
+
*/
|
|
359
|
+
function saveModelStats(stats) {
|
|
360
|
+
stats.lastUpdated = new Date().toISOString();
|
|
361
|
+
const dir = path.dirname(MODEL_STATS_PATH);
|
|
362
|
+
if (!fs.existsSync(dir)) {
|
|
363
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
364
|
+
}
|
|
365
|
+
fs.writeFileSync(MODEL_STATS_PATH, JSON.stringify(stats, null, 2));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Record a model execution result
|
|
370
|
+
* @param {string} modelName - Model that was used
|
|
371
|
+
* @param {object} result - { taskType, success, errorType?, errorContext? }
|
|
372
|
+
*/
|
|
373
|
+
function recordModelResult(modelName, result) {
|
|
374
|
+
const config = getConfig();
|
|
375
|
+
// Enable tracking if modelAdapters.enabled is true OR if hybrid mode is enabled
|
|
376
|
+
// This allows stats to be collected without explicit modelAdapters config
|
|
377
|
+
const trackingEnabled = config.modelAdapters?.enabled !== false &&
|
|
378
|
+
(config.modelAdapters?.enabled || config.hybrid?.enabled);
|
|
379
|
+
if (!trackingEnabled) return;
|
|
380
|
+
|
|
381
|
+
const stats = loadModelStats();
|
|
382
|
+
const model = normalizeModelName(modelName);
|
|
383
|
+
|
|
384
|
+
// Initialize model entry
|
|
385
|
+
if (!stats.models[model]) {
|
|
386
|
+
stats.models[model] = {
|
|
387
|
+
totalTasks: 0,
|
|
388
|
+
successes: 0,
|
|
389
|
+
failures: 0,
|
|
390
|
+
taskTypes: {},
|
|
391
|
+
errorTypes: {},
|
|
392
|
+
recentErrors: []
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const modelStats = stats.models[model];
|
|
397
|
+
modelStats.totalTasks++;
|
|
398
|
+
|
|
399
|
+
if (result.success) {
|
|
400
|
+
modelStats.successes++;
|
|
401
|
+
} else {
|
|
402
|
+
modelStats.failures++;
|
|
403
|
+
|
|
404
|
+
// Track error types
|
|
405
|
+
if (result.errorType) {
|
|
406
|
+
modelStats.errorTypes[result.errorType] = (modelStats.errorTypes[result.errorType] || 0) + 1;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Add to recent errors (keep last 20)
|
|
410
|
+
modelStats.recentErrors.unshift({
|
|
411
|
+
timestamp: new Date().toISOString(),
|
|
412
|
+
taskType: result.taskType || 'unknown',
|
|
413
|
+
errorType: result.errorType || 'unknown',
|
|
414
|
+
errorContext: result.errorContext || null
|
|
415
|
+
});
|
|
416
|
+
modelStats.recentErrors = modelStats.recentErrors.slice(0, 20);
|
|
417
|
+
|
|
418
|
+
// Check for repeated errors (trigger auto-learning)
|
|
419
|
+
if (config.modelAdapters?.autoLearn) {
|
|
420
|
+
checkAndAutoLearn(model, modelStats);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Track task types
|
|
425
|
+
if (result.taskType) {
|
|
426
|
+
if (!modelStats.taskTypes[result.taskType]) {
|
|
427
|
+
modelStats.taskTypes[result.taskType] = { total: 0, success: 0 };
|
|
428
|
+
}
|
|
429
|
+
modelStats.taskTypes[result.taskType].total++;
|
|
430
|
+
if (result.success) {
|
|
431
|
+
modelStats.taskTypes[result.taskType].success++;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
saveModelStats(stats);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Check for repeated errors and auto-learn
|
|
440
|
+
*/
|
|
441
|
+
function checkAndAutoLearn(modelName, modelStats) {
|
|
442
|
+
const recentErrors = modelStats.recentErrors.slice(0, 10);
|
|
443
|
+
|
|
444
|
+
// Group by error type
|
|
445
|
+
const errorCounts = {};
|
|
446
|
+
for (const error of recentErrors) {
|
|
447
|
+
const key = error.errorType;
|
|
448
|
+
if (!errorCounts[key]) {
|
|
449
|
+
errorCounts[key] = { count: 0, contexts: [] };
|
|
450
|
+
}
|
|
451
|
+
errorCounts[key].count++;
|
|
452
|
+
if (error.errorContext) {
|
|
453
|
+
errorCounts[key].contexts.push(error.errorContext);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Find errors that occurred 3+ times
|
|
458
|
+
const repeatedErrors = Object.entries(errorCounts)
|
|
459
|
+
.filter(([_, data]) => data.count >= 3)
|
|
460
|
+
.map(([type, data]) => ({ type, ...data }));
|
|
461
|
+
|
|
462
|
+
if (repeatedErrors.length > 0) {
|
|
463
|
+
addLearningToAdapter(modelName, repeatedErrors);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Store a single learning/correction in model adapter file
|
|
469
|
+
* Used by knowledge-router for direct learning storage
|
|
470
|
+
* @param {string} modelName - Model identifier
|
|
471
|
+
* @param {string} learning - The learning text
|
|
472
|
+
* @param {object} context - Optional context { taskId, sourceContext, trigger }
|
|
473
|
+
*/
|
|
474
|
+
function storeSingleLearning(modelName, learning, context = {}) {
|
|
475
|
+
const adapterPath = getAdapterPath(modelName);
|
|
476
|
+
let content = '';
|
|
477
|
+
|
|
478
|
+
if (fs.existsSync(adapterPath)) {
|
|
479
|
+
content = fs.readFileSync(adapterPath, 'utf-8');
|
|
480
|
+
} else {
|
|
481
|
+
// Ensure directory exists
|
|
482
|
+
const dir = path.dirname(adapterPath);
|
|
483
|
+
if (!fs.existsSync(dir)) {
|
|
484
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Create from template or minimal
|
|
488
|
+
const templatePath = path.join(ADAPTERS_DIR, '_template.md');
|
|
489
|
+
if (fs.existsSync(templatePath)) {
|
|
490
|
+
content = fs.readFileSync(templatePath, 'utf-8')
|
|
491
|
+
.replace('{{MODEL_NAME}}', modelName);
|
|
492
|
+
} else {
|
|
493
|
+
content = `# ${modelName} Adapter\n\n## Learnings\n`;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const date = new Date().toISOString().split('T')[0];
|
|
498
|
+
const source = context.taskId || context.sourceContext || 'user-correction';
|
|
499
|
+
const trigger = context.trigger || 'knowledge-router';
|
|
500
|
+
|
|
501
|
+
const learningEntry = `
|
|
502
|
+
### ${date} - ${source}
|
|
503
|
+
|
|
504
|
+
**Trigger**: ${trigger}
|
|
505
|
+
|
|
506
|
+
${learning}
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
`;
|
|
510
|
+
|
|
511
|
+
// Find Learnings section or append at end
|
|
512
|
+
if (content.includes('## Learnings')) {
|
|
513
|
+
const learningsIdx = content.indexOf('## Learnings');
|
|
514
|
+
const nextSectionIdx = content.indexOf('\n## ', learningsIdx + 1);
|
|
515
|
+
|
|
516
|
+
if (nextSectionIdx !== -1) {
|
|
517
|
+
content = content.slice(0, nextSectionIdx) + learningEntry + content.slice(nextSectionIdx);
|
|
518
|
+
} else {
|
|
519
|
+
content += learningEntry;
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
content += '\n## Learnings\n' + learningEntry;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
fs.writeFileSync(adapterPath, content);
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
success: true,
|
|
529
|
+
file: adapterPath,
|
|
530
|
+
message: `Stored as ${modelName}-specific learning`
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Add learning entry to adapter file
|
|
536
|
+
*/
|
|
537
|
+
function addLearningToAdapter(modelName, errors) {
|
|
538
|
+
const adapterPath = getAdapterPath(modelName);
|
|
539
|
+
let content = '';
|
|
540
|
+
|
|
541
|
+
if (fs.existsSync(adapterPath)) {
|
|
542
|
+
content = fs.readFileSync(adapterPath, 'utf-8');
|
|
543
|
+
} else {
|
|
544
|
+
// Create from template
|
|
545
|
+
const templatePath = path.join(ADAPTERS_DIR, '_template.md');
|
|
546
|
+
if (fs.existsSync(templatePath)) {
|
|
547
|
+
content = fs.readFileSync(templatePath, 'utf-8')
|
|
548
|
+
.replace('{{MODEL_NAME}}', modelName);
|
|
549
|
+
} else {
|
|
550
|
+
content = `# ${modelName} Adapter\n\n## Learnings\n`;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Add new learning entry
|
|
555
|
+
const date = new Date().toISOString().split('T')[0];
|
|
556
|
+
const learningEntry = [];
|
|
557
|
+
|
|
558
|
+
learningEntry.push(`\n### ${date} - Auto-learned from repeated errors\n`);
|
|
559
|
+
|
|
560
|
+
for (const error of errors) {
|
|
561
|
+
learningEntry.push(`**Error Type**: ${error.type} (occurred ${error.count} times)`);
|
|
562
|
+
if (error.contexts.length > 0) {
|
|
563
|
+
learningEntry.push(`**Context**: ${error.contexts[0]}`);
|
|
564
|
+
}
|
|
565
|
+
learningEntry.push('');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Find Learnings section or append at end
|
|
569
|
+
if (content.includes('## Learnings')) {
|
|
570
|
+
content = content.replace(
|
|
571
|
+
/(## Learnings.*?)(?=\n## |$)/s,
|
|
572
|
+
`$1${learningEntry.join('\n')}`
|
|
573
|
+
);
|
|
574
|
+
} else {
|
|
575
|
+
content += '\n## Learnings\n' + learningEntry.join('\n');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Ensure directory exists
|
|
579
|
+
const dir = path.dirname(adapterPath);
|
|
580
|
+
if (!fs.existsSync(dir)) {
|
|
581
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
fs.writeFileSync(adapterPath, content);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Get statistics for all models
|
|
589
|
+
*/
|
|
590
|
+
function getAllModelStats() {
|
|
591
|
+
const stats = loadModelStats();
|
|
592
|
+
|
|
593
|
+
return Object.entries(stats.models).map(([model, data]) => ({
|
|
594
|
+
model,
|
|
595
|
+
totalTasks: data.totalTasks,
|
|
596
|
+
successRate: data.totalTasks > 0
|
|
597
|
+
? ((data.successes / data.totalTasks) * 100).toFixed(1) + '%'
|
|
598
|
+
: 'N/A',
|
|
599
|
+
topErrors: Object.entries(data.errorTypes)
|
|
600
|
+
.sort((a, b) => b[1] - a[1])
|
|
601
|
+
.slice(0, 3)
|
|
602
|
+
.map(([type, count]) => `${type} (${count})`),
|
|
603
|
+
taskBreakdown: Object.entries(data.taskTypes)
|
|
604
|
+
.map(([type, info]) => ({
|
|
605
|
+
type,
|
|
606
|
+
total: info.total,
|
|
607
|
+
successRate: info.total > 0 ? ((info.success / info.total) * 100).toFixed(0) + '%' : 'N/A'
|
|
608
|
+
}))
|
|
609
|
+
}));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ============================================================
|
|
613
|
+
// CLI & Formatting
|
|
614
|
+
// ============================================================
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* List available adapters
|
|
618
|
+
*/
|
|
619
|
+
function listAdapters() {
|
|
620
|
+
if (!fs.existsSync(ADAPTERS_DIR)) {
|
|
621
|
+
return [];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return fs.readdirSync(ADAPTERS_DIR)
|
|
625
|
+
.filter(f => f.endsWith('.md') && !f.startsWith('_'))
|
|
626
|
+
.map(f => f.replace('.md', ''));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Format adapter info for display
|
|
631
|
+
*/
|
|
632
|
+
function formatAdapterInfo(modelName) {
|
|
633
|
+
const adjustments = getPromptAdjustments(modelName);
|
|
634
|
+
let output = '';
|
|
635
|
+
|
|
636
|
+
output += `${colors.cyan}Model: ${adjustments.model}${colors.reset}\n`;
|
|
637
|
+
|
|
638
|
+
if (adjustments.isTemplate) {
|
|
639
|
+
output += `${colors.yellow}Using template (no specific adapter)${colors.reset}\n`;
|
|
640
|
+
} else if (adjustments.isDefault) {
|
|
641
|
+
output += `${colors.dim}Using family default adapter${colors.reset}\n`;
|
|
642
|
+
} else {
|
|
643
|
+
output += `${colors.green}Using model-specific adapter${colors.reset}\n`;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
output += `Path: ${adjustments.adapterPath || 'N/A'}\n\n`;
|
|
647
|
+
|
|
648
|
+
if (adjustments.strengths.length > 0) {
|
|
649
|
+
output += `${colors.green}Strengths:${colors.reset}\n`;
|
|
650
|
+
for (const s of adjustments.strengths.slice(0, 5)) {
|
|
651
|
+
output += ` • ${s}\n`;
|
|
652
|
+
}
|
|
653
|
+
output += '\n';
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (adjustments.weaknesses.length > 0) {
|
|
657
|
+
output += `${colors.yellow}Weaknesses:${colors.reset}\n`;
|
|
658
|
+
for (const w of adjustments.weaknesses.slice(0, 5)) {
|
|
659
|
+
output += ` • ${w}\n`;
|
|
660
|
+
}
|
|
661
|
+
output += '\n';
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (adjustments.adjustments.length > 0) {
|
|
665
|
+
output += `${colors.bold}Prompt Adjustments:${colors.reset}\n`;
|
|
666
|
+
for (const a of adjustments.adjustments) {
|
|
667
|
+
output += ` • ${a}\n`;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return output;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Format statistics for display
|
|
676
|
+
*/
|
|
677
|
+
function formatStats() {
|
|
678
|
+
const allStats = getAllModelStats();
|
|
679
|
+
let output = '';
|
|
680
|
+
|
|
681
|
+
output += `${colors.cyan}Model Statistics${colors.reset}\n`;
|
|
682
|
+
output += `${'═'.repeat(50)}\n\n`;
|
|
683
|
+
|
|
684
|
+
if (allStats.length === 0) {
|
|
685
|
+
output += `${colors.dim}No statistics recorded yet.${colors.reset}\n`;
|
|
686
|
+
return output;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
for (const modelStat of allStats) {
|
|
690
|
+
const icon = parseFloat(modelStat.successRate) >= 90
|
|
691
|
+
? colors.green + '●' + colors.reset
|
|
692
|
+
: parseFloat(modelStat.successRate) >= 70
|
|
693
|
+
? colors.yellow + '●' + colors.reset
|
|
694
|
+
: colors.red + '●' + colors.reset;
|
|
695
|
+
|
|
696
|
+
output += `${icon} ${colors.bold}${modelStat.model}${colors.reset}\n`;
|
|
697
|
+
output += ` Tasks: ${modelStat.totalTasks} | Success: ${modelStat.successRate}\n`;
|
|
698
|
+
|
|
699
|
+
if (modelStat.topErrors.length > 0) {
|
|
700
|
+
output += ` Top errors: ${modelStat.topErrors.join(', ')}\n`;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (modelStat.taskBreakdown.length > 0) {
|
|
704
|
+
output += ` By type: `;
|
|
705
|
+
output += modelStat.taskBreakdown
|
|
706
|
+
.map(t => `${t.type}(${t.successRate})`)
|
|
707
|
+
.join(', ');
|
|
708
|
+
output += '\n';
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
output += '\n';
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return output;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function showHelp() {
|
|
718
|
+
console.log(`
|
|
719
|
+
Wogi Flow - Model Adapters
|
|
720
|
+
|
|
721
|
+
Manages per-model learning and prompt adjustments for different LLMs.
|
|
722
|
+
|
|
723
|
+
Usage:
|
|
724
|
+
flow model-adapter Show current model info
|
|
725
|
+
flow model-adapter --list List available adapters
|
|
726
|
+
flow model-adapter --stats Show per-model statistics
|
|
727
|
+
flow model-adapter --json Output current model as JSON
|
|
728
|
+
flow model-adapter [model] Show info for specific model
|
|
729
|
+
|
|
730
|
+
Options:
|
|
731
|
+
--list List all available adapter files
|
|
732
|
+
--stats Show success/failure statistics per model
|
|
733
|
+
--json Output as JSON
|
|
734
|
+
--help, -h Show this help
|
|
735
|
+
|
|
736
|
+
Examples:
|
|
737
|
+
flow model-adapter # Show current model
|
|
738
|
+
flow model-adapter claude-sonnet # Show Sonnet adapter
|
|
739
|
+
flow model-adapter --stats # View all model stats
|
|
740
|
+
`);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function main() {
|
|
744
|
+
const args = process.argv.slice(2);
|
|
745
|
+
|
|
746
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
747
|
+
showHelp();
|
|
748
|
+
process.exit(0);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (args.includes('--list')) {
|
|
752
|
+
const adapters = listAdapters();
|
|
753
|
+
console.log(`${colors.cyan}Available Adapters:${colors.reset}`);
|
|
754
|
+
if (adapters.length === 0) {
|
|
755
|
+
console.log(` ${colors.dim}No adapters found. Create one in .workflow/model-adapters/${colors.reset}`);
|
|
756
|
+
} else {
|
|
757
|
+
for (const adapter of adapters) {
|
|
758
|
+
console.log(` • ${adapter}`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
process.exit(0);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (args.includes('--stats')) {
|
|
765
|
+
console.log(formatStats());
|
|
766
|
+
process.exit(0);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Get model name from args or detect
|
|
770
|
+
const modelArg = args.find(a => !a.startsWith('--'));
|
|
771
|
+
const modelName = modelArg || getCurrentModel();
|
|
772
|
+
|
|
773
|
+
if (args.includes('--json')) {
|
|
774
|
+
console.log(JSON.stringify(getPromptAdjustments(modelName), null, 2));
|
|
775
|
+
process.exit(0);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
console.log(formatAdapterInfo(modelName));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// ============================================================
|
|
782
|
+
// Exports
|
|
783
|
+
// ============================================================
|
|
784
|
+
|
|
785
|
+
module.exports = {
|
|
786
|
+
getCurrentModel,
|
|
787
|
+
normalizeModelName,
|
|
788
|
+
getModelFamily,
|
|
789
|
+
loadAdapter,
|
|
790
|
+
parseAdapter,
|
|
791
|
+
getPromptAdjustments,
|
|
792
|
+
recordModelResult,
|
|
793
|
+
getAllModelStats,
|
|
794
|
+
listAdapters,
|
|
795
|
+
addLearningToAdapter,
|
|
796
|
+
storeSingleLearning,
|
|
797
|
+
getAdapterPath
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
if (require.main === module) {
|
|
801
|
+
main();
|
|
802
|
+
}
|