@tryarcanist/cli 0.1.222 → 0.1.223

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 +153 -140
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -976,58 +976,97 @@ async function agentSubscriptionLogoutCommand(config, options, command) {
976
976
  );
977
977
  }
978
978
 
979
- // src/commands/review.ts
980
- var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
981
- var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
982
- async function reviewCommand(prUrl, options = {}, command) {
983
- assertArcanistSessionMutationAllowed("review");
984
- const { config } = resolveBusinessContext(command, options);
985
- const trigger = await apiFetch(config, "/api/pr-reviews", {
986
- method: "POST",
987
- body: JSON.stringify({
988
- prUrl,
989
- ...options.focus ? { focus: options.focus } : {},
990
- // Send an explicitly passed model even when empty (e.g. --model "$VAR" with an
991
- // unset VAR) so the server rejects it as invalid instead of silently routing.
992
- ...options.model !== void 0 ? { model: options.model } : {}
993
- })
994
- });
995
- if (!options.wait || trigger.cached) {
996
- emit(command, options, trigger, (payload) => {
997
- if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
998
- else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
999
- });
1000
- return;
979
+ // ../../shared/utils/timing.ts
980
+ function sleep(ms) {
981
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
982
+ }
983
+
984
+ // src/status-poll.ts
985
+ async function pollUntilSettled(opts) {
986
+ const deadline = Date.now() + opts.timeoutMs;
987
+ while (Date.now() < deadline) {
988
+ const settled = await opts.fetchStatus();
989
+ if (settled !== null) return settled;
990
+ await sleep(opts.pollIntervalMs);
1001
991
  }
1002
- const result = await waitForReview(config, prUrl, trigger.sessionId ?? null);
1003
- emit(command, options, result, (payload) => {
1004
- console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
1005
- for (const finding of payload.findings) {
1006
- console.log(
1007
- `${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
1008
- );
1009
- }
1010
- });
992
+ throw new CliError("server", opts.timeoutMessage);
1011
993
  }
1012
- function sanitizeTerminalText(value) {
1013
- return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
994
+ function assertSameAttempt(triggeredSessionId, settledSessionId, label) {
995
+ if (triggeredSessionId && settledSessionId !== triggeredSessionId) {
996
+ throw new CliError("conflict", `${label} completed for a different attempt; retry the command.`);
997
+ }
1014
998
  }
1015
- async function waitForReview(config, prUrl, sessionId) {
1016
- const deadline = Date.now() + PR_REVIEW_WAIT_TIMEOUT_MS;
1017
- while (Date.now() < deadline) {
1018
- const status = await apiFetch(
1019
- config,
1020
- `/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
1021
- );
1022
- if (status.status === "completed") {
1023
- if (sessionId && status.sessionId !== sessionId) {
1024
- throw new CliError("conflict", "PR review completed for a different attempt; retry the command.");
1025
- }
1026
- return { ...status, sessionId: status.sessionId ?? sessionId };
1027
- }
1028
- await new Promise((resolve2) => setTimeout(resolve2, PR_REVIEW_POLL_INTERVAL_MS));
999
+
1000
+ // ../../shared/session/phase.ts
1001
+ var PHASES = [
1002
+ "idle",
1003
+ "running",
1004
+ "waiting_for_input",
1005
+ "finalizing",
1006
+ "review_listening",
1007
+ "completed",
1008
+ "superseded",
1009
+ "needs_you",
1010
+ "blocked",
1011
+ "failed",
1012
+ "stopped",
1013
+ "archived"
1014
+ ];
1015
+ var TERMINAL_PHASES_ARRAY = [
1016
+ "completed",
1017
+ "superseded",
1018
+ "needs_you",
1019
+ "blocked",
1020
+ "failed",
1021
+ "stopped",
1022
+ "archived"
1023
+ ];
1024
+ var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
1025
+ var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
1026
+ TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
1027
+ );
1028
+ var CHILD_SLOT_RELEASE_PHASES = new Set(
1029
+ TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
1030
+ );
1031
+ var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
1032
+ (phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
1033
+ );
1034
+ function isTerminalPhase(phase, _sessionKind) {
1035
+ return TERMINAL_PHASES.has(phase);
1036
+ }
1037
+
1038
+ // src/constants/watch.ts
1039
+ var MIN_WATCH_POLL_INTERVAL_MS = 250;
1040
+ var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
1041
+ var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
1042
+ var WATCH_REPLAY_PAGE_SIZE = 200;
1043
+ function isWatchTerminal(phase) {
1044
+ return isTerminalPhase(phase);
1045
+ }
1046
+
1047
+ // src/utils/poll-interval.ts
1048
+ function clampPollInterval(ms, minimum) {
1049
+ return Math.max(ms, minimum);
1050
+ }
1051
+ function parsePollInterval(raw, opts = {}) {
1052
+ const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
1053
+ const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
1054
+ const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
1055
+ if (!raw) return defaultMs;
1056
+ if (!/^\d+$/.test(raw)) {
1057
+ throw new CliError("user", "Polling interval must be a non-negative integer.");
1029
1058
  }
1030
- throw new CliError("server", "Timed out waiting for Zeus review results.");
1059
+ const value = Number(raw);
1060
+ if (value < minMs || maxMs !== null && value > maxMs) {
1061
+ const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
1062
+ throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
1063
+ }
1064
+ return value;
1065
+ }
1066
+
1067
+ // src/utils/terminal-text.ts
1068
+ function sanitizeTerminalText(value) {
1069
+ return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -\/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
1031
1070
  }
1032
1071
 
1033
1072
  // src/commands/anubis.ts
@@ -1035,6 +1074,7 @@ var ANUBIS_POLL_INTERVAL_MS = 15e3;
1035
1074
  var ANUBIS_WAIT_TIMEOUT_MS = 45 * 60 * 1e3;
1036
1075
  async function anubisCommand(prUrl, options = {}, command) {
1037
1076
  assertArcanistSessionMutationAllowed("anubis");
1077
+ const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: ANUBIS_POLL_INTERVAL_MS, maxMs: null }) : null;
1038
1078
  const { config } = resolveBusinessContext(command, options);
1039
1079
  const trigger = await apiFetch(config, "/api/anubis-runs", {
1040
1080
  method: "POST",
@@ -1046,29 +1086,25 @@ async function anubisCommand(prUrl, options = {}, command) {
1046
1086
  });
1047
1087
  return;
1048
1088
  }
1049
- const result = await waitForAnubis(config, prUrl, trigger.sessionId);
1089
+ const result = await pollUntilSettled({
1090
+ pollIntervalMs: pollIntervalMs ?? ANUBIS_POLL_INTERVAL_MS,
1091
+ timeoutMs: ANUBIS_WAIT_TIMEOUT_MS,
1092
+ timeoutMessage: "Timed out waiting for Anubis results.",
1093
+ fetchStatus: async () => {
1094
+ const status = await apiFetch(
1095
+ config,
1096
+ `/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
1097
+ );
1098
+ if (status.sessionStatus === "active") return null;
1099
+ assertSameAttempt(trigger.sessionId, status.sessionId, "Anubis");
1100
+ return status;
1101
+ }
1102
+ });
1050
1103
  emit(command, options, result, (payload) => {
1051
1104
  console.log(`Anubis: ${sanitizeTerminalText(payload.verdict ?? "could-not-verify")}.`);
1052
1105
  if (payload.summary) console.log(sanitizeTerminalText(payload.summary));
1053
1106
  });
1054
1107
  }
1055
- async function waitForAnubis(config, prUrl, sessionId) {
1056
- const deadline = Date.now() + ANUBIS_WAIT_TIMEOUT_MS;
1057
- while (Date.now() < deadline) {
1058
- const status = await apiFetch(
1059
- config,
1060
- `/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
1061
- );
1062
- if (status.sessionStatus !== "active") {
1063
- if (status.sessionId !== sessionId) {
1064
- throw new CliError("conflict", "Anubis completed for a different attempt; retry the command.");
1065
- }
1066
- return status;
1067
- }
1068
- await new Promise((resolve2) => setTimeout(resolve2, ANUBIS_POLL_INTERVAL_MS));
1069
- }
1070
- throw new CliError("server", "Timed out waiting for Anubis results.");
1071
- }
1072
1108
 
1073
1109
  // src/commands/auth.ts
1074
1110
  async function whoamiCommand(options, command) {
@@ -2226,58 +2262,6 @@ async function codexLogoutCommand(options, command) {
2226
2262
  emit(command, options, payload, () => console.log("Codex subscription auth deactivated and cleared."));
2227
2263
  }
2228
2264
 
2229
- // ../../shared/utils/timing.ts
2230
- function sleep(ms) {
2231
- return new Promise((resolve2) => setTimeout(resolve2, ms));
2232
- }
2233
-
2234
- // ../../shared/session/phase.ts
2235
- var PHASES = [
2236
- "idle",
2237
- "running",
2238
- "waiting_for_input",
2239
- "finalizing",
2240
- "review_listening",
2241
- "completed",
2242
- "superseded",
2243
- "needs_you",
2244
- "blocked",
2245
- "failed",
2246
- "stopped",
2247
- "archived"
2248
- ];
2249
- var TERMINAL_PHASES_ARRAY = [
2250
- "completed",
2251
- "superseded",
2252
- "needs_you",
2253
- "blocked",
2254
- "failed",
2255
- "stopped",
2256
- "archived"
2257
- ];
2258
- var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
2259
- var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
2260
- TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
2261
- );
2262
- var CHILD_SLOT_RELEASE_PHASES = new Set(
2263
- TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
2264
- );
2265
- var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
2266
- (phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
2267
- );
2268
- function isTerminalPhase(phase, _sessionKind) {
2269
- return TERMINAL_PHASES.has(phase);
2270
- }
2271
-
2272
- // src/constants/watch.ts
2273
- var MIN_WATCH_POLL_INTERVAL_MS = 250;
2274
- var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
2275
- var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
2276
- var WATCH_REPLAY_PAGE_SIZE = 200;
2277
- function isWatchTerminal(phase) {
2278
- return isTerminalPhase(phase);
2279
- }
2280
-
2281
2265
  // src/uploads.ts
2282
2266
  import { readFile as readFile3 } from "fs/promises";
2283
2267
  import { basename, extname } from "path";
@@ -2423,26 +2407,6 @@ function normalizeUploadedFileOptions(files) {
2423
2407
  return Array.isArray(files) ? files : [files];
2424
2408
  }
2425
2409
 
2426
- // src/utils/poll-interval.ts
2427
- function clampPollInterval(ms, minimum) {
2428
- return Math.max(ms, minimum);
2429
- }
2430
- function parsePollInterval(raw, opts = {}) {
2431
- const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
2432
- const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
2433
- const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
2434
- if (!raw) return defaultMs;
2435
- if (!/^\d+$/.test(raw)) {
2436
- throw new CliError("user", "Polling interval must be a non-negative integer.");
2437
- }
2438
- const value = Number(raw);
2439
- if (value < minMs || maxMs !== null && value > maxMs) {
2440
- const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
2441
- throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
2442
- }
2443
- return value;
2444
- }
2445
-
2446
2410
  // ../../shared/session/no-change-outcome.ts
2447
2411
  function isNoChangesPromptResult(result) {
2448
2412
  return typeof result === "object" && result !== null && "noChanges" in result && result.noChanges === true;
@@ -4783,6 +4747,55 @@ function parseRespondConflict(err) {
4783
4747
  };
4784
4748
  }
4785
4749
 
4750
+ // src/commands/review.ts
4751
+ var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
4752
+ var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
4753
+ async function reviewCommand(prUrl, options = {}, command) {
4754
+ assertArcanistSessionMutationAllowed("review");
4755
+ const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: PR_REVIEW_POLL_INTERVAL_MS, maxMs: null }) : null;
4756
+ const { config } = resolveBusinessContext(command, options);
4757
+ const trigger = await apiFetch(config, "/api/pr-reviews", {
4758
+ method: "POST",
4759
+ body: JSON.stringify({
4760
+ prUrl,
4761
+ ...options.focus ? { focus: options.focus } : {},
4762
+ // Send an explicitly passed model even when empty (e.g. --model "$VAR" with an
4763
+ // unset VAR) so the server rejects it as invalid instead of silently routing.
4764
+ ...options.model !== void 0 ? { model: options.model } : {}
4765
+ })
4766
+ });
4767
+ if (!options.wait || trigger.cached) {
4768
+ emit(command, options, trigger, (payload) => {
4769
+ if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
4770
+ else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
4771
+ });
4772
+ return;
4773
+ }
4774
+ const triggeredSessionId = trigger.sessionId ?? null;
4775
+ const result = await pollUntilSettled({
4776
+ pollIntervalMs: pollIntervalMs ?? PR_REVIEW_POLL_INTERVAL_MS,
4777
+ timeoutMs: PR_REVIEW_WAIT_TIMEOUT_MS,
4778
+ timeoutMessage: "Timed out waiting for Zeus review results.",
4779
+ fetchStatus: async () => {
4780
+ const status = await apiFetch(
4781
+ config,
4782
+ `/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
4783
+ );
4784
+ if (status.status !== "completed") return null;
4785
+ assertSameAttempt(triggeredSessionId, status.sessionId, "PR review");
4786
+ return { ...status, sessionId: status.sessionId ?? triggeredSessionId };
4787
+ }
4788
+ });
4789
+ emit(command, options, result, (payload) => {
4790
+ console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
4791
+ for (const finding of payload.findings) {
4792
+ console.log(
4793
+ `${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
4794
+ );
4795
+ }
4796
+ });
4797
+ }
4798
+
4786
4799
  // src/commands/sandbox.ts
4787
4800
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4788
4801
  import { dirname as dirname2 } from "path";
@@ -6391,7 +6404,7 @@ cursor.command("use <state>").description("Turn using your saved Cursor subscrip
6391
6404
  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));
6392
6405
  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));
6393
6406
  var sessions = program.command("sessions").description("Session commands");
6394
- 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(
6407
+ 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").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 10000)").addHelpText(
6395
6408
  "after",
6396
6409
  `
6397
6410
  Examples:
@@ -6405,7 +6418,7 @@ a specific reviewer model instead (members only).
6405
6418
  Zeus review is currently available to Arcanist members only.
6406
6419
  `
6407
6420
  ).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
6408
- program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").addHelpText(
6421
+ program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 15000)").addHelpText(
6409
6422
  "after",
6410
6423
  `
6411
6424
  Examples:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.222",
3
+ "version": "0.1.223",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {