swarmdo 1.54.1 → 1.58.6

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 (188) hide show
  1. package/.claude/helpers/statusline.cjs +17 -11
  2. package/README.md +4 -3
  3. package/package.json +1 -1
  4. package/v3/@swarmdo/cli/dist/src/command-usage/usage.d.ts +74 -0
  5. package/v3/@swarmdo/cli/dist/src/command-usage/usage.js +161 -0
  6. package/v3/@swarmdo/cli/dist/src/commands/commands.d.ts +19 -0
  7. package/v3/@swarmdo/cli/dist/src/commands/commands.js +107 -0
  8. package/v3/@swarmdo/cli/dist/src/commands/hooks.d.ts +26 -0
  9. package/v3/@swarmdo/cli/dist/src/commands/hooks.js +96 -24
  10. package/v3/@swarmdo/cli/dist/src/commands/index.js +7 -3
  11. package/v3/@swarmdo/cli/dist/src/commands/swarm.js +5 -0
  12. package/v3/@swarmdo/cli/dist/src/commands/usage.js +57 -1
  13. package/v3/@swarmdo/cli/dist/src/comms/store.d.ts +11 -1
  14. package/v3/@swarmdo/cli/dist/src/comms/store.js +16 -2
  15. package/v3/@swarmdo/cli/dist/src/init/statusline-generator.d.ts +1 -1
  16. package/v3/@swarmdo/cli/dist/src/init/statusline-generator.js +17 -11
  17. package/v3/@swarmdo/cli/dist/src/integrations/integrations.js +19 -8
  18. package/v3/@swarmdo/cli/dist/src/mcp-tools/comms-tools.js +4 -4
  19. package/v3/@swarmdo/cli/dist/src/mcp-tools/swarm-tools.js +2 -0
  20. package/v3/@swarmdo/cli/dist/src/usage/transcript-friction.d.ts +77 -0
  21. package/v3/@swarmdo/cli/dist/src/usage/transcript-friction.js +151 -0
  22. package/v3/@swarmdo/cli/package.json +1 -1
  23. package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
  24. package/v3/@swarmdo/codex/dist/cli.js +0 -0
  25. package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
  26. package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
  27. package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
  28. package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +4 -1
  29. package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
  30. package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
  31. package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
  32. package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
  33. package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
  34. package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
  35. package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
  36. package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
  37. package/v3/@swarmdo/shared/dist/mcp/transport/http.js +8 -1
  38. package/.claude/settings.local.json +0 -7
  39. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +0 -2
  40. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +0 -2
  41. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +0 -138
  42. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +0 -418
  43. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +0 -15
  44. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +0 -240
  45. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +0 -7
  46. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +0 -508
  47. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +0 -54
  48. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +0 -2
  49. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +0 -46
  50. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +0 -2
  51. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +0 -18
  52. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +0 -28
  53. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +0 -5
  54. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +0 -2
  55. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +0 -35
  56. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +0 -2
  57. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +0 -11
  58. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +0 -2
  59. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +0 -9
  60. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +0 -2
  61. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +0 -5
  62. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +0 -2
  63. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +0 -12
  64. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +0 -2
  65. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +0 -19
  66. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +0 -2
  67. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +0 -44
  68. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +0 -117
  69. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +0 -70
  70. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +0 -194
  71. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +0 -69
  72. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +0 -139
  73. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +0 -34
  74. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +0 -101
  75. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +0 -10
  76. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +0 -10
  77. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +0 -64
  78. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +0 -70
  79. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +0 -44
  80. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +0 -79
  81. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +0 -37
  82. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +0 -39
  83. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +0 -67
  84. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +0 -58
  85. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +0 -45
  86. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +0 -63
  87. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +0 -26
  88. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +0 -17
  89. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +0 -51
  90. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +0 -110
  91. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +0 -13
  92. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +0 -25
  93. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +0 -11
  94. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +0 -19
  95. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +0 -11
  96. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +0 -27
  97. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +0 -6
  98. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +0 -6
  99. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +0 -41
  100. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +0 -64
  101. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +0 -8
  102. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +0 -703
  103. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +0 -23
  104. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +0 -239
  105. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +0 -17
  106. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +0 -42
  107. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +0 -18
  108. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +0 -45
  109. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +0 -19
  110. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +0 -50
  111. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +0 -19
  112. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +0 -47
  113. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +0 -17
  114. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +0 -40
  115. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +0 -18
  116. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +0 -51
  117. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +0 -214
  118. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +0 -595
  119. package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +0 -76
  120. package/v3/@swarmdo/testing/dist/fixtures/agents.js +0 -158
  121. package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +0 -157
  122. package/v3/@swarmdo/testing/dist/fixtures/configurations.js +0 -337
  123. package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +0 -10
  124. package/v3/@swarmdo/testing/dist/fixtures/index.js +0 -17
  125. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +0 -329
  126. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +0 -714
  127. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +0 -159
  128. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +0 -262
  129. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +0 -290
  130. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +0 -547
  131. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +0 -289
  132. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +0 -534
  133. package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +0 -106
  134. package/v3/@swarmdo/testing/dist/fixtures/tasks.js +0 -229
  135. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +0 -213
  136. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +0 -435
  137. package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +0 -135
  138. package/v3/@swarmdo/testing/dist/helpers/assertions.js +0 -217
  139. package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +0 -96
  140. package/v3/@swarmdo/testing/dist/helpers/create-mock.js +0 -164
  141. package/v3/@swarmdo/testing/dist/helpers/index.d.ts +0 -14
  142. package/v3/@swarmdo/testing/dist/helpers/index.js +0 -22
  143. package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +0 -323
  144. package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +0 -350
  145. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +0 -271
  146. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +0 -446
  147. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +0 -142
  148. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +0 -205
  149. package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +0 -190
  150. package/v3/@swarmdo/testing/dist/helpers/test-application.js +0 -138
  151. package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +0 -274
  152. package/v3/@swarmdo/testing/dist/helpers/test-utils.js +0 -456
  153. package/v3/@swarmdo/testing/dist/index.d.ts +0 -42
  154. package/v3/@swarmdo/testing/dist/index.js +0 -66
  155. package/v3/@swarmdo/testing/dist/mocks/index.d.ts +0 -8
  156. package/v3/@swarmdo/testing/dist/mocks/index.js +0 -10
  157. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +0 -206
  158. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +0 -575
  159. package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +0 -287
  160. package/v3/@swarmdo/testing/dist/mocks/mock-services.js +0 -563
  161. package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +0 -109
  162. package/v3/@swarmdo/testing/dist/regression/api-contract.js +0 -374
  163. package/v3/@swarmdo/testing/dist/regression/index.d.ts +0 -18
  164. package/v3/@swarmdo/testing/dist/regression/index.js +0 -18
  165. package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +0 -67
  166. package/v3/@swarmdo/testing/dist/regression/integration-regression.js +0 -334
  167. package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +0 -106
  168. package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +0 -268
  169. package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +0 -124
  170. package/v3/@swarmdo/testing/dist/regression/regression-runner.js +0 -227
  171. package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +0 -73
  172. package/v3/@swarmdo/testing/dist/regression/security-regression.js +0 -258
  173. package/v3/@swarmdo/testing/dist/setup.d.ts +0 -43
  174. package/v3/@swarmdo/testing/dist/setup.js +0 -91
  175. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +0 -10
  176. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +0 -433
  177. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +0 -10
  178. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +0 -352
  179. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +0 -206
  180. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +0 -839
  181. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +0 -10
  182. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +0 -428
  183. package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +0 -38
  184. package/v3/@swarmdo/testing/dist/v2-compat/index.js +0 -41
  185. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +0 -10
  186. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +0 -419
  187. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +0 -34
  188. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +0 -372
@@ -11,6 +11,94 @@ import { join } from 'node:path';
11
11
  import { bridgeSearchEntries } from '../memory/memory-bridge.js';
12
12
  import { selectInjectionMemories, mapSearchResultsToCandidates, extractPromptFromPayload, } from '../memory-inject/select.js';
13
13
  import { classifyCommand, extractBashCommand, denyOutput } from '../hooks-recipe/command-guard.js';
14
+ // Read-only liveness probe (does not persist). Mirrors swarm-tools' isPidAlive
15
+ // so the statusline never counts a swarm whose host process has exited.
16
+ function statuslinePidAlive(pid) {
17
+ try {
18
+ process.kill(pid, 0);
19
+ return true;
20
+ }
21
+ catch (e) {
22
+ return e.code === 'EPERM';
23
+ }
24
+ }
25
+ /**
26
+ * Live swarm + agent counts for the statusline (`🐝 Swarms N 🤖 Agents M`).
27
+ * These are two distinct numbers, read from two canonical registries:
28
+ * - agents: `.swarmdo/agents/store.json` + the hive `.swarmdo/agents.json`
29
+ * (what `agent_spawn`, `swarm_init`, `agent bridge register`, and hive-mind
30
+ * workers write) — every non-terminated agent is counted.
31
+ * - swarms: `.swarmdo/swarm/swarm-state.json` — every `status: 'running'`
32
+ * swarm whose host process is still alive; dead-pid / >24h-stale orphans are
33
+ * skipped read-only (mirrors reconcileOrphanSwarms without persisting).
34
+ *
35
+ * The agent count replaces the old `ps aux | grep -c agentic-flow` heuristic,
36
+ * which (a) was blind to in-process agents — Claude Code Task-tool subagents and
37
+ * bridged agents never appear as a separate `agentic-flow` OS process — so the
38
+ * count never moved when a swarm spun up, and (b) false-positived on any process
39
+ * whose args merely mention "agentic-flow" (a grep, an open editor buffer, the
40
+ * ONNX embedder), showing phantom agents when none were running.
41
+ *
42
+ * No `maxAgents` denominator is reported: swarm size is never enforced at spawn
43
+ * time (the config value is an advisory hint feeding an internal [1,50]
44
+ * backstop), so a `[N/max]` gauge would imply a live cap that does not exist.
45
+ */
46
+ export function computeSwarmStatus(cwd = process.cwd()) {
47
+ const agents = {};
48
+ // Both stores hold a { agents: { <id>: { status } } } map; merge them.
49
+ for (const rel of [['.swarmdo', 'agents', 'store.json'], ['.swarmdo', 'agents.json']]) {
50
+ try {
51
+ const p = join(cwd, ...rel);
52
+ if (existsSync(p)) {
53
+ const data = JSON.parse(readFileSync(p, 'utf-8'));
54
+ if (data && data.agents && typeof data.agents === 'object') {
55
+ Object.assign(agents, data.agents);
56
+ }
57
+ }
58
+ }
59
+ catch {
60
+ // unreadable/corrupt store — skip it, count what we can
61
+ }
62
+ }
63
+ let activeAgents = 0;
64
+ for (const id of Object.keys(agents)) {
65
+ if (agents[id] && agents[id].status !== 'terminated')
66
+ activeAgents++;
67
+ }
68
+ // Active swarms — running, non-orphaned entries in swarm-state.json.
69
+ let activeSwarms = 0;
70
+ try {
71
+ const sp = join(cwd, '.swarmdo', 'swarm', 'swarm-state.json');
72
+ if (existsSync(sp)) {
73
+ const store = JSON.parse(readFileSync(sp, 'utf-8'));
74
+ const swarms = store && store.swarms && typeof store.swarms === 'object' ? store.swarms : {};
75
+ const ORPHAN_TTL_MS = 24 * 60 * 60 * 1000;
76
+ const nowMs = Date.now();
77
+ for (const s of Object.values(swarms)) {
78
+ if (!s || s.status !== 'running')
79
+ continue;
80
+ if (typeof s.pid === 'number') {
81
+ if (!statuslinePidAlive(s.pid))
82
+ continue; // host process gone → orphan
83
+ }
84
+ else if (s.updatedAt) {
85
+ const ageMs = nowMs - new Date(s.updatedAt).getTime();
86
+ if (Number.isFinite(ageMs) && ageMs > ORPHAN_TTL_MS)
87
+ continue; // heartbeat stale
88
+ }
89
+ activeSwarms++;
90
+ }
91
+ }
92
+ }
93
+ catch {
94
+ // unreadable/corrupt swarm store — count 0
95
+ }
96
+ return {
97
+ activeSwarms,
98
+ activeAgents,
99
+ coordinationActive: activeSwarms > 0 || activeAgents > 0,
100
+ };
101
+ }
14
102
  /**
15
103
  * #1686 — `?? 0` only defaults null/undefined; NaN slips through and
16
104
  * surfaces as `"NaN"` (or earlier crashed `.toFixed`) in the metrics
@@ -3613,29 +3701,13 @@ const statuslineCommand = {
3613
3701
  // no scan has ever run — nothing is "pending", there is just no data
3614
3702
  return { status: 'NONE', critical: 0, high: 0, total: 0, scannedAt: null };
3615
3703
  }
3616
- // Get swarm status
3704
+ // Get swarm status from the canonical agent registry (not `ps | grep agentic-flow`).
3617
3705
  function getSwarmStatus() {
3618
- let activeAgents = 0;
3619
- let coordinationActive = false;
3620
- const maxAgents = 15;
3621
- const isWindows = process.platform === 'win32';
3622
- try {
3623
- const psCmd = isWindows
3624
- ? 'tasklist /FI "IMAGENAME eq node.exe" /NH 2>NUL | find /c /v "" 2>NUL || echo 0'
3625
- : 'ps aux 2>/dev/null | grep -c agentic-flow || echo "0"';
3626
- const ps = execSync(psCmd, { encoding: 'utf-8', timeout: 3000 });
3627
- activeAgents = Math.max(0, parseInt(ps.trim()) - 1);
3628
- coordinationActive = activeAgents > 0;
3629
- }
3630
- catch {
3631
- // ps/tasklist unavailable or timed out — report zero
3632
- }
3633
- return { activeAgents, maxAgents, coordinationActive };
3706
+ return computeSwarmStatus(process.cwd());
3634
3707
  }
3635
3708
  // Get system metrics
3636
3709
  function getSystemMetrics() {
3637
3710
  let memoryMB = 0;
3638
- let subAgents = 0;
3639
3711
  const learning = getLearningStats();
3640
3712
  try {
3641
3713
  memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
@@ -3697,7 +3769,7 @@ const statuslineCommand = {
3697
3769
  intelligencePct = Math.min(100, maturityScore);
3698
3770
  }
3699
3771
  const contextPct = Math.min(100, Math.floor(learning.sessions * 5));
3700
- return { memoryMB, contextPct, intelligencePct, subAgents };
3772
+ return { memoryMB, contextPct, intelligencePct };
3701
3773
  }
3702
3774
  // Get user info
3703
3775
  function getUserInfo() {
@@ -3751,7 +3823,7 @@ const statuslineCommand = {
3751
3823
  const terminalCols = process.stdout.columns ?? 80;
3752
3824
  const autoCompact = !ctx.flags.full && terminalCols < COMPACT_WIDTH_THRESHOLD;
3753
3825
  if (ctx.flags.compact || autoCompact) {
3754
- const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} Sec:${security.status} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
3826
+ const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} Sec:${security.status} Swarms:${swarm.activeSwarms} Agents:${swarm.activeAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
3755
3827
  output.writeln(line);
3756
3828
  return { success: true, data: statusData };
3757
3829
  }
@@ -3923,13 +3995,13 @@ const statuslineCommand = {
3923
3995
  const line1 = `${c.brightCyan}🏗️ DDD Domains${c.reset} ${progressBar(progress.domainsCompleted, progress.totalDomains)} ` +
3924
3996
  `${domainsColor}${progress.domainsCompleted}${c.reset}/${c.brightWhite}${progress.totalDomains}${c.reset} ` +
3925
3997
  perfIndicator;
3926
- const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}◉${c.reset}` : `${c.dim}○${c.reset}`;
3927
- const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
3998
+ const swarmsColor = swarm.activeSwarms > 0 ? c.brightGreen : c.dim;
3999
+ const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.dim;
3928
4000
  const securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'STALE' ? '🟡' : security.status === 'NONE' ? '⚪' : '🔴';
3929
4001
  const securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'STALE' ? c.brightYellow : security.status === 'NONE' ? c.dim : c.brightRed;
3930
4002
  const hooksColor = hooksStats.enabled > 0 ? c.brightGreen : c.dim;
3931
- const line2 = `${c.brightYellow}🤖 Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
3932
- `${c.brightPurple}👥 ${system.subAgents}${c.reset} ` +
4003
+ const line2 = `${c.brightYellow}🐝 Swarms${c.reset} ${swarmsColor}${swarm.activeSwarms}${c.reset} ` +
4004
+ `${c.brightYellow}🤖 Agents${c.reset} ${agentsColor}${swarm.activeAgents}${c.reset} ` +
3933
4005
  `${c.brightBlue}🪝 ${hooksColor}${hooksStats.enabled}${c.reset}/${c.brightWhite}${hooksStats.total}${c.reset} ` +
3934
4006
  `${securityIcon} ${securityColor}sec ${security.status === 'VULN' ? (security.critical + security.high) + '!' : security.status === 'CLEAN' ? '✓' : security.status === 'STALE' ? 'stale' : '—'}${c.reset} ` +
3935
4007
  `${c.brightCyan}💾 ${system.memoryMB}MB${c.reset} ` +
@@ -64,6 +64,10 @@ const commandLoaders = {
64
64
  // — commits since your last working day, grouped by day with a diffstat.
65
65
  standup: () => import('./standup.js'),
66
66
  mine: () => import('./standup.js'), // alias via loader key
67
+ // Hot/cold/orphan USAGE report for authored .claude/ slash-commands + subagents
68
+ // — the "dead-code report" for the authoring surface (config lint validates it).
69
+ commands: () => import('./commands.js'),
70
+ slash: () => import('./commands.js'), // alias via loader key
67
71
  // JUnit/TAP test-result parser (dorny/test-reporter demand) — raw results →
68
72
  // failing test + file:line + message; the front-half of the test→fix loop.
69
73
  testreport: () => import('./testreport.js'),
@@ -306,7 +310,7 @@ export async function getCommandsByCategory() {
306
310
  // three slots from 'statusline' onward (completionsCmd received the
307
311
  // statusline module, analyzeCmd received completions, …), scrambling the
308
312
  // categorized help. Every load now has a named slot.
309
- const [daemonCmd, doctorCmd, embeddingsCmd, neuralCmd, performanceCmd, securityCmd, swarmvectorCmd, hiveMindCmd, configCmd, statuslineCmd, compressCmd, efficiencyCmd, completionsCmd, migrateCmd, workflowCmd, analyzeCmd, routeCmd, progressCmd, providersCmd, pluginsCmd, deploymentCmd, claimsCmd, issuesCmd, updateCmd, processCmd, guidanceCmd, applianceCmd, cleanupCmd, autopilotCmd, demoCmd, usageCmd, repairCmd, hudCmd, compactCmd, codegraphCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, affectedCmd, cyclesCmd, couplingCmd, ownershipCmd, hiddenCouplingCmd, testreportCmd, compactSnapshotCmd, standupCmd,] = await Promise.all([
313
+ const [daemonCmd, doctorCmd, embeddingsCmd, neuralCmd, performanceCmd, securityCmd, swarmvectorCmd, hiveMindCmd, configCmd, statuslineCmd, compressCmd, efficiencyCmd, completionsCmd, migrateCmd, workflowCmd, analyzeCmd, routeCmd, progressCmd, providersCmd, pluginsCmd, deploymentCmd, claimsCmd, issuesCmd, updateCmd, processCmd, guidanceCmd, applianceCmd, cleanupCmd, autopilotCmd, demoCmd, usageCmd, repairCmd, hudCmd, compactCmd, codegraphCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, affectedCmd, cyclesCmd, couplingCmd, ownershipCmd, hiddenCouplingCmd, testreportCmd, compactSnapshotCmd, standupCmd, commandsUsageCmd,] = await Promise.all([
310
314
  loadCommand('daemon'), loadCommand('doctor'), loadCommand('embeddings'), loadCommand('neural'),
311
315
  loadCommand('performance'), loadCommand('security'), loadCommand('swarmvector'), loadCommand('hive-mind'),
312
316
  loadCommand('config'), loadCommand('statusline'), loadCommand('compress'), loadCommand('efficiency'),
@@ -314,7 +318,7 @@ export async function getCommandsByCategory() {
314
318
  loadCommand('analyze'), loadCommand('route'), loadCommand('progress'), loadCommand('providers'),
315
319
  loadCommand('plugins'), loadCommand('deployment'), loadCommand('claims'), loadCommand('issues'),
316
320
  loadCommand('update'), loadCommand('process'), loadCommand('guidance'), loadCommand('appliance'),
317
- loadCommand('cleanup'), loadCommand('autopilot'), loadCommand('demo'), loadCommand('usage'), loadCommand('repair'), loadCommand('hud'), loadCommand('compact'), loadCommand('codegraph'), loadCommand('redact'), loadCommand('pack'), loadCommand('env'), loadCommand('license'), loadCommand('sbom'), loadCommand('apply'), loadCommand('hotspots'), loadCommand('affected'), loadCommand('cycles'), loadCommand('coupling'), loadCommand('ownership'), loadCommand('hidden-coupling'), loadCommand('testreport'), loadCommand('compact-snapshot'), loadCommand('standup'),
321
+ loadCommand('cleanup'), loadCommand('autopilot'), loadCommand('demo'), loadCommand('usage'), loadCommand('repair'), loadCommand('hud'), loadCommand('compact'), loadCommand('codegraph'), loadCommand('redact'), loadCommand('pack'), loadCommand('env'), loadCommand('license'), loadCommand('sbom'), loadCommand('apply'), loadCommand('hotspots'), loadCommand('affected'), loadCommand('cycles'), loadCommand('coupling'), loadCommand('ownership'), loadCommand('hidden-coupling'), loadCommand('testreport'), loadCommand('compact-snapshot'), loadCommand('standup'), loadCommand('commands'),
318
322
  ]);
319
323
  return {
320
324
  primary: [
@@ -333,7 +337,7 @@ export async function getCommandsByCategory() {
333
337
  statuslineCmd, compressCmd, compactCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, affectedCmd, cyclesCmd, couplingCmd, ownershipCmd, hiddenCouplingCmd, testreportCmd, compactSnapshotCmd, efficiencyCmd,
334
338
  ].filter(Boolean),
335
339
  analysis: [
336
- analyzeCmd, routeCmd, progressCmd, usageCmd, hudCmd, codegraphCmd, standupCmd,
340
+ analyzeCmd, routeCmd, progressCmd, usageCmd, hudCmd, codegraphCmd, standupCmd, commandsUsageCmd,
337
341
  ].filter(Boolean),
338
342
  management: [
339
343
  providersCmd, pluginsCmd, deploymentCmd, claimsCmd,
@@ -355,6 +355,9 @@ const initCommand = {
355
355
  const result = await callMCPTool('swarm_init', {
356
356
  topology: topology,
357
357
  maxAgents,
358
+ // A one-shot CLI process is not a lasting host — create a persistent
359
+ // (pid-less) swarm so it isn't reaped the instant `swarm init` exits.
360
+ persistent: true,
358
361
  config: {
359
362
  communicationProtocol: 'message-bus',
360
363
  consensusMechanism: 'majority',
@@ -520,6 +523,7 @@ const startCommand = {
520
523
  topology: 'hierarchical',
521
524
  maxAgents: totalAgents,
522
525
  strategy: strategy === 'development' ? 'specialized' : strategy,
526
+ persistent: true, // CLI process is ephemeral — pid-less swarm survives exit
523
527
  });
524
528
  spinner.succeed('Swarm initialized via MCP');
525
529
  }
@@ -818,6 +822,7 @@ const coordinateCommand = {
818
822
  topology: 'hierarchical-mesh',
819
823
  maxAgents: agentCount,
820
824
  strategy: 'specialized',
825
+ persistent: true, // CLI process is ephemeral — pid-less swarm survives exit
821
826
  });
822
827
  output.printSuccess(`Swarm coordination initialized with ${agentCount} agent slots via MCP`);
823
828
  }
@@ -15,6 +15,7 @@
15
15
  import { output } from '../output.js';
16
16
  import { aggregateBlocks, aggregateUsage, collectUsage, totalUsage, localDateKey, } from '../usage/transcript-usage.js';
17
17
  import { collectToolErrors, delegationFromReport } from '../usage/transcript-errors.js';
18
+ import { collectFriction } from '../usage/transcript-friction.js';
18
19
  import { computeCacheStats } from '../usage/cache-stats.js';
19
20
  import { evaluateGuard } from '../usage/spend-guard.js';
20
21
  import { resolvePeriodPair, parseRange, diffPeriods, modelMovers } from '../usage/diff.js';
@@ -201,6 +202,53 @@ function runErrorsView(ctx, report) {
201
202
  }
202
203
  return { success: true, exitCode: 0 };
203
204
  }
205
+ /** Friction view — how often you interrupt Claude (ESC mid-turn), which tool was
206
+ * running when you did, and what KIND of errors dominate. The sniffly axes that
207
+ * `usage errors` (error rate) left on the table (#102). */
208
+ function runFrictionView(ctx, report) {
209
+ if (ctx.flags.json === true) {
210
+ output.writeln(JSON.stringify(report, null, 2));
211
+ return { success: true, exitCode: 0 };
212
+ }
213
+ const pct = (v) => `${(v * 100).toFixed(1)}%`;
214
+ if (ctx.flags.csv === true) {
215
+ // Category table export (parity with `usage --csv`).
216
+ const headers = ['category', 'count', 'share'];
217
+ const rows = report.categories.map((c) => [c.category, c.count, c.share.toFixed(4)]);
218
+ output.writeln(toCsv(headers, rows));
219
+ return { success: true, exitCode: 0 };
220
+ }
221
+ output.writeln(output.bold('Claude Code usage — friction'));
222
+ if (report.interruptions === 0 && report.totalErrors === 0) {
223
+ output.writeln(output.info(`no friction detected in ${report.filesScanned} transcript files`));
224
+ return { success: true, exitCode: 0 };
225
+ }
226
+ output.writeln(output.dim(`${report.filesScanned} files · ${fmtInt(report.interruptions)} interruption${report.interruptions === 1 ? '' : 's'} across ${fmtInt(report.assistantTurns)} assistant turns (${pct(report.interruptionRate)})`));
227
+ if (report.byTool.length > 0) {
228
+ output.writeln('');
229
+ output.writeln(output.bold('Interrupted while running'));
230
+ output.printTable({
231
+ columns: [
232
+ { key: 'tool', header: 'Tool / context', width: 22 },
233
+ { key: 'count', header: 'Interrupts', width: 12, align: 'right' },
234
+ ],
235
+ data: report.byTool.slice(0, 15).map((t) => ({ tool: t.tool, count: fmtInt(t.interruptions) })),
236
+ });
237
+ }
238
+ if (report.categories.length > 0) {
239
+ output.writeln('');
240
+ output.writeln(output.bold('Error categories') + output.dim(` (${fmtInt(report.totalErrors)} tool errors)`));
241
+ output.printTable({
242
+ columns: [
243
+ { key: 'category', header: 'Category', width: 14 },
244
+ { key: 'count', header: 'Count', width: 10, align: 'right' },
245
+ { key: 'share', header: 'Share', width: 8, align: 'right' },
246
+ ],
247
+ data: report.categories.map((c) => ({ category: c.category, count: fmtInt(c.count), share: pct(c.share) })),
248
+ });
249
+ }
250
+ return { success: true, exitCode: 0 };
251
+ }
204
252
  /** Cache view — prompt-cache efficiency + $ saved by caching (the #1 cost lever). */
205
253
  function runCacheView(ctx, collection) {
206
254
  const stats = computeCacheStats(aggregateUsage(collection.events, 'model'));
@@ -547,6 +595,13 @@ async function run(ctx) {
547
595
  const dirs = typeof dirFlag === 'string' ? [dirFlag] : Array.isArray(dirFlag) ? dirFlag.map(String) : undefined;
548
596
  return runErrorsView(ctx, collectToolErrors({ dirs, since, until }));
549
597
  }
598
+ if (viewName === 'friction') {
599
+ const since = typeof ctx.flags.since === 'string' ? ctx.flags.since : undefined;
600
+ const until = typeof ctx.flags.until === 'string' ? ctx.flags.until : undefined;
601
+ const dirFlag = ctx.flags.dir;
602
+ const dirs = typeof dirFlag === 'string' ? [dirFlag] : Array.isArray(dirFlag) ? dirFlag.map(String) : undefined;
603
+ return runFrictionView(ctx, collectFriction({ dirs, since, until }));
604
+ }
550
605
  if (viewName === 'cache') {
551
606
  const since = typeof ctx.flags.since === 'string' ? ctx.flags.since : undefined;
552
607
  const until = typeof ctx.flags.until === 'string' ? ctx.flags.until : undefined;
@@ -574,7 +629,7 @@ async function run(ctx) {
574
629
  }
575
630
  const view = VIEWS[viewName];
576
631
  if (!view) {
577
- output.writeln(output.error(`unknown view: ${viewName} (expected ${Object.keys(VIEWS).join('|')}|blocks|errors|cache|guard|diff|reflect|limits)`));
632
+ output.writeln(output.error(`unknown view: ${viewName} (expected ${Object.keys(VIEWS).join('|')}|blocks|errors|friction|cache|guard|diff|reflect|limits)`));
578
633
  return { success: false, exitCode: 1 };
579
634
  }
580
635
  const since = typeof ctx.flags.since === 'string' ? ctx.flags.since : undefined;
@@ -667,6 +722,7 @@ export const usageCommand = {
667
722
  { command: 'swarmdo usage projects --json', description: 'Per-project totals as JSON' },
668
723
  { command: 'swarmdo usage monthly --csv > usage.csv', description: 'Export monthly spend to CSV' },
669
724
  { command: 'swarmdo cost sessions --limit 10', description: 'Ten most expensive sessions (alias)' },
725
+ { command: 'swarmdo usage friction', description: 'How often you interrupt Claude + which error kinds dominate' },
670
726
  ],
671
727
  action: run,
672
728
  };
@@ -14,7 +14,17 @@ export declare const RETENTION: {
14
14
  export declare function storePath(cwd: string): string;
15
15
  export declare function loadMailbox(cwd: string): Mailbox;
16
16
  export declare function saveMailbox(cwd: string, box: Mailbox): void;
17
- /** This session's name: explicit override → SWARMDO_SESSION → hostname → "me". */
17
+ /**
18
+ * This session's name, in precedence order:
19
+ * explicit override (flag/param)
20
+ * → $SWARMDO_SESSION (set by Claude Code)
21
+ * → $SWARMDO_AGENT (harness-neutral — Codex/Copilot/pi agents export this)
22
+ * → hostname → "me".
23
+ *
24
+ * The $SWARMDO_AGENT fallback lets a non-Claude agent declare a stable identity
25
+ * once (instead of passing `from`/`--self` on every call); without it, every
26
+ * agent on a host collapses to the shared hostname and direct addressing breaks.
27
+ */
18
28
  export declare function resolveSelf(flag?: unknown): string;
19
29
  /** Crypto-strong message id. */
20
30
  export declare function newMessageId(): string;
@@ -32,10 +32,24 @@ export function saveMailbox(cwd, box) {
32
32
  mkdirSync(dirname(p), { recursive: true });
33
33
  writeFileSync(p, JSON.stringify(box, null, 2) + '\n', 'utf-8');
34
34
  }
35
- /** This session's name: explicit override → SWARMDO_SESSION → hostname → "me". */
35
+ /**
36
+ * This session's name, in precedence order:
37
+ * explicit override (flag/param)
38
+ * → $SWARMDO_SESSION (set by Claude Code)
39
+ * → $SWARMDO_AGENT (harness-neutral — Codex/Copilot/pi agents export this)
40
+ * → hostname → "me".
41
+ *
42
+ * The $SWARMDO_AGENT fallback lets a non-Claude agent declare a stable identity
43
+ * once (instead of passing `from`/`--self` on every call); without it, every
44
+ * agent on a host collapses to the shared hostname and direct addressing breaks.
45
+ */
36
46
  export function resolveSelf(flag) {
37
47
  const f = typeof flag === 'string' ? flag.trim() : '';
38
- return f || (process.env.SWARMDO_SESSION || '').trim() || hostname() || 'me';
48
+ return (f ||
49
+ (process.env.SWARMDO_SESSION || '').trim() ||
50
+ (process.env.SWARMDO_AGENT || '').trim() ||
51
+ hostname() ||
52
+ 'me');
39
53
  }
40
54
  /** Crypto-strong message id. */
41
55
  export function newMessageId() {
@@ -16,7 +16,7 @@ import type { InitOptions } from './types.js';
16
16
  * ▊ Swarmdo V3.6 ● user │ ⎇ branch │ Opus 4.7
17
17
  * ─────────────────────────────────────────────────────
18
18
  * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
19
- * 🤖 Swarm [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
19
+ * 🐝 Swarms 1 🤖 Agents 5 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
20
20
  * 🔧 Architecture ADRs ●71% │ DDD ● 13% │ Security ●CLEAN
21
21
  * 📊 AgentDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
22
22
  */
@@ -53,7 +53,7 @@ const BAKED_CLI_VERSION = readBakedCliVersion();
53
53
  * ▊ Swarmdo V3.6 ● user │ ⎇ branch │ Opus 4.7
54
54
  * ─────────────────────────────────────────────────────
55
55
  * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
56
- * 🤖 Swarm [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
56
+ * 🐝 Swarms 1 🤖 Agents 5 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
57
57
  * 🔧 Architecture ADRs ●71% │ DDD ● 13% │ Security ●CLEAN
58
58
  * 📊 AgentDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
59
59
  */
@@ -166,7 +166,7 @@ function resolveCliBin() {
166
166
  ];
167
167
  try {
168
168
  const binDir = path.dirname(process.execPath);
169
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
169
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules'), '/opt/homebrew/lib/node_modules', '/usr/local/lib/node_modules'];
170
170
  for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
171
171
  if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
172
172
  }
@@ -209,6 +209,13 @@ function writeCache(data) {
209
209
  * and only counted ADRs in v3/implementation/adrs/ (missed v3/docs/adr/).
210
210
  */
211
211
  function getStatuslineData() {
212
+ // SWARMDO_STATUSLINE_NO_CLI: skip the CLI-delegation fork and render from local
213
+ // reads + stdin only. A cold render otherwise forks the hooks-statusline CLI
214
+ // (now more reachable via the global-node_modules probes below), and that fork
215
+ // can disturb the stdin cost payload; skipping it keeps renders fast, hermetic,
216
+ // and subprocess-free. Set by the statusline tests; usable by anyone who wants
217
+ // a no-fork statusline.
218
+ if (process.env.SWARMDO_STATUSLINE_NO_CLI) return buildLocalFallback();
212
219
  const cached = readCache();
213
220
  if (cached) return cached;
214
221
 
@@ -385,8 +392,8 @@ function buildLocalFallback() {
385
392
  user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
386
393
  v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
387
394
  security: { status: 'NONE', critical: 0, high: 0, total: 0 },
388
- swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
389
- system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
395
+ swarm: { activeSwarms: 0, activeAgents: 0, coordinationActive: false },
396
+ system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0 },
390
397
  lastUpdated: new Date().toISOString(),
391
398
  });
392
399
  }
@@ -592,7 +599,7 @@ function getPkgVersion() {
592
599
  // (bin/node_modules) layouts.
593
600
  try {
594
601
  const binDir = path.dirname(process.execPath);
595
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
602
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules'), '/opt/homebrew/lib/node_modules', '/usr/local/lib/node_modules'];
596
603
  // #2221 follow-up: a custom npm prefix (e.g. ~/.npm-global) is decoupled from
597
604
  // the node binary location, so the binDir-derived probes above all miss. Also
598
605
  // probe the npm prefix from the environment and the common ~/.npm-global default.
@@ -646,12 +653,11 @@ function generateStatusline() {
646
653
  const totalDomains = progress.totalDomains || 5;
647
654
  const dddProgress = progress.dddProgress || 0;
648
655
  const patternsLearned = progress.patternsLearned || 0;
656
+ const activeSwarms = swarm.activeSwarms || 0;
649
657
  const activeAgents = swarm.activeAgents || 0;
650
- const maxAgents = swarm.maxAgents || CONFIG.maxAgents;
651
658
  const coordinationActive = swarm.coordinationActive || false;
652
659
  const intelligencePct = system.intelligencePct || 0;
653
660
  const memoryMB = system.memoryMB || 0;
654
- const subAgents = system.subAgents || 0;
655
661
  const secCritHigh = (security.critical || 0) + (security.high || 0);
656
662
  const secStatus = security.status || 'NONE';
657
663
  const secLabel = secStatus === 'VULN' ? 'sec ' + secCritHigh + '!' : secStatus === 'CLEAN' ? 'sec ✓' : secStatus === 'STALE' ? 'sec stale' : 'sec —';
@@ -718,16 +724,16 @@ function generateStatusline() {
718
724
  );
719
725
 
720
726
  // Line 2: Swarm + Hooks + CVE + Memory + Intelligence
721
- const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset : c.dim + '○' + c.reset;
722
- const agentsColor = activeAgents > 0 ? c.brightGreen : c.red;
727
+ const swarmsColor = activeSwarms > 0 ? c.brightGreen : c.dim;
728
+ const agentsColor = activeAgents > 0 ? c.brightGreen : c.dim;
723
729
  const secIcon = secStatus === 'CLEAN' ? '🟢' : secStatus === 'STALE' ? '🟡' : (secStatus === 'NONE' ? '⚪' : '🔴');
724
730
  const secColor = secStatus === 'CLEAN' ? c.brightGreen : secStatus === 'STALE' ? c.brightYellow : (secStatus === 'NONE' ? c.dim : c.brightRed);
725
731
  const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
726
732
  const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
727
733
 
728
734
  if (seg('swarm')) lines.push(
729
- c.brightYellow + '🤖 Swarm' + c.reset + ' ' + swarmInd + ' [' + agentsColor + String(activeAgents).padStart(2) + c.reset + '/' + c.brightWhite + maxAgents + c.reset + '] ' +
730
- c.brightPurple + '👥 ' + subAgents + c.reset + ' ' +
735
+ c.brightYellow + '🐝 Swarms' + c.reset + ' ' + swarmsColor + activeSwarms + c.reset + ' ' +
736
+ c.brightYellow + '🤖 Agents' + c.reset + ' ' + agentsColor + activeAgents + c.reset + ' ' +
731
737
  c.brightBlue + '🪝 ' + hooksColor + hooksEnabled + c.reset + '/' + c.brightWhite + hooksTotal + c.reset + ' ' +
732
738
  secIcon + ' ' + secColor + secLabel + c.reset + ' ' +
733
739
  c.brightCyan + '💾 ' + memoryMB + 'MB' + c.reset + ' ' +
@@ -63,23 +63,34 @@ export function crossAgentAgentsMd() {
63
63
  ## What swarmdo gives you
64
64
 
65
65
  This project is wired to **swarmdo** — agent orchestration with persistent
66
- vector memory, swarm coordination, and 300+ MCP tools.
66
+ vector memory, swarm coordination, cross-agent messaging, and 300+ MCP tools.
67
+ Everything below works the same from Codex, Copilot, pi, or a plain shell —
68
+ not just Claude Code.
67
69
 
68
- - **MCP server**: \`swarmdo mcp start\` (stdio). If your CLI supports MCP,
69
- \`swarmdo integrations install <your-cli>\` wires it; key tools:
70
- \`memory_search\`, \`memory_store\`, \`swarm_init\`, \`agent_spawn\`.
70
+ - **MCP server**: \`swarmdo mcp start\` (stdio; serves every tool by default).
71
+ If your CLI supports MCP, \`swarmdo integrations install <your-cli>\` wires
72
+ it. Key tools: \`memory_search\`, \`memory_store\`, \`swarm_init\`,
73
+ \`agent_spawn\`, \`comms_send\` / \`comms_inbox\` (message other agents),
74
+ \`hotspots\` / \`coupling\` / \`ownership\` (git-history analysis).
71
75
  - **CLI**: \`npx swarmdo@latest <command>\` — \`memory search -q "topic"\`,
72
- \`usage\`, \`task\`, \`hud\`, \`security scan\`, \`config lint\`.
76
+ \`comms send\` / \`comms inbox\`, \`standup\`, \`hotspots\`, \`coupling\`,
77
+ \`ownership\`, \`hidden-coupling\`, \`task\`, \`hud\`.
73
78
 
74
79
  ## Working agreement (any agent)
75
80
 
76
81
  1. **Search memory BEFORE starting**: \`memory_search(query="task keywords")\`
77
82
  — patterns with score > 0.7 are load-bearing precedent.
78
- 2. **You are the executor** swarmdo coordinates and remembers; it never
83
+ 2. **Coordinate through the cross-tool mailbox**: announce the files you're
84
+ about to change with \`comms_send(to="all", message="…")\` and check
85
+ \`comms_inbox\` before overlapping work. Non-Claude agents: set a stable
86
+ identity with \`export SWARMDO_AGENT=<your-name>\` (or pass \`from\` /
87
+ \`--self\`) so replies address you — without it every agent on this host
88
+ shares the hostname identity.
89
+ 3. **You are the executor** — swarmdo coordinates and remembers; it never
79
90
  writes your code for you.
80
- 3. **Store what worked AFTER success**:
91
+ 4. **Store what worked AFTER success**:
81
92
  \`memory_store(key="pattern-…", value="what worked", namespace="patterns")\`.
82
- 4. Claude Code users: the same surfaces are namespaced under \`/sDo:\`
93
+ 5. Claude Code users: the same surfaces are namespaced under \`/sDo:\`
83
94
  commands and \`/sdo-\` skills.
84
95
 
85
96
  ## Do not
@@ -12,7 +12,7 @@ import { loadMailbox, saveMailbox, resolveSelf, newMessageId, RETENTION } from '
12
12
  export const commsTools = [
13
13
  {
14
14
  name: 'comms_send',
15
- description: 'Send a message to another Claude Code session working on the same repo (cross-session mailbox). Address "all" to broadcast to every session. Use for multi-session/"multiplayer" coordination; delivery is via a shared file, not the network.',
15
+ description: 'Send a message to another agent session — Claude Code, Codex, Copilot, pi, or any tool on the same repo (a cross-session/cross-tool mailbox). Address "all" to broadcast to every session. Use for multi-agent "multiplayer" coordination; delivery is via a shared file, not the network.',
16
16
  category: 'comms',
17
17
  inputSchema: {
18
18
  type: 'object',
@@ -20,7 +20,7 @@ export const commsTools = [
20
20
  to: { type: 'string', description: 'Recipient session name, or "all" to broadcast' },
21
21
  message: { type: 'string', description: 'Message body' },
22
22
  subject: { type: 'string', description: 'Optional subject line' },
23
- from: { type: 'string', description: 'Sender name (default: this session $SWARMDO_SESSION or hostname)' },
23
+ from: { type: 'string', description: 'Sender name. Default: this session ($SWARMDO_SESSION, else $SWARMDO_AGENT, else hostname). Non-Claude agents should pass this (or export $SWARMDO_AGENT) so replies address them, not the shared hostname.' },
24
24
  },
25
25
  required: ['to', 'message'],
26
26
  },
@@ -49,12 +49,12 @@ export const commsTools = [
49
49
  },
50
50
  {
51
51
  name: 'comms_inbox',
52
- description: 'List messages other sessions have sent to this session (cross-session mailbox), newest first. Includes "all" broadcasts. Set markRead to acknowledge them.',
52
+ description: 'List messages other agent sessions (Claude Code, Codex, Copilot, pi, …) have sent to this one — a cross-session/cross-tool mailbox newest first. Includes "all" broadcasts. Set markRead to acknowledge them.',
53
53
  category: 'comms',
54
54
  inputSchema: {
55
55
  type: 'object',
56
56
  properties: {
57
- to: { type: 'string', description: 'Whose inbox (default: this session)' },
57
+ to: { type: 'string', description: 'Whose inbox (default: this session — $SWARMDO_SESSION, else $SWARMDO_AGENT, else hostname)' },
58
58
  unreadOnly: { type: 'boolean', description: 'Only unread messages' },
59
59
  from: { type: 'string', description: 'Only messages from this sender' },
60
60
  since: { type: 'string', description: 'Only messages after this ISO-8601 timestamp' },
@@ -179,6 +179,7 @@ export const swarmTools = [
179
179
  maxAgents: { type: 'number', description: 'Maximum number of agents (1-50)' },
180
180
  strategy: { type: 'string', description: 'Agent strategy (specialized, balanced, adaptive)' },
181
181
  config: { type: 'object', description: 'Additional swarm configuration' },
182
+ persistent: { type: 'boolean', description: 'Create a persistent (pid-less) swarm that survives the creating process — set by the CLI, whose process is ephemeral. Reaped by the 24h idle-TTL instead of on-exit. Leave unset from a long-lived host (MCP server/daemon) so a dead host is reaped promptly.' },
182
183
  },
183
184
  },
184
185
  handler: async (input) => {
@@ -201,6 +202,7 @@ export const swarmTools = [
201
202
  maxAgents: input.maxAgents,
202
203
  strategy,
203
204
  config: (input.config || {}),
205
+ persistent: input.persistent,
204
206
  });
205
207
  }
206
208
  catch (e) {