moflo 4.9.0-rc.8 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.claude/helpers/auto-memory-hook.mjs +1 -1
  2. package/.claude/helpers/gate-hook.mjs +7 -0
  3. package/.claude/helpers/gate.cjs +100 -16
  4. package/.claude/helpers/intelligence.cjs +3 -3
  5. package/.claude/helpers/statusline.cjs +73 -50
  6. package/.claude/helpers/subagent-bootstrap.json +3 -0
  7. package/.claude/helpers/subagent-start.cjs +58 -22
  8. package/.claude/skills/fl/SKILL.md +86 -181
  9. package/.claude/skills/fl/epic.md +23 -33
  10. package/.claude/skills/fl/execution-modes.md +17 -17
  11. package/.claude/skills/fl/phases.md +48 -65
  12. package/.claude/skills/fl/ticket.md +46 -51
  13. package/.claude/skills/hive-mind-advanced/SKILL.md +4 -4
  14. package/.claude/skills/memory-patterns/SKILL.md +3 -3
  15. package/.claude/skills/swarm-advanced/SKILL.md +77 -0
  16. package/.claude/skills/vector-search/SKILL.md +1 -1
  17. package/README.md +68 -93
  18. package/bin/build-embeddings.mjs +17 -10
  19. package/bin/gate-hook.mjs +7 -0
  20. package/bin/gate.cjs +100 -16
  21. package/bin/index-all.mjs +20 -2
  22. package/bin/lib/moflo-paths.mjs +53 -4
  23. package/bin/migrations/knowledge-purge.mjs +107 -0
  24. package/bin/migrations/knowledge-to-learnings.mjs +27 -15
  25. package/bin/migrations/lib/markers.mjs +11 -0
  26. package/bin/run-migrations.mjs +13 -2
  27. package/bin/session-start-launcher.mjs +150 -37
  28. package/dist/src/cli/commands/analyze.js +10 -9
  29. package/dist/src/cli/commands/appliance-advanced.js +9 -11
  30. package/dist/src/cli/commands/appliance.js +7 -9
  31. package/dist/src/cli/commands/benchmark.js +4 -3
  32. package/dist/src/cli/commands/daemon.js +21 -25
  33. package/dist/src/cli/commands/deployment.js +1 -1
  34. package/dist/src/cli/commands/diagnose.js +10 -9
  35. package/dist/src/cli/commands/doctor-checks-deep.js +40 -21
  36. package/dist/src/cli/commands/doctor-checks-functional-shared.js +111 -0
  37. package/dist/src/cli/commands/doctor-checks-memory-access.js +307 -0
  38. package/dist/src/cli/commands/doctor-checks-swarm.js +364 -0
  39. package/dist/src/cli/commands/doctor.js +191 -56
  40. package/dist/src/cli/commands/embeddings.js +32 -52
  41. package/dist/src/cli/commands/epic.js +90 -20
  42. package/dist/src/cli/commands/github.js +12 -11
  43. package/dist/src/cli/commands/guidance.js +14 -13
  44. package/dist/src/cli/commands/hive-mind.js +10 -9
  45. package/dist/src/cli/commands/hooks.js +14 -13
  46. package/dist/src/cli/commands/init.js +11 -10
  47. package/dist/src/cli/commands/issues.js +4 -3
  48. package/dist/src/cli/commands/memory.js +47 -8
  49. package/dist/src/cli/commands/neural.js +13 -12
  50. package/dist/src/cli/commands/process.js +2 -2
  51. package/dist/src/cli/commands/progress.js +2 -1
  52. package/dist/src/cli/commands/route.js +10 -9
  53. package/dist/src/cli/commands/session.js +7 -7
  54. package/dist/src/cli/commands/spell-schedule.js +10 -57
  55. package/dist/src/cli/commands/start.js +3 -2
  56. package/dist/src/cli/commands/status.js +4 -4
  57. package/dist/src/cli/commands/task.js +1 -1
  58. package/dist/src/cli/commands/update.js +4 -4
  59. package/dist/src/cli/config/moflo-config.js +0 -3
  60. package/dist/src/cli/embeddings/fastembed-embedding-service.js +4 -3
  61. package/dist/src/cli/embeddings/fastembed-inline/model-loader.js +27 -5
  62. package/dist/src/cli/embeddings/index.js +1 -1
  63. package/dist/src/cli/embeddings/migration/migrate-store.js +3 -2
  64. package/dist/src/cli/embeddings/neural-integration.js +0 -9
  65. package/dist/src/cli/epic/index.js +1 -2
  66. package/dist/src/cli/epic/types.js +1 -6
  67. package/dist/src/cli/guidance/hooks.js +3 -2
  68. package/dist/src/cli/hooks/daemons/index.js +3 -2
  69. package/dist/src/cli/hooks/executor/index.js +2 -1
  70. package/dist/src/cli/hooks/workers/index.js +2 -1
  71. package/dist/src/cli/hooks/workers/session-hook.js +2 -1
  72. package/dist/src/cli/index.js +17 -8
  73. package/dist/src/cli/init/executor.js +116 -89
  74. package/dist/src/cli/init/helpers-generator.js +92 -15
  75. package/dist/src/cli/init/moflo-init.js +66 -56
  76. package/dist/src/cli/init/settings-generator.js +12 -2
  77. package/dist/src/cli/mcp-client.js +2 -1
  78. package/dist/src/cli/mcp-server.js +2 -1
  79. package/dist/src/cli/mcp-tools/agent-tools.js +332 -211
  80. package/dist/src/cli/mcp-tools/coordinator-views.js +23 -0
  81. package/dist/src/cli/mcp-tools/github-tools.js +2 -1
  82. package/dist/src/cli/mcp-tools/hive-mind-tools.js +145 -49
  83. package/dist/src/cli/mcp-tools/hooks-tools.js +8 -6
  84. package/dist/src/cli/mcp-tools/json-store.js +9 -6
  85. package/dist/src/cli/mcp-tools/memory-tools.js +4 -1
  86. package/dist/src/cli/mcp-tools/neural-tools.js +4 -2
  87. package/dist/src/cli/mcp-tools/session-tools.js +11 -7
  88. package/dist/src/cli/mcp-tools/spell-tools.js +4 -7
  89. package/dist/src/cli/mcp-tools/swarm-coordinator-singleton.js +119 -0
  90. package/dist/src/cli/mcp-tools/swarm-scale-handler.js +211 -0
  91. package/dist/src/cli/mcp-tools/swarm-tools.js +208 -27
  92. package/dist/src/cli/mcp-tools/task-tools.js +299 -166
  93. package/dist/src/cli/memory/bridge-core.js +2 -1
  94. package/dist/src/cli/memory/bridge-embedder.js +2 -1
  95. package/dist/src/cli/memory/bridge-entries.js +25 -14
  96. package/dist/src/cli/memory/controller-registry.js +3 -2
  97. package/dist/src/cli/memory/controllers/_shared.js +20 -0
  98. package/dist/src/cli/memory/controllers/nightly-learner.js +2 -1
  99. package/dist/src/cli/memory/ewc-consolidation.js +2 -1
  100. package/dist/src/cli/memory/hnsw-lite.js +142 -1
  101. package/dist/src/cli/memory/hnsw-persistence.js +101 -0
  102. package/dist/src/cli/memory/index.js +1 -0
  103. package/dist/src/cli/memory/intelligence.js +2 -1
  104. package/dist/src/cli/memory/memory-bridge.js +3 -1
  105. package/dist/src/cli/memory/memory-initializer.js +51 -65
  106. package/dist/src/cli/parser.js +27 -40
  107. package/dist/src/cli/plugins/manager.js +5 -4
  108. package/dist/src/cli/runtime/headless.js +2 -1
  109. package/dist/src/cli/services/daemon-dashboard.js +4 -3
  110. package/dist/src/cli/services/daemon-readiness.js +5 -8
  111. package/dist/src/cli/services/daemon-service.js +12 -7
  112. package/dist/src/cli/services/daemon-spell-executor.js +3 -2
  113. package/dist/src/cli/services/headless-worker-executor.js +2 -1
  114. package/dist/src/cli/services/hook-wiring.js +6 -0
  115. package/dist/src/cli/services/index.js +2 -0
  116. package/dist/src/cli/services/moflo-paths.js +72 -6
  117. package/dist/src/cli/services/moflo-require.js +3 -0
  118. package/dist/src/cli/services/movector-training.js +9 -16
  119. package/dist/src/cli/services/spell-gate.js +1 -1
  120. package/dist/src/cli/services/subagent-bootstrap.js +57 -0
  121. package/dist/src/cli/services/worker-daemon.js +5 -4
  122. package/dist/src/cli/services/worker-queue.js +4 -3
  123. package/dist/src/cli/shared/mcp/tool-registry.js +2 -1
  124. package/dist/src/cli/shared/plugins/official/maestro-plugin.js +3 -2
  125. package/dist/src/cli/shared/utils/error-detail.js +18 -0
  126. package/dist/src/cli/spells/commands/composite-command.js +2 -1
  127. package/dist/src/cli/spells/commands/github-command.js +5 -5
  128. package/dist/src/cli/spells/connectors/github-cli.js +32 -35
  129. package/dist/src/cli/spells/connectors/http-tool.js +2 -1
  130. package/dist/src/cli/spells/connectors/imap.js +2 -1
  131. package/dist/src/cli/spells/connectors/slack.js +2 -1
  132. package/dist/src/cli/spells/core/connector-accessor.js +2 -1
  133. package/dist/src/cli/spells/core/dry-run-validator.js +4 -2
  134. package/dist/src/cli/spells/core/gated-connector-accessor.js +2 -1
  135. package/dist/src/cli/spells/core/interpolation.js +7 -0
  136. package/dist/src/cli/spells/core/platform-sandbox.js +80 -59
  137. package/dist/src/cli/spells/core/prerequisite-checker.js +8 -3
  138. package/dist/src/cli/spells/core/rollback-orchestrator.js +2 -1
  139. package/dist/src/cli/spells/core/runner.js +19 -5
  140. package/dist/src/cli/spells/core/shell.js +19 -1
  141. package/dist/src/cli/spells/core/step-executor.js +4 -3
  142. package/dist/src/cli/spells/factory/runner-factory.js +2 -1
  143. package/dist/src/cli/spells/loaders/definition-loader.js +2 -1
  144. package/dist/src/cli/spells/loaders/directory-step-loader.js +2 -1
  145. package/dist/src/cli/spells/loaders/npm-step-loader.js +2 -1
  146. package/dist/src/cli/spells/registry/connector-registry.js +3 -2
  147. package/dist/src/cli/spells/scheduler/scheduler.js +2 -1
  148. package/dist/src/cli/swarm/swarm-persistence.js +144 -0
  149. package/dist/src/cli/swarm/unified-coordinator.js +260 -66
  150. package/dist/src/cli/transfer/ipfs/client.js +3 -2
  151. package/dist/src/cli/transfer/serialization/cfp.js +2 -1
  152. package/dist/src/cli/version.js +1 -1
  153. package/package.json +6 -5
  154. package/dist/src/cli/epic/execution-order.js +0 -58
  155. /package/{dist/src/cli/epic/spells/auto-merge.yaml → src/cli/spells/definitions/epic-auto-merge.yaml} +0 -0
  156. /package/{dist/src/cli/epic/spells/single-branch.yaml → src/cli/spells/definitions/epic-single-branch.yaml} +0 -0
@@ -16,7 +16,7 @@ import { fileURLToPath } from 'url';
16
16
  const __filename = fileURLToPath(import.meta.url);
17
17
  const __dirname = dirname(__filename);
18
18
  const PROJECT_ROOT = join(__dirname, '../..');
19
- const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
19
+ const DATA_DIR = join(PROJECT_ROOT, '.moflo', 'data');
20
20
  const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
21
21
 
22
22
  const DIM = '\x1b[2m';
@@ -25,6 +25,13 @@ try { if (stdinData.trim()) hookContext = JSON.parse(stdinData); } catch (e) {}
25
25
  // Pass tool info as env vars for gate.cjs
26
26
  var env = Object.assign({}, process.env);
27
27
  if (hookContext.tool_name) env.TOOL_NAME = hookContext.tool_name;
28
+ // Forward Claude Code's session_id so gate.cjs can enforce memory-first
29
+ // per-actor (#838) — each spawned subagent gets its own session_id, so a
30
+ // shared workflow-state.json no longer lets one subagent's directive be
31
+ // silently satisfied by the parent's earlier search.
32
+ if (typeof hookContext.session_id === 'string' && hookContext.session_id) {
33
+ env.HOOK_SESSION_ID = hookContext.session_id;
34
+ }
28
35
  if (hookContext.tool_input && typeof hookContext.tool_input === 'object') {
29
36
  Object.keys(hookContext.tool_input).forEach(function(key) {
30
37
  if (typeof hookContext.tool_input[key] === 'string') {
@@ -6,7 +6,37 @@ var path = require('path');
6
6
  var PROJECT_DIR = (process.env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\/([a-z])\//i, '$1:/');
7
7
  var STATE_FILE = path.join(PROJECT_DIR, '.claude', 'workflow-state.json');
8
8
 
9
- var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, learningsStored: false, interactionCount: 0, sessionStart: null, lastBlockedAt: null };
9
+ var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, interactionCount: 0, sessionStart: null, lastBlockedAt: null };
10
+
11
+ // Per-actor memory-search tracking (#838). The legacy `memorySearched` boolean
12
+ // is session-wide, so once the parent searches memory, every spawned subagent
13
+ // inherits the satisfied flag and the directive's "WILL BLOCK" promise becomes
14
+ // false. When gate-hook.mjs forwards Claude Code's stdin `session_id` as
15
+ // HOOK_SESSION_ID, prefer the per-session map so each subagent must search
16
+ // memory itself before its first Glob/Grep/Read. Falls back to the legacy
17
+ // boolean when no session id is present (CLI invocations, tests, older hosts).
18
+ function isMemorySearchedFor(state) {
19
+ var sid = process.env.HOOK_SESSION_ID || '';
20
+ if (sid) {
21
+ var map = state.memorySearchedBy || {};
22
+ return map[sid] === true;
23
+ }
24
+ return state.memorySearched === true;
25
+ }
26
+
27
+ // Stamp the legacy bool plus (when HOOK_SESSION_ID is set) the per-actor map.
28
+ // Returns true if anything actually changed — callers gate writeState() on it
29
+ // to avoid redundant fsyncs in tight bash-memory loops.
30
+ function markMemorySearched(state) {
31
+ var sid = process.env.HOOK_SESSION_ID || '';
32
+ var changed = false;
33
+ if (state.memorySearched !== true) { state.memorySearched = true; changed = true; }
34
+ if (sid) {
35
+ if (!state.memorySearchedBy) state.memorySearchedBy = {};
36
+ if (state.memorySearchedBy[sid] !== true) { state.memorySearchedBy[sid] = true; changed = true; }
37
+ }
38
+ return changed;
39
+ }
10
40
 
11
41
  function readState() {
12
42
  try {
@@ -29,7 +59,7 @@ function writeState(s) {
29
59
 
30
60
  // Load moflo.yaml gate config (defaults: all enabled)
31
61
  function loadGateConfig() {
32
- var defaults = { memory_first: true, task_create_first: true, context_tracking: true };
62
+ var defaults = { memory_first: true, task_create_first: true, context_tracking: true, testing_gate: true, simplify_gate: true, learnings_gate: true };
33
63
  try {
34
64
  var yamlPath = path.join(PROJECT_DIR, 'moflo.yaml');
35
65
  if (fs.existsSync(yamlPath)) {
@@ -37,6 +67,9 @@ function loadGateConfig() {
37
67
  if (/memory_first:\s*false/i.test(content)) defaults.memory_first = false;
38
68
  if (/task_create_first:\s*false/i.test(content)) defaults.task_create_first = false;
39
69
  if (/context_tracking:\s*false/i.test(content)) defaults.context_tracking = false;
70
+ if (/testing_gate:\s*false/i.test(content)) defaults.testing_gate = false;
71
+ if (/simplify_gate:\s*false/i.test(content)) defaults.simplify_gate = false;
72
+ if (/learnings_gate:\s*false/i.test(content)) defaults.learnings_gate = false;
40
73
  }
41
74
  } catch (e) { /* use defaults */ }
42
75
  return defaults;
@@ -49,6 +82,13 @@ var EXEMPT = ['.claude/', '.claude\\', 'CLAUDE.md', 'MEMORY.md', 'workflow-state
49
82
  var DANGEROUS = ['rm -rf /', 'format c:', 'del /s /q c:\\', ':(){:|:&};:', 'mkfs.', '> /dev/sda'];
50
83
  var DIRECTIVE_RE = /^(yes|no|yeah|yep|nope|sure|ok|okay|correct|right|exactly|perfect)\b/i;
51
84
  var TASK_RE = /\b(fix|bug|error|implement|add|create|build|write|refactor|debug|test|feature|issue|security|optimi)\b/i;
85
+ // Match npm/yarn/pnpm/bun test, npx vitest|jest|..., bare runners at command-start only,
86
+ // and language-native test commands. The bare-runner arm is anchored so that
87
+ // `npm install jest`, `grep -r vitest src/`, and similar don't false-positive.
88
+ var TEST_RUNNER_RE = /(?:^|[^a-z])(?:npm|yarn|pnpm|bun)\s+(?:run\s+)?(?:test|t)(?:[:\s]|$)|\b(?:npx|pnpx)\s+(?:vitest|jest|mocha|ava|tap|jasmine|pytest)\b|(?:^|;|&&|\|\|)\s*(?:vitest|jest|pytest|mocha|jasmine|tap|ava)\s|\b(?:cargo|go|deno|dotnet|mvn)\s+test\b|\bgradle\w*\s+test\b/i;
89
+ // Edits to these don't change runtime behaviour, so they don't invalidate prior test/simplify runs.
90
+ // Lock files and .gitignore are tracked but inert; package.json/*.yaml ARE source — they reset.
91
+ var EDIT_RESET_SKIP_RE = /\.(md|markdown|txt|rst|adoc|lock|gitignore)$|(?:^|[\\\/])(CHANGELOG(?:\.md)?|\.env\.example|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb)$/i;
52
92
 
53
93
  switch (command) {
54
94
  case 'check-before-agent': {
@@ -67,7 +107,7 @@ switch (command) {
67
107
  case 'check-before-scan': {
68
108
  if (!config.memory_first) break;
69
109
  var s = readState();
70
- if (s.memorySearched || !s.memoryRequired) break;
110
+ if (!s.memoryRequired || isMemorySearchedFor(s)) break;
71
111
  var target = (process.env.TOOL_INPUT_pattern || '') + ' ' + (process.env.TOOL_INPUT_path || '');
72
112
  if (EXEMPT.some(function(p) { return target.indexOf(p) >= 0; })) break;
73
113
  process.stderr.write('BLOCKED: Search memory before exploring files. Use mcp__moflo__memory_search.\n');
@@ -76,7 +116,7 @@ switch (command) {
76
116
  case 'check-before-read': {
77
117
  if (!config.memory_first) break;
78
118
  var s = readState();
79
- if (s.memorySearched || !s.memoryRequired) break;
119
+ if (!s.memoryRequired || isMemorySearchedFor(s)) break;
80
120
  var fp = process.env.TOOL_INPUT_file_path || '';
81
121
  var isGuidance = fp.indexOf('.claude/guidance/') >= 0 || fp.indexOf('.claude\\guidance\\') >= 0;
82
122
  if (!isGuidance && EXEMPT.some(function(p) { return fp.indexOf(p) >= 0; })) break;
@@ -92,16 +132,14 @@ switch (command) {
92
132
  }
93
133
  case 'record-memory-searched': {
94
134
  var s = readState();
95
- s.memorySearched = true;
96
- writeState(s);
135
+ if (markMemorySearched(s)) writeState(s);
97
136
  break;
98
137
  }
99
138
  case 'check-bash-memory': {
100
139
  var cmd = process.env.TOOL_INPUT_command || '';
101
140
  if (/semantic-search|memory search|memory retrieve|memory-search/.test(cmd)) {
102
141
  var s = readState();
103
- s.memorySearched = true;
104
- writeState(s);
142
+ if (markMemorySearched(s)) writeState(s);
105
143
  }
106
144
  break;
107
145
  }
@@ -113,21 +151,64 @@ switch (command) {
113
151
  }
114
152
  case 'record-learnings-stored': {
115
153
  var s = readState();
116
- s.learningsStored = true;
117
- writeState(s);
154
+ if (!s.learningsStored) {
155
+ s.learningsStored = true;
156
+ writeState(s);
157
+ }
118
158
  break;
119
159
  }
120
- case 'check-before-pr': {
160
+ case 'record-test-run': {
121
161
  var cmd = process.env.TOOL_INPUT_command || '';
122
- if (/gh\s+pr\s+create/.test(cmd)) {
162
+ if (TEST_RUNNER_RE.test(cmd)) {
163
+ var s = readState();
164
+ if (!s.testsRun) {
165
+ s.testsRun = true;
166
+ writeState(s);
167
+ }
168
+ }
169
+ break;
170
+ }
171
+ case 'record-skill-run': {
172
+ if ((process.env.TOOL_INPUT_skill || '') === 'simplify') {
123
173
  var s = readState();
124
- if (!s.learningsStored && (s.interactionCount || 0) > 5) {
125
- // Advisory for long sessions — remind but don't block
126
- process.stdout.write('ADVISORY: Consider storing learnings (mcp__moflo__memory_store) before creating a PR — this was a substantial session.\n');
174
+ if (!s.simplifyRun) {
175
+ s.simplifyRun = true;
176
+ writeState(s);
127
177
  }
128
178
  }
129
179
  break;
130
180
  }
181
+ case 'reset-edit-gates': {
182
+ var fp = process.env.TOOL_INPUT_file_path || '';
183
+ if (fp && EDIT_RESET_SKIP_RE.test(fp)) break;
184
+ var s = readState();
185
+ if (!s.testsRun && !s.simplifyRun) break;
186
+ s.testsRun = false;
187
+ s.simplifyRun = false;
188
+ writeState(s);
189
+ break;
190
+ }
191
+ case 'check-before-pr': {
192
+ // Anchored to command-start (or chained via && / || / ;) so heredoc bodies
193
+ // and quoted strings that contain the literal "gh pr create" don't trip
194
+ // the gate during regular `git commit -m "...gh pr create..."` flows. The
195
+ // optional ENV=val prefix segment catches `GH_TOKEN=x gh pr create`.
196
+ var cmd = process.env.TOOL_INPUT_command || '';
197
+ if (!/(?:^|&&\s*|\|\|\s*|;\s*)\s*(?:[A-Z_][A-Z0-9_]*=\S+\s+)*gh\s+pr\s+create\b/.test(cmd)) break;
198
+ var s = readState();
199
+ var missing = [];
200
+ if (config.testing_gate && !s.testsRun) missing.push('tests have not run since the last code edit (run npm test, vitest, jest, pytest, or similar)');
201
+ if (config.simplify_gate && !s.simplifyRun) missing.push('/simplify has not run since the last code edit');
202
+ if (config.learnings_gate && !s.learningsStored) missing.push('learnings have not been stored (call mcp__moflo__memory_store)');
203
+ if (missing.length === 0) break;
204
+ process.stderr.write('BLOCKED: gh pr create requires the following before opening a PR:\n');
205
+ for (var i = 0; i < missing.length; i++) {
206
+ process.stderr.write(' - ' + missing[i] + '\n');
207
+ }
208
+ process.stderr.write('Disable per-gate via moflo.yaml:\n');
209
+ process.stderr.write(' gates:\n testing_gate: false\n simplify_gate: false\n learnings_gate: false\n');
210
+ process.exit(2);
211
+ }
131
212
  case 'check-dangerous-command': {
132
213
  var cmd = (process.env.TOOL_INPUT_command || '').toLowerCase();
133
214
  for (var i = 0; i < DANGEROUS.length; i++) {
@@ -141,6 +222,9 @@ switch (command) {
141
222
  case 'prompt-reminder': {
142
223
  var s = readState();
143
224
  s.memorySearched = false;
225
+ // Wipe per-actor memory tracking too — a new user prompt is a fresh window
226
+ // for both parent AND any subagents the parent may spawn during this turn.
227
+ s.memorySearchedBy = {};
144
228
  // learningsStored is session-scoped — once stored, it stays true until session reset.
145
229
  // Resetting per-prompt caused false blocks when PR creation was on a later prompt.
146
230
  var prompt = process.env.CLAUDE_USER_PROMPT || '';
@@ -163,7 +247,7 @@ switch (command) {
163
247
  break;
164
248
  }
165
249
  case 'session-reset': {
166
- writeState({ tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, learningsStored: false, interactionCount: 0, sessionStart: new Date().toISOString(), lastBlockedAt: null });
250
+ writeState({ tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, interactionCount: 0, sessionStart: new Date().toISOString(), lastBlockedAt: null });
167
251
  break;
168
252
  }
169
253
  default:
@@ -11,11 +11,11 @@ const path = require('path');
11
11
  const os = require('os');
12
12
 
13
13
  const PROJECT_ROOT = (process.env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\/([a-z])\//i, '$1:/');
14
- const DATA_DIR = path.join(PROJECT_ROOT, '.claude-flow', 'data');
14
+ const DATA_DIR = path.join(PROJECT_ROOT, '.moflo', 'data');
15
15
  const STORE_PATH = path.join(DATA_DIR, 'auto-memory-store.json');
16
16
  const RANKED_PATH = path.join(DATA_DIR, 'ranked-context.json');
17
17
  const PENDING_PATH = path.join(DATA_DIR, 'pending-insights.jsonl');
18
- const SESSION_DIR = path.join(PROJECT_ROOT, '.claude-flow', 'sessions');
18
+ const SESSION_DIR = path.join(PROJECT_ROOT, '.moflo', 'sessions');
19
19
  const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
20
20
 
21
21
  function ensureDir(dir) {
@@ -55,7 +55,7 @@ function bootstrapFromMemoryFiles() {
55
55
  var entries = [];
56
56
  var candidates = [
57
57
  path.join(os.homedir(), ".claude", "projects"),
58
- path.join(PROJECT_ROOT, ".claude-flow", "memory"),
58
+ path.join(PROJECT_ROOT, ".moflo", "memory"),
59
59
  path.join(PROJECT_ROOT, ".claude", "memory"),
60
60
  ];
61
61
  for (var i = 0; i < candidates.length; i++) {
@@ -51,7 +51,6 @@ function loadStatusLineConfig() {
51
51
  show_mcp: true,
52
52
  show_security: true,
53
53
  show_adrs: true,
54
- show_agentdb: true,
55
54
  show_tests: true,
56
55
  mode: 'compact',
57
56
  };
@@ -383,7 +382,7 @@ function getSwarmStatus() {
383
382
  function getSystemMetrics() {
384
383
  const memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
385
384
  const learning = getLearningStats();
386
- const agentdb = getAgentDBStats();
385
+ const embeddings = getEmbeddingsStats();
387
386
 
388
387
  // Intelligence from learning.json
389
388
  const learningData = readJSON(path.join(CWD, '.moflo', 'metrics', 'learning.json'));
@@ -394,7 +393,7 @@ function getSystemMetrics() {
394
393
  intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score));
395
394
  } else {
396
395
  const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0;
397
- const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 100)) : 0;
396
+ const fromVectors = embeddings.vectorCount > 0 ? Math.min(100, Math.floor(embeddings.vectorCount / 100)) : 0;
398
397
  intelligencePct = Math.max(fromPatterns, fromVectors);
399
398
  }
400
399
 
@@ -424,7 +423,7 @@ function getSystemMetrics() {
424
423
  subAgents = activityData.processes.estimated_agents;
425
424
  }
426
425
 
427
- return { memoryMB, contextPct, intelligencePct, subAgents };
426
+ return { memoryMB, contextPct, intelligencePct, subAgents, embeddings };
428
427
  }
429
428
 
430
429
  // ADR status (count files only — don't read contents)
@@ -485,9 +484,9 @@ function getHooksStatus() {
485
484
  return { enabled, total };
486
485
  }
487
486
 
488
- // AgentDB stats — reads from cache file written by embedding/memory operations.
487
+ // Embeddings stats — reads from cache file written by embedding/memory ops.
489
488
  // No subprocess spawning. Falls back to DB file size estimate if cache is missing.
490
- function getAgentDBStats() {
489
+ function getEmbeddingsStats() {
491
490
  let vectorCount = 0;
492
491
  let dbSizeKB = 0;
493
492
  let namespaces = 0;
@@ -602,20 +601,25 @@ function getIntegrationStatus() {
602
601
  return { mcpServers, hasDatabase, hasApi };
603
602
  }
604
603
 
605
- // Upgrade notice (#636, #738, #743) — written by the session-start launcher
606
- // ONLY while upgrade work is in flight; the launcher deletes the file when
607
- // work completes. We render it strictly for status='in-progress' so a stale
608
- // notice (legacy "complete" file from pre-#738 launchers, zombie write from
609
- // an aborted launcher, future writer mistakes) cannot turn the statusline
610
- // segment into a permanent column. The launcher's section 0-pre also drops
611
- // any leftover file at session start as a second line of defence.
604
+ // Upgrade notice (#636, #738, #743) — written by the session-start launcher.
605
+ // status='in-progress' work is running; rendered with "(updating…)".
606
+ // status='completed' — work just finished; short-TTL post-upgrade badge so
607
+ // the user sees something on the very next render
608
+ // (Claude Code only paints the statusline AFTER the
609
+ // SessionStart hook returns, so the in-progress badge
610
+ // has effectively zero visibility window).
611
+ // Anything else is dropped (legacy "complete" pre-#738 files, zombie writes,
612
+ // future writer mistakes) so a stale notice can never turn the segment into a
613
+ // permanent column. Section 0-pre of the launcher also wipes any leftover at
614
+ // session start as a second line of defence.
612
615
  function getUpgradeNotice() {
613
616
  const data = readJSON(path.join(CWD, '.moflo', 'upgrade-notice.json'));
614
617
  if (!data || typeof data !== 'object') return null;
615
- if (data.status !== 'in-progress') return null;
618
+ if (data.status !== 'in-progress' && data.status !== 'completed') return null;
616
619
  const expiresAt = data.expiresAt ? new Date(data.expiresAt).getTime() : 0;
617
620
  if (!expiresAt || Date.now() > expiresAt) return null;
618
621
  return {
622
+ status: data.status,
619
623
  kind: data.kind === 'repair' ? 'repair' : 'upgrade',
620
624
  from: typeof data.from === 'string' ? data.from : '',
621
625
  to: typeof data.to === 'string' ? data.to : '',
@@ -624,14 +628,20 @@ function getUpgradeNotice() {
624
628
 
625
629
  function formatUpgradeNoticeSegment(notice) {
626
630
  if (!notice) return '';
627
- const suffix = ` ${c.dim}(updating…)${c.reset}`;
631
+ const inFlight = notice.status === 'in-progress';
632
+ const suffix = inFlight ? ` ${c.dim}(updating…)${c.reset}` : '';
633
+ // Pick body text: repair > in-flight version range > completed "upgraded to"
634
+ // > bare "upgraded" fallback when no version is known.
635
+ let body;
628
636
  if (notice.kind === 'repair') {
629
- return `${c.brightYellow}📦 install repaired${c.reset}${suffix}`;
637
+ body = 'install repaired';
638
+ } else if (inFlight) {
639
+ body = notice.from && notice.to ? `${notice.from} → ${notice.to}` : (notice.to || 'upgraded');
640
+ } else {
641
+ const target = notice.to || notice.from || '';
642
+ body = target ? `upgraded to ${target}` : 'upgraded';
630
643
  }
631
- const versions = notice.from && notice.to
632
- ? `${notice.from} → ${notice.to}`
633
- : (notice.to || 'upgraded');
634
- return `${c.brightYellow}📦 ${versions}${c.reset}${suffix}`;
644
+ return `${c.brightYellow}📦 ${body}${c.reset}${suffix}`;
635
645
  }
636
646
 
637
647
  // Session stats (pure file reads)
@@ -785,28 +795,37 @@ function generateDashboard() {
785
795
  );
786
796
  }
787
797
 
788
- // AgentDB + MCP line (if either enabled)
789
- if (SL_CONFIG.show_agentdb || SL_CONFIG.show_mcp) {
790
- const parts = [];
791
- if (SL_CONFIG.show_agentdb) {
792
- const agentdb = getAgentDBStats();
793
- const hnswInd = agentdb.hasHnsw ? `${c.brightGreen}\u26A1${c.reset}` : '';
794
- const sizeDisp = agentdb.dbSizeKB >= 1024 ? `${(agentdb.dbSizeKB / 1024).toFixed(1)}MB` : `${agentdb.dbSizeKB}KB`;
795
- const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
796
- parts.push(`${c.cyan}Vectors${c.reset} ${vectorColor}\u25CF${agentdb.vectorCount}${hnswInd}${c.reset}`);
797
- parts.push(`${c.cyan}Size${c.reset} ${c.brightWhite}${sizeDisp}${c.reset}`);
798
- }
799
- if (SL_CONFIG.show_mcp) {
800
- const integration = getIntegrationStatus();
801
- if (integration.mcpServers.total > 0) {
802
- const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
803
- integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
804
- parts.push(`${c.cyan}MCP${c.reset} ${mcpCol}\u25CF${integration.mcpServers.enabled}/${integration.mcpServers.total}${c.reset}`);
798
+ // Embeddings line \u2014 vector store stats from .moflo/vector-stats.json.
799
+ // Reuses `system.embeddings` (already computed by getSystemMetrics()) instead
800
+ // of re-probing the cache file on every render.
801
+ {
802
+ const vec = system.embeddings;
803
+ if (vec.vectorCount > 0) {
804
+ const hnswInd = vec.hasHnsw ? `${c.brightGreen}\u26A1${c.reset}` : '';
805
+ const sizeDisp = vec.dbSizeKB >= 1024 ? `${(vec.dbSizeKB / 1024).toFixed(1)}MB` : `${vec.dbSizeKB}KB`;
806
+ const eParts = [
807
+ `${c.cyan}Vectors${c.reset} ${c.brightGreen}\u25CF${vec.vectorCount}${c.reset}${hnswInd}`,
808
+ `${c.cyan}Size${c.reset} ${c.brightWhite}${sizeDisp}${c.reset}`,
809
+ ];
810
+ if (vec.namespaces > 0) {
811
+ eParts.push(`${c.cyan}NS${c.reset} ${c.brightWhite}${vec.namespaces}${c.reset}`);
805
812
  }
806
- if (integration.hasDatabase) parts.push(`${c.brightGreen}\u25C6${c.reset}DB`);
813
+ lines.push(`${c.brightCyan}\uD83D\uDCCA Embeddings${c.reset} ${eParts.join(` ${c.dim}\u2502${c.reset} `)}`);
814
+ }
815
+ }
816
+
817
+ // MCP line
818
+ if (SL_CONFIG.show_mcp) {
819
+ const parts = [];
820
+ const integration = getIntegrationStatus();
821
+ if (integration.mcpServers.total > 0) {
822
+ const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
823
+ integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
824
+ parts.push(`${c.cyan}MCP${c.reset} ${mcpCol}\u25CF${integration.mcpServers.enabled}/${integration.mcpServers.total}${c.reset}`);
807
825
  }
826
+ if (integration.hasDatabase) parts.push(`${c.brightGreen}\u25C6${c.reset}DB`);
808
827
  if (parts.length > 0) {
809
- lines.push(`${c.brightCyan}\uD83D\uDCCA AgentDB${c.reset} ${parts.join(` ${c.dim}\u2502${c.reset} `)}`);
828
+ lines.push(`${c.brightCyan}\uD83D\uDD0C MCP${c.reset} ${parts.join(` ${c.dim}\u2502${c.reset} `)}`);
810
829
  }
811
830
  }
812
831
 
@@ -846,7 +865,7 @@ function generateCompactDashboard() {
846
865
  pushUpgradeNoticeSegment(lines);
847
866
  lines.push(header);
848
867
 
849
- // Combined swarm + agentdb + mcp line
868
+ // Combined swarm + embeddings + mcp line
850
869
  const segments = [];
851
870
  if (SL_CONFIG.show_swarm) {
852
871
  const swarm = getSwarmStatus();
@@ -856,14 +875,17 @@ function generateCompactDashboard() {
856
875
  `${c.brightYellow}\uD83E\uDD16${c.reset} ${swarmInd}[${agentsColor}${swarm.activeAgents}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}]`
857
876
  );
858
877
  }
859
- if (SL_CONFIG.show_agentdb) {
860
- const agentdb = getAgentDBStats();
861
- const hnswInd = agentdb.hasHnsw ? `\u26A1` : '';
862
- const sizeDisp = agentdb.dbSizeKB >= 1024 ? `${(agentdb.dbSizeKB / 1024).toFixed(1)}MB` : `${agentdb.dbSizeKB}KB`;
863
- const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
864
- segments.push(
865
- `${c.brightCyan}\uD83D\uDCCA${c.reset} ${vectorColor}${agentdb.vectorCount}${hnswInd}${c.reset} ${c.dim}(${sizeDisp})${c.reset}`
866
- );
878
+ // Embeddings \u2014 always-on when vectorCount > 0; self-hides on a fresh install.
879
+ // Compact doesn't call getSystemMetrics() so this is the only probe per render.
880
+ {
881
+ const vec = getEmbeddingsStats();
882
+ if (vec.vectorCount > 0) {
883
+ const hnswInd = vec.hasHnsw ? '\u26A1' : '';
884
+ const sizeDisp = vec.dbSizeKB >= 1024 ? `${(vec.dbSizeKB / 1024).toFixed(1)}MB` : `${vec.dbSizeKB}KB`;
885
+ segments.push(
886
+ `${c.brightCyan}\uD83D\uDCCA${c.reset} ${c.brightGreen}${vec.vectorCount}${hnswInd}${c.reset} ${c.dim}(${sizeDisp})${c.reset}`
887
+ );
888
+ }
867
889
  }
868
890
  if (SL_CONFIG.show_mcp) {
869
891
  const integration = getIntegrationStatus();
@@ -883,15 +905,16 @@ function generateCompactDashboard() {
883
905
  // JSON output
884
906
  function generateJSON() {
885
907
  const git = getGitInfo();
908
+ const system = getSystemMetrics();
886
909
  return {
887
910
  user: { name: git.name, gitBranch: git.gitBranch, modelName: getModelName() },
888
911
  v3Progress: getV3Progress(),
889
912
  security: getSecurityStatus(),
890
913
  swarm: getSwarmStatus(),
891
- system: getSystemMetrics(),
914
+ system,
892
915
  adrs: getADRStatus(),
893
916
  hooks: getHooksStatus(),
894
- agentdb: getAgentDBStats(),
917
+ embeddings: system.embeddings,
895
918
  tests: getTestStats(),
896
919
  git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
897
920
  upgradeNotice: getUpgradeNotice(),
@@ -0,0 +1,3 @@
1
+ {
2
+ "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/shipped/moflo-subagents.md` protocol."
3
+ }
@@ -1,22 +1,58 @@
1
- #!/usr/bin/env node
2
- /**
3
- * SubagentStart Hook — injects a directive into every subagent's context
4
- * telling it to read the subagent protocol guidance before doing any work.
5
- *
6
- * Output format: JSON with additionalContext (Claude Code hook protocol).
7
- * Exit 0 = allow (SubagentStart cannot block).
8
- */
9
- 'use strict';
10
-
11
- const output = {
12
- hookSpecificOutput: {
13
- hookEventName: 'SubagentStart',
14
- additionalContext:
15
- 'MANDATORY FIRST ACTION: Your very first tool call MUST be mcp__moflo__memory_search (any query, any namespace). ' +
16
- 'The memory-first gate WILL BLOCK all Glob, Grep, and Read calls until you do this. ' +
17
- 'After memory search, follow `.claude/guidance/shipped/moflo-subagents.md` protocol.',
18
- },
19
- };
20
-
21
- process.stdout.write(JSON.stringify(output));
22
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SubagentStart Hook — injects a directive into every subagent's context
4
+ * telling it to read the subagent protocol guidance before doing any work.
5
+ *
6
+ * Output format: JSON with additionalContext (Claude Code hook protocol).
7
+ * Exit 0 = allow (SubagentStart cannot block).
8
+ *
9
+ * Source of truth: ./subagent-bootstrap.json (sibling). The TS export at
10
+ * `src/cli/services/subagent-bootstrap.ts` reads the same file so future
11
+ * agent_spawn surfaces (epic #798 stories 3 + 9) inject byte-identical text.
12
+ *
13
+ * Inline FALLBACK keeps the hook functional if the JSON sibling is ever
14
+ * missing — a SubagentStart that emits nothing leaves the memory-first gate
15
+ * un-announced and silently regresses subagent behavior.
16
+ */
17
+ 'use strict';
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ // Defense-in-depth copy of the canonical directive in subagent-bootstrap.json.
23
+ // Kept as a single-line literal so the parity test in tests/bin/subagent-start.test.ts
24
+ // can verify it matches the JSON via plain substring containment.
25
+ 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/shipped/moflo-subagents.md` protocol.';
26
+
27
+ function loadDirective() {
28
+ const jsonPath = path.join(__dirname, 'subagent-bootstrap.json');
29
+ let raw;
30
+ try {
31
+ raw = fs.readFileSync(jsonPath, 'utf8');
32
+ } catch (err) {
33
+ if (err && err.code !== 'ENOENT') {
34
+ process.stderr.write(`[subagent-start] read failed: ${err.message} — using inline fallback\n`);
35
+ }
36
+ return FALLBACK_DIRECTIVE;
37
+ }
38
+ try {
39
+ const data = JSON.parse(raw);
40
+ if (typeof data.directive === 'string' && data.directive.length > 0) {
41
+ return data.directive;
42
+ }
43
+ process.stderr.write('[subagent-start] subagent-bootstrap.json missing string `directive` — using inline fallback\n');
44
+ } catch (err) {
45
+ process.stderr.write(`[subagent-start] subagent-bootstrap.json parse failed: ${err.message} — using inline fallback\n`);
46
+ }
47
+ return FALLBACK_DIRECTIVE;
48
+ }
49
+
50
+ const output = {
51
+ hookSpecificOutput: {
52
+ hookEventName: 'SubagentStart',
53
+ additionalContext: loadDirective(),
54
+ },
55
+ };
56
+
57
+ process.stdout.write(JSON.stringify(output));
58
+ process.exit(0);