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,522 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Knowledge Router
|
|
5
|
+
*
|
|
6
|
+
* Auto-detects where learnings/corrections should be stored:
|
|
7
|
+
* - model-specific: Applies only to a specific LLM
|
|
8
|
+
* - skill: Related to a specific skill (nestjs, react, etc.)
|
|
9
|
+
* - project: Project-specific decisions
|
|
10
|
+
* - team: General patterns worthy of team sharing
|
|
11
|
+
*
|
|
12
|
+
* Implements "auto-detect + confirm" pattern:
|
|
13
|
+
* 1. Analyzes correction text and context
|
|
14
|
+
* 2. Suggests best route with confidence score
|
|
15
|
+
* 3. Asks user to confirm or choose alternative
|
|
16
|
+
*
|
|
17
|
+
* Part of v1.8.0 Team Collaboration
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const {
|
|
23
|
+
getConfig,
|
|
24
|
+
PATHS,
|
|
25
|
+
STATE_DIR,
|
|
26
|
+
colors,
|
|
27
|
+
color,
|
|
28
|
+
success,
|
|
29
|
+
warn,
|
|
30
|
+
error,
|
|
31
|
+
readFile,
|
|
32
|
+
writeFile,
|
|
33
|
+
fileExists,
|
|
34
|
+
printHeader
|
|
35
|
+
} = require('./flow-utils');
|
|
36
|
+
|
|
37
|
+
// Import from skill-learn and model-adapter to avoid duplication
|
|
38
|
+
const { appendLearning: appendSkillLearning, discoverSkills } = require('./flow-skill-learn');
|
|
39
|
+
const { storeSingleLearning: storeModelLearning, getCurrentModel } = require('./flow-model-adapter');
|
|
40
|
+
|
|
41
|
+
// Use shared memory database for proposals
|
|
42
|
+
const memoryDb = require('./flow-memory-db');
|
|
43
|
+
|
|
44
|
+
// Rules sync for Claude Code integration
|
|
45
|
+
const { syncDecisionsToRules } = require('./flow-rules-sync');
|
|
46
|
+
|
|
47
|
+
// ============================================================
|
|
48
|
+
// Route Detection
|
|
49
|
+
// ============================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect possible routes for a learning/correction
|
|
53
|
+
* @param {string} correction - The correction or learning text
|
|
54
|
+
* @param {object} context - Context about where this came from
|
|
55
|
+
* @returns {Array} Sorted array of route suggestions with confidence
|
|
56
|
+
*/
|
|
57
|
+
function detectKnowledgeRoute(correction, context = {}) {
|
|
58
|
+
const routes = [];
|
|
59
|
+
const correctionLower = correction.toLowerCase();
|
|
60
|
+
|
|
61
|
+
// 1. Check if model-specific
|
|
62
|
+
const modelPatterns = [
|
|
63
|
+
{ pattern: /claude|anthropic/i, model: 'claude' },
|
|
64
|
+
{ pattern: /gemini|google/i, model: 'gemini' },
|
|
65
|
+
{ pattern: /gpt|openai|chatgpt/i, model: 'openai' },
|
|
66
|
+
{ pattern: /ollama|local|llama|qwen|deepseek|mistral|nemotron/i, model: 'local' }
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const modelMatch = modelPatterns.find(p =>
|
|
70
|
+
p.pattern.test(correction) || (context.currentModel && p.pattern.test(context.currentModel))
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Check for model-specific error patterns
|
|
74
|
+
const modelErrorIndicators = [
|
|
75
|
+
'this model',
|
|
76
|
+
'claude tends to',
|
|
77
|
+
'gemini often',
|
|
78
|
+
'when using',
|
|
79
|
+
'with this llm',
|
|
80
|
+
'model-specific'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const hasModelIndicator = modelErrorIndicators.some(ind =>
|
|
84
|
+
correctionLower.includes(ind)
|
|
85
|
+
) || context.errorWasModelSpecific;
|
|
86
|
+
|
|
87
|
+
if (modelMatch || hasModelIndicator) {
|
|
88
|
+
routes.push({
|
|
89
|
+
type: 'model-specific',
|
|
90
|
+
model: modelMatch?.model || context.currentModel || 'unknown',
|
|
91
|
+
confidence: hasModelIndicator ? 0.85 : 0.7,
|
|
92
|
+
description: `Store as ${modelMatch?.model || 'model'}-specific learning`
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 2. Check if skill-specific
|
|
97
|
+
const skillMatch = matchSkillFromContext(correction, context);
|
|
98
|
+
if (skillMatch) {
|
|
99
|
+
routes.push({
|
|
100
|
+
type: 'skill',
|
|
101
|
+
skill: skillMatch.name,
|
|
102
|
+
file: `.claude/skills/${skillMatch.name}/knowledge/learnings.md`,
|
|
103
|
+
confidence: skillMatch.confidence,
|
|
104
|
+
description: `Add to ${skillMatch.name} skill knowledge`
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 3. Check if project-specific
|
|
109
|
+
const projectPatterns = [
|
|
110
|
+
/this project|our codebase|in this repo/i,
|
|
111
|
+
/\bour api\b|\bour database\b|\bour schema\b/i,
|
|
112
|
+
/project.?specific|local rule/i
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
// Also check for project name reference
|
|
116
|
+
const projectName = context.projectName || getConfig().projectName;
|
|
117
|
+
if (projectName) {
|
|
118
|
+
projectPatterns.push(new RegExp(projectName.replace(/[-_]/g, '[-_]?'), 'i'));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (projectPatterns.some(p => p.test(correction))) {
|
|
122
|
+
routes.push({
|
|
123
|
+
type: 'project',
|
|
124
|
+
file: 'decisions.md',
|
|
125
|
+
confidence: 0.75,
|
|
126
|
+
description: 'Add to project decisions.md'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 4. Check if general/team-worthy
|
|
131
|
+
const generalPatterns = [
|
|
132
|
+
/always|never|best practice/i,
|
|
133
|
+
/convention|standard|pattern/i,
|
|
134
|
+
/\bdo not\b|\bdon't\b.*\buse\b/i,
|
|
135
|
+
/prefer|avoid|instead of/i,
|
|
136
|
+
/rule of thumb|general rule/i
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
if (generalPatterns.some(p => p.test(correction))) {
|
|
140
|
+
routes.push({
|
|
141
|
+
type: 'team',
|
|
142
|
+
scope: 'proposal',
|
|
143
|
+
confidence: 0.65,
|
|
144
|
+
description: 'Propose as team rule (requires approval)'
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 5. Default: local project decision
|
|
149
|
+
if (routes.length === 0) {
|
|
150
|
+
routes.push({
|
|
151
|
+
type: 'project',
|
|
152
|
+
file: 'decisions.md',
|
|
153
|
+
confidence: 0.5,
|
|
154
|
+
description: 'Add to project decisions.md (default)'
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Sort by confidence
|
|
159
|
+
return routes.sort((a, b) => b.confidence - a.confidence);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Match correction to installed skills based on file types and keywords
|
|
164
|
+
*/
|
|
165
|
+
function matchSkillFromContext(correction, context) {
|
|
166
|
+
const config = getConfig();
|
|
167
|
+
const installedSkills = config.skills?.installed || [];
|
|
168
|
+
|
|
169
|
+
if (installedSkills.length === 0) return null;
|
|
170
|
+
|
|
171
|
+
// Skill detection patterns
|
|
172
|
+
const skillPatterns = {
|
|
173
|
+
nestjs: {
|
|
174
|
+
keywords: ['nestjs', 'nest.js', '@nestjs', 'module', 'controller', 'service', 'dto', 'typeorm', 'prisma'],
|
|
175
|
+
fileExtensions: ['.module.ts', '.controller.ts', '.service.ts', '.dto.ts', '.entity.ts']
|
|
176
|
+
},
|
|
177
|
+
react: {
|
|
178
|
+
keywords: ['react', 'component', 'hook', 'usestate', 'useeffect', 'jsx', 'tsx', 'props'],
|
|
179
|
+
fileExtensions: ['.tsx', '.jsx']
|
|
180
|
+
},
|
|
181
|
+
python: {
|
|
182
|
+
keywords: ['python', 'fastapi', 'django', 'flask', 'pydantic', 'pytest'],
|
|
183
|
+
fileExtensions: ['.py']
|
|
184
|
+
},
|
|
185
|
+
typescript: {
|
|
186
|
+
keywords: ['typescript', 'type', 'interface', 'generic', 'tsconfig'],
|
|
187
|
+
fileExtensions: ['.ts', '.tsx']
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const correctionLower = correction.toLowerCase();
|
|
192
|
+
const filesModified = context.filesModified || [];
|
|
193
|
+
|
|
194
|
+
for (const skillName of installedSkills) {
|
|
195
|
+
const patterns = skillPatterns[skillName];
|
|
196
|
+
if (!patterns) continue;
|
|
197
|
+
|
|
198
|
+
// Check keywords
|
|
199
|
+
const keywordMatch = patterns.keywords.some(kw =>
|
|
200
|
+
correctionLower.includes(kw.toLowerCase())
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Check file extensions
|
|
204
|
+
const fileMatch = patterns.fileExtensions.some(ext =>
|
|
205
|
+
filesModified.some(f => f.endsWith(ext))
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (keywordMatch || fileMatch) {
|
|
209
|
+
return {
|
|
210
|
+
name: skillName,
|
|
211
|
+
confidence: keywordMatch && fileMatch ? 0.9 : keywordMatch ? 0.75 : 0.65
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ============================================================
|
|
220
|
+
// Route Handlers
|
|
221
|
+
// ============================================================
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Store learning based on selected route
|
|
225
|
+
*/
|
|
226
|
+
async function storeByRoute(correction, route, context = {}) {
|
|
227
|
+
switch (route.type) {
|
|
228
|
+
case 'model-specific':
|
|
229
|
+
return await storeModelSpecific(correction, route, context);
|
|
230
|
+
|
|
231
|
+
case 'skill':
|
|
232
|
+
return await storeSkillLearning(correction, route, context);
|
|
233
|
+
|
|
234
|
+
case 'project':
|
|
235
|
+
return await storeProjectDecision(correction, route, context);
|
|
236
|
+
|
|
237
|
+
case 'team':
|
|
238
|
+
return await createTeamProposal(correction, route, context);
|
|
239
|
+
|
|
240
|
+
default:
|
|
241
|
+
return { success: false, error: `Unknown route type: ${route.type}` };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function storeModelSpecific(correction, route, context) {
|
|
246
|
+
// Use the centralized model-adapter module
|
|
247
|
+
const modelName = route.model || getCurrentModel();
|
|
248
|
+
return storeModelLearning(modelName, correction, context);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function storeSkillLearning(correction, route, context) {
|
|
252
|
+
// Use the centralized skill-learn module
|
|
253
|
+
const skillPath = path.join(PATHS.skills, route.skill);
|
|
254
|
+
|
|
255
|
+
// Adapt context format for skill-learn's appendLearning
|
|
256
|
+
const skillContext = {
|
|
257
|
+
trigger: context.trigger || 'knowledge-router',
|
|
258
|
+
timestamp: new Date().toISOString(),
|
|
259
|
+
files: context.filesModified || [],
|
|
260
|
+
summary: correction.slice(0, 100) + (correction.length > 100 ? '...' : ''),
|
|
261
|
+
type: 'correction'
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const success = appendSkillLearning(skillPath, skillContext);
|
|
265
|
+
|
|
266
|
+
if (success) {
|
|
267
|
+
return {
|
|
268
|
+
success: true,
|
|
269
|
+
file: path.join(PATHS.skills, route.skill, 'knowledge', 'learnings.md'),
|
|
270
|
+
message: `Added to ${route.skill} skill learnings`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
success: false,
|
|
276
|
+
error: `Failed to append learning to ${route.skill} skill`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function storeProjectDecision(correction, route, context) {
|
|
281
|
+
const decisionsPath = PATHS.decisions;
|
|
282
|
+
|
|
283
|
+
let content = '';
|
|
284
|
+
if (fs.existsSync(decisionsPath)) {
|
|
285
|
+
content = fs.readFileSync(decisionsPath, 'utf-8');
|
|
286
|
+
} else {
|
|
287
|
+
content = `# Project Decisions
|
|
288
|
+
|
|
289
|
+
Coding conventions and project-specific rules.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const date = new Date().toISOString().split('T')[0];
|
|
297
|
+
const entry = `\n### ${date}
|
|
298
|
+
|
|
299
|
+
${correction}
|
|
300
|
+
|
|
301
|
+
`;
|
|
302
|
+
|
|
303
|
+
content += entry;
|
|
304
|
+
fs.writeFileSync(decisionsPath, content);
|
|
305
|
+
|
|
306
|
+
// Sync to .claude/rules/ for Claude Code integration
|
|
307
|
+
syncDecisionsToRules();
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
success: true,
|
|
311
|
+
file: decisionsPath,
|
|
312
|
+
message: 'Added to project decisions.md'
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function createTeamProposal(correction, route, context) {
|
|
317
|
+
const config = getConfig();
|
|
318
|
+
|
|
319
|
+
// Check if team features are enabled
|
|
320
|
+
if (!config.team?.enabled) {
|
|
321
|
+
return {
|
|
322
|
+
success: false,
|
|
323
|
+
error: 'Team features not enabled. Use `./scripts/flow team login` to enable.',
|
|
324
|
+
fallback: 'project'
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Store in shared database (will sync when team sync runs)
|
|
329
|
+
const result = await memoryDb.createProposal({
|
|
330
|
+
rule: correction,
|
|
331
|
+
category: route.category || 'pattern',
|
|
332
|
+
rationale: context.originalError || 'Learned from correction',
|
|
333
|
+
sourceContext: context.taskId || null
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
success: true,
|
|
338
|
+
proposalId: result.id,
|
|
339
|
+
message: 'Team proposal created. Will sync on next `./scripts/flow team sync`.'
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ============================================================
|
|
344
|
+
// Interactive Confirmation
|
|
345
|
+
// ============================================================
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Format route for display
|
|
349
|
+
*/
|
|
350
|
+
function formatRouteChoice(route, index) {
|
|
351
|
+
const confidence = Math.round(route.confidence * 100);
|
|
352
|
+
const prefix = index === 0 ? '(Recommended) ' : '';
|
|
353
|
+
const confStr = color('dim', `[${confidence}%]`);
|
|
354
|
+
|
|
355
|
+
switch (route.type) {
|
|
356
|
+
case 'model-specific':
|
|
357
|
+
return `${prefix}Model-specific (${route.model}) ${confStr}`;
|
|
358
|
+
case 'skill':
|
|
359
|
+
return `${prefix}Skill: ${route.skill} ${confStr}`;
|
|
360
|
+
case 'project':
|
|
361
|
+
return `${prefix}Project decisions.md ${confStr}`;
|
|
362
|
+
case 'team':
|
|
363
|
+
return `${prefix}Team proposal (requires approval) ${confStr}`;
|
|
364
|
+
default:
|
|
365
|
+
return `${prefix}${route.type} ${confStr}`;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Print routes for user selection (non-interactive output)
|
|
371
|
+
*/
|
|
372
|
+
function printRouteOptions(correction, routes) {
|
|
373
|
+
printHeader('Knowledge Router');
|
|
374
|
+
|
|
375
|
+
console.log(color('dim', 'Learning:'));
|
|
376
|
+
console.log(` "${correction.slice(0, 100)}${correction.length > 100 ? '...' : ''}"`);
|
|
377
|
+
console.log('');
|
|
378
|
+
|
|
379
|
+
console.log('Suggested destinations:');
|
|
380
|
+
routes.forEach((route, i) => {
|
|
381
|
+
console.log(` ${i + 1}. ${formatRouteChoice(route, i)}`);
|
|
382
|
+
console.log(color('dim', ` ${route.description}`));
|
|
383
|
+
});
|
|
384
|
+
console.log(` ${routes.length + 1}. Skip - don't save`);
|
|
385
|
+
console.log('');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ============================================================
|
|
389
|
+
// CLI Interface
|
|
390
|
+
// ============================================================
|
|
391
|
+
|
|
392
|
+
function printUsage() {
|
|
393
|
+
console.log(`
|
|
394
|
+
Usage: flow-knowledge-router.js [command] [args]
|
|
395
|
+
|
|
396
|
+
Commands:
|
|
397
|
+
detect <text> Detect route for a learning (returns JSON)
|
|
398
|
+
store <text> <route> Store learning with specified route type
|
|
399
|
+
routes List all possible route types
|
|
400
|
+
--help Show this help
|
|
401
|
+
|
|
402
|
+
Route types:
|
|
403
|
+
model-specific Store as model-specific learning
|
|
404
|
+
skill:<name> Store in skill knowledge
|
|
405
|
+
project Store in project decisions.md
|
|
406
|
+
team Create team proposal
|
|
407
|
+
|
|
408
|
+
Examples:
|
|
409
|
+
node scripts/flow-knowledge-router.js detect "Always use explicit types"
|
|
410
|
+
node scripts/flow-knowledge-router.js store "Use kebab-case" project
|
|
411
|
+
node scripts/flow-knowledge-router.js store "Claude needs explicit types" model-specific
|
|
412
|
+
`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Main CLI handler
|
|
416
|
+
if (require.main === module) {
|
|
417
|
+
const args = process.argv.slice(2);
|
|
418
|
+
const command = args[0];
|
|
419
|
+
|
|
420
|
+
switch (command) {
|
|
421
|
+
case 'detect': {
|
|
422
|
+
const text = args.slice(1).join(' ');
|
|
423
|
+
if (!text) {
|
|
424
|
+
error('Please provide text to analyze');
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const routes = detectKnowledgeRoute(text, {});
|
|
429
|
+
console.log(JSON.stringify(routes, null, 2));
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
case 'store': {
|
|
434
|
+
const routeType = args[args.length - 1];
|
|
435
|
+
const text = args.slice(1, -1).join(' ');
|
|
436
|
+
|
|
437
|
+
if (!text || !routeType) {
|
|
438
|
+
error('Usage: store <text> <route-type>');
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let route;
|
|
443
|
+
if (routeType.startsWith('skill:')) {
|
|
444
|
+
route = { type: 'skill', skill: routeType.split(':')[1] };
|
|
445
|
+
} else if (routeType === 'model-specific') {
|
|
446
|
+
route = { type: 'model-specific', model: 'unknown' };
|
|
447
|
+
} else {
|
|
448
|
+
route = { type: routeType };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
storeByRoute(text, route, {}).then(result => {
|
|
452
|
+
if (result.success) {
|
|
453
|
+
success(result.message);
|
|
454
|
+
} else {
|
|
455
|
+
error(result.error);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
case 'routes': {
|
|
463
|
+
console.log(`
|
|
464
|
+
Available route types:
|
|
465
|
+
|
|
466
|
+
model-specific Learnings specific to a particular LLM
|
|
467
|
+
Stored in: .workflow/model-adapters/<model>.md
|
|
468
|
+
|
|
469
|
+
skill:<name> Learnings related to a specific skill
|
|
470
|
+
Stored in: .claude/skills/<name>/knowledge/learnings.md
|
|
471
|
+
|
|
472
|
+
project Project-specific decisions and conventions
|
|
473
|
+
Stored in: .workflow/state/decisions.md
|
|
474
|
+
|
|
475
|
+
team General patterns worthy of team sharing
|
|
476
|
+
Stored as: proposal for team approval (requires subscription)
|
|
477
|
+
`);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case 'show': {
|
|
482
|
+
// Show routes for text without storing
|
|
483
|
+
const text = args.slice(1).join(' ');
|
|
484
|
+
if (!text) {
|
|
485
|
+
error('Please provide text to analyze');
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const routes = detectKnowledgeRoute(text, {});
|
|
490
|
+
printRouteOptions(text, routes);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
case '--help':
|
|
495
|
+
case '-h':
|
|
496
|
+
printUsage();
|
|
497
|
+
break;
|
|
498
|
+
|
|
499
|
+
default:
|
|
500
|
+
if (command) {
|
|
501
|
+
error(`Unknown command: ${command}`);
|
|
502
|
+
}
|
|
503
|
+
printUsage();
|
|
504
|
+
process.exit(command ? 1 : 0);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// ============================================================
|
|
509
|
+
// Exports
|
|
510
|
+
// ============================================================
|
|
511
|
+
|
|
512
|
+
module.exports = {
|
|
513
|
+
detectKnowledgeRoute,
|
|
514
|
+
matchSkillFromContext,
|
|
515
|
+
storeByRoute,
|
|
516
|
+
storeModelSpecific,
|
|
517
|
+
storeSkillLearning,
|
|
518
|
+
storeProjectDecision,
|
|
519
|
+
createTeamProposal,
|
|
520
|
+
formatRouteChoice,
|
|
521
|
+
printRouteOptions
|
|
522
|
+
};
|