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.
- package/.claude/settings.local.json +4 -1
- package/.claude/workflow-state.json +3 -7
- package/README.md +3 -1
- package/bin/build-embeddings.mjs +59 -3
- package/bin/generate-code-map.mjs +3 -1
- package/bin/hooks.mjs +23 -20
- package/bin/index-guidance.mjs +3 -1
- package/bin/lib/moflo-resolve.mjs +14 -0
- package/bin/semantic-search.mjs +10 -5
- package/bin/session-start-launcher.mjs +116 -3
- package/package.json +6 -6
- package/src/@claude-flow/cli/dist/src/appliance/ruvllm-bridge.js +3 -7
- package/src/@claude-flow/cli/dist/src/commands/daemon.js +42 -95
- package/src/@claude-flow/cli/dist/src/commands/doctor.js +127 -6
- package/src/@claude-flow/cli/dist/src/commands/embeddings.js +4 -3
- package/src/@claude-flow/cli/dist/src/commands/hooks.js +3 -2
- package/src/@claude-flow/cli/dist/src/commands/mcp.js +38 -22
- package/src/@claude-flow/cli/dist/src/commands/memory.js +2 -1
- package/src/@claude-flow/cli/dist/src/commands/neural.js +10 -5
- package/src/@claude-flow/cli/dist/src/config/moflo-config.d.ts +5 -0
- package/src/@claude-flow/cli/dist/src/config/moflo-config.js +16 -0
- package/src/@claude-flow/cli/dist/src/index.js +12 -0
- package/src/@claude-flow/cli/dist/src/init/executor.js +74 -0
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +49 -0
- package/src/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +2 -2
- package/src/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +2 -1
- package/src/@claude-flow/cli/dist/src/memory/memory-bridge.js +5 -1
- package/src/@claude-flow/cli/dist/src/memory/memory-initializer.js +29 -24
- package/src/@claude-flow/cli/dist/src/ruvector/ast-analyzer.js +2 -1
- package/src/@claude-flow/cli/dist/src/ruvector/coverage-router.js +2 -1
- package/src/@claude-flow/cli/dist/src/ruvector/diff-classifier.js +2 -1
- package/src/@claude-flow/cli/dist/src/ruvector/enhanced-model-router.js +3 -3
- package/src/@claude-flow/cli/dist/src/ruvector/index.js +6 -13
- package/src/@claude-flow/cli/dist/src/ruvector/q-learning-router.js +4 -1
- package/src/@claude-flow/cli/dist/src/services/daemon-lock.d.ts +39 -0
- package/src/@claude-flow/cli/dist/src/services/daemon-lock.js +213 -0
- package/src/@claude-flow/cli/dist/src/services/learning-service.js +2 -1
- package/src/@claude-flow/cli/dist/src/services/moflo-require.d.ts +34 -0
- package/src/@claude-flow/cli/dist/src/services/moflo-require.js +67 -0
- package/src/@claude-flow/cli/dist/src/services/ruvector-training.js +8 -6
- package/src/@claude-flow/cli/package.json +6 -6
- package/.claude/helpers/README.md +0 -97
- package/.claude/helpers/adr-compliance.sh +0 -186
- package/.claude/helpers/aggressive-microcompact.mjs +0 -36
- package/.claude/helpers/auto-commit.sh +0 -178
- package/.claude/helpers/checkpoint-manager.sh +0 -251
- package/.claude/helpers/context-persistence-hook.mjs +0 -1979
- package/.claude/helpers/daemon-manager.sh +0 -252
- package/.claude/helpers/ddd-tracker.sh +0 -144
- package/.claude/helpers/github-safe.js +0 -106
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/guidance-hook.sh +0 -13
- package/.claude/helpers/guidance-hooks.sh +0 -102
- package/.claude/helpers/health-monitor.sh +0 -108
- package/.claude/helpers/learning-hooks.sh +0 -329
- package/.claude/helpers/learning-optimizer.sh +0 -127
- package/.claude/helpers/learning-service.mjs +0 -1211
- package/.claude/helpers/memory.cjs +0 -84
- package/.claude/helpers/metrics-db.mjs +0 -492
- package/.claude/helpers/patch-aggressive-prune.mjs +0 -184
- package/.claude/helpers/pattern-consolidator.sh +0 -86
- package/.claude/helpers/perf-worker.sh +0 -160
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/router.cjs +0 -62
- package/.claude/helpers/security-scanner.sh +0 -127
- package/.claude/helpers/session.cjs +0 -125
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +0 -189
- package/.claude/helpers/swarm-comms.sh +0 -353
- package/.claude/helpers/swarm-hooks.sh +0 -761
- package/.claude/helpers/swarm-monitor.sh +0 -211
- package/.claude/helpers/sync-v3-metrics.sh +0 -245
- package/.claude/helpers/update-v3-progress.sh +0 -166
- package/.claude/helpers/v3-quick-status.sh +0 -58
- package/.claude/helpers/v3.sh +0 -111
- package/.claude/helpers/validate-v3-config.sh +0 -216
- 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":
|
|
3
|
-
"taskCount":
|
|
4
|
-
"memorySearched":
|
|
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
|
-
- **
|
|
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
|
|
package/bin/build-embeddings.mjs
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
22
22
|
import { resolve, dirname } from 'path';
|
|
23
|
-
import
|
|
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
|
|
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 (!
|
|
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
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
-
|
|
509
|
-
|
|
510
|
+
const localCli = getLocalCliPath();
|
|
511
|
+
if (!localCli) {
|
|
512
|
+
log('warn', 'Local CLI not found, skipping daemon start');
|
|
510
513
|
return;
|
|
511
514
|
}
|
|
512
515
|
|
package/bin/index-guidance.mjs
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/bin/semantic-search.mjs
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
import { existsSync, readFileSync } from 'fs';
|
|
18
18
|
import { resolve, dirname } from 'path';
|
|
19
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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.
|
|
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
|
-
// ──
|
|
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.
|
|
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.
|
|
66
|
-
"sql.js": "^1.
|
|
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.
|
|
84
|
+
"@types/node": "^20.19.37",
|
|
85
85
|
"eslint": "^8.0.0",
|
|
86
|
-
"moflo": "^4.
|
|
86
|
+
"moflo": "^4.8.0",
|
|
87
87
|
"tsx": "^4.21.0",
|
|
88
|
-
"typescript": "^5.
|
|
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
|
-
|
|
285
|
-
|
|
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
|