moflo 4.8.0 → 4.8.2

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 (38) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.claude/workflow-state.json +3 -7
  3. package/README.md +18 -3
  4. package/bin/build-embeddings.mjs +59 -3
  5. package/bin/generate-code-map.mjs +3 -1
  6. package/bin/index-guidance.mjs +3 -1
  7. package/bin/lib/moflo-resolve.mjs +14 -0
  8. package/bin/semantic-search.mjs +10 -5
  9. package/bin/session-start-launcher.mjs +28 -0
  10. package/package.json +6 -6
  11. package/src/@claude-flow/cli/dist/src/appliance/ruvllm-bridge.js +3 -7
  12. package/src/@claude-flow/cli/dist/src/commands/diagnose.d.ts +16 -0
  13. package/src/@claude-flow/cli/dist/src/commands/diagnose.js +503 -0
  14. package/src/@claude-flow/cli/dist/src/commands/doctor.js +116 -1
  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/index.d.ts +1 -0
  18. package/src/@claude-flow/cli/dist/src/commands/index.js +7 -0
  19. package/src/@claude-flow/cli/dist/src/commands/mcp.js +38 -22
  20. package/src/@claude-flow/cli/dist/src/commands/memory.js +167 -3
  21. package/src/@claude-flow/cli/dist/src/commands/neural.js +10 -5
  22. package/src/@claude-flow/cli/dist/src/index.js +12 -0
  23. package/src/@claude-flow/cli/dist/src/init/moflo-init.js +49 -0
  24. package/src/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +2 -2
  25. package/src/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +2 -1
  26. package/src/@claude-flow/cli/dist/src/memory/memory-bridge.js +5 -1
  27. package/src/@claude-flow/cli/dist/src/memory/memory-initializer.js +29 -24
  28. package/src/@claude-flow/cli/dist/src/ruvector/ast-analyzer.js +2 -1
  29. package/src/@claude-flow/cli/dist/src/ruvector/coverage-router.js +2 -1
  30. package/src/@claude-flow/cli/dist/src/ruvector/diff-classifier.js +2 -1
  31. package/src/@claude-flow/cli/dist/src/ruvector/enhanced-model-router.js +3 -3
  32. package/src/@claude-flow/cli/dist/src/ruvector/index.js +6 -13
  33. package/src/@claude-flow/cli/dist/src/ruvector/q-learning-router.js +4 -1
  34. package/src/@claude-flow/cli/dist/src/services/learning-service.js +2 -1
  35. package/src/@claude-flow/cli/dist/src/services/moflo-require.d.ts +34 -0
  36. package/src/@claude-flow/cli/dist/src/services/moflo-require.js +67 -0
  37. package/src/@claude-flow/cli/dist/src/services/ruvector-training.js +8 -6
  38. package/src/@claude-flow/cli/package.json +6 -6
@@ -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
 
@@ -122,7 +124,11 @@ npx flo memory code-map # Index your code structure
122
124
  npx flo doctor # Verify everything works
123
125
  ```
124
126
 
125
- Both indexes run automatically at session start after this, so you only need to run them manually on first setup or after major structural changes.
127
+ Both indexes run automatically at session start after this, so you only need to run them manually on first setup or after major structural changes. To reindex everything at once:
128
+
129
+ ```bash
130
+ npx flo memory refresh # Reindex all content, rebuild embeddings, cleanup, vacuum
131
+ ```
126
132
 
127
133
  ## The `/flo` Skill
128
134
 
@@ -215,6 +221,7 @@ flo memory search -q "auth patterns" # Semantic search
215
221
  flo memory index-guidance # Index guidance docs
216
222
  flo memory code-map # Index code structure
217
223
  flo memory rebuild-index # Regenerate all embeddings
224
+ flo memory refresh # Reindex all + rebuild + cleanup + vacuum
218
225
  flo memory stats # Show statistics
219
226
  ```
220
227
 
@@ -236,11 +243,19 @@ flo gate prompt-reminder # Context bracket tracking
236
243
  flo gate session-reset # Reset workflow state
237
244
  ```
238
245
 
246
+ ### Diagnostics
247
+
248
+ ```bash
249
+ flo doctor # Quick health check (environment, deps, config)
250
+ flo diagnose # Full integration test (memory, swarm, hive, hooks, neural)
251
+ flo diagnose --suite memory # Run only memory tests
252
+ flo diagnose --json # JSON output for CI/automation
253
+ ```
254
+
239
255
  ### System
240
256
 
241
257
  ```bash
242
258
  flo init # Initialize project (one-time setup)
243
- flo doctor # Health check
244
259
  flo --version # Show version
245
260
  ```
246
261
 
@@ -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
 
@@ -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;
@@ -120,6 +120,20 @@ try {
120
120
  }
121
121
  }
122
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
+
123
137
  // Write version stamp
124
138
  try {
125
139
  const cfDir = resolve(projectRoot, '.claude-flow');
@@ -132,6 +146,20 @@ try {
132
146
  // Non-fatal — scripts will still work, just may be stale
133
147
  }
134
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
+
135
163
  // ── 4. Spawn background tasks ───────────────────────────────────────────────
136
164
  const localCli = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js');
137
165
  const hasLocalCli = existsSync(localCli);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moflo",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
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.1",
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
@@ -0,0 +1,16 @@
1
+ /**
2
+ * V3 CLI Diagnose Command
3
+ * Full integration test suite — runs non-destructively in the destination project.
4
+ *
5
+ * Unlike `doctor` (which checks environment health), `diagnose` exercises
6
+ * every subsystem end-to-end: memory CRUD, swarm lifecycle, hive-mind,
7
+ * task management, hooks, config, neural, and init idempotency.
8
+ *
9
+ * All test data is cleaned up after each test — no code or state is left behind.
10
+ *
11
+ * Created with motailz.com
12
+ */
13
+ import type { Command } from '../types.js';
14
+ export declare const diagnoseCommand: Command;
15
+ export default diagnoseCommand;
16
+ //# sourceMappingURL=diagnose.d.ts.map