@tryarcanist/cli 0.1.131 → 0.1.133

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 +19 -2
  2. package/dist/index.js +51 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -123,6 +123,7 @@ printf "add tests" | arcanist sessions create your-org/your-repo --prompt-stdin
123
123
  arcanist sessions create your-org/your-repo - --model gpt-5.5
124
124
  arcanist sessions create your-org/your-repo "port to claude" --backend claude_code --model claude-opus-4-8
125
125
  arcanist sessions create your-org/your-repo "refactor auth" --reasoning-effort xhigh
126
+ arcanist sessions create your-org/your-repo "fix release branch" --base-branch release/2026-06
126
127
  arcanist sessions create your-org/your-repo "review the trace" --uploaded-file trace.txt
127
128
  arcanist sessions create your-org/your-repo "verify the deployed change" --cold
128
129
  arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-key 1f0e6f1a-...
@@ -130,7 +131,9 @@ arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-ke
130
131
 
131
132
  `--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`) — and the CLI rejects a mismatch before any network call. `claude_code` sessions require an Anthropic API key configured in Settings.
132
133
 
133
- `--wait` blocks until the created prompt finishes and exits non-zero if the prompt finishes with `status: failed`, making it suitable for cron or other schedulers that alert on command failure. `--poll-interval <ms>` tunes the completion check frequency.
134
+ `--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. `--poll-interval <ms>` tunes the completion check frequency.
135
+
136
+ Use `--base-branch <branch>` to target a non-default base branch. The CLI only validates that the branch value is non-empty; branch existence is checked later by the session runtime.
134
137
 
135
138
  Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the prompt. Uploaded file names come from the local basename; directory components are not sent.
136
139
 
@@ -140,7 +143,7 @@ Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the p
140
143
 
141
144
  `--onboarding` creates an onboarding session: the agent authors the repo's `.arcanist.json`, `.arcanist/` runtime files, `ARCANIST.md`, and conditionally `.arcanist/sandbox.yaml` + `.arcanist/sandbox.layer.Dockerfile` (when the base sandbox lacks a needed toolchain), proves what it can inside its sandbox, and opens the setup PR ready for review. Team use; not part of the external API surface.
142
145
 
143
- JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed.
146
+ JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed.
144
147
 
145
148
  ### `arcanist sessions send <session-id> [prompt]`
146
149
 
@@ -173,6 +176,8 @@ arcanist sessions get abc123
173
176
  arcanist sessions get abc123 --json
174
177
  ```
175
178
 
179
+ Human-readable output includes `PR: <url> (<branch>)` when the session has opened a PR, or `Branch: <branch>` when a produced branch is known but no PR URL is present yet. If publishing is still settling, rerun `sessions get`; JSON mode returns the raw session payload, with result fields at `.session.prUrl`, `.session.publishedBranch`, and `.session.lastBranch`.
180
+
176
181
  ### `arcanist sessions list`
177
182
 
178
183
  ```bash
@@ -321,6 +326,18 @@ arcanist sessions create your-org/your-repo "verify the new rate limiter is acti
321
326
 
322
327
  For cron, prefer `ARCANIST_TOKEN` or a logged-in `~/.arcanist/config.json` over passing `--token` on the command line.
323
328
 
329
+ To capture the PR URL after a successful run, use `create --wait` for the exit code, then read the session state:
330
+
331
+ ```bash
332
+ SESSION_ID=$(arcanist sessions create your-org/your-repo "fix the flaky test" --wait --json | jq -r .sessionId)
333
+ for _ in {1..20}; do
334
+ PR_URL=$(arcanist sessions get "$SESSION_ID" --json | jq -r '.session.prUrl // empty')
335
+ [ -n "$PR_URL" ] && break
336
+ sleep 5
337
+ done
338
+ [ -n "$PR_URL" ] || { echo "PR URL not ready for $SESSION_ID" >&2; exit 1; }
339
+ ```
340
+
324
341
  ## Troubleshooting
325
342
 
326
343
  - **401 on login verification**: token may be invalid or expired. Regenerate in **Settings > CLI Tokens**.
package/dist/index.js CHANGED
@@ -1023,22 +1023,6 @@ function noChangeOutcomeCopy(reason) {
1023
1023
  }
1024
1024
  }
1025
1025
 
1026
- // ../../shared/session/transient-disconnect.ts
1027
- var TRANSIENT_DISCONNECT_VISIBILITY_MS = 15e3;
1028
- function nextDisconnectMask(mask, previous, current, now) {
1029
- if (!current || current.sandboxSubstate !== "reconnecting") return null;
1030
- if (mask) return mask;
1031
- if (!previous || previous.sandboxSubstate === "reconnecting") return null;
1032
- return { since: now, heldPhase: previous.phase, heldSubstate: previous.sandboxSubstate ?? "none" };
1033
- }
1034
- function isDisconnectMaskActive(mask, now) {
1035
- return mask !== null && now - mask.since < TRANSIENT_DISCONNECT_VISIBILITY_MS;
1036
- }
1037
- function applyDisconnectMask(view, mask, now) {
1038
- if (!isDisconnectMaskActive(mask, now)) return view;
1039
- return { ...view, phase: mask.heldPhase, sandboxSubstate: mask.heldSubstate };
1040
- }
1041
-
1042
1026
  // ../../shared/transcript/malformed-search.ts
1043
1027
  var MALFORMED_SEARCH_BLOCKED_TRANSCRIPT_PREFIX = "Arcanist blocked this malformed search command before execution.";
1044
1028
  var BASH_UNMATCHED_QUOTE_EOF_RE = /\/bin\/bash:\s+-c:\s+line\s+\d+:\s+unexpected EOF while looking for matching [`'"][`'"]?/i;
@@ -2063,6 +2047,23 @@ ${event.answer ? `**Answer:** ${event.answer}
2063
2047
  function formatNumber(value) {
2064
2048
  return value.toLocaleString();
2065
2049
  }
2050
+ function stringField(value) {
2051
+ return typeof value === "string" && value.trim() ? value : null;
2052
+ }
2053
+ function formatSessionResult(session) {
2054
+ const prUrl = stringField(session.prUrl);
2055
+ const publishedBranch = stringField(session.publishedBranch);
2056
+ const lastBranch = stringField(session.lastBranch);
2057
+ const baseBranch = stringField(session.baseBranch);
2058
+ const branch = publishedBranch ?? (lastBranch && lastBranch !== baseBranch ? lastBranch : null);
2059
+ if (prUrl) {
2060
+ return `PR: ${prUrl}${branch ? ` (${branch})` : ""}`;
2061
+ }
2062
+ if (branch) {
2063
+ return `Branch: ${branch}`;
2064
+ }
2065
+ return null;
2066
+ }
2066
2067
  function latestCompletedPromptResult(prompts) {
2067
2068
  for (let i = prompts.length - 1; i >= 0; i--) {
2068
2069
  if (prompts[i].status === "completed") return prompts[i].result;
@@ -2314,6 +2315,22 @@ function renderWatchEvent(event, state) {
2314
2315
  }
2315
2316
  }
2316
2317
 
2318
+ // ../../shared/session/transient-disconnect.ts
2319
+ var TRANSIENT_DISCONNECT_VISIBILITY_MS = 15e3;
2320
+ function nextDisconnectMask(mask, previous, current, now) {
2321
+ if (!current || current.sandboxSubstate !== "reconnecting") return null;
2322
+ if (mask) return mask;
2323
+ if (!previous || previous.sandboxSubstate === "reconnecting") return null;
2324
+ return { since: now, heldPhase: previous.phase, heldSubstate: previous.sandboxSubstate ?? "none" };
2325
+ }
2326
+ function isDisconnectMaskActive(mask, now) {
2327
+ return mask !== null && now - mask.since < TRANSIENT_DISCONNECT_VISIBILITY_MS;
2328
+ }
2329
+ function applyDisconnectMask(view, mask, now) {
2330
+ if (!isDisconnectMaskActive(mask, now)) return view;
2331
+ return { ...view, phase: mask.heldPhase, sandboxSubstate: mask.heldSubstate };
2332
+ }
2333
+
2317
2334
  // src/commands/watch.ts
2318
2335
  function parsePollInterval(raw) {
2319
2336
  if (!raw) return DEFAULT_WATCH_POLL_INTERVAL_MS;
@@ -2571,6 +2588,10 @@ async function createCommand(repoUrl, promptArg, options, command) {
2571
2588
  if (repoError) {
2572
2589
  throw new CliError("user", repoError);
2573
2590
  }
2591
+ const baseBranch = options.baseBranch?.trim();
2592
+ if (options.baseBranch !== void 0 && !baseBranch) {
2593
+ throw new CliError("user", "--base-branch must not be empty.");
2594
+ }
2574
2595
  const uploadedFiles = await resolveUploadedFileOptions(options.uploadedFile);
2575
2596
  const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
2576
2597
  const sessionIdempotencyKey = `${idempotencyKey}:session`;
@@ -2579,6 +2600,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
2579
2600
  if (options.model) body.model = options.model;
2580
2601
  if (options.backend) body.agentRuntimeBackend = agentRuntimeBackend;
2581
2602
  if (options.reasoningEffort) body.reasoningEffort = options.reasoningEffort;
2603
+ if (baseBranch) body.baseBranch = baseBranch;
2582
2604
  if (options.cold) body.cold = true;
2583
2605
  if (options.onboarding) body.onboarding = true;
2584
2606
  const sessionData = await apiFetch(config, "/api/sessions", {
@@ -2616,6 +2638,15 @@ async function createCommand(repoUrl, promptArg, options, command) {
2616
2638
  if (sessionData.sessionUrl) console.log(`URL: ${sessionData.sessionUrl}`);
2617
2639
  }
2618
2640
  await waitForCreatedPrompt(sessionId, promptId, sessionData.sessionUrl, waitPollIntervalMs, runtime, command);
2641
+ if (!isJson(command, options)) {
2642
+ try {
2643
+ const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
2644
+ const sessionState = sessionPayload.session && typeof sessionPayload.session === "object" ? sessionPayload.session : sessionPayload;
2645
+ const resultLine = formatSessionResult(sessionState);
2646
+ if (resultLine) console.log(resultLine);
2647
+ } catch {
2648
+ }
2649
+ }
2619
2650
  }
2620
2651
  if (isJson(command, options)) {
2621
2652
  const output = { sessionId };
@@ -2624,6 +2655,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
2624
2655
  if (options.model) output.model = options.model;
2625
2656
  if (options.backend) output.agentRuntimeBackend = agentRuntimeBackend;
2626
2657
  if (options.reasoningEffort) output.reasoningEffort = options.reasoningEffort;
2658
+ if (baseBranch) output.baseBranch = baseBranch;
2627
2659
  if (options.onboarding) output.onboarding = true;
2628
2660
  if (promptId) output.promptId = promptId;
2629
2661
  writeJson(output);
@@ -3840,6 +3872,8 @@ async function getSessionCommand(sessionId, options, command) {
3840
3872
  console.log(`Status: ${String(session.phase ?? session.status ?? "unknown")}`);
3841
3873
  if (session.repoUrl) console.log(`Repo: ${String(session.repoUrl)}`);
3842
3874
  if (session.title) console.log(`Title: ${String(session.title)}`);
3875
+ const resultLine = formatSessionResult(session);
3876
+ if (resultLine) console.log(resultLine);
3843
3877
  }
3844
3878
  async function sessionEventsCommand(sessionId, options, command) {
3845
3879
  const afterSequence = resolveSequenceOption(options.afterSequence, options.after, "--after-sequence", "--after");
@@ -4146,7 +4180,7 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
4146
4180
  applyColorEnvironment(getRuntimeOptions(actionCommand));
4147
4181
  });
4148
4182
  function addCreateOptions(cmd) {
4149
- 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) or claude_code").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--prompt-stdin", "Read prompt from stdin").option(
4183
+ 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) or claude_code").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--base-branch <branch>", "Base branch to create the session against (defaults to the repo default branch)").option("--prompt-stdin", "Read prompt from stdin").option(
4150
4184
  "--uploaded-file <path>",
4151
4185
  "Attach a local text file to the prompt as uploadedFiles; repeat for multiple files",
4152
4186
  collectUploadedFileOption
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.131",
3
+ "version": "0.1.133",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {