swarmdo 1.54.1 → 1.58.1

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 (183) hide show
  1. package/.claude/helpers/statusline.cjs +10 -3
  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/index.js +7 -3
  9. package/v3/@swarmdo/cli/dist/src/commands/usage.js +57 -1
  10. package/v3/@swarmdo/cli/dist/src/comms/store.d.ts +11 -1
  11. package/v3/@swarmdo/cli/dist/src/comms/store.js +16 -2
  12. package/v3/@swarmdo/cli/dist/src/init/statusline-generator.js +9 -2
  13. package/v3/@swarmdo/cli/dist/src/integrations/integrations.js +19 -8
  14. package/v3/@swarmdo/cli/dist/src/mcp-tools/comms-tools.js +4 -4
  15. package/v3/@swarmdo/cli/dist/src/usage/transcript-friction.d.ts +77 -0
  16. package/v3/@swarmdo/cli/dist/src/usage/transcript-friction.js +151 -0
  17. package/v3/@swarmdo/cli/package.json +1 -1
  18. package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
  19. package/v3/@swarmdo/codex/dist/cli.js +0 -0
  20. package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
  21. package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
  22. package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
  23. package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +4 -1
  24. package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
  25. package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
  26. package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
  27. package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
  28. package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
  29. package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
  30. package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
  31. package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
  32. package/v3/@swarmdo/shared/dist/mcp/transport/http.js +8 -1
  33. package/.claude/settings.local.json +0 -7
  34. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +0 -2
  35. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +0 -2
  36. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +0 -138
  37. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +0 -418
  38. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +0 -15
  39. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +0 -240
  40. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +0 -7
  41. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +0 -508
  42. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +0 -54
  43. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +0 -2
  44. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +0 -46
  45. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +0 -2
  46. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +0 -18
  47. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +0 -28
  48. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +0 -5
  49. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +0 -2
  50. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +0 -35
  51. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +0 -2
  52. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +0 -11
  53. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +0 -2
  54. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +0 -9
  55. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +0 -2
  56. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +0 -5
  57. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +0 -2
  58. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +0 -12
  59. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +0 -2
  60. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +0 -19
  61. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +0 -2
  62. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +0 -44
  63. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +0 -117
  64. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +0 -70
  65. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +0 -194
  66. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +0 -69
  67. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +0 -139
  68. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +0 -34
  69. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +0 -101
  70. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +0 -10
  71. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +0 -10
  72. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +0 -64
  73. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +0 -70
  74. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +0 -44
  75. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +0 -79
  76. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +0 -37
  77. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +0 -39
  78. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +0 -67
  79. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +0 -58
  80. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +0 -45
  81. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +0 -63
  82. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +0 -26
  83. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +0 -17
  84. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +0 -51
  85. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +0 -110
  86. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +0 -13
  87. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +0 -25
  88. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +0 -11
  89. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +0 -19
  90. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +0 -11
  91. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +0 -27
  92. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +0 -6
  93. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +0 -6
  94. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +0 -41
  95. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +0 -64
  96. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +0 -8
  97. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +0 -703
  98. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +0 -23
  99. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +0 -239
  100. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +0 -17
  101. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +0 -42
  102. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +0 -18
  103. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +0 -45
  104. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +0 -19
  105. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +0 -50
  106. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +0 -19
  107. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +0 -47
  108. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +0 -17
  109. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +0 -40
  110. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +0 -18
  111. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +0 -51
  112. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +0 -214
  113. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +0 -595
  114. package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +0 -76
  115. package/v3/@swarmdo/testing/dist/fixtures/agents.js +0 -158
  116. package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +0 -157
  117. package/v3/@swarmdo/testing/dist/fixtures/configurations.js +0 -337
  118. package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +0 -10
  119. package/v3/@swarmdo/testing/dist/fixtures/index.js +0 -17
  120. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +0 -329
  121. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +0 -714
  122. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +0 -159
  123. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +0 -262
  124. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +0 -290
  125. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +0 -547
  126. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +0 -289
  127. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +0 -534
  128. package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +0 -106
  129. package/v3/@swarmdo/testing/dist/fixtures/tasks.js +0 -229
  130. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +0 -213
  131. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +0 -435
  132. package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +0 -135
  133. package/v3/@swarmdo/testing/dist/helpers/assertions.js +0 -217
  134. package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +0 -96
  135. package/v3/@swarmdo/testing/dist/helpers/create-mock.js +0 -164
  136. package/v3/@swarmdo/testing/dist/helpers/index.d.ts +0 -14
  137. package/v3/@swarmdo/testing/dist/helpers/index.js +0 -22
  138. package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +0 -323
  139. package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +0 -350
  140. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +0 -271
  141. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +0 -446
  142. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +0 -142
  143. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +0 -205
  144. package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +0 -190
  145. package/v3/@swarmdo/testing/dist/helpers/test-application.js +0 -138
  146. package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +0 -274
  147. package/v3/@swarmdo/testing/dist/helpers/test-utils.js +0 -456
  148. package/v3/@swarmdo/testing/dist/index.d.ts +0 -42
  149. package/v3/@swarmdo/testing/dist/index.js +0 -66
  150. package/v3/@swarmdo/testing/dist/mocks/index.d.ts +0 -8
  151. package/v3/@swarmdo/testing/dist/mocks/index.js +0 -10
  152. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +0 -206
  153. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +0 -575
  154. package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +0 -287
  155. package/v3/@swarmdo/testing/dist/mocks/mock-services.js +0 -563
  156. package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +0 -109
  157. package/v3/@swarmdo/testing/dist/regression/api-contract.js +0 -374
  158. package/v3/@swarmdo/testing/dist/regression/index.d.ts +0 -18
  159. package/v3/@swarmdo/testing/dist/regression/index.js +0 -18
  160. package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +0 -67
  161. package/v3/@swarmdo/testing/dist/regression/integration-regression.js +0 -334
  162. package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +0 -106
  163. package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +0 -268
  164. package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +0 -124
  165. package/v3/@swarmdo/testing/dist/regression/regression-runner.js +0 -227
  166. package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +0 -73
  167. package/v3/@swarmdo/testing/dist/regression/security-regression.js +0 -258
  168. package/v3/@swarmdo/testing/dist/setup.d.ts +0 -43
  169. package/v3/@swarmdo/testing/dist/setup.js +0 -91
  170. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +0 -10
  171. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +0 -433
  172. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +0 -10
  173. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +0 -352
  174. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +0 -206
  175. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +0 -839
  176. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +0 -10
  177. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +0 -428
  178. package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +0 -38
  179. package/v3/@swarmdo/testing/dist/v2-compat/index.js +0 -41
  180. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +0 -10
  181. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +0 -419
  182. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +0 -34
  183. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +0 -372
@@ -105,7 +105,7 @@ function resolveCliBin() {
105
105
  ];
106
106
  try {
107
107
  const binDir = path.dirname(process.execPath);
108
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
108
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules'), '/opt/homebrew/lib/node_modules', '/usr/local/lib/node_modules'];
109
109
  for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
110
110
  if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
111
111
  }
@@ -148,6 +148,13 @@ function writeCache(data) {
148
148
  * and only counted ADRs in v3/implementation/adrs/ (missed v3/docs/adr/).
149
149
  */
150
150
  function getStatuslineData() {
151
+ // SWARMDO_STATUSLINE_NO_CLI: skip the CLI-delegation fork and render from local
152
+ // reads + stdin only. A cold render otherwise forks the hooks-statusline CLI
153
+ // (now more reachable via the global-node_modules probes below), and that fork
154
+ // can disturb the stdin cost payload; skipping it keeps renders fast, hermetic,
155
+ // and subprocess-free. Set by the statusline tests; usable by anyone who wants
156
+ // a no-fork statusline.
157
+ if (process.env.SWARMDO_STATUSLINE_NO_CLI) return buildLocalFallback();
151
158
  const cached = readCache();
152
159
  if (cached) return cached;
153
160
 
@@ -515,7 +522,7 @@ function getCostFromStdin() {
515
522
  // misses, the displayed version is meaningful (matches what the user
516
523
  // installed), not a stale hard-coded string.
517
524
  function getPkgVersion() {
518
- let ver = '1.19.0';
525
+ let ver = '1.58.0';
519
526
  try {
520
527
  const home = os.homedir();
521
528
  const pkgPaths = [
@@ -531,7 +538,7 @@ function getPkgVersion() {
531
538
  // (bin/node_modules) layouts.
532
539
  try {
533
540
  const binDir = path.dirname(process.execPath);
534
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
541
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules'), '/opt/homebrew/lib/node_modules', '/usr/local/lib/node_modules'];
535
542
  // #2221 follow-up: a custom npm prefix (e.g. ~/.npm-global) is decoupled from
536
543
  // the node binary location, so the binDir-derived probes above all miss. Also
537
544
  // probe the npm prefix from the environment and the common ~/.npm-global default.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![swarmdo](swarmdo/assets/brand/logo-full.svg)](https://swarmdo.com)
4
4
 
5
- [![npm version (swarmdo)](https://img.shields.io/badge/npx%20swarmdo-v1.54.1-cb3837?style=for-the-badge&logo=npm&logoColor=white)](https://github.com/SwarmDo/swarmdo/releases)
5
+ [![npm version (swarmdo)](https://img.shields.io/badge/npx%20swarmdo-v1.58.1-cb3837?style=for-the-badge&logo=npm&logoColor=white)](https://github.com/SwarmDo/swarmdo/releases)
6
6
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://github.com/SwarmDo/swarmdo/blob/main/LICENSE)
7
7
  [![Website](https://img.shields.io/badge/swarmdo.com-e2a33c?style=for-the-badge&logoColor=black)](https://swarmdo.com)
8
8
  [![Star on GitHub](https://img.shields.io/github/stars/SwarmDo/swarmdo?style=for-the-badge&logo=github&color=gold)](https://github.com/SwarmDo/swarmdo)
@@ -271,7 +271,7 @@ The recent release train added a full day-to-day operations layer around the swa
271
271
 
272
272
  | Command | What it does |
273
273
  |---------|-------------|
274
- | `swarmdo usage` (alias `cost`) | Claude Code **spend analytics** from your local transcripts — `daily`, `weekly`, `monthly` (with a month-end spend projection), `models`, `projects`, `sessions`, live 5-hour `blocks` burn, `errors` (tool-failure analytics), `cache` (prompt-cache efficiency + $ saved), `diff` (period-over-period comparison with per-model movers), `reflect` (a wrapped-style retrospective — busiest day, streak, top models/projects, peak hour, delegation ratio — with a shareable `--html` dashboard), and `limits` (an official-quota **exhaustion forecaster** over Claude Code's `rate_limits`); the standard views take `--csv` for spreadsheet export |
274
+ | `swarmdo usage` (alias `cost`) | Claude Code **spend analytics** from your local transcripts — `daily`, `weekly`, `monthly` (with a month-end spend projection), `models`, `projects`, `sessions`, live 5-hour `blocks` burn, `errors` (tool-failure analytics), `friction` (how often you interrupt Claude + which tool was running + dominant error categories), `cache` (prompt-cache efficiency + $ saved), `diff` (period-over-period comparison with per-model movers), `reflect` (a wrapped-style retrospective — busiest day, streak, top models/projects, peak hour, delegation ratio — with a shareable `--html` dashboard), and `limits` (an official-quota **exhaustion forecaster** over Claude Code's `rate_limits`); the standard views take `--csv` for spreadsheet export |
275
275
  | `swarmdo usage guard` | **Budget policy** — limits for the active 5h block / today / month via flags or `SWARMDO_GUARD_*` env → ok / warn / over; `--strict` exits 1, safe for CI gates and Stop hooks |
276
276
  | `swarmdo hud` | **One-screen ops HUD** — 5h block burn, task readiness, daemon workers, memory snapshots (`--watch`, `--json`) |
277
277
  | `swarmdo repair` (alias `tdd-repair`) | **Test-Driven Repair** — a bounded, budget-capped headless `claude` loop that fixes source until a failing test passes; dry-run unless `--confirm` |
@@ -293,6 +293,7 @@ The recent release train added a full day-to-day operations layer around the swa
293
293
  | `swarmdo ownership` | **Who owns each file, and what breaks if they leave** — a per-file knowledge map + bus factor from git history: the dominant author, ownership concentration, and the fewest authors whose churn clears 50% (a lone owner is flagged ⚠ key-person), plus a repo-wide truck factor. `--since`, `--min-churn`, `--top`, `--csv`, `--format json`. code-maat main-dev / CodeScene "Knowledge Map"; also an MCP tool (`ownership`). Deterministic |
294
294
  | `swarmdo hidden-coupling` | **Co-change with no import edge** — join the two graphs swarmdo already owns (temporal `coupling` from git + `codegraph`'s import graph) and emit the set difference: file pairs that keep changing together yet nothing in the code links them ("logical minus structural coupling" — a config and its consumers, a schema and its mirror type). The co-edit `affected` can't see. `--since`, `--min-shared`, `--csv`, `--format json`. Grounded in Gall et al. (ICSM 1998). Deterministic |
295
295
  | `swarmdo standup` (alias `mine`) | **What did I do?** — your commits since your last working day, grouped by day with a diffstat. **Weekend-aware** (git-standup parity): on Monday it reaches back to Friday, on Sunday to Friday, otherwise to yesterday — one-command recall for a standup or for re-orienting at the start of a session. Defaults to the current git user; `--author <name>`/`--all` for teammates, `--days N` or `--since 2w` to override the window, `--format json`. Pure engine (weekend math + per-day fold) → fixture-tested. Deterministic |
296
+ | `swarmdo commands` (alias `slash`) | **Hot/cold/orphan usage report** for your authored `.claude/` surface — which custom slash-commands (`.claude/commands/**`) and subagents (`.claude/agents/**`) you actually invoke (**hot**, with counts), which are **cold** (defined but never used → prune candidates), and which are **orphan** (invoked but undefined → a typo or a builtin). `config lint` says "are these valid?"; this says "are these used?" — joining the defined files against invocation counts mined from local transcripts (`<command-name>` markers + `subagent_type` fields). `--unused` prints just the prune list, `--unused --strict` is a CI gate (exit 1 if anything's dead), `--all` counts across every project, `--json`. Pure parsers + join → fixture-tested |
296
297
  | `swarmdo agent bridge` | **Link Claude Code agents ⇄ Swarmdo** — the fix for "Swarmdo is installed but never actually used." Swarmdo can't call Claude Code's `Agent` tool (only the main loop can), so `agent bridge register -n <name>` binds a real Claude Code agent into Swarmdo's registry (`swarmdo agent list` stops being empty) and **auto-spins-up a swarm from your config, enrolling the agent** — the next bind joins the same swarm. `bridge list` (bound vs native), `bridge sync --live a,b` (reconcile drift), `bridge advise "<prompt>"` (should this warrant a swarm? which roles?). Also MCP tools `agent_bridge_register`/`agent_bridge_list`. Pure engine → 16 tests |
297
298
  | `swarmdo testreport` | **JUnit/TAP → failure digest** — turn raw test-result files into the exact failing test names + `file:line` + assertion messages, instead of scanning hundreds of log lines. The front-half of the test→fix loop: feed the failures straight into `repair`. Reads a file, a directory, or stdin; `--ci` exits 1 on any failure, `--format json`. Also an MCP tool (`testreport`). Deterministic |
298
299
  | `swarmdo integrations` (alias `integrate`) | **Use swarmdo from Codex CLI, GitHub Copilot CLI, and pi** — one command wires AGENTS.md + each CLI's MCP config (idempotent, dry-run first, never touches your Claude Code setup) |
@@ -301,7 +302,7 @@ The recent release train added a full day-to-day operations layer around the swa
301
302
  | `swarmdo mcp doctor` | **MCP config diagnosis** — missing binaries, bad URLs, malformed entries across `.mcp.json` + `~/.claude.json` |
302
303
  | `swarmdo config lint` | **Static config validation** — the pure shape layer for `swarmdo.config.json`, the `.claude/settings*.json` hooks block, `.mcp.json`, **`.claude/agents/*.md` subagents** (missing `name`/`description`, a `name` duplicated across files — CC silently loads only one — a bad `model`, malformed frontmatter), and **custom slash commands + skills** (malformed YAML that makes CC load empty metadata, a bad `effort`, an inline `` !`cmd` `` bash-injection that's inert or not covered by `allowed-tools`). `--strict` gates CI |
303
304
  | `swarmdo permissions` (alias `perms`) | **Audit your Claude Code permission rules** — static analysis of `permissions.allow`/`deny`/`ask` in `.claude/settings*.json`: flags allow↔deny conflicts (dead rules), over-broad `Bash(*)` grants, shadowed/redundant rules, duplicates, and malformed entries. `--strict` gates CI. Read-only; the static-safety sibling of `config lint` / `mcp doctor` |
304
- | `swarmdo comms` (alias `mailbox`) | **Cross-session agent mailbox** — one Claude Code session messages another by name (`send -t <session>`, `-t all` broadcasts, `inbox`, `read`, `watch`); sessions on the same repo share `.swarmdo/comms/`. `inbox --hook` surfaces new mail as prompt context without polling. Also MCP tools (`comms_send`/`comms_inbox`) |
305
+ | `swarmdo comms` (alias `mailbox`) | **Cross-session, cross-tool agent mailbox** — one agent session (Claude Code, Codex, Copilot, or pi) messages another by name (`send -t <session>`, `-t all` broadcasts, `inbox`, `read`, `watch`); sessions on the same repo share `.swarmdo/comms/`. Non-Claude agents set `$SWARMDO_AGENT` for a stable identity. `inbox --hook` surfaces new mail as prompt context without polling. Also MCP tools (`comms_send`/`comms_inbox`) |
305
306
  | `swarmdo hooks memory-inject` | **Prompt-time semantic memory injection** — embeds each prompt, vector-searches your stored memories, and injects the most relevant under a token budget (recall at the moment of need); wire it with `hooks recipe memory-inject` |
306
307
  | `swarmdo hooks notify -d` | **Desktop notifications** — OS-native toast (macOS `osascript`, Linux `notify-send`) |
307
308
  | `swarmdo hooks recipe` | **One-command Claude Code hooks** — install `notify-done`/`notify-input` (desktop pings), `memory-inject` (relevant memories each prompt), `comms-inbox` (new mail as context), or `command-guard` (a PreToolUse deny hook that blocks dangerous bash — `rm -rf /`, pipe-to-shell, force-push to main — the net swarmdo needs when running headless); dry-run by default, idempotent merge that never clobbers your settings |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swarmdo",
3
- "version": "1.54.1",
3
+ "version": "1.58.1",
4
4
  "description": "Swarmdo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,74 @@
1
+ /**
2
+ * command-usage/usage.ts — a hot/cold/orphan USAGE report for a project's
3
+ * authored `.claude/` surface. `config lint` validates these files; this answers
4
+ * the orthogonal "are they actually used?" by joining the DEFINED set
5
+ * (`.claude/commands/**​/*.md` + `.claude/agents/**​/*.md`) against INVOCATION
6
+ * counts mined from the local transcripts (`<command-name>` markers +
7
+ * `"subagent_type"` fields). Three buckets: hot (defined & invoked), cold
8
+ * (defined, never invoked → prune candidates), orphan (invoked, not defined →
9
+ * typo / removed / builtin). #101.
10
+ *
11
+ * The parsers + join are pure and unit-tested; only collectCommandUsage touches
12
+ * the filesystem. Reuses defaultClaudeProjectDirs/findTranscriptFiles.
13
+ */
14
+ export interface UsageItem {
15
+ name: string;
16
+ count: number;
17
+ }
18
+ export interface UsageBuckets {
19
+ /** defined AND invoked (count > 0), desc by count */
20
+ hot: UsageItem[];
21
+ /** defined but never invoked — prune candidates, sorted */
22
+ cold: string[];
23
+ /** invoked but not defined (typo / removed / builtin), sorted */
24
+ orphan: string[];
25
+ }
26
+ export interface CommandUsageReport {
27
+ commands: UsageBuckets;
28
+ agents: UsageBuckets;
29
+ filesScanned: number;
30
+ definedCommands: number;
31
+ definedAgents: number;
32
+ scope: 'project' | 'all';
33
+ }
34
+ /**
35
+ * Count custom slash-command invocations from raw transcript lines. Scoped to
36
+ * user-role lines (the `<command-name>` marker is injected on the user turn) so
37
+ * an assistant merely quoting the marker never inflates a count. The captured
38
+ * name is de-slashed (`/goal` → `goal`) to match the discovered-file key space.
39
+ * Pure.
40
+ */
41
+ export declare function parseCommandInvocations(lines: string[]): Map<string, number>;
42
+ /**
43
+ * Count subagent (Task tool) invocations by `subagent_type` from raw transcript
44
+ * lines. Not user-scoped — the field lives in an assistant `tool_use` block. Pure.
45
+ */
46
+ export declare function parseSubagentInvocations(lines: string[]): Map<string, number>;
47
+ /** Merge src counts into dest (dest mutated). */
48
+ export declare function mergeCounts(dest: Map<string, number>, src: Map<string, number>): void;
49
+ /**
50
+ * Join a DEFINED name list against INVOCATION counts into hot/cold/orphan. Both
51
+ * sides are compared de-slashed, so a defined `/build` matches a `build` count
52
+ * (agents carry no slash, so they're unaffected). Pure & deterministic. */
53
+ export declare function joinUsage(defined: string[], counts: Map<string, number> | Record<string, number>): UsageBuckets;
54
+ /**
55
+ * Discover the authored `.claude/` surface for a project. Command names use
56
+ * Claude Code's subdir namespacing (`.claude/commands/sDo/x.md` → `sDo:x`, top
57
+ * level → `x`) to match the `<command-name>` marker; agent names are the file
58
+ * basename (subagent_type is never namespaced). Pure w.r.t. inputs (reads fs).
59
+ */
60
+ export declare function discoverAuthored(cwd: string): {
61
+ commands: string[];
62
+ agents: string[];
63
+ };
64
+ export interface CollectOptions {
65
+ cwd?: string;
66
+ /** scan every project's transcripts (default: only the current project) */
67
+ all?: boolean;
68
+ }
69
+ /** The encoded transcript-dir name Claude Code uses for a project path. */
70
+ export declare function encodeProjectDir(cwd: string): string;
71
+ /** Build the full report: discover authored files, mine invocation counts from
72
+ * the (project-scoped, or --all) transcripts, and join. fs-touching wrapper. */
73
+ export declare function collectCommandUsage(opts?: CollectOptions): CommandUsageReport;
74
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1,161 @@
1
+ /**
2
+ * command-usage/usage.ts — a hot/cold/orphan USAGE report for a project's
3
+ * authored `.claude/` surface. `config lint` validates these files; this answers
4
+ * the orthogonal "are they actually used?" by joining the DEFINED set
5
+ * (`.claude/commands/**​/*.md` + `.claude/agents/**​/*.md`) against INVOCATION
6
+ * counts mined from the local transcripts (`<command-name>` markers +
7
+ * `"subagent_type"` fields). Three buckets: hot (defined & invoked), cold
8
+ * (defined, never invoked → prune candidates), orphan (invoked, not defined →
9
+ * typo / removed / builtin). #101.
10
+ *
11
+ * The parsers + join are pure and unit-tested; only collectCommandUsage touches
12
+ * the filesystem. Reuses defaultClaudeProjectDirs/findTranscriptFiles.
13
+ */
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import { defaultClaudeProjectDirs, findTranscriptFiles } from '../usage/transcript-usage.js';
17
+ const uniqSort = (xs) => [...new Set(xs)].sort((a, b) => a.localeCompare(b));
18
+ /**
19
+ * Count custom slash-command invocations from raw transcript lines. Scoped to
20
+ * user-role lines (the `<command-name>` marker is injected on the user turn) so
21
+ * an assistant merely quoting the marker never inflates a count. The captured
22
+ * name is de-slashed (`/goal` → `goal`) to match the discovered-file key space.
23
+ * Pure.
24
+ */
25
+ export function parseCommandInvocations(lines) {
26
+ const counts = new Map();
27
+ for (const line of lines) {
28
+ if (!/"(?:type|role)"\s*:\s*"user"/.test(line))
29
+ continue;
30
+ for (const m of line.matchAll(/<command-name>([^<]+)<\/command-name>/g)) {
31
+ const name = m[1].trim().replace(/^\/+/, '');
32
+ if (!name || name === '…')
33
+ continue;
34
+ counts.set(name, (counts.get(name) ?? 0) + 1);
35
+ }
36
+ }
37
+ return counts;
38
+ }
39
+ /**
40
+ * Count subagent (Task tool) invocations by `subagent_type` from raw transcript
41
+ * lines. Not user-scoped — the field lives in an assistant `tool_use` block. Pure.
42
+ */
43
+ export function parseSubagentInvocations(lines) {
44
+ const counts = new Map();
45
+ for (const line of lines) {
46
+ for (const m of line.matchAll(/"subagent_type"\s*:\s*"([^"]+)"/g)) {
47
+ const name = m[1].trim();
48
+ if (name)
49
+ counts.set(name, (counts.get(name) ?? 0) + 1);
50
+ }
51
+ }
52
+ return counts;
53
+ }
54
+ /** Merge src counts into dest (dest mutated). */
55
+ export function mergeCounts(dest, src) {
56
+ for (const [k, v] of src)
57
+ dest.set(k, (dest.get(k) ?? 0) + v);
58
+ }
59
+ /**
60
+ * Join a DEFINED name list against INVOCATION counts into hot/cold/orphan. Both
61
+ * sides are compared de-slashed, so a defined `/build` matches a `build` count
62
+ * (agents carry no slash, so they're unaffected). Pure & deterministic. */
63
+ export function joinUsage(defined, counts) {
64
+ const asMap = counts instanceof Map ? counts : new Map(Object.entries(counts));
65
+ const norm = (n) => n.replace(/^\/+/, '');
66
+ const get = (n) => asMap.get(norm(n)) ?? 0;
67
+ const definedNorm = new Set(defined.map(norm));
68
+ const hot = [];
69
+ const cold = [];
70
+ for (const d of defined) {
71
+ const c = get(d);
72
+ if (c > 0)
73
+ hot.push({ name: d, count: c });
74
+ else
75
+ cold.push(d);
76
+ }
77
+ const orphan = [];
78
+ for (const [k, v] of asMap) {
79
+ if (v > 0 && !definedNorm.has(norm(k)))
80
+ orphan.push(k);
81
+ }
82
+ hot.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
83
+ return { hot, cold: uniqSort(cold), orphan: uniqSort(orphan) };
84
+ }
85
+ /** All `.md` files under `root`, as paths RELATIVE to root (recursive; skips
86
+ * dotfiles). Mirrors config.ts's walkMd so `config lint` and this command agree
87
+ * on what counts as "defined". */
88
+ function walkMdRel(root) {
89
+ const out = [];
90
+ const rec = (dir, prefix) => {
91
+ let entries;
92
+ try {
93
+ entries = fs.readdirSync(dir, { withFileTypes: true });
94
+ }
95
+ catch {
96
+ return;
97
+ }
98
+ for (const e of entries) {
99
+ if (e.name.startsWith('.'))
100
+ continue;
101
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
102
+ if (e.isDirectory())
103
+ rec(path.join(dir, e.name), rel);
104
+ else if (e.isFile() && e.name.endsWith('.md'))
105
+ out.push(rel);
106
+ }
107
+ };
108
+ rec(root, '');
109
+ return out;
110
+ }
111
+ /**
112
+ * Discover the authored `.claude/` surface for a project. Command names use
113
+ * Claude Code's subdir namespacing (`.claude/commands/sDo/x.md` → `sDo:x`, top
114
+ * level → `x`) to match the `<command-name>` marker; agent names are the file
115
+ * basename (subagent_type is never namespaced). Pure w.r.t. inputs (reads fs).
116
+ */
117
+ export function discoverAuthored(cwd) {
118
+ const commands = walkMdRel(path.join(cwd, '.claude', 'commands')).map((rel) => rel.replace(/\.md$/, '').split('/').join(':'));
119
+ const agents = walkMdRel(path.join(cwd, '.claude', 'agents')).map((rel) => path.basename(rel).replace(/\.md$/, ''));
120
+ return { commands: uniqSort(commands), agents: uniqSort(agents) };
121
+ }
122
+ /** The encoded transcript-dir name Claude Code uses for a project path. */
123
+ export function encodeProjectDir(cwd) {
124
+ return cwd.replace(/\//g, '-');
125
+ }
126
+ /** Build the full report: discover authored files, mine invocation counts from
127
+ * the (project-scoped, or --all) transcripts, and join. fs-touching wrapper. */
128
+ export function collectCommandUsage(opts = {}) {
129
+ const cwd = opts.cwd ?? process.cwd();
130
+ const { commands: definedCommands, agents: definedAgents } = discoverAuthored(cwd);
131
+ const encoded = encodeProjectDir(cwd);
132
+ const cmdCounts = new Map();
133
+ const agentCounts = new Map();
134
+ let filesScanned = 0;
135
+ for (const dir of defaultClaudeProjectDirs()) {
136
+ for (const t of findTranscriptFiles(dir)) {
137
+ if (!opts.all && t.project !== encoded)
138
+ continue;
139
+ filesScanned++;
140
+ let content;
141
+ try {
142
+ content = fs.readFileSync(t.file, 'utf8');
143
+ }
144
+ catch {
145
+ continue;
146
+ }
147
+ const fileLines = content.split('\n');
148
+ mergeCounts(cmdCounts, parseCommandInvocations(fileLines));
149
+ mergeCounts(agentCounts, parseSubagentInvocations(fileLines));
150
+ }
151
+ }
152
+ return {
153
+ commands: joinUsage(definedCommands, cmdCounts),
154
+ agents: joinUsage(definedAgents, agentCounts),
155
+ filesScanned,
156
+ definedCommands: definedCommands.length,
157
+ definedAgents: definedAgents.length,
158
+ scope: opts.all ? 'all' : 'project',
159
+ };
160
+ }
161
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `swarmdo commands` (alias `slash`) — a hot/cold/orphan USAGE report for the
3
+ * project's authored `.claude/` slash-commands + subagents. `config lint` says
4
+ * "are these valid?"; this says "are these used?" by joining defined files
5
+ * against invocation counts mined from the local transcripts. #101.
6
+ *
7
+ * swarmdo commands # this project's authored surface
8
+ * swarmdo commands --all # count invocations across every project
9
+ * swarmdo commands --unused # only the cold (never-invoked) items
10
+ * swarmdo commands --unused --strict # exit 1 if anything is unused (CI gate)
11
+ * swarmdo commands --json # machine-readable
12
+ *
13
+ * Engine (../command-usage/usage.ts) is pure + fixture-tested; this thin layer
14
+ * renders and applies the --unused/--strict policy.
15
+ */
16
+ import type { Command } from '../types.js';
17
+ export declare const commandsCommand: Command;
18
+ export default commandsCommand;
19
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `swarmdo commands` (alias `slash`) — a hot/cold/orphan USAGE report for the
3
+ * project's authored `.claude/` slash-commands + subagents. `config lint` says
4
+ * "are these valid?"; this says "are these used?" by joining defined files
5
+ * against invocation counts mined from the local transcripts. #101.
6
+ *
7
+ * swarmdo commands # this project's authored surface
8
+ * swarmdo commands --all # count invocations across every project
9
+ * swarmdo commands --unused # only the cold (never-invoked) items
10
+ * swarmdo commands --unused --strict # exit 1 if anything is unused (CI gate)
11
+ * swarmdo commands --json # machine-readable
12
+ *
13
+ * Engine (../command-usage/usage.ts) is pure + fixture-tested; this thin layer
14
+ * renders and applies the --unused/--strict policy.
15
+ */
16
+ import { output } from '../output.js';
17
+ import { collectCommandUsage } from '../command-usage/usage.js';
18
+ /** Render one domain's hot/cold/orphan buckets. `slash` prefixes command names. */
19
+ function renderBuckets(label, defined, b, slash) {
20
+ const nm = (n) => (slash ? `/${n}` : n);
21
+ output.writeln('');
22
+ output.writeln(output.bold(`${label}`) + output.dim(` (${defined} defined · ${b.hot.length} hot · ${b.cold.length} cold · ${b.orphan.length} orphan)`));
23
+ if (b.hot.length > 0) {
24
+ output.printTable({
25
+ columns: [
26
+ { key: 'name', header: 'Hot (used)', width: 34 },
27
+ { key: 'count', header: 'Invocations', width: 12, align: 'right' },
28
+ ],
29
+ data: b.hot.map((h) => ({ name: nm(h.name), count: h.count.toLocaleString('en-US') })),
30
+ });
31
+ }
32
+ if (b.cold.length > 0) {
33
+ output.writeln(output.warning(` cold (never invoked — prune candidates): `) + b.cold.map(nm).join(' '));
34
+ }
35
+ if (b.orphan.length > 0) {
36
+ output.writeln(output.dim(` orphan (invoked, not defined — builtin or typo): `) + b.orphan.map(nm).join(' '));
37
+ }
38
+ if (b.hot.length === 0 && b.cold.length === 0 && b.orphan.length === 0) {
39
+ output.writeln(output.dim(' (none)'));
40
+ }
41
+ }
42
+ /** Print only the cold (unused) items — the prune list. Returns the cold count. */
43
+ function renderUnused(report) {
44
+ const coldCmds = report.commands.cold;
45
+ const coldAgents = report.agents.cold;
46
+ const total = coldCmds.length + coldAgents.length;
47
+ output.writeln(output.bold('Unused (cold) authored items') + output.dim(` (${report.scope} scope · ${report.filesScanned} transcript files)`));
48
+ if (total === 0) {
49
+ output.writeln(output.info(' none — every authored command and agent has been invoked 🎉'));
50
+ return 0;
51
+ }
52
+ if (coldCmds.length > 0)
53
+ output.writeln(` commands: ${coldCmds.map((c) => `/${c}`).join(' ')}`);
54
+ if (coldAgents.length > 0)
55
+ output.writeln(` agents: ${coldAgents.join(' ')}`);
56
+ return total;
57
+ }
58
+ async function run(ctx) {
59
+ const cwd = ctx.cwd || process.cwd();
60
+ const all = ctx.flags.all === true;
61
+ const report = collectCommandUsage({ cwd, all });
62
+ if (ctx.flags.json === true) {
63
+ output.writeln(JSON.stringify(report, null, 2));
64
+ return { success: true, exitCode: 0 };
65
+ }
66
+ if (report.definedCommands === 0 && report.definedAgents === 0) {
67
+ output.writeln(output.info('nothing authored — no .claude/commands or .claude/agents in this project'));
68
+ return { success: true, exitCode: 0 };
69
+ }
70
+ if (ctx.flags.unused === true) {
71
+ const coldCount = renderUnused(report);
72
+ const strict = ctx.flags.strict === true;
73
+ const exitCode = strict && coldCount > 0 ? 1 : 0;
74
+ if (exitCode === 1)
75
+ output.writeln(output.dim(`exit 1 (--strict + ${coldCount} unused)`));
76
+ return { success: exitCode === 0, exitCode };
77
+ }
78
+ output.writeln(output.bold('Authored .claude/ surface — usage') + output.dim(` (${report.scope} scope · ${report.filesScanned} transcript files)`));
79
+ renderBuckets('Commands', report.definedCommands, report.commands, true);
80
+ renderBuckets('Agents', report.definedAgents, report.agents, false);
81
+ const totalCold = report.commands.cold.length + report.agents.cold.length;
82
+ if (totalCold > 0) {
83
+ output.writeln('');
84
+ output.writeln(output.dim(`${totalCold} unused — see \`swarmdo commands --unused\` (add --strict for a CI gate)`));
85
+ }
86
+ return { success: true, exitCode: 0 };
87
+ }
88
+ export const commandsCommand = {
89
+ name: 'commands',
90
+ aliases: ['slash'],
91
+ description: "Usage report (hot/cold/orphan) for the project's authored .claude/ slash-commands & subagents",
92
+ options: [
93
+ { name: 'all', description: 'count invocations across every project (default: current project only)', type: 'boolean', default: false },
94
+ { name: 'unused', description: 'print only the cold (never-invoked) items — the prune list', type: 'boolean', default: false },
95
+ { name: 'strict', description: 'with --unused, exit 1 if anything is unused (CI gate)', type: 'boolean', default: false },
96
+ { name: 'json', description: 'machine-readable output', type: 'boolean', default: false },
97
+ ],
98
+ examples: [
99
+ { command: 'swarmdo commands', description: 'Hot/cold/orphan report for your authored .claude/ surface' },
100
+ { command: 'swarmdo commands --unused', description: 'List slash-commands & agents you defined but never invoke' },
101
+ { command: 'swarmdo commands --unused --strict', description: 'CI gate: fail if any authored command/agent is unused' },
102
+ { command: 'swarmdo slash --all --json', description: 'Cross-project invocation counts as JSON (alias)' },
103
+ ],
104
+ action: run,
105
+ };
106
+ export default commandsCommand;
107
+ //# sourceMappingURL=commands.js.map
@@ -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,
@@ -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() {