@lesscap/baton-cli 0.1.3 → 0.2.1

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 (2) hide show
  1. package/dist/baton.mjs +162 -180
  2. package/package.json +5 -2
package/dist/baton.mjs CHANGED
@@ -578,6 +578,7 @@ var sessionsClient = (baseUrl) => {
578
578
  resume: (id) => request(u(`/sessions/${id}/resume`), { method: "POST" }),
579
579
  stop: (id) => request(u(`/sessions/${id}/stop`), { method: "POST" }),
580
580
  clear: (id) => request(u(`/sessions/${id}/clear`), { method: "POST" }),
581
+ abort: (id) => request(u(`/sessions/${id}/abort`), { method: "POST" }),
581
582
  rename: (id, name) => request(u(`/sessions/${id}/rename`), { method: "POST", body: { name } }),
582
583
  listByProject: (projectId) => request(u(`/projects/${projectId}/sessions`), { method: "GET" }),
583
584
  get: (id) => request(u(`/sessions/${id}`), { method: "GET" }),
@@ -1035,6 +1036,22 @@ var resolveSession = async (client, projectId, handle) => {
1035
1036
  throw new Error(`session "${handle}" not found in project ${projectId}`);
1036
1037
  };
1037
1038
 
1039
+ // src/commands/session/abort.ts
1040
+ var sessionAbortCommand = defineCommand({
1041
+ meta: { name: "abort", description: "interrupt the in-flight turn (like Esc); keeps the session" },
1042
+ args: {
1043
+ session: { type: "positional", required: true, description: "session int id or name" },
1044
+ project: { type: "string", description: "project id (overrides .baton.json)" },
1045
+ ...common
1046
+ },
1047
+ run: async ({ args }) => {
1048
+ const c = clientFor(args);
1049
+ const handle = await resolveSession(c, resolveProjectId(args), args.session);
1050
+ const s = await c.sessions.abort(handle.id);
1051
+ console.log(renderOne(s, fmtSession, Boolean(args.json)));
1052
+ }
1053
+ });
1054
+
1038
1055
  // src/commands/session/clear.ts
1039
1056
  var sessionClearCommand = defineCommand({
1040
1057
  meta: { name: "clear", description: "reset the conversation context (keeps the session + worktree)" },
@@ -1637,34 +1654,15 @@ function getBaseURL() {
1637
1654
  }
1638
1655
 
1639
1656
  // src/session/runner.ts
1640
- import { spawn, spawnSync } from "node:child_process";
1657
+ import { spawnSync } from "node:child_process";
1658
+ import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
1641
1659
 
1642
1660
  // src/session/runner/log.ts
1643
- var claudeBin = () => process.env.BATON_CLAUDE_BIN ?? "claude";
1644
- var buildClaudeArgs = (claudeSessionId, text, resuming) => [
1645
- "--print",
1646
- resuming ? "--resume" : "--session-id",
1647
- claudeSessionId,
1648
- "--output-format",
1649
- "stream-json",
1650
- "--verbose",
1651
- "--dangerously-skip-permissions",
1652
- text
1653
- ];
1654
- var STDERR_TAIL_BYTES = 2048;
1655
- var tailBuffer = (cap = STDERR_TAIL_BYTES) => {
1656
- let buf = "";
1657
- return {
1658
- append(chunk) {
1659
- buf = (buf + chunk).slice(-cap);
1660
- },
1661
- toString() {
1662
- return buf;
1663
- }
1664
- };
1665
- };
1666
1661
  var maskedEnvKeys = (env) => env ? Object.keys(env).sort().join(", ") : "(none)";
1667
- var previewText = (text, max = 80) => text.length > max ? `${text.slice(0, max)}\u2026` : text;
1662
+
1663
+ // src/session/runner/sdk-env.ts
1664
+ var buildSdkEnv = (overlay) => overlay ? { ...process.env, ...overlay } : void 0;
1665
+ var claudeExecutable = () => process.env.BATON_CLAUDE_BIN;
1668
1666
 
1669
1667
  // src/session/runner/transcript.ts
1670
1668
  import { readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
@@ -1788,90 +1786,74 @@ var augmentPrompt = (text, relPaths, labels) => {
1788
1786
  ${text}` : header;
1789
1787
  };
1790
1788
 
1791
- // src/session/runner/spawn.ts
1792
- import { createInterface as createInterface2 } from "node:readline";
1793
- var spawnClaude = async (config, worker2, text, resuming, spawnImpl, log, envOverlay) => {
1794
- const childEnv = envOverlay ? { ...process.env, ...envOverlay } : process.env;
1795
- const bin = claudeBin();
1796
- const args = buildClaudeArgs(config.agentSessionId, text, resuming);
1797
- log(`[spawn] ${bin} ${args.slice(0, -1).join(" ")} -- "${previewText(text)}"`);
1798
- log(`[spawn] cwd: ${config.worktreePath}`);
1799
- log(`[spawn] runtime env keys: ${maskedEnvKeys(envOverlay)}`);
1800
- let child;
1801
- try {
1802
- child = spawnImpl(bin, args, {
1803
- cwd: config.worktreePath,
1804
- stdio: ["ignore", "pipe", "pipe"],
1805
- env: childEnv
1806
- });
1807
- } catch (err) {
1808
- const message = err instanceof Error ? err.message : String(err);
1809
- log(`[spawn] failed: ${message}`);
1810
- await worker2.emitEvent("turn_error", { message: `spawn failed: ${message}` });
1811
- return null;
1812
- }
1813
- if (!child.stdout) {
1814
- log("[spawn] no stdout from claude \u2014 aborting turn");
1815
- await worker2.emitEvent("turn_error", { message: "no stdout from claude" });
1816
- return null;
1817
- }
1818
- const tail = tailBuffer();
1819
- if (child.stderr) {
1820
- const errLines = createInterface2({ input: child.stderr });
1821
- void (async () => {
1822
- for await (const line of errLines) {
1823
- tail.append(`${line}
1824
- `);
1825
- log(`[claude] ${line}`);
1826
- }
1827
- })();
1828
- }
1829
- return { child, tail };
1789
+ // src/session/runner/query.ts
1790
+ import "@anthropic-ai/claude-agent-sdk";
1791
+ var startQuery = (config, text, resuming, queryFn, abortController, log, envOverlay) => {
1792
+ const env = buildSdkEnv(envOverlay);
1793
+ const exe = claudeExecutable();
1794
+ const options = {
1795
+ cwd: config.worktreePath,
1796
+ permissionMode: "bypassPermissions",
1797
+ includePartialMessages: false,
1798
+ abortController,
1799
+ stderr: (line) => log(`[claude] ${line.trimEnd()}`),
1800
+ ...resuming ? { resume: config.agentSessionId } : { sessionId: config.agentSessionId },
1801
+ ...env ? { env } : {},
1802
+ ...exe ? { pathToClaudeCodeExecutable: exe } : {}
1803
+ };
1804
+ return queryFn({ prompt: text, options });
1830
1805
  };
1831
1806
 
1832
1807
  // src/session/runner/stream.ts
1833
- import { createInterface as createInterface3 } from "node:readline";
1834
- var streamSdkEvents = async (stdout, worker2) => {
1835
- const rl = createInterface3({ input: stdout });
1836
- for await (const line of rl) {
1837
- const trimmed = line.trim();
1838
- if (!trimmed) continue;
1839
- let payload;
1840
- try {
1841
- payload = JSON.parse(trimmed);
1842
- } catch {
1843
- payload = { raw: trimmed };
1844
- }
1845
- await worker2.emitEvent("sdk_event", payload);
1808
+ var streamSdkEvents = async (messages, worker2) => {
1809
+ let result = null;
1810
+ for await (const message of messages) {
1811
+ await worker2.emitEvent("sdk_event", message);
1812
+ if (message.type === "result") result = toTurnResult(message);
1846
1813
  }
1814
+ return result;
1815
+ };
1816
+ var toTurnResult = (message) => {
1817
+ const rec = message;
1818
+ const subtype = typeof rec.subtype === "string" ? rec.subtype : "unknown";
1819
+ const isError = rec.is_error === true || subtype !== "success";
1820
+ const resultText = typeof rec.result === "string" ? rec.result : "";
1821
+ return { subtype, isError, resultText };
1847
1822
  };
1848
1823
 
1849
1824
  // src/session/runner/turn.ts
1850
- var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1851
1825
  var turnTimeoutMs = () => Number(process.env.BATON_TURN_TIMEOUT_MS) || 30 * 6e4;
1852
- var DRAIN_GRACE_MS = 3e3;
1853
- var waitExit = (child) => {
1854
- if (typeof child.exitCode === "number") return Promise.resolve(child.exitCode);
1855
- if (typeof child.signalCode === "string") return Promise.resolve(-1);
1856
- return new Promise((resolve) => child.once("exit", (code) => resolve(code ?? -1)));
1857
- };
1858
- var finalizeTurn = async (exitCode, tail, worker2, log) => {
1859
- log(`[exit] code=${exitCode}`);
1860
- const stderrTail = tail.toString();
1861
- if (exitCode !== 0) {
1826
+ var TIMEOUT = /* @__PURE__ */ Symbol("turn-timeout");
1827
+ var ABORTED = /* @__PURE__ */ Symbol("turn-aborted");
1828
+ var finalizeTurn = async (result, worker2, log) => {
1829
+ if (!result) {
1830
+ log("[exit] stream ended with no result");
1831
+ await worker2.emitEvent("turn_error", { message: "claude produced no result" });
1832
+ await worker2.emitEvent("turn_complete", {});
1833
+ return -1;
1834
+ }
1835
+ log(`[exit] subtype=${result.subtype} isError=${result.isError}`);
1836
+ if (result.isError) {
1862
1837
  await worker2.emitEvent("turn_error", {
1863
- message: `claude exited with code ${exitCode}`,
1864
- exitCode,
1865
- stderrTail
1838
+ message: result.resultText || `claude result: ${result.subtype}`,
1839
+ subtype: result.subtype
1866
1840
  });
1867
1841
  }
1868
- await worker2.emitEvent("turn_complete", {
1869
- exitCode,
1870
- ...stderrTail ? { stderrTail } : {}
1842
+ await worker2.emitEvent("turn_complete", { subtype: result.subtype });
1843
+ return result.isError ? 1 : 0;
1844
+ };
1845
+ var buildPrompt = async (config, rawText, attachments, log, fetchImpl) => {
1846
+ if (attachments.length === 0) return rawText;
1847
+ const relPaths = await materializeAttachments({
1848
+ worktreePath: config.worktreePath,
1849
+ serverBase: config.server,
1850
+ attachments,
1851
+ fetchImpl
1871
1852
  });
1872
- return exitCode;
1853
+ log(`[attach] downloaded ${relPaths.length} file(s) \u2192 ${config.worktreePath}/attachments`);
1854
+ return augmentPrompt(rawText, relPaths, labelAttachments(attachments));
1873
1855
  };
1874
- var runTurn = async (config, worker2, msg, resuming, spawnImpl, log = (m) => console.log(m), envOverlay, fetchImpl) => {
1856
+ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => console.log(m), envOverlay, fetchImpl, externalSignal) => {
1875
1857
  await worker2.emitEvent("turn_start", { messageId: msg.id });
1876
1858
  const payload = msg.payload;
1877
1859
  const rawText = typeof payload?.text === "string" ? payload.text : "";
@@ -1880,61 +1862,58 @@ var runTurn = async (config, worker2, msg, resuming, spawnImpl, log = (m) => con
1880
1862
  await worker2.emitEvent("turn_error", { message: "user_message missing text and attachments" });
1881
1863
  return -1;
1882
1864
  }
1883
- let text = rawText;
1884
- if (attachments.length > 0) {
1885
- try {
1886
- const relPaths = await materializeAttachments({
1887
- worktreePath: config.worktreePath,
1888
- serverBase: config.server,
1889
- attachments,
1890
- fetchImpl
1891
- });
1892
- log(`[attach] downloaded ${relPaths.length} file(s) \u2192 ${config.worktreePath}/attachments`);
1893
- text = augmentPrompt(rawText, relPaths, labelAttachments(attachments));
1894
- } catch (err) {
1895
- const message = err instanceof Error ? err.message : String(err);
1896
- await worker2.emitEvent("turn_error", { message: `attachment download failed: ${message}` });
1897
- return -1;
1898
- }
1865
+ let text;
1866
+ try {
1867
+ text = await buildPrompt(config, rawText, attachments, log, fetchImpl);
1868
+ } catch (err) {
1869
+ const message = err instanceof Error ? err.message : String(err);
1870
+ await worker2.emitEvent("turn_error", { message: `attachment download failed: ${message}` });
1871
+ return -1;
1899
1872
  }
1900
- const result = await spawnClaude(config, worker2, text, resuming, spawnImpl, log, envOverlay);
1901
- if (!result) return -1;
1902
- const { child, tail } = result;
1903
- const stdout = child.stdout;
1904
- const exited = waitExit(child);
1905
- let timedOut = false;
1873
+ const abort = new AbortController();
1906
1874
  const ceiling = turnTimeoutMs();
1907
- const watchdog = setTimeout(() => {
1908
- timedOut = true;
1909
- log(`[watchdog] turn exceeded ${ceiling}ms \u2014 killing claude`);
1910
- child.kill("SIGKILL");
1911
- }, ceiling);
1875
+ let timer;
1876
+ const timeout = new Promise((resolve) => {
1877
+ timer = setTimeout(() => resolve(TIMEOUT), ceiling);
1878
+ });
1879
+ const interrupted = new Promise((resolve) => {
1880
+ if (externalSignal?.aborted) resolve(ABORTED);
1881
+ else externalSignal?.addEventListener("abort", () => resolve(ABORTED), { once: true });
1882
+ });
1912
1883
  try {
1913
- const streaming = streamSdkEvents(stdout, worker2);
1914
- void streaming.catch(() => {
1884
+ const messages = startQuery(config, text, resuming, queryFn, abort, log, envOverlay);
1885
+ const consume = streamSdkEvents(messages, worker2);
1886
+ void consume.catch(() => {
1915
1887
  });
1916
- await Promise.race([streaming, exited.then(() => delay(DRAIN_GRACE_MS))]);
1917
- stdout.destroy();
1918
- const exitCode = await exited;
1919
- if (timedOut) {
1920
- await worker2.emitEvent("turn_error", {
1921
- message: `turn exceeded ${ceiling}ms \u2014 claude killed`,
1922
- stderrTail: tail.toString()
1923
- });
1888
+ const outcome = await Promise.race([consume, timeout, interrupted]);
1889
+ if (outcome === ABORTED) {
1890
+ log("[abort] interrupted by user");
1891
+ abort.abort();
1892
+ await worker2.emitEvent("turn_error", { message: "interrupted by user" });
1893
+ await worker2.emitEvent("turn_complete", {});
1894
+ return -1;
1924
1895
  }
1925
- return await finalizeTurn(exitCode, tail, worker2, log);
1896
+ if (outcome === TIMEOUT) {
1897
+ log(`[watchdog] turn exceeded ${ceiling}ms \u2014 aborting claude`);
1898
+ abort.abort();
1899
+ await worker2.emitEvent("turn_error", { message: `turn exceeded ${ceiling}ms \u2014 claude aborted` });
1900
+ await worker2.emitEvent("turn_complete", {});
1901
+ return -1;
1902
+ }
1903
+ return await finalizeTurn(outcome, worker2, log);
1926
1904
  } catch (err) {
1927
1905
  const message = err instanceof Error ? err.message : String(err);
1928
1906
  log(`[run] ${message}`);
1929
- await worker2.emitEvent("turn_error", { message, stderrTail: tail.toString() });
1907
+ await worker2.emitEvent("turn_error", { message });
1908
+ await worker2.emitEvent("turn_complete", {});
1930
1909
  return -1;
1931
1910
  } finally {
1932
- clearTimeout(watchdog);
1911
+ clearTimeout(timer);
1933
1912
  }
1934
1913
  };
1935
1914
 
1936
1915
  // src/session/runner.ts
1937
- var subscribeStream = (url, ESCtor, seen, log, onUserMessage, onReady) => {
1916
+ var subscribeStream = (url, ESCtor, seen, log, onUserMessage, onInterrupt, onReady) => {
1938
1917
  const es = new ESCtor(url);
1939
1918
  es.onopen = onReady;
1940
1919
  es.onmessage = (e) => {
@@ -1943,6 +1922,8 @@ var subscribeStream = (url, ESCtor, seen, log, onUserMessage, onReady) => {
1943
1922
  if (seen.has(ev.sequence)) return;
1944
1923
  seen.add(ev.sequence);
1945
1924
  if (ev.type === "user_message") onUserMessage(ev);
1925
+ else if (ev.type === "system" && ev.payload?.action === "interrupt")
1926
+ onInterrupt();
1946
1927
  } catch {
1947
1928
  }
1948
1929
  };
@@ -1964,9 +1945,9 @@ var restoreTty = () => {
1964
1945
  };
1965
1946
  var runDaemon = async (config, deps, signal) => {
1966
1947
  const log = deps.log ?? ((m) => console.log(`[#${config.sessionId} ${config.name}] ${m}`));
1967
- const sp = deps.spawnImpl ?? spawn;
1948
+ const qf = deps.queryFn ?? query2;
1968
1949
  const ESCtor = deps.eventSourceImpl ?? EventSource;
1969
- log(`bin: ${claudeBin()} cwd: ${config.worktreePath}`);
1950
+ log(`bin: ${claudeExecutable() ?? "(sdk bundled)"} cwd: ${config.worktreePath}`);
1970
1951
  log(`runtime env keys: ${maskedEnvKeys(deps.env)}`);
1971
1952
  const state = {
1972
1953
  firstSpawnDone: findTranscriptPath(config.agentSessionId) !== null,
@@ -1975,6 +1956,7 @@ var runDaemon = async (config, deps, signal) => {
1975
1956
  const pendingQueue = [];
1976
1957
  let busy = false;
1977
1958
  let lastActivity = Date.now();
1959
+ let currentTurn;
1978
1960
  const drain = async () => {
1979
1961
  if (busy) return;
1980
1962
  busy = true;
@@ -1984,16 +1966,20 @@ var runDaemon = async (config, deps, signal) => {
1984
1966
  if (!msg) break;
1985
1967
  const resuming = state.firstSpawnDone;
1986
1968
  log(`\u25B6 msg #${msg.sequence} (${resuming ? "resume" : "first"})`);
1969
+ const turnAbort = new AbortController();
1970
+ currentTurn = turnAbort;
1987
1971
  const code = await runTurn(
1988
1972
  config,
1989
1973
  deps.worker,
1990
1974
  msg,
1991
1975
  resuming,
1992
- sp,
1976
+ qf,
1993
1977
  log,
1994
1978
  deps.env,
1995
- deps.fetchImpl
1979
+ deps.fetchImpl,
1980
+ turnAbort.signal
1996
1981
  );
1982
+ currentTurn = void 0;
1997
1983
  state.firstSpawnDone = true;
1998
1984
  lastActivity = Date.now();
1999
1985
  log(`\u2714 msg #${msg.sequence}${code === 0 ? "" : ` (exit ${code})`}`);
@@ -2014,6 +2000,13 @@ var runDaemon = async (config, deps, signal) => {
2014
2000
  pendingQueue.push(ev);
2015
2001
  void drain();
2016
2002
  },
2003
+ () => {
2004
+ lastActivity = Date.now();
2005
+ if (currentTurn) {
2006
+ log("interrupt \u2014 aborting current turn");
2007
+ currentTurn.abort();
2008
+ }
2009
+ },
2017
2010
  // Report active only now that we're subscribed — before this, a message
2018
2011
  // sent right after spawn would be missed (no replay on the live stream).
2019
2012
  () => {
@@ -2194,6 +2187,7 @@ var session = defineCommand({
2194
2187
  resume: sessionResumeCommand,
2195
2188
  stop: sessionStopCommand,
2196
2189
  clear: sessionClearCommand,
2190
+ abort: sessionAbortCommand,
2197
2191
  rename: sessionRenameCommand,
2198
2192
  rm: sessionRmCommand,
2199
2193
  send: sessionSendCommand,
@@ -2345,11 +2339,12 @@ var task = defineCommand({
2345
2339
  import { hostname as osHostname } from "node:os";
2346
2340
 
2347
2341
  // src/worker/daemon.ts
2348
- import { spawn as spawn2 } from "node:child_process";
2342
+ import { spawn } from "node:child_process";
2349
2343
  import { randomUUID } from "node:crypto";
2350
2344
  import { existsSync as existsSync4 } from "node:fs";
2351
2345
  import { dirname as dirname2, join as join5 } from "node:path";
2352
2346
  import { fileURLToPath } from "node:url";
2347
+ import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
2353
2348
 
2354
2349
  // src/session/runner/title.ts
2355
2350
  var clip = (s, max) => s.length > max ? s.slice(0, max) : s;
@@ -2357,7 +2352,7 @@ var isDeclined = (title) => title.replace(/[^a-z]/gi, "").toUpperCase() === "NON
2357
2352
  var sanitizeTitle = (raw) => raw.replace(/[\r\n]+/g, " ").replace(/["'`]/g, "").replace(/^\s*(title|session)\s*[:-]\s*/i, "").replace(/\s+/g, " ").trim().slice(0, 40).trim();
2358
2353
  var TITLE_TIMEOUT_MS = 3e4;
2359
2354
  var generateTitle = async (input) => {
2360
- const { worktreePath, userText, assistantText, spawnImpl } = input;
2355
+ const { worktreePath, userText, assistantText, queryFn } = input;
2361
2356
  if (`${userText}${assistantText}`.trim().length < 4) return null;
2362
2357
  const exchange = [
2363
2358
  `User: ${clip(userText, 800)}`,
@@ -2365,44 +2360,31 @@ var generateTitle = async (input) => {
2365
2360
  ].filter(Boolean).join("\n");
2366
2361
  const prompt = `Reply with ONLY a 2-5 word title (no quotes, no punctuation) summarizing this coding session. If it is too short or generic to title meaningfully, reply with exactly NONE:
2367
2362
  ${exchange}`;
2368
- const out = await new Promise((resolve) => {
2369
- let buf = "";
2370
- let done = false;
2371
- const finish = (s) => {
2372
- if (!done) {
2373
- done = true;
2374
- resolve(s);
2375
- }
2376
- };
2377
- let child;
2378
- try {
2379
- child = spawnImpl(claudeBin(), ["--print", "--dangerously-skip-permissions", prompt], {
2363
+ const exe = claudeExecutable();
2364
+ const abort = new AbortController();
2365
+ const timer = setTimeout(() => abort.abort(), TITLE_TIMEOUT_MS);
2366
+ let out = "";
2367
+ try {
2368
+ const messages = queryFn({
2369
+ prompt,
2370
+ options: {
2380
2371
  cwd: worktreePath,
2381
- stdio: ["ignore", "pipe", "pipe"],
2382
- env: process.env
2383
- });
2384
- } catch {
2385
- return finish("");
2386
- }
2387
- const timer = setTimeout(() => {
2388
- try {
2389
- child.kill();
2390
- } catch {
2372
+ permissionMode: "bypassPermissions",
2373
+ allowedTools: [],
2374
+ includePartialMessages: false,
2375
+ abortController: abort,
2376
+ ...exe ? { pathToClaudeCodeExecutable: exe } : {}
2391
2377
  }
2392
- finish("");
2393
- }, TITLE_TIMEOUT_MS);
2394
- child.stdout?.on("data", (d) => {
2395
- buf += String(d);
2396
- });
2397
- child.on("exit", () => {
2398
- clearTimeout(timer);
2399
- finish(buf);
2400
- });
2401
- child.on("error", () => {
2402
- clearTimeout(timer);
2403
- finish("");
2404
2378
  });
2405
- });
2379
+ for await (const m of messages) {
2380
+ if (m.type === "result" && typeof m.result === "string")
2381
+ out = m.result;
2382
+ }
2383
+ } catch {
2384
+ return null;
2385
+ } finally {
2386
+ clearTimeout(timer);
2387
+ }
2406
2388
  const title = sanitizeTitle(out);
2407
2389
  return !title || isDeclined(title) ? null : title;
2408
2390
  };
@@ -2468,7 +2450,7 @@ var runWorkerDaemon = async (cfg, client, signal) => {
2468
2450
  };
2469
2451
  const spawnChild = (sessionId, worktreePath) => {
2470
2452
  if (children.has(sessionId)) return;
2471
- const child = spawn2(process.execPath, [binPath(), "session", "run", String(sessionId)], {
2453
+ const child = spawn(process.execPath, [binPath(), "session", "run", String(sessionId)], {
2472
2454
  detached: true,
2473
2455
  stdio: "inherit",
2474
2456
  env: {
@@ -2528,7 +2510,7 @@ var runWorkerDaemon = async (cfg, client, signal) => {
2528
2510
  worktreePath,
2529
2511
  userText: exchange.userText,
2530
2512
  assistantText: exchange.assistantText,
2531
- spawnImpl: spawn2
2513
+ queryFn: query3
2532
2514
  });
2533
2515
  if (!title) return log(`title #${sessionId}: not enough to title yet, skipping`);
2534
2516
  await client.sessions.setName(sessionId, title, cfg.apiToken);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesscap/baton-cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "baton CLI — register/run a worker, manage projects/requirements/tasks/sessions",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "baton": "tsx src/index.ts",
14
- "build": "esbuild src/index.ts --bundle --platform=node --format=esm --packages=bundle --banner:js=\"#!/usr/bin/env node\" --outfile=dist/baton.mjs && chmod +x dist/baton.mjs",
14
+ "build": "esbuild src/index.ts --bundle --platform=node --format=esm --packages=bundle --external:@anthropic-ai/claude-agent-sdk --banner:js=\"#!/usr/bin/env node\" --outfile=dist/baton.mjs && chmod +x dist/baton.mjs",
15
15
  "prepublishOnly": "pnpm build",
16
16
  "typecheck": "tsc --noEmit -p tsconfig.json",
17
17
  "test": "tsx --test 'src/**/*.test.ts'"
@@ -24,5 +24,8 @@
24
24
  "eventsource": "^4.1.0",
25
25
  "tsx": "^4.22.4",
26
26
  "typescript": "^6.0.3"
27
+ },
28
+ "dependencies": {
29
+ "@anthropic-ai/claude-agent-sdk": "^0.3.161"
27
30
  }
28
31
  }