cf-doctor 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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/bin/cf-doctor.js +18 -0
  4. package/dist/doctor.d.ts +21 -0
  5. package/dist/doctor.d.ts.map +1 -0
  6. package/dist/doctor.js +33 -0
  7. package/dist/doctor.js.map +1 -0
  8. package/dist/index.d.ts +9 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +9 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/mcp-server.d.ts +12 -0
  13. package/dist/mcp-server.d.ts.map +1 -0
  14. package/dist/mcp-server.js +200 -0
  15. package/dist/mcp-server.js.map +1 -0
  16. package/dist/patches/apply.d.ts +7 -0
  17. package/dist/patches/apply.d.ts.map +1 -0
  18. package/dist/patches/apply.js +51 -0
  19. package/dist/patches/apply.js.map +1 -0
  20. package/dist/persistence/episodes.d.ts +42 -0
  21. package/dist/persistence/episodes.d.ts.map +1 -0
  22. package/dist/persistence/episodes.js +160 -0
  23. package/dist/persistence/episodes.js.map +1 -0
  24. package/dist/persistence/index.d.ts +4 -0
  25. package/dist/persistence/index.d.ts.map +1 -0
  26. package/dist/persistence/index.js +4 -0
  27. package/dist/persistence/index.js.map +1 -0
  28. package/dist/persistence/q-table.d.ts +42 -0
  29. package/dist/persistence/q-table.d.ts.map +1 -0
  30. package/dist/persistence/q-table.js +138 -0
  31. package/dist/persistence/q-table.js.map +1 -0
  32. package/dist/persistence/sona.d.ts +45 -0
  33. package/dist/persistence/sona.d.ts.map +1 -0
  34. package/dist/persistence/sona.js +142 -0
  35. package/dist/persistence/sona.js.map +1 -0
  36. package/package.json +63 -0
  37. package/patches/README.md +68 -0
  38. package/patches/neural-index.patch +8 -0
  39. package/patches/quick-test.patch +25 -0
  40. package/patches/sona-integration.patch +76 -0
  41. package/patches/version-bridge.patch +30 -0
  42. package/scripts/cf-doctor.sh +684 -0
  43. package/tests/run-all-tests.sh +32 -0
  44. package/tests/test-01-doctor-passes.sh +43 -0
  45. package/tests/test-02-mcp-init.sh +36 -0
  46. package/tests/test-03-agent-spawn-no-ruvector.sh +84 -0
  47. package/tests/test-04-agent-spawn-with-ruvector.sh +37 -0
  48. package/tests/test-05-learning-persists.sh +94 -0
  49. package/tests/test-06-hooks-version-bridge.sh +82 -0
  50. package/tests/test-helpers.sh +88 -0
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Episode Recall with TF-IDF Similarity
3
+ *
4
+ * Searches past agent episodes by text similarity to provide
5
+ * relevant context to newly spawned agents.
6
+ */
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync } from 'fs';
8
+ import { join } from 'path';
9
+ const MEMORY_DIR = '.claude-flow/memory';
10
+ const EPISODES_FILE = join(MEMORY_DIR, 'episodes.json');
11
+ const MAX_EPISODES = 1000; // keep last 1000 episodes
12
+ // --- TF-IDF Implementation ---
13
+ function tokenize(text) {
14
+ return text
15
+ .toLowerCase()
16
+ .replace(/[^a-z0-9\s]/g, ' ')
17
+ .split(/\s+/)
18
+ .filter((t) => t.length > 2); // skip tiny words
19
+ }
20
+ function termFrequency(tokens) {
21
+ const tf = new Map();
22
+ for (const token of tokens) {
23
+ tf.set(token, (tf.get(token) || 0) + 1);
24
+ }
25
+ // Normalize by document length
26
+ if (tokens.length > 0) {
27
+ for (const [term, count] of tf) {
28
+ tf.set(term, count / tokens.length);
29
+ }
30
+ }
31
+ return tf;
32
+ }
33
+ function computeIDF(documents) {
34
+ const docCount = documents.length;
35
+ const docFreq = new Map();
36
+ for (const doc of documents) {
37
+ const seen = new Set(doc);
38
+ for (const term of seen) {
39
+ docFreq.set(term, (docFreq.get(term) || 0) + 1);
40
+ }
41
+ }
42
+ const idf = new Map();
43
+ for (const [term, freq] of docFreq) {
44
+ idf.set(term, Math.log((docCount + 1) / (freq + 1)) + 1); // smoothed IDF
45
+ }
46
+ return idf;
47
+ }
48
+ function tfidfVector(tokens, idf) {
49
+ const tf = termFrequency(tokens);
50
+ const tfidf = new Map();
51
+ for (const [term, tfVal] of tf) {
52
+ tfidf.set(term, tfVal * (idf.get(term) || 1));
53
+ }
54
+ return tfidf;
55
+ }
56
+ function cosineSimilarity(a, b) {
57
+ let dotProduct = 0;
58
+ let normA = 0;
59
+ let normB = 0;
60
+ for (const [term, val] of a) {
61
+ normA += val * val;
62
+ if (b.has(term)) {
63
+ dotProduct += val * b.get(term);
64
+ }
65
+ }
66
+ for (const [, val] of b) {
67
+ normB += val * val;
68
+ }
69
+ if (normA === 0 || normB === 0)
70
+ return 0;
71
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
72
+ }
73
+ // --- Episode Store ---
74
+ export function loadEpisodes() {
75
+ try {
76
+ if (existsSync(EPISODES_FILE)) {
77
+ const raw = readFileSync(EPISODES_FILE, 'utf-8');
78
+ const store = JSON.parse(raw);
79
+ if (store.version === 1)
80
+ return store.episodes;
81
+ }
82
+ }
83
+ catch (err) {
84
+ console.warn('[episodes] Failed to load episodes:', err.message);
85
+ }
86
+ return [];
87
+ }
88
+ export function saveEpisode(episode) {
89
+ if (!existsSync(MEMORY_DIR)) {
90
+ mkdirSync(MEMORY_DIR, { recursive: true });
91
+ }
92
+ const episodes = loadEpisodes();
93
+ episodes.push(episode);
94
+ // Trim to MAX_EPISODES (keep most recent)
95
+ const trimmed = episodes.length > MAX_EPISODES
96
+ ? episodes.slice(episodes.length - MAX_EPISODES)
97
+ : episodes;
98
+ const store = {
99
+ version: 1,
100
+ episodes: trimmed,
101
+ idf: {},
102
+ lastRecomputed: Date.now(),
103
+ };
104
+ try {
105
+ const tmpFile = EPISODES_FILE + '.tmp';
106
+ writeFileSync(tmpFile, JSON.stringify(store, null, 2), 'utf-8');
107
+ renameSync(tmpFile, EPISODES_FILE);
108
+ }
109
+ catch (err) {
110
+ console.warn('[episodes] Failed to save episode:', err.message);
111
+ }
112
+ }
113
+ /**
114
+ * Recall episodes relevant to the given context using TF-IDF similarity.
115
+ *
116
+ * @param context - Text describing what the agent needs to do
117
+ * @param topK - Number of episodes to return (default: 5)
118
+ * @param minSimilarity - Minimum similarity threshold (default: 0.1)
119
+ * @returns Array of relevant episodes sorted by similarity (descending)
120
+ */
121
+ export function recallRelevantEpisodes(context, topK = 5, minSimilarity = 0.1) {
122
+ const episodes = loadEpisodes();
123
+ if (episodes.length === 0)
124
+ return [];
125
+ // Tokenize all episode descriptions (input + outcome)
126
+ const episodeTokens = episodes.map((ep) => tokenize(`${ep.input} ${ep.outcome}`));
127
+ const queryTokens = tokenize(context);
128
+ // Compute IDF across all documents
129
+ const allDocs = [...episodeTokens, queryTokens];
130
+ const idf = computeIDF(allDocs);
131
+ // Compute TF-IDF vectors
132
+ const queryVector = tfidfVector(queryTokens, idf);
133
+ // Score each episode
134
+ const scored = episodes.map((ep, i) => ({
135
+ ...ep,
136
+ similarity: cosineSimilarity(queryVector, tfidfVector(episodeTokens[i], idf)),
137
+ }));
138
+ // Filter and sort
139
+ return scored
140
+ .filter((ep) => ep.similarity >= minSimilarity)
141
+ .sort((a, b) => b.similarity - a.similarity)
142
+ .slice(0, topK);
143
+ }
144
+ /**
145
+ * Format episodes as context string for injection into agent prompts.
146
+ */
147
+ export function formatEpisodesAsContext(episodes) {
148
+ if (episodes.length === 0)
149
+ return '';
150
+ const lines = ['## Relevant Past Episodes', ''];
151
+ for (const ep of episodes) {
152
+ lines.push(`### Episode (${new Date(ep.timestamp).toISOString().split('T')[0]}, relevance: ${(ep.similarity * 100).toFixed(0)}%)`);
153
+ lines.push(`**Task:** ${ep.input.slice(0, 200)}`);
154
+ lines.push(`**Outcome:** ${ep.outcome.slice(0, 200)}`);
155
+ lines.push(`**Reward:** ${ep.reward}`);
156
+ lines.push('');
157
+ }
158
+ return lines.join('\n');
159
+ }
160
+ //# sourceMappingURL=episodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"episodes.js","sourceRoot":"","sources":["../../src/persistence/episodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { loadQTable, saveQTable, updateQValue, qKey, getBestAction, epsilonGreedyAction, ensureLearningDir, } from './q-table.js';
2
+ export { PersistentSonaEngine, loadSonaPatterns, saveSonaPatterns, registerSonaExitHandler, } from './sona.js';
3
+ export { saveEpisode, loadEpisodes, recallRelevantEpisodes, formatEpisodesAsContext, type Episode, type EpisodeStore, } from './episodes.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/persistence/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,UAAU,EACV,YAAY,EACZ,IAAI,EACJ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { loadQTable, saveQTable, updateQValue, qKey, getBestAction, epsilonGreedyAction, ensureLearningDir, } from './q-table.js';
2
+ export { PersistentSonaEngine, loadSonaPatterns, saveSonaPatterns, registerSonaExitHandler, } from './sona.js';
3
+ export { saveEpisode, loadEpisodes, recallRelevantEpisodes, formatEpisodesAsContext, } from './episodes.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/persistence/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,UAAU,EACV,YAAY,EACZ,IAAI,EACJ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,GAGxB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Q-Table Persistence Layer
3
+ *
4
+ * Wraps the Q-learning router's in-memory Q-table with file-based persistence.
5
+ * Pattern: save on update, load on init (same as saveStore/loadStore in tool files).
6
+ */
7
+ interface QEntry {
8
+ state: string;
9
+ action: string;
10
+ value: number;
11
+ visits: number;
12
+ lastUpdated: number;
13
+ }
14
+ export declare function ensureLearningDir(): void;
15
+ export declare function loadQTable(): Map<string, QEntry>;
16
+ export declare function saveQTable(table: Map<string, QEntry>, totalUpdates: number): void;
17
+ /**
18
+ * Q-Table key format: "state::action"
19
+ * Where state = serialized agent context, action = chosen routing decision
20
+ */
21
+ export declare function qKey(state: string, action: string): string;
22
+ /**
23
+ * Update Q-value using standard Q-learning formula:
24
+ * Q(s,a) = Q(s,a) + alpha * (reward + gamma * max_a' Q(s',a') - Q(s,a))
25
+ */
26
+ export declare function updateQValue(table: Map<string, QEntry>, state: string, action: string, reward: number, nextStateBestValue?: number, learningRate?: number, discountFactor?: number): QEntry;
27
+ /**
28
+ * Get the best action for a given state (greedy policy).
29
+ * Returns null if the state has never been visited.
30
+ */
31
+ export declare function getBestAction(table: Map<string, QEntry>, state: string): {
32
+ action: string;
33
+ value: number;
34
+ } | null;
35
+ /**
36
+ * Epsilon-greedy action selection.
37
+ * With probability epsilon, return a random action from the candidates.
38
+ * Otherwise, return the best known action for this state.
39
+ */
40
+ export declare function epsilonGreedyAction(table: Map<string, QEntry>, state: string, candidateActions: string[], epsilon?: number): string;
41
+ export {};
42
+ //# sourceMappingURL=q-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"q-table.d.ts","sourceRoot":"","sources":["../../src/persistence/q-table.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,UAAU,MAAM;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC;AAED,wBAAgB,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAiBhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAmCjF;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,kBAAkB,GAAE,MAAU,EAC9B,YAAY,GAAE,MAAY,EAC1B,cAAc,GAAE,MAAa,GAC5B,MAAM,CAwBR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,KAAK,EAAE,MAAM,GACZ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAY1C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,KAAK,EAAE,MAAM,EACb,gBAAgB,EAAE,MAAM,EAAE,EAC1B,OAAO,GAAE,MAAY,GACpB,MAAM,CAkBR"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Q-Table Persistence Layer
3
+ *
4
+ * Wraps the Q-learning router's in-memory Q-table with file-based persistence.
5
+ * Pattern: save on update, load on init (same as saveStore/loadStore in tool files).
6
+ */
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync } from 'fs';
8
+ import { join } from 'path';
9
+ const LEARNING_DIR = '.claude-flow/learning';
10
+ const Q_TABLE_FILE = join(LEARNING_DIR, 'q-table.json');
11
+ export function ensureLearningDir() {
12
+ if (!existsSync(LEARNING_DIR)) {
13
+ mkdirSync(LEARNING_DIR, { recursive: true });
14
+ }
15
+ }
16
+ export function loadQTable() {
17
+ const map = new Map();
18
+ try {
19
+ if (existsSync(Q_TABLE_FILE)) {
20
+ const raw = readFileSync(Q_TABLE_FILE, 'utf-8');
21
+ const store = JSON.parse(raw);
22
+ if (store.version === 1 && store.entries) {
23
+ for (const [key, entry] of Object.entries(store.entries)) {
24
+ map.set(key, entry);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ catch (err) {
30
+ // Corrupted file -- start fresh but don't crash
31
+ console.warn('[q-table] Failed to load Q-table, starting fresh:', err.message);
32
+ }
33
+ return map;
34
+ }
35
+ export function saveQTable(table, totalUpdates) {
36
+ ensureLearningDir();
37
+ // Preserve original createdAt if the file already exists
38
+ let createdAt = Date.now();
39
+ try {
40
+ if (existsSync(Q_TABLE_FILE)) {
41
+ const raw = readFileSync(Q_TABLE_FILE, 'utf-8');
42
+ const existing = JSON.parse(raw);
43
+ if (existing.metadata?.createdAt) {
44
+ createdAt = existing.metadata.createdAt;
45
+ }
46
+ }
47
+ }
48
+ catch {
49
+ // If we can't read the existing file, use current timestamp
50
+ }
51
+ const store = {
52
+ version: 1,
53
+ entries: Object.fromEntries(table),
54
+ metadata: {
55
+ totalUpdates,
56
+ createdAt,
57
+ lastSavedAt: Date.now(),
58
+ },
59
+ };
60
+ try {
61
+ // Atomic write: write to temp file then rename
62
+ const tmpFile = Q_TABLE_FILE + '.tmp';
63
+ writeFileSync(tmpFile, JSON.stringify(store, null, 2), 'utf-8');
64
+ renameSync(tmpFile, Q_TABLE_FILE);
65
+ }
66
+ catch (err) {
67
+ console.warn('[q-table] Failed to save Q-table:', err.message);
68
+ }
69
+ }
70
+ /**
71
+ * Q-Table key format: "state::action"
72
+ * Where state = serialized agent context, action = chosen routing decision
73
+ */
74
+ export function qKey(state, action) {
75
+ return `${state}::${action}`;
76
+ }
77
+ /**
78
+ * Update Q-value using standard Q-learning formula:
79
+ * Q(s,a) = Q(s,a) + alpha * (reward + gamma * max_a' Q(s',a') - Q(s,a))
80
+ */
81
+ export function updateQValue(table, state, action, reward, nextStateBestValue = 0, learningRate = 0.1, discountFactor = 0.95) {
82
+ const key = qKey(state, action);
83
+ const existing = table.get(key) || {
84
+ state,
85
+ action,
86
+ value: 0,
87
+ visits: 0,
88
+ lastUpdated: 0,
89
+ };
90
+ const newValue = existing.value +
91
+ learningRate * (reward + discountFactor * nextStateBestValue - existing.value);
92
+ const updated = {
93
+ state,
94
+ action,
95
+ value: newValue,
96
+ visits: existing.visits + 1,
97
+ lastUpdated: Date.now(),
98
+ };
99
+ table.set(key, updated);
100
+ return updated;
101
+ }
102
+ /**
103
+ * Get the best action for a given state (greedy policy).
104
+ * Returns null if the state has never been visited.
105
+ */
106
+ export function getBestAction(table, state) {
107
+ let best = null;
108
+ for (const [key, entry] of table) {
109
+ if (key.startsWith(`${state}::`)) {
110
+ if (best === null || entry.value > best.value) {
111
+ best = { action: entry.action, value: entry.value };
112
+ }
113
+ }
114
+ }
115
+ return best;
116
+ }
117
+ /**
118
+ * Epsilon-greedy action selection.
119
+ * With probability epsilon, return a random action from the candidates.
120
+ * Otherwise, return the best known action for this state.
121
+ */
122
+ export function epsilonGreedyAction(table, state, candidateActions, epsilon = 0.1) {
123
+ if (candidateActions.length === 0) {
124
+ throw new Error('No candidate actions provided');
125
+ }
126
+ if (Math.random() < epsilon) {
127
+ // Explore: pick a random action
128
+ return candidateActions[Math.floor(Math.random() * candidateActions.length)];
129
+ }
130
+ // Exploit: pick the best known action
131
+ const best = getBestAction(table, state);
132
+ if (best && candidateActions.includes(best.action)) {
133
+ return best.action;
134
+ }
135
+ // Fallback: no knowledge for this state, pick randomly
136
+ return candidateActions[Math.floor(Math.random() * candidateActions.length)];
137
+ }
138
+ //# sourceMappingURL=q-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"q-table.js","sourceRoot":"","sources":["../../src/persistence/q-table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * SONA Pattern Persistence Layer
3
+ *
4
+ * Persists learned patterns to disk so they survive process restarts.
5
+ * Works with both the real @ruvector/sona engine and the mock fallback.
6
+ */
7
+ interface SonaPattern {
8
+ key: string;
9
+ pattern: unknown;
10
+ confidence: number;
11
+ learnCount: number;
12
+ createdAt: number;
13
+ lastAccessedAt: number;
14
+ }
15
+ export declare function loadSonaPatterns(): Map<string, SonaPattern>;
16
+ export declare function saveSonaPatterns(patterns: Map<string, SonaPattern>): void;
17
+ /**
18
+ * PersistentSonaEngine - wraps the mock with disk persistence
19
+ *
20
+ * Use this as a drop-in replacement for MockSonaEngine in sona-integration.ts
21
+ */
22
+ export declare class PersistentSonaEngine {
23
+ private patterns;
24
+ private saveDebounceTimer;
25
+ constructor();
26
+ store(key: string, pattern: unknown): Promise<void>;
27
+ recall(key: string): Promise<unknown>;
28
+ learn(_input: unknown, feedback: number): Promise<void>;
29
+ private scheduleSave;
30
+ /** Force save immediately (call on process exit) */
31
+ flush(): void;
32
+ getPatternCount(): number;
33
+ /**
34
+ * Get patterns sorted by confidence (descending).
35
+ * Useful for debugging and introspection.
36
+ */
37
+ getTopPatterns(limit?: number): SonaPattern[];
38
+ }
39
+ /**
40
+ * Register a process exit handler to flush patterns.
41
+ * Call this once during application bootstrap.
42
+ */
43
+ export declare function registerSonaExitHandler(engine: PersistentSonaEngine): void;
44
+ export {};
45
+ //# sourceMappingURL=sona.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sona.d.ts","sourceRoot":"","sources":["../../src/persistence/sona.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAYD,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAgB3D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI,CA2BzE;AAED;;;;GAIG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,iBAAiB,CAA8C;;IAMjE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAanD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASrC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB7D,OAAO,CAAC,YAAY;IAOpB,oDAAoD;IACpD,KAAK,IAAI,IAAI;IAKb,eAAe,IAAI,MAAM;IAIzB;;;OAGG;IACH,cAAc,CAAC,KAAK,GAAE,MAAW,GAAG,WAAW,EAAE;CAKlD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAW1E"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * SONA Pattern Persistence Layer
3
+ *
4
+ * Persists learned patterns to disk so they survive process restarts.
5
+ * Works with both the real @ruvector/sona engine and the mock fallback.
6
+ */
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync } from 'fs';
8
+ import { join } from 'path';
9
+ const LEARNING_DIR = '.claude-flow/learning';
10
+ const SONA_FILE = join(LEARNING_DIR, 'sona-patterns.json');
11
+ export function loadSonaPatterns() {
12
+ const map = new Map();
13
+ try {
14
+ if (existsSync(SONA_FILE)) {
15
+ const raw = readFileSync(SONA_FILE, 'utf-8');
16
+ const store = JSON.parse(raw);
17
+ if (store.version === 1 && store.patterns) {
18
+ for (const [key, pattern] of Object.entries(store.patterns)) {
19
+ map.set(key, pattern);
20
+ }
21
+ }
22
+ }
23
+ }
24
+ catch (err) {
25
+ console.warn('[sona] Failed to load patterns, starting fresh:', err.message);
26
+ }
27
+ return map;
28
+ }
29
+ export function saveSonaPatterns(patterns) {
30
+ if (!existsSync(LEARNING_DIR)) {
31
+ mkdirSync(LEARNING_DIR, { recursive: true });
32
+ }
33
+ let totalLearnings = 0;
34
+ for (const p of patterns.values()) {
35
+ totalLearnings += p.learnCount;
36
+ }
37
+ const store = {
38
+ version: 1,
39
+ patterns: Object.fromEntries(patterns),
40
+ metadata: {
41
+ totalPatterns: patterns.size,
42
+ totalLearnings,
43
+ lastSavedAt: Date.now(),
44
+ },
45
+ };
46
+ try {
47
+ const tmpFile = SONA_FILE + '.tmp';
48
+ writeFileSync(tmpFile, JSON.stringify(store, null, 2), 'utf-8');
49
+ renameSync(tmpFile, SONA_FILE);
50
+ }
51
+ catch (err) {
52
+ console.warn('[sona] Failed to save patterns:', err.message);
53
+ }
54
+ }
55
+ /**
56
+ * PersistentSonaEngine - wraps the mock with disk persistence
57
+ *
58
+ * Use this as a drop-in replacement for MockSonaEngine in sona-integration.ts
59
+ */
60
+ export class PersistentSonaEngine {
61
+ patterns;
62
+ saveDebounceTimer = null;
63
+ constructor() {
64
+ this.patterns = loadSonaPatterns();
65
+ }
66
+ async store(key, pattern) {
67
+ const existing = this.patterns.get(key);
68
+ this.patterns.set(key, {
69
+ key,
70
+ pattern,
71
+ confidence: existing?.confidence ?? 0.5,
72
+ learnCount: existing?.learnCount ?? 0,
73
+ createdAt: existing?.createdAt ?? Date.now(),
74
+ lastAccessedAt: Date.now(),
75
+ });
76
+ this.scheduleSave();
77
+ }
78
+ async recall(key) {
79
+ const entry = this.patterns.get(key);
80
+ if (entry) {
81
+ entry.lastAccessedAt = Date.now();
82
+ return entry.pattern;
83
+ }
84
+ return null;
85
+ }
86
+ async learn(_input, feedback) {
87
+ // Update confidence of most recently accessed patterns based on feedback
88
+ const recentThreshold = Date.now() - 60_000; // last 60 seconds
89
+ for (const pattern of this.patterns.values()) {
90
+ if (pattern.lastAccessedAt > recentThreshold) {
91
+ // Bayesian-style confidence update
92
+ const alpha = pattern.confidence * pattern.learnCount + (feedback > 0 ? 1 : 0);
93
+ const beta = (1 - pattern.confidence) * pattern.learnCount + (feedback <= 0 ? 1 : 0);
94
+ pattern.learnCount += 1;
95
+ pattern.confidence = alpha / (alpha + beta);
96
+ }
97
+ }
98
+ this.scheduleSave();
99
+ }
100
+ scheduleSave() {
101
+ if (this.saveDebounceTimer)
102
+ clearTimeout(this.saveDebounceTimer);
103
+ this.saveDebounceTimer = setTimeout(() => {
104
+ saveSonaPatterns(this.patterns);
105
+ }, 1000); // debounce: save at most once per second
106
+ }
107
+ /** Force save immediately (call on process exit) */
108
+ flush() {
109
+ if (this.saveDebounceTimer)
110
+ clearTimeout(this.saveDebounceTimer);
111
+ saveSonaPatterns(this.patterns);
112
+ }
113
+ getPatternCount() {
114
+ return this.patterns.size;
115
+ }
116
+ /**
117
+ * Get patterns sorted by confidence (descending).
118
+ * Useful for debugging and introspection.
119
+ */
120
+ getTopPatterns(limit = 10) {
121
+ return Array.from(this.patterns.values())
122
+ .sort((a, b) => b.confidence - a.confidence)
123
+ .slice(0, limit);
124
+ }
125
+ }
126
+ /**
127
+ * Register a process exit handler to flush patterns.
128
+ * Call this once during application bootstrap.
129
+ */
130
+ export function registerSonaExitHandler(engine) {
131
+ const flush = () => engine.flush();
132
+ process.on('exit', flush);
133
+ process.on('SIGINT', () => {
134
+ flush();
135
+ process.exit(0);
136
+ });
137
+ process.on('SIGTERM', () => {
138
+ flush();
139
+ process.exit(0);
140
+ });
141
+ }
142
+ //# sourceMappingURL=sona.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sona.js","sourceRoot":"","sources":["../../src/persistence/sona.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "cf-doctor",
3
+ "version": "1.0.0",
4
+ "description": "Setup validator, auto-fixer, and learning persistence for claude-flow + ruvector integration on Linux/Ubuntu",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "cf-doctor": "./bin/cf-doctor.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./persistence": {
17
+ "import": "./dist/persistence/index.js",
18
+ "types": "./dist/persistence/index.d.ts"
19
+ },
20
+ "./mcp": {
21
+ "import": "./dist/mcp-server.js",
22
+ "types": "./dist/mcp-server.d.ts"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "test": "bash tests/run-all-tests.sh",
28
+ "doctor": "bash scripts/cf-doctor.sh",
29
+ "doctor:fix": "bash scripts/cf-doctor.sh --fix",
30
+ "doctor:json": "bash scripts/cf-doctor.sh --json",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "keywords": [
34
+ "claude-flow",
35
+ "ruvector",
36
+ "claude-code",
37
+ "mcp",
38
+ "doctor",
39
+ "integration",
40
+ "linux",
41
+ "ubuntu"
42
+ ],
43
+ "author": "FoxFlow",
44
+ "license": "MIT",
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "files": [
49
+ "dist/",
50
+ "bin/",
51
+ "scripts/",
52
+ "patches/",
53
+ "tests/"
54
+ ],
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/foxflow/cf-doctor"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^22.0.0",
61
+ "typescript": "^5.5.0"
62
+ }
63
+ }