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.
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/bin/cf-doctor.js +18 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +33 -0
- package/dist/doctor.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +12 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +200 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/patches/apply.d.ts +7 -0
- package/dist/patches/apply.d.ts.map +1 -0
- package/dist/patches/apply.js +51 -0
- package/dist/patches/apply.js.map +1 -0
- package/dist/persistence/episodes.d.ts +42 -0
- package/dist/persistence/episodes.d.ts.map +1 -0
- package/dist/persistence/episodes.js +160 -0
- package/dist/persistence/episodes.js.map +1 -0
- package/dist/persistence/index.d.ts +4 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +4 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/q-table.d.ts +42 -0
- package/dist/persistence/q-table.d.ts.map +1 -0
- package/dist/persistence/q-table.js +138 -0
- package/dist/persistence/q-table.js.map +1 -0
- package/dist/persistence/sona.d.ts +45 -0
- package/dist/persistence/sona.d.ts.map +1 -0
- package/dist/persistence/sona.js +142 -0
- package/dist/persistence/sona.js.map +1 -0
- package/package.json +63 -0
- package/patches/README.md +68 -0
- package/patches/neural-index.patch +8 -0
- package/patches/quick-test.patch +25 -0
- package/patches/sona-integration.patch +76 -0
- package/patches/version-bridge.patch +30 -0
- package/scripts/cf-doctor.sh +684 -0
- package/tests/run-all-tests.sh +32 -0
- package/tests/test-01-doctor-passes.sh +43 -0
- package/tests/test-02-mcp-init.sh +36 -0
- package/tests/test-03-agent-spawn-no-ruvector.sh +84 -0
- package/tests/test-04-agent-spawn-with-ruvector.sh +37 -0
- package/tests/test-05-learning-persists.sh +94 -0
- package/tests/test-06-hooks-version-bridge.sh +82 -0
- 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
|
+
}
|