moflo 4.9.9 → 4.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/prompt-hook.mjs +16 -2
- package/.claude/skills/simplify/SKILL.md +82 -0
- package/bin/generate-code-map.mjs +4 -5
- package/bin/hooks.mjs +4 -14
- package/bin/index-all.mjs +2 -10
- package/bin/index-guidance.mjs +5 -7
- package/bin/index-patterns.mjs +7 -9
- package/bin/index-tests.mjs +4 -5
- package/bin/lib/resolve-bin.mjs +62 -0
- package/bin/session-start-launcher.mjs +12 -17
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFileSync } from 'child_process';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
3
4
|
import { resolve } from 'path';
|
|
4
5
|
|
|
5
6
|
// Read stdin JSON from Claude Code
|
|
@@ -70,6 +71,19 @@ if (TEST_HINTS.test(lower)) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// #867 — surface post-install restart notice. File is written by
|
|
75
|
+
// scripts/post-install-notice.mjs and cleared by the SessionStart launcher
|
|
76
|
+
// once the running moflo matches the file's version, so the file's
|
|
77
|
+
// existence at prompt-time is itself the "still needs restart" signal.
|
|
78
|
+
var restartNotice = '';
|
|
79
|
+
try {
|
|
80
|
+
var pendingPath = resolve(projectDir, '.moflo', 'restart-pending.json');
|
|
81
|
+
var pending = JSON.parse(readFileSync(pendingPath, 'utf-8'));
|
|
82
|
+
if (pending && typeof pending.message === 'string' && pending.message.length > 0) {
|
|
83
|
+
restartNotice = pending.message;
|
|
84
|
+
}
|
|
85
|
+
} catch (e) { /* ENOENT or malformed — silent fast-path */ }
|
|
86
|
+
|
|
87
|
+
var parts = [restartNotice, output.trim(), nsHint].filter(Boolean);
|
|
88
|
+
if (parts.length) process.stdout.write(parts.join('\n\n') + '\n');
|
|
75
89
|
process.exit(0);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: simplify
|
|
3
|
+
description: Review changed code for reuse, quality, and efficiency, then fix any issues found. Sizes review effort to the diff — trivial edits get a self-review, substantial edits get parallel agents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /simplify — Adaptive Code Review
|
|
7
|
+
|
|
8
|
+
Review changed code for reuse opportunities, quality issues, and efficiency improvements. **Effort scales with diff size** — a 5-line comment trim doesn't get the same treatment as a 500-line refactor.
|
|
9
|
+
|
|
10
|
+
## Phase 1: Identify changes
|
|
11
|
+
|
|
12
|
+
Run `git diff HEAD` (working tree) and `git diff main...HEAD` (committed) to get the full set of changes since the branch diverged. If on `main` with uncommitted changes, just `git diff HEAD`.
|
|
13
|
+
|
|
14
|
+
Treat the union of staged + unstaged + committed-since-base as the diff to review.
|
|
15
|
+
|
|
16
|
+
## Phase 2: Classify the diff
|
|
17
|
+
|
|
18
|
+
Pick the **smallest tier** the diff genuinely fits. When in doubt, escalate.
|
|
19
|
+
|
|
20
|
+
### TRIVIAL — self-review, no agent spawn
|
|
21
|
+
ALL of these must hold:
|
|
22
|
+
- ≤10 net LOC changed (insertions + deletions, excluding pure whitespace)
|
|
23
|
+
- Single file
|
|
24
|
+
- No logic changes — only comments, formatting, renames of local vars, JSDoc, or string-literal edits
|
|
25
|
+
- No new imports, no new exports, no new function/class declarations
|
|
26
|
+
- No removed safety checks, error handlers, or guards
|
|
27
|
+
|
|
28
|
+
Examples that qualify: trimming a comment, fixing a typo in a log message, renaming a private helper, reformatting a single block.
|
|
29
|
+
Examples that DON'T qualify: changing an `if` condition, reordering function args, deleting a try/catch.
|
|
30
|
+
|
|
31
|
+
### SMALL — single agent, all three categories
|
|
32
|
+
ALL of these must hold:
|
|
33
|
+
- ≤50 net LOC changed
|
|
34
|
+
- ≤2 files
|
|
35
|
+
- No structural changes (no new modules, no API additions/removals, no contract changes)
|
|
36
|
+
|
|
37
|
+
Examples that qualify: extracting a constant, inlining a one-liner, swapping a `for` for a `forEach`, adding one early-return.
|
|
38
|
+
|
|
39
|
+
### NORMAL — three parallel agents (the original flow)
|
|
40
|
+
Anything that doesn't fit TRIVIAL or SMALL. Includes any diff that:
|
|
41
|
+
- Spans 3+ files
|
|
42
|
+
- Adds/removes/renames a public API
|
|
43
|
+
- Changes control flow in a non-trivial way
|
|
44
|
+
- Introduces or removes a dependency
|
|
45
|
+
- Touches `bin/`, hooks, MCP tool handlers, or anything called out in `CLAUDE.md` as critical surface
|
|
46
|
+
|
|
47
|
+
When CLAUDE.md flags a file as critical surface (SessionStart, launcher, hooks, MCP coordinator wiring, swarm/hive-mind), **always escalate to NORMAL** regardless of LOC count. Risk-weighted, not size-weighted.
|
|
48
|
+
|
|
49
|
+
## Phase 3: Run the appropriate review
|
|
50
|
+
|
|
51
|
+
### TRIVIAL: self-review
|
|
52
|
+
Run the same three category checks (reuse / quality / efficiency) yourself, in one pass, against the diff. Most TRIVIAL diffs will be clean — the goal is to confirm, not to fan out. If you find an issue, fix it; otherwise stamp clean. Total budget: ~30 seconds, no Agent calls.
|
|
53
|
+
|
|
54
|
+
### SMALL: one agent
|
|
55
|
+
Launch a SINGLE Agent with subagent_type `reviewer` covering all three categories in one prompt. Pass the diff inline. Budget: ~1 minute.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Agent — subagent_type: "reviewer", prompt: "Review this diff for reuse, quality, and efficiency. <diff inline>. Flag specific issues with file:line; skip generic advice. Under 200 words."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### NORMAL: three parallel agents (original flow)
|
|
62
|
+
Launch three agents in a single message — Reuse, Quality, Efficiency — passing the full diff to each. Use the original flow's category checklists.
|
|
63
|
+
|
|
64
|
+
**Reuse**: existing helpers/utilities that should be used instead; duplicated patterns; new functions that re-implement something already in the codebase.
|
|
65
|
+
|
|
66
|
+
**Quality**: redundant state, parameter sprawl, copy-paste with variation, leaky abstractions, stringly-typed code, nested conditionals 3+ levels, unnecessary comments (WHAT-explanations, task references).
|
|
67
|
+
|
|
68
|
+
**Efficiency**: unnecessary work, missed concurrency, hot-path bloat, recurring no-op updates, TOCTOU existence checks, unbounded structures, over-broad reads.
|
|
69
|
+
|
|
70
|
+
## Phase 4: Fix or skip
|
|
71
|
+
|
|
72
|
+
Aggregate findings. Fix each one directly. False positives or not-worth-fixing — note and skip without arguing. If TRIVIAL self-review found nothing, just confirm clean and exit.
|
|
73
|
+
|
|
74
|
+
If fixes were made, re-run tests to confirm nothing broke. If tests fail after a fix, revert it.
|
|
75
|
+
|
|
76
|
+
## Phase 5: Stamp the gate
|
|
77
|
+
|
|
78
|
+
Whatever tier ran, the gate (`check-before-pr`) registers /simplify as having executed. The skill is satisfied.
|
|
79
|
+
|
|
80
|
+
## Briefly summarize
|
|
81
|
+
|
|
82
|
+
End with one or two sentences: which tier, what was fixed (or "clean — no changes"). No headers, no bullets unless needed.
|
|
@@ -32,6 +32,7 @@ import { execSync, execFileSync, spawn } from 'child_process';
|
|
|
32
32
|
import { mofloResolveURL } from './lib/moflo-resolve.mjs';
|
|
33
33
|
import { memoryDbPath, MOFLO_DIR } from './lib/moflo-paths.mjs';
|
|
34
34
|
import { applyIncrementalChunks, computeContentListHash } from './lib/incremental-write.mjs';
|
|
35
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
35
36
|
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
36
37
|
|
|
37
38
|
|
|
@@ -914,11 +915,9 @@ async function main() {
|
|
|
914
915
|
}
|
|
915
916
|
|
|
916
917
|
async function runEmbeddings() {
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
];
|
|
921
|
-
const embedScript = embedCandidates.find(p => existsSync(p));
|
|
918
|
+
const embedScript = resolveMofloBin(
|
|
919
|
+
projectRoot, 'flo-embeddings', 'build-embeddings.mjs', { includeDevFallback: true },
|
|
920
|
+
);
|
|
922
921
|
if (!embedScript) return;
|
|
923
922
|
|
|
924
923
|
log('Generating embeddings for code-map...');
|
package/bin/hooks.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import { resolve, dirname } from 'path';
|
|
|
25
25
|
import { fileURLToPath } from 'url';
|
|
26
26
|
import { createProcessManager } from './lib/process-manager.mjs';
|
|
27
27
|
import { shouldDaemonAutoStart } from './lib/daemon-config.mjs';
|
|
28
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
28
29
|
|
|
29
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
30
31
|
const __dirname = dirname(__filename);
|
|
@@ -449,21 +450,10 @@ function spawnWindowless(cmd, args, description) {
|
|
|
449
450
|
return result;
|
|
450
451
|
}
|
|
451
452
|
|
|
452
|
-
// Resolve a moflo
|
|
453
|
+
// Resolve a moflo bin script via the shared helper (bin/lib/resolve-bin.mjs).
|
|
454
|
+
// Bin-first ordering — see #866 / #869.
|
|
453
455
|
function resolveBinOrLocal(binName, localScript) {
|
|
454
|
-
|
|
455
|
-
const mofloScript = resolve(projectRoot, 'node_modules/moflo/bin', localScript);
|
|
456
|
-
if (existsSync(mofloScript)) return mofloScript;
|
|
457
|
-
|
|
458
|
-
// 2. npm bin from .bin (symlinked by npm install)
|
|
459
|
-
const npmBin = resolve(projectRoot, 'node_modules/.bin', binName);
|
|
460
|
-
if (existsSync(npmBin)) return npmBin;
|
|
461
|
-
|
|
462
|
-
// 3. Local .claude/scripts/ copy (may be stale but better than nothing)
|
|
463
|
-
const localPath = resolve(projectRoot, '.claude/scripts', localScript);
|
|
464
|
-
if (existsSync(localPath)) return localPath;
|
|
465
|
-
|
|
466
|
-
return null;
|
|
456
|
+
return resolveMofloBin(projectRoot, binName, localScript);
|
|
467
457
|
}
|
|
468
458
|
|
|
469
459
|
// Run the guidance indexer in background (non-blocking - used for session start and file changes)
|
package/bin/index-all.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
saveStepFingerprint,
|
|
26
26
|
cleanupLegacyFingerprint,
|
|
27
27
|
} from './lib/index-fingerprint.mjs';
|
|
28
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
28
29
|
|
|
29
30
|
// Cap fastembed/ONNX thread count when spawning the heavy steps. Without
|
|
30
31
|
// this, ONNX defaults to one thread per CPU core (22+ on a modern dev box),
|
|
@@ -61,16 +62,7 @@ function log(msg) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
function resolveBin(binName, localScript) {
|
|
64
|
-
|
|
65
|
-
if (existsSync(mofloScript)) return mofloScript;
|
|
66
|
-
const npmBin = resolve(projectRoot, 'node_modules/.bin', binName);
|
|
67
|
-
if (existsSync(npmBin)) return npmBin;
|
|
68
|
-
const localPath = resolve(projectRoot, '.claude/scripts', localScript);
|
|
69
|
-
if (existsSync(localPath)) return localPath;
|
|
70
|
-
// Also check bin/ directory (for development use)
|
|
71
|
-
const binPath = resolve(projectRoot, 'bin', localScript);
|
|
72
|
-
if (existsSync(binPath)) return binPath;
|
|
73
|
-
return null;
|
|
65
|
+
return resolveMofloBin(projectRoot, binName, localScript, { includeDevFallback: true });
|
|
74
66
|
}
|
|
75
67
|
|
|
76
68
|
function getLocalCliPath() {
|
package/bin/index-guidance.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import { resolve, relative, dirname, basename, extname } from 'path';
|
|
|
27
27
|
import { fileURLToPath } from 'url';
|
|
28
28
|
import { mofloResolveURL } from './lib/moflo-resolve.mjs';
|
|
29
29
|
import { memoryDbPath } from './lib/moflo-paths.mjs';
|
|
30
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
30
31
|
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
31
32
|
|
|
32
33
|
|
|
@@ -873,14 +874,11 @@ if (!skipEmbeddings && needsEmbeddings) {
|
|
|
873
874
|
|
|
874
875
|
const { spawn } = await import('child_process');
|
|
875
876
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
const mofloScript = resolve(__dirname, 'build-embeddings.mjs');
|
|
880
|
-
const projectLocalScript = resolve(projectRoot, '.claude/scripts/build-embeddings.mjs');
|
|
881
|
-
const embeddingScript = existsSync(mofloScript) ? mofloScript : projectLocalScript;
|
|
877
|
+
const embeddingScript = resolveMofloBin(
|
|
878
|
+
projectRoot, 'flo-embeddings', 'build-embeddings.mjs', { includeDevFallback: true },
|
|
879
|
+
);
|
|
882
880
|
|
|
883
|
-
if (
|
|
881
|
+
if (embeddingScript) {
|
|
884
882
|
const embeddingArgs = ['--namespace', NAMESPACE];
|
|
885
883
|
|
|
886
884
|
// Create log file for background process output
|
package/bin/index-patterns.mjs
CHANGED
|
@@ -28,10 +28,10 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from
|
|
|
28
28
|
import { resolve, dirname, relative, basename, extname } from 'path';
|
|
29
29
|
import { fileURLToPath } from 'url';
|
|
30
30
|
import { spawn } from 'child_process';
|
|
31
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
31
32
|
import { mofloResolveURL } from './lib/moflo-resolve.mjs';
|
|
32
33
|
import { memoryDbPath, MOFLO_DIR } from './lib/moflo-paths.mjs';
|
|
33
34
|
import { applyIncrementalChunks, computeContentListHash } from './lib/incremental-write.mjs';
|
|
34
|
-
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
35
35
|
|
|
36
36
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
37
37
|
|
|
@@ -75,6 +75,9 @@ function ensureDbDir() {
|
|
|
75
75
|
|
|
76
76
|
async function getDb() {
|
|
77
77
|
ensureDbDir();
|
|
78
|
+
// Lazy: hash-cache-match and no-source-files early-exits in main() never
|
|
79
|
+
// reach this, and the sql.js wasm cold-load is ~400ms otherwise wasted.
|
|
80
|
+
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
78
81
|
const SQL = await initSqlJs();
|
|
79
82
|
let db;
|
|
80
83
|
if (existsSync(DB_PATH)) {
|
|
@@ -341,14 +344,9 @@ async function main() {
|
|
|
341
344
|
|
|
342
345
|
// Trigger embedding generation in background
|
|
343
346
|
try {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
resolve(__dirname, 'build-embeddings.mjs'),
|
|
348
|
-
resolve(projectRoot, 'node_modules/moflo/bin/build-embeddings.mjs'),
|
|
349
|
-
resolve(projectRoot, '.claude/scripts/build-embeddings.mjs'),
|
|
350
|
-
];
|
|
351
|
-
const embeddingScript = candidates.find(p => existsSync(p));
|
|
347
|
+
const embeddingScript = resolveMofloBin(
|
|
348
|
+
projectRoot, 'flo-embeddings', 'build-embeddings.mjs', { includeDevFallback: true },
|
|
349
|
+
);
|
|
352
350
|
if (embeddingScript) {
|
|
353
351
|
const child = spawn('node', [embeddingScript, '--namespace', NAMESPACE], {
|
|
354
352
|
cwd: projectRoot,
|
package/bin/index-tests.mjs
CHANGED
|
@@ -28,6 +28,7 @@ import { fileURLToPath } from 'url';
|
|
|
28
28
|
import { execSync, execFileSync, spawn } from 'child_process';
|
|
29
29
|
import { mofloResolveURL } from './lib/moflo-resolve.mjs';
|
|
30
30
|
import { memoryDbPath, MOFLO_DIR } from './lib/moflo-paths.mjs';
|
|
31
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
31
32
|
import { applyIncrementalChunks, computeContentListHash } from './lib/incremental-write.mjs';
|
|
32
33
|
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
33
34
|
|
|
@@ -687,11 +688,9 @@ async function main() {
|
|
|
687
688
|
}
|
|
688
689
|
|
|
689
690
|
async function runEmbeddings() {
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
];
|
|
694
|
-
const embedScript = embedCandidates.find(p => existsSync(p));
|
|
691
|
+
const embedScript = resolveMofloBin(
|
|
692
|
+
projectRoot, 'flo-embeddings', 'build-embeddings.mjs', { includeDevFallback: true },
|
|
693
|
+
);
|
|
695
694
|
if (!embedScript) return;
|
|
696
695
|
|
|
697
696
|
log('Generating embeddings for tests...');
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared moflo bin-script path resolver.
|
|
3
|
+
*
|
|
4
|
+
* Resolves a moflo bin script in a consumer project, preferring the
|
|
5
|
+
* npm-installed copy over the .claude/scripts/ mirror so the spawned process
|
|
6
|
+
* matches the installed package version.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order (intentional — see #866):
|
|
9
|
+
* 1. <projectRoot>/node_modules/moflo/bin/<localScript> — published, atomic
|
|
10
|
+
* 2. <projectRoot>/node_modules/.bin/<binName> — npm-managed alias (only if binName given)
|
|
11
|
+
* 3. <projectRoot>/.claude/scripts/<localScript> — derived sync, can lag a session
|
|
12
|
+
* 4. <projectRoot>/bin/<localScript> — DEV ONLY, opt-in via includeDevFallback
|
|
13
|
+
*
|
|
14
|
+
* Why bin-first matters: the .claude/scripts/ mirror is updated by a sync
|
|
15
|
+
* step that races the launcher's section-3 file copies during the very
|
|
16
|
+
* upgrade session, so a still-stale mirror can spawn pre-upgrade argv into
|
|
17
|
+
* a post-upgrade chain (#866). The npm package copy is updated atomically
|
|
18
|
+
* by `npm install moflo`, so spawning from there guarantees the running
|
|
19
|
+
* script matches the installed package.
|
|
20
|
+
*
|
|
21
|
+
* Cross-platform: paths are joined via `node:path.resolve`, which normalises
|
|
22
|
+
* separators on Windows + POSIX. Callers compute `projectRoot` themselves
|
|
23
|
+
* (typically via findProjectRoot()) so this helper has no implicit cwd.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
import { existsSync } from 'node:fs';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} projectRoot Consumer's project root (caller-computed).
|
|
31
|
+
* @param {string|null} binName npm bin alias (e.g. 'flo-index'), or null/undefined when no alias exists.
|
|
32
|
+
* @param {string} localScript Script filename (e.g. 'index-guidance.mjs').
|
|
33
|
+
* @param {{ includeDevFallback?: boolean }} [opts]
|
|
34
|
+
* includeDevFallback: also probe `<projectRoot>/bin/<localScript>` for source-tree dev.
|
|
35
|
+
* @returns {string|null} Resolved absolute path, or null when no candidate exists.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveMofloBin(projectRoot, binName, localScript, opts = {}) {
|
|
38
|
+
for (const candidate of mofloBinCandidates(projectRoot, binName, localScript, opts)) {
|
|
39
|
+
if (existsSync(candidate)) return candidate;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pure-function variant — returns the candidate list in resolution order
|
|
46
|
+
* without touching the filesystem. Used by the source-invariant test that
|
|
47
|
+
* pins the bin-first ordering, and by callers that want to log what was
|
|
48
|
+
* probed when nothing resolved.
|
|
49
|
+
*/
|
|
50
|
+
export function mofloBinCandidates(projectRoot, binName, localScript, opts = {}) {
|
|
51
|
+
const candidates = [
|
|
52
|
+
resolve(projectRoot, 'node_modules', 'moflo', 'bin', localScript),
|
|
53
|
+
];
|
|
54
|
+
if (binName) {
|
|
55
|
+
candidates.push(resolve(projectRoot, 'node_modules', '.bin', binName));
|
|
56
|
+
}
|
|
57
|
+
candidates.push(resolve(projectRoot, '.claude', 'scripts', localScript));
|
|
58
|
+
if (opts.includeDevFallback) {
|
|
59
|
+
candidates.push(resolve(projectRoot, 'bin', localScript));
|
|
60
|
+
}
|
|
61
|
+
return candidates;
|
|
62
|
+
}
|
|
@@ -13,6 +13,7 @@ import { resolve, dirname, join } from 'path';
|
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import { mofloDir } from './lib/moflo-paths.mjs';
|
|
15
15
|
import { repairMemoryDbIfCorrupt } from './lib/db-repair.mjs';
|
|
16
|
+
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
16
17
|
|
|
17
18
|
// Headless skip (#860). The daemon's headless workers spawn `claude --print`
|
|
18
19
|
// with CLAUDE_CODE_HEADLESS=true (see src/cli/services/headless-worker-
|
|
@@ -1125,19 +1126,15 @@ if (mutationCount > 0) {
|
|
|
1125
1126
|
// ── 4. Spawn background tasks ───────────────────────────────────────────────
|
|
1126
1127
|
|
|
1127
1128
|
// hooks.mjs session-start (daemon, indexer, pretrain, HNSW, neural patterns).
|
|
1128
|
-
//
|
|
1129
|
-
//
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
//
|
|
1134
|
-
//
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const hooksPkg = resolve(projectRoot, 'node_modules/moflo/bin/hooks.mjs');
|
|
1138
|
-
const hooksMirror = resolve(projectRoot, '.claude/scripts/hooks.mjs');
|
|
1139
|
-
const hooksScript = existsSync(hooksPkg) ? hooksPkg : hooksMirror;
|
|
1140
|
-
if (existsSync(hooksScript)) {
|
|
1129
|
+
// Bin-first ordering via resolveMofloBin — prefers the npm-package copy over
|
|
1130
|
+
// the `.claude/scripts/` mirror (#866). The mirror is a derived sync that
|
|
1131
|
+
// races the launcher's section-3 file copies during the very upgrade session;
|
|
1132
|
+
// spawning the still-stale mirror produces an orphan running pre-upgrade argv
|
|
1133
|
+
// (e.g. `rebuild-index --force` after #859 had already dropped it). The pkg
|
|
1134
|
+
// copy is updated atomically by `npm install moflo`, so spawning from there
|
|
1135
|
+
// guarantees the running hook code matches the installed package.
|
|
1136
|
+
const hooksScript = resolveMofloBin(projectRoot, null, 'hooks.mjs');
|
|
1137
|
+
if (hooksScript) {
|
|
1141
1138
|
fireAndForget('node', [hooksScript, 'session-start'], 'hooks session-start');
|
|
1142
1139
|
}
|
|
1143
1140
|
|
|
@@ -1152,10 +1149,8 @@ if (existsSync(hooksScript)) {
|
|
|
1152
1149
|
// Run synchronously (capture stdout) so each completed migration surfaces
|
|
1153
1150
|
// through emitMutation — Claude's session-start hook captures launcher
|
|
1154
1151
|
// stdout and that's the only channel that reaches the user.
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
const runMigrations = existsSync(runMigrationsPkg) ? runMigrationsPkg : runMigrationsMirror;
|
|
1158
|
-
if (existsSync(runMigrations)) {
|
|
1152
|
+
const runMigrations = resolveMofloBin(projectRoot, null, 'run-migrations.mjs');
|
|
1153
|
+
if (runMigrations) {
|
|
1159
1154
|
runMigrationsAndAnnounce(runMigrations);
|
|
1160
1155
|
}
|
|
1161
1156
|
|
package/dist/src/cli/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.10",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. A standalone, opinionated toolkit with semantic memory, learned routing, gates, spells, and the /flo issue-execution skill.",
|
|
5
5
|
"main": "dist/src/cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
82
82
|
"@typescript-eslint/parser": "^7.18.0",
|
|
83
83
|
"eslint": "^8.0.0",
|
|
84
|
-
"moflo": "^4.9.
|
|
84
|
+
"moflo": "^4.9.9",
|
|
85
85
|
"tsx": "^4.21.0",
|
|
86
86
|
"typescript": "^5.9.3",
|
|
87
87
|
"vitest": "^4.0.0"
|