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.
- package/README.md +2 -2
- package/dist/assets/.arcane/governance/spell-authoring-standards.md +34 -22
- package/dist/assets/.arcane/governance/threat-model.md +4 -4
- package/dist/assets/.arcane/spells/_fragments/action-required.md +27 -0
- package/dist/assets/.arcane/spells/_fragments/presence-report.md +1 -1
- package/dist/assets/.arcane/spells/spell-address-review.md +21 -9
- package/dist/assets/.arcane/spells/spell-adopt-docs.md +21 -9
- package/dist/assets/.arcane/spells/spell-app-store-accounts.md +22 -10
- package/dist/assets/.arcane/spells/spell-architect.md +21 -9
- package/dist/assets/.arcane/spells/spell-close-session.md +26 -14
- package/dist/assets/.arcane/spells/spell-commit-work.md +22 -10
- package/dist/assets/.arcane/spells/spell-create-pull-request.md +22 -10
- package/dist/assets/.arcane/spells/spell-eas-store-deploy.md +21 -9
- package/dist/assets/.arcane/spells/spell-full-cycle.md +21 -9
- package/dist/assets/.arcane/spells/spell-implement.md +1 -1
- package/dist/assets/.arcane/spells/spell-make-discoverable.md +21 -9
- package/dist/assets/.arcane/spells/spell-open-session.md +23 -11
- package/dist/assets/.arcane/spells/spell-parallel-workers.md +23 -11
- package/dist/assets/.arcane/spells/spell-plan.md +21 -9
- package/dist/assets/.arcane/spells/spell-review-batch.md +21 -9
- package/dist/assets/.arcane/spells/spell-review.md +22 -10
- package/dist/assets/.arcane/spells/spell-scope.md +21 -9
- package/dist/assets/.arcane/spells/spell-ship.md +21 -9
- package/dist/assets/.arcane/spells/spell-sync-pull-request.md +22 -10
- package/dist/assets/ai-context/system-prompt-context.md +1 -1
- package/dist/assets/report/show-report.template.html +31 -14
- package/dist/assets/schemas/presence.schema.json +28 -9
- package/dist/index.js +34 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- 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
|
|
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.
|
|
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
|
|
11259
|
-
function
|
|
11260
|
-
const at = line2.indexOf(
|
|
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 +
|
|
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
|
-
|
|
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
|
-
|
|
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["
|
|
11424
|
+
const needs = args["required_actions"];
|
|
11425
11425
|
if (needs !== void 0 && (!Array.isArray(needs) || needs.some((n) => typeof n !== "string"))) {
|
|
11426
|
-
return text("
|
|
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.
|
|
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
|
-
|
|
11820
|
-
|
|
11821
|
-
if (value
|
|
11822
|
-
|
|
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 =
|
|
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", "
|
|
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 ("
|
|
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
|
|
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
|
|
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
|
-
|
|
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 = !
|
|
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.
|
|
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.
|
|
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.
|
|
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("--
|
|
12709
|
-
const
|
|
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
|
});
|