@testchimp/cli 0.1.15 → 0.1.17

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.
@@ -269,6 +269,106 @@ export function buildCliProgram() {
269
269
  const out = await runTool("get-manual-session-details", merged, { postMcp });
270
270
  console.log(out);
271
271
  });
272
+ program
273
+ .command("get-issue-details")
274
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-issue-details").description)
275
+ .addOption(jsonInputOption())
276
+ .option("--issue-id <id>", "Issue ordinal id (#B-123, B-123, B123, or 123)")
277
+ .action(async (opts) => {
278
+ const body = {};
279
+ if (opts.issueId)
280
+ body.issueId = String(opts.issueId).trim();
281
+ const merged = mergeBodies(body, opts.jsonInput);
282
+ if (!merged.issueId || String(merged.issueId).trim() === "") {
283
+ throw new Error("issueId is required (--issue-id)");
284
+ }
285
+ const out = await runTool("get-issue-details", { issueId: String(merged.issueId).trim() }, { postMcp });
286
+ console.log(out);
287
+ });
288
+ program
289
+ .command("update-issue-status")
290
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-issue-status").description)
291
+ .addOption(jsonInputOption())
292
+ .option("--issue-id <id>", "Issue ordinal id (#B-123, B-123, B123, or 123)")
293
+ .option("--status <status>", "ACTIVE | IGNORED | FIXED | DUPLICATE | IN_PROGRESS_BUG | ARCHIVED_BUG | BLOCKED")
294
+ .option("--ignore-reason <reason>", "When status=IGNORED: INTENDED_BEHAVIOUR | INACCURATE_ASSESSMENT | NOT_IMPORTANT")
295
+ .action(async (opts) => {
296
+ const body = {};
297
+ if (opts.issueId)
298
+ body.issueId = String(opts.issueId).trim();
299
+ if (opts.status)
300
+ body.status = String(opts.status).trim();
301
+ if (opts.ignoreReason)
302
+ body.ignoreReason = String(opts.ignoreReason).trim();
303
+ const merged = mergeBodies(body, opts.jsonInput);
304
+ if (!merged.issueId || String(merged.issueId).trim() === "") {
305
+ throw new Error("issueId is required (--issue-id)");
306
+ }
307
+ if (!merged.status || String(merged.status).trim() === "") {
308
+ throw new Error("status is required (ACTIVE | IGNORED | FIXED | DUPLICATE | IN_PROGRESS_BUG | ARCHIVED_BUG | BLOCKED)");
309
+ }
310
+ const out = await runTool("update-issue-status", {
311
+ issueId: String(merged.issueId).trim(),
312
+ status: String(merged.status).trim(),
313
+ ...(merged.ignoreReason
314
+ ? { ignoreReason: String(merged.ignoreReason).trim() }
315
+ : {}),
316
+ }, { postMcp });
317
+ console.log(out);
318
+ });
319
+ program
320
+ .command("create-issue")
321
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "create-issue").description)
322
+ .addOption(jsonInputOption())
323
+ .option("--title <title>", "Issue title (required unless provided via --json-input)")
324
+ .option("--description <text>", "Issue description (markdown supported)")
325
+ .option("--issue-type <type>", "BUG_ISSUE | SUGGESTION_ISSUE | OBSERVATION_ISSUE | TASK_ISSUE")
326
+ .option("--category <category>", "FUNCTIONAL | SECURITY | ACCESSIBILITY | PERFORMANCE | VISUAL | …")
327
+ .option("--severity <severity>", "LOW_SEVERITY | MEDIUM_SEVERITY | HIGH_SEVERITY | CRITICAL_SEVERITY")
328
+ .option("--status <status>", "ACTIVE | IGNORED | FIXED | DUPLICATE | IN_PROGRESS_BUG | ARCHIVED_BUG | BLOCKED")
329
+ .option("--reported-release-id <id>", "Release label/id to attach to the issue")
330
+ .option("--due-date-millis <ms>", "Due date as UTC epoch millis")
331
+ .option("--assignee <userId>", "Assignee user id")
332
+ .option("--labels <csv>", "Comma-separated labels")
333
+ .option("--source <name>", "External ingest source identifier (stored as label source:<name>)")
334
+ .option("--environment <name>", "Environment tag (defaults to QA when omitted)")
335
+ .action(async (opts) => {
336
+ const body = {};
337
+ if (opts.title)
338
+ body.title = String(opts.title).trim();
339
+ if (opts.description)
340
+ body.description = String(opts.description);
341
+ if (opts.issueType)
342
+ body.issueType = String(opts.issueType).trim();
343
+ if (opts.category)
344
+ body.category = String(opts.category).trim();
345
+ if (opts.severity)
346
+ body.severity = String(opts.severity).trim();
347
+ if (opts.status)
348
+ body.status = String(opts.status).trim();
349
+ if (opts.reportedReleaseId)
350
+ body.reportedReleaseId = String(opts.reportedReleaseId).trim();
351
+ if (opts.dueDateMillis != null)
352
+ body.dueDateMillis = Number(opts.dueDateMillis);
353
+ if (opts.assignee)
354
+ body.assignee = String(opts.assignee).trim();
355
+ if (opts.labels) {
356
+ body.labels = String(opts.labels)
357
+ .split(",")
358
+ .map((s) => s.trim())
359
+ .filter(Boolean);
360
+ }
361
+ if (opts.source)
362
+ body.source = String(opts.source).trim();
363
+ if (opts.environment)
364
+ body.environment = String(opts.environment).trim();
365
+ const merged = mergeBodies(body, opts.jsonInput);
366
+ if (!merged.title || String(merged.title).trim() === "") {
367
+ throw new Error("title is required (--title or --json-input {\"title\":\"...\"})");
368
+ }
369
+ const out = await runTool("create-issue", merged, { postMcp });
370
+ console.log(out);
371
+ });
272
372
  program
273
373
  .command("mark-plan-items-implementation-done")
274
374
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "mark-plan-items-implementation-done").description)
@@ -79,6 +79,92 @@ export declare const getTestScenariosInput: z.ZodObject<{
79
79
  export declare const getManualSessionDetailsInput: z.ZodObject<{
80
80
  manualSessionId: z.ZodString;
81
81
  }, z.core.$strip>;
82
+ export declare const getIssueDetailsInput: z.ZodObject<{
83
+ issueId: z.ZodString;
84
+ }, z.core.$strip>;
85
+ export declare const updateIssueStatusInput: z.ZodObject<{
86
+ issueId: z.ZodString;
87
+ status: z.ZodEnum<{
88
+ ACTIVE: "ACTIVE";
89
+ IGNORED: "IGNORED";
90
+ FIXED: "FIXED";
91
+ DUPLICATE: "DUPLICATE";
92
+ IN_PROGRESS_BUG: "IN_PROGRESS_BUG";
93
+ ARCHIVED_BUG: "ARCHIVED_BUG";
94
+ BLOCKED: "BLOCKED";
95
+ }>;
96
+ ignoreReason: z.ZodOptional<z.ZodEnum<{
97
+ INTENDED_BEHAVIOUR: "INTENDED_BEHAVIOUR";
98
+ INACCURATE_ASSESSMENT: "INACCURATE_ASSESSMENT";
99
+ NOT_IMPORTANT: "NOT_IMPORTANT";
100
+ }>>;
101
+ }, z.core.$strip>;
102
+ export declare const createIssueInput: z.ZodObject<{
103
+ title: z.ZodString;
104
+ description: z.ZodOptional<z.ZodString>;
105
+ issueType: z.ZodOptional<z.ZodEnum<{
106
+ BUG_ISSUE: "BUG_ISSUE";
107
+ SUGGESTION_ISSUE: "SUGGESTION_ISSUE";
108
+ OBSERVATION_ISSUE: "OBSERVATION_ISSUE";
109
+ TASK_ISSUE: "TASK_ISSUE";
110
+ }>>;
111
+ category: z.ZodOptional<z.ZodEnum<{
112
+ ACCESSIBILITY: "ACCESSIBILITY";
113
+ SECURITY: "SECURITY";
114
+ VISUAL: "VISUAL";
115
+ PERFORMANCE: "PERFORMANCE";
116
+ FUNCTIONAL: "FUNCTIONAL";
117
+ NETWORK: "NETWORK";
118
+ USABILITY: "USABILITY";
119
+ COMPATIBILITY: "COMPATIBILITY";
120
+ DATA_INTEGRITY: "DATA_INTEGRITY";
121
+ INTERACTION: "INTERACTION";
122
+ LOCALIZATION: "LOCALIZATION";
123
+ RESPONSIVENESS: "RESPONSIVENESS";
124
+ LAYOUT: "LAYOUT";
125
+ VISUAL_REGRESSION: "VISUAL_REGRESSION";
126
+ MEMORY: "MEMORY";
127
+ PERFORMANCE_REGRESSION: "PERFORMANCE_REGRESSION";
128
+ MEMORY_REGRESSION: "MEMORY_REGRESSION";
129
+ FORM_VALIDATION_BUG: "FORM_VALIDATION_BUG";
130
+ OTHER: "OTHER";
131
+ }>>;
132
+ severity: z.ZodOptional<z.ZodEnum<{
133
+ LOW_SEVERITY: "LOW_SEVERITY";
134
+ MEDIUM_SEVERITY: "MEDIUM_SEVERITY";
135
+ HIGH_SEVERITY: "HIGH_SEVERITY";
136
+ CRITICAL_SEVERITY: "CRITICAL_SEVERITY";
137
+ }>>;
138
+ status: z.ZodOptional<z.ZodEnum<{
139
+ ACTIVE: "ACTIVE";
140
+ IGNORED: "IGNORED";
141
+ FIXED: "FIXED";
142
+ DUPLICATE: "DUPLICATE";
143
+ IN_PROGRESS_BUG: "IN_PROGRESS_BUG";
144
+ ARCHIVED_BUG: "ARCHIVED_BUG";
145
+ BLOCKED: "BLOCKED";
146
+ }>>;
147
+ reportedReleaseId: z.ZodOptional<z.ZodString>;
148
+ dueDateMillis: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
149
+ assignee: z.ZodOptional<z.ZodString>;
150
+ linkTargets: z.ZodOptional<z.ZodArray<z.ZodObject<{
151
+ toEntityType: z.ZodEnum<{
152
+ STORY: "STORY";
153
+ SCENARIO: "SCENARIO";
154
+ TEST: "TEST";
155
+ ISSUE: "ISSUE";
156
+ EXTERNAL: "EXTERNAL";
157
+ TEST_EXECUTION: "TEST_EXECUTION";
158
+ BATCH_INVOCATION: "BATCH_INVOCATION";
159
+ }>;
160
+ toEntityId: z.ZodString;
161
+ }, z.core.$strip>>>;
162
+ labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
163
+ source: z.ZodOptional<z.ZodString>;
164
+ environment: z.ZodOptional<z.ZodString>;
165
+ attachments: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
166
+ artifactReference: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
167
+ }, z.core.$strip>;
82
168
  export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
83
169
  export declare const getBranchSpecificEndpointConfigInput: z.ZodObject<{
84
170
  branchName: z.ZodOptional<z.ZodString>;
@@ -80,6 +80,91 @@ export const getTestScenariosInput = z
80
80
  export const getManualSessionDetailsInput = z.object({
81
81
  manualSessionId: z.string().min(1),
82
82
  });
83
+ export const getIssueDetailsInput = z.object({
84
+ /** Accepts #B-123, B-123, #B123, B123, or plain 123 */
85
+ issueId: z.string().min(1),
86
+ });
87
+ export const updateIssueStatusInput = z.object({
88
+ issueId: z.string().min(1),
89
+ status: z.enum([
90
+ "ACTIVE",
91
+ "IGNORED",
92
+ "FIXED",
93
+ "DUPLICATE",
94
+ "IN_PROGRESS_BUG",
95
+ "ARCHIVED_BUG",
96
+ "BLOCKED",
97
+ ]),
98
+ ignoreReason: z
99
+ .enum(["INTENDED_BEHAVIOUR", "INACCURATE_ASSESSMENT", "NOT_IMPORTANT"])
100
+ .optional(),
101
+ });
102
+ const linkedEntityTypeSchema = z.enum([
103
+ "STORY",
104
+ "SCENARIO",
105
+ "TEST",
106
+ "ISSUE",
107
+ "EXTERNAL",
108
+ "TEST_EXECUTION",
109
+ "BATCH_INVOCATION",
110
+ ]);
111
+ const createIssueLinkTargetSchema = z.object({
112
+ toEntityType: linkedEntityTypeSchema,
113
+ toEntityId: z.string().min(1),
114
+ });
115
+ export const createIssueInput = z.object({
116
+ title: z.string().min(1),
117
+ description: z.string().optional(),
118
+ issueType: z
119
+ .enum(["BUG_ISSUE", "SUGGESTION_ISSUE", "OBSERVATION_ISSUE", "TASK_ISSUE"])
120
+ .optional(),
121
+ category: z
122
+ .enum([
123
+ "ACCESSIBILITY",
124
+ "SECURITY",
125
+ "VISUAL",
126
+ "PERFORMANCE",
127
+ "FUNCTIONAL",
128
+ "NETWORK",
129
+ "USABILITY",
130
+ "COMPATIBILITY",
131
+ "DATA_INTEGRITY",
132
+ "INTERACTION",
133
+ "LOCALIZATION",
134
+ "RESPONSIVENESS",
135
+ "LAYOUT",
136
+ "VISUAL_REGRESSION",
137
+ "MEMORY",
138
+ "PERFORMANCE_REGRESSION",
139
+ "MEMORY_REGRESSION",
140
+ "FORM_VALIDATION_BUG",
141
+ "OTHER",
142
+ ])
143
+ .optional(),
144
+ severity: z
145
+ .enum(["LOW_SEVERITY", "MEDIUM_SEVERITY", "HIGH_SEVERITY", "CRITICAL_SEVERITY"])
146
+ .optional(),
147
+ status: z
148
+ .enum([
149
+ "ACTIVE",
150
+ "IGNORED",
151
+ "FIXED",
152
+ "DUPLICATE",
153
+ "IN_PROGRESS_BUG",
154
+ "ARCHIVED_BUG",
155
+ "BLOCKED",
156
+ ])
157
+ .optional(),
158
+ reportedReleaseId: z.string().optional(),
159
+ dueDateMillis: z.coerce.number().optional(),
160
+ assignee: z.string().optional(),
161
+ linkTargets: z.array(createIssueLinkTargetSchema).optional(),
162
+ labels: z.array(z.string()).optional(),
163
+ source: z.string().optional(),
164
+ environment: z.string().optional(),
165
+ attachments: z.array(z.record(z.string(), z.unknown())).optional(),
166
+ artifactReference: z.record(z.string(), z.unknown()).optional(),
167
+ });
83
168
  export const emptyInput = z.object({});
84
169
  export const getBranchSpecificEndpointConfigInput = z.object({
85
170
  branchName: z.string().optional(),
@@ -219,6 +219,79 @@ export const TOOL_DEFINITIONS = [
219
219
  });
220
220
  },
221
221
  },
222
+ {
223
+ kebab: "get-issue-details",
224
+ description: "Fetch a TestChimp issue (bug) by ordinal id. Accepts flexible issueId formats: " +
225
+ "#B-123, B-123, #B123, B123, or plain 123. Returns title, description, status, linked entities, " +
226
+ "artifact references, and short-lived signed URLs for GCS attachments/screenshots.",
227
+ inputSchema: S.getIssueDetailsInput,
228
+ execute: async (args, { postMcp }) => {
229
+ const a = args;
230
+ return postMcp("/api/mcp/get_issue_details", {
231
+ issueId: a.issueId.trim(),
232
+ });
233
+ },
234
+ },
235
+ {
236
+ kebab: "update-issue-status",
237
+ description: "Update a TestChimp issue status by ordinal id (same flexible issueId formats as get-issue-details). " +
238
+ "status must be one of: ACTIVE, IGNORED, FIXED, DUPLICATE, IN_PROGRESS_BUG, ARCHIVED_BUG, BLOCKED. " +
239
+ "For /testchimp fix issue: set IN_PROGRESS_BUG after applying a code fix; set FIXED only after user confirmation / commits pushed. " +
240
+ "Optional ignoreReason when status is IGNORED: INTENDED_BEHAVIOUR | INACCURATE_ASSESSMENT | NOT_IMPORTANT.",
241
+ inputSchema: S.updateIssueStatusInput,
242
+ execute: async (args, { postMcp }) => {
243
+ const a = args;
244
+ const body = {
245
+ issueId: a.issueId.trim(),
246
+ status: a.status,
247
+ };
248
+ if (a.ignoreReason)
249
+ body.ignoreReason = a.ignoreReason;
250
+ return postMcp("/api/mcp/update_issue_status", body);
251
+ },
252
+ },
253
+ {
254
+ kebab: "create-issue",
255
+ description: "Create a TestChimp issue in the current project. title is required. " +
256
+ "Use simple fields for common creates, or pass the full curated contract via --json-input " +
257
+ "(description, issueType, category, severity, status, reportedReleaseId, dueDateMillis, assignee, " +
258
+ "linkTargets, labels, source, environment, attachments, artifactReference). " +
259
+ "Authenticated via project API key; project is resolved from the key.",
260
+ inputSchema: S.createIssueInput,
261
+ execute: async (args, { postMcp }) => {
262
+ const a = args;
263
+ const body = { title: a.title.trim() };
264
+ if (a.description != null)
265
+ body.description = a.description;
266
+ if (a.issueType)
267
+ body.issueType = a.issueType;
268
+ if (a.category)
269
+ body.category = a.category;
270
+ if (a.severity)
271
+ body.severity = a.severity;
272
+ if (a.status)
273
+ body.status = a.status;
274
+ if (a.reportedReleaseId != null)
275
+ body.reportedReleaseId = a.reportedReleaseId;
276
+ if (a.dueDateMillis != null)
277
+ body.dueDateMillis = a.dueDateMillis;
278
+ if (a.assignee != null)
279
+ body.assignee = a.assignee;
280
+ if (a.linkTargets?.length)
281
+ body.linkTargets = a.linkTargets;
282
+ if (a.labels?.length)
283
+ body.labels = a.labels;
284
+ if (a.source != null)
285
+ body.source = a.source;
286
+ if (a.environment != null)
287
+ body.environment = a.environment;
288
+ if (a.attachments?.length)
289
+ body.attachments = a.attachments;
290
+ if (a.artifactReference != null)
291
+ body.artifactReference = a.artifactReference;
292
+ return postMcp("/api/mcp/create_issue", body);
293
+ },
294
+ },
222
295
  {
223
296
  kebab: "mark-plan-items-implementation-done",
224
297
  description: "Mark user stories and/or test scenarios implementation-complete in platform lifecycle (DB only; does not rewrite plan markdown). " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
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",