@mmerterden/multi-agent-pipeline 20.35.0 → 20.37.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 (207) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +6 -5
  3. package/README.tr.md +6 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +4 -4
  7. package/install/catalog-history.json +1 -1
  8. package/manifest.json +210 -136
  9. package/package.json +5 -1
  10. package/pipeline/commands/multi-agent/SKILL.md +33 -154
  11. package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
  12. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
  13. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
  14. package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
  15. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
  16. package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
  17. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
  18. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
  19. package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
  20. package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
  21. package/pipeline/commands/multi-agent/office-status/SKILL.md +1 -1
  22. package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
  23. package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
  24. package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
  25. package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
  26. package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
  27. package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
  28. package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
  29. package/pipeline/commands/multi-agent/sync/SKILL.md +38 -167
  30. package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
  31. package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
  32. package/pipeline/contract/CHANGELOG.md +9 -0
  33. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  34. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  35. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  36. package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
  37. package/pipeline/contract/fixtures/runs-empty.json +1 -1
  38. package/pipeline/contract/fixtures/runs-failed.json +1 -1
  39. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
  40. package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
  41. package/pipeline/contract/fixtures/runs-running.json +1 -1
  42. package/pipeline/contract/manifest.json +5 -5
  43. package/pipeline/contract/types/index.d.ts +3 -2
  44. package/pipeline/lib/qr-code.mjs +26 -0
  45. package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
  46. package/pipeline/multi-agent-refs/channels/generator.md +14 -0
  47. package/pipeline/multi-agent-refs/channels/preview.md +13 -0
  48. package/pipeline/multi-agent-refs/channels/reference.md +46 -0
  49. package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
  50. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  51. package/pipeline/multi-agent-refs/design-check/export.md +1 -0
  52. package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
  53. package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
  54. package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
  55. package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
  56. package/pipeline/multi-agent-refs/help/en.md +1 -0
  57. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  58. package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
  59. package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
  60. package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
  61. package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
  62. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
  63. package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
  64. package/pipeline/multi-agent-refs/resume/details.md +115 -2
  65. package/pipeline/multi-agent-refs/review/inputs.md +33 -1
  66. package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
  67. package/pipeline/multi-agent-refs/review/panel.md +75 -0
  68. package/pipeline/multi-agent-refs/review/valven.md +5 -1
  69. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
  70. package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
  71. package/pipeline/multi-agent-refs/stack/background.md +72 -0
  72. package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
  73. package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
  74. package/pipeline/multi-agent-refs/sync/office.md +10 -0
  75. package/pipeline/multi-agent-refs/sync/overview.md +61 -0
  76. package/pipeline/multi-agent-refs/sync/repo.md +91 -0
  77. package/pipeline/multi-agent-refs/update/details.md +83 -0
  78. package/pipeline/schemas/runs-index.schema.json +2 -1
  79. package/pipeline/scripts/contract-server.mjs +1 -1
  80. package/pipeline/scripts/office-pair.mjs +40 -9
  81. package/pipeline/scripts/runs-index.mjs +34 -7
  82. package/pipeline/scripts/status-overview.mjs +401 -0
  83. package/pipeline/skills/.skill-manifest.json +69 -65
  84. package/pipeline/skills/.skills-index.json +15 -4
  85. package/pipeline/skills/shared/README.md +4 -3
  86. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -71
  87. package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
  88. package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
  89. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +11 -73
  90. package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
  91. package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
  92. package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
  93. package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
  94. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
  95. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
  96. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
  97. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
  98. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
  99. package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
  100. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +1 -1
  101. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
  102. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
  103. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
  104. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
  105. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +75 -219
  106. package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
  107. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
  108. package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
  109. package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
  110. package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
  111. package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
  112. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
  113. package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
  114. package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
  115. package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
  116. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
  117. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
  118. package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
  119. package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
  120. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
  121. package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
  122. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
  123. package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
  124. package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
  125. package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
  126. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
  127. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
  128. package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
  129. package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
  130. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
  131. package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
  132. package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
  133. package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
  134. package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
  135. package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
  136. package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
  137. package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
  138. package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
  139. package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
  140. package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
  141. package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
  142. package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
  143. package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
  144. package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
  145. package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
  146. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
  147. package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
  148. package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
  149. package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
  150. package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
  151. package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
  152. package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
  153. package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
  154. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
  155. package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
  156. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
  157. package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
  158. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
  159. package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
  160. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
  161. package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
  162. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
  163. package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
  164. package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
  165. package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
  166. package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
  167. package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
  168. package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
  169. package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
  170. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
  171. package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
  172. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
  173. package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
  174. package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
  175. package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
  176. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
  177. package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
  178. package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
  179. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
  180. package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
  181. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
  182. package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
  183. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
  184. package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
  185. package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
  186. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
  187. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
  188. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
  189. package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
  190. package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
  191. package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
  192. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
  193. package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
  194. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
  195. package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
  196. package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
  197. package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
  198. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
  199. package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
  200. package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
  201. package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
  202. package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
  203. package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
  204. package/pipeline/skills/skills-index.md +2 -1
  205. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
  206. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
  207. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file status-overview.mjs - what is happening on this machine right now,
4
+ * in one answer: the live Claude Code sessions and the run each one drives,
5
+ * the tokens they used, which accounts and credentials are set up, and the
6
+ * run list.
7
+ *
8
+ * Sources, all read on every call:
9
+ *
10
+ * 1. sessions: `claude agents --json` (lib/claude-sessions.mjs), matched to
11
+ * a run by the session id it recorded, else by a working directory inside
12
+ * the run's worktree (activity-snapshot.mjs matchRun);
13
+ * 2. tokens: the `usage` block of every assistant message in the session's
14
+ * transcript and its subagent transcripts, counted once per message id
15
+ * (a message split into several lines repeats its usage). The last-24h
16
+ * figure covers every transcript written in that window. Prices come from
17
+ * cost-table.json; cache writes are priced at the input rate, so the
18
+ * estimate errs low;
19
+ * 3. accounts: the github.com logins gh holds (usage-identity.mjs) and the
20
+ * author git would write in the current directory;
21
+ * 4. credentials: each logical name in prefs.global.keychainMapping is
22
+ * "registered" when its service name is in `credential-store.sh list`.
23
+ * Only service names are read: the store is never asked for a value, and
24
+ * nothing about a value (length, prefix) is printed;
25
+ * 5. runs: runs-index.mjs, the same records /multi-agent:status always used.
26
+ *
27
+ * Usage:
28
+ * status-overview.mjs [--json] [--all]
29
+ *
30
+ * Env: MA_CREDENTIAL_STORE (the store script), MA_PREFS (the preferences
31
+ * file), CLAUDE_PROJECTS_ROOT (the transcript root), MA_AP_AGENTS_JSON (the
32
+ * session list, see claude-sessions.mjs).
33
+ *
34
+ * Exit: 0 printed, 2 usage.
35
+ *
36
+ * @module pipeline/scripts/status-overview
37
+ */
38
+
39
+ import { createReadStream, existsSync, readdirSync, readFileSync, statSync } from "node:fs";
40
+ import { spawnSync } from "node:child_process";
41
+ import { homedir } from "node:os";
42
+ import { basename, join } from "node:path";
43
+ import { createInterface } from "node:readline";
44
+
45
+ import { listSessions } from "../lib/claude-sessions.mjs";
46
+ import { runMain } from "../lib/fatal.mjs";
47
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
48
+ import { costUsd } from "./_cost.mjs";
49
+ import { matchRun } from "./activity-snapshot.mjs";
50
+ import { buildIndex, phaseCell } from "./runs-index.mjs";
51
+ import { listAccounts } from "./usage-identity.mjs";
52
+
53
+ export const CONTRACT_VERSION = "1.0.0";
54
+ export const WINDOW_MS = 24 * 3_600_000;
55
+ const SAFE_SESSION_ID = /^[A-Za-z0-9-]{8,64}$/;
56
+ /**
57
+ * States of a session that is still open. Wider than claude-sessions.mjs
58
+ * LIVE_STATUSES, which asks whether a session is working: an idle or blocked
59
+ * session is not working, but it is open and it is what this overview counts.
60
+ */
61
+ const OPEN = new Set(["busy", "running", "waiting", "idle", "blocked"]);
62
+
63
+ const projectsRoot = (env) => env.CLAUDE_PROJECTS_ROOT || join(homedir(), ".claude", "projects");
64
+ const prefsPath = (env) =>
65
+ env.MA_PREFS || join(homedir(), ".claude", "multi-agent-preferences.json");
66
+ const storePath = (env) =>
67
+ env.MA_CREDENTIAL_STORE || new URL("../lib/credential-store.sh", import.meta.url).pathname;
68
+
69
+ function listDir(dir) {
70
+ try {
71
+ return readdirSync(dir);
72
+ } catch {
73
+ return [];
74
+ }
75
+ }
76
+
77
+ let PRICES = null;
78
+ /** The cost-table rate for a wire model id, by its `modelId`, else by rung name in the id. */
79
+ export function rateFor(model) {
80
+ if (!PRICES) {
81
+ try {
82
+ PRICES = JSON.parse(
83
+ readFileSync(new URL("./cost-table.json", import.meta.url), "utf8"),
84
+ ).prices;
85
+ } catch {
86
+ PRICES = {};
87
+ }
88
+ }
89
+ if (typeof model !== "string" || !model) return null;
90
+ for (const rate of Object.values(PRICES)) if (rate?.modelId === model) return rate;
91
+ for (const [rung, rate] of Object.entries(PRICES)) {
92
+ if (/^[a-z]+$/.test(rung) && model.includes(`-${rung}-`)) return rate;
93
+ }
94
+ return null;
95
+ }
96
+
97
+ const emptyUsage = () => ({ in: 0, out: 0, cacheRead: 0, cacheWrite: 0, usd: 0, unpriced: 0 });
98
+
99
+ function addUsage(acc, model, u) {
100
+ const tin = Number(u.input_tokens) || 0;
101
+ const tout = Number(u.output_tokens) || 0;
102
+ const read = Number(u.cache_read_input_tokens) || 0;
103
+ const write = Number(u.cache_creation_input_tokens) || 0;
104
+ acc.in += tin;
105
+ acc.out += tout;
106
+ acc.cacheRead += read;
107
+ acc.cacheWrite += write;
108
+ const usd = costUsd(rateFor(model), tin + write, tout, read);
109
+ if (usd === null) acc.unpriced++;
110
+ else acc.usd += usd;
111
+ }
112
+
113
+ /**
114
+ * Token usage in one transcript: the whole file, and the part at or after
115
+ * `since`. Each message id counts once in the file; `windowSeen`, shared
116
+ * across files, keeps a message a resumed or forked session copied into a
117
+ * second transcript from counting twice in the window. A file that cannot be
118
+ * read adds nothing and comes back `readable: false`.
119
+ *
120
+ * @returns {Promise<{total: object, window: object, readable: boolean}>}
121
+ */
122
+ export async function transcriptUsage(path, since = 0, windowSeen = new Set()) {
123
+ const total = emptyUsage();
124
+ const window = emptyUsage();
125
+ const seen = new Set();
126
+ try {
127
+ await scan(path, (rec, msg, id) => {
128
+ if (id) {
129
+ if (seen.has(id)) return;
130
+ seen.add(id);
131
+ }
132
+ addUsage(total, msg.model, msg.usage);
133
+ const t = Date.parse(rec.timestamp);
134
+ if (!Number.isFinite(t) || t < since) return;
135
+ if (id) {
136
+ if (windowSeen.has(id)) return;
137
+ windowSeen.add(id);
138
+ }
139
+ addUsage(window, msg.model, msg.usage);
140
+ });
141
+ } catch {
142
+ return { total: emptyUsage(), window: emptyUsage(), readable: false };
143
+ }
144
+ return { total, window, readable: true };
145
+ }
146
+
147
+ async function scan(path, visit) {
148
+ const lines = createInterface({ input: createReadStream(path), crlfDelay: Infinity });
149
+ for await (const line of lines) {
150
+ if (!line.includes('"usage"')) continue;
151
+ let rec;
152
+ try {
153
+ rec = JSON.parse(line);
154
+ } catch {
155
+ continue;
156
+ }
157
+ const msg = rec?.message;
158
+ if (rec?.type !== "assistant" || !msg?.usage || typeof msg.usage !== "object") continue;
159
+ // The host writes its own notices as "<synthetic>" messages; no model ran.
160
+ if (msg.model === "<synthetic>") continue;
161
+ visit(rec, msg, typeof msg.id === "string" ? msg.id : null);
162
+ }
163
+ }
164
+
165
+ function merge(a, b) {
166
+ for (const k of Object.keys(a)) a[k] += b[k];
167
+ return a;
168
+ }
169
+
170
+ /** The session transcript and its subagent transcripts, or [] when the id is unsafe or absent. */
171
+ export function sessionFiles(root, sessionId) {
172
+ if (typeof sessionId !== "string" || !SAFE_SESSION_ID.test(sessionId)) return [];
173
+ for (const slug of listDir(root)) {
174
+ const main = join(root, slug, `${sessionId}.jsonl`);
175
+ if (!existsSync(main)) continue;
176
+ const sub = join(root, slug, sessionId, "subagents");
177
+ return [
178
+ main,
179
+ ...listDir(sub)
180
+ .filter((f) => f.endsWith(".jsonl"))
181
+ .map((f) => join(sub, f)),
182
+ ];
183
+ }
184
+ return [];
185
+ }
186
+
187
+ /** Every transcript under the root written at or after `since`. */
188
+ function recentFiles(root, since) {
189
+ const out = [];
190
+ const visit = (dir, depth) => {
191
+ for (const name of listDir(dir)) {
192
+ const p = join(dir, name);
193
+ let st;
194
+ try {
195
+ st = statSync(p);
196
+ } catch {
197
+ continue;
198
+ }
199
+ if (st.isDirectory() && depth < 3) visit(p, depth + 1);
200
+ else if (st.isFile() && name.endsWith(".jsonl") && st.mtimeMs >= since) out.push(p);
201
+ }
202
+ };
203
+ visit(root, 0);
204
+ return out;
205
+ }
206
+
207
+ /** Logical credential names and whether each is registered; values are never read. */
208
+ export function credentialStatus({ env = process.env } = {}) {
209
+ let mapping;
210
+ try {
211
+ mapping = JSON.parse(readFileSync(prefsPath(env), "utf8"))?.global?.keychainMapping ?? {};
212
+ } catch {
213
+ return { readable: false, items: [] };
214
+ }
215
+ const listed = spawnSync("bash", [storePath(env), "list"], {
216
+ encoding: "utf8",
217
+ timeout: 15000,
218
+ env,
219
+ });
220
+ if (listed.status !== 0) return { readable: false, items: [] };
221
+ const services = new Set(String(listed.stdout).split("\n").filter(Boolean));
222
+ const items = Object.entries(mapping)
223
+ .filter(([k, v]) => typeof k === "string" && typeof v === "string" && v)
224
+ .map(([name, service]) => ({ name, registered: services.has(service) }))
225
+ .sort((a, b) => a.name.localeCompare(b.name));
226
+ return { readable: true, items };
227
+ }
228
+
229
+ /** The author git would write in the current directory. */
230
+ function gitIdentity(env) {
231
+ const read = (key) => {
232
+ const r = spawnSync("git", ["config", "--get", key], { encoding: "utf8", timeout: 5000, env });
233
+ return r.status === 0 ? r.stdout.trim() || null : null;
234
+ };
235
+ const name = read("user.name");
236
+ return name ? { name, email: read("user.email") } : null;
237
+ }
238
+
239
+ /**
240
+ * The whole overview as one document.
241
+ *
242
+ * @param {{env?: object, now?: number, all?: boolean}} [opts]
243
+ */
244
+ export async function statusOverview({ env = process.env, now = Date.now(), all = false } = {}) {
245
+ const root = projectsRoot(env);
246
+ const since = now - WINDOW_MS;
247
+ const runs = buildIndex();
248
+ const rows = listSessions(env);
249
+
250
+ // One read per transcript: a live session's file is usually also one of the
251
+ // files written in the window.
252
+ const open = (Array.isArray(rows) ? rows : []).filter((s) => {
253
+ const state = s?.status ?? s?.state ?? null;
254
+ return !state || OPEN.has(state);
255
+ });
256
+ const files = new Map();
257
+ for (const s of open) for (const f of sessionFiles(root, s?.sessionId)) files.set(f, null);
258
+ for (const f of recentFiles(root, since)) files.set(f, null);
259
+ const windowSeen = new Set();
260
+ const last24h = emptyUsage();
261
+ let unreadable = 0;
262
+ for (const f of files.keys()) {
263
+ const u = await transcriptUsage(f, since, windowSeen);
264
+ if (!u.readable) unreadable++;
265
+ files.set(f, u);
266
+ merge(last24h, u.window);
267
+ }
268
+
269
+ const sessions = [];
270
+ for (const s of open) {
271
+ const state = s?.status ?? s?.state ?? null;
272
+ const { run, matchedBy } = matchRun(s?.sessionId, s?.cwd ?? null, null, runs);
273
+ const usage = emptyUsage();
274
+ for (const f of sessionFiles(root, s?.sessionId))
275
+ merge(usage, files.get(f)?.total ?? emptyUsage());
276
+ sessions.push({
277
+ sessionId: typeof s?.sessionId === "string" ? s.sessionId : null,
278
+ kind: typeof s?.kind === "string" ? s.kind : null,
279
+ state,
280
+ project: s?.cwd ? basename(s.cwd) : null,
281
+ startedAt: s?.startedAt ?? null,
282
+ run: run ? { taskId: run.taskId, currentPhase: run.currentPhase, status: run.status } : null,
283
+ matchedBy,
284
+ usage,
285
+ });
286
+ }
287
+
288
+ const accounts = listAccounts({ env });
289
+ return {
290
+ contractVersion: CONTRACT_VERSION,
291
+ generatedAt: new Date(now).toISOString(),
292
+ sessions,
293
+ sessionListReadable: Array.isArray(rows),
294
+ usage: {
295
+ liveSessions: sessions.reduce((acc, s) => merge(acc, { ...s.usage }), emptyUsage()),
296
+ last24h,
297
+ unreadableTranscripts: unreadable,
298
+ },
299
+ accounts: {
300
+ github: accounts.map((a) => ({ login: a.login, active: a.active })),
301
+ git: gitIdentity(env),
302
+ },
303
+ credentials: credentialStatus({ env }),
304
+ runs: all ? runs : runs.filter((r) => r.group !== "done" && r.group !== "unknown"),
305
+ runCounts: runs.reduce((acc, r) => ({ ...acc, [r.group]: (acc[r.group] ?? 0) + 1 }), {}),
306
+ };
307
+ }
308
+
309
+ const n = (x) =>
310
+ x >= 1e9
311
+ ? `${(x / 1e9).toFixed(1)}B`
312
+ : x >= 1e6
313
+ ? `${(x / 1e6).toFixed(1)}M`
314
+ : x >= 1e3
315
+ ? `${(x / 1e3).toFixed(1)}k`
316
+ : String(x);
317
+ const usageLine = (u) =>
318
+ `in ${n(u.in)} out ${n(u.out)} cache read ${n(u.cacheRead)} cache write ${n(u.cacheWrite)} ~$${u.usd.toFixed(2)}` +
319
+ (u.unpriced ? ` (${u.unpriced} unpriced)` : "");
320
+
321
+ export function renderHuman(doc) {
322
+ const out = [];
323
+ const busy = doc.sessions.filter((s) => s.state === "busy" || s.state === "running").length;
324
+ out.push(`Live sessions (${doc.sessions.length}, ${busy} busy)`);
325
+ if (!doc.sessionListReadable)
326
+ out.push(" the session list could not be read (claude agents --json)");
327
+ for (const s of doc.sessions) {
328
+ const run = s.run ? `${s.run.taskId} ${phaseCell(s.run.currentPhase)}` : "no run";
329
+ out.push(
330
+ ` ${(s.sessionId ?? "-").slice(0, 8)} ${(s.kind ?? "-").padEnd(11)} ${(s.state ?? "-").padEnd(7)} ` +
331
+ `${(s.project ?? "-").padEnd(24).slice(0, 24)} ${run.padEnd(22)} ~$${s.usage.usd.toFixed(2)}`,
332
+ );
333
+ }
334
+ out.push("");
335
+ out.push("Tokens");
336
+ out.push(` live sessions ${usageLine(doc.usage.liveSessions)}`);
337
+ out.push(` last 24h ${usageLine(doc.usage.last24h)}`);
338
+ if (doc.usage.unreadableTranscripts)
339
+ out.push(
340
+ ` ${doc.usage.unreadableTranscripts} transcript(s) could not be read; the totals leave them out`,
341
+ );
342
+ out.push("");
343
+ out.push("Accounts");
344
+ const gh = doc.accounts.github;
345
+ out.push(
346
+ ` github ${gh.length ? gh.map((a) => (a.active ? `${a.login} (active)` : a.login)).join(", ") : "none logged in"}`,
347
+ );
348
+ if (doc.accounts.git)
349
+ out.push(` git ${doc.accounts.git.name} <${doc.accounts.git.email ?? "-"}>`);
350
+ out.push("");
351
+ const c = doc.credentials;
352
+ if (!c.readable) out.push("Credentials could not be read");
353
+ else {
354
+ const missing = c.items.filter((i) => !i.registered).map((i) => i.name);
355
+ out.push(`Credentials ${c.items.length - missing.length}/${c.items.length} registered`);
356
+ out.push(
357
+ ` ${
358
+ c.items
359
+ .filter((i) => i.registered)
360
+ .map((i) => i.name)
361
+ .join(", ") || "-"
362
+ }`,
363
+ );
364
+ if (missing.length) out.push(` missing: ${missing.join(", ")}`);
365
+ }
366
+ out.push("");
367
+ const counts = Object.entries(doc.runCounts)
368
+ .map(([g, k]) => `${g} ${k}`)
369
+ .join(", ");
370
+ out.push(`Runs (${counts || "none"}) - runs-index.mjs lists them`);
371
+ const driven = new Set(doc.sessions.map((x) => x.run?.taskId).filter(Boolean));
372
+ for (const r of doc.runs.filter((x) => x.group === "waiting" || driven.has(x.taskId))) {
373
+ out.push(
374
+ ` ${r.taskId.padEnd(30).slice(0, 30)} ${phaseCell(r.currentPhase).padStart(5)} ${r.group}`,
375
+ );
376
+ }
377
+ return out.join("\n");
378
+ }
379
+
380
+ async function main() {
381
+ const args = process.argv.slice(2);
382
+ const flags = { json: false, all: false };
383
+ for (const a of args) {
384
+ if (a === "--json") flags.json = true;
385
+ else if (a === "--all") flags.all = true;
386
+ else if (a === "-h" || a === "--help") {
387
+ process.stdout.write("Usage: status-overview.mjs [--json] [--all]\n");
388
+ return 0;
389
+ } else {
390
+ process.stderr.write(`status-overview: unknown argument ${a}\n`);
391
+ return 2;
392
+ }
393
+ }
394
+ const doc = await statusOverview({ all: flags.all });
395
+ process.stdout.write((flags.json ? JSON.stringify(doc, null, 2) : renderHuman(doc)) + "\n");
396
+ return 0;
397
+ }
398
+
399
+ if (invokedDirectly(import.meta.url)) {
400
+ runMain("status-overview", main);
401
+ }