moflo 4.7.8 → 4.8.1

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 (77) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.claude/workflow-state.json +3 -7
  3. package/README.md +3 -1
  4. package/bin/build-embeddings.mjs +59 -3
  5. package/bin/generate-code-map.mjs +3 -1
  6. package/bin/hooks.mjs +23 -20
  7. package/bin/index-guidance.mjs +3 -1
  8. package/bin/lib/moflo-resolve.mjs +14 -0
  9. package/bin/semantic-search.mjs +10 -5
  10. package/bin/session-start-launcher.mjs +116 -3
  11. package/package.json +6 -6
  12. package/src/@claude-flow/cli/dist/src/appliance/ruvllm-bridge.js +3 -7
  13. package/src/@claude-flow/cli/dist/src/commands/daemon.js +42 -95
  14. package/src/@claude-flow/cli/dist/src/commands/doctor.js +127 -6
  15. package/src/@claude-flow/cli/dist/src/commands/embeddings.js +4 -3
  16. package/src/@claude-flow/cli/dist/src/commands/hooks.js +3 -2
  17. package/src/@claude-flow/cli/dist/src/commands/mcp.js +38 -22
  18. package/src/@claude-flow/cli/dist/src/commands/memory.js +2 -1
  19. package/src/@claude-flow/cli/dist/src/commands/neural.js +10 -5
  20. package/src/@claude-flow/cli/dist/src/config/moflo-config.d.ts +5 -0
  21. package/src/@claude-flow/cli/dist/src/config/moflo-config.js +16 -0
  22. package/src/@claude-flow/cli/dist/src/index.js +12 -0
  23. package/src/@claude-flow/cli/dist/src/init/executor.js +74 -0
  24. package/src/@claude-flow/cli/dist/src/init/moflo-init.js +49 -0
  25. package/src/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +2 -2
  26. package/src/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +2 -1
  27. package/src/@claude-flow/cli/dist/src/memory/memory-bridge.js +5 -1
  28. package/src/@claude-flow/cli/dist/src/memory/memory-initializer.js +29 -24
  29. package/src/@claude-flow/cli/dist/src/ruvector/ast-analyzer.js +2 -1
  30. package/src/@claude-flow/cli/dist/src/ruvector/coverage-router.js +2 -1
  31. package/src/@claude-flow/cli/dist/src/ruvector/diff-classifier.js +2 -1
  32. package/src/@claude-flow/cli/dist/src/ruvector/enhanced-model-router.js +3 -3
  33. package/src/@claude-flow/cli/dist/src/ruvector/index.js +6 -13
  34. package/src/@claude-flow/cli/dist/src/ruvector/q-learning-router.js +4 -1
  35. package/src/@claude-flow/cli/dist/src/services/daemon-lock.d.ts +39 -0
  36. package/src/@claude-flow/cli/dist/src/services/daemon-lock.js +213 -0
  37. package/src/@claude-flow/cli/dist/src/services/learning-service.js +2 -1
  38. package/src/@claude-flow/cli/dist/src/services/moflo-require.d.ts +34 -0
  39. package/src/@claude-flow/cli/dist/src/services/moflo-require.js +67 -0
  40. package/src/@claude-flow/cli/dist/src/services/ruvector-training.js +8 -6
  41. package/src/@claude-flow/cli/package.json +6 -6
  42. package/.claude/helpers/README.md +0 -97
  43. package/.claude/helpers/adr-compliance.sh +0 -186
  44. package/.claude/helpers/aggressive-microcompact.mjs +0 -36
  45. package/.claude/helpers/auto-commit.sh +0 -178
  46. package/.claude/helpers/checkpoint-manager.sh +0 -251
  47. package/.claude/helpers/context-persistence-hook.mjs +0 -1979
  48. package/.claude/helpers/daemon-manager.sh +0 -252
  49. package/.claude/helpers/ddd-tracker.sh +0 -144
  50. package/.claude/helpers/github-safe.js +0 -106
  51. package/.claude/helpers/github-setup.sh +0 -28
  52. package/.claude/helpers/guidance-hook.sh +0 -13
  53. package/.claude/helpers/guidance-hooks.sh +0 -102
  54. package/.claude/helpers/health-monitor.sh +0 -108
  55. package/.claude/helpers/learning-hooks.sh +0 -329
  56. package/.claude/helpers/learning-optimizer.sh +0 -127
  57. package/.claude/helpers/learning-service.mjs +0 -1211
  58. package/.claude/helpers/memory.cjs +0 -84
  59. package/.claude/helpers/metrics-db.mjs +0 -492
  60. package/.claude/helpers/patch-aggressive-prune.mjs +0 -184
  61. package/.claude/helpers/pattern-consolidator.sh +0 -86
  62. package/.claude/helpers/perf-worker.sh +0 -160
  63. package/.claude/helpers/quick-start.sh +0 -19
  64. package/.claude/helpers/router.cjs +0 -62
  65. package/.claude/helpers/security-scanner.sh +0 -127
  66. package/.claude/helpers/session.cjs +0 -125
  67. package/.claude/helpers/setup-mcp.sh +0 -18
  68. package/.claude/helpers/standard-checkpoint-hooks.sh +0 -189
  69. package/.claude/helpers/swarm-comms.sh +0 -353
  70. package/.claude/helpers/swarm-hooks.sh +0 -761
  71. package/.claude/helpers/swarm-monitor.sh +0 -211
  72. package/.claude/helpers/sync-v3-metrics.sh +0 -245
  73. package/.claude/helpers/update-v3-progress.sh +0 -166
  74. package/.claude/helpers/v3-quick-status.sh +0 -58
  75. package/.claude/helpers/v3.sh +0 -111
  76. package/.claude/helpers/validate-v3-config.sh +0 -216
  77. package/.claude/helpers/worker-manager.sh +0 -170
@@ -8,7 +8,10 @@
8
8
  "Bash(tasklist.exe /FI \"IMAGENAME eq node.exe\" /V)",
9
9
  "Bash(powershell.exe -NoProfile -Command \"Get-Process node -ErrorAction SilentlyContinue | Select-Object Id,ProcessName,StartTime,CommandLine | Format-Table -AutoSize\")",
10
10
  "Bash(npm version:*)",
11
- "Bash(gh pr:*)"
11
+ "Bash(gh pr:*)",
12
+ "mcp__claude-flow__memory_store",
13
+ "mcp__claude-flow__memory_retrieve",
14
+ "mcp__claude-flow__memory_search"
12
15
  ]
13
16
  }
14
17
  }
@@ -1,9 +1,5 @@
1
1
  {
2
- "tasksCreated": false,
3
- "taskCount": 0,
4
- "memorySearched": false,
5
- "memoryRequired": true,
6
- "interactionCount": 10,
7
- "sessionStart": null,
8
- "lastBlockedAt": "2026-03-21T05:00:01.929Z"
2
+ "tasksCreated": true,
3
+ "taskCount": 1,
4
+ "memorySearched": true
9
5
  }
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  # MoFlo
6
6
 
7
+ **⚠️ MoFlo is experimental software. APIs, commands, and behavior may change without notice.**
8
+
7
9
  **An opinionated fork of [Ruflo/Claude Flow](https://github.com/ruvnet/ruflo), optimized for local development.**
8
10
 
9
11
  MoFlo adds automatic code and guidance cataloging along with memory gating on top of the original Ruflo/Claude Flow orchestration engine. Where the upstream project provides raw building blocks, MoFlo ships opinionated defaults — workflow gates that enforce memory-first patterns, semantic indexing that runs at session start, and learned routing that improves over time — so you get a productive setup from `flo init` without manual tuning.
@@ -30,7 +32,7 @@ MoFlo makes deliberate choices so you don't have to:
30
32
  - **Task registration before agents** — Sub-agents can't spawn until work is tracked. Prevents runaway agent proliferation.
31
33
  - **Learned routing** — Task outcomes feed back into the routing system automatically. No manual configuration needed — it gets smarter with use.
32
34
  - **Incremental indexing** — Guidance and code map indexes run on every session start but skip unchanged files. Fast after the first run.
33
- - **AI client agnostic** Works with any MCP-capable AI client. We develop and test with Claude Code, but the MCP tools, memory system, and hooks are client-independent.
35
+ - **Built for Claude Code, works with others** We develop and test exclusively with Claude Code. The MCP tools, memory system, and hooks are client-independent and should work with any MCP-capable AI client, but Claude Code is the only tested target.
34
36
  - **GitHub-oriented** — The `/flo` skill, PR workflows, and issue tracking are built around GitHub. With Claude's help, you can adapt them to your own issue tracker and source control system.
35
37
  - **Cross-platform** — Forward-slash path normalization, no `sh -c` shell commands, `windowsHide` on all spawn calls.
36
38
 
@@ -20,7 +20,8 @@
20
20
 
21
21
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
22
22
  import { resolve, dirname } from 'path';
23
- import initSqlJs from 'sql.js';
23
+ import { mofloResolveURL } from './lib/moflo-resolve.mjs';
24
+ const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
24
25
 
25
26
  function findProjectRoot() {
26
27
  let dir = process.cwd();
@@ -227,7 +228,7 @@ async function loadTransformersModel() {
227
228
  log('Attempting to load Transformers.js neural model...');
228
229
 
229
230
  try {
230
- const { env, pipeline: createPipeline } = await import('@xenova/transformers');
231
+ const { env, pipeline: createPipeline } = await import(mofloResolveURL('@xenova/transformers'));
231
232
  env.allowLocalModels = false;
232
233
  env.backends.onnx.wasm.numThreads = 1;
233
234
 
@@ -310,6 +311,25 @@ function updateEmbedding(db, id, embedding, model) {
310
311
  stmt.free();
311
312
  }
312
313
 
314
+ function getNamespaceStats(db) {
315
+ const stmt = db.prepare(`
316
+ SELECT
317
+ namespace,
318
+ COUNT(*) as total,
319
+ SUM(CASE WHEN embedding IS NOT NULL AND embedding != '' AND embedding_model != 'domain-aware-hash-v1' THEN 1 ELSE 0 END) as vectorized,
320
+ SUM(CASE WHEN embedding IS NULL OR embedding = '' THEN 1 ELSE 0 END) as missing,
321
+ SUM(CASE WHEN embedding_model = 'domain-aware-hash-v1' THEN 1 ELSE 0 END) as hash_only
322
+ FROM memory_entries
323
+ WHERE status = 'active'
324
+ GROUP BY namespace
325
+ ORDER BY namespace
326
+ `);
327
+ const results = [];
328
+ while (stmt.step()) results.push(stmt.getAsObject());
329
+ stmt.free();
330
+ return results;
331
+ }
332
+
313
333
  function getEmbeddingStats(db) {
314
334
  const stmtTotal = db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE status = 'active'`);
315
335
  const total = stmtTotal.step() ? stmtTotal.getAsObject() : { cnt: 0 };
@@ -426,7 +446,6 @@ async function main() {
426
446
  }
427
447
  }
428
448
  }
429
- db.close();
430
449
 
431
450
  console.log('');
432
451
  log('═══════════════════════════════════════════════════════════');
@@ -445,7 +464,44 @@ async function main() {
445
464
  log(` - ${m.embedding_model}: ${m.cnt}`);
446
465
  }
447
466
  }
467
+ log('');
468
+
469
+ // Per-namespace health report
470
+ const nsStats = getNamespaceStats(db);
471
+ if (nsStats.length > 0) {
472
+ log(' Namespace Health:');
473
+ log(' ┌─────────────────┬───────┬────────────┬─────────┬───────────┐');
474
+ log(' │ Namespace │ Total │ Vectorized │ Missing │ Hash-Only │');
475
+ log(' ├─────────────────┼───────┼────────────┼─────────┼───────────┤');
476
+ let hasWarnings = false;
477
+ for (const ns of nsStats) {
478
+ const name = String(ns.namespace).padEnd(15);
479
+ const total = String(ns.total).padStart(5);
480
+ const vectorized = String(ns.vectorized).padStart(10);
481
+ const missing = String(ns.missing).padStart(7);
482
+ const hashOnly = String(ns.hash_only).padStart(9);
483
+ const warn = (ns.missing > 0 || ns.hash_only > 0) ? ' ⚠' : ' ';
484
+ log(` │ ${name} │${total} │${vectorized} │${missing} │${hashOnly} │${warn}`);
485
+ if (ns.missing > 0 || ns.hash_only > 0) hasWarnings = true;
486
+ }
487
+ log(' └─────────────────┴───────┴────────────┴─────────┴───────────┘');
488
+ if (hasWarnings) {
489
+ log('');
490
+ log(' ⚠ Some namespaces have entries without Xenova embeddings.');
491
+ log(' Run with --force to re-embed all entries:');
492
+ log(' node node_modules/moflo/bin/build-embeddings.mjs --force');
493
+ if (!useTransformers) {
494
+ log('');
495
+ log(' ⚠ Xenova model not available — using hash fallback.');
496
+ log(' Install @xenova/transformers for neural embeddings:');
497
+ log(' npm install @xenova/transformers');
498
+ }
499
+ }
500
+ }
501
+
448
502
  log('═══════════════════════════════════════════════════════════');
503
+
504
+ db.close();
449
505
  }
450
506
 
451
507
  main().catch(err => {
@@ -30,7 +30,9 @@ import { resolve, dirname, relative, basename, extname } from 'path';
30
30
  import { fileURLToPath } from 'url';
31
31
  import { createHash } from 'crypto';
32
32
  import { execSync, spawn } from 'child_process';
33
- import initSqlJs from 'sql.js';
33
+ import { mofloResolveURL } from './lib/moflo-resolve.mjs';
34
+ const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
35
+
34
36
 
35
37
  const __dirname = dirname(fileURLToPath(import.meta.url));
36
38
 
package/bin/hooks.mjs CHANGED
@@ -306,10 +306,10 @@ async function main() {
306
306
  }
307
307
 
308
308
  case 'daemon-start': {
309
- if (!isDaemonRunning()) {
309
+ if (!isDaemonLockHeld()) {
310
310
  await runClaudeFlow('daemon', ['start', '--quiet']);
311
311
  } else {
312
- log('info', 'Daemon already running, skipping start');
312
+ log('info', 'Daemon already running (lock held), skipping start');
313
313
  }
314
314
  break;
315
315
  }
@@ -479,34 +479,37 @@ function runBackgroundTraining() {
479
479
  spawnWindowless('node', [localCli, 'neural', 'optimize'], 'neural optimize');
480
480
  }
481
481
 
482
- // Check if daemon is already running via PID file.
483
- // Returns true if a live daemon process exists, false otherwise.
484
- function isDaemonRunning() {
485
- const pidFile = resolve(projectRoot, '.claude-flow', 'daemon.pid');
486
- if (existsSync(pidFile)) {
487
- try {
488
- const pid = parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);
489
- if (pid && !isNaN(pid)) {
490
- process.kill(pid, 0); // signal 0 = check if process exists, doesn't kill
491
- return true;
492
- }
493
- } catch {
494
- // Process doesn't exist — stale PID file
482
+ // Check if daemon lock exists fast pre-check to avoid spawning a Node process
483
+ // just to have it bail out via the atomic lock in daemon.ts.
484
+ // Uses daemon.lock (atomic wx-based) instead of the old daemon.pid (TOCTOU-vulnerable).
485
+ function isDaemonLockHeld() {
486
+ const lockFile = resolve(projectRoot, '.claude-flow', 'daemon.lock');
487
+ if (!existsSync(lockFile)) return false;
488
+
489
+ try {
490
+ const data = JSON.parse(readFileSync(lockFile, 'utf-8'));
491
+ if (typeof data.pid === 'number' && data.pid > 0) {
492
+ process.kill(data.pid, 0); // check if alive
493
+ return true;
495
494
  }
495
+ } catch {
496
+ // Dead process or corrupt file — lock is stale
496
497
  }
497
498
  return false;
498
499
  }
499
500
 
500
501
  // Run daemon start in background (non-blocking) — skip if already running
501
502
  function runDaemonStartBackground() {
502
- const localCli = getLocalCliPath();
503
- if (!localCli) {
504
- log('warn', 'Local CLI not found, skipping daemon start');
503
+ // Fast check: if daemon lock is held by a live process, skip spawning entirely.
504
+ // This avoids zombie Node processes from subagents that all fire SessionStart.
505
+ if (isDaemonLockHeld()) {
506
+ log('info', 'Daemon already running (lock held), skipping start');
505
507
  return;
506
508
  }
507
509
 
508
- if (isDaemonRunning()) {
509
- log('info', 'Daemon already running, skipping start');
510
+ const localCli = getLocalCliPath();
511
+ if (!localCli) {
512
+ log('warn', 'Local CLI not found, skipping daemon start');
510
513
  return;
511
514
  }
512
515
 
@@ -25,7 +25,9 @@
25
25
  import { existsSync, readdirSync, readFileSync, statSync, mkdirSync, writeFileSync } from 'fs';
26
26
  import { resolve, dirname, basename, extname } from 'path';
27
27
  import { fileURLToPath } from 'url';
28
- import initSqlJs from 'sql.js';
28
+ import { mofloResolveURL } from './lib/moflo-resolve.mjs';
29
+ const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
30
+
29
31
 
30
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
31
33
 
@@ -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
+ }
@@ -16,7 +16,8 @@
16
16
 
17
17
  import { existsSync, readFileSync } from 'fs';
18
18
  import { resolve, dirname } from 'path';
19
- import initSqlJs from 'sql.js';
19
+ import { mofloResolveURL } from './lib/moflo-resolve.mjs';
20
+ const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
20
21
 
21
22
  function findProjectRoot() {
22
23
  let dir = process.cwd();
@@ -34,6 +35,8 @@ const DB_PATH = resolve(projectRoot, '.swarm/memory.db');
34
35
  const EMBEDDING_DIMS = 384;
35
36
  const EMBEDDING_MODEL_NEURAL = 'Xenova/all-MiniLM-L6-v2';
36
37
  const EMBEDDING_MODEL_HASH = 'domain-aware-hash-v1';
38
+ // 'onnx' is a legacy alias for the Xenova model — treat them as compatible vector spaces
39
+ const NEURAL_ALIASES = new Set([EMBEDDING_MODEL_NEURAL, 'onnx']);
37
40
 
38
41
  // Parse args
39
42
  const args = process.argv.slice(2);
@@ -58,7 +61,7 @@ let useTransformers = false;
58
61
 
59
62
  async function loadTransformersModel() {
60
63
  try {
61
- const { env, pipeline: createPipeline } = await import('@xenova/transformers');
64
+ const { env, pipeline: createPipeline } = await import(mofloResolveURL('@xenova/transformers'));
62
65
  env.allowLocalModels = false;
63
66
  env.backends.onnx.wasm.numThreads = 1;
64
67
 
@@ -251,7 +254,8 @@ async function generateQueryEmbedding(queryText, db) {
251
254
  if (debug) console.error(`[semantic-search] Stored model: ${storedModel}`);
252
255
 
253
256
  // If stored embeddings are neural, try to use neural for query too
254
- if (storedModel === EMBEDDING_MODEL_NEURAL) {
257
+ // Accept both canonical name and legacy 'onnx' tag (both use the same Xenova pipeline)
258
+ if (storedModel === EMBEDDING_MODEL_NEURAL || storedModel === 'onnx') {
255
259
  await loadTransformersModel();
256
260
  if (useTransformers) {
257
261
  const neuralEmb = await generateNeuralEmbedding(queryText);
@@ -325,8 +329,9 @@ async function semanticSearch(queryText, options = {}) {
325
329
  while (stmt.step()) {
326
330
  const entry = stmt.getAsObject();
327
331
  try {
328
- // Skip entries with mismatched embedding model (incompatible vector spaces)
329
- if (entry.embedding_model && entry.embedding_model !== queryModel) continue;
332
+ const storedIsNeural = NEURAL_ALIASES.has(entry.embedding_model);
333
+ const queryIsNeural = NEURAL_ALIASES.has(queryModel);
334
+ if (entry.embedding_model && entry.embedding_model !== queryModel && !(storedIsNeural && queryIsNeural)) continue;
330
335
 
331
336
  const embedding = JSON.parse(entry.embedding);
332
337
  if (!Array.isArray(embedding) || embedding.length !== EMBEDDING_DIMS) continue;
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { spawn } from 'child_process';
11
- import { existsSync } from 'fs';
11
+ import { existsSync, readFileSync, copyFileSync } from 'fs';
12
12
  import { resolve, dirname } from 'path';
13
13
  import { fileURLToPath } from 'url';
14
14
 
@@ -47,7 +47,120 @@ try {
47
47
  // Non-fatal - workflow gate will use defaults
48
48
  }
49
49
 
50
- // ── 3. Spawn background tasks ───────────────────────────────────────────────
50
+ // ── 3. Auto-sync scripts and helpers on version change ───────────────────────
51
+ // Controlled by `auto_update.enabled` in moflo.yaml (default: true).
52
+ // When moflo is upgraded (npm install), scripts and helpers may be stale.
53
+ // Detect version change and sync from source before running hooks.
54
+ let autoUpdateConfig = { enabled: true, scripts: true, helpers: true };
55
+ try {
56
+ const mofloYaml = resolve(projectRoot, 'moflo.yaml');
57
+ if (existsSync(mofloYaml)) {
58
+ const yamlContent = readFileSync(mofloYaml, 'utf-8');
59
+ // Simple YAML parsing for auto_update block (avoids js-yaml dependency)
60
+ const enabledMatch = yamlContent.match(/auto_update:\s*\n\s+enabled:\s*(true|false)/);
61
+ const scriptsMatch = yamlContent.match(/auto_update:\s*\n(?:\s+\w+:.*\n)*?\s+scripts:\s*(true|false)/);
62
+ const helpersMatch = yamlContent.match(/auto_update:\s*\n(?:\s+\w+:.*\n)*?\s+helpers:\s*(true|false)/);
63
+ if (enabledMatch) autoUpdateConfig.enabled = enabledMatch[1] === 'true';
64
+ if (scriptsMatch) autoUpdateConfig.scripts = scriptsMatch[1] === 'true';
65
+ if (helpersMatch) autoUpdateConfig.helpers = helpersMatch[1] === 'true';
66
+ }
67
+ } catch { /* non-fatal — use defaults (all true) */ }
68
+
69
+ try {
70
+ const mofloPkgPath = resolve(projectRoot, 'node_modules/moflo/package.json');
71
+ const versionStampPath = resolve(projectRoot, '.claude-flow', 'moflo-version');
72
+ if (autoUpdateConfig.enabled && existsSync(mofloPkgPath)) {
73
+ const installedVersion = JSON.parse(readFileSync(mofloPkgPath, 'utf-8')).version;
74
+ let cachedVersion = '';
75
+ try { cachedVersion = readFileSync(versionStampPath, 'utf-8').trim(); } catch {}
76
+
77
+ if (installedVersion !== cachedVersion) {
78
+ // Version changed — sync scripts from bin/
79
+ if (autoUpdateConfig.scripts) {
80
+ const binDir = resolve(projectRoot, 'node_modules/moflo/bin');
81
+ const scriptsDir = resolve(projectRoot, '.claude/scripts');
82
+ const scriptFiles = [
83
+ 'hooks.mjs', 'session-start-launcher.mjs', 'index-guidance.mjs',
84
+ 'build-embeddings.mjs', 'generate-code-map.mjs', 'semantic-search.mjs',
85
+ ];
86
+ for (const file of scriptFiles) {
87
+ const src = resolve(binDir, file);
88
+ const dest = resolve(scriptsDir, file);
89
+ if (existsSync(src)) {
90
+ try { copyFileSync(src, dest); } catch { /* non-fatal */ }
91
+ }
92
+ }
93
+ }
94
+
95
+ // Sync helpers from source .claude/helpers/
96
+ if (autoUpdateConfig.helpers) {
97
+ const sourceHelpersDir = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/.claude/helpers');
98
+ const helpersDir = resolve(projectRoot, '.claude/helpers');
99
+ const helperFiles = [
100
+ 'auto-memory-hook.mjs', 'statusline.cjs', 'pre-commit', 'post-commit',
101
+ ];
102
+ for (const file of helperFiles) {
103
+ const src = resolve(sourceHelpersDir, file);
104
+ const dest = resolve(helpersDir, file);
105
+ if (existsSync(src)) {
106
+ try { copyFileSync(src, dest); } catch { /* non-fatal */ }
107
+ }
108
+ }
109
+
110
+ // Also sync generated helpers via upgrade CLI (hook-handler.cjs, gate.cjs, etc.)
111
+ // These are generated, not shipped, so we trigger an upgrade in the background
112
+ const localCli = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js');
113
+ if (existsSync(localCli)) {
114
+ try {
115
+ const proc = spawn('node', [localCli, 'init', '--upgrade', '--quiet'], {
116
+ cwd: projectRoot, stdio: 'ignore', detached: true, windowsHide: true,
117
+ });
118
+ proc.unref();
119
+ } catch { /* non-fatal */ }
120
+ }
121
+ }
122
+
123
+ // Sync guidance bootstrap file (moflo-bootstrap.md)
124
+ // Ensures subagents can read guidance directly from disk
125
+ const bootstrapSrc = resolve(projectRoot, 'node_modules/moflo/.claude/guidance/agent-bootstrap.md');
126
+ const guidanceDir = resolve(projectRoot, '.claude/guidance');
127
+ const bootstrapDest = resolve(guidanceDir, 'moflo-bootstrap.md');
128
+ if (existsSync(bootstrapSrc)) {
129
+ try {
130
+ if (!existsSync(guidanceDir)) mkdirSync(guidanceDir, { recursive: true });
131
+ const header = '<!-- AUTO-GENERATED by moflo session-start. Do not edit — changes will be overwritten. -->\n<!-- Source: node_modules/moflo/.claude/guidance/agent-bootstrap.md -->\n\n';
132
+ const content = readFileSync(bootstrapSrc, 'utf-8');
133
+ writeFileSync(bootstrapDest, header + content);
134
+ } catch { /* non-fatal */ }
135
+ }
136
+
137
+ // Write version stamp
138
+ try {
139
+ const cfDir = resolve(projectRoot, '.claude-flow');
140
+ if (!existsSync(cfDir)) mkdirSync(cfDir, { recursive: true });
141
+ writeFileSync(versionStampPath, installedVersion);
142
+ } catch {}
143
+ }
144
+ }
145
+ } catch {
146
+ // Non-fatal — scripts will still work, just may be stale
147
+ }
148
+
149
+ // ── 3b. Ensure guidance bootstrap file exists (even without version change) ──
150
+ // Subagents need this file on disk for direct reads without memory search.
151
+ try {
152
+ const bootstrapSrc = resolve(projectRoot, 'node_modules/moflo/.claude/guidance/agent-bootstrap.md');
153
+ const guidanceDir = resolve(projectRoot, '.claude/guidance');
154
+ const bootstrapDest = resolve(guidanceDir, 'moflo-bootstrap.md');
155
+ if (existsSync(bootstrapSrc) && !existsSync(bootstrapDest)) {
156
+ if (!existsSync(guidanceDir)) mkdirSync(guidanceDir, { recursive: true });
157
+ const header = '<!-- AUTO-GENERATED by moflo session-start. Do not edit — changes will be overwritten. -->\n<!-- Source: node_modules/moflo/.claude/guidance/agent-bootstrap.md -->\n\n';
158
+ const content = readFileSync(bootstrapSrc, 'utf-8');
159
+ writeFileSync(bootstrapDest, header + content);
160
+ }
161
+ } catch { /* non-fatal */ }
162
+
163
+ // ── 4. Spawn background tasks ───────────────────────────────────────────────
51
164
  const localCli = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js');
52
165
  const hasLocalCli = existsSync(localCli);
53
166
 
@@ -59,5 +172,5 @@ if (existsSync(hooksScript)) {
59
172
 
60
173
  // Patches are now baked into moflo@4.0.0 source — no runtime patching needed.
61
174
 
62
- // ── 4. Done — exit immediately ──────────────────────────────────────────────
175
+ // ── 5. Done — exit immediately ──────────────────────────────────────────────
63
176
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moflo",
3
- "version": "4.7.8",
3
+ "version": "4.8.1",
4
4
  "description": "MoFlo — AI agent orchestration for Claude Code. Forked from ruflo/claude-flow with patches applied to source, plus feature-level orchestration.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -62,8 +62,8 @@
62
62
  "dependencies": {
63
63
  "@ruvector/learning-wasm": "^0.1.29",
64
64
  "js-yaml": "^4.1.1",
65
- "semver": "^7.6.0",
66
- "sql.js": "^1.12.0",
65
+ "semver": "^7.7.4",
66
+ "sql.js": "^1.14.1",
67
67
  "zod": "^3.22.4"
68
68
  },
69
69
  "optionalDependencies": {
@@ -81,11 +81,11 @@
81
81
  },
82
82
  "devDependencies": {
83
83
  "@types/bcrypt": "^5.0.2",
84
- "@types/node": "^20.0.0",
84
+ "@types/node": "^20.19.37",
85
85
  "eslint": "^8.0.0",
86
- "moflo": "^4.7.4",
86
+ "moflo": "^4.8.0",
87
87
  "tsx": "^4.21.0",
88
- "typescript": "^5.0.0",
88
+ "typescript": "^5.9.3",
89
89
  "vitest": "^4.0.0"
90
90
  },
91
91
  "engines": {
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { readdir, stat } from 'node:fs/promises';
16
16
  import { join, extname, basename } from 'node:path';
17
+ import { mofloImport } from '../services/moflo-require.js';
17
18
  const DEFAULT_CONFIG = {
18
19
  modelsDir: './models', defaultModel: '', maxTokens: 512,
19
20
  temperature: 0.7, contextSize: 4096, kvCachePath: '', verbose: false,
@@ -281,12 +282,7 @@ export function getRuvllmBridge(config) {
281
282
  export function resetRuvllmBridge() { instance = null; }
282
283
  /** Check whether @ruvector/core is importable without loading the bridge. */
283
284
  export async function isRuvllmAvailable() {
284
- try {
285
- await import('@ruvector/core');
286
- return true;
287
- }
288
- catch {
289
- return false;
290
- }
285
+ const mod = await mofloImport('@ruvector/core');
286
+ return mod !== null;
291
287
  }
292
288
  //# sourceMappingURL=ruvllm-bridge.js.map