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,484 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Memory to Instructions Sync
|
|
5
|
+
*
|
|
6
|
+
* Promotes high-relevance facts and patterns to decisions.md
|
|
7
|
+
* This is the "self-editing core memory" feature.
|
|
8
|
+
*
|
|
9
|
+
* Commands:
|
|
10
|
+
* ./scripts/flow memory-sync - Check for patterns to promote
|
|
11
|
+
* ./scripts/flow memory-sync --auto - Auto-promote without asking
|
|
12
|
+
* ./scripts/flow memory-sync --list - List candidates only
|
|
13
|
+
* ./scripts/flow memory-sync --promote <id> - Promote specific fact
|
|
14
|
+
*
|
|
15
|
+
* Part of v1.8.0 - Automatic Memory Management
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const memoryDb = require('./flow-memory-db');
|
|
21
|
+
|
|
22
|
+
// Lazy-load to avoid circular dependency
|
|
23
|
+
let _syncDecisionsToRules = null;
|
|
24
|
+
function syncDecisionsToRules() {
|
|
25
|
+
if (!_syncDecisionsToRules) {
|
|
26
|
+
_syncDecisionsToRules = require('./flow-rules-sync').syncDecisionsToRules;
|
|
27
|
+
}
|
|
28
|
+
return _syncDecisionsToRules();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// Configuration
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
const PROJECT_ROOT = process.env.WOGI_PROJECT_ROOT || process.cwd();
|
|
36
|
+
const CONFIG_PATH = path.join(PROJECT_ROOT, '.workflow', 'config.json');
|
|
37
|
+
const DECISIONS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'decisions.md');
|
|
38
|
+
|
|
39
|
+
function loadConfig() {
|
|
40
|
+
try {
|
|
41
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
42
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Output Formatting
|
|
50
|
+
// ============================================================
|
|
51
|
+
|
|
52
|
+
function color(c, text) {
|
|
53
|
+
const colors = {
|
|
54
|
+
red: '\x1b[31m',
|
|
55
|
+
green: '\x1b[32m',
|
|
56
|
+
yellow: '\x1b[33m',
|
|
57
|
+
blue: '\x1b[34m',
|
|
58
|
+
cyan: '\x1b[36m',
|
|
59
|
+
gray: '\x1b[90m',
|
|
60
|
+
reset: '\x1b[0m'
|
|
61
|
+
};
|
|
62
|
+
return `${colors[c] || ''}${text}${colors.reset}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================================
|
|
66
|
+
// Pattern Analysis
|
|
67
|
+
// ============================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extract patterns from facts by category
|
|
71
|
+
*/
|
|
72
|
+
async function analyzePatterns() {
|
|
73
|
+
const facts = await memoryDb.getAllFacts();
|
|
74
|
+
const patterns = {};
|
|
75
|
+
|
|
76
|
+
for (const fact of facts) {
|
|
77
|
+
const category = fact.category || 'general';
|
|
78
|
+
if (!patterns[category]) {
|
|
79
|
+
patterns[category] = [];
|
|
80
|
+
}
|
|
81
|
+
patterns[category].push(fact);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Sort each category by relevance/access count
|
|
85
|
+
for (const category of Object.keys(patterns)) {
|
|
86
|
+
patterns[category].sort((a, b) => {
|
|
87
|
+
const scoreA = (a.relevance_score || 0.5) * (1 + (a.access_count || 0) * 0.1);
|
|
88
|
+
const scoreB = (b.relevance_score || 0.5) * (1 + (b.access_count || 0) * 0.1);
|
|
89
|
+
return scoreB - scoreA;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return patterns;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get current decisions.md content
|
|
98
|
+
*/
|
|
99
|
+
function loadDecisions() {
|
|
100
|
+
try {
|
|
101
|
+
if (fs.existsSync(DECISIONS_PATH)) {
|
|
102
|
+
return fs.readFileSync(DECISIONS_PATH, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
} catch {}
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if a fact is already in decisions.md
|
|
110
|
+
*/
|
|
111
|
+
function isAlreadyInDecisions(fact, decisionsContent) {
|
|
112
|
+
// Simple check - see if key phrases exist
|
|
113
|
+
const keywords = fact.split(/\s+/)
|
|
114
|
+
.filter(w => w.length > 4)
|
|
115
|
+
.slice(0, 5);
|
|
116
|
+
|
|
117
|
+
let matches = 0;
|
|
118
|
+
for (const keyword of keywords) {
|
|
119
|
+
if (decisionsContent.toLowerCase().includes(keyword.toLowerCase())) {
|
|
120
|
+
matches++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If more than half of keywords match, likely already there
|
|
125
|
+
return matches > keywords.length / 2;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Format fact for decisions.md
|
|
130
|
+
*/
|
|
131
|
+
function formatForDecisions(fact) {
|
|
132
|
+
// Map category to decisions.md section
|
|
133
|
+
const sectionMap = {
|
|
134
|
+
'naming': 'Naming Conventions',
|
|
135
|
+
'pattern': 'Coding Patterns',
|
|
136
|
+
'architecture': 'Architecture Decisions',
|
|
137
|
+
'styling': 'Styling Rules',
|
|
138
|
+
'testing': 'Testing Conventions',
|
|
139
|
+
'error-handling': 'Error Handling',
|
|
140
|
+
'general': 'General Rules',
|
|
141
|
+
'api': 'API Patterns',
|
|
142
|
+
'component': 'Component Patterns'
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const section = sectionMap[fact.category] || 'Learned Patterns';
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
section,
|
|
149
|
+
rule: `- ${fact.fact}`,
|
|
150
|
+
source: fact.source_context ? `(Source: ${fact.source_context})` : '(Auto-promoted from memory)'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Append rule to decisions.md
|
|
156
|
+
*/
|
|
157
|
+
function appendToDecisions(formatted, decisionsContent) {
|
|
158
|
+
const lines = decisionsContent.split('\n');
|
|
159
|
+
let sectionIndex = -1;
|
|
160
|
+
|
|
161
|
+
// Find the section
|
|
162
|
+
for (let i = 0; i < lines.length; i++) {
|
|
163
|
+
if (lines[i].includes(formatted.section) && lines[i].startsWith('#')) {
|
|
164
|
+
sectionIndex = i;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (sectionIndex === -1) {
|
|
170
|
+
// Section doesn't exist, append at end
|
|
171
|
+
return decisionsContent.trim() + `\n\n## ${formatted.section}\n\n${formatted.rule}\n`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Find end of section (next heading or end of file)
|
|
175
|
+
let insertIndex = lines.length;
|
|
176
|
+
for (let i = sectionIndex + 1; i < lines.length; i++) {
|
|
177
|
+
if (lines[i].startsWith('#')) {
|
|
178
|
+
insertIndex = i;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Insert before next section
|
|
184
|
+
lines.splice(insertIndex, 0, formatted.rule);
|
|
185
|
+
|
|
186
|
+
return lines.join('\n');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ============================================================
|
|
190
|
+
// Commands
|
|
191
|
+
// ============================================================
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* List promotion candidates
|
|
195
|
+
*/
|
|
196
|
+
async function listCandidates(config) {
|
|
197
|
+
console.log(color('cyan', '\nPromotion Candidates'));
|
|
198
|
+
console.log('═'.repeat(70));
|
|
199
|
+
|
|
200
|
+
const candidates = await memoryDb.getPromotionCandidates({
|
|
201
|
+
minRelevance: config.automaticPromotion?.minRelevance || 0.8,
|
|
202
|
+
minAccessCount: config.automaticPromotion?.threshold || 3
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const decisionsContent = loadDecisions();
|
|
206
|
+
|
|
207
|
+
if (candidates.length === 0) {
|
|
208
|
+
console.log(color('gray', '\nNo facts ready for promotion.'));
|
|
209
|
+
console.log('Requirements: 80%+ relevance and 3+ accesses\n');
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Filter out already-promoted
|
|
214
|
+
const notPromoted = candidates.filter(c =>
|
|
215
|
+
!c.promoted_to && !isAlreadyInDecisions(c.fact, decisionsContent)
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
if (notPromoted.length === 0) {
|
|
219
|
+
console.log(color('green', '\n✓ All high-relevance facts are already promoted!\n'));
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`\nFound ${color('green', notPromoted.length)} candidates:\n`);
|
|
224
|
+
|
|
225
|
+
for (let i = 0; i < Math.min(notPromoted.length, 15); i++) {
|
|
226
|
+
const c = notPromoted[i];
|
|
227
|
+
const relevance = Math.round((c.relevance_score || 0) * 100);
|
|
228
|
+
const category = c.category || 'general';
|
|
229
|
+
|
|
230
|
+
console.log(`${color('blue', `[${i + 1}]`)} ${c.id}`);
|
|
231
|
+
console.log(` ${c.fact.substring(0, 70)}${c.fact.length > 70 ? '...' : ''}`);
|
|
232
|
+
console.log(` ${color('gray', `Category: ${category} | Relevance: ${relevance}% | Accessed: ${c.access_count || 0}x`)}\n`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (notPromoted.length > 15) {
|
|
236
|
+
console.log(color('gray', ` ... and ${notPromoted.length - 15} more\n`));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return notPromoted;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Promote a specific fact
|
|
244
|
+
*/
|
|
245
|
+
async function promoteFact(factId, dryRun = false) {
|
|
246
|
+
// Get fact details
|
|
247
|
+
const facts = await memoryDb.getAllFacts();
|
|
248
|
+
const fact = facts.find(f => f.id === factId);
|
|
249
|
+
|
|
250
|
+
if (!fact) {
|
|
251
|
+
console.log(color('red', `✗ Fact not found: ${factId}`));
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const decisionsContent = loadDecisions();
|
|
256
|
+
|
|
257
|
+
if (isAlreadyInDecisions(fact.fact, decisionsContent)) {
|
|
258
|
+
console.log(color('yellow', `⚠ Fact appears to already be in decisions.md`));
|
|
259
|
+
console.log(` ${fact.fact.substring(0, 60)}...`);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const formatted = formatForDecisions(fact);
|
|
264
|
+
|
|
265
|
+
console.log(`\n${color('cyan', 'Promoting to decisions.md:')}`);
|
|
266
|
+
console.log(` Section: ${formatted.section}`);
|
|
267
|
+
console.log(` Rule: ${formatted.rule}`);
|
|
268
|
+
|
|
269
|
+
if (dryRun) {
|
|
270
|
+
console.log(color('yellow', '\n [Dry run - no changes made]'));
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Update decisions.md
|
|
275
|
+
const newContent = appendToDecisions(formatted, decisionsContent);
|
|
276
|
+
fs.writeFileSync(DECISIONS_PATH, newContent);
|
|
277
|
+
|
|
278
|
+
// Sync to .claude/rules/ for Claude Code integration
|
|
279
|
+
syncDecisionsToRules();
|
|
280
|
+
|
|
281
|
+
// Mark fact as promoted
|
|
282
|
+
await memoryDb.markFactPromoted(factId, 'decisions.md');
|
|
283
|
+
|
|
284
|
+
console.log(color('green', '\n✓ Promoted successfully'));
|
|
285
|
+
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Interactive promotion
|
|
291
|
+
*/
|
|
292
|
+
async function interactiveSync(config) {
|
|
293
|
+
console.log(color('cyan', '\nMemory to Instructions Sync'));
|
|
294
|
+
console.log('═'.repeat(70));
|
|
295
|
+
|
|
296
|
+
const candidates = await listCandidates(config);
|
|
297
|
+
|
|
298
|
+
if (candidates.length === 0) {
|
|
299
|
+
return { promoted: 0 };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log(color('yellow', '\nNote: Use --auto to promote without prompts'));
|
|
303
|
+
console.log(' Use --promote <id> to promote specific fact\n');
|
|
304
|
+
|
|
305
|
+
return { candidates: candidates.length };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Auto-promote all candidates
|
|
310
|
+
*/
|
|
311
|
+
async function autoPromote(config) {
|
|
312
|
+
console.log(color('cyan', '\nAuto-Promoting Patterns'));
|
|
313
|
+
console.log('═'.repeat(70));
|
|
314
|
+
|
|
315
|
+
const candidates = await memoryDb.getPromotionCandidates({
|
|
316
|
+
minRelevance: config.automaticPromotion?.minRelevance || 0.8,
|
|
317
|
+
minAccessCount: config.automaticPromotion?.threshold || 3
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const decisionsContent = loadDecisions();
|
|
321
|
+
let promoted = 0;
|
|
322
|
+
let skipped = 0;
|
|
323
|
+
let currentContent = decisionsContent;
|
|
324
|
+
|
|
325
|
+
for (const candidate of candidates) {
|
|
326
|
+
if (candidate.promoted_to) {
|
|
327
|
+
skipped++;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (isAlreadyInDecisions(candidate.fact, currentContent)) {
|
|
332
|
+
// Mark as promoted even if already there
|
|
333
|
+
await memoryDb.markFactPromoted(candidate.id, 'decisions.md');
|
|
334
|
+
skipped++;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const formatted = formatForDecisions(candidate);
|
|
339
|
+
currentContent = appendToDecisions(formatted, currentContent);
|
|
340
|
+
|
|
341
|
+
await memoryDb.markFactPromoted(candidate.id, 'decisions.md');
|
|
342
|
+
promoted++;
|
|
343
|
+
|
|
344
|
+
console.log(`${color('green', '✓')} Promoted: ${candidate.fact.substring(0, 50)}...`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (promoted > 0) {
|
|
348
|
+
fs.writeFileSync(DECISIONS_PATH, currentContent);
|
|
349
|
+
// Sync to .claude/rules/ for Claude Code integration
|
|
350
|
+
syncDecisionsToRules();
|
|
351
|
+
await memoryDb.recordMemoryMetric('auto_promote');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
console.log(`\n${color('cyan', 'Summary')}`);
|
|
355
|
+
console.log(` Promoted: ${promoted}`);
|
|
356
|
+
console.log(` Skipped: ${skipped} (already in decisions.md)\n`);
|
|
357
|
+
|
|
358
|
+
return { promoted, skipped };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Show sync status
|
|
363
|
+
*/
|
|
364
|
+
async function showStatus(config) {
|
|
365
|
+
console.log(color('cyan', '\nMemory Sync Status'));
|
|
366
|
+
console.log('═'.repeat(50));
|
|
367
|
+
|
|
368
|
+
const stats = await memoryDb.getStats();
|
|
369
|
+
const candidates = await memoryDb.getPromotionCandidates({
|
|
370
|
+
minRelevance: config.automaticPromotion?.minRelevance || 0.8,
|
|
371
|
+
minAccessCount: config.automaticPromotion?.threshold || 3
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const autoEnabled = config.automaticPromotion?.enabled || false;
|
|
375
|
+
const requireApproval = config.automaticPromotion?.requireApproval !== false;
|
|
376
|
+
|
|
377
|
+
console.log(`\n${color('blue', 'Configuration')}`);
|
|
378
|
+
console.log(` Auto-promotion: ${autoEnabled ? color('green', 'Enabled') : color('gray', 'Disabled')}`);
|
|
379
|
+
console.log(` Require approval: ${requireApproval ? 'Yes' : 'No'}`);
|
|
380
|
+
console.log(` Min relevance: ${(config.automaticPromotion?.minRelevance || 0.8) * 100}%`);
|
|
381
|
+
console.log(` Min accesses: ${config.automaticPromotion?.threshold || 3}`);
|
|
382
|
+
|
|
383
|
+
console.log(`\n${color('blue', 'Memory Status')}`);
|
|
384
|
+
console.log(` Total facts: ${stats.facts.total}`);
|
|
385
|
+
console.log(` Candidates: ${candidates.filter(c => !c.promoted_to).length} ready`);
|
|
386
|
+
console.log(` Already promoted: ${candidates.filter(c => c.promoted_to).length}`);
|
|
387
|
+
|
|
388
|
+
console.log(`\n${color('blue', 'decisions.md')}`);
|
|
389
|
+
const decisionsContent = loadDecisions();
|
|
390
|
+
const lines = decisionsContent.split('\n').filter(l => l.trim().startsWith('-')).length;
|
|
391
|
+
console.log(` Rules defined: ${lines}`);
|
|
392
|
+
|
|
393
|
+
console.log('');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ============================================================
|
|
397
|
+
// Main
|
|
398
|
+
// ============================================================
|
|
399
|
+
|
|
400
|
+
async function main() {
|
|
401
|
+
const args = process.argv.slice(2);
|
|
402
|
+
const config = loadConfig();
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
if (args.includes('--list')) {
|
|
406
|
+
await listCandidates(config);
|
|
407
|
+
} else if (args.includes('--auto')) {
|
|
408
|
+
await autoPromote(config);
|
|
409
|
+
} else if (args.includes('--status')) {
|
|
410
|
+
await showStatus(config);
|
|
411
|
+
} else if (args.includes('--promote')) {
|
|
412
|
+
const idx = args.indexOf('--promote');
|
|
413
|
+
const factId = args[idx + 1];
|
|
414
|
+
if (!factId) {
|
|
415
|
+
console.error(color('red', 'Error: Missing fact ID'));
|
|
416
|
+
console.log('Usage: ./scripts/flow memory-sync --promote <fact_id>');
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
const dryRun = args.includes('--dry-run');
|
|
420
|
+
await promoteFact(factId, dryRun);
|
|
421
|
+
} else if (args.includes('--help') || args.includes('-h')) {
|
|
422
|
+
console.log(`
|
|
423
|
+
${color('cyan', 'Memory to Instructions Sync')}
|
|
424
|
+
|
|
425
|
+
Promotes high-relevance facts from memory to decisions.md
|
|
426
|
+
|
|
427
|
+
Usage: ./scripts/flow memory-sync [options]
|
|
428
|
+
|
|
429
|
+
Options:
|
|
430
|
+
(none) Show candidates and status
|
|
431
|
+
--list List all promotion candidates
|
|
432
|
+
--auto Auto-promote all candidates
|
|
433
|
+
--status Show sync configuration and status
|
|
434
|
+
--promote <id> Promote a specific fact by ID
|
|
435
|
+
--dry-run With --promote, preview without changing
|
|
436
|
+
--help, -h Show this help
|
|
437
|
+
|
|
438
|
+
Promotion Criteria:
|
|
439
|
+
- Relevance score >= 80% (configurable)
|
|
440
|
+
- Access count >= 3 (configurable)
|
|
441
|
+
- Not already in decisions.md
|
|
442
|
+
- Not already marked as promoted
|
|
443
|
+
|
|
444
|
+
Configure in config.json:
|
|
445
|
+
"automaticPromotion": {
|
|
446
|
+
"enabled": true, // Enable auto-promotion
|
|
447
|
+
"threshold": 3, // Min access count
|
|
448
|
+
"minRelevance": 0.8, // Min relevance score
|
|
449
|
+
"requireApproval": true
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
Examples:
|
|
453
|
+
./scripts/flow memory-sync # Check for patterns
|
|
454
|
+
./scripts/flow memory-sync --auto # Auto-promote all
|
|
455
|
+
./scripts/flow memory-sync --promote fact_123_abc
|
|
456
|
+
`);
|
|
457
|
+
} else {
|
|
458
|
+
await interactiveSync(config);
|
|
459
|
+
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error(color('red', `Error: ${error.message}`));
|
|
462
|
+
if (process.env.DEBUG) console.error(error.stack);
|
|
463
|
+
process.exit(1);
|
|
464
|
+
} finally {
|
|
465
|
+
memoryDb.closeDatabase();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ============================================================
|
|
470
|
+
// Exports (for use by session-end and other modules)
|
|
471
|
+
// ============================================================
|
|
472
|
+
|
|
473
|
+
module.exports = {
|
|
474
|
+
autoPromote,
|
|
475
|
+
listCandidates,
|
|
476
|
+
promoteFact,
|
|
477
|
+
showStatus,
|
|
478
|
+
loadConfig
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// Run CLI if executed directly
|
|
482
|
+
if (require.main === module) {
|
|
483
|
+
main();
|
|
484
|
+
}
|