arcane-cli 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/.arcane/governance/spell-authoring-standards.md +34 -22
  3. package/dist/assets/.arcane/governance/threat-model.md +4 -4
  4. package/dist/assets/.arcane/spells/_fragments/action-required.md +27 -0
  5. package/dist/assets/.arcane/spells/_fragments/presence-report.md +1 -1
  6. package/dist/assets/.arcane/spells/spell-address-review.md +21 -9
  7. package/dist/assets/.arcane/spells/spell-adopt-docs.md +21 -9
  8. package/dist/assets/.arcane/spells/spell-app-store-accounts.md +22 -10
  9. package/dist/assets/.arcane/spells/spell-architect.md +21 -9
  10. package/dist/assets/.arcane/spells/spell-close-session.md +26 -14
  11. package/dist/assets/.arcane/spells/spell-commit-work.md +22 -10
  12. package/dist/assets/.arcane/spells/spell-create-pull-request.md +22 -10
  13. package/dist/assets/.arcane/spells/spell-eas-store-deploy.md +21 -9
  14. package/dist/assets/.arcane/spells/spell-full-cycle.md +21 -9
  15. package/dist/assets/.arcane/spells/spell-implement.md +1 -1
  16. package/dist/assets/.arcane/spells/spell-make-discoverable.md +21 -9
  17. package/dist/assets/.arcane/spells/spell-open-session.md +23 -11
  18. package/dist/assets/.arcane/spells/spell-parallel-workers.md +23 -11
  19. package/dist/assets/.arcane/spells/spell-plan.md +21 -9
  20. package/dist/assets/.arcane/spells/spell-review-batch.md +21 -9
  21. package/dist/assets/.arcane/spells/spell-review.md +22 -10
  22. package/dist/assets/.arcane/spells/spell-scope.md +21 -9
  23. package/dist/assets/.arcane/spells/spell-ship.md +21 -9
  24. package/dist/assets/.arcane/spells/spell-sync-pull-request.md +22 -10
  25. package/dist/assets/ai-context/system-prompt-context.md +1 -1
  26. package/dist/assets/schemas/presence.schema.json +28 -9
  27. package/dist/index.js +34 -28
  28. package/dist/index.js.map +1 -1
  29. package/package.json +1 -1
  30. package/dist/assets/.arcane/spells/_fragments/needs-you.md +0 -15
@@ -252,23 +252,35 @@ If `external_provider=jira` or `external_provider=other`, add explicit TODOs in
252
252
 
253
253
  ### 9. Present and Confirm
254
254
 
255
- <!-- fragment:needs-you:start -->
256
- **Needs you — required operator actions.** When this run leaves an action that only the operator can take, and the work is not correct or complete until it is taken, report it in one block at the very top of the final report, before every other section and before any optional next step:
255
+ <!-- fragment:action-required:start -->
256
+ **Action required — what only the operator can do.** When this run leaves an action that only the operator can take, report it in one block at the very top of the final report, before every other section and before any optional next step. The block exists because these are the operator's decisions to make, not because the agent is stuck: write it as a hand-off of authority, never as a plea for help. Frame it with a horizontal rule above and below, so it stands apart in a terminal as well as on a rendered page:
257
257
 
258
258
  ```markdown
259
- ## ⚠ Needs you
259
+ ---
260
+
261
+ ## 🚧 Action required
262
+
263
+ **Now**
264
+
265
+ - <exactly what to do: the command, the link, or the question> — <why only the operator can do it>
260
266
 
261
- - <exactly what to do: the command, the link, or the question> — <why the agent cannot do it itself>
267
+ **When you're at your desk**
268
+
269
+ - <exactly what to do> — <why only the operator can do it>
270
+
271
+ ---
262
272
  ```
263
273
 
264
- - **What belongs in it:** an action the work needs that only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
265
- - **One line per action.** Say exactly what to do, then, after the dash, why the agent cannot do it. Write the command, link or question out in full; never point elsewhere with "see above".
274
+ - **What belongs in it:** an action only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
275
+ - **Two tiers, one block.** **Now** holds what the work is not correct or complete without. **When you're at your desk** holds what only the operator can do but that can wait: a permission rule, a leftover remote branch, a credential to set up. Leave out a tier that has nothing in it; never split the tiers into two blocks.
276
+ - **One line per action.** Say exactly what to do, then, after the dash, why only the operator can do it. Write the command, link or question out in full; never point elsewhere with "see above".
266
277
  - **Never only among optional steps.** An action required for correctness is never listed only under next steps, suggestions, `Carry Forward` or `What's Next?`. It may be repeated there, but it always appears in this block.
267
- - **Omit it when empty.** When nothing needs the operator, leave the block out entirely: no heading and no "None" line.
278
+ - **Done items stay, struck through.** Within one session, once an item has been listed, keep it in every later report of the block; when the operator has done it, strike it through (`~~<the line>~~`) rather than dropping it, so progress shows and nothing disappears silently. The session's close carries only the open items forward.
279
+ - **Omit it when empty.** When nothing is left for the operator and nothing was struck through this session, leave the block out entirely: no heading, no rules and no "None" line.
268
280
  - **Every final report.** The block belongs in whatever report ends the run, including a report written when the spell stops early.
269
- - **Report it to the presence channel.** When the block is not empty, also run `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line of the block; with no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
281
+ - **Report the Now tier to the presence channel.** When the **Now** tier has open items, also run `spell presence report --status waiting --required-action "<line 1>" --required-action "<line 2>"`, one flag per open line of that tier; the other tier is not blocking and is not reported. With no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
270
282
  - Enforcement: explicitly advisory prose (ARC-023) — no check reads a spell's actual output for this block; it holds by these instructions alone.
271
- <!-- fragment:needs-you:end -->
283
+ <!-- fragment:action-required:end -->
272
284
 
273
285
  Present the execution plan to the user. Ask:
274
286
  1. Does the epic split make sense?
@@ -97,23 +97,35 @@ Workflow:
97
97
  - OR: BLOCK — [reason]
98
98
  ```
99
99
 
100
- <!-- fragment:needs-you:start -->
101
- **Needs you — required operator actions.** When this run leaves an action that only the operator can take, and the work is not correct or complete until it is taken, report it in one block at the very top of the final report, before every other section and before any optional next step:
100
+ <!-- fragment:action-required:start -->
101
+ **Action required — what only the operator can do.** When this run leaves an action that only the operator can take, report it in one block at the very top of the final report, before every other section and before any optional next step. The block exists because these are the operator's decisions to make, not because the agent is stuck: write it as a hand-off of authority, never as a plea for help. Frame it with a horizontal rule above and below, so it stands apart in a terminal as well as on a rendered page:
102
102
 
103
103
  ```markdown
104
- ## ⚠ Needs you
104
+ ---
105
105
 
106
- - <exactly what to do: the command, the link, or the question> — <why the agent cannot do it itself>
106
+ ## 🚧 Action required
107
+
108
+ **Now**
109
+
110
+ - <exactly what to do: the command, the link, or the question> — <why only the operator can do it>
111
+
112
+ **When you're at your desk**
113
+
114
+ - <exactly what to do> — <why only the operator can do it>
115
+
116
+ ---
107
117
  ```
108
118
 
109
- - **What belongs in it:** an action the work needs that only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
110
- - **One line per action.** Say exactly what to do, then, after the dash, why the agent cannot do it. Write the command, link or question out in full; never point elsewhere with "see above".
119
+ - **What belongs in it:** an action only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
120
+ - **Two tiers, one block.** **Now** holds what the work is not correct or complete without. **When you're at your desk** holds what only the operator can do but that can wait: a permission rule, a leftover remote branch, a credential to set up. Leave out a tier that has nothing in it; never split the tiers into two blocks.
121
+ - **One line per action.** Say exactly what to do, then, after the dash, why only the operator can do it. Write the command, link or question out in full; never point elsewhere with "see above".
111
122
  - **Never only among optional steps.** An action required for correctness is never listed only under next steps, suggestions, `Carry Forward` or `What's Next?`. It may be repeated there, but it always appears in this block.
112
- - **Omit it when empty.** When nothing needs the operator, leave the block out entirely: no heading and no "None" line.
123
+ - **Done items stay, struck through.** Within one session, once an item has been listed, keep it in every later report of the block; when the operator has done it, strike it through (`~~<the line>~~`) rather than dropping it, so progress shows and nothing disappears silently. The session's close carries only the open items forward.
124
+ - **Omit it when empty.** When nothing is left for the operator and nothing was struck through this session, leave the block out entirely: no heading, no rules and no "None" line.
113
125
  - **Every final report.** The block belongs in whatever report ends the run, including a report written when the spell stops early.
114
- - **Report it to the presence channel.** When the block is not empty, also run `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line of the block; with no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
126
+ - **Report the Now tier to the presence channel.** When the **Now** tier has open items, also run `spell presence report --status waiting --required-action "<line 1>" --required-action "<line 2>"`, one flag per open line of that tier; the other tier is not blocking and is not reported. With no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
115
127
  - Enforcement: explicitly advisory prose (ARC-023) — no check reads a spell's actual output for this block; it holds by these instructions alone.
116
- <!-- fragment:needs-you:end -->
128
+ <!-- fragment:action-required:end -->
117
129
 
118
130
  7. **Request human approval** — present the ship report and wait for explicit approval.
119
131
 
@@ -110,7 +110,7 @@ then classify **each conflicted hunk independently** — a single file can conta
110
110
  that one side's content is a strict superset of the other's. **STOP.** Do not guess, do not pick a
111
111
  side, do not attempt a "reasonable-looking" merge of the two. Abort the in-progress rebase/merge
112
112
  (`git rebase --abort` / `git merge --abort`) so the branch is left exactly as it was, then report:
113
- - First, the `## ⚠ Needs you` block described in Step 6, naming the manual resolution as the action.
113
+ - First, the `## 🚧 Action required` block described in Step 6, naming the manual resolution as the action.
114
114
  - The exact conflicting file(s) and hunk(s).
115
115
  - Both sides' conflicting content, verbatim.
116
116
  - That the recoverable ref from Step 1 still points at the pre-sync state.
@@ -172,23 +172,35 @@ report the discrepancy rather than declaring the sync done.
172
172
 
173
173
  ## Step 6 — Report
174
174
 
175
- <!-- fragment:needs-you:start -->
176
- **Needs you — required operator actions.** When this run leaves an action that only the operator can take, and the work is not correct or complete until it is taken, report it in one block at the very top of the final report, before every other section and before any optional next step:
175
+ <!-- fragment:action-required:start -->
176
+ **Action required — what only the operator can do.** When this run leaves an action that only the operator can take, report it in one block at the very top of the final report, before every other section and before any optional next step. The block exists because these are the operator's decisions to make, not because the agent is stuck: write it as a hand-off of authority, never as a plea for help. Frame it with a horizontal rule above and below, so it stands apart in a terminal as well as on a rendered page:
177
177
 
178
178
  ```markdown
179
- ## ⚠ Needs you
179
+ ---
180
+
181
+ ## 🚧 Action required
182
+
183
+ **Now**
184
+
185
+ - <exactly what to do: the command, the link, or the question> — <why only the operator can do it>
180
186
 
181
- - <exactly what to do: the command, the link, or the question> — <why the agent cannot do it itself>
187
+ **When you're at your desk**
188
+
189
+ - <exactly what to do> — <why only the operator can do it>
190
+
191
+ ---
182
192
  ```
183
193
 
184
- - **What belongs in it:** an action the work needs that only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
185
- - **One line per action.** Say exactly what to do, then, after the dash, why the agent cannot do it. Write the command, link or question out in full; never point elsewhere with "see above".
194
+ - **What belongs in it:** an action only the operator can take — approving or merging a pull request, accepting a decision record, answering a question the work is waiting on, confirming a `guarded` push, running an interactive command such as `spell unblock-push`, or a step on an external platform the agent cannot reach. An optional suggestion, a next spell to run, or anything the agent could do itself is not a required action and stays out of the block.
195
+ - **Two tiers, one block.** **Now** holds what the work is not correct or complete without. **When you're at your desk** holds what only the operator can do but that can wait: a permission rule, a leftover remote branch, a credential to set up. Leave out a tier that has nothing in it; never split the tiers into two blocks.
196
+ - **One line per action.** Say exactly what to do, then, after the dash, why only the operator can do it. Write the command, link or question out in full; never point elsewhere with "see above".
186
197
  - **Never only among optional steps.** An action required for correctness is never listed only under next steps, suggestions, `Carry Forward` or `What's Next?`. It may be repeated there, but it always appears in this block.
187
- - **Omit it when empty.** When nothing needs the operator, leave the block out entirely: no heading and no "None" line.
198
+ - **Done items stay, struck through.** Within one session, once an item has been listed, keep it in every later report of the block; when the operator has done it, strike it through (`~~<the line>~~`) rather than dropping it, so progress shows and nothing disappears silently. The session's close carries only the open items forward.
199
+ - **Omit it when empty.** When nothing is left for the operator and nothing was struck through this session, leave the block out entirely: no heading, no rules and no "None" line.
188
200
  - **Every final report.** The block belongs in whatever report ends the run, including a report written when the spell stops early.
189
- - **Report it to the presence channel.** When the block is not empty, also run `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line of the block; with no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
201
+ - **Report the Now tier to the presence channel.** When the **Now** tier has open items, also run `spell presence report --status waiting --required-action "<line 1>" --required-action "<line 2>"`, one flag per open line of that tier; the other tier is not blocking and is not reported. With no presence channel configured on this machine the command prints nothing and exits 0, so the block itself and the rest of the run are unchanged.
190
202
  - Enforcement: explicitly advisory prose (ARC-023) — no check reads a spell's actual output for this block; it holds by these instructions alone.
191
- <!-- fragment:needs-you:end -->
203
+ <!-- fragment:action-required:end -->
192
204
 
193
205
  ```
194
206
  ✓ PR synced: <URL>
@@ -33,7 +33,7 @@
33
33
  - **Branch:** main
34
34
  - **Blockers:** None.
35
35
  - **Pending Verification:** None.
36
- - **Needs you:** None.
36
+ - **Action required:** None.
37
37
  - **Notes:** —
38
38
 
39
39
  > ✓ Consumed: YYYY-MM-DD — scaffold placeholder, not a real session; ships pre-consumed so the first
@@ -15,7 +15,7 @@
15
15
  "task",
16
16
  "message",
17
17
  "status",
18
- "needs_you",
18
+ "required_actions",
19
19
  "started_at",
20
20
  "reported_at",
21
21
  "ended_at",
@@ -73,11 +73,11 @@
73
73
  ],
74
74
  "description": "working: a spell is running; waiting: the session asked the operator for something; blocked: cannot proceed for an external reason; idle: open, no spell running; done: ended (set by end only)."
75
75
  },
76
- "needs_you": {
76
+ "required_actions": {
77
77
  "type": "array",
78
78
  "maxItems": 20,
79
79
  "items": {
80
- "$ref": "#/$defs/needsYou"
80
+ "$ref": "#/$defs/requiredAction"
81
81
  }
82
82
  },
83
83
  "started_at": {
@@ -216,7 +216,7 @@
216
216
  }
217
217
  }
218
218
  },
219
- "needsYou": {
219
+ "requiredAction": {
220
220
  "type": "object",
221
221
  "additionalProperties": false,
222
222
  "required": [
@@ -227,7 +227,7 @@
227
227
  "action": {
228
228
  "type": "string",
229
229
  "maxLength": 256,
230
- "description": "Exactly what the operator must do: one line of a '## ⚠ Needs you' block.",
230
+ "description": "Exactly what the operator must do: one line of a '## 🚧 Action required' block.",
231
231
  "minLength": 1
232
232
  },
233
233
  "reason": {
@@ -390,14 +390,33 @@
390
390
  "maxLength": 256,
391
391
  "description": "Absent keeps the stored value; null clears it."
392
392
  },
393
- "needs_you": {
393
+ "required_actions": {
394
394
  "type": "array",
395
395
  "maxItems": 20,
396
396
  "items": {
397
- "$ref": "#/$defs/needsYou"
397
+ "$ref": "#/$defs/requiredAction"
398
398
  },
399
399
  "description": "Replaces the stored list on every report; absent means []. More than 20 entries is refused with 400, never cut."
400
+ },
401
+ "needs_you": {
402
+ "type": "array",
403
+ "maxItems": 20,
404
+ "items": {
405
+ "$ref": "#/$defs/requiredAction"
406
+ },
407
+ "deprecated": true,
408
+ "description": "The name of required_actions before 1.20 (ARC-060 decision 14), accepted as required_actions for the 1.20 series. Never sent together with required_actions: a body with both is refused with 400 naming needs_you."
400
409
  }
410
+ },
411
+ "not": {
412
+ "properties": {
413
+ "required_actions": true,
414
+ "needs_you": true
415
+ },
416
+ "required": [
417
+ "required_actions",
418
+ "needs_you"
419
+ ]
401
420
  }
402
421
  },
403
422
  "end": {
@@ -528,7 +547,7 @@
528
547
  }
529
548
  },
530
549
  "event": {
531
- "description": "One change on GET /v0/events: the event name and the full record after the change. Exactly one event per accepted report, by the precedence started > resumed > needs_you > updated (architecture D5). The record is the truth and the event name is why it was sent: a consumer that alerts on 'needs you' reads the record's needs_you, because a first or a resumed report can carry lines under another event name.",
550
+ "description": "One change on GET /v0/events: the event name and the full record after the change. Exactly one event per accepted report, by the precedence started > resumed > action_required > updated (architecture D5). The record is the truth and the event name is why it was sent: a consumer that alerts on 'action required' reads the record's required_actions, because a first or a resumed report can carry lines under another event name.",
532
551
  "type": "object",
533
552
  "additionalProperties": false,
534
553
  "required": [
@@ -540,7 +559,7 @@
540
559
  "enum": [
541
560
  "session.started",
542
561
  "session.updated",
543
- "session.needs_you",
562
+ "session.action_required",
544
563
  "session.stale",
545
564
  "session.resumed",
546
565
  "session.ended",
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from "node:module";
3
3
  const require = __createRequire(import.meta.url);
4
4
 
5
5
  // src/index.ts
6
- import { Command } from "commander";
6
+ import { Command, Option } from "commander";
7
7
  import { fileURLToPath } from "url";
8
8
  import { dirname as dirname15, join as join31 } from "path";
9
9
 
@@ -8419,7 +8419,7 @@ var PRESENCE_ENV = { port: "ARCANE_PRESENCE_PORT", token: "ARCANE_PRESENCE_TOKEN
8419
8419
  var LOOPBACK_HOST = "127.0.0.1";
8420
8420
  var MAX_BODY_BYTES = 65536;
8421
8421
  var MAX_LINE_CHARS = 256;
8422
- var MAX_NEEDS_YOU = 20;
8422
+ var MAX_REQUIRED_ACTIONS = 20;
8423
8423
  var MAX_SUBSCRIBERS = 32;
8424
8424
  var MAX_SUBSCRIBER_BUFFER_BYTES = 1048576;
8425
8425
  var MAX_CONNECTIONS = 128;
@@ -8438,7 +8438,7 @@ var ALIAS_CONTEXTS = ["openclaw", "slug"];
8438
8438
  var SESSION_EVENTS = [
8439
8439
  "session.started",
8440
8440
  "session.updated",
8441
- "session.needs_you",
8441
+ "session.action_required",
8442
8442
  "session.stale",
8443
8443
  "session.resumed",
8444
8444
  "session.ended",
@@ -11255,11 +11255,11 @@ function detectHarness(env) {
11255
11255
  }
11256
11256
 
11257
11257
  // src/modules/presence/reporter.ts
11258
- var NEEDS_YOU_SEPARATOR = " \u2014 ";
11259
- function parseNeedsYou(line2) {
11260
- const at = line2.indexOf(NEEDS_YOU_SEPARATOR);
11258
+ var REQUIRED_ACTION_SEPARATOR = " \u2014 ";
11259
+ function parseRequiredAction(line2) {
11260
+ const at = line2.indexOf(REQUIRED_ACTION_SEPARATOR);
11261
11261
  if (at < 0) return { action: line2, reason: null };
11262
- return { action: line2.slice(0, at), reason: line2.slice(at + NEEDS_YOU_SEPARATOR.length) };
11262
+ return { action: line2.slice(0, at), reason: line2.slice(at + REQUIRED_ACTION_SEPARATOR.length) };
11263
11263
  }
11264
11264
  async function quiet(fn) {
11265
11265
  try {
@@ -11315,7 +11315,7 @@ async function sendReport(cwd, flags, deps = {}) {
11315
11315
  agent: agent2,
11316
11316
  repository: located.repository,
11317
11317
  checkout: located.checkout,
11318
- needs_you: (flags.needsYou ?? []).map(parseNeedsYou)
11318
+ required_actions: (flags.requiredActions ?? []).map(parseRequiredAction)
11319
11319
  };
11320
11320
  if (flags.spell !== void 0) body["spell"] = flags.spell;
11321
11321
  if (flags.task !== void 0) body["task"] = flags.task;
@@ -11364,7 +11364,7 @@ var PRESENCE_TOOLS = [
11364
11364
  task: { ...LINE, description: "What the session is doing, in one line." },
11365
11365
  spell: { ...LINE, description: "The spell running, e.g. spell-implement." },
11366
11366
  message: { ...LINE, description: "A free one-line note; an empty string clears it." },
11367
- needs_you: { type: "array", maxItems: 20, items: { type: "string" }, description: "Lines of a Needs you block, '<action> \u2014 <reason>'." },
11367
+ required_actions: { type: "array", maxItems: 20, items: { type: "string" }, description: "Lines of the Now tier of an Action required block, '<action> \u2014 <reason>'." },
11368
11368
  agent: { type: "string", description: "The roster agent (a slug or an alias); default: the orchestrator." },
11369
11369
  harness: { type: "string", enum: [...HARNESSES] },
11370
11370
  cwd: { type: "string", description: "The checkout to report for; default: the server's working directory." }
@@ -11421,16 +11421,16 @@ function serveMcp(options) {
11421
11421
  const report = async (args) => {
11422
11422
  const status = stringArg(args, "status");
11423
11423
  if (status === void 0) return text("status is required: one of working, waiting, blocked, idle.", true);
11424
- const needs = args["needs_you"];
11424
+ const needs = args["required_actions"];
11425
11425
  if (needs !== void 0 && (!Array.isArray(needs) || needs.some((n) => typeof n !== "string"))) {
11426
- return text("needs_you must be a list of strings.", true);
11426
+ return text("required_actions must be a list of strings.", true);
11427
11427
  }
11428
11428
  const flags = { status };
11429
11429
  for (const key of ["task", "spell", "message", "agent", "harness"]) {
11430
11430
  const v = stringArg(args, key);
11431
11431
  if (v !== void 0) flags[key] = v;
11432
11432
  }
11433
- if (Array.isArray(needs)) flags.needsYou = needs;
11433
+ if (Array.isArray(needs)) flags.requiredActions = needs;
11434
11434
  if (resolveEndpoint(deps) === null) return text(NOT_CONFIGURED_TEXT);
11435
11435
  const where = stringArg(args, "cwd") ?? cwd;
11436
11436
  const outcome = await sendReport(where, flags, deps);
@@ -11816,13 +11816,14 @@ function checkout(value) {
11816
11816
  }
11817
11817
  return { path: nonEmpty(c["path"], "checkout.path"), branch: line(c["branch"], "checkout.branch", true), linked_worktree: c["linked_worktree"] };
11818
11818
  }
11819
- function needsYou(value) {
11820
- if (!Array.isArray(value)) throw new Refusal("invalid_field", "needs_you", "needs_you must be a list");
11821
- if (value.length > MAX_NEEDS_YOU) {
11822
- throw new Refusal("too_many_lines", "needs_you", `needs_you carries at most ${MAX_NEEDS_YOU} lines; ${value.length} were sent`);
11819
+ var LEGACY_REQUIRED_ACTIONS_KEY = "needs_you";
11820
+ function requiredActions(value, key = "required_actions") {
11821
+ if (!Array.isArray(value)) throw new Refusal("invalid_field", key, `${key} must be a list`);
11822
+ if (value.length > MAX_REQUIRED_ACTIONS) {
11823
+ throw new Refusal("too_many_lines", key, `${key} carries at most ${MAX_REQUIRED_ACTIONS} lines; ${value.length} were sent`);
11823
11824
  }
11824
11825
  return value.map((item, i) => {
11825
- const field = `needs_you.${i}`;
11826
+ const field = `${key}.${i}`;
11826
11827
  const n = closed(item, field, ["action", "reason"]);
11827
11828
  return { action: nonEmptyLine(n["action"], `${field}.action`), reason: line(n["reason"], `${field}.reason`, true) };
11828
11829
  });
@@ -11839,7 +11840,7 @@ function refuse(fn) {
11839
11840
  }
11840
11841
  function validateReport(value) {
11841
11842
  return refuse(() => {
11842
- const b = closed(value, void 0, ["status", "harness", "agent", "repository", "checkout"], ["spell", "task", "message", "needs_you"]);
11843
+ const b = closed(value, void 0, ["status", "harness", "agent", "repository", "checkout"], ["spell", "task", "message", "required_actions", LEGACY_REQUIRED_ACTIONS_KEY]);
11843
11844
  if (b["status"] === "done") {
11844
11845
  throw new Refusal("invalid_field", "status", "status done is set by ending a session: use POST /v0/sessions/end (spell presence end)");
11845
11846
  }
@@ -11853,7 +11854,11 @@ function validateReport(value) {
11853
11854
  for (const key of ["spell", "task", "message"]) {
11854
11855
  if (key in b) out[key] = line(b[key], key, true);
11855
11856
  }
11856
- if ("needs_you" in b) out.needs_you = needsYou(b["needs_you"]);
11857
+ if ("required_actions" in b && LEGACY_REQUIRED_ACTIONS_KEY in b) {
11858
+ throw new Refusal("invalid_field", LEGACY_REQUIRED_ACTIONS_KEY, "send required_actions only: needs_you is its name before 1.20");
11859
+ }
11860
+ if ("required_actions" in b) out.required_actions = requiredActions(b["required_actions"]);
11861
+ else if (LEGACY_REQUIRED_ACTIONS_KEY in b) out.required_actions = requiredActions(b[LEGACY_REQUIRED_ACTIONS_KEY], LEGACY_REQUIRED_ACTIONS_KEY);
11857
11862
  return out;
11858
11863
  });
11859
11864
  }
@@ -12095,7 +12100,7 @@ data: ${JSON.stringify(hello)}
12095
12100
  function copy(record) {
12096
12101
  return structuredClone(record);
12097
12102
  }
12098
- function sameNeedsYou(a, b) {
12103
+ function sameRequiredActions(a, b) {
12099
12104
  if (a.length !== b.length) return false;
12100
12105
  return a.every((item, i) => item.action === b[i]?.action && item.reason === b[i]?.reason);
12101
12106
  }
@@ -12120,7 +12125,7 @@ function createSessionTable(options) {
12120
12125
  const ms = now();
12121
12126
  const at = new Date(ms).toISOString();
12122
12127
  const key = body.checkout.path;
12123
- const needsYou2 = (body.needs_you ?? []).map((n) => ({ action: n.action, reason: n.reason }));
12128
+ const requiredActions2 = (body.required_actions ?? []).map((n) => ({ action: n.action, reason: n.reason }));
12124
12129
  const existing = entries.get(key);
12125
12130
  if (existing === void 0) {
12126
12131
  const record2 = {
@@ -12133,7 +12138,7 @@ function createSessionTable(options) {
12133
12138
  task: body.task ?? null,
12134
12139
  message: body.message ?? null,
12135
12140
  status: body.status,
12136
- needs_you: needsYou2,
12141
+ required_actions: requiredActions2,
12137
12142
  started_at: at,
12138
12143
  reported_at: at,
12139
12144
  ended_at: null,
@@ -12145,7 +12150,7 @@ function createSessionTable(options) {
12145
12150
  }
12146
12151
  const record = existing.record;
12147
12152
  const wasStale = record.stale;
12148
- const needsChanged = !sameNeedsYou(record.needs_you, needsYou2);
12153
+ const needsChanged = !sameRequiredActions(record.required_actions, requiredActions2);
12149
12154
  record.agent = structuredClone(body.agent);
12150
12155
  record.harness = body.harness;
12151
12156
  record.repository = structuredClone(body.repository);
@@ -12154,11 +12159,11 @@ function createSessionTable(options) {
12154
12159
  if (body.task !== void 0) record.task = body.task;
12155
12160
  if (body.message !== void 0) record.message = body.message;
12156
12161
  record.status = body.status;
12157
- record.needs_you = needsYou2;
12162
+ record.required_actions = requiredActions2;
12158
12163
  record.reported_at = at;
12159
12164
  record.stale = false;
12160
12165
  existing.reportedMs = ms;
12161
- const event = wasStale ? "session.resumed" : needsChanged ? "session.needs_you" : "session.updated";
12166
+ const event = wasStale ? "session.resumed" : needsChanged ? "session.action_required" : "session.updated";
12162
12167
  emit(event, record);
12163
12168
  return { record: copy(record), created: false, event };
12164
12169
  },
@@ -12380,7 +12385,7 @@ function renderSessionsTable(sessions, now) {
12380
12385
  const out = [line2(header)];
12381
12386
  sessions.forEach((s, i) => {
12382
12387
  out.push(line2(rows[i]));
12383
- for (const n of s.needs_you) out.push(` needs you: ${n.reason === null ? n.action : `${n.action} \u2014 ${n.reason}`}`);
12388
+ for (const n of s.required_actions) out.push(` action required: ${n.reason === null ? n.action : `${n.action} \u2014 ${n.reason}`}`);
12384
12389
  });
12385
12390
  return out;
12386
12391
  }
@@ -12705,8 +12710,9 @@ function collectLine(value, previous) {
12705
12710
  return [...previous, value];
12706
12711
  }
12707
12712
  var exitZero = () => process.exit(0);
12708
- presenceCommand.command("report").description("Report this checkout's session to the presence service. Silent, exit 0, when no channel is configured; never blocks").requiredOption("--status <status>", "working | waiting | blocked | idle").option("--task <line>", "What the session is doing, in one line").option("--spell <id>", "The spell running, e.g. spell-implement").option("--message <line>", "A free one-line note; an empty value clears it").option("--needs-you <line>", "One line of a Needs you block, '<action> \u2014 <reason>'; repeat per line", collectLine, []).option("--agent <slug>", "The roster agent this session reports as (a slug or an alias); default: the orchestrator").option("--harness <id>", "claude-code | copilot | codex | openclaw | cron | other; default: detected, else other").exitOverride(exitZero).action(async (opts) => {
12709
- const run = await runPresenceReport(process.cwd(), opts);
12713
+ presenceCommand.command("report").description("Report this checkout's session to the presence service. Silent, exit 0, when no channel is configured; never blocks").requiredOption("--status <status>", "working | waiting | blocked | idle").option("--task <line>", "What the session is doing, in one line").option("--spell <id>", "The spell running, e.g. spell-implement").option("--message <line>", "A free one-line note; an empty value clears it").option("--required-action <line>", "One open line of the Now tier of an Action required block, '<action> \u2014 <reason>'; repeat per line", collectLine, []).addOption(new Option("--needs-you <line>").argParser(collectLine).default([]).hideHelp()).option("--agent <slug>", "The roster agent this session reports as (a slug or an alias); default: the orchestrator").option("--harness <id>", "claude-code | copilot | codex | openclaw | cron | other; default: detected, else other").exitOverride(exitZero).action(async (opts) => {
12714
+ const { requiredAction, needsYou, ...rest } = opts;
12715
+ const run = await runPresenceReport(process.cwd(), { ...rest, requiredActions: [...requiredAction, ...needsYou] });
12710
12716
  process.exitCode = 0;
12711
12717
  if (run.timedOut) process.exit(0);
12712
12718
  });