@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.
- package/README.md +19 -2
- package/dist/index.js +51 -17
- 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
|