@testchimp/cli 0.1.23 → 0.1.26

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.
@@ -169,6 +169,7 @@ export function buildCliProgram() {
169
169
  .option("--environment <s>")
170
170
  .option("--branch-name <s>")
171
171
  .option("--scenario-id <id>")
172
+ .option("--test-id <id>")
172
173
  .option("--platform <web|ios|android>")
173
174
  .option("--file-paths <csv>")
174
175
  .option("--folder-path <path>")
@@ -182,6 +183,8 @@ export function buildCliProgram() {
182
183
  body.branchName = opts.branchName;
183
184
  if (opts.scenarioId)
184
185
  body.scenarioId = opts.scenarioId;
186
+ if (opts.testId)
187
+ body.testId = opts.testId;
185
188
  if (opts.platform)
186
189
  body.platform = opts.platform;
187
190
  const scope = {};
@@ -281,15 +284,33 @@ export function buildCliProgram() {
281
284
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-test-scenarios").description)
282
285
  .addOption(jsonInputOption())
283
286
  .option("--scenario-ordinal-ids <csv>", "comma-separated TS-<n> numeric ids")
287
+ .option("--external-ids <csv>", "comma-separated external TMS ids (e.g. C12345,PROJ-101); server matches exact then numerical part")
284
288
  .action(async (opts) => {
285
289
  const body = {};
286
290
  if (opts.scenarioOrdinalIds) {
287
- body.scenarioOrdinalIds = String(opts.scenarioOrdinalIds)
291
+ const ids = String(opts.scenarioOrdinalIds)
288
292
  .split(",")
289
293
  .map((s) => Number(s.trim()))
290
294
  .filter((n) => Number.isFinite(n) && n > 0);
295
+ if (ids.length > 0)
296
+ body.scenarioOrdinalIds = ids;
297
+ }
298
+ if (opts.externalIds) {
299
+ const ids = String(opts.externalIds)
300
+ .split(",")
301
+ .map((s) => s.trim())
302
+ .filter((s) => s.length > 0);
303
+ if (ids.length > 0)
304
+ body.externalIds = ids;
291
305
  }
292
306
  const merged = mergeBodies(body, opts.jsonInput);
307
+ // Drop empty arrays so refine / merge with json-input does not fail misleadingly.
308
+ if (Array.isArray(merged.scenarioOrdinalIds) && merged.scenarioOrdinalIds.length === 0) {
309
+ delete merged.scenarioOrdinalIds;
310
+ }
311
+ if (Array.isArray(merged.externalIds) && merged.externalIds.length === 0) {
312
+ delete merged.externalIds;
313
+ }
293
314
  const out = await runTool("get-test-scenarios", merged, { postMcp });
294
315
  console.log(out);
295
316
  });
@@ -1064,6 +1085,28 @@ export function buildCliProgram() {
1064
1085
  }
1065
1086
  console.log(await runTool("upsert-policy", merged, { postMcp }));
1066
1087
  });
1088
+ program
1089
+ .command("upsert-plans-support-file")
1090
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "upsert-plans-support-file").description)
1091
+ .addOption(jsonInputOption())
1092
+ .option("--file-path <path>", "path relative to mapped plans root (e.g. knowledge/workflow_plans/run-qa/<ulid>.plan.md)")
1093
+ .option("--content <markdown>", "full file content")
1094
+ .option("--content-file <path>", "read content from local file")
1095
+ .action(async (opts) => {
1096
+ let content = opts.content;
1097
+ if (opts.contentFile)
1098
+ content = await readFile(String(opts.contentFile), "utf8");
1099
+ const body = {};
1100
+ if (opts.filePath)
1101
+ body.filePath = String(opts.filePath);
1102
+ if (content !== undefined)
1103
+ body.content = content;
1104
+ const merged = mergeBodies(body, opts.jsonInput);
1105
+ if (!merged.filePath || merged.content === undefined || merged.content === null) {
1106
+ throw new Error("Provide --file-path and --content or --content-file (or full body via --json-input)");
1107
+ }
1108
+ console.log(await runTool("upsert-plans-support-file", merged, { postMcp }));
1109
+ });
1067
1110
  program.on("--help", () => {
1068
1111
  /* default */
1069
1112
  });
@@ -38,6 +38,7 @@ export declare const listExecutionInput: z.ZodObject<{
38
38
  }, z.core.$strip>>;
39
39
  branchName: z.ZodOptional<z.ZodString>;
40
40
  scenarioId: z.ZodOptional<z.ZodString>;
41
+ testId: z.ZodOptional<z.ZodString>;
41
42
  platform: z.ZodOptional<z.ZodEnum<{
42
43
  web: "web";
43
44
  ios: "ios";
@@ -225,7 +226,8 @@ export declare const getUserStoriesInput: z.ZodObject<{
225
226
  userStoryOrdinalIds: z.ZodArray<z.ZodCoercedNumber<unknown>>;
226
227
  }, z.core.$strip>;
227
228
  export declare const getTestScenariosInput: z.ZodObject<{
228
- scenarioOrdinalIds: z.ZodArray<z.ZodCoercedNumber<unknown>>;
229
+ scenarioOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
230
+ externalIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
229
231
  }, z.core.$strip>;
230
232
  export declare const getManualSessionDetailsInput: z.ZodObject<{
231
233
  manualSessionId: z.ZodString;
@@ -1350,4 +1352,8 @@ export declare const upsertPolicyInput: z.ZodObject<{
1350
1352
  policyFileName: z.ZodString;
1351
1353
  content: z.ZodString;
1352
1354
  }, z.core.$strip>;
1355
+ export declare const upsertPlansSupportFileInput: z.ZodObject<{
1356
+ filePath: z.ZodString;
1357
+ content: z.ZodString;
1358
+ }, z.core.$strip>;
1353
1359
  export declare const listWorkflowCatalogInput: z.ZodObject<{}, z.core.$strip>;
@@ -33,6 +33,7 @@ export const listExecutionInput = z.object({
33
33
  scope: scopeSchema,
34
34
  branchName: z.string().optional(),
35
35
  scenarioId: z.string().optional(),
36
+ testId: z.string().optional(),
36
37
  platform: executionPlatformSchema.optional(),
37
38
  dimensionFilters: z.array(executionJobDimensionFilterSchema).optional(),
38
39
  limit: z.number().int().positive().max(500).optional(),
@@ -110,8 +111,12 @@ export const getUserStoriesInput = z
110
111
  });
111
112
  export const getTestScenariosInput = z
112
113
  .object({
113
- scenarioOrdinalIds: z.array(z.coerce.number().int().positive()).min(1),
114
- });
114
+ scenarioOrdinalIds: z.array(z.coerce.number().int().positive()).min(1).optional(),
115
+ /** Full TMS external ids (e.g. C12345, PROJ-101). Server matches exact then numerical part. */
116
+ externalIds: z.array(z.string().min(1)).min(1).optional(),
117
+ })
118
+ .refine((v) => (v.scenarioOrdinalIds != null && v.scenarioOrdinalIds.length > 0) ||
119
+ (v.externalIds != null && v.externalIds.length > 0), { message: "Provide scenarioOrdinalIds and/or externalIds (non-empty)" });
115
120
  export const getManualSessionDetailsInput = z.object({
116
121
  manualSessionId: z.string().min(1),
117
122
  });
@@ -682,4 +687,9 @@ export const upsertPolicyInput = z.object({
682
687
  policyFileName: z.string().min(1),
683
688
  content: z.string().min(1),
684
689
  });
690
+ export const upsertPlansSupportFileInput = z.object({
691
+ /** Path relative to mapped plans root (e.g. knowledge/workflow_plans/run-qa/<ulid>.plan.md). */
692
+ filePath: z.string().min(1),
693
+ content: z.string().min(1),
694
+ });
685
695
  export const listWorkflowCatalogInput = z.object({});
@@ -69,6 +69,8 @@ function listExecutionBody(args) {
69
69
  body.branchName = args.branchName.trim();
70
70
  if (args.scenarioId != null && args.scenarioId.trim() !== "")
71
71
  body.scenarioId = args.scenarioId.trim();
72
+ if (args.testId != null && args.testId.trim() !== "")
73
+ body.testId = args.testId.trim();
72
74
  const dimensionFilters = [...(args.dimensionFilters ?? [])];
73
75
  if (args.platform != null) {
74
76
  const hasPlatformFilter = dimensionFilters.some((f) => f.dimension === "PLATFORM_EXECUTION_JOB_FILTER_DIMENSION");
@@ -139,7 +141,8 @@ export const TOOL_DEFINITIONS = [
139
141
  },
140
142
  {
141
143
  kebab: "get-execution-history",
142
- description: "Fetch SmartTest execution history for an optional platform-rooted folder scope, or for a scenario when scenarioId is set. " +
144
+ description: "Fetch SmartTest execution history for a testId (top 5 recent runs), an optional platform-rooted folder/file scope, or a scenario when scenarioId is set. " +
145
+ "Prefer testId when you have it from fetch-execution-report. Typically omit environment to avoid env scoping. " +
143
146
  "Use branchName and scope.filePaths as for coverage. Optional platform (web|ios|android) and dimensionFilters narrow results.",
144
147
  inputSchema: S.listExecutionInput,
145
148
  execute: async (args, { postMcp }) => {
@@ -255,15 +258,20 @@ export const TOOL_DEFINITIONS = [
255
258
  },
256
259
  {
257
260
  kebab: "get-test-scenarios",
258
- description: "Fetch test scenarios from the TestChimp platform by ordinal id (numeric part of TS-<n>). " +
259
- "Returns full plan markdown content, title, platform file path, and linked user story ordinal ids. " +
260
- "Use when plan files are not yet synced to the repo.",
261
+ description: "Fetch test scenarios from the TestChimp platform by ordinal id (numeric part of TS-<n>) " +
262
+ "and/or external TMS ids (e.g. C12345, PROJ-101 server strips prefixes and matches numerical part). " +
263
+ "Returns full plan markdown content, title, platform file path, linked user story ordinal ids, " +
264
+ "and external_source / external_system_id when present. " +
265
+ "Use when plan files are not yet synced to the repo, or when linking imported tests to scenarios by TMS id.",
261
266
  inputSchema: S.getTestScenariosInput,
262
267
  execute: async (args, { postMcp }) => {
263
268
  const a = args;
264
- return postMcp("/api/mcp/get_test_scenarios", {
265
- scenarioOrdinalIds: a.scenarioOrdinalIds,
266
- });
269
+ const body = {};
270
+ if (a.scenarioOrdinalIds?.length)
271
+ body.scenarioOrdinalIds = a.scenarioOrdinalIds;
272
+ if (a.externalIds?.length)
273
+ body.externalIds = a.externalIds;
274
+ return postMcp("/api/mcp/get_test_scenarios", body);
267
275
  },
268
276
  },
269
277
  {
@@ -975,6 +983,21 @@ export const TOOL_DEFINITIONS = [
975
983
  });
976
984
  },
977
985
  },
986
+ {
987
+ kebab: "upsert-plans-support-file",
988
+ description: "Create or update any file under the mapped plans root on the platform by relative path (no git commit/push required). " +
989
+ "Primary use: upload workflow execution plans at knowledge/workflow_plans/<workflow-id>/<workflow_execution_id>.plan.md after the Plan phase. " +
990
+ "filePath is relative to the plans mapped root (leading plans/ is stripped). Under workflow_plans/, filenames are coerced to *.plan.md and stored as WORKFLOW_EXECUTION_PLAN. " +
991
+ "Response includes supportFileId, filePath (canonical), filetype, created. Blocking step before Execute for cloud agents.",
992
+ inputSchema: S.upsertPlansSupportFileInput,
993
+ execute: async (args, { postMcp }) => {
994
+ const a = args;
995
+ return postMcp("/api/mcp/upsert_plans_support_file", {
996
+ filePath: a.filePath,
997
+ content: a.content,
998
+ });
999
+ },
1000
+ },
978
1001
  {
979
1002
  kebab: "list-workflow-catalog",
980
1003
  description: "List supported TestChimp workflows with Active / Disabled / Missing Config status for the project.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "description": "TestChimp CLI and MCP server \u2014 coverage, plans, EaaS, TrueCoverage (calls /api/mcp/*)",
5
5
  "type": "module",
6
6
  "main": "dist/bin/testchimp.js",