@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.
Files changed (2) hide show
  1. package/dist/index.js +128 -50
  2. 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" ? "review" : `sessions ${subcommand}`;
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
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.219",
3
+ "version": "0.1.221",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {