@testchimp/cli 0.1.28 → 0.1.30

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.
@@ -55,7 +55,9 @@ function addAgentTraceabilityOptions(cmd) {
55
55
  .option("--actor-type <type>", "LOCAL_AGENT | CLOUD_AGENT")
56
56
  .option("--user-id <id>", "Optional user id")
57
57
  .option("--branch-name <name>", "Git branch name")
58
- .option("--agent-model <model>", "Optional agent model id (agent/CLI only)");
58
+ .option("--agent-model <model>", "Optional agent model id (agent/CLI only)")
59
+ .option("--skill-version <semver>", "TestChimp skill version from SKILL.md frontmatter")
60
+ .option("--cli-version <semver>", "CLI version (defaults to this package version)");
59
61
  }
60
62
  function collectAgentTraceabilityFlags(opts) {
61
63
  const body = {};
@@ -77,6 +79,10 @@ function collectAgentTraceabilityFlags(opts) {
77
79
  body.branchName = String(opts.branchName).trim();
78
80
  if (opts.agentModel)
79
81
  body.agentModel = String(opts.agentModel).trim();
82
+ if (opts.skillVersion)
83
+ body.skillVersion = String(opts.skillVersion).trim();
84
+ if (opts.cliVersion)
85
+ body.cliVersion = String(opts.cliVersion).trim();
80
86
  return body;
81
87
  }
82
88
  function stderrProgress(msg) {
@@ -127,8 +133,14 @@ export function buildCliProgram() {
127
133
  .option("--branch-name <s>")
128
134
  .option("--platform <web|ios|android>")
129
135
  .option("--record-types <csv>", "coverage sources: smart_test,manual (aliases: automated,smarttest)")
130
- .option("--include-manual", "include manual sessions in addition to automated (default)")
136
+ .option("--include-manual", "include manual session coverage in addition to automated SmartTests")
131
137
  .option("--manual-only", "manual-only coverage (no automated)")
138
+ .option("--lifecycle-statuses <csv>", "scenario lifecycle allowlist (e.g. ready or draft,ready)")
139
+ .option("--limit <n>", "top N gaps after filter+rank into rankedScenarios (max 200)", (v) => parseInt(v, 10))
140
+ .option("--consider-scenario-priority", "rank by scenario priority high→medium→low→unset")
141
+ .option("--consider-semantic-coverage", "reserved ranking signal (accepted; no server effect yet)")
142
+ .option("--auto-verification-only", "exclude verification_strategy=manual (server default when unset)")
143
+ .option("--include-manual-verification", "include verification_strategy=manual (overrides --auto-verification-only)")
132
144
  .option("--file-paths <csv>", "comma-separated paths under platform tests root")
133
145
  .option("--folder-path <path>", "folder under tests root, slash-separated")
134
146
  .action(async (opts) => {
@@ -150,6 +162,22 @@ export function buildCliProgram() {
150
162
  recordTypes = ["manual"];
151
163
  if (recordTypes && recordTypes.length > 0)
152
164
  body.recordTypes = recordTypes;
165
+ if (opts.lifecycleStatuses) {
166
+ body.scenarioLifecycleStatuses = String(opts.lifecycleStatuses)
167
+ .split(",")
168
+ .map((s) => s.trim())
169
+ .filter(Boolean);
170
+ }
171
+ if (opts.limit != null && !Number.isNaN(opts.limit))
172
+ body.limit = opts.limit;
173
+ if (opts.considerScenarioPriority)
174
+ body.considerScenarioPriority = true;
175
+ if (opts.considerSemanticCoverage)
176
+ body.considerSemanticCoverage = true;
177
+ if (opts.includeManualVerification)
178
+ body.autoVerificationOnly = false;
179
+ else if (opts.autoVerificationOnly)
180
+ body.autoVerificationOnly = true;
153
181
  const scope = {};
154
182
  if (opts.filePaths)
155
183
  scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
@@ -198,6 +226,43 @@ export function buildCliProgram() {
198
226
  const out = await runTool("get-execution-history", merged, { postMcp });
199
227
  console.log(out);
200
228
  });
229
+ program
230
+ .command("get-suite-execution-stats")
231
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-suite-execution-stats").description)
232
+ .addOption(jsonInputOption())
233
+ .option("--release <s>")
234
+ .option("--environment <s>")
235
+ .option("--branch-name <s>")
236
+ .option("--scenario-id <id>")
237
+ .option("--test-id <id>")
238
+ .option("--platform <web|ios|android>")
239
+ .option("--file-paths <csv>")
240
+ .option("--folder-path <path>")
241
+ .action(async (opts) => {
242
+ const body = {};
243
+ if (opts.release)
244
+ body.release = opts.release;
245
+ if (opts.environment)
246
+ body.environment = opts.environment;
247
+ if (opts.branchName)
248
+ body.branchName = opts.branchName;
249
+ if (opts.scenarioId)
250
+ body.scenarioId = opts.scenarioId;
251
+ if (opts.testId)
252
+ body.testId = opts.testId;
253
+ if (opts.platform)
254
+ body.platform = opts.platform;
255
+ const scope = {};
256
+ if (opts.filePaths)
257
+ scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
258
+ if (opts.folderPath)
259
+ scope.folderPath = opts.folderPath;
260
+ if (Object.keys(scope).length)
261
+ body.scope = scope;
262
+ const merged = mergeBodies(body, opts.jsonInput);
263
+ const out = await runTool("get-suite-execution-stats", merged, { postMcp });
264
+ console.log(out);
265
+ });
201
266
  program
202
267
  .command("fetch-execution-report")
203
268
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "fetch-execution-report").description)
@@ -481,6 +546,40 @@ export function buildCliProgram() {
481
546
  }, { postMcp });
482
547
  console.log(out);
483
548
  });
549
+ program
550
+ .command("get-spec-lifecycle-details")
551
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-spec-lifecycle-details").description)
552
+ .addOption(jsonInputOption())
553
+ .option("--scenario-ids <csv>", "comma-separated scenario ordinals (bare 107 or TS-107 / #TS-107)")
554
+ .option("--story-ids <csv>", "comma-separated story ordinals (bare 12 or US-12 / #US-12)")
555
+ .action(async (opts) => {
556
+ const body = {};
557
+ if (opts.scenarioIds) {
558
+ const ids = String(opts.scenarioIds)
559
+ .split(",")
560
+ .map((s) => s.trim())
561
+ .filter((s) => s.length > 0);
562
+ if (ids.length > 0)
563
+ body.scenarioIds = ids;
564
+ }
565
+ if (opts.storyIds) {
566
+ const ids = String(opts.storyIds)
567
+ .split(",")
568
+ .map((s) => s.trim())
569
+ .filter((s) => s.length > 0);
570
+ if (ids.length > 0)
571
+ body.storyIds = ids;
572
+ }
573
+ const merged = mergeBodies(body, opts.jsonInput);
574
+ if (Array.isArray(merged.scenarioIds) && merged.scenarioIds.length === 0) {
575
+ delete merged.scenarioIds;
576
+ }
577
+ if (Array.isArray(merged.storyIds) && merged.storyIds.length === 0) {
578
+ delete merged.storyIds;
579
+ }
580
+ const out = await runTool("get-spec-lifecycle-details", merged, { postMcp });
581
+ console.log(out);
582
+ });
484
583
  addAgentTraceabilityOptions(program
485
584
  .command("update-test-scenario")
486
585
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-test-scenario").description)
@@ -1026,6 +1125,8 @@ export function buildCliProgram() {
1026
1125
  .option("--user-id <id>", "Optional user id for traceability")
1027
1126
  .option("--branch-name <name>", "Git branch")
1028
1127
  .option("--agent-model <model>", "Optional agent model id (agent/CLI only)")
1128
+ .option("--skill-version <semver>", "TestChimp skill version from SKILL.md frontmatter")
1129
+ .option("--cli-version <semver>", "CLI version (defaults to this package version)")
1029
1130
  .requiredOption("--entity-type <type>", "USER_STORY|SCENARIO|SMART_TEST|POLICY|ISSUE|TEST_EXECUTION|TEST_INVOCATION_BATCH|EXPLORATION|EVENT|WORKFLOW")
1030
1131
  .option("--entity-identity <ordinal>", "Project-scoped ordinal id (mutually exclusive with --test-json)")
1031
1132
  .option("--test-json <json>", "TestLocator JSON (folderPath/fileName/testSuite/testName)")
@@ -1051,6 +1152,10 @@ export function buildCliProgram() {
1051
1152
  body.branchName = String(opts.branchName);
1052
1153
  if (opts.agentModel)
1053
1154
  body.agentModel = String(opts.agentModel).trim();
1155
+ if (opts.skillVersion)
1156
+ body.skillVersion = String(opts.skillVersion).trim();
1157
+ if (opts.cliVersion)
1158
+ body.cliVersion = String(opts.cliVersion).trim();
1054
1159
  if (opts.entityIdentity)
1055
1160
  body.entityIdentity = String(opts.entityIdentity);
1056
1161
  if (opts.testJson)
@@ -1075,6 +1180,7 @@ export function buildCliProgram() {
1075
1180
  console.log(await runTool("get-last-run-workflow-detail", merged, { postMcp }));
1076
1181
  });
1077
1182
  for (const kebab of [
1183
+ "get-org-capabilities",
1078
1184
  "list-workflow-executions",
1079
1185
  "get-workflow-execution",
1080
1186
  "get-policy",
@@ -9,14 +9,31 @@ 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
18
32
  * **and** a non-empty workflowId (server requires workflow_id for inline Activity).
19
- * Auto-fills gitSha / agentModel / userId only after that bar is met.
33
+ * For Activity/timeline attachment the server also requires workflowExecutionId
34
+ * (stable Plan ULID for the whole run) — omit it and the mutation still succeeds
35
+ * but no workflow_executions / Activity row is recorded (server does not auto-mint).
36
+ * Auto-fills gitSha / agentModel / userId / cliVersion only after the workflowId bar is met.
20
37
  * Non-empty nested `agentTraceability` wins over flat for overlapping keys.
21
38
  */
22
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,11 +44,28 @@ 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
45
64
  * **and** a non-empty workflowId (server requires workflow_id for inline Activity).
46
- * Auto-fills gitSha / agentModel / userId only after that bar is met.
65
+ * For Activity/timeline attachment the server also requires workflowExecutionId
66
+ * (stable Plan ULID for the whole run) — omit it and the mutation still succeeds
67
+ * but no workflow_executions / Activity row is recorded (server does not auto-mint).
68
+ * Auto-fills gitSha / agentModel / userId / cliVersion only after the workflowId bar is met.
47
69
  * Non-empty nested `agentTraceability` wins over flat for overlapping keys.
48
70
  */
49
71
  export function buildAgentTraceabilityPayload(a) {
@@ -69,6 +91,11 @@ export function buildAgentTraceabilityPayload(a) {
69
91
  const agentModel = nonEmptyString(nested?.agentModel) ??
70
92
  nonEmptyString(a.agentModel) ??
71
93
  nonEmptyString(process.env.TESTCHIMP_AGENT_MODEL);
94
+ const { skillVersion, cliVersion } = resolveToolchainVersions({
95
+ skillVersion: a.skillVersion,
96
+ cliVersion: a.cliVersion,
97
+ nested,
98
+ });
72
99
  // Server requires workflow_id for inline mutation Activity — do not send orphan payloads.
73
100
  if (!workflowId) {
74
101
  return undefined;
@@ -91,5 +118,9 @@ export function buildAgentTraceabilityPayload(a) {
91
118
  out.branchName = branchName;
92
119
  if (agentModel)
93
120
  out.agentModel = agentModel;
121
+ if (skillVersion)
122
+ out.skillVersion = skillVersion;
123
+ if (cliVersion)
124
+ out.cliVersion = cliVersion;
94
125
  return out;
95
126
  }
@@ -28,6 +28,11 @@ export declare const listCoverageInput: z.ZodObject<{
28
28
  SMART_TEST: "SMART_TEST";
29
29
  MANUAL: "MANUAL";
30
30
  }>>>;
31
+ scenarioLifecycleStatuses: z.ZodOptional<z.ZodArray<z.ZodString>>;
32
+ limit: z.ZodOptional<z.ZodNumber>;
33
+ considerScenarioPriority: z.ZodOptional<z.ZodBoolean>;
34
+ considerSemanticCoverage: z.ZodOptional<z.ZodBoolean>;
35
+ autoVerificationOnly: z.ZodOptional<z.ZodBoolean>;
31
36
  }, z.core.$strip>;
32
37
  export declare const listExecutionInput: z.ZodObject<{
33
38
  release: z.ZodOptional<z.ZodString>;
@@ -51,6 +56,29 @@ export declare const listExecutionInput: z.ZodObject<{
51
56
  limit: z.ZodOptional<z.ZodNumber>;
52
57
  offset: z.ZodOptional<z.ZodNumber>;
53
58
  }, z.core.$strip>;
59
+ /** Same filters as get-execution-history; rolls up list_execution_history testStats. */
60
+ export declare const suiteExecutionStatsInput: z.ZodObject<{
61
+ release: z.ZodOptional<z.ZodString>;
62
+ environment: z.ZodOptional<z.ZodString>;
63
+ scope: z.ZodOptional<z.ZodObject<{
64
+ filePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
65
+ folderPath: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>;
66
+ }, z.core.$strip>>;
67
+ branchName: z.ZodOptional<z.ZodString>;
68
+ scenarioId: z.ZodOptional<z.ZodString>;
69
+ testId: z.ZodOptional<z.ZodString>;
70
+ platform: z.ZodOptional<z.ZodEnum<{
71
+ web: "web";
72
+ ios: "ios";
73
+ android: "android";
74
+ }>>;
75
+ dimensionFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
76
+ dimension: z.ZodString;
77
+ values: z.ZodArray<z.ZodString>;
78
+ }, z.core.$strip>>>;
79
+ limit: z.ZodOptional<z.ZodNumber>;
80
+ offset: z.ZodOptional<z.ZodNumber>;
81
+ }, z.core.$strip>;
54
82
  export declare const fetchExecutionReportInput: z.ZodObject<{
55
83
  batchInvocationId: z.ZodOptional<z.ZodString>;
56
84
  jobId: z.ZodOptional<z.ZodString>;
@@ -77,6 +105,8 @@ export declare const agentActionTraceabilitySchema: z.ZodObject<{
77
105
  userId: z.ZodOptional<z.ZodString>;
78
106
  branchName: z.ZodOptional<z.ZodString>;
79
107
  agentModel: z.ZodOptional<z.ZodString>;
108
+ skillVersion: z.ZodOptional<z.ZodString>;
109
+ cliVersion: z.ZodOptional<z.ZodString>;
80
110
  }, z.core.$strict>;
81
111
  /** Flat + nested traceability fields shared by create/update MCP tools. */
82
112
  export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
@@ -94,6 +124,8 @@ export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
94
124
  userId: z.ZodOptional<z.ZodString>;
95
125
  branchName: z.ZodOptional<z.ZodString>;
96
126
  agentModel: z.ZodOptional<z.ZodString>;
127
+ skillVersion: z.ZodOptional<z.ZodString>;
128
+ cliVersion: z.ZodOptional<z.ZodString>;
97
129
  agentTraceability: z.ZodOptional<z.ZodObject<{
98
130
  workflowId: z.ZodOptional<z.ZodString>;
99
131
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -109,6 +141,8 @@ export declare const agentTraceabilityFieldsSchema: z.ZodObject<{
109
141
  userId: z.ZodOptional<z.ZodString>;
110
142
  branchName: z.ZodOptional<z.ZodString>;
111
143
  agentModel: z.ZodOptional<z.ZodString>;
144
+ skillVersion: z.ZodOptional<z.ZodString>;
145
+ cliVersion: z.ZodOptional<z.ZodString>;
112
146
  }, z.core.$strict>>;
113
147
  }, z.core.$strip>;
114
148
  export declare const createUserStoryInput: z.ZodObject<{
@@ -128,6 +162,8 @@ export declare const createUserStoryInput: z.ZodObject<{
128
162
  userId: z.ZodOptional<z.ZodString>;
129
163
  branchName: z.ZodOptional<z.ZodString>;
130
164
  agentModel: z.ZodOptional<z.ZodString>;
165
+ skillVersion: z.ZodOptional<z.ZodString>;
166
+ cliVersion: z.ZodOptional<z.ZodString>;
131
167
  agentTraceability: z.ZodOptional<z.ZodObject<{
132
168
  workflowId: z.ZodOptional<z.ZodString>;
133
169
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -143,6 +179,8 @@ export declare const createUserStoryInput: z.ZodObject<{
143
179
  userId: z.ZodOptional<z.ZodString>;
144
180
  branchName: z.ZodOptional<z.ZodString>;
145
181
  agentModel: z.ZodOptional<z.ZodString>;
182
+ skillVersion: z.ZodOptional<z.ZodString>;
183
+ cliVersion: z.ZodOptional<z.ZodString>;
146
184
  }, z.core.$strict>>;
147
185
  }, z.core.$strip>;
148
186
  export declare const createTestScenarioInput: z.ZodObject<{
@@ -163,6 +201,8 @@ export declare const createTestScenarioInput: z.ZodObject<{
163
201
  userId: z.ZodOptional<z.ZodString>;
164
202
  branchName: z.ZodOptional<z.ZodString>;
165
203
  agentModel: z.ZodOptional<z.ZodString>;
204
+ skillVersion: z.ZodOptional<z.ZodString>;
205
+ cliVersion: z.ZodOptional<z.ZodString>;
166
206
  agentTraceability: z.ZodOptional<z.ZodObject<{
167
207
  workflowId: z.ZodOptional<z.ZodString>;
168
208
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -178,6 +218,8 @@ export declare const createTestScenarioInput: z.ZodObject<{
178
218
  userId: z.ZodOptional<z.ZodString>;
179
219
  branchName: z.ZodOptional<z.ZodString>;
180
220
  agentModel: z.ZodOptional<z.ZodString>;
221
+ skillVersion: z.ZodOptional<z.ZodString>;
222
+ cliVersion: z.ZodOptional<z.ZodString>;
181
223
  }, z.core.$strict>>;
182
224
  }, z.core.$strip>;
183
225
  export declare const updatePlanMarkdownInput: z.ZodObject<{
@@ -196,6 +238,8 @@ export declare const updatePlanMarkdownInput: z.ZodObject<{
196
238
  userId: z.ZodOptional<z.ZodString>;
197
239
  branchName: z.ZodOptional<z.ZodString>;
198
240
  agentModel: z.ZodOptional<z.ZodString>;
241
+ skillVersion: z.ZodOptional<z.ZodString>;
242
+ cliVersion: z.ZodOptional<z.ZodString>;
199
243
  agentTraceability: z.ZodOptional<z.ZodObject<{
200
244
  workflowId: z.ZodOptional<z.ZodString>;
201
245
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -211,6 +255,8 @@ export declare const updatePlanMarkdownInput: z.ZodObject<{
211
255
  userId: z.ZodOptional<z.ZodString>;
212
256
  branchName: z.ZodOptional<z.ZodString>;
213
257
  agentModel: z.ZodOptional<z.ZodString>;
258
+ skillVersion: z.ZodOptional<z.ZodString>;
259
+ cliVersion: z.ZodOptional<z.ZodString>;
214
260
  }, z.core.$strict>>;
215
261
  }, z.core.$strip>;
216
262
  export declare const markPlanItemsImplementationDoneInput: z.ZodObject<{
@@ -222,6 +268,10 @@ export declare const updatePlanItemsLifecycleStatusInput: z.ZodObject<{
222
268
  ordinalId: z.ZodCoercedNumber<unknown>;
223
269
  status: z.ZodString;
224
270
  }, z.core.$strip>;
271
+ export declare const getSpecLifecycleDetailsInput: z.ZodObject<{
272
+ scenarioIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>, z.ZodString>>>;
273
+ storyIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>, z.ZodString>>>;
274
+ }, z.core.$strip>;
225
275
  export declare const getUserStoriesInput: z.ZodObject<{
226
276
  userStoryOrdinalIds: z.ZodArray<z.ZodCoercedNumber<unknown>>;
227
277
  }, z.core.$strip>;
@@ -265,6 +315,8 @@ export declare const updateIssueStatusInput: z.ZodObject<{
265
315
  userId: z.ZodOptional<z.ZodString>;
266
316
  branchName: z.ZodOptional<z.ZodString>;
267
317
  agentModel: z.ZodOptional<z.ZodString>;
318
+ skillVersion: z.ZodOptional<z.ZodString>;
319
+ cliVersion: z.ZodOptional<z.ZodString>;
268
320
  agentTraceability: z.ZodOptional<z.ZodObject<{
269
321
  workflowId: z.ZodOptional<z.ZodString>;
270
322
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -280,6 +332,8 @@ export declare const updateIssueStatusInput: z.ZodObject<{
280
332
  userId: z.ZodOptional<z.ZodString>;
281
333
  branchName: z.ZodOptional<z.ZodString>;
282
334
  agentModel: z.ZodOptional<z.ZodString>;
335
+ skillVersion: z.ZodOptional<z.ZodString>;
336
+ cliVersion: z.ZodOptional<z.ZodString>;
283
337
  }, z.core.$strict>>;
284
338
  }, z.core.$strip>;
285
339
  export declare const createIssueInput: z.ZodObject<{
@@ -361,6 +415,8 @@ export declare const createIssueInput: z.ZodObject<{
361
415
  userId: z.ZodOptional<z.ZodString>;
362
416
  branchName: z.ZodOptional<z.ZodString>;
363
417
  agentModel: z.ZodOptional<z.ZodString>;
418
+ skillVersion: z.ZodOptional<z.ZodString>;
419
+ cliVersion: z.ZodOptional<z.ZodString>;
364
420
  agentTraceability: z.ZodOptional<z.ZodObject<{
365
421
  workflowId: z.ZodOptional<z.ZodString>;
366
422
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -376,6 +432,8 @@ export declare const createIssueInput: z.ZodObject<{
376
432
  userId: z.ZodOptional<z.ZodString>;
377
433
  branchName: z.ZodOptional<z.ZodString>;
378
434
  agentModel: z.ZodOptional<z.ZodString>;
435
+ skillVersion: z.ZodOptional<z.ZodString>;
436
+ cliVersion: z.ZodOptional<z.ZodString>;
379
437
  }, z.core.$strict>>;
380
438
  }, z.core.$strip>;
381
439
  export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
@@ -1332,6 +1390,8 @@ export declare const reportAgentActionInput: z.ZodObject<{
1332
1390
  userId: z.ZodOptional<z.ZodString>;
1333
1391
  branchName: z.ZodOptional<z.ZodString>;
1334
1392
  agentModel: z.ZodOptional<z.ZodString>;
1393
+ skillVersion: z.ZodOptional<z.ZodString>;
1394
+ cliVersion: z.ZodOptional<z.ZodString>;
1335
1395
  traceability: z.ZodOptional<z.ZodObject<{
1336
1396
  workflowId: z.ZodOptional<z.ZodString>;
1337
1397
  workflowExecutionId: z.ZodOptional<z.ZodString>;
@@ -1347,6 +1407,8 @@ export declare const reportAgentActionInput: z.ZodObject<{
1347
1407
  userId: z.ZodOptional<z.ZodString>;
1348
1408
  branchName: z.ZodOptional<z.ZodString>;
1349
1409
  agentModel: z.ZodOptional<z.ZodString>;
1410
+ skillVersion: z.ZodOptional<z.ZodString>;
1411
+ cliVersion: z.ZodOptional<z.ZodString>;
1350
1412
  }, z.core.$strict>>;
1351
1413
  entityType: z.ZodEnum<{
1352
1414
  SMART_TEST: "SMART_TEST";
@@ -26,6 +26,19 @@ export const listCoverageInput = z.object({
26
26
  * When provided, send proto enum names ("SMART_TEST", "MANUAL") or CLI-friendly aliases ("smart_test", "manual").
27
27
  */
28
28
  recordTypes: z.array(requirementCoverageRecordTypeSchema).optional(),
29
+ /** Allowlist of scenario lifecycle statuses (e.g. ["ready"] or ["draft","ready"]). Empty/omit = no status filter. */
30
+ scenarioLifecycleStatuses: z.array(z.string().min(1)).optional(),
31
+ /** When set (>0), truncate rankedScenarios to top N after filter+rank (server clamps to 200). */
32
+ limit: z.number().int().positive().max(200).optional(),
33
+ /** Rank by scenario priority high > medium > low > unset. */
34
+ considerScenarioPriority: z.boolean().optional(),
35
+ /** Reserved for future semantic-gap ranking; accepted by server, ignored in v1. */
36
+ considerSemanticCoverage: z.boolean().optional(),
37
+ /**
38
+ * Exclude scenarios with verification_strategy=manual. Server defaults to true when unset.
39
+ * Prefer --include-manual-verification (sets false) over setting this explicitly.
40
+ */
41
+ autoVerificationOnly: z.boolean().optional(),
29
42
  });
30
43
  export const listExecutionInput = z.object({
31
44
  release: z.string().optional(),
@@ -39,6 +52,8 @@ export const listExecutionInput = z.object({
39
52
  limit: z.number().int().positive().max(500).optional(),
40
53
  offset: z.number().int().nonnegative().optional(),
41
54
  });
55
+ /** Same filters as get-execution-history; rolls up list_execution_history testStats. */
56
+ export const suiteExecutionStatsInput = listExecutionInput;
42
57
  export const fetchExecutionReportInput = z
43
58
  .object({
44
59
  batchInvocationId: z.string().optional(),
@@ -67,6 +82,8 @@ export const agentActionTraceabilitySchema = z
67
82
  userId: z.string().optional(),
68
83
  branchName: z.string().optional(),
69
84
  agentModel: z.string().optional(),
85
+ skillVersion: z.string().optional(),
86
+ cliVersion: z.string().optional(),
70
87
  })
71
88
  .strict();
72
89
  /** Flat + nested traceability fields shared by create/update MCP tools. */
@@ -80,6 +97,8 @@ export const agentTraceabilityFieldsSchema = z.object({
80
97
  userId: z.string().optional(),
81
98
  branchName: z.string().optional(),
82
99
  agentModel: z.string().optional(),
100
+ skillVersion: z.string().optional(),
101
+ cliVersion: z.string().optional(),
83
102
  agentTraceability: agentActionTraceabilitySchema.optional(),
84
103
  });
85
104
  export const createUserStoryInput = z.object({
@@ -105,6 +124,19 @@ export const updatePlanItemsLifecycleStatusInput = z.object({
105
124
  /** draft | ready | in progress | blocked | done | archived */
106
125
  status: z.string().min(1),
107
126
  });
127
+ export const getSpecLifecycleDetailsInput = z
128
+ .object({
129
+ /** Bare ordinals or TS-/ #TS- forms; numbers coerced to strings. */
130
+ scenarioIds: z
131
+ .array(z.union([z.string(), z.number()]).transform((v) => String(v).trim()).pipe(z.string().min(1)))
132
+ .optional(),
133
+ /** Bare ordinals or US-/ #US- forms; numbers coerced to strings. */
134
+ storyIds: z
135
+ .array(z.union([z.string(), z.number()]).transform((v) => String(v).trim()).pipe(z.string().min(1)))
136
+ .optional(),
137
+ })
138
+ .refine((v) => (v.scenarioIds != null && v.scenarioIds.length > 0) ||
139
+ (v.storyIds != null && v.storyIds.length > 0), { message: "Provide scenarioIds and/or storyIds (non-empty)" });
108
140
  export const getUserStoriesInput = z
109
141
  .object({
110
142
  userStoryOrdinalIds: z.array(z.coerce.number().int().positive()).min(1),
@@ -659,6 +691,8 @@ export const reportAgentActionInput = z
659
691
  userId: z.string().optional(),
660
692
  branchName: z.string().optional(),
661
693
  agentModel: z.string().optional(),
694
+ skillVersion: z.string().optional(),
695
+ cliVersion: z.string().optional(),
662
696
  traceability: agentActionTraceabilitySchema.optional(),
663
697
  entityType: agentActionEntityTypeSchema,
664
698
  /** Project-scoped ordinal id (or explicitly provided execution/batch id). Mutually exclusive with `test`. */
@@ -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":
@@ -55,6 +55,17 @@ function listCoverageBody(args) {
55
55
  });
56
56
  body.recordTypes = normalized;
57
57
  }
58
+ if (args.scenarioLifecycleStatuses != null && args.scenarioLifecycleStatuses.length > 0) {
59
+ body.scenarioLifecycleStatuses = args.scenarioLifecycleStatuses.map((s) => String(s).trim()).filter(Boolean);
60
+ }
61
+ if (args.limit != null)
62
+ body.limit = args.limit;
63
+ if (args.considerScenarioPriority != null)
64
+ body.considerScenarioPriority = args.considerScenarioPriority;
65
+ if (args.considerSemanticCoverage != null)
66
+ body.considerSemanticCoverage = args.considerSemanticCoverage;
67
+ if (args.autoVerificationOnly != null)
68
+ body.autoVerificationOnly = args.autoVerificationOnly;
58
69
  return body;
59
70
  }
60
71
  function listExecutionBody(args) {
@@ -89,6 +100,45 @@ function listExecutionBody(args) {
89
100
  body.offset = args.offset;
90
101
  return body;
91
102
  }
103
+ /** Mean success duration above this counts as a slow test (mirrors UI ExecutionTimingSummary). */
104
+ const SLOW_TEST_MEAN_SECS = 30;
105
+ function parseMeanSecs(raw) {
106
+ if (raw == null)
107
+ return null;
108
+ const n = Number(raw);
109
+ return Number.isFinite(n) && !Number.isNaN(n) && n >= 0 ? n : null;
110
+ }
111
+ /** Suite rollup from list_execution_history testStats (sum of success means = UI Total time). */
112
+ function aggregateSuiteExecutionStats(testStats) {
113
+ const stats = Array.isArray(testStats) ? testStats : [];
114
+ let timedTestCount = 0;
115
+ let sumSuccessMeanSecs = 0;
116
+ let sumFailMeanSecs = 0;
117
+ let maxSuccessMeanSecs = 0;
118
+ let slowTestCount = 0;
119
+ for (const s of stats) {
120
+ const successMean = parseMeanSecs(s.successTiming?.meanSecs);
121
+ if (successMean != null) {
122
+ timedTestCount += 1;
123
+ sumSuccessMeanSecs += successMean;
124
+ if (successMean > maxSuccessMeanSecs)
125
+ maxSuccessMeanSecs = successMean;
126
+ if (successMean > SLOW_TEST_MEAN_SECS)
127
+ slowTestCount += 1;
128
+ }
129
+ const failMean = parseMeanSecs(s.failTiming?.meanSecs);
130
+ if (failMean != null)
131
+ sumFailMeanSecs += failMean;
132
+ }
133
+ return {
134
+ testCount: stats.length,
135
+ timedTestCount,
136
+ sumSuccessMeanSecs,
137
+ sumFailMeanSecs,
138
+ maxSuccessMeanSecs: timedTestCount > 0 ? maxSuccessMeanSecs : 0,
139
+ slowTestCount,
140
+ };
141
+ }
92
142
  function requirementQualitySubjectBody(subjectType, opts) {
93
143
  const body = { subjectType };
94
144
  const entityId = (opts.subjectEntityId ?? "").trim();
@@ -127,12 +177,22 @@ async function loadRequirementQualityReportJson(args) {
127
177
  return {};
128
178
  }
129
179
  export const TOOL_DEFINITIONS = [
180
+ {
181
+ kebab: "get-org-capabilities",
182
+ description: "Fetch the organization's enabled capabilities (e.g. TRUE_COVERAGE, API_CONTRACT_COVERAGE) and " +
183
+ "freeTrialActive flag. Call before relying on TrueCoverage / API contract coverage features so " +
184
+ "playbooks can soft-skip gated insights instead of failing. Authenticated via project API key.",
185
+ inputSchema: S.emptyInput,
186
+ execute: async (_args, { postMcp }) => postMcp("/api/mcp/get_org_capabilities", {}),
187
+ },
130
188
  {
131
189
  kebab: "get-requirement-coverage",
132
190
  description: "Fetch requirement (scenario) coverage under an optional platform-rooted folder scope (tests/... or plans/...). " +
133
191
  "Use scope.filePaths or scope.folderPath (platform tests/plans roots). Omit branchName for cross-branch coverage " +
134
192
  "(aggregates branch copies; execution jobs deduped by stable hash of tests-root-relative path + test name). " +
135
- "Pass branchName only when results must be limited to one Git branch. Optional platform (web|ios|android) filters rollup.",
193
+ "Pass branchName only when results must be limited to one Git branch. Optional platform (web|ios|android) filters rollup. " +
194
+ "For top-N gap recommendations: set scenarioLifecycleStatuses, considerScenarioPriority / considerSemanticCoverage, and limit; " +
195
+ "prefer response rankedScenarios (gaps only). Server excludes verification_strategy=manual by default (autoVerificationOnly).",
136
196
  inputSchema: S.listCoverageInput,
137
197
  execute: async (args, { postMcp }) => {
138
198
  const json = await postMcp("/api/mcp/list_requirement_coverage", listCoverageBody(args));
@@ -150,6 +210,20 @@ export const TOOL_DEFINITIONS = [
150
210
  return json;
151
211
  },
152
212
  },
213
+ {
214
+ kebab: "get-suite-execution-stats",
215
+ description: "Aggregate suite timing from list_execution_history testStats (same filters as get-execution-history). " +
216
+ "Returns testCount, timedTestCount, sumSuccessMeanSecs (UI ExecutionTimingSummary total), sumFailMeanSecs, " +
217
+ "maxSuccessMeanSecs, slowTestCount (success mean > 30s). " +
218
+ "Sum of means is aggregate test CPU-time, not CI wall-clock under parallelism. " +
219
+ "Compare sumSuccessMeanSecs against any suite budget in the agent — this tool only returns stats.",
220
+ inputSchema: S.suiteExecutionStatsInput,
221
+ execute: async (args, { postMcp }) => {
222
+ const json = await postMcp("/api/mcp/list_execution_history", listExecutionBody(args));
223
+ const parsed = JSON.parse(json);
224
+ return JSON.stringify(aggregateSuiteExecutionStats(parsed.testStats));
225
+ },
226
+ },
153
227
  {
154
228
  kebab: "fetch-execution-report",
155
229
  description: "Fetch a detailed execution report for failing SmartTests, given a batchInvocationId (batch run) or jobId (single run). " +
@@ -306,7 +380,8 @@ export const TOOL_DEFINITIONS = [
306
380
  "status must be one of: ACTIVE, IGNORED, FIXED, DUPLICATE, IN_PROGRESS_BUG, ARCHIVED_BUG, BLOCKED. " +
307
381
  "For /testchimp fix issue: set IN_PROGRESS_BUG after applying a code fix; set FIXED only after user confirmation / commits pushed. " +
308
382
  "Optional ignoreReason when status is IGNORED: INTENDED_BEHAVIOUR | INACCURATE_ASSESSMENT | NOT_IMPORTANT. " +
309
- "Optional agentTraceability records UPDATED Activity inline.",
383
+ "Optional agentTraceability records UPDATED Activity inline " +
384
+ "(requires both workflowId and workflowExecutionId for Activity attachment).",
310
385
  inputSchema: S.updateIssueStatusInput,
311
386
  execute: async (args, { postMcp }) => {
312
387
  const a = args;
@@ -330,8 +405,10 @@ export const TOOL_DEFINITIONS = [
330
405
  "linkTargets, labels, source, environment, attachments, artifactReference). " +
331
406
  "For /testchimp implement TASK_ISSUE creates: set labels=[\"TestChimp Implement\"] (not source), " +
332
407
  "severity from task priority, category (e.g. FUNCTIONAL), and linkTargets for STORY and/or SCENARIO ordinals. " +
333
- "Optional agentTraceability (or flat workflowId/policyFile/…) records CREATED Activity inline — " +
408
+ "Optional agentTraceability (or flat workflowId/workflowExecutionId/policyFile/…) records CREATED Activity inline — " +
334
409
  "prefer this over a separate report-agent-action for issue creates. " +
410
+ "For Activity/timeline attachment both workflowId and workflowExecutionId (stable Plan ULID) are required; " +
411
+ "do not omit workflowExecutionId or mint a new ULID per issue. " +
335
412
  "Authenticated via project API key; project is resolved from the key.",
336
413
  inputSchema: S.createIssueInput,
337
414
  execute: async (args, { postMcp }) => {
@@ -401,6 +478,22 @@ export const TOOL_DEFINITIONS = [
401
478
  });
402
479
  },
403
480
  },
481
+ {
482
+ kebab: "get-spec-lifecycle-details",
483
+ description: "Fetch lifecycle_fields for user stories and/or test scenarios by ordinal id (DB only; no markdown). " +
484
+ "Pass scenarioIds / storyIds as lists of bare ordinals (canonical) or prefixed forms (TS-107, #US-12). " +
485
+ "Use after identifying scenarios in scope for create-tests to read verification_strategy (auto|manual) and skip manual ones.",
486
+ inputSchema: S.getSpecLifecycleDetailsInput,
487
+ execute: async (args, { postMcp }) => {
488
+ const a = args;
489
+ const body = {};
490
+ if (a.scenarioIds?.length)
491
+ body.scenarioIds = a.scenarioIds;
492
+ if (a.storyIds?.length)
493
+ body.storyIds = a.storyIds;
494
+ return postMcp("/api/mcp/get_spec_lifecycle_details", body);
495
+ },
496
+ },
404
497
  {
405
498
  kebab: "get-eaas-config",
406
499
  description: "Return the project's BunnyShell (Environment-as-a-Service) settings. Secrets are never returned.",
@@ -947,7 +1040,15 @@ export const TOOL_DEFINITIONS = [
947
1040
  body.userId = process.env.TESTCHIMP_USER_ID;
948
1041
  if (a.branchName)
949
1042
  body.branchName = a.branchName;
950
- // Nested traceability wins for agentModel; only fill from flat/env when nested omits it.
1043
+ // Nested traceability wins for agentModel / skillVersion / cliVersion;
1044
+ // fill from flat/env/package when nested omits them.
1045
+ const { skillVersion, cliVersion } = resolveToolchainVersions({
1046
+ skillVersion: a.skillVersion,
1047
+ cliVersion: a.cliVersion,
1048
+ nested: a.traceability && typeof a.traceability === "object"
1049
+ ? a.traceability
1050
+ : null,
1051
+ });
951
1052
  if (a.traceability && typeof a.traceability === "object") {
952
1053
  const nested = { ...a.traceability };
953
1054
  const nestedModel = nested.agentModel != null && String(nested.agentModel).trim() !== ""
@@ -960,12 +1061,23 @@ export const TOOL_DEFINITIONS = [
960
1061
  else if (flatModel) {
961
1062
  nested.agentModel = flatModel;
962
1063
  }
1064
+ if (skillVersion)
1065
+ nested.skillVersion = skillVersion;
1066
+ if (cliVersion)
1067
+ nested.cliVersion = cliVersion;
963
1068
  body.traceability = nested;
964
1069
  }
965
1070
  else {
966
1071
  const model = a.agentModel?.trim() || process.env.TESTCHIMP_AGENT_MODEL?.trim();
967
- if (model) {
968
- body.traceability = { agentModel: model };
1072
+ const nested = {};
1073
+ if (model)
1074
+ nested.agentModel = model;
1075
+ if (skillVersion)
1076
+ nested.skillVersion = skillVersion;
1077
+ if (cliVersion)
1078
+ nested.cliVersion = cliVersion;
1079
+ if (Object.keys(nested).length > 0) {
1080
+ body.traceability = nested;
969
1081
  }
970
1082
  }
971
1083
  if (a.test) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.28",
4
- "description": "TestChimp CLI and MCP server \u2014 coverage, plans, EaaS, TrueCoverage, API operations (calls /api/mcp/*)",
3
+ "version": "0.1.30",
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",
7
7
  "types": "dist/bin/testchimp.d.ts",