@testchimp/cli 0.1.19 → 0.1.21

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.
@@ -897,6 +897,107 @@ export function buildCliProgram() {
897
897
  const out = await runTool("report-requirement-quality-findings", merged, { postMcp });
898
898
  console.log(out);
899
899
  });
900
+ // Workflow policy + traceability tools (US-181). Prefer --json-input for nested TestLocator.
901
+ program
902
+ .command("report-agent-action")
903
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "report-agent-action").description)
904
+ .addOption(jsonInputOption())
905
+ .requiredOption("--workflow-id <id>", "Catalog workflow id")
906
+ .requiredOption("--workflow-execution-id <ulid>", "Stable ULID for the whole run")
907
+ .requiredOption("--action-type <type>", "CREATED|UPDATED|DELETED|ANALYZED|ACTION_COMPLETED|ACTION_FAILED")
908
+ .option("--policy-file <name>", "Policy filename")
909
+ .option("--policy-version <semver>", "Policy version from frontmatter")
910
+ .option("--git-sha <sha>", "Current HEAD sha")
911
+ .option("--actor-type <type>", "LOCAL_AGENT|CLOUD_AGENT (or local-agent|cloud-agent)")
912
+ .option("--user-id <id>", "Optional user id for traceability")
913
+ .option("--branch-name <name>", "Git branch")
914
+ .option("--entity-type <type>", "test|story|scenario|issue|workflow|…")
915
+ .option("--entity-identity <ordinal>", "Project-scoped ordinal id (mutually exclusive with --test-json)")
916
+ .option("--test-json <json>", "TestLocator JSON (folderPath/fileName/testSuite/testName)")
917
+ .option("--detail-json <json>", "Optional detail payload")
918
+ .action(async (opts) => {
919
+ const body = {
920
+ workflowId: String(opts.workflowId),
921
+ workflowExecutionId: String(opts.workflowExecutionId),
922
+ actionType: String(opts.actionType),
923
+ };
924
+ if (opts.policyFile)
925
+ body.policyFile = String(opts.policyFile);
926
+ if (opts.policyVersion)
927
+ body.policyVersion = String(opts.policyVersion);
928
+ if (opts.gitSha)
929
+ body.gitSha = String(opts.gitSha);
930
+ if (opts.actorType)
931
+ body.actorType = String(opts.actorType);
932
+ if (opts.userId)
933
+ body.userId = String(opts.userId);
934
+ if (opts.branchName)
935
+ body.branchName = String(opts.branchName);
936
+ if (opts.entityType)
937
+ body.entityType = String(opts.entityType);
938
+ if (opts.entityIdentity)
939
+ body.entityIdentity = String(opts.entityIdentity);
940
+ if (opts.testJson)
941
+ body.test = JSON.parse(String(opts.testJson));
942
+ if (opts.detailJson)
943
+ body.detailJson = String(opts.detailJson);
944
+ const merged = mergeBodies(body, opts.jsonInput);
945
+ console.log(await runTool("report-agent-action", merged, { postMcp }));
946
+ });
947
+ program
948
+ .command("get-last-run-workflow-detail")
949
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "get-last-run-workflow-detail").description)
950
+ .addOption(jsonInputOption())
951
+ .requiredOption("--workflow-id <id>", "Catalog workflow id")
952
+ .option("--branch-name <name>", "Optional branch filter (omit for any branch)")
953
+ .option("--user-id <id>", "Optional per-user last run")
954
+ .action(async (opts) => {
955
+ const body = { workflowId: String(opts.workflowId) };
956
+ if (opts.branchName)
957
+ body.branchName = String(opts.branchName);
958
+ if (opts.userId)
959
+ body.userId = String(opts.userId);
960
+ const merged = mergeBodies(body, opts.jsonInput);
961
+ console.log(await runTool("get-last-run-workflow-detail", merged, { postMcp }));
962
+ });
963
+ for (const kebab of [
964
+ "list-workflow-executions",
965
+ "get-workflow-execution",
966
+ "get-policy",
967
+ "list-policies",
968
+ "list-workflow-catalog",
969
+ ]) {
970
+ program
971
+ .command(kebab)
972
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === kebab).description)
973
+ .addOption(jsonInputOption())
974
+ .action(async (opts) => {
975
+ const merged = mergeBodies({}, opts.jsonInput);
976
+ console.log(await runTool(kebab, merged, { postMcp }));
977
+ });
978
+ }
979
+ program
980
+ .command("upsert-policy")
981
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "upsert-policy").description)
982
+ .addOption(jsonInputOption())
983
+ .option("--policy-file-name <name>", "e.g. connect-to-test-env.policy.md")
984
+ .option("--content <markdown>", "full markdown including frontmatter")
985
+ .option("--content-file <path>", "read markdown from file")
986
+ .action(async (opts) => {
987
+ let content = opts.content;
988
+ if (opts.contentFile)
989
+ content = await readFile(String(opts.contentFile), "utf8");
990
+ const body = {};
991
+ if (opts.policyFileName)
992
+ body.policyFileName = String(opts.policyFileName);
993
+ if (content)
994
+ body.content = content;
995
+ const merged = mergeBodies(body, opts.jsonInput);
996
+ if (!merged.policyFileName || !merged.content) {
997
+ throw new Error("Provide --policy-file-name and --content or --content-file (or full body via --json-input)");
998
+ }
999
+ console.log(await runTool("upsert-policy", merged, { postMcp }));
1000
+ });
900
1001
  program.on("--help", () => {
901
1002
  /* default */
902
1003
  });
@@ -1015,3 +1015,90 @@ export declare const reportRequirementQualityFindingsInput: z.ZodObject<{
1015
1015
  subjectEntityId: z.ZodOptional<z.ZodString>;
1016
1016
  ordinalId: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1017
1017
  }, z.core.$strip>;
1018
+ export declare const agentActorTypeSchema: z.ZodEnum<{
1019
+ LOCAL_AGENT: "LOCAL_AGENT";
1020
+ CLOUD_AGENT: "CLOUD_AGENT";
1021
+ "local-agent": "local-agent";
1022
+ "cloud-agent": "cloud-agent";
1023
+ }>;
1024
+ export declare const agentActionTypeSchema: z.ZodEnum<{
1025
+ failed: "failed";
1026
+ CREATED: "CREATED";
1027
+ UPDATED: "UPDATED";
1028
+ DELETED: "DELETED";
1029
+ ANALYZED: "ANALYZED";
1030
+ ACTION_COMPLETED: "ACTION_COMPLETED";
1031
+ ACTION_FAILED: "ACTION_FAILED";
1032
+ created: "created";
1033
+ updated: "updated";
1034
+ deleted: "deleted";
1035
+ analyzed: "analyzed";
1036
+ completed: "completed";
1037
+ action_completed: "action_completed";
1038
+ action_failed: "action_failed";
1039
+ }>;
1040
+ export declare const reportAgentActionInput: z.ZodObject<{
1041
+ workflowId: z.ZodString;
1042
+ workflowExecutionId: z.ZodString;
1043
+ policyFile: z.ZodOptional<z.ZodString>;
1044
+ policyVersion: z.ZodOptional<z.ZodString>;
1045
+ gitSha: z.ZodOptional<z.ZodString>;
1046
+ actorType: z.ZodOptional<z.ZodEnum<{
1047
+ LOCAL_AGENT: "LOCAL_AGENT";
1048
+ CLOUD_AGENT: "CLOUD_AGENT";
1049
+ "local-agent": "local-agent";
1050
+ "cloud-agent": "cloud-agent";
1051
+ }>>;
1052
+ userId: z.ZodOptional<z.ZodString>;
1053
+ branchName: z.ZodOptional<z.ZodString>;
1054
+ entityType: z.ZodOptional<z.ZodString>;
1055
+ entityIdentity: z.ZodOptional<z.ZodString>;
1056
+ test: z.ZodOptional<z.ZodObject<{
1057
+ folderPath: z.ZodOptional<z.ZodArray<z.ZodString>>;
1058
+ fileName: z.ZodString;
1059
+ testSuite: z.ZodOptional<z.ZodArray<z.ZodString>>;
1060
+ testName: z.ZodString;
1061
+ }, z.core.$strip>>;
1062
+ actionType: z.ZodEnum<{
1063
+ failed: "failed";
1064
+ CREATED: "CREATED";
1065
+ UPDATED: "UPDATED";
1066
+ DELETED: "DELETED";
1067
+ ANALYZED: "ANALYZED";
1068
+ ACTION_COMPLETED: "ACTION_COMPLETED";
1069
+ ACTION_FAILED: "ACTION_FAILED";
1070
+ created: "created";
1071
+ updated: "updated";
1072
+ deleted: "deleted";
1073
+ analyzed: "analyzed";
1074
+ completed: "completed";
1075
+ action_completed: "action_completed";
1076
+ action_failed: "action_failed";
1077
+ }>;
1078
+ detailJson: z.ZodOptional<z.ZodString>;
1079
+ }, z.core.$strip>;
1080
+ export declare const getLastRunWorkflowDetailInput: z.ZodObject<{
1081
+ workflowId: z.ZodString;
1082
+ branchName: z.ZodOptional<z.ZodString>;
1083
+ userId: z.ZodOptional<z.ZodString>;
1084
+ }, z.core.$strip>;
1085
+ export declare const listWorkflowExecutionsInput: z.ZodObject<{
1086
+ workflowId: z.ZodOptional<z.ZodString>;
1087
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1088
+ offset: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
1089
+ }, z.core.$strip>;
1090
+ export declare const getWorkflowExecutionInput: z.ZodObject<{
1091
+ workflowExecutionId: z.ZodString;
1092
+ includeActions: z.ZodOptional<z.ZodBoolean>;
1093
+ }, z.core.$strip>;
1094
+ export declare const getPolicyInput: z.ZodObject<{
1095
+ policyFileName: z.ZodString;
1096
+ }, z.core.$strip>;
1097
+ export declare const listPoliciesInput: z.ZodObject<{
1098
+ workflowId: z.ZodOptional<z.ZodString>;
1099
+ }, z.core.$strip>;
1100
+ export declare const upsertPolicyInput: z.ZodObject<{
1101
+ policyFileName: z.ZodString;
1102
+ content: z.ZodString;
1103
+ }, z.core.$strip>;
1104
+ export declare const listWorkflowCatalogInput: z.ZodObject<{}, z.core.$strip>;
@@ -500,3 +500,72 @@ export const reportRequirementQualityFindingsInput = z
500
500
  });
501
501
  }
502
502
  });
503
+ export const agentActorTypeSchema = z.enum(["LOCAL_AGENT", "CLOUD_AGENT", "local-agent", "cloud-agent"]);
504
+ export const agentActionTypeSchema = z.enum([
505
+ "CREATED",
506
+ "UPDATED",
507
+ "DELETED",
508
+ "ANALYZED",
509
+ "ACTION_COMPLETED",
510
+ "ACTION_FAILED",
511
+ "created",
512
+ "updated",
513
+ "deleted",
514
+ "analyzed",
515
+ "completed",
516
+ "failed",
517
+ "action_completed",
518
+ "action_failed",
519
+ ]);
520
+ export const reportAgentActionInput = z
521
+ .object({
522
+ workflowId: z.string().min(1),
523
+ workflowExecutionId: z.string().min(1),
524
+ policyFile: z.string().optional(),
525
+ policyVersion: z.string().optional(),
526
+ gitSha: z.string().optional(),
527
+ actorType: agentActorTypeSchema.optional(),
528
+ userId: z.string().optional(),
529
+ branchName: z.string().optional(),
530
+ entityType: z.string().optional(),
531
+ /** Project-scoped ordinal id (or explicitly provided execution/batch id). Mutually exclusive with `test`. */
532
+ entityIdentity: z.string().optional(),
533
+ /** SmartTest TestLocator. Mutually exclusive with `entityIdentity`. */
534
+ test: testLocatorSchema.optional(),
535
+ actionType: agentActionTypeSchema,
536
+ detailJson: z.string().optional(),
537
+ })
538
+ .superRefine((val, ctx) => {
539
+ if (val.test && val.entityIdentity) {
540
+ ctx.addIssue({
541
+ code: z.ZodIssueCode.custom,
542
+ message: "Provide either test (TestLocator) or entityIdentity (ordinal), not both",
543
+ path: ["test"],
544
+ });
545
+ }
546
+ });
547
+ export const getLastRunWorkflowDetailInput = z.object({
548
+ workflowId: z.string().min(1),
549
+ branchName: z.string().optional(),
550
+ userId: z.string().optional(),
551
+ });
552
+ export const listWorkflowExecutionsInput = z.object({
553
+ workflowId: z.string().optional(),
554
+ limit: z.coerce.number().int().positive().max(200).optional(),
555
+ offset: z.coerce.number().int().nonnegative().optional(),
556
+ });
557
+ export const getWorkflowExecutionInput = z.object({
558
+ workflowExecutionId: z.string().min(1),
559
+ includeActions: z.boolean().optional(),
560
+ });
561
+ export const getPolicyInput = z.object({
562
+ policyFileName: z.string().min(1),
563
+ });
564
+ export const listPoliciesInput = z.object({
565
+ workflowId: z.string().optional(),
566
+ });
567
+ export const upsertPolicyInput = z.object({
568
+ policyFileName: z.string().min(1),
569
+ content: z.string().min(1),
570
+ });
571
+ export const listWorkflowCatalogInput = z.object({});
@@ -776,6 +776,140 @@ export const TOOL_DEFINITIONS = [
776
776
  return postMcp("/api/mcp/report_requirement_quality_findings", { report });
777
777
  },
778
778
  },
779
+ {
780
+ kebab: "report-agent-action",
781
+ description: "Report a mutating agent action under a stable workflow-execution-id (ULID). " +
782
+ "First call for an id creates the workflow_executions row; later calls append agent_actions. " +
783
+ "Identity: pass `test` (TestLocator: folderPath/fileName/testSuite/testName) for SmartTests, " +
784
+ "or `entityIdentity` as a project-scoped ordinal id for stories/scenarios/issues " +
785
+ "(or an execution/batch id only when the prompt explicitly provided it). Do not use platform UUIDs.",
786
+ inputSchema: S.reportAgentActionInput,
787
+ execute: async (args, { postMcp }) => {
788
+ const a = args;
789
+ const actorRaw = (a.actorType ?? "local-agent").toString();
790
+ const actorType = actorRaw.toUpperCase().replace(/-/g, "_") === "CLOUD_AGENT" ? "CLOUD_AGENT" : "LOCAL_AGENT";
791
+ const actionNorm = a.actionType.toString().toUpperCase().replace(/-/g, "_");
792
+ let actionType = actionNorm;
793
+ if (actionNorm === "COMPLETED" || actionNorm === "ACTION_COMPLETED") {
794
+ actionType = "ACTION_COMPLETED";
795
+ }
796
+ else if (actionNorm === "FAILED" || actionNorm === "ACTION_FAILED") {
797
+ actionType = "ACTION_FAILED";
798
+ }
799
+ const body = {
800
+ workflowId: a.workflowId,
801
+ workflowExecutionId: a.workflowExecutionId,
802
+ actionType,
803
+ actorType,
804
+ };
805
+ if (a.policyFile)
806
+ body.policyFile = a.policyFile;
807
+ if (a.policyVersion)
808
+ body.policyVersion = a.policyVersion;
809
+ if (a.gitSha)
810
+ body.gitSha = a.gitSha;
811
+ if (a.userId)
812
+ body.userId = a.userId;
813
+ else if (process.env.TESTCHIMP_USER_ID)
814
+ body.userId = process.env.TESTCHIMP_USER_ID;
815
+ if (a.branchName)
816
+ body.branchName = a.branchName;
817
+ if (a.entityType)
818
+ body.entityType = a.entityType;
819
+ if (a.test) {
820
+ body.test = a.test;
821
+ }
822
+ else if (a.entityIdentity) {
823
+ body.entityIdentity = a.entityIdentity;
824
+ }
825
+ if (a.detailJson)
826
+ body.detailJson = a.detailJson;
827
+ return postMcp("/api/mcp/report_agent_action", body);
828
+ },
829
+ },
830
+ {
831
+ kebab: "get-last-run-workflow-detail",
832
+ description: "Fetch the last workflow execution for a workflow-id on a branch (optional userId for per-user last run). " +
833
+ "Used for since-last-run scoping.",
834
+ inputSchema: S.getLastRunWorkflowDetailInput,
835
+ execute: async (args, { postMcp }) => {
836
+ const a = args;
837
+ const body = {
838
+ workflowId: a.workflowId,
839
+ branchName: a.branchName,
840
+ };
841
+ if (a.userId)
842
+ body.userId = a.userId;
843
+ return postMcp("/api/mcp/get_last_run_workflow_detail", body);
844
+ },
845
+ },
846
+ {
847
+ kebab: "list-workflow-executions",
848
+ description: "List recent workflow executions for the project, optionally filtered by workflowId.",
849
+ inputSchema: S.listWorkflowExecutionsInput,
850
+ execute: async (args, { postMcp }) => {
851
+ const a = args;
852
+ const body = {};
853
+ if (a.workflowId)
854
+ body.workflowId = a.workflowId;
855
+ if (a.limit != null)
856
+ body.limit = a.limit;
857
+ if (a.offset != null)
858
+ body.offset = a.offset;
859
+ return postMcp("/api/mcp/list_workflow_executions", body);
860
+ },
861
+ },
862
+ {
863
+ kebab: "get-workflow-execution",
864
+ description: "Get a workflow execution by id; pass includeActions=true for the action timeline.",
865
+ inputSchema: S.getWorkflowExecutionInput,
866
+ execute: async (args, { postMcp }) => {
867
+ const a = args;
868
+ return postMcp("/api/mcp/get_workflow_execution", {
869
+ workflowExecutionId: a.workflowExecutionId,
870
+ includeActions: a.includeActions ?? true,
871
+ });
872
+ },
873
+ },
874
+ {
875
+ kebab: "get-policy",
876
+ description: "Fetch a workflow policy file by name (e.g. run-qa.policy.md) from the platform POLICY_FILE store. Filename is coerced to *.policy.md (same as upsert-policy).",
877
+ inputSchema: S.getPolicyInput,
878
+ execute: async (args, { postMcp }) => {
879
+ const a = args;
880
+ return postMcp("/api/mcp/get_policy", { policyFileName: a.policyFileName });
881
+ },
882
+ },
883
+ {
884
+ kebab: "list-policies",
885
+ description: "List policy files for an optional workflow-id. Marks isDefault when filename is <workflow-id>.policy.md.",
886
+ inputSchema: S.listPoliciesInput,
887
+ execute: async (args, { postMcp }) => {
888
+ const a = args;
889
+ const body = {};
890
+ if (a.workflowId)
891
+ body.workflowId = a.workflowId;
892
+ return postMcp("/api/mcp/list_policies", body);
893
+ },
894
+ },
895
+ {
896
+ kebab: "upsert-policy",
897
+ description: "Create or update a workflow policy file on the platform (plans/knowledge/policies/*.policy.md). policyFileName is coerced to *.policy.md (same as get-policy). Prefer after writing the file locally so the policy is available immediately (git sync also works later).",
898
+ inputSchema: S.upsertPolicyInput,
899
+ execute: async (args, { postMcp }) => {
900
+ const a = args;
901
+ return postMcp("/api/mcp/upsert_policy", {
902
+ policyFileName: a.policyFileName,
903
+ content: a.content,
904
+ });
905
+ },
906
+ },
907
+ {
908
+ kebab: "list-workflow-catalog",
909
+ description: "List supported TestChimp workflows with Active / Disabled / Missing Config status for the project.",
910
+ inputSchema: S.listWorkflowCatalogInput,
911
+ execute: async (_args, { postMcp }) => postMcp("/api/mcp/list_workflow_catalog", {}),
912
+ },
779
913
  ];
780
914
  const TOOL_BY_KEBAB = new Map(TOOL_DEFINITIONS.map((t) => [t.kebab, t]));
781
915
  export function getToolDefinition(kebab) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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",