@tryarcanist/cli 0.1.219 → 0.1.221
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 +128 -50
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -580,7 +580,7 @@ function assertArcanistSessionMutationAllowed(subcommand) {
|
|
|
580
580
|
}
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
|
-
const commandName = subcommand === "review"
|
|
583
|
+
const commandName = subcommand === "review" || subcommand === "anubis" ? subcommand : `sessions ${subcommand}`;
|
|
584
584
|
throw new CliError(
|
|
585
585
|
"user",
|
|
586
586
|
`\`arcanist ${commandName}\` is disabled inside ${role} sessions because nested Arcanist work is not observable to read-only agents.`,
|
|
@@ -967,6 +967,100 @@ async function agentSubscriptionLogoutCommand(config, options, command) {
|
|
|
967
967
|
);
|
|
968
968
|
}
|
|
969
969
|
|
|
970
|
+
// src/commands/review.ts
|
|
971
|
+
var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
|
|
972
|
+
var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
973
|
+
async function reviewCommand(prUrl, options = {}, command) {
|
|
974
|
+
assertArcanistSessionMutationAllowed("review");
|
|
975
|
+
const { config } = resolveBusinessContext(command, options);
|
|
976
|
+
const trigger = await apiFetch(config, "/api/pr-reviews", {
|
|
977
|
+
method: "POST",
|
|
978
|
+
body: JSON.stringify({
|
|
979
|
+
prUrl,
|
|
980
|
+
...options.focus ? { focus: options.focus } : {},
|
|
981
|
+
// Send an explicitly passed model even when empty (e.g. --model "$VAR" with an
|
|
982
|
+
// unset VAR) so the server rejects it as invalid instead of silently routing.
|
|
983
|
+
...options.model !== void 0 ? { model: options.model } : {}
|
|
984
|
+
})
|
|
985
|
+
});
|
|
986
|
+
if (!options.wait || trigger.cached) {
|
|
987
|
+
emit(command, options, trigger, (payload) => {
|
|
988
|
+
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
989
|
+
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
990
|
+
});
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const result = await waitForReview(config, prUrl, trigger.sessionId ?? null);
|
|
994
|
+
emit(command, options, result, (payload) => {
|
|
995
|
+
console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
996
|
+
for (const finding of payload.findings) {
|
|
997
|
+
console.log(
|
|
998
|
+
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function sanitizeTerminalText(value) {
|
|
1004
|
+
return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
|
|
1005
|
+
}
|
|
1006
|
+
async function waitForReview(config, prUrl, sessionId) {
|
|
1007
|
+
const deadline = Date.now() + PR_REVIEW_WAIT_TIMEOUT_MS;
|
|
1008
|
+
while (Date.now() < deadline) {
|
|
1009
|
+
const status = await apiFetch(
|
|
1010
|
+
config,
|
|
1011
|
+
`/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
1012
|
+
);
|
|
1013
|
+
if (status.status === "completed") {
|
|
1014
|
+
if (sessionId && status.sessionId !== sessionId) {
|
|
1015
|
+
throw new CliError("conflict", "PR review completed for a different attempt; retry the command.");
|
|
1016
|
+
}
|
|
1017
|
+
return { ...status, sessionId: status.sessionId ?? sessionId };
|
|
1018
|
+
}
|
|
1019
|
+
await new Promise((resolve2) => setTimeout(resolve2, PR_REVIEW_POLL_INTERVAL_MS));
|
|
1020
|
+
}
|
|
1021
|
+
throw new CliError("server", "Timed out waiting for Zeus review results.");
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// src/commands/anubis.ts
|
|
1025
|
+
var ANUBIS_POLL_INTERVAL_MS = 15e3;
|
|
1026
|
+
var ANUBIS_WAIT_TIMEOUT_MS = 45 * 60 * 1e3;
|
|
1027
|
+
async function anubisCommand(prUrl, options = {}, command) {
|
|
1028
|
+
assertArcanistSessionMutationAllowed("anubis");
|
|
1029
|
+
const { config } = resolveBusinessContext(command, options);
|
|
1030
|
+
const trigger = await apiFetch(config, "/api/anubis-runs", {
|
|
1031
|
+
method: "POST",
|
|
1032
|
+
body: JSON.stringify({ prUrl })
|
|
1033
|
+
});
|
|
1034
|
+
if (!options.wait) {
|
|
1035
|
+
emit(command, options, trigger, (payload) => {
|
|
1036
|
+
console.log(`${payload.duplicate ? "Attached to existing" : "Started"} Anubis run (${payload.sessionId}).`);
|
|
1037
|
+
});
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const result = await waitForAnubis(config, prUrl, trigger.sessionId);
|
|
1041
|
+
emit(command, options, result, (payload) => {
|
|
1042
|
+
console.log(`Anubis: ${sanitizeTerminalText(payload.verdict ?? "could-not-verify")}.`);
|
|
1043
|
+
if (payload.summary) console.log(sanitizeTerminalText(payload.summary));
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
async function waitForAnubis(config, prUrl, sessionId) {
|
|
1047
|
+
const deadline = Date.now() + ANUBIS_WAIT_TIMEOUT_MS;
|
|
1048
|
+
while (Date.now() < deadline) {
|
|
1049
|
+
const status = await apiFetch(
|
|
1050
|
+
config,
|
|
1051
|
+
`/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
1052
|
+
);
|
|
1053
|
+
if (status.sessionStatus !== "active") {
|
|
1054
|
+
if (status.sessionId !== sessionId) {
|
|
1055
|
+
throw new CliError("conflict", "Anubis completed for a different attempt; retry the command.");
|
|
1056
|
+
}
|
|
1057
|
+
return status;
|
|
1058
|
+
}
|
|
1059
|
+
await new Promise((resolve2) => setTimeout(resolve2, ANUBIS_POLL_INTERVAL_MS));
|
|
1060
|
+
}
|
|
1061
|
+
throw new CliError("server", "Timed out waiting for Anubis results.");
|
|
1062
|
+
}
|
|
1063
|
+
|
|
970
1064
|
// src/commands/auth.ts
|
|
971
1065
|
async function whoamiCommand(options, command) {
|
|
972
1066
|
const { config } = resolveBusinessContext(command, options);
|
|
@@ -1031,6 +1125,7 @@ var OpenAIModel = {
|
|
|
1031
1125
|
};
|
|
1032
1126
|
var AnthropicModel = {
|
|
1033
1127
|
Opus48: "claude-opus-4-8",
|
|
1128
|
+
Opus5: "claude-opus-5",
|
|
1034
1129
|
Sonnet46: "claude-sonnet-4-6",
|
|
1035
1130
|
Sonnet5: "claude-sonnet-5",
|
|
1036
1131
|
Fable5: "claude-fable-5"
|
|
@@ -1307,6 +1402,18 @@ var MODEL_REGISTRY = [
|
|
|
1307
1402
|
pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.5, cacheWritePerMillion: 6.25 },
|
|
1308
1403
|
sessionStart: { eligible: true, isDefault: true }
|
|
1309
1404
|
},
|
|
1405
|
+
{
|
|
1406
|
+
id: AnthropicModel.Opus5,
|
|
1407
|
+
name: "Claude Opus 5",
|
|
1408
|
+
provider: "anthropic",
|
|
1409
|
+
backends: [CLAUDE_CODE_AGENT_RUNTIME_BACKEND],
|
|
1410
|
+
contextWindow: 1e6,
|
|
1411
|
+
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "high" },
|
|
1412
|
+
// Bridge cost estimate; authoritative Anthropic Messages pricing lives in
|
|
1413
|
+
// apps/control-plane-worker/src/anthropic/cost.ts.
|
|
1414
|
+
pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.5, cacheWritePerMillion: 6.25 },
|
|
1415
|
+
sessionStart: { eligible: true }
|
|
1416
|
+
},
|
|
1310
1417
|
{
|
|
1311
1418
|
id: AnthropicModel.Sonnet46,
|
|
1312
1419
|
name: "Claude Sonnet 4.6",
|
|
@@ -4670,54 +4777,6 @@ function parseRespondConflict(err) {
|
|
|
4670
4777
|
}
|
|
4671
4778
|
}
|
|
4672
4779
|
|
|
4673
|
-
// src/commands/review.ts
|
|
4674
|
-
var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
|
|
4675
|
-
var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4676
|
-
async function reviewCommand(prUrl, options = {}, command) {
|
|
4677
|
-
assertArcanistSessionMutationAllowed("review");
|
|
4678
|
-
const { config } = resolveBusinessContext(command, options);
|
|
4679
|
-
const trigger = await apiFetch(config, "/api/pr-reviews", {
|
|
4680
|
-
method: "POST",
|
|
4681
|
-
body: JSON.stringify({ prUrl, ...options.focus ? { focus: options.focus } : {} })
|
|
4682
|
-
});
|
|
4683
|
-
if (!options.wait || trigger.cached) {
|
|
4684
|
-
emit(command, options, trigger, (payload) => {
|
|
4685
|
-
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
4686
|
-
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
4687
|
-
});
|
|
4688
|
-
return;
|
|
4689
|
-
}
|
|
4690
|
-
const result = await waitForReview(config, prUrl, trigger.sessionId ?? null);
|
|
4691
|
-
emit(command, options, result, (payload) => {
|
|
4692
|
-
console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
4693
|
-
for (const finding of payload.findings) {
|
|
4694
|
-
console.log(
|
|
4695
|
-
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
4696
|
-
);
|
|
4697
|
-
}
|
|
4698
|
-
});
|
|
4699
|
-
}
|
|
4700
|
-
function sanitizeTerminalText(value) {
|
|
4701
|
-
return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
|
|
4702
|
-
}
|
|
4703
|
-
async function waitForReview(config, prUrl, sessionId) {
|
|
4704
|
-
const deadline = Date.now() + PR_REVIEW_WAIT_TIMEOUT_MS;
|
|
4705
|
-
while (Date.now() < deadline) {
|
|
4706
|
-
const status = await apiFetch(
|
|
4707
|
-
config,
|
|
4708
|
-
`/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
4709
|
-
);
|
|
4710
|
-
if (status.status === "completed") {
|
|
4711
|
-
if (sessionId && status.sessionId !== sessionId) {
|
|
4712
|
-
throw new CliError("conflict", "PR review completed for a different attempt; retry the command.");
|
|
4713
|
-
}
|
|
4714
|
-
return { ...status, sessionId: status.sessionId ?? sessionId };
|
|
4715
|
-
}
|
|
4716
|
-
await new Promise((resolve2) => setTimeout(resolve2, PR_REVIEW_POLL_INTERVAL_MS));
|
|
4717
|
-
}
|
|
4718
|
-
throw new CliError("server", "Timed out waiting for Zeus review results.");
|
|
4719
|
-
}
|
|
4720
|
-
|
|
4721
4780
|
// src/commands/sandbox.ts
|
|
4722
4781
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
4723
4782
|
import { dirname as dirname2 } from "path";
|
|
@@ -6352,16 +6411,35 @@ cursor.command("use <state>").description("Turn using your saved Cursor subscrip
|
|
|
6352
6411
|
cursor.command("status").description("Show whether your workspace is eligible and whether a Cursor subscription auth is saved").action((options, command) => agentSubscriptionStatusCommand(CURSOR_SUBSCRIPTION_CLI_CONFIG, options, command));
|
|
6353
6412
|
cursor.command("logout").description("Deactivate the selector and remove the stored Cursor subscription auth for your user").action((options, command) => agentSubscriptionLogoutCommand(CURSOR_SUBSCRIPTION_CLI_CONFIG, options, command));
|
|
6354
6413
|
var sessions = program.command("sessions").description("Session commands");
|
|
6355
|
-
program.command("review <pr-url>").description("Run a Zeus review for a GitHub pull request").option("--focus <text>", "Focus the review on a specific concern").option("--wait", "Wait for the review and print its verdict and findings").addHelpText(
|
|
6414
|
+
program.command("review <pr-url>").description("Run a Zeus review for a GitHub pull request").option("--focus <text>", "Focus the review on a specific concern").option("--model <model>", "Pin the reviewer model (Arcanist members only); defaults to automatic routing").option("--wait", "Wait for the review and print its verdict and findings").addHelpText(
|
|
6356
6415
|
"after",
|
|
6357
6416
|
`
|
|
6358
6417
|
Examples:
|
|
6359
6418
|
arcanist review https://github.com/org/repo/pull/123 --wait --json
|
|
6360
6419
|
arcanist review https://github.com/org/repo/pull/123 --focus "Check auth" --wait
|
|
6420
|
+
arcanist review https://github.com/org/repo/pull/123 --model gpt-5.5 --wait
|
|
6361
6421
|
|
|
6422
|
+
When the PR came from an Arcanist session, Zeus defaults to the runtime backend opposite
|
|
6423
|
+
the author's as a cross-check; otherwise it uses the default Codex reviewer. --model pins
|
|
6424
|
+
a specific reviewer model instead (members only).
|
|
6362
6425
|
Zeus review is currently available to Arcanist members only.
|
|
6363
6426
|
`
|
|
6364
6427
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|
|
6428
|
+
program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").addHelpText(
|
|
6429
|
+
"after",
|
|
6430
|
+
`
|
|
6431
|
+
Examples:
|
|
6432
|
+
arcanist anubis https://github.com/org/repo/pull/123 --wait --json
|
|
6433
|
+
arcanist anubis https://github.com/org/repo/pull/123
|
|
6434
|
+
|
|
6435
|
+
--json without --wait returns { sessionId, claimed? | duplicate? }.
|
|
6436
|
+
--wait --json returns { sessionId, sessionStatus, verdict, summary? }.
|
|
6437
|
+
Known verdicts are works, broken, and could-not-verify.
|
|
6438
|
+
Anubis deduplicates active runs server-side; it does not use --idempotency-key.
|
|
6439
|
+
The result is posted as a PR comment marked arcanist-anubis:v1.
|
|
6440
|
+
Anubis is currently available to Arcanist members only.
|
|
6441
|
+
`
|
|
6442
|
+
).action((prUrl, options, command) => anubisCommand(prUrl, options, command));
|
|
6365
6443
|
addCreateOptions(sessions.command("create").description("Create a session and send a prompt")).action(
|
|
6366
6444
|
(repoUrl, prompt, options, command) => createCommand(repoUrl, prompt, options, command)
|
|
6367
6445
|
);
|