moflo 4.9.20 → 4.9.21
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/commands/{simplify.md → flo-simplify.md} +4 -4
- package/.claude/guidance/shipped/moflo-agent-rules.md +172 -0
- package/.claude/guidance/shipped/moflo-claude-swarm-cohesion.md +73 -265
- package/.claude/guidance/shipped/moflo-cli-reference.md +6 -6
- package/.claude/guidance/shipped/moflo-core-guidance.md +66 -184
- package/.claude/guidance/shipped/moflo-cross-platform.md +1 -1
- package/.claude/guidance/shipped/moflo-error-handling.md +3 -3
- package/.claude/guidance/shipped/moflo-guidance-rules.md +17 -7
- package/.claude/guidance/shipped/moflo-memory-strategy.md +76 -182
- package/.claude/guidance/shipped/moflo-memorydb-maintenance.md +6 -8
- package/.claude/guidance/shipped/moflo-settings-injection.md +7 -9
- package/.claude/guidance/shipped/moflo-source-hygiene.md +5 -5
- package/.claude/guidance/shipped/moflo-spell-connectors.md +3 -4
- package/.claude/guidance/shipped/moflo-spell-custom-steps.md +3 -4
- package/.claude/guidance/shipped/moflo-spell-engine.md +40 -162
- package/.claude/guidance/shipped/moflo-spell-runner.md +134 -0
- package/.claude/guidance/shipped/moflo-spell-sandboxing.md +10 -57
- package/.claude/guidance/shipped/moflo-spell-troubleshooting.md +149 -0
- package/.claude/guidance/shipped/moflo-subagents.md +43 -114
- package/.claude/guidance/shipped/moflo-task-icons.md +4 -4
- package/.claude/guidance/shipped/moflo-user-facing-language.md +3 -3
- package/.claude/guidance/shipped/moflo-verbose-command-filtering.md +3 -3
- package/.claude/guidance/shipped/moflo-yaml-reference.md +4 -5
- package/.claude/helpers/gate.cjs +124 -14
- package/.claude/helpers/prompt-hook.mjs +4 -38
- package/.claude/helpers/simplify-classify.cjs +32 -11
- package/.claude/helpers/subagent-bootstrap.json +1 -1
- package/.claude/helpers/subagent-start.cjs +1 -1
- package/.claude/skills/connector-builder/SKILL.md +42 -429
- package/.claude/skills/connector-builder/templates/connector.md +189 -0
- package/.claude/skills/connector-builder/templates/step-command.md +176 -0
- package/.claude/skills/eldar/SKILL.md +7 -7
- package/.claude/skills/fl/SKILL.md +3 -3
- package/.claude/skills/fl/execution-modes.md +3 -3
- package/.claude/skills/fl/phases.md +3 -3
- package/.claude/skills/{simplify → flo-simplify}/SKILL.md +11 -11
- package/.claude/skills/guidance/SKILL.md +17 -9
- package/.claude/skills/memory-patterns/SKILL.md +1 -1
- package/.claude/skills/publish/SKILL.md +121 -36
- package/.claude/skills/reset-epic/SKILL.md +2 -2
- package/.claude/skills/spell-builder/SKILL.md +39 -226
- package/.claude/skills/spell-builder/architecture.md +1 -1
- package/.claude/skills/spell-builder/permissions.md +107 -0
- package/.claude/skills/spell-builder/preflight.md +101 -0
- package/.claude/skills/spell-schedule/SKILL.md +2 -3
- package/bin/gate.cjs +124 -14
- package/bin/prompt-hook.mjs +4 -38
- package/bin/session-start-launcher.mjs +19 -1
- package/bin/setup-project.mjs +63 -69
- package/bin/simplify-classify.cjs +32 -11
- package/dist/src/cli/commands/doctor-checks-deep.js +4 -0
- package/dist/src/cli/init/claudemd-generator.js +30 -33
- package/dist/src/cli/init/executor.js +28 -16
- package/dist/src/cli/init/helpers-generator.js +101 -51
- package/dist/src/cli/init/moflo-init.js +41 -114
- package/dist/src/cli/init/settings-generator.js +32 -14
- package/dist/src/cli/services/hook-block-hash.js +7 -2
- package/dist/src/cli/services/hook-wiring.js +86 -3
- package/dist/src/cli/services/subagent-bootstrap.js +1 -1
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
- package/scripts/post-install-bootstrap.mjs +19 -0
- package/.claude/guidance/shipped/moflo-session-start.md +0 -154
- package/.claude/guidance/shipped/moflo-spell-engine-architecture.md +0 -145
- package/.claude/skills/browser/SKILL.md +0 -204
- package/.claude/skills/github-code-review/SKILL.md +0 -1140
- package/.claude/skills/github-multi-repo/SKILL.md +0 -866
- package/.claude/skills/github-project-management/SKILL.md +0 -1272
- package/.claude/skills/github-release-management/SKILL.md +0 -1074
- package/.claude/skills/github-workflow-automation/SKILL.md +0 -1060
- package/.claude/skills/hive-mind-advanced/SKILL.md +0 -712
- package/.claude/skills/hooks-automation/SKILL.md +0 -1193
- package/.claude/skills/pair-programming/SKILL.md +0 -1202
- package/.claude/skills/performance-analysis/SKILL.md +0 -563
- package/.claude/skills/skill-builder/SKILL.md +0 -910
- package/.claude/skills/sparc-methodology/SKILL.md +0 -904
- package/.claude/skills/stream-chain/SKILL.md +0 -563
- package/.claude/skills/swarm-advanced/SKILL.md +0 -811
- package/.claude/skills/swarm-orchestration/SKILL.md +0 -179
- package/.claude/skills/verification-quality/SKILL.md +0 -649
- package/.claude/skills/worker-benchmarks/skill.md +0 -135
- package/.claude/skills/worker-integration/skill.md +0 -154
|
@@ -236,6 +236,18 @@ function generateHooksConfig(config) {
|
|
|
236
236
|
{ type: 'command', command: gateHookCmd('check-before-pr'), timeout: 2000 },
|
|
237
237
|
],
|
|
238
238
|
},
|
|
239
|
+
// #931 — TaskCreate REMINDER + namespace hint moved here from
|
|
240
|
+
// UserPromptSubmit. They only matter when Claude is about to spawn an
|
|
241
|
+
// Agent; emitting per-prompt cost ~90 tokens × every prompt × every
|
|
242
|
+
// consumer. Advisory only — never blocks. Routed through gate-hook.mjs
|
|
243
|
+
// so Claude Code's session_id is forwarded as HOOK_SESSION_ID — the
|
|
244
|
+
// namespace hint emission tracks per-actor (mirror of #879's fix for
|
|
245
|
+
// record-memory-searched), so subagents that spawn their own agents
|
|
246
|
+
// still get the hint on their first check.
|
|
247
|
+
{
|
|
248
|
+
matcher: '^Agent$',
|
|
249
|
+
hooks: [{ type: 'command', command: gateHookCmd('check-before-agent'), timeout: 2000 }],
|
|
250
|
+
},
|
|
239
251
|
];
|
|
240
252
|
}
|
|
241
253
|
// PostToolUse — record outcomes for learning
|
|
@@ -270,13 +282,16 @@ function generateHooksConfig(config) {
|
|
|
270
282
|
hooks: [{ type: 'command', command: gateHookCmd('record-skill-run'), timeout: 2000 }],
|
|
271
283
|
},
|
|
272
284
|
{
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
285
|
+
// Anchored alternation — Claude Code anchors hook matchers (`^…$` semantics),
|
|
286
|
+
// so a bare `mcp__moflo__memory_` never matches any real MCP tool name and the
|
|
287
|
+
// hook silently no-ops (#929 regression). Listing the explicit suffixes keeps
|
|
288
|
+
// the matcher narrow while still catching every memory_* tool we ship.
|
|
289
|
+
// Use gateHookCmd (not gateCmd) so the wrapper forwards Claude Code's session_id
|
|
290
|
+
// as HOOK_SESSION_ID — record-memory-searched needs this to mark the per-actor
|
|
291
|
+
// map (memorySearchedBy[sid]) that check-before-read consults under #838's
|
|
292
|
+
// per-actor gating. Without it, the legacy boolean is set but the per-actor map
|
|
293
|
+
// stays empty, and the gate blocks every Read forever within the turn (#879).
|
|
294
|
+
matcher: '^mcp__moflo__memory_(search|retrieve|list|stats|store)$',
|
|
280
295
|
hooks: [{ type: 'command', command: gateHookCmd('record-memory-searched'), timeout: 3000 }],
|
|
281
296
|
},
|
|
282
297
|
{
|
|
@@ -289,12 +304,15 @@ function generateHooksConfig(config) {
|
|
|
289
304
|
},
|
|
290
305
|
];
|
|
291
306
|
}
|
|
292
|
-
// UserPromptSubmit —
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
307
|
+
// UserPromptSubmit — per-prompt state reset + Context warnings.
|
|
308
|
+
// Two separate hook entries (not chained) so an exception in prompt-hook.mjs
|
|
309
|
+
// doesn't skip the reset. The first hook runs prompt-hook.mjs which invokes
|
|
310
|
+
// gate.cjs `prompt-reminder` (full reset + interactionCount + Context
|
|
311
|
+
// warnings). The second hook runs `prompt-state-reset` — a defensive
|
|
312
|
+
// safety-net that re-applies the idempotent state reset (memorySearched,
|
|
313
|
+
// memorySearchedBy, memoryRequired, lastNamespaceHint) without
|
|
314
|
+
// double-incrementing interactionCount or duplicating any user-visible
|
|
315
|
+
// emission (#931).
|
|
298
316
|
if (config.userPromptSubmit) {
|
|
299
317
|
hooks.UserPromptSubmit = [
|
|
300
318
|
{
|
|
@@ -304,7 +322,7 @@ function generateHooksConfig(config) {
|
|
|
304
322
|
},
|
|
305
323
|
{
|
|
306
324
|
hooks: [
|
|
307
|
-
{ type: 'command', command: gateHookCmd('prompt-
|
|
325
|
+
{ type: 'command', command: gateHookCmd('prompt-state-reset'), timeout: 3000 },
|
|
308
326
|
],
|
|
309
327
|
},
|
|
310
328
|
];
|
|
@@ -59,6 +59,10 @@ export function getReferenceHookBlock() {
|
|
|
59
59
|
matcher: '^Bash$',
|
|
60
60
|
hooks: [gateHook('check-dangerous-command', 2000), gateHook('check-before-pr', 2000)],
|
|
61
61
|
},
|
|
62
|
+
// #931 — TaskCreate REMINDER + namespace hint advisory at Agent-spawn time.
|
|
63
|
+
// Routed via gate-hook.mjs so HOOK_SESSION_ID is forwarded for per-actor
|
|
64
|
+
// single-shot emission of the namespace hint.
|
|
65
|
+
{ matcher: '^Agent$', hooks: [gateHook('check-before-agent', 2000)] },
|
|
62
66
|
],
|
|
63
67
|
PostToolUse: [
|
|
64
68
|
{
|
|
@@ -72,13 +76,14 @@ export function getReferenceHookBlock() {
|
|
|
72
76
|
hooks: [gateHook('check-bash-memory', 2000), gateHook('record-test-run', 2000)],
|
|
73
77
|
},
|
|
74
78
|
{ matcher: '^Skill$', hooks: [gateHook('record-skill-run', 2000)] },
|
|
75
|
-
{ matcher: 'mcp__moflo__memory_', hooks: [gateHook('record-memory-searched', 3000)] },
|
|
79
|
+
{ matcher: '^mcp__moflo__memory_(search|retrieve|list|stats|store)$', hooks: [gateHook('record-memory-searched', 3000)] },
|
|
76
80
|
{ matcher: '^TaskUpdate$', hooks: [gateCjs('check-task-transition', 2000)] },
|
|
77
81
|
{ matcher: '^mcp__moflo__memory_store$', hooks: [gateCjs('record-learnings-stored', 2000)] },
|
|
78
82
|
],
|
|
79
83
|
UserPromptSubmit: [
|
|
80
84
|
{ hooks: [helperHook('prompt-hook.mjs', '', 3000)] },
|
|
81
|
-
|
|
85
|
+
// #931 — Defensive safety-net hook. State reset only, no emission.
|
|
86
|
+
{ hooks: [gateHook('prompt-state-reset', 3000)] },
|
|
82
87
|
],
|
|
83
88
|
SubagentStart: [
|
|
84
89
|
{ hooks: [helperHook('subagent-start.cjs', '', 2000)] },
|
|
@@ -18,6 +18,9 @@ export const REQUIRED_HOOK_WIRING = [
|
|
|
18
18
|
{ event: 'PreToolUse', pattern: 'check-before-read' },
|
|
19
19
|
{ event: 'PreToolUse', pattern: 'check-dangerous-command' },
|
|
20
20
|
{ event: 'PreToolUse', pattern: 'check-before-pr' },
|
|
21
|
+
// #931 — TaskCreate REMINDER + namespace hint emit only at Agent spawn now,
|
|
22
|
+
// not on every prompt. Saves ~90 tokens × every prompt × every consumer.
|
|
23
|
+
{ event: 'PreToolUse', pattern: 'check-before-agent' },
|
|
21
24
|
{ event: 'PostToolUse', pattern: 'record-task-created' },
|
|
22
25
|
{ event: 'PostToolUse', pattern: 'record-memory-searched' },
|
|
23
26
|
{ event: 'PostToolUse', pattern: 'check-task-transition' },
|
|
@@ -26,7 +29,14 @@ export const REQUIRED_HOOK_WIRING = [
|
|
|
26
29
|
{ event: 'PostToolUse', pattern: 'record-test-run' },
|
|
27
30
|
{ event: 'PostToolUse', pattern: 'record-skill-run' },
|
|
28
31
|
{ event: 'PostToolUse', pattern: 'reset-edit-gates' },
|
|
29
|
-
|
|
32
|
+
// First UserPromptSubmit hook (prompt-hook.mjs internally calls
|
|
33
|
+
// `gate.cjs prompt-reminder`). Substring check tolerates either the
|
|
34
|
+
// shipped helper-script command or an inlined gate call.
|
|
35
|
+
{ event: 'UserPromptSubmit', pattern: 'prompt-hook.mjs' },
|
|
36
|
+
// Second (defensive) UserPromptSubmit hook — state reset only. Replaced
|
|
37
|
+
// the duplicate `prompt-reminder` wiring that was emitting the TaskCreate
|
|
38
|
+
// REMINDER twice per prompt (#931).
|
|
39
|
+
{ event: 'UserPromptSubmit', pattern: 'prompt-state-reset' },
|
|
30
40
|
];
|
|
31
41
|
/**
|
|
32
42
|
* Map gate pattern → hook entry to add when missing from settings.json.
|
|
@@ -41,14 +51,29 @@ export const HOOK_ENTRY_MAP = {
|
|
|
41
51
|
// record-memory-searched MUST go through gate-hook.mjs (not gate.cjs directly)
|
|
42
52
|
// — the wrapper forwards Claude Code's session_id as HOOK_SESSION_ID, which
|
|
43
53
|
// markMemorySearched needs to stamp the per-actor map (#879).
|
|
44
|
-
|
|
54
|
+
// Matcher MUST be a fully-anchored regex: Claude Code anchors hook matchers
|
|
55
|
+
// (`^...$` semantics), so a bare `mcp__moflo__memory_` never fires for any
|
|
56
|
+
// tool name (#929 regression — the hook silently no-ops, leaving every
|
|
57
|
+
// memory_search uncounted by the gate).
|
|
58
|
+
'record-memory-searched': { event: 'PostToolUse', matcher: '^mcp__moflo__memory_(search|retrieve|list|stats|store)$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" record-memory-searched', timeout: 3000 } },
|
|
45
59
|
'check-task-transition': { event: 'PostToolUse', matcher: '^TaskUpdate$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate.cjs" check-task-transition', timeout: 2000 } },
|
|
46
60
|
'record-learnings-stored': { event: 'PostToolUse', matcher: '^mcp__moflo__memory_store$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate.cjs" record-learnings-stored', timeout: 2000 } },
|
|
47
61
|
'check-bash-memory': { event: 'PostToolUse', matcher: '^Bash$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" check-bash-memory', timeout: 2000 } },
|
|
48
62
|
'record-test-run': { event: 'PostToolUse', matcher: '^Bash$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" record-test-run', timeout: 2000 } },
|
|
49
63
|
'record-skill-run': { event: 'PostToolUse', matcher: '^Skill$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" record-skill-run', timeout: 2000 } },
|
|
50
64
|
'reset-edit-gates': { event: 'PostToolUse', matcher: '^(Write|Edit|MultiEdit)$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" reset-edit-gates', timeout: 2000 } },
|
|
51
|
-
|
|
65
|
+
// #931 — Agent-time advisory; never blocks. Pulled the TaskCreate REMINDER
|
|
66
|
+
// and namespace hint out of prompt-reminder so they fire only when Claude is
|
|
67
|
+
// actually about to spawn an Agent. Routed via gate-hook.mjs so Claude Code's
|
|
68
|
+
// session_id is forwarded as HOOK_SESSION_ID — the namespace hint emission
|
|
69
|
+
// is per-actor single-shot (mirror of #879's record-memory-searched fix).
|
|
70
|
+
'check-before-agent': { event: 'PreToolUse', matcher: '^Agent$', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" check-before-agent', timeout: 2000 } },
|
|
71
|
+
// Re-add the prompt-hook.mjs wiring as its own bare UserPromptSubmit block
|
|
72
|
+
// when missing. Empty matcher = bare block, like settings-generator emits.
|
|
73
|
+
'prompt-hook.mjs': { event: 'UserPromptSubmit', matcher: '', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/prompt-hook.mjs"', timeout: 3000 } },
|
|
74
|
+
// Defensive safety-net — runs gate.cjs `prompt-state-reset` if prompt-hook.mjs
|
|
75
|
+
// throws before completing the per-prompt state reset. State-only, no emission.
|
|
76
|
+
'prompt-state-reset': { event: 'UserPromptSubmit', matcher: '', hook: { type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" prompt-state-reset', timeout: 3000 } },
|
|
52
77
|
};
|
|
53
78
|
/**
|
|
54
79
|
* Inspect a parsed settings.json object for missing required hook wirings
|
|
@@ -104,6 +129,42 @@ export const HOOK_REWRITE_RULES = [
|
|
|
104
129
|
from: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate.cjs" check-bash-memory',
|
|
105
130
|
to: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" check-bash-memory',
|
|
106
131
|
},
|
|
132
|
+
// Issue #931 — the second UserPromptSubmit hook used to invoke
|
|
133
|
+
// gate-hook.mjs `prompt-reminder`, which (a) duplicated the first hook's
|
|
134
|
+
// emission of `REMINDER: Use TaskCreate...` per prompt and (b) double-
|
|
135
|
+
// incremented `interactionCount`. The first hook (prompt-hook.mjs) still
|
|
136
|
+
// calls gate.cjs `prompt-reminder` internally for the full reset + Context
|
|
137
|
+
// warnings; the safety-net hook now runs `prompt-state-reset` instead —
|
|
138
|
+
// idempotent state reset, no emission, no increment.
|
|
139
|
+
{
|
|
140
|
+
name: '#931: dedupe UserPromptSubmit prompt-reminder → prompt-state-reset',
|
|
141
|
+
from: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" prompt-reminder',
|
|
142
|
+
to: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" prompt-state-reset',
|
|
143
|
+
},
|
|
144
|
+
// Issue #931 — `check-before-agent` was first wired through gate.cjs
|
|
145
|
+
// directly (no stdin parsing). Without HOOK_SESSION_ID the namespace hint's
|
|
146
|
+
// per-actor tracking falls back to a single `_legacy_` bucket, so a
|
|
147
|
+
// subagent spawning its own agent would silently miss the hint after the
|
|
148
|
+
// parent already consumed it. Route through gate-hook.mjs (the same wrapper
|
|
149
|
+
// that fixed #879) so each session_id gets its own single-shot.
|
|
150
|
+
{
|
|
151
|
+
name: '#931: route check-before-agent → gate-hook.mjs (forwards HOOK_SESSION_ID)',
|
|
152
|
+
from: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate.cjs" check-before-agent',
|
|
153
|
+
to: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" check-before-agent',
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
export const MATCHER_REWRITE_RULES = [
|
|
157
|
+
// Issue #929 — Claude Code anchors hook matchers (`^…$` semantics), so a
|
|
158
|
+
// bare `mcp__moflo__memory_` never matches any real MCP tool name and the
|
|
159
|
+
// record-memory-searched stamp silently no-ops on every memory_search.
|
|
160
|
+
// The fix is anchored alternation. Existing consumers that upgrade through
|
|
161
|
+
// this version self-heal here without needing `flo doctor --fix`.
|
|
162
|
+
{
|
|
163
|
+
name: '#929: anchor record-memory-searched matcher',
|
|
164
|
+
from: 'mcp__moflo__memory_',
|
|
165
|
+
to: '^mcp__moflo__memory_(search|retrieve|list|stats|store)$',
|
|
166
|
+
cmdContains: 'record-memory-searched',
|
|
167
|
+
},
|
|
107
168
|
];
|
|
108
169
|
/**
|
|
109
170
|
* Apply HOOK_REWRITE_RULES to every hook command in `settings.hooks.*`.
|
|
@@ -135,6 +196,28 @@ export function rewriteIncorrectHookWiring(settings) {
|
|
|
135
196
|
if (count > 0)
|
|
136
197
|
rewrites.push({ name: rule.name, count });
|
|
137
198
|
}
|
|
199
|
+
for (const rule of MATCHER_REWRITE_RULES) {
|
|
200
|
+
let count = 0;
|
|
201
|
+
for (const eventName of Object.keys(hooks)) {
|
|
202
|
+
const eventArray = hooks[eventName];
|
|
203
|
+
if (!Array.isArray(eventArray))
|
|
204
|
+
continue;
|
|
205
|
+
for (const block of eventArray) {
|
|
206
|
+
if (block.matcher !== rule.from)
|
|
207
|
+
continue;
|
|
208
|
+
const blockHooks = block.hooks;
|
|
209
|
+
if (!Array.isArray(blockHooks))
|
|
210
|
+
continue;
|
|
211
|
+
const hasMatchingCmd = blockHooks.some((h) => typeof h.command === 'string' && h.command.includes(rule.cmdContains));
|
|
212
|
+
if (!hasMatchingCmd)
|
|
213
|
+
continue;
|
|
214
|
+
block.matcher = rule.to;
|
|
215
|
+
count++;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (count > 0)
|
|
219
|
+
rewrites.push({ name: rule.name, count });
|
|
220
|
+
}
|
|
138
221
|
return { settings, rewrites };
|
|
139
222
|
}
|
|
140
223
|
//# sourceMappingURL=hook-wiring.js.map
|
|
@@ -21,7 +21,7 @@ const BOOTSTRAP_JSON_REL = '.claude/helpers/subagent-bootstrap.json';
|
|
|
21
21
|
// Defense-in-depth copy of the canonical directive in subagent-bootstrap.json.
|
|
22
22
|
// Kept as a single-line literal so the parity test can verify it matches the
|
|
23
23
|
// JSON via plain substring containment.
|
|
24
|
-
const FALLBACK_DIRECTIVE = 'MANDATORY FIRST ACTION: Your very first tool call MUST be mcp__moflo__memory_search (any query, any namespace). The memory-first gate WILL BLOCK all Glob, Grep, and Read calls until you do this. After memory search, follow `.claude/guidance/
|
|
24
|
+
const FALLBACK_DIRECTIVE = 'MANDATORY FIRST ACTION: Your very first tool call MUST be mcp__moflo__memory_search (any query, any namespace). The memory-first gate WILL BLOCK all Glob, Grep, and Read calls until you do this. After memory search, follow `.claude/guidance/moflo-subagents.md` protocol.';
|
|
25
25
|
function loadDirective() {
|
|
26
26
|
const jsonPath = locateMofloRootPath(BOOTSTRAP_JSON_REL);
|
|
27
27
|
if (!jsonPath) {
|
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.21",
|
|
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.20",
|
|
85
85
|
"tsx": "^4.21.0",
|
|
86
86
|
"typescript": "^5.9.3",
|
|
87
87
|
"vitest": "^4.0.0"
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
existsSync,
|
|
42
42
|
mkdirSync,
|
|
43
43
|
readdirSync,
|
|
44
|
+
readFileSync,
|
|
44
45
|
statSync,
|
|
45
46
|
} from 'node:fs';
|
|
46
47
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -175,9 +176,27 @@ export async function runBootstrap({
|
|
|
175
176
|
// The source repo's .claude/ IS the truth source — we'd be copying
|
|
176
177
|
// the very files we just built ON TOP of themselves, which on Windows
|
|
177
178
|
// hits the same file-lock issues we're trying to avoid.
|
|
179
|
+
//
|
|
180
|
+
// Two paths can hit this branch:
|
|
181
|
+
// 1. The source's own postinstall: mofloRoot === projectRoot (path equality).
|
|
182
|
+
// 2. The DEPENDENCY's postinstall on `npm ci` of the moflo source repo:
|
|
183
|
+
// mofloRoot = <src>/node_modules/moflo/, projectRoot = <src>/. Path
|
|
184
|
+
// equality fails — moflo-as-its-own-devDep would clobber the source
|
|
185
|
+
// .claude/helpers/ with the OLDER published artifacts, breaking CI
|
|
186
|
+
// against any in-flight fixes. Detect by reading projectRoot's
|
|
187
|
+
// package.json name and bailing whenever it's "moflo".
|
|
178
188
|
if (resolve(projectRoot) === resolve(mofloRoot)) {
|
|
179
189
|
return { ran: false, reason: 'moflo-self-install' };
|
|
180
190
|
}
|
|
191
|
+
try {
|
|
192
|
+
const projectPkgPath = resolve(projectRoot, 'package.json');
|
|
193
|
+
if (existsSync(projectPkgPath)) {
|
|
194
|
+
const projectPkg = JSON.parse(readFileSync(projectPkgPath, 'utf-8'));
|
|
195
|
+
if (projectPkg.name === 'moflo') {
|
|
196
|
+
return { ran: false, reason: 'moflo-self-dev-install' };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch { /* unparseable package.json — fall through, treat as consumer */ }
|
|
181
200
|
|
|
182
201
|
const binDir = resolve(mofloRoot, 'bin');
|
|
183
202
|
if (!existsSync(binDir)) {
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# MoFlo Session-Start Lifecycle
|
|
2
|
-
|
|
3
|
-
**Purpose:** Document everything that runs when Claude Code fires the `SessionStart` hook in a moflo-enabled project. The launcher does substantially more than "start the daemon" — it heals stale state, migrates DBs, syncs scripts, and reconciles hook wiring. Knowing what runs (and in what order) makes diagnosis fast.
|
|
4
|
-
|
|
5
|
-
**Audience:** Agents and developers diagnosing slow session starts, unexpected mutations to `.claude/`, embedded migrations, daemon recycling, and post-upgrade behaviour.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Quick reference
|
|
10
|
-
|
|
11
|
-
The launcher (`bin/session-start-launcher.mjs`, synced to `.claude/scripts/session-start-launcher.mjs`) is the single SessionStart hook moflo registers. Everything below runs in one process, in the listed order, before Claude Code begins the session. Total cost on a healthy install with no version change is typically < 200 ms; an upgrade or DB heal can extend it to several seconds.
|
|
12
|
-
|
|
13
|
-
When anything runs, the launcher prints `moflo: <action> (<details>)` to stdout. Claude Code captures stdout as session-start `additionalContext`, so the agent sees every mutation. Failures land on stderr and similarly surface.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Stages, in execution order
|
|
18
|
-
|
|
19
|
-
### 0. Headless skip
|
|
20
|
-
|
|
21
|
-
If `CLAUDE_CODE_HEADLESS=true` is set in the environment, the launcher exits immediately. The moflo daemon spawns headless Claude processes (for indexing, pretraining, etc.); without this guard each spawned Claude would re-enter the launcher and fork the indexer chain on every daemon worker tick. ([#860](https://github.com/eric-cielo/moflo/issues/860).)
|
|
22
|
-
|
|
23
|
-
### 0-pre. Drop stale upgrade-notice files
|
|
24
|
-
|
|
25
|
-
`.moflo/upgrade-notice.json` is a transient handshake between launcher and statusline. Pre-#738 launchers wrote a 1-hour-TTL "complete" notice that could survive past the launcher run that wrote it. The launcher unconditionally removes any leftover before its own run begins.
|
|
26
|
-
|
|
27
|
-
### 0c. Memory DB index repair
|
|
28
|
-
|
|
29
|
-
Probes `.moflo/moflo.db` for `sqlite_autoindex_memory_entries_1` corruption and runs `REINDEX` in place if found. Must run before any sql.js consumer (embeddings migration, ephemeral-namespace purge, MCP server, daemon) — those swallow corruption errors and silently drop work otherwise. Cost on the happy path: one PRAGMA check (~10 ms). ([#743](https://github.com/eric-cielo/moflo/issues/743).)
|
|
30
|
-
|
|
31
|
-
### 0d. Clear post-install restart notice
|
|
32
|
-
|
|
33
|
-
If `.moflo/restart-pending.json` was written by `scripts/post-install-notice.mjs` after the last `npm install moflo` and the running version now matches the pending version, the file is removed. The user has restarted; the notice has done its job. ([#867](https://github.com/eric-cielo/moflo/issues/867).)
|
|
34
|
-
|
|
35
|
-
### 2. Reset workflow state
|
|
36
|
-
|
|
37
|
-
Writes a fresh `.claude/workflow-state.json` for the new session: `tasksCreated: false`, `memorySearched: false`, `sessionStart: <ISO timestamp>`. The gate scripts read this on every tool call.
|
|
38
|
-
|
|
39
|
-
### 3. Auto-sync on version change (or drift)
|
|
40
|
-
|
|
41
|
-
This is the biggest stage. It fires when **either** condition is true:
|
|
42
|
-
|
|
43
|
-
- The installed version (`node_modules/moflo/package.json`) differs from the cached version stamp (`.moflo/moflo-version`), OR
|
|
44
|
-
- Any file in `.moflo/installed-files.json` (the manifest from the previous successful sync) is missing or has drifted in size since we last wrote it.
|
|
45
|
-
|
|
46
|
-
When triggered, the launcher walks the following sub-steps in order:
|
|
47
|
-
|
|
48
|
-
| Sub-step | What runs | Why |
|
|
49
|
-
|---------|-----------|-----|
|
|
50
|
-
| Stop daemon | Kills the daemon recorded in `.moflo/daemon.lock` | The daemon was started under the previous moflo image; old module-cache + path resolution would clobber the upgrade. ([#851](https://github.com/eric-cielo/moflo/issues/851).) |
|
|
51
|
-
| Cherry-pick learnings | `cherry-pick-learnings.js` reads legacy `.swarm/memory.db` and `INSERT OR IGNORE`s into `.moflo/moflo.db` | Carries user-authored knowledge forward across the v3 DB rename. Idempotent. |
|
|
52
|
-
| Sync scripts | Copy `bin/*.mjs` and `bin/lib/**` and `bin/migrations/**` into `.claude/scripts/` | Hooks always run the latest version |
|
|
53
|
-
| Sync helpers | Copy `bin/{gate.cjs,gate-hook.mjs,prompt-hook.mjs,hook-handler.cjs}` plus `.claude/helpers/{auto-memory-hook.mjs,statusline.cjs,...}` into `.claude/helpers/` | Same |
|
|
54
|
-
| Sync shipped guidance | Copy every `node_modules/moflo/.claude/guidance/shipped/moflo-*.md` into `.claude/guidance/`, prefixing each with the auto-generated mirror header | These are the files indexed by the consumer's memory namespace |
|
|
55
|
-
| Cleanup retired files | Anything in the OLD manifest but NOT in the new manifest is unlinked | Auto-removes files moflo no longer ships |
|
|
56
|
-
| Per-file retry/circuit | Each copy retries `[50, 200, 800] ms` on `EBUSY`/`EPERM`/`EACCES` (Windows file lock, AV scan); after 5 distinct failures the circuit opens and the rest of the sync runs without retries | Prevents a sick host from compounding wall-clock cost; persistent failures land on stderr |
|
|
57
|
-
| Manifest write | New `{path, size}` manifest committed to `.moflo/installed-files.json` | Drift detection on next launcher run |
|
|
58
|
-
| Defer version stamp | Pending write of `.moflo/moflo-version` queued for stage 3g | Stamp is "launcher fully completed" — writing it mid-flight strands consumers on a half-applied upgrade ([#730](https://github.com/eric-cielo/moflo/issues/730)) |
|
|
59
|
-
|
|
60
|
-
### 3a-pre. Recycle stale daemons
|
|
61
|
-
|
|
62
|
-
Even when the version hasn't changed, the daemon-lock's `startedAt` is compared against `node_modules/moflo`'s install mtime. If the daemon predates the current install (with a 5 s skew margin), it's recycled. This catches the "user upgraded ages ago, ran one session that bumped the stamp + recycled the daemon, but the daemon they started long-ago is still alive holding stale module cache" failure mode.
|
|
63
|
-
|
|
64
|
-
### 3a. Settings.json migration and self-heal
|
|
65
|
-
|
|
66
|
-
See `moflo-settings-injection.md` for the full mechanism. The launcher applies, in order:
|
|
67
|
-
|
|
68
|
-
1. **Drop stale `PATH` override** — pre-4.x settings injected `${PATH}` which Claude Code didn't expand, breaking node resolution.
|
|
69
|
-
2. **Replace `npx flo` hook commands with direct `node "$CLAUDE_PROJECT_DIR/..."` invocations** — saves 2–5 s of `npx` cold-start per hook.
|
|
70
|
-
3. **Ensure `statusLine` is wired** — the helper script is synced by stage 3 but the config block may be absent.
|
|
71
|
-
4. **Repair + rewrite hook wirings** — `repairHookWiring()` adds missing required hooks; `rewriteIncorrectHookWiring()` rewrites known-stale hook commands (e.g. [#879](https://github.com/eric-cielo/moflo/issues/879) `gate.cjs record-memory-searched` → `gate-hook.mjs record-memory-searched`).
|
|
72
|
-
|
|
73
|
-
### 3b. Restore + prune shipped guidance mirrors
|
|
74
|
-
|
|
75
|
-
Even when stage 3 didn't fire (no version change, no drift), the launcher checks that every `moflo-*.md` from `node_modules/moflo/.claude/guidance/shipped/` exists at `.claude/guidance/<file>` with the auto-generated mirror header. Missing files are restored; mirror files whose source has been removed (and which still carry the `<!-- AUTO-GENERATED by moflo session-start.` marker) are pruned. ([#839](https://github.com/eric-cielo/moflo/issues/839).)
|
|
76
|
-
|
|
77
|
-
### 3b-714, 3c. Legacy cleanup
|
|
78
|
-
|
|
79
|
-
- `.swarm/vector-stats.json` (replaced by `.moflo/vector-stats.json` post-#699) is unlinked.
|
|
80
|
-
- Pre-4.8.45 double-prefixed guidance files (`moflo-moflo-*.md`) are unlinked.
|
|
81
|
-
|
|
82
|
-
### 3d-yaml. Append missing top-level sections to `moflo.yaml`
|
|
83
|
-
|
|
84
|
-
`bin/lib/yaml-upgrader.mjs` walks the user's `moflo.yaml` and idempotently appends any registered top-level section that's absent (e.g., `sandbox:`, `auto_update:`, `daemon:`). Never modifies user-set values; never reorders or reformats. Surfaces appended section names so the user can find what changed. (Pattern documented in `internal/upgrade-contract.md` "Yaml upgrade pattern".)
|
|
85
|
-
|
|
86
|
-
### 3d. Install global `flo` shim
|
|
87
|
-
|
|
88
|
-
Best-effort install of a shim into npm's global bin so bare `flo` resolves to the local project's `node_modules/.bin/flo`. Idempotent — skips when already present. Non-fatal on failure: `flo` still works via `npx`.
|
|
89
|
-
|
|
90
|
-
### 3e. Foreground embeddings migration
|
|
91
|
-
|
|
92
|
-
`runEmbeddingsMigrationIfNeeded` checks `.moflo/moflo.db` for embeddings-version drift and, if needed, re-embeds rows in chunks. Runs synchronously with piped stdio so the renderer's progress bar reaches the user. Returns fast (~ms) when no migration is needed.
|
|
93
|
-
|
|
94
|
-
### 3e-728, 3e-729. Hard-purge legacy memory rows
|
|
95
|
-
|
|
96
|
-
- Soft-deleted tombstones (status='deleted' rows from pre-#728 builds) are hard-deleted and the DB is VACUUMed.
|
|
97
|
-
- Ephemeral-namespace rows (`hive-mind`, `tasklist`, `epic-state`, `test-bridge-fix`) accumulated by pre-#729 builds are hard-deleted; going forward, writes to those namespaces skip embedding generation entirely.
|
|
98
|
-
|
|
99
|
-
Both run **before** the daemon is restarted in stage 4 so a concurrent sql.js flush can't clobber the foreground purge.
|
|
100
|
-
|
|
101
|
-
### 3f. Flip upgrade notice to "completed"
|
|
102
|
-
|
|
103
|
-
If stage 3 wrote an "in-progress" upgrade notice for the statusline, it's flipped to "completed" with a 2-minute TTL. The next session-start's stage 0-pre wipes any leftover.
|
|
104
|
-
|
|
105
|
-
### 3g. Commit deferred version stamp
|
|
106
|
-
|
|
107
|
-
`.moflo/moflo-version` is finally written with the installed version. Any abort above leaves the stamp unchanged so the next launcher re-detects the upgrade and re-runs stage 3. ([#730](https://github.com/eric-cielo/moflo/issues/730).)
|
|
108
|
-
|
|
109
|
-
### 4. Spawn background tasks
|
|
110
|
-
|
|
111
|
-
- `node bin/hooks.mjs session-start` is spawned detached + unref'd. That script starts the daemon, the indexer, the pretrain pass, the HNSW build, and the neural-pattern training. None of it blocks the session.
|
|
112
|
-
- Migrations (`run-migrations.mjs`) consult `.moflo/migrations.json` and run any unmigrated steps synchronously, then announce completed migrations via `moflo:` mutation lines.
|
|
113
|
-
|
|
114
|
-
### 5. Exit
|
|
115
|
-
|
|
116
|
-
`process.exit(0)`. Claude Code now begins the session with the launcher's stdout captured as additionalContext.
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Diagnosing slow session starts
|
|
121
|
-
|
|
122
|
-
Bottlenecks, in rough order of likelihood:
|
|
123
|
-
|
|
124
|
-
| Symptom | Likely stage | Mitigation |
|
|
125
|
-
|---------|-------------|------------|
|
|
126
|
-
| Several-second delay on first start after `npm install` | Stage 3 (script + helper + guidance sync) | Expected — one-shot cost. Subsequent starts are ms. |
|
|
127
|
-
| 30–60 s delay on first start after a major upgrade | Stage 3e (embeddings migration) | Surfaces a progress bar via stderr; cannot be skipped. |
|
|
128
|
-
| Repeated multi-second delay on every start | Stage 3 firing every time = manifest drift loop | Check `moflo: repaired stale install` lines on stdout — usually means a file copy keeps failing (Windows AV, file lock). Run `flo doctor --fix`. |
|
|
129
|
-
| Daemon appears to restart on every start | Stage 3a-pre — daemon predates install | Expected if you just upgraded; should stop after one session. |
|
|
130
|
-
|
|
131
|
-
If a stage hangs, the launcher's per-step timeouts (5–30 s depending on the operation) protect against deadlock. A blocked stage that times out lands an error on stderr; the next launcher run retries.
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## Adding a new stage (developer note)
|
|
136
|
-
|
|
137
|
-
Before adding work to the launcher, ask:
|
|
138
|
-
|
|
139
|
-
1. Is this **per-session** work (hook reset, mirror restore) or **per-version** work (script sync, manifest)? Per-version belongs in stage 3 under the version-change gate; per-session goes in stage 2 or as its own gated stage.
|
|
140
|
-
2. Does it touch `sql.js`? It must run **before** stage 4's daemon spawn — concurrent flushes from a long-lived sql.js consumer will clobber a foreground write.
|
|
141
|
-
3. Does it mutate consumer state? It must `emitMutation()` so the user sees what changed via additionalContext. Silent mutations are a regression of the upgrade contract.
|
|
142
|
-
4. Does it depend on a fresh-state invariant (e.g., daemon stopped, manifest written, version stamp committed)? Order matters — see the existing 3 → 3a → 3a-pre → ... → 3g → 4 sequence and slot in accordingly.
|
|
143
|
-
|
|
144
|
-
Full ordering rules and historical violations live in `internal/upgrade-contract.md`.
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## See Also
|
|
149
|
-
|
|
150
|
-
- `moflo-settings-injection.md` — the contract for what moflo writes into `.claude/settings.json` and how the surgical self-heal works.
|
|
151
|
-
- `moflo-core-guidance.md` (Helper Script Auto-Sync) — the file lists for stage 3.
|
|
152
|
-
- `moflo-memorydb-maintenance.md` — what runs against `.moflo/moflo.db` (embeddings, soft-delete, ephemeral purge).
|
|
153
|
-
- Internal-only: `internal/upgrade-contract.md` — the "user must never re-run init" invariant the launcher enforces, with historical violations to learn from.
|
|
154
|
-
- Internal-only: `internal/guidance-sync.md` — Stages 3 and 3b documented end-to-end as part of the three-layer guidance sync (filesystem → DB → HNSW).
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# Spell Engine & Messaging Architecture
|
|
2
|
-
|
|
3
|
-
**Purpose:** Architectural decisions and analysis findings for Epic #100 (Generalized Spell Engine) and Epic #110 (Messaging Migration). Reference when working on stories #100-#117.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Epic #100: Generalized Spell Engine
|
|
8
|
-
|
|
9
|
-
**Ships as part of moflo** — not a separate project. Heavy deps (Playwright, isolated-vm) are optional peer dependencies, same pattern as cli's embeddings module requiring `sql.js`.
|
|
10
|
-
|
|
11
|
-
### Command Pattern for Steps
|
|
12
|
-
|
|
13
|
-
Every spell step type implements a `StepCommand` interface with `execute()`, `validate()`, `describeOutputs()`, and optional `rollback()`. Register step commands via a plugin-style `StepCommandRegistry` — follow the existing `PluginRegistry` pattern in `src/cli/plugins/`.
|
|
14
|
-
|
|
15
|
-
### Spell Definitions
|
|
16
|
-
|
|
17
|
-
Spells are YAML/JSON files in `workflows/` or `.claude/workflows/`. Each declares:
|
|
18
|
-
- `name` and `abbreviation` (short name for `/flo -wf {abbrev}`)
|
|
19
|
-
- Typed `arguments` with `required`, `default`, `enum`, `description`
|
|
20
|
-
- Ordered `steps` with `{stepId.outputKey}` variable interpolation
|
|
21
|
-
|
|
22
|
-
### MoFlo Integration Levels Per Step
|
|
23
|
-
|
|
24
|
-
| Level | Capabilities | Default For |
|
|
25
|
-
|-------|-------------|-------------|
|
|
26
|
-
| `none` | No MoFlo access | `bash`, `condition`, `wait` |
|
|
27
|
-
| `memory` | search/store/list | `agent` steps |
|
|
28
|
-
| `hooks` | Above + pre/post task hooks | — |
|
|
29
|
-
| `full` | Above + swarm/hive-mind spawning | — |
|
|
30
|
-
| `recursive` | Above + nested `/flo -wf` invocation | — |
|
|
31
|
-
|
|
32
|
-
Capabilities only narrow going deeper — a nested spell cannot escalate beyond its parent.
|
|
33
|
-
|
|
34
|
-
### Sandboxing Tiers
|
|
35
|
-
|
|
36
|
-
| Tier | Mechanism | Status |
|
|
37
|
-
|------|-----------|--------|
|
|
38
|
-
| 1 | Capability declarations + enforcement | Build first |
|
|
39
|
-
| 2 | Node `--experimental-permission` for bash steps | Build second |
|
|
40
|
-
| 3 | `isolated-vm` for expression evaluation | Build third |
|
|
41
|
-
| 4 | Linux namespaces (optional) | Defer |
|
|
42
|
-
| 5 | WASM sandbox for community commands | Defer |
|
|
43
|
-
|
|
44
|
-
### Build Order (no circular dependencies)
|
|
45
|
-
|
|
46
|
-
| Phase | Stories | Can Start |
|
|
47
|
-
|-------|---------|-----------|
|
|
48
|
-
| 1 | #101 (Interface), #103 (Schema) | Immediately |
|
|
49
|
-
| 2 | #102 (Commands), #106 (Credentials) | After Phase 1 |
|
|
50
|
-
| 3 | #104 (Runner) | After Phase 2 |
|
|
51
|
-
| 4 | #105 (Registry), #108 (Sandboxing T1-2), #107 (Playwright) | After Phase 3 |
|
|
52
|
-
| 5 | #109 (Integration Levels), #117 (Scheduling) | After Phase 4 + Epic #110 |
|
|
53
|
-
|
|
54
|
-
### Critical Findings
|
|
55
|
-
|
|
56
|
-
- **Existing `spell_execute` is a no-op placeholder.** The engine is greenfield.
|
|
57
|
-
- **`js-yaml` already a dependency** — used in `moflo-config.ts` and `epic.ts`.
|
|
58
|
-
- **Plugin registry pattern is directly reusable** for `StepCommandRegistry`.
|
|
59
|
-
- **No cron support exists** — needs `node-cron` dependency for #117.
|
|
60
|
-
- **Retry logic is consumer-side**, not bus-side. Steps implement their own retry.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Epic #110: Messaging Migration to Memory DB
|
|
65
|
-
|
|
66
|
-
**Migrate inter-agent messaging** from in-memory EventEmitter bus to memory DB namespace. The current bus only works within a single process — spawned subagents cannot participate.
|
|
67
|
-
|
|
68
|
-
### Session Isolation
|
|
69
|
-
|
|
70
|
-
All messages scoped by `sessionId`. Queries implicitly filter by current session. `endSession()` expires unhandled messages. `gc()` cleans up abandoned sessions after configurable TTL.
|
|
71
|
-
|
|
72
|
-
### Build Order
|
|
73
|
-
|
|
74
|
-
| Phase | Stories |
|
|
75
|
-
|-------|---------|
|
|
76
|
-
| 1 | #111 (Schema + CRUD) |
|
|
77
|
-
| 2 | #112 (Notification/Pub-Sub) |
|
|
78
|
-
| 3 | #113, #114, #115 (parallel) |
|
|
79
|
-
| 4 | #116 (Deprecate old bus — last) |
|
|
80
|
-
|
|
81
|
-
**Minimum viable to unblock Epic #100:** #111 + #112 only.
|
|
82
|
-
|
|
83
|
-
### Critical Findings
|
|
84
|
-
|
|
85
|
-
- **`queen-coordinator.ts` does NOT use MessageBus.** Hive-mind operates via MCP tools. Story #114 is greenfield, not migration.
|
|
86
|
-
- **`unified-coordinator.ts` is coupled to concrete `MessageBus` class**, not `IMessageBus` interface. Must refactor.
|
|
87
|
-
- **Zero existing message bus tests.** Write baseline suite first, then verify both backends pass.
|
|
88
|
-
- **Two competing Message types:** `SwarmMessage` (shared/types.ts) vs `Message` (swarm/types.ts). Story #111 must reconcile.
|
|
89
|
-
- **Memory DB supports namespaces and TTL** but needs queue semantics and metadata indexing added.
|
|
90
|
-
- **`enablePersistence` config flag exists but was never implemented** — now being properly built.
|
|
91
|
-
|
|
92
|
-
### Interface Incompatibility: Not a Clean Swap
|
|
93
|
-
|
|
94
|
-
The `MemoryDbMessageBus` adapter (~300 lines) must handle:
|
|
95
|
-
|
|
96
|
-
| Aspect | Current `IMessageBus` | New `MessageStore` |
|
|
97
|
-
|--------|----------------------|-------------------|
|
|
98
|
-
| Subscribe | Push-based callback | Pull-based `receive()` |
|
|
99
|
-
| Acknowledge | `acknowledge(ack)` | `markRead()` |
|
|
100
|
-
| Timestamps | `Date` objects | `number` (epoch ms) |
|
|
101
|
-
| Sessions | None | `sessionId` required |
|
|
102
|
-
| Channels | None (per-agent queues) | Channel-based routing |
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Epic #118: Consolidate Messaging Systems (Pre-req for #110)
|
|
107
|
-
|
|
108
|
-
**Three messaging systems → one layered broker.** Must complete before Epic #110 migration.
|
|
109
|
-
|
|
110
|
-
| Story | What | Depends On |
|
|
111
|
-
|-------|------|------------|
|
|
112
|
-
| #119 | Unified Message Type & Extended IMessageBus | — |
|
|
113
|
-
| #120 | SwarmCommunication → MessageBus consumer | #119 |
|
|
114
|
-
| #121 | Hive-Mind → MessageBus + Memory DB write-through | #119, #120 |
|
|
115
|
-
|
|
116
|
-
**Key decisions:**
|
|
117
|
-
- MessageBus keeps its Deque hot path for delivery (~1000+ msg/sec)
|
|
118
|
-
- Memory DB write-through is fire-and-forget, initially only for hive-mind namespace
|
|
119
|
-
- TTL reaper lives in the broker (60s sweep), not the daemon
|
|
120
|
-
- Daemon can do deeper GC pass but system must not depend on it
|
|
121
|
-
|
|
122
|
-
**Build order:** Epic #118 → remaining #110 stories (#111, #115, #116) → Epic #100 Phase 5.
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Cross-Epic Dependencies
|
|
127
|
-
|
|
128
|
-
**Most of Epic #100 can proceed without Epic #110.** Only two things are hard-blocked:
|
|
129
|
-
|
|
130
|
-
| Blocked Story | Needs from #110 | Why |
|
|
131
|
-
|--------------|-----------------|-----|
|
|
132
|
-
| #109 `full` tier | #113 (Swarm Migration) | Swarm spawning needs DB-backed messaging |
|
|
133
|
-
| #109 `recursive` tier | #113 + #114 | Nested spells with hive-mind need consensus messaging |
|
|
134
|
-
|
|
135
|
-
Stories #101-#108, #105, #117 have no dependency on Epic #110.
|
|
136
|
-
|
|
137
|
-
**Epic #118 simplifies #110:** #112 (pub/sub) largely solved, #113 (swarm) done via #120, #114 (hive-mind) done via #121.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## See Also
|
|
142
|
-
|
|
143
|
-
- `.claude/guidance/shipped/moflo-core-guidance.md` — CLI, hooks, swarm, memory reference
|
|
144
|
-
- `.claude/guidance/shipped/moflo-claude-swarm-cohesion.md` — Task & swarm coordination
|
|
145
|
-
- `.claude/guidance/shipped/moflo-subagents.md` — Subagents protocol
|