@livx.cc/agentx 0.94.38 → 0.95.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.
package/dist/cli.js CHANGED
@@ -1420,6 +1420,172 @@ var init_JailedFilesystem = __esm({
1420
1420
  }
1421
1421
  });
1422
1422
 
1423
+ // src/worktree.ts
1424
+ var worktree_exports = {};
1425
+ __export(worktree_exports, {
1426
+ cleanupWorktree: () => cleanupWorktree,
1427
+ enterWorktree: () => enterWorktree,
1428
+ exitWorktree: () => exitWorktree,
1429
+ findGitRoot: () => findGitRoot,
1430
+ getOrCreateWorktree: () => getOrCreateWorktree,
1431
+ getWorktreeSession: () => getWorktreeSession,
1432
+ validateWorktreeSlug: () => validateWorktreeSlug,
1433
+ worktreeBranchName: () => worktreeBranchName
1434
+ });
1435
+ import { spawnSync } from "child_process";
1436
+ import { symlinkSync, mkdirSync, statSync as statSync2, readFileSync, rmSync } from "fs";
1437
+ import { join as join3 } from "path";
1438
+ function validateWorktreeSlug(slug2) {
1439
+ if (!slug2) throw new Error("worktree slug must not be empty");
1440
+ if (slug2.length > MAX_SLUG_LENGTH) throw new Error(`worktree slug must be \u2264${MAX_SLUG_LENGTH} chars (got ${slug2.length})`);
1441
+ for (const seg of slug2.split("/")) {
1442
+ if (seg === "." || seg === "..") throw new Error(`worktree slug must not contain "." or ".." segments`);
1443
+ if (!VALID_SEGMENT.test(seg)) throw new Error(`worktree slug segment "${seg}" contains invalid characters (allowed: a-z A-Z 0-9 . _ -)`);
1444
+ }
1445
+ }
1446
+ function flattenSlug(slug2) {
1447
+ return slug2.replaceAll("/", "+");
1448
+ }
1449
+ function worktreeBranchName(slug2) {
1450
+ return `worktree-${flattenSlug(slug2)}`;
1451
+ }
1452
+ function worktreesDir(repoRoot) {
1453
+ return join3(repoRoot, ".claude", "worktrees");
1454
+ }
1455
+ function worktreePathFor(repoRoot, slug2) {
1456
+ return join3(worktreesDir(repoRoot), flattenSlug(slug2));
1457
+ }
1458
+ function git(args, cwd) {
1459
+ const r = spawnSync("git", args, {
1460
+ cwd,
1461
+ stdio: ["ignore", "pipe", "pipe"],
1462
+ env: { ...process.env, ...GIT_NO_PROMPT_ENV }
1463
+ });
1464
+ return { ok: r.status === 0, stdout: (r.stdout ?? "").toString().trim(), stderr: (r.stderr ?? "").toString().trim() };
1465
+ }
1466
+ function findGitRoot(from) {
1467
+ const r = git(["rev-parse", "--show-toplevel"], from);
1468
+ return r.ok ? r.stdout : null;
1469
+ }
1470
+ function readWorktreeHead(worktreePath) {
1471
+ try {
1472
+ const gitFile = readFileSync(join3(worktreePath, ".git"), "utf-8").trim();
1473
+ const m = gitFile.match(/^gitdir:\s*(.+)$/);
1474
+ if (!m) return null;
1475
+ const headPath = join3(m[1], "HEAD");
1476
+ const head = readFileSync(headPath, "utf-8").trim();
1477
+ const refMatch = head.match(/^ref:\s*(.+)$/);
1478
+ if (!refMatch) return head.length === 40 ? head : null;
1479
+ const refPath = join3(m[1], "..", refMatch[1]);
1480
+ try {
1481
+ return readFileSync(refPath, "utf-8").trim();
1482
+ } catch {
1483
+ }
1484
+ const r = git(["rev-parse", "HEAD"], worktreePath);
1485
+ return r.ok ? r.stdout : null;
1486
+ } catch {
1487
+ return null;
1488
+ }
1489
+ }
1490
+ function getOrCreateWorktree(repoRoot, slug2) {
1491
+ validateWorktreeSlug(slug2);
1492
+ const worktreePath = worktreePathFor(repoRoot, slug2);
1493
+ const branch = worktreeBranchName(slug2);
1494
+ const existingHead = readWorktreeHead(worktreePath);
1495
+ if (existingHead) return { worktreePath, branch, headCommit: existingHead, existed: true };
1496
+ mkdirSync(worktreesDir(repoRoot), { recursive: true });
1497
+ const defaultBranch = git(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoRoot);
1498
+ let baseBranch = defaultBranch.ok ? defaultBranch.stdout.replace(/^origin\//, "") : "HEAD";
1499
+ const originRef = `origin/${baseBranch}`;
1500
+ const hasOrigin = git(["rev-parse", "--verify", originRef], repoRoot);
1501
+ if (!hasOrigin.ok) {
1502
+ const fetched = git(["fetch", "origin", baseBranch], repoRoot);
1503
+ if (!fetched.ok) baseBranch = "HEAD";
1504
+ }
1505
+ const base = baseBranch === "HEAD" ? "HEAD" : originRef;
1506
+ const r = git(["worktree", "add", "-B", branch, worktreePath, base], repoRoot);
1507
+ if (!r.ok) throw new Error(`failed to create worktree: ${r.stderr}`);
1508
+ const headSha = git(["rev-parse", "HEAD"], worktreePath);
1509
+ symlinkDirs(repoRoot, worktreePath, ["node_modules", ".bun"]);
1510
+ return { worktreePath, branch, headCommit: headSha.stdout, existed: false, baseBranch };
1511
+ }
1512
+ function symlinkDirs(repoRoot, worktreePath, dirs) {
1513
+ for (const dir of dirs) {
1514
+ const src = join3(repoRoot, dir);
1515
+ const dst = join3(worktreePath, dir);
1516
+ try {
1517
+ statSync2(src);
1518
+ } catch {
1519
+ continue;
1520
+ }
1521
+ try {
1522
+ symlinkSync(src, dst, "dir");
1523
+ } catch (e) {
1524
+ if (e?.code !== "EEXIST") console.error(`[worktree] symlink ${dir}: ${e?.message ?? e}`);
1525
+ }
1526
+ }
1527
+ }
1528
+ function cleanupWorktree(repoRoot, slug2, opts) {
1529
+ validateWorktreeSlug(slug2);
1530
+ const worktreePath = worktreePathFor(repoRoot, slug2);
1531
+ const branch = worktreeBranchName(slug2);
1532
+ try {
1533
+ statSync2(worktreePath);
1534
+ } catch {
1535
+ return { removed: false, reason: "not found" };
1536
+ }
1537
+ if (!opts?.force) {
1538
+ const status = git(["status", "--porcelain", "-uno"], worktreePath);
1539
+ if (status.ok && status.stdout) return { removed: false, reason: "uncommitted changes" };
1540
+ }
1541
+ for (const dir of ["node_modules", ".bun"]) {
1542
+ try {
1543
+ rmSync(join3(worktreePath, dir));
1544
+ } catch {
1545
+ }
1546
+ }
1547
+ const rm = git(["worktree", "remove", "--force", worktreePath], repoRoot);
1548
+ if (!rm.ok) return { removed: false, reason: rm.stderr };
1549
+ git(["branch", "-D", branch], repoRoot);
1550
+ return { removed: true };
1551
+ }
1552
+ function getWorktreeSession() {
1553
+ return _session;
1554
+ }
1555
+ function enterWorktree(slug2, cwd) {
1556
+ const repoRoot = findGitRoot(cwd);
1557
+ if (!repoRoot) throw new Error("--worktree requires a git repository");
1558
+ const info = getOrCreateWorktree(repoRoot, slug2);
1559
+ process.chdir(info.worktreePath);
1560
+ _session = {
1561
+ originalCwd: cwd,
1562
+ worktreePath: info.worktreePath,
1563
+ slug: slug2,
1564
+ branch: info.branch,
1565
+ headCommit: info.headCommit
1566
+ };
1567
+ return _session;
1568
+ }
1569
+ function exitWorktree() {
1570
+ if (!_session) return null;
1571
+ const repoRoot = findGitRoot(_session.originalCwd);
1572
+ if (!repoRoot) return null;
1573
+ process.chdir(_session.originalCwd);
1574
+ const result = cleanupWorktree(repoRoot, _session.slug);
1575
+ _session = null;
1576
+ return result;
1577
+ }
1578
+ var VALID_SEGMENT, MAX_SLUG_LENGTH, GIT_NO_PROMPT_ENV, _session;
1579
+ var init_worktree = __esm({
1580
+ "src/worktree.ts"() {
1581
+ "use strict";
1582
+ VALID_SEGMENT = /^[a-zA-Z0-9._-]+$/;
1583
+ MAX_SLUG_LENGTH = 64;
1584
+ GIT_NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "" };
1585
+ _session = null;
1586
+ }
1587
+ });
1588
+
1423
1589
  // src/shell.sandbox.ts
1424
1590
  function writable(cwd, o, tmpDir) {
1425
1591
  const set = /* @__PURE__ */ new Set([cwd, "/tmp", "/private/tmp", "/private/var/folders", "/dev", ...tmpDir ? [tmpDir] : [], ...o.writePaths]);
@@ -1741,7 +1907,7 @@ var init_tools_shell = __esm({
1741
1907
 
1742
1908
  // cli/cli.ts
1743
1909
  import { createInterface } from "readline/promises";
1744
- import { existsSync as existsSync10, readFileSync as readFileSync6, appendFileSync, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readdirSync as readdirSync4, statSync as statSync3, unlinkSync as unlinkSync5 } from "fs";
1910
+ import { existsSync as existsSync10, readFileSync as readFileSync7, appendFileSync, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, readdirSync as readdirSync4, statSync as statSync4, unlinkSync as unlinkSync5 } from "fs";
1745
1911
  import { homedir as homedir9, tmpdir as tmpdir3 } from "os";
1746
1912
 
1747
1913
  // cli/clipboard.ts
@@ -1809,7 +1975,7 @@ function copyTextToClipboard(text, platform2 = process.platform) {
1809
1975
  }
1810
1976
 
1811
1977
  // cli/cli.ts
1812
- import { join as join12, resolve as resolve3, basename as basename2, extname, dirname as dirname4 } from "path";
1978
+ import { join as join13, resolve as resolve3, basename as basename2, extname, dirname as dirname4 } from "path";
1813
1979
  import { AIClient, listModels, listProviders, getProviderFromModel, getModelInfo, resolveModel, isModelSupported, disposeCursorSessions } from "ai.libx.js";
1814
1980
 
1815
1981
  // src/llm.ts
@@ -5736,6 +5902,7 @@ function base64ToBytes(b64) {
5736
5902
  }
5737
5903
 
5738
5904
  // src/index.ts
5905
+ init_worktree();
5739
5906
  import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
5740
5907
 
5741
5908
  // src/mcp.client.ts
@@ -6064,7 +6231,7 @@ var defaultPool = new McpPool();
6064
6231
  // cli/mcpOAuth.ts
6065
6232
  import { createServer } from "http";
6066
6233
  import { randomBytes, createHash as createHash2 } from "crypto";
6067
- import { readFileSync, writeFileSync as writeFileSync2, mkdirSync, existsSync } from "fs";
6234
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync } from "fs";
6068
6235
  import { dirname as dirname2 } from "path";
6069
6236
  var McpOAuthOptions = class {
6070
6237
  /** Where to persist tokens. Mode 0600. */
@@ -6092,13 +6259,13 @@ var McpOAuth = class {
6092
6259
  // -- store I/O ----------------------------------------------------------------
6093
6260
  load() {
6094
6261
  try {
6095
- return existsSync(this.options.storePath) ? JSON.parse(readFileSync(this.options.storePath, "utf8")) : {};
6262
+ return existsSync(this.options.storePath) ? JSON.parse(readFileSync2(this.options.storePath, "utf8")) : {};
6096
6263
  } catch {
6097
6264
  return {};
6098
6265
  }
6099
6266
  }
6100
6267
  save(store) {
6101
- mkdirSync(dirname2(this.options.storePath), { recursive: true });
6268
+ mkdirSync2(dirname2(this.options.storePath), { recursive: true });
6102
6269
  writeFileSync2(this.options.storePath, JSON.stringify(store, null, 2), { mode: 384 });
6103
6270
  }
6104
6271
  // -- ATTENDED: one-time authorization ----------------------------------------
@@ -6257,8 +6424,8 @@ function defaultOpenBrowser(url) {
6257
6424
  // cli/core.ts
6258
6425
  import { randomUUID } from "crypto";
6259
6426
  import { execFile as execFile2 } from "child_process";
6260
- import { resolve, basename, join as join3 } from "path";
6261
- import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
6427
+ import { resolve, basename, join as join4 } from "path";
6428
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
6262
6429
  import { platform, arch, release, userInfo, homedir as homedir2, tmpdir } from "os";
6263
6430
  init_tools_shell();
6264
6431
 
@@ -6303,7 +6470,7 @@ import { BodDB as BodDB2 } from "@bod.ee/db";
6303
6470
 
6304
6471
  // cli/util.ts
6305
6472
  init_logging();
6306
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
6473
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
6307
6474
  import { homedir } from "os";
6308
6475
  var log14 = forComponent("cli-util");
6309
6476
  function dotDirs(base, sub, opts = {}) {
@@ -6330,7 +6497,7 @@ function readJsonFile(path, fallback) {
6330
6497
  if (!existsSync2(path)) return fallback;
6331
6498
  let text;
6332
6499
  try {
6333
- text = readFileSync2(path, "utf8");
6500
+ text = readFileSync3(path, "utf8");
6334
6501
  } catch (e) {
6335
6502
  log14.debug(`readJsonFile(${path}) unreadable: ${e.message}`);
6336
6503
  return fallback;
@@ -6416,8 +6583,8 @@ async function buildAgent(o) {
6416
6583
  fs = mem;
6417
6584
  } else if (o.boddb) {
6418
6585
  const root = resolve(o.boddb);
6419
- mkdirSync2(root, { recursive: true });
6420
- const db = new BodDB2({ path: join3(root, "meta.db"), sweepInterval: 0, vfs: { storageRoot: join3(root, "files") } });
6586
+ mkdirSync3(root, { recursive: true });
6587
+ const db = new BodDB2({ path: join4(root, "meta.db"), sweepInterval: 0, vfs: { storageRoot: join4(root, "files") } });
6421
6588
  const bod = new BodDbFilesystem(db);
6422
6589
  const firstRun = (await bod.readDir("/").catch(() => [])).length === 0;
6423
6590
  await mkdirp(bod, cwd);
@@ -6576,10 +6743,10 @@ var trunc = (s, n) => (s == null ? "" : String(s).length > n ? String(s).slice(0
6576
6743
 
6577
6744
  // cli/voice.ts
6578
6745
  init_logging();
6579
- import { spawn as spawn2, spawnSync } from "child_process";
6580
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync2 } from "fs";
6746
+ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
6747
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
6581
6748
  import { homedir as homedir3 } from "os";
6582
- import { dirname as dirname3, join as join4 } from "path";
6749
+ import { dirname as dirname3, join as join5 } from "path";
6583
6750
  import { fileURLToPath } from "url";
6584
6751
  var log15 = forComponent("VoiceIO");
6585
6752
  var now4 = () => performance.now();
@@ -6622,10 +6789,10 @@ var Player = class {
6622
6789
  this.proc = null;
6623
6790
  }
6624
6791
  };
6625
- var nativeDir = () => join4(dirname3(fileURLToPath(import.meta.url)), "native");
6792
+ var nativeDir = () => join5(dirname3(fileURLToPath(import.meta.url)), "native");
6626
6793
  function detectFfmpegMic() {
6627
6794
  if (process.env.MIC_DEVICE) return process.env.MIC_DEVICE;
6628
- const out = spawnSync("ffmpeg", ["-f", "avfoundation", "-list_devices", "true", "-i", ""], { encoding: "utf8" }).stderr;
6795
+ const out = spawnSync2("ffmpeg", ["-f", "avfoundation", "-list_devices", "true", "-i", ""], { encoding: "utf8" }).stderr;
6629
6796
  const audio = out.slice(out.indexOf("audio devices"));
6630
6797
  const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
6631
6798
  const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
@@ -6636,10 +6803,10 @@ function detectFfmpegMic() {
6636
6803
  function detectedInputDevice() {
6637
6804
  if (process.platform !== "darwin") return null;
6638
6805
  let name = "";
6639
- if (spawnSync("which", ["SwitchAudioSource"]).status === 0) {
6640
- name = (spawnSync("SwitchAudioSource", ["-c", "-t", "input"], { encoding: "utf8" }).stdout || "").trim();
6806
+ if (spawnSync2("which", ["SwitchAudioSource"]).status === 0) {
6807
+ name = (spawnSync2("SwitchAudioSource", ["-c", "-t", "input"], { encoding: "utf8" }).stdout || "").trim();
6641
6808
  } else {
6642
- const out = spawnSync("system_profiler", ["SPAudioDataType"], { encoding: "utf8" }).stdout || "";
6809
+ const out = spawnSync2("system_profiler", ["SPAudioDataType"], { encoding: "utf8" }).stdout || "";
6643
6810
  let last = "";
6644
6811
  for (const ln of out.split("\n")) {
6645
6812
  const hdr = ln.match(/^\s{6,}(\S.*?):\s*$/);
@@ -6658,21 +6825,21 @@ function detectedInputDevice() {
6658
6825
  }
6659
6826
  function resolveAecBinary() {
6660
6827
  if (process.env.MIC_AEC === "0" || process.platform !== "darwin") return null;
6661
- const src = join4(nativeDir(), "mic-aec.swift");
6662
- const plist = join4(nativeDir(), "Info.plist");
6828
+ const src = join5(nativeDir(), "mic-aec.swift");
6829
+ const plist = join5(nativeDir(), "Info.plist");
6663
6830
  if (!existsSync4(src)) return null;
6664
- const cacheDir = join4(homedir3(), ".agent", "cache");
6665
- const bin = join4(cacheDir, "mic-aec");
6666
- if (existsSync4(bin) && statSync2(bin).mtimeMs >= statSync2(src).mtimeMs) return bin;
6667
- if (spawnSync("which", ["swiftc"]).status !== 0) return null;
6668
- mkdirSync3(cacheDir, { recursive: true });
6831
+ const cacheDir = join5(homedir3(), ".agent", "cache");
6832
+ const bin = join5(cacheDir, "mic-aec");
6833
+ if (existsSync4(bin) && statSync3(bin).mtimeMs >= statSync3(src).mtimeMs) return bin;
6834
+ if (spawnSync2("which", ["swiftc"]).status !== 0) return null;
6835
+ mkdirSync4(cacheDir, { recursive: true });
6669
6836
  log15.info("compiling AEC mic helper (first run)\u2026");
6670
- const build = spawnSync("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
6837
+ const build = spawnSync2("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
6671
6838
  if (build.status !== 0) {
6672
6839
  log15.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
6673
6840
  return null;
6674
6841
  }
6675
- const sign = spawnSync("codesign", ["-fs", "-", bin], { encoding: "utf8" });
6842
+ const sign = spawnSync2("codesign", ["-fs", "-", bin], { encoding: "utf8" });
6676
6843
  if (sign.status !== 0) {
6677
6844
  log15.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
6678
6845
  return null;
@@ -6692,7 +6859,7 @@ var NodeMicSource = class {
6692
6859
  if (this.bin) {
6693
6860
  this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
6694
6861
  } else {
6695
- if (spawnSync("which", ["ffmpeg"]).status !== 0) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
6862
+ if (spawnSync2("which", ["ffmpeg"]).status !== 0) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
6696
6863
  log15.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
6697
6864
  this.proc = spawn2(
6698
6865
  "ffmpeg",
@@ -6861,13 +7028,13 @@ var VoiceIO = class extends VoiceEngine {
6861
7028
 
6862
7029
  // cli/config.ts
6863
7030
  import { homedir as homedir4 } from "os";
6864
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
6865
- import { join as join5 } from "path";
7031
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
7032
+ import { join as join6 } from "path";
6866
7033
  import { pathToFileURL } from "url";
6867
7034
  var FILES = ["config.ts", "config.js", "config.mjs", "config.json"];
6868
7035
  async function loadFrom(dir) {
6869
7036
  for (const f of FILES) {
6870
- const p = join5(dir, ".agent", f);
7037
+ const p = join6(dir, ".agent", f);
6871
7038
  if (!existsSync5(p)) continue;
6872
7039
  try {
6873
7040
  const mod = await import(pathToFileURL(p).href, f.endsWith(".json") ? { with: { type: "json" } } : void 0);
@@ -6880,10 +7047,10 @@ async function loadFrom(dir) {
6880
7047
  return {};
6881
7048
  }
6882
7049
  function loadSettings(dir) {
6883
- const p = join5(dir, ".agent", "settings.json");
7050
+ const p = join6(dir, ".agent", "settings.json");
6884
7051
  if (!existsSync5(p)) return {};
6885
7052
  try {
6886
- const raw = JSON.parse(readFileSync3(p, "utf8"));
7053
+ const raw = JSON.parse(readFileSync4(p, "utf8"));
6887
7054
  const cfg = {};
6888
7055
  if (raw.mcpServers && typeof raw.mcpServers === "object") cfg.mcpServers = raw.mcpServers;
6889
7056
  if (raw.permissions && typeof raw.permissions === "object") cfg.permissions = raw.permissions;
@@ -6916,7 +7083,7 @@ async function loadConfig(cwd) {
6916
7083
  }
6917
7084
 
6918
7085
  // cli/hooks-config.ts
6919
- import { spawnSync as spawnSync2 } from "child_process";
7086
+ import { spawnSync as spawnSync3 } from "child_process";
6920
7087
  var log16 = forComponent("hooks");
6921
7088
  var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
6922
7089
  function ruleMatches(rule, toolName) {
@@ -6926,7 +7093,7 @@ function ruleMatches(rule, toolName) {
6926
7093
  }
6927
7094
  function runCmd(rule, env) {
6928
7095
  try {
6929
- const r = spawnSync2(rule.command, {
7096
+ const r = spawnSync3(rule.command, {
6930
7097
  shell: true,
6931
7098
  encoding: "utf8",
6932
7099
  timeout: rule.timeoutMs ?? 1e4,
@@ -7038,15 +7205,15 @@ function formatDiff(ops, opts = {}) {
7038
7205
  }
7039
7206
 
7040
7207
  // cli/session.ts
7041
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, readdirSync, renameSync, symlinkSync, unlinkSync, readlinkSync } from "fs";
7208
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync, renameSync, symlinkSync as symlinkSync2, unlinkSync, readlinkSync } from "fs";
7042
7209
  import { homedir as homedir5 } from "os";
7043
- import { join as join6 } from "path";
7210
+ import { join as join7 } from "path";
7044
7211
  var log17 = forComponent("session");
7045
- var globalDir = () => join6(homedir5(), ".agent", "sessions");
7212
+ var globalDir = () => join7(homedir5(), ".agent", "sessions");
7046
7213
  var SessionStore = class {
7047
7214
  dir;
7048
7215
  constructor(cwd) {
7049
- this.dir = join6(cwd, ".agent", "sessions");
7216
+ this.dir = join7(cwd, ".agent", "sessions");
7050
7217
  }
7051
7218
  /** Sortable, human-readable id: `YYYYMMDD-HHMMSS-<folder>`. */
7052
7219
  newId(now5 = Date.now(), cwd) {
@@ -7054,10 +7221,10 @@ var SessionStore = class {
7054
7221
  const p = (n, w = 2) => String(n).padStart(w, "0");
7055
7222
  const slug2 = (cwd ?? process.cwd()).split("/").pop()?.replace(/[^A-Za-z0-9_-]/g, "") || "session";
7056
7223
  let id = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}-${slug2}`;
7057
- if (existsSync6(this.dir) && existsSync6(join6(this.dir, `${id}.json`))) {
7224
+ if (existsSync6(this.dir) && existsSync6(join7(this.dir, `${id}.json`))) {
7058
7225
  for (let i = 2; i <= 99; i++) {
7059
7226
  const c = `${id}-${i}`;
7060
- if (!existsSync6(join6(this.dir, `${c}.json`))) {
7227
+ if (!existsSync6(join7(this.dir, `${c}.json`))) {
7061
7228
  id = c;
7062
7229
  break;
7063
7230
  }
@@ -7071,16 +7238,16 @@ var SessionStore = class {
7071
7238
  }
7072
7239
  save(data) {
7073
7240
  if (!this.safeId(data.meta.id)) throw new Error(`unsafe session id: ${data.meta.id}`);
7074
- if (!existsSync6(this.dir)) mkdirSync4(this.dir, { recursive: true });
7075
- const path = join6(this.dir, `${data.meta.id}.json`);
7241
+ if (!existsSync6(this.dir)) mkdirSync5(this.dir, { recursive: true });
7242
+ const path = join7(this.dir, `${data.meta.id}.json`);
7076
7243
  const tmp = `${path}.${process.pid}.tmp`;
7077
7244
  writeFileSync3(tmp, JSON.stringify(data));
7078
7245
  renameSync(tmp, path);
7079
7246
  try {
7080
7247
  const gd = globalDir();
7081
- if (!existsSync6(gd)) mkdirSync4(gd, { recursive: true });
7082
- const link2 = join6(gd, `${data.meta.id}.json`);
7083
- if (!existsSync6(link2)) symlinkSync(path, link2);
7248
+ if (!existsSync6(gd)) mkdirSync5(gd, { recursive: true });
7249
+ const link2 = join7(gd, `${data.meta.id}.json`);
7250
+ if (!existsSync6(link2)) symlinkSync2(path, link2);
7084
7251
  } catch {
7085
7252
  }
7086
7253
  }
@@ -7089,10 +7256,10 @@ var SessionStore = class {
7089
7256
  log17.debug(`rejecting unsafe session id: ${id}`);
7090
7257
  return void 0;
7091
7258
  }
7092
- const path = join6(this.dir, `${id}.json`);
7259
+ const path = join7(this.dir, `${id}.json`);
7093
7260
  if (!existsSync6(path)) return void 0;
7094
7261
  try {
7095
- return JSON.parse(readFileSync4(path, "utf8"));
7262
+ return JSON.parse(readFileSync5(path, "utf8"));
7096
7263
  } catch (e) {
7097
7264
  log17.debug(`unreadable session ${id} \u2014 ignoring`, e);
7098
7265
  return void 0;
@@ -7105,7 +7272,7 @@ var SessionStore = class {
7105
7272
  for (const f of readdirSync(this.dir)) {
7106
7273
  if (!f.endsWith(".json")) continue;
7107
7274
  try {
7108
- metas.push(JSON.parse(readFileSync4(join6(this.dir, f), "utf8")).meta);
7275
+ metas.push(JSON.parse(readFileSync5(join7(this.dir, f), "utf8")).meta);
7109
7276
  } catch (e) {
7110
7277
  log17.debug(`skipping unreadable session file ${f}`, e);
7111
7278
  }
@@ -7124,11 +7291,11 @@ var SessionStore = class {
7124
7291
  function globalSessionLoad(idOrPrefix) {
7125
7292
  const gd = globalDir();
7126
7293
  if (!existsSync6(gd)) return void 0;
7127
- const exact = join6(gd, `${idOrPrefix}.json`);
7294
+ const exact = join7(gd, `${idOrPrefix}.json`);
7128
7295
  if (existsSync6(exact)) {
7129
7296
  try {
7130
7297
  const target = readlinkSync(exact);
7131
- return JSON.parse(readFileSync4(target, "utf8"));
7298
+ return JSON.parse(readFileSync5(target, "utf8"));
7132
7299
  } catch {
7133
7300
  return void 0;
7134
7301
  }
@@ -7138,8 +7305,8 @@ function globalSessionLoad(idOrPrefix) {
7138
7305
  if (!f.endsWith(".json")) continue;
7139
7306
  const base = f.slice(0, -5);
7140
7307
  if (base.includes(idOrPrefix) || base.endsWith(idOrPrefix)) {
7141
- const target = readlinkSync(join6(gd, f));
7142
- return JSON.parse(readFileSync4(target, "utf8"));
7308
+ const target = readlinkSync(join7(gd, f));
7309
+ return JSON.parse(readFileSync5(target, "utf8"));
7143
7310
  }
7144
7311
  }
7145
7312
  } catch {
@@ -7153,15 +7320,15 @@ function globalSessionList() {
7153
7320
  for (const f of readdirSync(gd)) {
7154
7321
  if (!f.endsWith(".json")) continue;
7155
7322
  try {
7156
- const target = readlinkSync(join6(gd, f));
7323
+ const target = readlinkSync(join7(gd, f));
7157
7324
  if (!existsSync6(target)) {
7158
7325
  try {
7159
- unlinkSync(join6(gd, f));
7326
+ unlinkSync(join7(gd, f));
7160
7327
  } catch {
7161
7328
  }
7162
7329
  continue;
7163
7330
  }
7164
- metas.push(JSON.parse(readFileSync4(target, "utf8")).meta);
7331
+ metas.push(JSON.parse(readFileSync5(target, "utf8")).meta);
7165
7332
  } catch {
7166
7333
  }
7167
7334
  }
@@ -7273,18 +7440,18 @@ ${formatDiff(ops)}`);
7273
7440
  // cli/gitCheckpoints.ts
7274
7441
  import { execFile as execFile3 } from "child_process";
7275
7442
  import { promisify } from "util";
7276
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
7277
- import { join as join7, resolve as resolve2, sep as sep2 } from "path";
7443
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync7 } from "fs";
7444
+ import { join as join8, resolve as resolve2, sep as sep2 } from "path";
7278
7445
  var log18 = forComponent("checkpoints");
7279
7446
  var exec = promisify(execFile3);
7280
7447
  var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
7281
7448
  var ShadowRepo = class {
7282
7449
  // undefined = unprobed; false = git/this root unusable
7283
- constructor(workTree, gitDir, exclude, git) {
7450
+ constructor(workTree, gitDir, exclude, git2) {
7284
7451
  this.workTree = workTree;
7285
7452
  this.gitDir = gitDir;
7286
7453
  this.exclude = exclude;
7287
- this.git = git;
7454
+ this.git = git2;
7288
7455
  }
7289
7456
  workTree;
7290
7457
  gitDir;
@@ -7307,10 +7474,10 @@ var ShadowRepo = class {
7307
7474
  try {
7308
7475
  await exec(this.git, ["--version"]);
7309
7476
  if (!existsSync7(this.gitDir)) {
7310
- mkdirSync5(this.gitDir, { recursive: true });
7477
+ mkdirSync6(this.gitDir, { recursive: true });
7311
7478
  await this.run("init", "-q");
7312
7479
  }
7313
- writeFileSync4(join7(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
7480
+ writeFileSync4(join8(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
7314
7481
  this.ready = true;
7315
7482
  } catch (e) {
7316
7483
  log18.debug(`git checkpoints unavailable for ${this.workTree}`, e);
@@ -7425,7 +7592,7 @@ var GitCheckpoints = class {
7425
7592
  const abs = resolve2(d);
7426
7593
  if (abs === cwd || abs.startsWith(cwd + sep2)) continue;
7427
7594
  if (cwd.startsWith(abs + sep2)) continue;
7428
- out.push({ workTree: abs, gitDir: join7(abs, ".agent", "checkpoints.git") });
7595
+ out.push({ workTree: abs, gitDir: join8(abs, ".agent", "checkpoints.git") });
7429
7596
  }
7430
7597
  return out;
7431
7598
  }
@@ -7546,7 +7713,7 @@ var GitCheckpointsOptions = class {
7546
7713
  /** Real working tree to snapshot (the launch cwd). */
7547
7714
  workTree = process.cwd();
7548
7715
  /** Isolated git dir for the cwd shadow repo (kept out of the user's real .git). */
7549
- gitDir = join7(process.cwd(), ".agent", "checkpoints.git");
7716
+ gitDir = join8(process.cwd(), ".agent", "checkpoints.git");
7550
7717
  /** Extra mounted dirs (`--add-dir`); those outside cwd each get their own shadow repo. */
7551
7718
  addDirs = [];
7552
7719
  /** Conversation id → per-session restore-point ref. */
@@ -7560,9 +7727,9 @@ var GitCheckpointsOptions = class {
7560
7727
  };
7561
7728
 
7562
7729
  // cli/permissions.ts
7563
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
7730
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7 } from "fs";
7564
7731
  import { homedir as homedir6 } from "os";
7565
- import { join as join8 } from "path";
7732
+ import { join as join9 } from "path";
7566
7733
  var RULE_RE = /^(\w+)(?:\((.+)\))?$/;
7567
7734
  function parseOne(raw, decision) {
7568
7735
  const m = RULE_RE.exec(raw.trim());
@@ -7584,13 +7751,13 @@ function parsePermRules(perms) {
7584
7751
  function describeRule(r) {
7585
7752
  return `${r.decision.padEnd(5)} ${r.tool ?? "*"}${r.pathGlob ? `(${r.pathGlob})` : ""}`;
7586
7753
  }
7587
- var PERM_FILE = (cwd) => join8(cwd, ".agent", "permissions.json");
7754
+ var PERM_FILE = (cwd) => join9(cwd, ".agent", "permissions.json");
7588
7755
  function loadPersistedRules(cwd) {
7589
7756
  const j = readJsonFile(PERM_FILE(cwd), null);
7590
7757
  return j ? { allow: j.allow ?? [], ask: j.ask ?? [], deny: j.deny ?? [] } : {};
7591
7758
  }
7592
7759
  function loadClaudeSettings(cwd, home = homedir6()) {
7593
- const files = [join8(home, ".claude", "settings.json"), join8(cwd, ".claude", "settings.json"), join8(cwd, ".claude", "settings.local.json")];
7760
+ const files = [join9(home, ".claude", "settings.json"), join9(cwd, ".claude", "settings.json"), join9(cwd, ".claude", "settings.local.json")];
7594
7761
  let out = {};
7595
7762
  for (const p of files) {
7596
7763
  const perms = readJsonFile(p, null)?.permissions;
@@ -7603,7 +7770,7 @@ function persistRule(cwd, decision, ruleStr) {
7603
7770
  const list = cur[decision] ??= [];
7604
7771
  if (!list.includes(ruleStr)) list.push(ruleStr);
7605
7772
  try {
7606
- mkdirSync6(join8(cwd, ".agent"), { recursive: true });
7773
+ mkdirSync7(join9(cwd, ".agent"), { recursive: true });
7607
7774
  writeFileSync5(PERM_FILE(cwd), JSON.stringify(cur, null, 2) + "\n");
7608
7775
  } catch {
7609
7776
  }
@@ -7617,7 +7784,7 @@ function mergePerms(a, b) {
7617
7784
  }
7618
7785
  return Object.keys(out).length ? out : void 0;
7619
7786
  }
7620
- var TRUST_FILE = join8(homedir6(), ".agent", "trusted.json");
7787
+ var TRUST_FILE = join9(homedir6(), ".agent", "trusted.json");
7621
7788
  function isTrusted(cwd, file = TRUST_FILE) {
7622
7789
  const list = readJsonFile(file, []);
7623
7790
  return Array.isArray(list) && list.includes(cwd);
@@ -7627,7 +7794,7 @@ function trustDir(cwd, file = TRUST_FILE) {
7627
7794
  if (!Array.isArray(list)) list = [];
7628
7795
  if (!list.includes(cwd)) list.push(cwd);
7629
7796
  try {
7630
- mkdirSync6(join8(file, ".."), { recursive: true });
7797
+ mkdirSync7(join9(file, ".."), { recursive: true });
7631
7798
  writeFileSync5(file, JSON.stringify(list, null, 2) + "\n");
7632
7799
  } catch {
7633
7800
  }
@@ -7687,10 +7854,10 @@ function completePath(listDir, ref) {
7687
7854
 
7688
7855
  // cli/lineEditor.ts
7689
7856
  import { emitKeypressEvents } from "readline";
7690
- import { spawnSync as spawnSync3 } from "child_process";
7691
- import { writeFileSync as writeFileSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync2 } from "fs";
7857
+ import { spawnSync as spawnSync4 } from "child_process";
7858
+ import { writeFileSync as writeFileSync6, readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
7692
7859
  import { tmpdir as tmpdir2 } from "os";
7693
- import { join as join9 } from "path";
7860
+ import { join as join10 } from "path";
7694
7861
 
7695
7862
  // cli/bidi.ts
7696
7863
  var RTL_RE = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufdff\ufe70-\ufeff]/;
@@ -8767,14 +8934,14 @@ function createLineEditor(out) {
8767
8934
  function externalEdit(s) {
8768
8935
  const spec = process.env.VISUAL || process.env.EDITOR || "vi";
8769
8936
  const [cmd, ...cargs] = spec.split(" ").filter(Boolean);
8770
- const file = join9(tmpdir2(), `agentx-edit-${process.pid}-${Date.now()}.md`);
8937
+ const file = join10(tmpdir2(), `agentx-edit-${process.pid}-${Date.now()}.md`);
8771
8938
  try {
8772
8939
  writeFileSync6(file, s.buf);
8773
8940
  process.stdin.setRawMode(false);
8774
8941
  out.write("\x1B[?2004l");
8775
- const r = spawnSync3(cmd, [...cargs, file], { stdio: "inherit" });
8942
+ const r = spawnSync4(cmd, [...cargs, file], { stdio: "inherit" });
8776
8943
  if (r.status === 0) {
8777
- const text = readFileSync5(file, "utf8").replace(/\n$/, "");
8944
+ const text = readFileSync6(file, "utf8").replace(/\n$/, "");
8778
8945
  s.reset();
8779
8946
  if (text) s.insert(text);
8780
8947
  }
@@ -9196,10 +9363,10 @@ function readPlainLine() {
9196
9363
  }
9197
9364
 
9198
9365
  // cli/osScheduler.ts
9199
- import { spawnSync as spawnSync4 } from "child_process";
9200
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync8 } from "fs";
9366
+ import { spawnSync as spawnSync5 } from "child_process";
9367
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync8 } from "fs";
9201
9368
  import { homedir as homedir7 } from "os";
9202
- import { join as join10 } from "path";
9369
+ import { join as join11 } from "path";
9203
9370
  var log19 = forComponent("os-sched");
9204
9371
  var OsScheduler = class {
9205
9372
  options;
@@ -9207,13 +9374,13 @@ var OsScheduler = class {
9207
9374
  this.options = { ...new OsSchedulerOptions(), ...options };
9208
9375
  }
9209
9376
  get dir() {
9210
- return join10(this.options.home, ".agent", "sched");
9377
+ return join11(this.options.home, ".agent", "sched");
9211
9378
  }
9212
9379
  label(id) {
9213
9380
  return `cc.livx.agentx.sched-${id}`;
9214
9381
  }
9215
9382
  plistPath(id) {
9216
- return join10(this.options.home, "Library", "LaunchAgents", `${this.label(id)}.plist`);
9383
+ return join11(this.options.home, "Library", "LaunchAgents", `${this.label(id)}.plist`);
9217
9384
  }
9218
9385
  run(cmd, args, input) {
9219
9386
  return this.options.exec(cmd, args, input);
@@ -9224,16 +9391,16 @@ var OsScheduler = class {
9224
9391
  /** Register the job with the OS. Returns a human description of the mechanism used. Throws on failure. */
9225
9392
  schedule(spec) {
9226
9393
  if (!this.available()) throw new Error(`no OS scheduler on ${this.options.platform}`);
9227
- mkdirSync7(this.dir, { recursive: true });
9394
+ mkdirSync8(this.dir, { recursive: true });
9228
9395
  const oneOff = "at" in spec.trigger;
9229
9396
  const script = this.writeScript(spec, oneOff);
9230
9397
  const mechanism = this.options.platform === "darwin" ? this.scheduleDarwin(spec, script) : this.scheduleLinux(spec, script, oneOff);
9231
9398
  const meta = { ...spec, created: Date.now(), mechanism };
9232
- writeFileSync7(join10(this.dir, `${spec.id}.json`), JSON.stringify(meta, null, 2));
9399
+ writeFileSync7(join11(this.dir, `${spec.id}.json`), JSON.stringify(meta, null, 2));
9233
9400
  return mechanism;
9234
9401
  }
9235
9402
  cancel(id) {
9236
- const meta = readJsonFile(join10(this.dir, `${id}.json`), null);
9403
+ const meta = readJsonFile(join11(this.dir, `${id}.json`), null);
9237
9404
  if (!meta) return false;
9238
9405
  try {
9239
9406
  if (this.options.platform === "darwin") {
@@ -9266,7 +9433,7 @@ var OsScheduler = class {
9266
9433
  }
9267
9434
  for (const f of [`${id}.json`, `${id}.sh`]) {
9268
9435
  try {
9269
- unlinkSync3(join10(this.dir, f));
9436
+ unlinkSync3(join11(this.dir, f));
9270
9437
  } catch {
9271
9438
  }
9272
9439
  }
@@ -9274,19 +9441,19 @@ var OsScheduler = class {
9274
9441
  }
9275
9442
  list() {
9276
9443
  if (!existsSync8(this.dir)) return [];
9277
- return readdirSync2(this.dir).filter((f) => f.endsWith(".json")).map((f) => readJsonFile(join10(this.dir, f), null)).filter(Boolean);
9444
+ return readdirSync2(this.dir).filter((f) => f.endsWith(".json")).map((f) => readJsonFile(join11(this.dir, f), null)).filter(Boolean);
9278
9445
  }
9279
9446
  /** The per-job runner script: cd to the project, headless-resume the session, log, notify. */
9280
9447
  writeScript(spec, oneOff) {
9281
- const p = join10(this.dir, `${spec.id}.sh`);
9448
+ const p = join11(this.dir, `${spec.id}.sh`);
9282
9449
  const q2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
9283
- const cleanup = oneOff ? this.options.platform === "darwin" ? `launchctl remove ${this.label(spec.id)} 2>/dev/null; rm -f ${q2(this.plistPath(spec.id))} ${q2(join10(this.dir, `${spec.id}.json`))} ${q2(p)}
9284
- ` : `rm -f ${q2(join10(this.dir, `${spec.id}.json`))} ${q2(p)}
9450
+ const cleanup = oneOff ? this.options.platform === "darwin" ? `launchctl remove ${this.label(spec.id)} 2>/dev/null; rm -f ${q2(this.plistPath(spec.id))} ${q2(join11(this.dir, `${spec.id}.json`))} ${q2(p)}
9451
+ ` : `rm -f ${q2(join11(this.dir, `${spec.id}.json`))} ${q2(p)}
9285
9452
  ` : "";
9286
9453
  writeFileSync7(p, `#!/bin/sh
9287
9454
  # agentx scheduled job ${spec.id}${spec.label ? ` \u2014 ${spec.label}` : ""}
9288
9455
  cd ${q2(spec.cwd)} || exit 1
9289
- ${this.options.agentx} -p ${q2(spec.prompt)} --resume ${q2(spec.sessionId)} --yes >> ${q2(join10(this.dir, `${spec.id}.log`))} 2>&1
9456
+ ${this.options.agentx} -p ${q2(spec.prompt)} --resume ${q2(spec.sessionId)} --yes >> ${q2(join11(this.dir, `${spec.id}.log`))} 2>&1
9290
9457
  ${cleanup}`);
9291
9458
  chmodSync(p, 493);
9292
9459
  return p;
@@ -9323,7 +9490,7 @@ ${trigger}
9323
9490
  <key>RunAtLoad</key><false/>
9324
9491
  </dict></plist>
9325
9492
  `;
9326
- mkdirSync7(join10(this.options.home, "Library", "LaunchAgents"), { recursive: true });
9493
+ mkdirSync8(join11(this.options.home, "Library", "LaunchAgents"), { recursive: true });
9327
9494
  writeFileSync7(this.plistPath(spec.id), plist);
9328
9495
  this.run("launchctl", ["load", this.plistPath(spec.id)]);
9329
9496
  return `launchd:${this.label(spec.id)}`;
@@ -9361,7 +9528,7 @@ var OsSchedulerOptions = class {
9361
9528
  /** Injectable executor for tests. Returns stdout+stderr merged — `at` reports "job N" on STDERR,
9362
9529
  * and the job id is what atrm needs for cancel. Throws on non-zero exit. */
9363
9530
  exec = (cmd, args, input) => {
9364
- const r = spawnSync4(cmd, args, { input, encoding: "utf8" });
9531
+ const r = spawnSync5(cmd, args, { input, encoding: "utf8" });
9365
9532
  if (r.error) throw r.error;
9366
9533
  if (r.status !== 0) throw new Error(`${cmd} exited ${r.status}: ${r.stderr || r.stdout}`);
9367
9534
  return `${r.stdout ?? ""}${r.stderr ?? ""}`;
@@ -9376,12 +9543,12 @@ function routeTrigger(trigger, backendHint, now5 = Date.now()) {
9376
9543
  // cli/remoteTrigger.ts
9377
9544
  import { createServer as createServer2, createConnection } from "net";
9378
9545
  import { spawn as spawn3 } from "child_process";
9379
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
9546
+ import { existsSync as existsSync9, mkdirSync as mkdirSync9, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
9380
9547
  import { homedir as homedir8 } from "os";
9381
- import { join as join11 } from "path";
9548
+ import { join as join12 } from "path";
9382
9549
  var log20 = forComponent("remote-trigger");
9383
- var TRIGGER_DIR = () => join11(homedir8(), ".agent", "triggers");
9384
- var sockPath = (sessionId, dir = TRIGGER_DIR()) => join11(dir, `${sessionId}.sock`);
9550
+ var TRIGGER_DIR = () => join12(homedir8(), ".agent", "triggers");
9551
+ var sockPath = (sessionId, dir = TRIGGER_DIR()) => join12(dir, `${sessionId}.sock`);
9385
9552
  var TriggerServer = class {
9386
9553
  constructor(onTrigger) {
9387
9554
  this.onTrigger = onTrigger;
@@ -9393,7 +9560,7 @@ var TriggerServer = class {
9393
9560
  start(sessionId, dir = TRIGGER_DIR()) {
9394
9561
  this.stop();
9395
9562
  try {
9396
- mkdirSync8(dir, { recursive: true, mode: 448 });
9563
+ mkdirSync9(dir, { recursive: true, mode: 448 });
9397
9564
  const p = sockPath(sessionId, dir);
9398
9565
  try {
9399
9566
  unlinkSync4(p);
@@ -9547,7 +9714,7 @@ var err = (s) => process.stderr.write(s);
9547
9714
  var log21 = forComponent("cli");
9548
9715
  var VERSION = (() => {
9549
9716
  try {
9550
- return JSON.parse(readFileSync6(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
9717
+ return JSON.parse(readFileSync7(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
9551
9718
  } catch {
9552
9719
  return "?";
9553
9720
  }
@@ -9659,6 +9826,7 @@ function parseArgs(argv) {
9659
9826
  else if (x === "--reasoning") a.reasoning = parseReasoning(val(++i, x));
9660
9827
  else if (x === "--session-id") a.sessionId = val(++i, x);
9661
9828
  else if (x === "--fork-session") a.fork = true;
9829
+ else if (x === "--worktree" || x === "-w") a.worktree = val(++i, x);
9662
9830
  else if (x === "--max-steps") a.maxSteps = numFlag(argv[++i], "--max-steps");
9663
9831
  else if (x === "--max-tokens") a.maxTokens = numFlag(argv[++i], "--max-tokens");
9664
9832
  else if (x === "--timeout") a.timeoutMs = numFlag(argv[++i], "--timeout") * 1e3;
@@ -9671,6 +9839,7 @@ function parseArgs(argv) {
9671
9839
  if (!a.task && rest.length) a.task = rest.join(" ");
9672
9840
  if (a.boddb && a.vfs) throw new Error("--boddb and --sandbox are mutually exclusive (both are non-disk filesystems; pick one)");
9673
9841
  if (a.seed && !a.boddb) throw new Error("--seed only applies with --boddb (it seeds the database from cwd on first run)");
9842
+ if (a.worktree && (a.vfs || a.boddb)) throw new Error("--worktree is a real-disk concept \u2014 incompatible with --vfs/--boddb");
9674
9843
  if (a.duplex && (a.task || a.print)) throw new Error("--duplex is interactive-only (a conversational mode) \u2014 drop the task/-p");
9675
9844
  if (a.duplex && a.plan) throw new Error("--plan is not supported in --duplex (workers are non-interactive; a plan could never be approved)");
9676
9845
  if (a.voiceModel && !a.duplex) throw new Error("--voice-model only applies with --duplex");
@@ -9722,6 +9891,7 @@ Flags:
9722
9891
  --reasoning <e> extended thinking: off|low|medium|high or a token budget (anthropic/openai)
9723
9892
  --session-id <id> use this id for the session (instead of an auto-generated one)
9724
9893
  --fork-session with -r/-c: branch the resumed session into a new id (source left untouched)
9894
+ -w, --worktree <slug> run in an isolated git worktree (.claude/worktrees/<slug>); auto-cleaned on exit if no changes
9725
9895
  --max-steps <n> step budget (default 30)
9726
9896
  --max-tokens <n> token budget kill-switch (default 200000)
9727
9897
  --timeout <sec> wall-clock kill-switch (default 120)
@@ -9769,11 +9939,11 @@ function resolveModelOrNewest(model) {
9769
9939
  var ENV_KEY_ALIASES = { google: ["GEMINI_API_KEY"] };
9770
9940
  function loadInstallEnv() {
9771
9941
  let dir = dirname4(import.meta.path);
9772
- for (let i = 0; i < 5 && !existsSync10(join12(dir, "package.json")); i++) dir = dirname4(dir);
9942
+ for (let i = 0; i < 5 && !existsSync10(join13(dir, "package.json")); i++) dir = dirname4(dir);
9773
9943
  for (const name of [".env", ".env.local"]) {
9774
- const file = join12(dir, name);
9944
+ const file = join13(dir, name);
9775
9945
  if (!existsSync10(file)) continue;
9776
- for (const line of readFileSync6(file, "utf8").split("\n")) {
9946
+ for (const line of readFileSync7(file, "utf8").split("\n")) {
9777
9947
  const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
9778
9948
  if (!m || m[1] in process.env) continue;
9779
9949
  let val = m[2].trim();
@@ -10365,7 +10535,7 @@ var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg"
10365
10535
  function mentionRefs(line) {
10366
10536
  return [...line.matchAll(/(?:^|\s)@(?:"([^"]+)"|(\S+))/g)].map((m) => m[1] ?? m[2].replace(/[?!.,;:)\]}'">]+$/, "")).filter(Boolean);
10367
10537
  }
10368
- var untilde = (p) => p.startsWith("~/") ? join12(homedir9(), p.slice(2)) : p;
10538
+ var untilde = (p) => p.startsWith("~/") ? join13(homedir9(), p.slice(2)) : p;
10369
10539
  function readImageParts(cwd, line) {
10370
10540
  const refs = mentionRefs(line);
10371
10541
  const parts = [];
@@ -10374,7 +10544,7 @@ function readImageParts(cwd, line) {
10374
10544
  if (!mime) continue;
10375
10545
  const abs = ref.startsWith("~/") ? untilde(ref) : resolve3(cwd, ref);
10376
10546
  try {
10377
- parts.push(imagePart(`data:${mime};base64,${readFileSync6(abs).toString("base64")}`));
10547
+ parts.push(imagePart(`data:${mime};base64,${readFileSync7(abs).toString("base64")}`));
10378
10548
  } catch {
10379
10549
  }
10380
10550
  }
@@ -10388,7 +10558,7 @@ function pastePathClassifier(cwd) {
10388
10558
  if (!/^(\/|~\/|\.\/|\.\.\/)/.test(t)) return null;
10389
10559
  const abs = t.startsWith("~/") ? untilde(t) : resolve3(cwd, t);
10390
10560
  try {
10391
- if (!statSync3(abs).isFile()) return null;
10561
+ if (!statSync4(abs).isFile()) return null;
10392
10562
  } catch {
10393
10563
  return null;
10394
10564
  }
@@ -10605,24 +10775,24 @@ var AGENTS_MD_TEMPLATE = `# ${"${name}"}
10605
10775
  `;
10606
10776
  function initInstructions(cwd) {
10607
10777
  for (const f of ["AGENTS.md", "CLAUDE.md"]) {
10608
- if (existsSync10(join12(cwd, f))) {
10778
+ if (existsSync10(join13(cwd, f))) {
10609
10779
  err(yellow(` ${f} already exists \u2014 leaving it as-is
10610
10780
  `));
10611
10781
  return;
10612
10782
  }
10613
10783
  }
10614
- const path = join12(cwd, "AGENTS.md");
10784
+ const path = join13(cwd, "AGENTS.md");
10615
10785
  writeFileSync8(path, AGENTS_MD_TEMPLATE.replace("${name}", basename2(cwd)));
10616
10786
  err(green(` created ${path}
10617
10787
  `) + dim(" edit it, then it auto-loads into every run.\n"));
10618
10788
  }
10619
10789
  function persistSetting(cwd, key, value) {
10620
- const path = join12(cwd, ".agent", "settings.json");
10790
+ const path = join13(cwd, ".agent", "settings.json");
10621
10791
  try {
10622
- const obj = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
10792
+ const obj = existsSync10(path) ? JSON.parse(readFileSync7(path, "utf8")) : {};
10623
10793
  if (obj[key] === value) return;
10624
10794
  obj[key] = value;
10625
- mkdirSync9(dirname4(path), { recursive: true });
10795
+ mkdirSync10(dirname4(path), { recursive: true });
10626
10796
  writeFileSync8(path, JSON.stringify(obj, null, 2) + "\n");
10627
10797
  } catch (e) {
10628
10798
  err(yellow(` \u26A0 couldn't persist ${key} to ${path} \u2014 ${e?.message ?? e}
@@ -10655,7 +10825,7 @@ function installCancelGuards(mounted) {
10655
10825
  });
10656
10826
  }
10657
10827
  async function repl(args, ai, cfg, cwd) {
10658
- const oauth = new McpOAuth({ storePath: join12(cwd, ".agent", "mcp-auth.json") });
10828
+ const oauth = new McpOAuth({ storePath: join13(cwd, ".agent", "mcp-auth.json") });
10659
10829
  const mounted = await mountMcp(cfg, oauth);
10660
10830
  let mcpToolNames = [];
10661
10831
  const initialMcpTools = mcpAgentTools(mounted);
@@ -10863,7 +11033,7 @@ async function repl(args, ai, cfg, cwd) {
10863
11033
  quickLook: {
10864
11034
  branch: () => {
10865
11035
  try {
10866
- const head = readFileSync6(join12(cwd, ".git", "HEAD"), "utf8").trim();
11036
+ const head = readFileSync7(join13(cwd, ".git", "HEAD"), "utf8").trim();
10867
11037
  return head.startsWith("ref: refs/heads/") ? `branch: ${head.slice("ref: refs/heads/".length)}` : `detached HEAD at ${head.slice(0, 12)}`;
10868
11038
  } catch {
10869
11039
  return "not a git repository";
@@ -10992,9 +11162,9 @@ async function repl(args, ai, cfg, cwd) {
10992
11162
  const pendingImages = [];
10993
11163
  const bangContext = [];
10994
11164
  const grabClipboardAttachment = () => {
10995
- const dir = join12(tmpdir3(), "agentx-pasted");
11165
+ const dir = join13(tmpdir3(), "agentx-pasted");
10996
11166
  try {
10997
- mkdirSync9(dir, { recursive: true });
11167
+ mkdirSync10(dir, { recursive: true });
10998
11168
  } catch {
10999
11169
  }
11000
11170
  const img = grabClipboardImage(dir, String(Date.now()));
@@ -11045,7 +11215,7 @@ async function repl(args, ai, cfg, cwd) {
11045
11215
  err(dim(` \u23F0 ${scheduler.size} scheduled job(s) re-armed
11046
11216
  `));
11047
11217
  }
11048
- const checkpoints = args.vfs || args.boddb ? new CheckpointStack(agent.options.fs) : new GitCheckpoints({ workTree: cwd, gitDir: join12(cwd, ".agent", "checkpoints.git"), addDirs: args.addDirs, sessionId: session.meta.id });
11218
+ const checkpoints = args.vfs || args.boddb ? new CheckpointStack(agent.options.fs) : new GitCheckpoints({ workTree: cwd, gitDir: join13(cwd, ".agent", "checkpoints.git"), addDirs: args.addDirs, sessionId: session.meta.id });
11049
11219
  const cpHooks = checkpoints.hooks?.();
11050
11220
  if (cpHooks) {
11051
11221
  agent.options.hooks = composeHooks(agent.options.hooks, cpHooks);
@@ -11097,11 +11267,11 @@ ${lines.join("\n")}
11097
11267
  Added entries are loadable now via the Skill/SlashCommand tools; removed ones are gone even if still listed in the system prompt.
11098
11268
  </system-reminder>`;
11099
11269
  };
11100
- const histPath = join12(cwd, ".agent", "history");
11101
- const history = existsSync10(histPath) ? readFileSync6(histPath, "utf8").split("\n").filter(Boolean).reverse().slice(0, 500) : [];
11270
+ const histPath = join13(cwd, ".agent", "history");
11271
+ const history = existsSync10(histPath) ? readFileSync7(histPath, "utf8").split("\n").filter(Boolean).reverse().slice(0, 500) : [];
11102
11272
  const remember = (line) => {
11103
11273
  try {
11104
- mkdirSync9(join12(cwd, ".agent"), { recursive: true });
11274
+ mkdirSync10(join13(cwd, ".agent"), { recursive: true });
11105
11275
  appendFileSync(histPath, line + "\n");
11106
11276
  } catch (e) {
11107
11277
  log21.debug("history write failed", e);
@@ -11401,8 +11571,8 @@ ${task}`;
11401
11571
  const wasRaw = process.stdin.isTTY && process.stdin.isRaw;
11402
11572
  if (wasRaw) process.stdin.setRawMode(false);
11403
11573
  try {
11404
- const { spawnSync: spawnSync5 } = await import("child_process");
11405
- const r = spawnSync5("less", ["-R"], { input: text, stdio: ["pipe", "inherit", "inherit"] });
11574
+ const { spawnSync: spawnSync6 } = await import("child_process");
11575
+ const r = spawnSync6("less", ["-R"], { input: text, stdio: ["pipe", "inherit", "inherit"] });
11406
11576
  if (r.error) err(text);
11407
11577
  } finally {
11408
11578
  if (wasRaw) process.stdin.setRawMode(true);
@@ -11426,7 +11596,7 @@ ${task}`;
11426
11596
  cfgFiles.length ? ok(`config: ${cfgFiles.join(", ")}`) : warn("no .agent/config.* found (project or ~) \u2014 running on defaults");
11427
11597
  try {
11428
11598
  const probe = `${cwd}/.agent/sessions/.doctor-probe`;
11429
- mkdirSync9(`${cwd}/.agent/sessions`, { recursive: true });
11599
+ mkdirSync10(`${cwd}/.agent/sessions`, { recursive: true });
11430
11600
  writeFileSync8(probe, "ok");
11431
11601
  unlinkSync5(probe);
11432
11602
  ok(`session store writable (${cwd}/.agent/sessions)`);
@@ -11792,8 +11962,8 @@ ${task}`;
11792
11962
  const wasRaw = process.stdin.isTTY && process.stdin.isRaw;
11793
11963
  if (wasRaw) process.stdin.setRawMode(false);
11794
11964
  try {
11795
- const { spawnSync: spawnSync5 } = await import("child_process");
11796
- spawnSync5(ed, [idx], { stdio: "inherit" });
11965
+ const { spawnSync: spawnSync6 } = await import("child_process");
11966
+ spawnSync6(ed, [idx], { stdio: "inherit" });
11797
11967
  } finally {
11798
11968
  if (wasRaw) process.stdin.setRawMode(true);
11799
11969
  }
@@ -12160,10 +12330,10 @@ ${task}`;
12160
12330
  return;
12161
12331
  }
12162
12332
  const md = exportMarkdown(session.meta, shown);
12163
- const name = a[0] ? extname(a[0]) ? a[0] : a[0] + ".md" : join12(".agent", "exports", `${session.meta.id}.md`);
12333
+ const name = a[0] ? extname(a[0]) ? a[0] : a[0] + ".md" : join13(".agent", "exports", `${session.meta.id}.md`);
12164
12334
  const path = resolve3(cwd, name);
12165
12335
  try {
12166
- mkdirSync9(dirname4(path), { recursive: true });
12336
+ mkdirSync10(dirname4(path), { recursive: true });
12167
12337
  writeFileSync8(path, md);
12168
12338
  err(green(` \u2713 exported \u2192 ${path}
12169
12339
  `) + dim(` ${shown.length} message(s) \xB7 ${md.length} chars
@@ -12226,7 +12396,7 @@ ${task}`;
12226
12396
  if (dx) banner(dim(`\u25D1 duplex \u2014 reflex: ${dx.options.reflexModel} \xB7 act: ${work.model}${dx.options.thinkModel !== false ? ` \xB7 think: ${dx.options.thinkModel}` : ""} (real work runs in background tasks, re-voiced when done)`));
12227
12397
  const listDir = (absDir) => {
12228
12398
  try {
12229
- return readdirSync4(join12(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
12399
+ return readdirSync4(join13(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
12230
12400
  } catch (e) {
12231
12401
  log21.debug("completion readdir failed", absDir, e);
12232
12402
  return null;
@@ -12697,6 +12867,19 @@ async function main() {
12697
12867
  }
12698
12868
  if (args.debug) process.env.DEBUG ||= "*";
12699
12869
  if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
12870
+ let worktreeCleanup;
12871
+ if (args.worktree) {
12872
+ const { enterWorktree: enterWorktree2, exitWorktree: exitWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
12873
+ const session = enterWorktree2(args.worktree, resolve3(args.cwd ?? process.cwd()));
12874
+ err(dim(` \u2295 worktree: ${session.worktreePath} (branch ${session.branch})
12875
+ `));
12876
+ worktreeCleanup = () => {
12877
+ const r = exitWorktree2();
12878
+ if (r?.removed) err(dim(" \u2296 worktree cleaned up (no changes)\n"));
12879
+ else if (r && !r.removed) err(dim(` \u2295 worktree kept: ${r.reason}
12880
+ `));
12881
+ };
12882
+ }
12700
12883
  const cwd = resolve3(args.cwd ?? process.cwd());
12701
12884
  const cfg = await loadConfig(cwd);
12702
12885
  cfg.permissions = mergePerms(loadClaudeSettings(cwd), mergePerms(loadPersistedRules(cwd), cfg.permissions));
@@ -12742,7 +12925,7 @@ async function main() {
12742
12925
  }
12743
12926
  });
12744
12927
  if (args.task) {
12745
- const mounted = await mountMcp(cfg, new McpOAuth({ storePath: join12(cwd, ".agent", "mcp-auth.json") }));
12928
+ const mounted = await mountMcp(cfg, new McpOAuth({ storePath: join13(cwd, ".agent", "mcp-auth.json") }));
12746
12929
  const agent = await makeAgent(args, ai, cfg, mcpAgentTools(mounted));
12747
12930
  const store = new SessionStore(cwd);
12748
12931
  const session = startSession(args, store, agent, cwd);
@@ -12759,9 +12942,11 @@ async function main() {
12759
12942
  const obj = res ? jsonResult(res, session) : { ok: false, finishReason: "error", sessionId: session.meta.id };
12760
12943
  process.stdout.write(JSON.stringify(args.outputFormat === "stream-json" ? { type: "result", ...obj } : obj) + "\n");
12761
12944
  }
12945
+ worktreeCleanup?.();
12762
12946
  process.exit(ok ? 0 : 1);
12763
12947
  }
12764
12948
  await repl(args, ai, cfg, cwd);
12949
+ worktreeCleanup?.();
12765
12950
  }
12766
12951
  if (import.meta.main) main().catch((e) => {
12767
12952
  console.error(e?.message ?? e);