@workser/cli 0.6.7 → 0.6.9

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/index.js CHANGED
@@ -4636,6 +4636,47 @@ workser audio understand "transcribe this" --file ./voicemail.m4a -t transcribe
4636
4636
  - **This is billed to the project's organization**, same as image generation \u2014 it's
4637
4637
  a real provider call, not free introspection. Don't loop it over every file in a
4638
4638
  folder "just in case"; use it when you actually need to know what's in one.
4639
+ `
4640
+ },
4641
+ {
4642
+ topic: "key",
4643
+ title: "Managed keys \u2014 list and rotate this app's Workser-issued credentials",
4644
+ summary: "See this app's AI Gateway and auth keys (redacted), and rotate one without opening Orbit.",
4645
+ commands: ["key"],
4646
+ source: "skills/workser/reference/key.md",
4647
+ body: `# Managed keys \u2014 list and rotate this app's Workser-issued credentials
4648
+
4649
+ \`\`\`
4650
+ workser key list [--app <webAppId>]
4651
+ workser key rotate <key> [--app <webAppId>] [--env production|preview]
4652
+ \`\`\`
4653
+
4654
+ App-scoped, not project-scoped: run from the app's own folder and \`--app\` is
4655
+ picked up automatically; from anywhere else, pass \`--app <webAppId>\` \u2014 a
4656
+ mistake here is the wrong app's key, so there is no "primary app" default the
4657
+ way \`env\` has one.
4658
+
4659
+ ## What's rotatable
4660
+
4661
+ Only credentials with a real rotation handler:
4662
+
4663
+ - \`AI_GATEWAY_API_KEY\` \u2014 the deployed agent's key into the Workser AI
4664
+ Gateway. Split by environment (\`--env production\` or \`--env preview\`
4665
+ required), so rotating one never touches the other.
4666
+ - \`BETTER_AUTH_SECRET\` \u2014 a single value, no \`--env\` needed.
4667
+
4668
+ Every other Workser-managed value (\`DATABASE_URL\`, the internal service
4669
+ keys) has no rotation handler yet \u2014 each belongs to a different subsystem
4670
+ with its own cutover semantics, and \`key rotate\` refuses rather than fake
4671
+ one.
4672
+
4673
+ ## After rotating
4674
+
4675
+ The old value stops authorizing immediately, server-side. An already-running
4676
+ deployment keeps using whatever it was built with until it's redeployed \u2014
4677
+ run \`workser deploy\` (\`--prod\` for the production key) to pick up the new
4678
+ value. \`key rotate\` prints the new secret exactly once; it is never shown
4679
+ again or stored anywhere in the clear.
4639
4680
  `
4640
4681
  },
4641
4682
  {
@@ -4865,6 +4906,17 @@ workser doc create "Checkout \u2014 implementation plan" --markdown "$(cat plan.
4865
4906
  workser decision create "Carts live server-side" --context "\u2026" --decision "\u2026" --json
4866
4907
  \`\`\`
4867
4908
 
4909
+ **A wrong row is edited, not replaced.** If a subtask's title, teammate, note or
4910
+ scope is wrong after you created it, fix that same row:
4911
+
4912
+ \`\`\`bash
4913
+ workser task subtask update <id> --title "\u2026" --role api --note "\u2026" --scope "src/api"
4914
+ \`\`\`
4915
+
4916
+ Never tell the user a subtask is locked, and never file a second one alongside
4917
+ the wrong one \u2014 a plan with a duplicate phase in it is a plan nobody can read
4918
+ the progress of.
4919
+
4868
4920
  **Don't pass \`--work-item\` for a multi-phase plan.** A linked document renders on
4869
4921
  its card and is *hidden* from the Docs panel; a plan spanning three phases
4870
4922
  belongs to the project, not to phase 1.
@@ -5511,6 +5563,10 @@ function requireProject(ctx) {
5511
5563
  }
5512
5564
  return ctx.projectId;
5513
5565
  }
5566
+ function appTarget(ctx, explicit) {
5567
+ const said = typeof explicit === "string" ? explicit.trim() : "";
5568
+ return said || ctx.appId || void 0;
5569
+ }
5514
5570
 
5515
5571
  // src/run.ts
5516
5572
  function action(fn) {
@@ -6791,8 +6847,71 @@ function formatEnvValue(value) {
6791
6847
  return /[\s"'#]/.test(value) ? JSON.stringify(value) : value;
6792
6848
  }
6793
6849
 
6794
- // src/commands/deploy.ts
6850
+ // src/commands/key.ts
6795
6851
  var import_picocolors10 = __toESM(require_picocolors(), 1);
6852
+ function requireApp(ctx, explicit) {
6853
+ const appId = appTarget(ctx, explicit);
6854
+ if (!appId) {
6855
+ throw new WorkserError(
6856
+ "No app to target.\n\n \u2022 Run this from the app's own folder (its ID is picked up automatically), or\n \u2022 pass --app <webAppId> \u2014 `workser project show` lists an app's ID.",
6857
+ { code: "no_app" }
6858
+ );
6859
+ }
6860
+ return appId;
6861
+ }
6862
+ function registerKey(program3) {
6863
+ const key = program3.command("key").description("Manage this app's Workser-issued credentials (AI Gateway, auth secret)");
6864
+ key.command("list").description("List this app's managed keys (redacted \u2014 never the secret)").option("--app <webAppId>", "Which app (default: the one this folder is linked to)").action(
6865
+ action(async ({ ctx, opts }) => {
6866
+ const appId = requireApp(ctx, opts.app);
6867
+ const items = await api(ctx, `/v1/apps/${appId}/keys`);
6868
+ ok(items, () => {
6869
+ if (!items?.length) {
6870
+ return line(import_picocolors10.default.dim("No managed keys yet \u2014 they're created the first time this app deploys."));
6871
+ }
6872
+ for (const item of items) {
6873
+ const lastUsed = item.last_used_at ? `last used ${item.last_used_at}` : "never used yet";
6874
+ line(`${import_picocolors10.default.bold(item.environment)} ${item.prefix}\u2026 ${import_picocolors10.default.dim(`(${lastUsed})`)}`);
6875
+ }
6876
+ });
6877
+ })
6878
+ );
6879
+ key.command("rotate <key>").description("Rotate a managed key (AI_GATEWAY_API_KEY, BETTER_AUTH_SECRET)").option("--app <webAppId>", "Which app (default: the one this folder is linked to)").option("--env <environment>", "production or preview \u2014 required for AI_GATEWAY_API_KEY").action(
6880
+ action(async ({ ctx, args, opts }) => {
6881
+ const appId = requireApp(ctx, opts.app);
6882
+ const keyName = args[0];
6883
+ const environment = typeof opts.env === "string" ? opts.env : void 0;
6884
+ if (environment && environment !== "production" && environment !== "preview") {
6885
+ throw new WorkserError(
6886
+ `--env must be "production" or "preview", got "${environment}".`,
6887
+ { code: "bad_input" }
6888
+ );
6889
+ }
6890
+ const res = await api(
6891
+ ctx,
6892
+ `/v1/apps/${appId}/keys/${encodeURIComponent(keyName)}/rotate`,
6893
+ { body: { environment } }
6894
+ );
6895
+ ok(res, () => {
6896
+ success(`Rotated ${import_picocolors10.default.bold(keyName)}${res?.environment ? ` (${res.environment})` : ""}.`);
6897
+ if (res?.key) {
6898
+ line("");
6899
+ line(import_picocolors10.default.bold("New value \u2014 shown once, not stored anywhere in the clear:"));
6900
+ line(import_picocolors10.default.yellow(res.key));
6901
+ }
6902
+ line("");
6903
+ line(
6904
+ import_picocolors10.default.dim(
6905
+ "The old value stopped working immediately. If this app is already deployed, redeploy so it picks up the new one: workser deploy" + (res?.environment === "production" ? " --prod" : "")
6906
+ )
6907
+ );
6908
+ });
6909
+ })
6910
+ );
6911
+ }
6912
+
6913
+ // src/commands/deploy.ts
6914
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
6796
6915
  var TERMINAL = /* @__PURE__ */ new Set(["ready", "live", "success", "error", "failed", "canceled"]);
6797
6916
  function registerDeploy(program3) {
6798
6917
  const deploy = program3.command("deploy").description("Deploy the current project to Workser (git \u2192 Vercel) and return a live URL").option("--prod", "deploy to production (same as --env production)", false).option(
@@ -6843,7 +6962,7 @@ async function watchDeploy(ctx, id) {
6843
6962
  for (; ; ) {
6844
6963
  const dep = await api(ctx, `/v1/deployments/${encodeURIComponent(id)}`);
6845
6964
  if (!isJson() && dep.status !== last) {
6846
- line(` ${colorStatus(dep.status)}${dep.url ? " " + import_picocolors10.default.cyan(dep.url) : ""}`);
6965
+ line(` ${colorStatus(dep.status)}${dep.url ? " " + import_picocolors11.default.cyan(dep.url) : ""}`);
6847
6966
  last = dep.status;
6848
6967
  }
6849
6968
  if (TERMINAL.has(String(dep.status).toLowerCase())) return dep;
@@ -6853,12 +6972,12 @@ async function watchDeploy(ctx, id) {
6853
6972
  function printDeploy(dep) {
6854
6973
  if (!dep) return;
6855
6974
  const ready = ["ready", "live", "success"].includes(String(dep.status).toLowerCase());
6856
- if (ready && dep.url) success(`Live at ${import_picocolors10.default.cyan(dep.url)}`);
6857
- else line(`deploy ${colorStatus(dep.status)} ${import_picocolors10.default.dim(`(${dep.id ?? "?"})`)}${dep.url ? " " + dep.url : ""}`);
6975
+ if (ready && dep.url) success(`Live at ${import_picocolors11.default.cyan(dep.url)}`);
6976
+ else line(`deploy ${colorStatus(dep.status)} ${import_picocolors11.default.dim(`(${dep.id ?? "?"})`)}${dep.url ? " " + dep.url : ""}`);
6858
6977
  }
6859
6978
 
6860
6979
  // src/commands/versions.ts
6861
- var import_picocolors11 = __toESM(require_picocolors(), 1);
6980
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
6862
6981
  function registerVersions(program3) {
6863
6982
  program3.command("versions").description("List the Workser-managed versions of the project (deploy history)").option("--app <webAppId>", "which app (defaults to the primary app)").option(
6864
6983
  "--env <environment>",
@@ -6877,7 +6996,7 @@ function registerVersions(program3) {
6877
6996
  }
6878
6997
  });
6879
6998
  ok(items, () => {
6880
- if (!items?.length) return line(import_picocolors11.default.dim("No versions yet. `workser deploy` to create one."));
6999
+ if (!items?.length) return line(import_picocolors12.default.dim("No versions yet. `workser deploy` to create one."));
6881
7000
  for (const v of items) {
6882
7001
  line(formatVersion(v));
6883
7002
  }
@@ -6886,11 +7005,11 @@ function registerVersions(program3) {
6886
7005
  );
6887
7006
  }
6888
7007
  function formatVersion(v) {
6889
- const ref = import_picocolors11.default.yellow(shortRef(v.ref));
6890
- const when = import_picocolors11.default.dim(formatTime(v.createdAt));
6891
- const msg = (v.message ?? "").trim() || import_picocolors11.default.dim("(no message)");
6892
- const badge = v.deployed ? " " + import_picocolors11.default.green(v.deployedEnvironment ? `live in ${v.deployedEnvironment}` : "deployed") : "";
6893
- const url = v.url ? " " + import_picocolors11.default.cyan(v.url) : "";
7008
+ const ref = import_picocolors12.default.yellow(shortRef(v.ref));
7009
+ const when = import_picocolors12.default.dim(formatTime(v.createdAt));
7010
+ const msg = (v.message ?? "").trim() || import_picocolors12.default.dim("(no message)");
7011
+ const badge = v.deployed ? " " + import_picocolors12.default.green(v.deployedEnvironment ? `live in ${v.deployedEnvironment}` : "deployed") : "";
7012
+ const url = v.url ? " " + import_picocolors12.default.cyan(v.url) : "";
6894
7013
  return `${ref} ${when} ${msg}${badge}${url}`;
6895
7014
  }
6896
7015
  function shortRef(ref) {
@@ -6904,7 +7023,7 @@ function formatTime(t) {
6904
7023
  }
6905
7024
 
6906
7025
  // src/commands/logs.ts
6907
- var import_picocolors12 = __toESM(require_picocolors(), 1);
7026
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
6908
7027
  function registerLogs(program3) {
6909
7028
  program3.command("logs").description("Show the build logs of the latest deployment").option("-n, --lines <n>", "number of lines", "100").option("-f, --follow", "keep streaming new logs", false).option("--app <webAppId>", "which app (defaults to the primary app)").option("--env <environment>", "preview or production (default: whichever deployed last)").action(
6910
7029
  action(async ({ ctx, opts }) => {
@@ -6938,10 +7057,10 @@ function registerLogs(program3) {
6938
7057
  function print(res) {
6939
7058
  const entries = res?.entries ?? [];
6940
7059
  for (const e of entries) line(formatLog(e));
6941
- if (!entries.length && res?.note) line(import_picocolors12.default.dim(res.note));
7060
+ if (!entries.length && res?.note) line(import_picocolors13.default.dim(res.note));
6942
7061
  if (entries.length && res?.deploymentId) {
6943
7062
  const where = res.environment ? ` (${res.environment})` : "";
6944
- line(import_picocolors12.default.dim(`\u2014 build ${res.deploymentId}${where}`));
7063
+ line(import_picocolors13.default.dim(`\u2014 build ${res.deploymentId}${where}`));
6945
7064
  }
6946
7065
  }
6947
7066
  function formatLog(e) {
@@ -6950,7 +7069,7 @@ function formatLog(e) {
6950
7069
  }
6951
7070
 
6952
7071
  // src/commands/domain.ts
6953
- var import_picocolors13 = __toESM(require_picocolors(), 1);
7072
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
6954
7073
 
6955
7074
  // src/reserved-domains.ts
6956
7075
  var RESERVED_APEX = [
@@ -7013,9 +7132,9 @@ function registerDomain(program3) {
7013
7132
  { method: "POST", body: { domain: host } }
7014
7133
  );
7015
7134
  ok(result, () => {
7016
- line(`${import_picocolors13.default.green("Attached")} ${host}`);
7135
+ line(`${import_picocolors14.default.green("Attached")} ${host}`);
7017
7136
  line(
7018
- import_picocolors13.default.dim(
7137
+ import_picocolors14.default.dim(
7019
7138
  "It goes live once DNS points at us. Run `workser domain list` to see its status."
7020
7139
  )
7021
7140
  );
@@ -7031,7 +7150,7 @@ function registerDomain(program3) {
7031
7150
  opts.app ? `/v1/projects/${projectId}/web-apps/${opts.app}/domains/${encodeURIComponent(host)}` : `/v1/projects/${projectId}/domains/${encodeURIComponent(host)}`,
7032
7151
  { method: "DELETE" }
7033
7152
  );
7034
- ok(result, () => line(`${import_picocolors13.default.yellow("Detached")} ${host}`));
7153
+ ok(result, () => line(`${import_picocolors14.default.yellow("Detached")} ${host}`));
7035
7154
  })
7036
7155
  );
7037
7156
  domain.command("list").description("List domains attached to the project").action(
@@ -7039,8 +7158,8 @@ function registerDomain(program3) {
7039
7158
  const projectId = requireProject(ctx);
7040
7159
  const items = await api(ctx, `/v1/projects/${projectId}/domains`);
7041
7160
  ok(items, () => {
7042
- if (!items?.length) return line(import_picocolors13.default.dim("No custom domains."));
7043
- for (const d of items) line(`${d.domain}${import_picocolors13.default.dim(" " + (d.status ?? ""))}`);
7161
+ if (!items?.length) return line(import_picocolors14.default.dim("No custom domains."));
7162
+ for (const d of items) line(`${d.domain}${import_picocolors14.default.dim(" " + (d.status ?? ""))}`);
7044
7163
  });
7045
7164
  })
7046
7165
  );
@@ -7072,7 +7191,7 @@ function openUrl(url) {
7072
7191
  }
7073
7192
 
7074
7193
  // src/commands/doctor.ts
7075
- var import_picocolors14 = __toESM(require_picocolors(), 1);
7194
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
7076
7195
  import { spawnSync } from "child_process";
7077
7196
  function registerDoctor(program3) {
7078
7197
  program3.command("doctor").description("Print the resolved endpoint, mode, token presence (masked), and current project").action(
@@ -7106,29 +7225,29 @@ function registerDoctor(program3) {
7106
7225
  workspace: session.workspaceName ?? null
7107
7226
  };
7108
7227
  ok(report, () => {
7109
- line(import_picocolors14.default.bold("workser doctor"));
7110
- line(` endpoint: ${ctx.endpoint} ${import_picocolors14.default.dim(`(${endpointSource})`)}`);
7228
+ line(import_picocolors15.default.bold("workser doctor"));
7229
+ line(` endpoint: ${ctx.endpoint} ${import_picocolors15.default.dim(`(${endpointSource})`)}`);
7111
7230
  line(
7112
- ` env: ${env === "prod" ? import_picocolors14.default.yellow(env) : env}` + import_picocolors14.default.dim(process.env.WORKSER_ENV ? " ($WORKSER_ENV)" : " (default)")
7231
+ ` env: ${env === "prod" ? import_picocolors15.default.yellow(env) : env}` + import_picocolors15.default.dim(process.env.WORKSER_ENV ? " ($WORKSER_ENV)" : " (default)")
7113
7232
  );
7114
7233
  line(` mode: ${ctx.mode}`);
7115
7234
  line(
7116
- ` token: ${ctx.token ? `${maskToken(ctx.token)} ${import_picocolors14.default.dim(`(${tokenSource})`)}` : import_picocolors14.default.yellow("none \u2014 run `workser login`")}`
7235
+ ` token: ${ctx.token ? `${maskToken(ctx.token)} ${import_picocolors15.default.dim(`(${tokenSource})`)}` : import_picocolors15.default.yellow("none \u2014 run `workser login`")}`
7117
7236
  );
7118
7237
  line(
7119
- ` project: ${ctx.projectId ?? import_picocolors14.default.dim("none")}` + (link?.name ? ` ${import_picocolors14.default.dim(`(${link.name})`)}` : "") + (projectSource ? import_picocolors14.default.dim(` [${projectSource}]`) : "")
7238
+ ` project: ${ctx.projectId ?? import_picocolors15.default.dim("none")}` + (link?.name ? ` ${import_picocolors15.default.dim(`(${link.name})`)}` : "") + (projectSource ? import_picocolors15.default.dim(` [${projectSource}]`) : "")
7120
7239
  );
7121
7240
  line(` cwd: ${ctx.cwd}`);
7122
- line(` git: ${git2 ?? import_picocolors14.default.dim("not on this shell's PATH")}`);
7241
+ line(` git: ${git2 ?? import_picocolors15.default.dim("not on this shell's PATH")}`);
7123
7242
  if (!git2) {
7124
7243
  line("");
7125
7244
  line(
7126
- import_picocolors14.default.dim(
7245
+ import_picocolors15.default.dim(
7127
7246
  " Workser brings its own git, so syncing and publishing still work."
7128
7247
  )
7129
7248
  );
7130
7249
  line(
7131
- import_picocolors14.default.dim(
7250
+ import_picocolors15.default.dim(
7132
7251
  ` Only needed if you want to run git yourself here. ${GIT_INSTALL_HINT}`
7133
7252
  )
7134
7253
  );
@@ -7136,16 +7255,16 @@ function registerDoctor(program3) {
7136
7255
  if (envIgnored) {
7137
7256
  line("");
7138
7257
  line(
7139
- import_picocolors14.default.yellow(
7258
+ import_picocolors15.default.yellow(
7140
7259
  ` WORKSER_ENV=${env} is not in effect \u2014 ${endpointSource} wins.`
7141
7260
  )
7142
7261
  );
7143
7262
  line(
7144
- import_picocolors14.default.dim(
7263
+ import_picocolors15.default.dim(
7145
7264
  ` Re-run \`workser login\` to switch (the saved token is tied to ${ctx.endpoint}),`
7146
7265
  )
7147
7266
  );
7148
- line(import_picocolors14.default.dim(` or pass --endpoint ${ENV_BASE_URLS[env]}.`));
7267
+ line(import_picocolors15.default.dim(` or pass --endpoint ${ENV_BASE_URLS[env]}.`));
7149
7268
  }
7150
7269
  });
7151
7270
  })
@@ -7175,7 +7294,32 @@ function maskToken(token) {
7175
7294
  }
7176
7295
 
7177
7296
  // src/commands/agent.ts
7178
- var import_picocolors15 = __toESM(require_picocolors(), 1);
7297
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
7298
+
7299
+ // src/model-policy.ts
7300
+ var DELEGATION_BLOCKED_MODELS = [
7301
+ {
7302
+ match: "fable",
7303
+ label: "Claude Fable 5",
7304
+ reason: "it is the most expensive model available and no delegated run is worth it \u2014 ask the owner if you think this one is"
7305
+ }
7306
+ ];
7307
+ function blockedDelegationModel(model) {
7308
+ const value = (model ?? "").trim().toLowerCase();
7309
+ if (!value) return null;
7310
+ return DELEGATION_BLOCKED_MODELS.find((entry) => value.includes(entry.match)) ?? null;
7311
+ }
7312
+ function assertDelegatableModel(flag, model) {
7313
+ const blocked = blockedDelegationModel(model);
7314
+ if (!blocked || !model) return;
7315
+ throw new WorkserError(
7316
+ `${flag} "${model}" can't be chosen for a delegated run: ${blocked.reason}.
7317
+ Pick another model, or leave ${flag} off to use the agent's own default.`,
7318
+ { code: "bad_request" }
7319
+ );
7320
+ }
7321
+
7322
+ // src/commands/agent.ts
7179
7323
  var SPAWNABLE_AGENTS = ["claude_code", "codex", "kimi", "opencode", "grok"];
7180
7324
  function registerAgent(program3) {
7181
7325
  const agent = program3.command("agent").description("Delegate focused subtasks to your configured agent roles (each runs isolated)");
@@ -7183,20 +7327,20 @@ function registerAgent(program3) {
7183
7327
  action(async ({ ctx }) => {
7184
7328
  const cfg = await api(ctx, "/v1/agents");
7185
7329
  ok(cfg, () => {
7186
- line(import_picocolors15.default.bold("main agent:") + " " + (cfg?.mainAgent ?? import_picocolors15.default.dim("none")));
7187
- line(import_picocolors15.default.bold("backup agent:") + " " + (cfg?.backupAgent ?? import_picocolors15.default.dim("none")));
7330
+ line(import_picocolors16.default.bold("main agent:") + " " + (cfg?.mainAgent ?? import_picocolors16.default.dim("none")));
7331
+ line(import_picocolors16.default.bold("backup agent:") + " " + (cfg?.backupAgent ?? import_picocolors16.default.dim("none")));
7188
7332
  if (cfg?.effectiveMainAgent && cfg.effectiveMainAgent !== cfg.mainAgent) {
7189
7333
  line(
7190
- import_picocolors15.default.yellow(
7334
+ import_picocolors16.default.yellow(
7191
7335
  ` \u2937 failover active: runs use ${cfg.effectiveMainAgent} (main not available)`
7192
7336
  )
7193
7337
  );
7194
7338
  }
7195
7339
  const roles = cfg?.roles ?? [];
7196
7340
  if (!roles.length) {
7197
- line(import_picocolors15.default.dim("No subagents configured. Add them in the Workser Orbit Agents screen."));
7341
+ line(import_picocolors16.default.dim("No subagents configured. Add them in the Workser Orbit Agents screen."));
7198
7342
  } else {
7199
- line(import_picocolors15.default.bold("subagents:"));
7343
+ line(import_picocolors16.default.bold("subagents:"));
7200
7344
  for (const r of roles) line(" " + formatRole(r));
7201
7345
  }
7202
7346
  const detected = cfg?.detected ?? [];
@@ -7204,7 +7348,7 @@ function registerAgent(program3) {
7204
7348
  (d) => d?.installed && d?.authed !== false
7205
7349
  );
7206
7350
  line(
7207
- import_picocolors15.default.bold("spawnable (workser agent spawn <agent>):") + " " + (spawnable.length ? spawnable.map((d) => toContractId(d.id)).join(", ") : import_picocolors15.default.dim("none connected"))
7351
+ import_picocolors16.default.bold("spawnable (workser agent spawn <agent>):") + " " + (spawnable.length ? spawnable.map((d) => toContractId(d.id)).join(", ") : import_picocolors16.default.dim("none connected"))
7208
7352
  );
7209
7353
  });
7210
7354
  })
@@ -7218,8 +7362,8 @@ function registerAgent(program3) {
7218
7362
  backupAgent: cfg?.backupAgent ?? null
7219
7363
  },
7220
7364
  () => {
7221
- line(import_picocolors15.default.bold("main agent:") + " " + (cfg?.mainAgent ?? import_picocolors15.default.dim("none")));
7222
- line(import_picocolors15.default.bold("backup agent:") + " " + (cfg?.backupAgent ?? import_picocolors15.default.dim("none")));
7365
+ line(import_picocolors16.default.bold("main agent:") + " " + (cfg?.mainAgent ?? import_picocolors16.default.dim("none")));
7366
+ line(import_picocolors16.default.bold("backup agent:") + " " + (cfg?.backupAgent ?? import_picocolors16.default.dim("none")));
7223
7367
  }
7224
7368
  );
7225
7369
  })
@@ -7251,6 +7395,7 @@ function registerAgent(program3) {
7251
7395
  action(async ({ ctx, args, opts }) => {
7252
7396
  const spawnAgent = args[0];
7253
7397
  const task = args[1].join(" ");
7398
+ assertDelegatableModel("--model", opts.model);
7254
7399
  const res = await api(ctx, "/v1/agents/run", {
7255
7400
  body: {
7256
7401
  agent: spawnAgent,
@@ -7279,21 +7424,21 @@ function toContractId(id) {
7279
7424
  return id === "claude" ? "claude_code" : id;
7280
7425
  }
7281
7426
  function formatRole(r) {
7282
- const label = import_picocolors15.default.yellow(r.role);
7283
- const agent = import_picocolors15.default.dim("\xB7 " + (r.agent ?? "?"));
7284
- const enabled = r.enabled === false ? import_picocolors15.default.red("disabled") : import_picocolors15.default.green("enabled");
7427
+ const label = import_picocolors16.default.yellow(r.role);
7428
+ const agent = import_picocolors16.default.dim("\xB7 " + (r.agent ?? "?"));
7429
+ const enabled = r.enabled === false ? import_picocolors16.default.red("disabled") : import_picocolors16.default.green("enabled");
7285
7430
  const runnable = r.installed && r.authed !== false;
7286
- const ready = runnable ? import_picocolors15.default.green("runnable") : import_picocolors15.default.dim("not runnable");
7431
+ const ready = runnable ? import_picocolors16.default.green("runnable") : import_picocolors16.default.dim("not runnable");
7287
7432
  const extras = [];
7288
7433
  if (r.model) extras.push(`model ${r.model}`);
7289
7434
  if (Array.isArray(r.apps) && r.apps.length) extras.push(`apps: ${r.apps.join(",")}`);
7290
7435
  if (Array.isArray(r.mcp) && r.mcp.length) extras.push(`mcp: ${r.mcp.length}`);
7291
- const tail = extras.length ? " " + import_picocolors15.default.dim(extras.join(" \xB7 ")) : "";
7436
+ const tail = extras.length ? " " + import_picocolors16.default.dim(extras.join(" \xB7 ")) : "";
7292
7437
  return `${label} ${agent} ${enabled} ${ready}${tail}`;
7293
7438
  }
7294
7439
 
7295
7440
  // src/commands/verify.ts
7296
- var import_picocolors16 = __toESM(require_picocolors(), 1);
7441
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
7297
7442
  function registerVerify(program3) {
7298
7443
  program3.command("verify").description(
7299
7444
  "Run the project's checks (typecheck/lint/build) \u2014 use before declaring a task done"
@@ -7315,23 +7460,23 @@ function registerVerify(program3) {
7315
7460
  function printVerify(res) {
7316
7461
  if (!res) return;
7317
7462
  if (!res.checks?.length) {
7318
- line(import_picocolors16.default.dim(res.note ?? "No checks detected."));
7463
+ line(import_picocolors17.default.dim(res.note ?? "No checks detected."));
7319
7464
  return;
7320
7465
  }
7321
7466
  for (const c of res.checks) {
7322
7467
  line(
7323
- ` ${c.ok ? import_picocolors16.default.green("\u2713") : import_picocolors16.default.red("\u2717")} ${c.name}${c.ok ? "" : import_picocolors16.default.dim(` (exit ${c.exitCode})`)}`
7468
+ ` ${c.ok ? import_picocolors17.default.green("\u2713") : import_picocolors17.default.red("\u2717")} ${c.name}${c.ok ? "" : import_picocolors17.default.dim(` (exit ${c.exitCode})`)}`
7324
7469
  );
7325
7470
  }
7326
7471
  if (res.ok) success("All checks passed");
7327
7472
  else
7328
7473
  line(
7329
- import_picocolors16.default.red("Some checks failed \u2014 fix the errors above and re-run ") + import_picocolors16.default.bold("workser verify") + import_picocolors16.default.red(".")
7474
+ import_picocolors17.default.red("Some checks failed \u2014 fix the errors above and re-run ") + import_picocolors17.default.bold("workser verify") + import_picocolors17.default.red(".")
7330
7475
  );
7331
7476
  }
7332
7477
 
7333
7478
  // src/commands/checkpoint.ts
7334
- var import_picocolors17 = __toESM(require_picocolors(), 1);
7479
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
7335
7480
  function registerCheckpoint(program3) {
7336
7481
  program3.command("checkpoint [label]").description(
7337
7482
  "Save the current state of this folder so you can come back to it"
@@ -7348,8 +7493,8 @@ function registerCheckpoint(program3) {
7348
7493
  ok(res, () => {
7349
7494
  const p = res?.point;
7350
7495
  success(`Saved a checkpoint${p?.label ? `: ${p.label}` : ""}`);
7351
- if (p?.ref) line(import_picocolors17.default.dim(` ${p.ref.slice(0, 7)}`));
7352
- line(import_picocolors17.default.dim(" Come back to it with `workser restore`."));
7496
+ if (p?.ref) line(import_picocolors18.default.dim(` ${p.ref.slice(0, 7)}`));
7497
+ line(import_picocolors18.default.dim(" Come back to it with `workser restore`."));
7353
7498
  });
7354
7499
  })
7355
7500
  );
@@ -7375,12 +7520,12 @@ function registerCheckpoint(program3) {
7375
7520
  );
7376
7521
  if (res?.filesChanged) {
7377
7522
  line(
7378
- import_picocolors17.default.dim(
7523
+ import_picocolors18.default.dim(
7379
7524
  ` ${res.filesChanged} file${res.filesChanged === 1 ? "" : "s"} changed`
7380
7525
  )
7381
7526
  );
7382
7527
  }
7383
- line(import_picocolors17.default.dim(" This is reversible: `workser restore` again."));
7528
+ line(import_picocolors18.default.dim(" This is reversible: `workser restore` again."));
7384
7529
  });
7385
7530
  })
7386
7531
  );
@@ -7397,25 +7542,25 @@ function registerCheckpoint(program3) {
7397
7542
  function printPoints(points) {
7398
7543
  if (!points.length) {
7399
7544
  info("No checkpoints yet for this folder.");
7400
- line(import_picocolors17.default.dim(" Take one with `workser checkpoint`."));
7545
+ line(import_picocolors18.default.dim(" Take one with `workser checkpoint`."));
7401
7546
  return;
7402
7547
  }
7403
- line(import_picocolors17.default.bold("Checkpoints"));
7548
+ line(import_picocolors18.default.bold("Checkpoints"));
7404
7549
  for (const p of points) {
7405
7550
  const when = p.at ? new Date(p.at).toLocaleString() : "";
7406
7551
  line(
7407
- ` ${import_picocolors17.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors17.default.dim(` ${when}`) : ""}`
7552
+ ` ${import_picocolors18.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors18.default.dim(` ${when}`) : ""}`
7408
7553
  );
7409
7554
  }
7410
7555
  line(
7411
- import_picocolors17.default.dim(
7556
+ import_picocolors18.default.dim(
7412
7557
  "\nGo back with `workser restore <ref>`, or just `workser restore` for the newest."
7413
7558
  )
7414
7559
  );
7415
7560
  }
7416
7561
 
7417
7562
  // src/commands/sync.ts
7418
- var import_picocolors18 = __toESM(require_picocolors(), 1);
7563
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
7419
7564
  function registerSync(program3) {
7420
7565
  program3.command("sync").description(
7421
7566
  "Reconcile this folder with the copy Workser holds (pull, then push)"
@@ -7442,7 +7587,7 @@ function registerSync(program3) {
7442
7587
  warn(res?.message ?? "Couldn't sync this folder.");
7443
7588
  if (res?.state === "diverged") {
7444
7589
  line(
7445
- import_picocolors18.default.dim(
7590
+ import_picocolors19.default.dim(
7446
7591
  " This folder and Workser's copy have both changed. Open Workser to resolve it."
7447
7592
  )
7448
7593
  );
@@ -7454,7 +7599,7 @@ function registerSync(program3) {
7454
7599
  return;
7455
7600
  }
7456
7601
  success("Synced");
7457
- if (res?.ref) line(import_picocolors18.default.dim(` ${String(res.ref).slice(0, 7)}`));
7602
+ if (res?.ref) line(import_picocolors19.default.dim(` ${String(res.ref).slice(0, 7)}`));
7458
7603
  });
7459
7604
  if (refused) process.exitCode = 1;
7460
7605
  })
@@ -7462,7 +7607,7 @@ function registerSync(program3) {
7462
7607
  }
7463
7608
 
7464
7609
  // src/commands/workflow.ts
7465
- var import_picocolors19 = __toESM(require_picocolors(), 1);
7610
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
7466
7611
  function registerWorkflow(program3) {
7467
7612
  const wf = program3.command("workflow").description("Create, run, and inspect workflow automations for the project");
7468
7613
  wf.command("list").description("List the project's workflows").action(
@@ -7470,10 +7615,10 @@ function registerWorkflow(program3) {
7470
7615
  const projectId = requireProject(ctx);
7471
7616
  const items = await api(ctx, `/v1/projects/${projectId}/workflows`);
7472
7617
  ok(items, () => {
7473
- if (!items?.length) return line(import_picocolors19.default.dim("No workflows yet. `workser workflow create`."));
7618
+ if (!items?.length) return line(import_picocolors20.default.dim("No workflows yet. `workser workflow create`."));
7474
7619
  for (const w of items) {
7475
- const status = w.is_active ? import_picocolors19.default.green("active") : import_picocolors19.default.dim("inactive");
7476
- line(`${w.id} ${import_picocolors19.default.bold(w.name ?? "Untitled")} ${status}`);
7620
+ const status = w.is_active ? import_picocolors20.default.green("active") : import_picocolors20.default.dim("inactive");
7621
+ line(`${w.id} ${import_picocolors20.default.bold(w.name ?? "Untitled")} ${status}`);
7477
7622
  }
7478
7623
  });
7479
7624
  })
@@ -7485,7 +7630,7 @@ function registerWorkflow(program3) {
7485
7630
  const res = await api(ctx, `/v1/projects/${projectId}/workflows`, {
7486
7631
  body: { name: args[0], ...extra }
7487
7632
  });
7488
- ok(res, () => line(`Created workflow ${import_picocolors19.default.bold(res.id)}.`));
7633
+ ok(res, () => line(`Created workflow ${import_picocolors20.default.bold(res.id)}.`));
7489
7634
  })
7490
7635
  );
7491
7636
  wf.command("get <id>").description("Show a workflow's full definition").action(
@@ -7520,8 +7665,8 @@ function registerWorkflow(program3) {
7520
7665
  action(async ({ ctx, args }) => {
7521
7666
  const items = await api(ctx, `/v1/workflows/${args[0]}/executions`);
7522
7667
  ok(items, () => {
7523
- if (!items?.length) return line(import_picocolors19.default.dim("No runs yet."));
7524
- for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors19.default.dim(e.started_at ?? "")}`);
7668
+ if (!items?.length) return line(import_picocolors20.default.dim("No runs yet."));
7669
+ for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors20.default.dim(e.started_at ?? "")}`);
7525
7670
  });
7526
7671
  })
7527
7672
  );
@@ -7529,15 +7674,15 @@ function registerWorkflow(program3) {
7529
7674
  action(async ({ ctx, args }) => {
7530
7675
  const items = await api(ctx, `/v1/node-types`, { query: { q: args[0] } });
7531
7676
  ok(items, () => {
7532
- if (!items?.length) return line(import_picocolors19.default.dim("No matching node types."));
7533
- for (const n of items) line(`${n.name ?? n.type} ${import_picocolors19.default.dim(n.category ?? "")}`);
7677
+ if (!items?.length) return line(import_picocolors20.default.dim("No matching node types."));
7678
+ for (const n of items) line(`${n.name ?? n.type} ${import_picocolors20.default.dim(n.category ?? "")}`);
7534
7679
  });
7535
7680
  })
7536
7681
  );
7537
7682
  }
7538
7683
 
7539
7684
  // src/commands/connection.ts
7540
- var import_picocolors20 = __toESM(require_picocolors(), 1);
7685
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
7541
7686
  function registerConnection(program3) {
7542
7687
  const connection = program3.command("connection").description("Connect and use third-party app connections (Gmail, Slack, Stripe, ...)");
7543
7688
  connection.command("list").description("List connectable toolkits and this project's existing connections").option("--toolkit <slug>", "filter connections to one toolkit").action(
@@ -7550,8 +7695,8 @@ function registerConnection(program3) {
7550
7695
  ok({ catalog, connections }, () => {
7551
7696
  const connected = new Set((connections ?? []).map((c) => c.toolkit ?? c.composio_app));
7552
7697
  for (const t of catalog ?? []) {
7553
- const status = connected.has(t.slug) ? import_picocolors20.default.green("connected") : import_picocolors20.default.dim("not connected");
7554
- line(`${t.slug} ${import_picocolors20.default.bold(t.name ?? t.slug)} ${status}`);
7698
+ const status = connected.has(t.slug) ? import_picocolors21.default.green("connected") : import_picocolors21.default.dim("not connected");
7699
+ line(`${t.slug} ${import_picocolors21.default.bold(t.name ?? t.slug)} ${status}`);
7555
7700
  }
7556
7701
  });
7557
7702
  })
@@ -7563,9 +7708,9 @@ function registerConnection(program3) {
7563
7708
  query: { q: args[0], toolkit: opts.toolkit, limit: opts.limit }
7564
7709
  });
7565
7710
  ok(items, () => {
7566
- if (!items?.length) return line(import_picocolors20.default.dim("No matching actions."));
7711
+ if (!items?.length) return line(import_picocolors21.default.dim("No matching actions."));
7567
7712
  for (const t of items) {
7568
- line(`${t.slug} ${import_picocolors20.default.dim(`[${t.toolkit}]`)} ${t.description ?? ""}`);
7713
+ line(`${t.slug} ${import_picocolors21.default.dim(`[${t.toolkit}]`)} ${t.description ?? ""}`);
7569
7714
  }
7570
7715
  });
7571
7716
  })
@@ -7582,7 +7727,7 @@ function registerConnection(program3) {
7582
7727
  });
7583
7728
  ok(
7584
7729
  res,
7585
- () => res.oauth_url ? line(`Open this URL to finish connecting: ${import_picocolors20.default.underline(res.oauth_url)}`) : line(`Connection ${res.connection_id} is ${res.status}.`)
7730
+ () => res.oauth_url ? line(`Open this URL to finish connecting: ${import_picocolors21.default.underline(res.oauth_url)}`) : line(`Connection ${res.connection_id} is ${res.status}.`)
7586
7731
  );
7587
7732
  })
7588
7733
  );
@@ -7600,8 +7745,8 @@ function registerConnection(program3) {
7600
7745
  const projectId = requireProject(ctx);
7601
7746
  const items = await api(ctx, `/v1/projects/${projectId}/integrations/${args[0]}/tools`);
7602
7747
  ok(items, () => {
7603
- if (!items?.length) return line(import_picocolors20.default.dim("No tools found."));
7604
- for (const t of items) line(`${t.slug} ${import_picocolors20.default.dim(t.description ?? "")}`);
7748
+ if (!items?.length) return line(import_picocolors21.default.dim("No tools found."));
7749
+ for (const t of items) line(`${t.slug} ${import_picocolors21.default.dim(t.description ?? "")}`);
7605
7750
  });
7606
7751
  })
7607
7752
  );
@@ -7617,7 +7762,7 @@ function registerConnection(program3) {
7617
7762
  }
7618
7763
 
7619
7764
  // src/commands/tool.ts
7620
- var import_picocolors21 = __toESM(require_picocolors(), 1);
7765
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
7621
7766
  function registerTool(program3) {
7622
7767
  const tool = program3.command("tool").description(
7623
7768
  "Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
@@ -7626,7 +7771,7 @@ function registerTool(program3) {
7626
7771
  action(async ({ ctx }) => {
7627
7772
  const tools = await api(ctx, "/v1/tool/list");
7628
7773
  ok(tools, () => {
7629
- if (!tools?.length) return line(import_picocolors21.default.dim("No tools available."));
7774
+ if (!tools?.length) return line(import_picocolors22.default.dim("No tools available."));
7630
7775
  const byCategory = /* @__PURE__ */ new Map();
7631
7776
  for (const t of tools) {
7632
7777
  const list = byCategory.get(t.category) ?? [];
@@ -7634,9 +7779,9 @@ function registerTool(program3) {
7634
7779
  byCategory.set(t.category, list);
7635
7780
  }
7636
7781
  for (const [category, items] of byCategory) {
7637
- line(import_picocolors21.default.bold(category) + ":");
7782
+ line(import_picocolors22.default.bold(category) + ":");
7638
7783
  for (const t of items) {
7639
- line(` ${t.name} ${import_picocolors21.default.dim(t.description ?? "")}`);
7784
+ line(` ${t.name} ${import_picocolors22.default.dim(t.description ?? "")}`);
7640
7785
  }
7641
7786
  }
7642
7787
  });
@@ -7654,7 +7799,7 @@ function registerTool(program3) {
7654
7799
  }
7655
7800
 
7656
7801
  // src/commands/memory.ts
7657
- var import_picocolors22 = __toESM(require_picocolors(), 1);
7802
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
7658
7803
  function registerMemory(program3) {
7659
7804
  const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
7660
7805
  memory.command("add <content>").description("Store something worth remembering across future conversations").option("--metadata <json>", "extra metadata for filtering, as a JSON string").option("--id <customId>", "custom id for dedup/updates").action(
@@ -7678,9 +7823,9 @@ function registerMemory(program3) {
7678
7823
  });
7679
7824
  ok(res, () => {
7680
7825
  const results = res?.results ?? res ?? [];
7681
- if (!results?.length) return line(import_picocolors22.default.dim("No matching memories."));
7826
+ if (!results?.length) return line(import_picocolors23.default.dim("No matching memories."));
7682
7827
  for (const r of results) {
7683
- line(`${import_picocolors22.default.dim(r.id ?? "?")} ${r.memory ?? r.content ?? ""}`);
7828
+ line(`${import_picocolors23.default.dim(r.id ?? "?")} ${r.memory ?? r.content ?? ""}`);
7684
7829
  }
7685
7830
  });
7686
7831
  })
@@ -7697,7 +7842,7 @@ function registerMemory(program3) {
7697
7842
  }
7698
7843
 
7699
7844
  // src/commands/business.ts
7700
- var import_picocolors23 = __toESM(require_picocolors(), 1);
7845
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
7701
7846
  var RESOURCE_PATHS = {
7702
7847
  "business-config": "business-config",
7703
7848
  "business-settings": "business-settings",
@@ -7757,7 +7902,7 @@ function registerBusiness(program3) {
7757
7902
  const projectId = requireProject(ctx);
7758
7903
  const [resource] = args;
7759
7904
  const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
7760
- ok(res, () => line(`Created ${resource} ${import_picocolors23.default.bold(res?.id ?? "")}.`));
7905
+ ok(res, () => line(`Created ${resource} ${import_picocolors24.default.bold(res?.id ?? "")}.`));
7761
7906
  })
7762
7907
  );
7763
7908
  biz.command("update <resource> <id>").description("Update a record by id (PATCH/PUT \u2014 matches the underlying route)").option("--body <json>", "changed fields as a JSON object string", "{}").action(
@@ -7799,7 +7944,7 @@ function businessPath(projectId, resource, subpath) {
7799
7944
  }
7800
7945
 
7801
7946
  // src/commands/artifact.ts
7802
- var import_picocolors24 = __toESM(require_picocolors(), 1);
7947
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
7803
7948
  import { existsSync as existsSync2, statSync } from "fs";
7804
7949
  import { resolve as resolve3, basename as basename3 } from "path";
7805
7950
  var KINDS = [
@@ -7896,7 +8041,7 @@ function registerArtifact(program3) {
7896
8041
  ok(
7897
8042
  res,
7898
8043
  () => success(
7899
- `Recorded ${import_picocolors24.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors24.default.dim(` (${res.kind})`) : ""}`
8044
+ `Recorded ${import_picocolors25.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors25.default.dim(` (${res.kind})`) : ""}`
7900
8045
  )
7901
8046
  );
7902
8047
  })
@@ -7934,7 +8079,7 @@ function registerArtifact(program3) {
7934
8079
  }
7935
8080
  function printArtifacts(rows) {
7936
8081
  if (!rows.length) {
7937
- line(import_picocolors24.default.dim("Nothing produced yet."));
8082
+ line(import_picocolors25.default.dim("Nothing produced yet."));
7938
8083
  return;
7939
8084
  }
7940
8085
  const byStep = /* @__PURE__ */ new Map();
@@ -7944,26 +8089,26 @@ function printArtifacts(rows) {
7944
8089
  byStep.set(r.subtask_id, list);
7945
8090
  }
7946
8091
  for (const [stepId, items] of byStep) {
7947
- line(import_picocolors24.default.bold(`step ${stepId}`));
8092
+ line(import_picocolors25.default.bold(`step ${stepId}`));
7948
8093
  for (const a of items) {
7949
- const flag = a.promoted_at ? import_picocolors24.default.green(" *") : " ";
8094
+ const flag = a.promoted_at ? import_picocolors25.default.green(" *") : " ";
7950
8095
  const where = a.local_path || a.cloud_url || "";
7951
8096
  line(
7952
- `${flag} ${import_picocolors24.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors24.default.dim(` ${where}`) : "")
8097
+ `${flag} ${import_picocolors25.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors25.default.dim(` ${where}`) : "")
7953
8098
  );
7954
- if (a.description) line(import_picocolors24.default.dim(` ${a.description}`));
8099
+ if (a.description) line(import_picocolors25.default.dim(` ${a.description}`));
7955
8100
  }
7956
8101
  line("");
7957
8102
  }
7958
- line(import_picocolors24.default.dim("* = handed over as a deliverable; the rest is working material."));
8103
+ line(import_picocolors25.default.dim("* = handed over as a deliverable; the rest is working material."));
7959
8104
  }
7960
8105
  function printRun(run) {
7961
8106
  if (!run) return;
7962
- line(` run ${import_picocolors24.default.bold(run.runId)}`);
8107
+ line(` run ${import_picocolors25.default.bold(run.runId)}`);
7963
8108
  if (run.taskId) line(` task ${run.taskId}`);
7964
8109
  if (run.conversationId) line(` chat ${run.conversationId}`);
7965
8110
  if (run.projectId) line(` project ${run.projectId}`);
7966
- if (run.cwd) line(` folder ${import_picocolors24.default.dim(run.cwd)}`);
8111
+ if (run.cwd) line(` folder ${import_picocolors25.default.dim(run.cwd)}`);
7967
8112
  }
7968
8113
 
7969
8114
  // src/commands/image.ts
@@ -8144,7 +8289,7 @@ function registerAudio(program3) {
8144
8289
  }
8145
8290
 
8146
8291
  // src/commands/ask.ts
8147
- var import_picocolors25 = __toESM(require_picocolors(), 1);
8292
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
8148
8293
  var TYPES = [
8149
8294
  "input",
8150
8295
  "choice",
@@ -8222,7 +8367,7 @@ function registerAsk(program3) {
8222
8367
  code: "bad_request"
8223
8368
  });
8224
8369
  }
8225
- info(import_picocolors25.default.dim("Waiting for the user to answer\u2026"));
8370
+ info(import_picocolors26.default.dim("Waiting for the user to answer\u2026"));
8226
8371
  const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
8227
8372
  body: {
8228
8373
  type,
@@ -8250,12 +8395,12 @@ function deriveTitle(message) {
8250
8395
  function printAnswer(res) {
8251
8396
  if (!res) return;
8252
8397
  if (res.status === "answered") {
8253
- line(` ${import_picocolors25.default.green("answered")}`);
8398
+ line(` ${import_picocolors26.default.green("answered")}`);
8254
8399
  const value = extract(res.response);
8255
8400
  if (value) line(` ${value}`);
8256
8401
  return;
8257
8402
  }
8258
- line(` ${import_picocolors25.default.yellow(res.status)} ${import_picocolors25.default.dim(res.reason ?? "")}`);
8403
+ line(` ${import_picocolors26.default.yellow(res.status)} ${import_picocolors26.default.dim(res.reason ?? "")}`);
8259
8404
  }
8260
8405
  function extract(response) {
8261
8406
  if (response == null) return "";
@@ -8274,7 +8419,7 @@ function extract(response) {
8274
8419
  }
8275
8420
 
8276
8421
  // src/commands/search.ts
8277
- var import_picocolors26 = __toESM(require_picocolors(), 1);
8422
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
8278
8423
  function registerSearch(program3) {
8279
8424
  program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
8280
8425
  action(async ({ ctx, args, opts }) => {
@@ -8287,9 +8432,9 @@ function registerSearch(program3) {
8287
8432
  line("");
8288
8433
  }
8289
8434
  const results = res?.results ?? [];
8290
- if (!results.length) return line(import_picocolors26.default.dim("No results."));
8435
+ if (!results.length) return line(import_picocolors27.default.dim("No results."));
8291
8436
  for (const r of results) {
8292
- line(`${r.title || import_picocolors26.default.dim("(untitled)")} ${import_picocolors26.default.dim(r.url)}`);
8437
+ line(`${r.title || import_picocolors27.default.dim("(untitled)")} ${import_picocolors27.default.dim(r.url)}`);
8293
8438
  }
8294
8439
  });
8295
8440
  })
@@ -8297,7 +8442,7 @@ function registerSearch(program3) {
8297
8442
  }
8298
8443
 
8299
8444
  // src/commands/board.ts
8300
- var import_picocolors27 = __toESM(require_picocolors(), 1);
8445
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
8301
8446
 
8302
8447
  // src/commands/record-step.ts
8303
8448
  async function recordEntityStep(ctx, opts) {
@@ -8336,7 +8481,7 @@ function registerBoard(program3) {
8336
8481
  }
8337
8482
  ok(rows, () => {
8338
8483
  if (!rows.length) {
8339
- line(import_picocolors27.default.dim("No cards on the Board yet."));
8484
+ line(import_picocolors28.default.dim("No cards on the Board yet."));
8340
8485
  return;
8341
8486
  }
8342
8487
  for (const r of rows) line(formatRow(r));
@@ -8351,7 +8496,7 @@ function registerBoard(program3) {
8351
8496
  `/v1/projects/${projectId}/work-items/${args[0]}`
8352
8497
  );
8353
8498
  ok(row, () => {
8354
- line(`${import_picocolors27.default.bold(row.title)} ${import_picocolors27.default.dim(row.id)}`);
8499
+ line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.id)}`);
8355
8500
  line(`${statusTag(row.status)} priority ${row.priority}`);
8356
8501
  if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
8357
8502
  if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
@@ -8392,7 +8537,7 @@ ${row.description}`);
8392
8537
  refId: row?.id,
8393
8538
  output: { workItem: row }
8394
8539
  });
8395
- ok(row, () => line(`Created work item ${import_picocolors27.default.bold(row?.id ?? "")} \u2014 ${title}`));
8540
+ ok(row, () => line(`Created work item ${import_picocolors28.default.bold(row?.id ?? "")} \u2014 ${title}`));
8396
8541
  })
8397
8542
  );
8398
8543
  board.command("update <id>").description("Change fields on a card \u2014 pass only what changes").option("--title <text>", "new title").option("--description <text>", "new description").option("--status <value>", STATUSES.join(" | ")).option("--priority <value>", PRIORITIES.join(" | ")).option(
@@ -8426,7 +8571,7 @@ ${row.description}`);
8426
8571
  );
8427
8572
  }
8428
8573
  const row = await patchItem(ctx, projectId, String(args[0]), body);
8429
- ok(row, () => line(`Updated ${import_picocolors27.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
8574
+ ok(row, () => line(`Updated ${import_picocolors28.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
8430
8575
  })
8431
8576
  );
8432
8577
  board.command("move <id> <status>").description(
@@ -8437,14 +8582,14 @@ ${row.description}`);
8437
8582
  const status = String(args[1]);
8438
8583
  assertStatus(status);
8439
8584
  const row = await patchItem(ctx, projectId, String(args[0]), { status });
8440
- ok(row, () => line(`Moved ${import_picocolors27.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
8585
+ ok(row, () => line(`Moved ${import_picocolors28.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
8441
8586
  })
8442
8587
  );
8443
8588
  board.command("close <id>").description("Shorthand for `board move <id> done`").action(
8444
8589
  action(async ({ ctx, args }) => {
8445
8590
  const projectId = requireProject(ctx);
8446
8591
  const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
8447
- ok(row, () => line(`Closed ${import_picocolors27.default.bold(row.title)} ${statusTag(row.status)}`));
8592
+ ok(row, () => line(`Closed ${import_picocolors28.default.bold(row.title)} ${statusTag(row.status)}`));
8448
8593
  })
8449
8594
  );
8450
8595
  }
@@ -8477,32 +8622,74 @@ function assertPriority(value) {
8477
8622
  }
8478
8623
  }
8479
8624
  function formatRow(r) {
8480
- const labels = r.labels?.length ? import_picocolors27.default.dim(` [${r.labels.join(", ")}]`) : "";
8481
- const owner = r.ownerHuman ? import_picocolors27.default.dim(` @${r.ownerHuman}`) : "";
8482
- return `${import_picocolors27.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
8625
+ const labels = r.labels?.length ? import_picocolors28.default.dim(` [${r.labels.join(", ")}]`) : "";
8626
+ const owner = r.ownerHuman ? import_picocolors28.default.dim(` @${r.ownerHuman}`) : "";
8627
+ return `${import_picocolors28.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
8483
8628
  }
8484
8629
  function statusTag(status) {
8485
8630
  const label = status.padEnd(11);
8486
- if (status === "done") return import_picocolors27.default.green(label);
8487
- if (status === "in-progress") return import_picocolors27.default.yellow(label);
8488
- if (status === "in-review") return import_picocolors27.default.cyan(label);
8489
- return import_picocolors27.default.dim(label);
8631
+ if (status === "done") return import_picocolors28.default.green(label);
8632
+ if (status === "in-progress") return import_picocolors28.default.yellow(label);
8633
+ if (status === "in-review") return import_picocolors28.default.cyan(label);
8634
+ return import_picocolors28.default.dim(label);
8490
8635
  }
8491
8636
  function collect2(value, previous) {
8492
8637
  return [...previous, value];
8493
8638
  }
8494
8639
 
8495
8640
  // src/commands/task.ts
8496
- var import_picocolors28 = __toESM(require_picocolors(), 1);
8497
- var STATUSES2 = ["todo", "working", "checking", "ready", "accepted", "archived"];
8498
- var ROLES = ["pm", "architect", "web", "api", "automation", "qa"];
8499
- var KINDS2 = ["data_reports", "web", "mobile", "service", "automation", "docs"];
8641
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
8642
+ var STATUSES2 = [
8643
+ "todo",
8644
+ "working",
8645
+ "checking",
8646
+ "ready",
8647
+ "accepted",
8648
+ "archived"
8649
+ ];
8650
+ var ROLES = [
8651
+ "pm",
8652
+ "architect",
8653
+ "designer",
8654
+ "web",
8655
+ "api",
8656
+ "mobile",
8657
+ "python",
8658
+ "automation",
8659
+ "qa",
8660
+ "devops",
8661
+ "sre",
8662
+ "security",
8663
+ "analyst"
8664
+ ];
8665
+ var AGENTS = [
8666
+ "claude_code",
8667
+ "codex",
8668
+ "workser_code",
8669
+ "opencode",
8670
+ "cursor",
8671
+ "gemini"
8672
+ ];
8673
+ var EFFORTS = ["low", "medium", "high", "xhigh", "max"];
8674
+ var KINDS2 = [
8675
+ "data_reports",
8676
+ "web",
8677
+ "mobile",
8678
+ "service",
8679
+ "automation",
8680
+ "docs"
8681
+ ];
8682
+ var KIND_HELP = "what it produces (web app \u2192 web \xB7 phone app \u2192 mobile \xB7 api or ai-agent \u2192 service \xB7 worker or scheduled job \u2192 automation \xB7 report or analysis \u2192 data_reports \xB7 written page \u2192 docs)";
8500
8683
  function registerTask(program3) {
8501
8684
  const task = program3.command("task").description("The project's tasks and their subtasks (AI Tech Team)");
8502
- task.command("list").description("List the board's tasks \u2014 run this before starting anything").option("--status <value>", `only tasks in this status (${STATUSES2.join(" | ")})`).option("--label <value>", "only tasks carrying this label").option("--limit <n>", "cap the number of tasks returned").action(
8685
+ task.command("list").description("List the board's tasks \u2014 run this before starting anything").option(
8686
+ "--status <value>",
8687
+ `only tasks in this status (${STATUSES2.join(" | ")})`
8688
+ ).option("--label <value>", "only tasks carrying this label").option("--limit <n>", "cap the number of tasks returned").action(
8503
8689
  action(async ({ ctx, opts }) => {
8504
8690
  requireProject(ctx);
8505
- if (opts.status !== void 0) assertOneOf("--status", opts.status, STATUSES2);
8691
+ if (opts.status !== void 0)
8692
+ assertOneOf("--status", opts.status, STATUSES2);
8506
8693
  const rows = await api(ctx, "/v1/project-tasks", {
8507
8694
  query: {
8508
8695
  status: opts.status,
@@ -8512,7 +8699,7 @@ function registerTask(program3) {
8512
8699
  }) ?? [];
8513
8700
  ok(rows, () => {
8514
8701
  if (!rows.length) {
8515
- line(import_picocolors28.default.dim("No tasks on the board yet."));
8702
+ line(import_picocolors29.default.dim("No tasks on the board yet."));
8516
8703
  return;
8517
8704
  }
8518
8705
  for (const r of rows) line(formatRow2(r));
@@ -8524,7 +8711,10 @@ function registerTask(program3) {
8524
8711
  ).action(
8525
8712
  action(async ({ ctx, args }) => {
8526
8713
  const id = args[0] || ctx.parentTaskId || resolveTaskId(ctx);
8527
- const row = await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}`);
8714
+ const row = await api(
8715
+ ctx,
8716
+ `/v1/project-tasks/${encodeURIComponent(id)}`
8717
+ );
8528
8718
  const goalId = row.goal_id;
8529
8719
  const goal = goalId ? await api(
8530
8720
  ctx,
@@ -8533,7 +8723,7 @@ function registerTask(program3) {
8533
8723
  ok({ ...row, goal }, () => printTask(row, goal));
8534
8724
  })
8535
8725
  );
8536
- task.command("create <title>").description("Open a new root task for the project team").option("--note <text>", "context, constraints and expected outcome").option("--kind <value>", `what it produces (${KINDS2.join(" | ")})`).option("--label <value...>", "labels to put on the task").option("--app <id...>", "apps the task touches").option("--infra <ref...>", "shared setup it touches").option("--goal <id>", "the business goal this delivers part of").option(
8726
+ task.command("create <title>").description("Open a new root task for the project team").option("--note <text>", "context, constraints and expected outcome").option("--kind <value>", KIND_HELP).option("--label <value...>", "labels to put on the task").option("--app <id...>", "apps the task touches").option("--infra <ref...>", "shared setup it touches").option("--goal <id>", "the business goal this delivers part of").option(
8537
8727
  "--phase <name>",
8538
8728
  "which slice of that goal \u2014 must match one of its phase names"
8539
8729
  ).action(
@@ -8554,8 +8744,14 @@ function registerTask(program3) {
8554
8744
  goalId: opts.goal,
8555
8745
  phase: opts.phase,
8556
8746
  targets: [
8557
- ...(opts.app ?? []).map((appId) => ({ kind: "app", appId })),
8558
- ...(opts.infra ?? []).map((ref) => ({ kind: "infra", ref }))
8747
+ ...(opts.app ?? []).map((appId) => ({
8748
+ kind: "app",
8749
+ appId
8750
+ })),
8751
+ ...(opts.infra ?? []).map((ref) => ({
8752
+ kind: "infra",
8753
+ ref
8754
+ }))
8559
8755
  ],
8560
8756
  ...hasChannelOrigin ? {
8561
8757
  channelId: ctx.projectChannelId,
@@ -8595,8 +8791,11 @@ function registerTask(program3) {
8595
8791
  ...channelMessageError ? { channelMessageError } : {}
8596
8792
  };
8597
8793
  ok(result, () => {
8598
- line(`${import_picocolors28.default.green("opened")} ${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.key ?? row.id)}`);
8599
- if (channelMessage) line(import_picocolors28.default.dim("posted to the channel as Project Manager"));
8794
+ line(
8795
+ `${import_picocolors29.default.green("opened")} ${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.key ?? row.id)}`
8796
+ );
8797
+ if (channelMessage)
8798
+ line(import_picocolors29.default.dim("posted to the channel as Project Manager"));
8600
8799
  if (channelMessageError) {
8601
8800
  warn(
8602
8801
  `Task opened, but its Project Manager card could not be posted: ${channelMessageError.message}`
@@ -8608,14 +8807,30 @@ function registerTask(program3) {
8608
8807
  const subtask = task.command("subtask").description("The subtasks a task is broken into");
8609
8808
  subtask.command("add <title>").description(
8610
8809
  'Add one subtask, e.g. `workser task subtask add "Build the upload screen" --role web`'
8611
- ).option("--task <id>", "the parent task (defaults to the task this run is inside)").option("--role <value>", `who does it (${ROLES.join(" | ")})`).option("--kind <value>", `what it produces (${KINDS2.join(" | ")})`).option("--note <text>", "one sentence on what this subtask does").option(
8810
+ ).option(
8811
+ "--task <id>",
8812
+ "the parent task (defaults to the task this run is inside)"
8813
+ ).option("--role <value>", `who does it (${ROLES.join(" | ")})`).option("--kind <value>", KIND_HELP).option("--note <text>", "one sentence on what this subtask does").option(
8612
8814
  "--app <id...>",
8613
8815
  "the app this subtask is for \u2014 one id runs it inside that app's folder; leave it off and it runs at the project, seeing every app"
8614
- ).option("--infra <ref...>", "shared setup it touches (database | storage | auth | hosting | jobs)").option("--scope <path...>", "files or folders THIS subtask owns").option("--depends-on <id...>", "subtasks that must finish first (key or id)").action(
8816
+ ).option(
8817
+ "--infra <ref...>",
8818
+ "shared setup it touches (database | storage | auth | hosting | jobs)"
8819
+ ).option("--scope <path...>", "files or folders THIS subtask owns").option(
8820
+ "--depends-on <id...>",
8821
+ "subtasks that must finish first (key or id)"
8822
+ ).option(
8823
+ "--agent <id>",
8824
+ `run this step on a specific CLI (${AGENTS.join(" | ")}) instead of the role's own`
8825
+ ).option("--model <id>", "the model that CLI should use for this step").option("--effort <level>", `how hard it thinks (${EFFORTS.join(" | ")})`).action(
8615
8826
  action(async ({ ctx, args, opts }) => {
8616
8827
  const parent = resolveTaskId(ctx, opts.task);
8617
8828
  if (opts.role !== void 0) assertOneOf("--role", opts.role, ROLES);
8618
8829
  if (opts.kind !== void 0) assertOneOf("--kind", opts.kind, KINDS2);
8830
+ if (opts.agent !== void 0) assertOneOf("--agent", opts.agent, AGENTS);
8831
+ if (opts.effort !== void 0)
8832
+ assertOneOf("--effort", opts.effort, EFFORTS);
8833
+ assertDelegatableModel("--model", opts.model);
8619
8834
  const dependsOn = await resolveDeps(ctx, parent, opts.dependsOn ?? []);
8620
8835
  const row = await api(ctx, "/v1/project-tasks", {
8621
8836
  body: {
@@ -8627,35 +8842,78 @@ function registerTask(program3) {
8627
8842
  scopePaths: opts.scope,
8628
8843
  dependsOn,
8629
8844
  targets: [
8630
- ...(opts.app ?? []).map((appId) => ({ kind: "app", appId })),
8631
- ...(opts.infra ?? []).map((ref) => ({ kind: "infra", ref }))
8845
+ ...(opts.app ?? []).map((appId) => ({
8846
+ kind: "app",
8847
+ appId
8848
+ })),
8849
+ ...(opts.infra ?? []).map((ref) => ({
8850
+ kind: "infra",
8851
+ ref
8852
+ }))
8632
8853
  ]
8633
8854
  }
8634
8855
  });
8635
- ok(row, () => {
8636
- line(`${import_picocolors28.default.green("added")} ${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.key ?? row.id)}`);
8637
- if (row.role) line(import_picocolors28.default.dim(`role: ${row.role}`));
8856
+ const runner = opts.agent !== void 0 || opts.model !== void 0 || opts.effort !== void 0 ? await api(
8857
+ ctx,
8858
+ `/v1/project-tasks/${encodeURIComponent(row.id)}`,
8859
+ {
8860
+ method: "PATCH",
8861
+ body: {
8862
+ agentOverride: opts.agent,
8863
+ agentModelOverride: opts.model,
8864
+ agentEffortOverride: opts.effort
8865
+ }
8866
+ }
8867
+ ) : null;
8868
+ ok(runner ?? row, () => {
8869
+ line(
8870
+ `${import_picocolors29.default.green("added")} ${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.key ?? row.id)}`
8871
+ );
8872
+ if (row.role) line(import_picocolors29.default.dim(`role: ${row.role}`));
8873
+ if (runner) {
8874
+ line(
8875
+ import_picocolors29.default.dim(
8876
+ `runs on: ${[opts.agent, opts.model, opts.effort].filter(Boolean).join(" \xB7 ")}`
8877
+ )
8878
+ );
8879
+ }
8638
8880
  });
8639
8881
  })
8640
8882
  );
8641
8883
  subtask.command("list [taskId]").description("The subtasks of a task, in order").action(
8642
8884
  action(async ({ ctx, args }) => {
8643
8885
  const id = resolveTaskId(ctx, args[0]);
8644
- const row = await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}`);
8886
+ const row = await api(
8887
+ ctx,
8888
+ `/v1/project-tasks/${encodeURIComponent(id)}`
8889
+ );
8645
8890
  const rows = row.subtasks ?? [];
8646
8891
  ok(rows, () => {
8647
8892
  if (!rows.length) {
8648
- line(import_picocolors28.default.dim("No subtasks yet."));
8893
+ line(import_picocolors29.default.dim("No subtasks yet."));
8649
8894
  return;
8650
8895
  }
8651
8896
  rows.forEach((r, i) => line(formatSubtask(r, i + 1)));
8652
8897
  });
8653
8898
  })
8654
8899
  );
8655
- subtask.command("update <id>").description("Change a subtask \u2014 its title, its role, what it touches").option("--title <text>").option("--note <text>").option("--role <value>", ROLES.join(" | ")).option("--kind <value>", KINDS2.join(" | ")).option("--scope <path...>", "replaces the subtask's scope entirely").action(
8900
+ subtask.command("update <id>").description("Change a subtask \u2014 its title, its role, what it touches").option("--title <text>").option("--note <text>").option("--role <value>", ROLES.join(" | ")).option("--kind <value>", KINDS2.join(" | ")).option("--scope <path...>", "replaces the subtask's scope entirely").option(
8901
+ "--agent <id>",
8902
+ `run this step on a specific CLI (${AGENTS.join(" | ")}); "default" hands it back to the role's own`
8903
+ ).option("--model <id>", 'the model that CLI should use; "default" clears it').option(
8904
+ "--effort <level>",
8905
+ `how hard it thinks (${EFFORTS.join(" | ")}); "default" clears it`
8906
+ ).action(
8656
8907
  action(async ({ ctx, args, opts }) => {
8657
8908
  if (opts.role !== void 0) assertOneOf("--role", opts.role, ROLES);
8658
8909
  if (opts.kind !== void 0) assertOneOf("--kind", opts.kind, KINDS2);
8910
+ if (opts.agent !== void 0 && opts.agent !== "default")
8911
+ assertOneOf("--agent", opts.agent, AGENTS);
8912
+ if (opts.effort !== void 0 && opts.effort !== "default")
8913
+ assertOneOf("--effort", opts.effort, EFFORTS);
8914
+ if (opts.model !== "default")
8915
+ assertDelegatableModel("--model", opts.model);
8916
+ const clearable = (value) => value === void 0 ? void 0 : value === "default" ? null : value;
8659
8917
  const row = await api(
8660
8918
  ctx,
8661
8919
  `/v1/project-tasks/${encodeURIComponent(args[0])}`,
@@ -8666,11 +8924,14 @@ function registerTask(program3) {
8666
8924
  summary: opts.note,
8667
8925
  role: opts.role,
8668
8926
  category: opts.kind,
8669
- scopePaths: opts.scope
8927
+ scopePaths: opts.scope,
8928
+ agentOverride: clearable(opts.agent),
8929
+ agentModelOverride: clearable(opts.model),
8930
+ agentEffortOverride: clearable(opts.effort)
8670
8931
  }
8671
8932
  }
8672
8933
  );
8673
- ok(row, () => line(`${import_picocolors28.default.green("updated")} ${import_picocolors28.default.bold(row.title)}`));
8934
+ ok(row, () => line(`${import_picocolors29.default.green("updated")} ${import_picocolors29.default.bold(row.title)}`));
8674
8935
  })
8675
8936
  );
8676
8937
  subtask.command("remove <id>").description("Remove a subtask (only before the work starts)").action(
@@ -8678,10 +8939,12 @@ function registerTask(program3) {
8678
8939
  await api(ctx, `/v1/project-tasks/${encodeURIComponent(args[0])}`, {
8679
8940
  method: "DELETE"
8680
8941
  });
8681
- ok({ removed: args[0] }, () => line(import_picocolors28.default.green("removed")));
8942
+ ok({ removed: args[0] }, () => line(import_picocolors29.default.green("removed")));
8682
8943
  })
8683
8944
  );
8684
- task.command("move <id> <status>").description(`Move a task or step along the board (${STATUSES2.join(" | ")})`).action(
8945
+ task.command("move <id> <status>").description(
8946
+ `Move a task or step along the board (${STATUSES2.join(" | ")})`
8947
+ ).action(
8685
8948
  action(async ({ ctx, args }) => {
8686
8949
  assertOneOf("<status>", args[1], STATUSES2);
8687
8950
  const row = await api(
@@ -8689,7 +8952,10 @@ function registerTask(program3) {
8689
8952
  `/v1/project-tasks/${encodeURIComponent(args[0])}/move`,
8690
8953
  { body: { status: args[1] } }
8691
8954
  );
8692
- ok(row, () => line(`${import_picocolors28.default.green("moved")} ${import_picocolors28.default.bold(row.title)} \u2192 ${args[1]}`));
8955
+ ok(
8956
+ row,
8957
+ () => line(`${import_picocolors29.default.green("moved")} ${import_picocolors29.default.bold(row.title)} \u2192 ${args[1]}`)
8958
+ );
8693
8959
  })
8694
8960
  );
8695
8961
  task.command("resume [id]").description(
@@ -8704,18 +8970,18 @@ function registerTask(program3) {
8704
8970
  );
8705
8971
  ok(row, () => {
8706
8972
  if (!row?.queued) {
8707
- line(import_picocolors28.default.yellow("could not resume it \u2014 check the step id"));
8973
+ line(import_picocolors29.default.yellow("could not resume it \u2014 check the step id"));
8708
8974
  return;
8709
8975
  }
8710
8976
  const started = row.started ?? 0;
8711
8977
  if (started > 0) {
8712
8978
  line(
8713
- `${import_picocolors28.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
8979
+ `${import_picocolors29.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
8714
8980
  );
8715
8981
  return;
8716
8982
  }
8717
8983
  line(
8718
- `${import_picocolors28.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
8984
+ `${import_picocolors29.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
8719
8985
  );
8720
8986
  });
8721
8987
  })
@@ -8729,18 +8995,20 @@ function registerTask(program3) {
8729
8995
  );
8730
8996
  ok(row, () => {
8731
8997
  if (row?.reopened) {
8732
- line(`${import_picocolors28.default.green("sent back")} \u2014 it will be picked up again`);
8998
+ line(`${import_picocolors29.default.green("sent back")} \u2014 it will be picked up again`);
8733
8999
  return;
8734
9000
  }
8735
9001
  line(
8736
- import_picocolors28.default.yellow(
9002
+ import_picocolors29.default.yellow(
8737
9003
  row?.reason === "already_open" ? "already waiting to be picked up \u2014 nothing to send back" : row?.reason === "still_working" ? "still working \u2014 let it finish before sending it back" : "could not send that step back"
8738
9004
  )
8739
9005
  );
8740
9006
  });
8741
9007
  })
8742
9008
  );
8743
- task.command("can-start [id]").description("Ask whether work on this task may begin. Refuses until the owner approves.").action(
9009
+ task.command("can-start [id]").description(
9010
+ "Ask whether work on this task may begin. Refuses until the owner approves."
9011
+ ).action(
8744
9012
  action(async ({ ctx, args }) => {
8745
9013
  const id = resolveTaskId(ctx, args[0]);
8746
9014
  const row = await api(
@@ -8748,7 +9016,7 @@ function registerTask(program3) {
8748
9016
  `/v1/project-tasks/${encodeURIComponent(id)}/dispatch-check`,
8749
9017
  { method: "POST" }
8750
9018
  );
8751
- ok(row, () => line(import_picocolors28.default.green("approved \u2014 you may start")));
9019
+ ok(row, () => line(import_picocolors29.default.green("approved \u2014 you may start")));
8752
9020
  })
8753
9021
  );
8754
9022
  task.command("start [id]").description(
@@ -8756,22 +9024,20 @@ function registerTask(program3) {
8756
9024
  ).action(
8757
9025
  action(async ({ ctx, args }) => {
8758
9026
  const id = resolveTaskId(ctx, args[0]);
8759
- const row = await api(
8760
- ctx,
8761
- `/v1/project-tasks/${encodeURIComponent(id)}/start`,
8762
- { method: "POST" }
8763
- );
9027
+ const row = await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}/start`, {
9028
+ method: "POST"
9029
+ });
8764
9030
  ok(row, () => {
8765
9031
  const started = row?.started ?? null;
8766
9032
  if (started && started > 0) {
8767
9033
  line(
8768
- import_picocolors28.default.green(
9034
+ import_picocolors29.default.green(
8769
9035
  `started ${started} step${started === 1 ? "" : "s"} \u2014 they are running now`
8770
9036
  )
8771
9037
  );
8772
9038
  return;
8773
9039
  }
8774
- line(import_picocolors28.default.yellow(row?.note ?? "Nothing started."));
9040
+ line(import_picocolors29.default.yellow(row?.note ?? "Nothing started."));
8775
9041
  });
8776
9042
  })
8777
9043
  );
@@ -8786,7 +9052,9 @@ function registerTask(program3) {
8786
9052
  );
8787
9053
  ok({ awaiting: row2.approval_state === "awaiting", task: row2 }, () => {
8788
9054
  line(
8789
- row2.approval_state === "awaiting" ? import_picocolors28.default.yellow("The plan is waiting on the owner. They see it in the task.") : `Already ${row2.approval_state}.`
9055
+ row2.approval_state === "awaiting" ? import_picocolors29.default.yellow(
9056
+ "The plan is waiting on the owner. They see it in the task."
9057
+ ) : `Already ${row2.approval_state}.`
8790
9058
  );
8791
9059
  });
8792
9060
  return;
@@ -8807,7 +9075,10 @@ function registerTask(program3) {
8807
9075
  }
8808
9076
  }
8809
9077
  );
8810
- ok(row, () => line(`${import_picocolors28.default.green(row.approval_state)} ${import_picocolors28.default.bold(row.title)}`));
9078
+ ok(
9079
+ row,
9080
+ () => line(`${import_picocolors29.default.green(row.approval_state)} ${import_picocolors29.default.bold(row.title)}`)
9081
+ );
8811
9082
  })
8812
9083
  );
8813
9084
  task.command("done [id]").description("Record what a subtask produced, and move it to ready").option("--summary <text>", "what changed, in the owner's words").action(
@@ -8822,7 +9093,7 @@ function registerTask(program3) {
8822
9093
  `/v1/project-tasks/${encodeURIComponent(id)}/move`,
8823
9094
  { body: { status: "ready" } }
8824
9095
  );
8825
- ok(row, () => line(`${import_picocolors28.default.green("ready")} ${import_picocolors28.default.bold(row.title)}`));
9096
+ ok(row, () => line(`${import_picocolors29.default.green("ready")} ${import_picocolors29.default.bold(row.title)}`));
8826
9097
  })
8827
9098
  );
8828
9099
  }
@@ -8867,24 +9138,24 @@ function assertOneOf(flag, value, allowed) {
8867
9138
  }
8868
9139
  }
8869
9140
  function formatRow2(r) {
8870
- const key = import_picocolors28.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
8871
- const steps = r.subtaskTotal ? import_picocolors28.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
8872
- const gate = r.approval_state === "awaiting" ? import_picocolors28.default.yellow(" awaiting approval") : "";
9141
+ const key = import_picocolors29.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
9142
+ const steps = r.subtaskTotal ? import_picocolors29.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
9143
+ const gate = r.approval_state === "awaiting" ? import_picocolors29.default.yellow(" awaiting approval") : "";
8873
9144
  return `${key} ${statusTag2(r.status)} ${r.title}${steps}${gate}`;
8874
9145
  }
8875
9146
  function formatSubtask(r, index) {
8876
- const n = import_picocolors28.default.dim(String(index).padStart(2, "0"));
8877
- const role = r.role ? import_picocolors28.default.dim(` [${r.role}]`) : "";
8878
- const scope = r.scope_paths?.length ? import_picocolors28.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
9147
+ const n = import_picocolors29.default.dim(String(index).padStart(2, "0"));
9148
+ const role = r.role ? import_picocolors29.default.dim(` [${r.role}]`) : "";
9149
+ const scope = r.scope_paths?.length ? import_picocolors29.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
8879
9150
  const head = `${n} ${statusTag2(r.status)} ${r.title}${role}${scope}`;
8880
9151
  const summary = (r.result_summary ?? "").trim();
8881
9152
  if (!summary) return head;
8882
9153
  const wrapped = summary.split("\n").map((l) => ` ${l}`).join("\n");
8883
9154
  return `${head}
8884
- ${import_picocolors28.default.dim(wrapped)}`;
9155
+ ${import_picocolors29.default.dim(wrapped)}`;
8885
9156
  }
8886
9157
  function printTask(row, goal) {
8887
- line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.key ?? row.id)}`);
9158
+ line(`${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.key ?? row.id)}`);
8888
9159
  line(`${statusTag2(row.status)} approval: ${row.approval_state}`);
8889
9160
  if (row.summary) line(`
8890
9161
  ${row.summary}`);
@@ -8897,49 +9168,53 @@ touches: ${row.targets.map((t) => t.appName ?? t.ref ?? t.kind).join(", ")}`
8897
9168
  }
8898
9169
  if (row.subtasks?.length) {
8899
9170
  line(`
8900
- ${import_picocolors28.default.bold("subtasks")}`);
9171
+ ${import_picocolors29.default.bold("subtasks")}`);
8901
9172
  row.subtasks.forEach((s, i) => line(formatSubtask(s, i + 1)));
8902
9173
  line(
8903
- import_picocolors28.default.dim(
9174
+ import_picocolors29.default.dim(
8904
9175
  `
8905
9176
  Run \`workser artifact list\` to see what these subtasks produced,`
8906
9177
  )
8907
9178
  );
8908
9179
  line(
8909
- import_picocolors28.default.dim(
9180
+ import_picocolors29.default.dim(
8910
9181
  `or \`workser artifact list --step <id>\` for one subtask's output alone.`
8911
9182
  )
8912
9183
  );
8913
9184
  } else {
8914
- line(import_picocolors28.default.dim("\nNo subtasks yet."));
9185
+ line(import_picocolors29.default.dim("\nNo subtasks yet."));
8915
9186
  }
8916
9187
  }
8917
9188
  function printGoalContext(row, goal) {
8918
9189
  const mine = row.phase ?? null;
8919
9190
  line(`
8920
- ${import_picocolors28.default.bold("part of")}: ${goal.title}`);
8921
- if (goal.outcome) line(import_picocolors28.default.dim(`done means: ${goal.outcome}`));
9191
+ ${import_picocolors29.default.bold("part of")}: ${goal.title}`);
9192
+ if (goal.outcome) line(import_picocolors29.default.dim(`done means: ${goal.outcome}`));
8922
9193
  const progress = goal.progress ?? [];
8923
9194
  for (const p of progress) {
8924
9195
  const where = p.total === 0 ? "not started" : `${p.done} of ${p.total} done`;
8925
- const here = mine && p.name === mine ? import_picocolors28.default.cyan(" <- this task") : "";
8926
- const mark = p.state === "done" ? import_picocolors28.default.green("*") : import_picocolors28.default.dim("-");
9196
+ const here = mine && p.name === mine ? import_picocolors29.default.cyan(" <- this task") : "";
9197
+ const mark = p.state === "done" ? import_picocolors29.default.green("*") : import_picocolors29.default.dim("-");
8927
9198
  line(` ${mark} ${p.name} \u2014 ${where}${here}`);
8928
9199
  }
8929
9200
  const next = progress.find((p) => p.state !== "done");
8930
9201
  const running = progress.some((p) => p.state === "working");
8931
9202
  if (next && !running) {
8932
9203
  line(
8933
- import_picocolors28.default.dim(
9204
+ import_picocolors29.default.dim(
8934
9205
  `
8935
9206
  Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to them in a sentence rather than starting it unasked.`
8936
9207
  )
8937
9208
  );
8938
9209
  } else if (!next) {
8939
- line(import_picocolors28.default.dim("\nEvery part of this plan is done. Say so, and offer to wrap it up."));
9210
+ line(
9211
+ import_picocolors29.default.dim(
9212
+ "\nEvery part of this plan is done. Say so, and offer to wrap it up."
9213
+ )
9214
+ );
8940
9215
  }
8941
9216
  line(
8942
- import_picocolors28.default.dim(
9217
+ import_picocolors29.default.dim(
8943
9218
  "The plan is a reference for what was agreed, not a rule about what may run. Work can start on any part at any time if they ask for it."
8944
9219
  )
8945
9220
  );
@@ -8947,22 +9222,22 @@ Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to t
8947
9222
  function statusTag2(status) {
8948
9223
  switch (status) {
8949
9224
  case "ready":
8950
- return import_picocolors28.default.green("[ready]");
9225
+ return import_picocolors29.default.green("[ready]");
8951
9226
  case "working":
8952
- return import_picocolors28.default.blue("[working]");
9227
+ return import_picocolors29.default.blue("[working]");
8953
9228
  case "checking":
8954
- return import_picocolors28.default.cyan("[checking]");
9229
+ return import_picocolors29.default.cyan("[checking]");
8955
9230
  case "accepted":
8956
- return import_picocolors28.default.green("[accepted]");
9231
+ return import_picocolors29.default.green("[accepted]");
8957
9232
  case "archived":
8958
- return import_picocolors28.default.dim("[archived]");
9233
+ return import_picocolors29.default.dim("[archived]");
8959
9234
  default:
8960
- return import_picocolors28.default.dim("[todo]");
9235
+ return import_picocolors29.default.dim("[todo]");
8961
9236
  }
8962
9237
  }
8963
9238
 
8964
9239
  // src/commands/goal.ts
8965
- var import_picocolors29 = __toESM(require_picocolors(), 1);
9240
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
8966
9241
  var STATUSES3 = ["proposed", "agreed", "working", "delivered", "abandoned"];
8967
9242
  function registerGoal(program3) {
8968
9243
  const goal = program3.command("goal").description("Business goals and the phases that deliver them");
@@ -8974,7 +9249,7 @@ function registerGoal(program3) {
8974
9249
  }) ?? [];
8975
9250
  ok(rows, () => {
8976
9251
  if (!rows.length) {
8977
- line(import_picocolors29.default.dim("No goals yet \u2014 every task here stands on its own."));
9252
+ line(import_picocolors30.default.dim("No goals yet \u2014 every task here stands on its own."));
8978
9253
  return;
8979
9254
  }
8980
9255
  for (const g of rows) line(formatGoal(g));
@@ -9051,10 +9326,10 @@ function registerGoal(program3) {
9051
9326
  }
9052
9327
  });
9053
9328
  ok(row, () => {
9054
- success(`Proposed ${import_picocolors29.default.bold(row?.title ?? "goal")}`);
9329
+ success(`Proposed ${import_picocolors30.default.bold(row?.title ?? "goal")}`);
9055
9330
  printGoal(row);
9056
9331
  line(
9057
- import_picocolors29.default.dim(
9332
+ import_picocolors30.default.dim(
9058
9333
  "\nNothing has been created yet. The owner agrees the shape first."
9059
9334
  )
9060
9335
  );
@@ -9083,7 +9358,7 @@ function registerGoal(program3) {
9083
9358
  ok(
9084
9359
  row,
9085
9360
  () => line(
9086
- row?.recorded ? met === true ? import_picocolors29.default.green("recorded \u2014 met") : met === false ? import_picocolors29.default.yellow("recorded \u2014 not met") : import_picocolors29.default.dim("recorded \u2014 back to unchecked") : import_picocolors29.default.yellow(
9361
+ row?.recorded ? met === true ? import_picocolors30.default.green("recorded \u2014 met") : met === false ? import_picocolors30.default.yellow("recorded \u2014 not met") : import_picocolors30.default.dim("recorded \u2014 back to unchecked") : import_picocolors30.default.yellow(
9087
9362
  "couldn't record it \u2014 check the goal id, the phase name and the criterion id"
9088
9363
  )
9089
9364
  )
@@ -9117,42 +9392,42 @@ function registerGoal(program3) {
9117
9392
  function appScope(g) {
9118
9393
  const n = g.appIds?.length ?? 0;
9119
9394
  if (!n) return "";
9120
- return import_picocolors29.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
9395
+ return import_picocolors30.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
9121
9396
  }
9122
9397
  function formatGoal(g) {
9123
- const where = g.currentPhase && g.status !== "delivered" ? import_picocolors29.default.dim(` now: ${g.currentPhase}`) : "";
9124
- const count = g.taskTotal != null ? import_picocolors29.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
9125
- return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors29.default.dim(g.id)}`;
9398
+ const where = g.currentPhase && g.status !== "delivered" ? import_picocolors30.default.dim(` now: ${g.currentPhase}`) : "";
9399
+ const count = g.taskTotal != null ? import_picocolors30.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
9400
+ return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors30.default.dim(g.id)}`;
9126
9401
  }
9127
9402
  function printGoal(g) {
9128
9403
  if (!g) return;
9129
- line(`${import_picocolors29.default.bold(g.title)} ${import_picocolors29.default.dim(g.id)}`);
9404
+ line(`${import_picocolors30.default.bold(g.title)} ${import_picocolors30.default.dim(g.id)}`);
9130
9405
  line(statusTag3(g.status));
9131
9406
  if (g.outcome) line(`
9132
9407
  done means: ${g.outcome}`);
9133
9408
  const progress = g.progress ?? [];
9134
9409
  if (!progress.length) {
9135
- line(import_picocolors29.default.dim("\nNo phases agreed yet."));
9410
+ line(import_picocolors30.default.dim("\nNo phases agreed yet."));
9136
9411
  return;
9137
9412
  }
9138
9413
  line(`
9139
- ${import_picocolors29.default.bold("phases")}`);
9414
+ ${import_picocolors30.default.bold("phases")}`);
9140
9415
  for (const p of progress) {
9141
- const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors29.default.dim("nothing filed yet");
9142
- const mark = p.state === "done" ? import_picocolors29.default.green("done") : p.state === "working" ? import_picocolors29.default.blue("working") : import_picocolors29.default.dim("waiting");
9416
+ const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors30.default.dim("nothing filed yet");
9417
+ const mark = p.state === "done" ? import_picocolors30.default.green("done") : p.state === "working" ? import_picocolors30.default.blue("working") : import_picocolors30.default.dim("waiting");
9143
9418
  line(
9144
- ` ${import_picocolors29.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors29.default.dim(bar2)}`
9419
+ ` ${import_picocolors30.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors30.default.dim(bar2)}`
9145
9420
  );
9146
9421
  for (const c of p.criteria ?? []) {
9147
- const tick = c.met === true ? import_picocolors29.default.green("\u2713") : c.met === false ? import_picocolors29.default.red("\u2717") : import_picocolors29.default.dim("\xB7");
9148
- line(` ${tick} ${c.text} ${import_picocolors29.default.dim(c.id)}`);
9149
- if (c.note) line(import_picocolors29.default.dim(` ${c.note}`));
9422
+ const tick = c.met === true ? import_picocolors30.default.green("\u2713") : c.met === false ? import_picocolors30.default.red("\u2717") : import_picocolors30.default.dim("\xB7");
9423
+ line(` ${tick} ${c.text} ${import_picocolors30.default.dim(c.id)}`);
9424
+ if (c.note) line(import_picocolors30.default.dim(` ${c.note}`));
9150
9425
  }
9151
9426
  }
9152
9427
  const current = progress.find((p) => p.name === g.currentPhase);
9153
9428
  if (current) {
9154
9429
  line(
9155
- import_picocolors29.default.dim(
9430
+ import_picocolors30.default.dim(
9156
9431
  `
9157
9432
  ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current.index} of ${progress.length}).`
9158
9433
  )
@@ -9162,15 +9437,15 @@ ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current
9162
9437
  function statusTag3(status) {
9163
9438
  switch (status) {
9164
9439
  case "agreed":
9165
- return import_picocolors29.default.cyan("[agreed]");
9440
+ return import_picocolors30.default.cyan("[agreed]");
9166
9441
  case "working":
9167
- return import_picocolors29.default.blue("[working]");
9442
+ return import_picocolors30.default.blue("[working]");
9168
9443
  case "delivered":
9169
- return import_picocolors29.default.green("[delivered]");
9444
+ return import_picocolors30.default.green("[delivered]");
9170
9445
  case "abandoned":
9171
- return import_picocolors29.default.dim("[abandoned]");
9446
+ return import_picocolors30.default.dim("[abandoned]");
9172
9447
  default:
9173
- return import_picocolors29.default.yellow("[proposed]");
9448
+ return import_picocolors30.default.yellow("[proposed]");
9174
9449
  }
9175
9450
  }
9176
9451
 
@@ -9345,7 +9620,7 @@ function stripLeadingGlobalOptions(argv) {
9345
9620
  }
9346
9621
 
9347
9622
  // src/commands/decision.ts
9348
- var import_picocolors30 = __toESM(require_picocolors(), 1);
9623
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
9349
9624
  function registerDecision(program3) {
9350
9625
  const decision = program3.command("decision").description("Read and record the project's architecture decisions");
9351
9626
  decision.command("list").description("Every decision on record \u2014 read this before changing how something works").option("--limit <n>", "cap the number returned (newest first)").action(
@@ -9358,11 +9633,11 @@ function registerDecision(program3) {
9358
9633
  rows = applyLimit(rows, opts.limit);
9359
9634
  ok(rows, () => {
9360
9635
  if (!rows.length) {
9361
- line(import_picocolors30.default.dim("No decisions recorded yet."));
9636
+ line(import_picocolors31.default.dim("No decisions recorded yet."));
9362
9637
  return;
9363
9638
  }
9364
9639
  for (const r of rows) {
9365
- line(`${import_picocolors30.default.dim(r.id)} ${import_picocolors30.default.dim(shortDate(r.createdAt))} ${r.title}`);
9640
+ line(`${import_picocolors31.default.dim(r.id)} ${import_picocolors31.default.dim(shortDate(r.createdAt))} ${r.title}`);
9366
9641
  line(` ${truncate(r.decision, 100)}`);
9367
9642
  }
9368
9643
  });
@@ -9376,16 +9651,16 @@ function registerDecision(program3) {
9376
9651
  `/v1/projects/${projectId}/architecture-decisions/${args[0]}`
9377
9652
  );
9378
9653
  ok(row, () => {
9379
- line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.id)}`);
9380
- line(import_picocolors30.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9654
+ line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9655
+ line(import_picocolors31.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9381
9656
  line(`
9382
- ${import_picocolors30.default.bold("Context")}
9657
+ ${import_picocolors31.default.bold("Context")}
9383
9658
  ${row.context}`);
9384
9659
  line(`
9385
- ${import_picocolors30.default.bold("Decision")}
9660
+ ${import_picocolors31.default.bold("Decision")}
9386
9661
  ${row.decision}`);
9387
9662
  if (row.consequences) line(`
9388
- ${import_picocolors30.default.bold("Consequences")}
9663
+ ${import_picocolors31.default.bold("Consequences")}
9389
9664
  ${row.consequences}`);
9390
9665
  });
9391
9666
  })
@@ -9414,7 +9689,7 @@ ${row.consequences}`);
9414
9689
  refId: row?.id,
9415
9690
  output: { decision: row }
9416
9691
  });
9417
- ok(row, () => line(`Recorded decision ${import_picocolors30.default.bold(row?.id ?? "")} \u2014 ${title}`));
9692
+ ok(row, () => line(`Recorded decision ${import_picocolors31.default.bold(row?.id ?? "")} \u2014 ${title}`));
9418
9693
  })
9419
9694
  );
9420
9695
  const requirement = program3.command("requirement").description("Read and record the project's requirements");
@@ -9426,11 +9701,11 @@ ${row.consequences}`);
9426
9701
  rows = applyLimit(rows, opts.limit);
9427
9702
  ok(rows, () => {
9428
9703
  if (!rows.length) {
9429
- line(import_picocolors30.default.dim("No requirements recorded yet."));
9704
+ line(import_picocolors31.default.dim("No requirements recorded yet."));
9430
9705
  return;
9431
9706
  }
9432
9707
  for (const r of rows) {
9433
- line(`${import_picocolors30.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
9708
+ line(`${import_picocolors31.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
9434
9709
  }
9435
9710
  });
9436
9711
  })
@@ -9443,8 +9718,8 @@ ${row.consequences}`);
9443
9718
  `/v1/projects/${projectId}/requirements/${args[0]}`
9444
9719
  );
9445
9720
  ok(row, () => {
9446
- line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.id)}`);
9447
- line(import_picocolors30.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9721
+ line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9722
+ line(import_picocolors31.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9448
9723
  line(`
9449
9724
  ${row.body}`);
9450
9725
  });
@@ -9470,7 +9745,7 @@ ${row.body}`);
9470
9745
  refId: row?.id,
9471
9746
  output: { requirement: row }
9472
9747
  });
9473
- ok(row, () => line(`Recorded requirement ${import_picocolors30.default.bold(row?.id ?? "")} \u2014 ${title}`));
9748
+ ok(row, () => line(`Recorded requirement ${import_picocolors31.default.bold(row?.id ?? "")} \u2014 ${title}`));
9474
9749
  })
9475
9750
  );
9476
9751
  requirement.command("update <id>").description(
@@ -9499,7 +9774,7 @@ ${row.body}`);
9499
9774
  code: "bad_request"
9500
9775
  });
9501
9776
  }
9502
- ok(row, () => line(`Updated requirement ${import_picocolors30.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
9777
+ ok(row, () => line(`Updated requirement ${import_picocolors31.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
9503
9778
  })
9504
9779
  );
9505
9780
  }
@@ -9522,7 +9797,7 @@ function shortDate(iso) {
9522
9797
  }
9523
9798
 
9524
9799
  // src/commands/doc.ts
9525
- var import_picocolors31 = __toESM(require_picocolors(), 1);
9800
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
9526
9801
 
9527
9802
  // src/mermaid-fences.ts
9528
9803
  function hasDiagram(code) {
@@ -9567,13 +9842,13 @@ function registerDoc(program3) {
9567
9842
  }) ?? [];
9568
9843
  ok(rows, () => {
9569
9844
  if (!rows.length) {
9570
- line(import_picocolors31.default.dim("No documents yet."));
9845
+ line(import_picocolors32.default.dim("No documents yet."));
9571
9846
  return;
9572
9847
  }
9573
9848
  for (const r of rows) {
9574
- const link = r.workItemId ? import_picocolors31.default.dim(` \u21B3 ${r.workItemId}`) : "";
9575
- const file = r.filePath ? import_picocolors31.default.dim(` ${r.filePath}`) : "";
9576
- line(`${import_picocolors31.default.dim(r.id)} ${r.title}${link}${file}`);
9849
+ const link = r.workItemId ? import_picocolors32.default.dim(` \u21B3 ${r.workItemId}`) : "";
9850
+ const file = r.filePath ? import_picocolors32.default.dim(` ${r.filePath}`) : "";
9851
+ line(`${import_picocolors32.default.dim(r.id)} ${r.title}${link}${file}`);
9577
9852
  }
9578
9853
  });
9579
9854
  })
@@ -9587,17 +9862,17 @@ function registerDoc(program3) {
9587
9862
  );
9588
9863
  if (opts.markdown) {
9589
9864
  ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
9590
- line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9865
+ line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.id)}`);
9591
9866
  line(
9592
- row.filePath ? `Read it at ${import_picocolors31.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors31.default.dim("This document has no markdown mirror on disk yet.")
9867
+ row.filePath ? `Read it at ${import_picocolors32.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors32.default.dim("This document has no markdown mirror on disk yet.")
9593
9868
  );
9594
9869
  });
9595
9870
  return;
9596
9871
  }
9597
9872
  ok(row, () => {
9598
- line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9599
- if (row.workItemId) line(import_picocolors31.default.dim(`linked to work item ${row.workItemId}`));
9600
- if (row.filePath) line(import_picocolors31.default.dim(`markdown mirror: ${row.filePath}`));
9873
+ line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.id)}`);
9874
+ if (row.workItemId) line(import_picocolors32.default.dim(`linked to work item ${row.workItemId}`));
9875
+ if (row.filePath) line(import_picocolors32.default.dim(`markdown mirror: ${row.filePath}`));
9601
9876
  line("");
9602
9877
  line(row.contentJson);
9603
9878
  });
@@ -9626,7 +9901,7 @@ function registerDoc(program3) {
9626
9901
  refId: row?.id,
9627
9902
  output: { document: row }
9628
9903
  });
9629
- ok(row, () => line(`Created document ${import_picocolors31.default.bold(row?.id ?? "")} \u2014 ${title}`));
9904
+ ok(row, () => line(`Created document ${import_picocolors32.default.bold(row?.id ?? "")} \u2014 ${title}`));
9630
9905
  })
9631
9906
  );
9632
9907
  doc.command("diagram <id>").description(
@@ -9652,19 +9927,19 @@ function registerDoc(program3) {
9652
9927
  diagrams: diagrams.map((code) => ({ kind: diagramKind(code), code }))
9653
9928
  };
9654
9929
  ok(payload, () => {
9655
- line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9930
+ line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.id)}`);
9656
9931
  if (markdown === null) {
9657
9932
  line(
9658
- import_picocolors31.default.dim(
9933
+ import_picocolors32.default.dim(
9659
9934
  row.filePath ? `Could not read ${row.filePath} from this folder.` : "This document has no markdown mirror on disk yet."
9660
9935
  )
9661
9936
  );
9662
9937
  } else if (!diagrams.length) {
9663
- line(import_picocolors31.default.dim("No diagrams in this document."));
9938
+ line(import_picocolors32.default.dim("No diagrams in this document."));
9664
9939
  } else {
9665
9940
  for (const [i, code] of diagrams.entries()) {
9666
9941
  const kind = diagramKind(code) ?? "diagram";
9667
- line(`${import_picocolors31.default.dim(String(i + 1))} ${kind} ${import_picocolors31.default.dim(`${code.split("\n").length} lines`)}`);
9942
+ line(`${import_picocolors32.default.dim(String(i + 1))} ${kind} ${import_picocolors32.default.dim(`${code.split("\n").length} lines`)}`);
9668
9943
  }
9669
9944
  }
9670
9945
  });
@@ -9700,7 +9975,7 @@ function registerDoc(program3) {
9700
9975
  code: "bad_request"
9701
9976
  });
9702
9977
  }
9703
- ok(row, () => line(`Updated document ${import_picocolors31.default.bold(row.id)} \u2014 ${row.title}`));
9978
+ ok(row, () => line(`Updated document ${import_picocolors32.default.bold(row.id)} \u2014 ${row.title}`));
9704
9979
  })
9705
9980
  );
9706
9981
  }
@@ -9714,7 +9989,7 @@ function readMirror(cwd, filePath) {
9714
9989
  }
9715
9990
 
9716
9991
  // src/commands/design.ts
9717
- var import_picocolors32 = __toESM(require_picocolors(), 1);
9992
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
9718
9993
  function registerDesign(program3) {
9719
9994
  const design = program3.command("design").description("Read the project's brand (colours, fonts, logo)");
9720
9995
  design.command("show").description("Show this project's brand \u2014 read it before writing any UI").option("--raw", "print the generated token files verbatim instead of a summary").action(
@@ -9728,11 +10003,11 @@ function registerDesign(program3) {
9728
10003
  if (opts.raw) {
9729
10004
  ok(files, () => {
9730
10005
  if (!files.length) {
9731
- line(import_picocolors32.default.dim("No brand set for this project."));
10006
+ line(import_picocolors33.default.dim("No brand set for this project."));
9732
10007
  return;
9733
10008
  }
9734
10009
  for (const f of files) {
9735
- line(import_picocolors32.default.bold(f.path));
10010
+ line(import_picocolors33.default.bold(f.path));
9736
10011
  line(f.contents);
9737
10012
  line("");
9738
10013
  }
@@ -9749,21 +10024,21 @@ function registerDesign(program3) {
9749
10024
  } : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
9750
10025
  ok(summary, () => {
9751
10026
  if (!tokens) {
9752
- line(import_picocolors32.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
10027
+ line(import_picocolors33.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
9753
10028
  return;
9754
10029
  }
9755
10030
  for (const [name, value] of Object.entries(tokens.brand)) {
9756
- line(`${import_picocolors32.default.dim(name.padEnd(12))} ${value}`);
10031
+ line(`${import_picocolors33.default.dim(name.padEnd(12))} ${value}`);
9757
10032
  }
9758
10033
  for (const [name, value] of Object.entries(tokens.color)) {
9759
- line(`${import_picocolors32.default.dim(`color.${name}`.padEnd(12))} ${value}`);
10034
+ line(`${import_picocolors33.default.dim(`color.${name}`.padEnd(12))} ${value}`);
9760
10035
  }
9761
10036
  for (const [name, value] of Object.entries(tokens.font)) {
9762
- line(`${import_picocolors32.default.dim(`font.${name}`.padEnd(12))} ${value}`);
10037
+ line(`${import_picocolors33.default.dim(`font.${name}`.padEnd(12))} ${value}`);
9763
10038
  }
9764
10039
  line("");
9765
10040
  line(
9766
- import_picocolors32.default.dim(
10041
+ import_picocolors33.default.dim(
9767
10042
  `Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
9768
10043
  )
9769
10044
  );
@@ -9794,7 +10069,7 @@ function unwrap(group) {
9794
10069
  }
9795
10070
 
9796
10071
  // src/commands/api.ts
9797
- var import_picocolors33 = __toESM(require_picocolors(), 1);
10072
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
9798
10073
  import { readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
9799
10074
  import { join as join3, relative, sep } from "path";
9800
10075
 
@@ -9905,13 +10180,13 @@ function registerApi(program3) {
9905
10180
  const cmd = program3.command("api").description("Call this service and check it describes its own routes");
9906
10181
  cmd.command("list").description("The requests saved with this service, in api/requests.json").option("--app <webAppId>", "the service, when this folder holds more than one").action(
9907
10182
  action(async ({ ctx, opts }) => {
9908
- const appId = requireApp(opts.app);
10183
+ const appId = requireApp2(opts.app);
9909
10184
  const res = await api(ctx, `/v1/apps/${encodeURIComponent(appId)}/api/requests`);
9910
10185
  ok(res, () => {
9911
- for (const note of res?.notes ?? []) line(import_picocolors33.default.dim(note));
10186
+ for (const note of res?.notes ?? []) line(import_picocolors34.default.dim(note));
9912
10187
  for (const r of res?.requests ?? []) {
9913
10188
  line(
9914
- `${import_picocolors33.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors33.default.dim(` ${r.note}`) : ""}`
10189
+ `${import_picocolors34.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors34.default.dim(` ${r.note}`) : ""}`
9915
10190
  );
9916
10191
  }
9917
10192
  });
@@ -9929,7 +10204,7 @@ function registerApi(program3) {
9929
10204
  "local (default), preview or production \u2014 the host comes from here, never from the path"
9930
10205
  ).action(
9931
10206
  action(async ({ ctx, args, opts }) => {
9932
- const appId = requireApp(opts.app);
10207
+ const appId = requireApp2(opts.app);
9933
10208
  const res = await api(
9934
10209
  ctx,
9935
10210
  `/v1/apps/${encodeURIComponent(appId)}/api/call`,
@@ -9945,14 +10220,14 @@ function registerApi(program3) {
9945
10220
  );
9946
10221
  ok(res, () => {
9947
10222
  if (!res?.ok) {
9948
- line(import_picocolors33.default.red(res?.error ?? "The service did not answer."));
10223
+ line(import_picocolors34.default.red(res?.error ?? "The service did not answer."));
9949
10224
  return;
9950
10225
  }
9951
10226
  const code = `${res.status}${res.statusText ? ` ${res.statusText}` : ""}`;
9952
- const colour2 = res.status && res.status < 300 ? import_picocolors33.default.green : res.status && res.status < 500 ? import_picocolors33.default.yellow : import_picocolors33.default.red;
9953
- line(`${colour2(code)} ${import_picocolors33.default.dim(`${res.durationMs}ms ${res.url}`)}`);
10227
+ const colour2 = res.status && res.status < 300 ? import_picocolors34.default.green : res.status && res.status < 500 ? import_picocolors34.default.yellow : import_picocolors34.default.red;
10228
+ line(`${colour2(code)} ${import_picocolors34.default.dim(`${res.durationMs}ms ${res.url}`)}`);
9954
10229
  if (res.body) line(res.body);
9955
- if (res.truncated) line(import_picocolors33.default.dim("(answer truncated)"));
10230
+ if (res.truncated) line(import_picocolors34.default.dim("(answer truncated)"));
9956
10231
  });
9957
10232
  if (!res?.ok) process.exitCode = 1;
9958
10233
  })
@@ -9972,15 +10247,15 @@ function registerApi(program3) {
9972
10247
  for (const r of report.routes) {
9973
10248
  const known = report.missing.some((m) => m.path === r.path);
9974
10249
  line(
9975
- `${known ? import_picocolors33.default.yellow("undocumented") : import_picocolors33.default.green("documented ")} ${r.path}${import_picocolors33.default.dim(` ${r.file}`)}`
10250
+ `${known ? import_picocolors34.default.yellow("undocumented") : import_picocolors34.default.green("documented ")} ${r.path}${import_picocolors34.default.dim(` ${r.file}`)}`
9976
10251
  );
9977
10252
  }
9978
10253
  for (const p of report.stale) {
9979
- line(`${import_picocolors33.default.dim("in spec only ")} ${p}`);
10254
+ line(`${import_picocolors34.default.dim("in spec only ")} ${p}`);
9980
10255
  }
9981
10256
  line("");
9982
10257
  if (report.ok && specFile) success(summary);
9983
- else line(import_picocolors33.default.yellow(summary));
10258
+ else line(import_picocolors34.default.yellow(summary));
9984
10259
  });
9985
10260
  if (opts.check && !report.ok) {
9986
10261
  throw new WorkserError(summary, { code: "bad_request" });
@@ -9996,7 +10271,7 @@ function collectHeader(raw, previous) {
9996
10271
  if (at <= 0) return previous;
9997
10272
  return { ...previous, [raw.slice(0, at).trim()]: raw.slice(at + 1).trim() };
9998
10273
  }
9999
- function requireApp(app) {
10274
+ function requireApp2(app) {
10000
10275
  const value = typeof app === "string" ? app.trim() : "";
10001
10276
  if (value) return value;
10002
10277
  throw new WorkserError(
@@ -10051,7 +10326,7 @@ function listRepoFiles(root, maxDepth = 8) {
10051
10326
  }
10052
10327
 
10053
10328
  // src/commands/analysis.ts
10054
- var import_picocolors34 = __toESM(require_picocolors(), 1);
10329
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
10055
10330
  import { readFileSync as readFileSync4 } from "fs";
10056
10331
  function registerAnalysis(program3) {
10057
10332
  const cmd = program3.command("analysis").description("Run Python analysis locally, recorded in the task");
@@ -10061,14 +10336,14 @@ function registerAnalysis(program3) {
10061
10336
  const res = await api(ctx, path);
10062
10337
  ok(res, () => {
10063
10338
  line(
10064
- `${res?.available ? import_picocolors34.default.green("python") : import_picocolors34.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors34.default.dim(res?.python ?? "")}`
10339
+ `${res?.available ? import_picocolors35.default.green("python") : import_picocolors35.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors35.default.dim(res?.python ?? "")}`
10065
10340
  );
10066
10341
  for (const lib of res?.libraries ?? []) {
10067
10342
  line(
10068
- `${lib.present ? import_picocolors34.default.green(lib.name) : import_picocolors34.default.yellow(lib.name)}${import_picocolors34.default.dim(lib.present ? "" : " missing")}`
10343
+ `${lib.present ? import_picocolors35.default.green(lib.name) : import_picocolors35.default.yellow(lib.name)}${import_picocolors35.default.dim(lib.present ? "" : " missing")}`
10069
10344
  );
10070
10345
  }
10071
- for (const note of res?.notes ?? []) line(import_picocolors34.default.dim(note));
10346
+ for (const note of res?.notes ?? []) line(import_picocolors35.default.dim(note));
10072
10347
  });
10073
10348
  if (!res?.available) process.exitCode = 1;
10074
10349
  })
@@ -10090,15 +10365,15 @@ function registerAnalysis(program3) {
10090
10365
  );
10091
10366
  ok(res, () => {
10092
10367
  if (res?.stdout) line(res.stdout.replace(/\n$/, ""));
10093
- if (res?.stderr) line(import_picocolors34.default.dim(res.stderr.replace(/\n$/, "")));
10094
- if (res?.truncated) line(import_picocolors34.default.dim("(output truncated)"));
10368
+ if (res?.stderr) line(import_picocolors35.default.dim(res.stderr.replace(/\n$/, "")));
10369
+ if (res?.truncated) line(import_picocolors35.default.dim("(output truncated)"));
10095
10370
  const took = `${Math.round((res?.durationMs ?? 0) / 100) / 10}s`;
10096
10371
  line(
10097
- res?.ok ? import_picocolors34.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors34.default.dim(` ${took}`) : import_picocolors34.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors34.default.dim(` ${took}`)
10372
+ res?.ok ? import_picocolors35.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors35.default.dim(` ${took}`) : import_picocolors35.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors35.default.dim(` ${took}`)
10098
10373
  );
10099
10374
  if (res && !res.sandboxed) {
10100
10375
  line(
10101
- import_picocolors34.default.dim(
10376
+ import_picocolors35.default.dim(
10102
10377
  "This platform has no OS sandbox, so the script ran with your own file access."
10103
10378
  )
10104
10379
  );
@@ -10126,7 +10401,7 @@ function readCode(file, inline) {
10126
10401
  }
10127
10402
 
10128
10403
  // src/commands/scan.ts
10129
- var import_picocolors35 = __toESM(require_picocolors(), 1);
10404
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
10130
10405
  import { spawnSync as spawnSync2 } from "child_process";
10131
10406
  import { existsSync as existsSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
10132
10407
  import { join as join4, relative as relative2, sep as sep2 } from "path";
@@ -10410,22 +10685,22 @@ function runPermissions(cwd, findings, checked, skipped) {
10410
10685
  }
10411
10686
  function print2(report, summary) {
10412
10687
  for (const s of report.skipped) {
10413
- line(`${import_picocolors35.default.yellow("not checked")} ${s.check}${import_picocolors35.default.dim(` \u2014 ${s.reason}`)}`);
10688
+ line(`${import_picocolors36.default.yellow("not checked")} ${s.check}${import_picocolors36.default.dim(` \u2014 ${s.reason}`)}`);
10414
10689
  }
10415
10690
  for (const f of report.findings) {
10416
- const where = f.file ? import_picocolors35.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
10691
+ const where = f.file ? import_picocolors36.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
10417
10692
  line(`${severityTag(f.severity)} ${f.title}${where}`);
10418
- line(` ${import_picocolors35.default.dim(f.fix)}`);
10693
+ line(` ${import_picocolors36.default.dim(f.fix)}`);
10419
10694
  }
10420
10695
  if (report.findings.length || report.skipped.length) line("");
10421
10696
  if (report.ok && !report.skipped.length) success(summary);
10422
- else if (report.ok) line(import_picocolors35.default.yellow(summary));
10423
- else line(import_picocolors35.default.red(summary));
10697
+ else if (report.ok) line(import_picocolors36.default.yellow(summary));
10698
+ else line(import_picocolors36.default.red(summary));
10424
10699
  }
10425
10700
  function severityTag(severity) {
10426
- if (severity === "high") return import_picocolors35.default.red("serious ");
10427
- if (severity === "medium") return import_picocolors35.default.yellow("worth fixing");
10428
- return import_picocolors35.default.dim("minor ");
10701
+ if (severity === "high") return import_picocolors36.default.red("serious ");
10702
+ if (severity === "medium") return import_picocolors36.default.yellow("worth fixing");
10703
+ return import_picocolors36.default.dim("minor ");
10429
10704
  }
10430
10705
  function git(cwd, args) {
10431
10706
  try {
@@ -10486,7 +10761,7 @@ function listRepoFiles2(root, maxDepth = 8) {
10486
10761
  }
10487
10762
 
10488
10763
  // src/commands/health.ts
10489
- var import_picocolors36 = __toESM(require_picocolors(), 1);
10764
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
10490
10765
  function registerHealth(program3) {
10491
10766
  program3.command("health").description("Check that the published apps in this project are still answering").option("--app <webAppId>", "check one app rather than all of them").action(
10492
10767
  action(async ({ ctx, opts }) => {
@@ -10500,16 +10775,16 @@ function registerHealth(program3) {
10500
10775
  }
10501
10776
  function print3(res) {
10502
10777
  if (!res?.checks?.length) {
10503
- line(import_picocolors36.default.dim(res?.note ?? "Nothing to check."));
10778
+ line(import_picocolors37.default.dim(res?.note ?? "Nothing to check."));
10504
10779
  return;
10505
10780
  }
10506
10781
  for (const c of res.checks) {
10507
- const mark = c.ok ? import_picocolors36.default.green("up ") : import_picocolors36.default.red("down");
10508
- const timing = import_picocolors36.default.dim(`${c.ms}ms`);
10509
- const detail = c.ok ? timing : import_picocolors36.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
10510
- line(` ${mark} ${c.appName} ${import_picocolors36.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
10782
+ const mark = c.ok ? import_picocolors37.default.green("up ") : import_picocolors37.default.red("down");
10783
+ const timing = import_picocolors37.default.dim(`${c.ms}ms`);
10784
+ const detail = c.ok ? timing : import_picocolors37.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
10785
+ line(` ${mark} ${c.appName} ${import_picocolors37.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
10511
10786
  if (c.incidentOpened) {
10512
- line(import_picocolors36.default.yellow(` An incident has been opened on the board for this.`));
10787
+ line(import_picocolors37.default.yellow(` An incident has been opened on the board for this.`));
10513
10788
  }
10514
10789
  }
10515
10790
  const down = res.checks.filter((c) => !c.ok);
@@ -10522,14 +10797,14 @@ function print3(res) {
10522
10797
  }
10523
10798
  const production = down.filter((c) => c.environment === "production").length;
10524
10799
  line(
10525
- import_picocolors36.default.red(
10800
+ import_picocolors37.default.red(
10526
10801
  `${down.length} of ${res.checks.length} not answering` + (production ? ` \u2014 ${production} customer-facing.` : " (preview only).")
10527
10802
  )
10528
10803
  );
10529
10804
  }
10530
10805
 
10531
10806
  // src/commands/urls.ts
10532
- var import_picocolors37 = __toESM(require_picocolors(), 1);
10807
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
10533
10808
  function registerUrls(program3) {
10534
10809
  program3.command("urls").description("The stable preview and production addresses of every app in this project").option("--app <webAppId>", "just one app").action(
10535
10810
  action(async ({ ctx, opts }) => {
@@ -10543,20 +10818,20 @@ function registerUrls(program3) {
10543
10818
  const summary = urlsSummary(rows);
10544
10819
  ok({ rows, summary }, () => {
10545
10820
  for (const row of rows) {
10546
- const label = import_picocolors37.default.dim(row.environment.padEnd(10));
10547
- const value = row.url ? import_picocolors37.default.cyan(row.url) : import_picocolors37.default.dim(row.note ?? "not published");
10821
+ const label = import_picocolors38.default.dim(row.environment.padEnd(10));
10822
+ const value = row.url ? import_picocolors38.default.cyan(row.url) : import_picocolors38.default.dim(row.note ?? "not published");
10548
10823
  line(` ${row.appName.padEnd(22)} ${label} ${value}`);
10549
10824
  }
10550
10825
  line("");
10551
10826
  if (rows.some((r) => r.url)) success(summary);
10552
- else line(import_picocolors37.default.yellow(summary));
10827
+ else line(import_picocolors38.default.yellow(summary));
10553
10828
  });
10554
10829
  })
10555
10830
  );
10556
10831
  }
10557
10832
 
10558
10833
  // src/commands/deployments.ts
10559
- var import_picocolors38 = __toESM(require_picocolors(), 1);
10834
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
10560
10835
  function registerDeployments(program3) {
10561
10836
  const cmd = program3.command("deployments").description("Deployment history, and putting a build in front of customers");
10562
10837
  cmd.command("list").description("What has been built, newest first").option("--app <webAppId>", "just one app (default: every app in the project)").option("--env <environment>", "preview or production").option("--limit <n>", "how many to show", "20").action(
@@ -10574,7 +10849,7 @@ function registerDeployments(program3) {
10574
10849
  ok(res, () => {
10575
10850
  if (!items.length) {
10576
10851
  return line(
10577
- import_picocolors38.default.dim(
10852
+ import_picocolors39.default.dim(
10578
10853
  environment ? `Nothing has been deployed to ${environment} yet.` : "Nothing has been deployed yet. `workser deploy` builds the first one."
10579
10854
  )
10580
10855
  );
@@ -10594,13 +10869,13 @@ function registerDeployments(program3) {
10594
10869
  ).catch(() => null) : null;
10595
10870
  ok({ ...dep, logs }, () => {
10596
10871
  line(formatDeployment(dep));
10597
- if (dep?.error_message) line(import_picocolors38.default.red(` ${dep.error_message}`));
10872
+ if (dep?.error_message) line(import_picocolors39.default.red(` ${dep.error_message}`));
10598
10873
  const events = logs?.events ?? [];
10599
10874
  for (const e of events) {
10600
- line(` ${import_picocolors38.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
10875
+ line(` ${import_picocolors39.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
10601
10876
  }
10602
10877
  if (opts.logs && !events.length) {
10603
- line(import_picocolors38.default.dim(" That build produced no output."));
10878
+ line(import_picocolors39.default.dim(" That build produced no output."));
10604
10879
  }
10605
10880
  });
10606
10881
  })
@@ -10644,16 +10919,16 @@ function printPromoted(res, version) {
10644
10919
  const what = version === null ? "the latest build" : `version ${version}`;
10645
10920
  const url = res.url ?? res.vercel_url;
10646
10921
  success(`Production is being rebuilt from ${what}.`);
10647
- if (url) line(import_picocolors38.default.dim(`It will be at ${url}`));
10648
- line(import_picocolors38.default.dim("`workser deploy status` follows it."));
10922
+ if (url) line(import_picocolors39.default.dim(`It will be at ${url}`));
10923
+ line(import_picocolors39.default.dim("`workser deploy status` follows it."));
10649
10924
  }
10650
10925
  function formatDeployment(d) {
10651
10926
  if (!d) return "";
10652
- const version = d.version !== void 0 ? import_picocolors38.default.yellow(`v${d.version}`) : import_picocolors38.default.dim("v?");
10653
- const env = import_picocolors38.default.dim((d.environment ?? "?").padEnd(10));
10927
+ const version = d.version !== void 0 ? import_picocolors39.default.yellow(`v${d.version}`) : import_picocolors39.default.dim("v?");
10928
+ const env = import_picocolors39.default.dim((d.environment ?? "?").padEnd(10));
10654
10929
  const app = d.webAppName ? `${d.webAppName} ` : "";
10655
- const when = import_picocolors38.default.dim(formatTime2(d.created_at));
10656
- const url = d.url ? " " + import_picocolors38.default.cyan(d.url) : "";
10930
+ const when = import_picocolors39.default.dim(formatTime2(d.created_at));
10931
+ const url = d.url ? " " + import_picocolors39.default.cyan(d.url) : "";
10657
10932
  return `${version} ${env} ${colorStatus(d.status ?? "")} ${app}${when}${url}`;
10658
10933
  }
10659
10934
  function formatTime2(t) {
@@ -10663,7 +10938,7 @@ function formatTime2(t) {
10663
10938
  }
10664
10939
 
10665
10940
  // src/commands/usage.ts
10666
- var import_picocolors39 = __toESM(require_picocolors(), 1);
10941
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
10667
10942
 
10668
10943
  // src/usage.ts
10669
10944
  var NEAR_LIMIT_FRACTION = 0.8;
@@ -10758,7 +11033,7 @@ function registerUsage(program3) {
10758
11033
  function print4(report) {
10759
11034
  const dims = report.dimensions ?? [];
10760
11035
  if (!dims.length) {
10761
- return line(import_picocolors39.default.dim("Nothing to measure for this project yet."));
11036
+ return line(import_picocolors40.default.dim("Nothing to measure for this project yet."));
10762
11037
  }
10763
11038
  const width = Math.max(...dims.map((d) => d.label.length));
10764
11039
  for (const d of dims) {
@@ -10767,23 +11042,23 @@ function print4(report) {
10767
11042
  line("");
10768
11043
  const summary = usageSummary(report);
10769
11044
  const worst = dims.map(usageState);
10770
- if (worst.includes("over")) line(import_picocolors39.default.red(summary));
11045
+ if (worst.includes("over")) line(import_picocolors40.default.red(summary));
10771
11046
  else if (worst.includes("near") || worst.includes("unknown"))
10772
- line(import_picocolors39.default.yellow(summary));
11047
+ line(import_picocolors40.default.yellow(summary));
10773
11048
  else success(summary);
10774
11049
  }
10775
11050
  function gauge(d, _labelWidth) {
10776
11051
  const drawn = bar(d);
10777
- return drawn ? ` ${import_picocolors39.default.dim(drawn)}` : "";
11052
+ return drawn ? ` ${import_picocolors40.default.dim(drawn)}` : "";
10778
11053
  }
10779
11054
  function colour(d) {
10780
11055
  switch (usageState(d)) {
10781
11056
  case "over":
10782
- return d.kind === "hard" ? import_picocolors39.default.red : import_picocolors39.default.yellow;
11057
+ return d.kind === "hard" ? import_picocolors40.default.red : import_picocolors40.default.yellow;
10783
11058
  case "near":
10784
- return import_picocolors39.default.yellow;
11059
+ return import_picocolors40.default.yellow;
10785
11060
  case "unknown":
10786
- return import_picocolors39.default.dim;
11061
+ return import_picocolors40.default.dim;
10787
11062
  default:
10788
11063
  return (s) => s;
10789
11064
  }
@@ -10791,7 +11066,7 @@ function colour(d) {
10791
11066
 
10792
11067
  // src/index.ts
10793
11068
  var pkg = {
10794
- version: true ? "0.6.7" : "0.0.0-dev"
11069
+ version: true ? "0.6.9" : "0.0.0-dev"
10795
11070
  };
10796
11071
  var program2 = new Command();
10797
11072
  program2.name("workser").description(
@@ -10816,6 +11091,7 @@ registerAuth(program2);
10816
11091
  registerStorage(program2);
10817
11092
  registerNeon(program2);
10818
11093
  registerEnv(program2);
11094
+ registerKey(program2);
10819
11095
  registerDeploy(program2);
10820
11096
  registerVersions(program2);
10821
11097
  registerLogs(program2);