@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.
Files changed (3) hide show
  1. package/README.md +15 -12
  2. package/dist/index.js +71 -7
  3. 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}` where status is `stopping` or `already_stopped`.
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
- Follow mode emits NDJSON, one replay event per line, until the session becomes idle.
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, use `create --wait` for the exit code, then read the session state:
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
- SESSION_ID=$(arcanist sessions create your-org/your-repo "fix the flaky test" --wait --json | jq -r .sessionId)
336
- for _ in {1..20}; do
337
- PR_URL=$(arcanist sessions get "$SESSION_ID" --json | jq -r '.session.prUrl // empty')
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 = stringField(session.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.session && typeof sessionPayload.session === "object" ? sessionPayload.session : 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(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.140",
3
+ "version": "0.1.142",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {