moflo 4.8.41 → 4.8.43
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/.claude/helpers/hook-handler.cjs +8 -1
- package/.claude/scripts/build-embeddings.mjs +549 -549
- package/.claude/scripts/generate-code-map.mjs +249 -69
- package/.claude/scripts/hooks.mjs +28 -79
- package/.claude/scripts/index-all.mjs +183 -127
- package/.claude/scripts/index-guidance.mjs +16 -3
- package/.claude/scripts/index-tests.mjs +38 -19
- package/.claude/scripts/lib/moflo-resolve.mjs +14 -0
- package/.claude/scripts/lib/process-manager.mjs +256 -0
- package/.claude/scripts/lib/registry-cleanup.cjs +41 -0
- package/.claude/scripts/semantic-search.mjs +1 -1
- package/.claude/scripts/session-start-launcher.mjs +16 -1
- package/bin/index-all.mjs +14 -4
- package/package.json +2 -2
- package/src/@claude-flow/cli/dist/src/version.js +1 -1
- package/src/@claude-flow/cli/package.json +106 -106
|
@@ -1,127 +1,183 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Sequential indexer chain for session-start.
|
|
4
|
-
*
|
|
5
|
-
* Runs all DB-writing indexers one at a time to avoid sql.js last-write-wins
|
|
6
|
-
* concurrency issues, then triggers HNSW rebuild once everything is
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sequential indexer chain for session-start.
|
|
4
|
+
*
|
|
5
|
+
* Runs all DB-writing indexers one at a time to avoid sql.js last-write-wins
|
|
6
|
+
* concurrency issues (#78), then triggers HNSW rebuild once everything is
|
|
7
|
+
* committed (#81).
|
|
8
|
+
*
|
|
9
|
+
* Spawned as a single detached background process by hooks.mjs session-start.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, appendFileSync, readFileSync } from 'fs';
|
|
13
|
+
import { resolve, dirname } from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { execFileSync } from 'child_process';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
// Detect project root by walking up from cwd to find package.json.
|
|
20
|
+
// IMPORTANT: Do NOT use resolve(__dirname, '..') — this script lives in bin/
|
|
21
|
+
// during development but gets synced to .claude/scripts/ in consumer projects,
|
|
22
|
+
// so __dirname-relative paths break. findProjectRoot() works in both locations.
|
|
23
|
+
function findProjectRoot() {
|
|
24
|
+
let dir = process.cwd();
|
|
25
|
+
const root = resolve(dir, '/');
|
|
26
|
+
while (dir !== root) {
|
|
27
|
+
if (existsSync(resolve(dir, 'package.json'))) return dir;
|
|
28
|
+
dir = dirname(dir);
|
|
29
|
+
}
|
|
30
|
+
return process.cwd();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const projectRoot = findProjectRoot();
|
|
34
|
+
const LOG_PATH = resolve(projectRoot, '.swarm/hooks.log');
|
|
35
|
+
|
|
36
|
+
function log(msg) {
|
|
37
|
+
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
38
|
+
const line = `[${ts}] [index-all] ${msg}\n`;
|
|
39
|
+
try { appendFileSync(LOG_PATH, line); } catch { /* ignore */ }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveBin(binName, localScript) {
|
|
43
|
+
const mofloScript = resolve(projectRoot, 'node_modules/moflo/bin', localScript);
|
|
44
|
+
if (existsSync(mofloScript)) return mofloScript;
|
|
45
|
+
const npmBin = resolve(projectRoot, 'node_modules/.bin', binName);
|
|
46
|
+
if (existsSync(npmBin)) return npmBin;
|
|
47
|
+
const localPath = resolve(projectRoot, '.claude/scripts', localScript);
|
|
48
|
+
if (existsSync(localPath)) return localPath;
|
|
49
|
+
// Also check bin/ directory (for development use)
|
|
50
|
+
const binPath = resolve(projectRoot, 'bin', localScript);
|
|
51
|
+
if (existsSync(binPath)) return binPath;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getLocalCliPath() {
|
|
56
|
+
const paths = [
|
|
57
|
+
resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js'),
|
|
58
|
+
resolve(projectRoot, 'node_modules/moflo/bin/cli.js'),
|
|
59
|
+
resolve(projectRoot, 'node_modules/.bin/flo'),
|
|
60
|
+
// Development: local CLI
|
|
61
|
+
resolve(projectRoot, 'src/@claude-flow/cli/bin/cli.js'),
|
|
62
|
+
];
|
|
63
|
+
for (const p of paths) {
|
|
64
|
+
if (existsSync(p)) return p;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Read moflo.yaml once and cache auto_index flags. */
|
|
70
|
+
let _autoIndexFlags = null;
|
|
71
|
+
function isIndexEnabled(key) {
|
|
72
|
+
if (_autoIndexFlags === null) {
|
|
73
|
+
_autoIndexFlags = {};
|
|
74
|
+
const yamlPath = resolve(projectRoot, 'moflo.yaml');
|
|
75
|
+
if (existsSync(yamlPath)) {
|
|
76
|
+
try {
|
|
77
|
+
const content = readFileSync(yamlPath, 'utf-8');
|
|
78
|
+
for (const k of ['guidance', 'code_map', 'tests', 'patterns']) {
|
|
79
|
+
const re = new RegExp(`auto_index:\\s*\\n(?:.*\\n)*?\\s+${k}:\\s*(true|false)`);
|
|
80
|
+
const match = content.match(re);
|
|
81
|
+
_autoIndexFlags[k] = match ? match[1] !== 'false' : true;
|
|
82
|
+
}
|
|
83
|
+
} catch { /* ignore, all default to true */ }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return _autoIndexFlags[key] !== false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function runStep(label, cmd, args, timeoutMs = 120_000) {
|
|
90
|
+
const start = Date.now();
|
|
91
|
+
log(`START ${label}`);
|
|
92
|
+
try {
|
|
93
|
+
execFileSync(cmd, args, {
|
|
94
|
+
cwd: projectRoot,
|
|
95
|
+
timeout: timeoutMs,
|
|
96
|
+
stdio: 'ignore',
|
|
97
|
+
windowsHide: true,
|
|
98
|
+
});
|
|
99
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
100
|
+
log(`DONE ${label} (${elapsed}s)`);
|
|
101
|
+
return true;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
104
|
+
log(`FAIL ${label} (${elapsed}s): ${err.message?.split('\n')[0] || 'unknown'}`);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function main() {
|
|
110
|
+
const startTime = Date.now();
|
|
111
|
+
log('Sequential indexing chain started');
|
|
112
|
+
|
|
113
|
+
// 1. Guidance indexer
|
|
114
|
+
if (isIndexEnabled('guidance')) {
|
|
115
|
+
const guidanceScript = resolveBin('flo-index', 'index-guidance.mjs');
|
|
116
|
+
if (guidanceScript) {
|
|
117
|
+
runStep('guidance-index', 'node', [guidanceScript]);
|
|
118
|
+
} else {
|
|
119
|
+
log('SKIP guidance-index (script not found)');
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
log('SKIP guidance-index (disabled in moflo.yaml)');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2. Code map generator (the big one — ~22s)
|
|
126
|
+
if (isIndexEnabled('code_map')) {
|
|
127
|
+
const codeMapScript = resolveBin('flo-codemap', 'generate-code-map.mjs');
|
|
128
|
+
if (codeMapScript) {
|
|
129
|
+
runStep('code-map', 'node', [codeMapScript], 180_000);
|
|
130
|
+
} else {
|
|
131
|
+
log('SKIP code-map (script not found)');
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
log('SKIP code-map (disabled in moflo.yaml)');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 3. Test indexer
|
|
138
|
+
if (isIndexEnabled('tests')) {
|
|
139
|
+
const testScript = resolveBin('flo-testmap', 'index-tests.mjs');
|
|
140
|
+
if (testScript) {
|
|
141
|
+
runStep('test-index', 'node', [testScript]);
|
|
142
|
+
} else {
|
|
143
|
+
log('SKIP test-index (script not found)');
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
log('SKIP test-index (disabled in moflo.yaml)');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 4. Patterns indexer
|
|
150
|
+
if (isIndexEnabled('patterns')) {
|
|
151
|
+
const patternsScript = resolveBin('flo-patterns', 'index-patterns.mjs');
|
|
152
|
+
if (patternsScript) {
|
|
153
|
+
runStep('patterns-index', 'node', [patternsScript]);
|
|
154
|
+
} else {
|
|
155
|
+
log('SKIP patterns-index (script not found)');
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
log('SKIP patterns-index (disabled in moflo.yaml)');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 5. Pretrain (extracts patterns from repository)
|
|
162
|
+
const localCli = getLocalCliPath();
|
|
163
|
+
if (localCli) {
|
|
164
|
+
runStep('pretrain', 'node', [localCli, 'hooks', 'pretrain']);
|
|
165
|
+
} else {
|
|
166
|
+
log('SKIP pretrain (CLI not found)');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 6. HNSW rebuild — MUST run last, after all writes are committed (#81)
|
|
170
|
+
if (localCli) {
|
|
171
|
+
runStep('hnsw-rebuild', 'node', [localCli, 'memory', 'rebuild', '--force']);
|
|
172
|
+
} else {
|
|
173
|
+
log('SKIP hnsw-rebuild (CLI not found)');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
177
|
+
log(`Sequential indexing chain complete (${totalElapsed}s)`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
main().catch(err => {
|
|
181
|
+
log(`FATAL: ${err.message}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
});
|
|
@@ -819,8 +819,17 @@ if (!specificFile) {
|
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
821
|
|
|
822
|
-
// Write changes back to disk
|
|
822
|
+
// Write changes back to disk
|
|
823
823
|
if (docsIndexed > 0 || chunksIndexed > 0 || staleRemoved > 0) saveDb(db);
|
|
824
|
+
|
|
825
|
+
// Check for entries missing embeddings (e.g. prior background run failed)
|
|
826
|
+
let missingEmbeddings = 0;
|
|
827
|
+
{
|
|
828
|
+
const stmt = db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = ? AND (embedding IS NULL OR embedding = '')`);
|
|
829
|
+
stmt.bind([NAMESPACE]);
|
|
830
|
+
if (stmt.step()) missingEmbeddings = stmt.getAsObject().cnt;
|
|
831
|
+
stmt.free();
|
|
832
|
+
}
|
|
824
833
|
db.close();
|
|
825
834
|
|
|
826
835
|
console.log('');
|
|
@@ -840,9 +849,13 @@ log(` • Hierarchical links (h2 -> h3 parent/children)`);
|
|
|
840
849
|
log(` • Context overlap: ${overlapPercent}% (contextBefore/contextAfter)`);
|
|
841
850
|
log('═══════════════════════════════════════════════════════════');
|
|
842
851
|
|
|
843
|
-
// Generate embeddings for new entries
|
|
852
|
+
// Generate embeddings for new entries or backfill missing ones
|
|
844
853
|
// Runs in BACKGROUND to avoid blocking startup
|
|
845
|
-
|
|
854
|
+
const needsEmbeddings = (docsIndexed > 0 || chunksIndexed > 0 || missingEmbeddings > 0);
|
|
855
|
+
if (!skipEmbeddings && needsEmbeddings) {
|
|
856
|
+
if (missingEmbeddings > 0 && docsIndexed === 0 && chunksIndexed === 0) {
|
|
857
|
+
log(`${missingEmbeddings} entries missing embeddings — backfilling...`);
|
|
858
|
+
}
|
|
846
859
|
console.log('');
|
|
847
860
|
log('Spawning embedding generation in background...');
|
|
848
861
|
|
|
@@ -153,6 +153,15 @@ function countNamespace(db) {
|
|
|
153
153
|
return count;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function countMissingEmbeddings(db) {
|
|
157
|
+
const stmt = db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = ? AND (embedding IS NULL OR embedding = '')`);
|
|
158
|
+
stmt.bind([NAMESPACE]);
|
|
159
|
+
let count = 0;
|
|
160
|
+
if (stmt.step()) count = stmt.getAsObject().cnt;
|
|
161
|
+
stmt.free();
|
|
162
|
+
return count;
|
|
163
|
+
}
|
|
164
|
+
|
|
156
165
|
// ---------------------------------------------------------------------------
|
|
157
166
|
// Test directory discovery
|
|
158
167
|
// ---------------------------------------------------------------------------
|
|
@@ -618,9 +627,15 @@ async function main() {
|
|
|
618
627
|
if (isUnchanged(currentHash)) {
|
|
619
628
|
const db = await getDb();
|
|
620
629
|
const count = countNamespace(db);
|
|
630
|
+
const missing = countMissingEmbeddings(db);
|
|
621
631
|
db.close();
|
|
622
632
|
if (count > 0) {
|
|
623
|
-
|
|
633
|
+
if (missing > 0 && !skipEmbeddings) {
|
|
634
|
+
log(`File list unchanged but ${missing}/${count} entries missing embeddings — generating...`);
|
|
635
|
+
await runEmbeddings();
|
|
636
|
+
} else {
|
|
637
|
+
log(`Skipping — file list unchanged (${count} chunks in DB, hash ${currentHash.slice(0, 12)}...)`);
|
|
638
|
+
}
|
|
624
639
|
return;
|
|
625
640
|
}
|
|
626
641
|
log('File list unchanged but no chunks in DB — forcing regeneration');
|
|
@@ -683,24 +698,28 @@ async function main() {
|
|
|
683
698
|
|
|
684
699
|
// 7. Generate embeddings (inline, like code-map)
|
|
685
700
|
if (!skipEmbeddings && allChunks.length > 0) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
701
|
+
await runEmbeddings();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
async function runEmbeddings() {
|
|
706
|
+
const embedCandidates = [
|
|
707
|
+
resolve(dirname(fileURLToPath(import.meta.url)), 'build-embeddings.mjs'),
|
|
708
|
+
resolve(projectRoot, '.claude/scripts/build-embeddings.mjs'),
|
|
709
|
+
];
|
|
710
|
+
const embedScript = embedCandidates.find(p => existsSync(p));
|
|
711
|
+
if (!embedScript) return;
|
|
712
|
+
|
|
713
|
+
log('Generating embeddings for tests...');
|
|
714
|
+
try {
|
|
715
|
+
execSync(`node "${embedScript}" --namespace tests`, {
|
|
716
|
+
cwd: projectRoot,
|
|
717
|
+
stdio: 'inherit',
|
|
718
|
+
timeout: 120000,
|
|
719
|
+
windowsHide: true,
|
|
720
|
+
});
|
|
721
|
+
} catch (err) {
|
|
722
|
+
log(`Warning: embedding generation failed: ${err.message?.split('\n')[0]}`);
|
|
704
723
|
}
|
|
705
724
|
}
|
|
706
725
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared dependency resolver for moflo bin scripts.
|
|
3
|
+
* Resolves packages from moflo's own node_modules (not the consuming project's).
|
|
4
|
+
* On Windows, converts native paths to file:// URLs required by ESM import().
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createRequire } from 'module';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
9
|
+
|
|
10
|
+
const __require = createRequire(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
export function mofloResolveURL(specifier) {
|
|
13
|
+
return pathToFileURL(__require.resolve(specifier)).href;
|
|
14
|
+
}
|