claude-nomad 0.69.2 → 0.69.3

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/nomad.mjs CHANGED
@@ -848,7 +848,7 @@ import {
848
848
  symlinkSync,
849
849
  writeFileSync
850
850
  } from "node:fs";
851
- import { dirname, join as join5, relative as relative3, sep as sep2 } from "node:path";
851
+ import { dirname as dirname2, join as join5, relative as relative3, sep as sep2 } from "node:path";
852
852
  function isRetryableRenameCode(code) {
853
853
  return code === "EPERM" || code === "EBUSY";
854
854
  }
@@ -883,7 +883,7 @@ function writeJsonAtomic(path, data) {
883
883
  closeSync(fd);
884
884
  }
885
885
  renameAtomicRetry(tmp, path);
886
- const dirFd = openSync(dirname(path), "r");
886
+ const dirFd = openSync(dirname2(path), "r");
887
887
  try {
888
888
  fsyncSync(dirFd);
889
889
  } catch (e) {
@@ -921,7 +921,7 @@ function ensureSymlink(linkPath, target) {
921
921
  if (lstatSync5(linkPath).isSymbolicLink()) return;
922
922
  die(`${linkPath} exists and is not a symlink. Move it aside first.`);
923
923
  }
924
- mkdirSync(dirname(linkPath), { recursive: true });
924
+ mkdirSync(dirname2(linkPath), { recursive: true });
925
925
  symlinkSync(target, linkPath);
926
926
  log(`linked ${linkPath} -> ${target}`);
927
927
  }
@@ -931,7 +931,7 @@ function backupUnder(absPath, anchor, destRoot, hint) {
931
931
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep2}`)) return false;
932
932
  const dst = join5(destRoot, rel);
933
933
  try {
934
- mkdirSync(dirname(dst), { recursive: true });
934
+ mkdirSync(dirname2(dst), { recursive: true });
935
935
  cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
936
936
  } catch (err) {
937
937
  const fragment = `could not back up ${absPath} into ${dst} (${errorText(err)}); a partial copy may be left there`;
@@ -963,12 +963,12 @@ var init_utils_fs = __esm({
963
963
 
964
964
  // src/commands/pull/wedge.ts
965
965
  import { execFileSync as execFileSync4 } from "node:child_process";
966
- import { existsSync as existsSync17 } from "node:fs";
967
- import { join as join23 } from "node:path";
966
+ import { existsSync as existsSync18 } from "node:fs";
967
+ import { join as join24 } from "node:path";
968
968
  function detectWedge(repo) {
969
- const g = join23(repo, ".git");
970
- if (existsSync17(join23(g, "rebase-merge")) || existsSync17(join23(g, "rebase-apply"))) return "rebase";
971
- if (existsSync17(join23(g, "MERGE_HEAD"))) return "merge";
969
+ const g = join24(repo, ".git");
970
+ if (existsSync18(join24(g, "rebase-merge")) || existsSync18(join24(g, "rebase-apply"))) return "rebase";
971
+ if (existsSync18(join24(g, "MERGE_HEAD"))) return "merge";
972
972
  return null;
973
973
  }
974
974
  function probeUnmergedIndex(repo) {
@@ -1038,17 +1038,17 @@ var init_wedge = __esm({
1038
1038
  });
1039
1039
 
1040
1040
  // src/core/package-root.ts
1041
- import { existsSync as existsSync18 } from "node:fs";
1042
- import { basename as basename4, dirname as dirname6, join as join24 } from "node:path";
1041
+ import { existsSync as existsSync19 } from "node:fs";
1042
+ import { basename as basename5, dirname as dirname7, join as join25 } from "node:path";
1043
1043
  import { fileURLToPath } from "node:url";
1044
1044
  function packageRoot(from = HERE) {
1045
1045
  let dir = from;
1046
1046
  for (; ; ) {
1047
- if (existsSync18(join24(dir, "package.json"))) return dir;
1048
- if (basename4(dir) === "node_modules") {
1047
+ if (existsSync19(join25(dir, "package.json"))) return dir;
1048
+ if (basename5(dir) === "node_modules") {
1049
1049
  throw new Error(`no package.json above ${from} within its own package`);
1050
1050
  }
1051
- const parent = dirname6(dir);
1051
+ const parent = dirname7(dir);
1052
1052
  if (parent === dir) throw new Error(`no package.json above ${from}`);
1053
1053
  dir = parent;
1054
1054
  }
@@ -1057,17 +1057,17 @@ var HERE;
1057
1057
  var init_package_root = __esm({
1058
1058
  "src/core/package-root.ts"() {
1059
1059
  "use strict";
1060
- HERE = dirname6(fileURLToPath(import.meta.url));
1060
+ HERE = dirname7(fileURLToPath(import.meta.url));
1061
1061
  }
1062
1062
  });
1063
1063
 
1064
1064
  // src/commands/push/gitleaks.config.ts
1065
- import { existsSync as existsSync19, mkdtempSync, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
1065
+ import { existsSync as existsSync20, mkdtempSync, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
1066
1066
  import { tmpdir } from "node:os";
1067
- import { join as join25 } from "node:path";
1067
+ import { join as join26 } from "node:path";
1068
1068
  function resolveTomlPath(repo = repoHome()) {
1069
- const repoToml = join25(repo, ".gitleaks.toml");
1070
- if (existsSync19(repoToml)) return repoToml;
1069
+ const repoToml = join26(repo, ".gitleaks.toml");
1070
+ if (existsSync20(repoToml)) return repoToml;
1071
1071
  let root;
1072
1072
  try {
1073
1073
  root = packageRoot();
@@ -1076,8 +1076,8 @@ function resolveTomlPath(repo = repoHome()) {
1076
1076
  "claude-nomad install looks incomplete (no package.json above the CLI); reinstall with `npm i -g claude-nomad`"
1077
1077
  );
1078
1078
  }
1079
- const bundled = join25(root, ".gitleaks.toml");
1080
- return existsSync19(bundled) ? bundled : null;
1079
+ const bundled = join26(root, ".gitleaks.toml");
1080
+ return existsSync20(bundled) ? bundled : null;
1081
1081
  }
1082
1082
  function assertOverlayAllowlistsScoped(overlayBody) {
1083
1083
  if (OVERLAY_INLINE_ALLOWLIST_RE.test(overlayBody)) {
@@ -1115,17 +1115,17 @@ function buildOverlayTempConfig(overlayBody, bundled) {
1115
1115
  path = ${JSON.stringify(bundled)}
1116
1116
 
1117
1117
  ${overlayBody}`;
1118
- const tempPath = mkdtempSync(join25(tmpdir(), "nomad-gitleaks-cfg-"));
1119
- const configPath = join25(tempPath, "config.toml");
1118
+ const tempPath = mkdtempSync(join26(tmpdir(), "nomad-gitleaks-cfg-"));
1119
+ const configPath = join26(tempPath, "config.toml");
1120
1120
  writeFileSync3(configPath, tempBody, { mode: 384, flag: "wx" });
1121
1121
  return { configPath, tempPath };
1122
1122
  }
1123
1123
  function resolveTomlConfig() {
1124
1124
  const repo = repoHome();
1125
- const overlayPath = join25(repo, ".gitleaks.overlay.toml");
1126
- const repoToml = join25(repo, ".gitleaks.toml");
1125
+ const overlayPath = join26(repo, ".gitleaks.overlay.toml");
1126
+ const repoToml = join26(repo, ".gitleaks.toml");
1127
1127
  const bundled = resolveTomlPath(repo);
1128
- if (!existsSync19(overlayPath)) {
1128
+ if (!existsSync20(overlayPath)) {
1129
1129
  return { path: bundled, tempPath: null };
1130
1130
  }
1131
1131
  if (bundled === repoToml) {
@@ -1212,9 +1212,9 @@ var init_autostash_guard = __esm({
1212
1212
 
1213
1213
  // src/commands/push/checks.ts
1214
1214
  import { execFileSync as execFileSync5 } from "node:child_process";
1215
- import { readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
1215
+ import { readdirSync as readdirSync9, rmSync as rmSync11 } from "node:fs";
1216
1216
  import { homedir as homedir2, platform } from "node:os";
1217
- import { delimiter, join as join26 } from "node:path";
1217
+ import { delimiter, join as join27 } from "node:path";
1218
1218
  function gitleaksInstallHint() {
1219
1219
  const head = "gitleaks not on PATH (required for nomad push). Install:";
1220
1220
  const plat = platform();
@@ -1257,12 +1257,12 @@ function findGitlinks(dir) {
1257
1257
  function walk2(current) {
1258
1258
  let entries;
1259
1259
  try {
1260
- entries = readdirSync8(current, { withFileTypes: true });
1260
+ entries = readdirSync9(current, { withFileTypes: true });
1261
1261
  } catch {
1262
1262
  return;
1263
1263
  }
1264
1264
  for (const e of entries) {
1265
- const p = join26(current, e.name);
1265
+ const p = join27(current, e.name);
1266
1266
  if (e.name === ".git") {
1267
1267
  hits.push(p);
1268
1268
  continue;
@@ -1284,7 +1284,7 @@ function probeGitleaks() {
1284
1284
  if (e.code === "ENOENT") throw new NomadFatal(gitleaksInstallHint());
1285
1285
  throw new NomadFatal(`gitleaks --version failed: ${e.message}`);
1286
1286
  } finally {
1287
- if (tempPath !== null) rmSync10(tempPath, { recursive: true, force: true });
1287
+ if (tempPath !== null) rmSync11(tempPath, { recursive: true, force: true });
1288
1288
  }
1289
1289
  }
1290
1290
  function wedgePreflight(wedge) {
@@ -1324,9 +1324,9 @@ var init_checks = __esm({
1324
1324
 
1325
1325
  // src/commands/push/gitleaks.scan.ts
1326
1326
  import { execFileSync as execFileSync9 } from "node:child_process";
1327
- import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, rmSync as rmSync11 } from "node:fs";
1327
+ import { mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, rmSync as rmSync12 } from "node:fs";
1328
1328
  import { homedir as homedir3 } from "node:os";
1329
- import { join as join30 } from "node:path";
1329
+ import { join as join31 } from "node:path";
1330
1330
  function toFinding(entry) {
1331
1331
  if (typeof entry !== "object" || entry === null) return null;
1332
1332
  const e = entry;
@@ -1374,9 +1374,9 @@ function resolveScanFailure(reportPath, e, forwardStreams) {
1374
1374
  return report;
1375
1375
  }
1376
1376
  function scanStagedTree(repoDir, forwardStreams = false) {
1377
- const cacheDir = join30(homedir3(), ".cache", "claude-nomad");
1378
- mkdirSync4(cacheDir, { recursive: true });
1379
- const reportPath = join30(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
1377
+ const cacheDir = join31(homedir3(), ".cache", "claude-nomad");
1378
+ mkdirSync5(cacheDir, { recursive: true });
1379
+ const reportPath = join31(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
1380
1380
  const { path: toml, tempPath } = resolveTomlConfig();
1381
1381
  const args = [
1382
1382
  "protect",
@@ -1399,19 +1399,19 @@ function scanStagedTree(repoDir, forwardStreams = false) {
1399
1399
  if (e.code === "ENOENT") throw err;
1400
1400
  return resolveScanFailure(reportPath, e, forwardStreams);
1401
1401
  } finally {
1402
- if (tempPath !== null) rmSync11(tempPath, { recursive: true, force: true });
1403
- rmSync11(reportPath, { force: true });
1402
+ if (tempPath !== null) rmSync12(tempPath, { recursive: true, force: true });
1403
+ rmSync12(reportPath, { force: true });
1404
1404
  }
1405
1405
  }
1406
1406
  function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TIMEOUT_MS) {
1407
- const cacheDir = join30(homedir3(), ".cache", "claude-nomad");
1408
- mkdirSync4(cacheDir, { recursive: true });
1407
+ const cacheDir = join31(homedir3(), ".cache", "claude-nomad");
1408
+ mkdirSync5(cacheDir, { recursive: true });
1409
1409
  const { path: toml, tempPath } = resolveTomlConfig();
1410
1410
  let reportDir;
1411
1411
  let reportPath;
1412
1412
  try {
1413
- reportDir = mkdtempSync2(join30(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
1414
- reportPath = join30(reportDir, "report.json");
1413
+ reportDir = mkdtempSync2(join31(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
1414
+ reportPath = join31(reportDir, "report.json");
1415
1415
  const args = [
1416
1416
  "detect",
1417
1417
  "--no-git",
@@ -1432,8 +1432,8 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
1432
1432
  if (e.code === "ENOENT") return null;
1433
1433
  return resolveScanFailure(reportPath, e, forwardStreams);
1434
1434
  } finally {
1435
- if (tempPath !== null) rmSync11(tempPath, { recursive: true, force: true });
1436
- if (reportDir !== void 0) rmSync11(reportDir, { recursive: true, force: true });
1435
+ if (tempPath !== null) rmSync12(tempPath, { recursive: true, force: true });
1436
+ if (reportDir !== void 0) rmSync12(reportDir, { recursive: true, force: true });
1437
1437
  }
1438
1438
  }
1439
1439
  var init_gitleaks_scan = __esm({
@@ -1640,7 +1640,7 @@ import { join as join10 } from "node:path";
1640
1640
 
1641
1641
  // src/commands/adopt.recover.ts
1642
1642
  import { rmSync as rmSync6 } from "node:fs";
1643
- import { dirname as dirname3, join as join8, resolve as resolve2 } from "node:path";
1643
+ import { dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
1644
1644
 
1645
1645
  // src/commands/adopt.scan.ts
1646
1646
  init_config();
@@ -1734,14 +1734,17 @@ import { basename as basename2, join as join4, relative as relative2 } from "nod
1734
1734
  // src/sync/extras/collision.ts
1735
1735
  init_utils();
1736
1736
  import { cpSync, existsSync, lstatSync as lstatSync2, readdirSync as readdirSync2, rmSync } from "node:fs";
1737
- import { basename, join as join3 } from "node:path";
1737
+ import { basename, dirname, join as join3 } from "node:path";
1738
1738
  function quoted(p) {
1739
1739
  return `"${p}"`;
1740
1740
  }
1741
- function stripCollidingDstSymlinks(src, dst, isExcluded) {
1741
+ function isRootExcludedEntry(src, entry, isRootExcluded) {
1742
+ return dirname(entry) === src && isRootExcluded?.(basename(entry)) === true;
1743
+ }
1744
+ function stripCollidingDstSymlinks(src, dst, isExcluded, isRootExcluded) {
1742
1745
  if (!existsSync(dst)) return;
1743
1746
  for (const name of readdirSync2(src)) {
1744
- if (isExcluded(name)) continue;
1747
+ if (isExcluded(name) || isRootExcluded?.(name) === true) continue;
1745
1748
  const dstPath = join3(dst, name);
1746
1749
  const dstStat = lstatSync2(dstPath, { throwIfNoEntry: false });
1747
1750
  if (dstStat === void 0) continue;
@@ -1782,7 +1785,7 @@ function prunePreservingBy(src, dst, isPreserved, isRootPreserved) {
1782
1785
  }
1783
1786
  }
1784
1787
  }
1785
- function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved) {
1788
+ function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved, isRootExcluded) {
1786
1789
  const dstStat = lstatSync2(dst, { throwIfNoEntry: false });
1787
1790
  if (dstStat !== void 0) {
1788
1791
  if (dstStat.isDirectory()) {
@@ -1792,17 +1795,22 @@ function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved)
1792
1795
  `copyExtrasFilteredPreservingBy: type mismatch copying ${quoted(src)} -> ${quoted(dst)}: the repo entry is a file but the local entry is a directory; run nomad pull --force-remote to recover`
1793
1796
  );
1794
1797
  }
1795
- prunePreservingBy(src, dst, isPreserved, isRootPreserved);
1798
+ prunePreservingBy(
1799
+ src,
1800
+ dst,
1801
+ isPreserved,
1802
+ (name) => isRootExcluded?.(name) === true || isRootPreserved?.(name) === true
1803
+ );
1796
1804
  } else {
1797
1805
  rmSync(dst, { recursive: true, force: true });
1798
1806
  }
1799
1807
  }
1800
- stripCollidingDstSymlinks(src, dst, isPreserved);
1808
+ stripCollidingDstSymlinks(src, dst, isPreserved, isRootExcluded);
1801
1809
  cpSync(src, dst, {
1802
1810
  recursive: true,
1803
1811
  force: true,
1804
1812
  verbatimSymlinks: true,
1805
- filter: (srcEntry) => srcEntry === src || !isPreserved(basename(srcEntry))
1813
+ filter: (srcEntry) => srcEntry === src || !isPreserved(basename(srcEntry)) && !isRootExcludedEntry(src, srcEntry, isRootExcluded)
1806
1814
  });
1807
1815
  }
1808
1816
 
@@ -1853,12 +1861,12 @@ function* eachExtrasTarget(v, counts) {
1853
1861
  counts.unmapped++;
1854
1862
  continue;
1855
1863
  }
1856
- for (const dirname18 of dirnames) {
1857
- if (!whitelist.includes(dirname18)) {
1864
+ for (const dirname19 of dirnames) {
1865
+ if (!whitelist.includes(dirname19)) {
1858
1866
  counts.skipped++;
1859
1867
  continue;
1860
1868
  }
1861
- yield { logical, localRoot, dirname: dirname18 };
1869
+ yield { logical, localRoot, dirname: dirname19 };
1862
1870
  }
1863
1871
  }
1864
1872
  }
@@ -1900,16 +1908,16 @@ function copyExtrasFileSkipDiverged(src, dst) {
1900
1908
  }
1901
1909
  copyExtras(src, dst);
1902
1910
  }
1903
- function extrasDenySet(dirname18) {
1904
- return isClaudeExtraName(dirname18) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1911
+ function extrasDenySet(dirname19) {
1912
+ return isClaudeExtraName(dirname19) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1905
1913
  }
1906
- function copyExtrasFiltered(src, dst, blockSet) {
1914
+ function copyExtrasFiltered(src, dst, blockSet, isRootExcluded) {
1907
1915
  rmSync2(dst, { recursive: true, force: true });
1908
1916
  cpSync2(src, dst, {
1909
1917
  recursive: true,
1910
1918
  force: true,
1911
1919
  verbatimSymlinks: true,
1912
- filter: (srcEntry) => srcEntry === src || !isDeniedName(blockSet, basename2(srcEntry))
1920
+ filter: (srcEntry) => srcEntry === src || !isDeniedName(blockSet, basename2(srcEntry)) && !isRootExcludedEntry(src, srcEntry, isRootExcluded)
1913
1921
  });
1914
1922
  }
1915
1923
  function prunePreservingDenied(src, dst, blockSet) {
@@ -2490,7 +2498,7 @@ init_config();
2490
2498
  init_utils_fs();
2491
2499
  init_utils();
2492
2500
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync3 } from "node:fs";
2493
- import { dirname as dirname2 } from "node:path";
2501
+ import { dirname as dirname3 } from "node:path";
2494
2502
  var SETTINGS_WRITTEN_KIND = "settings-written/2";
2495
2503
  function isObject(v) {
2496
2504
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -2510,7 +2518,7 @@ function readWrittenSettingsKeys() {
2510
2518
  function recordWrittenSettingsKeys(written) {
2511
2519
  try {
2512
2520
  const path = settingsWrittenPath();
2513
- mkdirSync2(dirname2(path), { recursive: true });
2521
+ mkdirSync2(dirname3(path), { recursive: true });
2514
2522
  rmSync3(path, { force: true });
2515
2523
  const keys = Object.fromEntries(
2516
2524
  Object.entries(stripGsdHookEntries(written)).map(([k, v]) => [k, settingValueHash(v)])
@@ -2729,7 +2737,7 @@ function regenerateSettings(ts, opts = {}) {
2729
2737
  // src/commands/adopt.recover.ts
2730
2738
  init_utils();
2731
2739
  function isDirectChildOf(root, abs) {
2732
- return dirname3(resolve2(abs)) === resolve2(root);
2740
+ return dirname4(resolve2(abs)) === resolve2(root);
2733
2741
  }
2734
2742
  function clearPartialCopy(linkPath) {
2735
2743
  return clearIfDirectChild(claudeHome(), linkPath);
@@ -2849,13 +2857,13 @@ init_utils_fs();
2849
2857
  init_config();
2850
2858
  init_utils();
2851
2859
  import { closeSync as closeSync2, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
2852
- import { dirname as dirname4, join as join9 } from "node:path";
2860
+ import { dirname as dirname5, join as join9 } from "node:path";
2853
2861
  function lockFilePath() {
2854
2862
  return join9(home(), ".cache", "claude-nomad", "nomad.lock");
2855
2863
  }
2856
2864
  function acquireLock(verb) {
2857
2865
  const lp = lockFilePath();
2858
- mkdirSync3(dirname4(lp), { recursive: true });
2866
+ mkdirSync3(dirname5(lp), { recursive: true });
2859
2867
  try {
2860
2868
  const fd = openSync2(lp, "wx");
2861
2869
  try {
@@ -3406,7 +3414,7 @@ init_config_sharedDirs_guard();
3406
3414
  init_utils();
3407
3415
  init_utils_json();
3408
3416
  import { existsSync as existsSync11, realpathSync as realpathSync2, rmSync as rmSync9 } from "node:fs";
3409
- import { basename as basename3, dirname as dirname5, join as join15 } from "node:path";
3417
+ import { basename as basename3, dirname as dirname6, join as join15 } from "node:path";
3410
3418
 
3411
3419
  // src/commands/eject.materialize.ts
3412
3420
  init_config();
@@ -3498,7 +3506,7 @@ function ejectChecklist() {
3498
3506
  ` 3. Optionally delete the local sync checkout: rm -rf ${shellPath(repoHome())}`,
3499
3507
  ` 4. Optionally delete the private sync repo on GitHub`,
3500
3508
  ` 5. Optionally delete nomad's cache folder, which holds backups and crash reports, once you`,
3501
- ` no longer need the backups in it: rm -rf ${shellPath(dirname5(backupBase()))}`
3509
+ ` no longer need the backups in it: rm -rf ${shellPath(dirname6(backupBase()))}`
3502
3510
  ].join("\n");
3503
3511
  }
3504
3512
  function forgetHostRecords(cacheDir, dryRun) {
@@ -3587,7 +3595,7 @@ function ejectNames(map) {
3587
3595
  return [.../* @__PURE__ */ new Set([...allSharedLinks(map), ...alreadyMaterialized])];
3588
3596
  }
3589
3597
  function defaultEjectRoots() {
3590
- return { claudeHome: claudeHome(), repoHome: repoHome(), cacheDir: dirname5(backupBase()) };
3598
+ return { claudeHome: claudeHome(), repoHome: repoHome(), cacheDir: dirname6(backupBase()) };
3591
3599
  }
3592
3600
  function cmdEject(opts = {}, roots = defaultEjectRoots()) {
3593
3601
  const dryRun = opts.dryRun === true;
@@ -4336,15 +4344,80 @@ function reportNeverSync(section2) {
4336
4344
 
4337
4345
  // src/commands/doctor/checks/skills.ts
4338
4346
  init_color();
4339
- import { existsSync as existsSync16 } from "node:fs";
4340
- import { join as join22 } from "node:path";
4347
+ import { existsSync as existsSync17 } from "node:fs";
4348
+ import { join as join23 } from "node:path";
4349
+ init_config();
4350
+
4351
+ // src/sync/skills-sync.ts
4341
4352
  init_config();
4353
+ import { existsSync as existsSync16, lstatSync as lstatSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
4354
+ import { join as join22 } from "node:path";
4355
+
4356
+ // src/sync/skills-sync.tracked.ts
4357
+ init_utils();
4358
+ import { basename as basename4 } from "node:path";
4359
+ function trackedRootSkillsAt(ref, repo) {
4360
+ try {
4361
+ const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
4362
+ const entries = raw.split("\0").filter((entry) => entry !== "");
4363
+ return new Set(entries.map((entry) => basename4(entry)));
4364
+ } catch {
4365
+ return /* @__PURE__ */ new Set();
4366
+ }
4367
+ }
4368
+
4369
+ // src/sync/skills-sync.ts
4370
+ init_utils_fs();
4371
+ function isGsdOwned(name) {
4372
+ return name.startsWith(GSD_PREFIX);
4373
+ }
4374
+ function isSkillExcluded(name) {
4375
+ return isGsdOwned(name) || isDeniedName(ALWAYS_NEVER_SYNC, name);
4376
+ }
4377
+ var ROOT_EXCLUDED_SKILLS_DIRS = /* @__PURE__ */ new Set(["synced", ".trash", ".staging"]);
4378
+ function isRootSkillExcluded(name) {
4379
+ return isSkillExcluded(name) || ROOT_EXCLUDED_SKILLS_DIRS.has(name.toLowerCase());
4380
+ }
4381
+ function copySkillsPush(src, dst) {
4382
+ const srcNames = readdirSync8(src, { encoding: "utf8" });
4383
+ const blockSet = /* @__PURE__ */ new Set([
4384
+ ...srcNames.filter((n) => isGsdOwned(n)),
4385
+ ...ALWAYS_NEVER_SYNC
4386
+ ]);
4387
+ copyExtrasFiltered(src, dst, blockSet, isRootSkillExcluded);
4388
+ }
4389
+ function copySkillsPull(src, dst, isRootPreserved) {
4390
+ copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved, isRootSkillExcluded);
4391
+ }
4392
+ function syncSkillsPull(ts, prePostHeads) {
4393
+ const sharedSkills = join22(repoHome(), "shared", "skills");
4394
+ if (!existsSync16(sharedSkills)) return;
4395
+ const localSkills = join22(claudeHome(), "skills");
4396
+ backupBeforeWrite(localSkills, ts);
4397
+ const dstStat = lstatSync13(localSkills, { throwIfNoEntry: false });
4398
+ if (dstStat?.isSymbolicLink() === true) {
4399
+ rmSync10(localSkills, { recursive: true, force: true });
4400
+ mkdirSync4(localSkills, { recursive: true });
4401
+ }
4402
+ const tracked = prePostHeads === void 0 ? /* @__PURE__ */ new Set() : trackedRootSkillsAt(prePostHeads.pre, repoHome());
4403
+ copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
4404
+ }
4405
+ function syncSkillsPush() {
4406
+ const localSkills = join22(claudeHome(), "skills");
4407
+ const stat = lstatSync13(localSkills, { throwIfNoEntry: false });
4408
+ if (stat === void 0) return;
4409
+ if (stat.isSymbolicLink()) return;
4410
+ const sharedSkills = join22(repoHome(), "shared", "skills");
4411
+ copySkillsPush(localSkills, sharedSkills);
4412
+ }
4413
+
4414
+ // src/commands/doctor/checks/skills.ts
4342
4415
  function stripSideIndicator(line) {
4343
4416
  if (line.endsWith(" (local only)")) return line.slice(0, -" (local only)".length);
4344
4417
  if (line.endsWith(" (repo only)")) return line.slice(0, -" (repo only)".length);
4345
4418
  return line;
4346
4419
  }
4347
- function isGsdDiffLine(line, localBase, sharedBase) {
4420
+ function isExcludedDiffLine(line, localBase, sharedBase) {
4348
4421
  const bare = stripSideIndicator(line);
4349
4422
  let relative12;
4350
4423
  if (bare.startsWith(localBase + "/")) {
@@ -4354,21 +4427,21 @@ function isGsdDiffLine(line, localBase, sharedBase) {
4354
4427
  } else {
4355
4428
  relative12 = bare;
4356
4429
  }
4357
- return relative12.split("/")[0].startsWith(GSD_PREFIX);
4430
+ return isRootSkillExcluded(relative12.split("/")[0]);
4358
4431
  }
4359
4432
  function reportSkillsDivergence(section2) {
4360
- const sharedSkills = join22(repoHome(), "shared", "skills");
4361
- const localSkills = join22(claudeHome(), "skills");
4362
- if (!existsSync16(sharedSkills)) {
4433
+ const sharedSkills = join23(repoHome(), "shared", "skills");
4434
+ const localSkills = join23(claudeHome(), "skills");
4435
+ if (!existsSync17(sharedSkills)) {
4363
4436
  addItem(section2, `${dim(infoGlyph)} skills: no shared/skills/ to compare`);
4364
4437
  return;
4365
4438
  }
4366
- if (!existsSync16(localSkills)) {
4439
+ if (!existsSync17(localSkills)) {
4367
4440
  addItem(section2, `${dim(infoGlyph)} skills: no local skills/ to compare`);
4368
4441
  return;
4369
4442
  }
4370
4443
  const diff = listDivergingFiles(localSkills, sharedSkills);
4371
- const relevant = diff.filter((line) => !isGsdDiffLine(line, localSkills, sharedSkills));
4444
+ const relevant = diff.filter((line) => !isExcludedDiffLine(line, localSkills, sharedSkills));
4372
4445
  if (relevant.length === 0) {
4373
4446
  addItem(section2, `${green(okGlyph)} skills: in sync with shared/skills/`);
4374
4447
  return;
@@ -4386,8 +4459,8 @@ function reportSkillsDivergence(section2) {
4386
4459
  init_color();
4387
4460
  init_config();
4388
4461
  import { execFileSync as execFileSync6 } from "node:child_process";
4389
- import { existsSync as existsSync20 } from "node:fs";
4390
- import { join as join27, relative as relative5 } from "node:path";
4462
+ import { existsSync as existsSync21 } from "node:fs";
4463
+ import { join as join28, relative as relative5 } from "node:path";
4391
4464
  init_wedge();
4392
4465
  init_checks();
4393
4466
  init_utils();
@@ -4410,8 +4483,8 @@ function reportGitleaksProbe(section2) {
4410
4483
  }
4411
4484
  function reportGitlinks(section2) {
4412
4485
  const repo = repoHome();
4413
- const sharedDir = join27(repo, "shared");
4414
- if (existsSync20(sharedDir)) {
4486
+ const sharedDir = join28(repo, "shared");
4487
+ if (existsSync21(sharedDir)) {
4415
4488
  const gitlinks = findGitlinks(sharedDir);
4416
4489
  for (const p of gitlinks) {
4417
4490
  const rel = relative5(repo, p).replaceAll("\\", "/");
@@ -4548,13 +4621,13 @@ function reportTrackedDeniedShared(section2) {
4548
4621
 
4549
4622
  // src/commands/doctor/checks/backups.ts
4550
4623
  init_color();
4551
- import { existsSync as existsSync21, lstatSync as lstatSync13, readdirSync as readdirSync9 } from "node:fs";
4552
- import { join as join28 } from "node:path";
4624
+ import { existsSync as existsSync22, lstatSync as lstatSync14, readdirSync as readdirSync10 } from "node:fs";
4625
+ import { join as join29 } from "node:path";
4553
4626
  init_config();
4554
4627
  var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
4555
4628
  function safeReaddir(dir) {
4556
4629
  try {
4557
- return readdirSync9(dir);
4630
+ return readdirSync10(dir);
4558
4631
  } catch {
4559
4632
  return [];
4560
4633
  }
@@ -4565,8 +4638,8 @@ var BYTES_PER_MB = 1024 * 1024;
4565
4638
  function dirSizeBytes(dir) {
4566
4639
  let bytes = 0;
4567
4640
  for (const entry of safeReaddir(dir)) {
4568
- const full = join28(dir, entry);
4569
- const st = lstatSync13(full, { throwIfNoEntry: false });
4641
+ const full = join29(dir, entry);
4642
+ const st = lstatSync14(full, { throwIfNoEntry: false });
4570
4643
  if (!st) continue;
4571
4644
  if (st.isSymbolicLink()) continue;
4572
4645
  if (st.isDirectory()) bytes += dirSizeBytes(full);
@@ -4576,11 +4649,11 @@ function dirSizeBytes(dir) {
4576
4649
  }
4577
4650
  function totalSizeMb(backupBase2, dirs) {
4578
4651
  let bytes = 0;
4579
- for (const name of dirs) bytes += dirSizeBytes(join28(backupBase2, name));
4652
+ for (const name of dirs) bytes += dirSizeBytes(join29(backupBase2, name));
4580
4653
  return bytes / BYTES_PER_MB;
4581
4654
  }
4582
4655
  function reportBackupsCheck(section2, backupBase2 = backupBase()) {
4583
- if (!existsSync21(backupBase2)) return;
4656
+ if (!existsSync22(backupBase2)) return;
4584
4657
  const dirs = safeReaddir(backupBase2).filter((n) => TS_SHAPE2.test(n));
4585
4658
  const count = dirs.length;
4586
4659
  const sizeMb = totalSizeMb(backupBase2, dirs);
@@ -4661,8 +4734,8 @@ function reportCheckRemote(section2) {
4661
4734
 
4662
4735
  // src/commands/doctor/checks/schema.ts
4663
4736
  init_color();
4664
- import { existsSync as existsSync22 } from "node:fs";
4665
- import { join as join29 } from "node:path";
4737
+ import { existsSync as existsSync23 } from "node:fs";
4738
+ import { join as join30 } from "node:path";
4666
4739
  init_config();
4667
4740
 
4668
4741
  // src/core/http-fetch.ts
@@ -4699,8 +4772,8 @@ function fetchSchemaKeys() {
4699
4772
  }
4700
4773
  }
4701
4774
  function reportCheckSchema(section2) {
4702
- const settingsPath = join29(claudeHome(), "settings.json");
4703
- if (!existsSync22(settingsPath)) {
4775
+ const settingsPath = join30(claudeHome(), "settings.json");
4776
+ if (!existsSync23(settingsPath)) {
4704
4777
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json to check`);
4705
4778
  return;
4706
4779
  }
@@ -4730,17 +4803,17 @@ function reportCheckSchema(section2) {
4730
4803
  init_color();
4731
4804
  import { randomBytes as randomBytes3 } from "node:crypto";
4732
4805
  import { execFileSync as execFileSync12 } from "node:child_process";
4733
- import { existsSync as existsSync26, mkdirSync as mkdirSync9, readdirSync as readdirSync13, rmSync as rmSync15 } from "node:fs";
4806
+ import { existsSync as existsSync27, mkdirSync as mkdirSync10, readdirSync as readdirSync14, rmSync as rmSync16 } from "node:fs";
4734
4807
  import { homedir as homedir6 } from "node:os";
4735
- import { join as join37 } from "node:path";
4808
+ import { join as join38 } from "node:path";
4736
4809
 
4737
4810
  // src/commands/doctor/checks/shared.memory.ts
4738
4811
  init_color();
4739
4812
  import { execFileSync as execFileSync10 } from "node:child_process";
4740
4813
  import { randomBytes } from "node:crypto";
4741
- import { mkdirSync as mkdirSync5, rmSync as rmSync12, writeFileSync as writeFileSync4 } from "node:fs";
4814
+ import { mkdirSync as mkdirSync6, rmSync as rmSync13, writeFileSync as writeFileSync4 } from "node:fs";
4742
4815
  import { homedir as homedir4 } from "node:os";
4743
- import { dirname as dirname7, join as join31 } from "node:path";
4816
+ import { dirname as dirname8, join as join32 } from "node:path";
4744
4817
  init_config();
4745
4818
  init_gitleaks();
4746
4819
  init_utils_fs();
@@ -4773,8 +4846,8 @@ function buildMemoryScanTree(tmpRoot) {
4773
4846
  incomplete = true;
4774
4847
  continue;
4775
4848
  }
4776
- const dest = join31(tmpRoot, rel);
4777
- mkdirSync5(dirname7(dest), { recursive: true });
4849
+ const dest = join32(tmpRoot, rel);
4850
+ mkdirSync6(dirname8(dest), { recursive: true });
4778
4851
  writeFileSync4(dest, blob);
4779
4852
  logicals.add(m[1]);
4780
4853
  }
@@ -4792,11 +4865,11 @@ function reportMemoryFindings(section2, findings) {
4792
4865
  function reportCommittedMemory(section2) {
4793
4866
  let tmpRoot;
4794
4867
  try {
4795
- const cacheDir = join31(homedir4(), ".cache", "claude-nomad");
4796
- mkdirSync5(cacheDir, { recursive: true });
4868
+ const cacheDir = join32(homedir4(), ".cache", "claude-nomad");
4869
+ mkdirSync6(cacheDir, { recursive: true });
4797
4870
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
4798
- tmpRoot = join31(cacheDir, `check-shared-memory-tree-${stamp}`);
4799
- mkdirSync5(tmpRoot, { recursive: true, mode: 448 });
4871
+ tmpRoot = join32(cacheDir, `check-shared-memory-tree-${stamp}`);
4872
+ mkdirSync6(tmpRoot, { recursive: true, mode: 448 });
4800
4873
  const { staged, incomplete } = buildMemoryScanTree(tmpRoot);
4801
4874
  if (incomplete) {
4802
4875
  addItem(
@@ -4833,7 +4906,7 @@ function reportCommittedMemory(section2) {
4833
4906
  } finally {
4834
4907
  if (tmpRoot !== void 0) {
4835
4908
  try {
4836
- rmSync12(tmpRoot, { recursive: true, force: true });
4909
+ rmSync13(tmpRoot, { recursive: true, force: true });
4837
4910
  } catch {
4838
4911
  }
4839
4912
  }
@@ -4842,12 +4915,12 @@ function reportCommittedMemory(section2) {
4842
4915
 
4843
4916
  // src/commands/doctor/checks/shared.scan.ts
4844
4917
  init_color();
4845
- import { join as join32 } from "node:path";
4918
+ import { join as join33 } from "node:path";
4846
4919
  init_config();
4847
4920
  init_gitleaks();
4848
4921
  function scrubPath(logical, sid, logicalToEncoded) {
4849
4922
  const encoded = logicalToEncoded.get(logical) ?? logical;
4850
- return join32(claudeHome(), "projects", encoded, `${sid}.jsonl`);
4923
+ return join33(claudeHome(), "projects", encoded, `${sid}.jsonl`);
4851
4924
  }
4852
4925
  function reportSessionFindings(section2, bySession) {
4853
4926
  for (const [sid, counts] of bySession) {
@@ -4934,9 +5007,9 @@ function scanAndReport(section2, tmpRoot, staged, logicalToEncoded) {
4934
5007
  init_color();
4935
5008
  import { execFileSync as execFileSync11 } from "node:child_process";
4936
5009
  import { randomBytes as randomBytes2 } from "node:crypto";
4937
- import { mkdirSync as mkdirSync6, rmSync as rmSync13, writeFileSync as writeFileSync5 } from "node:fs";
5010
+ import { mkdirSync as mkdirSync7, rmSync as rmSync14, writeFileSync as writeFileSync5 } from "node:fs";
4938
5011
  import { homedir as homedir5 } from "node:os";
4939
- import { dirname as dirname8, join as join33 } from "node:path";
5012
+ import { dirname as dirname9, join as join34 } from "node:path";
4940
5013
  init_config();
4941
5014
  init_gitleaks();
4942
5015
  init_utils_fs();
@@ -4969,8 +5042,8 @@ function buildSkillScanTree(tmpRoot) {
4969
5042
  incomplete = true;
4970
5043
  continue;
4971
5044
  }
4972
- const dest = join33(tmpRoot, rel);
4973
- mkdirSync6(dirname8(dest), { recursive: true });
5045
+ const dest = join34(tmpRoot, rel);
5046
+ mkdirSync7(dirname9(dest), { recursive: true });
4974
5047
  writeFileSync5(dest, blob);
4975
5048
  names.add(m[1]);
4976
5049
  }
@@ -4992,11 +5065,11 @@ function reportSkillFindings(section2, findings) {
4992
5065
  function reportCommittedSkills(section2) {
4993
5066
  let tmpRoot;
4994
5067
  try {
4995
- const cacheDir = join33(homedir5(), ".cache", "claude-nomad");
4996
- mkdirSync6(cacheDir, { recursive: true });
5068
+ const cacheDir = join34(homedir5(), ".cache", "claude-nomad");
5069
+ mkdirSync7(cacheDir, { recursive: true });
4997
5070
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
4998
- tmpRoot = join33(cacheDir, `check-shared-skills-tree-${stamp}`);
4999
- mkdirSync6(tmpRoot, { recursive: true, mode: 448 });
5071
+ tmpRoot = join34(cacheDir, `check-shared-skills-tree-${stamp}`);
5072
+ mkdirSync7(tmpRoot, { recursive: true, mode: 448 });
5000
5073
  const { staged, incomplete } = buildSkillScanTree(tmpRoot);
5001
5074
  if (incomplete) {
5002
5075
  addItem(
@@ -5033,7 +5106,7 @@ function reportCommittedSkills(section2) {
5033
5106
  } finally {
5034
5107
  if (tmpRoot !== void 0) {
5035
5108
  try {
5036
- rmSync13(tmpRoot, { recursive: true, force: true });
5109
+ rmSync14(tmpRoot, { recursive: true, force: true });
5037
5110
  } catch {
5038
5111
  }
5039
5112
  }
@@ -5045,8 +5118,8 @@ init_config();
5045
5118
 
5046
5119
  // src/sync/remap.ts
5047
5120
  init_config_sharedDirs_guard();
5048
- import { cpSync as cpSync5, existsSync as existsSync25, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync14, statSync as statSync6 } from "node:fs";
5049
- import { dirname as dirname10, join as join36, relative as relative6, sep as sep5 } from "node:path";
5121
+ import { cpSync as cpSync5, existsSync as existsSync26, mkdirSync as mkdirSync9, readdirSync as readdirSync13, rmSync as rmSync15, statSync as statSync6 } from "node:fs";
5122
+ import { dirname as dirname11, join as join37, relative as relative6, sep as sep5 } from "node:path";
5050
5123
  init_config();
5051
5124
 
5052
5125
  // src/commands/push/manifest.ts
@@ -5054,8 +5127,8 @@ init_config();
5054
5127
  init_gitleaks_config();
5055
5128
  init_utils_fs();
5056
5129
  import { createHash as createHash2 } from "node:crypto";
5057
- import { existsSync as existsSync23, mkdirSync as mkdirSync7, readdirSync as readdirSync10, readFileSync as readFileSync8, statSync as statSync5 } from "node:fs";
5058
- import { dirname as dirname9, join as join34 } from "node:path";
5130
+ import { existsSync as existsSync24, mkdirSync as mkdirSync8, readdirSync as readdirSync11, readFileSync as readFileSync8, statSync as statSync5 } from "node:fs";
5131
+ import { dirname as dirname10, join as join35 } from "node:path";
5059
5132
  function isChanged(prev, cur, hash) {
5060
5133
  if (prev === void 0) return true;
5061
5134
  if (prev.size !== cur.size) return true;
@@ -5095,8 +5168,8 @@ function hashFile(absPath) {
5095
5168
  return createHash2("sha256").update(readFileSync8(absPath)).digest("hex");
5096
5169
  }
5097
5170
  function enumerateDir(dir, results) {
5098
- for (const entry of readdirSync10(dir)) {
5099
- const fullPath = join34(dir, entry);
5171
+ for (const entry of readdirSync11(dir)) {
5172
+ const fullPath = join35(dir, entry);
5100
5173
  const st = statSync5(fullPath);
5101
5174
  if (st.isDirectory()) {
5102
5175
  enumerateDir(fullPath, results);
@@ -5107,8 +5180,8 @@ function enumerateDir(dir, results) {
5107
5180
  }
5108
5181
  function enumerateSourceFiles(projectDir) {
5109
5182
  const results = [];
5110
- for (const entry of readdirSync10(projectDir)) {
5111
- const fullPath = join34(projectDir, entry);
5183
+ for (const entry of readdirSync11(projectDir)) {
5184
+ const fullPath = join35(projectDir, entry);
5112
5185
  const st = statSync5(fullPath);
5113
5186
  if (st.isDirectory()) {
5114
5187
  enumerateDir(fullPath, results);
@@ -5120,15 +5193,15 @@ function enumerateSourceFiles(projectDir) {
5120
5193
  }
5121
5194
  function fileIdentity(p) {
5122
5195
  if (p === null) return "none::absent";
5123
- if (!existsSync23(p)) return `${p}::absent`;
5196
+ if (!existsSync24(p)) return `${p}::absent`;
5124
5197
  return `${p}::${createHash2("sha256").update(readFileSync8(p)).digest("hex")}`;
5125
5198
  }
5126
5199
  function computeConfigHash() {
5127
5200
  const repo = repoHome();
5128
5201
  const parts = [
5129
5202
  fileIdentity(resolveTomlPath(repo)),
5130
- fileIdentity(join34(repo, ".gitleaks.overlay.toml")),
5131
- fileIdentity(join34(repo, ".gitleaksignore"))
5203
+ fileIdentity(join35(repo, ".gitleaks.overlay.toml")),
5204
+ fileIdentity(join35(repo, ".gitleaksignore"))
5132
5205
  ];
5133
5206
  return createHash2("sha256").update(parts.join("\n")).digest("hex");
5134
5207
  }
@@ -5148,7 +5221,7 @@ function readManifest(path) {
5148
5221
  }
5149
5222
  }
5150
5223
  function writeManifest(path, manifest) {
5151
- mkdirSync7(dirname9(path), { recursive: true });
5224
+ mkdirSync8(dirname10(path), { recursive: true });
5152
5225
  writeJsonAtomic(path, manifest);
5153
5226
  }
5154
5227
  function buildManifest(files, scannerVersion, configHash) {
@@ -5162,18 +5235,18 @@ init_utils_json();
5162
5235
 
5163
5236
  // src/sync/remap.scan.ts
5164
5237
  init_config_sharedDirs_guard();
5165
- import { existsSync as existsSync24, lstatSync as lstatSync14, readdirSync as readdirSync11 } from "node:fs";
5166
- import { join as join35 } from "node:path";
5238
+ import { existsSync as existsSync25, lstatSync as lstatSync15, readdirSync as readdirSync12 } from "node:fs";
5239
+ import { join as join36 } from "node:path";
5167
5240
  init_config();
5168
5241
  init_utils_json();
5169
5242
  function countLocalOnly(src, dst) {
5170
5243
  let count = 0;
5171
- for (const name of readdirSync11(dst)) {
5172
- const dstPath = join35(dst, name);
5173
- const srcPath = join35(src, name);
5174
- if (lstatSync14(dstPath).isDirectory()) {
5244
+ for (const name of readdirSync12(dst)) {
5245
+ const dstPath = join36(dst, name);
5246
+ const srcPath = join36(src, name);
5247
+ if (lstatSync15(dstPath).isDirectory()) {
5175
5248
  count += countLocalOnly(srcPath, dstPath);
5176
- } else if (lstatSync14(srcPath, { throwIfNoEntry: false }) === void 0) {
5249
+ } else if (lstatSync15(srcPath, { throwIfNoEntry: false }) === void 0) {
5177
5250
  count++;
5178
5251
  }
5179
5252
  }
@@ -5182,20 +5255,20 @@ function countLocalOnly(src, dst) {
5182
5255
  function scanLocalOnly() {
5183
5256
  const repo = repoHome();
5184
5257
  const claude = claudeHome();
5185
- const mapPath = join35(repo, "path-map.json");
5186
- const repoProjects = join35(repo, "shared", "projects");
5187
- if (!existsSync24(mapPath) || !existsSync24(repoProjects)) return 0;
5258
+ const mapPath = join36(repo, "path-map.json");
5259
+ const repoProjects = join36(repo, "shared", "projects");
5260
+ if (!existsSync25(mapPath) || !existsSync25(repoProjects)) return 0;
5188
5261
  const map = readPathMap(mapPath);
5189
- const localProjects = join35(claude, "projects");
5262
+ const localProjects = join36(claude, "projects");
5190
5263
  let count = 0;
5191
5264
  for (const [logical, hosts] of Object.entries(map.projects)) {
5192
5265
  assertSafeLogical(logical);
5193
5266
  const localPath = hosts[HOST];
5194
5267
  if (!localPath || localPath === "TBD") continue;
5195
5268
  assertSafeLocalRoot(localPath, logical);
5196
- const dst = join35(localProjects, encodePath(localPath));
5197
- if (!existsSync24(dst)) continue;
5198
- count += countLocalOnly(join35(repoProjects, logical), dst);
5269
+ const dst = join36(localProjects, encodePath(localPath));
5270
+ if (!existsSync25(dst)) continue;
5271
+ count += countLocalOnly(join36(repoProjects, logical), dst);
5199
5272
  }
5200
5273
  return count;
5201
5274
  }
@@ -5204,9 +5277,9 @@ function scanLocalOnly() {
5204
5277
  var TMP_SUFFIX = ".nomad-tmp";
5205
5278
  function atomicMirror(src, dst, options) {
5206
5279
  const tmp = `${dst}${TMP_SUFFIX}`;
5207
- rmSync14(tmp, { recursive: true, force: true });
5280
+ rmSync15(tmp, { recursive: true, force: true });
5208
5281
  cpSync5(src, tmp, options);
5209
- rmSync14(dst, { recursive: true, force: true });
5282
+ rmSync15(dst, { recursive: true, force: true });
5210
5283
  renameAtomicRetry(tmp, dst);
5211
5284
  }
5212
5285
  function overlaySessionDir(src, dst) {
@@ -5214,7 +5287,7 @@ function overlaySessionDir(src, dst) {
5214
5287
  cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
5215
5288
  }
5216
5289
  function copyFileAtomic(src, dst) {
5217
- mkdirSync8(dirname10(dst), { recursive: true });
5290
+ mkdirSync9(dirname11(dst), { recursive: true });
5218
5291
  const tmp = `${dst}.tmp.${process.pid}`;
5219
5292
  cpSync5(src, tmp, { force: true, preserveTimestamps: true });
5220
5293
  renameAtomicRetry(tmp, dst);
@@ -5231,12 +5304,12 @@ function applySelective(sel, localDir, repoDst) {
5231
5304
  const prefix = `${localDir}${sep5}`;
5232
5305
  for (const src of sel.changed) {
5233
5306
  if (!src.startsWith(prefix)) continue;
5234
- copyFileAtomic(src, join36(repoDst, relative6(localDir, src)));
5307
+ copyFileAtomic(src, join37(repoDst, relative6(localDir, src)));
5235
5308
  }
5236
5309
  for (const src of sel.deleted) {
5237
5310
  if (!src.startsWith(prefix)) continue;
5238
- const dst = join36(repoDst, relative6(localDir, src));
5239
- if (existsSync25(dst)) rmSync14(dst);
5311
+ const dst = join37(repoDst, relative6(localDir, src));
5312
+ if (existsSync26(dst)) rmSync15(dst);
5240
5313
  }
5241
5314
  }
5242
5315
  function copyDirJsonlOnly(src, dst) {
@@ -5265,16 +5338,16 @@ function remapPull(ts, opts = {}) {
5265
5338
  const wouldPull = [];
5266
5339
  const repo = repoHome();
5267
5340
  const claude = claudeHome();
5268
- const mapPath = join36(repo, "path-map.json");
5269
- const repoProjects = join36(repo, "shared", "projects");
5270
- if (!existsSync25(mapPath) || !existsSync25(repoProjects)) {
5341
+ const mapPath = join37(repo, "path-map.json");
5342
+ const repoProjects = join37(repo, "shared", "projects");
5343
+ if (!existsSync26(mapPath) || !existsSync26(repoProjects)) {
5271
5344
  const text = "no path-map or repo projects dir; skipping session remap";
5272
5345
  emitPreview(opts.onPreview, { kind: "note", text }, text);
5273
5346
  return { unmapped: 0, pulled, wouldPull };
5274
5347
  }
5275
5348
  const map = readPathMap(mapPath);
5276
- const localProjects = join36(claude, "projects");
5277
- if (!dryRun) mkdirSync8(localProjects, { recursive: true });
5349
+ const localProjects = join37(claude, "projects");
5350
+ if (!dryRun) mkdirSync9(localProjects, { recursive: true });
5278
5351
  for (const [logical, hosts] of Object.entries(map.projects)) {
5279
5352
  assertSafeLogical(logical);
5280
5353
  const localPath = hosts[HOST];
@@ -5283,9 +5356,9 @@ function remapPull(ts, opts = {}) {
5283
5356
  continue;
5284
5357
  }
5285
5358
  assertSafeLocalRoot(localPath, logical);
5286
- const src = join36(repoProjects, logical);
5287
- if (!existsSync25(src)) continue;
5288
- const dst = join36(localProjects, encodePath(localPath));
5359
+ const src = join37(repoProjects, logical);
5360
+ if (!existsSync26(src)) continue;
5361
+ const dst = join37(localProjects, encodePath(localPath));
5289
5362
  if (dryRun) {
5290
5363
  wouldPull.push(logical);
5291
5364
  emitPreview(
@@ -5332,26 +5405,26 @@ function remapPush(ts, opts = {}) {
5332
5405
  const wouldPush = [];
5333
5406
  const repo = repoHome();
5334
5407
  const claude = claudeHome();
5335
- const mapPath = join36(repo, "path-map.json");
5336
- if (!existsSync25(mapPath)) {
5408
+ const mapPath = join37(repo, "path-map.json");
5409
+ if (!existsSync26(mapPath)) {
5337
5410
  log("no path-map.json; skipping session export");
5338
5411
  return { unmapped: 0, collisions: 0, pushed, wouldPush };
5339
5412
  }
5340
5413
  const map = readPathMap(mapPath);
5341
- const localProjects = join36(claude, "projects");
5342
- const repoProjects = join36(repo, "shared", "projects");
5414
+ const localProjects = join37(claude, "projects");
5415
+ const repoProjects = join37(repo, "shared", "projects");
5343
5416
  const reverse = buildReverseMap(map);
5344
- if (!existsSync25(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
5345
- if (!dryRun) mkdirSync8(repoProjects, { recursive: true });
5346
- for (const dir of readdirSync12(localProjects)) {
5417
+ if (!existsSync26(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
5418
+ if (!dryRun) mkdirSync9(repoProjects, { recursive: true });
5419
+ for (const dir of readdirSync13(localProjects)) {
5347
5420
  if (dir.endsWith(TMP_SUFFIX)) continue;
5348
5421
  const logical = reverse.get(dir);
5349
5422
  if (!logical) {
5350
5423
  unmapped++;
5351
5424
  continue;
5352
5425
  }
5353
- const localDir = join36(localProjects, dir);
5354
- const repoDst = join36(repoProjects, logical);
5426
+ const localDir = join37(localProjects, dir);
5427
+ const repoDst = join37(repoProjects, logical);
5355
5428
  if (skipForSelection(opts.selection, localDir)) continue;
5356
5429
  if (dryRun) {
5357
5430
  wouldPush.push(logical);
@@ -5375,8 +5448,8 @@ function buildScanTree(tmpRoot) {
5375
5448
  const logicalToEncoded = /* @__PURE__ */ new Map();
5376
5449
  let staged = 0;
5377
5450
  const repo = repoHome();
5378
- const mapPath = join37(repo, "path-map.json");
5379
- if (!existsSync26(mapPath)) return { logicalToEncoded, staged, malformed: false };
5451
+ const mapPath = join38(repo, "path-map.json");
5452
+ if (!existsSync27(mapPath)) return { logicalToEncoded, staged, malformed: false };
5380
5453
  let map;
5381
5454
  try {
5382
5455
  map = readJson(mapPath);
@@ -5393,12 +5466,12 @@ function buildScanTree(tmpRoot) {
5393
5466
  if (!p || p === "TBD") continue;
5394
5467
  reverse.set(encodePath(p), logical);
5395
5468
  }
5396
- const localProjects = join37(claudeHome(), "projects");
5397
- if (!existsSync26(localProjects)) return { logicalToEncoded, staged, malformed: false };
5398
- for (const dir of readdirSync13(localProjects)) {
5469
+ const localProjects = join38(claudeHome(), "projects");
5470
+ if (!existsSync27(localProjects)) return { logicalToEncoded, staged, malformed: false };
5471
+ for (const dir of readdirSync14(localProjects)) {
5399
5472
  const logical = reverse.get(dir);
5400
5473
  if (!logical) continue;
5401
- copyDirJsonlOnly(join37(localProjects, dir), join37(tmpRoot, "shared", "projects", logical));
5474
+ copyDirJsonlOnly(join38(localProjects, dir), join38(tmpRoot, "shared", "projects", logical));
5402
5475
  logicalToEncoded.set(logical, dir);
5403
5476
  staged++;
5404
5477
  }
@@ -5428,11 +5501,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
5428
5501
  return true;
5429
5502
  }
5430
5503
  function runLocalPreviewScan(section2) {
5431
- const cacheDir = join37(homedir6(), ".cache", "claude-nomad");
5432
- mkdirSync9(cacheDir, { recursive: true });
5504
+ const cacheDir = join38(homedir6(), ".cache", "claude-nomad");
5505
+ mkdirSync10(cacheDir, { recursive: true });
5433
5506
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
5434
- const reportPath = join37(cacheDir, `check-shared-${stamp}.json`);
5435
- const tmpRoot = join37(cacheDir, `check-shared-tree-${stamp}`);
5507
+ const reportPath = join38(cacheDir, `check-shared-${stamp}.json`);
5508
+ const tmpRoot = join38(cacheDir, `check-shared-tree-${stamp}`);
5436
5509
  try {
5437
5510
  const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
5438
5511
  if (malformed) {
@@ -5446,8 +5519,8 @@ function runLocalPreviewScan(section2) {
5446
5519
  }
5447
5520
  scanAndReport(section2, tmpRoot, staged, logicalToEncoded);
5448
5521
  } finally {
5449
- rmSync15(reportPath, { force: true });
5450
- rmSync15(tmpRoot, { recursive: true, force: true });
5522
+ rmSync16(reportPath, { force: true });
5523
+ rmSync16(tmpRoot, { recursive: true, force: true });
5451
5524
  }
5452
5525
  }
5453
5526
  function reportCheckShared(section2, gitleaksReady) {
@@ -5465,8 +5538,8 @@ function reportCheckShared(section2, gitleaksReady) {
5465
5538
 
5466
5539
  // src/commands/doctor/checks/hooks.scope.ts
5467
5540
  init_color();
5468
- import { existsSync as existsSync27, readFileSync as readFileSync9, readdirSync as readdirSync14, realpathSync as realpathSync4 } from "node:fs";
5469
- import { dirname as dirname11, extname, join as join38 } from "node:path";
5541
+ import { existsSync as existsSync28, readFileSync as readFileSync9, readdirSync as readdirSync15, realpathSync as realpathSync4 } from "node:fs";
5542
+ import { dirname as dirname12, extname, join as join39 } from "node:path";
5470
5543
  init_config();
5471
5544
  function typeFromPackageJson(pkgPath) {
5472
5545
  try {
@@ -5486,11 +5559,11 @@ function effectiveType(hookPath) {
5486
5559
  } catch {
5487
5560
  return null;
5488
5561
  }
5489
- let dir = dirname11(real);
5562
+ let dir = dirname12(real);
5490
5563
  for (; ; ) {
5491
- const pkg = join38(dir, "package.json");
5492
- if (existsSync27(pkg)) return typeFromPackageJson(pkg);
5493
- const parent = dirname11(dir);
5564
+ const pkg = join39(dir, "package.json");
5565
+ if (existsSync28(pkg)) return typeFromPackageJson(pkg);
5566
+ const parent = dirname12(dir);
5494
5567
  if (parent === dir) return "cjs";
5495
5568
  dir = parent;
5496
5569
  }
@@ -5518,7 +5591,7 @@ function remedy(family) {
5518
5591
  }
5519
5592
  function safeReaddir2(dir) {
5520
5593
  try {
5521
- return readdirSync14(dir);
5594
+ return readdirSync15(dir);
5522
5595
  } catch {
5523
5596
  return [];
5524
5597
  }
@@ -5531,15 +5604,15 @@ function safeRead(path) {
5531
5604
  }
5532
5605
  }
5533
5606
  function reportHookScopeCheck(section2) {
5534
- const hooksDir = join38(claudeHome(), "hooks");
5535
- if (!existsSync27(hooksDir)) {
5607
+ const hooksDir = join39(claudeHome(), "hooks");
5608
+ if (!existsSync28(hooksDir)) {
5536
5609
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
5537
5610
  return;
5538
5611
  }
5539
5612
  let anyWarn = false;
5540
5613
  for (const name of safeReaddir2(hooksDir)) {
5541
5614
  if (extname(name) !== ".js") continue;
5542
- const abs = join38(hooksDir, name);
5615
+ const abs = join39(hooksDir, name);
5543
5616
  const eff = effectiveType(abs);
5544
5617
  if (eff === null) continue;
5545
5618
  const src = safeRead(abs);
@@ -5559,8 +5632,8 @@ function reportHookScopeCheck(section2) {
5559
5632
 
5560
5633
  // src/commands/doctor/checks/hooks.ts
5561
5634
  init_color();
5562
- import { existsSync as existsSync28 } from "node:fs";
5563
- import { join as join39 } from "node:path";
5635
+ import { existsSync as existsSync29 } from "node:fs";
5636
+ import { join as join40 } from "node:path";
5564
5637
  init_config();
5565
5638
  function expandHome(token) {
5566
5639
  const h = home();
@@ -5603,7 +5676,7 @@ function checkEventGroups(section2, event, groups) {
5603
5676
  for (const group of groups) {
5604
5677
  for (const cmd of commandsFromGroup(group)) {
5605
5678
  for (const resolved of claudePathsIn(cmd)) {
5606
- if (existsSync28(resolved)) continue;
5679
+ if (existsSync29(resolved)) continue;
5607
5680
  addItem(
5608
5681
  section2,
5609
5682
  `${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
@@ -5616,8 +5689,8 @@ function checkEventGroups(section2, event, groups) {
5616
5689
  return anyFail;
5617
5690
  }
5618
5691
  function reportHooksTargetCheck(section2) {
5619
- const settingsPath = join39(claudeHome(), "settings.json");
5620
- if (!existsSync28(settingsPath)) {
5692
+ const settingsPath = join40(claudeHome(), "settings.json");
5693
+ if (!existsSync29(settingsPath)) {
5621
5694
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
5622
5695
  return;
5623
5696
  }
@@ -5640,12 +5713,12 @@ function reportHooksTargetCheck(section2) {
5640
5713
 
5641
5714
  // src/commands/doctor/checks/hooks.preserve-symlinks.ts
5642
5715
  init_color();
5643
- import { existsSync as existsSync30, readFileSync as readFileSync10 } from "node:fs";
5644
- import { join as join41 } from "node:path";
5716
+ import { existsSync as existsSync31, readFileSync as readFileSync10 } from "node:fs";
5717
+ import { join as join42 } from "node:path";
5645
5718
 
5646
5719
  // src/commands/doctor/checks/hooks.preserve-symlinks.probe.ts
5647
- import { closeSync as closeSync3, existsSync as existsSync29, openSync as openSync3, readSync, realpathSync as realpathSync5 } from "node:fs";
5648
- import { dirname as dirname12, join as join40, resolve as resolve3 } from "node:path";
5720
+ import { closeSync as closeSync3, existsSync as existsSync30, openSync as openSync3, readSync, realpathSync as realpathSync5 } from "node:fs";
5721
+ import { dirname as dirname13, join as join41, resolve as resolve3 } from "node:path";
5649
5722
  function suppressedRanges(src) {
5650
5723
  const ranges = [];
5651
5724
  const re = /\/\*[^]*?\*\/|\/\/[^\n]*|(['"`])(?:\\[^]|[^\\])*?\1/g;
@@ -5677,12 +5750,12 @@ function relativeSpecifiers(src) {
5677
5750
  }
5678
5751
  function specifierIsMissing(specifier, baseDir) {
5679
5752
  const base = resolve3(baseDir, specifier);
5680
- if (existsSync29(base)) return false;
5753
+ if (existsSync30(base)) return false;
5681
5754
  for (const ext of [".js", ".cjs", ".mjs"]) {
5682
- if (existsSync29(base + ext)) return false;
5755
+ if (existsSync30(base + ext)) return false;
5683
5756
  }
5684
5757
  for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
5685
- if (existsSync29(join40(base, idx))) return false;
5758
+ if (existsSync30(join41(base, idx))) return false;
5686
5759
  }
5687
5760
  return true;
5688
5761
  }
@@ -5708,7 +5781,7 @@ function relativeRequireTargetsBroken(scriptPath) {
5708
5781
  }
5709
5782
  const specifiers = relativeSpecifiers(raw);
5710
5783
  if (specifiers.length === 0) return false;
5711
- const baseDir = dirname12(realPath);
5784
+ const baseDir = dirname13(realPath);
5712
5785
  for (const spec of specifiers) {
5713
5786
  if (specifierIsMissing(spec, baseDir)) return true;
5714
5787
  }
@@ -5737,8 +5810,8 @@ function commandTokens(command) {
5737
5810
  return tokens;
5738
5811
  }
5739
5812
  function readPathMapSafe() {
5740
- const mapPath = join41(repoHome(), "path-map.json");
5741
- if (!existsSync30(mapPath)) return { projects: {} };
5813
+ const mapPath = join42(repoHome(), "path-map.json");
5814
+ if (!existsSync31(mapPath)) return { projects: {} };
5742
5815
  try {
5743
5816
  return JSON.parse(readFileSync10(mapPath, "utf8"));
5744
5817
  } catch {
@@ -5812,8 +5885,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
5812
5885
  return anyWarn;
5813
5886
  }
5814
5887
  function reportPreserveSymlinksCheck(section2) {
5815
- const settingsPath = join41(claudeHome(), "settings.json");
5816
- if (!existsSync30(settingsPath)) {
5888
+ const settingsPath = join42(claudeHome(), "settings.json");
5889
+ if (!existsSync31(settingsPath)) {
5817
5890
  addItem(
5818
5891
  section2,
5819
5892
  `${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
@@ -5846,8 +5919,8 @@ function reportPreserveSymlinksCheck(section2) {
5846
5919
 
5847
5920
  // src/commands/doctor/checks/settings-drift.ts
5848
5921
  init_color();
5849
- import { existsSync as existsSync31, readFileSync as readFileSync11 } from "node:fs";
5850
- import { join as join42 } from "node:path";
5922
+ import { existsSync as existsSync32, readFileSync as readFileSync11 } from "node:fs";
5923
+ import { join as join43 } from "node:path";
5851
5924
  init_config();
5852
5925
  init_utils_json();
5853
5926
  function diffMergedSettings(merged, settings) {
@@ -5865,7 +5938,7 @@ function tryReadJson(filePath) {
5865
5938
  }
5866
5939
  }
5867
5940
  function reportHooksBaseSelfCleanNote(section2) {
5868
- const basePath = join42(repoHome(), "shared", "settings.base.json");
5941
+ const basePath = join43(repoHome(), "shared", "settings.base.json");
5869
5942
  const base = tryReadJson(basePath);
5870
5943
  if (base === null) return;
5871
5944
  if (!baseHasGsdHookEntries(base)) return;
@@ -5878,14 +5951,14 @@ function reportSettingsDriftCheck(section2) {
5878
5951
  const claude = claudeHome();
5879
5952
  const repo = repoHome();
5880
5953
  const host = HOST;
5881
- const settingsPath = join42(claude, "settings.json");
5882
- const basePath = join42(repo, "shared", "settings.base.json");
5883
- const hostPath = join42(repo, "hosts", `${host}.json`);
5884
- if (!existsSync31(settingsPath)) {
5954
+ const settingsPath = join43(claude, "settings.json");
5955
+ const basePath = join43(repo, "shared", "settings.base.json");
5956
+ const hostPath = join43(repo, "hosts", `${host}.json`);
5957
+ if (!existsSync32(settingsPath)) {
5885
5958
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
5886
5959
  return;
5887
5960
  }
5888
- if (!existsSync31(basePath)) {
5961
+ if (!existsSync32(basePath)) {
5889
5962
  addItem(
5890
5963
  section2,
5891
5964
  `${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
@@ -5904,7 +5977,7 @@ function reportSettingsDriftCheck(section2) {
5904
5977
  if (settings === null) {
5905
5978
  return;
5906
5979
  }
5907
- const hostExists = existsSync31(hostPath);
5980
+ const hostExists = existsSync32(hostPath);
5908
5981
  const hostObj = hostExists ? tryReadJson(hostPath) : null;
5909
5982
  if (hostExists && hostObj === null) {
5910
5983
  addItem(
@@ -5965,12 +6038,12 @@ init_config();
5965
6038
  // src/commands/doctor/engine.ts
5966
6039
  init_color();
5967
6040
  import { readFileSync as readFileSync13 } from "node:fs";
5968
- import { join as join44 } from "node:path";
6041
+ import { join as join45 } from "node:path";
5969
6042
 
5970
6043
  // src/commands/doctor/version.ts
5971
6044
  init_color();
5972
6045
  import { readFileSync as readFileSync12 } from "node:fs";
5973
- import { join as join43 } from "node:path";
6046
+ import { join as join44 } from "node:path";
5974
6047
  init_config();
5975
6048
  init_package_root();
5976
6049
  var STRICT_SEMVER = /^\d+\.\d+\.\d+$/;
@@ -5986,7 +6059,7 @@ function compareSemver(a, b) {
5986
6059
  }
5987
6060
  function readLocalVersion() {
5988
6061
  try {
5989
- const pkgPath = join43(packageRoot(), "package.json");
6062
+ const pkgPath = join44(packageRoot(), "package.json");
5990
6063
  const parsed = JSON.parse(readFileSync12(pkgPath, "utf8"));
5991
6064
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
5992
6065
  return parsed.version;
@@ -6046,7 +6119,7 @@ function parseMinVersion(spec) {
6046
6119
  }
6047
6120
  function readEnginesNode() {
6048
6121
  try {
6049
- const pkgPath = join44(packageRoot(), "package.json");
6122
+ const pkgPath = join45(packageRoot(), "package.json");
6050
6123
  const parsed = JSON.parse(readFileSync13(pkgPath, "utf8"));
6051
6124
  const node = parsed.engines?.node;
6052
6125
  if (typeof node === "string" && node.length > 0) return node;
@@ -6134,15 +6207,15 @@ function reportSyncModality(section2) {
6134
6207
  // src/commands/doctor/checks/crlf.ts
6135
6208
  init_color();
6136
6209
  import { execFileSync as execFileSync14 } from "node:child_process";
6137
- import { existsSync as existsSync32, readFileSync as readFileSync14 } from "node:fs";
6138
- import { join as join45 } from "node:path";
6210
+ import { existsSync as existsSync33, readFileSync as readFileSync14 } from "node:fs";
6211
+ import { join as join46 } from "node:path";
6139
6212
  init_config();
6140
6213
  var PROBE_TIMEOUT_MS3 = 3e3;
6141
6214
  var GUARD_LINE = /^\*\s+-text\b/;
6142
6215
  function hasGitattributesGuard(repo) {
6143
6216
  try {
6144
- const path = join45(repo, ".gitattributes");
6145
- if (!existsSync32(path)) return false;
6217
+ const path = join46(repo, ".gitattributes");
6218
+ if (!existsSync33(path)) return false;
6146
6219
  const content = readFileSync14(path, "utf8");
6147
6220
  return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
6148
6221
  } catch {
@@ -6203,14 +6276,14 @@ init_config();
6203
6276
  init_config_sharedDirs_guard();
6204
6277
  import {
6205
6278
  cpSync as cpSync6,
6206
- lstatSync as lstatSync15,
6207
- mkdirSync as mkdirSync10,
6279
+ lstatSync as lstatSync16,
6280
+ mkdirSync as mkdirSync11,
6208
6281
  readFileSync as readFileSync15,
6209
6282
  realpathSync as realpathSync6,
6210
6283
  statSync as statSync7,
6211
6284
  writeFileSync as writeFileSync6
6212
6285
  } from "node:fs";
6213
- import { dirname as dirname13, join as join46, sep as sep6 } from "node:path";
6286
+ import { dirname as dirname14, join as join47, sep as sep6 } from "node:path";
6214
6287
  init_gitleaks_scan();
6215
6288
 
6216
6289
  // src/core/rel-path-guard.ts
@@ -6239,11 +6312,11 @@ function resolveMemoryLocalPath(logical, relPath, map) {
6239
6312
  if (!isSafeRelPath(relPath)) return null;
6240
6313
  const abs = map.projects[logical]?.[HOST];
6241
6314
  if (abs === void 0) return null;
6242
- const memoryRoot = join46(claudeHome(), "projects", encodePath(abs), "memory");
6243
- const localPath = join46(memoryRoot, ...relPath.split("/"));
6315
+ const memoryRoot = join47(claudeHome(), "projects", encodePath(abs), "memory");
6316
+ const localPath = join47(memoryRoot, ...relPath.split("/"));
6244
6317
  if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep6)) return null;
6245
6318
  try {
6246
- if (lstatSync15(localPath).isSymbolicLink()) return null;
6319
+ if (lstatSync16(localPath).isSymbolicLink()) return null;
6247
6320
  const realLocal = realpathSync6(localPath);
6248
6321
  const realRoot = realpathSync6(memoryRoot);
6249
6322
  if (!realLocal.startsWith(realRoot + sep6)) return null;
@@ -6294,8 +6367,8 @@ function applyMemoryRedact(f, ts, map, scan = scanFile) {
6294
6367
  );
6295
6368
  }
6296
6369
  writeFileSync6(localPath, after, "utf8");
6297
- const dest = join46(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
6298
- mkdirSync10(dirname13(dest), { recursive: true });
6370
+ const dest = join47(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
6371
+ mkdirSync11(dirname14(dest), { recursive: true });
6299
6372
  cpSync6(localPath, dest, { force: true });
6300
6373
  return true;
6301
6374
  }
@@ -6311,68 +6384,7 @@ import {
6311
6384
  statSync as statSync8,
6312
6385
  writeFileSync as writeFileSync7
6313
6386
  } from "node:fs";
6314
- import { dirname as dirname14, join as join48, sep as sep7 } from "node:path";
6315
-
6316
- // src/sync/skills-sync.ts
6317
- init_config();
6318
- import { existsSync as existsSync33, lstatSync as lstatSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync15, rmSync as rmSync16 } from "node:fs";
6319
- import { join as join47 } from "node:path";
6320
-
6321
- // src/sync/skills-sync.tracked.ts
6322
- init_utils();
6323
- import { basename as basename5 } from "node:path";
6324
- function trackedRootSkillsAt(ref, repo) {
6325
- try {
6326
- const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
6327
- const entries = raw.split("\0").filter((entry) => entry !== "");
6328
- return new Set(entries.map((entry) => basename5(entry)));
6329
- } catch {
6330
- return /* @__PURE__ */ new Set();
6331
- }
6332
- }
6333
-
6334
- // src/sync/skills-sync.ts
6335
- init_utils_fs();
6336
- function isGsdOwned(name) {
6337
- return name.startsWith(GSD_PREFIX);
6338
- }
6339
- function isSkillExcluded(name) {
6340
- return isGsdOwned(name) || isDeniedName(ALWAYS_NEVER_SYNC, name);
6341
- }
6342
- function copySkillsPush(src, dst) {
6343
- const srcNames = readdirSync15(src, { encoding: "utf8" });
6344
- const blockSet = /* @__PURE__ */ new Set([
6345
- ...srcNames.filter((n) => isGsdOwned(n)),
6346
- ...ALWAYS_NEVER_SYNC
6347
- ]);
6348
- copyExtrasFiltered(src, dst, blockSet);
6349
- }
6350
- function copySkillsPull(src, dst, isRootPreserved) {
6351
- copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved);
6352
- }
6353
- function syncSkillsPull(ts, prePostHeads) {
6354
- const sharedSkills = join47(repoHome(), "shared", "skills");
6355
- if (!existsSync33(sharedSkills)) return;
6356
- const localSkills = join47(claudeHome(), "skills");
6357
- backupBeforeWrite(localSkills, ts);
6358
- const dstStat = lstatSync16(localSkills, { throwIfNoEntry: false });
6359
- if (dstStat?.isSymbolicLink() === true) {
6360
- rmSync16(localSkills, { recursive: true, force: true });
6361
- mkdirSync11(localSkills, { recursive: true });
6362
- }
6363
- const tracked = prePostHeads === void 0 ? /* @__PURE__ */ new Set() : trackedRootSkillsAt(prePostHeads.pre, repoHome());
6364
- copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
6365
- }
6366
- function syncSkillsPush() {
6367
- const localSkills = join47(claudeHome(), "skills");
6368
- const stat = lstatSync16(localSkills, { throwIfNoEntry: false });
6369
- if (stat === void 0) return;
6370
- if (stat.isSymbolicLink()) return;
6371
- const sharedSkills = join47(repoHome(), "shared", "skills");
6372
- copySkillsPush(localSkills, sharedSkills);
6373
- }
6374
-
6375
- // src/commands/push/recovery/skills.ts
6387
+ import { dirname as dirname15, join as join48, sep as sep7 } from "node:path";
6376
6388
  init_gitleaks_scan();
6377
6389
  init_utils_fs();
6378
6390
  init_utils();
@@ -6446,7 +6458,7 @@ function applySkillRedact(f, ts, scan = scanFile) {
6446
6458
  }
6447
6459
  writeFileSync7(localPath, after, "utf8");
6448
6460
  const dest = join48(repoHome(), "shared", "skills", name, ...relPath.split("/"));
6449
- mkdirSync12(dirname14(dest), { recursive: true });
6461
+ mkdirSync12(dirname15(dest), { recursive: true });
6450
6462
  cpSync7(localPath, dest, { force: true });
6451
6463
  return true;
6452
6464
  }
@@ -6524,12 +6536,12 @@ function applyDeferredAllows(findings, state, repo) {
6524
6536
  init_config();
6525
6537
  init_config_sharedDirs_guard();
6526
6538
  import { cpSync as cpSync8, existsSync as existsSync36, mkdirSync as mkdirSync13 } from "node:fs";
6527
- import { dirname as dirname16, join as join51, sep as sep8 } from "node:path";
6539
+ import { dirname as dirname17, join as join51, sep as sep8 } from "node:path";
6528
6540
 
6529
6541
  // src/commands/redact/redact.ts
6530
6542
  init_config();
6531
6543
  import { existsSync as existsSync35 } from "node:fs";
6532
- import { dirname as dirname15, join as join50 } from "node:path";
6544
+ import { dirname as dirname16, join as join50 } from "node:path";
6533
6545
 
6534
6546
  // src/commands/redact/subtree.ts
6535
6547
  import { existsSync as existsSync34, lstatSync as lstatSync18, readFileSync as readFileSync17, readdirSync as readdirSync16, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
@@ -6689,7 +6701,7 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
6689
6701
  process.exitCode = 1;
6690
6702
  return;
6691
6703
  }
6692
- const sessionDir = join50(dirname15(localPath), id);
6704
+ const sessionDir = join50(dirname16(localPath), id);
6693
6705
  const subtreeFiles = listSubtreeFiles(sessionDir);
6694
6706
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6695
6707
  log(
@@ -6761,7 +6773,7 @@ function preflightRedactable(f, map, nowMs) {
6761
6773
  if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
6762
6774
  const localPath = resolveLiveTranscript(sid);
6763
6775
  if (localPath === null) return `session ${sid}: local transcript not found`;
6764
- const sessionDir = join51(dirname16(localPath), sid);
6776
+ const sessionDir = join51(dirname17(localPath), sid);
6765
6777
  const subtreeFiles = listSubtreeFiles(sessionDir);
6766
6778
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6767
6779
  return `session ${sid}: looks active (modified within the last 5 minutes)`;
@@ -6790,7 +6802,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
6790
6802
  `could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
6791
6803
  );
6792
6804
  }
6793
- const sessionDir = join51(dirname16(localPath), sid);
6805
+ const sessionDir = join51(dirname17(localPath), sid);
6794
6806
  const subtreeFiles = listSubtreeFiles(sessionDir);
6795
6807
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6796
6808
  return refuse(
@@ -8051,7 +8063,7 @@ import { join as join60 } from "node:path";
8051
8063
 
8052
8064
  // src/sync/extras/planning-deletes.ts
8053
8065
  import { existsSync as existsSync42, readdirSync as readdirSync18, readFileSync as readFileSync20, realpathSync as realpathSync8, rmSync as rmSync19 } from "node:fs";
8054
- import { dirname as dirname17, join as join59, sep as sep11 } from "node:path";
8066
+ import { dirname as dirname18, join as join59, sep as sep11 } from "node:path";
8055
8067
 
8056
8068
  // src/sync/extras/planning-diff.ts
8057
8069
  import { join as join58, normalize as normalize2, sep as sep10 } from "node:path";
@@ -8130,7 +8142,7 @@ function planningDeleteTargets(opts) {
8130
8142
  init_utils_fs();
8131
8143
  init_utils();
8132
8144
  function pruneEmptyAncestors(target, planningRoot) {
8133
- let dir = dirname17(target);
8145
+ let dir = dirname18(target);
8134
8146
  while (dir !== planningRoot && dir.startsWith(planningRoot + sep11)) {
8135
8147
  try {
8136
8148
  if (readdirSync18(dir).length > 0) break;
@@ -8138,7 +8150,7 @@ function pruneEmptyAncestors(target, planningRoot) {
8138
8150
  } catch {
8139
8151
  break;
8140
8152
  }
8141
- dir = dirname17(dir);
8153
+ dir = dirname18(dir);
8142
8154
  }
8143
8155
  }
8144
8156
  function tryRealpath(dir) {
@@ -8153,7 +8165,7 @@ function isInsidePlanningRoot(parentReal, rootReal) {
8153
8165
  }
8154
8166
  function deletePlanningTarget(target, planningRoot, repoCounterpart) {
8155
8167
  if (existsSync42(repoCounterpart)) return;
8156
- const parentReal = tryRealpath(dirname17(target));
8168
+ const parentReal = tryRealpath(dirname18(target));
8157
8169
  if (parentReal === void 0) return;
8158
8170
  const rootReal = tryRealpath(planningRoot);
8159
8171
  if (rootReal === void 0) return;
@@ -8264,9 +8276,9 @@ function remapExtrasPush(ts, opts = {}) {
8264
8276
  const { unmapped, skipped, done, would } = runExtrasOp(
8265
8277
  v,
8266
8278
  dryRun,
8267
- ({ localRoot, logical, dirname: dirname18 }) => ({
8268
- src: join60(localRoot, dirname18),
8269
- dst: join60(repoExtras, logical, dirname18)
8279
+ ({ localRoot, logical, dirname: dirname19 }) => ({
8280
+ src: join60(localRoot, dirname19),
8281
+ dst: join60(repoExtras, logical, dirname19)
8270
8282
  }),
8271
8283
  (dst) => backupRepoWrite(dst, ts, repo),
8272
8284
  // Push copy routing per extra type:
@@ -8279,7 +8291,7 @@ function remapExtrasPush(ts, opts = {}) {
8279
8291
  // config.never-sync.ts)
8280
8292
  // remains the hard security boundary.
8281
8293
  // All others: copyExtrasFiltered with per-extra denylist.
8282
- (src, dst, dirname18) => dirname18 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname18)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname18))
8294
+ (src, dst, dirname19) => dirname19 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname19)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname19))
8283
8295
  );
8284
8296
  return { unmapped, skipped, pushed: done, wouldPush: would };
8285
8297
  }
@@ -8295,9 +8307,9 @@ function remapExtrasPull(ts, opts = {}) {
8295
8307
  const { unmapped, skipped, done, would } = runExtrasOp(
8296
8308
  v,
8297
8309
  dryRun,
8298
- ({ localRoot, logical, dirname: dirname18 }) => ({
8299
- src: join60(repo, "shared", "extras", logical, dirname18),
8300
- dst: join60(localRoot, dirname18)
8310
+ ({ localRoot, logical, dirname: dirname19 }) => ({
8311
+ src: join60(repo, "shared", "extras", logical, dirname19),
8312
+ dst: join60(localRoot, dirname19)
8301
8313
  }),
8302
8314
  // Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
8303
8315
  // localRoot so the backup tree mirrors the project layout.
@@ -8315,12 +8327,12 @@ function remapExtrasPull(ts, opts = {}) {
8315
8327
  // copyExtrasFileSkipDiverged keeps a locally-edited file rather than
8316
8328
  // clobbering it, so the divergence WARN's keep-local promise holds for
8317
8329
  // every extra, not just `.planning`.
8318
- (src, dst, dirname18) => {
8319
- if (dirname18 === ".claude")
8320
- return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname18));
8321
- if (dirname18 === ".planning") {
8330
+ (src, dst, dirname19) => {
8331
+ if (dirname19 === ".claude")
8332
+ return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname19));
8333
+ if (dirname19 === ".planning") {
8322
8334
  const divergedSet = new Set(listDivergingModified(dst, src));
8323
- return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname18), divergedSet);
8335
+ return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname19), divergedSet);
8324
8336
  }
8325
8337
  return copyExtrasFileSkipDiverged(src, dst);
8326
8338
  }
@@ -8347,9 +8359,9 @@ function divergenceCheckExtras(ts, prePostHeads) {
8347
8359
  const backupRoot = join61(backupBase(), ts, "extras");
8348
8360
  const repo = repoHome();
8349
8361
  let divergedCount = 0;
8350
- for (const { logical, localRoot, dirname: dirname18 } of eachExtrasTarget(v, counts)) {
8351
- const local = join61(localRoot, dirname18);
8352
- const repoEntry = join61(repo, "shared", "extras", logical, dirname18);
8362
+ for (const { logical, localRoot, dirname: dirname19 } of eachExtrasTarget(v, counts)) {
8363
+ const local = join61(localRoot, dirname19);
8364
+ const repoEntry = join61(repo, "shared", "extras", logical, dirname19);
8353
8365
  if (!existsSync44(local) || !existsSync44(repoEntry)) continue;
8354
8366
  const modified = listDivergingModified(local, repoEntry);
8355
8367
  if (modified.length === 0) continue;
@@ -8357,7 +8369,7 @@ function divergenceCheckExtras(ts, prePostHeads) {
8357
8369
  const projectBackupRoot = join61(backupRoot, encodePath(localRoot));
8358
8370
  warn(
8359
8371
  divergenceWarnLine({
8360
- dirname: dirname18,
8372
+ dirname: dirname19,
8361
8373
  logical,
8362
8374
  isDir: statSync11(local).isDirectory(),
8363
8375
  count: modified.length,
@@ -9062,11 +9074,11 @@ function buildSkillsPreviewSection() {
9062
9074
  const s = section("Skills");
9063
9075
  const sharedSkills = join66(repoHome(), "shared", "skills");
9064
9076
  if (!existsSync48(sharedSkills)) return s;
9065
- const sharedNames = readdirSync20(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
9077
+ const sharedNames = readdirSync20(sharedSkills, { encoding: "utf8" }).filter((name) => !isRootSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
9066
9078
  for (const name of sharedNames) addItem(s, name);
9067
9079
  const localSkills = join66(claudeHome(), "skills");
9068
9080
  const localOnly = existsSync48(localSkills) ? readdirSync20(localSkills, { encoding: "utf8" }).filter(
9069
- (name) => !isSkillExcluded(name) && !sharedNames.includes(name)
9081
+ (name) => !isRootSkillExcluded(name) && !sharedNames.includes(name)
9070
9082
  ).length : 0;
9071
9083
  if (localOnly > 0) {
9072
9084
  addItem(s, `${localOnly} local-only present, not in repo (push to reconcile)`);
@@ -10178,11 +10190,11 @@ function stageExtras(tmpRoot, map) {
10178
10190
  assertSafeLogical(logical);
10179
10191
  const localRoot = map.projects[logical]?.[HOST];
10180
10192
  if (!localRoot || localRoot === "TBD") continue;
10181
- for (const dirname18 of dirnames) {
10182
- if (!whitelist.includes(dirname18)) continue;
10183
- const src = join74(localRoot, dirname18);
10193
+ for (const dirname19 of dirnames) {
10194
+ if (!whitelist.includes(dirname19)) continue;
10195
+ const src = join74(localRoot, dirname19);
10184
10196
  if (!existsSync55(src)) continue;
10185
- const dst = join74(tmpRoot, "shared", "extras", logical, dirname18);
10197
+ const dst = join74(tmpRoot, "shared", "extras", logical, dirname19);
10186
10198
  copyExtras(src, dst);
10187
10199
  staged++;
10188
10200
  }
@@ -10193,7 +10205,9 @@ function stageSkills(tmpRoot) {
10193
10205
  const localSkills = join74(claudeHome(), "skills");
10194
10206
  const stat = lstatSync24(localSkills, { throwIfNoEntry: false });
10195
10207
  if (stat === void 0 || stat.isSymbolicLink()) return 0;
10196
- const names = readdirSync21(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
10208
+ const names = readdirSync21(localSkills, { encoding: "utf8" }).filter(
10209
+ (n) => !isRootSkillExcluded(n)
10210
+ );
10197
10211
  if (names.length === 0) return 0;
10198
10212
  copySkillsPush(localSkills, join74(tmpRoot, "shared", "skills"));
10199
10213
  return names.length;
@@ -11219,7 +11233,7 @@ function parseSyncArgs(argv) {
11219
11233
  // package.json
11220
11234
  var package_default = {
11221
11235
  name: "claude-nomad",
11222
- version: "0.69.2",
11236
+ version: "0.69.3",
11223
11237
  type: "module",
11224
11238
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
11225
11239
  keywords: [