moflo 4.8.42 → 4.8.44

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.
@@ -27,7 +27,22 @@ import { createProcessManager } from './lib/process-manager.mjs';
27
27
 
28
28
  const __filename = fileURLToPath(import.meta.url);
29
29
  const __dirname = dirname(__filename);
30
- const projectRoot = resolve(__dirname, '../..');
30
+
31
+ // Detect project root by walking up from cwd to find package.json.
32
+ // IMPORTANT: Do NOT use resolve(__dirname, '..') or '../..' — this script lives
33
+ // in bin/ during development but gets synced to .claude/scripts/ in consumer
34
+ // projects, so __dirname-relative paths break. findProjectRoot() works everywhere.
35
+ function findProjectRoot() {
36
+ let dir = process.cwd();
37
+ const root = resolve(dir, '/');
38
+ while (dir !== root) {
39
+ if (existsSync(resolve(dir, 'package.json'))) return dir;
40
+ dir = dirname(dir);
41
+ }
42
+ return process.cwd();
43
+ }
44
+
45
+ const projectRoot = findProjectRoot();
31
46
  const logFile = resolve(projectRoot, '.swarm/hooks.log');
32
47
  const pm = createProcessManager(projectRoot);
33
48
 
@@ -260,21 +275,14 @@ async function main() {
260
275
  case 'session-start': {
261
276
  // Start daemon quietly in background (no DB writes)
262
277
  runDaemonStartBackground();
263
- // Run pretrain in background (writes to patterns namespace, fast)
264
- runBackgroundPretrain();
278
+ // Initialize embeddings engine (must run before indexers that generate embeddings)
279
+ runEmbeddingsInitBackground();
265
280
  // Run all DB-writing indexers SEQUENTIALLY in a single background process.
266
- // This avoids sql.js last-write-wins concurrency (issue #78).
267
- // Chain: guidance code-map tests HNSW rebuild (must be last).
268
- const indexAllScript = resolve(__dirname, 'index-all.mjs');
269
- if (existsSync(indexAllScript)) {
270
- spawnWindowless('node', [indexAllScript], 'sequential indexing chain');
271
- } else {
272
- // Fallback to parallel if index-all.mjs not available
273
- runIndexGuidanceBackground();
274
- runCodeMapBackground();
275
- runTestIndexBackground();
276
- runHNSWRebuildBackground();
277
- }
281
+ // This avoids sql.js last-write-wins concurrency (#78) and ensures
282
+ // HNSW rebuild runs after all indexers finish (#81).
283
+ // Chain: guidance code-map → tests → patterns → pretrain → HNSW rebuild.
284
+ spawnWindowless('node', [resolve(__dirname, 'index-all.mjs')], 'sequential indexing chain');
285
+ // Neural patterns now loaded by moflo core routing — no external patching.
278
286
  break;
279
287
  }
280
288
 
@@ -469,55 +477,6 @@ function runIndexGuidanceBackground(specificFile = null) {
469
477
  spawnWindowless('node', indexArgs, desc);
470
478
  }
471
479
 
472
- // Run structural code map generator in background (non-blocking)
473
- function runCodeMapBackground() {
474
- // Check auto_index.code_map flag in moflo.yaml (default: true)
475
- const yamlPath = resolve(projectRoot, 'moflo.yaml');
476
- if (existsSync(yamlPath)) {
477
- try {
478
- const content = readFileSync(yamlPath, 'utf-8');
479
- const match = content.match(/auto_index:\s*\n(?:.*\n)*?\s+code_map:\s*(true|false)/);
480
- if (match && match[1] === 'false') {
481
- log('info', 'Code map generation disabled (auto_index.code_map: false)');
482
- return;
483
- }
484
- } catch { /* ignore, proceed with indexing */ }
485
- }
486
-
487
- const codeMapScript = resolveBinOrLocal('flo-codemap', 'generate-code-map.mjs');
488
-
489
- if (!codeMapScript) {
490
- log('warn', 'Code map generator not found (checked npm bin + .claude/scripts/)');
491
- return;
492
- }
493
-
494
- spawnWindowless('node', [codeMapScript], 'background code map generation');
495
- }
496
-
497
- // Run test file indexer in background (non-blocking)
498
- function runTestIndexBackground() {
499
- // Check auto_index.tests flag in moflo.yaml (default: true)
500
- const yamlPath = resolve(projectRoot, 'moflo.yaml');
501
- if (existsSync(yamlPath)) {
502
- try {
503
- const content = readFileSync(yamlPath, 'utf-8');
504
- const match = content.match(/auto_index:\s*\n(?:.*\n)*?\s+tests:\s*(true|false)/);
505
- if (match && match[1] === 'false') {
506
- log('info', 'Test indexing disabled (auto_index.tests: false)');
507
- return;
508
- }
509
- } catch { /* ignore, proceed with indexing */ }
510
- }
511
-
512
- const testIndexScript = resolveBinOrLocal('flo-testmap', 'index-tests.mjs');
513
-
514
- if (!testIndexScript) {
515
- log('info', 'Test indexer not found (checked npm bin + .claude/scripts/)');
516
- return;
517
- }
518
-
519
- spawnWindowless('node', [testIndexScript], 'background test indexing');
520
- }
521
480
 
522
481
  // Run ReasoningBank + MicroLoRA training + EWC++ consolidation in background (non-blocking)
523
482
  function runBackgroundTraining() {
@@ -625,29 +584,19 @@ function runDaemonStartBackground() {
625
584
  spawnWindowless('node', [localCli, 'daemon', 'start', '--quiet'], 'daemon');
626
585
  }
627
586
 
628
- // Run pretrain in background on session start (non-blocking)
629
- function runBackgroundPretrain() {
630
- const localCli = getLocalCliPath();
631
- if (!localCli) {
632
- log('warn', 'Local CLI not found, skipping background pretrain');
633
- return;
634
- }
635
587
 
636
- spawnWindowless('node', [localCli, 'hooks', 'pretrain'], 'background pretrain');
637
- }
638
-
639
- // Force HNSW rebuild in background to ensure all processes use identical fresh index
640
- // This fixes the issue where spawned agents return different search results than CLI/MCP
641
- function runHNSWRebuildBackground() {
588
+ // Initialize embeddings ONNX engine on session start (non-blocking)
589
+ function runEmbeddingsInitBackground() {
642
590
  const localCli = getLocalCliPath();
643
591
  if (!localCli) {
644
- log('warn', 'Local CLI not found, skipping HNSW rebuild');
592
+ log('warn', 'Local CLI not found, skipping embeddings init');
645
593
  return;
646
594
  }
647
595
 
648
- spawnWindowless('node', [localCli, 'memory', 'rebuild', '--force'], 'HNSW rebuild');
596
+ spawnWindowless('node', [localCli, 'embeddings', 'init'], 'embeddings init');
649
597
  }
650
598
 
599
+
651
600
  // Neural pattern application — now handled by moflo core routing (learned patterns
652
601
  // loaded from routing-outcomes.json by hooks-tools.ts getSemanticRouter).
653
602
  // No external patch script needed.
@@ -1,127 +1,193 @@
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 committed.
7
- *
8
- * Spawned as a single detached background process by hooks.mjs session-start.
9
- */
10
-
11
- import { existsSync, appendFileSync } from 'fs';
12
- import { resolve, dirname } from 'path';
13
- import { fileURLToPath } from 'url';
14
- import { execFileSync } from 'child_process';
15
-
16
- const __dirname = dirname(fileURLToPath(import.meta.url));
17
- const projectRoot = resolve(__dirname, '../..');
18
- const LOG_PATH = resolve(projectRoot, '.claude/hooks.log');
19
-
20
- function log(msg) {
21
- const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
22
- const line = `[${ts}] [index-all] ${msg}\n`;
23
- try { appendFileSync(LOG_PATH, line); } catch { /* ignore */ }
24
- }
25
-
26
- function resolveBin(binName, localScript) {
27
- const mofloScript = resolve(projectRoot, 'node_modules/moflo/bin', localScript);
28
- if (existsSync(mofloScript)) return mofloScript;
29
- const npmBin = resolve(projectRoot, 'node_modules/.bin', binName);
30
- if (existsSync(npmBin)) return npmBin;
31
- const localPath = resolve(projectRoot, '.claude/scripts', localScript);
32
- if (existsSync(localPath)) return localPath;
33
- return null;
34
- }
35
-
36
- function getLocalCliPath() {
37
- const paths = [
38
- resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js'),
39
- resolve(projectRoot, 'node_modules/moflo/bin/cli.js'),
40
- resolve(projectRoot, 'node_modules/.bin/flo'),
41
- resolve(projectRoot, 'src/@claude-flow/cli/bin/cli.js'),
42
- ];
43
- for (const p of paths) {
44
- if (existsSync(p)) return p;
45
- }
46
- return null;
47
- }
48
-
49
- function runStep(label, cmd, args, timeoutMs = 120_000) {
50
- const start = Date.now();
51
- log(`START ${label}`);
52
- try {
53
- execFileSync(cmd, args, {
54
- cwd: projectRoot,
55
- timeout: timeoutMs,
56
- stdio: 'ignore',
57
- windowsHide: true,
58
- });
59
- const elapsed = ((Date.now() - start) / 1000).toFixed(1);
60
- log(`DONE ${label} (${elapsed}s)`);
61
- return true;
62
- } catch (err) {
63
- const elapsed = ((Date.now() - start) / 1000).toFixed(1);
64
- log(`FAIL ${label} (${elapsed}s): ${err.message?.split('\n')[0] || 'unknown'}`);
65
- return false;
66
- }
67
- }
68
-
69
- async function main() {
70
- const startTime = Date.now();
71
- log('Sequential indexing chain started');
72
-
73
- // 1. Guidance indexer
74
- const guidanceScript = resolveBin('flo-index', 'index-guidance.mjs');
75
- if (guidanceScript) {
76
- runStep('guidance-index', 'node', [guidanceScript]);
77
- } else {
78
- log('SKIP guidance-index (script not found)');
79
- }
80
-
81
- // 2. Code map generator (the big one — ~22s)
82
- const codeMapScript = resolveBin('flo-codemap', 'generate-code-map.mjs');
83
- if (codeMapScript) {
84
- runStep('code-map', 'node', [codeMapScript], 180_000);
85
- } else {
86
- log('SKIP code-map (script not found)');
87
- }
88
-
89
- // 3. Test indexer
90
- const testScript = resolveBin('flo-testmap', 'index-tests.mjs');
91
- if (testScript) {
92
- runStep('test-index', 'node', [testScript]);
93
- } else {
94
- log('SKIP test-index (script not found)');
95
- }
96
-
97
- // 4. Patterns indexer
98
- const patternsScript = resolveBin('flo-patterns', 'index-patterns.mjs');
99
- if (patternsScript) {
100
- runStep('patterns-index', 'node', [patternsScript]);
101
- } else {
102
- log('SKIP patterns-index (script not found)');
103
- }
104
-
105
- // 5. Pretrain (extracts patterns from repository)
106
- const localCli = getLocalCliPath();
107
- if (localCli) {
108
- runStep('pretrain', 'node', [localCli, 'hooks', 'pretrain']);
109
- } else {
110
- log('SKIP pretrain (CLI not found)');
111
- }
112
-
113
- // 6. HNSW rebuild — MUST run last, after all writes are committed (#81)
114
- if (localCli) {
115
- runStep('hnsw-rebuild', 'node', [localCli, 'memory', 'rebuild', '--force']);
116
- } else {
117
- log('SKIP hnsw-rebuild (CLI not found)');
118
- }
119
-
120
- const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
121
- log(`Sequential indexing chain complete (${totalElapsed}s)`);
122
- }
123
-
124
- main().catch(err => {
125
- log(`FATAL: ${err.message}`);
126
- process.exit(1);
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, '--no-embeddings']);
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, '--no-embeddings'], 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, '--no-embeddings']);
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. Build embeddings — single pass for ALL namespaces, after all indexers finish.
170
+ // Individual indexers are called with --no-embeddings to prevent background
171
+ // embedding spawns that race with this chain (sql.js last-write-wins).
172
+ const embeddingsScript = resolveBin('flo-embeddings', 'build-embeddings.mjs');
173
+ if (embeddingsScript) {
174
+ runStep('build-embeddings', 'node', [embeddingsScript], 300_000);
175
+ } else {
176
+ log('SKIP build-embeddings (script not found)');
177
+ }
178
+
179
+ // 7. HNSW rebuild — MUST run last, after all writes are committed (#81)
180
+ if (localCli) {
181
+ runStep('hnsw-rebuild', 'node', [localCli, 'memory', 'rebuild', '--force']);
182
+ } else {
183
+ log('SKIP hnsw-rebuild (CLI not found)');
184
+ }
185
+
186
+ const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
187
+ log(`Sequential indexing chain complete (${totalElapsed}s)`);
188
+ }
189
+
190
+ main().catch(err => {
191
+ log(`FATAL: ${err.message}`);
192
+ process.exit(1);
193
+ });
@@ -106,7 +106,18 @@ function loadGuidanceDirs() {
106
106
  ? bundledShippedDir
107
107
  : resolve(mofloRoot, '.claude/guidance');
108
108
  const projectGuidanceDir = resolve(projectRoot, '.claude/guidance');
109
+
110
+ // Detect self-reference: if the project IS the moflo package, skip bundled scan
111
+ // to avoid double-indexing the same files under two prefixes.
112
+ let isSelfRef = false;
113
+ try {
114
+ const projPkg = JSON.parse(readFileSync(resolve(projectRoot, 'package.json'), 'utf-8'));
115
+ const mofloPkg = JSON.parse(readFileSync(resolve(mofloRoot, 'package.json'), 'utf-8'));
116
+ isSelfRef = projPkg.name === mofloPkg.name;
117
+ } catch { /* ignore — defaults to false */ }
118
+
109
119
  if (
120
+ !isSelfRef &&
110
121
  existsSync(bundledGuidanceDir) &&
111
122
  resolve(bundledGuidanceDir) !== resolve(projectGuidanceDir) &&
112
123
  resolve(bundledGuidanceDir) !== resolve(projectGuidanceDir, 'shipped')
@@ -819,8 +830,17 @@ if (!specificFile) {
819
830
  }
820
831
  }
821
832
 
822
- // Write changes back to disk and close
833
+ // Write changes back to disk
823
834
  if (docsIndexed > 0 || chunksIndexed > 0 || staleRemoved > 0) saveDb(db);
835
+
836
+ // Check for entries missing embeddings (e.g. prior background run failed)
837
+ let missingEmbeddings = 0;
838
+ {
839
+ const stmt = db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = ? AND (embedding IS NULL OR embedding = '')`);
840
+ stmt.bind([NAMESPACE]);
841
+ if (stmt.step()) missingEmbeddings = stmt.getAsObject().cnt;
842
+ stmt.free();
843
+ }
824
844
  db.close();
825
845
 
826
846
  console.log('');
@@ -840,9 +860,13 @@ log(` • Hierarchical links (h2 -> h3 parent/children)`);
840
860
  log(` • Context overlap: ${overlapPercent}% (contextBefore/contextAfter)`);
841
861
  log('═══════════════════════════════════════════════════════════');
842
862
 
843
- // Generate embeddings for new entries (unless skipped or nothing changed)
863
+ // Generate embeddings for new entries or backfill missing ones
844
864
  // Runs in BACKGROUND to avoid blocking startup
845
- if (!skipEmbeddings && (docsIndexed > 0 || chunksIndexed > 0)) {
865
+ const needsEmbeddings = (docsIndexed > 0 || chunksIndexed > 0 || missingEmbeddings > 0);
866
+ if (!skipEmbeddings && needsEmbeddings) {
867
+ if (missingEmbeddings > 0 && docsIndexed === 0 && chunksIndexed === 0) {
868
+ log(`${missingEmbeddings} entries missing embeddings — backfilling...`);
869
+ }
846
870
  console.log('');
847
871
  log('Spawning embedding generation in background...');
848
872