@withgauge/cli 0.12.0 → 0.12.1

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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/dist/index.js +268 -112
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -215,6 +215,24 @@ Edit a skill with `--edit-skill <versionId> --skill-dir <path>`: the directory
215
215
  holds the complete bundle, `SKILL.md` included. `trials template --kind <kind>`
216
216
  prints a skeleton for the `--file` form.
217
217
 
218
+ A chat-guided round, such as a CLI change, goes through preview and save. Save
219
+ replaces the pending drafts, so it can fix a draft that is wrong:
220
+
221
+ ```sh
222
+ gauge optimizations authoring cl_1 > context.json # revision, CLI targets, invocations
223
+ gauge optimizations invocation cl_1 inv_1 # one recorded baseline invocation
224
+ gauge optimizations cli-change cl_1 --file change.json > bundle.json
225
+ # round.json: {"revision": "...", "trials": [{"trial": {"title": "...",
226
+ # "lever": "CLI", "intervention": {"kind": "CLI_INTERVENTION", "after": <bundle.json>}}}]}
227
+ gauge optimizations round preview cl_1 --file round.json # prints previewDigest
228
+ gauge optimizations round save cl_1 --file round.json --digest <previewDigest>
229
+ gauge optimizations trials remove cl_1 R1.2 --yes # drop one draft
230
+ gauge optimizations launch cl_1 --yes
231
+ ```
232
+
233
+ A `completeOutput` literal matcher is an exact, case-sensitive match. Copy it
234
+ from the recorded command output.
235
+
218
236
  Every detail carries `nextAction` — `START`, `WAIT`, `AUTHOR_TRIAL`,
219
237
  `LAUNCH_ROUND`, `ADOPT`, `RESUME`, or `NONE`. Read it (`get -o json`) instead of
220
238
  inferring whose turn it is from round and trial statuses. `watch` exits `4` when
package/dist/index.js CHANGED
@@ -2568,9 +2568,6 @@ var meResponseSchema = z27.object({
2568
2568
  // ../packages/api-schemas/src/optimizations.ts
2569
2569
  import { z as z31 } from "zod";
2570
2570
 
2571
- // ../packages/api-schemas/src/climb.ts
2572
- import { z as z30 } from "zod";
2573
-
2574
2571
  // ../packages/api-schemas/src/cli-interventions.ts
2575
2572
  import { z as z28 } from "zod";
2576
2573
  var digest = z28.string().regex(/^[a-f0-9]{64}$/);
@@ -2598,12 +2595,47 @@ var cliLaunchFormSchema = z28.enum([
2598
2595
  "execveat-path",
2599
2596
  "execveat-fd"
2600
2597
  ]);
2598
+ var cliObservationReasonSchema = z28.enum([
2599
+ "eligible",
2600
+ "other-target",
2601
+ "other-command",
2602
+ "selector-mismatch",
2603
+ "unknown-command",
2604
+ "identity-unresolved",
2605
+ "artifact-mismatch",
2606
+ "runtime-mismatch",
2607
+ "launch-form",
2608
+ "io-profile",
2609
+ "operation-profile",
2610
+ "descriptor-lifetime"
2611
+ ]);
2601
2612
  var cliIoProfileSchema = z28.enum([
2602
2613
  "pipes",
2603
2614
  "redirected",
2604
2615
  "shared-descriptor",
2605
2616
  "pty"
2606
2617
  ]);
2618
+ var cliObservationSchema = z28.object({
2619
+ invocationId: z28.string().regex(/^[a-f0-9]{32}$/),
2620
+ targetId: token,
2621
+ commandId: token.nullable(),
2622
+ entrypointSha256: digest,
2623
+ runtimeSha256: digest.nullable(),
2624
+ launchForm: cliLaunchFormSchema.nullable(),
2625
+ ioProfile: cliIoProfileSchema.nullable(),
2626
+ reason: cliObservationReasonSchema
2627
+ });
2628
+ var cliCoverageSchema = z28.object({
2629
+ attempt: z28.number().int().nonnegative(),
2630
+ observedInvocationCount: z28.number().int().nonnegative().nullable(),
2631
+ invocationCount: z28.number().int().nonnegative(),
2632
+ appliedInvocationCount: z28.number().int().nonnegative(),
2633
+ incompleteCount: z28.number().int().nonnegative(),
2634
+ unsupportedCount: z28.number().int().nonnegative(),
2635
+ instrumentationFailed: z28.boolean(),
2636
+ observations: z28.array(cliObservationSchema),
2637
+ unsupportedDiagnostics: z28.array(z28.string())
2638
+ });
2607
2639
  var cliOperationKindSchema = z28.enum([
2608
2640
  "ensureOption",
2609
2641
  "replaceOption",
@@ -2866,12 +2898,14 @@ var baselineInvocationSchema = z28.object({
2866
2898
  artifactSha256: digest.nullable(),
2867
2899
  entrypointSha256: digest,
2868
2900
  engineSha256: digest.optional(),
2901
+ runtimeSha256: digest.optional(),
2869
2902
  launchForm: cliLaunchFormSchema,
2870
2903
  ioProfile: cliIoProfileSchema,
2871
2904
  recordFiles: z28.array(cliBaselineRecordFileSchema).max(50).default([])
2872
2905
  }).strict();
2873
2906
  var cliInterventionSchema = z28.object({
2874
2907
  schemaVersion: z28.union([z28.literal(1), z28.literal(2)]),
2908
+ identityPolicy: z28.literal("artifact").optional(),
2875
2909
  engineVersion: token,
2876
2910
  engineSha256: digest.nullable(),
2877
2911
  bundleSha256: digest,
@@ -2904,6 +2938,13 @@ var cliInterventionSchema = z28.object({
2904
2938
  const fail = (path2, message) => ctx.addIssue({ code: "custom", path: path2, message });
2905
2939
  if (bundle.schemaVersion !== bundle.catalog.version)
2906
2940
  fail(["schemaVersion"], "Bundle and catalog schema versions differ");
2941
+ if (bundle.identityPolicy && bundle.schemaVersion !== 2)
2942
+ fail(["identityPolicy"], "Artifact identity requires schema version 2");
2943
+ if (bundle.identityPolicy && bundle.baselineInvocations.some((row2) => !row2.runtimeSha256))
2944
+ fail(
2945
+ ["baselineInvocations"],
2946
+ "Artifact identity requires the recorded runtime bytes"
2947
+ );
2907
2948
  if (!bundle.engineSha256 && (bundle.schemaVersion === 1 || bundle.mode !== "record"))
2908
2949
  fail(["engineSha256"], "A trial must pin the recorded engine bytes");
2909
2950
  if (!bundle.catalog.targets.length)
@@ -3095,6 +3136,9 @@ var cliTrialChangeSchema = z28.object({
3095
3136
  shippingIntent: cliInterventionSchema.shape.shippingIntent.unwrap()
3096
3137
  }).strict();
3097
3138
 
3139
+ // ../packages/api-schemas/src/climb.ts
3140
+ import { z as z30 } from "zod";
3141
+
3098
3142
  // ../packages/api-schemas/src/climb-kinds.ts
3099
3143
  var LEVER_KINDS = [
3100
3144
  "DOCS",
@@ -3536,6 +3580,7 @@ var diagnosisSchema = z31.object({
3536
3580
  var trialRunSchema = z31.object({
3537
3581
  id: z31.string(),
3538
3582
  status: z31.string(),
3583
+ cliCoverage: cliCoverageSchema.nullable().optional(),
3539
3584
  passed: z31.boolean().nullable(),
3540
3585
  criteria: z31.array(
3541
3586
  z31.object({ name: z31.string(), passed: z31.boolean(), reasoning: z31.string() })
@@ -4013,11 +4058,18 @@ var runPlanSchema = z39.object({
4013
4058
  resources: z39.array(runPlanResourceSchema).default([]),
4014
4059
  /** Server-populated context retained across deferred preparation. */
4015
4060
  launch: z39.object({
4016
- origin: z39.object({
4017
- initiator: z39.literal("API"),
4018
- userId: z39.string().min(1),
4019
- email: z39.string().nullable()
4020
- }).strict(),
4061
+ origin: z39.discriminatedUnion("initiator", [
4062
+ z39.object({
4063
+ initiator: z39.literal("API"),
4064
+ userId: z39.string().min(1),
4065
+ email: z39.string().nullable()
4066
+ }).strict(),
4067
+ z39.object({
4068
+ initiator: z39.literal("GITHUB"),
4069
+ userId: z39.null(),
4070
+ email: z39.null()
4071
+ }).strict()
4072
+ ]),
4021
4073
  funding: z39.enum(["ORG", "PLATFORM"]),
4022
4074
  queueClass: z39.literal("INTERACTIVE")
4023
4075
  }).strict().optional(),
@@ -4028,7 +4080,7 @@ var runPlanSchema = z39.object({
4028
4080
  digest: z39.string().min(1),
4029
4081
  /** File submissions are asserted by the caller until server-side
4030
4082
  * committed-byte fetching is available. */
4031
- verification: z39.literal("SUBMITTED_UNVERIFIED").optional()
4083
+ verification: z39.enum(["SUBMITTED_UNVERIFIED", "GITHUB_VERIFIED"]).optional()
4032
4084
  }).optional(),
4033
4085
  liveDependencies: z39.array(
4034
4086
  z39.object({
@@ -4158,7 +4210,7 @@ var runDefinitionSubmissionSchema = z40.object({
4158
4210
  repoUrl: uncredentialedRepoUrlSchema,
4159
4211
  commit: z40.string().regex(/^[a-f0-9]{40}$/i),
4160
4212
  digest: z40.string().regex(/^sha256:[a-f0-9]{64}$/),
4161
- verification: z40.literal("SUBMITTED_UNVERIFIED")
4213
+ verification: z40.enum(["SUBMITTED_UNVERIFIED", "GITHUB_VERIFIED"])
4162
4214
  }).strict(),
4163
4215
  resources: z40.array(preparedDirectoryResourceSchema).max(1).default([]),
4164
4216
  cases: z40.array(runDefinitionCaseSchema).min(1)
@@ -4200,6 +4252,13 @@ var runDefinitionSubmissionSchema = z40.object({
4200
4252
  });
4201
4253
  }
4202
4254
  });
4255
+ var runDefinitionPublicSubmissionSchema = runDefinitionSubmissionSchema.refine(
4256
+ (definition) => definition.source.verification === "SUBMITTED_UNVERIFIED",
4257
+ {
4258
+ path: ["source", "verification"],
4259
+ message: "Only the GitHub App may verify committed bytes"
4260
+ }
4261
+ );
4203
4262
  var runDefinitionPlanResponseSchema = z40.object({
4204
4263
  source: runDefinitionSubmissionSchema.shape.source,
4205
4264
  measurementMode: z40.literal("AGENT_EXPERIENCE"),
@@ -6347,9 +6406,112 @@ function cadenceLabel(cadence) {
6347
6406
 
6348
6407
  // src/lib/runDefinitionFiles.ts
6349
6408
  import { execFileSync } from "child_process";
6350
- import { createHash as createHash2 } from "crypto";
6351
6409
  import { isAbsolute, relative, resolve, sep } from "path";
6410
+
6411
+ // ../packages/api-schemas/src/compileRunDefinition.ts
6412
+ import { createHash as createHash2 } from "crypto";
6352
6413
  import { parseDocument } from "yaml";
6414
+ function utf8(bytes, path2) {
6415
+ try {
6416
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
6417
+ } catch {
6418
+ throw new Error(`${path2} is not UTF-8 text`);
6419
+ }
6420
+ }
6421
+ function markdownCase(path2, bytes) {
6422
+ const source = utf8(bytes, path2);
6423
+ const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(source);
6424
+ if (!match)
6425
+ throw new Error(`${path2}: expected YAML frontmatter between --- lines`);
6426
+ const document = parseDocument(match[1], { uniqueKeys: true });
6427
+ if (document.errors.length)
6428
+ throw new Error(
6429
+ `${path2}: ${document.errors.map((error) => error.message).join("; ")}`
6430
+ );
6431
+ const parsed = runDefinitionFrontmatterSchema.safeParse(document.toJS());
6432
+ if (!parsed.success) throw new Error(`${path2}: ${parsed.error.message}`);
6433
+ const prompt = match[2].trim();
6434
+ if (!prompt) throw new Error(`${path2}: Markdown body is empty`);
6435
+ return { path: path2, prompt, ...parsed.data };
6436
+ }
6437
+ function compileRunDefinitionFromBlobs(input) {
6438
+ if (!uncredentialedRepoUrlSchema.safeParse(input.repoUrl).success)
6439
+ throw new Error("Git origin must not contain embedded credentials");
6440
+ if (!/^[a-f0-9]{40}$/i.test(input.commit))
6441
+ throw new Error("Expected a full Git commit SHA");
6442
+ if (input.casePaths.length === 0)
6443
+ throw new Error("At least one Markdown case is required");
6444
+ if (new Set(input.casePaths).size !== input.casePaths.length)
6445
+ throw new Error("Case paths must be unique");
6446
+ const configBytes = input.blobs.get("gauge.json");
6447
+ let config;
6448
+ if (configBytes) {
6449
+ try {
6450
+ config = runDefinitionFileConfigSchema.parse(
6451
+ JSON.parse(utf8(configBytes, "gauge.json"))
6452
+ );
6453
+ } catch (error) {
6454
+ throw new Error(
6455
+ `gauge.json: ${error instanceof Error ? error.message : error}`
6456
+ );
6457
+ }
6458
+ }
6459
+ const conventionalPrepare = input.blobs.has(".gauge/prepare.sh");
6460
+ if (config?.product && conventionalPrepare)
6461
+ throw new Error(
6462
+ "gauge.json: product and .gauge/prepare.sh are alternatives"
6463
+ );
6464
+ const prepare = config?.prepare ?? (conventionalPrepare ? "bash .gauge/prepare.sh" : void 0);
6465
+ const preparation = config?.product ?? (prepare ? { run: prepare } : void 0);
6466
+ if (config?.install && !prepare)
6467
+ throw new Error("gauge.json: install requires prepare");
6468
+ if (preparation && !input.repoUrl.startsWith("https://"))
6469
+ throw new Error("Project preparation requires a public HTTPS Git origin");
6470
+ const cases = input.casePaths.map((path2) => {
6471
+ const bytes = input.blobs.get(path2);
6472
+ if (!path2.endsWith(".md") || !bytes)
6473
+ throw new Error(`Missing committed Markdown case: ${path2}`);
6474
+ return markdownCase(path2, bytes);
6475
+ });
6476
+ const hash = createHash2("sha256");
6477
+ hash.update("gauge-run-definition-v1\0");
6478
+ for (const [path2, bytes] of [...input.blobs.entries()].sort(
6479
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
6480
+ )) {
6481
+ hash.update(path2);
6482
+ hash.update("\0");
6483
+ hash.update(String(bytes.length));
6484
+ hash.update("\0");
6485
+ hash.update(bytes);
6486
+ hash.update("\0");
6487
+ }
6488
+ const submission = runDefinitionSubmissionSchema.parse({
6489
+ version: 1,
6490
+ source: {
6491
+ repoUrl: input.repoUrl,
6492
+ commit: input.commit,
6493
+ digest: `sha256:${hash.digest("hex")}`,
6494
+ verification: input.verification
6495
+ },
6496
+ resources: preparation ? [
6497
+ {
6498
+ name: "project",
6499
+ type: "prepared-directory",
6500
+ source: {
6501
+ type: "git-checkout",
6502
+ repoUrl: input.repoUrl,
6503
+ commit: input.commit
6504
+ },
6505
+ prepare: preparation,
6506
+ ...config?.install ? { install: config.install } : {}
6507
+ }
6508
+ ] : [],
6509
+ cases
6510
+ });
6511
+ return { definition: submission, org: config?.org, github: config?.github };
6512
+ }
6513
+
6514
+ // src/lib/runDefinitionFiles.ts
6353
6515
  function git(cwd, args) {
6354
6516
  try {
6355
6517
  return execFileSync("git", args, {
@@ -6366,39 +6528,6 @@ function git(cwd, args) {
6366
6528
  function committedBlob(root, commit, path2) {
6367
6529
  return git(root, ["show", `${commit}:${path2}`]);
6368
6530
  }
6369
- function decode(blob, path2) {
6370
- try {
6371
- return new TextDecoder("utf-8", { fatal: true }).decode(blob);
6372
- } catch {
6373
- throw new CliError("usage", `${path2} is not UTF-8 text`);
6374
- }
6375
- }
6376
- function parseYaml(text, path2) {
6377
- const document = parseDocument(text, { uniqueKeys: true });
6378
- if (document.errors.length) {
6379
- throw new CliError(
6380
- "usage",
6381
- `${path2}: ${document.errors.map((error) => error.message).join("; ")}`
6382
- );
6383
- }
6384
- return document.toJS();
6385
- }
6386
- function caseFromMarkdown(path2, source) {
6387
- const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(source);
6388
- if (!match)
6389
- throw new CliError(
6390
- "usage",
6391
- `${path2}: expected YAML frontmatter between --- lines`
6392
- );
6393
- const parsed = runDefinitionFrontmatterSchema.safeParse(
6394
- parseYaml(match[1], path2)
6395
- );
6396
- if (!parsed.success)
6397
- throw new CliError("usage", `${path2}: ${parsed.error.message}`);
6398
- const prompt = match[2].trim();
6399
- if (!prompt) throw new CliError("usage", `${path2}: Markdown body is empty`);
6400
- return { path: path2, prompt, ...parsed.data };
6401
- }
6402
6531
  function relativeGitPath(root, cwd, input) {
6403
6532
  if (!input || input.includes("\0") || /[*?[\]{}]/.test(input))
6404
6533
  throw new CliError("usage", `expected an explicit case path: ${input}`);
@@ -6436,83 +6565,34 @@ function loadCommittedRunDefinitionWithContext(files, cwd = process.cwd()) {
6436
6565
  const paths = files.map((file) => relativeGitPath(root, cwd, file));
6437
6566
  if (new Set(paths).size !== paths.length)
6438
6567
  throw new CliError("usage", "case paths must be unique");
6439
- const blobs = [];
6440
- let config;
6568
+ const blobs = /* @__PURE__ */ new Map();
6441
6569
  const hasConfig = git(root, ["ls-tree", "--name-only", commit, "--", "gauge.json"]).toString("utf8").trim() === "gauge.json";
6442
6570
  if (hasConfig) {
6443
- try {
6444
- const bytes = committedBlob(root, commit, "gauge.json");
6445
- blobs.push({ path: "gauge.json", bytes });
6446
- config = runDefinitionFileConfigSchema.parse(
6447
- JSON.parse(decode(bytes, "gauge.json"))
6448
- );
6449
- } catch (error) {
6450
- throw new CliError(
6451
- "usage",
6452
- `gauge.json: ${error instanceof Error ? error.message : error}`
6453
- );
6454
- }
6571
+ blobs.set("gauge.json", committedBlob(root, commit, "gauge.json"));
6455
6572
  }
6456
6573
  const hasConventionalPrepare = git(root, ["ls-tree", "--name-only", commit, "--", ".gauge/prepare.sh"]).toString("utf8").trim() === ".gauge/prepare.sh";
6457
6574
  if (hasConventionalPrepare) {
6458
- blobs.push({
6459
- path: ".gauge/prepare.sh",
6460
- bytes: committedBlob(root, commit, ".gauge/prepare.sh")
6461
- });
6462
- }
6463
- if (config?.product && hasConventionalPrepare)
6464
- throw new CliError(
6465
- "usage",
6466
- "gauge.json: product and .gauge/prepare.sh are alternatives"
6575
+ blobs.set(
6576
+ ".gauge/prepare.sh",
6577
+ committedBlob(root, commit, ".gauge/prepare.sh")
6467
6578
  );
6468
- const prepare = config?.prepare ?? (hasConventionalPrepare ? "bash .gauge/prepare.sh" : void 0);
6469
- const preparation = config?.product ?? (prepare ? { run: prepare } : void 0);
6470
- if (config?.install && !prepare)
6471
- throw new CliError("usage", "gauge.json: install requires prepare");
6472
- if (preparation && !repoUrl.startsWith("https://"))
6473
- throw new CliError(
6474
- "usage",
6475
- "Project preparation requires a public HTTPS Git origin"
6476
- );
6477
- const cases = paths.map((path2) => {
6478
- const bytes = committedBlob(root, commit, path2);
6479
- blobs.push({ path: path2, bytes });
6480
- return caseFromMarkdown(path2, decode(bytes, path2));
6481
- });
6482
- const hash = createHash2("sha256");
6483
- hash.update("gauge-run-definition-v1\0");
6484
- for (const entry of blobs.sort(
6485
- (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
6486
- )) {
6487
- hash.update(entry.path);
6488
- hash.update("\0");
6489
- hash.update(String(entry.bytes.length));
6490
- hash.update("\0");
6491
- hash.update(entry.bytes);
6492
- hash.update("\0");
6493
6579
  }
6494
- const submission = runDefinitionSubmissionSchema.safeParse({
6495
- version: 1,
6496
- source: {
6580
+ for (const path2 of paths) blobs.set(path2, committedBlob(root, commit, path2));
6581
+ try {
6582
+ const result = compileRunDefinitionFromBlobs({
6497
6583
  repoUrl,
6498
6584
  commit,
6499
- digest: `sha256:${hash.digest("hex")}`,
6585
+ casePaths: paths,
6586
+ blobs,
6500
6587
  verification: "SUBMITTED_UNVERIFIED"
6501
- },
6502
- resources: preparation ? [
6503
- {
6504
- name: "project",
6505
- type: "prepared-directory",
6506
- source: { type: "git-checkout", repoUrl, commit },
6507
- prepare: preparation,
6508
- ...config?.install ? { install: config.install } : {}
6509
- }
6510
- ] : [],
6511
- cases
6512
- });
6513
- if (!submission.success)
6514
- throw new CliError("usage", submission.error.message);
6515
- return { definition: submission.data, org: config?.org };
6588
+ });
6589
+ return { definition: result.definition, org: result.org };
6590
+ } catch (error) {
6591
+ throw new CliError(
6592
+ "usage",
6593
+ error instanceof Error ? error.message : String(error)
6594
+ );
6595
+ }
6516
6596
  }
6517
6597
 
6518
6598
  // src/lib/staff.ts
@@ -8269,6 +8349,18 @@ function printChange(content) {
8269
8349
  console.log(doc.after || "(empty)");
8270
8350
  }
8271
8351
  }
8352
+ function printRoundPreview(preview) {
8353
+ for (const trial of preview.trials) {
8354
+ console.log(trial.title);
8355
+ for (const change of trial.changes ?? []) {
8356
+ console.log(`=== ${change.path}`);
8357
+ console.log(change.hunks);
8358
+ }
8359
+ for (const path2 of trial.droppedFiles ?? [])
8360
+ console.log(` ${path2}: dropped`);
8361
+ }
8362
+ console.log(`previewDigest ${preview.previewDigest}`);
8363
+ }
8272
8364
  function printEstimate(e) {
8273
8365
  console.log(
8274
8366
  `plan: ${e.sampleCount} session${e.sampleCount === 1 ? "" : "s"} per trial \xD7 up to ${e.maxRounds} rounds`
@@ -8644,7 +8736,7 @@ function register19(program2) {
8644
8736
  let last = "";
8645
8737
  for (; ; ) {
8646
8738
  const detail = await client.get(itemPath2(org, id));
8647
- const trials2 = detail.rounds.flatMap((round) => round.trials);
8739
+ const trials2 = detail.rounds.flatMap((round2) => round2.trials);
8648
8740
  const line = `${detail.status}${detail.stopReason ? ` (${detail.stopReason})` : ""} \xB7 round ${detail.round}/${detail.maxRounds} \xB7 ${pct(detail.before)} \u2192 ${pct(detail.after)} \xB7 ${detail.creditsSpent} credits spent \xB7 ${trials2.filter((t) => t.status === "RUNNING").map((t) => t.key).join(",") || "idle"}`;
8649
8741
  if (line !== last) {
8650
8742
  last = line;
@@ -8661,6 +8753,60 @@ function register19(program2) {
8661
8753
  }
8662
8754
  }
8663
8755
  );
8756
+ group.command("authoring <id>").description(
8757
+ "What the next round may change: kinds, CLI targets and commands, baseline invocations, revision"
8758
+ ).action(async (id, _opts, cmd) => {
8759
+ printJson(
8760
+ await createClient().get(`${itemPath2(resolveOrg(cmd), id)}/authoring`)
8761
+ );
8762
+ });
8763
+ group.command("invocation <id> <invocationId>").description(
8764
+ "One baseline CLI invocation from `authoring`, with its recorded command evidence"
8765
+ ).action(
8766
+ async (id, invocationId, _opts, cmd) => {
8767
+ printJson(
8768
+ await createClient().get(
8769
+ `${itemPath2(resolveOrg(cmd), id)}/authoring/target`,
8770
+ { cliInvocationId: invocationId }
8771
+ )
8772
+ );
8773
+ }
8774
+ );
8775
+ group.command("cli-change <id>").description(
8776
+ "Build a CLI_INTERVENTION bundle from {targetId, commandId, selector, operations, shippingIntent}; use the output as intervention.after in a round file. Saves nothing"
8777
+ ).requiredOption("--file <path>", "the change JSON; '-' for stdin").action(async (id, opts, cmd) => {
8778
+ printJson(
8779
+ await createClient().post(
8780
+ `${itemPath2(resolveOrg(cmd), id)}/cli-changes`,
8781
+ readObject(opts.file)
8782
+ )
8783
+ );
8784
+ });
8785
+ const round = group.command("round").description(
8786
+ "Preview and save the round waiting to launch. A round file is {revision, trials: [{trial: {title, hypothesis, lever, intervention}, files?}]}; revision comes from `authoring`"
8787
+ );
8788
+ round.command("preview <id>").description("Validate a round file and print its diff and previewDigest").requiredOption("--file <path>", "the round JSON; '-' for stdin").action(async (id, opts, cmd) => {
8789
+ const preview = await createClient().post(
8790
+ `${itemPath2(resolveOrg(cmd), id)}/rounds/current/preview`,
8791
+ readObject(opts.file)
8792
+ );
8793
+ if (resolveOutput(cmd) === "json") printJson(preview);
8794
+ else printRoundPreview(preview);
8795
+ });
8796
+ round.command("save <id>").description(
8797
+ "Save a previewed round as drafts, replacing every pending draft. Never launches"
8798
+ ).requiredOption("--file <path>", "the round JSON you previewed").requiredOption("--digest <digest>", "previewDigest from `round preview`").action(
8799
+ async (id, opts, cmd) => {
8800
+ const result = await createClient().put(
8801
+ `${itemPath2(resolveOrg(cmd), id)}/rounds/current`,
8802
+ { ...readObject(opts.file), previewDigest: opts.digest }
8803
+ );
8804
+ if (resolveOutput(cmd) === "json") printJson(result);
8805
+ else
8806
+ for (const trial of result.trials)
8807
+ console.log(`Saved ${trial.key} "${trial.title}"`);
8808
+ }
8809
+ );
8664
8810
  const trials = group.command("trials").description("List, author, and run trials");
8665
8811
  trials.command("list <id>").description("Every trial of an optimization with score and verdict").action(async (id, _opts, cmd) => {
8666
8812
  const org = resolveOrg(cmd);
@@ -8732,6 +8878,16 @@ function register19(program2) {
8732
8878
  );
8733
8879
  }
8734
8880
  );
8881
+ trials.command("remove <id> <trialId>").description("Drop one draft trial from the round waiting to launch").option("--yes", "skip the confirmation").action(
8882
+ async (id, trialId, opts, cmd) => {
8883
+ await confirmOrAbort(`Remove draft ${trialId} from ${id}?`, opts.yes);
8884
+ const detail = await createClient().delete(
8885
+ trialPath(resolveOrg(cmd), id, trialId)
8886
+ );
8887
+ if (resolveOutput(cmd) === "json") printJson(detail);
8888
+ else console.log(`Removed ${trialId}.`);
8889
+ }
8890
+ );
8735
8891
  group.command("trial <id> <trialId>").description("One trial by id or key (B0, R1.1, \u2026) with its sessions").action(
8736
8892
  async (id, trialId, _opts, cmd) => {
8737
8893
  const org = resolveOrg(cmd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withgauge/cli",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Gauge customer CLI — Agent Preference, evals, runs, and organization settings over the Gauge API",
5
5
  "private": false,
6
6
  "author": "Gauge <support@withgauge.com> (https://withgauge.com)",