@forwardimpact/libharness 0.1.20 → 1.0.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 (86) hide show
  1. package/LICENSE +21 -201
  2. package/README.md +196 -80
  3. package/bin/fit-benchmark.js +44 -0
  4. package/bin/fit-harness.js +358 -0
  5. package/bin/fit-selfedit.js +165 -0
  6. package/bin/fit-trace.js +510 -0
  7. package/package.json +42 -12
  8. package/src/agent-runner.js +256 -0
  9. package/src/benchmark/apm-installer.js +207 -0
  10. package/src/benchmark/env-loader.js +158 -0
  11. package/src/benchmark/hook-env.js +40 -0
  12. package/src/benchmark/invariants.js +141 -0
  13. package/src/benchmark/judge.js +187 -0
  14. package/src/benchmark/npm-installer.js +87 -0
  15. package/src/benchmark/report.js +522 -0
  16. package/src/benchmark/result.js +127 -0
  17. package/src/benchmark/runner.js +583 -0
  18. package/src/benchmark/task-family.js +260 -0
  19. package/src/benchmark/workdir.js +298 -0
  20. package/src/commands/assert.js +153 -0
  21. package/src/commands/benchmark-definition.js +165 -0
  22. package/src/commands/benchmark-invariants.js +73 -0
  23. package/src/commands/benchmark-report.js +51 -0
  24. package/src/commands/benchmark-run.js +111 -0
  25. package/src/commands/by-discussion.js +94 -0
  26. package/src/commands/callback.js +119 -0
  27. package/src/commands/discuss.js +132 -0
  28. package/src/commands/facilitate.js +123 -0
  29. package/src/commands/output.js +36 -0
  30. package/src/commands/run.js +152 -0
  31. package/src/commands/supervise.js +136 -0
  32. package/src/commands/task-input.js +54 -0
  33. package/src/commands/tee.js +53 -0
  34. package/src/commands/trace.js +630 -0
  35. package/src/commands/work-tracker.js +35 -0
  36. package/src/cost.js +79 -0
  37. package/src/discuss-tools.js +173 -0
  38. package/src/discusser.js +394 -0
  39. package/src/events/github.js +161 -0
  40. package/src/facilitator.js +205 -0
  41. package/src/inbox-poller.js +81 -0
  42. package/src/index.js +72 -2
  43. package/src/judge.js +210 -0
  44. package/src/message-bus.js +118 -0
  45. package/src/orchestration-loop.js +330 -0
  46. package/src/orchestration-toolkit.js +441 -0
  47. package/src/orchestrator-helpers.js +23 -0
  48. package/src/profile-prompt.js +266 -0
  49. package/src/redaction.js +253 -0
  50. package/src/render/line-renderer.js +54 -0
  51. package/src/render/orchestrator-filter.js +19 -0
  52. package/src/render/palette.js +63 -0
  53. package/src/render/tool-hints.js +154 -0
  54. package/src/render/turn-renderer.js +96 -0
  55. package/src/reply-emitter.js +47 -0
  56. package/src/sequence-counter.js +21 -0
  57. package/src/signature-filter.js +27 -0
  58. package/src/supervisor.js +236 -0
  59. package/src/tee-writer.js +150 -0
  60. package/src/trace-collector.js +444 -0
  61. package/src/trace-github.js +473 -0
  62. package/src/trace-multi.js +101 -0
  63. package/src/trace-query.js +748 -0
  64. package/src/trace-render.js +211 -0
  65. package/src/trace-usage.js +249 -0
  66. package/src/fixture/assertions.js +0 -42
  67. package/src/fixture/cache.js +0 -50
  68. package/src/fixture/eval.js +0 -146
  69. package/src/fixture/index.js +0 -9
  70. package/src/fixture/pathway.js +0 -451
  71. package/src/fixture/services.js +0 -56
  72. package/src/mock/clients.js +0 -135
  73. package/src/mock/config.js +0 -45
  74. package/src/mock/data.js +0 -46
  75. package/src/mock/fs.js +0 -111
  76. package/src/mock/grpc.js +0 -94
  77. package/src/mock/http.js +0 -60
  78. package/src/mock/index.js +0 -36
  79. package/src/mock/infra.js +0 -219
  80. package/src/mock/logger.js +0 -42
  81. package/src/mock/observer.js +0 -74
  82. package/src/mock/resource-index.js +0 -95
  83. package/src/mock/service-callbacks.js +0 -39
  84. package/src/mock/services.js +0 -79
  85. package/src/mock/spy.js +0 -44
  86. package/src/mock/storage.js +0 -118
@@ -0,0 +1,510 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "@forwardimpact/libpreflight/node22";
4
+
5
+ import { createCli } from "@forwardimpact/libcli";
6
+ import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
7
+ import { createScriptConfig } from "@forwardimpact/libconfig";
8
+ import { createLogger } from "@forwardimpact/libtelemetry";
9
+
10
+ import {
11
+ runRunsCommand,
12
+ runFindCommand,
13
+ runDownloadCommand,
14
+ runOverviewCommand,
15
+ runCountCommand,
16
+ runBatchCommand,
17
+ runHeadCommand,
18
+ runTailCommand,
19
+ runSearchCommand,
20
+ runToolsCommand,
21
+ runToolCommand,
22
+ runErrorsCommand,
23
+ runReasoningCommand,
24
+ runTimelineCommand,
25
+ runStatsCommand,
26
+ runCostCommand,
27
+ runInitCommand,
28
+ runTurnCommand,
29
+ runFilterCommand,
30
+ runSplitCommand,
31
+ runToolCallsCommand,
32
+ runCommandsCommand,
33
+ runPathsCommand,
34
+ runCompareCommand,
35
+ } from "../src/commands/trace.js";
36
+ import { runAssertCommand } from "../src/commands/assert.js";
37
+ import { runByDiscussionCommand } from "../src/commands/by-discussion.js";
38
+
39
+ // Cross-trace verbs take one or more trace files via repeated `--file`
40
+ // (libcli's named-slot `dispatch()` has no variadic positional). A value with
41
+ // glob metacharacters is expanded by the handler via `runtime.fsSync.globSync`.
42
+ const fileOption = () => ({
43
+ file: {
44
+ type: "string",
45
+ multiple: true,
46
+ description: "Trace file (repeat or pass a quoted glob for several)",
47
+ },
48
+ });
49
+
50
+ const definition = {
51
+ name: "fit-trace",
52
+ description:
53
+ "Download, query, and analyze agent execution traces — read NDJSON output from fit-harness as qualitative research",
54
+ commands: [
55
+ {
56
+ name: "runs",
57
+ args: ["pattern"],
58
+ argsUsage: "[pattern]",
59
+ handler: runRunsCommand,
60
+ description:
61
+ "List recent GitHub Actions workflow runs (default pattern: kata|agent)",
62
+ options: {
63
+ lookback: {
64
+ type: "string",
65
+ description: "How far back to search (default: 7d)",
66
+ },
67
+ participant: {
68
+ type: "string",
69
+ description:
70
+ "Filter to runs carrying this participant's trace lane; in-progress candidates are labeled, not dropped",
71
+ },
72
+ repo: {
73
+ type: "string",
74
+ description:
75
+ "GitHub repo override (default: $GITHUB_REPOSITORY or 'origin' git remote)",
76
+ },
77
+ },
78
+ },
79
+ {
80
+ name: "find",
81
+ args: ["run-id", "participant"],
82
+ argsUsage: "<run-id> <participant>",
83
+ handler: runFindCommand,
84
+ description:
85
+ "Resolve a participant's lane trace for a known run id in one keyed lookup (no run enumeration, no content inspection)",
86
+ options: {
87
+ dir: {
88
+ type: "string",
89
+ description: "Output directory for a downloaded dispatch artifact",
90
+ },
91
+ repo: {
92
+ type: "string",
93
+ description:
94
+ "GitHub repo override (default: $GITHUB_REPOSITORY or 'origin' git remote)",
95
+ },
96
+ },
97
+ },
98
+ {
99
+ name: "download",
100
+ args: ["run-id"],
101
+ argsUsage: "<run-id>",
102
+ handler: runDownloadCommand,
103
+ description:
104
+ "Download trace artifact and convert to structured JSON; pass --artifact to pick one when a matrix workflow emits multiple `trace--*` artifacts",
105
+ options: {
106
+ dir: { type: "string", description: "Output directory" },
107
+ artifact: { type: "string", description: "Artifact name override" },
108
+ repo: {
109
+ type: "string",
110
+ description:
111
+ "GitHub repo override (default: $GITHUB_REPOSITORY or 'origin' git remote)",
112
+ },
113
+ },
114
+ },
115
+ {
116
+ name: "overview",
117
+ args: [],
118
+ handler: runOverviewCommand,
119
+ description: "Metadata, summary, turn count, tool frequency",
120
+ options: fileOption(),
121
+ },
122
+ {
123
+ name: "count",
124
+ args: [],
125
+ handler: runCountCommand,
126
+ description: "Number of turns",
127
+ options: fileOption(),
128
+ },
129
+ {
130
+ name: "batch",
131
+ args: ["file", "from", "to"],
132
+ argsUsage: "<file> <from> <to>",
133
+ handler: runBatchCommand,
134
+ description: "Turns in range [from, to) (zero-indexed)",
135
+ },
136
+ {
137
+ name: "head",
138
+ args: [],
139
+ handler: runHeadCommand,
140
+ description: "First N turns (default 10; set with --lines)",
141
+ options: {
142
+ ...fileOption(),
143
+ lines: { type: "string", description: "Number of turns (default: 10)" },
144
+ },
145
+ },
146
+ {
147
+ name: "tail",
148
+ args: [],
149
+ handler: runTailCommand,
150
+ description: "Last N turns (default 10; set with --lines)",
151
+ options: {
152
+ ...fileOption(),
153
+ lines: { type: "string", description: "Number of turns (default: 10)" },
154
+ },
155
+ },
156
+ {
157
+ name: "search",
158
+ args: ["file", "pattern"],
159
+ argsUsage: "<file> <pattern>",
160
+ handler: runSearchCommand,
161
+ description: "Search all content for regex pattern",
162
+ options: {
163
+ limit: {
164
+ type: "string",
165
+ description: "Max results (default: 50)",
166
+ },
167
+ context: {
168
+ type: "string",
169
+ description: "Surrounding turns per hit (default: 0)",
170
+ },
171
+ full: {
172
+ type: "boolean",
173
+ description: "Full content block in match descriptions",
174
+ },
175
+ },
176
+ },
177
+ {
178
+ name: "tools",
179
+ args: [],
180
+ handler: runToolsCommand,
181
+ description:
182
+ "Tool usage frequency (descending). See also `tool` (turns for one tool) and `tool-calls` (paired use+result records)",
183
+ options: fileOption(),
184
+ },
185
+ {
186
+ name: "tool",
187
+ args: ["file", "name"],
188
+ argsUsage: "<file> <name>",
189
+ handler: runToolCommand,
190
+ description:
191
+ "All turns involving a specific tool. See also `tools` (frequency) and `tool-calls` (paired use+result records)",
192
+ },
193
+ {
194
+ name: "tool-calls",
195
+ args: [],
196
+ handler: runToolCallsCommand,
197
+ description:
198
+ "One record per tool_use block, each paired with its tool_result by toolUseId (orphans emit result:null). See also `tool` and `tools`",
199
+ options: fileOption(),
200
+ },
201
+ {
202
+ name: "commands",
203
+ args: [],
204
+ handler: runCommandsCommand,
205
+ description:
206
+ "One record per Bash tool_use block, carrying the command text",
207
+ options: {
208
+ ...fileOption(),
209
+ match: {
210
+ type: "string",
211
+ description: "Filter to commands whose text matches this regex",
212
+ },
213
+ },
214
+ },
215
+ {
216
+ name: "paths",
217
+ args: [],
218
+ handler: runPathsCommand,
219
+ description:
220
+ "Distinct Read/Edit/Write file_path arguments, frequency-sorted",
221
+ options: {
222
+ ...fileOption(),
223
+ prefix: {
224
+ type: "string",
225
+ description: "Filter to paths beginning with this prefix",
226
+ },
227
+ },
228
+ },
229
+ {
230
+ name: "compare",
231
+ args: ["file-a", "file-b"],
232
+ argsUsage: "<file-a> <file-b>",
233
+ handler: runCompareCommand,
234
+ description:
235
+ "Side-by-side comparison of two traces: turns, tools, paths, cost, and per-tool delta",
236
+ },
237
+ {
238
+ name: "errors",
239
+ args: [],
240
+ handler: runErrorsCommand,
241
+ description: "Tool results with isError=true",
242
+ options: fileOption(),
243
+ },
244
+ {
245
+ name: "reasoning",
246
+ args: [],
247
+ handler: runReasoningCommand,
248
+ description: "Agent reasoning text only",
249
+ options: {
250
+ ...fileOption(),
251
+ from: { type: "string", description: "Start at turn index" },
252
+ to: { type: "string", description: "Stop before turn index" },
253
+ },
254
+ },
255
+ {
256
+ name: "timeline",
257
+ args: [],
258
+ handler: runTimelineCommand,
259
+ description: "Compact one-line-per-turn overview",
260
+ options: fileOption(),
261
+ },
262
+ {
263
+ name: "stats",
264
+ args: [],
265
+ handler: runStatsCommand,
266
+ description: "Token usage and cost breakdown",
267
+ options: {
268
+ ...fileOption(),
269
+ "by-tool": {
270
+ type: "boolean",
271
+ description: "Per-tool token attribution and cost share",
272
+ },
273
+ summary: {
274
+ type: "boolean",
275
+ description: "Totals only (suppress the per-turn array)",
276
+ },
277
+ },
278
+ },
279
+ {
280
+ name: "cost",
281
+ args: ["file"],
282
+ argsUsage: "<file>",
283
+ handler: runCostCommand,
284
+ description:
285
+ "Total run cost across every participant (agent, supervisor, judge, named profiles), with a per-source breakdown",
286
+ options: {
287
+ markdown: {
288
+ type: "boolean",
289
+ description:
290
+ "Emit a GitHub-flavored markdown block (redirect into $GITHUB_STEP_SUMMARY)",
291
+ },
292
+ },
293
+ },
294
+ {
295
+ name: "init",
296
+ args: [],
297
+ handler: runInitCommand,
298
+ description: "Full system/init event",
299
+ options: fileOption(),
300
+ },
301
+ {
302
+ name: "turn",
303
+ args: ["file", "index"],
304
+ argsUsage: "<file> <index>",
305
+ handler: runTurnCommand,
306
+ description: "Single turn by index",
307
+ },
308
+ {
309
+ name: "by-discussion",
310
+ args: ["discussion-id", "trace-dir"],
311
+ argsUsage: "<discussion-id> [trace-dir]",
312
+ handler: runByDiscussionCommand,
313
+ description:
314
+ "List trace files whose meta header carries the given discussion_id, ordered by first-event timestamp",
315
+ options: {
316
+ "trace-dir": {
317
+ type: "string",
318
+ description: "Directory to scan (default: traces)",
319
+ },
320
+ },
321
+ },
322
+ {
323
+ name: "filter",
324
+ args: [],
325
+ handler: runFilterCommand,
326
+ description: "Filter turns by role, tool, or error status",
327
+ options: {
328
+ ...fileOption(),
329
+ role: {
330
+ type: "string",
331
+ description: "Turn role (system, user, assistant, tool_result)",
332
+ },
333
+ tool: {
334
+ type: "string",
335
+ description: "Tool name (matches assistant turns)",
336
+ },
337
+ error: {
338
+ type: "boolean",
339
+ description: "Error tool_result turns only",
340
+ },
341
+ },
342
+ },
343
+ {
344
+ name: "split",
345
+ args: ["file"],
346
+ argsUsage: "<file>",
347
+ handler: runSplitCommand,
348
+ description:
349
+ "Split a combined trace into per-source files following the `trace--<case>--<participant>.<role>.ndjson` convention",
350
+ options: {
351
+ mode: {
352
+ type: "string",
353
+ description: "Execution mode: run, supervise, or facilitate",
354
+ },
355
+ case: {
356
+ type: "string",
357
+ description:
358
+ "Case identifier embedded in output filenames (default: default)",
359
+ },
360
+ "output-dir": {
361
+ type: "string",
362
+ description: "Output directory (default: same as input)",
363
+ },
364
+ },
365
+ },
366
+ {
367
+ name: "assert",
368
+ args: ["test-name", "file"],
369
+ argsUsage: "<test-name> <file>",
370
+ handler: runAssertCommand,
371
+ description:
372
+ "Shell-friendly assertion — outputs structured JSON for invariant hooks",
373
+ options: {
374
+ grep: {
375
+ type: "string",
376
+ description:
377
+ "Pass if extended regex matches file content (case-insensitive)",
378
+ },
379
+ query: {
380
+ type: "string",
381
+ description:
382
+ "Pass if JMESPath expression against JSON/NDJSON yields a truthy result",
383
+ },
384
+ exists: {
385
+ type: "boolean",
386
+ description: "Pass if file exists",
387
+ },
388
+ "cites-job": {
389
+ type: "string",
390
+ description:
391
+ "Pass if <file> contains the canonical citation from a <job> tag in the given JTBD file",
392
+ },
393
+ not: {
394
+ type: "boolean",
395
+ description: "Invert the assertion",
396
+ },
397
+ message: {
398
+ type: "string",
399
+ description: "Custom failure message",
400
+ },
401
+ },
402
+ },
403
+ ],
404
+ globalOptions: {
405
+ help: { type: "boolean", short: "h", description: "Show this help" },
406
+ version: { type: "boolean", description: "Show version" },
407
+ json: {
408
+ type: "boolean",
409
+ description: "Output help as JSON (use --format json for command output)",
410
+ },
411
+ format: {
412
+ type: "string",
413
+ default: "text",
414
+ description: "Command output format: text (default) or json",
415
+ },
416
+ signatures: {
417
+ type: "boolean",
418
+ description: "Include thinking.signature blobs in output",
419
+ },
420
+ },
421
+ examples: [
422
+ "fit-trace runs --lookback 7d",
423
+ "fit-trace runs --participant release-engineer",
424
+ "fit-trace find 27401632821 release-engineer",
425
+ "fit-trace download 24497273755",
426
+ "fit-trace split structured.json --mode=facilitate",
427
+ "fit-trace overview --file structured.json",
428
+ "fit-trace overview --file structured.json --format json",
429
+ "fit-trace timeline --file structured.json",
430
+ "fit-trace stats --file structured.json --by-tool",
431
+ "fit-trace cost trace.ndjson",
432
+ "fit-trace cost trace.ndjson --markdown",
433
+ "fit-trace tool structured.json Conclude",
434
+ "fit-trace tool-calls --file structured.json",
435
+ "fit-trace commands --file structured.json --match '^git'",
436
+ "fit-trace paths --file 'traces/*.ndjson' --prefix /app",
437
+ "fit-trace compare trace-a.ndjson trace-b.ndjson",
438
+ "fit-trace search structured.json 'error|fail' --context 1",
439
+ "fit-trace filter --file structured.json --tool Bash --error",
440
+ "fit-trace turn structured.json 3",
441
+ "fit-trace assert has-heading --grep '^## Problem' spec.md",
442
+ "fit-trace assert no-leak --not --grep 'password' output.log",
443
+ "fit-trace assert file-present --exists path/to/spec.md",
444
+ "fit-trace assert cites-jtbd --cites-job jtbd-excerpt.md spec.md",
445
+ "fit-trace assert used-edit --query \"[?type=='assistant'].message.content[] | [?name=='Edit']\" trace.ndjson",
446
+ ],
447
+ documentation: [
448
+ {
449
+ title: "Analyze Traces",
450
+ url: "https://www.forwardimpact.team/docs/libraries/prove-changes/trace-analysis/index.md",
451
+ description:
452
+ "The full method walkthrough with worked examples (an eval that failed, a multi-agent session that stalled).",
453
+ },
454
+ {
455
+ title: "Run an Eval",
456
+ url: "https://www.forwardimpact.team/docs/libraries/prove-changes/run-eval/index.md",
457
+ description:
458
+ "How `fit-harness supervise` produces the traces this skill analyzes.",
459
+ },
460
+ {
461
+ title: "Prove Agent Changes",
462
+ url: "https://www.forwardimpact.team/docs/libraries/prove-changes/index.md",
463
+ description:
464
+ "End-to-end workflow including multi-agent collaboration; `split` is the bridge into per-source trace files.",
465
+ },
466
+ ],
467
+ };
468
+
469
+ const runtime = createDefaultRuntime();
470
+ const logger = createLogger("trace", runtime);
471
+
472
+ // Commands that talk to the GitHub API need a config-backed token resolver;
473
+ // the rest only read local trace files through the runtime.
474
+ const NEEDS_CONFIG = new Set(["runs", "find", "download"]);
475
+
476
+ async function main() {
477
+ const cli = createCli(definition, {
478
+ runtime,
479
+ packageJsonUrl: new URL("../package.json", import.meta.url),
480
+ });
481
+ const parsed = cli.parse(runtime.proc.argv.slice(2));
482
+ if (!parsed) return runtime.proc.exit(0);
483
+
484
+ const { positionals } = parsed;
485
+ if (positionals.length === 0) {
486
+ cli.usageError("no command specified");
487
+ return runtime.proc.exit(2);
488
+ }
489
+
490
+ const command = positionals[0];
491
+ if (!definition.commands.some((c) => c.name === command)) {
492
+ cli.usageError(`unknown command "${command}"`);
493
+ return runtime.proc.exit(2);
494
+ }
495
+
496
+ const config = NEEDS_CONFIG.has(command)
497
+ ? await createScriptConfig("eval")
498
+ : undefined;
499
+
500
+ const result = await cli.dispatch(parsed, { deps: { runtime, config } });
501
+ const envelope = result ?? { ok: true };
502
+ if (!envelope.ok && envelope.error) cli.error(envelope.error);
503
+ runtime.proc.exit(envelope.ok ? 0 : (envelope.code ?? 1));
504
+ }
505
+
506
+ main().catch((error) => {
507
+ logger.exception("main", error);
508
+ createCli(definition, { runtime }).error(error.message);
509
+ process.exit(1);
510
+ });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@forwardimpact/libharness",
3
- "version": "0.1.20",
4
- "description": "Shared mocks and test fixtures so every library and service tests the same way.",
3
+ "version": "1.0.0",
4
+ "description": "Agent evaluation framework — prove whether agent changes improved outcomes with reproducible evidence.",
5
5
  "keywords": [
6
- "test",
7
- "mock",
8
- "fixture",
9
- "harness",
10
- "agent"
6
+ "eval",
7
+ "agent",
8
+ "trace",
9
+ "claude-code",
10
+ "supervisor"
11
11
  ],
12
12
  "homepage": "https://www.forwardimpact.team",
13
13
  "repository": {
@@ -17,26 +17,56 @@
17
17
  },
18
18
  "license": "Apache-2.0",
19
19
  "author": "D. Olsson <hi@senzilla.io>",
20
+ "jobs": [
21
+ {
22
+ "user": "Platform Builders",
23
+ "goal": "Prove Agent Changes",
24
+ "trigger": "An eval passes locally but fails in CI and the only output is 'assertion failed.'",
25
+ "bigHire": "prove whether agent changes improved outcomes with reproducible evidence.",
26
+ "littleHire": "run an eval and get a trace that shows exactly what the agent did.",
27
+ "competesWith": "manual before/after comparison; trusting gut feeling over evidence; skipping evaluation entirely"
28
+ }
29
+ ],
20
30
  "type": "module",
21
31
  "main": "./src/index.js",
22
32
  "exports": {
23
33
  ".": "./src/index.js",
24
- "./mock": "./src/mock/index.js",
25
- "./fixture": "./src/fixture/index.js"
34
+ "./bin/fit-harness.js": "./bin/fit-harness.js",
35
+ "./bin/fit-trace.js": "./bin/fit-trace.js",
36
+ "./bin/fit-benchmark.js": "./bin/fit-benchmark.js",
37
+ "./bin/fit-selfedit.js": "./bin/fit-selfedit.js"
38
+ },
39
+ "bin": {
40
+ "fit-harness": "./bin/fit-harness.js",
41
+ "fit-trace": "./bin/fit-trace.js",
42
+ "fit-benchmark": "./bin/fit-benchmark.js",
43
+ "fit-selfedit": "./bin/fit-selfedit.js"
26
44
  },
27
45
  "files": [
28
46
  "src/**/*.js",
47
+ "bin/**/*.js",
29
48
  "README.md"
30
49
  ],
31
50
  "scripts": {
32
51
  "test": "bun test test/*.test.js"
33
52
  },
34
- "peerDependencies": {
35
- "@forwardimpact/libtype": "*"
53
+ "dependencies": {
54
+ "@anthropic-ai/claude-agent-sdk": "0.3.170",
55
+ "@forwardimpact/libcli": "^0.1.0",
56
+ "@forwardimpact/libconfig": "^0.1.0",
57
+ "@forwardimpact/libpreflight": "^0.1.0",
58
+ "@forwardimpact/libtelemetry": "^0.1.22",
59
+ "@forwardimpact/libutil": "^0.1.0",
60
+ "jmespath": "^0.16.0",
61
+ "minimatch": "^10.0.0",
62
+ "zod": "^4.4.3"
63
+ },
64
+ "devDependencies": {
65
+ "@forwardimpact/libmock": "^0.1.0"
36
66
  },
37
67
  "engines": {
38
68
  "bun": ">=1.2.0",
39
- "node": ">=18.0.0"
69
+ "node": ">=22.0.0"
40
70
  },
41
71
  "publishConfig": {
42
72
  "access": "public"