swarmdo 1.50.0 → 1.54.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 (184) hide show
  1. package/.claude/helpers/hook-handler.cjs +16 -0
  2. package/.claude/helpers/router.cjs +27 -1
  3. package/.claude/settings.local.json +7 -0
  4. package/README.md +3 -1
  5. package/package.json +1 -1
  6. package/v3/@swarmdo/cli/dist/src/agent-bridge/bridge.d.ts +104 -0
  7. package/v3/@swarmdo/cli/dist/src/agent-bridge/bridge.js +133 -0
  8. package/v3/@swarmdo/cli/dist/src/commands/agent.js +133 -1
  9. package/v3/@swarmdo/cli/dist/src/commands/index.js +7 -3
  10. package/v3/@swarmdo/cli/dist/src/commands/standup.d.ts +18 -0
  11. package/v3/@swarmdo/cli/dist/src/commands/standup.js +116 -0
  12. package/v3/@swarmdo/cli/dist/src/commands/swarm.js +29 -0
  13. package/v3/@swarmdo/cli/dist/src/init/helpers-generator.js +41 -1
  14. package/v3/@swarmdo/cli/dist/src/mcp-tools/agent-tools.js +166 -0
  15. package/v3/@swarmdo/cli/dist/src/mcp-tools/swarm-tools.d.ts +33 -0
  16. package/v3/@swarmdo/cli/dist/src/mcp-tools/swarm-tools.js +73 -38
  17. package/v3/@swarmdo/cli/dist/src/standup/standup.d.ts +83 -0
  18. package/v3/@swarmdo/cli/dist/src/standup/standup.js +157 -0
  19. package/v3/@swarmdo/cli/package.json +1 -1
  20. package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
  21. package/v3/@swarmdo/codex/dist/cli.js +0 -0
  22. package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
  23. package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
  24. package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
  25. package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +1 -4
  26. package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
  27. package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
  28. package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
  29. package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
  30. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +2 -0
  31. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +2 -0
  32. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +138 -0
  33. package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +418 -0
  34. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +15 -0
  35. package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +240 -0
  36. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +7 -0
  37. package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +508 -0
  38. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +54 -0
  39. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +2 -0
  40. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +46 -0
  41. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +2 -0
  42. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +18 -0
  43. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +28 -0
  44. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +5 -0
  45. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +2 -0
  46. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +35 -0
  47. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +2 -0
  48. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +11 -0
  49. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +2 -0
  50. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +9 -0
  51. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +2 -0
  52. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +5 -0
  53. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +2 -0
  54. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +12 -0
  55. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +2 -0
  56. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +19 -0
  57. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +2 -0
  58. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +44 -0
  59. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +117 -0
  60. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +70 -0
  61. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +194 -0
  62. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +69 -0
  63. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +139 -0
  64. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +34 -0
  65. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +101 -0
  66. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +10 -0
  67. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +10 -0
  68. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +64 -0
  69. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +70 -0
  70. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +44 -0
  71. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +79 -0
  72. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +37 -0
  73. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +39 -0
  74. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +67 -0
  75. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +58 -0
  76. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +45 -0
  77. package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +63 -0
  78. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +26 -0
  79. package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +17 -0
  80. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +51 -0
  81. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +110 -0
  82. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +13 -0
  83. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +25 -0
  84. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +11 -0
  85. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +19 -0
  86. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +11 -0
  87. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +27 -0
  88. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +6 -0
  89. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +6 -0
  90. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +41 -0
  91. package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +64 -0
  92. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +8 -0
  93. package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +703 -0
  94. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +23 -0
  95. package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +239 -0
  96. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +17 -0
  97. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +42 -0
  98. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +18 -0
  99. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +45 -0
  100. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +19 -0
  101. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +50 -0
  102. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +19 -0
  103. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +47 -0
  104. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +17 -0
  105. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +40 -0
  106. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +18 -0
  107. package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +51 -0
  108. package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
  109. package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
  110. package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
  111. package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
  112. package/v3/@swarmdo/shared/dist/mcp/transport/http.js +1 -8
  113. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +214 -0
  114. package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +595 -0
  115. package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +76 -0
  116. package/v3/@swarmdo/testing/dist/fixtures/agents.js +158 -0
  117. package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +157 -0
  118. package/v3/@swarmdo/testing/dist/fixtures/configurations.js +337 -0
  119. package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +10 -0
  120. package/v3/@swarmdo/testing/dist/fixtures/index.js +17 -0
  121. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +329 -0
  122. package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +714 -0
  123. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +159 -0
  124. package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +262 -0
  125. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +290 -0
  126. package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +547 -0
  127. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +289 -0
  128. package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +534 -0
  129. package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +106 -0
  130. package/v3/@swarmdo/testing/dist/fixtures/tasks.js +229 -0
  131. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +213 -0
  132. package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +435 -0
  133. package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +135 -0
  134. package/v3/@swarmdo/testing/dist/helpers/assertions.js +217 -0
  135. package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +96 -0
  136. package/v3/@swarmdo/testing/dist/helpers/create-mock.js +164 -0
  137. package/v3/@swarmdo/testing/dist/helpers/index.d.ts +14 -0
  138. package/v3/@swarmdo/testing/dist/helpers/index.js +22 -0
  139. package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +323 -0
  140. package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +350 -0
  141. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +271 -0
  142. package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +446 -0
  143. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +142 -0
  144. package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +205 -0
  145. package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +190 -0
  146. package/v3/@swarmdo/testing/dist/helpers/test-application.js +138 -0
  147. package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +274 -0
  148. package/v3/@swarmdo/testing/dist/helpers/test-utils.js +456 -0
  149. package/v3/@swarmdo/testing/dist/index.d.ts +42 -0
  150. package/v3/@swarmdo/testing/dist/index.js +66 -0
  151. package/v3/@swarmdo/testing/dist/mocks/index.d.ts +8 -0
  152. package/v3/@swarmdo/testing/dist/mocks/index.js +10 -0
  153. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +206 -0
  154. package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +575 -0
  155. package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +287 -0
  156. package/v3/@swarmdo/testing/dist/mocks/mock-services.js +563 -0
  157. package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +109 -0
  158. package/v3/@swarmdo/testing/dist/regression/api-contract.js +374 -0
  159. package/v3/@swarmdo/testing/dist/regression/index.d.ts +18 -0
  160. package/v3/@swarmdo/testing/dist/regression/index.js +18 -0
  161. package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +67 -0
  162. package/v3/@swarmdo/testing/dist/regression/integration-regression.js +334 -0
  163. package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +106 -0
  164. package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +268 -0
  165. package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +124 -0
  166. package/v3/@swarmdo/testing/dist/regression/regression-runner.js +227 -0
  167. package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +73 -0
  168. package/v3/@swarmdo/testing/dist/regression/security-regression.js +258 -0
  169. package/v3/@swarmdo/testing/dist/setup.d.ts +43 -0
  170. package/v3/@swarmdo/testing/dist/setup.js +91 -0
  171. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +10 -0
  172. package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +433 -0
  173. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +10 -0
  174. package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +352 -0
  175. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +206 -0
  176. package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +839 -0
  177. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +10 -0
  178. package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +428 -0
  179. package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +38 -0
  180. package/v3/@swarmdo/testing/dist/v2-compat/index.js +41 -0
  181. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +10 -0
  182. package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +419 -0
  183. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +34 -0
  184. package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +372 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `swarmdo standup` — recall what you committed since your last working day.
3
+ *
4
+ * swarmdo standup # your commits since the last working day
5
+ * swarmdo standup --all # everyone's commits
6
+ * swarmdo standup --author "Ada" # a specific author (or: swarmdo standup Ada)
7
+ * swarmdo standup --days 7 # explicit N-day window
8
+ * swarmdo standup --since 2w # explicit window (git approxidate)
9
+ * swarmdo standup --format json # machine-readable
10
+ *
11
+ * Weekend-aware (git-standup parity): on Monday it reaches back to Friday, on
12
+ * Sunday to Friday, otherwise to yesterday. Engine (../standup/standup.ts) is
13
+ * pure + tested; this thin layer captures the git log and renders.
14
+ */
15
+ import { execFileSync } from 'node:child_process';
16
+ import { output } from '../output.js';
17
+ import { parseStandupLog, groupByDay, sinceLastWorkingDay, formatStandup } from '../standup/standup.js';
18
+ import { normalizeSince } from '../util/since.js';
19
+ /** Read a non-negative numeric flag the parser may deliver as number OR string. */
20
+ function numFlag(v) {
21
+ const n = typeof v === 'number' ? v : typeof v === 'string' ? parseInt(v, 10) : NaN;
22
+ return Number.isFinite(n) && n >= 0 ? n : undefined;
23
+ }
24
+ /** Resolve the default author (git config user.name); '' if unset/unavailable. */
25
+ function gitUserName(root) {
26
+ try {
27
+ return execFileSync('git', ['config', 'user.name'], {
28
+ cwd: root,
29
+ encoding: 'utf8',
30
+ stdio: ['ignore', 'pipe', 'ignore'],
31
+ }).trim();
32
+ }
33
+ catch {
34
+ return '';
35
+ }
36
+ }
37
+ async function run(ctx) {
38
+ const root = ctx.cwd || process.cwd();
39
+ const asJson = ctx.flags.format === 'json';
40
+ const all = ctx.flags.all === true;
41
+ // Author: explicit --author, else positional arg, else current git user
42
+ // (unless --all, which clears the filter to show everyone).
43
+ const authorFlag = typeof ctx.flags.author === 'string' ? ctx.flags.author : ctx.args[0];
44
+ let author = '';
45
+ if (!all)
46
+ author = ((authorFlag && authorFlag.trim()) || gitUserName(root)).trim();
47
+ // Window resolution (priority): --since (approxidate passthrough) >
48
+ // --days N > the weekend-aware default.
49
+ const now = new Date();
50
+ const sinceExpr = typeof ctx.flags.since === 'string' ? ctx.flags.since.trim() : '';
51
+ const daysFlag = numFlag(ctx.flags.days);
52
+ let sinceArg;
53
+ let windowLabel;
54
+ if (sinceExpr) {
55
+ sinceArg = normalizeSince(sinceExpr);
56
+ windowLabel = sinceExpr;
57
+ }
58
+ else {
59
+ const days = daysFlag ?? sinceLastWorkingDay(now).sinceDays;
60
+ // Cutoff = LOCAL midnight `days` ago, so the WHOLE last working day is
61
+ // included (a plain "3 days ago" would start mid-morning and miss commits).
62
+ const cutoff = new Date(now.getFullYear(), now.getMonth(), now.getDate() - days);
63
+ sinceArg = cutoff.toISOString();
64
+ windowLabel = days === 1 ? 'since yesterday' : `since ${days} days ago`;
65
+ }
66
+ const args = ['log', '--numstat', `--since=${sinceArg}`, '--format=format:%x01%H%x1f%aN%x1f%aI%x1f%s'];
67
+ if (author)
68
+ args.push(`--author=${author}`);
69
+ let raw;
70
+ try {
71
+ raw = execFileSync('git', args, {
72
+ cwd: root,
73
+ encoding: 'utf8',
74
+ stdio: ['ignore', 'pipe', 'pipe'],
75
+ maxBuffer: 128 * 1024 * 1024,
76
+ });
77
+ }
78
+ catch {
79
+ output.printError('git log failed — is this a git repository?');
80
+ return { success: false, exitCode: 1 };
81
+ }
82
+ const buckets = groupByDay(parseStandupLog(raw));
83
+ if (asJson) {
84
+ const count = buckets.reduce((n, b) => n + b.commits.length, 0);
85
+ process.stdout.write(JSON.stringify({ generated: now.toISOString(), author: all ? null : author || null, since: sinceArg, count, buckets }, null, 2) + '\n');
86
+ }
87
+ else {
88
+ const who = all ? 'everyone' : author || 'you';
89
+ if (buckets.length === 0) {
90
+ output.writeln(output.dim(`no commits for ${who} ${windowLabel} — nothing to report`));
91
+ }
92
+ else {
93
+ output.writeln(output.bold(`Standup for ${who} (${windowLabel})`));
94
+ output.writeln(formatStandup(buckets));
95
+ }
96
+ }
97
+ return { success: true, exitCode: 0 };
98
+ }
99
+ export const standupCommand = {
100
+ name: 'standup',
101
+ description: 'Recall what you committed since your last working day (weekend-aware: Monday reaches back to Friday) — git-standup parity',
102
+ options: [
103
+ { name: 'all', description: 'show commits from all authors (not just you)', type: 'boolean' },
104
+ { name: 'author', description: 'filter to a specific author (default: git config user.name)', type: 'string' },
105
+ { name: 'days', description: 'explicit N-day window (overrides the weekend-aware default)', type: 'string' },
106
+ { name: 'since', description: 'explicit window, e.g. 2w or "last monday" (git approxidate)', type: 'string' },
107
+ ],
108
+ examples: [
109
+ { command: 'swarmdo standup', description: 'Your commits since the last working day' },
110
+ { command: 'swarmdo standup --all --since 1w', description: "Everyone's commits in the last week" },
111
+ { command: 'swarmdo standup --format json', description: 'Machine-readable per-day buckets' },
112
+ ],
113
+ action: run,
114
+ };
115
+ export default standupCommand;
116
+ //# sourceMappingURL=standup.js.map
@@ -103,6 +103,35 @@ function getSwarmStatus(swarmId) {
103
103
  // Ignore
104
104
  }
105
105
  }
106
+ // Canonical-store fallback: the `.swarm/*` paths above are the pre-#2085
107
+ // layout. The real swarm store written by swarm_init / agent_spawn / the
108
+ // agent bridge lives at `.swarmdo/swarm/swarm-state.json`. When the legacy
109
+ // paths are empty, read the canonical store so `swarm status` reflects a
110
+ // bridge-created (or MCP-created) swarm instead of falsely reporting "none".
111
+ if (!swarmState) {
112
+ try {
113
+ const swFile = path.join(process.cwd(), '.swarmdo', 'swarm', 'swarm-state.json');
114
+ if (fs.existsSync(swFile)) {
115
+ const swStore = JSON.parse(fs.readFileSync(swFile, 'utf-8'));
116
+ const running = Object.values(swStore.swarms || {})
117
+ .filter((s) => s.status === 'running')
118
+ .sort((a, b) => new Date(String(b.createdAt)).getTime() - new Date(String(a.createdAt)).getTime())[0];
119
+ if (running) {
120
+ const cfg = running.config || {};
121
+ swarmState = {
122
+ id: running.swarmId,
123
+ topology: running.topology,
124
+ strategy: cfg.strategy || 'specialized',
125
+ };
126
+ const enrolled = Array.isArray(running.agents) ? running.agents.length : 0;
127
+ totalAgents = Math.max(totalAgents, enrolled);
128
+ }
129
+ }
130
+ }
131
+ catch {
132
+ // Canonical store unavailable — keep the legacy view.
133
+ }
134
+ }
106
135
  // Calculate dynamic progress based on actual state
107
136
  // If no swarm state, show 0%. Otherwise calculate from completed tasks
108
137
  const totalTasks = completedTasks + inProgressTasks + pendingTasks;
@@ -277,6 +277,31 @@ function routeTask(task) {
277
277
  };
278
278
  }
279
279
 
280
+ // Mirror of src/agent-bridge/bridge.ts classifyPrompt — decides whether a
281
+ // prompt warrants spinning up a bound-agent swarm (and which roles), so the
282
+ // UserPromptSubmit hook can tell the main agent to spawn + bridge agents
283
+ // (making Swarmdo used by default instead of sitting idle).
284
+ const AGENTIC_RE = /\\b(implement|build|create|add(?:ing)?|develop|refactor\\w*|migrat\\w*|redesign|re-?architect\\w*|architect\\w*|feature|integrat\\w*|overhaul|rewrite|port|scaffold|end-to-end|multi-file|test suite|coverage|audit|harden\\w*|vulnerab\\w*|cve|fix\\w*|debug\\w*|optimi[sz]e|performance)\\b/i;
285
+ const TRIVIAL_RE = /\\b(what|why|how|explain|describe|show|list|find|search|where|which|typo|readme|comment|one-?liner|quick question|rename|bump (?:the )?version|status|help)\\b/i;
286
+ const ROLE_SIGNALS = [
287
+ { re: /\\b(security|vulnerab\\w*|cve|auth\\w*|inject\\w*|xss|ssrf)\\b/i, roles: ['security-auditor', 'coder', 'reviewer'] },
288
+ { re: /\\b(refactor\\w*|migrat\\w*|redesign|re-?architect\\w*|overhaul|rewrite)\\b/i, roles: ['system-architect', 'coder', 'reviewer'] },
289
+ { re: /\\b(perf\\w*|optimi[sz]e|benchmark|latency|throughput)\\b/i, roles: ['perf-analyzer', 'coder', 'tester'] },
290
+ { re: /\\b(test|coverage|tdd|spec)\\b/i, roles: ['tester', 'coder', 'reviewer'] },
291
+ { re: /\\b(feature|implement|build|integrat\\w*|end-to-end|api)\\b/i, roles: ['researcher', 'system-architect', 'coder', 'tester', 'reviewer'] },
292
+ ];
293
+ function classifyAgentic(task) {
294
+ const p = String(task == null ? '' : task).trim();
295
+ if (!p) return { requiresAgents: false, roles: [] };
296
+ if (!AGENTIC_RE.test(p)) return { requiresAgents: false, roles: [] };
297
+ if (TRIVIAL_RE.test(p) && p.length < 40) return { requiresAgents: false, roles: [] };
298
+ let roles = ['coder'];
299
+ for (const s of ROLE_SIGNALS) {
300
+ if (s.re.test(p)) { roles = s.roles; break; }
301
+ }
302
+ return { requiresAgents: true, roles };
303
+ }
304
+
280
305
  // CLI
281
306
  const task = process.argv.slice(2).join(' ');
282
307
 
@@ -288,7 +313,7 @@ if (task) {
288
313
  console.log('\\nAvailable agents:', Object.keys(AGENT_CAPABILITIES).join(', '));
289
314
  }
290
315
 
291
- module.exports = { routeTask, AGENT_CAPABILITIES, TASK_PATTERNS, buildPattern };
316
+ module.exports = { routeTask, classifyAgentic, AGENT_CAPABILITIES, TASK_PATTERNS, buildPattern };
292
317
  `;
293
318
  }
294
319
  /**
@@ -482,6 +507,21 @@ export function generateHookHandler() {
482
507
  ' } else {',
483
508
  " console.log('[INFO] Router not available, using default routing');",
484
509
  ' }',
510
+ ' // Agentic prompts → remind the main agent to actually USE Swarmdo:',
511
+ ' // spawn Claude Code agents AND bridge them (auto-forms a swarm).',
512
+ ' // Advisory + fully guarded — never breaks the prompt.',
513
+ ' try {',
514
+ ' if (router && router.classifyAgentic) {',
515
+ ' var intent = router.classifyAgentic(prompt);',
516
+ ' if (intent.requiresAgents) {',
517
+ " console.log('');",
518
+ " console.log('[SWARMDO] Agentic task — use the swarm: after you spawn Claude Code agents,');",
519
+ " console.log(' register each so Swarmdo tracks them and auto-forms a swarm from your config:');",
520
+ " console.log(' swarmdo agent bridge register -n <agent-name> -t <role> -s <session>');",
521
+ " console.log(' suggested roles: ' + intent.roles.join(', ') + ' then: swarmdo agent bridge list / swarm status');",
522
+ ' }',
523
+ ' }',
524
+ ' } catch (e) { /* advisory only — never break the prompt */ }',
485
525
  ' },',
486
526
  '',
487
527
  " 'pre-bash': () => {",
@@ -9,6 +9,7 @@ import { join } from 'node:path';
9
9
  import { getProjectCwd } from './types.js';
10
10
  import { validateIdentifier, validateText, validateAgentSpawn } from './validate-input.js';
11
11
  import { executeAgentTask } from './agent-execute-core.js';
12
+ import { buildSpawnInput, reconcile, isBound, orphanedAgentIds } from '../agent-bridge/bridge.js';
12
13
  // Storage paths
13
14
  const STORAGE_DIR = '.swarmdo';
14
15
  const AGENT_DIR = 'agents';
@@ -74,6 +75,27 @@ function loadHiveAgents() {
74
75
  function loadAllAgents() {
75
76
  return { ...loadHiveAgents(), ...loadAgentStore().agents };
76
77
  }
78
+ /**
79
+ * Read the project's swarm topology defaults from swarmdo.config.json for the
80
+ * agent bridge's auto-swarm. Defensive — returns {} on any error so the bridge
81
+ * falls back to the anti-drift defaults (hierarchical / 8 / specialized).
82
+ * Accepts either a top-level `swarm` block or the root object.
83
+ */
84
+ function readSwarmConfigDefaults() {
85
+ try {
86
+ const p = join(getProjectCwd(), 'swarmdo.config.json');
87
+ if (!existsSync(p))
88
+ return {};
89
+ const j = JSON.parse(readFileSync(p, 'utf-8'));
90
+ const sw = (j.swarm || j) ?? {};
91
+ const str = (v) => (typeof v === 'string' ? v : undefined);
92
+ const num = (v) => (typeof v === 'number' ? v : undefined);
93
+ return { topology: str(sw.topology), maxAgents: num(sw.maxAgents), strategy: str(sw.strategy) };
94
+ }
95
+ catch {
96
+ return {};
97
+ }
98
+ }
77
99
  // Default model mappings for agent types (can be overridden)
78
100
  const AGENT_TYPE_MODEL_DEFAULTS = {
79
101
  // Complex agents → opus
@@ -323,6 +345,150 @@ export const agentTools = [
323
345
  return response;
324
346
  },
325
347
  },
348
+ {
349
+ name: 'agent_bridge_register',
350
+ description: "Register a REAL Claude Code Agent-tool agent into Swarmdo's registry so `swarmdo agent list` and `swarm_status` reflect it. Call this right AFTER you spawn a Claude Code subagent (Task/Agent tool): pass its name, session id, subagent type, and a one-line task. Idempotent — re-registering the same name+session UPDATES the bound record instead of duplicating. This is the bridge that stops Swarmdo sitting empty while real agents run: unlike agent_spawn (which only registers coordination metadata), this binds the record to a worker that actually exists.",
351
+ category: 'agent',
352
+ inputSchema: {
353
+ type: 'object',
354
+ properties: {
355
+ name: { type: 'string', description: 'The Claude Code agent name (e.g. "research-ccgap")' },
356
+ sessionId: { type: 'string', description: 'The Claude Code session id (from name@session-…)' },
357
+ agentType: { type: 'string', description: 'The subagent_type (e.g. general-purpose, coder). Default general-purpose.' },
358
+ task: { type: 'string', description: 'One-line task/prompt summary' },
359
+ },
360
+ required: ['name'],
361
+ },
362
+ handler: async (input) => {
363
+ const src = input;
364
+ const name = String(src.name || '').trim();
365
+ if (!name)
366
+ return { success: false, error: 'name is required (the Claude Code agent name)' };
367
+ const descriptor = {
368
+ name,
369
+ sessionId: src.sessionId ? String(src.sessionId) : undefined,
370
+ agentType: String(src.agentType || 'general-purpose'),
371
+ task: src.task ? String(src.task) : undefined,
372
+ };
373
+ const spawnInput = buildSpawnInput(descriptor, new Date().toISOString());
374
+ // Auto-swarm: registering a Claude Code agent spins up a swarm from the
375
+ // project config (anti-drift defaults if absent) when none is running, and
376
+ // enrolls this agent into it — so bridged agents coordinate instead of
377
+ // floating unattached. registerAgent honors spawnInput.swarmId to join.
378
+ let swarm;
379
+ try {
380
+ const { ensureActiveSwarm } = await import('./swarm-tools.js');
381
+ const cfg = readSwarmConfigDefaults();
382
+ swarm = ensureActiveSwarm({
383
+ topology: cfg.topology || 'hierarchical',
384
+ maxAgents: cfg.maxAgents || 8,
385
+ strategy: cfg.strategy || 'specialized',
386
+ });
387
+ spawnInput.swarmId = swarm.swarmId;
388
+ }
389
+ catch {
390
+ /* swarm optional — the agent still registers in the global store */
391
+ }
392
+ const res = await registerAgent(spawnInput);
393
+ if (!res.success)
394
+ return res;
395
+ return {
396
+ ...res,
397
+ bound: true,
398
+ origin: 'claude-code',
399
+ claudeName: descriptor.name,
400
+ status: 'registered',
401
+ ...(swarm ? { swarm } : {}),
402
+ };
403
+ },
404
+ },
405
+ {
406
+ name: 'agent_bridge_list',
407
+ description: 'List Swarmdo agent records split into Claude-Code-BOUND (each mirrors a real Task/Agent-tool agent) vs NATIVE, with binding detail. Pass `live` (array of current Claude Code agent names) to also get a reconciliation: which live agents are unmirrored (need agent_bridge_register), and which bound records are orphaned (their Claude agent is gone).',
408
+ category: 'agent',
409
+ inputSchema: {
410
+ type: 'object',
411
+ properties: {
412
+ live: {
413
+ type: 'array',
414
+ items: { type: 'string' },
415
+ description: 'Current live Claude Code agent names, for reconciliation',
416
+ },
417
+ },
418
+ },
419
+ handler: async (input) => {
420
+ const all = Object.values(loadAllAgents());
421
+ const summarize = (a) => ({
422
+ agentId: a.agentId,
423
+ agentType: a.agentType,
424
+ status: a.status,
425
+ ...(isBound(a) ? { binding: a.config.binding } : {}),
426
+ });
427
+ const bound = all.filter(isBound).map(summarize);
428
+ const native = all.filter((a) => !isBound(a)).map(summarize);
429
+ const liveRaw = input.live;
430
+ const live = Array.isArray(liveRaw) ? liveRaw.map(String) : undefined;
431
+ const reconciliation = live ? reconcile(all, live) : undefined;
432
+ return {
433
+ total: all.length,
434
+ boundCount: bound.length,
435
+ nativeCount: native.length,
436
+ bound,
437
+ native,
438
+ ...(reconciliation ? { reconciliation } : {}),
439
+ };
440
+ },
441
+ },
442
+ {
443
+ name: 'agent_bridge_prune',
444
+ description: "Reap orphaned Claude-Code-bound records — bound agents whose Claude Code agent is no longer live. Pass the CURRENT live Claude Code agent names as `live`; any bound record NOT in that list is removed from the agent store and from every swarm roster. Native (unbound) Swarmdo agents are never touched. Idempotent — use it to stop stale bindings accumulating across sessions.",
445
+ category: 'agent',
446
+ inputSchema: {
447
+ type: 'object',
448
+ properties: {
449
+ live: {
450
+ type: 'array',
451
+ items: { type: 'string' },
452
+ description: 'Current live Claude Code agent names; bound records not in this list are pruned',
453
+ },
454
+ },
455
+ required: ['live'],
456
+ },
457
+ handler: async (input) => {
458
+ const liveRaw = input.live;
459
+ const live = Array.isArray(liveRaw) ? liveRaw.map(String) : [];
460
+ const all = Object.values(loadAllAgents());
461
+ const toPrune = orphanedAgentIds(all, live);
462
+ if (toPrune.length === 0)
463
+ return { pruned: [], count: 0 };
464
+ // Remove from the canonical agent store (bound records always live here).
465
+ const store = loadAgentStore();
466
+ for (const id of toPrune)
467
+ delete store.agents[id];
468
+ saveAgentStore(store);
469
+ // Remove from any swarm roster so swarm_status doesn't report ghosts.
470
+ try {
471
+ const { loadSwarmStore: _loadSwarmStore, saveSwarmStore: _saveSwarmStore } = await import('./swarm-tools.js');
472
+ const swarmStore = _loadSwarmStore();
473
+ const pruneSet = new Set(toPrune);
474
+ let changed = false;
475
+ for (const swarm of Object.values(swarmStore.swarms)) {
476
+ if (Array.isArray(swarm.agents)) {
477
+ const before = swarm.agents.length;
478
+ swarm.agents = swarm.agents.filter((a) => !pruneSet.has(a));
479
+ if (swarm.agents.length !== before)
480
+ changed = true;
481
+ }
482
+ }
483
+ if (changed)
484
+ _saveSwarmStore(swarmStore);
485
+ }
486
+ catch {
487
+ /* swarm store optional — records already removed from the agent store */
488
+ }
489
+ return { pruned: toPrune, count: toPrune.length };
490
+ },
491
+ },
326
492
  {
327
493
  // ADR-095 G3 — spawn + execute fused. Closes the #1 UX trap from the
328
494
  // 2026-04 audit (@roman-rr): `agent_spawn` looks like it spawns a worker
@@ -32,6 +32,39 @@ interface SwarmStore {
32
32
  }
33
33
  export declare function loadSwarmStore(): SwarmStore;
34
34
  export declare function saveSwarmStore(store: SwarmStore): void;
35
+ export interface CreateSwarmOptions {
36
+ topology?: string;
37
+ maxAgents?: number;
38
+ strategy?: string;
39
+ config?: Record<string, unknown>;
40
+ /**
41
+ * When true, do NOT stamp the host `pid` — the swarm is a persistent logical
42
+ * coordination record, not tied to one process's lifetime, so it survives
43
+ * ephemeral CLI invocations (the agent bridge) instead of being reaped by the
44
+ * #1799 PID-orphan check the instant the creating process exits. Such swarms
45
+ * still expire via the 24h idle-TTL fallback. Daemon-hosted swarms
46
+ * (`swarm_init`) leave this false so a dead host IS reaped promptly.
47
+ */
48
+ persistent?: boolean;
49
+ }
50
+ /**
51
+ * Create + persist a swarm. Extracted from the `swarm_init` handler so other
52
+ * subsystems (the Claude-Code agent bridge) can spin up a swarm from config
53
+ * WITHOUT duplicating the store-write. Throws on an invalid topology. Shared
54
+ * single source of truth — no parallel swarm creation.
55
+ */
56
+ export declare function createSwarm(opts: CreateSwarmOptions): SwarmState;
57
+ /**
58
+ * Return the most-recent RUNNING swarm, or create one from `defaults` if none
59
+ * exists. Used by the agent bridge so registering a Claude Code agent
60
+ * auto-spins-up a swarm (from the project's anti-drift config) and enrolls it,
61
+ * instead of leaving the agent unattached.
62
+ */
63
+ export declare function ensureActiveSwarm(defaults: CreateSwarmOptions): {
64
+ swarmId: string;
65
+ topology: string;
66
+ created: boolean;
67
+ };
35
68
  export declare const swarmTools: MCPTool[];
36
69
  export {};
37
70
  //# sourceMappingURL=swarm-tools.d.ts.map
@@ -109,6 +109,64 @@ export function saveSwarmStore(store) {
109
109
  const VALID_TOPOLOGIES = new Set([
110
110
  'hierarchical', 'mesh', 'hierarchical-mesh', 'ring', 'star', 'hybrid', 'adaptive',
111
111
  ]);
112
+ /**
113
+ * Create + persist a swarm. Extracted from the `swarm_init` handler so other
114
+ * subsystems (the Claude-Code agent bridge) can spin up a swarm from config
115
+ * WITHOUT duplicating the store-write. Throws on an invalid topology. Shared
116
+ * single source of truth — no parallel swarm creation.
117
+ */
118
+ export function createSwarm(opts) {
119
+ const topology = opts.topology || 'hierarchical-mesh';
120
+ const maxAgents = Math.min(Math.max(opts.maxAgents || 15, 1), 50);
121
+ const strategy = opts.strategy || 'specialized';
122
+ const config = opts.config || {};
123
+ if (!VALID_TOPOLOGIES.has(topology)) {
124
+ throw new Error(`Invalid topology: ${topology}. Valid: ${[...VALID_TOPOLOGIES].join(', ')}`);
125
+ }
126
+ const swarmId = `swarm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
127
+ const now = new Date().toISOString();
128
+ const swarmState = {
129
+ swarmId,
130
+ topology,
131
+ maxAgents,
132
+ status: 'running',
133
+ agents: [],
134
+ tasks: [],
135
+ config: {
136
+ topology,
137
+ maxAgents,
138
+ strategy,
139
+ communicationProtocol: config.communicationProtocol || 'message-bus',
140
+ autoScaling: config.autoScaling ?? true,
141
+ consensusMechanism: config.consensusMechanism || 'majority',
142
+ },
143
+ createdAt: now,
144
+ updatedAt: now,
145
+ // Persistent (bridge) swarms omit the pid so they aren't reaped on process
146
+ // exit; daemon-hosted swarms record it for prompt orphan reconciliation.
147
+ ...(opts.persistent ? {} : { pid: process.pid }),
148
+ };
149
+ const store = loadSwarmStore();
150
+ store.swarms[swarmId] = swarmState;
151
+ saveSwarmStore(store);
152
+ return swarmState;
153
+ }
154
+ /**
155
+ * Return the most-recent RUNNING swarm, or create one from `defaults` if none
156
+ * exists. Used by the agent bridge so registering a Claude Code agent
157
+ * auto-spins-up a swarm (from the project's anti-drift config) and enrolls it,
158
+ * instead of leaving the agent unattached.
159
+ */
160
+ export function ensureActiveSwarm(defaults) {
161
+ const store = loadSwarmStore();
162
+ const running = Object.values(store.swarms)
163
+ .filter((s) => s.status === 'running')
164
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];
165
+ if (running)
166
+ return { swarmId: running.swarmId, topology: running.topology, created: false };
167
+ const s = createSwarm({ ...defaults, persistent: true });
168
+ return { swarmId: s.swarmId, topology: s.topology, created: true };
169
+ }
112
170
  export const swarmTools = [
113
171
  {
114
172
  name: 'swarm_init',
@@ -135,49 +193,26 @@ export const swarmTools = [
135
193
  if (!v.valid)
136
194
  return { success: false, error: v.error };
137
195
  }
138
- const topology = input.topology || 'hierarchical-mesh';
139
- const maxAgents = Math.min(Math.max(input.maxAgents || 15, 1), 50);
140
196
  const strategy = input.strategy || 'specialized';
141
- const config = (input.config || {});
142
- if (!VALID_TOPOLOGIES.has(topology)) {
143
- return {
144
- success: false,
145
- error: `Invalid topology: ${topology}. Valid: ${[...VALID_TOPOLOGIES].join(', ')}`,
146
- };
147
- }
148
- const swarmId = `swarm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
149
- const now = new Date().toISOString();
150
- const swarmState = {
151
- swarmId,
152
- topology,
153
- maxAgents,
154
- status: 'running',
155
- agents: [],
156
- tasks: [],
157
- config: {
158
- topology,
159
- maxAgents,
197
+ let swarmState;
198
+ try {
199
+ swarmState = createSwarm({
200
+ topology: input.topology,
201
+ maxAgents: input.maxAgents,
160
202
  strategy,
161
- communicationProtocol: config.communicationProtocol || 'message-bus',
162
- autoScaling: config.autoScaling ?? true,
163
- consensusMechanism: config.consensusMechanism || 'majority',
164
- },
165
- createdAt: now,
166
- updatedAt: now,
167
- // #1799 — record host PID so subsequent loads can detect orphans
168
- // when this process exits without a graceful swarm_shutdown.
169
- pid: process.pid,
170
- };
171
- const store = loadSwarmStore();
172
- store.swarms[swarmId] = swarmState;
173
- saveSwarmStore(store);
203
+ config: (input.config || {}),
204
+ });
205
+ }
206
+ catch (e) {
207
+ return { success: false, error: e.message };
208
+ }
174
209
  return {
175
210
  success: true,
176
- swarmId,
177
- topology,
211
+ swarmId: swarmState.swarmId,
212
+ topology: swarmState.topology,
178
213
  strategy,
179
- maxAgents,
180
- initializedAt: now,
214
+ maxAgents: swarmState.maxAgents,
215
+ initializedAt: swarmState.createdAt,
181
216
  config: swarmState.config,
182
217
  persisted: true,
183
218
  };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * standup.ts — "what did I do?" recall from git history, weekend-aware.
3
+ *
4
+ * Ports the signature behavior of git-standup: list the commits since your last
5
+ * WORKING day, so on Monday you reach back to Friday (not merely yesterday).
6
+ * Power users juggling many repos + AI-agent sessions use it to re-orient at the
7
+ * start of a session and to answer the standup question in one command.
8
+ *
9
+ * Pure + deterministic: the grouping core takes the raw `git log --numstat`
10
+ * dump (control-char delimited, subject included) and a reference Date, and
11
+ * folds it into per-day buckets. No LLM, no network — the git subprocess lives
12
+ * in ../commands/standup.ts, so this module is fully fixture-testable.
13
+ * `resolveRenamePath` is reused from hotspots so renamed files fold correctly.
14
+ *
15
+ * Expected log format (control-char delimited):
16
+ * <SOH>%H<US>%aN<US>%aI<US>%s ← one header line per commit (subject last)
17
+ * <added>\t<deleted>\t<path> ← one numstat line per file ('-' = binary)
18
+ */
19
+ export interface StandupCommit {
20
+ hash: string;
21
+ author: string;
22
+ /** epoch milliseconds, parsed from the ISO author date */
23
+ date: number;
24
+ subject: string;
25
+ /** insertions summed across the commit's files (binary counts as 0) */
26
+ added: number;
27
+ /** deletions summed across the commit's files */
28
+ deleted: number;
29
+ /** count of files touched by the commit */
30
+ files: number;
31
+ }
32
+ export interface DayBucket {
33
+ /** local calendar day, `YYYY-MM-DD` */
34
+ day: string;
35
+ /** commits on this day, newest first */
36
+ commits: StandupCommit[];
37
+ /** day totals */
38
+ added: number;
39
+ deleted: number;
40
+ files: number;
41
+ }
42
+ /**
43
+ * How many days back to the last WORKING day (Mon–Fri), given a day-of-week
44
+ * (0=Sun..6=Sat). Weekend-aware, matching git-standup's signature behavior:
45
+ * Monday (1) → 3 (reach back to Friday)
46
+ * Sunday (0) → 2 (reach back to Friday)
47
+ * any other → 1 (yesterday)
48
+ * Pure integer core — no Date, no timezone. Out-of-range inputs are normalized
49
+ * into 0..6 so callers can pass raw arithmetic safely.
50
+ */
51
+ export declare function daysToLastWorkingDay(dayOfWeek: number): number;
52
+ /**
53
+ * The default weekend-aware window for a reference date. Reads the LOCAL
54
+ * day-of-week — a user's "last working day" is local to them. Pure w.r.t. the
55
+ * injected Date.
56
+ */
57
+ export declare function sinceLastWorkingDay(refDate: Date): {
58
+ sinceDays: number;
59
+ };
60
+ /** Parse the control-char-delimited standup log dump into commits. Pure. */
61
+ export declare function parseStandupLog(raw: string): StandupCommit[];
62
+ /**
63
+ * Local `YYYY-MM-DD` for an epoch-ms instant. Uses LOCAL calendar components so
64
+ * the day matches the user's wall clock; round-trips with local-constructed
65
+ * dates regardless of the runner timezone. Pure.
66
+ */
67
+ export declare function dayKey(ms: number): string;
68
+ /**
69
+ * Fold commits into per-day buckets — newest day first, and newest commit first
70
+ * within a day. Day boundaries are LOCAL. Pure (Array.sort is stable, so
71
+ * same-timestamp commits keep git-log order).
72
+ */
73
+ export declare function groupByDay(commits: StandupCommit[]): DayBucket[];
74
+ /** Weekday name for a `YYYY-MM-DD` day key (local). '' if unparseable. Pure. */
75
+ export declare function weekdayOf(day: string): string;
76
+ /**
77
+ * Human-readable standup: a heading per day (weekday + date + counts) followed
78
+ * by each commit's abbreviated hash and subject, then a totals footer. Pure.
79
+ */
80
+ export declare function formatStandup(buckets: DayBucket[], opts?: {
81
+ abbrev?: number;
82
+ }): string;
83
+ //# sourceMappingURL=standup.d.ts.map