arcane-cli 1.19.0 → 1.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) 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/report/show-report.template.html +31 -14
  27. package/dist/assets/schemas/presence.schema.json +28 -9
  28. package/dist/index.js +34 -28
  29. package/dist/index.js.map +1 -1
  30. package/package.json +1 -1
  31. package/dist/assets/.arcane/spells/_fragments/needs-you.md +0 -15
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
  });