@tryarcanist/cli 0.1.140 → 0.1.142
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 +15 -12
- package/dist/index.js +71 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,7 +132,7 @@ arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-ke
|
|
|
132
132
|
|
|
133
133
|
`--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.
|
|
134
134
|
|
|
135
|
-
`--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
|
+
`--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.
|
|
136
136
|
|
|
137
137
|
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.
|
|
138
138
|
|
|
@@ -144,9 +144,9 @@ Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the p
|
|
|
144
144
|
|
|
145
145
|
`--idempotency-key <uuid>` is for manually retrying a create request that may have reached the server. The CLI derives separate session and prompt idempotency keys from the provided value.
|
|
146
146
|
|
|
147
|
-
`--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.
|
|
147
|
+
`--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. The onboarding behavior is driven by the bridge's canonical onboarding playbook, not the prompt, so `--onboarding` needs no prompt: `arcanist sessions create <repo> --onboarding --wait`. Any prompt supplied alongside `--onboarding` is ignored. Team use; not part of the external API surface.
|
|
148
148
|
|
|
149
|
-
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.
|
|
149
|
+
JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, onboarding?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed. With `--wait`, JSON mode also includes best-effort result fields when available: `prUrl?`, `publishedBranch?`, and `lastBranch?`.
|
|
150
150
|
|
|
151
151
|
### `arcanist sessions send <session-id> [prompt]`
|
|
152
152
|
|
|
@@ -170,7 +170,7 @@ arcanist sessions stop abc123
|
|
|
170
170
|
arcanist sessions stop abc123 --json
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
JSON mode returns `{sessionId, status}`
|
|
173
|
+
JSON mode returns `{sessionId, status}`. `status` is the server stop status or, on a 409 stop-block, the block reason; it is an open set. Known values include `stopping`, `stopped`, `already_stopped`, `not_stoppable`, and lifecycle phase names.
|
|
174
174
|
|
|
175
175
|
### `arcanist sessions get <session-id>`
|
|
176
176
|
|
|
@@ -216,7 +216,13 @@ arcanist sessions events abc123 --follow --json
|
|
|
216
216
|
|
|
217
217
|
`--after` and `--before` are aliases for `--after-sequence` and `--before-sequence`.
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
JSON mode without `--follow` returns one JSON object from `/events/history`: `{events: [...], ...}`. Canonical events carry `phase`.
|
|
220
|
+
|
|
221
|
+
Follow mode emits NDJSON, one `{sequence, type, data}` object per line, until the session becomes idle. Actionable follow-mode signals:
|
|
222
|
+
|
|
223
|
+
- PR opened or updated: `pr_created` / `pr_updated`; URL at `data.prUrl`.
|
|
224
|
+
- Waiting for human input: `question`; question text at `data.question`.
|
|
225
|
+
- Terminal: `prompt_completed`, `prompt_failed`, and `session_idle`. Follow JSON mode suppresses lifecycle `status` frames, so key on these event names rather than `waiting_for_input`.
|
|
220
226
|
|
|
221
227
|
### `arcanist sessions transcript <session-id>`
|
|
222
228
|
|
|
@@ -329,15 +335,12 @@ arcanist sessions create your-org/your-repo "verify the new rate limiter is acti
|
|
|
329
335
|
|
|
330
336
|
For cron, prefer `ARCANIST_TOKEN` or a logged-in `~/.arcanist/config.json` over passing `--token` on the command line.
|
|
331
337
|
|
|
332
|
-
To capture the PR URL after a successful run,
|
|
338
|
+
To capture the PR URL after a successful run, read the best-effort result fields from `create --wait --json`:
|
|
333
339
|
|
|
334
340
|
```bash
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
[ -n "$PR_URL" ] && break
|
|
339
|
-
sleep 5
|
|
340
|
-
done
|
|
341
|
+
RESULT=$(arcanist sessions create your-org/your-repo "fix the flaky test" --wait --json)
|
|
342
|
+
PR_URL=$(jq -r '.prUrl // empty' <<<"$RESULT")
|
|
343
|
+
SESSION_ID=$(jq -r '.sessionId' <<<"$RESULT")
|
|
341
344
|
[ -n "$PR_URL" ] || { echo "PR URL not ready for $SESSION_ID" >&2; exit 1; }
|
|
342
345
|
```
|
|
343
346
|
|
package/dist/index.js
CHANGED
|
@@ -83,7 +83,7 @@ function parseApiErrorBody(body) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
function hintForHttpStatus(status) {
|
|
86
|
-
if (status === 401 || status === 403) return "Run `arcanist login` or set `ARCANIST_TOKEN`.";
|
|
86
|
+
if (status === 401 || status === 403) return "Run `arcanist auth login` or set `ARCANIST_TOKEN`.";
|
|
87
87
|
if (status === 404) return "List sessions with `arcanist sessions list`.";
|
|
88
88
|
if (status === 409) return "Check the current resource state and retry the command when it is ready.";
|
|
89
89
|
if (status >= 500) return "Retry later or check the control-plane logs.";
|
|
@@ -172,7 +172,7 @@ function saveConfig(config) {
|
|
|
172
172
|
function requireConfig(overrides) {
|
|
173
173
|
const config = loadConfig(overrides);
|
|
174
174
|
if (!config) {
|
|
175
|
-
throw new CliError("auth", "Not logged in.", { hint: "Run `arcanist login` or set `ARCANIST_TOKEN`." });
|
|
175
|
+
throw new CliError("auth", "Not logged in.", { hint: "Run `arcanist auth login` or set `ARCANIST_TOKEN`." });
|
|
176
176
|
}
|
|
177
177
|
return config;
|
|
178
178
|
}
|
|
@@ -2051,9 +2051,7 @@ function stringField(value) {
|
|
|
2051
2051
|
return typeof value === "string" && value.trim() ? value : null;
|
|
2052
2052
|
}
|
|
2053
2053
|
function formatSessionResult(session) {
|
|
2054
|
-
const prUrl =
|
|
2055
|
-
const publishedBranch = stringField(session.publishedBranch);
|
|
2056
|
-
const lastBranch = stringField(session.lastBranch);
|
|
2054
|
+
const { prUrl, publishedBranch, lastBranch } = extractSessionResultFields(session);
|
|
2057
2055
|
const baseBranch = stringField(session.baseBranch);
|
|
2058
2056
|
const branch = publishedBranch ?? (lastBranch && lastBranch !== baseBranch ? lastBranch : null);
|
|
2059
2057
|
if (prUrl) {
|
|
@@ -2064,6 +2062,16 @@ function formatSessionResult(session) {
|
|
|
2064
2062
|
}
|
|
2065
2063
|
return null;
|
|
2066
2064
|
}
|
|
2065
|
+
function extractSessionResultFields(session) {
|
|
2066
|
+
const result = {};
|
|
2067
|
+
const prUrl = stringField(session.prUrl);
|
|
2068
|
+
const publishedBranch = stringField(session.publishedBranch);
|
|
2069
|
+
const lastBranch = stringField(session.lastBranch);
|
|
2070
|
+
if (prUrl) result.prUrl = prUrl;
|
|
2071
|
+
if (publishedBranch) result.publishedBranch = publishedBranch;
|
|
2072
|
+
if (lastBranch) result.lastBranch = lastBranch;
|
|
2073
|
+
return result;
|
|
2074
|
+
}
|
|
2067
2075
|
function latestCompletedPromptResult(prompts) {
|
|
2068
2076
|
for (let i = prompts.length - 1; i >= 0; i--) {
|
|
2069
2077
|
if (prompts[i].status === "completed") return prompts[i].result;
|
|
@@ -2501,6 +2509,8 @@ function validateRepoUrl(url) {
|
|
|
2501
2509
|
return `Invalid repo URL: "${url}". Expected a GitHub URL (https://github.com/owner/repo) or owner/repo shorthand.`;
|
|
2502
2510
|
}
|
|
2503
2511
|
var PROMPT_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
2512
|
+
var RESULT_FETCH_ATTEMPTS = 5;
|
|
2513
|
+
var ONBOARDING_PROMPT = "Onboard this repository onto Arcanist.";
|
|
2504
2514
|
function selectCreatedPrompt(prompts, promptId) {
|
|
2505
2515
|
if (prompts.length === 0) return null;
|
|
2506
2516
|
if (promptId) {
|
|
@@ -2562,6 +2572,25 @@ async function waitForCreatedPrompt(sessionId, promptId, sessionUrl, pollInterva
|
|
|
2562
2572
|
);
|
|
2563
2573
|
}
|
|
2564
2574
|
}
|
|
2575
|
+
function unwrapSessionState(payload) {
|
|
2576
|
+
return payload.session && typeof payload.session === "object" ? payload.session : payload;
|
|
2577
|
+
}
|
|
2578
|
+
async function fetchSessionResultFields(config, sessionId) {
|
|
2579
|
+
const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
|
|
2580
|
+
return extractSessionResultFields(unwrapSessionState(sessionPayload));
|
|
2581
|
+
}
|
|
2582
|
+
async function fetchSettledSessionResultFields(config, sessionId, pollIntervalMs) {
|
|
2583
|
+
const effectivePollIntervalMs = Math.max(pollIntervalMs, MIN_WATCH_POLL_INTERVAL_MS);
|
|
2584
|
+
let latest = {};
|
|
2585
|
+
for (let attempt = 0; attempt < RESULT_FETCH_ATTEMPTS; attempt++) {
|
|
2586
|
+
latest = await fetchSessionResultFields(config, sessionId);
|
|
2587
|
+
if (latest.prUrl || latest.publishedBranch) return latest;
|
|
2588
|
+
if (attempt < RESULT_FETCH_ATTEMPTS - 1) {
|
|
2589
|
+
await sleep(effectivePollIntervalMs);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return latest;
|
|
2593
|
+
}
|
|
2565
2594
|
async function createCommand(repoUrl, promptArg, options, command) {
|
|
2566
2595
|
const runtime = getRuntimeOptions(command, options);
|
|
2567
2596
|
const config = requireConfig(runtime);
|
|
@@ -2582,7 +2611,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
2582
2611
|
);
|
|
2583
2612
|
}
|
|
2584
2613
|
}
|
|
2585
|
-
const prompt = await resolvePromptInput(promptArg, options);
|
|
2614
|
+
const prompt = options.onboarding ? ONBOARDING_PROMPT : await resolvePromptInput(promptArg, options);
|
|
2586
2615
|
const waitPollIntervalMs = options.wait ? parsePollInterval(options.pollInterval) : null;
|
|
2587
2616
|
const repoError = validateRepoUrl(repoUrl);
|
|
2588
2617
|
if (repoError) {
|
|
@@ -2615,6 +2644,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
2615
2644
|
});
|
|
2616
2645
|
const sessionId = sessionData.sessionId;
|
|
2617
2646
|
let promptId;
|
|
2647
|
+
let waitResultFields = {};
|
|
2618
2648
|
try {
|
|
2619
2649
|
const promptData = await apiFetch(
|
|
2620
2650
|
config,
|
|
@@ -2643,10 +2673,16 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
2643
2673
|
if (sessionData.sessionUrl) console.log(`URL: ${sessionData.sessionUrl}`);
|
|
2644
2674
|
}
|
|
2645
2675
|
await waitForCreatedPrompt(sessionId, promptId, sessionData.sessionUrl, waitPollIntervalMs, runtime, command);
|
|
2676
|
+
if (isJson(command, options)) {
|
|
2677
|
+
try {
|
|
2678
|
+
waitResultFields = await fetchSettledSessionResultFields(config, sessionId, waitPollIntervalMs);
|
|
2679
|
+
} catch {
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2646
2682
|
if (!isJson(command, options)) {
|
|
2647
2683
|
try {
|
|
2648
2684
|
const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
|
|
2649
|
-
const sessionState = sessionPayload
|
|
2685
|
+
const sessionState = unwrapSessionState(sessionPayload);
|
|
2650
2686
|
const resultLine = formatSessionResult(sessionState);
|
|
2651
2687
|
if (resultLine) console.log(resultLine);
|
|
2652
2688
|
} catch {
|
|
@@ -2664,12 +2700,14 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
2664
2700
|
if (startBranch) output.startBranch = startBranch;
|
|
2665
2701
|
if (options.onboarding) output.onboarding = true;
|
|
2666
2702
|
if (promptId) output.promptId = promptId;
|
|
2703
|
+
Object.assign(output, waitResultFields);
|
|
2667
2704
|
writeJson(output);
|
|
2668
2705
|
return;
|
|
2669
2706
|
}
|
|
2670
2707
|
if (options.wait) return;
|
|
2671
2708
|
console.log(`Session: ${sessionId}`);
|
|
2672
2709
|
if (sessionData.sessionUrl) console.log(`URL: ${sessionData.sessionUrl}`);
|
|
2710
|
+
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
2673
2711
|
}
|
|
2674
2712
|
|
|
2675
2713
|
// src/commands/egress.ts
|
|
@@ -4355,6 +4393,11 @@ Examples:
|
|
|
4355
4393
|
printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --json
|
|
4356
4394
|
printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --wait
|
|
4357
4395
|
arcanist sessions create https://github.com/org/repo - --json | jq -r .sessionId | xargs -I{} arcanist sessions events {} --follow --json
|
|
4396
|
+
|
|
4397
|
+
JSON:
|
|
4398
|
+
JSON mode returns {sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, onboarding?, promptId?}
|
|
4399
|
+
--wait --json also includes best-effort result fields when available: prUrl?, publishedBranch?, lastBranch?
|
|
4400
|
+
Follow async progress with: arcanist sessions events <session-id> --follow --json
|
|
4358
4401
|
`
|
|
4359
4402
|
);
|
|
4360
4403
|
}
|
|
@@ -4370,6 +4413,9 @@ Examples:
|
|
|
4370
4413
|
arcanist sessions send <session-id> "also update tests"
|
|
4371
4414
|
arcanist sessions send <session-id> "use this log" --uploaded-file failing.log
|
|
4372
4415
|
printf "also update tests" | arcanist sessions send <session-id> --prompt-stdin --json
|
|
4416
|
+
|
|
4417
|
+
JSON:
|
|
4418
|
+
JSON mode returns {sessionId, promptId?}
|
|
4373
4419
|
`
|
|
4374
4420
|
);
|
|
4375
4421
|
}
|
|
@@ -4404,6 +4450,10 @@ sessions.command("stop").description("Stop the active run for a session").argume
|
|
|
4404
4450
|
Examples:
|
|
4405
4451
|
arcanist sessions stop <session-id>
|
|
4406
4452
|
arcanist sessions stop <session-id> --json
|
|
4453
|
+
|
|
4454
|
+
JSON:
|
|
4455
|
+
JSON mode returns {sessionId, status}
|
|
4456
|
+
status is the server stop status or 409 stop-block reason. Known values include stopping, stopped, already_stopped, not_stoppable, and lifecycle phase names.
|
|
4407
4457
|
`
|
|
4408
4458
|
).action((sessionId, options, command) => stopCommand(sessionId, options, command));
|
|
4409
4459
|
sessions.command("get").description("Get session details").argument("<session-id>", "Session ID").addHelpText(
|
|
@@ -4412,6 +4462,9 @@ sessions.command("get").description("Get session details").argument("<session-id
|
|
|
4412
4462
|
Examples:
|
|
4413
4463
|
arcanist sessions get <session-id>
|
|
4414
4464
|
arcanist sessions get <session-id> --json
|
|
4465
|
+
|
|
4466
|
+
JSON:
|
|
4467
|
+
JSON mode returns the raw session payload. Result fields are at .session.prUrl, .session.publishedBranch, and .session.lastBranch.
|
|
4415
4468
|
`
|
|
4416
4469
|
).action((sessionId, options, command) => getSessionCommand(sessionId, options, command));
|
|
4417
4470
|
sessions.command("list").description("List sessions").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--search <query>", "Search session titles and repo metadata").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
|
|
@@ -4421,6 +4474,9 @@ Examples:
|
|
|
4421
4474
|
arcanist sessions list
|
|
4422
4475
|
arcanist sessions list --status idle --json
|
|
4423
4476
|
arcanist sessions list --search "architect agent" --repo owner/repo
|
|
4477
|
+
|
|
4478
|
+
JSON:
|
|
4479
|
+
JSON mode returns {sessions, nextCursor}
|
|
4424
4480
|
`
|
|
4425
4481
|
).action((options, command) => listSessionsCommand(options, command));
|
|
4426
4482
|
sessions.command("search").description("Search sessions by title and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
|
|
@@ -4429,6 +4485,9 @@ sessions.command("search").description("Search sessions by title and repo metada
|
|
|
4429
4485
|
Examples:
|
|
4430
4486
|
arcanist sessions search "architect agent"
|
|
4431
4487
|
arcanist sessions search "mcp debugging" --repo owner/repo --json
|
|
4488
|
+
|
|
4489
|
+
JSON:
|
|
4490
|
+
JSON mode returns {sessions, nextCursor}
|
|
4432
4491
|
`
|
|
4433
4492
|
).action((query, options, command) => searchSessionsCommand(query, options, command));
|
|
4434
4493
|
sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--after <n>", "Alias for --after-sequence").option("--before-sequence <n>", "Return events before this sequence").option("--before <n>", "Alias for --before-sequence").option("--prompt-id <id>", "Filter events by prompt ID").option("--limit <n>", "Maximum events to return").option("--follow", "Follow events until the session is idle").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
|
|
@@ -4439,6 +4498,11 @@ Examples:
|
|
|
4439
4498
|
arcanist sessions events <session-id> --after-sequence 540 --limit 250 --json
|
|
4440
4499
|
arcanist sessions events <session-id> --after 540 --limit 250 --json
|
|
4441
4500
|
arcanist sessions events <session-id> --follow --json
|
|
4501
|
+
|
|
4502
|
+
JSON:
|
|
4503
|
+
JSON mode without --follow returns one JSON object from /events/history: {events: [...], ...}; canonical events carry phase.
|
|
4504
|
+
--follow --json emits NDJSON, one {sequence, type, data} object per line.
|
|
4505
|
+
Actionable follow types: pr_created/pr_updated use data.prUrl; question uses data.question; terminal types are prompt_completed, prompt_failed, and session_idle.
|
|
4442
4506
|
`
|
|
4443
4507
|
).action((sessionId, options, command) => sessionEventsCommand(sessionId, options, command));
|
|
4444
4508
|
sessions.command("transcript").description("Render a session transcript").argument("<session-id>", "Session ID").addHelpText(
|