swarmdo 1.4.8 → 1.5.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 (210) hide show
  1. package/.claude/helpers/statusline.cjs +7 -7
  2. package/README.md +3 -1
  3. package/package.json +5 -3
  4. package/v3/@swarmdo/cli/dist/src/commands/hooks.js +26 -24
  5. package/v3/@swarmdo/cli/dist/src/commands/index.js +4 -0
  6. package/v3/@swarmdo/cli/dist/src/commands/integrations.d.ts +14 -0
  7. package/v3/@swarmdo/cli/dist/src/commands/integrations.js +158 -0
  8. package/v3/@swarmdo/cli/dist/src/commands/memory.js +50 -1
  9. package/v3/@swarmdo/cli/dist/src/commands/providers.js +3 -0
  10. package/v3/@swarmdo/cli/dist/src/commands/release.d.ts +15 -0
  11. package/v3/@swarmdo/cli/dist/src/commands/release.js +224 -0
  12. package/v3/@swarmdo/cli/dist/src/commands/security.js +14 -0
  13. package/v3/@swarmdo/cli/dist/src/commands/transcript.js +82 -1
  14. package/v3/@swarmdo/cli/dist/src/commands/usage.js +80 -1
  15. package/v3/@swarmdo/cli/dist/src/config-lint/lint.js +11 -1
  16. package/v3/@swarmdo/cli/dist/src/init/statusline-generator.js +6 -6
  17. package/v3/@swarmdo/cli/dist/src/integrations/integrations.d.ts +54 -0
  18. package/v3/@swarmdo/cli/dist/src/integrations/integrations.js +162 -0
  19. package/v3/@swarmdo/cli/dist/src/mcp-tools/agent-execute-core.js +25 -3
  20. package/v3/@swarmdo/cli/dist/src/mcp-tools/agentdb-tools.js +8 -8
  21. package/v3/@swarmdo/cli/dist/src/memory/graph-edge-writer.d.ts +12 -0
  22. package/v3/@swarmdo/cli/dist/src/memory/graph-edge-writer.js +32 -1
  23. package/v3/@swarmdo/cli/dist/src/memory-vault/watch.d.ts +32 -0
  24. package/v3/@swarmdo/cli/dist/src/memory-vault/watch.js +66 -0
  25. package/v3/@swarmdo/cli/dist/src/providers/openrouter-config.d.ts +91 -0
  26. package/v3/@swarmdo/cli/dist/src/providers/openrouter-config.js +186 -0
  27. package/v3/@swarmdo/cli/dist/src/release/release.d.ts +72 -0
  28. package/v3/@swarmdo/cli/dist/src/release/release.js +86 -0
  29. package/v3/@swarmdo/cli/dist/src/resilience/backoff.d.ts +23 -0
  30. package/v3/@swarmdo/cli/dist/src/resilience/backoff.js +30 -0
  31. package/v3/@swarmdo/cli/dist/src/swarmvector/model-router.js +14 -0
  32. package/v3/@swarmdo/cli/dist/src/transcript/search.d.ts +39 -0
  33. package/v3/@swarmdo/cli/dist/src/transcript/search.js +67 -0
  34. package/v3/@swarmdo/cli/dist/src/usage/diff.d.ts +69 -0
  35. package/v3/@swarmdo/cli/dist/src/usage/diff.js +112 -0
  36. package/v3/@swarmdo/cli/package.json +4 -4
  37. package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
  38. package/v3/@swarmdo/codex/dist/cli.js +0 -0
  39. package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
  40. package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
  41. package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
  42. package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +4 -1
  43. package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
  44. package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
  45. package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
  46. package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
  47. package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
  48. package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
  49. package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
  50. package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
  51. package/v3/@swarmdo/shared/dist/mcp/transport/http.js +8 -1
  52. package/v3/vendor/agentdb/package.json +0 -1
  53. package/v3/vendor/agentic-flow/package.json +0 -1
  54. package/.claude/scheduled_tasks.lock +0 -1
  55. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  56. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/__main__.cpython-314.pyc +0 -0
  57. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/cli.cpython-314.pyc +0 -0
  58. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/compress.cpython-314.pyc +0 -0
  59. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/detect.cpython-314.pyc +0 -0
  60. package/.claude/skills/sdo-caveman-compress/scripts/__pycache__/validate.cpython-314.pyc +0 -0
  61. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +0 -2
  62. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +0 -2
  63. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +0 -138
  64. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +0 -418
  65. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +0 -15
  66. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +0 -240
  67. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +0 -7
  68. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +0 -508
  69. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +0 -54
  70. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +0 -2
  71. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +0 -46
  72. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +0 -2
  73. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +0 -18
  74. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +0 -28
  75. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +0 -5
  76. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +0 -2
  77. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +0 -35
  78. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +0 -2
  79. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +0 -11
  80. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +0 -2
  81. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +0 -9
  82. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +0 -2
  83. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +0 -5
  84. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +0 -2
  85. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +0 -12
  86. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +0 -2
  87. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +0 -19
  88. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +0 -2
  89. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +0 -44
  90. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +0 -117
  91. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +0 -70
  92. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +0 -194
  93. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +0 -69
  94. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +0 -139
  95. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +0 -34
  96. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +0 -101
  97. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +0 -10
  98. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +0 -10
  99. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +0 -64
  100. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +0 -70
  101. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +0 -44
  102. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +0 -79
  103. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +0 -37
  104. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +0 -39
  105. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +0 -67
  106. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +0 -58
  107. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +0 -45
  108. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +0 -63
  109. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +0 -26
  110. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +0 -17
  111. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +0 -51
  112. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +0 -110
  113. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +0 -13
  114. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +0 -25
  115. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +0 -11
  116. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +0 -19
  117. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +0 -11
  118. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +0 -27
  119. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +0 -6
  120. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +0 -6
  121. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +0 -41
  122. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +0 -64
  123. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +0 -8
  124. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +0 -703
  125. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +0 -23
  126. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +0 -239
  127. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +0 -17
  128. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +0 -42
  129. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +0 -18
  130. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +0 -45
  131. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +0 -19
  132. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +0 -50
  133. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +0 -19
  134. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +0 -47
  135. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +0 -17
  136. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +0 -40
  137. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +0 -18
  138. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +0 -51
  139. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +0 -214
  140. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +0 -595
  141. package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +0 -76
  142. package/v3/@swarmdo/testing/dist/fixtures/agents.js +0 -158
  143. package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +0 -157
  144. package/v3/@swarmdo/testing/dist/fixtures/configurations.js +0 -337
  145. package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +0 -10
  146. package/v3/@swarmdo/testing/dist/fixtures/index.js +0 -17
  147. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +0 -329
  148. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +0 -714
  149. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +0 -159
  150. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +0 -262
  151. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +0 -290
  152. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +0 -547
  153. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +0 -289
  154. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +0 -534
  155. package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +0 -106
  156. package/v3/@swarmdo/testing/dist/fixtures/tasks.js +0 -229
  157. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +0 -213
  158. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +0 -435
  159. package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +0 -135
  160. package/v3/@swarmdo/testing/dist/helpers/assertions.js +0 -217
  161. package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +0 -96
  162. package/v3/@swarmdo/testing/dist/helpers/create-mock.js +0 -164
  163. package/v3/@swarmdo/testing/dist/helpers/index.d.ts +0 -14
  164. package/v3/@swarmdo/testing/dist/helpers/index.js +0 -22
  165. package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +0 -323
  166. package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +0 -350
  167. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +0 -271
  168. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +0 -446
  169. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +0 -142
  170. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +0 -205
  171. package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +0 -190
  172. package/v3/@swarmdo/testing/dist/helpers/test-application.js +0 -138
  173. package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +0 -274
  174. package/v3/@swarmdo/testing/dist/helpers/test-utils.js +0 -456
  175. package/v3/@swarmdo/testing/dist/index.d.ts +0 -42
  176. package/v3/@swarmdo/testing/dist/index.js +0 -66
  177. package/v3/@swarmdo/testing/dist/mocks/index.d.ts +0 -8
  178. package/v3/@swarmdo/testing/dist/mocks/index.js +0 -10
  179. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +0 -206
  180. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +0 -575
  181. package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +0 -287
  182. package/v3/@swarmdo/testing/dist/mocks/mock-services.js +0 -563
  183. package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +0 -109
  184. package/v3/@swarmdo/testing/dist/regression/api-contract.js +0 -374
  185. package/v3/@swarmdo/testing/dist/regression/index.d.ts +0 -18
  186. package/v3/@swarmdo/testing/dist/regression/index.js +0 -18
  187. package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +0 -67
  188. package/v3/@swarmdo/testing/dist/regression/integration-regression.js +0 -334
  189. package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +0 -106
  190. package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +0 -268
  191. package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +0 -124
  192. package/v3/@swarmdo/testing/dist/regression/regression-runner.js +0 -227
  193. package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +0 -73
  194. package/v3/@swarmdo/testing/dist/regression/security-regression.js +0 -258
  195. package/v3/@swarmdo/testing/dist/setup.d.ts +0 -43
  196. package/v3/@swarmdo/testing/dist/setup.js +0 -91
  197. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +0 -10
  198. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +0 -433
  199. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +0 -10
  200. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +0 -352
  201. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +0 -206
  202. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +0 -839
  203. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +0 -10
  204. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +0 -428
  205. package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +0 -38
  206. package/v3/@swarmdo/testing/dist/v2-compat/index.js +0 -41
  207. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +0 -10
  208. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +0 -419
  209. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +0 -34
  210. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +0 -372
@@ -0,0 +1,224 @@
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
+ after = after.replaceAll(`npx%20swarmdo-v${current}-cb3837`, `npx%20swarmdo-v${next}-cb3837`);
53
+ after = after.replaceAll(`"softwareVersion":"${current}"`, `"softwareVersion":"${next}"`);
54
+ after = after.replaceAll(`<span class="ver">v${current} · MIT</span>`, `<span class="ver">v${next} · MIT</span>`);
55
+ after = after.replaceAll(`▊ Swarmdo V${current} `, `▊ Swarmdo V${next} `);
56
+ if (after !== before) {
57
+ fs.writeFileSync(abs, after);
58
+ touched.push(rel);
59
+ }
60
+ }
61
+ return touched;
62
+ }
63
+ async function executePlan(plan, root) {
64
+ const run = (s) => {
65
+ output.writeln(output.dim(` $ ${s.cmd} ${s.args.join(' ')} (${path.relative(root, s.cwd) || '.'})`));
66
+ execFileSync(s.cmd, s.args, { cwd: s.cwd, stdio: 'inherit' });
67
+ };
68
+ for (const [i, step] of plan.steps.entries()) {
69
+ output.writeln(output.bold(`[${i + 1}/${plan.steps.length}] ${renderStep(step)}`));
70
+ switch (step.kind) {
71
+ case 'bump-json':
72
+ bumpJson(path.join(root, step.file), step.version);
73
+ break;
74
+ case 'bump-lock':
75
+ bumpLock(path.join(root, step.file), step.version);
76
+ break;
77
+ case 'sync-docs': {
78
+ const touched = syncDocs(root, step.files, plan.current, plan.next);
79
+ output.writeln(output.dim(` synced: ${touched.join(', ') || '(none)'}`));
80
+ break;
81
+ }
82
+ case 'exec':
83
+ run(step);
84
+ break;
85
+ case 'verify-npm': {
86
+ for (const pkg of step.packages) {
87
+ let seen = '';
88
+ for (let attempt = 0; attempt < 20; attempt++) {
89
+ try {
90
+ seen = execFileSync('npm', ['view', `${pkg}@latest`, 'version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
91
+ }
92
+ catch {
93
+ seen = '';
94
+ }
95
+ if (seen === step.version)
96
+ break;
97
+ execFileSync('sleep', ['3']);
98
+ }
99
+ if (seen !== step.version) {
100
+ output.printError(`${pkg}@latest is "${seen || '(unset)'}" — expected ${step.version}`);
101
+ return { success: false, exitCode: 1 };
102
+ }
103
+ output.writeln(output.dim(` ${pkg}@latest = ${seen} ✓`));
104
+ }
105
+ break;
106
+ }
107
+ case 'deploy-site': {
108
+ // user rule 2026-07-07: the live site ships with every release.
109
+ // Copy the working copy into the SwarmDo/swarmdo.com repo, push,
110
+ // and verify production actually serves the new version string.
111
+ const siteDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmdo-site-'));
112
+ execFileSync('gh', ['repo', 'clone', 'SwarmDo/swarmdo.com', siteDir, '--', '-q'], { stdio: 'inherit' });
113
+ fs.copyFileSync(path.join(root, 'website', 'index.html'), path.join(siteDir, 'index.html'));
114
+ const smap = path.join(siteDir, 'sitemap.xml');
115
+ if (fs.existsSync(smap)) {
116
+ const today = new Date().toISOString().slice(0, 10);
117
+ fs.writeFileSync(smap, fs.readFileSync(smap, 'utf8').replace(/<lastmod>[^<]*<\/lastmod>/g, `<lastmod>${today}</lastmod>`));
118
+ }
119
+ const g = (args) => { execFileSync('git', args, { cwd: siteDir, stdio: 'inherit' }); };
120
+ g(['add', '-A']);
121
+ try {
122
+ g(['commit', '-m', `release: sync site for v${plan.next}\n\nCo-Authored-By: Swarmdo <maintainers@swarmdo.com>`]);
123
+ }
124
+ catch {
125
+ output.writeln(output.dim(' site already in sync — nothing to commit'));
126
+ break;
127
+ }
128
+ g(['push', 'origin', 'main']);
129
+ // poll production (GitHub Pages deploys in ~1min)
130
+ let live = false;
131
+ for (let i = 0; i < 30; i++) {
132
+ try {
133
+ const body = execFileSync('curl', ['-s', 'https://swarmdo.com'], { encoding: 'utf8' });
134
+ if (body.includes(`v${plan.next}`)) {
135
+ live = true;
136
+ break;
137
+ }
138
+ }
139
+ catch { /* transient */ }
140
+ execFileSync('sleep', ['10']);
141
+ }
142
+ if (!live) {
143
+ output.printError(`swarmdo.com is not serving v${plan.next} after 5min — check the Pages build`);
144
+ return { success: false, exitCode: 1 };
145
+ }
146
+ output.writeln(output.dim(` swarmdo.com serves v${plan.next} ✓`));
147
+ break;
148
+ }
149
+ case 'gh-release': {
150
+ const notes = path.join(os.tmpdir(), `swarmdo-relnotes-${process.pid}.md`);
151
+ const { changelogCommand } = await import('./changelog.js');
152
+ const r = await changelogCommand.action({ args: [], flags: { from: step.notesFrom, version: plan.tag, out: notes }, cwd: root, interactive: false });
153
+ if (r && r.success === false)
154
+ return { success: false, exitCode: 1 };
155
+ execFileSync('gh', ['release', 'create', plan.tag, '--title', `${plan.tag}`, '--notes-file', notes], { cwd: root, stdio: 'inherit' });
156
+ try {
157
+ fs.rmSync(notes, { force: true });
158
+ }
159
+ catch { /* best-effort */ }
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ output.printSuccess(`released ${plan.tag}`);
165
+ return { success: true, exitCode: 0 };
166
+ }
167
+ export const releaseCommand = {
168
+ name: 'release',
169
+ aliases: ['ship'],
170
+ description: 'Run the full release train: bump trio+docs, commit, build+stage, publish both npm packages, verify, tag, GitHub release — dry-run unless --confirm',
171
+ options: [
172
+ { name: 'confirm', type: 'boolean', description: 'actually execute (default: print the plan)', default: false },
173
+ { name: 'skip-publish', type: 'boolean', description: 'skip npm publish + registry verify steps', default: false },
174
+ { name: 'skip-gh-release', type: 'boolean', description: 'skip the GitHub release step', default: false },
175
+ { name: 'skip-site', type: 'boolean', description: 'skip the live-site deploy (docs-sync rule: only for zero-user-facing releases)', default: false },
176
+ { name: 'json', type: 'boolean', description: 'print the plan as JSON', default: false },
177
+ ],
178
+ examples: [
179
+ { command: 'swarmdo release', description: 'Plan a patch release (dry-run)' },
180
+ { command: 'swarmdo release minor --confirm', description: 'Execute a minor release end-to-end' },
181
+ { command: 'swarmdo release 2.0.0 --skip-publish --confirm', description: 'Bump/tag/release 2.0.0 without publishing to npm' },
182
+ ],
183
+ action: async (ctx) => {
184
+ const cwd = ctx.cwd || process.cwd();
185
+ const root = repoRootFrom(cwd);
186
+ if (!root) {
187
+ output.printError('not a git repository');
188
+ return { success: false, exitCode: 1 };
189
+ }
190
+ const trioHead = path.join(root, 'v3/@swarmdo/cli/package.json');
191
+ if (!fs.existsSync(trioHead)) {
192
+ output.printError('this does not look like the swarmdo repo (v3/@swarmdo/cli/package.json missing)');
193
+ return { success: false, exitCode: 1 };
194
+ }
195
+ const current = String(JSON.parse(fs.readFileSync(trioHead, 'utf8')).version ?? '');
196
+ let plan;
197
+ try {
198
+ plan = planRelease({
199
+ current,
200
+ bump: (ctx.args[0] || 'patch').toLowerCase(),
201
+ repoRoot: root,
202
+ skipPublish: ctx.flags['skip-publish'] === true,
203
+ skipGhRelease: ctx.flags['skip-gh-release'] === true,
204
+ skipSite: ctx.flags['skip-site'] === true,
205
+ });
206
+ }
207
+ catch (e) {
208
+ output.printError(e.message);
209
+ return { success: false, exitCode: 1 };
210
+ }
211
+ if (ctx.flags.json === true) {
212
+ output.printJson(plan);
213
+ return { success: true, data: plan };
214
+ }
215
+ if (ctx.flags.confirm !== true) {
216
+ output.writeln(output.bold(`Release plan ${plan.current} → ${plan.next}`) + output.dim(' (dry-run — re-run with --confirm to execute)'));
217
+ plan.steps.forEach((s, i) => output.writeln(` ${String(i + 1).padStart(2)}. ${renderStep(s)}`));
218
+ return { success: true, data: { dryRun: true, ...plan } };
219
+ }
220
+ return executePlan(plan, root);
221
+ },
222
+ };
223
+ export default releaseCommand;
224
+ //# 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;
@@ -11,7 +11,8 @@
11
11
  const f = (file, severity, rule, message) => ({ file, severity, rule, message });
12
12
  export const TOPOLOGIES = ['hierarchical', 'mesh', 'hierarchical-mesh', 'ring', 'star', 'hybrid', 'adaptive'];
13
13
  export const MEMORY_BACKENDS = ['agentdb', 'sqlite', 'hybrid', 'memory'];
14
- export const KNOWN_CONFIG_KEYS = ['topology', 'maxAgents', 'strategy', 'consensus', 'memory', 'memoryBackend', 'hnsw', 'neural', 'embeddings', 'providers', 'mcp', 'logging', 'daemon', 'hooks', 'version', '$schema'];
14
+ import { parseOpenRouterConfig } from '../providers/openrouter-config.js';
15
+ export const KNOWN_CONFIG_KEYS = ['topology', 'maxAgents', 'strategy', 'consensus', 'memory', 'memoryBackend', 'hnsw', 'neural', 'embeddings', 'providers', 'mcp', 'logging', 'daemon', 'hooks', 'version', 'openrouter', '$schema'];
15
16
  export const HOOK_EVENTS = [
16
17
  'PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'Notification', 'Stop', 'SubagentStop',
17
18
  'SessionStart', 'SessionEnd', 'PreCompact', 'TeammateIdle', 'TaskCompleted', 'PermissionDecision',
@@ -39,6 +40,15 @@ export function lintSwarmdoConfig(file, obj) {
39
40
  for (const key of Object.keys(c)) {
40
41
  if (!KNOWN_CONFIG_KEYS.includes(key))
41
42
  out.push(f(file, 'warn', 'unknown-key', `unknown key "${key}" (known: ${KNOWN_CONFIG_KEYS.filter(k => k !== '$schema').join(', ')})`));
43
+ if (key === 'openrouter') {
44
+ // reuse the runtime parser so lint findings and runtime behavior can't drift
45
+ const { config: orCfg, warnings } = parseOpenRouterConfig(c.openrouter);
46
+ for (const w of warnings)
47
+ out.push(f(file, 'warn', 'openrouter-config', w));
48
+ if (orCfg.enabled && orCfg.models.length === 0 && !orCfg.defaultModel) {
49
+ out.push(f(file, 'warn', 'openrouter-config', 'openrouter.enabled=true but no valid models[] or defaultModel — swarms have nothing to select from'));
50
+ }
51
+ }
42
52
  }
43
53
  if (c.topology !== undefined && !TOPOLOGIES.includes(String(c.topology))) {
44
54
  out.push(f(file, 'error', 'bad-topology', `topology "${String(c.topology)}" is not one of: ${TOPOLOGIES.join(', ')}`));
@@ -384,7 +384,7 @@ function buildLocalFallback() {
384
384
  return applyLocalOverlays({
385
385
  user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
386
386
  v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
387
- security: { status: 'NONE', cvesFixed: 0, totalCves: 0 },
387
+ security: { status: 'NONE', critical: 0, high: 0, total: 0 },
388
388
  swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
389
389
  system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
390
390
  lastUpdated: new Date().toISOString(),
@@ -652,9 +652,9 @@ function generateStatusline() {
652
652
  const intelligencePct = system.intelligencePct || 0;
653
653
  const memoryMB = system.memoryMB || 0;
654
654
  const subAgents = system.subAgents || 0;
655
- const cvesFixed = security.cvesFixed || 0;
656
- const totalCves = security.totalCves || 0;
655
+ const secCritHigh = (security.critical || 0) + (security.high || 0);
657
656
  const secStatus = security.status || 'NONE';
657
+ const secLabel = secStatus === 'VULN' ? 'sec ' + secCritHigh + '!' : secStatus === 'CLEAN' ? 'sec ✓' : secStatus === 'STALE' ? 'sec stale' : 'sec —';
658
658
  const adrCount = adrs.count || 0;
659
659
  const adrImpl = adrs.implemented || 0;
660
660
  const hooksEnabled = hooks.enabled || 0;
@@ -720,8 +720,8 @@ function generateStatusline() {
720
720
  // Line 2: Swarm + Hooks + CVE + Memory + Intelligence
721
721
  const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset : c.dim + '○' + c.reset;
722
722
  const agentsColor = activeAgents > 0 ? c.brightGreen : c.red;
723
- const secIcon = secStatus === 'CLEAN' ? '🟢' : (secStatus === 'IN_PROGRESS' || secStatus === 'STALE') ? '🟡' : (secStatus === 'NONE' ? '⚪' : '🔴');
724
- const secColor = secStatus === 'CLEAN' ? c.brightGreen : (secStatus === 'IN_PROGRESS' || secStatus === 'STALE') ? c.brightYellow : (secStatus === 'NONE' ? c.dim : c.brightRed);
723
+ const secIcon = secStatus === 'CLEAN' ? '🟢' : secStatus === 'STALE' ? '🟡' : (secStatus === 'NONE' ? '⚪' : '🔴');
724
+ const secColor = secStatus === 'CLEAN' ? c.brightGreen : secStatus === 'STALE' ? c.brightYellow : (secStatus === 'NONE' ? c.dim : c.brightRed);
725
725
  const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
726
726
  const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
727
727
 
@@ -729,7 +729,7 @@ function generateStatusline() {
729
729
  c.brightYellow + '🤖 Swarm' + c.reset + ' ' + swarmInd + ' [' + agentsColor + String(activeAgents).padStart(2) + c.reset + '/' + c.brightWhite + maxAgents + c.reset + '] ' +
730
730
  c.brightPurple + '👥 ' + subAgents + c.reset + ' ' +
731
731
  c.brightBlue + '🪝 ' + hooksColor + hooksEnabled + c.reset + '/' + c.brightWhite + hooksTotal + c.reset + ' ' +
732
- secIcon + ' ' + secColor + 'CVE ' + cvesFixed + c.reset + '/' + c.brightWhite + totalCves + c.reset + ' ' +
732
+ secIcon + ' ' + secColor + secLabel + c.reset + ' ' +
733
733
  c.brightCyan + '💾 ' + memoryMB + 'MB' + c.reset + ' ' +
734
734
  intellColor + '🧠 ' + String(intelligencePct).padStart(3) + '%' + c.reset
735
735
  );
@@ -0,0 +1,54 @@
1
+ /**
2
+ * integrations.ts — wire swarmdo into agent CLIs beyond Claude Code.
3
+ *
4
+ * Targets: OpenAI Codex CLI, GitHub Copilot CLI, pi. Two standards do the
5
+ * heavy lifting: AGENTS.md (the cross-agent instructions file all three
6
+ * read) and MCP (swarmdo already ships a stdio server — `swarmdo mcp
7
+ * start`). Each merge below is PURE (string/JSON in → out), additive, and
8
+ * idempotent, so the whole surface is unit-testable and re-running install
9
+ * never duplicates or clobbers.
10
+ *
11
+ * INVARIANT (do-not-break-Claude): nothing in this module produces content
12
+ * for `.claude/**`, `.mcp.json`, or `CLAUDE.md` — the Claude Code surfaces
13
+ * are owned by init/mcp-generator and are only ever READ for status here.
14
+ */
15
+ export declare const MCP_COMMAND = "npx";
16
+ export declare const MCP_ARGS: string[];
17
+ /** Codex CLI (~/.codex/config.toml): add the marked [mcp_servers.swarmdo]
18
+ * block when absent. Existing content — including a user's own hand-written
19
+ * swarmdo server — is left untouched. */
20
+ export declare function mergeCodexToml(existing: string | null): {
21
+ content: string;
22
+ changed: boolean;
23
+ };
24
+ /** Copilot CLI (~/.copilot/mcp-config.json): same mcpServers schema Claude
25
+ * uses. Adds the swarmdo entry when absent; other servers preserved. */
26
+ export declare function mergeCopilotMcpJson(existing: string | null): {
27
+ content: string;
28
+ changed: boolean;
29
+ };
30
+ /** Cross-agent AGENTS.md for projects that don't have one. Existing files
31
+ * are never rewritten (report-only) — teams own their AGENTS.md. */
32
+ export declare function crossAgentAgentsMd(): string;
33
+ /** .github/copilot-instructions.md — Copilot reads this per-repo. Kept as a
34
+ * thin pointer so AGENTS.md stays the single source of truth. */
35
+ export declare function copilotInstructionsMd(): string;
36
+ export type IntegrationTarget = 'codex' | 'copilot' | 'pi' | 'claude';
37
+ export declare const INSTALLABLE: IntegrationTarget[];
38
+ export interface TargetStatus {
39
+ target: IntegrationTarget;
40
+ ok: boolean;
41
+ details: string[];
42
+ }
43
+ /** Pure status evaluation over pre-read file contents (null = absent). */
44
+ export declare function evaluateStatus(files: {
45
+ agentsMd: string | null;
46
+ codexToml: string | null;
47
+ copilotJson: string | null;
48
+ copilotInstructions: string | null;
49
+ claudeMcpJson: string | null;
50
+ /** ~/.claude.json — Claude Code's user-level config (claude mcp add) */
51
+ claudeUserJson?: string | null;
52
+ claudeDirExists: boolean;
53
+ }): TargetStatus[];
54
+ //# sourceMappingURL=integrations.d.ts.map