@tryarcanist/cli 0.1.225 → 0.1.226

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 +117 -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);
@@ -3842,35 +3858,6 @@ function unwrapSessionState(payload) {
3842
3858
  return payload.session && typeof payload.session === "object" ? payload.session : payload;
3843
3859
  }
3844
3860
 
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
3861
  // ../../shared/session/transient-disconnect.ts
3875
3862
  var TRANSIENT_DISCONNECT_VISIBILITY_MS = 15e3;
3876
3863
  function nextDisconnectMask(mask, previous, current, now) {
@@ -4137,7 +4124,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
4137
4124
  const runtime = getRuntimeOptions(command, options);
4138
4125
  assertArcanistSessionMutationAllowed("create");
4139
4126
  const config = requireConfig(runtime);
4140
- const agentRuntimeBackend = resolveModelAndBackend(options);
4127
+ const { agentRuntimeBackend } = resolveModelAndBackend(options);
4141
4128
  const prompt = options.onboarding ? ONBOARDING_PROMPT : await resolvePromptInput(promptArg, options);
4142
4129
  const waitPollIntervalMs = options.wait ? parsePollInterval(options.pollInterval) : null;
4143
4130
  const repoError = validateRepoUrl(repoUrl);
@@ -4563,7 +4550,7 @@ async function qaCommand(prUrl, options, command) {
4563
4550
  assertArcanistSessionMutationAllowed("qa");
4564
4551
  const config = requireConfig(runtime);
4565
4552
  const { targetPrUrl, repoUrl } = parseCanonicalPrUrl(prUrl);
4566
- const agentRuntimeBackend = resolveModelAndBackend(options);
4553
+ const { agentRuntimeBackend } = resolveModelAndBackend(options);
4567
4554
  const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
4568
4555
  const sessionIdempotencyKey = `${idempotencyKey}:session`;
4569
4556
  const promptIdempotencyKey = `${idempotencyKey}:prompt`;
@@ -6226,7 +6213,7 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
6226
6213
  applyColorEnvironment(getRuntimeOptions(actionCommand));
6227
6214
  });
6228
6215
  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(
6216
+ 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
6217
  "--start-branch <branch>",
6231
6218
  "Resume an existing branch with its history instead of forking a new one off base"
6232
6219
  ).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 +6263,7 @@ JSON mode returns {sessionId, promptId?}
6276
6263
  );
6277
6264
  }
6278
6265
  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(
6266
+ 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
6267
  "--poll-interval <ms>",
6281
6268
  "Polling interval in milliseconds while waiting",
6282
6269
  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.226",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {