@testchimp/cli 0.1.4 → 0.1.6

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.
@@ -46,6 +46,7 @@ export function buildCliProgram() {
46
46
  .option("--release <s>")
47
47
  .option("--environment <s>")
48
48
  .option("--branch-name <s>")
49
+ .option("--platform <web|ios|android>")
49
50
  .option("--file-paths <csv>", "comma-separated paths under platform tests root")
50
51
  .option("--folder-path <path>", "folder under tests root, slash-separated")
51
52
  .action(async (opts) => {
@@ -56,6 +57,8 @@ export function buildCliProgram() {
56
57
  body.environment = opts.environment;
57
58
  if (opts.branchName)
58
59
  body.branchName = opts.branchName;
60
+ if (opts.platform)
61
+ body.platform = opts.platform;
59
62
  const scope = {};
60
63
  if (opts.filePaths)
61
64
  scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
@@ -74,6 +77,8 @@ export function buildCliProgram() {
74
77
  .option("--release <s>")
75
78
  .option("--environment <s>")
76
79
  .option("--branch-name <s>")
80
+ .option("--scenario-id <id>")
81
+ .option("--platform <web|ios|android>")
77
82
  .option("--file-paths <csv>")
78
83
  .option("--folder-path <path>")
79
84
  .action(async (opts) => {
@@ -84,6 +89,10 @@ export function buildCliProgram() {
84
89
  body.environment = opts.environment;
85
90
  if (opts.branchName)
86
91
  body.branchName = opts.branchName;
92
+ if (opts.scenarioId)
93
+ body.scenarioId = opts.scenarioId;
94
+ if (opts.platform)
95
+ body.platform = opts.platform;
87
96
  const scope = {};
88
97
  if (opts.filePaths)
89
98
  scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
@@ -157,6 +166,30 @@ export function buildCliProgram() {
157
166
  const out = await runTool("update-user-story", merged, { postMcp });
158
167
  console.log(out);
159
168
  });
169
+ program
170
+ .command("mark-plan-items-implementation-done")
171
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "mark-plan-items-implementation-done").description)
172
+ .addOption(jsonInputOption())
173
+ .option("--scenario-ordinal-ids <csv>", "comma-separated TS-<n> numeric ids")
174
+ .option("--user-story-ordinal-ids <csv>", "comma-separated US-<n> numeric ids")
175
+ .action(async (opts) => {
176
+ const body = {};
177
+ if (opts.scenarioOrdinalIds) {
178
+ body.scenarioOrdinalIds = String(opts.scenarioOrdinalIds)
179
+ .split(",")
180
+ .map((s) => Number(s.trim()))
181
+ .filter((n) => Number.isFinite(n) && n > 0);
182
+ }
183
+ if (opts.userStoryOrdinalIds) {
184
+ body.userStoryOrdinalIds = String(opts.userStoryOrdinalIds)
185
+ .split(",")
186
+ .map((s) => Number(s.trim()))
187
+ .filter((n) => Number.isFinite(n) && n > 0);
188
+ }
189
+ const merged = mergeBodies(body, opts.jsonInput);
190
+ const out = await runTool("mark-plan-items-implementation-done", merged, { postMcp });
191
+ console.log(out);
192
+ });
160
193
  program
161
194
  .command("update-test-scenario")
162
195
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-test-scenario").description)
@@ -3,6 +3,10 @@ export declare const scopeSchema: z.ZodOptional<z.ZodObject<{
3
3
  filePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
4
4
  folderPath: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>;
5
5
  }, z.core.$strip>>;
6
+ export declare const executionJobDimensionFilterSchema: z.ZodObject<{
7
+ dimension: z.ZodString;
8
+ values: z.ZodArray<z.ZodString>;
9
+ }, z.core.$strip>;
6
10
  export declare const listCoverageInput: z.ZodObject<{
7
11
  release: z.ZodOptional<z.ZodString>;
8
12
  environment: z.ZodOptional<z.ZodString>;
@@ -13,6 +17,11 @@ export declare const listCoverageInput: z.ZodObject<{
13
17
  includeNonCoveredUserStories: z.ZodOptional<z.ZodBoolean>;
14
18
  includeNonCoveredTestScenarios: z.ZodOptional<z.ZodBoolean>;
15
19
  branchName: z.ZodOptional<z.ZodString>;
20
+ platform: z.ZodOptional<z.ZodEnum<{
21
+ web: "web";
22
+ ios: "ios";
23
+ android: "android";
24
+ }>>;
16
25
  }, z.core.$strip>;
17
26
  export declare const listExecutionInput: z.ZodObject<{
18
27
  release: z.ZodOptional<z.ZodString>;
@@ -22,6 +31,18 @@ export declare const listExecutionInput: z.ZodObject<{
22
31
  folderPath: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>;
23
32
  }, z.core.$strip>>;
24
33
  branchName: z.ZodOptional<z.ZodString>;
34
+ scenarioId: z.ZodOptional<z.ZodString>;
35
+ platform: z.ZodOptional<z.ZodEnum<{
36
+ web: "web";
37
+ ios: "ios";
38
+ android: "android";
39
+ }>>;
40
+ dimensionFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
41
+ dimension: z.ZodString;
42
+ values: z.ZodArray<z.ZodString>;
43
+ }, z.core.$strip>>>;
44
+ limit: z.ZodOptional<z.ZodNumber>;
45
+ offset: z.ZodOptional<z.ZodNumber>;
25
46
  }, z.core.$strip>;
26
47
  export declare const fetchExecutionReportInput: z.ZodObject<{
27
48
  batchInvocationId: z.ZodOptional<z.ZodString>;
@@ -39,6 +60,10 @@ export declare const createTestScenarioInput: z.ZodObject<{
39
60
  export declare const updatePlanMarkdownInput: z.ZodObject<{
40
61
  content: z.ZodString;
41
62
  }, z.core.$strip>;
63
+ export declare const markPlanItemsImplementationDoneInput: z.ZodObject<{
64
+ scenarioOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
65
+ userStoryOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
66
+ }, z.core.$strip>;
42
67
  export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
43
68
  export declare const getBranchSpecificEndpointConfigInput: z.ZodObject<{
44
69
  branchName: z.ZodOptional<z.ZodString>;
@@ -5,6 +5,11 @@ export const scopeSchema = z
5
5
  folderPath: z.union([z.array(z.string()), z.string()]).optional(),
6
6
  })
7
7
  .optional();
8
+ const executionPlatformSchema = z.enum(["web", "ios", "android"]);
9
+ export const executionJobDimensionFilterSchema = z.object({
10
+ dimension: z.string().min(1),
11
+ values: z.array(z.string()).min(1),
12
+ });
8
13
  export const listCoverageInput = z.object({
9
14
  release: z.string().optional(),
10
15
  environment: z.string().optional(),
@@ -12,12 +17,18 @@ export const listCoverageInput = z.object({
12
17
  includeNonCoveredUserStories: z.boolean().optional(),
13
18
  includeNonCoveredTestScenarios: z.boolean().optional(),
14
19
  branchName: z.string().optional(),
20
+ platform: executionPlatformSchema.optional(),
15
21
  });
16
22
  export const listExecutionInput = z.object({
17
23
  release: z.string().optional(),
18
24
  environment: z.string().optional(),
19
25
  scope: scopeSchema,
20
26
  branchName: z.string().optional(),
27
+ scenarioId: z.string().optional(),
28
+ platform: executionPlatformSchema.optional(),
29
+ dimensionFilters: z.array(executionJobDimensionFilterSchema).optional(),
30
+ limit: z.number().int().positive().max(500).optional(),
31
+ offset: z.number().int().nonnegative().optional(),
21
32
  });
22
33
  export const fetchExecutionReportInput = z
23
34
  .object({
@@ -46,6 +57,10 @@ export const createTestScenarioInput = z.object({
46
57
  export const updatePlanMarkdownInput = z.object({
47
58
  content: z.string().min(1),
48
59
  });
60
+ export const markPlanItemsImplementationDoneInput = z.object({
61
+ scenarioOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
62
+ userStoryOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
63
+ });
49
64
  export const emptyInput = z.object({});
50
65
  export const getBranchSpecificEndpointConfigInput = z.object({
51
66
  branchName: z.string().optional(),
@@ -1,6 +1,16 @@
1
1
  import { normalizeScope } from "./normalize.js";
2
2
  import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralWait.js";
3
3
  import * as S from "./schemas.js";
4
+ function platformToProtoEnum(platform) {
5
+ switch (platform) {
6
+ case "ios":
7
+ return "IOS_EXECUTION_PLATFORM";
8
+ case "android":
9
+ return "ANDROID_EXECUTION_PLATFORM";
10
+ default:
11
+ return "WEB_EXECUTION_PLATFORM";
12
+ }
13
+ }
4
14
  function listCoverageBody(args) {
5
15
  const body = {};
6
16
  if (args.release != null)
@@ -16,6 +26,8 @@ function listCoverageBody(args) {
16
26
  }
17
27
  if (args.branchName != null && args.branchName.trim() !== "")
18
28
  body.branchName = args.branchName.trim();
29
+ if (args.platform != null)
30
+ body.platform = platformToProtoEnum(args.platform);
19
31
  return body;
20
32
  }
21
33
  function listExecutionBody(args) {
@@ -28,13 +40,33 @@ function listExecutionBody(args) {
28
40
  body.scope = normalizeScope(args.scope);
29
41
  if (args.branchName != null && args.branchName.trim() !== "")
30
42
  body.branchName = args.branchName.trim();
43
+ if (args.scenarioId != null && args.scenarioId.trim() !== "")
44
+ body.scenarioId = args.scenarioId.trim();
45
+ const dimensionFilters = [...(args.dimensionFilters ?? [])];
46
+ if (args.platform != null) {
47
+ const hasPlatformFilter = dimensionFilters.some((f) => f.dimension === "PLATFORM_EXECUTION_JOB_FILTER_DIMENSION");
48
+ if (!hasPlatformFilter) {
49
+ dimensionFilters.push({
50
+ dimension: "PLATFORM_EXECUTION_JOB_FILTER_DIMENSION",
51
+ values: [args.platform.toUpperCase()],
52
+ });
53
+ }
54
+ }
55
+ if (dimensionFilters.length > 0)
56
+ body.dimensionFilters = dimensionFilters;
57
+ if (args.limit != null)
58
+ body.limit = args.limit;
59
+ if (args.offset != null)
60
+ body.offset = args.offset;
31
61
  return body;
32
62
  }
33
63
  export const TOOL_DEFINITIONS = [
34
64
  {
35
65
  kebab: "get-requirement-coverage",
36
66
  description: "Fetch requirement (scenario) coverage under an optional platform-rooted folder scope (tests/... or plans/...). " +
37
- "Use branchName (Git branch) and scope.filePaths (paths under platform tests root) rather than internal ids.",
67
+ "Use scope.filePaths or scope.folderPath (platform tests/plans roots). Omit branchName for cross-branch coverage " +
68
+ "(aggregates branch copies; execution jobs deduped by stable hash of tests-root-relative path + test name). " +
69
+ "Pass branchName only when results must be limited to one Git branch. Optional platform (web|ios|android) filters rollup.",
38
70
  inputSchema: S.listCoverageInput,
39
71
  execute: async (args, { postMcp }) => {
40
72
  const json = await postMcp("/api/mcp/list_requirement_coverage", listCoverageBody(args));
@@ -43,8 +75,8 @@ export const TOOL_DEFINITIONS = [
43
75
  },
44
76
  {
45
77
  kebab: "get-execution-history",
46
- description: "Fetch SmartTest execution history for an optional platform-rooted folder scope. " +
47
- "Use branchName and scope.filePaths as for coverage.",
78
+ description: "Fetch SmartTest execution history for an optional platform-rooted folder scope, or for a scenario when scenarioId is set. " +
79
+ "Use branchName and scope.filePaths as for coverage. Optional platform (web|ios|android) and dimensionFilters narrow results.",
48
80
  inputSchema: S.listExecutionInput,
49
81
  execute: async (args, { postMcp }) => {
50
82
  const json = await postMcp("/api/mcp/list_execution_history", listExecutionBody(args));
@@ -97,7 +129,8 @@ export const TOOL_DEFINITIONS = [
97
129
  {
98
130
  kebab: "update-user-story",
99
131
  description: "Sync a user story markdown file to the platform after local edits. " +
100
- "Parses frontmatter (id: US-..., title, priority, status) and updates the linked support file and entity.",
132
+ "Parses frontmatter (id: US-..., title, priority) and updates the linked support file and entity. " +
133
+ "Implementation status is not stored in markdown; use mark-plan-items-implementation-done.",
101
134
  inputSchema: S.updatePlanMarkdownInput,
102
135
  execute: async (args, { postMcp }) => {
103
136
  const a = args;
@@ -107,13 +140,29 @@ export const TOOL_DEFINITIONS = [
107
140
  {
108
141
  kebab: "update-test-scenario",
109
142
  description: "Sync a test scenario markdown file to the platform after local edits. " +
110
- "Parses frontmatter (id: TS-..., story: US-..., title, priority, status) and updates linking if story changes.",
143
+ "Parses frontmatter (id: TS-..., story: US-..., title, priority) and updates linking if story changes. " +
144
+ "Implementation status is not stored in markdown; use mark-plan-items-implementation-done.",
111
145
  inputSchema: S.updatePlanMarkdownInput,
112
146
  execute: async (args, { postMcp }) => {
113
147
  const a = args;
114
148
  return postMcp("/api/mcp/update_test_scenario", { content: a.content });
115
149
  },
116
150
  },
151
+ {
152
+ kebab: "mark-plan-items-implementation-done",
153
+ description: "Mark user stories and/or test scenarios implementation-complete in platform lifecycle (DB only; does not rewrite plan markdown). " +
154
+ "Use scenarioOrdinalIds / userStoryOrdinalIds (numeric parts of TS-<n> / US-<n>).",
155
+ inputSchema: S.markPlanItemsImplementationDoneInput,
156
+ execute: async (args, { postMcp }) => {
157
+ const a = args;
158
+ const body = {};
159
+ if (a.scenarioOrdinalIds?.length)
160
+ body.scenarioOrdinalIds = a.scenarioOrdinalIds;
161
+ if (a.userStoryOrdinalIds?.length)
162
+ body.userStoryOrdinalIds = a.userStoryOrdinalIds;
163
+ return postMcp("/api/mcp/mark_plan_items_implementation_done", body);
164
+ },
165
+ },
117
166
  {
118
167
  kebab: "get-eaas-config",
119
168
  description: "Return the project's BunnyShell (Environment-as-a-Service) settings. Secrets are never returned.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "TestChimp CLI and MCP server — coverage, plans, EaaS, TrueCoverage (calls /api/mcp/*)",
5
5
  "type": "module",
6
6
  "main": "dist/bin/testchimp.js",