@testchimp/cli 0.1.14 → 0.1.16
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/dist/cli/program.js +89 -12
- package/dist/core/schemas.d.ts +31 -2
- package/dist/core/schemas.js +33 -2
- package/dist/core/tools.js +80 -16
- package/package.json +1 -1
package/dist/cli/program.js
CHANGED
|
@@ -269,6 +269,53 @@ 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
|
+
});
|
|
272
319
|
program
|
|
273
320
|
.command("mark-plan-items-implementation-done")
|
|
274
321
|
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "mark-plan-items-implementation-done").description)
|
|
@@ -634,39 +681,69 @@ export function buildCliProgram() {
|
|
|
634
681
|
console.log(out);
|
|
635
682
|
});
|
|
636
683
|
program
|
|
637
|
-
.command("
|
|
638
|
-
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "
|
|
684
|
+
.command("report-sast-findings")
|
|
685
|
+
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "report-sast-findings").description)
|
|
639
686
|
.addOption(jsonInputOption())
|
|
640
|
-
.option("--id <scanId>")
|
|
687
|
+
.option("--id <scanId>", "Security scan id")
|
|
688
|
+
.option("--report-file <path>", "Path to full Semgrep CLI JSON report")
|
|
641
689
|
.action(async (opts) => {
|
|
642
690
|
const body = {};
|
|
643
691
|
if (opts.id)
|
|
644
692
|
body.id = String(opts.id);
|
|
645
|
-
|
|
693
|
+
if (opts.reportFile)
|
|
694
|
+
body.reportFile = String(opts.reportFile);
|
|
695
|
+
const merged = mergeBodies(body, opts.jsonInput);
|
|
696
|
+
if (!merged.id || String(merged.id).trim() === "") {
|
|
697
|
+
throw new Error("id is required");
|
|
698
|
+
}
|
|
699
|
+
if (!merged.reportFile || String(merged.reportFile).trim() === "") {
|
|
700
|
+
throw new Error("reportFile is required (--report-file)");
|
|
701
|
+
}
|
|
702
|
+
const out = await runTool("report-sast-findings", { id: String(merged.id).trim(), reportFile: String(merged.reportFile).trim() }, { postMcp });
|
|
646
703
|
console.log(out);
|
|
647
704
|
});
|
|
648
705
|
program
|
|
649
|
-
.command("
|
|
650
|
-
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "
|
|
706
|
+
.command("report-secrets-findings")
|
|
707
|
+
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "report-secrets-findings").description)
|
|
651
708
|
.addOption(jsonInputOption())
|
|
652
|
-
.option("--id <scanId>")
|
|
709
|
+
.option("--id <scanId>", "Security scan id")
|
|
710
|
+
.option("--report-file <path>", "Path to full Gitleaks JSON report")
|
|
653
711
|
.action(async (opts) => {
|
|
654
712
|
const body = {};
|
|
655
713
|
if (opts.id)
|
|
656
714
|
body.id = String(opts.id);
|
|
657
|
-
|
|
715
|
+
if (opts.reportFile)
|
|
716
|
+
body.reportFile = String(opts.reportFile);
|
|
717
|
+
const merged = mergeBodies(body, opts.jsonInput);
|
|
718
|
+
if (!merged.id || String(merged.id).trim() === "") {
|
|
719
|
+
throw new Error("id is required");
|
|
720
|
+
}
|
|
721
|
+
if (!merged.reportFile || String(merged.reportFile).trim() === "") {
|
|
722
|
+
throw new Error("reportFile is required (--report-file)");
|
|
723
|
+
}
|
|
724
|
+
const out = await runTool("report-secrets-findings", { id: String(merged.id).trim(), reportFile: String(merged.reportFile).trim() }, { postMcp });
|
|
658
725
|
console.log(out);
|
|
659
726
|
});
|
|
660
727
|
program
|
|
661
|
-
.command("
|
|
662
|
-
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "
|
|
728
|
+
.command("report-deps-findings")
|
|
729
|
+
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "report-deps-findings").description)
|
|
663
730
|
.addOption(jsonInputOption())
|
|
664
|
-
.option("--id <scanId>")
|
|
731
|
+
.option("--id <scanId>", "Security scan id")
|
|
732
|
+
.option("--report-file <path>", "Path to full Trivy JSON report")
|
|
665
733
|
.action(async (opts) => {
|
|
666
734
|
const body = {};
|
|
667
735
|
if (opts.id)
|
|
668
736
|
body.id = String(opts.id);
|
|
669
|
-
|
|
737
|
+
if (opts.reportFile)
|
|
738
|
+
body.reportFile = String(opts.reportFile);
|
|
739
|
+
const merged = mergeBodies(body, opts.jsonInput);
|
|
740
|
+
if (!merged.id || String(merged.id).trim() === "") {
|
|
741
|
+
throw new Error("id is required");
|
|
742
|
+
}
|
|
743
|
+
if (!merged.reportFile || String(merged.reportFile).trim() === "") {
|
|
744
|
+
throw new Error("reportFile is required (--report-file)");
|
|
745
|
+
}
|
|
746
|
+
const out = await runTool("report-deps-findings", { id: String(merged.id).trim(), reportFile: String(merged.reportFile).trim() }, { postMcp });
|
|
670
747
|
console.log(out);
|
|
671
748
|
});
|
|
672
749
|
program
|
package/dist/core/schemas.d.ts
CHANGED
|
@@ -79,6 +79,26 @@ 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>;
|
|
82
102
|
export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
|
|
83
103
|
export declare const getBranchSpecificEndpointConfigInput: z.ZodObject<{
|
|
84
104
|
branchName: z.ZodOptional<z.ZodString>;
|
|
@@ -577,8 +597,17 @@ export declare const reportDastFindingsInput: z.ZodObject<{
|
|
|
577
597
|
id: z.ZodString;
|
|
578
598
|
reportFile: z.ZodString;
|
|
579
599
|
}, z.core.$strip>;
|
|
580
|
-
export declare const
|
|
581
|
-
id: z.
|
|
600
|
+
export declare const reportSastFindingsInput: z.ZodObject<{
|
|
601
|
+
id: z.ZodString;
|
|
602
|
+
reportFile: z.ZodString;
|
|
603
|
+
}, z.core.$strip>;
|
|
604
|
+
export declare const reportSecretsFindingsInput: z.ZodObject<{
|
|
605
|
+
id: z.ZodString;
|
|
606
|
+
reportFile: z.ZodString;
|
|
607
|
+
}, z.core.$strip>;
|
|
608
|
+
export declare const reportDepsFindingsInput: z.ZodObject<{
|
|
609
|
+
id: z.ZodString;
|
|
610
|
+
reportFile: z.ZodString;
|
|
582
611
|
}, z.core.$strip>;
|
|
583
612
|
/** UpsertScreenStatesRequest JSON (proto camelCase). */
|
|
584
613
|
export declare const upsertScreenStatesInput: z.ZodObject<{
|
package/dist/core/schemas.js
CHANGED
|
@@ -80,6 +80,25 @@ 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
|
+
});
|
|
83
102
|
export const emptyInput = z.object({});
|
|
84
103
|
export const getBranchSpecificEndpointConfigInput = z.object({
|
|
85
104
|
branchName: z.string().optional(),
|
|
@@ -252,8 +271,20 @@ export const reportDastFindingsInput = z.object({
|
|
|
252
271
|
/** Path to ZAP Traditional JSON report file */
|
|
253
272
|
reportFile: z.string().min(1),
|
|
254
273
|
});
|
|
255
|
-
export const
|
|
256
|
-
id: z.string().min(1)
|
|
274
|
+
export const reportSastFindingsInput = z.object({
|
|
275
|
+
id: z.string().min(1),
|
|
276
|
+
/** Path to full Semgrep CLI JSON report file */
|
|
277
|
+
reportFile: z.string().min(1),
|
|
278
|
+
});
|
|
279
|
+
export const reportSecretsFindingsInput = z.object({
|
|
280
|
+
id: z.string().min(1),
|
|
281
|
+
/** Path to full Gitleaks JSON report file */
|
|
282
|
+
reportFile: z.string().min(1),
|
|
283
|
+
});
|
|
284
|
+
export const reportDepsFindingsInput = z.object({
|
|
285
|
+
id: z.string().min(1),
|
|
286
|
+
/** Path to full Trivy JSON report file */
|
|
287
|
+
reportFile: z.string().min(1),
|
|
257
288
|
});
|
|
258
289
|
const screenStatesEntrySchema = z.object({
|
|
259
290
|
screen: z.string().optional(),
|
package/dist/core/tools.js
CHANGED
|
@@ -219,6 +219,37 @@ 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
|
+
},
|
|
222
253
|
{
|
|
223
254
|
kebab: "mark-plan-items-implementation-done",
|
|
224
255
|
description: "Mark user stories and/or test scenarios implementation-complete in platform lifecycle (DB only; does not rewrite plan markdown). " +
|
|
@@ -481,8 +512,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
481
512
|
},
|
|
482
513
|
{
|
|
483
514
|
kebab: "get-security-scan-config",
|
|
484
|
-
description: "Fetch security scan config by scan id (
|
|
485
|
-
"Pass id (CLI: --id).
|
|
515
|
+
description: "Fetch security scan config by scan id (detail.dastCheckConfig / detail.sastCheckConfig / " +
|
|
516
|
+
"detail.depsCheckConfig / detail.leaksCheckConfig, release label, status). Pass id (CLI: --id). " +
|
|
517
|
+
"Used by /testchimp run security scan. For DAST honour allowActiveScan / useEphemeralSandbox / scope " +
|
|
518
|
+
"on dastCheckConfig.",
|
|
486
519
|
inputSchema: S.getSecurityScanConfigInput,
|
|
487
520
|
execute: async (args, { postMcp }) => {
|
|
488
521
|
const a = args;
|
|
@@ -492,7 +525,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
492
525
|
{
|
|
493
526
|
kebab: "update-scan-progress",
|
|
494
527
|
description: "Update a scan's status. status must be one of: QUEUED, IN_PROGRESS, COMPLETED, EXCEPTION. " +
|
|
495
|
-
"Call IN_PROGRESS when starting
|
|
528
|
+
"Call IN_PROGRESS when starting. Each scan is a single checker type: the category playbook " +
|
|
529
|
+
"sets COMPLETED after a successful report-*-findings (or EXCEPTION on hard failure).",
|
|
496
530
|
inputSchema: S.updateScanProgressInput,
|
|
497
531
|
execute: async (args, { postMcp }) => {
|
|
498
532
|
const a = args;
|
|
@@ -506,7 +540,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
506
540
|
kebab: "report-dast-findings",
|
|
507
541
|
description: "Upload a ZAP Traditional JSON report for a security scan. Pass --id and --report-file <path>. " +
|
|
508
542
|
"Backend parses alerts, dedupes by bug hash, and inserts new SECURITY bugs linked to the scan. " +
|
|
509
|
-
"Does not mark the scan COMPLETED.",
|
|
543
|
+
"Does not mark the scan COMPLETED — the DAST playbook calls update-scan-progress COMPLETED after this.",
|
|
510
544
|
inputSchema: S.reportDastFindingsInput,
|
|
511
545
|
execute: async (args, { postMcp }) => {
|
|
512
546
|
const a = args;
|
|
@@ -519,22 +553,52 @@ export const TOOL_DEFINITIONS = [
|
|
|
519
553
|
},
|
|
520
554
|
},
|
|
521
555
|
{
|
|
522
|
-
kebab: "
|
|
523
|
-
description: "
|
|
524
|
-
|
|
525
|
-
|
|
556
|
+
kebab: "report-sast-findings",
|
|
557
|
+
description: "Upload a full Semgrep CLI JSON report for a SAST security scan. Pass --id and --report-file <path>. " +
|
|
558
|
+
"Backend stores the raw report, parses results, dedupes by bug hash, and inserts new SECURITY bugs. " +
|
|
559
|
+
"Does not mark the scan COMPLETED — the SAST playbook calls update-scan-progress COMPLETED after this.",
|
|
560
|
+
inputSchema: S.reportSastFindingsInput,
|
|
561
|
+
execute: async (args, { postMcp }) => {
|
|
562
|
+
const a = args;
|
|
563
|
+
const { readFile } = await import("node:fs/promises");
|
|
564
|
+
const reportJson = await readFile(a.reportFile, "utf8");
|
|
565
|
+
return postMcp("/api/mcp/report_sast_findings", {
|
|
566
|
+
scanId: a.id.trim(),
|
|
567
|
+
reportJson,
|
|
568
|
+
});
|
|
569
|
+
},
|
|
526
570
|
},
|
|
527
571
|
{
|
|
528
|
-
kebab: "
|
|
529
|
-
description: "
|
|
530
|
-
|
|
531
|
-
|
|
572
|
+
kebab: "report-secrets-findings",
|
|
573
|
+
description: "Upload a full Gitleaks JSON report for a secrets security scan. Pass --id and --report-file <path>. " +
|
|
574
|
+
"Backend redacts secret payloads, stores the report, dedupes by bug hash, and inserts SECURITY bugs. " +
|
|
575
|
+
"Does not mark the scan COMPLETED — the secrets playbook calls update-scan-progress COMPLETED after this.",
|
|
576
|
+
inputSchema: S.reportSecretsFindingsInput,
|
|
577
|
+
execute: async (args, { postMcp }) => {
|
|
578
|
+
const a = args;
|
|
579
|
+
const { readFile } = await import("node:fs/promises");
|
|
580
|
+
const reportJson = await readFile(a.reportFile, "utf8");
|
|
581
|
+
return postMcp("/api/mcp/report_secrets_findings", {
|
|
582
|
+
scanId: a.id.trim(),
|
|
583
|
+
reportJson,
|
|
584
|
+
});
|
|
585
|
+
},
|
|
532
586
|
},
|
|
533
587
|
{
|
|
534
|
-
kebab: "
|
|
535
|
-
description: "
|
|
536
|
-
|
|
537
|
-
|
|
588
|
+
kebab: "report-deps-findings",
|
|
589
|
+
description: "Upload a full Trivy JSON report for a dependency security scan. Pass --id and --report-file <path>. " +
|
|
590
|
+
"Backend stores the report, filters by security profile / ignore-unfixed, dedupes, and inserts SECURITY bugs. " +
|
|
591
|
+
"Does not mark the scan COMPLETED — the deps playbook calls update-scan-progress COMPLETED after this.",
|
|
592
|
+
inputSchema: S.reportDepsFindingsInput,
|
|
593
|
+
execute: async (args, { postMcp }) => {
|
|
594
|
+
const a = args;
|
|
595
|
+
const { readFile } = await import("node:fs/promises");
|
|
596
|
+
const reportJson = await readFile(a.reportFile, "utf8");
|
|
597
|
+
return postMcp("/api/mcp/report_deps_findings", {
|
|
598
|
+
scanId: a.id.trim(),
|
|
599
|
+
reportJson,
|
|
600
|
+
});
|
|
601
|
+
},
|
|
538
602
|
},
|
|
539
603
|
{
|
|
540
604
|
kebab: "upsert-screen-states",
|