@eigenpal/cli 0.4.16 → 0.5.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.
package/dist/cli.js CHANGED
@@ -1019,7 +1019,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1019
1019
  this._exitCallback = (err) => {
1020
1020
  if (err.code !== "commander.executeSubCommandAsync") {
1021
1021
  throw err;
1022
- } else {}
1022
+ }
1023
1023
  };
1024
1024
  }
1025
1025
  return this;
@@ -20343,7 +20343,7 @@ function finalize(ctx, schema) {
20343
20343
  result.$schema = "http://json-schema.org/draft-07/schema#";
20344
20344
  } else if (ctx.target === "draft-04") {
20345
20345
  result.$schema = "http://json-schema.org/draft-04/schema#";
20346
- } else if (ctx.target === "openapi-3.0") {} else {}
20346
+ } else if (ctx.target === "openapi-3.0") {}
20347
20347
  if (ctx.external?.uri) {
20348
20348
  const id = ctx.external.registry.get(schema)?.id;
20349
20349
  if (!id)
@@ -20604,7 +20604,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
20604
20604
  if (val === undefined) {
20605
20605
  if (ctx.unrepresentable === "throw") {
20606
20606
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
20607
- } else {}
20607
+ }
20608
20608
  } else if (typeof val === "bigint") {
20609
20609
  if (ctx.unrepresentable === "throw") {
20610
20610
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -27388,7 +27388,7 @@ var require_tracestate_impl = __commonJS((exports) => {
27388
27388
  const value = listMember.slice(i + 1, part.length);
27389
27389
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
27390
27390
  agg.set(key, value);
27391
- } else {}
27391
+ }
27392
27392
  }
27393
27393
  return agg;
27394
27394
  }, new Map);
@@ -181229,7 +181229,7 @@ import { fileURLToPath as fileURLToPath6 } from "url";
181229
181229
  // package.json
181230
181230
  var package_default = {
181231
181231
  name: "@eigenpal/cli",
181232
- version: "0.4.16",
181232
+ version: "0.5.1",
181233
181233
  description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
181234
181234
  keywords: [
181235
181235
  "eigenpal",
@@ -181304,7 +181304,9 @@ var package_default = {
181304
181304
 
181305
181305
  // src/commands/agent.ts
181306
181306
  var import_yaml = __toESM(require_dist(), 1);
181307
- import { existsSync as existsSync2, promises as fs } from "node:fs";
181307
+ import { spawnSync } from "node:child_process";
181308
+ import { createHash } from "node:crypto";
181309
+ import { existsSync as existsSync2, promises as fs, readFileSync as readFileSync2 } from "node:fs";
181308
181310
  import path from "node:path";
181309
181311
  import { createInterface } from "node:readline/promises";
181310
181312
 
@@ -181387,6 +181389,15 @@ class ApiClient {
181387
181389
  });
181388
181390
  return this.handleResponse(res, url);
181389
181391
  }
181392
+ async put(path, body) {
181393
+ const url = `${this.baseUrl}${path}`;
181394
+ const res = await fetch(url, {
181395
+ method: "PUT",
181396
+ headers: this.headers({ "Content-Type": "application/json" }),
181397
+ body: body !== undefined ? JSON.stringify(body) : undefined
181398
+ });
181399
+ return this.handleResponse(res, url);
181400
+ }
181390
181401
  async patch(path, body) {
181391
181402
  const url = `${this.baseUrl}${path}`;
181392
181403
  const res = await fetch(url, {
@@ -181948,6 +181959,7 @@ Examples:
181948
181959
  Version flags are intentionally unsupported for agents in this release.
181949
181960
  `).action(action(pushAgent));
181950
181961
  withBaseUrl(agent.command("pull <agent-id-or-slug>")).description("Download an agent project as agent.yaml, agent/, and dataset/.").option("--out <dir>", "Output directory").action(action(pullAgent));
181962
+ registerAgentFileCommands(agent);
181951
181963
  addJsonFlag(agent.command("validate [dir]")).description("Validate a local agent project layout.").action(action(validateAgentCommand));
181952
181964
  registerDatasetCommands(agent);
181953
181965
  registerExecutionCommands(agent);
@@ -181957,6 +181969,16 @@ Version flags are intentionally unsupported for agents in this release.
181957
181969
  registerComingSoon(agent, "evaluators", "Agent evaluators are reserved for future workflow-style evaluators.");
181958
181970
  registerComingSoon(agent, "versions", "Agent versions are coming soon.");
181959
181971
  }
181972
+ function registerAgentFileCommands(agent) {
181973
+ const file2 = agent.command("file").description("List, download, and upload individual live agent files.").action(() => {
181974
+ process.stderr.write("`eigenpal agent file` requires a subcommand. Run `eigenpal agent file --help`.\n");
181975
+ process.exit(2);
181976
+ });
181977
+ addJsonFlag(withBaseUrl(file2.command("list <agent-id-or-slug>"))).description("List live files for an agent.").option("--path <prefix>", "Only list files beneath this relative path").action(action(listAgentFiles));
181978
+ addJsonFlag(withBaseUrl(file2.command("get <agent-id-or-slug> <remote-path>"))).description("Download one live agent file.").option("--out <file>", "Output file path").action(action(getAgentFile));
181979
+ addJsonFlag(withBaseUrl(file2.command("put <agent-id-or-slug> <remote-path> <local-path>"))).description("Upload one local file into the live agent namespace.").option("--dry-run", "Validate local file and remote path without uploading").option("--preview", "Compare remote and local file without uploading").action(action(putAgentFile));
181980
+ addJsonFlag(withBaseUrl(file2.command("diff <agent-id-or-slug> <remote-path> <local-path>"))).description("Compare one live agent file against a local file.").action(action(diffAgentFile));
181981
+ }
181960
181982
  function registerDatasetCommands(agent) {
181961
181983
  const dataset = agent.command("dataset").description("Manage an agent dataset.").action(() => {
181962
181984
  process.stderr.write("`eigenpal agent dataset` requires a subcommand. Run `eigenpal agent dataset --help`.\n");
@@ -181973,8 +181995,24 @@ function registerExecutionCommands(agent) {
181973
181995
  process.exit(2);
181974
181996
  });
181975
181997
  addJsonFlag(withBaseUrl(execution.command("run <agent-id-or-slug>"))).description("Start one agent execution.").option("--input-json <json>", "JSON input object").option("--input-file <path>", "Input file to upload as multipart form-data").option("--wait", "Poll until the execution reaches a terminal status").action(action(runExecution));
181976
- addJsonFlag(withBaseUrl(execution.command("get <execution-id>"))).description("Get one agent execution.").option("--include <parts>", "Comma-separated extra parts, e.g. files").action(action(getExecution));
181977
- addJsonFlag(withPagination(withBaseUrl(execution.command("list <agent-id-or-slug>")), 50)).description("List agent executions.").option("--status <status>", "Filter by status").action(action(listExecutions));
181998
+ addJsonFlag(withBaseUrl(execution.command("get <execution-id>"))).description("Get one agent execution.").option("--include <parts>", "Comma-separated extra parts: feedback,expected,files,trace,issues", "feedback").action(action(getExecution));
181999
+ addJsonFlag(withBaseUrl(execution.command("rerun <execution-id>"))).description("Create a new execution from a previous execution's stored input snapshot.").option("--wait", "Poll until the rerun reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(rerunExecution));
182000
+ const list = addJsonFlag(withPagination(withBaseUrl(execution.command("list <agent-id-or-slug>")), 50)).description("List agent executions.").option("--status <status>", "Filter by status").option("--batch-id <id>", "Filter by experiment batch id").option("--example-name <name>", "Filter by exact dataset example name").option("--example-name-contains <q>", "Filter by substring in example name").option("--created-after <time>", "Filter by created-at lower bound").option("--created-before <time>", "Filter by created-at upper bound").option("--completed-after <time>", "Filter by completed-at lower bound").option("--completed-before <time>", "Filter by completed-at upper bound").option("--feedback-status <open|resolved|ignored>", "Filter by feedback status").option("--feedback-rating <pass|fail|partial|none>", "Filter by feedback rating").option("--feedback-body-contains <q>", "Filter by feedback body substring").option("--feedback-created-after <time>", "Filter by feedback created-at lower bound").option("--feedback-created-before <time>", "Filter by feedback created-at upper bound").option("--feedback-updated-after <time>", "Filter by feedback updated-at lower bound").option("--feedback-updated-before <time>", "Filter by feedback updated-at upper bound").option("--feedback-resolved-after <time>", "Filter by feedback resolved-at lower bound").option("--feedback-resolved-before <time>", "Filter by feedback resolved-at upper bound").option("--has-feedback", "Only executions with feedback").option("--no-feedback", "Only executions without feedback").option("--has-expected", "Only executions with expected JSON or files").option("--has-expected-json", "Only executions with expected.json").option("--has-expected-files", "Only executions with expected files").option("--promoted-to-example", "Only executions promoted to dataset examples").option("--promoted-example-name <name>", "Filter by promoted dataset example name").option("--since-last-resolved", "Only executions created after the latest resolved feedback").option("--include <parts>", "Comma-separated extra parts: feedback,expected,files").option("--compact", "Return/print compact triage rows without full output payloads").option("--sort <field>", "Sort by createdAt, completedAt, status, or exampleName").option("--order <asc|desc>", "Sort direction").option("--scan-limit <n>", "Feedback scan window for feedback filters", intArg).action(action(listExecutions));
182001
+ list.addHelpText("after", `
182002
+ Examples:
182003
+ $ eigenpal agent execution list incorporator --feedback-rating fail --since-last-resolved --include feedback,expected
182004
+ $ eigenpal agent execution list activities --has-expected-files --created-after 2026-05-01
182005
+ `);
182006
+ withBaseUrl(execution.command("pull <execution-id>")).description("Download execution feedback, expected artifacts, files, and metadata.").option("--out <dir>", "Output directory").option("--include <parts>", "Comma-separated parts: feedback,expected,files,output,input,metadata,issues,trace,all", "feedback,expected").option("--json", "Output a JSON summary of written artifacts").action(action(pullExecution));
182007
+ addJsonFlag(withBaseUrl(execution.command("compare <reference-execution-id> <execution-id>"))).description("Compare one execution against another execution. PDF/DOCX text comparison uses pdftotext/python3 and reports byte fallbacks.").option("--baseline", "Compare actual outputs from both executions instead of expected artifacts").option("--out <dir>", "Write comparison artifacts to this directory").option("--normalize-dates", "Normalize YYYYMMDD and YYYY-MM-DD tokens in filenames/text").option("--fail-on-diff", "Exit 1 when comparison status is fail").action(action(compareExecution));
182008
+ const artifacts = execution.command("artifacts").description("Inspect execution artifact inventory.").action(() => {
182009
+ process.stderr.write("`eigenpal agent execution artifacts` requires a subcommand. Run `eigenpal agent execution artifacts --help`.\n");
182010
+ process.exit(2);
182011
+ });
182012
+ addJsonFlag(withBaseUrl(artifacts.command("list <execution-id>"))).description("List available execution artifacts without downloading them.").action(action(listExecutionArtifacts));
182013
+ withBaseUrl(execution.command("trace <execution-id>")).description("Print raw trace.jsonl for an execution, or write it with --out.").option("--out <file>", "Output file path").action(action(traceExecution));
182014
+ registerExecutionFeedbackCommands(execution);
182015
+ registerExecutionExpectedCommands(execution);
181978
182016
  addJsonFlag(withBaseUrl(execution.command("watch <execution-id>"))).description("Watch an execution until it reaches a terminal status.").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(watchExecutionCommand));
181979
182017
  addJsonFlag(withBaseUrl(execution.command("cancel <execution-id>"))).description("Cancel an agent execution.").option("--yes", "Required in non-interactive environments").action(action(cancelExecution));
181980
182018
  }
@@ -181990,6 +182028,27 @@ function registerExperimentCommands(agent) {
181990
182028
  addJsonFlag(withBaseUrl(experiment.command("compare <batch-id-a> <batch-id-b>"))).description("Compare two experiment batches.").option("--sort <mode>", "Accepted for compatibility; sorting happens client-side later").option("--regression-threshold <n>", "Accepted for compatibility", intArg).action(action(compareExperiments));
181991
182029
  addJsonFlag(withBaseUrl(experiment.command("cancel <agent-id-or-slug> <batch-id>"))).description("Cancel every active execution in an experiment.").option("--yes", "Required in non-interactive environments").action(action(cancelExperiment));
181992
182030
  }
182031
+ function registerExecutionFeedbackCommands(execution) {
182032
+ const feedback = execution.command("feedback").description("Update or clear feedback attached to an agent execution.").action(() => {
182033
+ process.stderr.write("`eigenpal agent execution feedback` requires a subcommand. Run `eigenpal agent execution feedback --help`.\n");
182034
+ process.exit(2);
182035
+ });
182036
+ addJsonFlag(withBaseUrl(feedback.command("update <execution-id>"))).description("Edit feedback state, rating, message, or expected JSON for an execution.").option("--status <open|resolved|ignored>", "Set feedback status").option("--rating <pass|fail|partial|none>", "Set feedback rating").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").option("--expected-json <json>", "Set structured expected JSON").option("--expected-json-file <path>", "Read structured expected JSON from a file").option("--clear-message", "Clear the feedback message body").option("--clear-rating", "Clear feedback rating").option("--clear-expected-json", "Delete structured expected JSON").action(action(updateExecutionFeedback));
182037
+ addJsonFlag(withBaseUrl(feedback.command("resolve <execution-id>"))).description("Mark execution feedback as resolved.").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").action(action((executionId, opts) => updateExecutionFeedback(executionId, { ...opts, status: "resolved" })));
182038
+ addJsonFlag(withBaseUrl(feedback.command("clear <execution-id>"))).description("Delete feedback, expected.json, and expected files for an execution.").option("--yes", "Required in non-interactive environments").action(action(clearExecutionFeedback));
182039
+ }
182040
+ function registerExecutionExpectedCommands(execution) {
182041
+ const expected = execution.command("expected").description("Manage expected artifacts attached to an agent execution.").action(() => {
182042
+ process.stderr.write("`eigenpal agent execution expected` requires a subcommand. Run `eigenpal agent execution expected --help`.\n");
182043
+ process.exit(2);
182044
+ });
182045
+ addJsonFlag(withBaseUrl(expected.command("list <execution-id>"))).description("List expected JSON and files attached to an execution.").action(action(listExecutionExpected));
182046
+ withBaseUrl(expected.command("pull <execution-id>")).description("Download expected JSON and files attached to an execution.").option("--out <dir>", "Output directory").action(action(pullExecutionExpected));
182047
+ addJsonFlag(withBaseUrl(expected.command("upload <execution-id> <file>"))).description("Upload a local file as an expected artifact.").option("--name <name>", "Expected artifact name").action(action(uploadExecutionExpected));
182048
+ addJsonFlag(withBaseUrl(expected.command("copy-output <execution-id> <output-file>"))).description("Copy a generated output file into expected artifacts.").option("--name <name>", "Expected artifact name").action(action(copyOutputToExpected));
182049
+ addJsonFlag(withBaseUrl(expected.command("rename <execution-id> <old-name> <new-name>"))).description("Rename an expected artifact.").action(action(renameExecutionExpected));
182050
+ addJsonFlag(withBaseUrl(expected.command("delete <execution-id> <name>"))).description("Delete an expected artifact.").option("--yes", "Required in non-interactive environments").action(action(deleteExecutionExpected));
182051
+ }
181993
182052
  function registerSessionCommands(agent) {
181994
182053
  const session = agent.command("session").description("Manage agent builder sessions.").action(() => {
181995
182054
  process.stderr.write("`eigenpal agent session` requires a subcommand. Run `eigenpal agent session --help`.\n");
@@ -182108,6 +182167,55 @@ async function pullAgent(agentId, opts) {
182108
182167
  await pullDataset(agentId, { ...opts, out: path.join(out, DATASET_DIR) });
182109
182168
  success2(`Pulled agent to ${ui.bold(out)}`);
182110
182169
  }
182170
+ async function listAgentFiles(agentId, opts) {
182171
+ const client = buildClient(opts);
182172
+ const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
182173
+ ...opts.path ? { prefix: opts.path } : {}
182174
+ });
182175
+ if (opts.json)
182176
+ return printJson(payload);
182177
+ console.log(table((payload.files ?? []).map((name) => ({ name })), [{ key: "name", header: "PATH" }]));
182178
+ }
182179
+ async function getAgentFile(agentId, remotePath, opts) {
182180
+ const client = buildClient(opts);
182181
+ const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
182182
+ path: remotePath
182183
+ });
182184
+ if (opts.json)
182185
+ return printJson(payload);
182186
+ const out = path.resolve(opts.out ?? payload.path);
182187
+ await writeBase64File(out, payload.contentBase64);
182188
+ success2(`Downloaded ${payload.path} to ${ui.bold(out)}`);
182189
+ }
182190
+ async function putAgentFile(agentId, remotePath, localPath, opts) {
182191
+ const absolute = path.resolve(localPath);
182192
+ const data = await fs.readFile(absolute);
182193
+ if (opts.dryRun) {
182194
+ const payload2 = { ok: true, dryRun: true, path: remotePath, bytes: data.length };
182195
+ if (opts.json)
182196
+ return printJson(payload2);
182197
+ success2(`Validated ${remotePath} from ${ui.bold(absolute)} (${data.length} bytes)`);
182198
+ return;
182199
+ }
182200
+ if (opts.preview) {
182201
+ const report = await buildAgentFileDiff(agentId, remotePath, absolute, opts);
182202
+ if (opts.json)
182203
+ return printJson(report);
182204
+ renderAgentFileDiff(report);
182205
+ return;
182206
+ }
182207
+ const client = buildClient(opts);
182208
+ const payload = await client.put(`/api/v1/agents/${encodeURIComponent(agentId)}/files?path=${encodeURIComponent(remotePath)}`, { contentBase64: data.toString("base64") });
182209
+ if (opts.json)
182210
+ return printJson(payload);
182211
+ success2(`Uploaded ${remotePath} from ${ui.bold(absolute)}`);
182212
+ }
182213
+ async function diffAgentFile(agentId, remotePath, localPath, opts) {
182214
+ const report = await buildAgentFileDiff(agentId, remotePath, path.resolve(localPath), opts);
182215
+ if (opts.json)
182216
+ return printJson(report);
182217
+ renderAgentFileDiff(report);
182218
+ }
182111
182219
  async function validateAgentCommand(dir = ".", opts) {
182112
182220
  const result = await validateAgentProject(path.resolve(dir));
182113
182221
  if (opts.json)
@@ -182184,18 +182292,302 @@ async function getExecution(executionId, opts) {
182184
182292
  });
182185
182293
  renderExecutionPayload(payload, opts);
182186
182294
  }
182187
- async function listExecutions(agentId, opts) {
182295
+ async function rerunExecution(executionId, opts) {
182188
182296
  const client = buildClient(opts);
182189
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/executions`, compactParams(opts));
182297
+ let payload = await client.post(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/rerun`, {});
182298
+ const rerunId = String(payload.executionId ?? "");
182299
+ if (opts.wait && rerunId) {
182300
+ payload = await pollExecution(client, rerunId, opts.interval, opts.maxWait);
182301
+ return renderExecutionPayload(payload, opts);
182302
+ }
182190
182303
  if (opts.json)
182191
182304
  return printJson(payload);
182192
- console.log(table(payload.executions, [
182305
+ success2(`Started rerun ${ui.bold(rerunId)} from ${executionId}`);
182306
+ }
182307
+ async function pullExecution(executionId, opts) {
182308
+ const client = buildClient(opts);
182309
+ const include = new Set(opts.include.split(",").map((part) => part.trim()).filter(Boolean));
182310
+ if (include.has("all")) {
182311
+ include.add("feedback");
182312
+ include.add("expected");
182313
+ include.add("files");
182314
+ include.add("input");
182315
+ include.add("output");
182316
+ include.add("metadata");
182317
+ include.add("issues");
182318
+ include.add("trace");
182319
+ }
182320
+ const payload = await client.get(`/api/v1/agents/executions/${encodeURIComponent(executionId)}`, { include: [...include].join(",") });
182321
+ const execution = payload.execution;
182322
+ if (!execution)
182323
+ throw new Error(`Execution ${executionId} not found`);
182324
+ const out = path.resolve(opts.out ?? path.join(".eigenpal", "artifacts", "executions", executionId));
182325
+ await fs.mkdir(out, { recursive: true });
182326
+ const written = [];
182327
+ const skipped = [];
182328
+ const writeJsonArtifact = async (name, value) => {
182329
+ await fs.writeFile(path.join(out, name), JSON.stringify(value, null, 2));
182330
+ written.push(name);
182331
+ };
182332
+ await writeJsonArtifact("execution.json", execution);
182333
+ const feedback = execution.feedback;
182334
+ if (include.has("feedback") && feedback) {
182335
+ await fs.writeFile(path.join(out, "feedback.md"), serializeCliFeedback(feedback));
182336
+ written.push("feedback.md");
182337
+ } else if (include.has("feedback")) {
182338
+ skipped.push("feedback");
182339
+ }
182340
+ if (include.has("expected")) {
182341
+ written.push(...await writeExpectedArtifacts(client, executionId, out, execution));
182342
+ }
182343
+ if (include.has("files") || include.has("output")) {
182344
+ written.push(...await writeListedFiles(client, executionId, out, "output", execution.resultFiles));
182345
+ }
182346
+ if (include.has("files") || include.has("input")) {
182347
+ written.push(...await writeListedFiles(client, executionId, out, "input", execution.inputFiles));
182348
+ if (execution.inputJson) {
182349
+ await writeJsonArtifact("input.json", execution.inputJson);
182350
+ } else {
182351
+ skipped.push("input.json");
182352
+ }
182353
+ }
182354
+ if (include.has("files") || include.has("metadata")) {
182355
+ if (execution.metadata) {
182356
+ await writeJsonArtifact("metadata.json", execution.metadata);
182357
+ } else {
182358
+ skipped.push("metadata.json");
182359
+ }
182360
+ }
182361
+ if (include.has("files") || include.has("issues")) {
182362
+ const file2 = await writeDiagnosticFile(client, executionId, out, "issues", "issues.md", execution.issueFiles);
182363
+ if (file2)
182364
+ written.push(file2);
182365
+ else
182366
+ skipped.push("issues.md");
182367
+ }
182368
+ if (include.has("files") || include.has("trace")) {
182369
+ const file2 = await writeDiagnosticFile(client, executionId, out, "trace", "trace.jsonl", execution.traceFiles);
182370
+ if (file2)
182371
+ written.push(file2);
182372
+ else
182373
+ skipped.push("trace.jsonl");
182374
+ }
182375
+ const summary = {
182376
+ executionId,
182377
+ out,
182378
+ written,
182379
+ skipped,
182380
+ counts: {
182381
+ written: written.length,
182382
+ skipped: skipped.length
182383
+ }
182384
+ };
182385
+ if (opts.json)
182386
+ return printJson(summary);
182387
+ success2(`Pulled execution ${executionId} to ${out}`);
182388
+ dim(`Wrote ${written.length} artifact${written.length === 1 ? "" : "s"}${skipped.length ? `; skipped missing ${skipped.join(", ")}` : ""}`);
182389
+ }
182390
+ async function listExecutionArtifacts(executionId, opts) {
182391
+ const client = buildClient(opts);
182392
+ const payload = await client.get(`/api/v1/agents/executions/${encodeURIComponent(executionId)}`, { include: "expected,files,input,output,issues,trace,metadata" });
182393
+ const execution = payload.execution;
182394
+ if (!execution)
182395
+ throw new Error(`Execution ${executionId} not found`);
182396
+ const artifacts = executionArtifactInventory(execution);
182397
+ if (opts.json)
182398
+ return printJson({ executionId, artifacts });
182399
+ console.log(table(artifacts, [
182400
+ { key: "kind", header: "KIND" },
182401
+ { key: "name", header: "NAME" },
182402
+ { key: "present", header: "PRESENT" }
182403
+ ]));
182404
+ }
182405
+ async function traceExecution(executionId, opts) {
182406
+ const client = buildClient(opts);
182407
+ const text = await downloadTraceText(client, executionId);
182408
+ if (!opts.out) {
182409
+ process.stdout.write(text);
182410
+ return;
182411
+ }
182412
+ const out = path.resolve(opts.out);
182413
+ await fs.mkdir(path.dirname(out), { recursive: true });
182414
+ await fs.writeFile(out, text);
182415
+ success2(`Downloaded trace for ${executionId} to ${ui.bold(out)}`);
182416
+ }
182417
+ async function compareExecution(referenceId, executionId, opts) {
182418
+ const client = buildClient(opts);
182419
+ const targetPayload = await client.get(`/api/v1/agents/executions/${encodeURIComponent(executionId)}`, { include: "files,output" });
182420
+ const target = targetPayload.execution;
182421
+ if (!target)
182422
+ throw new Error(`Execution ${executionId} not found`);
182423
+ const mode = opts.baseline ? "baseline" : "expected";
182424
+ const reference = (await client.get(`/api/v1/agents/executions/${encodeURIComponent(referenceId)}`, {
182425
+ include: mode === "baseline" ? "files,output" : "expected"
182426
+ })).execution;
182427
+ if (!reference)
182428
+ throw new Error(`Reference execution ${referenceId} not found`);
182429
+ const out = path.resolve(opts.out ?? path.join(".eigenpal", "artifacts", "comparisons", `${referenceId}..${executionId}`));
182430
+ await fs.mkdir(out, { recursive: true });
182431
+ const warnings = [];
182432
+ if (mode === "expected" && reference.expected == null && fileNames(reference.expectedFiles).length === 0) {
182433
+ warnings.push(`Reference execution ${referenceId} has no expected JSON or expected files; comparison has no baseline artifacts. Use --baseline to compare actual outputs.`);
182434
+ }
182435
+ const jsonDiffs = diffJson(mode === "baseline" ? reference.output : reference.expected, target.output);
182436
+ const expectedFiles = mode === "baseline" ? fileNames(reference.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl") : fileNames(reference.expectedFiles);
182437
+ const outputFiles = fileNames(target.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl");
182438
+ const inventory = compareFileInventory(expectedFiles, outputFiles, Boolean(opts.normalizeDates));
182439
+ const textDifferences = await compareMatchedFileText(client, { executionId: referenceId, kind: mode === "baseline" ? "output" : "expected" }, { executionId, kind: "output" }, out, inventory.matched, Boolean(opts.normalizeDates));
182440
+ const report = {
182441
+ status: jsonDiffs.length === 0 && inventory.missing.length === 0 && inventory.extra.length === 0 && textDifferences.every((diff) => diff.status === "match" || diff.status === "binary-match") ? "pass" : "fail",
182442
+ executionId,
182443
+ comparedWith: referenceId,
182444
+ mode,
182445
+ warnings,
182446
+ jsonDifferences: jsonDiffs,
182447
+ matchedFiles: inventory.matched,
182448
+ missingFiles: inventory.missing,
182449
+ extraFiles: inventory.extra,
182450
+ textDifferences
182451
+ };
182452
+ await fs.writeFile(path.join(out, "comparison.json"), JSON.stringify(report, null, 2));
182453
+ if (opts.json) {
182454
+ printJson(report);
182455
+ } else {
182456
+ renderComparisonReport(report);
182457
+ }
182458
+ dim(`Comparison artifacts written to ${ui.bold(out)}`);
182459
+ if (opts.failOnDiff && report.status === "fail")
182460
+ process.exit(1);
182461
+ }
182462
+ async function listExecutions(agentId, opts) {
182463
+ const client = buildClient(opts);
182464
+ const params = buildExecutionListParams(opts);
182465
+ const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/executions`, params);
182466
+ const rows = opts.compact ? payload.executions.map(compactExecutionRow) : payload.executions;
182467
+ if (opts.json)
182468
+ return printJson({ ...payload, executions: rows });
182469
+ if (payload.scanLimited) {
182470
+ warn("Feedback/expected filters scanned only the first matching window. Increase --scan-limit or narrow DB filters for a fuller result.");
182471
+ }
182472
+ if (payload.noResolvedAnchor) {
182473
+ warn("No resolved feedback anchor was found in the scan window; --since-last-resolved returned no rows.");
182474
+ }
182475
+ console.log(table(rows, [
182193
182476
  { key: "id", header: "ID" },
182194
182477
  { key: "status", header: "STATUS" },
182195
182478
  { key: "exampleId", header: "EXAMPLE" },
182479
+ {
182480
+ key: "feedback",
182481
+ header: "FEEDBACK",
182482
+ format: (value) => value && typeof value === "object" ? `${String(value.rating ?? "")}/${String(value.status ?? "")}` : ""
182483
+ },
182196
182484
  { key: "createdAt", header: "CREATED", format: formatTimestamp }
182197
182485
  ]));
182198
182486
  }
182487
+ function buildExecutionListParams(opts) {
182488
+ return compactParams({
182489
+ ...opts,
182490
+ feedback: undefined,
182491
+ noFeedback: opts.noFeedback ?? (opts.feedback === false ? true : undefined)
182492
+ });
182493
+ }
182494
+ async function updateExecutionFeedback(executionId, opts) {
182495
+ const client = buildClient(opts);
182496
+ const body = {};
182497
+ if (opts.status)
182498
+ body.status = opts.status;
182499
+ if (opts.rating)
182500
+ body.rating = opts.rating === "none" ? null : opts.rating;
182501
+ if (opts.clearRating)
182502
+ body.rating = null;
182503
+ if (opts.messageFile)
182504
+ body.body = await fs.readFile(opts.messageFile, "utf-8");
182505
+ if (opts.message !== undefined)
182506
+ body.body = opts.message;
182507
+ if (opts.clearMessage)
182508
+ body.body = "";
182509
+ if (opts.expectedJsonFile)
182510
+ body.expected = JSON.parse(await fs.readFile(opts.expectedJsonFile, "utf-8"));
182511
+ if (opts.expectedJson !== undefined)
182512
+ body.expected = JSON.parse(opts.expectedJson);
182513
+ if (opts.clearExpectedJson)
182514
+ body.expected = null;
182515
+ const payload = await client.patch(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/feedback`, body);
182516
+ renderGeneric(payload, opts, `Updated feedback for ${executionId}`);
182517
+ }
182518
+ function compactExecutionRow(execution) {
182519
+ const feedback = execution.feedback && typeof execution.feedback === "object" ? execution.feedback : null;
182520
+ return {
182521
+ id: execution.id,
182522
+ status: execution.status,
182523
+ exampleId: execution.exampleId,
182524
+ feedback: feedback ? {
182525
+ rating: feedback.rating ?? null,
182526
+ status: feedback.status ?? null,
182527
+ updatedAt: feedback.updatedAt ?? null
182528
+ } : null,
182529
+ hasExpectedJson: execution.expected != null,
182530
+ expectedFileCount: Array.isArray(execution.expectedFiles) ? execution.expectedFiles.length : undefined,
182531
+ createdAt: execution.createdAt,
182532
+ completedAt: execution.completedAt
182533
+ };
182534
+ }
182535
+ async function clearExecutionFeedback(executionId, opts) {
182536
+ if (!(opts.yes || await confirmTyped(executionId, "clear feedback artifacts"))) {
182537
+ throw new Error("Clear cancelled");
182538
+ }
182539
+ const client = buildClient(opts);
182540
+ const payload = await client.delete(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/feedback`);
182541
+ renderGeneric(payload, opts, `Cleared feedback for ${executionId}`);
182542
+ }
182543
+ async function listExecutionExpected(executionId, opts) {
182544
+ const client = buildClient(opts);
182545
+ const payload = await client.get(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected`);
182546
+ if (opts.json)
182547
+ return printJson(payload);
182548
+ console.log(table(payload.files ?? [], [{ key: "name", header: "NAME" }]));
182549
+ if (payload.expected != null)
182550
+ dim("expected.json present");
182551
+ }
182552
+ async function pullExecutionExpected(executionId, opts) {
182553
+ const client = buildClient(opts);
182554
+ const payload = await client.get(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected`);
182555
+ const out = path.resolve(opts.out ?? path.join(executionId, "expected"));
182556
+ await fs.mkdir(out, { recursive: true });
182557
+ if (payload.expected != null) {
182558
+ await fs.writeFile(path.join(out, "expected.json"), JSON.stringify(payload.expected, null, 2));
182559
+ }
182560
+ await downloadExpectedFiles(client, executionId, out, payload.files);
182561
+ success2(`Pulled expected artifacts for ${executionId} to ${out}`);
182562
+ }
182563
+ async function uploadExecutionExpected(executionId, file2, opts) {
182564
+ const client = buildClient(opts);
182565
+ const form = new FormData;
182566
+ const data = await fs.readFile(file2);
182567
+ form.append("file", new Blob([data]), path.basename(file2));
182568
+ if (opts.name)
182569
+ form.append("name", opts.name);
182570
+ const payload = await client.postFormData(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected`, form);
182571
+ renderGeneric(payload, opts, `Uploaded expected file for ${executionId}`);
182572
+ }
182573
+ async function copyOutputToExpected(executionId, outputFile, opts) {
182574
+ const client = buildClient(opts);
182575
+ const payload = await client.post(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected`, { outputFileName: outputFile, ...opts.name ? { expectedName: opts.name } : {} });
182576
+ renderGeneric(payload, opts, `Copied output file to expected for ${executionId}`);
182577
+ }
182578
+ async function renameExecutionExpected(executionId, oldName, newName, opts) {
182579
+ const client = buildClient(opts);
182580
+ const payload = await client.patch(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(oldName)}`, { name: newName });
182581
+ renderGeneric(payload, opts, `Renamed expected file for ${executionId}`);
182582
+ }
182583
+ async function deleteExecutionExpected(executionId, name, opts) {
182584
+ if (!(opts.yes || await confirmTyped(name, "delete expected file"))) {
182585
+ throw new Error("Delete cancelled");
182586
+ }
182587
+ const client = buildClient(opts);
182588
+ await client.delete(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
182589
+ renderGeneric({ ok: true }, opts, `Deleted expected file ${name}`);
182590
+ }
182199
182591
  async function watchExecutionCommand(executionId, opts) {
182200
182592
  const client = buildClient(opts);
182201
182593
  const payload = await pollExecution(client, executionId, opts.interval, opts.maxWait);
@@ -182410,6 +182802,336 @@ async function buildAgentExecutionRunFormData(inputFile, inputJson) {
182410
182802
  form.append("_json", inputJson);
182411
182803
  return form;
182412
182804
  }
182805
+ function serializeCliFeedback(feedback) {
182806
+ const keys = [
182807
+ "rating",
182808
+ "status",
182809
+ "createdAt",
182810
+ "createdBy",
182811
+ "createdByEmail",
182812
+ "updatedAt",
182813
+ "resolvedAt",
182814
+ "resolvedBy",
182815
+ "resolvedByEmail",
182816
+ "resolvedBySessionId",
182817
+ "promotedExampleName"
182818
+ ];
182819
+ const frontmatter = keys.map((key) => `${key}: ${feedback[key] ?? ""}`).join(`
182820
+ `);
182821
+ return `---
182822
+ ${frontmatter}
182823
+ ---
182824
+
182825
+ ${String(feedback.body ?? "").trim()}
182826
+ `;
182827
+ }
182828
+ async function writeExpectedArtifacts(client, executionId, out, execution) {
182829
+ const written = [];
182830
+ const expectedDir = path.join(out, "expected");
182831
+ await fs.mkdir(expectedDir, { recursive: true });
182832
+ if (execution.expected != null) {
182833
+ await fs.writeFile(path.join(out, "expected.json"), JSON.stringify(execution.expected, null, 2));
182834
+ written.push("expected.json");
182835
+ }
182836
+ written.push(...(await downloadExpectedFiles(client, executionId, expectedDir, execution.expectedFiles)).map((name) => `expected/${name}`));
182837
+ return written;
182838
+ }
182839
+ async function downloadExpectedFiles(client, executionId, out, files) {
182840
+ const rows = Array.isArray(files) ? files : [];
182841
+ const written = await Promise.all(rows.map(async (file2) => {
182842
+ const name = String(file2.name ?? "");
182843
+ if (!name || name.includes("/") || name.includes(".."))
182844
+ return null;
182845
+ const response = await client.getStream(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
182846
+ await fs.mkdir(out, { recursive: true });
182847
+ await fs.writeFile(path.join(out, name), Buffer.from(await response.arrayBuffer()));
182848
+ return name;
182849
+ }));
182850
+ return written.filter((name) => Boolean(name));
182851
+ }
182852
+ async function writeListedFiles(client, executionId, out, kind, files) {
182853
+ const rows = Array.isArray(files) ? files : [];
182854
+ const targetDir = path.join(out, kind);
182855
+ await fs.mkdir(targetDir, { recursive: true });
182856
+ const written = await Promise.all(rows.map(async (file2) => {
182857
+ const name = String(file2.name ?? "");
182858
+ if (!name || name.includes("/") || name.includes(".."))
182859
+ return null;
182860
+ const response = await client.getStream(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/files/${kind}/${encodeURIComponent(name)}`);
182861
+ await fs.writeFile(path.join(targetDir, name), Buffer.from(await response.arrayBuffer()));
182862
+ return `${kind}/${name}`;
182863
+ }));
182864
+ return written.filter((name) => Boolean(name));
182865
+ }
182866
+ async function writeDiagnosticFile(client, executionId, out, kind, filename, files) {
182867
+ const rows = Array.isArray(files) ? files : [];
182868
+ const exists = rows.some((file2) => String(file2.name ?? "") === filename);
182869
+ if (!exists)
182870
+ return null;
182871
+ const response = await client.getStream(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/files/${kind}/${filename}`);
182872
+ await fs.writeFile(path.join(out, filename), Buffer.from(await response.arrayBuffer()));
182873
+ return filename;
182874
+ }
182875
+ async function downloadTraceText(client, executionId) {
182876
+ const response = await client.getStream(`/api/v1/agents/executions/${encodeURIComponent(executionId)}/files/trace/trace.jsonl`);
182877
+ return Buffer.from(await response.arrayBuffer()).toString("utf-8");
182878
+ }
182879
+ function diffJson(expected, actual, basePath = "$") {
182880
+ if (expected === undefined || expected === null)
182881
+ return [];
182882
+ if (Object.is(expected, actual))
182883
+ return [];
182884
+ if (expected && actual && typeof expected === "object" && typeof actual === "object" && !Array.isArray(expected) && !Array.isArray(actual)) {
182885
+ const keys = new Set([...Object.keys(expected), ...Object.keys(actual)]);
182886
+ return [...keys].flatMap((key) => {
182887
+ const next = `${basePath}.${key}`;
182888
+ if (!(key in actual))
182889
+ return [{ path: next, type: "missing" }];
182890
+ if (!(key in expected))
182891
+ return [{ path: next, type: "extra" }];
182892
+ return diffJson(expected[key], actual[key], next);
182893
+ });
182894
+ }
182895
+ if (Array.isArray(expected) && Array.isArray(actual)) {
182896
+ const diffs = [];
182897
+ const max = Math.max(expected.length, actual.length);
182898
+ for (let index = 0;index < max; index += 1) {
182899
+ const next = `${basePath}[${index}]`;
182900
+ if (index >= actual.length)
182901
+ diffs.push({ path: next, type: "missing" });
182902
+ else if (index >= expected.length)
182903
+ diffs.push({ path: next, type: "extra" });
182904
+ else
182905
+ diffs.push(...diffJson(expected[index], actual[index], next));
182906
+ }
182907
+ return diffs;
182908
+ }
182909
+ return [
182910
+ {
182911
+ path: basePath,
182912
+ type: "changed",
182913
+ expected: JSON.stringify(expected),
182914
+ actual: JSON.stringify(actual)
182915
+ }
182916
+ ];
182917
+ }
182918
+ function fileNames(files) {
182919
+ return (Array.isArray(files) ? files : []).map((file2) => String(file2.name ?? "")).filter(Boolean).sort();
182920
+ }
182921
+ function executionArtifactInventory(execution) {
182922
+ const rows = [
182923
+ { kind: "metadata", name: "execution.json", present: "yes" }
182924
+ ];
182925
+ if (execution.inputJson != null)
182926
+ rows.push({ kind: "input", name: "input.json", present: "yes" });
182927
+ if (execution.metadata != null)
182928
+ rows.push({ kind: "metadata", name: "metadata.json", present: "yes" });
182929
+ if (execution.expected != null)
182930
+ rows.push({ kind: "expected", name: "expected.json", present: "yes" });
182931
+ for (const name of fileNames(execution.inputFiles))
182932
+ rows.push({ kind: "input", name, present: "yes" });
182933
+ for (const name of fileNames(execution.resultFiles).filter((name2) => name2 !== "issues.md" && name2 !== "trace.jsonl")) {
182934
+ rows.push({ kind: "output", name, present: "yes" });
182935
+ }
182936
+ for (const name of fileNames(execution.expectedFiles)) {
182937
+ rows.push({ kind: "expected", name, present: "yes" });
182938
+ }
182939
+ for (const name of fileNames(execution.issueFiles))
182940
+ rows.push({ kind: "issues", name, present: "yes" });
182941
+ for (const name of fileNames(execution.traceFiles))
182942
+ rows.push({ kind: "trace", name, present: "yes" });
182943
+ return rows.sort((a, b) => `${a.kind}/${a.name}`.localeCompare(`${b.kind}/${b.name}`));
182944
+ }
182945
+ async function buildAgentFileDiff(agentId, remotePath, localPath, opts) {
182946
+ const client = buildClient(opts);
182947
+ const local = await fs.readFile(localPath);
182948
+ let remote = null;
182949
+ let serverPath = remotePath;
182950
+ try {
182951
+ const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
182952
+ path: remotePath
182953
+ });
182954
+ serverPath = String(payload.path ?? remotePath);
182955
+ remote = Buffer.from(String(payload.contentBase64 ?? ""), "base64");
182956
+ } catch (err) {
182957
+ if (!(err instanceof ApiError) || err.status !== 404)
182958
+ throw err;
182959
+ }
182960
+ const localHash = createHash("sha256").update(local).digest("hex");
182961
+ const remoteHash = remote ? createHash("sha256").update(remote).digest("hex") : null;
182962
+ const status = !remote ? "remote-missing" : localHash === remoteHash ? "match" : "different";
182963
+ return {
182964
+ agentId,
182965
+ path: serverPath,
182966
+ localPath,
182967
+ status,
182968
+ localBytes: local.length,
182969
+ remoteBytes: remote?.length ?? null,
182970
+ localSha256: localHash,
182971
+ remoteSha256: remoteHash,
182972
+ textPreview: remote && isLikelyText(local) && isLikelyText(remote) ? buildTextDiffPreview(remote.toString("utf-8"), local.toString("utf-8")) : null
182973
+ };
182974
+ }
182975
+ function renderAgentFileDiff(report) {
182976
+ const label = `File ${report.status}`;
182977
+ if (report.status === "match")
182978
+ success2(label);
182979
+ else
182980
+ error51(label);
182981
+ console.log(table([
182982
+ { item: "Remote path", value: report.path },
182983
+ { item: "Local bytes", value: String(report.localBytes) },
182984
+ { item: "Remote bytes", value: String(report.remoteBytes ?? "missing") },
182985
+ { item: "Local sha256", value: report.localSha256.slice(0, 12) },
182986
+ { item: "Remote sha256", value: report.remoteSha256?.slice(0, 12) ?? "missing" }
182987
+ ], [
182988
+ { key: "item", header: "ITEM" },
182989
+ { key: "value", header: "VALUE" }
182990
+ ]));
182991
+ if (report.textPreview?.length) {
182992
+ console.log(report.textPreview.join(`
182993
+ `));
182994
+ }
182995
+ }
182996
+ function isLikelyText(buffer) {
182997
+ return !buffer.includes(0);
182998
+ }
182999
+ function buildTextDiffPreview(remoteText, localText) {
183000
+ if (remoteText === localText)
183001
+ return [];
183002
+ const remoteLines = remoteText.split(/\r?\n/);
183003
+ const localLines = localText.split(/\r?\n/);
183004
+ const max = Math.max(remoteLines.length, localLines.length);
183005
+ const preview = [];
183006
+ for (let index = 0;index < max && preview.length < 12; index += 1) {
183007
+ if (remoteLines[index] === localLines[index])
183008
+ continue;
183009
+ preview.push(`-${remoteLines[index] ?? ""}`);
183010
+ preview.push(`+${localLines[index] ?? ""}`);
183011
+ }
183012
+ return preview;
183013
+ }
183014
+ function normalizeGeneratedTokens(value) {
183015
+ return value.replace(/\d{4}-\d{2}-\d{2}/g, "<date>").replace(/\d{8}/g, "<date>");
183016
+ }
183017
+ function compareFileInventory(expected, output, normalizeDates) {
183018
+ const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value) : value;
183019
+ const outputByNormalized = new Map(output.map((name) => [normalize(name), name]));
183020
+ const expectedByNormalized = new Map(expected.map((name) => [normalize(name), name]));
183021
+ const matched = expected.map((expectedName) => {
183022
+ const outputName = outputByNormalized.get(normalize(expectedName));
183023
+ return outputName ? { expected: expectedName, output: outputName } : null;
183024
+ }).filter((match) => Boolean(match));
183025
+ return {
183026
+ matched,
183027
+ missing: expected.filter((name) => !outputByNormalized.has(normalize(name))),
183028
+ extra: output.filter((name) => !expectedByNormalized.has(normalize(name)))
183029
+ };
183030
+ }
183031
+ async function compareMatchedFileText(client, left, right, out, matched, normalizeDates) {
183032
+ const expectedDir = path.join(out, "expected");
183033
+ const outputDir = path.join(out, "output");
183034
+ await fs.mkdir(expectedDir, { recursive: true });
183035
+ await fs.mkdir(outputDir, { recursive: true });
183036
+ const diffs = [];
183037
+ for (const pair of matched) {
183038
+ const expectedPath = path.join(expectedDir, pair.expected);
183039
+ const outputPath = path.join(outputDir, pair.output);
183040
+ await downloadStreamToFile(client, executionFileUrl(left, pair.expected), expectedPath);
183041
+ await downloadStreamToFile(client, executionFileUrl(right, pair.output), outputPath);
183042
+ const expectedText = extractComparableText(expectedPath);
183043
+ const outputText = extractComparableText(outputPath);
183044
+ if (expectedText.text == null || outputText.text == null) {
183045
+ const [expectedBuffer, outputBuffer] = await Promise.all([
183046
+ fs.readFile(expectedPath),
183047
+ fs.readFile(outputPath)
183048
+ ]);
183049
+ const expectedHash = createHash("sha256").update(expectedBuffer).digest("hex");
183050
+ const outputHash = createHash("sha256").update(outputBuffer).digest("hex");
183051
+ diffs.push({
183052
+ ...pair,
183053
+ status: expectedBuffer.length === outputBuffer.length && expectedHash === outputHash ? "binary-match" : "binary-different",
183054
+ expectedBytes: expectedBuffer.length,
183055
+ outputBytes: outputBuffer.length,
183056
+ expectedSha256: expectedHash,
183057
+ outputSha256: outputHash,
183058
+ textExtraction: {
183059
+ expected: expectedText.reason ?? null,
183060
+ output: outputText.reason ?? null
183061
+ }
183062
+ });
183063
+ continue;
183064
+ }
183065
+ const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value).trim() : value.trim();
183066
+ diffs.push({
183067
+ ...pair,
183068
+ status: normalize(expectedText.text) === normalize(outputText.text) ? "match" : "text-different"
183069
+ });
183070
+ }
183071
+ return diffs;
183072
+ }
183073
+ function executionFileUrl(side, name) {
183074
+ const executionId = encodeURIComponent(side.executionId);
183075
+ const filename = encodeURIComponent(name);
183076
+ return side.kind === "expected" ? `/api/v1/agents/executions/${executionId}/expected/${filename}` : `/api/v1/agents/executions/${executionId}/files/output/${filename}`;
183077
+ }
183078
+ async function downloadStreamToFile(client, url2, target) {
183079
+ await fs.mkdir(path.dirname(target), { recursive: true });
183080
+ const response = await client.getStream(url2);
183081
+ await fs.writeFile(target, Buffer.from(await response.arrayBuffer()));
183082
+ }
183083
+ function extractComparableText(file2) {
183084
+ const ext = path.extname(file2).toLowerCase();
183085
+ if ([".txt", ".md", ".json", ".csv", ".xml"].includes(ext)) {
183086
+ return { text: readFileSync2(file2, "utf8") };
183087
+ }
183088
+ if (ext === ".pdf") {
183089
+ const result = spawnSync("pdftotext", [file2, "-"], { encoding: "utf8" });
183090
+ return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("pdftotext", result) };
183091
+ }
183092
+ if (ext === ".docx") {
183093
+ const script = "import re,sys,zipfile; z=zipfile.ZipFile(sys.argv[1]); x=z.read('word/document.xml').decode('utf-8','ignore'); print(re.sub('<[^>]+>',' ',x))";
183094
+ const result = spawnSync("python3", ["-c", script, file2], { encoding: "utf8" });
183095
+ return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("python3", result) };
183096
+ }
183097
+ return { text: null, reason: `No text extractor for ${ext || "extensionless"} files` };
183098
+ }
183099
+ function textExtractionFallbackReason(tool, result) {
183100
+ if (result.error)
183101
+ return `${tool} unavailable; compared bytes instead`;
183102
+ return `${tool} could not extract text; compared bytes instead`;
183103
+ }
183104
+ function renderComparisonReport(report) {
183105
+ const header = `Comparison ${report.status}`;
183106
+ if (report.status === "pass") {
183107
+ success2(header);
183108
+ } else {
183109
+ error51(header);
183110
+ }
183111
+ for (const warning of report.warnings ?? []) {
183112
+ dim(`warning: ${warning}`);
183113
+ }
183114
+ console.log(table([
183115
+ { item: "JSON differences", count: report.jsonDifferences.length },
183116
+ { item: "Matched files", count: report.matchedFiles.length },
183117
+ { item: "Missing files", count: report.missingFiles.length },
183118
+ { item: "Extra files", count: report.extraFiles.length },
183119
+ {
183120
+ item: "Text differences",
183121
+ count: report.textDifferences.filter((diff) => diff.status === "text-different").length
183122
+ }
183123
+ ], [
183124
+ { key: "item", header: "ITEM" },
183125
+ { key: "count", header: "COUNT" }
183126
+ ]));
183127
+ for (const diff of report.textDifferences) {
183128
+ const textExtraction = diff.textExtraction;
183129
+ const reasons = [textExtraction?.expected, textExtraction?.output].filter((value) => typeof value === "string" && value.length > 0).filter((value, index, values) => values.indexOf(value) === index);
183130
+ for (const reason of reasons) {
183131
+ dim(`warning: ${String(diff.expected)} vs ${String(diff.output)}: ${reason}`);
183132
+ }
183133
+ }
183134
+ }
182413
183135
  async function readAgentYaml(root) {
182414
183136
  const file2 = path.join(root, AGENT_YAML);
182415
183137
  const raw = await fs.readFile(file2, "utf-8");
@@ -182490,7 +183212,7 @@ function renderGeneric(payload, opts, message) {
182490
183212
  function compactParams(opts) {
182491
183213
  const params = {};
182492
183214
  for (const [key, value] of Object.entries(opts)) {
182493
- if (value === undefined || value === null || key === "json" || key === "baseUrl" || key === "yes")
183215
+ if (value === undefined || value === null || key === "json" || key === "baseUrl" || key === "yes" || key === "compact")
182494
183216
  continue;
182495
183217
  params[key] = String(value);
182496
183218
  }
@@ -183007,7 +183729,7 @@ import {
183007
183729
  existsSync as existsSync3,
183008
183730
  mkdirSync as mkdirSync2,
183009
183731
  readdirSync,
183010
- readFileSync as readFileSync2,
183732
+ readFileSync as readFileSync3,
183011
183733
  statSync,
183012
183734
  writeFileSync as writeFileSync2
183013
183735
  } from "node:fs";
@@ -183105,7 +183827,7 @@ function copyTemplate(src, dst, projectName) {
183105
183827
  }
183106
183828
  mkdirSync2(dirname2(targetPath), { recursive: true });
183107
183829
  if (isTextFile(sourcePath)) {
183108
- const text = readFileSync2(sourcePath, "utf-8").replaceAll(PLACEHOLDER, projectName);
183830
+ const text = readFileSync3(sourcePath, "utf-8").replaceAll(PLACEHOLDER, projectName);
183109
183831
  writeFileSync2(targetPath, text);
183110
183832
  } else {
183111
183833
  copyFileSync(sourcePath, targetPath);
@@ -183137,12 +183859,12 @@ function isTextFile(path2) {
183137
183859
 
183138
183860
  // src/commands/skill.ts
183139
183861
  init_dist4();
183140
- import { createHash } from "node:crypto";
183862
+ import { createHash as createHash2 } from "node:crypto";
183141
183863
  import {
183142
183864
  existsSync as existsSync4,
183143
183865
  lstatSync,
183144
183866
  mkdirSync as mkdirSync3,
183145
- readFileSync as readFileSync3,
183867
+ readFileSync as readFileSync4,
183146
183868
  readdirSync as readdirSync2,
183147
183869
  rmSync,
183148
183870
  rmdirSync,
@@ -183236,7 +183958,7 @@ function resolveSkillTarget(opts) {
183236
183958
  return opts.target ?? defaultClaudeTarget();
183237
183959
  }
183238
183960
  function sha256(buffer) {
183239
- return createHash("sha256").update(buffer).digest("hex");
183961
+ return createHash2("sha256").update(buffer).digest("hex");
183240
183962
  }
183241
183963
  var CLI_VERSION_PLACEHOLDER = "__CLI_VERSION__";
183242
183964
  function templateSourceFile(relativePath, bytes, cliVersion) {
@@ -183260,7 +183982,7 @@ async function installSkill(opts) {
183260
183982
  for (const relativePath of sourceFiles) {
183261
183983
  const sourcePath = join3(source, relativePath);
183262
183984
  const targetPath = join3(target, relativePath);
183263
- const sourceBytes = templateSourceFile(relativePath, readFileSync3(sourcePath), cliVersion);
183985
+ const sourceBytes = templateSourceFile(relativePath, readFileSync4(sourcePath), cliVersion);
183264
183986
  const sourceHash = sha256(sourceBytes);
183265
183987
  if (!existsSync4(targetPath)) {
183266
183988
  mkdirSync3(dirname3(targetPath), { recursive: true });
@@ -183268,7 +183990,7 @@ async function installSkill(opts) {
183268
183990
  written += 1;
183269
183991
  continue;
183270
183992
  }
183271
- const targetBytes = readFileSync3(targetPath);
183993
+ const targetBytes = readFileSync4(targetPath);
183272
183994
  const targetHash = sha256(targetBytes);
183273
183995
  if (targetHash === sourceHash) {
183274
183996
  skipped += 1;
@@ -183299,7 +184021,7 @@ async function installSkill(opts) {
183299
184021
  const targetPath = join3(target, relativePath);
183300
184022
  if (!existsSync4(targetPath))
183301
184023
  continue;
183302
- manifestEntries.push({ path: relativePath, sha256: sha256(readFileSync3(targetPath)) });
184024
+ manifestEntries.push({ path: relativePath, sha256: sha256(readFileSync4(targetPath)) });
183303
184025
  }
183304
184026
  const manifest = {
183305
184027
  cliVersion,
@@ -183340,7 +184062,7 @@ async function uninstallSkill(opts) {
183340
184062
  }
183341
184063
  if (!existsSync4(path2))
183342
184064
  continue;
183343
- const currentHash = sha256(readFileSync3(path2));
184065
+ const currentHash = sha256(readFileSync4(path2));
183344
184066
  if (currentHash !== entry.sha256 && !opts.yes) {
183345
184067
  kept += 1;
183346
184068
  continue;
@@ -183570,7 +184292,7 @@ function readManifest(target) {
183570
184292
  if (!existsSync4(path2))
183571
184293
  return null;
183572
184294
  try {
183573
- return JSON.parse(readFileSync3(path2, "utf-8"));
184295
+ return JSON.parse(readFileSync4(path2, "utf-8"));
183574
184296
  } catch {
183575
184297
  return null;
183576
184298
  }
@@ -183584,7 +184306,7 @@ function readCliVersion() {
183584
184306
  ];
183585
184307
  for (const candidate of candidates) {
183586
184308
  if (existsSync4(candidate)) {
183587
- const pkg = JSON.parse(readFileSync3(candidate, "utf-8"));
184309
+ const pkg = JSON.parse(readFileSync4(candidate, "utf-8"));
183588
184310
  if (pkg.version)
183589
184311
  return pkg.version;
183590
184312
  }
@@ -184386,8 +185108,8 @@ import path15 from "node:path";
184386
185108
  import { createInterface as createInterface3 } from "node:readline/promises";
184387
185109
 
184388
185110
  // src/lib/format-eigenpal.ts
184389
- import { spawnSync } from "child_process";
184390
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
185111
+ import { spawnSync as spawnSync2 } from "child_process";
185112
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
184391
185113
  import { join as join4, resolve as resolve3 } from "path";
184392
185114
  var PRETTRC_NAMES = [
184393
185115
  ".prettierrc",
@@ -184408,7 +185130,7 @@ function hasPrettierConfig(cwd) {
184408
185130
  const pkgPath = join4(cwd, "package.json");
184409
185131
  if (!existsSync5(pkgPath))
184410
185132
  return false;
184411
- const raw = readFileSync4(pkgPath, "utf-8");
185133
+ const raw = readFileSync5(pkgPath, "utf-8");
184412
185134
  const pkg = JSON.parse(raw);
184413
185135
  return pkg != null && typeof pkg === "object" && "prettier" in pkg;
184414
185136
  } catch {
@@ -184423,7 +185145,7 @@ function formatEigenpalDirIfAvailable(dir) {
184423
185145
  if (!hasPrettierConfig(cwd))
184424
185146
  return;
184425
185147
  const runner = typeof Bun !== "undefined" ? "bunx" : "npx";
184426
- spawnSync(runner, ["prettier", "--write", absDir], {
185148
+ spawnSync2(runner, ["prettier", "--write", absDir], {
184427
185149
  cwd,
184428
185150
  stdio: "ignore",
184429
185151
  shell: false
@@ -184509,7 +185231,7 @@ import { existsSync as existsSync7, readdirSync as readdirSync4, rmSync as rmSyn
184509
185231
  import { basename, join as join7 } from "path";
184510
185232
 
184511
185233
  // src/lib/payload.ts
184512
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
185234
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
184513
185235
  import { join as join6 } from "path";
184514
185236
 
184515
185237
  // src/lib/fs-helpers.ts
@@ -184566,7 +185288,7 @@ function getExampleNames(dir, exampleNamesFilter) {
184566
185288
  function readInput(exampleDir) {
184567
185289
  const argsPath = join6(exampleDir, "input", "arguments.json");
184568
185290
  const inputDir = join6(exampleDir, "input");
184569
- const scalars = JSON.parse(readFileSync5(argsPath, "utf-8"));
185291
+ const scalars = JSON.parse(readFileSync6(argsPath, "utf-8"));
184570
185292
  const files = [];
184571
185293
  for (const entry of readdirSync3(inputDir)) {
184572
185294
  const full = join6(inputDir, entry);
@@ -184577,7 +185299,7 @@ function readInput(exampleDir) {
184577
185299
  files.push({
184578
185300
  argument: entry,
184579
185301
  filename,
184580
- content: readFileSync5(join6(full, filename)),
185302
+ content: readFileSync6(join6(full, filename)),
184581
185303
  mimeType: guessMimeType(filename)
184582
185304
  });
184583
185305
  }
@@ -184588,7 +185310,7 @@ function readOverrides(exampleDir) {
184588
185310
  const metaPath = join6(exampleDir, "meta.json");
184589
185311
  if (!existsSync6(metaPath))
184590
185312
  return null;
184591
- const raw = JSON.parse(readFileSync5(metaPath, "utf-8"));
185313
+ const raw = JSON.parse(readFileSync6(metaPath, "utf-8"));
184592
185314
  const ov = raw.overrides;
184593
185315
  if (ov && typeof ov === "object" && "steps" in ov && typeof ov.steps === "object") {
184594
185316
  return ov;
@@ -184845,18 +185567,18 @@ async function watchExecution(opts) {
184845
185567
  import { join as join11 } from "path";
184846
185568
 
184847
185569
  // src/lib/execution-artifacts.ts
184848
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
185570
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
184849
185571
  import { join as join10 } from "path";
184850
185572
 
184851
185573
  // src/lib/format.ts
184852
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
185574
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
184853
185575
  import { extname as extname3, resolve as resolve5 } from "path";
184854
185576
  var PRETTIER_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
184855
185577
  async function formatFileIfAvailable(filepath) {
184856
185578
  if (!PRETTIER_EXTENSIONS.has(extname3(filepath)))
184857
185579
  return;
184858
185580
  try {
184859
- const content = readFileSync6(filepath, "utf-8");
185581
+ const content = readFileSync7(filepath, "utf-8");
184860
185582
  const prettier = await Promise.resolve().then(() => (init_prettier(), exports_prettier));
184861
185583
  const formatted = await prettier.format(content, {
184862
185584
  filepath: resolve5(filepath)
@@ -185623,7 +186345,7 @@ function parseWorkflow(yaml) {
185623
186345
  }
185624
186346
  // src/commands/validate.ts
185625
186347
  var import_yaml5 = __toESM(require_dist(), 1);
185626
- import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
186348
+ import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
185627
186349
  import { join as join12, resolve as resolve6 } from "path";
185628
186350
  var NAME_RE = /^[a-z0-9][a-z0-9-_]*$/;
185629
186351
  function isDirSafe(path15, issues, field) {
@@ -185641,7 +186363,7 @@ function validateWorkflowYaml(path15) {
185641
186363
  if (!existsSync9(path15)) {
185642
186364
  return [{ field: path15, message: "File not found." }];
185643
186365
  }
185644
- const text = readFileSync8(path15, "utf-8");
186366
+ const text = readFileSync9(path15, "utf-8");
185645
186367
  try {
185646
186368
  parseWorkflow(text);
185647
186369
  return [];
@@ -185663,7 +186385,7 @@ function validateEvaluatorsYaml(path15) {
185663
186385
  if (!existsSync9(path15)) {
185664
186386
  return [{ field: path15, message: "File not found." }];
185665
186387
  }
185666
- const text = readFileSync8(path15, "utf-8");
186388
+ const text = readFileSync9(path15, "utf-8");
185667
186389
  let parsed;
185668
186390
  try {
185669
186391
  parsed = import_yaml5.parse(text);
@@ -185715,7 +186437,7 @@ function validateDatasetFolder(root2) {
185715
186437
  }
185716
186438
  let args;
185717
186439
  try {
185718
- const parsed = JSON.parse(readFileSync8(argsPath, "utf-8"));
186440
+ const parsed = JSON.parse(readFileSync9(argsPath, "utf-8"));
185719
186441
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
185720
186442
  issues.push({
185721
186443
  field: `examples/${exampleName}/input/arguments.json`,
@@ -185773,7 +186495,7 @@ function validateDatasetFolder(root2) {
185773
186495
  if (existsSync9(expectedPath)) {
185774
186496
  let parsed;
185775
186497
  try {
185776
- parsed = JSON.parse(readFileSync8(expectedPath, "utf-8"));
186498
+ parsed = JSON.parse(readFileSync9(expectedPath, "utf-8"));
185777
186499
  } catch {
185778
186500
  issues.push({
185779
186501
  field: `examples/${exampleName}/expected/output.json`,
@@ -186316,7 +187038,7 @@ async function readDirAsZip(dir) {
186316
187038
  }
186317
187039
  return zipSync(tree);
186318
187040
  }
186319
- async function downloadStreamToFile(client, path16, outFile) {
187041
+ async function downloadStreamToFile2(client, path16, outFile) {
186320
187042
  const res = await client.getStream(path16);
186321
187043
  const buf = Buffer.from(await res.arrayBuffer());
186322
187044
  if (outFile) {
@@ -186400,7 +187122,8 @@ function registerWorkflowCoreCommands(parent) {
186400
187122
  withBaseUrl(pullCmd).action(action(async (workflow2, opts) => {
186401
187123
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
186402
187124
  const result = await client.get(`/api/v1/workflows/${workflowId}`);
186403
- await writeOrPrint(opts.out, result?.currentVersion?.yamlContent ?? "");
187125
+ const yaml = result?.currentVersion?.yamlContent ?? "";
187126
+ await writeOrPrint(opts.out, yaml);
186404
187127
  }));
186405
187128
  const pushCmd = parent.command("push").description(descriptionFor("workflow_set_definition")).option("--file <yaml>", "Path to YAML file").option("--workflow-id <id>", "Update existing workflow (default: create new)").option("--bump <level>", "Auto-bump from the server's current version: patch | minor | major. Mutually exclusive with `--version` and with a top-level `version:` in the YAML.").option("--set-version <semver>", "Push at this exact semver (e.g. 1.4.0). Mutually exclusive with `--bump` and with a top-level `version:` in the YAML. (Named `--set-version` to avoid the global `-v, --version` flag.)").addHelpText("after", `
186406
187129
  Examples:
@@ -186498,7 +187221,11 @@ flag would otherwise shadow it. Validation failures come back as
186498
187221
  throw new Error("--bump requires --workflow-id (need a current version to bump from). For a new workflow, use --set-version <X.Y.Z> or omit both.");
186499
187222
  }
186500
187223
  const yamlToSend = opts.setVersion && !extractWorkflowVersion(yamlOnDisk) ? spliceWorkflowVersion2(yamlOnDisk, opts.setVersion) : yamlOnDisk;
186501
- const result = await client.post("/api/v1/workflows", { yaml: yamlToSend });
187224
+ const createVersion = opts.setVersion ?? extractWorkflowVersion(yamlToSend) ?? undefined;
187225
+ const result = await client.post("/api/v1/workflows", {
187226
+ yaml: yamlToSend,
187227
+ ...createVersion ? { version: createVersion } : {}
187228
+ });
186502
187229
  if (opts.json) {
186503
187230
  printJson2(result);
186504
187231
  return;
@@ -186508,6 +187235,19 @@ flag would otherwise shadow it. Validation failures come back as
186508
187235
  success2(`Created ${ui.bold(id2)} (v${ver})`);
186509
187236
  }
186510
187237
  }));
187238
+ const moveCmd = parent.command("move <workflow-id>").description("Move a workflow to a folder path, creating folders as needed").requiredOption("--folder <path>", "Target folder path (`/` for root)");
187239
+ addJsonFlag(withBaseUrl(moveCmd)).action(action(async (workflow2, opts) => {
187240
+ const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
187241
+ const result = await client.patch(`/api/v1/workflows/${workflowId}`, {
187242
+ folderPath: opts.folder
187243
+ });
187244
+ if (opts.json) {
187245
+ printJson2(result);
187246
+ return;
187247
+ }
187248
+ const target = opts.folder.trim() === "/" ? "root" : opts.folder;
187249
+ success2(`Moved ${ui.bold(workflowId)} to ${ui.bold(target)}`);
187250
+ }));
186511
187251
  }
186512
187252
  var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
186513
187253
  function isBumpLevel(value) {
@@ -186602,7 +187342,7 @@ function registerDatasetCommands2(parent) {
186602
187342
  const pullCmd = dataset.command("pull <workflow-id>").description(descriptionFor("workflow_get_dataset")).option("--out <zip>", "Write the dataset ZIP to this path. When omitted, the binary streams to stdout.");
186603
187343
  withBaseUrl(pullCmd).action(action(async (workflow2, opts) => {
186604
187344
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
186605
- await downloadStreamToFile(client, `/api/v1/workflows/${workflowId}/dataset/export`, opts.out);
187345
+ await downloadStreamToFile2(client, `/api/v1/workflows/${workflowId}/dataset/export`, opts.out);
186606
187346
  }));
186607
187347
  const pushCmd = dataset.command("push <workflow-id>").description(descriptionFor("workflow_set_dataset")).requiredOption("--file <path>", "Path to a dataset ZIP file or a dataset/ folder (folder is zipped in memory before upload)").option("--mode <append|replace>", "Import mode (default: append)", "append").option("--yes", "Skip the destructive confirmation prompt for --mode replace (use in CI)", false).addHelpText("after", `
186608
187348
  Examples:
@@ -187051,7 +187791,7 @@ Behavior:
187051
187791
  const params = new URLSearchParams({ format: opts.format });
187052
187792
  if (batchId)
187053
187793
  params.set("batchId", batchId);
187054
- await downloadStreamToFile(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, opts.out);
187794
+ await downloadStreamToFile2(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, opts.out);
187055
187795
  }));
187056
187796
  const compareCmd = experiment.command("compare <batchIdA> <batchIdB>").description("Diff eval scores between two experiment batches.").option("--sort <abs-delta-desc|delta-asc|delta-desc|name>", "Row sort order (default: biggest movers first)", "abs-delta-desc").option("--regression-threshold <n>", "Δ below this is flagged as a regression (default 0.05)", Number.parseFloat, 0.05).addHelpText("after", `
187057
187797
  Examples:
@@ -187139,7 +187879,7 @@ Behavior:
187139
187879
  return;
187140
187880
  const params = new URLSearchParams({ format: opts.format, batchId });
187141
187881
  try {
187142
- await downloadStreamToFile(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, dest);
187882
+ await downloadStreamToFile2(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, dest);
187143
187883
  } catch (err2) {
187144
187884
  error51(`Watch finished but pulling results failed: ${err2 instanceof Error ? err2.message : String(err2)}. Re-run \`experiment results <wf> <batch>\` to retry.`);
187145
187885
  process.exit(1);
@@ -187340,6 +188080,39 @@ function registerStepTypeCommands(parent) {
187340
188080
  });
187341
188081
  }
187342
188082
 
188083
+ // src/lib/command-aliases.ts
188084
+ var CONVENTION_ALIASES = new Map([
188085
+ ["artifacts", "artifact"],
188086
+ ["execution", "exec"],
188087
+ ["experiment", "exp"],
188088
+ ["feedback", "fb"],
188089
+ ["list", "ls"],
188090
+ ["compare", "diff"]
188091
+ ]);
188092
+ function applyCommandAliasConventions(root2) {
188093
+ for (const command of root2.commands) {
188094
+ applySiblingAliases(command.parent ?? root2);
188095
+ applyCommandAliasConventions(command);
188096
+ }
188097
+ }
188098
+ function applySiblingAliases(parent) {
188099
+ const reserved = new Set;
188100
+ for (const sibling of parent.commands) {
188101
+ reserved.add(sibling.name());
188102
+ for (const alias of sibling.aliases())
188103
+ reserved.add(alias);
188104
+ }
188105
+ for (const command of parent.commands) {
188106
+ const alias = CONVENTION_ALIASES.get(command.name());
188107
+ if (!alias || command.aliases().includes(alias))
188108
+ continue;
188109
+ if (reserved.has(alias))
188110
+ continue;
188111
+ command.aliases([...command.aliases(), alias]);
188112
+ reserved.add(alias);
188113
+ }
188114
+ }
188115
+
187343
188116
  // src/lib/logo.ts
187344
188117
  var LOGO_WIDTH = 22;
187345
188118
  var LOGO_LINES = [
@@ -187785,6 +188558,7 @@ skillCmd.command("list").description("Show every tool that has an Eigenpal skill
187785
188558
  process.exit(1);
187786
188559
  }
187787
188560
  });
188561
+ applyCommandAliasConventions(program2);
187788
188562
  function isEntryPoint() {
187789
188563
  const argv1 = process.argv[1];
187790
188564
  if (!argv1)