moflo 4.9.21 → 4.9.23

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 (194) hide show
  1. package/.claude/agents/analysis/analyze-code-quality.md +0 -121
  2. package/.claude/agents/analysis/code-analyzer.md +5 -26
  3. package/.claude/agents/architecture/system-design/arch-system-design.md +0 -119
  4. package/.claude/agents/base-template-generator.md +0 -1
  5. package/.claude/agents/core/coder.md +0 -22
  6. package/.claude/agents/core/planner.md +0 -16
  7. package/.claude/agents/core/researcher.md +0 -16
  8. package/.claude/agents/core/reviewer.md +0 -17
  9. package/.claude/agents/core/tester.md +0 -19
  10. package/.claude/agents/custom/test-long-runner.md +0 -2
  11. package/.claude/agents/development/dev-backend-api.md +0 -167
  12. package/.claude/agents/development/dev-database.md +43 -0
  13. package/.claude/agents/development/dev-frontend.md +42 -0
  14. package/.claude/agents/devops/ci-cd/ops-cicd-github.md +0 -112
  15. package/.claude/agents/documentation/api-docs/docs-api-openapi.md +0 -111
  16. package/.claude/agents/security/security-auditor.md +45 -0
  17. package/.claude/guidance/shipped/moflo-cli-reference.md +19 -16
  18. package/.claude/guidance/shipped/moflo-core-guidance.md +0 -2
  19. package/.claude/guidance/shipped/moflo-guidance-rules.md +5 -5
  20. package/.claude/guidance/shipped/moflo-spell-runner.md +1 -0
  21. package/.claude/guidance/shipped/moflo-spell-scheduling.md +225 -0
  22. package/.claude/guidance/shipped/moflo-spell-troubleshooting.md +1 -0
  23. package/.claude/helpers/gate.cjs +70 -3
  24. package/.claude/skills/fl/execution-modes.md +38 -15
  25. package/.claude/skills/fl/phases.md +67 -0
  26. package/.claude/skills/spell-schedule/SKILL.md +18 -5
  27. package/README.md +1 -1
  28. package/bin/gate.cjs +70 -3
  29. package/bin/index-guidance.mjs +32 -6
  30. package/bin/lib/retired-files.mjs +146 -0
  31. package/bin/session-start-launcher.mjs +116 -8
  32. package/dist/src/cli/appliance/rvfa-builder.js +1 -1
  33. package/dist/src/cli/commands/agent.js +3 -9
  34. package/dist/src/cli/commands/daemon.js +13 -17
  35. package/dist/src/cli/commands/hooks.js +4 -9
  36. package/dist/src/cli/commands/index.js +2 -0
  37. package/dist/src/cli/commands/retire.js +111 -0
  38. package/dist/src/cli/commands/spell-schedule.js +237 -49
  39. package/dist/src/cli/hooks/reasoningbank/index.js +7 -7
  40. package/dist/src/cli/init/executor.js +26 -54
  41. package/dist/src/cli/init/helpers-generator.js +66 -3
  42. package/dist/src/cli/init/settings-generator.js +17 -6
  43. package/dist/src/cli/mcp-tools/agent-tools.js +9 -27
  44. package/dist/src/cli/mcp-tools/hooks-tools.js +23 -21
  45. package/dist/src/cli/mcp-tools/memory-tools.js +16 -5
  46. package/dist/src/cli/memory/bridge-embedder.js +26 -6
  47. package/dist/src/cli/memory/bridge-entries.js +33 -15
  48. package/dist/src/cli/memory/controllers/semantic-router.js +18 -12
  49. package/dist/src/cli/memory/sona-optimizer.js +6 -6
  50. package/dist/src/cli/neural/domain/services/learning-service.js +3 -3
  51. package/dist/src/cli/services/agent-router.js +2 -5
  52. package/dist/src/cli/services/daemon-autostart-lifecycle.js +62 -0
  53. package/dist/src/cli/services/daemon-dashboard.js +187 -18
  54. package/dist/src/cli/services/daemon-readiness.js +19 -31
  55. package/dist/src/cli/services/ephemeral-namespace-purge.js +61 -33
  56. package/dist/src/cli/services/headless-worker-executor.js +7 -94
  57. package/dist/src/cli/services/hook-block-hash.js +4 -0
  58. package/dist/src/cli/services/worker-daemon.js +40 -66
  59. package/dist/src/cli/shared/events/example-usage.js +6 -6
  60. package/dist/src/cli/shared/hooks/task-hooks.js +8 -8
  61. package/dist/src/cli/spells/core/runner.js +12 -0
  62. package/dist/src/cli/spells/scheduler/scheduler.js +24 -9
  63. package/dist/src/cli/spells/schema/validator.js +2 -1
  64. package/dist/src/cli/spells/schema/validators/top-level.js +18 -0
  65. package/dist/src/cli/version.js +1 -1
  66. package/package.json +5 -2
  67. package/retired-files.json +1989 -0
  68. package/src/cli/data/model-registry.json +2 -2
  69. package/.claude/agents/consensus/byzantine-coordinator.md +0 -63
  70. package/.claude/agents/consensus/crdt-synchronizer.md +0 -997
  71. package/.claude/agents/consensus/gossip-coordinator.md +0 -63
  72. package/.claude/agents/consensus/performance-benchmarker.md +0 -851
  73. package/.claude/agents/consensus/quorum-manager.md +0 -823
  74. package/.claude/agents/consensus/raft-manager.md +0 -63
  75. package/.claude/agents/consensus/security-manager.md +0 -622
  76. package/.claude/agents/data/ml/data-ml-model.md +0 -193
  77. package/.claude/agents/github/code-review-swarm.md +0 -538
  78. package/.claude/agents/github/github-modes.md +0 -172
  79. package/.claude/agents/github/issue-tracker.md +0 -311
  80. package/.claude/agents/github/multi-repo-swarm.md +0 -551
  81. package/.claude/agents/github/pr-manager.md +0 -183
  82. package/.claude/agents/github/project-board-sync.md +0 -508
  83. package/.claude/agents/github/release-manager.md +0 -360
  84. package/.claude/agents/github/release-swarm.md +0 -580
  85. package/.claude/agents/github/repo-architect.md +0 -391
  86. package/.claude/agents/github/swarm-issue.md +0 -566
  87. package/.claude/agents/github/swarm-pr.md +0 -414
  88. package/.claude/agents/github/sync-coordinator.md +0 -426
  89. package/.claude/agents/github/workflow-automation.md +0 -606
  90. package/.claude/agents/goal/code-goal-planner.md +0 -440
  91. package/.claude/agents/goal/goal-planner.md +0 -168
  92. package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +0 -127
  93. package/.claude/agents/hive-mind/queen-coordinator.md +0 -198
  94. package/.claude/agents/hive-mind/scout-explorer.md +0 -233
  95. package/.claude/agents/hive-mind/swarm-memory-manager.md +0 -184
  96. package/.claude/agents/hive-mind/worker-specialist.md +0 -208
  97. package/.claude/agents/neural/safla-neural.md +0 -73
  98. package/.claude/agents/optimization/benchmark-suite.md +0 -665
  99. package/.claude/agents/optimization/load-balancer.md +0 -431
  100. package/.claude/agents/optimization/performance-monitor.md +0 -672
  101. package/.claude/agents/optimization/resource-allocator.md +0 -674
  102. package/.claude/agents/optimization/topology-optimizer.md +0 -808
  103. package/.claude/agents/reasoning/goal-planner.md +0 -67
  104. package/.claude/agents/sona/sona-learning-optimizer.md +0 -74
  105. package/.claude/agents/sparc/architecture.md +0 -472
  106. package/.claude/agents/sparc/pseudocode.md +0 -318
  107. package/.claude/agents/sparc/refinement.md +0 -525
  108. package/.claude/agents/sparc/specification.md +0 -276
  109. package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +0 -225
  110. package/.claude/agents/swarm/adaptive-coordinator.md +0 -391
  111. package/.claude/agents/swarm/hierarchical-coordinator.md +0 -321
  112. package/.claude/agents/swarm/mesh-coordinator.md +0 -383
  113. package/.claude/agents/testing/production-validator.md +0 -395
  114. package/.claude/agents/testing/tdd-london-swarm.md +0 -244
  115. package/.claude/agents/v3/adr-architect.md +0 -184
  116. package/.claude/agents/v3/aidefence-guardian.md +0 -277
  117. package/.claude/agents/v3/claims-authorizer.md +0 -208
  118. package/.claude/agents/v3/collective-intelligence-coordinator.md +0 -988
  119. package/.claude/agents/v3/ddd-domain-expert.md +0 -220
  120. package/.claude/agents/v3/injection-analyst.md +0 -232
  121. package/.claude/agents/v3/memory-specialist.md +0 -987
  122. package/.claude/agents/v3/performance-engineer.md +0 -1225
  123. package/.claude/agents/v3/pii-detector.md +0 -146
  124. package/.claude/agents/v3/reasoningbank-learner.md +0 -213
  125. package/.claude/agents/v3/security-architect-aidefence.md +0 -405
  126. package/.claude/agents/v3/security-architect.md +0 -865
  127. package/.claude/agents/v3/security-auditor.md +0 -771
  128. package/.claude/agents/v3/sparc-orchestrator.md +0 -182
  129. package/.claude/agents/v3/swarm-memory-manager.md +0 -142
  130. package/.claude/agents/v3/v3-integration-architect.md +0 -205
  131. package/.claude/commands/claude-flow-help.md +0 -103
  132. package/.claude/commands/claude-flow-memory.md +0 -107
  133. package/.claude/commands/claude-flow-swarm.md +0 -205
  134. package/.claude/commands/flo-simplify.md +0 -101
  135. package/.claude/commands/github/README.md +0 -11
  136. package/.claude/commands/github/code-review-swarm.md +0 -514
  137. package/.claude/commands/github/code-review.md +0 -25
  138. package/.claude/commands/github/github-modes.md +0 -146
  139. package/.claude/commands/github/github-swarm.md +0 -113
  140. package/.claude/commands/github/issue-tracker.md +0 -284
  141. package/.claude/commands/github/issue-triage.md +0 -25
  142. package/.claude/commands/github/multi-repo-swarm.md +0 -519
  143. package/.claude/commands/github/pr-enhance.md +0 -26
  144. package/.claude/commands/github/pr-manager.md +0 -164
  145. package/.claude/commands/github/project-board-sync.md +0 -471
  146. package/.claude/commands/github/release-manager.md +0 -332
  147. package/.claude/commands/github/release-swarm.md +0 -544
  148. package/.claude/commands/github/repo-analyze.md +0 -25
  149. package/.claude/commands/github/repo-architect.md +0 -361
  150. package/.claude/commands/github/swarm-issue.md +0 -482
  151. package/.claude/commands/github/swarm-pr.md +0 -285
  152. package/.claude/commands/github/sync-coordinator.md +0 -294
  153. package/.claude/commands/github/workflow-automation.md +0 -442
  154. package/.claude/commands/hooks/README.md +0 -11
  155. package/.claude/commands/hooks/overview.md +0 -58
  156. package/.claude/commands/hooks/post-edit.md +0 -117
  157. package/.claude/commands/hooks/post-task.md +0 -112
  158. package/.claude/commands/hooks/pre-edit.md +0 -113
  159. package/.claude/commands/hooks/pre-task.md +0 -111
  160. package/.claude/commands/hooks/session-end.md +0 -118
  161. package/.claude/commands/hooks/setup.md +0 -103
  162. package/.claude/commands/sparc/analyzer.md +0 -42
  163. package/.claude/commands/sparc/architect.md +0 -43
  164. package/.claude/commands/sparc/ask.md +0 -86
  165. package/.claude/commands/sparc/batch-executor.md +0 -44
  166. package/.claude/commands/sparc/code.md +0 -78
  167. package/.claude/commands/sparc/coder.md +0 -44
  168. package/.claude/commands/sparc/debug.md +0 -72
  169. package/.claude/commands/sparc/debugger.md +0 -44
  170. package/.claude/commands/sparc/designer.md +0 -43
  171. package/.claude/commands/sparc/devops.md +0 -98
  172. package/.claude/commands/sparc/docs-writer.md +0 -69
  173. package/.claude/commands/sparc/documenter.md +0 -44
  174. package/.claude/commands/sparc/innovator.md +0 -44
  175. package/.claude/commands/sparc/integration.md +0 -72
  176. package/.claude/commands/sparc/mcp.md +0 -106
  177. package/.claude/commands/sparc/memory-manager.md +0 -44
  178. package/.claude/commands/sparc/optimizer.md +0 -44
  179. package/.claude/commands/sparc/orchestrator.md +0 -116
  180. package/.claude/commands/sparc/post-deployment-monitoring-mode.md +0 -72
  181. package/.claude/commands/sparc/refinement-optimization-mode.md +0 -72
  182. package/.claude/commands/sparc/researcher.md +0 -44
  183. package/.claude/commands/sparc/reviewer.md +0 -44
  184. package/.claude/commands/sparc/security-review.md +0 -69
  185. package/.claude/commands/sparc/sparc-modes.md +0 -139
  186. package/.claude/commands/sparc/sparc.md +0 -99
  187. package/.claude/commands/sparc/spec-pseudocode.md +0 -69
  188. package/.claude/commands/sparc/spell-manager.md +0 -44
  189. package/.claude/commands/sparc/supabase-admin.md +0 -337
  190. package/.claude/commands/sparc/swarm-coordinator.md +0 -44
  191. package/.claude/commands/sparc/tdd.md +0 -44
  192. package/.claude/commands/sparc/tester.md +0 -44
  193. package/.claude/commands/sparc/tutorial.md +0 -68
  194. package/.claude/commands/sparc.md +0 -151
@@ -15,8 +15,58 @@ import { callMCPTool } from '../mcp-client.js';
15
15
  import { TOOL_MEMORY_STORE, TOOL_MEMORY_LIST, TOOL_MEMORY_RETRIEVE } from '../mcp-tools/tool-names.js';
16
16
  import { handleMCPError } from '../services/cli-formatters.js';
17
17
  import { ensureDaemonForScheduling } from '../services/daemon-readiness.js';
18
+ import { reconcileDaemonAutostart } from '../services/daemon-autostart-lifecycle.js';
19
+ import { isDaemonInstalled } from '../services/daemon-service.js';
18
20
  import { validateSchedule, computeNextRun } from '../spells/scheduler/cron-parser.js';
19
21
  const NAMESPACE_SCHEDULES = 'scheduled-spells';
22
+ const NAMESPACE_EXECUTIONS = 'schedule-executions';
23
+ const DEFAULT_EXECUTIONS_LIMIT = 10;
24
+ const MAX_NAMESPACE_FETCH = 1000;
25
+ async function loadNamespaceValues(namespace, limit = MAX_NAMESPACE_FETCH) {
26
+ const listResult = await callMCPTool(TOOL_MEMORY_LIST, {
27
+ namespace,
28
+ limit,
29
+ });
30
+ const entries = listResult.entries ?? [];
31
+ // Parallelize retrieves — serial-await over N entries was the cost the
32
+ // reconcile-after-mutation path was paying on every create/cancel.
33
+ const fetched = await Promise.all(entries.map(async (entry) => {
34
+ try {
35
+ const result = await callMCPTool(TOOL_MEMORY_RETRIEVE, {
36
+ namespace,
37
+ key: entry.key,
38
+ });
39
+ if (result.value === null || result.value === undefined)
40
+ return null;
41
+ const parsed = typeof result.value === 'string'
42
+ ? JSON.parse(result.value)
43
+ : result.value;
44
+ return parsed;
45
+ }
46
+ catch {
47
+ output.printWarning(`Skipped malformed entry: ${entry.key}`);
48
+ return null;
49
+ }
50
+ }));
51
+ return fetched.filter((v) => v !== null);
52
+ }
53
+ /**
54
+ * Count enabled schedules in the `scheduled-spells` namespace. Drives the
55
+ * autostart reconcile after a create/cancel — see #960/#961.
56
+ */
57
+ async function countEnabledSchedules() {
58
+ const records = await loadNamespaceValues(NAMESPACE_SCHEDULES);
59
+ return records.filter(r => r.enabled === true).length;
60
+ }
61
+ /**
62
+ * Run the autostart reconcile and surface its message/warning to the user.
63
+ */
64
+ function emitReconcileResult(result) {
65
+ if (result.message)
66
+ output.printInfo(result.message);
67
+ if (result.warning)
68
+ output.printWarning(result.warning);
69
+ }
20
70
  // ── Schedule Create ───────────────────────────────────────────────────────────
21
71
  const createCommand = {
22
72
  name: 'create',
@@ -26,11 +76,13 @@ const createCommand = {
26
76
  { name: 'cron', short: 'c', description: 'Cron expression (5-field)', type: 'string' },
27
77
  { name: 'interval', short: 'i', description: 'Interval (e.g., "6h", "30m", "1d")', type: 'string' },
28
78
  { name: 'at', short: 'a', description: 'One-time ISO 8601 datetime', type: 'string' },
79
+ { name: 'no-autostart', description: 'Do not register the daemon as an OS login service', type: 'boolean' },
29
80
  ],
30
81
  examples: [
31
82
  { command: 'moflo spell schedule create -n audit --cron "0 9 * * *"', description: 'Daily at 9am' },
32
83
  { command: 'moflo spell schedule create -n security-audit --interval 6h', description: 'Every 6 hours' },
33
84
  { command: 'moflo spell schedule create -n report --at 2026-04-01T09:00:00Z', description: 'One-time cast' },
85
+ { command: 'moflo spell schedule create -n audit --interval 6h --no-autostart', description: 'Skip OS login service registration (e.g., container/CI)' },
34
86
  ],
35
87
  action: async (ctx) => {
36
88
  const name = ctx.flags.name || ctx.args[0];
@@ -86,15 +138,41 @@ const createCommand = {
86
138
  source: 'adhoc',
87
139
  };
88
140
  try {
89
- await callMCPTool(TOOL_MEMORY_STORE, {
141
+ const storeResult = await callMCPTool(TOOL_MEMORY_STORE, {
90
142
  namespace: NAMESPACE_SCHEDULES,
91
143
  key: id,
92
144
  value: JSON.stringify(record),
145
+ upsert: true,
93
146
  });
147
+ if (storeResult.success === false) {
148
+ output.printError(`Failed to save schedule: ${storeResult.error ?? 'unknown error'}`);
149
+ return { success: false, exitCode: 1 };
150
+ }
94
151
  }
95
152
  catch (error) {
96
153
  return handleMCPError(error, 'save schedule');
97
154
  }
155
+ // Reconcile OS-native autostart against the new enabled-schedule count.
156
+ // 0→1 installs the login service; 1→2/2→3/etc. is a no-op (idempotent).
157
+ // Short-circuit: a fresh create can only ever trigger an install (count
158
+ // just went up). If the service is already installed, the reconcile is a
159
+ // guaranteed noop — skip the count fetch entirely.
160
+ // Note: parser normalises --no-autostart to ctx.flags.noAutostart (#787).
161
+ const skipAutostart = ctx.flags.noAutostart === true;
162
+ const alreadyInstalled = readiness.daemonInstalled;
163
+ let reconcileTransition = 'noop';
164
+ if (!skipAutostart && !alreadyInstalled) {
165
+ const reconcile = reconcileDaemonAutostart({
166
+ projectRoot,
167
+ enabledScheduleCount: await countEnabledSchedules(),
168
+ skip: false,
169
+ });
170
+ emitReconcileResult(reconcile);
171
+ reconcileTransition = reconcile.transition;
172
+ }
173
+ const serviceState = reconcileTransition === 'installed' || alreadyInstalled
174
+ ? 'installed'
175
+ : 'not installed';
98
176
  if (ctx.flags.format === 'json') {
99
177
  output.printJson(record);
100
178
  return { success: true, data: record };
@@ -109,7 +187,7 @@ const createCommand = {
109
187
  at ? `At: ${at}` : null,
110
188
  `Next Cast: ${new Date(nextRunAt).toLocaleString()}`,
111
189
  `Daemon: ${readiness.daemonRunning ? 'running' : 'not running'}`,
112
- `Service: ${readiness.daemonInstalled ? 'installed' : 'not installed'}`,
190
+ `Service: ${serviceState}`,
113
191
  ].filter(Boolean).join('\n'), 'Scheduled Spell');
114
192
  return { success: true, data: record };
115
193
  },
@@ -127,61 +205,146 @@ const scheduleListCommand = {
127
205
  aliases: ['ls'],
128
206
  description: 'List all scheduled spells',
129
207
  action: async (ctx) => {
208
+ let raw;
130
209
  try {
131
- const result = await callMCPTool(TOOL_MEMORY_LIST, {
132
- namespace: NAMESPACE_SCHEDULES,
133
- });
134
- // Single-pass: parse + transform for display
135
- const schedules = [];
136
- for (const r of result.results ?? []) {
137
- try {
138
- const parsed = typeof r.value === 'string' ? JSON.parse(r.value) : r.value;
139
- if (parsed) {
140
- schedules.push({
141
- id: parsed.id,
142
- spellName: parsed.spellName,
143
- timing: parsed.cron || parsed.interval || parsed.at || '-',
144
- nextRun: parsed.nextRunAt ? new Date(parsed.nextRunAt).toLocaleString() : '-',
145
- enabled: parsed.enabled,
146
- });
147
- }
148
- }
149
- catch {
150
- output.printWarning(`Skipped malformed schedule record: ${r.key}`);
151
- }
152
- }
153
- if (ctx.flags.format === 'json') {
154
- output.printJson(schedules);
155
- return { success: true, data: schedules };
156
- }
157
- if (schedules.length === 0) {
158
- output.writeln();
159
- output.printInfo('No scheduled spells');
160
- return { success: true, data: [] };
161
- }
162
- output.writeln();
163
- output.writeln(output.bold('Scheduled Spells'));
164
- output.writeln();
165
- output.printTable({ columns: SCHEDULE_COLUMNS, data: schedules });
166
- output.writeln();
167
- output.printInfo(`Total: ${schedules.length} schedule(s)`);
168
- return { success: true, data: schedules };
210
+ raw = await loadNamespaceValues(NAMESPACE_SCHEDULES);
169
211
  }
170
212
  catch (error) {
171
213
  return handleMCPError(error, 'list schedules');
172
214
  }
215
+ const schedules = raw.map(parsed => ({
216
+ id: parsed.id,
217
+ spellName: parsed.spellName,
218
+ timing: parsed.cron || parsed.interval || parsed.at || '-',
219
+ nextRun: parsed.nextRunAt ? new Date(parsed.nextRunAt).toLocaleString() : '-',
220
+ enabled: parsed.enabled,
221
+ }));
222
+ if (ctx.flags.format === 'json') {
223
+ output.printJson(schedules);
224
+ return { success: true, data: schedules };
225
+ }
226
+ if (schedules.length === 0) {
227
+ output.writeln();
228
+ output.printInfo('No scheduled spells');
229
+ return { success: true, data: [] };
230
+ }
231
+ output.writeln();
232
+ output.writeln(output.bold('Scheduled Spells'));
233
+ output.writeln();
234
+ output.printTable({ columns: SCHEDULE_COLUMNS, data: schedules });
235
+ output.writeln();
236
+ output.printInfo(`Total: ${schedules.length} schedule(s)`);
237
+ return { success: true, data: schedules };
238
+ },
239
+ };
240
+ // ── Schedule Executions ───────────────────────────────────────────────────────
241
+ const EXECUTION_COLUMNS = [
242
+ { key: 'startedAt', header: 'Started', width: 22 },
243
+ { key: 'spellName', header: 'Spell', width: 20 },
244
+ { key: 'status', header: 'Status', width: 10 },
245
+ { key: 'duration', header: 'Duration', width: 10 },
246
+ { key: 'manualRun', header: 'Manual', width: 7 },
247
+ { key: 'scheduleId', header: 'Schedule', width: 30 },
248
+ ];
249
+ function formatExecutionRow(parsed) {
250
+ const completed = typeof parsed.completedAt === 'number';
251
+ let status;
252
+ if (!completed) {
253
+ status = output.warning('running');
254
+ }
255
+ else if (parsed.success === true) {
256
+ status = output.success('success');
257
+ }
258
+ else {
259
+ status = output.error('failed');
260
+ }
261
+ return {
262
+ id: String(parsed.id ?? ''),
263
+ scheduleId: String(parsed.scheduleId ?? ''),
264
+ spellName: String(parsed.spellName ?? ''),
265
+ startedAt: typeof parsed.startedAt === 'number'
266
+ ? new Date(parsed.startedAt).toLocaleString()
267
+ : '-',
268
+ status,
269
+ duration: typeof parsed.duration === 'number' ? `${parsed.duration}ms` : '-',
270
+ manualRun: parsed.manualRun === true ? 'yes' : '',
271
+ };
272
+ }
273
+ const executionsCommand = {
274
+ name: 'executions',
275
+ aliases: ['exec', 'history'],
276
+ description: 'Show recent scheduled spell executions',
277
+ options: [
278
+ { name: 'schedule', short: 's', description: 'Filter by schedule ID', type: 'string' },
279
+ { name: 'limit', short: 'l', description: `Max rows to return (default ${DEFAULT_EXECUTIONS_LIMIT})`, type: 'number' },
280
+ ],
281
+ examples: [
282
+ { command: 'moflo spell schedule executions', description: 'Most recent executions across all schedules' },
283
+ { command: 'moflo spell schedule executions --schedule sched-adhoc-123', description: 'Filter by schedule ID' },
284
+ { command: 'moflo spell schedule executions --limit 25', description: 'Show last 25 executions' },
285
+ ],
286
+ action: async (ctx) => {
287
+ const scheduleFilter = ctx.flags.schedule;
288
+ const rawLimit = ctx.flags.limit;
289
+ const limit = typeof rawLimit === 'number' && rawLimit > 0
290
+ ? Math.floor(rawLimit)
291
+ : DEFAULT_EXECUTIONS_LIMIT;
292
+ let raw;
293
+ try {
294
+ raw = await loadNamespaceValues(NAMESPACE_EXECUTIONS);
295
+ }
296
+ catch (error) {
297
+ return handleMCPError(error, 'list executions');
298
+ }
299
+ const parsed = raw.filter(v => typeof v.startedAt === 'number');
300
+ const filtered = scheduleFilter
301
+ ? parsed.filter(e => e.scheduleId === scheduleFilter)
302
+ : parsed;
303
+ filtered.sort((a, b) => b.startedAt - a.startedAt);
304
+ const truncated = filtered.slice(0, limit);
305
+ if (ctx.flags.format === 'json') {
306
+ output.printJson(truncated);
307
+ return { success: true, data: truncated };
308
+ }
309
+ if (truncated.length === 0) {
310
+ output.writeln();
311
+ output.printInfo(scheduleFilter
312
+ ? `No executions for schedule ${scheduleFilter}`
313
+ : 'No scheduled spell executions yet');
314
+ return { success: true, data: [] };
315
+ }
316
+ const rows = truncated.map(formatExecutionRow);
317
+ output.writeln();
318
+ output.writeln(output.bold(scheduleFilter
319
+ ? `Executions for ${scheduleFilter}`
320
+ : 'Recent Scheduled Executions'));
321
+ output.writeln();
322
+ output.printTable({ columns: EXECUTION_COLUMNS, data: rows });
323
+ output.writeln();
324
+ output.printInfo(filtered.length > truncated.length
325
+ ? `Showing ${truncated.length} of ${filtered.length} execution(s)`
326
+ : `Total: ${truncated.length} execution(s)`);
327
+ return { success: true, data: truncated };
173
328
  },
174
329
  };
175
330
  // ── Schedule Cancel ───────────────────────────────────────────────────────────
176
331
  const cancelCommand = {
177
332
  name: 'cancel',
178
333
  description: 'Cancel (disable) a scheduled spell',
334
+ options: [
335
+ { name: 'keep-autostart', description: 'Keep the OS login service registered even if no schedules remain', type: 'boolean' },
336
+ ],
337
+ examples: [
338
+ { command: 'moflo spell schedule cancel sched-adhoc-123', description: 'Cancel and auto-uninstall daemon service if no schedules remain' },
339
+ { command: 'moflo spell schedule cancel sched-adhoc-123 --keep-autostart', description: 'Cancel but keep the OS login service registered' },
340
+ ],
179
341
  action: async (ctx) => {
180
342
  const scheduleId = ctx.args[0];
181
343
  if (!scheduleId) {
182
344
  output.printError('Schedule ID is required');
183
345
  return { success: false, exitCode: 1 };
184
346
  }
347
+ let updated;
185
348
  try {
186
349
  // Fetch the current schedule
187
350
  const fetchResult = await callMCPTool(TOOL_MEMORY_RETRIEVE, {
@@ -195,19 +358,42 @@ const cancelCommand = {
195
358
  const schedule = typeof fetchResult.value === 'string'
196
359
  ? JSON.parse(fetchResult.value)
197
360
  : fetchResult.value;
198
- // Disable it
199
- const updated = { ...schedule, enabled: false };
200
- await callMCPTool(TOOL_MEMORY_STORE, {
361
+ // Disable it. upsert:true is critical — the cancel writes back to an
362
+ // existing key, and the historic default (insert-only) silently failed
363
+ // with a UNIQUE constraint violation on (namespace, key) — see #962.
364
+ updated = { ...schedule, enabled: false };
365
+ const storeResult = await callMCPTool(TOOL_MEMORY_STORE, {
201
366
  namespace: NAMESPACE_SCHEDULES,
202
367
  key: scheduleId,
203
368
  value: JSON.stringify(updated),
369
+ upsert: true,
204
370
  });
205
- output.printSuccess(`Schedule ${scheduleId} cancelled`);
206
- return { success: true, data: updated };
371
+ if (storeResult.success === false) {
372
+ output.printError(`Failed to cancel schedule: ${storeResult.error ?? 'unknown error'}`);
373
+ return { success: false, exitCode: 1 };
374
+ }
207
375
  }
208
376
  catch (error) {
209
377
  return handleMCPError(error, 'cancel schedule');
210
378
  }
379
+ output.printSuccess(`Schedule ${scheduleId} cancelled`);
380
+ // Reconcile OS-native autostart against the new enabled-schedule count.
381
+ // 1→0 uninstalls the login service; everything else is a no-op.
382
+ // Short-circuit: a fresh cancel can only ever trigger an uninstall (count
383
+ // just went down). If the service isn't currently installed, the reconcile
384
+ // is a guaranteed noop — skip the count fetch entirely.
385
+ // Note: parser normalises --keep-autostart to ctx.flags.keepAutostart (#787).
386
+ const projectRoot = ctx.cwd || process.cwd();
387
+ const skipAutostart = ctx.flags.keepAutostart === true;
388
+ if (!skipAutostart && isDaemonInstalled(projectRoot)) {
389
+ const reconcile = reconcileDaemonAutostart({
390
+ projectRoot,
391
+ enabledScheduleCount: await countEnabledSchedules(),
392
+ skip: false,
393
+ });
394
+ emitReconcileResult(reconcile);
395
+ }
396
+ return { success: true, data: updated };
211
397
  },
212
398
  };
213
399
  // ── Schedule Command (parent) ─────────────────────────────────────────────────
@@ -215,10 +401,11 @@ const SCHEDULE_DOCS_URL = 'https://github.com/eric-cielo/moflo/blob/main/docs/SP
215
401
  export const scheduleCommand = {
216
402
  name: 'schedule',
217
403
  description: `Manage scheduled spells (full reference: ${SCHEDULE_DOCS_URL})`,
218
- subcommands: [createCommand, scheduleListCommand, cancelCommand],
404
+ subcommands: [createCommand, scheduleListCommand, executionsCommand, cancelCommand],
219
405
  examples: [
220
406
  { command: 'moflo spell schedule create -n audit --cron "0 9 * * *"', description: 'Schedule daily audit' },
221
407
  { command: 'moflo spell schedule list', description: 'List all schedules' },
408
+ { command: 'moflo spell schedule executions --schedule <id>', description: 'Show execution audit trail' },
222
409
  { command: 'moflo spell schedule cancel <id>', description: 'Cancel a schedule' },
223
410
  ],
224
411
  action: async () => {
@@ -229,9 +416,10 @@ export const scheduleCommand = {
229
416
  output.writeln();
230
417
  output.writeln('Subcommands:');
231
418
  output.printList([
232
- `${output.highlight('create')} - Create a scheduled spell`,
233
- `${output.highlight('list')} - List all scheduled spells`,
234
- `${output.highlight('cancel')} - Cancel (disable) a schedule`,
419
+ `${output.highlight('create')} - Create a scheduled spell`,
420
+ `${output.highlight('list')} - List all scheduled spells`,
421
+ `${output.highlight('executions')} - Show recent execution history`,
422
+ `${output.highlight('cancel')} - Cancel (disable) a schedule`,
235
423
  ]);
236
424
  output.writeln();
237
425
  output.writeln(`Full reference: ${SCHEDULE_DOCS_URL}`);
@@ -33,15 +33,15 @@ const DEFAULT_CONFIG = {
33
33
  useMockEmbeddings: false,
34
34
  };
35
35
  /**
36
- * Agent mapping for routing
36
+ * Agent mapping for routing — keys must match agents shipped in .claude/agents/
37
+ * (or the canonical AgentType union).
37
38
  */
38
39
  const AGENT_PATTERNS = {
39
- 'security-architect': /security|auth|cve|vuln|encrypt|password|token/i,
40
- 'test-architect': /test|spec|mock|coverage|tdd|assert/i,
41
- 'performance-engineer': /perf|optim|fast|memory|cache|speed|slow/i,
42
- 'core-architect': /architect|design|ddd|domain|refactor|struct/i,
43
- 'swarm-specialist': /swarm|agent|coordinate|orchestrat|parallel/i,
44
- 'memory-specialist': /memory|agentdb|hnsw|vector|embedding/i,
40
+ 'security-auditor': /security|auth|cve|vuln|encrypt|password|token/i,
41
+ 'tester': /test|spec|mock|coverage|tdd|assert/i,
42
+ 'architect': /architect|design|ddd|domain|refactor|struct|perf|optim|fast|memory|cache|speed|slow/i,
43
+ 'coordinator': /swarm|agent|coordinate|orchestrat|parallel|hive/i,
44
+ 'researcher': /memory|agentdb|hnsw|vector|embedding|recall|persist/i,
45
45
  'coder': /fix|bug|implement|create|add|build|error|code/i,
46
46
  'reviewer': /review|quality|lint|check|audit/i,
47
47
  };
@@ -59,41 +59,28 @@ export const INTERNAL_SKILLS = [
59
59
  /**
60
60
  * Commands to copy based on configuration
61
61
  */
62
- const COMMANDS_MAP = {
63
- core: ['claude-flow-help.md', 'claude-flow-swarm.md', 'claude-flow-memory.md', 'flo-simplify.md'],
64
- analysis: [],
65
- automation: [],
66
- github: ['github'],
67
- hooks: ['hooks'],
68
- monitoring: [],
69
- optimization: [],
70
- sparc: ['sparc'],
71
- };
62
+ // Empty after #949 retired the last shipped slash commands. moflo's user-facing
63
+ // functionality now lives entirely in `.claude/skills/` (canonical) and
64
+ // `.claude/agents/` (subagent surface). Kept as an empty-record placeholder
65
+ // because callers iterate `Object.entries(COMMANDS_MAP)` to gate per-category
66
+ // install — see init-copy-maps.test.ts for the iteration contract.
67
+ const COMMANDS_MAP = {};
72
68
  /**
73
69
  * Agents to copy based on configuration. Exported for integrity tests.
70
+ *
71
+ * Each value is a directory name under `.claude/agents/` that ships in the
72
+ * moflo package. After #932 retired ~50 ruflo-aspirational agents, the set
73
+ * is narrowed to actual development specialties Claude is likely to invoke.
74
74
  */
75
75
  export const AGENTS_MAP = {
76
76
  core: ['core'],
77
- consensus: ['consensus'],
78
- github: ['github'],
79
- hiveMind: ['hive-mind'],
80
- sparc: ['sparc'],
81
- swarm: ['swarm'],
82
- browser: ['browser'], // agent-browser integration
83
- // V3-specific agents
84
- v3: ['v3'],
85
- optimization: ['optimization'],
86
- testing: ['testing'],
87
77
  analysis: ['analysis'],
88
78
  architecture: ['architecture'],
79
+ custom: ['custom'],
89
80
  development: ['development'],
90
81
  devops: ['devops'],
91
82
  documentation: ['documentation'],
92
- specialized: ['specialized'],
93
- goal: ['goal'],
94
- sona: ['sona'],
95
- data: ['data'],
96
- custom: ['custom'],
83
+ security: ['security'],
97
84
  };
98
85
  /**
99
86
  * Directory structure to create
@@ -1521,39 +1508,24 @@ npx moflo swarm monitor
1521
1508
  ### Core Development (5)
1522
1509
  \`coder\`, \`reviewer\`, \`tester\`, \`planner\`, \`researcher\`
1523
1510
 
1524
- ### V3 Specialized (4)
1525
- \`security-architect\`, \`security-auditor\`, \`memory-specialist\`, \`performance-engineer\`
1526
-
1527
- ### Swarm Coordination (5)
1528
- \`hierarchical-coordinator\`, \`mesh-coordinator\`, \`adaptive-coordinator\`, \`collective-intelligence-coordinator\`, \`swarm-memory-manager\`
1529
-
1530
- ### Consensus & Distributed (7)
1531
- \`byzantine-coordinator\`, \`raft-manager\`, \`gossip-coordinator\`, \`consensus-builder\`, \`crdt-synchronizer\`, \`quorum-manager\`, \`security-manager\`
1532
-
1533
- ### Performance & Optimization (5)
1534
- \`perf-analyzer\`, \`performance-benchmarker\`, \`task-orchestrator\`, \`memory-coordinator\`, \`smart-agent\`
1535
-
1536
- ### GitHub & Repository (9)
1537
- \`github-modes\`, \`pr-manager\`, \`code-review-swarm\`, \`issue-tracker\`, \`release-manager\`, \`workflow-automation\`, \`project-board-sync\`, \`repo-architect\`, \`multi-repo-swarm\`
1538
-
1539
- ### SPARC Methodology (6)
1540
- \`sparc-coord\`, \`sparc-coder\`, \`specification\`, \`pseudocode\`, \`architecture\`, \`refinement\`
1511
+ ### Code Analysis (2)
1512
+ \`code-analyzer\`, \`analyst\`
1541
1513
 
1542
- ### Specialized Development (8)
1543
- \`backend-dev\`, \`mobile-dev\`, \`ml-developer\`, \`cicd-engineer\`, \`api-docs\`, \`system-architect\`, \`code-analyzer\`, \`base-template-generator\`
1514
+ ### Specialized Development (5)
1515
+ \`backend-dev\`, \`frontend-dev\`, \`database-dev\`, \`cicd-engineer\`, \`api-docs\`
1544
1516
 
1545
- ### Testing & Validation (2)
1546
- \`tdd-london-swarm\`, \`production-validator\`
1517
+ ### Security (1)
1518
+ \`security-auditor\`
1547
1519
 
1548
1520
  ### Agent Routing by Task
1549
- | Task Type | Recommended Agents | Topology |
1550
- |-----------|-------------------|----------|
1551
- | Bug Fix | researcher, coder, tester | mesh |
1552
- | New Feature | coordinator, architect, coder, tester, reviewer | hierarchical |
1553
- | Refactoring | architect, coder, reviewer | mesh |
1554
- | Performance | researcher, perf-engineer, coder | hierarchical |
1555
- | Security | security-architect, auditor, reviewer | hierarchical |
1556
- | Docs | researcher, api-docs | mesh |
1521
+ | Task Type | Recommended Agents |
1522
+ |-----------|--------------------|
1523
+ | Bug Fix | researcher, coder, tester |
1524
+ | New Feature | planner, coder, tester, reviewer |
1525
+ | Refactoring | coder, reviewer |
1526
+ | Security | security-auditor, reviewer |
1527
+ | GitHub workflow | coder, reviewer (with \`gh\` CLI) |
1528
+ | Docs | researcher, api-docs |
1557
1529
 
1558
1530
  ---
1559
1531
 
@@ -207,7 +207,7 @@ var path = require('path');
207
207
  var PROJECT_DIR = (process.env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\\/([a-z])\\//i, '$1:/');
208
208
  var STATE_FILE = path.join(PROJECT_DIR, '.claude', 'workflow-state.json');
209
209
 
210
- var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, interactionCount: 0, sessionStart: null, lastBlockedAt: null, lastNamespaceHint: '', lastNamespaceHintEmittedBy: {} };
210
+ var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, interactionCount: 0, sessionStart: null, lastBlockedAt: null, lastNamespaceHint: '', lastNamespaceHintEmittedBy: {}, flMode: null, swarmInitialized: false, hiveInitialized: false };
211
211
 
212
212
  function readState() {
213
213
  try {
@@ -255,7 +255,7 @@ function writeState(s) {
255
255
 
256
256
  // Load moflo.yaml gate config (defaults: all enabled)
257
257
  function loadGateConfig() {
258
- var defaults = { memory_first: true, task_create_first: true, context_tracking: true, testing_gate: true, simplify_gate: true, learnings_gate: true };
258
+ var defaults = { memory_first: true, task_create_first: true, context_tracking: true, testing_gate: true, simplify_gate: true, learnings_gate: true, swarm_invocation_gate: true };
259
259
  try {
260
260
  var yamlPath = path.join(PROJECT_DIR, 'moflo.yaml');
261
261
  if (fs.existsSync(yamlPath)) {
@@ -266,6 +266,7 @@ function loadGateConfig() {
266
266
  if (/testing_gate:\\s*false/i.test(content)) defaults.testing_gate = false;
267
267
  if (/simplify_gate:\\s*false/i.test(content)) defaults.simplify_gate = false;
268
268
  if (/learnings_gate:\\s*false/i.test(content)) defaults.learnings_gate = false;
269
+ if (/swarm_invocation_gate:\\s*false/i.test(content)) defaults.swarm_invocation_gate = false;
269
270
  }
270
271
  } catch (e) { /* use defaults */ }
271
272
  return defaults;
@@ -305,6 +306,21 @@ var NS_NAV_RES = [
305
306
  /\\b(class|function|method|component|service|entity|module)\\b/,
306
307
  ];
307
308
 
309
+ // Detect whether the current prompt invoked /fl or /flo with a swarm/hive flag
310
+ // (#952). When set, check-before-agent BLOCKS the Agent spawn until the matching
311
+ // MCP init has been recorded — the user explicitly opted in to the protected
312
+ // coordination surface, so falling back to raw Agent dispatch silently regresses
313
+ // headline moflo product capability.
314
+ //
315
+ // SYNC: duplicated verbatim in bin/gate.cjs.
316
+ function detectFlMode(promptText) {
317
+ var p = promptText || '';
318
+ if (!/^\\s*\\/(?:fl|flo)\\b/i.test(p)) return null;
319
+ if (/(?:^|\\s)(?:-s|--swarm)\\b/.test(p)) return 'swarm';
320
+ if (/(?:^|\\s)(?:-h|--hive)\\b/.test(p)) return 'hive';
321
+ return null;
322
+ }
323
+
308
324
  function classifyNamespaceHint(promptText) {
309
325
  var lower = (promptText || '').toLowerCase();
310
326
  if (NS_TEST_RE.test(lower)) return 'Memory namespace hint: use "tests" for test inventory and coverage lookups.';
@@ -334,6 +350,11 @@ function applyPromptStateReset(state, promptText) {
334
350
  // Per-actor emission tracking — fresh window each prompt so subagents that
335
351
  // spawn their own agents still see the hint on their first check-before-agent.
336
352
  state.lastNamespaceHintEmittedBy = {};
353
+ // #952 — derive flMode from the user prompt and reset the matching init
354
+ // flag. Each /fl invocation must call its protected MCP init.
355
+ state.flMode = detectFlMode(promptText);
356
+ state.swarmInitialized = false;
357
+ state.hiveInitialized = false;
337
358
  }
338
359
  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;
339
360
  var EDIT_RESET_SKIP_BOTH_RE = /\\.(md|markdown|txt|rst|adoc|lock|gitignore)$|(?:^|[\\\\\\/])(CHANGELOG(?:\\.md)?|\\.env\\.example|package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock|bun\\.lockb)$/i;
@@ -373,6 +394,46 @@ switch (command) {
373
394
  writeState(s);
374
395
  }
375
396
  }
397
+ // #952 — when /fl was invoked with -s/-h, the protected MCP init must run
398
+ // BEFORE any Agent spawn. Hard block: the user explicitly opted in to
399
+ // moflo's coordination surface, so silently dispatching Agent calls
400
+ // without mcp__moflo__swarm_init / mcp__moflo__hive-mind_init is the
401
+ // failure mode this gate exists to prevent (CLAUDE.md "⛔ Protected
402
+ // functionality"). Other Agent uses remain advisory.
403
+ if (config.swarm_invocation_gate) {
404
+ if (s.flMode === 'swarm' && !s.swarmInitialized) {
405
+ process.stderr.write('BLOCKED: /fl was invoked with -s/--swarm but mcp__moflo__swarm_init has not been called.\\n');
406
+ process.stderr.write('Run mcp__moflo__swarm_init first, then mcp__moflo__agent_spawn for each role, then dispatch Agent.\\n');
407
+ process.stderr.write('See .claude/skills/fl/execution-modes.md "SWARM mode" and CLAUDE.md "⛔ Protected functionality".\\n');
408
+ process.stderr.write('Disable via moflo.yaml: gates: swarm_invocation_gate: false\\n');
409
+ process.exit(2);
410
+ }
411
+ if (s.flMode === 'hive' && !s.hiveInitialized) {
412
+ process.stderr.write('BLOCKED: /fl was invoked with -h/--hive but mcp__moflo__hive-mind_init has not been called.\\n');
413
+ process.stderr.write('Run mcp__moflo__hive-mind_init first, then dispatch Agent or hive-mind workers.\\n');
414
+ process.stderr.write('See .claude/skills/fl/execution-modes.md "HIVE-MIND mode" and CLAUDE.md "⛔ Protected functionality".\\n');
415
+ process.stderr.write('Disable via moflo.yaml: gates: swarm_invocation_gate: false\\n');
416
+ process.exit(2);
417
+ }
418
+ }
419
+ break;
420
+ }
421
+ case 'record-swarm-init': {
422
+ // #952 — wired to mcp__moflo__swarm_init PostToolUse.
423
+ var s = readState();
424
+ if (!s.swarmInitialized) {
425
+ s.swarmInitialized = true;
426
+ writeState(s);
427
+ }
428
+ break;
429
+ }
430
+ case 'record-hive-init': {
431
+ // #952 — wired to mcp__moflo__hive-mind_init PostToolUse.
432
+ var s = readState();
433
+ if (!s.hiveInitialized) {
434
+ s.hiveInitialized = true;
435
+ writeState(s);
436
+ }
376
437
  break;
377
438
  }
378
439
  case 'check-before-scan': {
@@ -533,7 +594,9 @@ switch (command) {
533
594
  break;
534
595
  }
535
596
  case 'session-reset': {
536
- writeState({ tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, interactionCount: 0, sessionStart: new Date().toISOString(), lastBlockedAt: null, lastNamespaceHint: '', lastNamespaceHintEmittedBy: {} });
597
+ // Derive from STATE_DEFAULTS so adding a new state field requires only one
598
+ // edit (the defaults object).
599
+ writeState(Object.assign({}, STATE_DEFAULTS, { sessionStart: new Date().toISOString() }));
537
600
  break;
538
601
  }
539
602
  default: