xtrm-tools 0.5.28 → 0.5.30

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 (31) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +19 -3
  3. package/README.md +13 -37
  4. package/cli/dist/index.cjs +311 -161
  5. package/cli/dist/index.cjs.map +1 -1
  6. package/cli/package.json +1 -1
  7. package/config/instructions/agents-top.md +1 -1
  8. package/config/instructions/claude-top.md +1 -1
  9. package/config/pi/extensions/beads/index.ts +68 -7
  10. package/config/pi/extensions/core/guard-rules.ts +0 -2
  11. package/config/pi/extensions/custom-footer/index.ts +5 -6
  12. package/hooks/beads-claim-sync.mjs +18 -6
  13. package/hooks/beads-gate-messages.mjs +5 -2
  14. package/hooks/beads-memory-gate.mjs +20 -7
  15. package/hooks/statusline.mjs +44 -8
  16. package/package.json +3 -2
  17. package/plugins/xtrm-tools/.claude-plugin/plugin.json +1 -1
  18. package/plugins/xtrm-tools/hooks/beads-claim-sync.mjs +18 -6
  19. package/plugins/xtrm-tools/hooks/beads-gate-messages.mjs +5 -2
  20. package/plugins/xtrm-tools/hooks/beads-memory-gate.mjs +20 -7
  21. package/plugins/xtrm-tools/hooks/statusline.mjs +44 -8
  22. package/plugins/xtrm-tools/skills/sync-docs/SKILL.md +57 -2
  23. package/plugins/xtrm-tools/skills/sync-docs/scripts/drift_detector.py +1 -1
  24. package/plugins/xtrm-tools/skills/sync-docs/scripts/validate_metadata.py +1 -1
  25. package/plugins/xtrm-tools/skills/xt-end/SKILL.md +4 -4
  26. package/plugins/xtrm-tools/skills/xt-merge/SKILL.md +190 -0
  27. package/skills/sync-docs/SKILL.md +57 -2
  28. package/skills/sync-docs/scripts/drift_detector.py +1 -1
  29. package/skills/sync-docs/scripts/validate_metadata.py +1 -1
  30. package/skills/xt-end/SKILL.md +4 -4
  31. package/skills/xt-merge/SKILL.md +190 -0
@@ -30319,10 +30319,10 @@ var require_stringify = __commonJS({
30319
30319
  replacer = null;
30320
30320
  indent2 = EMPTY;
30321
30321
  };
30322
- var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + LF + gap : two ? two.trimRight() + LF + gap : EMPTY;
30322
+ var join7 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + LF + gap : two ? two.trimRight() + LF + gap : EMPTY;
30323
30323
  var join_content = (inside, value, gap) => {
30324
30324
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent2, true);
30325
- return join6(comment, inside, gap);
30325
+ return join7(comment, inside, gap);
30326
30326
  };
30327
30327
  var array_stringify = (value, gap) => {
30328
30328
  const deeper_gap = gap + indent2;
@@ -30333,7 +30333,7 @@ var require_stringify = __commonJS({
30333
30333
  if (i !== 0) {
30334
30334
  inside += COMMA;
30335
30335
  }
30336
- const before = join6(
30336
+ const before = join7(
30337
30337
  after_comma,
30338
30338
  process_comments(value, BEFORE(i), deeper_gap),
30339
30339
  deeper_gap
@@ -30343,7 +30343,7 @@ var require_stringify = __commonJS({
30343
30343
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
30344
30344
  after_comma = process_comments(value, AFTER(i), deeper_gap);
30345
30345
  }
30346
- inside += join6(
30346
+ inside += join7(
30347
30347
  after_comma,
30348
30348
  process_comments(value, PREFIX_AFTER, deeper_gap),
30349
30349
  deeper_gap
@@ -30368,7 +30368,7 @@ var require_stringify = __commonJS({
30368
30368
  inside += COMMA;
30369
30369
  }
30370
30370
  first = false;
30371
- const before = join6(
30371
+ const before = join7(
30372
30372
  after_comma,
30373
30373
  process_comments(value, BEFORE(key), deeper_gap),
30374
30374
  deeper_gap
@@ -30378,7 +30378,7 @@ var require_stringify = __commonJS({
30378
30378
  after_comma = process_comments(value, AFTER(key), deeper_gap);
30379
30379
  };
30380
30380
  keys.forEach(iteratee);
30381
- inside += join6(
30381
+ inside += join7(
30382
30382
  after_comma,
30383
30383
  process_comments(value, PREFIX_AFTER, deeper_gap),
30384
30384
  deeper_gap
@@ -33827,8 +33827,8 @@ var init_boxen = __esm({
33827
33827
  });
33828
33828
 
33829
33829
  // src/index.ts
33830
- var import_node_fs6 = require("fs");
33831
- var import_node_path7 = require("path");
33830
+ var import_node_fs7 = require("fs");
33831
+ var import_node_path8 = require("path");
33832
33832
 
33833
33833
  // ../node_modules/commander/esm.mjs
33834
33834
  var import_index = __toESM(require_commander(), 1);
@@ -40806,12 +40806,111 @@ var import_path12 = __toESM(require("path"), 1);
40806
40806
  // src/commands/pi-install.ts
40807
40807
  var import_fs_extra11 = __toESM(require_lib2(), 1);
40808
40808
  var import_path11 = __toESM(require("path"), 1);
40809
- var import_node_child_process = require("child_process");
40809
+ var import_node_child_process2 = require("child_process");
40810
40810
  var import_node_os4 = require("os");
40811
40811
 
40812
40812
  // src/utils/pi-extensions.ts
40813
40813
  var import_fs_extra10 = __toESM(require_lib2(), 1);
40814
40814
  var import_path10 = __toESM(require("path"), 1);
40815
+ var import_node_child_process = require("child_process");
40816
+ async function listExtensionDirs(baseDir) {
40817
+ if (!await import_fs_extra10.default.pathExists(baseDir)) return [];
40818
+ const entries = await import_fs_extra10.default.readdir(baseDir, { withFileTypes: true });
40819
+ const extDirs = [];
40820
+ for (const entry of entries) {
40821
+ if (!entry.isDirectory()) continue;
40822
+ const extPath = import_path10.default.join(baseDir, entry.name);
40823
+ const pkgPath = import_path10.default.join(extPath, "package.json");
40824
+ if (await import_fs_extra10.default.pathExists(pkgPath)) {
40825
+ extDirs.push(entry.name);
40826
+ }
40827
+ }
40828
+ return extDirs.sort();
40829
+ }
40830
+ async function fileSha256(filePath) {
40831
+ const crypto2 = await import("crypto");
40832
+ const content = await import_fs_extra10.default.readFile(filePath);
40833
+ return crypto2.createHash("sha256").update(content).digest("hex");
40834
+ }
40835
+ async function extensionHash(extDir) {
40836
+ const pkgPath = import_path10.default.join(extDir, "package.json");
40837
+ const indexPath = import_path10.default.join(extDir, "index.ts");
40838
+ const hashes = [];
40839
+ if (await import_fs_extra10.default.pathExists(pkgPath)) {
40840
+ hashes.push(await fileSha256(pkgPath));
40841
+ }
40842
+ if (await import_fs_extra10.default.pathExists(indexPath)) {
40843
+ hashes.push(await fileSha256(indexPath));
40844
+ }
40845
+ return hashes.join(":");
40846
+ }
40847
+ async function diffPiExtensions(sourceDir, targetDir) {
40848
+ const sourceAbs = import_path10.default.resolve(sourceDir);
40849
+ const targetAbs = import_path10.default.resolve(targetDir);
40850
+ const sourceExts = await listExtensionDirs(sourceAbs);
40851
+ const missing = [];
40852
+ const stale = [];
40853
+ const upToDate = [];
40854
+ for (const extName of sourceExts) {
40855
+ const srcExtPath = import_path10.default.join(sourceAbs, extName);
40856
+ const dstExtPath = import_path10.default.join(targetAbs, extName);
40857
+ if (!await import_fs_extra10.default.pathExists(dstExtPath)) {
40858
+ missing.push(extName);
40859
+ continue;
40860
+ }
40861
+ const dstPkgPath = import_path10.default.join(dstExtPath, "package.json");
40862
+ if (!await import_fs_extra10.default.pathExists(dstPkgPath)) {
40863
+ missing.push(extName);
40864
+ continue;
40865
+ }
40866
+ const [srcHash, dstHash] = await Promise.all([
40867
+ extensionHash(srcExtPath),
40868
+ extensionHash(dstExtPath)
40869
+ ]);
40870
+ if (srcHash !== dstHash) {
40871
+ stale.push(extName);
40872
+ } else {
40873
+ upToDate.push(extName);
40874
+ }
40875
+ }
40876
+ return { missing, stale, upToDate };
40877
+ }
40878
+ function getInstalledPiPackages() {
40879
+ const result = (0, import_node_child_process.spawnSync)("pi", ["list"], { encoding: "utf8", stdio: "pipe" });
40880
+ if (result.status !== 0) return [];
40881
+ const output = result.stdout;
40882
+ const packages = [];
40883
+ let inUserPackages = false;
40884
+ for (const line of output.split("\n")) {
40885
+ if (line.includes("User packages:")) {
40886
+ inUserPackages = true;
40887
+ continue;
40888
+ }
40889
+ if (line.includes("Project packages:")) {
40890
+ inUserPackages = false;
40891
+ continue;
40892
+ }
40893
+ if (inUserPackages) {
40894
+ const match = line.match(/^\s+(npm:[\w\-/@]+)/);
40895
+ if (match) {
40896
+ packages.push(match[1]);
40897
+ }
40898
+ }
40899
+ }
40900
+ return packages.sort();
40901
+ }
40902
+ async function piPreCheck(sourceExtDir, targetExtDir, requiredPackages) {
40903
+ const extensions = await diffPiExtensions(sourceExtDir, targetExtDir);
40904
+ const installedPkgs = getInstalledPiPackages();
40905
+ const needed = requiredPackages.filter((pkg) => !installedPkgs.includes(pkg));
40906
+ return {
40907
+ extensions,
40908
+ packages: {
40909
+ installed: installedPkgs,
40910
+ needed
40911
+ }
40912
+ };
40913
+ }
40815
40914
  async function syncManagedPiExtensions({
40816
40915
  sourceDir,
40817
40916
  targetDir,
@@ -40819,25 +40918,40 @@ async function syncManagedPiExtensions({
40819
40918
  log
40820
40919
  }) {
40821
40920
  if (!await import_fs_extra10.default.pathExists(sourceDir)) return 0;
40921
+ const diff = await diffPiExtensions(sourceDir, targetDir);
40922
+ const toSync = [...diff.missing, ...diff.stale];
40923
+ if (toSync.length === 0) {
40924
+ if (log) {
40925
+ log(` \u2713 All ${diff.upToDate.length} extensions up-to-date, skipping sync`);
40926
+ }
40927
+ return diff.upToDate.length;
40928
+ }
40822
40929
  if (!dryRun) {
40823
- await import_fs_extra10.default.ensureDir(import_path10.default.dirname(targetDir));
40824
- await import_fs_extra10.default.copy(sourceDir, targetDir, { overwrite: true });
40930
+ await import_fs_extra10.default.ensureDir(targetDir);
40931
+ for (const extName of toSync) {
40932
+ const srcPath = import_path10.default.join(sourceDir, extName);
40933
+ const dstPath = import_path10.default.join(targetDir, extName);
40934
+ await import_fs_extra10.default.copy(srcPath, dstPath, { overwrite: true });
40935
+ if (log) {
40936
+ log(` ${diff.missing.includes(extName) ? "+" : "\u21BB"} ${extName}`);
40937
+ }
40938
+ }
40939
+ } else {
40940
+ if (log) {
40941
+ log(` [DRY RUN] would sync ${toSync.length} extensions: ${toSync.join(", ")}`);
40942
+ }
40825
40943
  }
40826
- const entries = await import_fs_extra10.default.readdir(sourceDir, { withFileTypes: true });
40827
- const managedPackages = entries.filter((entry) => entry.isDirectory()).length;
40828
40944
  if (log) {
40829
- if (dryRun) {
40830
- log(` [DRY RUN] sync extensions ${sourceDir} -> ${targetDir}`);
40831
- }
40832
- log(` Pi will auto-discover ${managedPackages} extension package(s) from ${targetDir}`);
40945
+ const action = dryRun ? "would sync" : "synced";
40946
+ log(` ${action} ${toSync.length} extension(s), skipped ${diff.upToDate.length} up-to-date`);
40833
40947
  }
40834
- return managedPackages;
40948
+ return diff.upToDate.length + toSync.length;
40835
40949
  }
40836
40950
 
40837
40951
  // src/commands/pi-install.ts
40838
40952
  var PI_AGENT_DIR = process.env.PI_AGENT_DIR || import_path11.default.join((0, import_node_os4.homedir)(), ".pi", "agent");
40839
40953
  function isPiInstalled() {
40840
- const r = (0, import_node_child_process.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
40954
+ const r = (0, import_node_child_process2.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
40841
40955
  return r.status === 0;
40842
40956
  }
40843
40957
  async function runPiInstall(dryRun = false) {
@@ -40848,7 +40962,7 @@ async function runPiInstall(dryRun = false) {
40848
40962
  if (!isPiInstalled()) {
40849
40963
  console.log(kleur_default.yellow(" pi not found \u2014 installing oh-pi globally..."));
40850
40964
  if (!dryRun) {
40851
- const r = (0, import_node_child_process.spawnSync)("npm", ["install", "-g", "oh-pi"], { stdio: "inherit" });
40965
+ const r = (0, import_node_child_process2.spawnSync)("npm", ["install", "-g", "oh-pi"], { stdio: "inherit" });
40852
40966
  if (r.status !== 0) {
40853
40967
  console.error(kleur_default.red(" \u2717 Failed to install oh-pi. Run: npm install -g oh-pi\n"));
40854
40968
  return;
@@ -40858,35 +40972,46 @@ async function runPiInstall(dryRun = false) {
40858
40972
  }
40859
40973
  console.log(t.success(" \u2713 pi installed"));
40860
40974
  } else {
40861
- const v = (0, import_node_child_process.spawnSync)("pi", ["--version"], { encoding: "utf8" });
40975
+ const v = (0, import_node_child_process2.spawnSync)("pi", ["--version"], { encoding: "utf8" });
40862
40976
  console.log(t.success(` \u2713 pi ${v.stdout.trim()} already installed`));
40863
40977
  }
40978
+ let packages = [];
40979
+ if (await import_fs_extra11.default.pathExists(schemaPath)) {
40980
+ const schema = await import_fs_extra11.default.readJson(schemaPath);
40981
+ packages = schema.packages;
40982
+ }
40864
40983
  const extensionsSrc = import_path11.default.join(piConfigDir, "extensions");
40865
40984
  const extensionsDst = import_path11.default.join(PI_AGENT_DIR, "extensions");
40985
+ const preCheck = await piPreCheck(extensionsSrc, extensionsDst, packages);
40986
+ const extTotal = preCheck.extensions.missing.length + preCheck.extensions.stale.length + preCheck.extensions.upToDate.length;
40987
+ const pkgTotal = packages.length;
40988
+ console.log(kleur_default.dim(`
40989
+ Pre-check:`));
40990
+ console.log(kleur_default.dim(` Extensions: ${preCheck.extensions.upToDate.length}/${extTotal} up-to-date, ${preCheck.extensions.stale.length} stale, ${preCheck.extensions.missing.length} missing`));
40991
+ console.log(kleur_default.dim(` Packages: ${preCheck.packages.installed.length}/${pkgTotal} installed, ${preCheck.packages.needed.length} needed`));
40866
40992
  const managedPackages = await syncManagedPiExtensions({
40867
40993
  sourceDir: extensionsSrc,
40868
40994
  targetDir: extensionsDst,
40869
40995
  dryRun,
40870
- log: (message) => console.log(kleur_default.dim(message))
40996
+ log: (message) => console.log(kleur_default.dim(` ${message}`))
40871
40997
  });
40872
- if (managedPackages > 0) {
40873
- console.log(t.success(` ${sym.ok} extensions synced (${managedPackages} packages)`));
40874
- }
40875
- if (!await import_fs_extra11.default.pathExists(schemaPath)) {
40876
- console.log(kleur_default.dim(" No install-schema.json found, skipping packages"));
40877
- return;
40878
- }
40879
- const schema = await import_fs_extra11.default.readJson(schemaPath);
40880
- for (const pkg of schema.packages) {
40881
- if (dryRun) {
40882
- console.log(kleur_default.cyan(` [DRY RUN] pi install ${pkg}`));
40883
- continue;
40884
- }
40885
- const r = (0, import_node_child_process.spawnSync)("pi", ["install", pkg], { stdio: "pipe", encoding: "utf8" });
40886
- if (r.status === 0) {
40887
- console.log(t.success(` ${sym.ok} ${pkg}`));
40998
+ if (packages.length > 0) {
40999
+ console.log(t.bold("\n npm Packages"));
41000
+ if (preCheck.packages.needed.length === 0) {
41001
+ console.log(kleur_default.dim(` \u2713 All ${packages.length} packages already installed`));
40888
41002
  } else {
40889
- console.log(kleur_default.yellow(` \u26A0 ${pkg} \u2014 install failed (run manually: pi install ${pkg})`));
41003
+ for (const pkg of preCheck.packages.needed) {
41004
+ if (dryRun) {
41005
+ console.log(kleur_default.cyan(` [DRY RUN] pi install ${pkg}`));
41006
+ continue;
41007
+ }
41008
+ const r = (0, import_node_child_process2.spawnSync)("pi", ["install", pkg], { stdio: "pipe", encoding: "utf8" });
41009
+ if (r.status === 0) {
41010
+ console.log(t.success(` ${sym.ok} ${pkg}`));
41011
+ } else {
41012
+ console.log(kleur_default.yellow(` \u26A0 ${pkg} \u2014 install failed (run manually: pi install ${pkg})`));
41013
+ }
41014
+ }
40890
41015
  }
40891
41016
  }
40892
41017
  console.log("");
@@ -41113,8 +41238,30 @@ async function cleanStalePrePluginFiles(repoRoot, dryRun) {
41113
41238
  console.log(t.success(" \u2713 No stale pre-plugin files found"));
41114
41239
  }
41115
41240
  }
41241
+ function warnIfOutdated() {
41242
+ try {
41243
+ const localPkg = JSON.parse(import_fs_extra12.default.readFileSync(import_path12.default.resolve(__dirname, "../package.json"), "utf8"));
41244
+ const result = (0, import_child_process3.spawnSync)("npm", ["show", "xtrm-tools", "version", "--json"], {
41245
+ encoding: "utf8",
41246
+ stdio: "pipe",
41247
+ timeout: 5e3
41248
+ });
41249
+ if (result.status !== 0 || !result.stdout) return;
41250
+ const npmVersion = JSON.parse(result.stdout.trim());
41251
+ const parse4 = (v) => v.split(".").map(Number);
41252
+ const [lMaj, lMin, lPat] = parse4(localPkg.version);
41253
+ const [rMaj, rMin, rPat] = parse4(npmVersion);
41254
+ const isNewer = rMaj > lMaj || rMaj === lMaj && rMin > lMin || rMaj === lMaj && rMin === lMin && rPat > lPat;
41255
+ if (isNewer) {
41256
+ console.log(t.warning(` \u26A0 npm has a newer version (${npmVersion} > ${localPkg.version})`));
41257
+ console.log(t.label(" Run: npm install -g xtrm-tools@latest"));
41258
+ }
41259
+ } catch {
41260
+ }
41261
+ }
41116
41262
  async function installPlugin(repoRoot, dryRun) {
41117
41263
  console.log(t.bold("\n \u2699 xtrm-tools (Claude Code plugin)"));
41264
+ warnIfOutdated();
41118
41265
  if (dryRun) {
41119
41266
  console.log(t.accent(" [DRY RUN] Would register xtrm-tools marketplace and install plugin\n"));
41120
41267
  await cleanStalePrePluginFiles(repoRoot, true);
@@ -41132,6 +41279,23 @@ async function installPlugin(repoRoot, dryRun) {
41132
41279
  console.log(t.warning(" \u21BB Restart Claude Code for the new plugin hooks to take effect"));
41133
41280
  await cleanStalePrePluginFiles(repoRoot, dryRun);
41134
41281
  await installOfficialClaudePlugins(false);
41282
+ installUserStatusLine(dryRun);
41283
+ }
41284
+ function installUserStatusLine(dryRun) {
41285
+ try {
41286
+ const scriptPath = import_path12.default.resolve(__dirname, "..", "..", "hooks", "statusline.mjs");
41287
+ if (!import_fs_extra12.default.existsSync(scriptPath)) return;
41288
+ const settingsPath = import_path12.default.join(import_os5.default.homedir(), ".claude", "settings.json");
41289
+ const settings = import_fs_extra12.default.existsSync(settingsPath) ? JSON.parse(import_fs_extra12.default.readFileSync(settingsPath, "utf8")) : {};
41290
+ if (dryRun) {
41291
+ console.log(t.accent(` [DRY RUN] Would write statusLine \u2192 ${scriptPath}`));
41292
+ return;
41293
+ }
41294
+ settings.statusLine = { type: "command", command: `node ${scriptPath}`, padding: 1 };
41295
+ import_fs_extra12.default.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
41296
+ console.log(t.success(` \u2713 statusLine registered in ~/.claude/settings.json`));
41297
+ } catch {
41298
+ }
41135
41299
  }
41136
41300
  function createInstallAllCommand() {
41137
41301
  return new Command("all").description("[deprecated] Use xtrm install").option("--dry-run", "Preview changes without making any modifications", false).option("-y, --yes", "Skip confirmation prompts", false).option("--no-mcp", "Skip MCP server registration", false).option("--force", "Overwrite locally drifted files", false).action(async (_opts) => {
@@ -41314,18 +41478,18 @@ function createInstallCommand() {
41314
41478
  }
41315
41479
 
41316
41480
  // src/commands/claude.ts
41317
- var import_node_child_process3 = require("child_process");
41481
+ var import_node_child_process4 = require("child_process");
41318
41482
 
41319
41483
  // src/utils/worktree-session.ts
41320
41484
  var import_node_path5 = __toESM(require("path"), 1);
41321
- var import_node_child_process2 = require("child_process");
41485
+ var import_node_child_process3 = require("child_process");
41322
41486
  var import_node_fs4 = require("fs");
41323
41487
  var import_node_os5 = require("os");
41324
41488
  function randomSlug(len = 4) {
41325
41489
  return Math.random().toString(36).slice(2, 2 + len);
41326
41490
  }
41327
41491
  function gitRepoRoot(cwd) {
41328
- const r = (0, import_node_child_process2.spawnSync)("git", ["rev-parse", "--show-toplevel"], {
41492
+ const r = (0, import_node_child_process3.spawnSync)("git", ["rev-parse", "--show-toplevel"], {
41329
41493
  cwd,
41330
41494
  stdio: "pipe",
41331
41495
  encoding: "utf8"
@@ -41364,7 +41528,7 @@ async function launchWorktreeSession(opts) {
41364
41528
  console.log(kleur_default.dim(` worktree: ${worktreePath}`));
41365
41529
  console.log(kleur_default.dim(` branch: ${branchName}
41366
41530
  `));
41367
- const bdResult = (0, import_node_child_process2.spawnSync)("bd", ["worktree", "create", worktreePath, "--branch", branchName], {
41531
+ const bdResult = (0, import_node_child_process3.spawnSync)("bd", ["worktree", "create", worktreePath, "--branch", branchName], {
41368
41532
  cwd: repoRoot,
41369
41533
  stdio: "inherit"
41370
41534
  });
@@ -41372,12 +41536,12 @@ async function launchWorktreeSession(opts) {
41372
41536
  if (bdResult.status !== 0 && !bdResult.error) {
41373
41537
  console.log(kleur_default.dim(" beads: no database found, creating worktree without redirect"));
41374
41538
  }
41375
- const branchExists = (0, import_node_child_process2.spawnSync)("git", ["rev-parse", "--verify", branchName], {
41539
+ const branchExists = (0, import_node_child_process3.spawnSync)("git", ["rev-parse", "--verify", branchName], {
41376
41540
  cwd: repoRoot,
41377
41541
  stdio: "pipe"
41378
41542
  }).status === 0;
41379
41543
  const gitArgs = branchExists ? ["worktree", "add", worktreePath, branchName] : ["worktree", "add", "-b", branchName, worktreePath];
41380
- const gitResult = (0, import_node_child_process2.spawnSync)("git", gitArgs, { cwd: repoRoot, stdio: "inherit" });
41544
+ const gitResult = (0, import_node_child_process3.spawnSync)("git", gitArgs, { cwd: repoRoot, stdio: "inherit" });
41381
41545
  if (gitResult.status !== 0) {
41382
41546
  console.error(kleur_default.red(`
41383
41547
  \u2717 Failed to create worktree at ${worktreePath}
@@ -41404,7 +41568,7 @@ async function launchWorktreeSession(opts) {
41404
41568
  }
41405
41569
  const runtimeCmd = runtime === "claude" ? "claude" : "pi";
41406
41570
  const runtimeArgs = runtime === "claude" ? ["--dangerously-skip-permissions"] : [];
41407
- const launchResult = (0, import_node_child_process2.spawnSync)(runtimeCmd, runtimeArgs, {
41571
+ const launchResult = (0, import_node_child_process3.spawnSync)(runtimeCmd, runtimeArgs, {
41408
41572
  cwd: worktreePath,
41409
41573
  stdio: "inherit"
41410
41574
  });
@@ -41427,14 +41591,14 @@ function createClaudeCommand() {
41427
41591
  cmd.command("status").description("Show Claude CLI version, plugin status, and hook wiring").action(async () => {
41428
41592
  console.log(t.bold("\n Claude Code Status\n"));
41429
41593
  try {
41430
- const version3 = (0, import_node_child_process3.execSync)("claude --version", { encoding: "utf8", stdio: "pipe" }).trim();
41594
+ const version3 = (0, import_node_child_process4.execSync)("claude --version", { encoding: "utf8", stdio: "pipe" }).trim();
41431
41595
  console.log(t.success(` \u2713 claude CLI: ${version3}`));
41432
41596
  } catch {
41433
41597
  console.log(kleur_default.red(" \u2717 claude CLI not found"));
41434
41598
  console.log("");
41435
41599
  return;
41436
41600
  }
41437
- const listResult = (0, import_node_child_process3.spawnSync)("claude", ["plugin", "list"], { encoding: "utf8", stdio: "pipe" });
41601
+ const listResult = (0, import_node_child_process4.spawnSync)("claude", ["plugin", "list"], { encoding: "utf8", stdio: "pipe" });
41438
41602
  const pluginOutput = listResult.stdout ?? "";
41439
41603
  if (pluginOutput.includes("xtrm-tools")) {
41440
41604
  console.log(t.success(" \u2713 xtrm-tools plugin installed"));
@@ -41442,7 +41606,7 @@ function createClaudeCommand() {
41442
41606
  console.log(kleur_default.yellow(" \u26A0 xtrm-tools plugin not installed \u2014 run: xt claude install"));
41443
41607
  }
41444
41608
  try {
41445
- (0, import_node_child_process3.execSync)("bd --version", { stdio: "ignore" });
41609
+ (0, import_node_child_process4.execSync)("bd --version", { stdio: "ignore" });
41446
41610
  console.log(t.success(" \u2713 beads (bd) available"));
41447
41611
  } catch {
41448
41612
  console.log(kleur_default.dim(" \u25CB beads (bd) not installed"));
@@ -41453,13 +41617,13 @@ function createClaudeCommand() {
41453
41617
  console.log(t.bold("\n Claude Code Doctor\n"));
41454
41618
  let allOk = true;
41455
41619
  try {
41456
- (0, import_node_child_process3.execSync)("claude --version", { stdio: "ignore" });
41620
+ (0, import_node_child_process4.execSync)("claude --version", { stdio: "ignore" });
41457
41621
  console.log(t.success(" \u2713 claude CLI available"));
41458
41622
  } catch {
41459
41623
  console.log(kleur_default.red(" \u2717 claude CLI not found \u2014 install Claude Code"));
41460
41624
  allOk = false;
41461
41625
  }
41462
- const listResult = (0, import_node_child_process3.spawnSync)("claude", ["plugin", "list"], { encoding: "utf8", stdio: "pipe" });
41626
+ const listResult = (0, import_node_child_process4.spawnSync)("claude", ["plugin", "list"], { encoding: "utf8", stdio: "pipe" });
41463
41627
  if (listResult.stdout?.includes("xtrm-tools")) {
41464
41628
  console.log(t.success(" \u2713 xtrm-tools plugin installed"));
41465
41629
  } else {
@@ -41467,21 +41631,21 @@ function createClaudeCommand() {
41467
41631
  allOk = false;
41468
41632
  }
41469
41633
  try {
41470
- (0, import_node_child_process3.execSync)("bd --version", { stdio: "ignore" });
41634
+ (0, import_node_child_process4.execSync)("bd --version", { stdio: "ignore" });
41471
41635
  console.log(t.success(" \u2713 beads (bd) installed"));
41472
41636
  } catch {
41473
41637
  console.log(kleur_default.yellow(" \u26A0 beads not installed \u2014 run: npm install -g @beads/bd"));
41474
41638
  allOk = false;
41475
41639
  }
41476
41640
  try {
41477
- (0, import_node_child_process3.execSync)("dolt version", { stdio: "ignore" });
41641
+ (0, import_node_child_process4.execSync)("dolt version", { stdio: "ignore" });
41478
41642
  console.log(t.success(" \u2713 dolt installed"));
41479
41643
  } catch {
41480
41644
  console.log(kleur_default.yellow(" \u26A0 dolt not installed \u2014 required for beads storage"));
41481
41645
  allOk = false;
41482
41646
  }
41483
41647
  try {
41484
- (0, import_node_child_process3.execSync)("gitnexus --version", { stdio: "ignore" });
41648
+ (0, import_node_child_process4.execSync)("gitnexus --version", { stdio: "ignore" });
41485
41649
  console.log(t.success(" \u2713 gitnexus installed"));
41486
41650
  } catch {
41487
41651
  console.log(kleur_default.dim(" \u25CB gitnexus not installed (optional) \u2014 npm install -g gitnexus"));
@@ -41498,7 +41662,7 @@ function createClaudeCommand() {
41498
41662
 
41499
41663
  // src/commands/pi.ts
41500
41664
  var import_path14 = __toESM(require("path"), 1);
41501
- var import_node_child_process5 = require("child_process");
41665
+ var import_node_child_process6 = require("child_process");
41502
41666
  var import_node_os7 = require("os");
41503
41667
  var import_fs_extra14 = __toESM(require_lib2(), 1);
41504
41668
 
@@ -41506,7 +41670,7 @@ var import_fs_extra14 = __toESM(require_lib2(), 1);
41506
41670
  var import_prompts2 = __toESM(require_prompts3(), 1);
41507
41671
  var import_fs_extra13 = __toESM(require_lib2(), 1);
41508
41672
  var import_path13 = __toESM(require("path"), 1);
41509
- var import_node_child_process4 = require("child_process");
41673
+ var import_node_child_process5 = require("child_process");
41510
41674
  var import_node_os6 = require("os");
41511
41675
  var PI_AGENT_DIR2 = process.env.PI_AGENT_DIR || import_path13.default.join((0, import_node_os6.homedir)(), ".pi", "agent");
41512
41676
  function fillTemplate(template, values) {
@@ -41530,69 +41694,7 @@ function readExistingPiValues(piAgentDir) {
41530
41694
  return values;
41531
41695
  }
41532
41696
  function isPiInstalled2() {
41533
- return (0, import_node_child_process4.spawnSync)("pi", ["--version"], { encoding: "utf8" }).status === 0;
41534
- }
41535
- async function listExtensionDirs(baseDir) {
41536
- if (!await import_fs_extra13.default.pathExists(baseDir)) return [];
41537
- const entries = await import_fs_extra13.default.readdir(baseDir, { withFileTypes: true });
41538
- const extDirs = [];
41539
- for (const entry of entries) {
41540
- if (!entry.isDirectory()) continue;
41541
- const extPath = import_path13.default.join(baseDir, entry.name);
41542
- const pkgPath = import_path13.default.join(extPath, "package.json");
41543
- if (await import_fs_extra13.default.pathExists(pkgPath)) {
41544
- extDirs.push(entry.name);
41545
- }
41546
- }
41547
- return extDirs.sort();
41548
- }
41549
- async function fileSha256(filePath) {
41550
- const crypto2 = await import("crypto");
41551
- const content = await import_fs_extra13.default.readFile(filePath);
41552
- return crypto2.createHash("sha256").update(content).digest("hex");
41553
- }
41554
- async function extensionHash(extDir) {
41555
- const pkgPath = import_path13.default.join(extDir, "package.json");
41556
- const indexPath = import_path13.default.join(extDir, "index.ts");
41557
- const hashes = [];
41558
- if (await import_fs_extra13.default.pathExists(pkgPath)) {
41559
- hashes.push(await fileSha256(pkgPath));
41560
- }
41561
- if (await import_fs_extra13.default.pathExists(indexPath)) {
41562
- hashes.push(await fileSha256(indexPath));
41563
- }
41564
- return hashes.join(":");
41565
- }
41566
- async function diffPiExtensions(sourceDir, targetDir) {
41567
- const sourceAbs = import_path13.default.resolve(sourceDir);
41568
- const targetAbs = import_path13.default.resolve(targetDir);
41569
- const sourceExts = await listExtensionDirs(sourceAbs);
41570
- const missing = [];
41571
- const stale = [];
41572
- const upToDate = [];
41573
- for (const extName of sourceExts) {
41574
- const srcExtPath = import_path13.default.join(sourceAbs, extName);
41575
- const dstExtPath = import_path13.default.join(targetAbs, extName);
41576
- if (!await import_fs_extra13.default.pathExists(dstExtPath)) {
41577
- missing.push(extName);
41578
- continue;
41579
- }
41580
- const dstPkgPath = import_path13.default.join(dstExtPath, "package.json");
41581
- if (!await import_fs_extra13.default.pathExists(dstPkgPath)) {
41582
- missing.push(extName);
41583
- continue;
41584
- }
41585
- const [srcHash, dstHash] = await Promise.all([
41586
- extensionHash(srcExtPath),
41587
- extensionHash(dstExtPath)
41588
- ]);
41589
- if (srcHash !== dstHash) {
41590
- stale.push(extName);
41591
- } else {
41592
- upToDate.push(extName);
41593
- }
41594
- }
41595
- return { missing, stale, upToDate };
41697
+ return (0, import_node_child_process5.spawnSync)("pi", ["--version"], { encoding: "utf8" }).status === 0;
41596
41698
  }
41597
41699
  function printPiCheckSummary(diff) {
41598
41700
  const totalDiff = diff.missing.length + diff.stale.length;
@@ -41632,14 +41734,14 @@ function createInstallPiCommand() {
41632
41734
  console.log(t.bold("\n Pi Coding Agent Setup\n"));
41633
41735
  if (!isPiInstalled2()) {
41634
41736
  console.log(kleur_default.yellow(" pi not found \u2014 installing oh-pi globally...\n"));
41635
- const r = (0, import_node_child_process4.spawnSync)("npm", ["install", "-g", "oh-pi"], { stdio: "inherit" });
41737
+ const r = (0, import_node_child_process5.spawnSync)("npm", ["install", "-g", "oh-pi"], { stdio: "inherit" });
41636
41738
  if (r.status !== 0) {
41637
41739
  console.error(kleur_default.red("\n Failed to install oh-pi. Run: npm install -g oh-pi\n"));
41638
41740
  process.exit(1);
41639
41741
  }
41640
41742
  console.log(t.success(" pi installed\n"));
41641
41743
  } else {
41642
- const v = (0, import_node_child_process4.spawnSync)("pi", ["--version"], { encoding: "utf8" });
41744
+ const v = (0, import_node_child_process5.spawnSync)("pi", ["--version"], { encoding: "utf8" });
41643
41745
  console.log(t.success(` pi ${v.stdout.trim()} already installed
41644
41746
  `));
41645
41747
  }
@@ -41675,9 +41777,17 @@ function createInstallPiCommand() {
41675
41777
  await import_fs_extra13.default.writeFile(destPath, fillTemplate(raw, values), "utf8");
41676
41778
  console.log(t.success(` ${sym.ok} ${name}`));
41677
41779
  }
41780
+ const extensionsSrc = import_path13.default.join(piConfigDir, "extensions");
41781
+ const extensionsDst = import_path13.default.join(PI_AGENT_DIR2, "extensions");
41782
+ const preCheck = await piPreCheck(extensionsSrc, extensionsDst, schema.packages);
41783
+ const extTotal = preCheck.extensions.missing.length + preCheck.extensions.stale.length + preCheck.extensions.upToDate.length;
41784
+ console.log(kleur_default.dim(`
41785
+ Pre-check:`));
41786
+ console.log(kleur_default.dim(` Extensions: ${preCheck.extensions.upToDate.length}/${extTotal} up-to-date, ${preCheck.extensions.stale.length} stale, ${preCheck.extensions.missing.length} missing`));
41787
+ console.log(kleur_default.dim(` Packages: ${preCheck.packages.installed.length}/${schema.packages.length} installed, ${preCheck.packages.needed.length} needed`));
41678
41788
  const managedPackages = await syncManagedPiExtensions({
41679
- sourceDir: import_path13.default.join(piConfigDir, "extensions"),
41680
- targetDir: import_path13.default.join(PI_AGENT_DIR2, "extensions"),
41789
+ sourceDir: extensionsSrc,
41790
+ targetDir: extensionsDst,
41681
41791
  dryRun: false,
41682
41792
  log: (message) => console.log(kleur_default.dim(` ${message}`))
41683
41793
  });
@@ -41685,10 +41795,14 @@ function createInstallPiCommand() {
41685
41795
  console.log(t.success(` ${sym.ok} extensions/ (${managedPackages} packages)`));
41686
41796
  }
41687
41797
  console.log(t.bold("\n npm Packages\n"));
41688
- for (const pkg of schema.packages) {
41689
- const r = (0, import_node_child_process4.spawnSync)("pi", ["install", pkg], { stdio: "inherit" });
41690
- if (r.status === 0) console.log(t.success(` ${sym.ok} ${pkg}`));
41691
- else console.log(kleur_default.yellow(` ${pkg} \u2014 failed, run manually: pi install ${pkg}`));
41798
+ if (preCheck.packages.needed.length === 0) {
41799
+ console.log(kleur_default.dim(` \u2713 All ${schema.packages.length} packages already installed`));
41800
+ } else {
41801
+ for (const pkg of preCheck.packages.needed) {
41802
+ const r = (0, import_node_child_process5.spawnSync)("pi", ["install", pkg], { stdio: "inherit" });
41803
+ if (r.status === 0) console.log(t.success(` ${sym.ok} ${pkg}`));
41804
+ else console.log(kleur_default.yellow(` ${pkg} \u2014 failed, run manually: pi install ${pkg}`));
41805
+ }
41692
41806
  }
41693
41807
  console.log(t.bold("\n OAuth (manual steps)\n"));
41694
41808
  for (const provider of schema.oauth_providers) {
@@ -41714,7 +41828,7 @@ function createPiCommand() {
41714
41828
  cmd.addCommand(piSetup);
41715
41829
  cmd.command("status").description("Check Pi version and extension deployment drift").action(async () => {
41716
41830
  console.log(t.bold("\n Pi Runtime Status\n"));
41717
- const piResult = (0, import_node_child_process5.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
41831
+ const piResult = (0, import_node_child_process6.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
41718
41832
  if (piResult.status === 0) {
41719
41833
  console.log(t.success(` \u2713 pi ${piResult.stdout.trim()} installed`));
41720
41834
  } else {
@@ -41738,7 +41852,7 @@ function createPiCommand() {
41738
41852
  cmd.command("doctor").description("Diagnostic checks: pi installed, extensions deployed, packages present").action(async () => {
41739
41853
  console.log(t.bold("\n Pi Doctor\n"));
41740
41854
  let allOk = true;
41741
- const piResult = (0, import_node_child_process5.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
41855
+ const piResult = (0, import_node_child_process6.spawnSync)("pi", ["--version"], { encoding: "utf8", stdio: "pipe" });
41742
41856
  if (piResult.status === 0) {
41743
41857
  console.log(t.success(` \u2713 pi ${piResult.stdout.trim()} installed`));
41744
41858
  } else {
@@ -41759,9 +41873,9 @@ function createPiCommand() {
41759
41873
  const schemaPath = import_path14.default.join(piConfigDir, "install-schema.json");
41760
41874
  if (await import_fs_extra14.default.pathExists(schemaPath)) {
41761
41875
  try {
41762
- (0, import_node_child_process5.execSync)("pi --version", { stdio: "ignore" });
41876
+ (0, import_node_child_process6.execSync)("pi --version", { stdio: "ignore" });
41763
41877
  const schema = await import_fs_extra14.default.readJson(schemaPath);
41764
- const listResult = (0, import_node_child_process5.spawnSync)("pi", ["list"], { encoding: "utf8", stdio: "pipe" });
41878
+ const listResult = (0, import_node_child_process6.spawnSync)("pi", ["list"], { encoding: "utf8", stdio: "pipe" });
41765
41879
  const installed = listResult.stdout ?? "";
41766
41880
  const missing = schema.packages.filter((p) => !installed.includes(p.replace("npm:", "")));
41767
41881
  if (missing.length === 0) {
@@ -56389,13 +56503,13 @@ function createCleanCommand() {
56389
56503
 
56390
56504
  // src/commands/end.ts
56391
56505
  var import_prompts4 = __toESM(require_prompts3(), 1);
56392
- var import_node_child_process6 = require("child_process");
56506
+ var import_node_child_process7 = require("child_process");
56393
56507
  function git(args, cwd) {
56394
- const r = (0, import_node_child_process6.spawnSync)("git", args, { cwd, encoding: "utf8", stdio: "pipe" });
56508
+ const r = (0, import_node_child_process7.spawnSync)("git", args, { cwd, encoding: "utf8", stdio: "pipe" });
56395
56509
  return { ok: r.status === 0, out: (r.stdout ?? "").trim(), err: (r.stderr ?? "").trim() };
56396
56510
  }
56397
56511
  function bd(args, cwd) {
56398
- const r = (0, import_node_child_process6.spawnSync)("bd", args, { cwd, encoding: "utf8", stdio: "pipe" });
56512
+ const r = (0, import_node_child_process7.spawnSync)("bd", args, { cwd, encoding: "utf8", stdio: "pipe" });
56399
56513
  return { ok: r.status === 0, out: (r.stdout ?? "").trim() };
56400
56514
  }
56401
56515
  function extractIssueIds(commitLog) {
@@ -56442,7 +56556,7 @@ function buildPrBody(issues, commitLog, diffStat, branch) {
56442
56556
  return lines.join("\n");
56443
56557
  }
56444
56558
  function createEndCommand() {
56445
- return new Command("end").description("Close session: rebase, push, open PR, link beads issues, clean up worktree").option("--draft", "Open PR as draft", false).option("--keep", "Keep worktree after PR creation (default: prompt)", false).option("-y, --yes", "Skip confirmation prompts", false).action(async (opts) => {
56559
+ return new Command("end").description("Close session: rebase, push, open PR, link beads issues, clean up worktree").option("--draft", "Open PR as draft", false).option("--keep", "Keep worktree after PR creation (default: prompt)", false).option("-y, --yes", "Skip confirmation prompts", false).option("--dry-run", "Preview PR title, body, and linked issues without pushing or creating PR", false).action(async (opts) => {
56446
56560
  const cwd = process.cwd();
56447
56561
  const branchResult = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
56448
56562
  const branch = branchResult.out;
@@ -56466,7 +56580,14 @@ function createEndCommand() {
56466
56580
  console.log(t.bold(`
56467
56581
  xt end \u2014 closing session on ${branch}
56468
56582
  `));
56469
- const logResult = git(["log", "origin/main..HEAD", "--oneline"], cwd);
56583
+ let defaultBranch = "main";
56584
+ const symRef = git(["symbolic-ref", "refs/remotes/origin/HEAD", "--short"], cwd);
56585
+ if (symRef.ok && symRef.out) {
56586
+ defaultBranch = symRef.out.replace("origin/", "");
56587
+ } else if (git(["rev-parse", "--verify", "origin/master"], cwd).ok) {
56588
+ defaultBranch = "master";
56589
+ }
56590
+ const logResult = git(["log", `origin/${defaultBranch}..HEAD`, "--oneline"], cwd);
56470
56591
  const issueIds = extractIssueIds(logResult.out);
56471
56592
  const issues = [];
56472
56593
  for (const id of issueIds) {
@@ -56490,10 +56611,28 @@ function createEndCommand() {
56490
56611
  } else {
56491
56612
  console.log(kleur_default.dim(" \u25CB No beads issues found in commit log"));
56492
56613
  }
56493
- console.log(kleur_default.dim(" Fetching origin/main..."));
56494
- git(["fetch", "origin", "main"], cwd);
56495
- console.log(kleur_default.dim(" Rebasing onto origin/main..."));
56496
- const rebaseResult = git(["rebase", "origin/main"], cwd);
56614
+ if (opts.dryRun) {
56615
+ const fullLog2 = git(["log", `origin/${defaultBranch}..HEAD`, "--oneline"], cwd).out;
56616
+ const diffStat2 = git(["diff", `origin/${defaultBranch}`, "--stat"], cwd).out;
56617
+ const prTitle2 = buildPrTitle(issues);
56618
+ const prBody2 = buildPrBody(issues, fullLog2, diffStat2, branch);
56619
+ console.log(t.bold("\n [DRY RUN] PR preview\n"));
56620
+ console.log(` ${kleur_default.bold("Title:")} ${prTitle2}`);
56621
+ if (issues.length > 0) {
56622
+ console.log(` ${kleur_default.bold("Issues:")} ${issueIds.join(", ")}`);
56623
+ }
56624
+ console.log(`
56625
+ ${kleur_default.bold("Body:")}`);
56626
+ for (const line of prBody2.split("\n")) {
56627
+ console.log(` ${kleur_default.dim(line)}`);
56628
+ }
56629
+ console.log(t.accent("\n [DRY RUN] No changes made \u2014 re-run without --dry-run to push and create PR\n"));
56630
+ return;
56631
+ }
56632
+ console.log(kleur_default.dim(` Fetching origin/${defaultBranch}...`));
56633
+ git(["fetch", "origin", defaultBranch], cwd);
56634
+ console.log(kleur_default.dim(` Rebasing onto origin/${defaultBranch}...`));
56635
+ const rebaseResult = git(["rebase", `origin/${defaultBranch}`], cwd);
56497
56636
  if (!rebaseResult.ok) {
56498
56637
  const conflicts = git(["diff", "--name-only", "--diff-filter=U"], cwd).out;
56499
56638
  console.error(kleur_default.red("\n \u2717 Rebase conflicts detected:\n"));
@@ -56505,7 +56644,7 @@ function createEndCommand() {
56505
56644
  ));
56506
56645
  process.exit(1);
56507
56646
  }
56508
- console.log(t.success(" \u2713 Rebased onto origin/main"));
56647
+ console.log(t.success(` \u2713 Rebased onto origin/${defaultBranch}`));
56509
56648
  console.log(kleur_default.dim(" Pushing branch..."));
56510
56649
  const pushResult = git(["push", "origin", branch, "--force-with-lease"], cwd);
56511
56650
  if (!pushResult.ok) {
@@ -56516,14 +56655,14 @@ function createEndCommand() {
56516
56655
  process.exit(1);
56517
56656
  }
56518
56657
  console.log(t.success(` \u2713 Pushed ${branch}`));
56519
- const fullLog = git(["log", "origin/main..HEAD", "--oneline"], cwd).out;
56520
- const diffStat = git(["diff", "origin/main", "--stat"], cwd).out;
56658
+ const fullLog = git(["log", `origin/${defaultBranch}..HEAD`, "--oneline"], cwd).out;
56659
+ const diffStat = git(["diff", `origin/${defaultBranch}`, "--stat"], cwd).out;
56521
56660
  const prTitle = buildPrTitle(issues);
56522
56661
  const prBody = buildPrBody(issues, fullLog, diffStat, branch);
56523
56662
  console.log(kleur_default.dim(" Creating PR..."));
56524
56663
  const prArgs = ["pr", "create", "--title", prTitle, "--body", prBody];
56525
56664
  if (opts.draft) prArgs.push("--draft");
56526
- const prResult = (0, import_node_child_process6.spawnSync)("gh", prArgs, { cwd, encoding: "utf8", stdio: "pipe" });
56665
+ const prResult = (0, import_node_child_process7.spawnSync)("gh", prArgs, { cwd, encoding: "utf8", stdio: "pipe" });
56527
56666
  if (prResult.status !== 0) {
56528
56667
  console.error(kleur_default.red(`
56529
56668
  \u2717 PR creation failed:
@@ -56553,7 +56692,7 @@ function createEndCommand() {
56553
56692
  if (doRemove) {
56554
56693
  try {
56555
56694
  const repoRoot = git(["rev-parse", "--show-toplevel"], cwd).out;
56556
- const removeResult = (0, import_node_child_process6.spawnSync)(
56695
+ const removeResult = (0, import_node_child_process7.spawnSync)(
56557
56696
  "git",
56558
56697
  ["worktree", "remove", cwd, "--force"],
56559
56698
  { cwd: repoRoot, encoding: "utf8", stdio: "pipe" }
@@ -56577,9 +56716,11 @@ function createEndCommand() {
56577
56716
 
56578
56717
  // src/commands/worktree.ts
56579
56718
  var import_prompts5 = __toESM(require_prompts3(), 1);
56580
- var import_node_child_process7 = require("child_process");
56719
+ var import_node_child_process8 = require("child_process");
56720
+ var import_node_fs5 = require("fs");
56721
+ var import_node_path6 = require("path");
56581
56722
  function listXtWorktrees(repoRoot) {
56582
- const r = (0, import_node_child_process7.spawnSync)("git", ["worktree", "list", "--porcelain"], {
56723
+ const r = (0, import_node_child_process8.spawnSync)("git", ["worktree", "list", "--porcelain"], {
56583
56724
  cwd: repoRoot,
56584
56725
  encoding: "utf8",
56585
56726
  stdio: "pipe"
@@ -56608,7 +56749,7 @@ function listXtWorktrees(repoRoot) {
56608
56749
  }
56609
56750
  function isMergedIntoMain(branch, repoRoot) {
56610
56751
  const branchShort = branch.replace("refs/heads/", "");
56611
- const r = (0, import_node_child_process7.spawnSync)("git", ["branch", "--merged", "origin/main", "--list", branchShort], {
56752
+ const r = (0, import_node_child_process8.spawnSync)("git", ["branch", "--merged", "origin/main", "--list", branchShort], {
56612
56753
  cwd: repoRoot,
56613
56754
  encoding: "utf8",
56614
56755
  stdio: "pipe"
@@ -56616,7 +56757,7 @@ function isMergedIntoMain(branch, repoRoot) {
56616
56757
  return (r.stdout ?? "").includes(branchShort);
56617
56758
  }
56618
56759
  function getRepoRoot(cwd) {
56619
- const r = (0, import_node_child_process7.spawnSync)("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: "pipe" });
56760
+ const r = (0, import_node_child_process8.spawnSync)("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: "pipe" });
56620
56761
  return r.ok ? r.stdout.trim() : cwd;
56621
56762
  }
56622
56763
  function createWorktreeCommand() {
@@ -56670,13 +56811,14 @@ function createWorktreeCommand() {
56670
56811
  return;
56671
56812
  }
56672
56813
  for (const wt of merged) {
56673
- const r = (0, import_node_child_process7.spawnSync)("git", ["worktree", "remove", wt.path, "--force"], {
56814
+ const r = (0, import_node_child_process8.spawnSync)("git", ["worktree", "remove", wt.path, "--force"], {
56674
56815
  cwd: repoRoot,
56675
56816
  encoding: "utf8",
56676
56817
  stdio: "pipe"
56677
56818
  });
56678
56819
  if (r.status === 0) {
56679
56820
  console.log(t.success(` \u2713 Removed ${wt.path}`));
56821
+ clearStatuslineClaim(repoRoot);
56680
56822
  } else {
56681
56823
  console.log(kleur_default.yellow(` \u26A0 Could not remove ${wt.path}: ${(r.stderr ?? "").trim()}`));
56682
56824
  }
@@ -56710,12 +56852,13 @@ function createWorktreeCommand() {
56710
56852
  console.log(kleur_default.dim(" Cancelled\n"));
56711
56853
  return;
56712
56854
  }
56713
- const r = (0, import_node_child_process7.spawnSync)("git", ["worktree", "remove", target.path, "--force"], {
56855
+ const r = (0, import_node_child_process8.spawnSync)("git", ["worktree", "remove", target.path, "--force"], {
56714
56856
  cwd: repoRoot,
56715
56857
  encoding: "utf8",
56716
56858
  stdio: "pipe"
56717
56859
  });
56718
56860
  if (r.status === 0) {
56861
+ clearStatuslineClaim(repoRoot);
56719
56862
  console.log(t.success(`
56720
56863
  \u2713 Removed ${target.path}
56721
56864
  `));
@@ -56728,6 +56871,13 @@ function createWorktreeCommand() {
56728
56871
  });
56729
56872
  return cmd;
56730
56873
  }
56874
+ function clearStatuslineClaim(repoRoot) {
56875
+ try {
56876
+ const claimFile = (0, import_node_path6.join)(repoRoot, ".xtrm", "statusline-claim");
56877
+ if ((0, import_node_fs5.existsSync)(claimFile)) (0, import_node_fs5.unlinkSync)(claimFile);
56878
+ } catch {
56879
+ }
56880
+ }
56731
56881
 
56732
56882
  // src/commands/docs.ts
56733
56883
  var import_fs_extra19 = __toESM(require_lib2(), 1);
@@ -56858,9 +57008,9 @@ function createDocsCommand() {
56858
57008
  }
56859
57009
 
56860
57010
  // src/commands/debug.ts
56861
- var import_node_child_process8 = require("child_process");
56862
- var import_node_fs5 = require("fs");
56863
- var import_node_path6 = require("path");
57011
+ var import_node_child_process9 = require("child_process");
57012
+ var import_node_fs6 = require("fs");
57013
+ var import_node_path7 = require("path");
56864
57014
  var KIND_LABELS = {
56865
57015
  "session.start": { label: "SESS+", color: kleur_default.green },
56866
57016
  "session.end": { label: "SESS-", color: kleur_default.white },
@@ -56960,14 +57110,14 @@ function buildDetail(event) {
56960
57110
  }
56961
57111
  if (event.kind === "tool.call") {
56962
57112
  if (d?.cmd) parts.push(kleur_default.dim(d.cmd.slice(0, 72)));
56963
- if (d?.file) parts.push(kleur_default.dim((0, import_node_path6.basename)(d.file)));
57113
+ if (d?.file) parts.push(kleur_default.dim((0, import_node_path7.basename)(d.file)));
56964
57114
  if (d?.pattern) parts.push(kleur_default.dim(`/${d.pattern}/`));
56965
57115
  if (d?.url) parts.push(kleur_default.dim(d.url.slice(0, 72)));
56966
57116
  if (d?.query) parts.push(kleur_default.dim(d.query.slice(0, 72)));
56967
57117
  if (d?.prompt) parts.push(kleur_default.dim(d.prompt.slice(0, 72)));
56968
57118
  } else {
56969
57119
  if (event.issue_id) parts.push(kleur_default.yellow(event.issue_id));
56970
- if (d?.file) parts.push(kleur_default.dim((0, import_node_path6.basename)(d.file)));
57120
+ if (d?.file) parts.push(kleur_default.dim((0, import_node_path7.basename)(d.file)));
56971
57121
  if (d?.reason_code) parts.push(kleur_default.dim(`[${d.reason_code}]`));
56972
57122
  if (event.worktree) parts.push(kleur_default.dim(`wt:${event.worktree}`));
56973
57123
  }
@@ -56984,8 +57134,8 @@ function formatLine(event, colorMap) {
56984
57134
  function findDbPath(cwd) {
56985
57135
  let dir = cwd;
56986
57136
  for (let i = 0; i < 10; i++) {
56987
- if ((0, import_node_fs5.existsSync)((0, import_node_path6.join)(dir, ".beads"))) return (0, import_node_path6.join)(dir, ".xtrm", "debug.db");
56988
- const parent = (0, import_node_path6.join)(dir, "..");
57137
+ if ((0, import_node_fs6.existsSync)((0, import_node_path7.join)(dir, ".beads"))) return (0, import_node_path7.join)(dir, ".xtrm", "debug.db");
57138
+ const parent = (0, import_node_path7.join)(dir, "..");
56989
57139
  if (parent === dir) break;
56990
57140
  dir = parent;
56991
57141
  }
@@ -57006,7 +57156,7 @@ function buildWhere(opts, base) {
57006
57156
  }
57007
57157
  function queryEvents(dbPath, where, limit) {
57008
57158
  const sql = `SELECT id,ts,session_id,runtime,worktree,kind,tool_name,outcome,issue_id,duration_ms,data FROM events${where ? ` WHERE ${where}` : ""} ORDER BY id ASC LIMIT ${limit}`;
57009
- const result = (0, import_node_child_process8.spawnSync)("sqlite3", [dbPath, "-json", sql], {
57159
+ const result = (0, import_node_child_process9.spawnSync)("sqlite3", [dbPath, "-json", sql], {
57010
57160
  stdio: ["pipe", "pipe", "pipe"],
57011
57161
  encoding: "utf8",
57012
57162
  timeout: 5e3
@@ -57046,7 +57196,7 @@ function createDebugCommand() {
57046
57196
  return new Command("debug").description("Watch xtrm events: tool calls, gate decisions, bd lifecycle").option("-f, --follow", "Follow new events (default)", false).option("--all", "Show full history and exit", false).option("--session <id>", "Filter by session ID (prefix match)").option("--type <domain>", "Filter by domain: tool | gate | bd | session").option("--json", "Output raw JSON lines", false).action((opts) => {
57047
57197
  const cwd = process.cwd();
57048
57198
  const dbPath = findDbPath(cwd);
57049
- if (!dbPath || !(0, import_node_fs5.existsSync)(dbPath)) return;
57199
+ if (!dbPath || !(0, import_node_fs6.existsSync)(dbPath)) return;
57050
57200
  if (opts.all) {
57051
57201
  const events = queryEvents(dbPath, buildWhere(opts, ""), 1e3);
57052
57202
  const colorMap = buildColorMap(events);
@@ -57231,7 +57381,7 @@ async function printBanner(version3) {
57231
57381
  // src/index.ts
57232
57382
  var version2 = "0.0.0";
57233
57383
  try {
57234
- version2 = JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path7.resolve)(__dirname, "../package.json"), "utf8")).version;
57384
+ version2 = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path8.resolve)(__dirname, "../package.json"), "utf8")).version;
57235
57385
  } catch {
57236
57386
  }
57237
57387
  var program2 = new Command();