@workser/cli 0.6.27 → 0.6.28

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
@@ -3857,26 +3857,28 @@ publishes an agent that never answers anybody. Pictures, video, voice and
3857
3857
  transcription are abilities, not models you set, and one call from the app's
3858
3858
  own code needs no agent at all: \`workser help agent-cloud-media\`.
3859
3859
 
3860
- ## Agents need a paid plan \u2014 the trial does not include them
3860
+ ## Every plan can use Agent Cloud \u2014 runs use credits
3861
3861
 
3862
- \`create\` and \`run\` both refuse with **402** on a free or trialling
3863
- organisation. That is a rule, not a fault: every run buys model tokens and
3864
- holds a machine, so it costs real money the moment it happens.
3862
+ Creating, teaching and publishing an agent are available on free, trial and paid
3863
+ plans. A \`try\` or live \`run\` spends real money on model usage and hosted runtime,
3864
+ so it starts only when the organisation's shared AI-credit wallet clears the
3865
+ minimum balance shown in the refusal.
3865
3866
 
3866
- **When you hit it, hand the plan over \u2014 do not just describe it.** Workser
3867
+ **When you hit a 402, hand the credit top-up over \u2014 do not just describe it.** Workser
3867
3868
  files the refusal on the conversation as a clickable step and artifact, so the
3868
- person gets something actionable. Say what you were doing, that agents need a
3869
- plan, and stop.
3869
+ person gets something actionable. Say what you were doing, that the wallet needs
3870
+ credits, and stop. The same rule applies when the owner supplies their own model
3871
+ key because the hosted runtime and fallback path still cost money.
3870
3872
 
3871
3873
  Do NOT:
3872
3874
 
3873
- - retry the call, or try a different agent \u2014 the answer is the same
3875
+ - retry the call, or try a different agent \u2014 the wallet is shared
3874
3876
  - try to work around it by running the job yourself as a coding agent; that is
3875
3877
  a different product and it will not be there when the user closes the window
3876
- - guess at prices, discounts or trial extensions
3878
+ - guess at prices, discounts or free usage
3877
3879
 
3878
3880
  If the user asks how much: it is per minute of run time plus the model, and the
3879
- plan page has the current figures. You do not have them.
3881
+ Agent Cloud usage view has the current figures. You do not have them.
3880
3882
 
3881
3883
  ## When to reach for this
3882
3884
 
@@ -3897,7 +3899,7 @@ workflow\`.
3897
3899
  ## If Agent Cloud is ruled out, there is an order
3898
3900
 
3899
3901
  Propose Agent Cloud first, every time. If it genuinely does not fit \u2014 the owner
3900
- has said no to the plan, or wants their own code in their own framework \u2014 go
3902
+ does not want a managed runtime, or wants their own code in their own framework \u2014 go
3901
3903
  down this list in order, and say which rung you took and why you skipped the
3902
3904
  ones above it:
3903
3905
 
@@ -3952,9 +3954,10 @@ watching sees the agent think. See the \`workser-sdk\` skill, \`reference/agents
3952
3954
  what to do, what to leave alone, when to ask. Vague instructions are the
3953
3955
  single biggest cause of an agent that "doesn't work".
3954
3956
 
3955
- 3. **Free plans cannot run agents at all**, and a trial has a small allowance.
3956
- A \`402\` with \`spend_limit_reached\` is not a bug \u2014 tell the user what it says
3957
- and point them at their plan.
3957
+ 3. **Every plan can run agents once the shared wallet has enough credits.** A
3958
+ \`402\` with \`spend_limit_reached\` means the wallet needs a top-up (or the owner
3959
+ reached their own spend cap). Tell them exactly what it says and hand over
3960
+ the credit action; never suggest a subscription upgrade for this refusal.
3958
3961
 
3959
3962
  4. **Say who it is for.** An agent acting for one of the app's customers needs
3960
3963
  \`referenceUserId\`, or its memory and audit trail belong to nobody.
@@ -5045,7 +5048,8 @@ that URL directly in the app.
5045
5048
 
5046
5049
  \`\`\`bash
5047
5050
  workser image generate "flat illustration of a farm delivery van, brand colors" --json
5048
- workser image gen "same van, from the side" -r https://\u2026 -o ./public/van.png --json
5051
+ workser image gen "same van, from the side" -r https://\u2026 --json # use .data.url
5052
+ workser image gen "van, rear view" -o /tmp/van.png --json # only if you must
5049
5053
  \`\`\`
5050
5054
 
5051
5055
  ## Notes that matter (generation)
@@ -5056,6 +5060,16 @@ workser image gen "same van, from the side" -r https://\u2026 -o ./public/van.pn
5056
5060
  question comes back as text rather than an image. Check that you actually got an
5057
5061
  image before wiring the URL into a page; an empty result is not a transport error
5058
5062
  to retry.
5063
+ - **The URL is the deliverable \u2014 don't download it into the app folder.** This
5064
+ example used to write to \`./public/van.png\`, which is the single most common way
5065
+ generated art ends up committed to the user's repository: in every deploy bundle
5066
+ for ever, unreplaceable without a redeploy, against a 25MB publish cap. The
5067
+ returned URL is already public and already served. Reference it.
5068
+
5069
+ When a file genuinely has to exist \u2014 an asset the build reads, something to hand
5070
+ the user \u2014 \`-o\` into a temp path and \`workser storage put\` it into the bucket
5071
+ (or whatever store the owner chose; the repo is never it). See
5072
+ \`reference/storage.md\`.
5059
5073
  - **\`--output\` writes only the first image.** If you asked for several, the rest
5060
5074
  exist only as URLs.
5061
5075
  - **Placeholder art is not a deliverable.** Generating a hero image to unblock a
@@ -5163,6 +5177,87 @@ deployment keeps using whatever it was built with until it's redeployed \u2014
5163
5177
  run \`workser deploy\` (\`--prod\` for the production key) to pick up the new
5164
5178
  value. \`key rotate\` prints the new secret exactly once; it is never shown
5165
5179
  again or stored anywhere in the clear.
5180
+ `
5181
+ },
5182
+ {
5183
+ topic: "line",
5184
+ title: "LINE Official Account",
5185
+ summary: "Connect a LINE OA once, then send, reply, broadcast, build rich menus and read insight \u2014 from the CLI or from an agent.",
5186
+ commands: ["line"],
5187
+ source: "skills/workser/reference/line.md",
5188
+ body: `# LINE Official Account
5189
+
5190
+ Connect the business's LINE account to this project **once**. After that the same
5191
+ account is reachable from here, from Workser Code, and from any Agent Cloud agent
5192
+ in this project \u2014 including the one a LINE message starts.
5193
+
5194
+ \`\`\`
5195
+ workser line status # is anything connected?
5196
+ workser line connect --token <channelAccessToken> [--secret <channelSecret>]
5197
+ workser line verify # ask LINE if the token still works
5198
+ workser line disconnect
5199
+
5200
+ workser line ops [--group messaging|richmenu|audience|insight|people|group|quota|content|account]
5201
+ workser line call <operation> --params '<json>' # any of the 70 operations
5202
+
5203
+ workser line send <to> "<text>" # push to a user, group or room id
5204
+ workser line reply <replyToken> "<text>" # answer a message \u2014 free, single-use
5205
+ workser line broadcast "<text>" # EVERY follower, one message each
5206
+ workser line quota # allowance left this month
5207
+ workser line profile <userId>
5208
+ \`\`\`
5209
+
5210
+ ## Getting the token
5211
+
5212
+ LINE Developers console \u2192 your Messaging API channel \u2192 **Messaging API** tab \u2192
5213
+ *Channel access token* (long-lived). \`--secret\` is the **Basic settings** \u2192
5214
+ *Channel secret*, and is only needed if you want LINE's own signature check on
5215
+ incoming webhooks.
5216
+
5217
+ \`connect\` calls LINE immediately to prove the token works, so a mistyped token
5218
+ fails here rather than an hour later as a customer message nobody answered.
5219
+
5220
+ ## \`call\` is the whole API
5221
+
5222
+ \`ops\` prints every operation with a one-line summary. The \`\u25CF\` marks the ones that
5223
+ send something, spend quota, or change the account.
5224
+
5225
+ \`\`\`
5226
+ workser line call push --params '{"to":"U4af\u2026","messages":[{"type":"text","text":"Your order shipped"}]}'
5227
+ workser line call richmenu_list
5228
+ workser line call insight_followers --params '{"date":"20260909"}'
5229
+ workser line call webhook_endpoint_set --params '{"endpoint":"https://\u2026"}'
5230
+ \`\`\`
5231
+
5232
+ Parameters are flat \u2014 path, query and body fields all go in \`--params\` together and
5233
+ the server sorts them. Fields it has never heard of are **forwarded to LINE
5234
+ untouched**, so a flex component LINE shipped last week works today.
5235
+
5236
+ ## Reply beats push
5237
+
5238
+ \`reply\` uses the token that came with the incoming message. It is **free** and does
5239
+ not touch the monthly quota; \`push\` costs one message per recipient. The token is
5240
+ single-use and expires within a minute or so, so reply first and fall back to push.
5241
+
5242
+ \`broadcast\` sends to every follower and spends one message each. On a large account
5243
+ that is the most expensive call in the list \u2014 say what it will cost before running it.
5244
+
5245
+ ## Rich menus, in order
5246
+
5247
+ A rich menu does nothing until it has both a picture and a place to appear:
5248
+
5249
+ 1. \`richmenu_create\` \u2014 the layout and tappable areas. Returns an id.
5250
+ 2. \`richmenu_upload_image\` \u2014 \`content\` is the file **base64-encoded**, with
5251
+ \`content_type\`. The image must match the declared size exactly.
5252
+ 3. \`richmenu_set_default\` (everybody) or \`richmenu_link_user\` (one person).
5253
+
5254
+ \`richmenu_validate\` checks a layout without creating it.
5255
+
5256
+ ## When it is an agent doing this
5257
+
5258
+ An Agent Cloud agent in a project with a connected LINE account gets these as tools
5259
+ automatically \u2014 there is nothing to bind. Sends go through the agent's approval gate;
5260
+ reads do not. See \`workser help agent-cloud\`.
5166
5261
  `
5167
5262
  },
5168
5263
  {
@@ -5533,6 +5628,40 @@ workser storage put <local> <key> # upload a file into the bucket
5533
5628
  workser storage get <key> [dest] # download an object (or print its URL)
5534
5629
  \`\`\`
5535
5630
 
5631
+ ## This is where media goes \u2014 the folder is for code
5632
+
5633
+ Every project has this bucket, provisioned and paid for, served over a CDN, and
5634
+ visible to the owner on the Files screen. It is the **default** home for generated
5635
+ art, product photography, PDFs, exports, avatars and anything a user uploads.
5636
+
5637
+ **Default, not mandate.** An owner who wants Cloudinary, their own S3, or anything
5638
+ else is entitled to it \u2014 build it and don't argue. What is not yours to do is pick
5639
+ the alternative for them, or drift into one because it was quicker. If you think
5640
+ there is a real reason to go outside, say so and let them answer, then
5641
+ \`workser decision create\` it so the next agent doesn't quietly reverse it.
5642
+
5643
+ **The repo is not one of the options.** Whichever provider the owner chose, files
5644
+ under \`public/\`, \`assets/\` or \`static/\` cost them four things, none recoverable
5645
+ later:
5646
+
5647
+ - **They are committed.** Publishing runs \`git add -A\` over the app folder, so
5648
+ every image enters the repository's history. Deleting it afterwards does not
5649
+ remove it.
5650
+ - **They ride in every deploy.** The source bundle has a 25MB cap; enough media
5651
+ and publishing stops working outright, with an error about the bundle rather
5652
+ than about the images.
5653
+ - **They cannot change without a redeploy.** A photo the owner wants swapped
5654
+ becomes a code change and a build. From the bucket it is one \`storage put\`.
5655
+ - **They are invisible.** The Files screen lists the bucket. Nothing there shows
5656
+ what is sitting in the repo, so the owner cannot find, replace or delete it.
5657
+
5658
+ Small build-time assets \u2014 a logo, a favicon, an icon, an SVG the bundler inlines \u2014
5659
+ are the exception. Anything that is content, or that a user produced, is not.
5660
+
5661
+ \`\`\`bash
5662
+ workser storage put ./out/hero.png products/hero.png --json # \u2192 .data.url
5663
+ \`\`\`
5664
+
5536
5665
  ## Notes that matter
5537
5666
 
5538
5667
  - **One bucket per project, shared by its apps.** Namespace your keys by app or
@@ -5542,7 +5671,9 @@ workser storage get <key> [dest] # download an object (or print its URL)
5542
5671
  - **This is not where app uploads should go through you.** At runtime the app uses
5543
5672
  \`workser.storage\` from \`@workser/app\`, and for anything large it should request a
5544
5673
  presigned upload URL so the bytes never pass through Workser. See the
5545
- \`workser-sdk\` skill.
5674
+ \`workser-sdk\` skill. Writing an upload handler that saves into the app's own
5675
+ filesystem is the same mistake as above, plus one more: on a serverless host the
5676
+ file is gone at the end of the request.
5546
5677
 
5547
5678
  ## Not the same as \`workser neon storage\`
5548
5679
 
@@ -7537,7 +7668,12 @@ function registerVersions(program3) {
7537
7668
  }
7538
7669
  const items = await api(ctx, `/v1/projects/${projectId}/versions`, {
7539
7670
  query: {
7540
- ...opts.app ? { webAppId: String(opts.app) } : {},
7671
+ // `--app` wins; otherwise the app this folder is linked to (`ctx.appId`,
7672
+ // from `.workser-app`). Standing in an app folder and being told
7673
+ // "needs a webAppId" was the CLI refusing to read the marker it had
7674
+ // already read for `status`. Outside any app folder this stays unset
7675
+ // and the server's own refusal (never the primary-app guess) applies.
7676
+ ...opts.app || ctx.appId ? { webAppId: String(opts.app ?? ctx.appId) } : {},
7541
7677
  ...parsed.value ? { environment: parsed.value } : {}
7542
7678
  }
7543
7679
  });
@@ -7579,7 +7715,12 @@ function registerLogs(program3) {
7579
7715
  throw new WorkserError(parsed.error, { code: "bad_input" });
7580
7716
  }
7581
7717
  const scope = {
7582
- ...opts.app ? { webAppId: String(opts.app) } : {},
7718
+ // `--app` wins; otherwise the app this folder is linked to (`ctx.appId`,
7719
+ // from `.workser-app`). Standing in an app folder and being told
7720
+ // "needs a webAppId" was the CLI refusing to read the marker it had
7721
+ // already read for `status`. Outside any app folder this stays unset
7722
+ // and the server's own refusal (never the primary-app guess) applies.
7723
+ ...opts.app || ctx.appId ? { webAppId: String(opts.app ?? ctx.appId) } : {},
7583
7724
  ...parsed.value ? { environment: parsed.value } : {}
7584
7725
  };
7585
7726
  const first = await api(ctx, `/v1/projects/${projectId}/logs`, {
@@ -7750,7 +7891,7 @@ function registerDoctor(program3) {
7750
7891
  const endpointSource = opts.endpoint ? "--endpoint" : process.env.WORKSER_DAEMON_URL ? "$WORKSER_DAEMON_URL" : session.endpoint ? "session" : process.env.WORKSER_API_URL ? "$WORKSER_API_URL" : `cloud-default: ${env}`;
7751
7892
  const projectSource = opts.project ? "--project" : link?.projectId ? ".workser link" : session.defaultProjectId ? "session" : void 0;
7752
7893
  const git2 = gitVersion();
7753
- const report = {
7894
+ const report2 = {
7754
7895
  endpoint: ctx.endpoint,
7755
7896
  endpointSource,
7756
7897
  env,
@@ -7770,7 +7911,7 @@ function registerDoctor(program3) {
7770
7911
  cwd: ctx.cwd,
7771
7912
  workspace: session.workspaceName ?? null
7772
7913
  };
7773
- ok(report, () => {
7914
+ ok(report2, () => {
7774
7915
  line(import_picocolors15.default.bold("workser doctor"));
7775
7916
  line(` endpoint: ${ctx.endpoint} ${import_picocolors15.default.dim(`(${endpointSource})`)}`);
7776
7917
  line(
@@ -8738,8 +8879,161 @@ function registerConnection(program3) {
8738
8879
  );
8739
8880
  }
8740
8881
 
8741
- // src/commands/tool.ts
8882
+ // src/commands/line.ts
8742
8883
  var import_picocolors23 = __toESM(require_picocolors(), 1);
8884
+ function registerLine(program3) {
8885
+ const cmd = program3.command("line").description("Connect a LINE Official Account and use the LINE Messaging API");
8886
+ cmd.command("status").description("Show this project's LINE connection").action(
8887
+ action(async ({ ctx }) => {
8888
+ const projectId = requireProject(ctx);
8889
+ const res = await api(ctx, `/v1/projects/${projectId}/line`);
8890
+ ok(res, () => {
8891
+ const c = res?.connection;
8892
+ if (!c) return line(import_picocolors23.default.dim("No LINE account connected. Run `workser line connect`."));
8893
+ const state = c.status === "connected" ? import_picocolors23.default.green(c.status) : import_picocolors23.default.yellow(c.status);
8894
+ line(`${import_picocolors23.default.bold(c.display_name ?? "LINE")} ${state}`);
8895
+ if (c.public_config?.basic_id) line(import_picocolors23.default.dim(` ${c.public_config.basic_id}`));
8896
+ if (c.last_error) line(import_picocolors23.default.red(` ${c.last_error}`));
8897
+ });
8898
+ })
8899
+ );
8900
+ cmd.command("connect").description("Connect a LINE Official Account with its channel access token").requiredOption("--token <channelAccessToken>", "Messaging API \u2192 Channel access token").option("--secret <channelSecret>", "Basic settings \u2192 Channel secret (for webhook signatures)").action(
8901
+ action(async ({ ctx, opts }) => {
8902
+ const projectId = requireProject(ctx);
8903
+ const res = await api(ctx, `/v1/projects/${projectId}/line/connect`, {
8904
+ body: { channel_access_token: opts.token, channel_secret: opts.secret }
8905
+ });
8906
+ ok(
8907
+ res,
8908
+ () => line(`Connected ${import_picocolors23.default.bold(res?.connection?.display_name ?? "LINE")}.`)
8909
+ );
8910
+ })
8911
+ );
8912
+ cmd.command("verify").description("Ask LINE whether the stored token still works").action(
8913
+ action(async ({ ctx }) => {
8914
+ const projectId = requireProject(ctx);
8915
+ const res = await api(ctx, `/v1/projects/${projectId}/line/verify`, { body: {} });
8916
+ ok(res, () => {
8917
+ const c = res?.connection;
8918
+ line(
8919
+ c?.status === "connected" ? import_picocolors23.default.green("The token still works.") : import_picocolors23.default.red(c?.last_error ?? "LINE refused the stored token.")
8920
+ );
8921
+ });
8922
+ })
8923
+ );
8924
+ cmd.command("disconnect").description("Disconnect LINE from this project").action(
8925
+ action(async ({ ctx }) => {
8926
+ const projectId = requireProject(ctx);
8927
+ const res = await api(ctx, `/v1/projects/${projectId}/line`, { method: "DELETE" });
8928
+ ok(res, () => line("Disconnected."));
8929
+ })
8930
+ );
8931
+ cmd.command("ops").description("List every LINE operation, optionally filtered by group").option("--group <name>", "messaging, richmenu, audience, insight, people, group, quota, content, account, validation").action(
8932
+ action(async ({ ctx, opts }) => {
8933
+ const projectId = requireProject(ctx);
8934
+ const res = await api(ctx, `/v1/projects/${projectId}/line/operations`);
8935
+ const all = res?.operations ?? [];
8936
+ const items = opts.group ? all.filter((o) => o.group === opts.group) : all;
8937
+ ok(items, () => {
8938
+ if (!items.length) return line(import_picocolors23.default.dim("No operations matched."));
8939
+ let group = "";
8940
+ for (const o of items) {
8941
+ if (o.group !== group) {
8942
+ group = o.group;
8943
+ line(import_picocolors23.default.dim(`
8944
+ ${group}`));
8945
+ }
8946
+ const mark = o.writes ? import_picocolors23.default.yellow(" \u25CF") : " ";
8947
+ line(`${mark} ${import_picocolors23.default.bold(o.id.padEnd(28))} ${o.summary}`);
8948
+ }
8949
+ line(import_picocolors23.default.dim("\n\u25CF sends something, spends quota, or changes the account."));
8950
+ });
8951
+ })
8952
+ );
8953
+ cmd.command("call <operation>").description("Run any LINE operation \u2014 see `workser line ops`").option("--params <json>", "the operation's parameters as a JSON string", "{}").action(
8954
+ action(async ({ ctx, args, opts }) => {
8955
+ const projectId = requireProject(ctx);
8956
+ const res = await callLine(ctx, projectId, args[0], parseParams(opts.params));
8957
+ ok(res, () => line(JSON.stringify(res?.data ?? res, null, 2)));
8958
+ })
8959
+ );
8960
+ cmd.command("send <to> <text>").description("Send a text message to a user, group or room id").action(
8961
+ action(async ({ ctx, args }) => {
8962
+ const projectId = requireProject(ctx);
8963
+ const res = await callLine(ctx, projectId, "push", {
8964
+ to: args[0],
8965
+ messages: [{ type: "text", text: args[1] }]
8966
+ });
8967
+ ok(res, () => report(res, "Sent."));
8968
+ })
8969
+ );
8970
+ cmd.command("reply <replyToken> <text>").description("Answer a message using its reply token (free, and single-use)").action(
8971
+ action(async ({ ctx, args }) => {
8972
+ const projectId = requireProject(ctx);
8973
+ const res = await callLine(ctx, projectId, "reply", {
8974
+ replyToken: args[0],
8975
+ messages: [{ type: "text", text: args[1] }]
8976
+ });
8977
+ ok(res, () => report(res, "Replied."));
8978
+ })
8979
+ );
8980
+ cmd.command("broadcast <text>").description("Send a text message to EVERY follower \u2014 spends one message each").action(
8981
+ action(async ({ ctx, args }) => {
8982
+ const projectId = requireProject(ctx);
8983
+ const res = await callLine(ctx, projectId, "broadcast", {
8984
+ messages: [{ type: "text", text: args[0] }]
8985
+ });
8986
+ ok(res, () => report(res, "Broadcast sent."));
8987
+ })
8988
+ );
8989
+ cmd.command("quota").description("How much of this month's message allowance is left").action(
8990
+ action(async ({ ctx }) => {
8991
+ const projectId = requireProject(ctx);
8992
+ const [quota, used] = await Promise.all([
8993
+ callLine(ctx, projectId, "quota", {}),
8994
+ callLine(ctx, projectId, "quota_consumption", {})
8995
+ ]);
8996
+ ok({ quota: quota?.data, consumption: used?.data }, () => {
8997
+ const limit = quota?.data?.value;
8998
+ const spent = used?.data?.totalUsage;
8999
+ if (limit === void 0) return line(JSON.stringify(quota?.data ?? quota, null, 2));
9000
+ line(`${spent ?? "?"} of ${limit} messages used this month.`);
9001
+ });
9002
+ })
9003
+ );
9004
+ cmd.command("profile <userId>").description("Look up one follower").action(
9005
+ action(async ({ ctx, args }) => {
9006
+ const projectId = requireProject(ctx);
9007
+ const res = await callLine(ctx, projectId, "profile", { userId: args[0] });
9008
+ ok(res, () => line(JSON.stringify(res?.data ?? res, null, 2)));
9009
+ })
9010
+ );
9011
+ }
9012
+ function parseParams(raw) {
9013
+ try {
9014
+ const parsed = JSON.parse(raw || "{}");
9015
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
9016
+ } catch {
9017
+ }
9018
+ throw new Error(
9019
+ `--params must be a JSON object, e.g. --params '{"to":"U123","messages":[{"type":"text","text":"hi"}]}'`
9020
+ );
9021
+ }
9022
+ async function callLine(ctx, projectId, operation, params) {
9023
+ return api(ctx, `/v1/projects/${projectId}/line/call`, {
9024
+ body: { operation, params }
9025
+ });
9026
+ }
9027
+ function report(res, success2) {
9028
+ if (res?.ok === false) {
9029
+ line(import_picocolors23.default.red(res.message ?? "LINE refused that."));
9030
+ return;
9031
+ }
9032
+ line(success2);
9033
+ }
9034
+
9035
+ // src/commands/tool.ts
9036
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
8743
9037
  function registerTool(program3) {
8744
9038
  const tool = program3.command("tool").description(
8745
9039
  "Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
@@ -8748,7 +9042,7 @@ function registerTool(program3) {
8748
9042
  action(async ({ ctx }) => {
8749
9043
  const tools = await api(ctx, "/v1/tool/list");
8750
9044
  ok(tools, () => {
8751
- if (!tools?.length) return line(import_picocolors23.default.dim("No tools available."));
9045
+ if (!tools?.length) return line(import_picocolors24.default.dim("No tools available."));
8752
9046
  const byCategory = /* @__PURE__ */ new Map();
8753
9047
  for (const t of tools) {
8754
9048
  const list = byCategory.get(t.category) ?? [];
@@ -8756,9 +9050,9 @@ function registerTool(program3) {
8756
9050
  byCategory.set(t.category, list);
8757
9051
  }
8758
9052
  for (const [category, items] of byCategory) {
8759
- line(import_picocolors23.default.bold(category) + ":");
9053
+ line(import_picocolors24.default.bold(category) + ":");
8760
9054
  for (const t of items) {
8761
- line(` ${t.name} ${import_picocolors23.default.dim(t.description ?? "")}`);
9055
+ line(` ${t.name} ${import_picocolors24.default.dim(t.description ?? "")}`);
8762
9056
  }
8763
9057
  }
8764
9058
  });
@@ -8776,7 +9070,7 @@ function registerTool(program3) {
8776
9070
  }
8777
9071
 
8778
9072
  // src/commands/memory.ts
8779
- var import_picocolors24 = __toESM(require_picocolors(), 1);
9073
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
8780
9074
  function registerMemory(program3) {
8781
9075
  const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
8782
9076
  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(
@@ -8800,9 +9094,9 @@ function registerMemory(program3) {
8800
9094
  });
8801
9095
  ok(res, () => {
8802
9096
  const results = res?.results ?? res ?? [];
8803
- if (!results?.length) return line(import_picocolors24.default.dim("No matching memories."));
9097
+ if (!results?.length) return line(import_picocolors25.default.dim("No matching memories."));
8804
9098
  for (const r of results) {
8805
- line(`${import_picocolors24.default.dim(r.id ?? "?")} ${memoryText(r)}`);
9099
+ line(`${import_picocolors25.default.dim(r.id ?? "?")} ${memoryText(r)}`);
8806
9100
  }
8807
9101
  });
8808
9102
  })
@@ -8822,11 +9116,11 @@ function memoryText(r) {
8822
9116
  if (typeof text === "string" && text.trim()) return text;
8823
9117
  const title = r?.documents?.[0]?.title;
8824
9118
  if (typeof title === "string" && title.trim()) return title;
8825
- return import_picocolors24.default.dim("(no readable text on this row)");
9119
+ return import_picocolors25.default.dim("(no readable text on this row)");
8826
9120
  }
8827
9121
 
8828
9122
  // src/commands/note.ts
8829
- var import_picocolors25 = __toESM(require_picocolors(), 1);
9123
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
8830
9124
  function registerNote(program3) {
8831
9125
  program3.command("note <text>").description("Leave a fact the rest of the team will need").addHelpText(
8832
9126
  "after",
@@ -8863,14 +9157,14 @@ function registerNote(program3) {
8863
9157
  }
8864
9158
  ok(res, () => {
8865
9159
  success("Noted for the team.");
8866
- line(import_picocolors25.default.dim(` ${text}`));
9160
+ line(import_picocolors26.default.dim(` ${text}`));
8867
9161
  });
8868
9162
  })
8869
9163
  );
8870
9164
  }
8871
9165
 
8872
9166
  // src/commands/business.ts
8873
- var import_picocolors26 = __toESM(require_picocolors(), 1);
9167
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
8874
9168
  var RESOURCE_PATHS = {
8875
9169
  "business-config": "business-config",
8876
9170
  "business-settings": "business-settings",
@@ -8930,7 +9224,7 @@ function registerBusiness(program3) {
8930
9224
  const projectId = requireProject(ctx);
8931
9225
  const [resource] = args;
8932
9226
  const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
8933
- ok(res, () => line(`Created ${resource} ${import_picocolors26.default.bold(res?.id ?? "")}.`));
9227
+ ok(res, () => line(`Created ${resource} ${import_picocolors27.default.bold(res?.id ?? "")}.`));
8934
9228
  })
8935
9229
  );
8936
9230
  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(
@@ -8972,7 +9266,7 @@ function businessPath(projectId, resource, subpath) {
8972
9266
  }
8973
9267
 
8974
9268
  // src/commands/artifact.ts
8975
- var import_picocolors27 = __toESM(require_picocolors(), 1);
9269
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
8976
9270
  import { existsSync as existsSync2, statSync } from "fs";
8977
9271
  import { resolve as resolve3, basename as basename3 } from "path";
8978
9272
  var KINDS = [
@@ -9069,7 +9363,7 @@ function registerArtifact(program3) {
9069
9363
  ok(
9070
9364
  res,
9071
9365
  () => success(
9072
- `Recorded ${import_picocolors27.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors27.default.dim(` (${res.kind})`) : ""}`
9366
+ `Recorded ${import_picocolors28.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors28.default.dim(` (${res.kind})`) : ""}`
9073
9367
  )
9074
9368
  );
9075
9369
  })
@@ -9107,7 +9401,7 @@ function registerArtifact(program3) {
9107
9401
  }
9108
9402
  function printArtifacts(rows) {
9109
9403
  if (!rows.length) {
9110
- line(import_picocolors27.default.dim("Nothing produced yet."));
9404
+ line(import_picocolors28.default.dim("Nothing produced yet."));
9111
9405
  return;
9112
9406
  }
9113
9407
  const byStep = /* @__PURE__ */ new Map();
@@ -9117,26 +9411,26 @@ function printArtifacts(rows) {
9117
9411
  byStep.set(r.subtask_id, list);
9118
9412
  }
9119
9413
  for (const [stepId, items] of byStep) {
9120
- line(import_picocolors27.default.bold(`step ${stepId}`));
9414
+ line(import_picocolors28.default.bold(`step ${stepId}`));
9121
9415
  for (const a of items) {
9122
- const flag = a.promoted_at ? import_picocolors27.default.green(" *") : " ";
9416
+ const flag = a.promoted_at ? import_picocolors28.default.green(" *") : " ";
9123
9417
  const where = a.local_path || a.cloud_url || "";
9124
9418
  line(
9125
- `${flag} ${import_picocolors27.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors27.default.dim(` ${where}`) : "")
9419
+ `${flag} ${import_picocolors28.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors28.default.dim(` ${where}`) : "")
9126
9420
  );
9127
- if (a.description) line(import_picocolors27.default.dim(` ${a.description}`));
9421
+ if (a.description) line(import_picocolors28.default.dim(` ${a.description}`));
9128
9422
  }
9129
9423
  line("");
9130
9424
  }
9131
- line(import_picocolors27.default.dim("* = handed over as a deliverable; the rest is working material."));
9425
+ line(import_picocolors28.default.dim("* = handed over as a deliverable; the rest is working material."));
9132
9426
  }
9133
9427
  function printRun2(run) {
9134
9428
  if (!run) return;
9135
- line(` run ${import_picocolors27.default.bold(run.runId)}`);
9429
+ line(` run ${import_picocolors28.default.bold(run.runId)}`);
9136
9430
  if (run.taskId) line(` task ${run.taskId}`);
9137
9431
  if (run.conversationId) line(` chat ${run.conversationId}`);
9138
9432
  if (run.projectId) line(` project ${run.projectId}`);
9139
- if (run.cwd) line(` folder ${import_picocolors27.default.dim(run.cwd)}`);
9433
+ if (run.cwd) line(` folder ${import_picocolors28.default.dim(run.cwd)}`);
9140
9434
  }
9141
9435
 
9142
9436
  // src/commands/image.ts
@@ -9343,7 +9637,7 @@ function registerAudio(program3) {
9343
9637
  }
9344
9638
 
9345
9639
  // src/commands/ask.ts
9346
- var import_picocolors28 = __toESM(require_picocolors(), 1);
9640
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
9347
9641
  var TYPES = [
9348
9642
  "input",
9349
9643
  "choice",
@@ -9440,7 +9734,7 @@ function registerAsk(program3) {
9440
9734
  code: "bad_request"
9441
9735
  });
9442
9736
  }
9443
- info(import_picocolors28.default.dim("Waiting for the user to answer\u2026"));
9737
+ info(import_picocolors29.default.dim("Waiting for the user to answer\u2026"));
9444
9738
  const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
9445
9739
  body: {
9446
9740
  type,
@@ -9468,12 +9762,12 @@ function deriveTitle(message) {
9468
9762
  function printAnswer(res) {
9469
9763
  if (!res) return;
9470
9764
  if (res.status === "answered") {
9471
- line(` ${import_picocolors28.default.green("answered")}`);
9765
+ line(` ${import_picocolors29.default.green("answered")}`);
9472
9766
  const value = extract(res.response);
9473
9767
  if (value) line(` ${value}`);
9474
9768
  return;
9475
9769
  }
9476
- line(` ${import_picocolors28.default.yellow(res.status)} ${import_picocolors28.default.dim(res.reason ?? "")}`);
9770
+ line(` ${import_picocolors29.default.yellow(res.status)} ${import_picocolors29.default.dim(res.reason ?? "")}`);
9477
9771
  }
9478
9772
  function extract(response) {
9479
9773
  if (response == null) return "";
@@ -9492,7 +9786,7 @@ function extract(response) {
9492
9786
  }
9493
9787
 
9494
9788
  // src/commands/search.ts
9495
- var import_picocolors29 = __toESM(require_picocolors(), 1);
9789
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
9496
9790
  function registerSearch(program3) {
9497
9791
  program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
9498
9792
  action(async ({ ctx, args, opts }) => {
@@ -9505,9 +9799,9 @@ function registerSearch(program3) {
9505
9799
  line("");
9506
9800
  }
9507
9801
  const results = res?.results ?? [];
9508
- if (!results.length) return line(import_picocolors29.default.dim("No results."));
9802
+ if (!results.length) return line(import_picocolors30.default.dim("No results."));
9509
9803
  for (const r of results) {
9510
- line(`${r.title || import_picocolors29.default.dim("(untitled)")} ${import_picocolors29.default.dim(r.url)}`);
9804
+ line(`${r.title || import_picocolors30.default.dim("(untitled)")} ${import_picocolors30.default.dim(r.url)}`);
9511
9805
  }
9512
9806
  });
9513
9807
  })
@@ -9515,7 +9809,7 @@ function registerSearch(program3) {
9515
9809
  }
9516
9810
 
9517
9811
  // src/commands/board.ts
9518
- var import_picocolors30 = __toESM(require_picocolors(), 1);
9812
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
9519
9813
 
9520
9814
  // src/commands/record-step.ts
9521
9815
  async function recordEntityStep(ctx, opts) {
@@ -9554,7 +9848,7 @@ function registerBoard(program3) {
9554
9848
  }
9555
9849
  ok(rows, () => {
9556
9850
  if (!rows.length) {
9557
- line(import_picocolors30.default.dim("No cards on the Board yet."));
9851
+ line(import_picocolors31.default.dim("No cards on the Board yet."));
9558
9852
  return;
9559
9853
  }
9560
9854
  for (const r of rows) line(formatRow(r));
@@ -9569,7 +9863,7 @@ function registerBoard(program3) {
9569
9863
  `/v1/projects/${projectId}/work-items/${args[0]}`
9570
9864
  );
9571
9865
  ok(row, () => {
9572
- line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.id)}`);
9866
+ line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9573
9867
  line(`${statusTag(row.status)} priority ${row.priority}`);
9574
9868
  if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
9575
9869
  if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
@@ -9610,7 +9904,7 @@ ${row.description}`);
9610
9904
  refId: row?.id,
9611
9905
  output: { workItem: row }
9612
9906
  });
9613
- ok(row, () => line(`Created work item ${import_picocolors30.default.bold(row?.id ?? "")} \u2014 ${title}`));
9907
+ ok(row, () => line(`Created work item ${import_picocolors31.default.bold(row?.id ?? "")} \u2014 ${title}`));
9614
9908
  })
9615
9909
  );
9616
9910
  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(
@@ -9644,7 +9938,7 @@ ${row.description}`);
9644
9938
  );
9645
9939
  }
9646
9940
  const row = await patchItem(ctx, projectId, String(args[0]), body);
9647
- ok(row, () => line(`Updated ${import_picocolors30.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
9941
+ ok(row, () => line(`Updated ${import_picocolors31.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
9648
9942
  })
9649
9943
  );
9650
9944
  board.command("move <id> <status>").description(
@@ -9655,14 +9949,14 @@ ${row.description}`);
9655
9949
  const status = String(args[1]);
9656
9950
  assertStatus(status);
9657
9951
  const row = await patchItem(ctx, projectId, String(args[0]), { status });
9658
- ok(row, () => line(`Moved ${import_picocolors30.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
9952
+ ok(row, () => line(`Moved ${import_picocolors31.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
9659
9953
  })
9660
9954
  );
9661
9955
  board.command("close <id>").description("Shorthand for `board move <id> done`").action(
9662
9956
  action(async ({ ctx, args }) => {
9663
9957
  const projectId = requireProject(ctx);
9664
9958
  const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
9665
- ok(row, () => line(`Closed ${import_picocolors30.default.bold(row.title)} ${statusTag(row.status)}`));
9959
+ ok(row, () => line(`Closed ${import_picocolors31.default.bold(row.title)} ${statusTag(row.status)}`));
9666
9960
  })
9667
9961
  );
9668
9962
  }
@@ -9695,23 +9989,23 @@ function assertPriority(value) {
9695
9989
  }
9696
9990
  }
9697
9991
  function formatRow(r) {
9698
- const labels = r.labels?.length ? import_picocolors30.default.dim(` [${r.labels.join(", ")}]`) : "";
9699
- const owner = r.ownerHuman ? import_picocolors30.default.dim(` @${r.ownerHuman}`) : "";
9700
- return `${import_picocolors30.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
9992
+ const labels = r.labels?.length ? import_picocolors31.default.dim(` [${r.labels.join(", ")}]`) : "";
9993
+ const owner = r.ownerHuman ? import_picocolors31.default.dim(` @${r.ownerHuman}`) : "";
9994
+ return `${import_picocolors31.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
9701
9995
  }
9702
9996
  function statusTag(status) {
9703
9997
  const label = status.padEnd(11);
9704
- if (status === "done") return import_picocolors30.default.green(label);
9705
- if (status === "in-progress") return import_picocolors30.default.yellow(label);
9706
- if (status === "in-review") return import_picocolors30.default.cyan(label);
9707
- return import_picocolors30.default.dim(label);
9998
+ if (status === "done") return import_picocolors31.default.green(label);
9999
+ if (status === "in-progress") return import_picocolors31.default.yellow(label);
10000
+ if (status === "in-review") return import_picocolors31.default.cyan(label);
10001
+ return import_picocolors31.default.dim(label);
9708
10002
  }
9709
10003
  function collect2(value, previous) {
9710
10004
  return [...previous, value];
9711
10005
  }
9712
10006
 
9713
10007
  // src/commands/task.ts
9714
- var import_picocolors31 = __toESM(require_picocolors(), 1);
10008
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
9715
10009
 
9716
10010
  // src/subtask-attachments.ts
9717
10011
  var MAX_REF_NOTE = 500;
@@ -9826,7 +10120,7 @@ function registerTask(program3) {
9826
10120
  }) ?? [];
9827
10121
  ok(rows, () => {
9828
10122
  if (!rows.length) {
9829
- line(import_picocolors31.default.dim("No tasks on the board yet."));
10123
+ line(import_picocolors32.default.dim("No tasks on the board yet."));
9830
10124
  return;
9831
10125
  }
9832
10126
  for (const r of rows) line(formatRow2(r));
@@ -9941,10 +10235,10 @@ function registerTask(program3) {
9941
10235
  };
9942
10236
  ok(result, () => {
9943
10237
  line(
9944
- `${import_picocolors31.default.green("opened")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
10238
+ `${import_picocolors32.default.green("opened")} ${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`
9945
10239
  );
9946
10240
  if (channelMessage)
9947
- line(import_picocolors31.default.dim("posted to the channel as Project Manager"));
10241
+ line(import_picocolors32.default.dim("posted to the channel as Project Manager"));
9948
10242
  if (channelMessageError) {
9949
10243
  warn(
9950
10244
  `Task opened, but its Project Manager card could not be posted: ${channelMessageError.message}`
@@ -10023,12 +10317,12 @@ function registerTask(program3) {
10023
10317
  ) : null;
10024
10318
  ok(runner ?? row, () => {
10025
10319
  line(
10026
- `${import_picocolors31.default.green("added")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
10320
+ `${import_picocolors32.default.green("added")} ${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`
10027
10321
  );
10028
- if (row.role) line(import_picocolors31.default.dim(`role: ${row.role}`));
10322
+ if (row.role) line(import_picocolors32.default.dim(`role: ${row.role}`));
10029
10323
  if (runner) {
10030
10324
  line(
10031
- import_picocolors31.default.dim(
10325
+ import_picocolors32.default.dim(
10032
10326
  `runs on: ${[opts.agent, opts.model, opts.effort].filter(Boolean).join(" \xB7 ")}`
10033
10327
  )
10034
10328
  );
@@ -10046,7 +10340,7 @@ function registerTask(program3) {
10046
10340
  const rows = row.subtasks ?? [];
10047
10341
  ok(rows, () => {
10048
10342
  if (!rows.length) {
10049
- line(import_picocolors31.default.dim("No subtasks yet."));
10343
+ line(import_picocolors32.default.dim("No subtasks yet."));
10050
10344
  return;
10051
10345
  }
10052
10346
  rows.forEach((r, i) => line(formatSubtask(r, i + 1)));
@@ -10094,7 +10388,7 @@ function registerTask(program3) {
10094
10388
  }
10095
10389
  }
10096
10390
  );
10097
- ok(row, () => line(`${import_picocolors31.default.green("updated")} ${import_picocolors31.default.bold(row.title)}`));
10391
+ ok(row, () => line(`${import_picocolors32.default.green("updated")} ${import_picocolors32.default.bold(row.title)}`));
10098
10392
  })
10099
10393
  );
10100
10394
  subtask.command("remove <id>").description("Remove a subtask (only before the work starts)").action(
@@ -10102,7 +10396,7 @@ function registerTask(program3) {
10102
10396
  await api(ctx, `/v1/project-tasks/${encodeURIComponent(args[0])}`, {
10103
10397
  method: "DELETE"
10104
10398
  });
10105
- ok({ removed: args[0] }, () => line(import_picocolors31.default.green("removed")));
10399
+ ok({ removed: args[0] }, () => line(import_picocolors32.default.green("removed")));
10106
10400
  })
10107
10401
  );
10108
10402
  task.command("move <id> <status>").description(
@@ -10117,7 +10411,7 @@ function registerTask(program3) {
10117
10411
  );
10118
10412
  ok(
10119
10413
  row,
10120
- () => line(`${import_picocolors31.default.green("moved")} ${import_picocolors31.default.bold(row.title)} \u2192 ${args[1]}`)
10414
+ () => line(`${import_picocolors32.default.green("moved")} ${import_picocolors32.default.bold(row.title)} \u2192 ${args[1]}`)
10121
10415
  );
10122
10416
  })
10123
10417
  );
@@ -10133,18 +10427,18 @@ function registerTask(program3) {
10133
10427
  );
10134
10428
  ok(row, () => {
10135
10429
  if (!row?.queued) {
10136
- line(import_picocolors31.default.yellow("could not resume it \u2014 check the step id"));
10430
+ line(import_picocolors32.default.yellow("could not resume it \u2014 check the step id"));
10137
10431
  return;
10138
10432
  }
10139
10433
  const started = row.started ?? 0;
10140
10434
  if (started > 0) {
10141
10435
  line(
10142
- `${import_picocolors31.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
10436
+ `${import_picocolors32.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
10143
10437
  );
10144
10438
  return;
10145
10439
  }
10146
10440
  line(
10147
- `${import_picocolors31.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
10441
+ `${import_picocolors32.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
10148
10442
  );
10149
10443
  });
10150
10444
  })
@@ -10158,11 +10452,11 @@ function registerTask(program3) {
10158
10452
  );
10159
10453
  ok(row, () => {
10160
10454
  if (row?.reopened) {
10161
- line(`${import_picocolors31.default.green("sent back")} \u2014 it will be picked up again`);
10455
+ line(`${import_picocolors32.default.green("sent back")} \u2014 it will be picked up again`);
10162
10456
  return;
10163
10457
  }
10164
10458
  line(
10165
- import_picocolors31.default.yellow(
10459
+ import_picocolors32.default.yellow(
10166
10460
  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"
10167
10461
  )
10168
10462
  );
@@ -10179,7 +10473,7 @@ function registerTask(program3) {
10179
10473
  `/v1/project-tasks/${encodeURIComponent(id)}/dispatch-check`,
10180
10474
  { method: "POST" }
10181
10475
  );
10182
- ok(row, () => line(import_picocolors31.default.green("approved \u2014 you may start")));
10476
+ ok(row, () => line(import_picocolors32.default.green("approved \u2014 you may start")));
10183
10477
  })
10184
10478
  );
10185
10479
  task.command("start [id]").description(
@@ -10194,13 +10488,13 @@ function registerTask(program3) {
10194
10488
  const started = row?.started ?? null;
10195
10489
  if (started && started > 0) {
10196
10490
  line(
10197
- import_picocolors31.default.green(
10491
+ import_picocolors32.default.green(
10198
10492
  `started ${started} step${started === 1 ? "" : "s"} \u2014 they are running now`
10199
10493
  )
10200
10494
  );
10201
10495
  return;
10202
10496
  }
10203
- line(import_picocolors31.default.yellow(row?.note ?? "Nothing started."));
10497
+ line(import_picocolors32.default.yellow(row?.note ?? "Nothing started."));
10204
10498
  });
10205
10499
  })
10206
10500
  );
@@ -10215,7 +10509,7 @@ function registerTask(program3) {
10215
10509
  );
10216
10510
  ok({ awaiting: row2.approval_state === "awaiting", task: row2 }, () => {
10217
10511
  line(
10218
- row2.approval_state === "awaiting" ? import_picocolors31.default.yellow(
10512
+ row2.approval_state === "awaiting" ? import_picocolors32.default.yellow(
10219
10513
  "The plan is waiting on the owner. They see it in the task."
10220
10514
  ) : `Already ${row2.approval_state}.`
10221
10515
  );
@@ -10240,7 +10534,7 @@ function registerTask(program3) {
10240
10534
  );
10241
10535
  ok(
10242
10536
  row,
10243
- () => line(`${import_picocolors31.default.green(row.approval_state)} ${import_picocolors31.default.bold(row.title)}`)
10537
+ () => line(`${import_picocolors32.default.green(row.approval_state)} ${import_picocolors32.default.bold(row.title)}`)
10244
10538
  );
10245
10539
  })
10246
10540
  );
@@ -10256,7 +10550,7 @@ function registerTask(program3) {
10256
10550
  `/v1/project-tasks/${encodeURIComponent(id)}/move`,
10257
10551
  { body: { status: "ready" } }
10258
10552
  );
10259
- ok(row, () => line(`${import_picocolors31.default.green("ready")} ${import_picocolors31.default.bold(row.title)}`));
10553
+ ok(row, () => line(`${import_picocolors32.default.green("ready")} ${import_picocolors32.default.bold(row.title)}`));
10260
10554
  })
10261
10555
  );
10262
10556
  }
@@ -10301,24 +10595,24 @@ function assertOneOf(flag, value, allowed) {
10301
10595
  }
10302
10596
  }
10303
10597
  function formatRow2(r) {
10304
- const key = import_picocolors31.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
10305
- const steps = r.subtaskTotal ? import_picocolors31.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
10306
- const gate = r.approval_state === "awaiting" ? import_picocolors31.default.yellow(" awaiting approval") : "";
10598
+ const key = import_picocolors32.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
10599
+ const steps = r.subtaskTotal ? import_picocolors32.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
10600
+ const gate = r.approval_state === "awaiting" ? import_picocolors32.default.yellow(" awaiting approval") : "";
10307
10601
  return `${key} ${statusTag2(r.status)} ${r.title}${steps}${gate}`;
10308
10602
  }
10309
10603
  function formatSubtask(r, index) {
10310
- const n = import_picocolors31.default.dim(String(index).padStart(2, "0"));
10311
- const role = r.role ? import_picocolors31.default.dim(` [${r.role}]`) : "";
10312
- const scope = r.scope_paths?.length ? import_picocolors31.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
10604
+ const n = import_picocolors32.default.dim(String(index).padStart(2, "0"));
10605
+ const role = r.role ? import_picocolors32.default.dim(` [${r.role}]`) : "";
10606
+ const scope = r.scope_paths?.length ? import_picocolors32.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
10313
10607
  const head = `${n} ${statusTag2(r.status)} ${r.title}${role}${scope}`;
10314
10608
  const summary = (r.result_summary ?? "").trim();
10315
10609
  if (!summary) return head;
10316
10610
  const wrapped = summary.split("\n").map((l) => ` ${l}`).join("\n");
10317
10611
  return `${head}
10318
- ${import_picocolors31.default.dim(wrapped)}`;
10612
+ ${import_picocolors32.default.dim(wrapped)}`;
10319
10613
  }
10320
10614
  function printTask(row, goal) {
10321
- line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`);
10615
+ line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`);
10322
10616
  line(`${statusTag2(row.status)} approval: ${row.approval_state}`);
10323
10617
  if (row.summary) line(`
10324
10618
  ${row.summary}`);
@@ -10331,53 +10625,53 @@ touches: ${row.targets.map((t) => t.appName ?? t.ref ?? t.kind).join(", ")}`
10331
10625
  }
10332
10626
  if (row.subtasks?.length) {
10333
10627
  line(`
10334
- ${import_picocolors31.default.bold("subtasks")}`);
10628
+ ${import_picocolors32.default.bold("subtasks")}`);
10335
10629
  row.subtasks.forEach((s, i) => line(formatSubtask(s, i + 1)));
10336
10630
  line(
10337
- import_picocolors31.default.dim(
10631
+ import_picocolors32.default.dim(
10338
10632
  `
10339
10633
  Run \`workser artifact list\` to see what these subtasks produced,`
10340
10634
  )
10341
10635
  );
10342
10636
  line(
10343
- import_picocolors31.default.dim(
10637
+ import_picocolors32.default.dim(
10344
10638
  `or \`workser artifact list --step <id>\` for one subtask's output alone.`
10345
10639
  )
10346
10640
  );
10347
10641
  } else {
10348
- line(import_picocolors31.default.dim("\nNo subtasks yet."));
10642
+ line(import_picocolors32.default.dim("\nNo subtasks yet."));
10349
10643
  }
10350
10644
  }
10351
10645
  function printGoalContext(row, goal) {
10352
10646
  const mine = row.phase ?? null;
10353
10647
  line(`
10354
- ${import_picocolors31.default.bold("part of")}: ${goal.title}`);
10355
- if (goal.outcome) line(import_picocolors31.default.dim(`done means: ${goal.outcome}`));
10648
+ ${import_picocolors32.default.bold("part of")}: ${goal.title}`);
10649
+ if (goal.outcome) line(import_picocolors32.default.dim(`done means: ${goal.outcome}`));
10356
10650
  const progress = goal.progress ?? [];
10357
10651
  for (const p of progress) {
10358
10652
  const where = p.total === 0 ? "not started" : `${p.done} of ${p.total} done`;
10359
- const here = mine && p.name === mine ? import_picocolors31.default.cyan(" <- this task") : "";
10360
- const mark = p.state === "done" ? import_picocolors31.default.green("*") : import_picocolors31.default.dim("-");
10653
+ const here = mine && p.name === mine ? import_picocolors32.default.cyan(" <- this task") : "";
10654
+ const mark = p.state === "done" ? import_picocolors32.default.green("*") : import_picocolors32.default.dim("-");
10361
10655
  line(` ${mark} ${p.name} \u2014 ${where}${here}`);
10362
10656
  }
10363
10657
  const next = progress.find((p) => p.state !== "done");
10364
10658
  const running = progress.some((p) => p.state === "working");
10365
10659
  if (next && !running) {
10366
10660
  line(
10367
- import_picocolors31.default.dim(
10661
+ import_picocolors32.default.dim(
10368
10662
  `
10369
10663
  Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to them in a sentence rather than starting it unasked.`
10370
10664
  )
10371
10665
  );
10372
10666
  } else if (!next) {
10373
10667
  line(
10374
- import_picocolors31.default.dim(
10668
+ import_picocolors32.default.dim(
10375
10669
  "\nEvery part of this plan is done. Say so, and offer to wrap it up."
10376
10670
  )
10377
10671
  );
10378
10672
  }
10379
10673
  line(
10380
- import_picocolors31.default.dim(
10674
+ import_picocolors32.default.dim(
10381
10675
  "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."
10382
10676
  )
10383
10677
  );
@@ -10385,22 +10679,22 @@ Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to t
10385
10679
  function statusTag2(status) {
10386
10680
  switch (status) {
10387
10681
  case "ready":
10388
- return import_picocolors31.default.green("[ready]");
10682
+ return import_picocolors32.default.green("[ready]");
10389
10683
  case "working":
10390
- return import_picocolors31.default.blue("[working]");
10684
+ return import_picocolors32.default.blue("[working]");
10391
10685
  case "checking":
10392
- return import_picocolors31.default.cyan("[checking]");
10686
+ return import_picocolors32.default.cyan("[checking]");
10393
10687
  case "accepted":
10394
- return import_picocolors31.default.green("[accepted]");
10688
+ return import_picocolors32.default.green("[accepted]");
10395
10689
  case "archived":
10396
- return import_picocolors31.default.dim("[archived]");
10690
+ return import_picocolors32.default.dim("[archived]");
10397
10691
  default:
10398
- return import_picocolors31.default.dim("[todo]");
10692
+ return import_picocolors32.default.dim("[todo]");
10399
10693
  }
10400
10694
  }
10401
10695
 
10402
10696
  // src/commands/goal.ts
10403
- var import_picocolors32 = __toESM(require_picocolors(), 1);
10697
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
10404
10698
  var STATUSES3 = ["proposed", "agreed", "working", "delivered", "abandoned"];
10405
10699
  var PACES = ["slice", "phase", "all"];
10406
10700
  function registerGoal(program3) {
@@ -10413,7 +10707,7 @@ function registerGoal(program3) {
10413
10707
  }) ?? [];
10414
10708
  ok(rows, () => {
10415
10709
  if (!rows.length) {
10416
- line(import_picocolors32.default.dim("No goals yet \u2014 every task here stands on its own."));
10710
+ line(import_picocolors33.default.dim("No goals yet \u2014 every task here stands on its own."));
10417
10711
  return;
10418
10712
  }
10419
10713
  for (const g of rows) line(formatGoal(g));
@@ -10499,10 +10793,10 @@ function registerGoal(program3) {
10499
10793
  }
10500
10794
  });
10501
10795
  ok(row, () => {
10502
- success(`Proposed ${import_picocolors32.default.bold(row?.title ?? "goal")}`);
10796
+ success(`Proposed ${import_picocolors33.default.bold(row?.title ?? "goal")}`);
10503
10797
  printGoal(row);
10504
10798
  line(
10505
- import_picocolors32.default.dim(
10799
+ import_picocolors33.default.dim(
10506
10800
  "\nNothing has been created yet. The owner agrees the shape first."
10507
10801
  )
10508
10802
  );
@@ -10531,7 +10825,7 @@ function registerGoal(program3) {
10531
10825
  ok(
10532
10826
  row,
10533
10827
  () => line(
10534
- row?.recorded ? met === true ? import_picocolors32.default.green("recorded \u2014 met") : met === false ? import_picocolors32.default.yellow("recorded \u2014 not met") : import_picocolors32.default.dim("recorded \u2014 back to unchecked") : import_picocolors32.default.yellow(
10828
+ row?.recorded ? met === true ? import_picocolors33.default.green("recorded \u2014 met") : met === false ? import_picocolors33.default.yellow("recorded \u2014 not met") : import_picocolors33.default.dim("recorded \u2014 back to unchecked") : import_picocolors33.default.yellow(
10535
10829
  "couldn't record it \u2014 check the goal id, the phase name and the criterion id"
10536
10830
  )
10537
10831
  )
@@ -10574,7 +10868,7 @@ function registerGoal(program3) {
10574
10868
  }
10575
10869
  ok({ goalId, phase, filed }, () => {
10576
10870
  success(
10577
- `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors32.default.bold(phase)}`
10871
+ `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors33.default.bold(phase)}`
10578
10872
  );
10579
10873
  });
10580
10874
  })
@@ -10612,43 +10906,43 @@ function registerGoal(program3) {
10612
10906
  function appScope(g) {
10613
10907
  const n = g.appIds?.length ?? 0;
10614
10908
  if (!n) return "";
10615
- return import_picocolors32.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
10909
+ return import_picocolors33.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
10616
10910
  }
10617
10911
  function formatGoal(g) {
10618
- const where = g.currentPhase && g.status !== "delivered" ? import_picocolors32.default.dim(` now: ${g.currentPhase}`) : "";
10619
- const count = g.taskTotal != null ? import_picocolors32.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
10620
- return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors32.default.dim(g.id)}`;
10912
+ const where = g.currentPhase && g.status !== "delivered" ? import_picocolors33.default.dim(` now: ${g.currentPhase}`) : "";
10913
+ const count = g.taskTotal != null ? import_picocolors33.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
10914
+ return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors33.default.dim(g.id)}`;
10621
10915
  }
10622
10916
  function printGoal(g) {
10623
10917
  if (!g) return;
10624
- line(`${import_picocolors32.default.bold(g.title)} ${import_picocolors32.default.dim(g.id)}`);
10918
+ line(`${import_picocolors33.default.bold(g.title)} ${import_picocolors33.default.dim(g.id)}`);
10625
10919
  line(statusTag3(g.status));
10626
10920
  if (g.outcome) line(`
10627
10921
  done means: ${g.outcome}`);
10628
- if (g.pace) line(import_picocolors32.default.dim(`pace: ${paceLine(g.pace)}`));
10922
+ if (g.pace) line(import_picocolors33.default.dim(`pace: ${paceLine(g.pace)}`));
10629
10923
  const progress = g.progress ?? [];
10630
10924
  if (!progress.length) {
10631
- line(import_picocolors32.default.dim("\nNo phases agreed yet."));
10925
+ line(import_picocolors33.default.dim("\nNo phases agreed yet."));
10632
10926
  return;
10633
10927
  }
10634
10928
  line(`
10635
- ${import_picocolors32.default.bold("phases")}`);
10929
+ ${import_picocolors33.default.bold("phases")}`);
10636
10930
  for (const p of progress) {
10637
- const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors32.default.dim("nothing filed yet");
10638
- const mark = p.state === "done" ? import_picocolors32.default.green("done") : p.state === "working" ? import_picocolors32.default.blue("working") : import_picocolors32.default.dim("waiting");
10931
+ const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors33.default.dim("nothing filed yet");
10932
+ const mark = p.state === "done" ? import_picocolors33.default.green("done") : p.state === "working" ? import_picocolors33.default.blue("working") : import_picocolors33.default.dim("waiting");
10639
10933
  line(
10640
- ` ${import_picocolors32.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors32.default.dim(bar2)}`
10934
+ ` ${import_picocolors33.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors33.default.dim(bar2)}`
10641
10935
  );
10642
10936
  for (const c of p.criteria ?? []) {
10643
- const tick = c.met === true ? import_picocolors32.default.green("\u2713") : c.met === false ? import_picocolors32.default.red("\u2717") : import_picocolors32.default.dim("\xB7");
10644
- line(` ${tick} ${c.text} ${import_picocolors32.default.dim(c.id)}`);
10645
- if (c.note) line(import_picocolors32.default.dim(` ${c.note}`));
10937
+ const tick = c.met === true ? import_picocolors33.default.green("\u2713") : c.met === false ? import_picocolors33.default.red("\u2717") : import_picocolors33.default.dim("\xB7");
10938
+ line(` ${tick} ${c.text} ${import_picocolors33.default.dim(c.id)}`);
10939
+ if (c.note) line(import_picocolors33.default.dim(` ${c.note}`));
10646
10940
  }
10647
10941
  }
10648
10942
  const current = progress.find((p) => p.name === g.currentPhase);
10649
10943
  if (current) {
10650
10944
  line(
10651
- import_picocolors32.default.dim(
10945
+ import_picocolors33.default.dim(
10652
10946
  `
10653
10947
  ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current.index} of ${progress.length}).`
10654
10948
  )
@@ -10658,15 +10952,15 @@ ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current
10658
10952
  function statusTag3(status) {
10659
10953
  switch (status) {
10660
10954
  case "agreed":
10661
- return import_picocolors32.default.cyan("[agreed]");
10955
+ return import_picocolors33.default.cyan("[agreed]");
10662
10956
  case "working":
10663
- return import_picocolors32.default.blue("[working]");
10957
+ return import_picocolors33.default.blue("[working]");
10664
10958
  case "delivered":
10665
- return import_picocolors32.default.green("[delivered]");
10959
+ return import_picocolors33.default.green("[delivered]");
10666
10960
  case "abandoned":
10667
- return import_picocolors32.default.dim("[abandoned]");
10961
+ return import_picocolors33.default.dim("[abandoned]");
10668
10962
  default:
10669
- return import_picocolors32.default.yellow("[proposed]");
10963
+ return import_picocolors33.default.yellow("[proposed]");
10670
10964
  }
10671
10965
  }
10672
10966
  function paceLine(pace) {
@@ -10720,7 +11014,7 @@ function stripLeadingGlobalOptions(argv) {
10720
11014
  }
10721
11015
 
10722
11016
  // src/commands/decision.ts
10723
- var import_picocolors33 = __toESM(require_picocolors(), 1);
11017
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
10724
11018
  function registerDecision(program3) {
10725
11019
  const decision = program3.command("decision").description("Read and record the project's architecture decisions");
10726
11020
  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)").option("--search <text>", "match the title, case-insensitively").option("--label <name>", "only decisions carrying this label").option("--app <id>", "only decisions about this app").option("--infra <name>", "only decisions touching this part of the infrastructure").option("--status <name>", "proposed | accepted | superseded").action(
@@ -10743,13 +11037,13 @@ function registerDecision(program3) {
10743
11037
  rows = applyLimit(rows, opts.limit);
10744
11038
  ok(rows, () => {
10745
11039
  if (!rows.length) {
10746
- line(import_picocolors33.default.dim("No decisions recorded yet."));
11040
+ line(import_picocolors34.default.dim("No decisions recorded yet."));
10747
11041
  return;
10748
11042
  }
10749
11043
  for (const r of rows) {
10750
11044
  const tags = [...r.labels ?? [], ...r.infraRefs ?? []];
10751
- const meta = tags.length ? import_picocolors33.default.dim(` [${tags.join(", ")}]`) : "";
10752
- line(`${import_picocolors33.default.dim(r.id)} ${import_picocolors33.default.dim(shortDate(r.createdAt))} ${r.title}${meta}`);
11045
+ const meta = tags.length ? import_picocolors34.default.dim(` [${tags.join(", ")}]`) : "";
11046
+ line(`${import_picocolors34.default.dim(r.id)} ${import_picocolors34.default.dim(shortDate(r.createdAt))} ${r.title}${meta}`);
10753
11047
  line(` ${truncate(r.decision, 100)}`);
10754
11048
  }
10755
11049
  });
@@ -10763,16 +11057,16 @@ function registerDecision(program3) {
10763
11057
  `/v1/projects/${projectId}/architecture-decisions/${args[0]}`
10764
11058
  );
10765
11059
  ok(row, () => {
10766
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10767
- line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
11060
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11061
+ line(import_picocolors34.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10768
11062
  line(`
10769
- ${import_picocolors33.default.bold("Context")}
11063
+ ${import_picocolors34.default.bold("Context")}
10770
11064
  ${row.context}`);
10771
11065
  line(`
10772
- ${import_picocolors33.default.bold("Decision")}
11066
+ ${import_picocolors34.default.bold("Decision")}
10773
11067
  ${row.decision}`);
10774
11068
  if (row.consequences) line(`
10775
- ${import_picocolors33.default.bold("Consequences")}
11069
+ ${import_picocolors34.default.bold("Consequences")}
10776
11070
  ${row.consequences}`);
10777
11071
  });
10778
11072
  })
@@ -10789,7 +11083,7 @@ ${row.consequences}`);
10789
11083
  const path = `/v1/projects/${projectId}/architecture-decisions/${id}`;
10790
11084
  if (opts.delete) {
10791
11085
  await api(ctx, path, { method: "DELETE" });
10792
- ok({ deleted: true, id }, () => line(`Deleted decision ${import_picocolors33.default.bold(id)}.`));
11086
+ ok({ deleted: true, id }, () => line(`Deleted decision ${import_picocolors34.default.bold(id)}.`));
10793
11087
  return;
10794
11088
  }
10795
11089
  const row = await api(ctx, path, {
@@ -10799,7 +11093,7 @@ ${row.consequences}`);
10799
11093
  ok(
10800
11094
  row,
10801
11095
  () => line(
10802
- `Marked ${import_picocolors33.default.bold(row?.title ?? id)} superseded.` + (opts.reason ? ` Record the replacement with \`decision create\`.` : "")
11096
+ `Marked ${import_picocolors34.default.bold(row?.title ?? id)} superseded.` + (opts.reason ? ` Record the replacement with \`decision create\`.` : "")
10803
11097
  )
10804
11098
  );
10805
11099
  })
@@ -10820,7 +11114,7 @@ ${row.consequences}`);
10820
11114
  `/v1/projects/${projectId}/architecture-decisions/${id}`,
10821
11115
  { method: "PATCH", body }
10822
11116
  );
10823
- ok(row, () => line(`Filed decision ${import_picocolors33.default.bold(row?.title ?? id)}.`));
11117
+ ok(row, () => line(`Filed decision ${import_picocolors34.default.bold(row?.title ?? id)}.`));
10824
11118
  })
10825
11119
  );
10826
11120
  decision.command("create <title>").description(
@@ -10851,7 +11145,7 @@ ${row.consequences}`);
10851
11145
  refId: row?.id,
10852
11146
  output: { decision: row }
10853
11147
  });
10854
- ok(row, () => line(`Recorded decision ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
11148
+ ok(row, () => line(`Recorded decision ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
10855
11149
  })
10856
11150
  );
10857
11151
  const requirement = program3.command("requirement").description("Read and record the project's requirements");
@@ -10863,11 +11157,11 @@ ${row.consequences}`);
10863
11157
  rows = applyLimit(rows, opts.limit);
10864
11158
  ok(rows, () => {
10865
11159
  if (!rows.length) {
10866
- line(import_picocolors33.default.dim("No requirements recorded yet."));
11160
+ line(import_picocolors34.default.dim("No requirements recorded yet."));
10867
11161
  return;
10868
11162
  }
10869
11163
  for (const r of rows) {
10870
- line(`${import_picocolors33.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
11164
+ line(`${import_picocolors34.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
10871
11165
  }
10872
11166
  });
10873
11167
  })
@@ -10880,8 +11174,8 @@ ${row.consequences}`);
10880
11174
  `/v1/projects/${projectId}/requirements/${args[0]}`
10881
11175
  );
10882
11176
  ok(row, () => {
10883
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10884
- line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
11177
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11178
+ line(import_picocolors34.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10885
11179
  line(`
10886
11180
  ${row.body}`);
10887
11181
  });
@@ -10907,7 +11201,7 @@ ${row.body}`);
10907
11201
  refId: row?.id,
10908
11202
  output: { requirement: row }
10909
11203
  });
10910
- ok(row, () => line(`Recorded requirement ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
11204
+ ok(row, () => line(`Recorded requirement ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
10911
11205
  })
10912
11206
  );
10913
11207
  requirement.command("update <id>").description(
@@ -10936,7 +11230,7 @@ ${row.body}`);
10936
11230
  code: "bad_request"
10937
11231
  });
10938
11232
  }
10939
- ok(row, () => line(`Updated requirement ${import_picocolors33.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
11233
+ ok(row, () => line(`Updated requirement ${import_picocolors34.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
10940
11234
  })
10941
11235
  );
10942
11236
  }
@@ -10975,7 +11269,7 @@ function toList(value) {
10975
11269
  }
10976
11270
 
10977
11271
  // src/commands/doc.ts
10978
- var import_picocolors34 = __toESM(require_picocolors(), 1);
11272
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
10979
11273
 
10980
11274
  // src/mermaid-fences.ts
10981
11275
  function hasDiagram(code) {
@@ -11062,16 +11356,16 @@ function registerDoc(program3) {
11062
11356
  }) ?? [];
11063
11357
  ok(rows, () => {
11064
11358
  if (!rows.length) {
11065
- line(import_picocolors34.default.dim("No documents yet."));
11359
+ line(import_picocolors35.default.dim("No documents yet."));
11066
11360
  return;
11067
11361
  }
11068
11362
  for (const r of rows) {
11069
- const link = r.workItemId ? import_picocolors34.default.dim(` \u21B3 ${r.workItemId}`) : "";
11070
- const file = r.filePath ? import_picocolors34.default.dim(` ${r.filePath}`) : "";
11363
+ const link = r.workItemId ? import_picocolors35.default.dim(` \u21B3 ${r.workItemId}`) : "";
11364
+ const file = r.filePath ? import_picocolors35.default.dim(` ${r.filePath}`) : "";
11071
11365
  const tags = [...r.labels ?? [], ...r.infraRefs ?? []];
11072
- const meta = tags.length ? import_picocolors34.default.dim(` [${tags.join(", ")}]`) : "";
11073
- const kind = r.docKind ? import_picocolors34.default.dim(`${r.docKind} `) : "";
11074
- line(`${import_picocolors34.default.dim(r.id)} ${kind}${r.title}${meta}${link}${file}`);
11366
+ const meta = tags.length ? import_picocolors35.default.dim(` [${tags.join(", ")}]`) : "";
11367
+ const kind = r.docKind ? import_picocolors35.default.dim(`${r.docKind} `) : "";
11368
+ line(`${import_picocolors35.default.dim(r.id)} ${kind}${r.title}${meta}${link}${file}`);
11075
11369
  }
11076
11370
  });
11077
11371
  })
@@ -11085,17 +11379,17 @@ function registerDoc(program3) {
11085
11379
  );
11086
11380
  if (opts.markdown) {
11087
11381
  ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
11088
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11382
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11089
11383
  line(
11090
- row.filePath ? `Read it at ${import_picocolors34.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors34.default.dim("This document has no markdown mirror on disk yet.")
11384
+ row.filePath ? `Read it at ${import_picocolors35.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors35.default.dim("This document has no markdown mirror on disk yet.")
11091
11385
  );
11092
11386
  });
11093
11387
  return;
11094
11388
  }
11095
11389
  ok(row, () => {
11096
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11097
- if (row.workItemId) line(import_picocolors34.default.dim(`linked to work item ${row.workItemId}`));
11098
- if (row.filePath) line(import_picocolors34.default.dim(`markdown mirror: ${row.filePath}`));
11390
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11391
+ if (row.workItemId) line(import_picocolors35.default.dim(`linked to work item ${row.workItemId}`));
11392
+ if (row.filePath) line(import_picocolors35.default.dim(`markdown mirror: ${row.filePath}`));
11099
11393
  line("");
11100
11394
  line(row.contentJson);
11101
11395
  });
@@ -11128,7 +11422,7 @@ function registerDoc(program3) {
11128
11422
  refId: row?.id,
11129
11423
  output: { document: row }
11130
11424
  });
11131
- ok(row, () => line(`Created document ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
11425
+ ok(row, () => line(`Created document ${import_picocolors35.default.bold(row?.id ?? "")} \u2014 ${title}`));
11132
11426
  })
11133
11427
  );
11134
11428
  doc.command("file <id>").description("File an existing document \u2014 its kind, labels, app, infrastructure").option("--kind <kind>", `what this document IS \u2014 ${KIND_HELP2}`).option("--label <name...>", "tag it \u2014 shares the project's label vocabulary").option("--app <id>", "which app this document is about").option("--infra <name...>", "what it touches: database, storage, auth, \u2026").action(
@@ -11147,7 +11441,7 @@ function registerDoc(program3) {
11147
11441
  `/v1/projects/${projectId}/documents/${id}`,
11148
11442
  { method: "PATCH", body }
11149
11443
  );
11150
- ok(row, () => line(`Filed ${import_picocolors34.default.bold(row?.title ?? id)}.`));
11444
+ ok(row, () => line(`Filed ${import_picocolors35.default.bold(row?.title ?? id)}.`));
11151
11445
  })
11152
11446
  );
11153
11447
  doc.command("diagram <id>").description(
@@ -11173,19 +11467,19 @@ function registerDoc(program3) {
11173
11467
  diagrams: diagrams.map((code) => ({ kind: diagramKind(code), code }))
11174
11468
  };
11175
11469
  ok(payload, () => {
11176
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11470
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11177
11471
  if (markdown === null) {
11178
11472
  line(
11179
- import_picocolors34.default.dim(
11473
+ import_picocolors35.default.dim(
11180
11474
  row.filePath ? `Could not read ${row.filePath} from this folder.` : "This document has no markdown mirror on disk yet."
11181
11475
  )
11182
11476
  );
11183
11477
  } else if (!diagrams.length) {
11184
- line(import_picocolors34.default.dim("No diagrams in this document."));
11478
+ line(import_picocolors35.default.dim("No diagrams in this document."));
11185
11479
  } else {
11186
11480
  for (const [i, code] of diagrams.entries()) {
11187
11481
  const kind = diagramKind(code) ?? "diagram";
11188
- line(`${import_picocolors34.default.dim(String(i + 1))} ${kind} ${import_picocolors34.default.dim(`${code.split("\n").length} lines`)}`);
11482
+ line(`${import_picocolors35.default.dim(String(i + 1))} ${kind} ${import_picocolors35.default.dim(`${code.split("\n").length} lines`)}`);
11189
11483
  }
11190
11484
  }
11191
11485
  });
@@ -11221,7 +11515,7 @@ function registerDoc(program3) {
11221
11515
  code: "bad_request"
11222
11516
  });
11223
11517
  }
11224
- ok(row, () => line(`Updated document ${import_picocolors34.default.bold(row.id)} \u2014 ${row.title}`));
11518
+ ok(row, () => line(`Updated document ${import_picocolors35.default.bold(row.id)} \u2014 ${row.title}`));
11225
11519
  })
11226
11520
  );
11227
11521
  }
@@ -11235,7 +11529,7 @@ function readMirror(cwd, filePath) {
11235
11529
  }
11236
11530
 
11237
11531
  // src/commands/design.ts
11238
- var import_picocolors35 = __toESM(require_picocolors(), 1);
11532
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
11239
11533
  function registerDesign(program3) {
11240
11534
  const design = program3.command("design").description("The project's brand, and pictures of the screens you draw");
11241
11535
  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(
@@ -11249,11 +11543,11 @@ function registerDesign(program3) {
11249
11543
  if (opts.raw) {
11250
11544
  ok(files, () => {
11251
11545
  if (!files.length) {
11252
- line(import_picocolors35.default.dim("No brand set for this project."));
11546
+ line(import_picocolors36.default.dim("No brand set for this project."));
11253
11547
  return;
11254
11548
  }
11255
11549
  for (const f of files) {
11256
- line(import_picocolors35.default.bold(f.path));
11550
+ line(import_picocolors36.default.bold(f.path));
11257
11551
  line(f.contents);
11258
11552
  line("");
11259
11553
  }
@@ -11270,21 +11564,21 @@ function registerDesign(program3) {
11270
11564
  } : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
11271
11565
  ok(summary, () => {
11272
11566
  if (!tokens) {
11273
- line(import_picocolors35.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
11567
+ line(import_picocolors36.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
11274
11568
  return;
11275
11569
  }
11276
11570
  for (const [name, value] of Object.entries(tokens.brand)) {
11277
- line(`${import_picocolors35.default.dim(name.padEnd(12))} ${value}`);
11571
+ line(`${import_picocolors36.default.dim(name.padEnd(12))} ${value}`);
11278
11572
  }
11279
11573
  for (const [name, value] of Object.entries(tokens.color)) {
11280
- line(`${import_picocolors35.default.dim(`color.${name}`.padEnd(12))} ${value}`);
11574
+ line(`${import_picocolors36.default.dim(`color.${name}`.padEnd(12))} ${value}`);
11281
11575
  }
11282
11576
  for (const [name, value] of Object.entries(tokens.font)) {
11283
- line(`${import_picocolors35.default.dim(`font.${name}`.padEnd(12))} ${value}`);
11577
+ line(`${import_picocolors36.default.dim(`font.${name}`.padEnd(12))} ${value}`);
11284
11578
  }
11285
11579
  line("");
11286
11580
  line(
11287
- import_picocolors35.default.dim(
11581
+ import_picocolors36.default.dim(
11288
11582
  `Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
11289
11583
  )
11290
11584
  );
@@ -11306,19 +11600,19 @@ function registerDesign(program3) {
11306
11600
  ok(res, () => {
11307
11601
  for (const shot of res.shots ?? []) {
11308
11602
  if (shot.out) {
11309
- line(`${import_picocolors35.default.green("\u2713")} ${shot.file} ${import_picocolors35.default.dim("\u2192")} ${shot.out}`);
11603
+ line(`${import_picocolors36.default.green("\u2713")} ${shot.file} ${import_picocolors36.default.dim("\u2192")} ${shot.out}`);
11310
11604
  } else {
11311
- line(`${import_picocolors35.default.red("\u2717")} ${shot.file} ${import_picocolors35.default.dim(shot.error ?? "no image")}`);
11605
+ line(`${import_picocolors36.default.red("\u2717")} ${shot.file} ${import_picocolors36.default.dim(shot.error ?? "no image")}`);
11312
11606
  }
11313
11607
  }
11314
11608
  for (const path of res.refused ?? []) {
11315
- line(import_picocolors35.default.dim(`skipped ${path} \u2014 not a page inside this project`));
11609
+ line(import_picocolors36.default.dim(`skipped ${path} \u2014 not a page inside this project`));
11316
11610
  }
11317
11611
  const made = (res.shots ?? []).filter((s) => s.out).length;
11318
11612
  if (made) {
11319
11613
  line("");
11320
11614
  line(
11321
- import_picocolors35.default.dim(
11615
+ import_picocolors36.default.dim(
11322
11616
  `Record each one so it shows up: workser artifact add <file>.png --kind design -d "<what this screen is>"`
11323
11617
  )
11324
11618
  );
@@ -11350,7 +11644,7 @@ function unwrap(group) {
11350
11644
  }
11351
11645
 
11352
11646
  // src/commands/api.ts
11353
- var import_picocolors36 = __toESM(require_picocolors(), 1);
11647
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
11354
11648
  import { readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
11355
11649
  import { join as join3, relative, sep } from "path";
11356
11650
 
@@ -11444,16 +11738,16 @@ function specReport(routes, documented) {
11444
11738
  ok: missing.length === 0
11445
11739
  };
11446
11740
  }
11447
- function specSummary(report, specFile) {
11741
+ function specSummary(report2, specFile) {
11448
11742
  if (!specFile) {
11449
11743
  return `This service has no API description. Write one at ${SPEC_FILES[0]} listing the routes it serves.`;
11450
11744
  }
11451
- if (report.ok) {
11452
- const n = report.routes.length;
11745
+ if (report2.ok) {
11746
+ const n = report2.routes.length;
11453
11747
  return n === 1 ? `The one route this service has is described in ${specFile}.` : `All ${n} routes are described in ${specFile}.`;
11454
11748
  }
11455
- const names = report.missing.map((m) => m.path).join(", ");
11456
- return `${report.missing.length} route${report.missing.length === 1 ? " is" : "s are"} not described in ${specFile}: ${names}`;
11749
+ const names = report2.missing.map((m) => m.path).join(", ");
11750
+ return `${report2.missing.length} route${report2.missing.length === 1 ? " is" : "s are"} not described in ${specFile}: ${names}`;
11457
11751
  }
11458
11752
 
11459
11753
  // src/commands/api.ts
@@ -11464,10 +11758,10 @@ function registerApi(program3) {
11464
11758
  const appId = requireApp2(opts.app);
11465
11759
  const res = await api(ctx, `/v1/apps/${encodeURIComponent(appId)}/api/requests`);
11466
11760
  ok(res, () => {
11467
- for (const note of res?.notes ?? []) line(import_picocolors36.default.dim(note));
11761
+ for (const note of res?.notes ?? []) line(import_picocolors37.default.dim(note));
11468
11762
  for (const r of res?.requests ?? []) {
11469
11763
  line(
11470
- `${import_picocolors36.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors36.default.dim(` ${r.note}`) : ""}`
11764
+ `${import_picocolors37.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors37.default.dim(` ${r.note}`) : ""}`
11471
11765
  );
11472
11766
  }
11473
11767
  });
@@ -11501,14 +11795,14 @@ function registerApi(program3) {
11501
11795
  );
11502
11796
  ok(res, () => {
11503
11797
  if (!res?.ok) {
11504
- line(import_picocolors36.default.red(res?.error ?? "The service did not answer."));
11798
+ line(import_picocolors37.default.red(res?.error ?? "The service did not answer."));
11505
11799
  return;
11506
11800
  }
11507
11801
  const code = `${res.status}${res.statusText ? ` ${res.statusText}` : ""}`;
11508
- const colour2 = res.status && res.status < 300 ? import_picocolors36.default.green : res.status && res.status < 500 ? import_picocolors36.default.yellow : import_picocolors36.default.red;
11509
- line(`${colour2(code)} ${import_picocolors36.default.dim(`${res.durationMs}ms ${res.url}`)}`);
11802
+ const colour2 = res.status && res.status < 300 ? import_picocolors37.default.green : res.status && res.status < 500 ? import_picocolors37.default.yellow : import_picocolors37.default.red;
11803
+ line(`${colour2(code)} ${import_picocolors37.default.dim(`${res.durationMs}ms ${res.url}`)}`);
11510
11804
  if (res.body) line(res.body);
11511
- if (res.truncated) line(import_picocolors36.default.dim("(answer truncated)"));
11805
+ if (res.truncated) line(import_picocolors37.default.dim("(answer truncated)"));
11512
11806
  });
11513
11807
  if (!res?.ok) process.exitCode = 1;
11514
11808
  })
@@ -11522,23 +11816,23 @@ function registerApi(program3) {
11522
11816
  const routes = discoverRoutes(files);
11523
11817
  const specFile = SPEC_FILES.find((f) => files.includes(f)) ?? null;
11524
11818
  const documented = specFile ? specPaths(readIfPresent(join3(ctx.cwd, specFile))) : [];
11525
- const report = specReport(routes, documented);
11526
- const summary = specSummary(report, specFile);
11527
- ok({ ...report, specFile, summary }, () => {
11528
- for (const r of report.routes) {
11529
- const known = report.missing.some((m) => m.path === r.path);
11819
+ const report2 = specReport(routes, documented);
11820
+ const summary = specSummary(report2, specFile);
11821
+ ok({ ...report2, specFile, summary }, () => {
11822
+ for (const r of report2.routes) {
11823
+ const known = report2.missing.some((m) => m.path === r.path);
11530
11824
  line(
11531
- `${known ? import_picocolors36.default.yellow("undocumented") : import_picocolors36.default.green("documented ")} ${r.path}${import_picocolors36.default.dim(` ${r.file}`)}`
11825
+ `${known ? import_picocolors37.default.yellow("undocumented") : import_picocolors37.default.green("documented ")} ${r.path}${import_picocolors37.default.dim(` ${r.file}`)}`
11532
11826
  );
11533
11827
  }
11534
- for (const p of report.stale) {
11535
- line(`${import_picocolors36.default.dim("in spec only ")} ${p}`);
11828
+ for (const p of report2.stale) {
11829
+ line(`${import_picocolors37.default.dim("in spec only ")} ${p}`);
11536
11830
  }
11537
11831
  line("");
11538
- if (report.ok && specFile) success(summary);
11539
- else line(import_picocolors36.default.yellow(summary));
11832
+ if (report2.ok && specFile) success(summary);
11833
+ else line(import_picocolors37.default.yellow(summary));
11540
11834
  });
11541
- if (opts.check && !report.ok) {
11835
+ if (opts.check && !report2.ok) {
11542
11836
  throw new WorkserError(summary, { code: "bad_request" });
11543
11837
  }
11544
11838
  if (opts.check && !specFile) {
@@ -11607,7 +11901,7 @@ function listRepoFiles(root, maxDepth = 8) {
11607
11901
  }
11608
11902
 
11609
11903
  // src/commands/analysis.ts
11610
- var import_picocolors37 = __toESM(require_picocolors(), 1);
11904
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
11611
11905
  import { readFileSync as readFileSync4 } from "fs";
11612
11906
  function registerAnalysis(program3) {
11613
11907
  const cmd = program3.command("analysis").description("Run Python analysis locally, recorded in the task");
@@ -11617,14 +11911,14 @@ function registerAnalysis(program3) {
11617
11911
  const res = await api(ctx, path);
11618
11912
  ok(res, () => {
11619
11913
  line(
11620
- `${res?.available ? import_picocolors37.default.green("python") : import_picocolors37.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors37.default.dim(res?.python ?? "")}`
11914
+ `${res?.available ? import_picocolors38.default.green("python") : import_picocolors38.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors38.default.dim(res?.python ?? "")}`
11621
11915
  );
11622
11916
  for (const lib of res?.libraries ?? []) {
11623
11917
  line(
11624
- `${lib.present ? import_picocolors37.default.green(lib.name) : import_picocolors37.default.yellow(lib.name)}${import_picocolors37.default.dim(lib.present ? "" : " missing")}`
11918
+ `${lib.present ? import_picocolors38.default.green(lib.name) : import_picocolors38.default.yellow(lib.name)}${import_picocolors38.default.dim(lib.present ? "" : " missing")}`
11625
11919
  );
11626
11920
  }
11627
- for (const note of res?.notes ?? []) line(import_picocolors37.default.dim(note));
11921
+ for (const note of res?.notes ?? []) line(import_picocolors38.default.dim(note));
11628
11922
  });
11629
11923
  if (!res?.available) process.exitCode = 1;
11630
11924
  })
@@ -11646,15 +11940,15 @@ function registerAnalysis(program3) {
11646
11940
  );
11647
11941
  ok(res, () => {
11648
11942
  if (res?.stdout) line(res.stdout.replace(/\n$/, ""));
11649
- if (res?.stderr) line(import_picocolors37.default.dim(res.stderr.replace(/\n$/, "")));
11650
- if (res?.truncated) line(import_picocolors37.default.dim("(output truncated)"));
11943
+ if (res?.stderr) line(import_picocolors38.default.dim(res.stderr.replace(/\n$/, "")));
11944
+ if (res?.truncated) line(import_picocolors38.default.dim("(output truncated)"));
11651
11945
  const took = `${Math.round((res?.durationMs ?? 0) / 100) / 10}s`;
11652
11946
  line(
11653
- res?.ok ? import_picocolors37.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors37.default.dim(` ${took}`) : import_picocolors37.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors37.default.dim(` ${took}`)
11947
+ res?.ok ? import_picocolors38.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors38.default.dim(` ${took}`) : import_picocolors38.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors38.default.dim(` ${took}`)
11654
11948
  );
11655
11949
  if (res && !res.sandboxed) {
11656
11950
  line(
11657
- import_picocolors37.default.dim(
11951
+ import_picocolors38.default.dim(
11658
11952
  "This platform has no OS sandbox, so the script ran with your own file access."
11659
11953
  )
11660
11954
  );
@@ -11682,7 +11976,7 @@ function readCode(file, inline) {
11682
11976
  }
11683
11977
 
11684
11978
  // src/commands/scan.ts
11685
- var import_picocolors38 = __toESM(require_picocolors(), 1);
11979
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
11686
11980
  import { spawnSync as spawnSync2 } from "child_process";
11687
11981
  import { existsSync as existsSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
11688
11982
  import { join as join4, relative as relative2, sep as sep2 } from "path";
@@ -11840,15 +12134,15 @@ function buildReport(input) {
11840
12134
  function rank(s) {
11841
12135
  return s === "high" ? 3 : s === "medium" ? 2 : 1;
11842
12136
  }
11843
- function scanSummary(report) {
11844
- const ran = report.checked.length;
12137
+ function scanSummary(report2) {
12138
+ const ran = report2.checked.length;
11845
12139
  if (!ran) return "Nothing could be checked \u2014 see the reasons above.";
11846
- const what = report.checked.join(", ");
11847
- const total = report.findings.length;
12140
+ const what = report2.checked.join(", ");
12141
+ const total = report2.findings.length;
11848
12142
  if (!total) {
11849
12143
  return `Checked ${what} \u2014 nothing found.`;
11850
12144
  }
11851
- const high = report.counts.high;
12145
+ const high = report2.counts.high;
11852
12146
  return `Checked ${what} \u2014 ${total} ${total === 1 ? "thing" : "things"} to look at` + (high ? `, ${high} of them serious.` : ".");
11853
12147
  }
11854
12148
 
@@ -11870,10 +12164,10 @@ function registerScan(program3) {
11870
12164
  if (only.has("secrets")) runSecrets(ctx.cwd, !!opts.staged, findings, checked, skipped);
11871
12165
  if (only.has("deps")) runDeps(ctx.cwd, findings, checked, skipped);
11872
12166
  if (only.has("permissions")) runPermissions(ctx.cwd, findings, checked, skipped);
11873
- const report = buildReport({ findings, checked, skipped });
11874
- const summary = scanSummary(report);
11875
- ok({ ...report, summary }, () => print2(report, summary));
11876
- if (opts.check && !report.ok) {
12167
+ const report2 = buildReport({ findings, checked, skipped });
12168
+ const summary = scanSummary(report2);
12169
+ ok({ ...report2, summary }, () => print2(report2, summary));
12170
+ if (opts.check && !report2.ok) {
11877
12171
  throw new WorkserError(summary, { code: "bad_request" });
11878
12172
  }
11879
12173
  })
@@ -11964,24 +12258,24 @@ function runPermissions(cwd, findings, checked, skipped) {
11964
12258
  });
11965
12259
  }
11966
12260
  }
11967
- function print2(report, summary) {
11968
- for (const s of report.skipped) {
11969
- line(`${import_picocolors38.default.yellow("not checked")} ${s.check}${import_picocolors38.default.dim(` \u2014 ${s.reason}`)}`);
12261
+ function print2(report2, summary) {
12262
+ for (const s of report2.skipped) {
12263
+ line(`${import_picocolors39.default.yellow("not checked")} ${s.check}${import_picocolors39.default.dim(` \u2014 ${s.reason}`)}`);
11970
12264
  }
11971
- for (const f of report.findings) {
11972
- const where = f.file ? import_picocolors38.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
12265
+ for (const f of report2.findings) {
12266
+ const where = f.file ? import_picocolors39.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
11973
12267
  line(`${severityTag(f.severity)} ${f.title}${where}`);
11974
- line(` ${import_picocolors38.default.dim(f.fix)}`);
12268
+ line(` ${import_picocolors39.default.dim(f.fix)}`);
11975
12269
  }
11976
- if (report.findings.length || report.skipped.length) line("");
11977
- if (report.ok && !report.skipped.length) success(summary);
11978
- else if (report.ok) line(import_picocolors38.default.yellow(summary));
11979
- else line(import_picocolors38.default.red(summary));
12270
+ if (report2.findings.length || report2.skipped.length) line("");
12271
+ if (report2.ok && !report2.skipped.length) success(summary);
12272
+ else if (report2.ok) line(import_picocolors39.default.yellow(summary));
12273
+ else line(import_picocolors39.default.red(summary));
11980
12274
  }
11981
12275
  function severityTag(severity) {
11982
- if (severity === "high") return import_picocolors38.default.red("serious ");
11983
- if (severity === "medium") return import_picocolors38.default.yellow("worth fixing");
11984
- return import_picocolors38.default.dim("minor ");
12276
+ if (severity === "high") return import_picocolors39.default.red("serious ");
12277
+ if (severity === "medium") return import_picocolors39.default.yellow("worth fixing");
12278
+ return import_picocolors39.default.dim("minor ");
11985
12279
  }
11986
12280
  function git(cwd, args) {
11987
12281
  try {
@@ -12042,7 +12336,7 @@ function listRepoFiles2(root, maxDepth = 8) {
12042
12336
  }
12043
12337
 
12044
12338
  // src/commands/health.ts
12045
- var import_picocolors39 = __toESM(require_picocolors(), 1);
12339
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
12046
12340
  function registerHealth(program3) {
12047
12341
  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(
12048
12342
  action(async ({ ctx, opts }) => {
@@ -12056,16 +12350,16 @@ function registerHealth(program3) {
12056
12350
  }
12057
12351
  function print3(res) {
12058
12352
  if (!res?.checks?.length) {
12059
- line(import_picocolors39.default.dim(res?.note ?? "Nothing to check."));
12353
+ line(import_picocolors40.default.dim(res?.note ?? "Nothing to check."));
12060
12354
  return;
12061
12355
  }
12062
12356
  for (const c of res.checks) {
12063
- const mark = c.ok ? import_picocolors39.default.green("up ") : import_picocolors39.default.red("down");
12064
- const timing = import_picocolors39.default.dim(`${c.ms}ms`);
12065
- const detail = c.ok ? timing : import_picocolors39.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
12066
- line(` ${mark} ${c.appName} ${import_picocolors39.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
12357
+ const mark = c.ok ? import_picocolors40.default.green("up ") : import_picocolors40.default.red("down");
12358
+ const timing = import_picocolors40.default.dim(`${c.ms}ms`);
12359
+ const detail = c.ok ? timing : import_picocolors40.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
12360
+ line(` ${mark} ${c.appName} ${import_picocolors40.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
12067
12361
  if (c.incidentOpened) {
12068
- line(import_picocolors39.default.yellow(` An incident has been opened on the board for this.`));
12362
+ line(import_picocolors40.default.yellow(` An incident has been opened on the board for this.`));
12069
12363
  }
12070
12364
  }
12071
12365
  const down = res.checks.filter((c) => !c.ok);
@@ -12078,14 +12372,14 @@ function print3(res) {
12078
12372
  }
12079
12373
  const production = down.filter((c) => c.environment === "production").length;
12080
12374
  line(
12081
- import_picocolors39.default.red(
12375
+ import_picocolors40.default.red(
12082
12376
  `${down.length} of ${res.checks.length} not answering` + (production ? ` \u2014 ${production} customer-facing.` : " (preview only).")
12083
12377
  )
12084
12378
  );
12085
12379
  }
12086
12380
 
12087
12381
  // src/commands/urls.ts
12088
- var import_picocolors40 = __toESM(require_picocolors(), 1);
12382
+ var import_picocolors41 = __toESM(require_picocolors(), 1);
12089
12383
  function registerUrls(program3) {
12090
12384
  program3.command("urls").description("The stable preview and production addresses of every app in this project").option("--app <webAppId>", "just one app").action(
12091
12385
  action(async ({ ctx, opts }) => {
@@ -12099,20 +12393,20 @@ function registerUrls(program3) {
12099
12393
  const summary = urlsSummary(rows);
12100
12394
  ok({ rows, summary }, () => {
12101
12395
  for (const row of rows) {
12102
- const label = import_picocolors40.default.dim(row.environment.padEnd(10));
12103
- const value = row.url ? import_picocolors40.default.cyan(row.url) : import_picocolors40.default.dim(row.note ?? "not published");
12396
+ const label = import_picocolors41.default.dim(row.environment.padEnd(10));
12397
+ const value = row.url ? import_picocolors41.default.cyan(row.url) : import_picocolors41.default.dim(row.note ?? "not published");
12104
12398
  line(` ${row.appName.padEnd(22)} ${label} ${value}`);
12105
12399
  }
12106
12400
  line("");
12107
12401
  if (rows.some((r) => r.url)) success(summary);
12108
- else line(import_picocolors40.default.yellow(summary));
12402
+ else line(import_picocolors41.default.yellow(summary));
12109
12403
  });
12110
12404
  })
12111
12405
  );
12112
12406
  }
12113
12407
 
12114
12408
  // src/commands/deployments.ts
12115
- var import_picocolors41 = __toESM(require_picocolors(), 1);
12409
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
12116
12410
  function registerDeployments(program3) {
12117
12411
  const cmd = program3.command("deployments").description("Deployment history, and putting a build in front of customers");
12118
12412
  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(
@@ -12130,7 +12424,7 @@ function registerDeployments(program3) {
12130
12424
  ok(res, () => {
12131
12425
  if (!items.length) {
12132
12426
  return line(
12133
- import_picocolors41.default.dim(
12427
+ import_picocolors42.default.dim(
12134
12428
  environment ? `Nothing has been deployed to ${environment} yet.` : "Nothing has been deployed yet. `workser deploy` builds the first one."
12135
12429
  )
12136
12430
  );
@@ -12150,13 +12444,13 @@ function registerDeployments(program3) {
12150
12444
  ).catch(() => null) : null;
12151
12445
  ok({ ...dep, logs }, () => {
12152
12446
  line(formatDeployment(dep));
12153
- if (dep?.error_message) line(import_picocolors41.default.red(` ${dep.error_message}`));
12447
+ if (dep?.error_message) line(import_picocolors42.default.red(` ${dep.error_message}`));
12154
12448
  const events = logs?.events ?? [];
12155
12449
  for (const e of events) {
12156
- line(` ${import_picocolors41.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
12450
+ line(` ${import_picocolors42.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
12157
12451
  }
12158
12452
  if (opts.logs && !events.length) {
12159
- line(import_picocolors41.default.dim(" That build produced no output."));
12453
+ line(import_picocolors42.default.dim(" That build produced no output."));
12160
12454
  }
12161
12455
  });
12162
12456
  })
@@ -12200,16 +12494,16 @@ function printPromoted(res, version) {
12200
12494
  const what = version === null ? "the latest build" : `version ${version}`;
12201
12495
  const url = res.url ?? res.vercel_url;
12202
12496
  success(`Production is being rebuilt from ${what}.`);
12203
- if (url) line(import_picocolors41.default.dim(`It will be at ${url}`));
12204
- line(import_picocolors41.default.dim("`workser deploy status` follows it."));
12497
+ if (url) line(import_picocolors42.default.dim(`It will be at ${url}`));
12498
+ line(import_picocolors42.default.dim("`workser deploy status` follows it."));
12205
12499
  }
12206
12500
  function formatDeployment(d) {
12207
12501
  if (!d) return "";
12208
- const version = d.version !== void 0 ? import_picocolors41.default.yellow(`v${d.version}`) : import_picocolors41.default.dim("v?");
12209
- const env = import_picocolors41.default.dim((d.environment ?? "?").padEnd(10));
12502
+ const version = d.version !== void 0 ? import_picocolors42.default.yellow(`v${d.version}`) : import_picocolors42.default.dim("v?");
12503
+ const env = import_picocolors42.default.dim((d.environment ?? "?").padEnd(10));
12210
12504
  const app = d.webAppName ? `${d.webAppName} ` : "";
12211
- const when = import_picocolors41.default.dim(formatTime2(d.created_at));
12212
- const url = d.url ? " " + import_picocolors41.default.cyan(d.url) : "";
12505
+ const when = import_picocolors42.default.dim(formatTime2(d.created_at));
12506
+ const url = d.url ? " " + import_picocolors42.default.cyan(d.url) : "";
12213
12507
  return `${version} ${env} ${colorStatus(d.status ?? "")} ${app}${when}${url}`;
12214
12508
  }
12215
12509
  function formatTime2(t) {
@@ -12219,7 +12513,7 @@ function formatTime2(t) {
12219
12513
  }
12220
12514
 
12221
12515
  // src/commands/usage.ts
12222
- var import_picocolors42 = __toESM(require_picocolors(), 1);
12516
+ var import_picocolors43 = __toESM(require_picocolors(), 1);
12223
12517
 
12224
12518
  // src/usage.ts
12225
12519
  var NEAR_LIMIT_FRACTION = 0.8;
@@ -12266,22 +12560,22 @@ function dimensionLine(d) {
12266
12560
  }
12267
12561
  return `${d.label}: ${used} of ${limit}`;
12268
12562
  }
12269
- function usageSummary(report) {
12270
- const dims = report.dimensions ?? [];
12563
+ function usageSummary(report2) {
12564
+ const dims = report2.dimensions ?? [];
12271
12565
  const unknown = dims.filter((d) => d.used === null);
12272
12566
  const over = dims.filter((d) => usageState(d) === "over");
12273
12567
  const near = dims.filter((d) => usageState(d) === "near");
12274
12568
  const parts = [];
12275
12569
  if (over.length) {
12276
12570
  parts.push(
12277
- `${over.length === 1 ? "One thing is" : `${over.length} things are`} over the ${report.tier} plan's limit`
12571
+ `${over.length === 1 ? "One thing is" : `${over.length} things are`} over the ${report2.tier} plan's limit`
12278
12572
  );
12279
12573
  } else if (near.length) {
12280
12574
  parts.push(
12281
- `${near.length === 1 ? "One thing is" : `${near.length} things are`} close to the ${report.tier} plan's limit`
12575
+ `${near.length === 1 ? "One thing is" : `${near.length} things are`} close to the ${report2.tier} plan's limit`
12282
12576
  );
12283
12577
  } else if (dims.some((d) => d.used !== null)) {
12284
- parts.push(`Everything is comfortably inside the ${report.tier} plan`);
12578
+ parts.push(`Everything is comfortably inside the ${report2.tier} plan`);
12285
12579
  }
12286
12580
  if (unknown.length) {
12287
12581
  parts.push(
@@ -12291,8 +12585,8 @@ function usageSummary(report) {
12291
12585
  if (!parts.length) return "Nothing could be measured.";
12292
12586
  return `${parts.join("; ")}.`;
12293
12587
  }
12294
- function shouldFail(report) {
12295
- return (report.dimensions ?? []).some(
12588
+ function shouldFail(report2) {
12589
+ return (report2.dimensions ?? []).some(
12296
12590
  (d) => d.kind === "hard" && usageState(d) === "over"
12297
12591
  );
12298
12592
  }
@@ -12302,12 +12596,12 @@ function registerUsage(program3) {
12302
12596
  const usage = program3.command("usage").description("What this project and your plan are using \u2014 storage, projects, apps").action(
12303
12597
  action(async ({ ctx }) => {
12304
12598
  const projectId = requireProject(ctx);
12305
- const report = await api(
12599
+ const report2 = await api(
12306
12600
  ctx,
12307
12601
  `/v1/projects/${projectId}/usage`
12308
12602
  );
12309
- ok(report, () => print4(report));
12310
- if (shouldFail(report)) process.exitCode = 1;
12603
+ ok(report2, () => print4(report2));
12604
+ if (shouldFail(report2)) process.exitCode = 1;
12311
12605
  })
12312
12606
  );
12313
12607
  usage.command("infra").description(
@@ -12332,15 +12626,15 @@ function printInfra(usage) {
12332
12626
  ];
12333
12627
  const width = Math.max(...rows.map(([label]) => label.length));
12334
12628
  for (const [label, dim, unit] of rows) {
12335
- const value = dim.value === null ? import_picocolors42.default.dim("not measured") : `${dim.value}${unit ? ` ${unit}` : ""}`;
12336
- const when = dim.live ? import_picocolors42.default.dim("now") : import_picocolors42.default.dim(`through ${shortDay(dim.asOf)}`);
12629
+ const value = dim.value === null ? import_picocolors43.default.dim("not measured") : `${dim.value}${unit ? ` ${unit}` : ""}`;
12630
+ const when = dim.live ? import_picocolors43.default.dim("now") : import_picocolors43.default.dim(`through ${shortDay(dim.asOf)}`);
12337
12631
  line(` ${label.padEnd(width)} ${value} ${when}`);
12338
12632
  }
12339
12633
  const stale = rows.find(([, dim]) => !dim.live && dim.nextUpdate);
12340
12634
  if (stale) {
12341
12635
  line("");
12342
12636
  line(
12343
- import_picocolors42.default.dim(
12637
+ import_picocolors43.default.dim(
12344
12638
  `Bandwidth is reported by the host in closed days, so it updates once a day \u2014 next around ${shortTime(
12345
12639
  stale[1].nextUpdate
12346
12640
  )}.`
@@ -12356,35 +12650,35 @@ function shortTime(iso) {
12356
12650
  const d = new Date(iso);
12357
12651
  return Number.isFinite(d.getTime()) ? `${d.toISOString().slice(11, 16)} UTC` : "\u2014";
12358
12652
  }
12359
- function print4(report) {
12360
- const dims = report.dimensions ?? [];
12653
+ function print4(report2) {
12654
+ const dims = report2.dimensions ?? [];
12361
12655
  if (!dims.length) {
12362
- return line(import_picocolors42.default.dim("Nothing to measure for this project yet."));
12656
+ return line(import_picocolors43.default.dim("Nothing to measure for this project yet."));
12363
12657
  }
12364
12658
  const width = Math.max(...dims.map((d) => d.label.length));
12365
12659
  for (const d of dims) {
12366
12660
  line(` ${colour(d)(dimensionLine(d).padEnd(0))}${gauge(d, width)}`);
12367
12661
  }
12368
12662
  line("");
12369
- const summary = usageSummary(report);
12663
+ const summary = usageSummary(report2);
12370
12664
  const worst = dims.map(usageState);
12371
- if (worst.includes("over")) line(import_picocolors42.default.red(summary));
12665
+ if (worst.includes("over")) line(import_picocolors43.default.red(summary));
12372
12666
  else if (worst.includes("near") || worst.includes("unknown"))
12373
- line(import_picocolors42.default.yellow(summary));
12667
+ line(import_picocolors43.default.yellow(summary));
12374
12668
  else success(summary);
12375
12669
  }
12376
12670
  function gauge(d, _labelWidth) {
12377
12671
  const drawn = bar(d);
12378
- return drawn ? ` ${import_picocolors42.default.dim(drawn)}` : "";
12672
+ return drawn ? ` ${import_picocolors43.default.dim(drawn)}` : "";
12379
12673
  }
12380
12674
  function colour(d) {
12381
12675
  switch (usageState(d)) {
12382
12676
  case "over":
12383
- return d.kind === "hard" ? import_picocolors42.default.red : import_picocolors42.default.yellow;
12677
+ return d.kind === "hard" ? import_picocolors43.default.red : import_picocolors43.default.yellow;
12384
12678
  case "near":
12385
- return import_picocolors42.default.yellow;
12679
+ return import_picocolors43.default.yellow;
12386
12680
  case "unknown":
12387
- return import_picocolors42.default.dim;
12681
+ return import_picocolors43.default.dim;
12388
12682
  default:
12389
12683
  return (s) => s;
12390
12684
  }
@@ -12392,7 +12686,7 @@ function colour(d) {
12392
12686
 
12393
12687
  // src/index.ts
12394
12688
  var pkg = {
12395
- version: true ? "0.6.27" : "0.0.0-dev"
12689
+ version: true ? "0.6.28" : "0.0.0-dev"
12396
12690
  };
12397
12691
  var program2 = new Command();
12398
12692
  program2.name("workser").description(
@@ -12438,6 +12732,7 @@ registerCheckpoint(program2);
12438
12732
  registerSync(program2);
12439
12733
  registerWorkflow(program2);
12440
12734
  registerConnection(program2);
12735
+ registerLine(program2);
12441
12736
  registerTool(program2);
12442
12737
  registerMemory(program2);
12443
12738
  registerNote(program2);