@testchimp/cli 0.1.29 → 0.1.31

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.
package/README.md CHANGED
@@ -42,10 +42,17 @@ testchimp get-requirement-coverage --branch-name main --help
42
42
  testchimp create-user-story --platform-file-path plans/stories/foo.md --title "Checkout"
43
43
  testchimp list-screen-states --json-input '{}'
44
44
  testchimp upsert-screen-states --json-input '{"screenStates":[{"screen":"Checkout","states":["empty","filled"]}]}'
45
+ testchimp list-perf-runs --testchimp-id TC-123 --kind JOURNEY --limit 20
46
+ testchimp get-perf-run --run-id 01ABC --include-raw
47
+ testchimp promote-perf-baseline --run-id 01ABC --env-class CI
48
+ testchimp compare-perf-to-baseline --run-id 01ABC --max-p95-regression-percent 10
49
+ testchimp list-related-perf-tests --scenario-titles "Checkout,Refund"
50
+ testchimp list-api-operation-interactions --operation-id 01XYZ --interaction-type REAL --limit 100
45
51
  ```
46
52
 
47
53
  - **stdout:** API response JSON.
48
54
  - **stderr:** progress for `provision-ephemeral-environment-and-wait` (“still waiting…” polls).
55
+ - **performance gate:** `compare-perf-to-baseline` still prints its JSON response but exits nonzero when `regressed` is `true` (top-level or under `comparison`).
49
56
  - **Flags:** default for each subcommand; **`--json-input '<json>'`** or **`--json-input @file.json`** merges over flags (JSON wins on conflicts). Use JSON for nested bodies (e.g. TrueCoverage scopes).
50
57
 
51
58
  ## Migration from `testchimp-mcp-client`
@@ -8,6 +8,13 @@ import { TOOL_DEFINITIONS } from "../core/tools.js";
8
8
  import { resolveGitHeadSha } from "../core/gitSha.js";
9
9
  import { PACKAGE_VERSION } from "../core/version.js";
10
10
  export { PACKAGE_VERSION };
11
+ /** True when ComparePerfToBaselineResponse (or a flat PerfComparison) reports a regression. */
12
+ function isPerfComparisonRegressed(parsed) {
13
+ if (!parsed || typeof parsed !== "object")
14
+ return false;
15
+ const body = parsed;
16
+ return body.regressed === true || body.comparison?.regressed === true;
17
+ }
11
18
  function parseRecordTypesCsv(raw) {
12
19
  return String(raw)
13
20
  .split(",")
@@ -23,9 +30,11 @@ function parseRecordTypesCsv(raw) {
23
30
  return "smart_test";
24
31
  if (s === "manual")
25
32
  return "manual";
33
+ if (s === "perf" || s === "perftest" || s === "perf_test")
34
+ return "perf_test";
26
35
  return s;
27
36
  })
28
- .filter((v) => v === "smart_test" || v === "manual");
37
+ .filter((v) => v === "smart_test" || v === "manual" || v === "perf_test");
29
38
  }
30
39
  function parseJsonInput(raw) {
31
40
  if (raw == null || raw.trim() === "")
@@ -55,7 +64,9 @@ function addAgentTraceabilityOptions(cmd) {
55
64
  .option("--actor-type <type>", "LOCAL_AGENT | CLOUD_AGENT")
56
65
  .option("--user-id <id>", "Optional user id")
57
66
  .option("--branch-name <name>", "Git branch name")
58
- .option("--agent-model <model>", "Optional agent model id (agent/CLI only)");
67
+ .option("--agent-model <model>", "Optional agent model id (agent/CLI only)")
68
+ .option("--skill-version <semver>", "TestChimp skill version from SKILL.md frontmatter")
69
+ .option("--cli-version <semver>", "CLI version (defaults to this package version)");
59
70
  }
60
71
  function collectAgentTraceabilityFlags(opts) {
61
72
  const body = {};
@@ -77,6 +88,10 @@ function collectAgentTraceabilityFlags(opts) {
77
88
  body.branchName = String(opts.branchName).trim();
78
89
  if (opts.agentModel)
79
90
  body.agentModel = String(opts.agentModel).trim();
91
+ if (opts.skillVersion)
92
+ body.skillVersion = String(opts.skillVersion).trim();
93
+ if (opts.cliVersion)
94
+ body.cliVersion = String(opts.cliVersion).trim();
80
95
  return body;
81
96
  }
82
97
  function stderrProgress(msg) {
@@ -126,8 +141,9 @@ export function buildCliProgram() {
126
141
  .option("--environment <s>")
127
142
  .option("--branch-name <s>")
128
143
  .option("--platform <web|ios|android>")
129
- .option("--record-types <csv>", "coverage sources: smart_test,manual (aliases: automated,smarttest)")
144
+ .option("--record-types <csv>", "coverage sources: smart_test,manual,perf_test (aliases: automated,smarttest,perf)")
130
145
  .option("--include-manual", "include manual session coverage in addition to automated SmartTests")
146
+ .option("--include-perf", "include PERF_TEST journey coverage in addition to automated SmartTests")
131
147
  .option("--manual-only", "manual-only coverage (no automated)")
132
148
  .option("--lifecycle-statuses <csv>", "scenario lifecycle allowlist (e.g. ready or draft,ready)")
133
149
  .option("--limit <n>", "top N gaps after filter+rank into rankedScenarios (max 200)", (v) => parseInt(v, 10))
@@ -154,6 +170,9 @@ export function buildCliProgram() {
154
170
  recordTypes = Array.from(new Set([...(recordTypes ?? ["smart_test"]), "manual"]));
155
171
  if (opts.manualOnly)
156
172
  recordTypes = ["manual"];
173
+ if (opts.includePerf) {
174
+ recordTypes = Array.from(new Set([...(recordTypes ?? ["smart_test"]), "perf_test"]));
175
+ }
157
176
  if (recordTypes && recordTypes.length > 0)
158
177
  body.recordTypes = recordTypes;
159
178
  if (opts.lifecycleStatuses) {
@@ -1119,6 +1138,8 @@ export function buildCliProgram() {
1119
1138
  .option("--user-id <id>", "Optional user id for traceability")
1120
1139
  .option("--branch-name <name>", "Git branch")
1121
1140
  .option("--agent-model <model>", "Optional agent model id (agent/CLI only)")
1141
+ .option("--skill-version <semver>", "TestChimp skill version from SKILL.md frontmatter")
1142
+ .option("--cli-version <semver>", "CLI version (defaults to this package version)")
1122
1143
  .requiredOption("--entity-type <type>", "USER_STORY|SCENARIO|SMART_TEST|POLICY|ISSUE|TEST_EXECUTION|TEST_INVOCATION_BATCH|EXPLORATION|EVENT|WORKFLOW")
1123
1144
  .option("--entity-identity <ordinal>", "Project-scoped ordinal id (mutually exclusive with --test-json)")
1124
1145
  .option("--test-json <json>", "TestLocator JSON (folderPath/fileName/testSuite/testName)")
@@ -1144,6 +1165,10 @@ export function buildCliProgram() {
1144
1165
  body.branchName = String(opts.branchName);
1145
1166
  if (opts.agentModel)
1146
1167
  body.agentModel = String(opts.agentModel).trim();
1168
+ if (opts.skillVersion)
1169
+ body.skillVersion = String(opts.skillVersion).trim();
1170
+ if (opts.cliVersion)
1171
+ body.cliVersion = String(opts.cliVersion).trim();
1147
1172
  if (opts.entityIdentity)
1148
1173
  body.entityIdentity = String(opts.entityIdentity);
1149
1174
  if (opts.testJson)
@@ -1290,6 +1315,174 @@ export function buildCliProgram() {
1290
1315
  const merged = mergeBodies(body, opts.jsonInput);
1291
1316
  console.log(await runTool("get-api-operation-detail", merged, { postMcp }));
1292
1317
  });
1318
+ program
1319
+ .command("list-perf-runs")
1320
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "list-perf-runs").description)
1321
+ .addOption(jsonInputOption())
1322
+ .option("--testchimp-id <id>")
1323
+ .option("--kind <kind>", "JOURNEY | COMPOSITE")
1324
+ .option("--branch-name <name>")
1325
+ .option("--profile <name>")
1326
+ .option("--dataset <name>")
1327
+ .option("--llm-mode <mode>")
1328
+ .option("--environment <name>")
1329
+ .option("--limit <n>", "Maximum results", (v) => Number(v))
1330
+ .option("--offset <n>", "Pagination offset", (v) => Number(v))
1331
+ .action(async (opts) => {
1332
+ const body = {};
1333
+ if (opts.testchimpId)
1334
+ body.testchimpId = String(opts.testchimpId);
1335
+ if (opts.kind)
1336
+ body.kind = String(opts.kind);
1337
+ if (opts.branchName)
1338
+ body.branchName = String(opts.branchName);
1339
+ if (opts.profile)
1340
+ body.profile = String(opts.profile);
1341
+ if (opts.dataset)
1342
+ body.dataset = String(opts.dataset);
1343
+ if (opts.llmMode)
1344
+ body.llmMode = String(opts.llmMode);
1345
+ if (opts.environment)
1346
+ body.environment = String(opts.environment);
1347
+ if (opts.limit != null)
1348
+ body.limit = opts.limit;
1349
+ if (opts.offset != null)
1350
+ body.offset = opts.offset;
1351
+ console.log(await runTool("list-perf-runs", mergeBodies(body, opts.jsonInput), { postMcp }));
1352
+ });
1353
+ program
1354
+ .command("get-perf-run")
1355
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-perf-run").description)
1356
+ .addOption(jsonInputOption())
1357
+ .option("--run-id <id>")
1358
+ .option("--include-raw", "Include the raw performance payload")
1359
+ .action(async (opts) => {
1360
+ const body = {};
1361
+ if (opts.runId)
1362
+ body.runId = String(opts.runId);
1363
+ if (opts.includeRaw)
1364
+ body.includeRaw = true;
1365
+ console.log(await runTool("get-perf-run", mergeBodies(body, opts.jsonInput), { postMcp }));
1366
+ });
1367
+ program
1368
+ .command("list-perf-baselines")
1369
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "list-perf-baselines").description)
1370
+ .addOption(jsonInputOption())
1371
+ .option("--testchimp-id <id>")
1372
+ .option("--limit <n>", "Maximum results", (v) => Number(v))
1373
+ .option("--offset <n>", "Pagination offset", (v) => Number(v))
1374
+ .action(async (opts) => {
1375
+ const body = {};
1376
+ if (opts.testchimpId)
1377
+ body.testchimpId = String(opts.testchimpId);
1378
+ if (opts.limit != null)
1379
+ body.limit = opts.limit;
1380
+ if (opts.offset != null)
1381
+ body.offset = opts.offset;
1382
+ console.log(await runTool("list-perf-baselines", mergeBodies(body, opts.jsonInput), { postMcp }));
1383
+ });
1384
+ addAgentTraceabilityOptions(program
1385
+ .command("promote-perf-baseline")
1386
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "promote-perf-baseline").description)
1387
+ .addOption(jsonInputOption())
1388
+ .option("--run-id <id>")
1389
+ .option("--env-class <name>")).action(async (opts) => {
1390
+ const body = {
1391
+ ...collectAgentTraceabilityFlags(opts),
1392
+ };
1393
+ if (opts.runId)
1394
+ body.runId = String(opts.runId);
1395
+ if (opts.envClass)
1396
+ body.envClass = String(opts.envClass);
1397
+ console.log(await runTool("promote-perf-baseline", mergeBodies(body, opts.jsonInput), { postMcp }));
1398
+ });
1399
+ program
1400
+ .command("compare-perf-to-baseline")
1401
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "compare-perf-to-baseline").description)
1402
+ .addOption(jsonInputOption())
1403
+ .option("--run-id <id>")
1404
+ .option("--testchimp-id <id>")
1405
+ .option("--profile <name>")
1406
+ .option("--dataset <name>")
1407
+ .option("--llm-mode <mode>")
1408
+ .option("--environment <name>")
1409
+ .option("--env-class <name>", "Baseline environment class (required)")
1410
+ .option("--max-p95-regression-percent <n>", "Allowed p95 regression percent", (v) => Number(v))
1411
+ .option("--max-fail-rate-increase <n>", "Allowed fail-rate increase", (v) => Number(v))
1412
+ .action(async (opts) => {
1413
+ const body = {};
1414
+ if (opts.runId)
1415
+ body.runId = String(opts.runId);
1416
+ if (opts.testchimpId)
1417
+ body.testchimpId = String(opts.testchimpId);
1418
+ if (opts.profile)
1419
+ body.profile = String(opts.profile);
1420
+ if (opts.dataset)
1421
+ body.dataset = String(opts.dataset);
1422
+ if (opts.llmMode)
1423
+ body.llmMode = String(opts.llmMode);
1424
+ if (opts.environment)
1425
+ body.environment = String(opts.environment);
1426
+ if (opts.envClass)
1427
+ body.envClass = String(opts.envClass);
1428
+ if (opts.maxP95RegressionPercent != null) {
1429
+ body.maxP95RegressionPercent = opts.maxP95RegressionPercent;
1430
+ }
1431
+ if (opts.maxFailRateIncrease != null)
1432
+ body.maxFailRateIncrease = opts.maxFailRateIncrease;
1433
+ const out = await runTool("compare-perf-to-baseline", mergeBodies(body, opts.jsonInput), { postMcp });
1434
+ // Always print response JSON; gate CI on regressed after stdout flush.
1435
+ console.log(out);
1436
+ try {
1437
+ if (isPerfComparisonRegressed(JSON.parse(out)))
1438
+ process.exitCode = 1;
1439
+ }
1440
+ catch {
1441
+ /* non-JSON responses still printed; leave exit 0 unless runTool threw */
1442
+ }
1443
+ });
1444
+ program
1445
+ .command("list-related-perf-tests")
1446
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "list-related-perf-tests").description)
1447
+ .addOption(jsonInputOption())
1448
+ .option("--scenario-titles <csv>", "Comma-separated scenario titles")
1449
+ .option("--testchimp-ids <csv>", "Comma-separated TestChimp ids")
1450
+ .option("--no-include-composites", "Exclude COMPOSITE tests")
1451
+ .option("--limit <n>", "Maximum results (max 100)", (v) => Number(v))
1452
+ .action(async (opts) => {
1453
+ const body = {
1454
+ includeComposites: opts.includeComposites,
1455
+ };
1456
+ if (opts.scenarioTitles) {
1457
+ body.scenarioTitles = String(opts.scenarioTitles).split(",").map((s) => s.trim()).filter(Boolean);
1458
+ }
1459
+ if (opts.testchimpIds) {
1460
+ body.testchimpIds = String(opts.testchimpIds).split(",").map((s) => s.trim()).filter(Boolean);
1461
+ }
1462
+ if (opts.limit != null)
1463
+ body.limit = opts.limit;
1464
+ console.log(await runTool("list-related-perf-tests", mergeBodies(body, opts.jsonInput), { postMcp }));
1465
+ });
1466
+ program
1467
+ .command("list-api-operation-interactions")
1468
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "list-api-operation-interactions").description)
1469
+ .addOption(jsonInputOption())
1470
+ .option("--test-id <id>")
1471
+ .option("--operation-id <id>")
1472
+ .option("--interaction-type <type>", "REAL | MOCKED", "REAL")
1473
+ .option("--limit <n>", "Maximum results (max 100)", (v) => Number(v))
1474
+ .action(async (opts) => {
1475
+ const body = {
1476
+ interactionType: opts.interactionType,
1477
+ };
1478
+ if (opts.testId)
1479
+ body.testId = String(opts.testId);
1480
+ if (opts.operationId)
1481
+ body.operationId = String(opts.operationId);
1482
+ if (opts.limit != null)
1483
+ body.limit = opts.limit;
1484
+ console.log(await runTool("list-api-operation-interactions", mergeBodies(body, opts.jsonInput), { postMcp }));
1485
+ });
1293
1486
  program.on("--help", () => {
1294
1487
  /* default */
1295
1488
  });
@@ -9,9 +9,23 @@ export type AgentTraceabilityFields = {
9
9
  userId?: string;
10
10
  branchName?: string;
11
11
  agentModel?: string;
12
+ skillVersion?: string;
13
+ cliVersion?: string;
12
14
  /** Nested form (wins over flat when both present and non-empty). */
13
15
  agentTraceability?: Record<string, unknown>;
14
16
  };
17
+ /**
18
+ * Resolve skill / CLI versions for traceability payloads.
19
+ * CLI version defaults to this package's version; skill version from flag/env only.
20
+ */
21
+ export declare function resolveToolchainVersions(a: {
22
+ skillVersion?: string;
23
+ cliVersion?: string;
24
+ nested?: Record<string, unknown> | null;
25
+ }): {
26
+ skillVersion?: string;
27
+ cliVersion?: string;
28
+ };
15
29
  /**
16
30
  * Build camelCase AgentActionTraceability for MCP JSON bodies.
17
31
  * Returns undefined unless the caller supplied explicit traceability intent
@@ -19,7 +33,7 @@ export type AgentTraceabilityFields = {
19
33
  * For Activity/timeline attachment the server also requires workflowExecutionId
20
34
  * (stable Plan ULID for the whole run) — omit it and the mutation still succeeds
21
35
  * but no workflow_executions / Activity row is recorded (server does not auto-mint).
22
- * Auto-fills gitSha / agentModel / userId only after the workflowId bar is met.
36
+ * Auto-fills gitSha / agentModel / userId / cliVersion only after the workflowId bar is met.
23
37
  * Non-empty nested `agentTraceability` wins over flat for overlapping keys.
24
38
  */
25
39
  export declare function buildAgentTraceabilityPayload(a: AgentTraceabilityFields): Record<string, unknown> | undefined;
@@ -1,4 +1,5 @@
1
1
  import { resolveGitHeadSha } from "./gitSha.js";
2
+ import { PACKAGE_VERSION } from "./version.js";
2
3
  function nonEmptyString(v) {
3
4
  if (v == null)
4
5
  return undefined;
@@ -24,6 +25,10 @@ function hasExplicitTraceabilityIntent(a) {
24
25
  return true;
25
26
  if (nonEmptyString(a.agentModel))
26
27
  return true;
28
+ if (nonEmptyString(a.skillVersion))
29
+ return true;
30
+ if (nonEmptyString(a.cliVersion))
31
+ return true;
27
32
  if (a.agentTraceability && typeof a.agentTraceability === "object") {
28
33
  return Object.keys(a.agentTraceability).some((k) => nonEmptyString(a.agentTraceability[k]) != null);
29
34
  }
@@ -39,6 +44,20 @@ function normalizeActorType(raw) {
39
44
  return "LOCAL_AGENT";
40
45
  return undefined;
41
46
  }
47
+ /**
48
+ * Resolve skill / CLI versions for traceability payloads.
49
+ * CLI version defaults to this package's version; skill version from flag/env only.
50
+ */
51
+ export function resolveToolchainVersions(a) {
52
+ const skillVersion = nonEmptyString(a.nested?.skillVersion) ??
53
+ nonEmptyString(a.skillVersion) ??
54
+ nonEmptyString(process.env.TESTCHIMP_SKILL_VERSION);
55
+ const cliVersion = nonEmptyString(a.nested?.cliVersion) ??
56
+ nonEmptyString(a.cliVersion) ??
57
+ nonEmptyString(process.env.TESTCHIMP_CLI_VERSION) ??
58
+ PACKAGE_VERSION;
59
+ return { skillVersion, cliVersion };
60
+ }
42
61
  /**
43
62
  * Build camelCase AgentActionTraceability for MCP JSON bodies.
44
63
  * Returns undefined unless the caller supplied explicit traceability intent
@@ -46,7 +65,7 @@ function normalizeActorType(raw) {
46
65
  * For Activity/timeline attachment the server also requires workflowExecutionId
47
66
  * (stable Plan ULID for the whole run) — omit it and the mutation still succeeds
48
67
  * but no workflow_executions / Activity row is recorded (server does not auto-mint).
49
- * Auto-fills gitSha / agentModel / userId only after the workflowId bar is met.
68
+ * Auto-fills gitSha / agentModel / userId / cliVersion only after the workflowId bar is met.
50
69
  * Non-empty nested `agentTraceability` wins over flat for overlapping keys.
51
70
  */
52
71
  export function buildAgentTraceabilityPayload(a) {
@@ -72,6 +91,11 @@ export function buildAgentTraceabilityPayload(a) {
72
91
  const agentModel = nonEmptyString(nested?.agentModel) ??
73
92
  nonEmptyString(a.agentModel) ??
74
93
  nonEmptyString(process.env.TESTCHIMP_AGENT_MODEL);
94
+ const { skillVersion, cliVersion } = resolveToolchainVersions({
95
+ skillVersion: a.skillVersion,
96
+ cliVersion: a.cliVersion,
97
+ nested,
98
+ });
75
99
  // Server requires workflow_id for inline mutation Activity — do not send orphan payloads.
76
100
  if (!workflowId) {
77
101
  return undefined;
@@ -94,5 +118,9 @@ export function buildAgentTraceabilityPayload(a) {
94
118
  out.branchName = branchName;
95
119
  if (agentModel)
96
120
  out.agentModel = agentModel;
121
+ if (skillVersion)
122
+ out.skillVersion = skillVersion;
123
+ if (cliVersion)
124
+ out.cliVersion = cliVersion;
97
125
  return out;
98
126
  }
@@ -25,8 +25,10 @@ export declare const listCoverageInput: z.ZodObject<{
25
25
  recordTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
26
26
  smart_test: "smart_test";
27
27
  manual: "manual";
28
+ perf_test: "perf_test";
28
29
  SMART_TEST: "SMART_TEST";
29
30
  MANUAL: "MANUAL";
31
+ PERF_TEST: "PERF_TEST";
30
32
  }>>>;
31
33
  scenarioLifecycleStatuses: z.ZodOptional<z.ZodArray<z.ZodString>>;
32
34
  limit: z.ZodOptional<z.ZodNumber>;
@@ -105,6 +107,8 @@ export declare const agentActionTraceabilitySchema: z.ZodObject<{
105
107
  userId: z.ZodOptional<z.ZodString>;
106
108
  branchName: z.ZodOptional<z.ZodString>;
107
109
  agentModel: z.ZodOptional<z.ZodString>;
110
+ skillVersion: z.ZodOptional<z.ZodString>;
111
+ cliVersion: z.ZodOptional<z.ZodString>;
108
112
  }, z.core.$strict>;
109
113
  /** Flat + nested traceability fields shared by create/update MCP tools. */
110
114
  export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
@@ -122,6 +126,8 @@ export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
122
126
  userId: z.ZodOptional<z.ZodString>;
123
127
  branchName: z.ZodOptional<z.ZodString>;
124
128
  agentModel: z.ZodOptional<z.ZodString>;
129
+ skillVersion: z.ZodOptional<z.ZodString>;
130
+ cliVersion: z.ZodOptional<z.ZodString>;
125
131
  agentTraceability: z.ZodOptional<z.ZodObject<{
126
132
  workflowId: z.ZodOptional<z.ZodString>;
127
133
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -137,6 +143,8 @@ export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
137
143
  userId: z.ZodOptional<z.ZodString>;
138
144
  branchName: z.ZodOptional<z.ZodString>;
139
145
  agentModel: z.ZodOptional<z.ZodString>;
146
+ skillVersion: z.ZodOptional<z.ZodString>;
147
+ cliVersion: z.ZodOptional<z.ZodString>;
140
148
  }, z.core.$strict>>;
141
149
  }, z.core.$strip>;
142
150
  export declare const createUserStoryInput: z.ZodObject<{
@@ -156,6 +164,8 @@ export declare const createUserStoryInput: z.ZodObject<{
156
164
  userId: z.ZodOptional<z.ZodString>;
157
165
  branchName: z.ZodOptional<z.ZodString>;
158
166
  agentModel: z.ZodOptional<z.ZodString>;
167
+ skillVersion: z.ZodOptional<z.ZodString>;
168
+ cliVersion: z.ZodOptional<z.ZodString>;
159
169
  agentTraceability: z.ZodOptional<z.ZodObject<{
160
170
  workflowId: z.ZodOptional<z.ZodString>;
161
171
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -171,6 +181,8 @@ export declare const createUserStoryInput: z.ZodObject<{
171
181
  userId: z.ZodOptional<z.ZodString>;
172
182
  branchName: z.ZodOptional<z.ZodString>;
173
183
  agentModel: z.ZodOptional<z.ZodString>;
184
+ skillVersion: z.ZodOptional<z.ZodString>;
185
+ cliVersion: z.ZodOptional<z.ZodString>;
174
186
  }, z.core.$strict>>;
175
187
  }, z.core.$strip>;
176
188
  export declare const createTestScenarioInput: z.ZodObject<{
@@ -191,6 +203,8 @@ export declare const createTestScenarioInput: z.ZodObject<{
191
203
  userId: z.ZodOptional<z.ZodString>;
192
204
  branchName: z.ZodOptional<z.ZodString>;
193
205
  agentModel: z.ZodOptional<z.ZodString>;
206
+ skillVersion: z.ZodOptional<z.ZodString>;
207
+ cliVersion: z.ZodOptional<z.ZodString>;
194
208
  agentTraceability: z.ZodOptional<z.ZodObject<{
195
209
  workflowId: z.ZodOptional<z.ZodString>;
196
210
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -206,6 +220,8 @@ export declare const createTestScenarioInput: z.ZodObject<{
206
220
  userId: z.ZodOptional<z.ZodString>;
207
221
  branchName: z.ZodOptional<z.ZodString>;
208
222
  agentModel: z.ZodOptional<z.ZodString>;
223
+ skillVersion: z.ZodOptional<z.ZodString>;
224
+ cliVersion: z.ZodOptional<z.ZodString>;
209
225
  }, z.core.$strict>>;
210
226
  }, z.core.$strip>;
211
227
  export declare const updatePlanMarkdownInput: z.ZodObject<{
@@ -224,6 +240,8 @@ export declare const updatePlanMarkdownInput: z.ZodObject<{
224
240
  userId: z.ZodOptional<z.ZodString>;
225
241
  branchName: z.ZodOptional<z.ZodString>;
226
242
  agentModel: z.ZodOptional<z.ZodString>;
243
+ skillVersion: z.ZodOptional<z.ZodString>;
244
+ cliVersion: z.ZodOptional<z.ZodString>;
227
245
  agentTraceability: z.ZodOptional<z.ZodObject<{
228
246
  workflowId: z.ZodOptional<z.ZodString>;
229
247
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -239,6 +257,8 @@ export declare const updatePlanMarkdownInput: z.ZodObject<{
239
257
  userId: z.ZodOptional<z.ZodString>;
240
258
  branchName: z.ZodOptional<z.ZodString>;
241
259
  agentModel: z.ZodOptional<z.ZodString>;
260
+ skillVersion: z.ZodOptional<z.ZodString>;
261
+ cliVersion: z.ZodOptional<z.ZodString>;
242
262
  }, z.core.$strict>>;
243
263
  }, z.core.$strip>;
244
264
  export declare const markPlanItemsImplementationDoneInput: z.ZodObject<{
@@ -297,6 +317,8 @@ export declare const updateIssueStatusInput: z.ZodObject<{
297
317
  userId: z.ZodOptional<z.ZodString>;
298
318
  branchName: z.ZodOptional<z.ZodString>;
299
319
  agentModel: z.ZodOptional<z.ZodString>;
320
+ skillVersion: z.ZodOptional<z.ZodString>;
321
+ cliVersion: z.ZodOptional<z.ZodString>;
300
322
  agentTraceability: z.ZodOptional<z.ZodObject<{
301
323
  workflowId: z.ZodOptional<z.ZodString>;
302
324
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -312,6 +334,8 @@ export declare const updateIssueStatusInput: z.ZodObject<{
312
334
  userId: z.ZodOptional<z.ZodString>;
313
335
  branchName: z.ZodOptional<z.ZodString>;
314
336
  agentModel: z.ZodOptional<z.ZodString>;
337
+ skillVersion: z.ZodOptional<z.ZodString>;
338
+ cliVersion: z.ZodOptional<z.ZodString>;
315
339
  }, z.core.$strict>>;
316
340
  }, z.core.$strip>;
317
341
  export declare const createIssueInput: z.ZodObject<{
@@ -393,6 +417,8 @@ export declare const createIssueInput: z.ZodObject<{
393
417
  userId: z.ZodOptional<z.ZodString>;
394
418
  branchName: z.ZodOptional<z.ZodString>;
395
419
  agentModel: z.ZodOptional<z.ZodString>;
420
+ skillVersion: z.ZodOptional<z.ZodString>;
421
+ cliVersion: z.ZodOptional<z.ZodString>;
396
422
  agentTraceability: z.ZodOptional<z.ZodObject<{
397
423
  workflowId: z.ZodOptional<z.ZodString>;
398
424
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -408,6 +434,8 @@ export declare const createIssueInput: z.ZodObject<{
408
434
  userId: z.ZodOptional<z.ZodString>;
409
435
  branchName: z.ZodOptional<z.ZodString>;
410
436
  agentModel: z.ZodOptional<z.ZodString>;
437
+ skillVersion: z.ZodOptional<z.ZodString>;
438
+ cliVersion: z.ZodOptional<z.ZodString>;
411
439
  }, z.core.$strict>>;
412
440
  }, z.core.$strip>;
413
441
  export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
@@ -1364,6 +1392,8 @@ export declare const reportAgentActionInput: z.ZodObject<{
1364
1392
  userId: z.ZodOptional<z.ZodString>;
1365
1393
  branchName: z.ZodOptional<z.ZodString>;
1366
1394
  agentModel: z.ZodOptional<z.ZodString>;
1395
+ skillVersion: z.ZodOptional<z.ZodString>;
1396
+ cliVersion: z.ZodOptional<z.ZodString>;
1367
1397
  traceability: z.ZodOptional<z.ZodObject<{
1368
1398
  workflowId: z.ZodOptional<z.ZodString>;
1369
1399
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -1379,6 +1409,8 @@ export declare const reportAgentActionInput: z.ZodObject<{
1379
1409
  userId: z.ZodOptional<z.ZodString>;
1380
1410
  branchName: z.ZodOptional<z.ZodString>;
1381
1411
  agentModel: z.ZodOptional<z.ZodString>;
1412
+ skillVersion: z.ZodOptional<z.ZodString>;
1413
+ cliVersion: z.ZodOptional<z.ZodString>;
1382
1414
  }, z.core.$strict>>;
1383
1415
  entityType: z.ZodEnum<{
1384
1416
  SMART_TEST: "SMART_TEST";
@@ -1465,3 +1497,95 @@ export declare const getApiOperationDetailInput: z.ZodObject<{
1465
1497
  includeManual: z.ZodOptional<z.ZodBoolean>;
1466
1498
  includeRemoved: z.ZodOptional<z.ZodBoolean>;
1467
1499
  }, z.core.$strip>;
1500
+ /** Performance run kind persisted by the Phase 2 performance API. */
1501
+ export declare const perfRunKindSchema: z.ZodEnum<{
1502
+ JOURNEY: "JOURNEY";
1503
+ COMPOSITE: "COMPOSITE";
1504
+ }>;
1505
+ export declare const listPerfRunsInput: z.ZodObject<{
1506
+ testchimpId: z.ZodOptional<z.ZodString>;
1507
+ kind: z.ZodOptional<z.ZodEnum<{
1508
+ JOURNEY: "JOURNEY";
1509
+ COMPOSITE: "COMPOSITE";
1510
+ }>>;
1511
+ branchName: z.ZodOptional<z.ZodString>;
1512
+ profile: z.ZodOptional<z.ZodString>;
1513
+ dataset: z.ZodOptional<z.ZodString>;
1514
+ llmMode: z.ZodOptional<z.ZodString>;
1515
+ environment: z.ZodOptional<z.ZodString>;
1516
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1517
+ offset: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1518
+ }, z.core.$strip>;
1519
+ export declare const getPerfRunInput: z.ZodObject<{
1520
+ runId: z.ZodString;
1521
+ includeRaw: z.ZodOptional<z.ZodBoolean>;
1522
+ }, z.core.$strip>;
1523
+ export declare const listPerfBaselinesInput: z.ZodObject<{
1524
+ testchimpId: z.ZodOptional<z.ZodString>;
1525
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1526
+ offset: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1527
+ }, z.core.$strip>;
1528
+ export declare const promotePerfBaselineInput: z.ZodObject<{
1529
+ runId: z.ZodString;
1530
+ envClass: z.ZodString;
1531
+ workflowId: z.ZodOptional<z.ZodString>;
1532
+ workflowExecutionId: z.ZodOptional<z.ZodString>;
1533
+ policyFile: z.ZodOptional<z.ZodString>;
1534
+ policyVersion: z.ZodOptional<z.ZodString>;
1535
+ gitSha: z.ZodOptional<z.ZodString>;
1536
+ actorType: z.ZodOptional<z.ZodEnum<{
1537
+ LOCAL_AGENT: "LOCAL_AGENT";
1538
+ CLOUD_AGENT: "CLOUD_AGENT";
1539
+ "local-agent": "local-agent";
1540
+ "cloud-agent": "cloud-agent";
1541
+ }>>;
1542
+ userId: z.ZodOptional<z.ZodString>;
1543
+ branchName: z.ZodOptional<z.ZodString>;
1544
+ agentModel: z.ZodOptional<z.ZodString>;
1545
+ skillVersion: z.ZodOptional<z.ZodString>;
1546
+ cliVersion: z.ZodOptional<z.ZodString>;
1547
+ agentTraceability: z.ZodOptional<z.ZodObject<{
1548
+ workflowId: z.ZodOptional<z.ZodString>;
1549
+ workflowExecutionId: z.ZodOptional<z.ZodString>;
1550
+ policyFile: z.ZodOptional<z.ZodString>;
1551
+ policyVersion: z.ZodOptional<z.ZodString>;
1552
+ gitSha: z.ZodOptional<z.ZodString>;
1553
+ actorType: z.ZodOptional<z.ZodEnum<{
1554
+ LOCAL_AGENT: "LOCAL_AGENT";
1555
+ CLOUD_AGENT: "CLOUD_AGENT";
1556
+ "local-agent": "local-agent";
1557
+ "cloud-agent": "cloud-agent";
1558
+ }>>;
1559
+ userId: z.ZodOptional<z.ZodString>;
1560
+ branchName: z.ZodOptional<z.ZodString>;
1561
+ agentModel: z.ZodOptional<z.ZodString>;
1562
+ skillVersion: z.ZodOptional<z.ZodString>;
1563
+ cliVersion: z.ZodOptional<z.ZodString>;
1564
+ }, z.core.$strict>>;
1565
+ }, z.core.$strip>;
1566
+ export declare const comparePerfToBaselineInput: z.ZodObject<{
1567
+ runId: z.ZodOptional<z.ZodString>;
1568
+ testchimpId: z.ZodOptional<z.ZodString>;
1569
+ profile: z.ZodOptional<z.ZodString>;
1570
+ dataset: z.ZodOptional<z.ZodString>;
1571
+ llmMode: z.ZodOptional<z.ZodString>;
1572
+ environment: z.ZodOptional<z.ZodString>;
1573
+ envClass: z.ZodString;
1574
+ maxP95RegressionPercent: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1575
+ maxFailRateIncrease: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1576
+ }, z.core.$strip>;
1577
+ export declare const listRelatedPerfTestsInput: z.ZodObject<{
1578
+ scenarioTitles: z.ZodOptional<z.ZodArray<z.ZodString>>;
1579
+ testchimpIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
1580
+ includeComposites: z.ZodDefault<z.ZodBoolean>;
1581
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1582
+ }, z.core.$strip>;
1583
+ export declare const listApiOperationInteractionsInput: z.ZodObject<{
1584
+ testId: z.ZodOptional<z.ZodString>;
1585
+ operationId: z.ZodOptional<z.ZodString>;
1586
+ interactionType: z.ZodDefault<z.ZodEnum<{
1587
+ REAL: "REAL";
1588
+ MOCKED: "MOCKED";
1589
+ }>>;
1590
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1591
+ }, z.core.$strip>;
@@ -6,7 +6,14 @@ export const scopeSchema = z
6
6
  })
7
7
  .optional();
8
8
  const executionPlatformSchema = z.enum(["web", "ios", "android"]);
9
- const requirementCoverageRecordTypeSchema = z.enum(["smart_test", "manual", "SMART_TEST", "MANUAL"]);
9
+ const requirementCoverageRecordTypeSchema = z.enum([
10
+ "smart_test",
11
+ "manual",
12
+ "perf_test",
13
+ "SMART_TEST",
14
+ "MANUAL",
15
+ "PERF_TEST",
16
+ ]);
10
17
  export const executionJobDimensionFilterSchema = z.object({
11
18
  dimension: z.string().min(1),
12
19
  values: z.array(z.string()).min(1),
@@ -23,7 +30,8 @@ export const listCoverageInput = z.object({
23
30
  * Which coverage sources to include.
24
31
  *
25
32
  * Omit for legacy default: SMART_TEST only.
26
- * When provided, send proto enum names ("SMART_TEST", "MANUAL") or CLI-friendly aliases ("smart_test", "manual").
33
+ * When provided, send proto enum names ("SMART_TEST", "MANUAL", "PERF_TEST")
34
+ * or CLI-friendly aliases ("smart_test", "manual", "perf_test").
27
35
  */
28
36
  recordTypes: z.array(requirementCoverageRecordTypeSchema).optional(),
29
37
  /** Allowlist of scenario lifecycle statuses (e.g. ["ready"] or ["draft","ready"]). Empty/omit = no status filter. */
@@ -82,6 +90,8 @@ export const agentActionTraceabilitySchema = z
82
90
  userId: z.string().optional(),
83
91
  branchName: z.string().optional(),
84
92
  agentModel: z.string().optional(),
93
+ skillVersion: z.string().optional(),
94
+ cliVersion: z.string().optional(),
85
95
  })
86
96
  .strict();
87
97
  /** Flat + nested traceability fields shared by create/update MCP tools. */
@@ -95,6 +105,8 @@ export const agentTraceabilityFieldsSchema = z.object({
95
105
  userId: z.string().optional(),
96
106
  branchName: z.string().optional(),
97
107
  agentModel: z.string().optional(),
108
+ skillVersion: z.string().optional(),
109
+ cliVersion: z.string().optional(),
98
110
  agentTraceability: agentActionTraceabilitySchema.optional(),
99
111
  });
100
112
  export const createUserStoryInput = z.object({
@@ -687,6 +699,8 @@ export const reportAgentActionInput = z
687
699
  userId: z.string().optional(),
688
700
  branchName: z.string().optional(),
689
701
  agentModel: z.string().optional(),
702
+ skillVersion: z.string().optional(),
703
+ cliVersion: z.string().optional(),
690
704
  traceability: agentActionTraceabilitySchema.optional(),
691
705
  entityType: agentActionEntityTypeSchema,
692
706
  /** Project-scoped ordinal id (or explicitly provided execution/batch id). Mutually exclusive with `test`. */
@@ -842,3 +856,81 @@ export const getApiOperationDetailInput = z
842
856
  "or --root-file-path/--service-key with --http-method and --path-template",
843
857
  });
844
858
  });
859
+ /** Performance run kind persisted by the Phase 2 performance API. */
860
+ export const perfRunKindSchema = z.enum(["JOURNEY", "COMPOSITE"]);
861
+ export const listPerfRunsInput = z.object({
862
+ testchimpId: z.string().min(1).optional(),
863
+ kind: perfRunKindSchema.optional(),
864
+ branchName: z.string().min(1).optional(),
865
+ profile: z.string().min(1).optional(),
866
+ dataset: z.string().min(1).optional(),
867
+ llmMode: z.string().min(1).optional(),
868
+ environment: z.string().min(1).optional(),
869
+ limit: z.coerce.number().int().positive().max(100).optional(),
870
+ offset: z.coerce.number().int().nonnegative().optional(),
871
+ });
872
+ export const getPerfRunInput = z.object({
873
+ runId: z.string().min(1),
874
+ includeRaw: z.boolean().optional(),
875
+ });
876
+ export const listPerfBaselinesInput = z.object({
877
+ testchimpId: z.string().min(1).optional(),
878
+ limit: z.coerce.number().int().positive().max(100).optional(),
879
+ offset: z.coerce.number().int().nonnegative().optional(),
880
+ });
881
+ export const promotePerfBaselineInput = z
882
+ .object({
883
+ runId: z.string().min(1),
884
+ envClass: z.string().min(1),
885
+ })
886
+ .merge(agentTraceabilityFieldsSchema);
887
+ export const comparePerfToBaselineInput = z
888
+ .object({
889
+ runId: z.string().min(1).optional(),
890
+ testchimpId: z.string().min(1).optional(),
891
+ profile: z.string().min(1).optional(),
892
+ dataset: z.string().min(1).optional(),
893
+ llmMode: z.string().min(1).optional(),
894
+ environment: z.string().min(1).optional(),
895
+ envClass: z.string().min(1),
896
+ maxP95RegressionPercent: z.coerce.number().nonnegative().optional(),
897
+ maxFailRateIncrease: z.coerce.number().nonnegative().optional(),
898
+ })
899
+ .superRefine((v, ctx) => {
900
+ if (!(v.runId ?? "").trim() && !(v.testchimpId ?? "").trim()) {
901
+ ctx.addIssue({
902
+ code: z.ZodIssueCode.custom,
903
+ message: "Provide runId or testchimpId",
904
+ });
905
+ }
906
+ });
907
+ export const listRelatedPerfTestsInput = z
908
+ .object({
909
+ scenarioTitles: z.array(z.string().min(1)).min(1).optional(),
910
+ testchimpIds: z.array(z.string().min(1)).min(1).optional(),
911
+ includeComposites: z.boolean().default(true),
912
+ limit: z.coerce.number().int().positive().max(100).optional(),
913
+ })
914
+ .superRefine((v, ctx) => {
915
+ if (!v.scenarioTitles?.length && !v.testchimpIds?.length) {
916
+ ctx.addIssue({
917
+ code: z.ZodIssueCode.custom,
918
+ message: "Provide scenarioTitles and/or testchimpIds",
919
+ });
920
+ }
921
+ });
922
+ export const listApiOperationInteractionsInput = z
923
+ .object({
924
+ testId: z.string().min(1).optional(),
925
+ operationId: z.string().min(1).optional(),
926
+ interactionType: z.enum(["REAL", "MOCKED"]).default("REAL"),
927
+ limit: z.coerce.number().int().positive().max(100).optional(),
928
+ })
929
+ .superRefine((v, ctx) => {
930
+ if (!(v.testId ?? "").trim() && !(v.operationId ?? "").trim()) {
931
+ ctx.addIssue({
932
+ code: z.ZodIssueCode.custom,
933
+ message: "Provide testId and/or operationId",
934
+ });
935
+ }
936
+ });
@@ -2,7 +2,7 @@ import { normalizeScope } from "./normalize.js";
2
2
  import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralWait.js";
3
3
  import * as S from "./schemas.js";
4
4
  import { resolveGitHeadSha } from "./gitSha.js";
5
- import { buildAgentTraceabilityPayload } from "./agentTraceability.js";
5
+ import { buildAgentTraceabilityPayload, resolveToolchainVersions } from "./agentTraceability.js";
6
6
  function platformToProtoEnum(platform) {
7
7
  switch (platform) {
8
8
  case "ios":
@@ -51,6 +51,8 @@ function listCoverageBody(args) {
51
51
  return "MANUAL";
52
52
  if (raw === "smart_test")
53
53
  return "SMART_TEST";
54
+ if (raw === "perf_test")
55
+ return "PERF_TEST";
54
56
  return raw;
55
57
  });
56
58
  body.recordTypes = normalized;
@@ -1040,7 +1042,15 @@ export const TOOL_DEFINITIONS = [
1040
1042
  body.userId = process.env.TESTCHIMP_USER_ID;
1041
1043
  if (a.branchName)
1042
1044
  body.branchName = a.branchName;
1043
- // Nested traceability wins for agentModel; only fill from flat/env when nested omits it.
1045
+ // Nested traceability wins for agentModel / skillVersion / cliVersion;
1046
+ // fill from flat/env/package when nested omits them.
1047
+ const { skillVersion, cliVersion } = resolveToolchainVersions({
1048
+ skillVersion: a.skillVersion,
1049
+ cliVersion: a.cliVersion,
1050
+ nested: a.traceability && typeof a.traceability === "object"
1051
+ ? a.traceability
1052
+ : null,
1053
+ });
1044
1054
  if (a.traceability && typeof a.traceability === "object") {
1045
1055
  const nested = { ...a.traceability };
1046
1056
  const nestedModel = nested.agentModel != null && String(nested.agentModel).trim() !== ""
@@ -1053,12 +1063,23 @@ export const TOOL_DEFINITIONS = [
1053
1063
  else if (flatModel) {
1054
1064
  nested.agentModel = flatModel;
1055
1065
  }
1066
+ if (skillVersion)
1067
+ nested.skillVersion = skillVersion;
1068
+ if (cliVersion)
1069
+ nested.cliVersion = cliVersion;
1056
1070
  body.traceability = nested;
1057
1071
  }
1058
1072
  else {
1059
1073
  const model = a.agentModel?.trim() || process.env.TESTCHIMP_AGENT_MODEL?.trim();
1060
- if (model) {
1061
- body.traceability = { agentModel: model };
1074
+ const nested = {};
1075
+ if (model)
1076
+ nested.agentModel = model;
1077
+ if (skillVersion)
1078
+ nested.skillVersion = skillVersion;
1079
+ if (cliVersion)
1080
+ nested.cliVersion = cliVersion;
1081
+ if (Object.keys(nested).length > 0) {
1082
+ body.traceability = nested;
1062
1083
  }
1063
1084
  }
1064
1085
  if (a.test) {
@@ -1222,6 +1243,143 @@ export const TOOL_DEFINITIONS = [
1222
1243
  return postMcp("/api/mcp/get_api_operation_detail", body);
1223
1244
  },
1224
1245
  },
1246
+ {
1247
+ kebab: "list-perf-runs",
1248
+ description: "List performance runs, optionally filtered by TestChimp id, JOURNEY/COMPOSITE kind, branch, profile, dataset, LLM mode, or environment.",
1249
+ inputSchema: S.listPerfRunsInput,
1250
+ execute: async (args, { postMcp }) => {
1251
+ const a = args;
1252
+ const body = {};
1253
+ if (a.testchimpId)
1254
+ body.testchimpId = a.testchimpId;
1255
+ if (a.kind)
1256
+ body.kind = a.kind;
1257
+ if (a.branchName)
1258
+ body.branchName = a.branchName;
1259
+ if (a.profile)
1260
+ body.profile = a.profile;
1261
+ if (a.dataset)
1262
+ body.dataset = a.dataset;
1263
+ if (a.llmMode)
1264
+ body.llmMode = a.llmMode;
1265
+ if (a.environment)
1266
+ body.environment = a.environment;
1267
+ if (a.limit != null)
1268
+ body.limit = a.limit;
1269
+ if (a.offset != null)
1270
+ body.offset = a.offset;
1271
+ return postMcp("/api/mcp/list_perf_runs", body);
1272
+ },
1273
+ },
1274
+ {
1275
+ kebab: "get-perf-run",
1276
+ description: "Fetch one performance run by runId; set includeRaw to include its raw payload.",
1277
+ inputSchema: S.getPerfRunInput,
1278
+ execute: async (args, { postMcp }) => {
1279
+ const a = args;
1280
+ return postMcp("/api/mcp/get_perf_run", {
1281
+ runId: a.runId,
1282
+ ...(a.includeRaw != null ? { includeRaw: a.includeRaw } : {}),
1283
+ });
1284
+ },
1285
+ },
1286
+ {
1287
+ kebab: "list-perf-baselines",
1288
+ description: "List promoted performance baselines, optionally filtered by TestChimp id.",
1289
+ inputSchema: S.listPerfBaselinesInput,
1290
+ execute: async (args, { postMcp }) => {
1291
+ const a = args;
1292
+ const body = {};
1293
+ if (a.testchimpId)
1294
+ body.testchimpId = a.testchimpId;
1295
+ if (a.limit != null)
1296
+ body.limit = a.limit;
1297
+ if (a.offset != null)
1298
+ body.offset = a.offset;
1299
+ return postMcp("/api/mcp/list_perf_baselines", body);
1300
+ },
1301
+ },
1302
+ {
1303
+ kebab: "promote-perf-baseline",
1304
+ description: "Promote a performance run as the baseline for an environment class. Optional agent traceability records the mutation.",
1305
+ inputSchema: S.promotePerfBaselineInput,
1306
+ execute: async (args, { postMcp }) => {
1307
+ const a = args;
1308
+ const body = {
1309
+ runId: a.runId,
1310
+ envClass: a.envClass,
1311
+ };
1312
+ const trace = buildAgentTraceabilityPayload(a);
1313
+ if (trace)
1314
+ body.agentTraceability = trace;
1315
+ return postMcp("/api/mcp/promote_perf_baseline", body);
1316
+ },
1317
+ },
1318
+ {
1319
+ kebab: "compare-perf-to-baseline",
1320
+ description: "Compare a run (runId) or filtered target (testchimpId plus optional dimensions) to its promoted baseline. " +
1321
+ "envClass is required. Optional thresholds override max p95 regression percent and maximum fail-rate increase.",
1322
+ inputSchema: S.comparePerfToBaselineInput,
1323
+ execute: async (args, { postMcp }) => {
1324
+ const a = args;
1325
+ const body = {};
1326
+ if (a.runId)
1327
+ body.runId = a.runId;
1328
+ if (a.testchimpId)
1329
+ body.testchimpId = a.testchimpId;
1330
+ if (a.profile)
1331
+ body.profile = a.profile;
1332
+ if (a.dataset)
1333
+ body.dataset = a.dataset;
1334
+ if (a.llmMode)
1335
+ body.llmMode = a.llmMode;
1336
+ if (a.environment)
1337
+ body.environment = a.environment;
1338
+ body.envClass = a.envClass;
1339
+ if (a.maxP95RegressionPercent != null)
1340
+ body.maxP95RegressionPercent = a.maxP95RegressionPercent;
1341
+ if (a.maxFailRateIncrease != null)
1342
+ body.maxFailRateIncrease = a.maxFailRateIncrease;
1343
+ return postMcp("/api/mcp/compare_perf_to_baseline", body);
1344
+ },
1345
+ },
1346
+ {
1347
+ kebab: "list-related-perf-tests",
1348
+ description: "Find JOURNEY and, by default, COMPOSITE performance tests related to scenario titles and/or TestChimp ids.",
1349
+ inputSchema: S.listRelatedPerfTestsInput,
1350
+ execute: async (args, { postMcp }) => {
1351
+ const a = args;
1352
+ const body = {
1353
+ includeComposites: a.includeComposites,
1354
+ };
1355
+ if (a.scenarioTitles?.length)
1356
+ body.scenarioTitles = a.scenarioTitles;
1357
+ if (a.testchimpIds?.length)
1358
+ body.testchimpIds = a.testchimpIds;
1359
+ if (a.limit != null)
1360
+ body.limit = a.limit;
1361
+ return postMcp("/api/mcp/list_related_perf_tests", body);
1362
+ },
1363
+ },
1364
+ {
1365
+ kebab: "list-api-operation-interactions",
1366
+ description: "List recorded API operation interactions. Requires testId and/or operationId. " +
1367
+ "Defaults to REAL interactions; limit is capped at 100.",
1368
+ inputSchema: S.listApiOperationInteractionsInput,
1369
+ execute: async (args, { postMcp }) => {
1370
+ const a = args;
1371
+ const body = {
1372
+ interactionType: a.interactionType,
1373
+ };
1374
+ if (a.testId)
1375
+ body.testId = a.testId;
1376
+ if (a.operationId)
1377
+ body.operationId = a.operationId;
1378
+ if (a.limit != null)
1379
+ body.limit = a.limit;
1380
+ return postMcp("/api/mcp/list_api_operation_interactions", body);
1381
+ },
1382
+ },
1225
1383
  ];
1226
1384
  const TOOL_BY_KEBAB = new Map(TOOL_DEFINITIONS.map((t) => [t.kebab, t]));
1227
1385
  export function getToolDefinition(kebab) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "TestChimp CLI and MCP server — coverage, plans, EaaS, TrueCoverage, API operations (calls /api/mcp/*)",
5
5
  "type": "module",
6
6
  "main": "dist/bin/testchimp.js",