@odla-ai/cli 0.27.2 → 0.27.4

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/bin.cjs CHANGED
@@ -32,6 +32,7 @@ var import_node_process7 = __toESM(require("process"), 1);
32
32
 
33
33
  // src/token.ts
34
34
  var import_db = require("@odla-ai/db");
35
+ var import_node_crypto = require("crypto");
35
36
  var import_node_process4 = __toESM(require("process"), 1);
36
37
 
37
38
  // src/handshake-approval.ts
@@ -386,6 +387,7 @@ async function freshHandshake(ctx, waitMs) {
386
387
  endpoint: ctx.cfg.platformUrl,
387
388
  email: ctx.email,
388
389
  label: `${ctx.cfg.app.id} provisioner`,
390
+ agentHandle: projectAgentHandle(ctx.cfg.app.id),
389
391
  projectIds: [ctx.cfg.app.id],
390
392
  fetch: ctx.doFetch,
391
393
  waitMs,
@@ -421,6 +423,12 @@ async function freshHandshake(ctx, waitMs) {
421
423
  stopReminder?.();
422
424
  }
423
425
  }
426
+ function projectAgentHandle(appId) {
427
+ const candidate = /^[a-z]/.test(appId) ? appId : `app-${appId}`;
428
+ if (candidate.length <= 32) return candidate;
429
+ const digest = (0, import_node_crypto.createHash)("sha256").update(appId).digest("hex").slice(0, 8);
430
+ return `${candidate.slice(0, 23).replace(/-+$/, "")}-${digest}`;
431
+ }
424
432
  function stillPending(pending, email) {
425
433
  return new import_db.OdlaError(
426
434
  "handshake_pending",
@@ -2633,8 +2641,8 @@ async function calendarCalendars(options) {
2633
2641
  async function calendarConnect(options) {
2634
2642
  const { cfg, ctx, out } = await lifecycleContext(options);
2635
2643
  productionConsent(ctx.env, options.yes, "connect calendar");
2636
- const page = calendarBookingPageUrl(cfg, ctx.env);
2637
- const applied = page === void 0 ? await readCalendarStatus(ctx) : await applyCalendarSettings(ctx, page);
2644
+ const page2 = calendarBookingPageUrl(cfg, ctx.env);
2645
+ const applied = page2 === void 0 ? await readCalendarStatus(ctx) : await applyCalendarSettings(ctx, page2);
2638
2646
  const connectOptions = connectionOptions(options, out);
2639
2647
  return await continueConnectedCalendar(ctx, applied, connectOptions) ?? connectWithContext(ctx, connectOptions);
2640
2648
  }
@@ -2883,12 +2891,12 @@ var import_apps3 = require("@odla-ai/apps");
2883
2891
  var import_node_fs10 = require("fs");
2884
2892
 
2885
2893
  // src/config-reconcile-digest.ts
2886
- var import_node_crypto = require("crypto");
2894
+ var import_node_crypto2 = require("crypto");
2887
2895
  function canonicalJson(value2) {
2888
2896
  return JSON.stringify(canonicalValue(value2));
2889
2897
  }
2890
2898
  function configDigest(value2) {
2891
- return `sha256:${(0, import_node_crypto.createHash)("sha256").update(canonicalJson(value2)).digest("hex")}`;
2899
+ return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(canonicalJson(value2)).digest("hex")}`;
2892
2900
  }
2893
2901
  function canonicalValue(value2) {
2894
2902
  if (value2 === null || typeof value2 === "string" || typeof value2 === "boolean") return value2;
@@ -4461,9 +4469,10 @@ For work that creates an odla app or adds odla services, read and follow
4461
4469
  \`.agents/skills/odla-migrate/SKILL.md\`. For production telemetry triage, use
4462
4470
  \`.agents/skills/odla-o11y-debug/SKILL.md\`.
4463
4471
 
4464
- Track the work in odla's PM as you go \u2014 record decisions when you make them, file
4465
- bugs when you notice them, and move tasks when the work moves
4466
- (\`npx @odla-ai/cli pm task list\`). The conventions and the full command set are
4472
+ Track the work in odla's PM as you go. Before project-mutating work, run
4473
+ \`npx @odla-ai/cli pm next --app <appId>\`, confirm alignment to an open goal,
4474
+ and atomically claim a refined Ready task. Record decisions when you make them
4475
+ and file bugs when you notice them. The conventions and the full command set are
4467
4476
  in \`.agents/skills/odla/references/pm.md\`.
4468
4477
 
4469
4478
  The setup runbooks and their references are installed in this repository, pinned
@@ -5687,7 +5696,7 @@ function dependenciesOf(values, influence = "data") {
5687
5696
  return [...unique3.values()];
5688
5697
  }
5689
5698
 
5690
- // ../camel/dist/chunk-S7EVNA2U.js
5699
+ // ../camel/dist/chunk-4DQ6BIHP.js
5691
5700
  var camelValueBrand = /* @__PURE__ */ Symbol("@odla-ai/camel/value");
5692
5701
  var authenticCamelValues = /* @__PURE__ */ new WeakSet();
5693
5702
  function isCamelValue(value2) {
@@ -5699,13 +5708,13 @@ function isSafe(value2) {
5699
5708
  function isUnsafe(value2) {
5700
5709
  return isCamelValue(value2) && value2.label.promptSafety === "unsafe";
5701
5710
  }
5702
- function createSafeInternal(value2, safeBasis, metadata2) {
5711
+ function createSafeInternal(value2, safeBasis, metadata2, extra) {
5703
5712
  return createValue(value2, {
5704
5713
  schemaVersion: 1,
5705
5714
  promptSafety: "safe",
5706
5715
  safeBasis,
5707
5716
  ...copyMetadata(metadata2)
5708
- });
5717
+ }, extra);
5709
5718
  }
5710
5719
  function createUnsafeInternal(value2, metadata2) {
5711
5720
  return createValue(value2, {
@@ -5714,8 +5723,8 @@ function createUnsafeInternal(value2, metadata2) {
5714
5723
  ...copyMetadata(metadata2)
5715
5724
  });
5716
5725
  }
5717
- function createValue(value2, label) {
5718
- const result = { value: value2, label: Object.freeze(label) };
5726
+ function createValue(value2, label, extra) {
5727
+ const result = { value: value2, label: Object.freeze(label), ...extra };
5719
5728
  Object.defineProperty(result, camelValueBrand, { value: label.promptSafety, enumerable: false });
5720
5729
  authenticCamelValues.add(result);
5721
5730
  return Object.freeze(result);
@@ -5976,7 +5985,7 @@ var import_path8 = require("path");
5976
5985
  var import_promises10 = require("fs/promises");
5977
5986
  var import_path9 = require("path");
5978
5987
 
5979
- // ../camel/dist/chunk-LAXU2AVK.js
5988
+ // ../camel/dist/chunk-4EIRFS3A.js
5980
5989
  function conversionPolicyDigest(policy) {
5981
5990
  return sha256Hex(canonicalJson2(policy));
5982
5991
  }
@@ -6064,10 +6073,18 @@ async function convert(source, policy, value2, counts) {
6064
6073
  const count = counts.get(countKey) ?? 0;
6065
6074
  if (count >= policy.maximumOutputsPerArtifact) throw new CamelError("limit_exceeded", "Conversion output count exceeds its per-source bound.");
6066
6075
  counts.set(countKey, count + 1);
6076
+ const conversionRecordId = await sha256Hex(canonicalJson2({ conversionId: policy.conversionId, digest: policy.digest, source: sourceKey, ordinal: count }));
6077
+ const datumId = await sha256Hex(canonicalJson2({ conversionRecordId, value: value2 }));
6067
6078
  return createSafeInternal(value2, "atomic_conversion", {
6068
6079
  readers: source.label.readers,
6069
6080
  provenance: [...source.label.provenance, { kind: "converter", id: policy.conversionId, digest: policy.digest }],
6070
6081
  dependencies: dependenciesOf([source])
6082
+ }, {
6083
+ datumId,
6084
+ kind: policy.output.kind,
6085
+ conversionId: policy.conversionId,
6086
+ conversionDigest: policy.digest,
6087
+ conversionRecordId
6071
6088
  });
6072
6089
  }
6073
6090
  function validatePolicyShape(policy) {
@@ -6132,7 +6149,7 @@ function missingPolicy() {
6132
6149
  throw new CamelError("conversion_rejected", "Conversion policy is not registered.");
6133
6150
  }
6134
6151
 
6135
- // ../camel/dist/chunk-P2WQIAG6.js
6152
+ // ../camel/dist/chunk-VEAUXH4F.js
6136
6153
  function createCamelIngress(constants2 = []) {
6137
6154
  const byId = /* @__PURE__ */ new Map();
6138
6155
  for (const item of constants2) {
@@ -8122,7 +8139,7 @@ async function defaultReadOrigin(cwd) {
8122
8139
 
8123
8140
  // src/code-local-source.ts
8124
8141
  var import_node_child_process5 = require("child_process");
8125
- var import_node_crypto2 = require("crypto");
8142
+ var import_node_crypto3 = require("crypto");
8126
8143
  var SOURCE_LIMITS2 = { maxFiles: 2e4, maxBytes: 512 * 1024 * 1024 };
8127
8144
  async function prepareCodeLocalSource(cwd, repository, readHead = readGitHead) {
8128
8145
  const headCommitSha = await readHead(cwd);
@@ -8173,12 +8190,12 @@ async function readGitHead(cwd) {
8173
8190
  return value2;
8174
8191
  }
8175
8192
  function digestText(value2) {
8176
- return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(value2).digest("hex")}`;
8193
+ return `sha256:${(0, import_node_crypto3.createHash)("sha256").update(value2).digest("hex")}`;
8177
8194
  }
8178
8195
 
8179
8196
  // src/code-images.ts
8180
8197
  var import_node_child_process6 = require("child_process");
8181
- var import_node_crypto3 = require("crypto");
8198
+ var import_node_crypto4 = require("crypto");
8182
8199
  var import_promises11 = require("fs/promises");
8183
8200
  var import_node_os3 = require("os");
8184
8201
  var import_node_path14 = require("path");
@@ -8264,7 +8281,7 @@ async function embeddedPiImageName() {
8264
8281
  const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
8265
8282
  throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
8266
8283
  });
8267
- return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
8284
+ return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto4.createHash)("sha256").update(bundle).digest("hex")}`;
8268
8285
  }
8269
8286
  async function buildEmbeddedPiImage(engine, image, run) {
8270
8287
  const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
@@ -8692,16 +8709,22 @@ Usage:
8692
8709
  odla-ai app owners remove <email> [--email <odla-account>] [--json]
8693
8710
  odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
8694
8711
  odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8695
- odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8712
+ odla-ai pm task list [--app <id>] [--column <backlog|ready|doing|review|done>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8696
8713
  odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8697
8714
  odla-ai pm bug list [--app <id>] [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8698
8715
  odla-ai pm goal add --app <id> --title <t> [--status <s>] [--proof <text>] [--target <pct>] [--mutation-id <id>] [--json]
8699
- odla-ai pm task add --app <id> --title <t> [--column <c>] [--goal <id>] [--assignee <id>] [--description <text>|--body <text>] [--due <epoch-ms>] [--mutation-id <id>] [--json]
8716
+ odla-ai pm task add --app <id> --title <t> [--column <backlog|ready|doing|review|done>] [--goal <id>|--alignment-decision <id>] [--assignee <id>] [--description <text>|--body <text>] [--acceptance <text>] [--execution <human|agent|either>] [--due <epoch-ms>] [--mutation-id <id>] [--json]
8717
+ odla-ai pm next --app <id> [--json]
8718
+ odla-ai pm watch --app <id> [--cursor <cursor>] [--entity goal|task|decision|bug] [--action created|updated|deleted|comment.created|comment.updated] [--state <state>] [--by <principalId>] [--self <principalId>] [--interval <s>] [--timeout <s>] [--json|--jsonl]
8719
+ odla-ai pm task ready <id> --expected-revision <n> [--goal <id>|--alignment-decision <id>] [--description <text>|--body <text>] [--acceptance <text>] [--execution <human|agent|either>] [--mutation-id <id>] [--json]
8720
+ odla-ai pm task claim <id> --expected-revision <n> [--mutation-id <id>] [--json]
8721
+ odla-ai pm task release <id> --expected-revision <n> [--mutation-id <id>] [--json]
8700
8722
  odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
8701
8723
  odla-ai pm bug add --app <id> --title <t> (--description <text>|--body <text>) [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--mutation-id <id>] [--json]
8702
8724
  odla-ai pm <goal|task|decision|bug> get <id> [--json]
8725
+ odla-ai pm <goal|task|decision|bug> ref <id> [--json]
8703
8726
  odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
8704
- odla-ai pm task set <id> [--title <t>|--column <c>|--rank <n>|--goal <id>|--no-goal|--assignee <id>|--no-assignee|--description <text>|--body <text>|--due <epoch-ms>|--no-due] [--mutation-id <id>] [--json]
8727
+ odla-ai pm task set <id> [--title <t>|--column <backlog|ready|doing|review|done>|--rank <n>|--goal <id>|--no-goal|--alignment-decision <id>|--no-alignment-decision|--execution <human|agent|either>|--assignee <id>|--no-assignee|--description <text>|--body <text>|--acceptance <text>|--no-acceptance|--due <epoch-ms>|--no-due|--expected-revision <n>] [--mutation-id <id>] [--json]
8705
8728
  odla-ai pm decision set <id> [--title <t>|--status <s>|--body <text>] [--mutation-id <id>] [--json]
8706
8729
  odla-ai pm bug set <id> [--title <t>|--status <s>|--severity <s>|--goal <id>|--no-goal|--assignee <id>|--no-assignee|--decision <id>|--no-decision|--description <text>|--body <text>] [--mutation-id <id>] [--json]
8707
8730
  odla-ai pm <goal|task|decision> done <id> [--mutation-id <id>]
@@ -9020,11 +9043,11 @@ async function discussList(ctx, parsed) {
9020
9043
  if (value2) query.set(param, value2);
9021
9044
  }
9022
9045
  const qs = query.toString();
9023
- const page = await request(ctx, "GET", `/topics${qs ? `?${qs}` : ""}`);
9024
- emit(ctx, page, () => {
9025
- ctx.out.log(`topics \u2014 ${page.topics.length} of ${page.total}`);
9046
+ const page2 = await request(ctx, "GET", `/topics${qs ? `?${qs}` : ""}`);
9047
+ emit(ctx, page2, () => {
9048
+ ctx.out.log(`topics \u2014 ${page2.topics.length} of ${page2.total}`);
9026
9049
  ctx.out.log("id state app replies subject");
9027
- for (const topic of page.topics) {
9050
+ for (const topic of page2.topics) {
9028
9051
  ctx.out.log(
9029
9052
  `${topic.id} ${state(topic)} ${topic.appId ?? ""} ${topic.replyCount} ${topic.subject}`
9030
9053
  );
@@ -9039,11 +9062,11 @@ async function discussRead(ctx, id, parsed) {
9039
9062
  limit: requestedLimit ?? "200",
9040
9063
  offset: requestedOffset ?? "0"
9041
9064
  });
9042
- const page = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
9065
+ const page2 = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
9043
9066
  emit(
9044
9067
  ctx,
9045
- page,
9046
- () => renderDiscussRead(ctx, page.topic, page.posts, page)
9068
+ page2,
9069
+ () => renderDiscussRead(ctx, page2.topic, page2.posts, page2)
9047
9070
  );
9048
9071
  return;
9049
9072
  }
@@ -9056,20 +9079,20 @@ async function discussRead(ctx, id, parsed) {
9056
9079
  let topic = null;
9057
9080
  let offset = 0;
9058
9081
  for (; ; ) {
9059
- const page = await request(
9082
+ const page2 = await request(
9060
9083
  ctx,
9061
9084
  "GET",
9062
9085
  `/topics/${encodeURIComponent(id)}?limit=200&offset=${offset}`
9063
9086
  );
9064
- topic = page.topic;
9065
- for (const post of page.posts) posts.set(post.id, post);
9066
- mergeDiscussPrincipals(projection, page);
9087
+ topic = page2.topic;
9088
+ for (const post of page2.posts) posts.set(post.id, post);
9089
+ mergeDiscussPrincipals(projection, page2);
9067
9090
  if (posts.size > 1e4) throw new Error("discuss read failed: conversation exceeds 10000 posts");
9068
- if (!page.page?.hasMore) break;
9069
- if (page.page.nextOffset === null || page.page.nextOffset <= offset) {
9091
+ if (!page2.page?.hasMore) break;
9092
+ if (page2.page.nextOffset === null || page2.page.nextOffset <= offset) {
9070
9093
  throw new Error("discuss read failed: registry returned a non-advancing post page");
9071
9094
  }
9072
- offset = page.page.nextOffset;
9095
+ offset = page2.page.nextOffset;
9073
9096
  }
9074
9097
  const ordered = [...posts.values()].sort(
9075
9098
  (a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
@@ -9237,9 +9260,9 @@ async function discussWatch(ctx, topicId, parsed) {
9237
9260
  let firstSuccess = true;
9238
9261
  let consecutiveFailures = 0;
9239
9262
  for (; ; ) {
9240
- let page;
9263
+ let page2;
9241
9264
  try {
9242
- page = await getWatchPage(ctx, requestPath(topicId, app, cursor));
9265
+ page2 = await getWatchPage(ctx, requestPath(topicId, app, cursor));
9243
9266
  consecutiveFailures = 0;
9244
9267
  } catch (error) {
9245
9268
  if (!(error instanceof WatchRequestError) || !error.retryable) {
@@ -9275,25 +9298,25 @@ async function discussWatch(ctx, topicId, parsed) {
9275
9298
  await sleep(deadline === void 0 ? backoff : Math.min(backoff, Math.max(0, deadline - now())));
9276
9299
  continue;
9277
9300
  }
9278
- cursor = page.cursor;
9279
- const baseline = firstSuccess && page.events.length === 0;
9301
+ cursor = page2.cursor;
9302
+ const baseline = firstSuccess && page2.events.length === 0;
9280
9303
  if (baseline) {
9281
9304
  jsonl(ctx, parsed, {
9282
9305
  type: "checkpoint",
9283
- streamId: page.streamId,
9306
+ streamId: page2.streamId,
9284
9307
  cursor,
9285
- serverTime: page.serverTime
9308
+ serverTime: page2.serverTime
9286
9309
  });
9287
9310
  }
9288
9311
  firstSuccess = false;
9289
- const matching = page.events.filter((event) => {
9312
+ const matching = page2.events.filter((event) => {
9290
9313
  if (topicId && (event.type !== "message" || event.action !== "created")) return false;
9291
9314
  return (!by || event.actor.id === by) && (!self || event.actor.id !== self);
9292
9315
  });
9293
9316
  for (const event of matching) {
9294
9317
  jsonl(ctx, parsed, {
9295
9318
  type: "event",
9296
- streamId: page.streamId,
9319
+ streamId: page2.streamId,
9297
9320
  eventId: event.id,
9298
9321
  cursor: event.cursor,
9299
9322
  event
@@ -9302,9 +9325,9 @@ async function discussWatch(ctx, topicId, parsed) {
9302
9325
  if (matching.length > 0) {
9303
9326
  jsonl(ctx, parsed, {
9304
9327
  type: "checkpoint",
9305
- streamId: page.streamId,
9328
+ streamId: page2.streamId,
9306
9329
  cursor,
9307
- serverTime: page.serverTime
9330
+ serverTime: page2.serverTime
9308
9331
  });
9309
9332
  const posts = topicId ? matching.filter((event) => event.type === "message").map((event) => event.payload) : void 0;
9310
9333
  const topics = topicId ? void 0 : matching.filter((event) => event.type === "activity").map((event) => event.payload);
@@ -9312,28 +9335,28 @@ async function discussWatch(ctx, topicId, parsed) {
9312
9335
  found: true,
9313
9336
  cursor,
9314
9337
  events: matching,
9315
- ...page.authors ? { authors: page.authors } : {},
9316
- ...page.principals ? { principals: page.principals } : {},
9338
+ ...page2.authors ? { authors: page2.authors } : {},
9339
+ ...page2.principals ? { principals: page2.principals } : {},
9317
9340
  ...posts && posts.length > 0 ? { posts } : {},
9318
9341
  ...topics && topics.length > 0 ? { topics } : {}
9319
9342
  });
9320
9343
  }
9321
- if (page.events.length > 0) {
9344
+ if (page2.events.length > 0) {
9322
9345
  jsonl(ctx, parsed, {
9323
9346
  type: "checkpoint",
9324
- streamId: page.streamId,
9347
+ streamId: page2.streamId,
9325
9348
  cursor,
9326
- serverTime: page.serverTime
9349
+ serverTime: page2.serverTime
9327
9350
  });
9328
9351
  } else if (!baseline) {
9329
9352
  jsonl(ctx, parsed, {
9330
9353
  type: "heartbeat",
9331
- streamId: page.streamId,
9354
+ streamId: page2.streamId,
9332
9355
  cursor,
9333
- serverTime: page.serverTime
9356
+ serverTime: page2.serverTime
9334
9357
  });
9335
9358
  }
9336
- if (page.hasMore) continue;
9359
+ if (page2.hasMore) continue;
9337
9360
  if (deadline !== void 0 && now() >= deadline) {
9338
9361
  return report2(ctx, parsed, { found: false, cursor });
9339
9362
  }
@@ -9452,7 +9475,13 @@ async function discussCommand(parsed, deps = {}) {
9452
9475
  }
9453
9476
  }
9454
9477
 
9455
- // src/pm-actions.ts
9478
+ // src/pm-action-core.ts
9479
+ var DONE = {
9480
+ goal: { status: "met", currentPct: 100 },
9481
+ task: { column: "done" },
9482
+ decision: { status: "accepted" },
9483
+ bug: { status: "fixed" }
9484
+ };
9456
9485
  var writeMutationId2 = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
9457
9486
  var FIELD_MAP = {
9458
9487
  title: { key: "title" },
@@ -9468,22 +9497,27 @@ var FIELD_MAP = {
9468
9497
  body: { key: "body" },
9469
9498
  target: { key: "targetPct", num: true },
9470
9499
  description: { key: "description" },
9471
- desc: { key: "description" }
9472
- };
9473
- var DONE = {
9474
- goal: { status: "met", currentPct: 100 },
9475
- task: { column: "done" },
9476
- decision: { status: "accepted" },
9477
- bug: { status: "fixed" }
9500
+ desc: { key: "description" },
9501
+ acceptance: { key: "acceptanceCriteria" },
9502
+ "alignment-decision": { key: "alignmentDecisionId" },
9503
+ execution: { key: "executionMode" },
9504
+ "expected-revision": { key: "expectedRevision", num: true }
9478
9505
  };
9479
9506
  async function pmRequest(ctx, method, path, body) {
9480
- const res = await ctx.doFetch(`${ctx.platformUrl}/registry/pm${path}`, {
9507
+ const response2 = await ctx.doFetch(`${ctx.platformUrl}/registry/pm${path}`, {
9481
9508
  method,
9482
- headers: { authorization: `Bearer ${ctx.token}`, "content-type": "application/json" },
9509
+ headers: {
9510
+ authorization: `Bearer ${ctx.token}`,
9511
+ "content-type": "application/json"
9512
+ },
9483
9513
  body: body === void 0 ? void 0 : JSON.stringify(body)
9484
9514
  });
9485
- const data = await res.json().catch(() => ({}));
9486
- if (!res.ok) throw new Error(`pm ${method} ${path} failed: ${data.error ?? `registry returned ${res.status}`}`);
9515
+ const data = await response2.json().catch(() => ({}));
9516
+ if (!response2.ok) {
9517
+ throw new Error(
9518
+ `pm ${method} ${path} failed: ${data.error ?? `registry returned ${response2.status}`}`
9519
+ );
9520
+ }
9487
9521
  return data;
9488
9522
  }
9489
9523
  function collectFields(parsed, allowClear) {
@@ -9506,20 +9540,32 @@ function collectEntityFields(entity, parsed, allowClear) {
9506
9540
  if (fields.description === void 0) fields.description = fields.body;
9507
9541
  delete fields.body;
9508
9542
  }
9543
+ if (entity === "task" && fields.column === "ready") fields.column = "todo";
9509
9544
  return fields;
9510
9545
  }
9511
- function statusCol(entity, r) {
9512
- if (entity === "bug") return `${r.status ?? ""}/${r.severity ?? ""}`;
9513
- if (entity === "task") return String(r.column ?? "");
9514
- return String(r.status ?? "");
9546
+ function statusCol(entity, record10) {
9547
+ if (entity === "bug") return `${record10.status ?? ""}/${record10.severity ?? ""}`;
9548
+ if (entity === "task") {
9549
+ const state2 = record10.column === "todo" ? "ready" : String(record10.column ?? "");
9550
+ return record10.revision ? `${state2}; r${record10.revision}` : state2;
9551
+ }
9552
+ return String(record10.status ?? "");
9515
9553
  }
9516
- function printRecord(ctx, entity, r) {
9517
- ctx.out.log(`${r.id} [${statusCol(entity, r)}] ${r.appId} ${r.title ?? ""}`);
9554
+ function referenceMarkup(entity, record10) {
9555
+ const label = (record10.title?.trim() || `${entity} ${record10.id}`).replaceAll("]", ")");
9556
+ return `@[${label}](pm:${entity}/${record10.id})`;
9557
+ }
9558
+ function printRecord(ctx, entity, record10) {
9559
+ ctx.out.log(
9560
+ `${record10.id} [${statusCol(entity, record10)}] ${record10.appId} ${record10.title ?? ""}`
9561
+ );
9518
9562
  }
9519
9563
  function emit2(ctx, value2, human) {
9520
9564
  if (ctx.json) ctx.out.log(JSON.stringify(value2, null, 2));
9521
9565
  else human();
9522
9566
  }
9567
+
9568
+ // src/pm-actions.ts
9523
9569
  async function pmList(ctx, entity, parsed) {
9524
9570
  const q = new URLSearchParams();
9525
9571
  const filters = {
@@ -9533,7 +9579,8 @@ async function pmList(ctx, entity, parsed) {
9533
9579
  const app = stringOpt(parsed.options.app) ?? ctx.appId;
9534
9580
  if (app) q.set("app", app);
9535
9581
  for (const [flag, param] of Object.entries(filters)) {
9536
- const v = stringOpt(parsed.options[flag]);
9582
+ const raw = stringOpt(parsed.options[flag]);
9583
+ const v = entity === "task" && flag === "column" && raw === "ready" ? "todo" : raw;
9537
9584
  if (v) q.set(param, v);
9538
9585
  }
9539
9586
  for (const opt of ["q", "limit", "offset"]) {
@@ -9541,11 +9588,11 @@ async function pmList(ctx, entity, parsed) {
9541
9588
  if (v) q.set(opt, v);
9542
9589
  }
9543
9590
  const qs = q.toString();
9544
- const page = await pmRequest(ctx, "GET", `/${entity}${qs ? `?${qs}` : ""}`);
9545
- emit2(ctx, page, () => {
9546
- ctx.out.log(`${entity} \u2014 ${page.records.length} of ${page.total}`);
9591
+ const page2 = await pmRequest(ctx, "GET", `/${entity}${qs ? `?${qs}` : ""}`);
9592
+ emit2(ctx, page2, () => {
9593
+ ctx.out.log(`${entity} \u2014 ${page2.records.length} of ${page2.total}`);
9547
9594
  ctx.out.log("id state app title");
9548
- for (const r of page.records) printRecord(ctx, entity, r);
9595
+ for (const r of page2.records) printRecord(ctx, entity, r);
9549
9596
  });
9550
9597
  }
9551
9598
  async function pmAdd(ctx, entity, parsed) {
@@ -9566,6 +9613,17 @@ async function pmGet(ctx, entity, id) {
9566
9613
  const { record: record10 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
9567
9614
  emit2(ctx, record10, () => printRecord(ctx, entity, record10));
9568
9615
  }
9616
+ async function pmReference(ctx, entity, id) {
9617
+ const { record: record10 } = await pmRequest(
9618
+ ctx,
9619
+ "GET",
9620
+ `/${entity}/${encodeURIComponent(id)}`
9621
+ );
9622
+ const markup = referenceMarkup(entity, record10);
9623
+ emit2(ctx, { kind: `pm:${entity}`, id: record10.id, label: record10.title ?? "", markup }, () => {
9624
+ ctx.out.log(markup);
9625
+ });
9626
+ }
9569
9627
  async function pmSet(ctx, entity, id, parsed) {
9570
9628
  const patch2 = collectEntityFields(entity, parsed, true);
9571
9629
  if (Object.keys(patch2).length === 0)
@@ -9586,6 +9644,36 @@ async function pmDone(ctx, entity, id, parsed) {
9586
9644
  });
9587
9645
  emit2(ctx, res, () => ctx.out.log(`${entity} ${id} \u2192 done`));
9588
9646
  }
9647
+ async function pmTaskLifecycle(ctx, id, action2, parsed) {
9648
+ const rawRevision = stringOpt(parsed.options["expected-revision"]);
9649
+ const expectedRevision = Number(rawRevision);
9650
+ if (!rawRevision || !Number.isSafeInteger(expectedRevision) || expectedRevision < 1) {
9651
+ throw new Error(`pm task ${action2} needs --expected-revision <n>`);
9652
+ }
9653
+ const mutationId = writeMutationId2(parsed);
9654
+ const res = action2 === "ready" ? await pmRequest(
9655
+ ctx,
9656
+ "PATCH",
9657
+ `/task/${encodeURIComponent(id)}`,
9658
+ {
9659
+ patch: {
9660
+ ...collectEntityFields("task", parsed, true),
9661
+ column: "todo",
9662
+ expectedRevision
9663
+ },
9664
+ mutationId
9665
+ }
9666
+ ) : await pmRequest(
9667
+ ctx,
9668
+ "POST",
9669
+ `/task/${encodeURIComponent(id)}/${action2}`,
9670
+ { expectedRevision, mutationId }
9671
+ );
9672
+ emit2(ctx, res, () => {
9673
+ const state2 = res.record ? statusCol("task", res.record) : action2;
9674
+ ctx.out.log(`task ${id} \u2192 ${state2}`);
9675
+ });
9676
+ }
9589
9677
  async function allRecords(ctx, entity, appId) {
9590
9678
  const records = [];
9591
9679
  for (; ; ) {
@@ -9594,11 +9682,48 @@ async function allRecords(ctx, entity, appId) {
9594
9682
  limit: "100",
9595
9683
  offset: String(records.length)
9596
9684
  });
9597
- const page = await pmRequest(ctx, "GET", `/${entity}?${q}`);
9598
- records.push(...page.records);
9599
- if (records.length >= page.total || page.records.length === 0) return records;
9685
+ const page2 = await pmRequest(ctx, "GET", `/${entity}?${q}`);
9686
+ records.push(...page2.records);
9687
+ if (records.length >= page2.total || page2.records.length === 0) return records;
9600
9688
  }
9601
9689
  }
9690
+ async function pmNext(ctx, parsed) {
9691
+ const appId = stringOpt(parsed.options.app) ?? ctx.appId;
9692
+ if (!appId) throw new Error("pm next needs --app <appId>");
9693
+ const [goals, tasks] = await Promise.all([
9694
+ allRecords(ctx, "goal", appId),
9695
+ allRecords(ctx, "task", appId)
9696
+ ]);
9697
+ const result = {
9698
+ appId,
9699
+ openGoals: goals.filter((record10) => record10.status === "open"),
9700
+ doing: tasks.filter((record10) => record10.column === "doing"),
9701
+ ready: tasks.filter((record10) => record10.column === "todo")
9702
+ };
9703
+ emit2(ctx, result, () => {
9704
+ ctx.out.log(`${appId}: goal-aligned work intake (read only)`);
9705
+ for (const [label, records] of [
9706
+ ["doing", result.doing],
9707
+ ["ready", result.ready],
9708
+ ["open goals", result.openGoals]
9709
+ ]) {
9710
+ ctx.out.log(`${label}:`);
9711
+ if (!records.length) ctx.out.log("- (none)");
9712
+ else for (const record10 of records) printRecord(
9713
+ ctx,
9714
+ label === "open goals" ? "goal" : "task",
9715
+ record10
9716
+ );
9717
+ }
9718
+ if (!result.openGoals.length) {
9719
+ ctx.out.log("next: discuss alignment with the user before creating or claiming project work");
9720
+ } else if (!result.ready.length) {
9721
+ ctx.out.log("next: refine a linked Backlog task and mark it Ready");
9722
+ } else {
9723
+ ctx.out.log("next: review a Ready task, then claim it with its revision");
9724
+ }
9725
+ });
9726
+ }
9602
9727
  async function pmHandoff(ctx, parsed) {
9603
9728
  const appId = stringOpt(parsed.options.app) ?? ctx.appId;
9604
9729
  if (!appId) throw new Error("pm handoff needs --app <appId>");
@@ -9638,6 +9763,12 @@ async function pmHandoff(ctx, parsed) {
9638
9763
  }
9639
9764
  });
9640
9765
  }
9766
+ async function pmRemove(ctx, entity, id) {
9767
+ await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id)}`);
9768
+ ctx.out.log(`deleted ${entity} ${id}`);
9769
+ }
9770
+
9771
+ // src/pm-comments.ts
9641
9772
  async function pmComment(ctx, entity, id, parsed) {
9642
9773
  const body = stringOpt(parsed.options.body);
9643
9774
  if (!body) throw new Error('pm comment needs --body "..."');
@@ -9648,19 +9779,218 @@ async function pmComment(ctx, entity, id, parsed) {
9648
9779
  ctx.out.log(`commented on ${entity} ${id}`);
9649
9780
  }
9650
9781
  async function pmComments(ctx, entity, id) {
9651
- const { messages } = await pmRequest(
9652
- ctx,
9653
- "GET",
9654
- `/${entity}/${encodeURIComponent(id)}/comments`
9655
- );
9782
+ const { messages } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}/comments`);
9656
9783
  emit2(ctx, messages, () => {
9657
9784
  if (messages.length === 0) ctx.out.log("(no comments)");
9658
- else for (const m of messages) ctx.out.log(`[${m.authorId ?? "?"}] ${m.body ?? ""}`);
9785
+ else for (const message2 of messages) {
9786
+ ctx.out.log(
9787
+ `[${message2.authorId ?? "?"}] ${message2.markup ?? message2.body ?? ""}`
9788
+ );
9789
+ }
9659
9790
  });
9660
9791
  }
9661
- async function pmRemove(ctx, entity, id) {
9662
- await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id)}`);
9663
- ctx.out.log(`deleted ${entity} ${id}`);
9792
+
9793
+ // src/pm-watch-types.ts
9794
+ var PmWatchCheckpointError = class extends Error {
9795
+ constructor(cursor, streamId) {
9796
+ super("PM work cursor requires a new checkpoint");
9797
+ this.cursor = cursor;
9798
+ this.streamId = streamId;
9799
+ this.name = "PmWatchCheckpointError";
9800
+ }
9801
+ cursor;
9802
+ streamId;
9803
+ code = "checkpoint_required";
9804
+ };
9805
+ var PmWatchRequestError = class extends Error {
9806
+ constructor(message2, retryable, status) {
9807
+ super(message2);
9808
+ this.retryable = retryable;
9809
+ this.status = status;
9810
+ this.name = "PmWatchRequestError";
9811
+ }
9812
+ retryable;
9813
+ status;
9814
+ };
9815
+
9816
+ // src/pm-watch.ts
9817
+ var DEFAULT_INTERVAL_MS2 = 15e3;
9818
+ var MAX_CONSECUTIVE_FAILURES2 = 5;
9819
+ var MAX_BACKOFF_MS2 = 3e4;
9820
+ function positiveNumber(parsed, flag, fallback) {
9821
+ const raw = stringOpt(parsed.options[flag]);
9822
+ const value2 = raw == null ? NaN : Number(raw);
9823
+ return Number.isFinite(value2) && value2 > 0 ? value2 : fallback;
9824
+ }
9825
+ function jsonl2(ctx, parsed, value2) {
9826
+ if (parsed.options.jsonl === true) ctx.out.log(JSON.stringify({ v: 1, ...value2 }));
9827
+ }
9828
+ async function page(ctx, appId, cursor) {
9829
+ const params = new URLSearchParams({ app: appId });
9830
+ if (cursor) params.set("cursor", cursor);
9831
+ let response2;
9832
+ try {
9833
+ response2 = await ctx.doFetch(`${ctx.platformUrl}/registry/pm/work/watch?${params}`, {
9834
+ headers: { authorization: `Bearer ${ctx.token}` }
9835
+ });
9836
+ } catch (error) {
9837
+ throw new PmWatchRequestError(
9838
+ `pm watch request failed: ${error instanceof Error ? error.message : String(error)}`,
9839
+ true
9840
+ );
9841
+ }
9842
+ const data = await response2.json().catch(() => ({}));
9843
+ if (response2.status === 409 && data.code === "checkpoint_required") {
9844
+ throw new PmWatchCheckpointError(data.cursor, data.streamId);
9845
+ }
9846
+ if (!response2.ok) {
9847
+ throw new PmWatchRequestError(
9848
+ `pm watch failed: ${data.error ?? `registry returned ${response2.status}`}`,
9849
+ response2.status === 429 || response2.status >= 500,
9850
+ response2.status
9851
+ );
9852
+ }
9853
+ return data;
9854
+ }
9855
+ function recordState(record10) {
9856
+ if (record10.column) return record10.column === "todo" ? "ready" : record10.column;
9857
+ return String(record10.status ?? "");
9858
+ }
9859
+ function eventRecord(event) {
9860
+ return event.payload.payload;
9861
+ }
9862
+ function eventLabel(event) {
9863
+ const record10 = eventRecord(event);
9864
+ if (record10) return String(record10.title ?? event.payload.entityId);
9865
+ const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
9866
+ return body || event.payload.entityId;
9867
+ }
9868
+ function report3(ctx, parsed, result) {
9869
+ if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
9870
+ else if (parsed.options.jsonl !== true && result.found) {
9871
+ for (const event of result.events ?? []) {
9872
+ const record10 = eventRecord(event);
9873
+ const state2 = record10 ? recordState(record10) : "comment";
9874
+ ctx.out.log(
9875
+ `${event.id} ${event.type} ${state2}${record10?.revision ? `; r${record10.revision}` : ""} ${eventLabel(event)}`
9876
+ );
9877
+ }
9878
+ }
9879
+ return result;
9880
+ }
9881
+ async function pmWatch(ctx, parsed) {
9882
+ if (ctx.json && parsed.options.jsonl === true) {
9883
+ throw new Error("--json and --jsonl cannot be combined");
9884
+ }
9885
+ const appId = stringOpt(parsed.options.app) ?? ctx.appId;
9886
+ if (!appId) throw new Error("pm watch needs --app <appId>");
9887
+ const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
9888
+ const now = ctx.now ?? Date.now;
9889
+ const intervalMs = (positiveNumber(parsed, "interval", DEFAULT_INTERVAL_MS2 / 1e3) ?? DEFAULT_INTERVAL_MS2 / 1e3) * 1e3;
9890
+ const timeoutSeconds = positiveNumber(parsed, "timeout");
9891
+ const deadline = timeoutSeconds === void 0 ? void 0 : now() + timeoutSeconds * 1e3;
9892
+ const entity = stringOpt(parsed.options.entity);
9893
+ const action2 = stringOpt(parsed.options.action);
9894
+ const wantedState = stringOpt(parsed.options.state)?.toLowerCase();
9895
+ const by = stringOpt(parsed.options.by);
9896
+ const self = stringOpt(parsed.options.self);
9897
+ let cursor = stringOpt(parsed.options.cursor);
9898
+ let firstSuccess = true;
9899
+ let consecutiveFailures = 0;
9900
+ for (; ; ) {
9901
+ let current;
9902
+ try {
9903
+ current = await page(ctx, appId, cursor);
9904
+ consecutiveFailures = 0;
9905
+ } catch (error) {
9906
+ if (error instanceof PmWatchCheckpointError) {
9907
+ jsonl2(ctx, parsed, {
9908
+ type: "status",
9909
+ state: "checkpoint_required",
9910
+ retryable: false,
9911
+ ...error.cursor ? { cursor: error.cursor } : {},
9912
+ ...error.streamId ? { streamId: error.streamId } : {}
9913
+ });
9914
+ throw error;
9915
+ }
9916
+ if (!(error instanceof PmWatchRequestError) || !error.retryable) throw error;
9917
+ consecutiveFailures++;
9918
+ jsonl2(ctx, parsed, {
9919
+ type: "status",
9920
+ state: "degraded",
9921
+ retryable: true,
9922
+ attempt: consecutiveFailures,
9923
+ ...cursor ? { cursor } : {},
9924
+ ...error.status ? { status: error.status } : {}
9925
+ });
9926
+ if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2) throw error;
9927
+ if (deadline !== void 0 && now() >= deadline) {
9928
+ return report3(ctx, parsed, { found: false, cursor: cursor ?? "" });
9929
+ }
9930
+ const backoff = Math.min(
9931
+ MAX_BACKOFF_MS2,
9932
+ Math.min(intervalMs, 1e3) * 2 ** (consecutiveFailures - 1)
9933
+ );
9934
+ await sleep(deadline === void 0 ? backoff : Math.min(backoff, Math.max(0, deadline - now())));
9935
+ continue;
9936
+ }
9937
+ cursor = current.cursor;
9938
+ const baseline = firstSuccess && current.events.length === 0;
9939
+ if (baseline) {
9940
+ jsonl2(ctx, parsed, {
9941
+ type: "checkpoint",
9942
+ streamId: current.streamId,
9943
+ cursor,
9944
+ serverTime: current.serverTime
9945
+ });
9946
+ }
9947
+ firstSuccess = false;
9948
+ const matching = current.events.filter((event) => {
9949
+ const record10 = eventRecord(event);
9950
+ const state2 = record10 ? recordState(record10).toLowerCase() : "";
9951
+ return (!entity || event.payload.entityKind === entity) && (!action2 || event.payload.action === action2) && (!wantedState || state2 === wantedState || wantedState === "todo" && state2 === "ready") && (!by || event.actor.id === by) && (!self || event.actor.id !== self);
9952
+ });
9953
+ for (const event of matching) {
9954
+ jsonl2(ctx, parsed, {
9955
+ type: "event",
9956
+ streamId: current.streamId,
9957
+ eventId: event.id,
9958
+ cursor: event.cursor,
9959
+ event
9960
+ });
9961
+ }
9962
+ if (matching.length > 0) {
9963
+ jsonl2(ctx, parsed, {
9964
+ type: "checkpoint",
9965
+ streamId: current.streamId,
9966
+ cursor,
9967
+ serverTime: current.serverTime
9968
+ });
9969
+ return report3(ctx, parsed, { found: true, cursor, events: matching });
9970
+ }
9971
+ if (current.events.length > 0) {
9972
+ jsonl2(ctx, parsed, {
9973
+ type: "checkpoint",
9974
+ streamId: current.streamId,
9975
+ cursor,
9976
+ serverTime: current.serverTime
9977
+ });
9978
+ } else if (!baseline) {
9979
+ jsonl2(ctx, parsed, {
9980
+ type: "heartbeat",
9981
+ streamId: current.streamId,
9982
+ cursor,
9983
+ serverTime: current.serverTime
9984
+ });
9985
+ }
9986
+ if (current.hasMore) continue;
9987
+ if (deadline !== void 0 && now() >= deadline) {
9988
+ return report3(ctx, parsed, { found: false, cursor });
9989
+ }
9990
+ await sleep(
9991
+ deadline === void 0 ? intervalMs : Math.min(intervalMs, Math.max(0, deadline - now()))
9992
+ );
9993
+ }
9664
9994
  }
9665
9995
 
9666
9996
  // src/pm-command.ts
@@ -9681,7 +10011,11 @@ var ACTION_OPTIONS = {
9681
10011
  done: ["mutation-id"],
9682
10012
  comment: ["body", "mutation-id"],
9683
10013
  comments: [],
9684
- rm: []
10014
+ rm: [],
10015
+ ready: ["goal", "alignment-decision", "execution", "description", "desc", "body", "acceptance", "expected-revision", "mutation-id"],
10016
+ claim: ["expected-revision", "mutation-id"],
10017
+ release: ["expected-revision", "mutation-id"],
10018
+ ref: []
9685
10019
  };
9686
10020
  var ENTITY_OPTIONS = {
9687
10021
  goal: {
@@ -9692,8 +10026,8 @@ var ENTITY_OPTIONS = {
9692
10026
  },
9693
10027
  task: {
9694
10028
  list: ["column", "goal", "assignee"],
9695
- add: ["column", "goal", "assignee", "due", "description", "desc", "body"],
9696
- set: ["title", "column", "rank", "goal", "assignee", "due", "description", "desc", "body"],
10029
+ add: ["column", "goal", "alignment-decision", "execution", "assignee", "due", "description", "desc", "body", "acceptance"],
10030
+ set: ["title", "column", "rank", "goal", "alignment-decision", "execution", "assignee", "due", "description", "desc", "body", "acceptance", "expected-revision"],
9697
10031
  done: []
9698
10032
  },
9699
10033
  decision: {
@@ -9744,11 +10078,33 @@ async function buildContext2(parsed, deps) {
9744
10078
  json: parsed.options.json === true,
9745
10079
  // Preserve PM's existing cross-project default when a config is present;
9746
10080
  // only an explicit remote-agent environment or profile selects an app.
9747
- appId: context.app.source === "environment" || context.app.source === "profile" ? context.app.value ?? void 0 : void 0
10081
+ appId: context.app.source === "environment" || context.app.source === "profile" ? context.app.value ?? void 0 : void 0,
10082
+ ...deps.sleep ? { sleep: deps.sleep } : {},
10083
+ ...deps.now ? { now: deps.now } : {}
9748
10084
  };
9749
10085
  }
9750
10086
  async function pmCommand(parsed, deps = {}) {
9751
10087
  const word = parsed.positionals[1] ?? "";
10088
+ if (word === "next") {
10089
+ assertArgs(parsed, [...COMMON_OPTIONS, "app"], 2);
10090
+ return pmNext(await buildContext2(parsed, deps), parsed);
10091
+ }
10092
+ if (word === "watch") {
10093
+ assertArgs(parsed, [
10094
+ ...COMMON_OPTIONS,
10095
+ "app",
10096
+ "cursor",
10097
+ "interval",
10098
+ "timeout",
10099
+ "jsonl",
10100
+ "entity",
10101
+ "action",
10102
+ "state",
10103
+ "by",
10104
+ "self"
10105
+ ], 2);
10106
+ return pmWatch(await buildContext2(parsed, deps), parsed).then(() => void 0);
10107
+ }
9752
10108
  if (word === "handoff") {
9753
10109
  assertArgs(parsed, [...COMMON_OPTIONS, "app"], 2);
9754
10110
  return pmHandoff(await buildContext2(parsed, deps), parsed);
@@ -9759,6 +10115,9 @@ async function pmCommand(parsed, deps = {}) {
9759
10115
  const action2 = canonicalAction(requestedAction);
9760
10116
  if (!action2) throw new Error(`unknown pm action "${requestedAction}". Try list|add|get|set|done|comment|comments|rm.`);
9761
10117
  assertArgs(parsed, allowedOptions(entity, action2), 4);
10118
+ if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
10119
+ throw new Error(`pm ${action2} is only valid for tasks`);
10120
+ }
9762
10121
  const ctx = await buildContext2(parsed, deps);
9763
10122
  const id = parsed.positionals[3];
9764
10123
  switch (action2) {
@@ -9778,6 +10137,12 @@ async function pmCommand(parsed, deps = {}) {
9778
10137
  return pmComments(ctx, entity, requireId2(id, action2));
9779
10138
  case "rm":
9780
10139
  return pmRemove(ctx, entity, requireId2(id, action2));
10140
+ case "ref":
10141
+ return pmReference(ctx, entity, requireId2(id, action2));
10142
+ case "ready":
10143
+ case "claim":
10144
+ case "release":
10145
+ return pmTaskLifecycle(ctx, requireId2(id, action2), action2, parsed);
9781
10146
  }
9782
10147
  }
9783
10148
 
@@ -10667,12 +11032,23 @@ var PM_ACTIONS = {
10667
11032
  done: {},
10668
11033
  comment: {},
10669
11034
  comments: {},
11035
+ ref: {},
10670
11036
  rm: {},
10671
11037
  delete: {}
10672
11038
  };
10673
- var PM_ENTITIES = Object.fromEntries(
10674
- ["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
10675
- );
11039
+ var PM_TASK_ACTIONS = {
11040
+ ...PM_ACTIONS,
11041
+ ready: {},
11042
+ claim: {},
11043
+ release: {}
11044
+ };
11045
+ var PM_ENTITIES = {
11046
+ ...Object.fromEntries(
11047
+ ["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
11048
+ ),
11049
+ task: PM_TASK_ACTIONS,
11050
+ kanban: PM_TASK_ACTIONS
11051
+ };
10676
11052
  var COMMAND_SURFACE = {
10677
11053
  agent: { jobs: {}, retry: {} },
10678
11054
  admin: {
@@ -10725,7 +11101,9 @@ var COMMAND_SURFACE = {
10725
11101
  },
10726
11102
  pm: {
10727
11103
  ...PM_ENTITIES,
10728
- handoff: {}
11104
+ handoff: {},
11105
+ next: {},
11106
+ watch: {}
10729
11107
  },
10730
11108
  provision: {},
10731
11109
  runbook: {
@@ -10889,12 +11267,12 @@ async function call(ctx, method, path, body) {
10889
11267
  throw new Error(message2);
10890
11268
  }
10891
11269
  async function bySlug(ctx, slug) {
10892
- const page = await call(
11270
+ const page2 = await call(
10893
11271
  ctx,
10894
11272
  "GET",
10895
11273
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
10896
11274
  );
10897
- const found = page.records[0];
11275
+ const found = page2.records[0];
10898
11276
  if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
10899
11277
  return found;
10900
11278
  }
@@ -10908,11 +11286,11 @@ async function runbookList(ctx, all, query) {
10908
11286
  const params = new URLSearchParams();
10909
11287
  if (!all) params.set("app", ctx.appId);
10910
11288
  if (query) params.set("q", query);
10911
- const page = await call(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
10912
- if (ctx.json) return ctx.out.log(JSON.stringify(page, null, 2));
10913
- if (!page.records.length) return ctx.out.log("(no runbooks)");
11289
+ const page2 = await call(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
11290
+ if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
11291
+ if (!page2.records.length) return ctx.out.log("(no runbooks)");
10914
11292
  ctx.out.log(["SLUG", "STATUS", "V", "SCOPE", "UPDATED", "TITLE"].join(" "));
10915
- for (const r of page.records)
11293
+ for (const r of page2.records)
10916
11294
  ctx.out.log(
10917
11295
  [r.slug, r.status, `v${r.version}`, r.appId === PLATFORM_SCOPE ? "platform" : r.appId, stamp(r.updatedAt), r.title].join(" ")
10918
11296
  );
@@ -10967,12 +11345,12 @@ async function runbookVisibility(ctx, slug, visibility) {
10967
11345
  }
10968
11346
  async function runbookHistory(ctx, slug) {
10969
11347
  const runbook = await bySlug(ctx, slug);
10970
- const page = await call(ctx, "GET", `/runbook/${encodeURIComponent(runbook.id)}/revisions`);
10971
- if (ctx.json) return ctx.out.log(JSON.stringify(page, null, 2));
11348
+ const page2 = await call(ctx, "GET", `/runbook/${encodeURIComponent(runbook.id)}/revisions`);
11349
+ if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
10972
11350
  ctx.out.log(`${slug} is at v${runbook.version}`);
10973
- if (!page.records.length) return ctx.out.log("(no earlier versions)");
11351
+ if (!page2.records.length) return ctx.out.log("(no earlier versions)");
10974
11352
  ctx.out.log(["V", "WHEN", "BY", "NOTE"].join(" "));
10975
- for (const r of page.records)
11353
+ for (const r of page2.records)
10976
11354
  ctx.out.log([`v${r.version}`, stamp(r.createdAt), r.ownerEmail ?? "", r.note ?? ""].join(" "));
10977
11355
  }
10978
11356
  async function runbookRevert(ctx, slug, version) {
@@ -11047,12 +11425,12 @@ ${counts.created} created, ${counts.updated} updated, ${counts.unchanged} unchan
11047
11425
  );
11048
11426
  }
11049
11427
  async function upsert(ctx, r, visibility) {
11050
- const page = await call(
11428
+ const page2 = await call(
11051
11429
  ctx,
11052
11430
  "GET",
11053
11431
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(r.slug)}&limit=1`
11054
11432
  );
11055
- const found = page.records[0];
11433
+ const found = page2.records[0];
11056
11434
  if (!found) {
11057
11435
  await call(ctx, "POST", "/runbook", {
11058
11436
  appId: ctx.appId,
@@ -11308,7 +11686,7 @@ async function assessImpact(ctx, surfaces, all, limit) {
11308
11686
  return out;
11309
11687
  }
11310
11688
  var editHint = (slug, appId) => `odla-ai runbook edit ${slug}${appId === PLATFORM_SCOPE ? "" : ` --app ${appId}`} --note "<what changed>"`;
11311
- function report3(ctx, impacts) {
11689
+ function report4(ctx, impacts) {
11312
11690
  const covered = impacts.filter((i) => i.runbooks.length);
11313
11691
  ctx.out.log(
11314
11692
  `${impacts.length} changed surface${impacts.length === 1 ? "" : "s"}; ${covered.length} covered by a runbook. Reread each one and fix any step this change made wrong.`
@@ -11346,7 +11724,7 @@ async function runbookImpact(ctx, options, deps = {}) {
11346
11724
  }
11347
11725
  const impacts = await assessImpact(ctx, surfaces, options.all, options.limit ?? 4);
11348
11726
  if (ctx.json) return ctx.out.log(JSON.stringify({ base: options.base, impacts }, null, 2));
11349
- report3(ctx, impacts);
11727
+ report4(ctx, impacts);
11350
11728
  }
11351
11729
 
11352
11730
  // src/runbook-lint.ts
@@ -11384,17 +11762,17 @@ function lintRunbook(runbook, installed) {
11384
11762
  async function runbookLint(ctx, all) {
11385
11763
  const params = new URLSearchParams();
11386
11764
  if (!all) params.set("app", ctx.appId);
11387
- const page = await call(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
11765
+ const page2 = await call(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
11388
11766
  const installed = { "@odla-ai/cli": cliVersion() };
11389
- const findings = page.records.flatMap((runbook) => lintRunbook(runbook, installed));
11390
- if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page.records.length, findings }, null, 2));
11391
- if (!page.records.length) return ctx.out.log("(no runbooks in scope)");
11767
+ const findings = page2.records.flatMap((runbook) => lintRunbook(runbook, installed));
11768
+ if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page2.records.length, findings }, null, 2));
11769
+ if (!page2.records.length) return ctx.out.log("(no runbooks in scope)");
11392
11770
  if (!findings.length) {
11393
11771
  return ctx.out.log(
11394
- `${page.records.length} runbook${page.records.length === 1 ? "" : "s"} checked; every command they name is real for @odla-ai/cli ${cliVersion()}.`
11772
+ `${page2.records.length} runbook${page2.records.length === 1 ? "" : "s"} checked; every command they name is real for @odla-ai/cli ${cliVersion()}.`
11395
11773
  );
11396
11774
  }
11397
- ctx.out.log(`${findings.length} finding${findings.length === 1 ? "" : "s"} across ${page.records.length} runbooks:`);
11775
+ ctx.out.log(`${findings.length} finding${findings.length === 1 ? "" : "s"} across ${page2.records.length} runbooks:`);
11398
11776
  for (const finding of findings) {
11399
11777
  const scope = finding.appId === PLATFORM_SCOPE ? "" : ` --app ${finding.appId}`;
11400
11778
  ctx.out.log("");
@@ -11457,12 +11835,12 @@ async function runbookAsk(ctx, question, all) {
11457
11835
  ctx.out.log(JSDOC_POINTER);
11458
11836
  }
11459
11837
  async function runbookComment(ctx, slug, body) {
11460
- const page = await call(
11838
+ const page2 = await call(
11461
11839
  ctx,
11462
11840
  "GET",
11463
11841
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
11464
11842
  );
11465
- const found = page.records[0];
11843
+ const found = page2.records[0];
11466
11844
  if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
11467
11845
  await call(ctx, "POST", `/runbook/${encodeURIComponent(found.id)}/comments`, { body });
11468
11846
  ctx.out.log(`commented on ${slug} (v${found.version})`);
@@ -11514,12 +11892,12 @@ var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
11514
11892
 
11515
11893
  // src/runbook-edit-flow.ts
11516
11894
  async function editRunbook(ctx, slug, deps = {}) {
11517
- const page = await call(
11895
+ const page2 = await call(
11518
11896
  ctx,
11519
11897
  "GET",
11520
11898
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
11521
11899
  );
11522
- const found = page.records[0];
11900
+ const found = page2.records[0];
11523
11901
  if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
11524
11902
  ctx.out.log(`opening ${slug} v${found.version} in your editor\u2026`);
11525
11903
  const body = await editText(found.body, slug, deps);
@@ -11940,31 +12318,31 @@ function printHostedJob(out, job, platform, appId) {
11940
12318
  url.searchParams.set("job", job.jobId);
11941
12319
  out.log(` Studio: ${url.toString()}`);
11942
12320
  }
11943
- function printHostedReport(out, report4) {
11944
- out.log(`security report ${report4.jobId}: ${report4.repository}@${report4.revision}`);
11945
- out.log(` coverage: ${report4.coverageStatus} cells=${report4.metrics.coverageCells} shallow=${report4.metrics.shallowCells} blocked=${report4.metrics.blockedCells} unscheduled=${report4.metrics.unscheduledCells} budget_exhausted=${report4.metrics.budgetExhaustedCells}`);
11946
- out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates} rejected=${report4.metrics.rejected}`);
11947
- out.log(` discovery: ${report4.provenance.discovery?.provider ?? "unknown"}/${report4.provenance.discovery?.model ?? "unknown"}`);
11948
- out.log(` validation: ${report4.provenance.validation?.provider ?? "unknown"}/${report4.provenance.validation?.model ?? "unknown"} independent=${String(report4.provenance.independentValidation)}`);
11949
- for (const finding of report4.findings) {
12321
+ function printHostedReport(out, report5) {
12322
+ out.log(`security report ${report5.jobId}: ${report5.repository}@${report5.revision}`);
12323
+ out.log(` coverage: ${report5.coverageStatus} cells=${report5.metrics.coverageCells} shallow=${report5.metrics.shallowCells} blocked=${report5.metrics.blockedCells} unscheduled=${report5.metrics.unscheduledCells} budget_exhausted=${report5.metrics.budgetExhaustedCells}`);
12324
+ out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates} rejected=${report5.metrics.rejected}`);
12325
+ out.log(` discovery: ${report5.provenance.discovery?.provider ?? "unknown"}/${report5.provenance.discovery?.model ?? "unknown"}`);
12326
+ out.log(` validation: ${report5.provenance.validation?.provider ?? "unknown"}/${report5.provenance.validation?.model ?? "unknown"} independent=${String(report5.provenance.independentValidation)}`);
12327
+ for (const finding of report5.findings) {
11950
12328
  const location = finding.locations[0];
11951
12329
  out.log(` [${finding.severity}] ${finding.title}${location ? ` (${location.path}:${location.line})` : ""} \xB7 ${finding.disposition}`);
11952
12330
  }
11953
- for (const limitation of report4.limitations) out.log(` limitation: ${limitation}`);
12331
+ for (const limitation of report5.limitations) out.log(` limitation: ${limitation}`);
11954
12332
  }
11955
- function enforceHostedReportGate(report4, parsed, out, emitSuccess) {
12333
+ function enforceHostedReportGate(report5, parsed, out, emitSuccess) {
11956
12334
  const failOn = hostedSeverity(stringOpt(parsed.options["fail-on"]) ?? "high", "--fail-on");
11957
12335
  const candidateValue = parsed.options["fail-on-candidates"];
11958
12336
  const failOnCandidates = candidateValue === false ? void 0 : hostedSeverity(stringOpt(candidateValue) ?? "critical", "--fail-on-candidates");
11959
12337
  const atOrAbove = (severity, threshold) => HOSTED_SEVERITIES.indexOf(severity) >= HOSTED_SEVERITIES.indexOf(threshold);
11960
- const confirmed = report4.findings.filter((finding) => finding.disposition === "confirmed" && atOrAbove(finding.severity, failOn));
11961
- const leads = failOnCandidates ? report4.findings.filter((finding) => finding.disposition !== "confirmed" && atOrAbove(finding.severity, failOnCandidates)) : [];
11962
- const incomplete = report4.coverageStatus !== "complete" && parsed.options["allow-incomplete"] !== true;
12338
+ const confirmed = report5.findings.filter((finding) => finding.disposition === "confirmed" && atOrAbove(finding.severity, failOn));
12339
+ const leads = failOnCandidates ? report5.findings.filter((finding) => finding.disposition !== "confirmed" && atOrAbove(finding.severity, failOnCandidates)) : [];
12340
+ const incomplete = report5.coverageStatus !== "complete" && parsed.options["allow-incomplete"] !== true;
11963
12341
  if (confirmed.length || leads.length || incomplete) {
11964
- throw new Error(`hosted security gate failed: ${confirmed.length} confirmed >= ${failOn}; ${leads.length} leads >= ${failOnCandidates ?? "disabled"}${incomplete ? `; coverage ${report4.coverageStatus}` : ""}`);
12342
+ throw new Error(`hosted security gate failed: ${confirmed.length} confirmed >= ${failOn}; ${leads.length} leads >= ${failOnCandidates ?? "disabled"}${incomplete ? `; coverage ${report5.coverageStatus}` : ""}`);
11965
12343
  }
11966
12344
  if (emitSuccess) {
11967
- out.log(`security gate passed: 0 confirmed >= ${failOn}; 0 leads >= ${failOnCandidates ?? "disabled"}; coverage ${report4.coverageStatus}. This is not proof that the application is secure.`);
12345
+ out.log(`security gate passed: 0 confirmed >= ${failOn}; 0 leads >= ${failOnCandidates ?? "disabled"}; coverage ${report5.coverageStatus}. This is not proof that the application is secure.`);
11968
12346
  }
11969
12347
  }
11970
12348
  function printHostedSecurityPlanRoute(out, label, route2) {
@@ -12047,17 +12425,17 @@ async function runHostedSecurity(options) {
12047
12425
  allowNetwork: false
12048
12426
  }
12049
12427
  });
12050
- const report4 = await harness.run(snapshot, { runId: hosted.run.runId, signal: options.signal });
12051
- await (0, import_node3.writeSecurityArtifacts)(output, report4);
12052
- const reportDigest = await (0, import_security.securityFingerprint)(report4);
12428
+ const report5 = await harness.run(snapshot, { runId: hosted.run.runId, signal: options.signal });
12429
+ await (0, import_node3.writeSecurityArtifacts)(output, report5);
12430
+ const reportDigest = await (0, import_security.securityFingerprint)(report5);
12053
12431
  await hosted.complete({
12054
12432
  reportDigest,
12055
- coverageStatus: report4.coverageStatus,
12056
- confirmed: report4.metrics.confirmed,
12057
- candidates: report4.metrics.candidates
12433
+ coverageStatus: report5.coverageStatus,
12434
+ confirmed: report5.metrics.confirmed,
12435
+ candidates: report5.metrics.candidates
12058
12436
  }, { signal: options.signal });
12059
- printSummary(options.stdout ?? console, appId, env, hosted.run, report4, output);
12060
- return Object.freeze({ report: report4, run: hosted.run, output });
12437
+ printSummary(options.stdout ?? console, appId, env, hosted.run, report5, output);
12438
+ return Object.freeze({ report: report5, run: hosted.run, output });
12061
12439
  }
12062
12440
  function selectEnv(requested, declared, configPath, rootDir) {
12063
12441
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
@@ -12083,14 +12461,14 @@ function profileFor(name, maxHuntTasks) {
12083
12461
  if (!Number.isSafeInteger(maxHuntTasks) || maxHuntTasks < 1) throw new Error("maxHuntTasks must be a positive integer");
12084
12462
  return { ...profile, maxHuntTasks };
12085
12463
  }
12086
- function printSummary(out, appId, env, run, report4, output) {
12087
- const complete = report4.coverage.filter((cell) => cell.state === "complete").length;
12464
+ function printSummary(out, appId, env, run, report5, output) {
12465
+ const complete = report5.coverage.filter((cell) => cell.state === "complete").length;
12088
12466
  out.log(`security: ${appId}/${env} run=${run.runId} profile=${run.profileVersion}`);
12089
12467
  out.log(` discovery: ${run.discovery.identity.provider}/${run.discovery.identity.model}`);
12090
12468
  out.log(` validation: ${run.validation.identity.provider}/${run.validation.identity.model}`);
12091
- out.log(` coverage: ${report4.coverageStatus} ${complete}/${report4.coverage.length} blocked=${report4.metrics.blockedCells} shallow=${report4.metrics.shallowCells} unscheduled=${report4.metrics.unscheduledCells} budget_exhausted=${report4.metrics.budgetExhaustedCells}`);
12092
- if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
12093
- out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates}`);
12469
+ out.log(` coverage: ${report5.coverageStatus} ${complete}/${report5.coverage.length} blocked=${report5.metrics.blockedCells} shallow=${report5.metrics.shallowCells} unscheduled=${report5.metrics.unscheduledCells} budget_exhausted=${report5.metrics.budgetExhaustedCells}`);
12470
+ if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
12471
+ out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
12094
12472
  out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
12095
12473
  }
12096
12474
  function formatBudget(usage) {
@@ -12326,13 +12704,13 @@ async function runSourceSecurityCommand(parsed, dependencies, sourceId) {
12326
12704
  }
12327
12705
  throw new Error(`hosted security job ${result.jobId} ended ${result.status}${result.errorCode ? `: ${result.errorCode}` : ""}`);
12328
12706
  }
12329
- const report4 = await getHostedSecurityReport({ ...context, jobId: result.jobId });
12707
+ const report5 = await getHostedSecurityReport({ ...context, jobId: result.jobId });
12330
12708
  if (parsed.options.json === true) {
12331
- context.stdout.log(JSON.stringify({ plan, intent: preview.intent, job: result, report: report4 }, null, 2));
12709
+ context.stdout.log(JSON.stringify({ plan, intent: preview.intent, job: result, report: report5 }, null, 2));
12332
12710
  } else {
12333
- printHostedReport(context.stdout, report4);
12711
+ printHostedReport(context.stdout, report5);
12334
12712
  }
12335
- enforceHostedReportGate(report4, parsed, context.stdout, parsed.options.json !== true);
12713
+ enforceHostedReportGate(report5, parsed, context.stdout, parsed.options.json !== true);
12336
12714
  }
12337
12715
  async function runLocalSecurityCommand(parsed, dependencies) {
12338
12716
  if (parsed.options.source === true) {
@@ -12399,13 +12777,13 @@ async function runLocalSecurityCommand(parsed, dependencies) {
12399
12777
  });
12400
12778
  enforceLocalGate(result.report, parsed);
12401
12779
  }
12402
- function enforceLocalGate(report4, parsed) {
12780
+ function enforceLocalGate(report5, parsed) {
12403
12781
  const failOn = severityOpt(stringOpt(parsed.options["fail-on"]) ?? "high", "--fail-on");
12404
12782
  const candidateValue = parsed.options["fail-on-candidates"];
12405
12783
  const failOnCandidates = candidateValue === false ? void 0 : severityOpt(stringOpt(candidateValue) ?? "critical", "--fail-on-candidates");
12406
- const confirmed = (0, import_security2.findingsAtOrAbove)(report4, failOn);
12407
- const leads = failOnCandidates ? (0, import_security2.findingsAtOrAbove)(report4, failOnCandidates, true).filter((finding) => finding.disposition !== "confirmed") : [];
12408
- const incomplete = report4.coverageStatus === "incomplete" && parsed.options["allow-incomplete"] !== true;
12784
+ const confirmed = (0, import_security2.findingsAtOrAbove)(report5, failOn);
12785
+ const leads = failOnCandidates ? (0, import_security2.findingsAtOrAbove)(report5, failOnCandidates, true).filter((finding) => finding.disposition !== "confirmed") : [];
12786
+ const incomplete = report5.coverageStatus === "incomplete" && parsed.options["allow-incomplete"] !== true;
12409
12787
  if (confirmed.length || leads.length || incomplete) {
12410
12788
  throw new Error(`hosted security gate failed: ${confirmed.length} confirmed >= ${failOn}; ${leads.length} leads >= ${failOnCandidates ?? "disabled"}${incomplete ? "; coverage incomplete" : ""}`);
12411
12789
  }
@@ -12444,9 +12822,9 @@ async function securityCommand(parsed, dependencies) {
12444
12822
  assertArgs(parsed, ["config", "env", "platform", "email", "open", "json"], 3);
12445
12823
  const jobId = requiredSecurityPositional(parsed, 2, "job id");
12446
12824
  const context = await hostedSecurityContext(parsed, dependencies);
12447
- const report4 = await getHostedSecurityReport({ ...context, jobId });
12448
- if (parsed.options.json === true) context.stdout.log(JSON.stringify(report4, null, 2));
12449
- else printHostedReport(context.stdout, report4);
12825
+ const report5 = await getHostedSecurityReport({ ...context, jobId });
12826
+ if (parsed.options.json === true) context.stdout.log(JSON.stringify(report5, null, 2));
12827
+ else printHostedReport(context.stdout, report5);
12450
12828
  return;
12451
12829
  }
12452
12830
  if (sub !== "run") {