@gempack/squad-mcp 0.8.2 → 0.10.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 (39) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +7 -4
  3. package/CHANGELOG.md +92 -0
  4. package/README.md +41 -35
  5. package/agents/senior-debugger.md +85 -0
  6. package/commands/debug.md +22 -0
  7. package/commands/stats.md +22 -0
  8. package/dist/config/ownership-matrix.d.ts +1 -1
  9. package/dist/config/ownership-matrix.js +16 -0
  10. package/dist/config/ownership-matrix.js.map +1 -1
  11. package/dist/errors.d.ts +1 -1
  12. package/dist/errors.js.map +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/resources/agent-loader.js +1 -0
  16. package/dist/resources/agent-loader.js.map +1 -1
  17. package/dist/runs/aggregate.d.ts +166 -0
  18. package/dist/runs/aggregate.js +378 -0
  19. package/dist/runs/aggregate.js.map +1 -0
  20. package/dist/runs/store.d.ts +328 -0
  21. package/dist/runs/store.js +406 -0
  22. package/dist/runs/store.js.map +1 -0
  23. package/dist/tools/list-runs.d.ts +52 -0
  24. package/dist/tools/list-runs.js +142 -0
  25. package/dist/tools/list-runs.js.map +1 -0
  26. package/dist/tools/record-run.d.ts +202 -0
  27. package/dist/tools/record-run.js +124 -0
  28. package/dist/tools/record-run.js.map +1 -0
  29. package/dist/tools/registry.js +15 -1
  30. package/dist/tools/registry.js.map +1 -1
  31. package/dist/util/path-safety.d.ts +36 -0
  32. package/dist/util/path-safety.js +76 -0
  33. package/dist/util/path-safety.js.map +1 -1
  34. package/package.json +1 -1
  35. package/skills/brainstorm/SKILL.md +70 -7
  36. package/skills/debug/SKILL.md +345 -0
  37. package/skills/question/SKILL.md +73 -1
  38. package/skills/squad/SKILL.md +83 -0
  39. package/skills/stats/SKILL.md +189 -0
@@ -0,0 +1,406 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { AGENT_NAMES_TUPLE } from "../config/ownership-matrix.js";
5
+ import { SquadError } from "../errors.js";
6
+ import { ensureRelativeInsideRoot } from "../util/path-safety.js";
7
+ import { withFileLock } from "../util/file-lock.js";
8
+ import { logger } from "../observability/logger.js";
9
+ import { SafeString } from "../tools/_shared/schemas.js";
10
+ /**
11
+ * SQUAD RUNS STORE — telemetry journal for skill invocations. As of v0.10.0
12
+ * the legitimate writers are the squad skill (`/squad:implement` and
13
+ * `/squad:review`, invocations `implement | review | task`) and the debug
14
+ * skill (`/squad:debug`, invocation `debug`). Each writer follows the same
15
+ * two-phase contract: one row at start (`in_flight`) and one at end
16
+ * (`completed | aborted`), paired by id. Mirrored line-for-line after
17
+ * `src/learning/store.ts` — same lock + quarantine + mtime cache + atomic-
18
+ * append-under-PIPE_BUF discipline.
19
+ *
20
+ * Plan v4 (cycle 2 advisory consensus) explicit decisions:
21
+ *
22
+ * - NO multi-row partial fallback. If `JSON.stringify(record)` exceeds
23
+ * MAX_RECORD_BYTES the store rejects with `RECORD_TOO_LARGE` rather
24
+ * than splitting into continuation rows. Five advisors converged on
25
+ * "splitting erodes the one-row-per-record JSONL invariant and
26
+ * reopens parsing ambiguities"; rejection puts the burden on the
27
+ * caller to cap their `mode_warning.message` (already capped 512B)
28
+ * or shorten their inputs.
29
+ *
30
+ * - File mode 0o600 (user-only), directory mode 0o700. The journal
31
+ * contains commit refs and prompt-length signals that can leak
32
+ * business context (branch names like `feat/acme-acquisition`); on
33
+ * shared workstations world-readable 0o644 would expose them to
34
+ * co-tenants.
35
+ *
36
+ * - Single-writer contract: the squad skill (`skills/squad/SKILL.md`)
37
+ * AND the debug skill (`skills/debug/SKILL.md`) are the only legitimate
38
+ * callers of `appendRun`. `apply_consolidation_rules` and other server-
39
+ * side code MUST NOT emit terminal rows; doing so breaks the two-phase
40
+ * `(in_flight, completed)` pair-by-id invariant.
41
+ */
42
+ /**
43
+ * Hard cap per JSONL entry so a single line fits in POSIX PIPE_BUF
44
+ * (4096 bytes) and `fs.appendFile` remains atomic w.r.t. concurrent
45
+ * appenders. Length includes serialised JSON + trailing newline.
46
+ *
47
+ * Realistic finalization row with 9 agents + capped mode_warning.message
48
+ * lands around 1.5-2 KB — well under the limit. Oversize is a hard error,
49
+ * not a soft truncation (see RECORD_TOO_LARGE in errors.ts).
50
+ */
51
+ export const MAX_RECORD_BYTES = 4_000;
52
+ /**
53
+ * Default location for the JSONL file, relative to workspace_root.
54
+ * Defaults are gitignored at the v0.9.0 release — the journal contains
55
+ * local-only operational telemetry; users opting into team-wide sharing
56
+ * remove `.squad/runs.jsonl` from their `.gitignore` deliberately.
57
+ */
58
+ export const DEFAULT_RUNS_PATH = ".squad/runs.jsonl";
59
+ /**
60
+ * Severity tally compacted into a single sortable number. The cycle-1
61
+ * design carried `{ Blocker, Major, Minor, Suggestion }` per agent
62
+ * (~30 bytes / agent of JSON overhead); cycle 2 architects + dev flagged
63
+ * this as PIPE_BUF-budget waste on 9-agent runs. Collapsed to one number
64
+ * with positional digits: B*1000 + M*100 + m*10 + s. Inverse decode in
65
+ * aggregate.ts. Safe up to 9 of each severity per agent (more than that
66
+ * is itself a signal something went sideways).
67
+ */
68
+ function severityScore(counts) {
69
+ return counts.Blocker * 1000 + counts.Major * 100 + counts.Minor * 10 + counts.Suggestion;
70
+ }
71
+ /** Inverse of `severityScore`. Used by aggregate.ts. */
72
+ export function decodeSeverityScore(n) {
73
+ return {
74
+ Blocker: Math.floor(n / 1000),
75
+ Major: Math.floor((n % 1000) / 100),
76
+ Minor: Math.floor((n % 100) / 10),
77
+ Suggestion: n % 10,
78
+ };
79
+ }
80
+ /** Public re-export so callers can build records without re-implementing the encoding. */
81
+ export { severityScore };
82
+ /**
83
+ * Strip C0 (`\x00`-`\x1F` except `\t`), C1 (`\x7F`-`\x9F`), and ESC (`\x1B`)
84
+ * from a string before it lands on disk. This is the WRITER-side mirror of
85
+ * the renderer's `aggregate.stripControlChars` (security #5).
86
+ *
87
+ * Defense in depth: render-time sanitisation protects users running
88
+ * `/squad:stats`; writer-side sanitisation also protects users running
89
+ * `cat .squad/runs.jsonl` or any other tool that bypasses the aggregator.
90
+ * The regex is intentionally duplicated (3 lines, no runtime cost) rather
91
+ * than importing from `aggregate.ts` because that would invert the natural
92
+ * dep direction (store is foundational; aggregate consumes store).
93
+ */
94
+ function stripWriterControlChars(s) {
95
+ return s.replace(/[\x00-\x08\x0A-\x1F\x7F-\x9F]/g, "");
96
+ }
97
+ /**
98
+ * Canonical tuple of accepted journal invocations. Single source of truth.
99
+ *
100
+ * Why a tuple, not just a Zod enum: the same set is consumed by FIVE call
101
+ * sites — this store's `InvocationEnum`, the tool boundary at
102
+ * `src/tools/record-run.ts`, the filter schema at `src/tools/list-runs.ts`,
103
+ * the Record literal in the aggregate output type, and the `invocation_counts`
104
+ * initialiser in `src/runs/aggregate.ts`. Exporting one tuple makes "add a
105
+ * new invocation" a single-line change instead of five-sites-must-stay-in-
106
+ * sync. Pattern parallels `AGENT_NAMES_TUPLE` in `src/config/ownership-matrix.ts`.
107
+ *
108
+ * `as const` (readonly tuple) is what Zod's `z.enum` requires.
109
+ */
110
+ export const INVOCATION_VALUES = [
111
+ "implement",
112
+ "review",
113
+ "task",
114
+ "question",
115
+ "brainstorm",
116
+ "debug",
117
+ ];
118
+ const InvocationEnum = z.enum(INVOCATION_VALUES);
119
+ const ModeEnum = z.enum(["quick", "normal", "deep"]);
120
+ const ModeSourceEnum = z.enum(["user", "auto"]);
121
+ const StatusEnum = z.enum(["in_flight", "completed", "aborted"]);
122
+ const WorkTypeEnum = z.enum([
123
+ "Feature",
124
+ "Bug Fix",
125
+ "Refactor",
126
+ "Performance",
127
+ "Security",
128
+ "Business Rule",
129
+ ]);
130
+ const VerdictEnum = z.enum(["APPROVED", "CHANGES_REQUIRED", "REJECTED"]);
131
+ const ModelEnum = z.enum(["haiku", "sonnet", "opus", "inherit"]);
132
+ const GitRefSchema = z
133
+ .object({
134
+ kind: z.enum(["head", "diff_base", "pr_head"]),
135
+ value: SafeString(200),
136
+ })
137
+ .nullable();
138
+ /**
139
+ * Per-agent dispatch metrics captured by the squad skill orchestrator.
140
+ *
141
+ * - `batch_duration_ms` (renamed from `duration_ms` in v0.9.0): wall-clock
142
+ * from this agent's Task() dispatch to its result. Note that advisors in
143
+ * a parallel batch overlap; this is "round-trip latency for this dispatch"
144
+ * not "exclusive time spent on this agent's work". Reflected in the
145
+ * /squad:stats output label.
146
+ *
147
+ * - `prompt_chars` / `response_chars` (renamed from input/output_chars in
148
+ * v0.9.0): orchestrator-visible character counts of the dispatch prompt
149
+ * and the agent's returned string. EXCLUDES the agent's own internal
150
+ * tool_use roundtrips (file reads, sub-dispatches like code-explorer).
151
+ * For agents that read heavily, the recorded chars are a substantial
152
+ * underestimate — documented in `est_tokens_method` and rendered in the
153
+ * stats panel disclaimer.
154
+ *
155
+ * - `severity_score`: encoded findings tally (see severityScore()).
156
+ */
157
+ const AgentMetricsSchema = z.object({
158
+ name: z.enum(AGENT_NAMES_TUPLE),
159
+ model: ModelEnum,
160
+ score: z.number().int().min(0).max(100).nullable(),
161
+ severity_score: z.number().int().min(0).max(9999).nullable(),
162
+ batch_duration_ms: z.number().int().nonnegative().finite(),
163
+ prompt_chars: z.number().int().nonnegative().finite(),
164
+ response_chars: z.number().int().nonnegative().finite(),
165
+ });
166
+ /**
167
+ * RunRecord schema_version 1. PUBLIC STABLE CONTRACT from v0.9.0 — readers
168
+ * (the `list_runs` MCP tool, the `/squad:stats` skill) key on
169
+ * `schema_version` and quarantine unknown versions rather than failing.
170
+ *
171
+ * Discriminated by `status`:
172
+ * - `in_flight` rows carry only the Phase-1-known fields (skill knows what
173
+ * it's about to do; verdict/scores are still pending).
174
+ * - `completed | aborted` rows carry full metrics + verdict.
175
+ *
176
+ * For ergonomics under Zod we keep finalisation fields optional on the base
177
+ * schema rather than splitting into two schemas; the writer validates the
178
+ * appropriate subset at the call site (`appendRun` vs `finalizeRun`).
179
+ */
180
+ const runRecordSchema = z.object({
181
+ schema_version: z.literal(1),
182
+ id: SafeString(40),
183
+ status: StatusEnum,
184
+ started_at: SafeString(40),
185
+ completed_at: SafeString(40).optional(),
186
+ duration_ms: z.number().int().nonnegative().finite().optional(),
187
+ invocation: InvocationEnum,
188
+ mode: ModeEnum,
189
+ mode_source: ModeSourceEnum,
190
+ work_type: WorkTypeEnum.optional(),
191
+ git_ref: GitRefSchema,
192
+ files_count: z.number().int().nonnegative().finite(),
193
+ agents: z.array(AgentMetricsSchema).max(20),
194
+ verdict: VerdictEnum.nullable().optional(),
195
+ weighted_score: z.number().min(0).max(100).nullable().optional(),
196
+ est_tokens_method: z.literal("chars-div-3.5"),
197
+ mode_warning: z
198
+ .object({
199
+ code: SafeString(64),
200
+ message: SafeString(512),
201
+ })
202
+ .nullable()
203
+ .optional(),
204
+ });
205
+ export { runRecordSchema, WorkTypeEnum };
206
+ const cache = new Map();
207
+ /** Test-only: clear the per-process cache. Production code MUST NOT call this. */
208
+ export function __resetRunsStoreCacheForTests() {
209
+ cache.clear();
210
+ }
211
+ function resolveRunsFile(workspaceRoot, configuredPath) {
212
+ const rel = configuredPath ?? DEFAULT_RUNS_PATH;
213
+ if (configuredPath !== undefined) {
214
+ ensureRelativeInsideRoot(workspaceRoot, rel, "runs.path");
215
+ }
216
+ return path.resolve(workspaceRoot, rel);
217
+ }
218
+ /**
219
+ * Generate a fresh run id. Date.now() base36 prefix + 6 chars from
220
+ * [a-z0-9] (36^6 = 2.18B unique values per millisecond — collision
221
+ * chance is effectively zero across realistic concurrent writers in
222
+ * the same ms).
223
+ */
224
+ export function generateRunId() {
225
+ const ts = Date.now().toString(36);
226
+ let suffix = "";
227
+ const ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
228
+ for (let i = 0; i < 6; i++) {
229
+ suffix += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];
230
+ }
231
+ return `${ts}-${suffix}`;
232
+ }
233
+ /**
234
+ * Read all run records from the JSONL file. Returns `[]` if the file does
235
+ * not exist (fresh repo, first run). Corrupt lines are quarantined to a
236
+ * timestamped sibling file and logged once; the surviving entries return
237
+ * in append order.
238
+ *
239
+ * Unknown `schema_version` rows are quarantined too — readers must NEVER
240
+ * silently include rows they don't understand. The quarantine file is
241
+ * `.squad/runs.jsonl.corrupt-<ts>.jsonl` alongside the source.
242
+ */
243
+ export async function readRuns(workspaceRoot, options = {}) {
244
+ const filePath = resolveRunsFile(workspaceRoot, options.configuredPath);
245
+ const absRoot = path.resolve(workspaceRoot);
246
+ let stat;
247
+ try {
248
+ stat = await fs.stat(filePath);
249
+ }
250
+ catch {
251
+ return [];
252
+ }
253
+ if (!stat.isFile())
254
+ return [];
255
+ const cached = cache.get(absRoot);
256
+ if (cached &&
257
+ cached.filePath === filePath &&
258
+ cached.mtimeMs === stat.mtimeMs &&
259
+ cached.size === stat.size) {
260
+ return cached.entries;
261
+ }
262
+ let raw;
263
+ try {
264
+ raw = await fs.readFile(filePath, "utf8");
265
+ }
266
+ catch (err) {
267
+ throw new SquadError("CONFIG_READ_FAILED", `failed to read runs file ${filePath}: ${err.message}`, { source: filePath });
268
+ }
269
+ const lines = raw.split(/\r?\n/);
270
+ const entries = [];
271
+ const corruptLines = [];
272
+ let skippedUnknownVersion = 0;
273
+ let lineNo = 0;
274
+ for (const line of lines) {
275
+ lineNo++;
276
+ const trimmed = line.trim();
277
+ if (trimmed === "")
278
+ continue;
279
+ let parsed;
280
+ try {
281
+ parsed = JSON.parse(trimmed);
282
+ }
283
+ catch (err) {
284
+ corruptLines.push({
285
+ line: lineNo,
286
+ raw: trimmed,
287
+ reason: `invalid JSON: ${err.message}`,
288
+ });
289
+ continue;
290
+ }
291
+ // Schema_version gate: skip+log instead of throwing. A future v2 writer
292
+ // would otherwise brick v1 readers; this lets a heterogeneous-version
293
+ // journal be partially read by older clients (architect A-6 + dev #11).
294
+ if (typeof parsed === "object" &&
295
+ parsed !== null &&
296
+ "schema_version" in parsed &&
297
+ parsed.schema_version !== 1) {
298
+ skippedUnknownVersion++;
299
+ continue;
300
+ }
301
+ const validated = runRecordSchema.safeParse(parsed);
302
+ if (!validated.success) {
303
+ corruptLines.push({
304
+ line: lineNo,
305
+ raw: trimmed,
306
+ reason: `schema violation: ${validated.error.message}`,
307
+ });
308
+ continue;
309
+ }
310
+ entries.push(validated.data);
311
+ }
312
+ if (skippedUnknownVersion > 0) {
313
+ logger.warn("runs: skipped rows with unknown schema_version", {
314
+ details: { file: filePath, count: skippedUnknownVersion },
315
+ });
316
+ }
317
+ if (corruptLines.length > 0) {
318
+ const quarantinePath = `${filePath}.corrupt-${Date.now()}.jsonl`;
319
+ try {
320
+ const body = corruptLines.map((c) => `# line ${c.line}: ${c.reason}\n${c.raw}\n`).join("");
321
+ // Write quarantine with same restricted mode as the source.
322
+ await fs.writeFile(quarantinePath, body, { encoding: "utf8", mode: 0o600 });
323
+ }
324
+ catch {
325
+ // Diagnostic, not load-bearing.
326
+ }
327
+ logger.warn("runs: corrupt lines quarantined", {
328
+ details: {
329
+ file: filePath,
330
+ quarantine: quarantinePath,
331
+ count: corruptLines.length,
332
+ lines: corruptLines.map((c) => c.line),
333
+ },
334
+ });
335
+ }
336
+ cache.set(absRoot, { mtimeMs: stat.mtimeMs, size: stat.size, filePath, entries });
337
+ return entries;
338
+ }
339
+ /**
340
+ * Append one RunRecord. Validates against Zod, then enforces
341
+ * MAX_RECORD_BYTES (post-serialisation) before acquiring the file lock.
342
+ * Oversize records throw `RECORD_TOO_LARGE` — no silent split, no soft
343
+ * truncation. The caller (the squad skill) is responsible for keeping
344
+ * `mode_warning.message` capped and the agent list short enough that
345
+ * realistic records stay well under the cap.
346
+ */
347
+ export async function appendRun(workspaceRoot, record, options = {}) {
348
+ const validated = runRecordSchema.safeParse(record);
349
+ if (!validated.success) {
350
+ throw new SquadError("INVALID_INPUT", `run record schema violation: ${validated.error.message}`, { issues: validated.error.issues.length });
351
+ }
352
+ // Defense in depth: strip control chars from `mode_warning.message` at the
353
+ // writer too. The aggregator's `stripControlChars` already runs at render
354
+ // (security #5 in v0.9.0), but direct file inspection (`cat`, `less`) sees
355
+ // raw bytes. Sanitising here keeps the recorded data terminal-safe for any
356
+ // future viewer that bypasses the aggregator. We strip only this one field
357
+ // because it's the documented free-form leak surface — other fields are
358
+ // schema-bounded to safer shapes (enums, ISO strings, numbers, sha refs).
359
+ const sanitized = validated.data.mode_warning != null
360
+ ? {
361
+ ...validated.data,
362
+ mode_warning: {
363
+ ...validated.data.mode_warning,
364
+ message: stripWriterControlChars(validated.data.mode_warning.message),
365
+ },
366
+ }
367
+ : validated.data;
368
+ const line = JSON.stringify(sanitized) + "\n";
369
+ const byteLen = Buffer.byteLength(line, "utf8");
370
+ if (byteLen > MAX_RECORD_BYTES) {
371
+ throw new SquadError("RECORD_TOO_LARGE", `run record exceeds MAX_RECORD_BYTES (${byteLen} > ${MAX_RECORD_BYTES}); ` +
372
+ `cap mode_warning.message or shorten inputs`, { byteLen, max: MAX_RECORD_BYTES, id: validated.data.id });
373
+ }
374
+ const filePath = resolveRunsFile(workspaceRoot, options.configuredPath);
375
+ const dir = path.dirname(filePath);
376
+ // Directory mode 0o700 — user-only. Subsequent runs inherit the existing
377
+ // mode if the dir already exists (mkdir recursive is idempotent on mode).
378
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
379
+ // Cross-process lock around the append. The lock file lives in the same
380
+ // directory; file-lock.ts cleans it up in a finally.
381
+ await withFileLock(filePath, async () => {
382
+ // Create the journal with explicit mode 0o600 on first write. fs.open
383
+ // honours mode only when O_CREAT applies (i.e. the file is being
384
+ // created); subsequent appends ride the existing mode.
385
+ const fh = await fs.open(filePath, "a", 0o600);
386
+ try {
387
+ await fh.writeFile(line, "utf8");
388
+ }
389
+ finally {
390
+ await fh.close();
391
+ }
392
+ });
393
+ // Invalidate cache so the next readRuns picks up the append.
394
+ const absRoot = path.resolve(workspaceRoot);
395
+ cache.delete(absRoot);
396
+ logger.info("run recorded", {
397
+ details: {
398
+ file: filePath,
399
+ id: validated.data.id,
400
+ status: validated.data.status,
401
+ invocation: validated.data.invocation,
402
+ },
403
+ });
404
+ return { filePath, record: validated.data };
405
+ }
406
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/runs/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,MAKtB;IACC,OAAO,MAAM,CAAC,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,GAAG,GAAG,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;AAC5F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAM3C,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7B,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;QACnC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;QACjC,UAAU,EAAE,CAAC,GAAG,EAAE;KACnB,CAAC;AACJ,CAAC;AAED,0FAA0F;AAC1F,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB;;;;;;;;;;;GAWG;AACH,SAAS,uBAAuB,CAAC,CAAS;IACxC,OAAO,CAAC,CAAC,OAAO,CAAC,gCAAgC,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,WAAW;IACX,QAAQ;IACR,MAAM;IACN,UAAU;IACV,YAAY;IACZ,OAAO;CACC,CAAC;AAEX,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AACjD,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACrD,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAChD,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;AACjE,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC;IAC1B,SAAS;IACT,SAAS;IACT,UAAU;IACV,aAAa;IACb,UAAU;IACV,eAAe;CAChB,CAAC,CAAC;AACH,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAC,CAAC;AAEzE,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAEjE,MAAM,YAAY,GAAG,CAAC;KACnB,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAC9C,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC;CACvB,CAAC;KACD,QAAQ,EAAE,CAAC;AAEd;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IAC/B,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC5D,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE;IAC1D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE;IACrD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE;CACxD,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5B,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC;IAClB,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,UAAU,CAAC,EAAE,CAAC;IAC1B,YAAY,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/D,UAAU,EAAE,cAAc;IAC1B,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,cAAc;IAC3B,SAAS,EAAE,YAAY,CAAC,QAAQ,EAAE;IAClC,OAAO,EAAE,YAAY;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChE,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IAC7C,YAAY,EAAE,CAAC;SACZ,MAAM,CAAC;QACN,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC;KACzB,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,EAAE;CACd,CAAC,CAAC;AAQH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAsBzC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE5C,kFAAkF;AAClF,MAAM,UAAU,6BAA6B;IAC3C,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,aAAqB,EAAE,cAAkC;IAChF,MAAM,GAAG,GAAG,cAAc,IAAI,iBAAiB,CAAC;IAChD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,wBAAwB,CAAC,aAAa,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,MAAM,QAAQ,GAAG,sCAAsC,CAAC;IACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,GAAG,EAAE,IAAI,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,aAAqB,EACrB,UAAuC,EAAE;IAEzC,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAE5C,IAAI,IAAI,CAAC;IACT,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,CAAC;IAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClC,IACE,MAAM;QACN,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAC5B,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;QAC/B,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,EACzB,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAClB,oBAAoB,EACpB,4BAA4B,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,EACjE,EAAE,MAAM,EAAE,QAAQ,EAAE,CACrB,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,YAAY,GAAoD,EAAE,CAAC;IACzE,IAAI,qBAAqB,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,CAAC;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,EAAE;YAAE,SAAS;QAC7B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,YAAY,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,OAAO;gBACZ,MAAM,EAAE,iBAAkB,GAAa,CAAC,OAAO,EAAE;aAClD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,wEAAwE;QACxE,sEAAsE;QACtE,wEAAwE;QACxE,IACE,OAAO,MAAM,KAAK,QAAQ;YAC1B,MAAM,KAAK,IAAI;YACf,gBAAgB,IAAI,MAAM;YACzB,MAAsC,CAAC,cAAc,KAAK,CAAC,EAC5D,CAAC;YACD,qBAAqB,EAAE,CAAC;YACxB,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,OAAO;gBACZ,MAAM,EAAE,qBAAqB,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE;aACvD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,qBAAqB,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,gDAAgD,EAAE;YAC5D,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE;SAC1D,CAAC,CAAC;IACL,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,cAAc,GAAG,GAAG,QAAQ,YAAY,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3F,4DAA4D;YAC5D,MAAM,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9E,CAAC;QAAC,MAAM,CAAC;YACP,gCAAgC;QAClC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE;YAC7C,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,cAAc;gBAC1B,KAAK,EAAE,YAAY,CAAC,MAAM;gBAC1B,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aACvC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAClF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,aAAqB,EACrB,MAAiB,EACjB,UAAuC,EAAE;IAEzC,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,UAAU,CAClB,eAAe,EACf,gCAAgC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EACzD,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAC1C,CAAC;IACJ,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,0EAA0E;IAC1E,MAAM,SAAS,GACb,SAAS,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI;QACjC,CAAC,CAAC;YACE,GAAG,SAAS,CAAC,IAAI;YACjB,YAAY,EAAE;gBACZ,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY;gBAC9B,OAAO,EAAE,uBAAuB,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;aACtE;SACF;QACH,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;IAErB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,OAAO,GAAG,gBAAgB,EAAE,CAAC;QAC/B,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,wCAAwC,OAAO,MAAM,gBAAgB,KAAK;YACxE,4CAA4C,EAC9C,EAAE,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAC1D,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,yEAAyE;IACzE,0EAA0E;IAC1E,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEtD,wEAAwE;IACxE,qDAAqD;IACrD,MAAM,YAAY,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACtC,sEAAsE;QACtE,iEAAiE;QACjE,uDAAuD;QACvD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6DAA6D;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEtB,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE;QAC1B,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,MAAM;YAC7B,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,UAAU;SACtC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import type { ToolDef } from "./registry.js";
3
+ import { type AgentName } from "../config/ownership-matrix.js";
4
+ declare const schema: z.ZodObject<{
5
+ workspace_root: z.ZodEffects<z.ZodString, string, string>;
6
+ /** ISO 8601 lower bound on `started_at`. */
7
+ since: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
8
+ /** Cap output to the most recent N folded runs. */
9
+ limit: z.ZodOptional<z.ZodNumber>;
10
+ /** Restrict to runs that included this agent in any role. */
11
+ agent: z.ZodOptional<z.ZodEnum<[AgentName, ...AgentName[]]>>;
12
+ /** Restrict to a single terminal verdict. */
13
+ verdict: z.ZodOptional<z.ZodEnum<["APPROVED", "CHANGES_REQUIRED", "REJECTED"]>>;
14
+ /** Restrict to a single dispatch mode. */
15
+ mode: z.ZodOptional<z.ZodEnum<["quick", "normal", "deep"]>>;
16
+ /** Restrict to a single invocation kind. */
17
+ invocation: z.ZodOptional<z.ZodEnum<["implement", "review", "task", "question", "brainstorm", "debug"]>>;
18
+ /** Restrict to a single work type. */
19
+ work_type: z.ZodOptional<z.ZodEnum<["Feature", "Bug Fix", "Refactor", "Performance", "Security", "Business Rule"]>>;
20
+ /**
21
+ * If true, return aggregate views (outcomes + health + trend) instead of
22
+ * the folded raw rows. The /squad:stats skill uses this; CLI inspections
23
+ * default to the raw list.
24
+ */
25
+ aggregate: z.ZodOptional<z.ZodBoolean>;
26
+ /** Days of trend sparkline data to compute when aggregate=true. Default 14. */
27
+ trend_days: z.ZodOptional<z.ZodNumber>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ workspace_root: string;
30
+ work_type?: "Feature" | "Bug Fix" | "Refactor" | "Performance" | "Security" | "Business Rule" | undefined;
31
+ agent?: AgentName | undefined;
32
+ mode?: "quick" | "normal" | "deep" | undefined;
33
+ limit?: number | undefined;
34
+ invocation?: "debug" | "review" | "task" | "implement" | "question" | "brainstorm" | undefined;
35
+ verdict?: "APPROVED" | "CHANGES_REQUIRED" | "REJECTED" | undefined;
36
+ since?: string | undefined;
37
+ aggregate?: boolean | undefined;
38
+ trend_days?: number | undefined;
39
+ }, {
40
+ workspace_root: string;
41
+ work_type?: "Feature" | "Bug Fix" | "Refactor" | "Performance" | "Security" | "Business Rule" | undefined;
42
+ agent?: AgentName | undefined;
43
+ mode?: "quick" | "normal" | "deep" | undefined;
44
+ limit?: number | undefined;
45
+ invocation?: "debug" | "review" | "task" | "implement" | "question" | "brainstorm" | undefined;
46
+ verdict?: "APPROVED" | "CHANGES_REQUIRED" | "REJECTED" | undefined;
47
+ since?: string | undefined;
48
+ aggregate?: boolean | undefined;
49
+ trend_days?: number | undefined;
50
+ }>;
51
+ export declare const listRunsToolDef: ToolDef<typeof schema>;
52
+ export {};
@@ -0,0 +1,142 @@
1
+ import { z } from "zod";
2
+ import { SafeString } from "./_shared/schemas.js";
3
+ import { AGENT_NAMES_TUPLE } from "../config/ownership-matrix.js";
4
+ import { readRuns, INVOCATION_VALUES } from "../runs/store.js";
5
+ import { foldById, applyFilters, aggregateOutcomes, aggregateHealth, trendByDay, getEstTokens, } from "../runs/aggregate.js";
6
+ /**
7
+ * Read tool: load runs from `.squad/runs.jsonl`, fold the two-phase
8
+ * (in_flight, terminal) pair by id, apply filters, and either return the
9
+ * folded raw list or a precomputed aggregate bundle.
10
+ *
11
+ * Plan v4 architect A-3: collapse the original `list_runs + aggregate_runs`
12
+ * pair into one tool with an `aggregate: boolean` flag. The skill calls
13
+ * the aggregate form for `/squad:stats`; humans (or other tools) call the
14
+ * non-aggregate form to inspect raw rows.
15
+ *
16
+ * No writes here. The single-writer contract belongs to `record_run`.
17
+ */
18
+ const InvocationEnum = z.enum(INVOCATION_VALUES);
19
+ const ModeEnum = z.enum(["quick", "normal", "deep"]);
20
+ const VerdictEnum = z.enum(["APPROVED", "CHANGES_REQUIRED", "REJECTED"]);
21
+ const WorkTypeEnum = z.enum([
22
+ "Feature",
23
+ "Bug Fix",
24
+ "Refactor",
25
+ "Performance",
26
+ "Security",
27
+ "Business Rule",
28
+ ]);
29
+ const schema = z.object({
30
+ workspace_root: SafeString(4096),
31
+ /** ISO 8601 lower bound on `started_at`. */
32
+ since: SafeString(40).optional(),
33
+ /** Cap output to the most recent N folded runs. */
34
+ limit: z.number().int().positive().max(5000).optional(),
35
+ /** Restrict to runs that included this agent in any role. */
36
+ agent: z.enum(AGENT_NAMES_TUPLE).optional(),
37
+ /** Restrict to a single terminal verdict. */
38
+ verdict: VerdictEnum.optional(),
39
+ /** Restrict to a single dispatch mode. */
40
+ mode: ModeEnum.optional(),
41
+ /** Restrict to a single invocation kind. */
42
+ invocation: InvocationEnum.optional(),
43
+ /** Restrict to a single work type. */
44
+ work_type: WorkTypeEnum.optional(),
45
+ /**
46
+ * If true, return aggregate views (outcomes + health + trend) instead of
47
+ * the folded raw rows. The /squad:stats skill uses this; CLI inspections
48
+ * default to the raw list.
49
+ */
50
+ aggregate: z.boolean().optional(),
51
+ /** Days of trend sparkline data to compute when aggregate=true. Default 14. */
52
+ trend_days: z.number().int().positive().max(90).optional(),
53
+ });
54
+ function serializeFolded(f) {
55
+ return {
56
+ id: f.id,
57
+ status: f.status,
58
+ synthesized_aborted: f.synthesized_aborted,
59
+ record: f.record,
60
+ est_tokens: getEstTokens(f.record),
61
+ };
62
+ }
63
+ function workTypeFilter(folded, wt) {
64
+ return folded.filter((f) => f.record.work_type === wt);
65
+ }
66
+ async function handler(input) {
67
+ // Read raw records. readRuns swallows ENOENT and returns []; missing-journal
68
+ // is a normal "no runs yet" state, not an error.
69
+ const records = await readRuns(input.workspace_root);
70
+ const totalInStore = records.length;
71
+ const folded = foldById(records);
72
+ // Apply work_type FIRST so it composes with limit semantically. If we applied
73
+ // it after applyFilters, `limit: N` would truncate by started_at BEFORE the
74
+ // work_type predicate, giving "bug fixes within the last N runs" instead of
75
+ // "last N bug fixes" — silently wrong. (senior-developer cycle-2 Major.)
76
+ let prefiltered = folded;
77
+ if (input.work_type) {
78
+ prefiltered = workTypeFilter(prefiltered, input.work_type);
79
+ }
80
+ const filtered = applyFilters(prefiltered, {
81
+ ...(input.since !== undefined && { since: input.since }),
82
+ ...(input.limit !== undefined && { limit: input.limit }),
83
+ ...(input.agent !== undefined && { agent: input.agent }),
84
+ ...(input.verdict !== undefined && { verdict: input.verdict }),
85
+ ...(input.mode !== undefined && { mode: input.mode }),
86
+ ...(input.invocation !== undefined && { invocation: input.invocation }),
87
+ });
88
+ // Filepath surfacing: we don't currently resolve the configured path here
89
+ // (the store handles its own resolution); reporting null when no records is
90
+ // honest about the "fresh repo" state.
91
+ const filePath = totalInStore > 0 ? `${input.workspace_root}/.squad/runs.jsonl` : null;
92
+ if (!input.aggregate) {
93
+ return {
94
+ ok: true,
95
+ file: filePath,
96
+ total_in_store: totalInStore,
97
+ total_folded: filtered.length,
98
+ runs: filtered.map(serializeFolded),
99
+ };
100
+ }
101
+ const outcomes = aggregateOutcomes(filtered);
102
+ const health = aggregateHealth(filtered);
103
+ const trendBuckets = trendByDay(filtered, input.trend_days ?? 14);
104
+ return {
105
+ ok: true,
106
+ file: filePath,
107
+ total_in_store: totalInStore,
108
+ total_folded: filtered.length,
109
+ outcomes: {
110
+ total_runs: outcomes.total_runs,
111
+ verdict_counts: outcomes.verdict_counts,
112
+ verdict_total: outcomes.verdict_total,
113
+ score_buckets: outcomes.score_buckets,
114
+ invocation_counts: outcomes.invocation_counts,
115
+ est_tokens_total: outcomes.est_tokens_total,
116
+ est_tokens_per_run_avg: outcomes.est_tokens_per_run_avg,
117
+ est_tokens_per_agent: Array.from(outcomes.est_tokens_per_agent.entries()).map(([agent, t]) => ({ agent, ...t })),
118
+ is_empty: outcomes.is_empty,
119
+ },
120
+ health: {
121
+ total_runs: health.total_runs,
122
+ in_flight: health.in_flight,
123
+ completed: health.completed,
124
+ aborted: health.aborted,
125
+ synthesized_aborted: health.synthesized_aborted,
126
+ avg_batch_duration_ms_per_agent: Array.from(health.avg_batch_duration_ms_per_agent.entries()).map(([agent, avg_ms]) => ({ agent, avg_ms })),
127
+ avg_total_duration_ms: health.avg_total_duration_ms,
128
+ },
129
+ trend: { days: input.trend_days ?? 14, counts: trendBuckets },
130
+ };
131
+ }
132
+ export const listRunsToolDef = {
133
+ name: "list_runs",
134
+ description: "Read tool for `.squad/runs.jsonl`. Folds the two-phase (in_flight, terminal) row pair by id, " +
135
+ "applies filters (since / limit / agent / verdict / mode / invocation / work_type), and returns " +
136
+ "either the folded list (aggregate=false, default) or a precomputed aggregate bundle " +
137
+ "(outcomes + health + trend sparkline buckets) when aggregate=true. Missing-journal returns " +
138
+ "an empty result, not an error. Read-only — never writes.",
139
+ schema,
140
+ handler,
141
+ };
142
+ //# sourceMappingURL=list-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-runs.js","sourceRoot":"","sources":["../../src/tools/list-runs.ts"],"names":[],"mappings":"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"}