@testchimp/cli 0.1.21 → 0.1.22

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.
@@ -5,6 +5,7 @@ import { DEFAULT_BACKEND, postMcp } from "../core/client.js";
5
5
  import { deepMerge } from "../core/merge.js";
6
6
  import { runTool } from "../core/tools.js";
7
7
  import { TOOL_DEFINITIONS } from "../core/tools.js";
8
+ import { resolveGitHeadSha } from "../core/gitSha.js";
8
9
  import { PACKAGE_VERSION } from "../core/version.js";
9
10
  export { PACKAGE_VERSION };
10
11
  function parseRecordTypesCsv(raw) {
@@ -393,6 +394,39 @@ export function buildCliProgram() {
393
394
  const out = await runTool("mark-plan-items-implementation-done", merged, { postMcp });
394
395
  console.log(out);
395
396
  });
397
+ program
398
+ .command("update-plan-items-lifecycle-status")
399
+ .description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-plan-items-lifecycle-status").description)
400
+ .addOption(jsonInputOption())
401
+ .option("--entity-type <type>", "story | scenario")
402
+ .option("--ordinal-id <n>", "numeric US-/TS- ordinal")
403
+ .option("--status <status>", "draft | ready | in progress | blocked | done | archived")
404
+ .action(async (opts) => {
405
+ const body = {};
406
+ if (opts.entityType)
407
+ body.entityType = String(opts.entityType).trim();
408
+ if (opts.ordinalId != null && String(opts.ordinalId).trim() !== "") {
409
+ body.ordinalId = Number(String(opts.ordinalId).trim());
410
+ }
411
+ if (opts.status)
412
+ body.status = String(opts.status).trim();
413
+ const merged = mergeBodies(body, opts.jsonInput);
414
+ if (!merged.entityType || String(merged.entityType).trim() === "") {
415
+ throw new Error("entity-type is required (story | scenario)");
416
+ }
417
+ if (merged.ordinalId == null || !Number.isFinite(merged.ordinalId) || merged.ordinalId <= 0) {
418
+ throw new Error("ordinal-id is required (positive integer)");
419
+ }
420
+ if (!merged.status || String(merged.status).trim() === "") {
421
+ throw new Error("status is required (draft | ready | in progress | blocked | done | archived)");
422
+ }
423
+ const out = await runTool("update-plan-items-lifecycle-status", {
424
+ entityType: String(merged.entityType).trim(),
425
+ ordinalId: merged.ordinalId,
426
+ status: String(merged.status).trim(),
427
+ }, { postMcp });
428
+ console.log(out);
429
+ });
396
430
  program
397
431
  .command("update-test-scenario")
398
432
  .description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-test-scenario").description)
@@ -904,43 +938,40 @@ export function buildCliProgram() {
904
938
  .addOption(jsonInputOption())
905
939
  .requiredOption("--workflow-id <id>", "Catalog workflow id")
906
940
  .requiredOption("--workflow-execution-id <ulid>", "Stable ULID for the whole run")
907
- .requiredOption("--action-type <type>", "CREATED|UPDATED|DELETED|ANALYZED|ACTION_COMPLETED|ACTION_FAILED")
941
+ .requiredOption("--action-type <type>", "CREATED|UPDATED|DELETED|ANALYZED|IMPLEMENTED|ACTION_COMPLETED|ACTION_FAILED")
908
942
  .option("--policy-file <name>", "Policy filename")
909
943
  .option("--policy-version <semver>", "Policy version from frontmatter")
910
944
  .option("--git-sha <sha>", "Current HEAD sha")
911
945
  .option("--actor-type <type>", "LOCAL_AGENT|CLOUD_AGENT (or local-agent|cloud-agent)")
912
946
  .option("--user-id <id>", "Optional user id for traceability")
913
947
  .option("--branch-name <name>", "Git branch")
914
- .option("--entity-type <type>", "test|story|scenario|issue|workflow|…")
948
+ .requiredOption("--entity-type <type>", "USER_STORY|SCENARIO|SMART_TEST|POLICY|ISSUE|TEST_EXECUTION|TEST_INVOCATION_BATCH|EXPLORATION|EVENT|WORKFLOW")
915
949
  .option("--entity-identity <ordinal>", "Project-scoped ordinal id (mutually exclusive with --test-json)")
916
950
  .option("--test-json <json>", "TestLocator JSON (folderPath/fileName/testSuite/testName)")
917
- .option("--detail-json <json>", "Optional detail payload")
918
951
  .action(async (opts) => {
919
952
  const body = {
920
953
  workflowId: String(opts.workflowId),
921
954
  workflowExecutionId: String(opts.workflowExecutionId),
922
955
  actionType: String(opts.actionType),
956
+ entityType: String(opts.entityType),
923
957
  };
924
958
  if (opts.policyFile)
925
959
  body.policyFile = String(opts.policyFile);
926
960
  if (opts.policyVersion)
927
961
  body.policyVersion = String(opts.policyVersion);
928
- if (opts.gitSha)
929
- body.gitSha = String(opts.gitSha);
962
+ const gitSha = resolveGitHeadSha(opts.gitSha ? String(opts.gitSha) : undefined);
963
+ if (gitSha)
964
+ body.gitSha = gitSha;
930
965
  if (opts.actorType)
931
966
  body.actorType = String(opts.actorType);
932
967
  if (opts.userId)
933
968
  body.userId = String(opts.userId);
934
969
  if (opts.branchName)
935
970
  body.branchName = String(opts.branchName);
936
- if (opts.entityType)
937
- body.entityType = String(opts.entityType);
938
971
  if (opts.entityIdentity)
939
972
  body.entityIdentity = String(opts.entityIdentity);
940
973
  if (opts.testJson)
941
974
  body.test = JSON.parse(String(opts.testJson));
942
- if (opts.detailJson)
943
- body.detailJson = String(opts.detailJson);
944
975
  const merged = mergeBodies(body, opts.jsonInput);
945
976
  console.log(await runTool("report-agent-action", merged, { postMcp }));
946
977
  });
@@ -0,0 +1,2 @@
1
+ /** Current git HEAD when running in a repo; used when agents omit git_sha on report-agent-action. */
2
+ export declare function resolveGitHeadSha(provided?: string): string | undefined;
@@ -0,0 +1,14 @@
1
+ import { execSync } from "node:child_process";
2
+ /** Current git HEAD when running in a repo; used when agents omit git_sha on report-agent-action. */
3
+ export function resolveGitHeadSha(provided) {
4
+ const trimmed = provided?.trim();
5
+ if (trimmed) {
6
+ return trimmed;
7
+ }
8
+ try {
9
+ return execSync("git rev-parse HEAD", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ }
@@ -70,6 +70,11 @@ export declare const markPlanItemsImplementationDoneInput: z.ZodObject<{
70
70
  scenarioOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
71
71
  userStoryOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
72
72
  }, z.core.$strip>;
73
+ export declare const updatePlanItemsLifecycleStatusInput: z.ZodObject<{
74
+ entityType: z.ZodString;
75
+ ordinalId: z.ZodCoercedNumber<unknown>;
76
+ status: z.ZodString;
77
+ }, z.core.$strip>;
73
78
  export declare const getUserStoriesInput: z.ZodObject<{
74
79
  userStoryOrdinalIds: z.ZodArray<z.ZodCoercedNumber<unknown>>;
75
80
  }, z.core.$strip>;
@@ -1021,6 +1026,19 @@ export declare const agentActorTypeSchema: z.ZodEnum<{
1021
1026
  "local-agent": "local-agent";
1022
1027
  "cloud-agent": "cloud-agent";
1023
1028
  }>;
1029
+ /** Closed vocabulary for report-agent-action entity_type (agent_workflow.proto AgentActionEntityType). */
1030
+ export declare const agentActionEntityTypeSchema: z.ZodEnum<{
1031
+ SMART_TEST: "SMART_TEST";
1032
+ SCENARIO: "SCENARIO";
1033
+ ISSUE: "ISSUE";
1034
+ TEST_EXECUTION: "TEST_EXECUTION";
1035
+ USER_STORY: "USER_STORY";
1036
+ POLICY: "POLICY";
1037
+ TEST_INVOCATION_BATCH: "TEST_INVOCATION_BATCH";
1038
+ EXPLORATION: "EXPLORATION";
1039
+ EVENT: "EVENT";
1040
+ WORKFLOW: "WORKFLOW";
1041
+ }>;
1024
1042
  export declare const agentActionTypeSchema: z.ZodEnum<{
1025
1043
  failed: "failed";
1026
1044
  CREATED: "CREATED";
@@ -1029,6 +1047,7 @@ export declare const agentActionTypeSchema: z.ZodEnum<{
1029
1047
  ANALYZED: "ANALYZED";
1030
1048
  ACTION_COMPLETED: "ACTION_COMPLETED";
1031
1049
  ACTION_FAILED: "ACTION_FAILED";
1050
+ IMPLEMENTED: "IMPLEMENTED";
1032
1051
  created: "created";
1033
1052
  updated: "updated";
1034
1053
  deleted: "deleted";
@@ -1036,6 +1055,7 @@ export declare const agentActionTypeSchema: z.ZodEnum<{
1036
1055
  completed: "completed";
1037
1056
  action_completed: "action_completed";
1038
1057
  action_failed: "action_failed";
1058
+ implemented: "implemented";
1039
1059
  }>;
1040
1060
  export declare const reportAgentActionInput: z.ZodObject<{
1041
1061
  workflowId: z.ZodString;
@@ -1051,7 +1071,18 @@ export declare const reportAgentActionInput: z.ZodObject<{
1051
1071
  }>>;
1052
1072
  userId: z.ZodOptional<z.ZodString>;
1053
1073
  branchName: z.ZodOptional<z.ZodString>;
1054
- entityType: z.ZodOptional<z.ZodString>;
1074
+ entityType: z.ZodEnum<{
1075
+ SMART_TEST: "SMART_TEST";
1076
+ SCENARIO: "SCENARIO";
1077
+ ISSUE: "ISSUE";
1078
+ TEST_EXECUTION: "TEST_EXECUTION";
1079
+ USER_STORY: "USER_STORY";
1080
+ POLICY: "POLICY";
1081
+ TEST_INVOCATION_BATCH: "TEST_INVOCATION_BATCH";
1082
+ EXPLORATION: "EXPLORATION";
1083
+ EVENT: "EVENT";
1084
+ WORKFLOW: "WORKFLOW";
1085
+ }>;
1055
1086
  entityIdentity: z.ZodOptional<z.ZodString>;
1056
1087
  test: z.ZodOptional<z.ZodObject<{
1057
1088
  folderPath: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1067,6 +1098,7 @@ export declare const reportAgentActionInput: z.ZodObject<{
1067
1098
  ANALYZED: "ANALYZED";
1068
1099
  ACTION_COMPLETED: "ACTION_COMPLETED";
1069
1100
  ACTION_FAILED: "ACTION_FAILED";
1101
+ IMPLEMENTED: "IMPLEMENTED";
1070
1102
  created: "created";
1071
1103
  updated: "updated";
1072
1104
  deleted: "deleted";
@@ -1074,8 +1106,8 @@ export declare const reportAgentActionInput: z.ZodObject<{
1074
1106
  completed: "completed";
1075
1107
  action_completed: "action_completed";
1076
1108
  action_failed: "action_failed";
1109
+ implemented: "implemented";
1077
1110
  }>;
1078
- detailJson: z.ZodOptional<z.ZodString>;
1079
1111
  }, z.core.$strip>;
1080
1112
  export declare const getLastRunWorkflowDetailInput: z.ZodObject<{
1081
1113
  workflowId: z.ZodString;
@@ -69,6 +69,13 @@ export const markPlanItemsImplementationDoneInput = z.object({
69
69
  scenarioOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
70
70
  userStoryOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
71
71
  });
72
+ export const updatePlanItemsLifecycleStatusInput = z.object({
73
+ /** story | scenario (also accepts user_story / USER_STORY / SCENARIO) */
74
+ entityType: z.string().min(1),
75
+ ordinalId: z.coerce.number().int().positive(),
76
+ /** draft | ready | in progress | blocked | done | archived */
77
+ status: z.string().min(1),
78
+ });
72
79
  export const getUserStoriesInput = z
73
80
  .object({
74
81
  userStoryOrdinalIds: z.array(z.coerce.number().int().positive()).min(1),
@@ -501,6 +508,19 @@ export const reportRequirementQualityFindingsInput = z
501
508
  }
502
509
  });
503
510
  export const agentActorTypeSchema = z.enum(["LOCAL_AGENT", "CLOUD_AGENT", "local-agent", "cloud-agent"]);
511
+ /** Closed vocabulary for report-agent-action entity_type (agent_workflow.proto AgentActionEntityType). */
512
+ export const agentActionEntityTypeSchema = z.enum([
513
+ "USER_STORY",
514
+ "SCENARIO",
515
+ "SMART_TEST",
516
+ "POLICY",
517
+ "ISSUE",
518
+ "TEST_EXECUTION",
519
+ "TEST_INVOCATION_BATCH",
520
+ "EXPLORATION",
521
+ "EVENT",
522
+ "WORKFLOW",
523
+ ]);
504
524
  export const agentActionTypeSchema = z.enum([
505
525
  "CREATED",
506
526
  "UPDATED",
@@ -508,6 +528,7 @@ export const agentActionTypeSchema = z.enum([
508
528
  "ANALYZED",
509
529
  "ACTION_COMPLETED",
510
530
  "ACTION_FAILED",
531
+ "IMPLEMENTED",
511
532
  "created",
512
533
  "updated",
513
534
  "deleted",
@@ -516,6 +537,7 @@ export const agentActionTypeSchema = z.enum([
516
537
  "failed",
517
538
  "action_completed",
518
539
  "action_failed",
540
+ "implemented",
519
541
  ]);
520
542
  export const reportAgentActionInput = z
521
543
  .object({
@@ -527,20 +549,83 @@ export const reportAgentActionInput = z
527
549
  actorType: agentActorTypeSchema.optional(),
528
550
  userId: z.string().optional(),
529
551
  branchName: z.string().optional(),
530
- entityType: z.string().optional(),
552
+ entityType: agentActionEntityTypeSchema,
531
553
  /** Project-scoped ordinal id (or explicitly provided execution/batch id). Mutually exclusive with `test`. */
532
554
  entityIdentity: z.string().optional(),
533
555
  /** SmartTest TestLocator. Mutually exclusive with `entityIdentity`. */
534
556
  test: testLocatorSchema.optional(),
535
557
  actionType: agentActionTypeSchema,
536
- detailJson: z.string().optional(),
537
558
  })
538
559
  .superRefine((val, ctx) => {
539
- if (val.test && val.entityIdentity) {
560
+ const actionNorm = val.actionType.toString().toUpperCase().replace(/-/g, "_");
561
+ const isCompletion = actionNorm === "ACTION_COMPLETED" ||
562
+ actionNorm === "ACTION_FAILED" ||
563
+ actionNorm === "COMPLETED" ||
564
+ actionNorm === "FAILED";
565
+ if (isCompletion) {
566
+ if (val.entityType !== "WORKFLOW") {
567
+ ctx.addIssue({
568
+ code: z.ZodIssueCode.custom,
569
+ message: "ACTION_COMPLETED / ACTION_FAILED require entityType WORKFLOW",
570
+ path: ["entityType"],
571
+ });
572
+ }
573
+ const identity = (val.entityIdentity ?? "").trim();
574
+ if (identity === "" || identity !== val.workflowId.trim()) {
575
+ ctx.addIssue({
576
+ code: z.ZodIssueCode.custom,
577
+ message: "entityIdentity must equal workflowId for WORKFLOW completion",
578
+ path: ["entityIdentity"],
579
+ });
580
+ }
581
+ if (val.test) {
582
+ ctx.addIssue({
583
+ code: z.ZodIssueCode.custom,
584
+ message: "test must not be set for WORKFLOW completion",
585
+ path: ["test"],
586
+ });
587
+ }
588
+ return;
589
+ }
590
+ if (val.entityType === "WORKFLOW") {
591
+ ctx.addIssue({
592
+ code: z.ZodIssueCode.custom,
593
+ message: "WORKFLOW entityType is only valid with ACTION_COMPLETED / ACTION_FAILED",
594
+ path: ["entityType"],
595
+ });
596
+ return;
597
+ }
598
+ if (actionNorm === "IMPLEMENTED" &&
599
+ val.entityType !== "USER_STORY" &&
600
+ val.entityType !== "SCENARIO") {
601
+ ctx.addIssue({
602
+ code: z.ZodIssueCode.custom,
603
+ message: "IMPLEMENTED is only valid for USER_STORY or SCENARIO",
604
+ path: ["entityType"],
605
+ });
606
+ }
607
+ if (val.entityType === "SMART_TEST") {
608
+ if (!val.test) {
609
+ ctx.addIssue({
610
+ code: z.ZodIssueCode.custom,
611
+ message: "SMART_TEST requires test (TestLocator)",
612
+ path: ["test"],
613
+ });
614
+ }
615
+ if (val.entityIdentity != null && val.entityIdentity.trim() !== "") {
616
+ ctx.addIssue({
617
+ code: z.ZodIssueCode.custom,
618
+ message: "SMART_TEST forbids entityIdentity; use test (TestLocator)",
619
+ path: ["entityIdentity"],
620
+ });
621
+ }
622
+ return;
623
+ }
624
+ if (!(val.entityIdentity ?? "").trim()) {
540
625
  ctx.addIssue({
541
626
  code: z.ZodIssueCode.custom,
542
- message: "Provide either test (TestLocator) or entityIdentity (ordinal), not both",
543
- path: ["test"],
627
+ message: `entityIdentity is required for ${val.entityType}`,
628
+ path: ["entityIdentity"],
544
629
  });
545
630
  }
546
631
  });
@@ -1,6 +1,7 @@
1
1
  import { normalizeScope } from "./normalize.js";
2
2
  import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralWait.js";
3
3
  import * as S from "./schemas.js";
4
+ import { resolveGitHeadSha } from "./gitSha.js";
4
5
  function platformToProtoEnum(platform) {
5
6
  switch (platform) {
6
7
  case "ios":
@@ -344,6 +345,21 @@ export const TOOL_DEFINITIONS = [
344
345
  return postMcp("/api/mcp/mark_plan_items_implementation_done", body);
345
346
  },
346
347
  },
348
+ {
349
+ kebab: "update-plan-items-lifecycle-status",
350
+ description: "Update lifecycle_fields.status for one user story or test scenario (DB only; does not rewrite plan markdown). " +
351
+ "entityType: story | scenario; ordinalId: numeric US-/TS- ordinal; status: draft | ready | in progress | blocked | done | archived. " +
352
+ "Used after /testchimp implement to set status to ready (unless policy overrides).",
353
+ inputSchema: S.updatePlanItemsLifecycleStatusInput,
354
+ execute: async (args, { postMcp }) => {
355
+ const a = args;
356
+ return postMcp("/api/mcp/update_plan_items_lifecycle_status", {
357
+ entityType: a.entityType,
358
+ ordinalId: a.ordinalId,
359
+ status: a.status,
360
+ });
361
+ },
362
+ },
347
363
  {
348
364
  kebab: "get-eaas-config",
349
365
  description: "Return the project's BunnyShell (Environment-as-a-Service) settings. Secrets are never returned.",
@@ -779,10 +795,15 @@ export const TOOL_DEFINITIONS = [
779
795
  {
780
796
  kebab: "report-agent-action",
781
797
  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.",
798
+ "First call for an id creates the workflow_executions row; later calls append Activity " +
799
+ "timeline rows (AGENT_WORKFLOW_ACTIVITY). " +
800
+ "Actions land on the entity's Activity timeline (plans, issues, SmartTest file). " +
801
+ "entityType: USER_STORY | SCENARIO | SMART_TEST | POLICY | ISSUE | TEST_EXECUTION | " +
802
+ "TEST_INVOCATION_BATCH | EXPLORATION | EVENT | WORKFLOW. " +
803
+ "actionType: CREATED | UPDATED | DELETED | ANALYZED | IMPLEMENTED | ACTION_COMPLETED | ACTION_FAILED. " +
804
+ "Identity: SMART_TEST uses `test` (TestLocator: folderPath/fileName/testSuite/testName); " +
805
+ "other artifact types use `entityIdentity` (ordinal / filename / opaque id). Do not use platform UUIDs. " +
806
+ "Completion (ACTION_COMPLETED / ACTION_FAILED): entityType WORKFLOW and entityIdentity = catalog workflow_id.",
786
807
  inputSchema: S.reportAgentActionInput,
787
808
  execute: async (args, { postMcp }) => {
788
809
  const a = args;
@@ -801,29 +822,27 @@ export const TOOL_DEFINITIONS = [
801
822
  workflowExecutionId: a.workflowExecutionId,
802
823
  actionType,
803
824
  actorType,
825
+ entityType: a.entityType,
804
826
  };
805
827
  if (a.policyFile)
806
828
  body.policyFile = a.policyFile;
807
829
  if (a.policyVersion)
808
830
  body.policyVersion = a.policyVersion;
809
- if (a.gitSha)
810
- body.gitSha = a.gitSha;
831
+ const gitSha = resolveGitHeadSha(a.gitSha);
832
+ if (gitSha)
833
+ body.gitSha = gitSha;
811
834
  if (a.userId)
812
835
  body.userId = a.userId;
813
836
  else if (process.env.TESTCHIMP_USER_ID)
814
837
  body.userId = process.env.TESTCHIMP_USER_ID;
815
838
  if (a.branchName)
816
839
  body.branchName = a.branchName;
817
- if (a.entityType)
818
- body.entityType = a.entityType;
819
840
  if (a.test) {
820
841
  body.test = a.test;
821
842
  }
822
843
  else if (a.entityIdentity) {
823
844
  body.entityIdentity = a.entityIdentity;
824
845
  }
825
- if (a.detailJson)
826
- body.detailJson = a.detailJson;
827
846
  return postMcp("/api/mcp/report_agent_action", body);
828
847
  },
829
848
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
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",