swarmdo 1.4.8 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/.claude/helpers/statusline.cjs +7 -7
  2. package/README.md +9 -6
  3. package/package.json +5 -3
  4. package/v3/@swarmdo/cli/dist/src/commands/compact.d.ts +16 -0
  5. package/v3/@swarmdo/cli/dist/src/commands/compact.js +106 -0
  6. package/v3/@swarmdo/cli/dist/src/commands/hooks.js +26 -24
  7. package/v3/@swarmdo/cli/dist/src/commands/index.js +10 -3
  8. package/v3/@swarmdo/cli/dist/src/commands/integrations.d.ts +14 -0
  9. package/v3/@swarmdo/cli/dist/src/commands/integrations.js +158 -0
  10. package/v3/@swarmdo/cli/dist/src/commands/memory.js +50 -1
  11. package/v3/@swarmdo/cli/dist/src/commands/providers.js +3 -0
  12. package/v3/@swarmdo/cli/dist/src/commands/release.d.ts +15 -0
  13. package/v3/@swarmdo/cli/dist/src/commands/release.js +241 -0
  14. package/v3/@swarmdo/cli/dist/src/commands/security.js +14 -0
  15. package/v3/@swarmdo/cli/dist/src/commands/transcript.js +82 -1
  16. package/v3/@swarmdo/cli/dist/src/commands/usage.js +80 -1
  17. package/v3/@swarmdo/cli/dist/src/compact/compact.d.ts +60 -0
  18. package/v3/@swarmdo/cli/dist/src/compact/compact.js +170 -0
  19. package/v3/@swarmdo/cli/dist/src/config-lint/lint.js +11 -1
  20. package/v3/@swarmdo/cli/dist/src/index.js +4 -2
  21. package/v3/@swarmdo/cli/dist/src/init/statusline-generator.js +6 -6
  22. package/v3/@swarmdo/cli/dist/src/integrations/integrations.d.ts +54 -0
  23. package/v3/@swarmdo/cli/dist/src/integrations/integrations.js +162 -0
  24. package/v3/@swarmdo/cli/dist/src/mcp-tools/agent-execute-core.js +25 -3
  25. package/v3/@swarmdo/cli/dist/src/mcp-tools/agentdb-tools.js +8 -8
  26. package/v3/@swarmdo/cli/dist/src/memory/graph-edge-writer.d.ts +12 -0
  27. package/v3/@swarmdo/cli/dist/src/memory/graph-edge-writer.js +32 -1
  28. package/v3/@swarmdo/cli/dist/src/memory-vault/watch.d.ts +32 -0
  29. package/v3/@swarmdo/cli/dist/src/memory-vault/watch.js +66 -0
  30. package/v3/@swarmdo/cli/dist/src/providers/openrouter-config.d.ts +91 -0
  31. package/v3/@swarmdo/cli/dist/src/providers/openrouter-config.js +186 -0
  32. package/v3/@swarmdo/cli/dist/src/release/release.d.ts +72 -0
  33. package/v3/@swarmdo/cli/dist/src/release/release.js +86 -0
  34. package/v3/@swarmdo/cli/dist/src/resilience/backoff.d.ts +23 -0
  35. package/v3/@swarmdo/cli/dist/src/resilience/backoff.js +30 -0
  36. package/v3/@swarmdo/cli/dist/src/swarmvector/model-router.js +14 -0
  37. package/v3/@swarmdo/cli/dist/src/transcript/search.d.ts +39 -0
  38. package/v3/@swarmdo/cli/dist/src/transcript/search.js +67 -0
  39. package/v3/@swarmdo/cli/dist/src/usage/diff.d.ts +69 -0
  40. package/v3/@swarmdo/cli/dist/src/usage/diff.js +112 -0
  41. package/v3/@swarmdo/cli/package.json +4 -4
  42. package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
  43. package/v3/@swarmdo/codex/dist/cli.js +0 -0
  44. package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
  45. package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
  46. package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
  47. package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +4 -1
  48. package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
  49. package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
  50. package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
  51. package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
  52. package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
  53. package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
  54. package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
  55. package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
  56. package/v3/@swarmdo/shared/dist/mcp/transport/http.js +8 -1
  57. package/v3/vendor/agentdb/package.json +0 -1
  58. package/v3/vendor/agentic-flow/package.json +0 -1
  59. package/.claude/scheduled_tasks.lock +0 -1
  60. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  61. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/__main__.cpython-314.pyc +0 -0
  62. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/cli.cpython-314.pyc +0 -0
  63. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/compress.cpython-314.pyc +0 -0
  64. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/detect.cpython-314.pyc +0 -0
  65. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/validate.cpython-314.pyc +0 -0
  66. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +0 -2
  67. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +0 -2
  68. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +0 -138
  69. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +0 -418
  70. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +0 -15
  71. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +0 -240
  72. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +0 -7
  73. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +0 -508
  74. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +0 -54
  75. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +0 -2
  76. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +0 -46
  77. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +0 -2
  78. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +0 -18
  79. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +0 -28
  80. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +0 -5
  81. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +0 -2
  82. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +0 -35
  83. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +0 -2
  84. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +0 -11
  85. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +0 -2
  86. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +0 -9
  87. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +0 -2
  88. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +0 -5
  89. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +0 -2
  90. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +0 -12
  91. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +0 -2
  92. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +0 -19
  93. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +0 -2
  94. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +0 -44
  95. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +0 -117
  96. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +0 -70
  97. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +0 -194
  98. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +0 -69
  99. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +0 -139
  100. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +0 -34
  101. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +0 -101
  102. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +0 -10
  103. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +0 -10
  104. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +0 -64
  105. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +0 -70
  106. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +0 -44
  107. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +0 -79
  108. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +0 -37
  109. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +0 -39
  110. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +0 -67
  111. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +0 -58
  112. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +0 -45
  113. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +0 -63
  114. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +0 -26
  115. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +0 -17
  116. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +0 -51
  117. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +0 -110
  118. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +0 -13
  119. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +0 -25
  120. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +0 -11
  121. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +0 -19
  122. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +0 -11
  123. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +0 -27
  124. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +0 -6
  125. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +0 -6
  126. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +0 -41
  127. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +0 -64
  128. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +0 -8
  129. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +0 -703
  130. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +0 -23
  131. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +0 -239
  132. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +0 -17
  133. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +0 -42
  134. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +0 -18
  135. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +0 -45
  136. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +0 -19
  137. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +0 -50
  138. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +0 -19
  139. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +0 -47
  140. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +0 -17
  141. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +0 -40
  142. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +0 -18
  143. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +0 -51
  144. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +0 -214
  145. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +0 -595
  146. package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +0 -76
  147. package/v3/@swarmdo/testing/dist/fixtures/agents.js +0 -158
  148. package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +0 -157
  149. package/v3/@swarmdo/testing/dist/fixtures/configurations.js +0 -337
  150. package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +0 -10
  151. package/v3/@swarmdo/testing/dist/fixtures/index.js +0 -17
  152. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +0 -329
  153. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +0 -714
  154. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +0 -159
  155. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +0 -262
  156. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +0 -290
  157. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +0 -547
  158. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +0 -289
  159. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +0 -534
  160. package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +0 -106
  161. package/v3/@swarmdo/testing/dist/fixtures/tasks.js +0 -229
  162. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +0 -213
  163. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +0 -435
  164. package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +0 -135
  165. package/v3/@swarmdo/testing/dist/helpers/assertions.js +0 -217
  166. package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +0 -96
  167. package/v3/@swarmdo/testing/dist/helpers/create-mock.js +0 -164
  168. package/v3/@swarmdo/testing/dist/helpers/index.d.ts +0 -14
  169. package/v3/@swarmdo/testing/dist/helpers/index.js +0 -22
  170. package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +0 -323
  171. package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +0 -350
  172. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +0 -271
  173. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +0 -446
  174. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +0 -142
  175. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +0 -205
  176. package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +0 -190
  177. package/v3/@swarmdo/testing/dist/helpers/test-application.js +0 -138
  178. package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +0 -274
  179. package/v3/@swarmdo/testing/dist/helpers/test-utils.js +0 -456
  180. package/v3/@swarmdo/testing/dist/index.d.ts +0 -42
  181. package/v3/@swarmdo/testing/dist/index.js +0 -66
  182. package/v3/@swarmdo/testing/dist/mocks/index.d.ts +0 -8
  183. package/v3/@swarmdo/testing/dist/mocks/index.js +0 -10
  184. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +0 -206
  185. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +0 -575
  186. package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +0 -287
  187. package/v3/@swarmdo/testing/dist/mocks/mock-services.js +0 -563
  188. package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +0 -109
  189. package/v3/@swarmdo/testing/dist/regression/api-contract.js +0 -374
  190. package/v3/@swarmdo/testing/dist/regression/index.d.ts +0 -18
  191. package/v3/@swarmdo/testing/dist/regression/index.js +0 -18
  192. package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +0 -67
  193. package/v3/@swarmdo/testing/dist/regression/integration-regression.js +0 -334
  194. package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +0 -106
  195. package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +0 -268
  196. package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +0 -124
  197. package/v3/@swarmdo/testing/dist/regression/regression-runner.js +0 -227
  198. package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +0 -73
  199. package/v3/@swarmdo/testing/dist/regression/security-regression.js +0 -258
  200. package/v3/@swarmdo/testing/dist/setup.d.ts +0 -43
  201. package/v3/@swarmdo/testing/dist/setup.js +0 -91
  202. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +0 -10
  203. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +0 -433
  204. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +0 -10
  205. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +0 -352
  206. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +0 -206
  207. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +0 -839
  208. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +0 -10
  209. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +0 -428
  210. package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +0 -38
  211. package/v3/@swarmdo/testing/dist/v2-compat/index.js +0 -41
  212. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +0 -10
  213. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +0 -419
  214. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +0 -34
  215. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +0 -372
@@ -0,0 +1,15 @@
1
+ /**
2
+ * release.ts — `swarmdo release [patch|minor|major|x.y.z]`
3
+ *
4
+ * One command for the whole proven release train: bump the version trio +
5
+ * lockfile, sync doc version strings, commit+push, build + stage the
6
+ * self-contained @swarmdo/cli, publish both packages, verify the registry,
7
+ * tag, and cut a GitHub release with `swarmdo changelog` notes.
8
+ *
9
+ * DRY-RUN BY DEFAULT — prints the exact plan; `--confirm` executes it
10
+ * step-by-step and stops on the first failure.
11
+ */
12
+ import type { Command } from '../types.js';
13
+ export declare const releaseCommand: Command;
14
+ export default releaseCommand;
15
+ //# sourceMappingURL=release.d.ts.map
@@ -0,0 +1,241 @@
1
+ /**
2
+ * release.ts — `swarmdo release [patch|minor|major|x.y.z]`
3
+ *
4
+ * One command for the whole proven release train: bump the version trio +
5
+ * lockfile, sync doc version strings, commit+push, build + stage the
6
+ * self-contained @swarmdo/cli, publish both packages, verify the registry,
7
+ * tag, and cut a GitHub release with `swarmdo changelog` notes.
8
+ *
9
+ * DRY-RUN BY DEFAULT — prints the exact plan; `--confirm` executes it
10
+ * step-by-step and stops on the first failure.
11
+ */
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import * as os from 'node:os';
15
+ import { execFileSync } from 'node:child_process';
16
+ import { output } from '../output.js';
17
+ import { planRelease, renderStep } from '../release/release.js';
18
+ function repoRootFrom(cwd) {
19
+ try {
20
+ return execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ function bumpJson(file, version) {
27
+ const d = JSON.parse(fs.readFileSync(file, 'utf8'));
28
+ d.version = version;
29
+ fs.writeFileSync(file, JSON.stringify(d, null, 2) + '\n');
30
+ }
31
+ function bumpLock(file, version) {
32
+ const d = JSON.parse(fs.readFileSync(file, 'utf8'));
33
+ d.version = version;
34
+ if (d.packages && d.packages[''])
35
+ d.packages[''].version = version;
36
+ fs.writeFileSync(file, JSON.stringify(d, null, 2) + '\n');
37
+ }
38
+ /** Replace the previous version's known doc occurrences with the next. */
39
+ function syncDocs(root, files, current, next) {
40
+ const today = new Date().toISOString().slice(0, 10);
41
+ const touched = [];
42
+ for (const rel of files) {
43
+ const abs = path.join(root, rel);
44
+ if (!fs.existsSync(abs))
45
+ continue;
46
+ const before = fs.readFileSync(abs, 'utf8');
47
+ let after = before;
48
+ // exact patterns used by every release since 1.4.x
49
+ after = after.replaceAll(`**Swarmdo v${current}** (`, `**Swarmdo v${next}** (`);
50
+ after = after.replace(new RegExp(`\\*\\*Swarmdo v${next.replace(/\./g, '\\.')}\\*\\* \\(\\d{4}-\\d{2}-\\d{2}\\)`), `**Swarmdo v${next}** (${today})`);
51
+ after = after.replaceAll(`swarmdo@${current}\` (umbrella), \`@swarmdo/cli@${current}\`, \`swarmdo-bridge@${current}\``, `swarmdo@${next}\` (umbrella), \`@swarmdo/cli@${next}\`, \`swarmdo-bridge@${next}\``);
52
+ // README badge + website carry the last PUBLISHED version, which lags
53
+ // the repo version (`current`) between releases — match any semver in
54
+ // these swarmdo-specific markers instead of interpolating `current`.
55
+ after = after.replace(/npx%20swarmdo-v\d+\.\d+\.\d+-cb3837/g, `npx%20swarmdo-v${next}-cb3837`);
56
+ after = after.replace(/"softwareVersion":"\d+\.\d+\.\d+"/g, `"softwareVersion":"${next}"`);
57
+ after = after.replace(/<span class="ver">v\d+\.\d+\.\d+ · MIT<\/span>/g, `<span class="ver">v${next} · MIT</span>`);
58
+ after = after.replace(/▊ Swarmdo V\d+\.\d+\.\d+ /g, `▊ Swarmdo V${next} `);
59
+ if (after !== before) {
60
+ fs.writeFileSync(abs, after);
61
+ touched.push(rel);
62
+ }
63
+ }
64
+ return touched;
65
+ }
66
+ async function executePlan(plan, root) {
67
+ const run = (s) => {
68
+ output.writeln(output.dim(` $ ${s.cmd} ${s.args.join(' ')} (${path.relative(root, s.cwd) || '.'})`));
69
+ execFileSync(s.cmd, s.args, { cwd: s.cwd, stdio: 'inherit' });
70
+ };
71
+ for (const [i, step] of plan.steps.entries()) {
72
+ output.writeln(output.bold(`[${i + 1}/${plan.steps.length}] ${renderStep(step)}`));
73
+ switch (step.kind) {
74
+ case 'bump-json':
75
+ bumpJson(path.join(root, step.file), step.version);
76
+ break;
77
+ case 'bump-lock':
78
+ bumpLock(path.join(root, step.file), step.version);
79
+ break;
80
+ case 'sync-docs': {
81
+ const touched = syncDocs(root, step.files, plan.current, plan.next);
82
+ output.writeln(output.dim(` synced: ${touched.join(', ') || '(none)'}`));
83
+ break;
84
+ }
85
+ case 'exec':
86
+ run(step);
87
+ break;
88
+ case 'verify-npm': {
89
+ for (const pkg of step.packages) {
90
+ let seen = '';
91
+ for (let attempt = 0; attempt < 20; attempt++) {
92
+ try {
93
+ seen = execFileSync('npm', ['view', `${pkg}@latest`, 'version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
94
+ }
95
+ catch {
96
+ seen = '';
97
+ }
98
+ if (seen === step.version)
99
+ break;
100
+ execFileSync('sleep', ['3']);
101
+ }
102
+ if (seen !== step.version) {
103
+ output.printError(`${pkg}@latest is "${seen || '(unset)'}" — expected ${step.version}`);
104
+ return { success: false, exitCode: 1 };
105
+ }
106
+ output.writeln(output.dim(` ${pkg}@latest = ${seen} ✓`));
107
+ }
108
+ break;
109
+ }
110
+ case 'deploy-site': {
111
+ // user rule 2026-07-07: the live site ships with every release.
112
+ // Copy the working copy into the SwarmDo/swarmdo.com repo, push,
113
+ // and verify production actually serves the new version string.
114
+ const siteDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmdo-site-'));
115
+ execFileSync('gh', ['repo', 'clone', 'SwarmDo/swarmdo.com', siteDir, '--', '-q'], { stdio: 'inherit' });
116
+ fs.copyFileSync(path.join(root, 'website', 'index.html'), path.join(siteDir, 'index.html'));
117
+ const smap = path.join(siteDir, 'sitemap.xml');
118
+ if (fs.existsSync(smap)) {
119
+ const today = new Date().toISOString().slice(0, 10);
120
+ fs.writeFileSync(smap, fs.readFileSync(smap, 'utf8').replace(/<lastmod>[^<]*<\/lastmod>/g, `<lastmod>${today}</lastmod>`));
121
+ }
122
+ const g = (args) => { execFileSync('git', args, { cwd: siteDir, stdio: 'inherit' }); };
123
+ g(['add', '-A']);
124
+ try {
125
+ g(['commit', '-m', `release: sync site for v${plan.next}\n\nCo-Authored-By: Swarmdo <maintainers@swarmdo.com>`]);
126
+ }
127
+ catch {
128
+ output.writeln(output.dim(' site already in sync — nothing to commit'));
129
+ break;
130
+ }
131
+ g(['push', 'origin', 'main']);
132
+ // poll production (GitHub Pages deploys in ~1min)
133
+ let live = false;
134
+ for (let i = 0; i < 30; i++) {
135
+ try {
136
+ const body = execFileSync('curl', ['-s', 'https://swarmdo.com'], { encoding: 'utf8' });
137
+ if (body.includes(`v${plan.next}`)) {
138
+ live = true;
139
+ break;
140
+ }
141
+ }
142
+ catch { /* transient */ }
143
+ execFileSync('sleep', ['10']);
144
+ }
145
+ if (!live) {
146
+ output.printError(`swarmdo.com is not serving v${plan.next} after 5min — check the Pages build`);
147
+ return { success: false, exitCode: 1 };
148
+ }
149
+ output.writeln(output.dim(` swarmdo.com serves v${plan.next} ✓`));
150
+ break;
151
+ }
152
+ case 'gh-release': {
153
+ const notes = path.join(os.tmpdir(), `swarmdo-relnotes-${process.pid}.md`);
154
+ const { changelogCommand } = await import('./changelog.js');
155
+ // step.notesFrom is v<previous repo version>, but the repo
156
+ // patch-bumps every commit while tags only land per release —
157
+ // that ref usually isn't a tag. Fall back to the newest real tag
158
+ // that isn't the one being released.
159
+ let notesFrom = step.notesFrom;
160
+ try {
161
+ execFileSync('git', ['rev-parse', '--verify', `${notesFrom}^{commit}`], { cwd: root, stdio: ['ignore', 'pipe', 'pipe'] });
162
+ }
163
+ catch {
164
+ const { makeGitRunner } = await import('../changelog/changelog.js');
165
+ const tags = makeGitRunner(root)(['tag', '--sort=-creatordate']).split('\n').map(t => t.trim()).filter(t => t && t !== plan.tag);
166
+ notesFrom = tags[0];
167
+ output.writeln(output.dim(` ${step.notesFrom} is not a ref — notes range falls back to ${notesFrom ?? '(repo start)'}`));
168
+ }
169
+ const r = await changelogCommand.action({ args: [], flags: { ...(notesFrom ? { from: notesFrom } : {}), version: plan.tag, out: notes }, cwd: root, interactive: false });
170
+ if (r && r.success === false)
171
+ return { success: false, exitCode: 1 };
172
+ execFileSync('gh', ['release', 'create', plan.tag, '--title', `${plan.tag}`, '--notes-file', notes], { cwd: root, stdio: 'inherit' });
173
+ try {
174
+ fs.rmSync(notes, { force: true });
175
+ }
176
+ catch { /* best-effort */ }
177
+ break;
178
+ }
179
+ }
180
+ }
181
+ output.printSuccess(`released ${plan.tag}`);
182
+ return { success: true, exitCode: 0 };
183
+ }
184
+ export const releaseCommand = {
185
+ name: 'release',
186
+ aliases: ['ship'],
187
+ description: 'Run the full release train: bump trio+docs, commit, build+stage, publish both npm packages, verify, tag, GitHub release — dry-run unless --confirm',
188
+ options: [
189
+ { name: 'confirm', type: 'boolean', description: 'actually execute (default: print the plan)', default: false },
190
+ { name: 'skip-publish', type: 'boolean', description: 'skip npm publish + registry verify steps', default: false },
191
+ { name: 'skip-gh-release', type: 'boolean', description: 'skip the GitHub release step', default: false },
192
+ { name: 'skip-site', type: 'boolean', description: 'skip the live-site deploy (docs-sync rule: only for zero-user-facing releases)', default: false },
193
+ { name: 'json', type: 'boolean', description: 'print the plan as JSON', default: false },
194
+ ],
195
+ examples: [
196
+ { command: 'swarmdo release', description: 'Plan a patch release (dry-run)' },
197
+ { command: 'swarmdo release minor --confirm', description: 'Execute a minor release end-to-end' },
198
+ { command: 'swarmdo release 2.0.0 --skip-publish --confirm', description: 'Bump/tag/release 2.0.0 without publishing to npm' },
199
+ ],
200
+ action: async (ctx) => {
201
+ const cwd = ctx.cwd || process.cwd();
202
+ const root = repoRootFrom(cwd);
203
+ if (!root) {
204
+ output.printError('not a git repository');
205
+ return { success: false, exitCode: 1 };
206
+ }
207
+ const trioHead = path.join(root, 'v3/@swarmdo/cli/package.json');
208
+ if (!fs.existsSync(trioHead)) {
209
+ output.printError('this does not look like the swarmdo repo (v3/@swarmdo/cli/package.json missing)');
210
+ return { success: false, exitCode: 1 };
211
+ }
212
+ const current = String(JSON.parse(fs.readFileSync(trioHead, 'utf8')).version ?? '');
213
+ let plan;
214
+ try {
215
+ plan = planRelease({
216
+ current,
217
+ bump: (ctx.args[0] || 'patch').toLowerCase(),
218
+ repoRoot: root,
219
+ skipPublish: ctx.flags['skip-publish'] === true,
220
+ skipGhRelease: ctx.flags['skip-gh-release'] === true,
221
+ skipSite: ctx.flags['skip-site'] === true,
222
+ });
223
+ }
224
+ catch (e) {
225
+ output.printError(e.message);
226
+ return { success: false, exitCode: 1 };
227
+ }
228
+ if (ctx.flags.json === true) {
229
+ output.printJson(plan);
230
+ return { success: true, data: plan };
231
+ }
232
+ if (ctx.flags.confirm !== true) {
233
+ output.writeln(output.bold(`Release plan ${plan.current} → ${plan.next}`) + output.dim(' (dry-run — re-run with --confirm to execute)'));
234
+ plan.steps.forEach((s, i) => output.writeln(` ${String(i + 1).padStart(2)}. ${renderStep(s)}`));
235
+ return { success: true, data: { dryRun: true, ...plan } };
236
+ }
237
+ return executePlan(plan, root);
238
+ },
239
+ };
240
+ export default releaseCommand;
241
+ //# sourceMappingURL=release.js.map
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { output } from '../output.js';
8
8
  import { execSync } from 'node:child_process';
9
+ import { mkdirSync, writeFileSync } from 'node:fs';
9
10
  // Scan subcommand
10
11
  const scanCommand = {
11
12
  name: 'scan',
@@ -190,6 +191,19 @@ const scanCommand = {
190
191
  scanCodeDir(path.resolve(target), scanDepth);
191
192
  }
192
193
  spinner.succeed('Scan complete');
194
+ // Persist the summary so the statusline (and future tooling) reflects
195
+ // real scans instead of the retired V3 CVE tracker (v1.4.17).
196
+ try {
197
+ const artifactDir = path.join(process.cwd(), '.swarmdo', 'security');
198
+ mkdirSync(artifactDir, { recursive: true });
199
+ writeFileSync(path.join(artifactDir, 'last-scan.json'), JSON.stringify({
200
+ scannedAt: new Date().toISOString(),
201
+ target, depth, type: scanType,
202
+ critical: criticalCount, high: highCount, medium: mediumCount, low: lowCount,
203
+ total: findings.length,
204
+ }, null, 2) + '\n');
205
+ }
206
+ catch { /* statusline signal is best-effort */ }
193
207
  // Display results
194
208
  output.writeln();
195
209
  if (findings.length > 0) {
@@ -9,6 +9,7 @@
9
9
  * unit-tested; reuses the usage transcript infra).
10
10
  */
11
11
  import * as fs from 'node:fs';
12
+ import { parseQuery, searchLines, rankMatches } from '../transcript/search.js';
12
13
  import { output } from '../output.js';
13
14
  import { listSessions, exportSession } from '../transcript/export.js';
14
15
  function fmtAge(mtimeMs, nowMs) {
@@ -108,11 +109,91 @@ const exportCommand = {
108
109
  return { success: true, data: { turns: res.turns, sessionId: res.sessionId } };
109
110
  },
110
111
  };
112
+ // Full-text search across session transcripts ("which session did I discuss X in?")
113
+ const searchCommand = {
114
+ name: 'search',
115
+ description: 'Search what you and Claude actually said across all session transcripts',
116
+ options: [
117
+ { name: 'limit', short: 'n', type: 'number', description: 'sessions to show', default: 10 },
118
+ { name: 'json', type: 'boolean', description: 'machine-readable output', default: false },
119
+ ],
120
+ examples: [
121
+ { command: 'swarmdo transcript search obsidian vault', description: 'Sessions mentioning both terms (AND)' },
122
+ { command: 'swarmdo tx search "npm publish" -n 5', description: 'Top 5 sessions about npm publish' },
123
+ ],
124
+ action: async (ctx) => {
125
+ const fs = await import('node:fs');
126
+ const { defaultClaudeProjectDirs, findTranscriptFiles } = await import('../usage/transcript-usage.js');
127
+ const { sessionIdFromFile } = await import('../transcript/export.js');
128
+ let terms;
129
+ try {
130
+ terms = parseQuery(ctx.args.slice(1).join(' '));
131
+ }
132
+ catch {
133
+ output.printError('usage: swarmdo transcript search <terms…>');
134
+ return { success: false, exitCode: 1 };
135
+ }
136
+ const matches = [];
137
+ let scanned = 0;
138
+ for (const dir of defaultClaudeProjectDirs()) {
139
+ for (const { file, project } of findTranscriptFiles(dir)) {
140
+ scanned++;
141
+ let raw;
142
+ try {
143
+ raw = fs.readFileSync(file, 'utf8');
144
+ }
145
+ catch {
146
+ continue;
147
+ }
148
+ const lines = raw.split('\n').flatMap((l) => {
149
+ if (!l.trim())
150
+ return [];
151
+ try {
152
+ return [JSON.parse(l)];
153
+ }
154
+ catch {
155
+ return [];
156
+ }
157
+ });
158
+ const { hits, snippets } = searchLines(lines, terms);
159
+ if (hits > 0) {
160
+ let mtimeMs = 0;
161
+ try {
162
+ mtimeMs = fs.statSync(file).mtimeMs;
163
+ }
164
+ catch { /* keep 0 */ }
165
+ matches.push({ sessionId: sessionIdFromFile(file), project, mtimeMs, hits, snippets });
166
+ }
167
+ }
168
+ }
169
+ const ranked = rankMatches(matches).slice(0, Number(ctx.flags.limit) || 10);
170
+ if (ctx.flags.json === true) {
171
+ output.printJson({ query: terms, scanned, matched: matches.length, sessions: ranked });
172
+ return { success: true, data: ranked };
173
+ }
174
+ if (ranked.length === 0) {
175
+ output.writeln(output.dim(`no sessions matched "${terms.join(' ')}" (${scanned} transcripts scanned)`));
176
+ return { success: true, exitCode: 0 };
177
+ }
178
+ output.writeln(output.bold(`${matches.length} session(s) match "${terms.join(' ')}"`) + output.dim(` — ${scanned} transcripts scanned`));
179
+ for (const m of ranked) {
180
+ const when = m.mtimeMs ? new Date(m.mtimeMs).toISOString().slice(0, 16).replace('T', ' ') : '?';
181
+ output.writeln('');
182
+ output.writeln(`${output.info(m.sessionId.slice(0, 8))} ${output.dim(when)} ${m.hits} hit${m.hits === 1 ? '' : 's'} ${output.dim(m.project)}`);
183
+ for (const sn of m.snippets) {
184
+ output.writeln(` ${sn.role === 'user' ? '👤' : '🤖'} ${sn.snippet}`);
185
+ }
186
+ }
187
+ output.writeln('');
188
+ output.writeln(output.dim('open one: swarmdo transcript export -s <id-prefix>'));
189
+ return { success: true, data: ranked };
190
+ },
191
+ };
111
192
  export const transcriptCommand = {
112
193
  name: 'transcript',
113
194
  aliases: ['tx'],
114
195
  description: 'Export Claude Code sessions to readable Markdown (list/export)',
115
- subcommands: [listCommand, exportCommand],
196
+ subcommands: [listCommand, exportCommand, searchCommand],
116
197
  options: [],
117
198
  examples: [
118
199
  { command: 'swarmdo transcript list', description: 'Recent sessions, newest first' },
@@ -17,6 +17,7 @@ import { aggregateBlocks, aggregateUsage, collectUsage, totalUsage, localDateKey
17
17
  import { collectToolErrors } from '../usage/transcript-errors.js';
18
18
  import { computeCacheStats } from '../usage/cache-stats.js';
19
19
  import { evaluateGuard } from '../usage/spend-guard.js';
20
+ import { resolvePeriodPair, parseRange, diffPeriods, modelMovers } from '../usage/diff.js';
20
21
  const VIEWS = {
21
22
  daily: { dimension: 'day', label: 'Date' },
22
23
  monthly: { dimension: 'month', label: 'Month' },
@@ -232,6 +233,79 @@ function runCacheView(ctx, collection) {
232
233
  output.writeln(output.dim('"From cache" = share of input tokens served as cache reads (0.1x cost); higher is cheaper.'));
233
234
  return { success: true, exitCode: 0 };
234
235
  }
236
+ /** Diff view — compare two periods (day|week|month presets or explicit
237
+ * --a/--b YYYY-MM-DD:YYYY-MM-DD ranges): totals, deltas, per-model movers. */
238
+ function runDiffView(ctx, collection) {
239
+ const kind = (ctx.args[1] || 'week').toLowerCase();
240
+ let a;
241
+ let b;
242
+ try {
243
+ if (typeof ctx.flags.a === 'string' || typeof ctx.flags.b === 'string') {
244
+ if (typeof ctx.flags.a !== 'string' || typeof ctx.flags.b !== 'string') {
245
+ output.printError('explicit ranges need BOTH --a and --b (YYYY-MM-DD:YYYY-MM-DD)');
246
+ return { success: false, exitCode: 1 };
247
+ }
248
+ a = parseRange(ctx.flags.a, 'A');
249
+ b = parseRange(ctx.flags.b, 'B');
250
+ }
251
+ else {
252
+ ({ a, b } = resolvePeriodPair(kind, localDateKey(new Date())));
253
+ }
254
+ }
255
+ catch (e) {
256
+ output.printError(e.message);
257
+ return { success: false, exitCode: 1 };
258
+ }
259
+ const dayRows = aggregateUsage(collection.events, 'day').map((r) => ({ key: r.key, totals: r.totals }));
260
+ const report = diffPeriods(dayRows, a, b);
261
+ // per-(model, day) fold for the movers table
262
+ const md = new Map();
263
+ for (const e of collection.events) {
264
+ const k = `${e.model}\u0000${e.dateKey}`;
265
+ const row = md.get(k) ?? { key: e.model, day: e.dateKey, totals: { costUsd: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 0 } };
266
+ row.totals.costUsd += e.costUsd;
267
+ row.totals.totalTokens += e.inputTokens + e.outputTokens + e.cacheWriteTokens + e.cacheReadTokens;
268
+ md.set(k, row);
269
+ }
270
+ const movers = modelMovers([...md.values()], a, b);
271
+ if (ctx.flags.json === true) {
272
+ output.printJson({ ...report, movers });
273
+ return { success: true, data: { ...report, movers } };
274
+ }
275
+ const pct = (m) => (m.pct === null ? 'new' : `${m.pct >= 0 ? '+' : ''}${Math.round(m.pct * 100)}%`);
276
+ const sign = (n, f) => `${n >= 0 ? '+' : '−'}${f(Math.abs(n))}`;
277
+ output.writeln(output.bold(`Usage diff ${a.label} vs ${b.label}`) + output.dim(` (${a.from}..${a.to} vs ${b.from}..${b.to})`));
278
+ output.printTable({
279
+ columns: [
280
+ { key: 'metric', header: 'Metric', width: 14 },
281
+ { key: 'a', header: a.label.slice(0, 14), width: 14, align: 'right' },
282
+ { key: 'b', header: b.label.slice(0, 14), width: 14, align: 'right' },
283
+ { key: 'delta', header: 'Δ', width: 14, align: 'right' },
284
+ { key: 'pct', header: '%', width: 7, align: 'right' },
285
+ ],
286
+ data: [
287
+ { metric: 'Cost', a: fmtCost(report.cost.a), b: fmtCost(report.cost.b), delta: sign(report.cost.delta, fmtCost), pct: pct(report.cost) },
288
+ { metric: 'Tokens', a: fmtInt(report.totalTokens.a), b: fmtInt(report.totalTokens.b), delta: sign(report.totalTokens.delta, fmtInt), pct: pct(report.totalTokens) },
289
+ { metric: 'Output tok', a: fmtInt(report.outputTokens.a), b: fmtInt(report.outputTokens.b), delta: sign(report.outputTokens.delta, fmtInt), pct: pct(report.outputTokens) },
290
+ { metric: 'Cache reads', a: fmtInt(report.cacheReadTokens.a), b: fmtInt(report.cacheReadTokens.b), delta: sign(report.cacheReadTokens.delta, fmtInt), pct: pct(report.cacheReadTokens) },
291
+ { metric: 'Active days', a: String(report.activeDays.a), b: String(report.activeDays.b), delta: sign(report.activeDays.delta, String), pct: pct(report.activeDays) },
292
+ ],
293
+ });
294
+ if (movers.length > 0) {
295
+ output.writeln(output.bold('Model movers') + output.dim(' (by |Δ cost|)'));
296
+ output.printTable({
297
+ columns: [
298
+ { key: 'model', header: 'Model', width: 34 },
299
+ { key: 'a', header: 'A', width: 12, align: 'right' },
300
+ { key: 'b', header: 'B', width: 12, align: 'right' },
301
+ { key: 'delta', header: 'Δ', width: 12, align: 'right' },
302
+ { key: 'pct', header: '%', width: 7, align: 'right' },
303
+ ],
304
+ data: movers.map((m) => ({ model: m.model, a: fmtCost(m.cost.a), b: fmtCost(m.cost.b), delta: sign(m.cost.delta, fmtCost), pct: pct(m.cost) })),
305
+ });
306
+ }
307
+ return { success: true, data: { ...report, movers } };
308
+ }
235
309
  /** Build a threshold from a --flag or its SWARMDO_GUARD_* env fallback. */
236
310
  function guardThreshold(ctx, flagKey, envKey, current, label, unit) {
237
311
  const raw = ctx.flags[flagKey] ?? process.env[envKey];
@@ -330,9 +404,14 @@ async function run(ctx) {
330
404
  const dirs = typeof dirFlag === 'string' ? [dirFlag] : Array.isArray(dirFlag) ? dirFlag.map(String) : undefined;
331
405
  return runGuardView(ctx, collectUsage({ dirs }));
332
406
  }
407
+ if (viewName === 'diff') {
408
+ const dirFlag = ctx.flags.dir;
409
+ const dirs = typeof dirFlag === 'string' ? [dirFlag] : Array.isArray(dirFlag) ? dirFlag.map(String) : undefined;
410
+ return runDiffView(ctx, collectUsage({ dirs }));
411
+ }
333
412
  const view = VIEWS[viewName];
334
413
  if (!view) {
335
- output.writeln(output.error(`unknown view: ${viewName} (expected ${Object.keys(VIEWS).join('|')}|blocks|errors|cache|guard)`));
414
+ output.writeln(output.error(`unknown view: ${viewName} (expected ${Object.keys(VIEWS).join('|')}|blocks|errors|cache|guard|diff)`));
336
415
  return { success: false, exitCode: 1 };
337
416
  }
338
417
  const since = typeof ctx.flags.since === 'string' ? ctx.flags.since : undefined;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * compact.ts — deterministic command-output compression.
3
+ *
4
+ * Noisy command output (test runners, builds, installers, `git log`) burns a
5
+ * large share of an agent's context window on repetition and formatting that
6
+ * carries no signal. This engine shrinks that output BEFORE it reaches an LLM,
7
+ * with no model call — every transform is a pure, signal-preserving rewrite:
8
+ *
9
+ * 1. strip ANSI escape sequences (colour, cursor moves)
10
+ * 2. resolve carriage-return progress spam to each line's final state
11
+ * 3. collapse runs of identical consecutive lines → `<line> … (×N)`
12
+ * 4. fold consecutive node_modules stack frames → `… N frames in node_modules`
13
+ * 5. collapse ≥3 blank lines to one; trim trailing whitespace
14
+ * 6. (opt-in) window very long output to head + tail with an elision marker
15
+ *
16
+ * Distinct from `swarmdo compress` (caveman, LLM-based, for memory FILES) —
17
+ * this is a zero-token STREAM filter. Pure: no I/O, fully testable.
18
+ */
19
+ export interface CompactOptions {
20
+ /** Strip ANSI escape sequences. Default true. */
21
+ stripAnsi?: boolean;
22
+ /** Collapse a run of ≥ this many identical lines. Default 3; 0 disables. */
23
+ minRun?: number;
24
+ /** Fold consecutive node_modules stack frames. Default true. */
25
+ foldNodeModules?: boolean;
26
+ /** Collapse ≥3 consecutive blank lines to one. Default true. */
27
+ collapseBlanks?: boolean;
28
+ /**
29
+ * Opt-in windowing for very long output: keep the first `head` and last
30
+ * `tail` lines, eliding the middle. Omit to keep everything.
31
+ */
32
+ window?: {
33
+ head: number;
34
+ tail: number;
35
+ };
36
+ }
37
+ export interface CompactStats {
38
+ linesIn: number;
39
+ linesOut: number;
40
+ bytesIn: number;
41
+ bytesOut: number;
42
+ /** Fraction of bytes removed, 0..1 (0 when input was empty). */
43
+ savedFraction: number;
44
+ }
45
+ export interface CompactResult {
46
+ text: string;
47
+ stats: CompactStats;
48
+ }
49
+ /** Remove ANSI escapes from a string. Exported for reuse/testing. */
50
+ export declare function stripAnsi(s: string): string;
51
+ /**
52
+ * Compact command output. Pure — returns the rewritten text plus byte/line
53
+ * savings. The input's trailing newline (if any) is preserved.
54
+ */
55
+ export declare function compactOutput(input: string, opts?: CompactOptions): CompactResult;
56
+ /** Human-readable byte count (for the savings summary). */
57
+ export declare function formatBytes(n: number): string;
58
+ /** One-line savings summary for stderr. */
59
+ export declare function formatSavings(stats: CompactStats): string;
60
+ //# sourceMappingURL=compact.d.ts.map