@tryarcanist/cli 0.1.225 → 0.1.227

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 (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +119 -130
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -197,7 +197,7 @@ arcanist sessions create your-org/your-repo "review the trace" --uploaded-file t
197
197
  arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-key 1f0e6f1a-...
198
198
  ```
199
199
 
200
- `--backend` picks the agent runtime backend: `codex` (default) or `claude_code`. `--model` must be valid for the chosen backend codex runs OpenAI models (`gpt-5.4` default, `gpt-5.5`), `claude_code` runs Anthropic models (`claude-opus-4-8` default, `claude-sonnet-4-6`, `claude-fable-5`) and the CLI rejects a mismatch before any network call. `claude_code` sessions require an Anthropic API key configured in Settings.
200
+ `--backend` picks the agent runtime backend: `codex` (default), `claude_code`, `opencode`, `cursor`, or `grok`. `--model` must be valid for the chosen backend and the CLI rejects a mismatch before any network call; run `arcanist models list` for the selectable models per backend. When `--model` is omitted the backend default is used. Non-codex backends require the matching provider credential configured in Settings.
201
201
 
202
202
  `--wait` blocks until the created prompt finishes, prints the resulting PR/branch line in human-readable mode when it is already available, and exits non-zero if the prompt finishes with `status: failed`, making it suitable for cron or other schedulers that alert on command failure. JSON mode waits quietly and prints the create payload after the prompt completes successfully. `--poll-interval <ms>` tunes the completion check frequency.
203
203
 
@@ -242,7 +242,7 @@ The PR URL must be `https://github.com/<owner>/<repo>/pull/<number>`.
242
242
  The CLI derives the repo from that URL, creates a QA session with `qa: true` and `targetPrUrl`, then enqueues the verifier prompt (skipped when the server signals `promptAlreadyEnqueued`).
243
243
  Inspect progress with the session URL or the session events stream.
244
244
 
245
- `--backend` picks the agent runtime backend: `codex` (default), `claude_code`, or `opencode`.
245
+ `--backend` picks the agent runtime backend: `codex` (default), `claude_code`, `opencode`, `cursor`, or `grok`.
246
246
  `--model` must be valid for the chosen backend, and the CLI rejects mismatches before any network call.
247
247
  When `--model` is omitted, the backend default is used.
248
248
 
package/dist/index.js CHANGED
@@ -4,6 +4,31 @@
4
4
  import { createRequire as createRequire2 } from "module";
5
5
  import { Command } from "commander";
6
6
 
7
+ // ../../shared/agent/agent-runtime-backend.ts
8
+ var CODEX_AGENT_RUNTIME_BACKEND = "codex";
9
+ var CLAUDE_CODE_AGENT_RUNTIME_BACKEND = "claude_code";
10
+ var OPENCODE_AGENT_RUNTIME_BACKEND = "opencode";
11
+ var CURSOR_AGENT_RUNTIME_BACKEND = "cursor";
12
+ var GROK_AGENT_RUNTIME_BACKEND = "grok";
13
+ var AGENT_RUNTIME_BACKENDS = [
14
+ CODEX_AGENT_RUNTIME_BACKEND,
15
+ CLAUDE_CODE_AGENT_RUNTIME_BACKEND,
16
+ OPENCODE_AGENT_RUNTIME_BACKEND,
17
+ CURSOR_AGENT_RUNTIME_BACKEND,
18
+ GROK_AGENT_RUNTIME_BACKEND
19
+ ];
20
+ var AGENT_RUNTIME_BACKEND_NAMES = {
21
+ [CODEX_AGENT_RUNTIME_BACKEND]: "Codex",
22
+ [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: "Claude Code",
23
+ // "opencode" is intentionally lowercase to match the project's brand name.
24
+ [OPENCODE_AGENT_RUNTIME_BACKEND]: "opencode",
25
+ [CURSOR_AGENT_RUNTIME_BACKEND]: "Cursor",
26
+ [GROK_AGENT_RUNTIME_BACKEND]: "Grok Build"
27
+ };
28
+ function isAgentRuntimeBackend(value) {
29
+ return AGENT_RUNTIME_BACKENDS.includes(value);
30
+ }
31
+
7
32
  // src/commands/agent-subscription.ts
8
33
  import { execFileSync, spawn as spawn2 } from "child_process";
9
34
  import { existsSync as existsSync2 } from "fs";
@@ -1138,29 +1163,61 @@ async function whoamiCommand(options, command) {
1138
1163
  });
1139
1164
  }
1140
1165
 
1141
- // ../../shared/agent/agent-runtime-backend.ts
1142
- var CODEX_AGENT_RUNTIME_BACKEND = "codex";
1143
- var CLAUDE_CODE_AGENT_RUNTIME_BACKEND = "claude_code";
1144
- var OPENCODE_AGENT_RUNTIME_BACKEND = "opencode";
1145
- var CURSOR_AGENT_RUNTIME_BACKEND = "cursor";
1146
- var GROK_AGENT_RUNTIME_BACKEND = "grok";
1147
- var AGENT_RUNTIME_BACKENDS = [
1148
- CODEX_AGENT_RUNTIME_BACKEND,
1149
- CLAUDE_CODE_AGENT_RUNTIME_BACKEND,
1150
- OPENCODE_AGENT_RUNTIME_BACKEND,
1151
- CURSOR_AGENT_RUNTIME_BACKEND,
1152
- GROK_AGENT_RUNTIME_BACKEND
1153
- ];
1154
- var AGENT_RUNTIME_BACKEND_NAMES = {
1155
- [CODEX_AGENT_RUNTIME_BACKEND]: "Codex",
1156
- [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: "Claude Code",
1157
- // "opencode" is intentionally lowercase to match the project's brand name.
1158
- [OPENCODE_AGENT_RUNTIME_BACKEND]: "opencode",
1159
- [CURSOR_AGENT_RUNTIME_BACKEND]: "Cursor",
1160
- [GROK_AGENT_RUNTIME_BACKEND]: "Grok Build"
1161
- };
1162
- function isAgentRuntimeBackend(value) {
1163
- return AGENT_RUNTIME_BACKENDS.includes(value);
1166
+ // src/utils/pagination.ts
1167
+ var MAX_ALL_PAGES = 1e3;
1168
+ async function fetchPages(label, all, initialCursor, fetchPage) {
1169
+ const items = [];
1170
+ let cursor2 = initialCursor;
1171
+ let nextCursor = null;
1172
+ let pageCount = 0;
1173
+ do {
1174
+ pageCount += 1;
1175
+ if (all && pageCount > MAX_ALL_PAGES) {
1176
+ throw new CliError("user", `${label} exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
1177
+ }
1178
+ const page = await fetchPage(cursor2);
1179
+ items.push(...page.items);
1180
+ nextCursor = page.nextCursor;
1181
+ cursor2 = nextCursor ?? void 0;
1182
+ } while (all && nextCursor);
1183
+ return { items, nextCursor: all ? null : nextCursor };
1184
+ }
1185
+
1186
+ // ../../shared/github/repo-url.ts
1187
+ function parseGithubRepoFullName(value) {
1188
+ const shorthand = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
1189
+ if (shorthand) return { owner: shorthand[1], repo: shorthand[2] };
1190
+ const ssh = value.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
1191
+ if (ssh) return { owner: ssh[1], repo: ssh[2] };
1192
+ const https = value.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
1193
+ if (https) return { owner: https[1], repo: https[2] };
1194
+ return null;
1195
+ }
1196
+
1197
+ // src/git.ts
1198
+ import { execFileSync as execFileSync2 } from "child_process";
1199
+ function git(args) {
1200
+ try {
1201
+ return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
1202
+ } catch (err) {
1203
+ throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
1204
+ }
1205
+ }
1206
+ function currentRepo() {
1207
+ const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
1208
+ if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
1209
+ return { owner: parsed.owner, repo: parsed.repo };
1210
+ }
1211
+
1212
+ // src/utils/repo-arg.ts
1213
+ function parseRepoArg(value, argName = "repo") {
1214
+ const parsed = parseGithubRepoFullName(value);
1215
+ if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
1216
+ return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
1217
+ }
1218
+ function parseRepoArgOrCurrent(value, argName = "repo") {
1219
+ if (value === void 0) return currentRepo();
1220
+ return parseRepoArg(value, argName);
1164
1221
  }
1165
1222
 
1166
1223
  // ../../shared/utils/type-guards.ts
@@ -1909,61 +1966,40 @@ var SESSION_START_MODEL_PROVIDER_GROUPS_BY_SUBSCRIPTIONS = {
1909
1966
  "true:true": buildSubscriptionAwareGroups({ codexSubscription: true, grokSubscription: true })
1910
1967
  };
1911
1968
 
1912
- // src/utils/pagination.ts
1913
- var MAX_ALL_PAGES = 1e3;
1914
- async function fetchPages(label, all, initialCursor, fetchPage) {
1915
- const items = [];
1916
- let cursor2 = initialCursor;
1917
- let nextCursor = null;
1918
- let pageCount = 0;
1919
- do {
1920
- pageCount += 1;
1921
- if (all && pageCount > MAX_ALL_PAGES) {
1922
- throw new CliError("user", `${label} exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
1923
- }
1924
- const page = await fetchPage(cursor2);
1925
- items.push(...page.items);
1926
- nextCursor = page.nextCursor;
1927
- cursor2 = nextCursor ?? void 0;
1928
- } while (all && nextCursor);
1929
- return { items, nextCursor: all ? null : nextCursor };
1930
- }
1931
-
1932
- // ../../shared/github/repo-url.ts
1933
- function parseGithubRepoFullName(value) {
1934
- const shorthand = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
1935
- if (shorthand) return { owner: shorthand[1], repo: shorthand[2] };
1936
- const ssh = value.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
1937
- if (ssh) return { owner: ssh[1], repo: ssh[2] };
1938
- const https = value.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
1939
- if (https) return { owner: https[1], repo: https[2] };
1940
- return null;
1969
+ // src/commands/model-options.ts
1970
+ function backendForProviderPrefix(rawModel) {
1971
+ const provider = rawModel.match(/^([a-z]+)[/:]/i)?.[1]?.toLowerCase();
1972
+ if (provider === "anthropic") return CLAUDE_CODE_AGENT_RUNTIME_BACKEND;
1973
+ if (provider === "baseten" || provider === "xai") return OPENCODE_AGENT_RUNTIME_BACKEND;
1974
+ if (provider === "cursor") return CURSOR_AGENT_RUNTIME_BACKEND;
1975
+ if (provider === "grok") return GROK_AGENT_RUNTIME_BACKEND;
1976
+ return void 0;
1941
1977
  }
1942
-
1943
- // src/git.ts
1944
- import { execFileSync as execFileSync2 } from "child_process";
1945
- function git(args) {
1946
- try {
1947
- return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
1948
- } catch (err) {
1949
- throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
1978
+ function resolveModelAndBackend(options) {
1979
+ let agentRuntimeBackend = CODEX_AGENT_RUNTIME_BACKEND;
1980
+ const normalizedModel = options.model !== void 0 ? extractModelId(options.model) : void 0;
1981
+ if (options.backend !== void 0) {
1982
+ if (!isAgentRuntimeBackend(options.backend)) {
1983
+ throw new CliError(
1984
+ "user",
1985
+ `Invalid --backend '${options.backend}'. Expected ${AGENT_RUNTIME_BACKENDS.join(", ")}.`
1986
+ );
1987
+ }
1988
+ agentRuntimeBackend = options.backend;
1989
+ } else if (options.model !== void 0) {
1990
+ const derived = (normalizedModel ? getAgentRuntimeBackendForModel(normalizedModel) : void 0) ?? backendForProviderPrefix(options.model);
1991
+ if (derived) agentRuntimeBackend = derived;
1950
1992
  }
1951
- }
1952
- function currentRepo() {
1953
- const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
1954
- if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
1955
- return { owner: parsed.owner, repo: parsed.repo };
1956
- }
1957
-
1958
- // src/utils/repo-arg.ts
1959
- function parseRepoArg(value, argName = "repo") {
1960
- const parsed = parseGithubRepoFullName(value);
1961
- if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
1962
- return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
1963
- }
1964
- function parseRepoArgOrCurrent(value, argName = "repo") {
1965
- if (value === void 0) return currentRepo();
1966
- return parseRepoArg(value, argName);
1993
+ if (options.model !== void 0) {
1994
+ if (normalizedModel === void 0 || !isSessionStartModelAllowedForBackend(normalizedModel, agentRuntimeBackend)) {
1995
+ const allowed = getSessionStartModelIdsForBackend(agentRuntimeBackend).join(", ");
1996
+ throw new CliError(
1997
+ "user",
1998
+ `Model '${options.model}' is not selectable for backend '${agentRuntimeBackend}'. Allowed: ${allowed}.`
1999
+ );
2000
+ }
2001
+ }
2002
+ return { agentRuntimeBackend, modelId: normalizedModel };
1967
2003
  }
1968
2004
 
1969
2005
  // src/commands/automations.ts
@@ -1996,15 +2032,7 @@ async function createAutomationCommand(repoUrl, promptArg, options, command) {
1996
2032
  }
1997
2033
  const runtime = getRuntimeOptions(command, options);
1998
2034
  const config = requireConfig(runtime);
1999
- let normalizedModel;
2000
- if (options.model !== void 0) {
2001
- normalizedModel = extractModelId(options.model);
2002
- const backend = resolveAutomationModelBackend(options.model, normalizedModel);
2003
- if (normalizedModel === void 0 || !isSessionStartModelAllowedForBackend(normalizedModel, backend)) {
2004
- const allowed = getSessionStartModelIdsForBackend(backend).join(", ");
2005
- throw new CliError("user", `Model '${options.model}' is not selectable. Allowed: ${allowed}.`);
2006
- }
2007
- }
2035
+ const { modelId: normalizedModel } = resolveModelAndBackend(options);
2008
2036
  const body = {
2009
2037
  repoOwner: repo.owner,
2010
2038
  repoName: repo.repo,
@@ -2077,18 +2105,6 @@ async function deleteAutomationCommand(id, options, command) {
2077
2105
  }
2078
2106
  console.log(`Deleted automation ${id}.`);
2079
2107
  }
2080
- function resolveAutomationModelBackend(rawModel, normalizedModel) {
2081
- if (normalizedModel) {
2082
- const backend = getAgentRuntimeBackendForModel(normalizedModel);
2083
- if (backend) return backend;
2084
- }
2085
- const provider = rawModel.match(/^([a-z]+)[/:]/i)?.[1]?.toLowerCase();
2086
- if (provider === "anthropic") return CLAUDE_CODE_AGENT_RUNTIME_BACKEND;
2087
- if (provider === "baseten" || provider === "xai") return OPENCODE_AGENT_RUNTIME_BACKEND;
2088
- if (provider === "cursor") return CURSOR_AGENT_RUNTIME_BACKEND;
2089
- if (provider === "grok") return GROK_AGENT_RUNTIME_BACKEND;
2090
- return CODEX_AGENT_RUNTIME_BACKEND;
2091
- }
2092
2108
  async function automationApiFetch(config, path, init) {
2093
2109
  try {
2094
2110
  return await apiFetch(config, path, init);
@@ -3387,6 +3403,7 @@ var ERROR_CODES = [
3387
3403
  "auth",
3388
3404
  "policy_block",
3389
3405
  "handled_automatically",
3406
+ "review_publish_blocked",
3390
3407
  "output_length",
3391
3408
  "context_overflow",
3392
3409
  "aborted",
@@ -3428,6 +3445,7 @@ var ERROR_CODE_LABELS = {
3428
3445
  auth: "Authentication failed",
3429
3446
  policy_block: "Policy block",
3430
3447
  handled_automatically: "Handled automatically",
3448
+ review_publish_blocked: "Review publication blocked",
3431
3449
  output_length: "Output was too long",
3432
3450
  context_overflow: "Context window exceeded",
3433
3451
  aborted: "Stopped by user",
@@ -3842,35 +3860,6 @@ function unwrapSessionState(payload) {
3842
3860
  return payload.session && typeof payload.session === "object" ? payload.session : payload;
3843
3861
  }
3844
3862
 
3845
- // src/commands/model-options.ts
3846
- function resolveModelAndBackend(options) {
3847
- let agentRuntimeBackend = CODEX_AGENT_RUNTIME_BACKEND;
3848
- if (options.backend !== void 0) {
3849
- if (!isAgentRuntimeBackend(options.backend)) {
3850
- throw new CliError(
3851
- "user",
3852
- `Invalid --backend '${options.backend}'. Expected ${AGENT_RUNTIME_BACKENDS.join(", ")}.`
3853
- );
3854
- }
3855
- agentRuntimeBackend = options.backend;
3856
- } else if (options.model !== void 0) {
3857
- const normalizedModel = extractModelId(options.model);
3858
- const derived = normalizedModel ? getAgentRuntimeBackendForModel(normalizedModel) : void 0;
3859
- if (derived) agentRuntimeBackend = derived;
3860
- }
3861
- if (options.model !== void 0) {
3862
- const normalizedModel = extractModelId(options.model);
3863
- if (normalizedModel === void 0 || !isSessionStartModelAllowedForBackend(normalizedModel, agentRuntimeBackend)) {
3864
- const allowed = getSessionStartModelIdsForBackend(agentRuntimeBackend).join(", ");
3865
- throw new CliError(
3866
- "user",
3867
- `Model '${options.model}' is not selectable for backend '${agentRuntimeBackend}'. Allowed: ${allowed}.`
3868
- );
3869
- }
3870
- }
3871
- return agentRuntimeBackend;
3872
- }
3873
-
3874
3863
  // ../../shared/session/transient-disconnect.ts
3875
3864
  var TRANSIENT_DISCONNECT_VISIBILITY_MS = 15e3;
3876
3865
  function nextDisconnectMask(mask, previous, current, now) {
@@ -4137,7 +4126,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
4137
4126
  const runtime = getRuntimeOptions(command, options);
4138
4127
  assertArcanistSessionMutationAllowed("create");
4139
4128
  const config = requireConfig(runtime);
4140
- const agentRuntimeBackend = resolveModelAndBackend(options);
4129
+ const { agentRuntimeBackend } = resolveModelAndBackend(options);
4141
4130
  const prompt = options.onboarding ? ONBOARDING_PROMPT : await resolvePromptInput(promptArg, options);
4142
4131
  const waitPollIntervalMs = options.wait ? parsePollInterval(options.pollInterval) : null;
4143
4132
  const repoError = validateRepoUrl(repoUrl);
@@ -4563,7 +4552,7 @@ async function qaCommand(prUrl, options, command) {
4563
4552
  assertArcanistSessionMutationAllowed("qa");
4564
4553
  const config = requireConfig(runtime);
4565
4554
  const { targetPrUrl, repoUrl } = parseCanonicalPrUrl(prUrl);
4566
- const agentRuntimeBackend = resolveModelAndBackend(options);
4555
+ const { agentRuntimeBackend } = resolveModelAndBackend(options);
4567
4556
  const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
4568
4557
  const sessionIdempotencyKey = `${idempotencyKey}:session`;
4569
4558
  const promptIdempotencyKey = `${idempotencyKey}:prompt`;
@@ -6226,7 +6215,7 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
6226
6215
  applyColorEnvironment(getRuntimeOptions(actionCommand));
6227
6216
  });
6228
6217
  function addCreateOptions(cmd) {
6229
- return cmd.argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to send, or '-' to read stdin").option("--model <model>", "Model to use").option("--backend <backend>", "Agent runtime backend: codex (default), claude_code, or opencode").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--auto-verify", "Opt this session into automatic QA verification after PR creation").option("--base-branch <branch>", "Base branch to create the session against (defaults to the repo default branch)").option(
6218
+ return cmd.argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to send, or '-' to read stdin").option("--model <model>", "Model to use").option("--backend <backend>", `Agent runtime backend: ${AGENT_RUNTIME_BACKENDS.join(", ")} (default codex)`).option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--auto-verify", "Opt this session into automatic QA verification after PR creation").option("--base-branch <branch>", "Base branch to create the session against (defaults to the repo default branch)").option(
6230
6219
  "--start-branch <branch>",
6231
6220
  "Resume an existing branch with its history instead of forking a new one off base"
6232
6221
  ).option("--linear-issue <id|url>", "Associate the session with a Linear issue for pickup writeback").option("--jira-issue <key|url>", "Associate the session with a Jira issue for pickup writeback").option("--continue-pr <url>", "Continue from an existing same-repo pull request").option("--continue-mode <mode>", "Continuation mode: auto (default), update-pr, or new-pr").option("--prompt-stdin", "Read prompt from stdin").option(
@@ -6276,7 +6265,7 @@ JSON mode returns {sessionId, promptId?}
6276
6265
  );
6277
6266
  }
6278
6267
  function addQaOptions(cmd) {
6279
- return cmd.argument("<pr-url>", "GitHub pull request URL to QA").option("--model <model>", "Model to use").option("--backend <backend>", "Agent runtime backend: codex (default), claude_code, or opencode").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--wait", "Wait for the QA prompt to finish and exit non-zero if it fails").option(
6268
+ return cmd.argument("<pr-url>", "GitHub pull request URL to QA").option("--model <model>", "Model to use").option("--backend <backend>", `Agent runtime backend: ${AGENT_RUNTIME_BACKENDS.join(", ")} (default codex)`).option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--wait", "Wait for the QA prompt to finish and exit non-zero if it fails").option(
6280
6269
  "--poll-interval <ms>",
6281
6270
  "Polling interval in milliseconds while waiting",
6282
6271
  String(DEFAULT_WATCH_POLL_INTERVAL_MS)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.225",
3
+ "version": "0.1.227",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {