claude-nomad 0.63.2 → 0.64.0

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
@@ -421,7 +421,6 @@ var init_settings_keys = __esm({
421
421
  "includeGitInstructions",
422
422
  "inputNeededNotifEnabled",
423
423
  "language",
424
- "leftArrowOpensAgents",
425
424
  "managedMcpServers",
426
425
  "minimumVersion",
427
426
  "model",
@@ -539,6 +538,9 @@ function crashDir() {
539
538
  function manifestPath() {
540
539
  return join(home(), ".cache", "claude-nomad", `push-manifest-${encodeURIComponent(HOST)}.json`);
541
540
  }
541
+ function sharedBaselinePath() {
542
+ return join(home(), ".cache", "claude-nomad", `shared-baseline-${encodeURIComponent(HOST)}.json`);
543
+ }
542
544
  function allSharedLinks(map) {
543
545
  const extras = [];
544
546
  for (const entry of map.sharedDirs ?? []) {
@@ -2743,7 +2745,7 @@ import { join as join45 } from "node:path";
2743
2745
  init_color();
2744
2746
  init_config();
2745
2747
  import { existsSync as existsSync11, lstatSync as lstatSync8, readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
2746
- import { join as join13 } from "node:path";
2748
+ import { basename as basename3, join as join13 } from "node:path";
2747
2749
 
2748
2750
  // src/commands.doctor.format.ts
2749
2751
  init_color();
@@ -2818,6 +2820,61 @@ function readJsonSafe(path, label, section2) {
2818
2820
  }
2819
2821
  }
2820
2822
 
2823
+ // src/extras-sync.diff.ts
2824
+ init_utils();
2825
+ import { execFileSync as execFileSync2 } from "node:child_process";
2826
+ import { relative as relative3 } from "node:path";
2827
+ var NAME_STATUS_DIFF_TIMEOUT_MS = 3e3;
2828
+ function parseNameStatus(stdout) {
2829
+ const fields = stdout.split("\0").filter((f) => f.length > 0);
2830
+ const entries = [];
2831
+ for (let i = 0; i + 1 < fields.length; i += 2) {
2832
+ entries.push({ status: fields[i], path: fields[i + 1] });
2833
+ }
2834
+ return entries;
2835
+ }
2836
+ function labelEntry(entry) {
2837
+ if (entry.status === "D") return `${entry.path} (local only)`;
2838
+ if (entry.status === "A") return `${entry.path} (repo only)`;
2839
+ return entry.path;
2840
+ }
2841
+ function parseDiffOutput(stdout) {
2842
+ return parseNameStatus(stdout).map(labelEntry);
2843
+ }
2844
+ function parseModifiedPaths(stdout, a) {
2845
+ return parseNameStatus(stdout).filter((entry) => entry.status === "M").map((entry) => relative3(a, entry.path));
2846
+ }
2847
+ function runNameStatusDiff(a, b, parse) {
2848
+ try {
2849
+ const stdout = execFileSync2(
2850
+ "git",
2851
+ ["diff", "--no-index", "--no-renames", "-z", "--name-status", a, b],
2852
+ {
2853
+ stdio: ["ignore", "pipe", "pipe"],
2854
+ timeout: NAME_STATUS_DIFF_TIMEOUT_MS
2855
+ }
2856
+ ).toString();
2857
+ return parse(stdout);
2858
+ } catch (err) {
2859
+ const e = err;
2860
+ if (e.status === 1 && e.stdout !== void 0) {
2861
+ return parse(e.stdout.toString());
2862
+ }
2863
+ if (e.code === "ENOENT") {
2864
+ warn(`git not on PATH; divergence check skipped for ${a}`);
2865
+ return [];
2866
+ }
2867
+ warn(`divergence check failed for ${a}: ${e.message ?? String(err)}`);
2868
+ return [];
2869
+ }
2870
+ }
2871
+ function listDivergingFiles(a, b) {
2872
+ return runNameStatusDiff(a, b, parseDiffOutput);
2873
+ }
2874
+ function listDivergingModified(a, b) {
2875
+ return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
2876
+ }
2877
+
2821
2878
  // src/init.classify.ts
2822
2879
  init_config();
2823
2880
  init_utils_json();
@@ -2942,6 +2999,25 @@ function reportRepoState(section2) {
2942
2999
  function repoHasSharedSource(name) {
2943
3000
  return existsSync11(join13(repoHome(), "shared", name));
2944
3001
  }
3002
+ function win32CopyOkRow(name) {
3003
+ return { line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)`, fail: false };
3004
+ }
3005
+ function divergingBasename(line) {
3006
+ return basename3(line.replace(/ \((?:local|repo) only\)$/, ""));
3007
+ }
3008
+ function classifyWin32Copy(name, p) {
3009
+ const sharedPath = join13(repoHome(), "shared", name);
3010
+ if (!existsSync11(sharedPath)) return win32CopyOkRow(name);
3011
+ const diverging = listDivergingFiles(p, sharedPath).filter(
3012
+ (line) => !isDeniedName(ALWAYS_NEVER_SYNC, divergingBasename(line))
3013
+ );
3014
+ if (diverging.length === 0) return win32CopyOkRow(name);
3015
+ return {
3016
+ line: `${yellow(warnGlyph)} ${name}: ${diverging.length} file(s) diverge from shared/${name}`,
3017
+ fail: false,
3018
+ children: diverging
3019
+ };
3020
+ }
2945
3021
  function classifySharedLink(name, p) {
2946
3022
  let stat;
2947
3023
  try {
@@ -2958,10 +3034,7 @@ function classifySharedLink(name, p) {
2958
3034
  }
2959
3035
  if (!stat.isSymbolicLink()) {
2960
3036
  if (process.platform === "win32") {
2961
- return {
2962
- line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)`,
2963
- fail: false
2964
- };
3037
+ return classifyWin32Copy(name, p);
2965
3038
  }
2966
3039
  return {
2967
3040
  line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync); run \`nomad adopt ${name}\` to fix`,
@@ -2995,9 +3068,12 @@ function reportSharedLinks(section2, map) {
2995
3068
  const claude = claudeHome();
2996
3069
  for (const name of allSharedLinks(map)) {
2997
3070
  const p = join13(claude, name);
2998
- const { line, fail: fail2 } = classifySharedLink(name, p);
3071
+ const { line, fail: fail2, children } = classifySharedLink(name, p);
2999
3072
  addItem(section2, line);
3000
3073
  if (fail2) process.exitCode = 1;
3074
+ if (children) {
3075
+ for (const child of children) addChildItem(section2, child);
3076
+ }
3001
3077
  }
3002
3078
  }
3003
3079
  function reportDroppedNamesMigration(section2) {
@@ -3179,61 +3255,6 @@ init_color();
3179
3255
  import { existsSync as existsSync14 } from "node:fs";
3180
3256
  import { join as join16 } from "node:path";
3181
3257
  init_config();
3182
-
3183
- // src/extras-sync.diff.ts
3184
- init_utils();
3185
- import { execFileSync as execFileSync2 } from "node:child_process";
3186
- import { relative as relative3 } from "node:path";
3187
- function parseNameStatus(stdout) {
3188
- const fields = stdout.split("\0").filter((f) => f.length > 0);
3189
- const entries = [];
3190
- for (let i = 0; i + 1 < fields.length; i += 2) {
3191
- entries.push({ status: fields[i], path: fields[i + 1] });
3192
- }
3193
- return entries;
3194
- }
3195
- function labelEntry(entry) {
3196
- if (entry.status === "D") return `${entry.path} (local only)`;
3197
- if (entry.status === "A") return `${entry.path} (repo only)`;
3198
- return entry.path;
3199
- }
3200
- function parseDiffOutput(stdout) {
3201
- return parseNameStatus(stdout).map(labelEntry);
3202
- }
3203
- function parseModifiedPaths(stdout, a) {
3204
- return parseNameStatus(stdout).filter((entry) => entry.status === "M").map((entry) => relative3(a, entry.path));
3205
- }
3206
- function runNameStatusDiff(a, b, parse) {
3207
- try {
3208
- const stdout = execFileSync2(
3209
- "git",
3210
- ["diff", "--no-index", "--no-renames", "-z", "--name-status", a, b],
3211
- {
3212
- stdio: ["ignore", "pipe", "pipe"]
3213
- }
3214
- ).toString();
3215
- return parse(stdout);
3216
- } catch (err) {
3217
- const e = err;
3218
- if (e.status === 1 && e.stdout !== void 0) {
3219
- return parse(e.stdout.toString());
3220
- }
3221
- if (e.code === "ENOENT") {
3222
- warn(`git not on PATH; divergence check skipped for ${a}`);
3223
- return [];
3224
- }
3225
- warn(`divergence check failed for ${a}: ${e.message ?? String(err)}`);
3226
- return [];
3227
- }
3228
- }
3229
- function listDivergingFiles(a, b) {
3230
- return runNameStatusDiff(a, b, parseDiffOutput);
3231
- }
3232
- function listDivergingModified(a, b) {
3233
- return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
3234
- }
3235
-
3236
- // src/commands.doctor.checks.skills.ts
3237
3258
  function stripSideIndicator(line) {
3238
3259
  if (line.endsWith(" (local only)")) return line.slice(0, -" (local only)".length);
3239
3260
  if (line.endsWith(" (repo only)")) return line.slice(0, -" (repo only)".length);
@@ -3241,15 +3262,15 @@ function stripSideIndicator(line) {
3241
3262
  }
3242
3263
  function isGsdDiffLine(line, localBase, sharedBase) {
3243
3264
  const bare = stripSideIndicator(line);
3244
- let relative9;
3265
+ let relative11;
3245
3266
  if (bare.startsWith(localBase + "/")) {
3246
- relative9 = bare.slice(localBase.length + 1);
3267
+ relative11 = bare.slice(localBase.length + 1);
3247
3268
  } else if (bare.startsWith(sharedBase + "/")) {
3248
- relative9 = bare.slice(sharedBase.length + 1);
3269
+ relative11 = bare.slice(sharedBase.length + 1);
3249
3270
  } else {
3250
- relative9 = bare;
3271
+ relative11 = bare;
3251
3272
  }
3252
- return relative9.split("/")[0].startsWith(GSD_PREFIX);
3273
+ return relative11.split("/")[0].startsWith(GSD_PREFIX);
3253
3274
  }
3254
3275
  function reportSkillsDivergence(section2) {
3255
3276
  const sharedSkills = join16(repoHome(), "shared", "skills");
@@ -5155,12 +5176,12 @@ import { join as join37 } from "node:path";
5155
5176
 
5156
5177
  // src/skills-sync.tracked.ts
5157
5178
  init_utils();
5158
- import { basename as basename3 } from "node:path";
5179
+ import { basename as basename4 } from "node:path";
5159
5180
  function trackedRootSkillsAt(ref, repo) {
5160
5181
  try {
5161
5182
  const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
5162
5183
  const entries = raw.split("\0").filter((entry) => entry !== "");
5163
- return new Set(entries.map((entry) => basename3(entry)));
5184
+ return new Set(entries.map((entry) => basename4(entry)));
5164
5185
  } catch {
5165
5186
  return /* @__PURE__ */ new Set();
5166
5187
  }
@@ -5876,8 +5897,8 @@ function sessionSuffix(finding) {
5876
5897
  const sid = sessionIdFromFinding(finding);
5877
5898
  if (sid === null) return "";
5878
5899
  const rawBasename = finding.File.replace(/^.*\//, "");
5879
- const basename4 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
5880
- return basename4 === sid ? "" : ` (session: ${sid})`;
5900
+ const basename5 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
5901
+ return basename5 === sid ? "" : ` (session: ${sid})`;
5881
5902
  }
5882
5903
  function renderFindingBlock(finding, readLine, siblings = []) {
5883
5904
  const lines = [` file: ${finding.File}:${finding.StartLine}${sessionSuffix(finding)}`];
@@ -6843,8 +6864,8 @@ function unstageOne(rel, repo) {
6843
6864
 
6844
6865
  // src/commands.pull.ts
6845
6866
  init_autostash_guard();
6846
- import { existsSync as existsSync42, mkdirSync as mkdirSync14 } from "node:fs";
6847
- import { join as join53 } from "node:path";
6867
+ import { existsSync as existsSync46, mkdirSync as mkdirSync14 } from "node:fs";
6868
+ import { join as join58 } from "node:path";
6848
6869
 
6849
6870
  // src/commands.push.sections.ts
6850
6871
  init_color();
@@ -7279,10 +7300,204 @@ function divergenceCheckExtras(ts, prePostHeads) {
7279
7300
  return divergedCount;
7280
7301
  }
7281
7302
 
7282
- // src/preview.ts
7303
+ // src/links.baseline.ts
7304
+ init_config();
7305
+ import { lstatSync as lstatSync15, readdirSync as readdirSync17 } from "node:fs";
7306
+ import { join as join51, relative as relative7, sep as sep11 } from "node:path";
7307
+ init_utils();
7308
+ var SHARED_BASELINE_KIND = "shared-links-baseline/1";
7309
+ var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
7310
+ function readSharedBaseline() {
7311
+ const parsed = readManifest(sharedBaselinePath());
7312
+ if (parsed === null) return null;
7313
+ if (parsed.scannerVersion !== SHARED_BASELINE_KIND) return null;
7314
+ return parsed;
7315
+ }
7316
+ function baselineKey(claude, abs) {
7317
+ return relative7(claude, abs).split(sep11).join("/");
7318
+ }
7319
+ function addLocalPath(abs, claude, scan) {
7320
+ const key = baselineKey(claude, abs);
7321
+ let st;
7322
+ try {
7323
+ st = lstatSync15(abs, { throwIfNoEntry: false });
7324
+ } catch {
7325
+ scan.declined.push(key);
7326
+ return;
7327
+ }
7328
+ if (st === void 0) return;
7329
+ if (st.isSymbolicLink()) {
7330
+ scan.declined.push(key);
7331
+ return;
7332
+ }
7333
+ if (st.isDirectory()) {
7334
+ collectSharedFiles(abs, claude, scan);
7335
+ return;
7336
+ }
7337
+ scan.files[key] = { size: st.size, mtime: st.mtimeMs };
7338
+ }
7339
+ function collectSharedFiles(dir, claude, scan) {
7340
+ let entries;
7341
+ try {
7342
+ entries = readdirSync17(dir);
7343
+ } catch {
7344
+ scan.declined.push(baselineKey(claude, dir));
7345
+ return;
7346
+ }
7347
+ for (const entry of entries) {
7348
+ const abs = join51(dir, entry);
7349
+ if (isDeniedName(ALWAYS_NEVER_SYNC, entry)) {
7350
+ scan.declined.push(baselineKey(claude, abs));
7351
+ continue;
7352
+ }
7353
+ addLocalPath(abs, claude, scan);
7354
+ }
7355
+ }
7356
+ function enumerateLocalSharedScan(map) {
7357
+ const claude = claudeHome();
7358
+ const scan = { files: {}, declined: [] };
7359
+ for (const name of allSharedLinks(map)) {
7360
+ if (isDeniedName(ALWAYS_NEVER_SYNC, name)) {
7361
+ scan.declined.push(name);
7362
+ continue;
7363
+ }
7364
+ addLocalPath(join51(claude, name), claude, scan);
7365
+ }
7366
+ return scan;
7367
+ }
7368
+ function enumerateLocalSharedFiles(map) {
7369
+ return enumerateLocalSharedScan(map).files;
7370
+ }
7371
+ function buildSharedBaseline(map) {
7372
+ const claude = claudeHome();
7373
+ const files = {};
7374
+ for (const [key, st] of Object.entries(enumerateLocalSharedFiles(map))) {
7375
+ try {
7376
+ files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join51(claude, key)) };
7377
+ } catch {
7378
+ }
7379
+ }
7380
+ return buildManifest(files, SHARED_BASELINE_KIND, SHARED_BASELINE_CONFIG_HASH);
7381
+ }
7382
+ function writeSharedBaseline(map) {
7383
+ if (process.platform !== "win32") return;
7384
+ try {
7385
+ writeManifest(sharedBaselinePath(), buildSharedBaseline(map));
7386
+ } catch (err) {
7387
+ warn(`could not record the shared-config baseline: ${err.message}`);
7388
+ }
7389
+ }
7390
+
7391
+ // src/commands.pull.win32.ts
7392
+ init_config();
7393
+ import { existsSync as existsSync44 } from "node:fs";
7394
+ import { join as join56 } from "node:path";
7395
+
7396
+ // src/git-probe.ts
7397
+ import { execFileSync as execFileSync21 } from "node:child_process";
7398
+ var PROBE_TIMEOUT_MS4 = 1e4;
7399
+ var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
7400
+ function gitProbe(args, repo) {
7401
+ try {
7402
+ return execFileSync21("git", args, {
7403
+ cwd: repo,
7404
+ stdio: ["ignore", "pipe", "pipe"],
7405
+ timeout: PROBE_TIMEOUT_MS4,
7406
+ maxBuffer: PROBE_MAX_BUFFER
7407
+ }).toString();
7408
+ } catch {
7409
+ return null;
7410
+ }
7411
+ }
7412
+
7413
+ // src/links.captures.ts
7283
7414
  init_config();
7284
- import { existsSync as existsSync41 } from "node:fs";
7415
+ import { existsSync as existsSync40, lstatSync as lstatSync16 } from "node:fs";
7285
7416
  import { join as join52 } from "node:path";
7417
+ function planSharedLinkCaptures(map) {
7418
+ if (process.platform !== "win32") return [];
7419
+ if (map === null) return [];
7420
+ const claude = claudeHome();
7421
+ const repo = repoHome();
7422
+ const plan = [];
7423
+ for (const name of allSharedLinks(map)) {
7424
+ const localPath = join52(claude, name);
7425
+ let stat;
7426
+ try {
7427
+ stat = lstatSync16(localPath, { throwIfNoEntry: false });
7428
+ } catch {
7429
+ continue;
7430
+ }
7431
+ if (stat === void 0) continue;
7432
+ if (stat.isSymbolicLink()) continue;
7433
+ const repoPath = join52(repo, "shared", name);
7434
+ if (!existsSync40(repoPath)) continue;
7435
+ plan.push({ name, localPath, repoPath });
7436
+ }
7437
+ return plan;
7438
+ }
7439
+
7440
+ // src/links.deletions.ts
7441
+ init_config();
7442
+ import { existsSync as existsSync41, lstatSync as lstatSync17, rmSync as rmSync17 } from "node:fs";
7443
+ import { join as join53, relative as relative8, resolve as resolve4, sep as sep12 } from "node:path";
7444
+ init_utils();
7445
+ init_utils_fs();
7446
+ function isUnknown(declined, key) {
7447
+ return declined.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
7448
+ }
7449
+ function deletionFor(key, names, claude, sharedRoot) {
7450
+ const segments = key.split("/");
7451
+ const name = segments[0];
7452
+ if (!names.has(name)) return null;
7453
+ if (!existsSync41(join53(claude, name))) return null;
7454
+ if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
7455
+ const repoPath = resolve4(sharedRoot, key);
7456
+ const rel = relative8(join53(sharedRoot, name), repoPath);
7457
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep12}`)) return null;
7458
+ if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
7459
+ return null;
7460
+ if (!existsSync41(repoPath)) return null;
7461
+ return { name, localPath: join53(claude, key), repoPath };
7462
+ }
7463
+ function planSharedLinkDeletions(map) {
7464
+ if (process.platform !== "win32") return [];
7465
+ if (map === null) return [];
7466
+ const baseline = readSharedBaseline();
7467
+ if (baseline === null) return [];
7468
+ const claude = claudeHome();
7469
+ const sharedRoot = join53(repoHome(), "shared");
7470
+ const scan = enumerateLocalSharedScan(map);
7471
+ const present = new Set(Object.keys(scan.files).map((key) => key.toLowerCase()));
7472
+ const declined = scan.declined.map((path) => path.toLowerCase());
7473
+ const names = new Set(allSharedLinks(map));
7474
+ const plan = [];
7475
+ for (const key of Object.keys(baseline.files)) {
7476
+ const folded = key.toLowerCase();
7477
+ if (present.has(folded)) continue;
7478
+ if (isUnknown(declined, folded)) continue;
7479
+ const entry = deletionFor(key, names, claude, sharedRoot);
7480
+ if (entry !== null) plan.push(entry);
7481
+ }
7482
+ return plan;
7483
+ }
7484
+ function applySharedLinkDeletions(map, ts) {
7485
+ const repo = repoHome();
7486
+ for (const entry of planSharedLinkDeletions(map)) {
7487
+ try {
7488
+ if (!lstatSync17(entry.repoPath).isFile()) continue;
7489
+ backupRepoWrite(entry.repoPath, ts, repo);
7490
+ rmSync17(entry.repoPath, { force: true });
7491
+ } catch (err) {
7492
+ warn(`could not remove ${entry.repoPath}: ${err.message}`);
7493
+ }
7494
+ }
7495
+ }
7496
+
7497
+ // src/preview.ts
7498
+ init_config();
7499
+ import { existsSync as existsSync43 } from "node:fs";
7500
+ import { join as join55 } from "node:path";
7286
7501
 
7287
7502
  // node_modules/diff/libesm/diff/base.js
7288
7503
  var Diff = class {
@@ -7557,16 +7772,16 @@ function diffLinesToUnified(oldStr, newStr) {
7557
7772
 
7558
7773
  // src/preview.skills.ts
7559
7774
  init_config();
7560
- import { existsSync as existsSync40, readdirSync as readdirSync17 } from "node:fs";
7561
- import { join as join51 } from "node:path";
7775
+ import { existsSync as existsSync42, readdirSync as readdirSync18 } from "node:fs";
7776
+ import { join as join54 } from "node:path";
7562
7777
  function buildSkillsPreviewSection() {
7563
7778
  const s = section("Skills");
7564
- const sharedSkills = join51(repoHome(), "shared", "skills");
7565
- if (!existsSync40(sharedSkills)) return s;
7566
- const sharedNames = readdirSync17(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
7779
+ const sharedSkills = join54(repoHome(), "shared", "skills");
7780
+ if (!existsSync42(sharedSkills)) return s;
7781
+ const sharedNames = readdirSync18(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
7567
7782
  for (const name of sharedNames) addItem(s, name);
7568
- const localSkills = join51(claudeHome(), "skills");
7569
- const localOnly = existsSync40(localSkills) ? readdirSync17(localSkills, { encoding: "utf8" }).filter(
7783
+ const localSkills = join54(claudeHome(), "skills");
7784
+ const localOnly = existsSync42(localSkills) ? readdirSync18(localSkills, { encoding: "utf8" }).filter(
7570
7785
  (name) => !isSkillExcluded(name) && !sharedNames.includes(name)
7571
7786
  ).length : 0;
7572
7787
  if (localOnly > 0) {
@@ -7588,7 +7803,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
7588
7803
  return lines.join("\n");
7589
7804
  }
7590
7805
  function readJsonOrNull(path) {
7591
- if (!existsSync41(path)) return null;
7806
+ if (!existsSync43(path)) return null;
7592
7807
  try {
7593
7808
  return readJson(path);
7594
7809
  } catch {
@@ -7602,12 +7817,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
7602
7817
  }
7603
7818
  const notes = [];
7604
7819
  const hostOverrides = readJsonOrNull(hostPath);
7605
- if (hostOverrides === null && existsSync41(hostPath)) {
7820
+ if (hostOverrides === null && existsSync43(hostPath)) {
7606
7821
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
7607
7822
  }
7608
7823
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
7609
7824
  const current = readJsonOrNull(settingsPath);
7610
- if (current === null && existsSync41(settingsPath)) {
7825
+ if (current === null && existsSync43(settingsPath)) {
7611
7826
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
7612
7827
  }
7613
7828
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -7623,6 +7838,12 @@ function formatLinkRow(e) {
7623
7838
  if (e.kind === "copy") return `would copy ${e.from} -> ${e.to}`;
7624
7839
  return `${e.kind} ${e.from} -> ${e.to}`;
7625
7840
  }
7841
+ function formatCaptureRow(c) {
7842
+ return `would capture ${c.localPath} -> ${c.repoPath}`;
7843
+ }
7844
+ function formatDeletionRow(d) {
7845
+ return `would remove ${d.repoPath} (gone from ${d.localPath})`;
7846
+ }
7626
7847
  function formatSessionRow(e) {
7627
7848
  return e.kind === "overwrite" ? `overwrite ${e.dst} (from ${e.src})` : e.text;
7628
7849
  }
@@ -7638,20 +7859,30 @@ function buildSettingsSectionForPreview(result) {
7638
7859
  }
7639
7860
  return s;
7640
7861
  }
7641
- function computePreview(ts, map, verb = "pull") {
7862
+ function computePreview(ts, map, verb = "pull", plans) {
7642
7863
  const repo = repoHome();
7643
7864
  const claude = claudeHome();
7644
7865
  console.log(`would pull on host=${HOST} (preview; nothing applied)`);
7645
7866
  console.log("");
7646
7867
  const links = section("Symlinks");
7868
+ const { captures, deletions } = plans ?? {
7869
+ captures: planSharedLinkCaptures(map),
7870
+ deletions: planSharedLinkDeletions(map)
7871
+ };
7872
+ for (const capture of captures) {
7873
+ addItem(links, formatCaptureRow(capture));
7874
+ }
7875
+ for (const deletion of deletions) {
7876
+ addItem(links, formatDeletionRow(deletion));
7877
+ }
7647
7878
  applySharedLinks(ts, map, {
7648
7879
  dryRun: true,
7649
7880
  onPreview: (e) => addItem(links, formatLinkRow(e))
7650
7881
  });
7651
7882
  const settingsResult = previewSettings(
7652
- join52(repo, "shared", "settings.base.json"),
7653
- join52(repo, "hosts", `${HOST}.json`),
7654
- join52(claude, "settings.json")
7883
+ join55(repo, "shared", "settings.base.json"),
7884
+ join55(repo, "hosts", `${HOST}.json`),
7885
+ join55(claude, "settings.json")
7655
7886
  );
7656
7887
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
7657
7888
  const sessions = section("Sessions");
@@ -7667,7 +7898,7 @@ function computePreview(ts, map, verb = "pull") {
7667
7898
  const extras = section("Extras");
7668
7899
  let extrasSkipped = 0;
7669
7900
  let extrasUnmapped = 0;
7670
- if (existsSync41(join52(repo, "path-map.json")) && existsSync41(join52(repo, "shared", "extras"))) {
7901
+ if (existsSync43(join55(repo, "path-map.json")) && existsSync43(join55(repo, "shared", "extras"))) {
7671
7902
  const extrasResult = remapExtrasPull(ts, { dryRun: true });
7672
7903
  for (const entry of extrasResult.wouldPull) {
7673
7904
  addItem(extras, entry);
@@ -7684,6 +7915,135 @@ function computePreview(ts, map, verb = "pull") {
7684
7915
  return { unmapped: remapResult.unmapped, collisions: 0, localOnly };
7685
7916
  }
7686
7917
 
7918
+ // src/commands.pull.win32.ts
7919
+ init_utils();
7920
+ init_utils_json();
7921
+ function readMapForMirror(mapPath) {
7922
+ if (!existsSync44(mapPath)) return { projects: {} };
7923
+ try {
7924
+ return readPathMap(mapPath);
7925
+ } catch {
7926
+ return null;
7927
+ }
7928
+ }
7929
+ function untrackedUnderShared(repo) {
7930
+ const out = gitProbe(["ls-files", "--others", "--exclude-standard", "-z", "--", "shared/"], repo);
7931
+ if (out === null) return null;
7932
+ return new Set(out.split("\0").filter((p) => p !== ""));
7933
+ }
7934
+ function newlyUntracked(before, after) {
7935
+ if (before === null || after === null) return [];
7936
+ return [...after].filter((p) => !before.has(p));
7937
+ }
7938
+ function reconcileSharedLinksBeforePull(repo, ts) {
7939
+ if (process.platform !== "win32") return [];
7940
+ const before = untrackedUnderShared(repo);
7941
+ try {
7942
+ const map = readMapForMirror(join56(repo, "path-map.json"));
7943
+ stageLocalSharedEdits(map, ts);
7944
+ applySharedLinkDeletions(map, ts);
7945
+ } catch (err) {
7946
+ warn(`could not reconcile local shared edits before the pull: ${err.message}`);
7947
+ }
7948
+ return newlyUntracked(before, untrackedUnderShared(repo));
7949
+ }
7950
+ function planSharedReconcileBeforePull(repo) {
7951
+ const map = readMapForMirror(join56(repo, "path-map.json"));
7952
+ return { captures: planSharedLinkCaptures(map), deletions: planSharedLinkDeletions(map) };
7953
+ }
7954
+
7955
+ // src/commands.pull.collision.ts
7956
+ init_config();
7957
+ init_exit_codes();
7958
+ import { existsSync as existsSync45, lstatSync as lstatSync18, readFileSync as readFileSync20, rmSync as rmSync18 } from "node:fs";
7959
+ import { join as join57 } from "node:path";
7960
+ init_utils();
7961
+ var SHARED_PREFIX = /^shared\//;
7962
+ var EXTENSION = /(\.[^.]+)?$/;
7963
+ function localOriginal(repoRel) {
7964
+ return `~/.claude/${repoRel.replace(SHARED_PREFIX, "")}`;
7965
+ }
7966
+ function renameSuggestion(localPath) {
7967
+ const base = localPath.slice(localPath.lastIndexOf("/") + 1);
7968
+ return base.replace(EXTENSION, ".local$1");
7969
+ }
7970
+ function collisionSteps(locals) {
7971
+ const single = locals.length === 1;
7972
+ const target = single ? locals[0] : "each file listed above";
7973
+ const rename = single ? renameSuggestion(locals[0]) : "mine.md becomes mine.local.md";
7974
+ const combine = single ? "combine the two files" : "combine each pair";
7975
+ return `Keep the incoming version (yours is set aside, not deleted):
7976
+ 1. move ${target} outside ~/.claude/
7977
+ 2. nomad pull
7978
+
7979
+ Keep both and merge them yourself:
7980
+ 1. rename ${target} to a name the repo does not use (${rename})
7981
+ 2. nomad pull (brings in the other machine's version)
7982
+ 3. ${combine}, then nomad push`;
7983
+ }
7984
+ function collisionAdvice(single, cleared) {
7985
+ const copies = single ? "nomad's copy" : "nomad's copies";
7986
+ const them = single ? "it" : "them";
7987
+ const fate = cleared ? "which nomad has now removed for you" : `which nomad could not remove (the warning above says why), so delete ${them} by hand if the next pull stops here again`;
7988
+ const moving = single ? "Moving only that copy would not have cleared this anyway: the next pull re-copies your local file over it before fetching. The file to move is the one under ~/.claude/." : "Moving only those copies would not have cleared this anyway: the next pull re-copies your local files over them before fetching. The files to move are the ones under ~/.claude/.";
7989
+ return `Git's advice above refers to ${copies} inside the sync repo, ${fate}. ${moving}`;
7990
+ }
7991
+ function untrackedCollisionRunbookText(repoRelPaths, cleared) {
7992
+ const locals = repoRelPaths.map(localOriginal);
7993
+ const single = locals.length === 1;
7994
+ const claim = single ? "One of those copies has the same name as a file the incoming update also adds:" : "Each of the copies below has the same name as a file the incoming update also adds:";
7995
+ const intact = single ? "Your file is still exactly as you left it." : "Your files are still exactly as you left them.";
7996
+ const listed = locals.map((p) => ` ${p}`).join("\n");
7997
+ return `nomad pull could not fetch. Your machine had unpublished shared-config edits, so nomad copied them into the sync repo first, the way an edit on macOS or Linux is already in the repo before a pull starts. ${claim}
7998
+
7999
+ ${listed}
8000
+
8001
+ Nothing changed. Your ~/.claude/ config is untouched, the update did not land, and nothing was published. ${intact}
8002
+
8003
+ ${collisionAdvice(single, cleared)}
8004
+
8005
+ ` + collisionSteps(locals);
8006
+ }
8007
+ function addedByIncomingUpdate(repo, created) {
8008
+ return created.filter((rel) => gitProbe(["cat-file", "-e", `FETCH_HEAD:${rel}`], repo) !== null);
8009
+ }
8010
+ function isContainedMirrorPath(rel) {
8011
+ return rel.startsWith("shared/") && isSafeRelPath(rel);
8012
+ }
8013
+ function matchesLocalOriginal(repo, rel) {
8014
+ try {
8015
+ const local = join57(claudeHome(), rel.replace(SHARED_PREFIX, ""));
8016
+ return readFileSync20(join57(repo, rel)).equals(readFileSync20(local));
8017
+ } catch {
8018
+ return false;
8019
+ }
8020
+ }
8021
+ function removeMirroredCopies(repo, repoRelPaths) {
8022
+ for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
8023
+ const abs = join57(repo, rel);
8024
+ try {
8025
+ if (lstatSync18(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
8026
+ if (!matchesLocalOriginal(repo, rel)) continue;
8027
+ rmSync18(abs, { force: true });
8028
+ } catch (err) {
8029
+ warn(`could not remove nomad's copy at ${abs}: ${err.message}`);
8030
+ }
8031
+ }
8032
+ return repoRelPaths.every((rel) => !existsSync45(join57(repo, rel)));
8033
+ }
8034
+ function pullWithCollisionRunbook(repo, mirrored) {
8035
+ try {
8036
+ gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
8037
+ } catch (err) {
8038
+ const colliding = addedByIncomingUpdate(repo, mirrored);
8039
+ if (colliding.length === 0) throw err;
8040
+ const cleared = removeMirroredCopies(repo, colliding);
8041
+ throw new NomadFatal(untrackedCollisionRunbookText(colliding, cleared), {
8042
+ code: EXIT.GENERIC_FAILURE
8043
+ });
8044
+ }
8045
+ }
8046
+
7687
8047
  // src/commands.pull.ts
7688
8048
  init_commands_pull_wedge();
7689
8049
 
@@ -7694,9 +8054,9 @@ init_utils_fs();
7694
8054
 
7695
8055
  // src/commands.pull.recovery.git.ts
7696
8056
  init_utils();
7697
- import { execFileSync as execFileSync21 } from "node:child_process";
8057
+ import { execFileSync as execFileSync22 } from "node:child_process";
7698
8058
  function gitCapture(args, cwd) {
7699
- return execFileSync21("git", args, {
8059
+ return execFileSync22("git", args, {
7700
8060
  cwd,
7701
8061
  stdio: ["ignore", "pipe", "pipe"],
7702
8062
  maxBuffer: 64 * 1024 * 1024
@@ -7849,6 +8209,7 @@ function capturePrePostHeads(repo, rebase) {
7849
8209
  }
7850
8210
  function buildWetPullSections(ts, map, prePostHeads) {
7851
8211
  applySharedLinks(ts, map);
8212
+ writeSharedBaseline(map);
7852
8213
  const { label } = regenerateSettings(ts);
7853
8214
  syncSkillsPull(ts, prePostHeads);
7854
8215
  const remapResult = withSpinner("Syncing sessions", () => remapPull(ts));
@@ -7888,22 +8249,6 @@ function handleWedge(repo, forceRemote) {
7888
8249
  const state = wedge === "rebase" ? "mid-rebase" : "mid-merge";
7889
8250
  die(wedgeMarkerRunbookText(state), { code: EXIT.CONFLICT });
7890
8251
  }
7891
- function readMapForMirror(mapPath) {
7892
- if (!existsSync42(mapPath)) return { projects: {} };
7893
- try {
7894
- return readPathMap(mapPath);
7895
- } catch {
7896
- return null;
7897
- }
7898
- }
7899
- function mirrorSharedLinksBeforePull(repo, ts) {
7900
- if (process.platform !== "win32") return;
7901
- try {
7902
- stageLocalSharedEdits(readMapForMirror(join53(repo, "path-map.json")), ts);
7903
- } catch (err) {
7904
- warn(`could not stage local shared edits before the pull: ${err.message}`);
7905
- }
7906
- }
7907
8252
  function runPullCore(opts = {}) {
7908
8253
  const dryRun = opts.dryRun === true;
7909
8254
  const forceRemote = opts.forceRemote === true;
@@ -7913,7 +8258,7 @@ function runPullCore(opts = {}) {
7913
8258
  const ts = freshBackupTs(backup);
7914
8259
  handleWedge(repo, forceRemote);
7915
8260
  if (!dryRun) {
7916
- const backupRoot = join53(backup, ts);
8261
+ const backupRoot = join58(backup, ts);
7917
8262
  try {
7918
8263
  mkdirSync14(backupRoot, { recursive: true });
7919
8264
  } catch (err) {
@@ -7925,16 +8270,17 @@ function runPullCore(opts = {}) {
7925
8270
  dryRun ? `pulling on host=${HOST} (backup=${ts}; dry-run)` : `pull on host=${HOST} (backup=${ts})`
7926
8271
  );
7927
8272
  }
7928
- if (!dryRun && !forceRemote) mirrorSharedLinksBeforePull(repo, ts);
8273
+ const mirrored = !dryRun && !forceRemote ? reconcileSharedLinksBeforePull(repo, ts) : [];
8274
+ const sharedPlans = dryRun ? planSharedReconcileBeforePull(repo) : void 0;
7929
8275
  const prePostHeads = capturePrePostHeads(repo, () => {
7930
- gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
8276
+ pullWithCollisionRunbook(repo, mirrored);
7931
8277
  });
7932
8278
  assertNoAutostashConflict(repo, "nomad pull");
7933
- const mapPath = join53(repo, "path-map.json");
7934
- const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
8279
+ const mapPath = join58(repo, "path-map.json");
8280
+ const map = existsSync46(mapPath) ? readPathMap(mapPath) : { projects: {} };
7935
8281
  const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
7936
8282
  if (dryRun) {
7937
- computePreview(ts, map, "pull");
8283
+ computePreview(ts, map, "pull", sharedPlans);
7938
8284
  return { tag: "dry" };
7939
8285
  }
7940
8286
  const { sections, localOnly, settingsLabel, unmapped, extrasSkipped } = buildWetPullSections(
@@ -7956,8 +8302,8 @@ function runPullCore(opts = {}) {
7956
8302
  }
7957
8303
  function cmdPull(opts = {}) {
7958
8304
  const repo = repoHome();
7959
- if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
7960
- if (!existsSync42(join53(repo, "shared", "settings.base.json"))) {
8305
+ if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
8306
+ if (!existsSync46(join58(repo, "shared", "settings.base.json"))) {
7961
8307
  die("repo not initialized; run 'nomad init' to scaffold");
7962
8308
  }
7963
8309
  const handle = acquireLock("pull");
@@ -7980,13 +8326,13 @@ function cmdPull(opts = {}) {
7980
8326
 
7981
8327
  // src/commands.push.ts
7982
8328
  init_config();
7983
- import { existsSync as existsSync46 } from "node:fs";
7984
- import { join as join58 } from "node:path";
8329
+ import { existsSync as existsSync50 } from "node:fs";
8330
+ import { join as join63 } from "node:path";
7985
8331
 
7986
8332
  // src/commands.push.selection.ts
7987
8333
  init_config();
7988
- import { existsSync as existsSync43, statSync as statSync11 } from "node:fs";
7989
- import { join as join54 } from "node:path";
8334
+ import { existsSync as existsSync47, statSync as statSync11 } from "node:fs";
8335
+ import { join as join59 } from "node:path";
7990
8336
  init_utils_json();
7991
8337
  function buildCurrentMap(map) {
7992
8338
  const current = {};
@@ -7995,8 +8341,8 @@ function buildCurrentMap(map) {
7995
8341
  for (const [, hostMap] of Object.entries(map.projects)) {
7996
8342
  const localPath = hostMap[HOST];
7997
8343
  if (!localPath) continue;
7998
- const localDir = join54(claude, "projects", encodePath(localPath));
7999
- if (!existsSync43(localDir)) continue;
8344
+ const localDir = join59(claude, "projects", encodePath(localPath));
8345
+ if (!existsSync47(localDir)) continue;
8000
8346
  for (const f of enumerateSourceFiles(localDir)) {
8001
8347
  const st = statSync11(f);
8002
8348
  current[f] = { size: st.size, mtime: st.mtimeMs };
@@ -8027,7 +8373,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
8027
8373
  };
8028
8374
  }
8029
8375
  function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
8030
- const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
8376
+ const map = existsSync47(mapPath) ? readPathMap(mapPath) : null;
8031
8377
  const { selection, newManifest } = computePushSelection(
8032
8378
  map,
8033
8379
  old,
@@ -8129,14 +8475,14 @@ function enforceAllowList(statusPorcelain, map) {
8129
8475
 
8130
8476
  // src/commands.push.settings.ts
8131
8477
  init_config();
8132
- import { existsSync as existsSync44 } from "node:fs";
8133
- import { join as join55 } from "node:path";
8478
+ import { existsSync as existsSync48 } from "node:fs";
8479
+ import { join as join60 } from "node:path";
8134
8480
  init_utils();
8135
8481
  init_utils_fs();
8136
8482
  init_utils_json();
8137
8483
  function stripGsdHooksFromBase(repo, backup) {
8138
- const basePath = join55(repo, "shared", "settings.base.json");
8139
- if (!existsSync44(basePath)) return;
8484
+ const basePath = join60(repo, "shared", "settings.base.json");
8485
+ if (!existsSync48(basePath)) return;
8140
8486
  let base;
8141
8487
  try {
8142
8488
  base = readJson(basePath);
@@ -8150,14 +8496,14 @@ function stripGsdHooksFromBase(repo, backup) {
8150
8496
  writeJsonAtomic(basePath, stripped);
8151
8497
  }
8152
8498
  function reportSettingsAheadDrift(repo) {
8153
- const basePath = join55(repo, "shared", "settings.base.json");
8154
- if (!existsSync44(basePath)) return;
8155
- const settingsPath = join55(claudeHome(), "settings.json");
8156
- if (!existsSync44(settingsPath)) return;
8499
+ const basePath = join60(repo, "shared", "settings.base.json");
8500
+ if (!existsSync48(basePath)) return;
8501
+ const settingsPath = join60(claudeHome(), "settings.json");
8502
+ if (!existsSync48(settingsPath)) return;
8157
8503
  try {
8158
8504
  const base = readJson(basePath);
8159
- const hostPath = join55(repo, "hosts", `${HOST}.json`);
8160
- const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
8505
+ const hostPath = join60(repo, "hosts", `${HOST}.json`);
8506
+ const overrides = existsSync48(hostPath) ? readJson(hostPath) : {};
8161
8507
  const merged = deepMerge(base, overrides);
8162
8508
  const settings = readJson(settingsPath);
8163
8509
  const { ahead } = classifySettingsDrift(merged, settings);
@@ -8174,12 +8520,12 @@ function reportSettingsAheadDrift(repo) {
8174
8520
  // src/commands.push.guards.ts
8175
8521
  init_push_checks();
8176
8522
  init_utils();
8177
- import { join as join56, relative as relative7 } from "node:path";
8523
+ import { join as join61, relative as relative9 } from "node:path";
8178
8524
  function guardGitlinks(repo) {
8179
- const gitlinks = findGitlinks(join56(repo, "shared"));
8525
+ const gitlinks = findGitlinks(join61(repo, "shared"));
8180
8526
  if (gitlinks.length === 0) return;
8181
8527
  for (const p of gitlinks) {
8182
- const rel = relative7(repo, p).replaceAll("\\", "/");
8528
+ const rel = relative9(repo, p).replaceAll("\\", "/");
8183
8529
  fail(`gitlink: ${rel} would push as submodule (run: rm -rf ${rel} or remove the nested repo)`);
8184
8530
  }
8185
8531
  const noun = gitlinks.length === 1 ? "entry" : "entries";
@@ -8208,7 +8554,7 @@ init_config();
8208
8554
 
8209
8555
  // src/push-global-config.ts
8210
8556
  init_config();
8211
- import { execFileSync as execFileSync22 } from "node:child_process";
8557
+ import { execFileSync as execFileSync23 } from "node:child_process";
8212
8558
  var STATUS_LABELS = {
8213
8559
  A: "add",
8214
8560
  M: "modify",
@@ -8248,7 +8594,7 @@ function isInScope(filePath, exactPrefixes, dirPrefixes) {
8248
8594
  }
8249
8595
  function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
8250
8596
  const args = opts.staged ? ["diff", "--cached", "--name-status", "-z"] : ["diff", "HEAD", "--name-status", "-z"];
8251
- const raw = execFileSync22("git", args, {
8597
+ const raw = execFileSync23("git", args, {
8252
8598
  cwd: repoHome2,
8253
8599
  stdio: ["ignore", "pipe", "pipe"]
8254
8600
  }).toString();
@@ -8286,9 +8632,9 @@ init_color();
8286
8632
  init_config();
8287
8633
  init_config_sharedDirs_guard();
8288
8634
  import { randomBytes as randomBytes4 } from "node:crypto";
8289
- import { copyFileSync, existsSync as existsSync45, lstatSync as lstatSync15, mkdirSync as mkdirSync15, readdirSync as readdirSync18, rmSync as rmSync17 } from "node:fs";
8635
+ import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync19, mkdirSync as mkdirSync15, readdirSync as readdirSync19, rmSync as rmSync19 } from "node:fs";
8290
8636
  import { homedir as homedir7 } from "node:os";
8291
- import { join as join57, relative as relative8, sep as sep11 } from "node:path";
8637
+ import { join as join62, relative as relative10, sep as sep13 } from "node:path";
8292
8638
  init_push_leak_verdict();
8293
8639
  init_push_gitleaks();
8294
8640
  init_utils_fs();
@@ -8296,11 +8642,11 @@ init_utils_json();
8296
8642
  var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
8297
8643
  function stageSessionDir(localDir, dstDir, changed) {
8298
8644
  if (changed !== void 0) {
8299
- const prefix = `${localDir}${sep11}`;
8645
+ const prefix = `${localDir}${sep13}`;
8300
8646
  const matching = [...changed].filter((p) => p.startsWith(prefix));
8301
8647
  if (matching.length === 0) return false;
8302
8648
  for (const src of matching) {
8303
- copyFileAtomic(src, join57(dstDir, relative8(localDir, src)));
8649
+ copyFileAtomic(src, join62(dstDir, relative10(localDir, src)));
8304
8650
  }
8305
8651
  return true;
8306
8652
  }
@@ -8316,14 +8662,14 @@ function stageSessions(tmpRoot, map, changed) {
8316
8662
  if (!p || p === "TBD") continue;
8317
8663
  reverse.set(encodePath(p), logical);
8318
8664
  }
8319
- const localProjects = join57(claudeHome(), "projects");
8320
- if (!existsSync45(localProjects)) return 0;
8665
+ const localProjects = join62(claudeHome(), "projects");
8666
+ if (!existsSync49(localProjects)) return 0;
8321
8667
  let staged = 0;
8322
- for (const dir of readdirSync18(localProjects)) {
8668
+ for (const dir of readdirSync19(localProjects)) {
8323
8669
  const logical = reverse.get(dir);
8324
8670
  if (!logical) continue;
8325
- const localDir = join57(localProjects, dir);
8326
- const dstDir = join57(tmpRoot, "shared", "projects", logical);
8671
+ const localDir = join62(localProjects, dir);
8672
+ const dstDir = join62(tmpRoot, "shared", "projects", logical);
8327
8673
  if (stageSessionDir(localDir, dstDir, changed)) staged++;
8328
8674
  }
8329
8675
  return staged;
@@ -8339,9 +8685,9 @@ function stageExtras(tmpRoot, map) {
8339
8685
  if (!localRoot || localRoot === "TBD") continue;
8340
8686
  for (const dirname14 of dirnames) {
8341
8687
  if (!whitelist.includes(dirname14)) continue;
8342
- const src = join57(localRoot, dirname14);
8343
- if (!existsSync45(src)) continue;
8344
- const dst = join57(tmpRoot, "shared", "extras", logical, dirname14);
8688
+ const src = join62(localRoot, dirname14);
8689
+ if (!existsSync49(src)) continue;
8690
+ const dst = join62(tmpRoot, "shared", "extras", logical, dirname14);
8345
8691
  copyExtras(src, dst);
8346
8692
  staged++;
8347
8693
  }
@@ -8349,19 +8695,19 @@ function stageExtras(tmpRoot, map) {
8349
8695
  return staged;
8350
8696
  }
8351
8697
  function stageSkills(tmpRoot) {
8352
- const localSkills = join57(claudeHome(), "skills");
8353
- const stat = lstatSync15(localSkills, { throwIfNoEntry: false });
8698
+ const localSkills = join62(claudeHome(), "skills");
8699
+ const stat = lstatSync19(localSkills, { throwIfNoEntry: false });
8354
8700
  if (stat === void 0 || stat.isSymbolicLink()) return 0;
8355
- const names = readdirSync18(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
8701
+ const names = readdirSync19(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
8356
8702
  if (names.length === 0) return 0;
8357
- copySkillsPush(localSkills, join57(tmpRoot, "shared", "skills"));
8703
+ copySkillsPush(localSkills, join62(tmpRoot, "shared", "skills"));
8358
8704
  return names.length;
8359
8705
  }
8360
8706
  function previewPushLeaks(map, opts = {}) {
8361
- const cacheDir = join57(homedir7(), ".cache", "claude-nomad");
8707
+ const cacheDir = join62(homedir7(), ".cache", "claude-nomad");
8362
8708
  mkdirSync15(cacheDir, { recursive: true });
8363
8709
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
8364
- const tmpRoot = join57(cacheDir, `push-preview-tree-${stamp}`);
8710
+ const tmpRoot = join62(cacheDir, `push-preview-tree-${stamp}`);
8365
8711
  try {
8366
8712
  const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
8367
8713
  const extrasCount = stageExtras(tmpRoot, map);
@@ -8369,9 +8715,9 @@ function previewPushLeaks(map, opts = {}) {
8369
8715
  if (sessionCount + extrasCount + skillCount === 0) {
8370
8716
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
8371
8717
  }
8372
- const ignoreFile = join57(repoHome(), ".gitleaksignore");
8373
- if (existsSync45(ignoreFile)) {
8374
- copyFileSync(ignoreFile, join57(tmpRoot, ".gitleaksignore"));
8718
+ const ignoreFile = join62(repoHome(), ".gitleaksignore");
8719
+ if (existsSync49(ignoreFile)) {
8720
+ copyFileSync(ignoreFile, join62(tmpRoot, ".gitleaksignore"));
8375
8721
  }
8376
8722
  let findings;
8377
8723
  try {
@@ -8384,7 +8730,7 @@ function previewPushLeaks(map, opts = {}) {
8384
8730
  }
8385
8731
  return verdictFromFindings(findings);
8386
8732
  } finally {
8387
- rmSync17(tmpRoot, { recursive: true, force: true });
8733
+ rmSync19(tmpRoot, { recursive: true, force: true });
8388
8734
  }
8389
8735
  }
8390
8736
 
@@ -8499,7 +8845,7 @@ async function runPushCore(opts = {}) {
8499
8845
  const scannerVersion = probeGitleaks();
8500
8846
  const configHash = computeConfigHash();
8501
8847
  const old = readManifest(manifestPath());
8502
- const mapPath = join58(repo, "path-map.json");
8848
+ const mapPath = join63(repo, "path-map.json");
8503
8849
  const { map, selection, newManifest } = loadSelectionForPush(
8504
8850
  mapPath,
8505
8851
  old,
@@ -8551,7 +8897,7 @@ async function cmdPush(opts = {}) {
8551
8897
  opts.allowRule
8552
8898
  );
8553
8899
  const repo = repoHome();
8554
- if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
8900
+ if (!existsSync50(repo)) die(`repo not cloned at ${repo}`);
8555
8901
  const handle = acquireLock("push");
8556
8902
  if (handle === null) process.exit(0);
8557
8903
  try {
@@ -8569,8 +8915,8 @@ async function cmdPush(opts = {}) {
8569
8915
  }
8570
8916
 
8571
8917
  // src/commands.sync.ts
8572
- import { existsSync as existsSync47 } from "node:fs";
8573
- import { join as join59 } from "node:path";
8918
+ import { existsSync as existsSync51 } from "node:fs";
8919
+ import { join as join64 } from "node:path";
8574
8920
  init_config();
8575
8921
  init_color();
8576
8922
  init_utils();
@@ -8704,8 +9050,8 @@ async function cmdSync(opts = {}) {
8704
9050
  const dryRun = opts.dryRun === true;
8705
9051
  const verbose = opts.verbose === true;
8706
9052
  const repo = repoHome();
8707
- if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
8708
- if (!existsSync47(join59(repo, "shared", "settings.base.json"))) {
9053
+ if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
9054
+ if (!existsSync51(join64(repo, "shared", "settings.base.json"))) {
8709
9055
  die("repo not initialized; run 'nomad init' to scaffold");
8710
9056
  }
8711
9057
  const handle = acquireLock("sync");
@@ -8729,9 +9075,9 @@ async function cmdSync(opts = {}) {
8729
9075
  }
8730
9076
 
8731
9077
  // src/commands.update.ts
8732
- import { execFileSync as execFileSync23 } from "node:child_process";
9078
+ import { execFileSync as execFileSync24 } from "node:child_process";
8733
9079
  init_utils();
8734
- function readInstalledVersion(run = execFileSync23) {
9080
+ function readInstalledVersion(run = execFileSync24) {
8735
9081
  const isWin = process.platform === "win32";
8736
9082
  try {
8737
9083
  return run(isWin ? "nomad.cmd" : "nomad", ["--version"], {
@@ -8742,7 +9088,7 @@ function readInstalledVersion(run = execFileSync23) {
8742
9088
  return null;
8743
9089
  }
8744
9090
  }
8745
- function cmdUpdate(currentVersion, run = execFileSync23) {
9091
+ function cmdUpdate(currentVersion, run = execFileSync24) {
8746
9092
  console.log(`Updating claude-nomad v${currentVersion}...`);
8747
9093
  const isWin = process.platform === "win32";
8748
9094
  try {
@@ -8781,8 +9127,8 @@ init_config();
8781
9127
  // src/crash-report.write.ts
8782
9128
  init_config();
8783
9129
  import { randomBytes as randomBytes5 } from "node:crypto";
8784
- import { mkdirSync as mkdirSync16, readdirSync as readdirSync19, statSync as statSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
8785
- import { join as join61 } from "node:path";
9130
+ import { mkdirSync as mkdirSync16, readdirSync as readdirSync20, statSync as statSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
9131
+ import { join as join66 } from "node:path";
8786
9132
 
8787
9133
  // src/crash-report.ts
8788
9134
  var CRASH_MAX_STACK_LINES = 50;
@@ -8865,17 +9211,17 @@ function buildCrashReport(input) {
8865
9211
  }
8866
9212
 
8867
9213
  // src/crash-report.redact.ts
8868
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync18, writeFileSync as writeFileSync10 } from "node:fs";
9214
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync20, writeFileSync as writeFileSync10 } from "node:fs";
8869
9215
  import { tmpdir as tmpdir2 } from "node:os";
8870
- import { join as join60 } from "node:path";
9216
+ import { join as join65 } from "node:path";
8871
9217
  init_push_gitleaks_scan();
8872
9218
  var CRASH_SCAN_TIMEOUT_MS = 3e3;
8873
9219
  var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
8874
9220
  function redactWithGitleaks(text, scan = scanFile) {
8875
9221
  let dir;
8876
9222
  try {
8877
- dir = mkdtempSync3(join60(tmpdir2(), "nomad-crash-scan-"));
8878
- const tmp = join60(dir, "crash.txt");
9223
+ dir = mkdtempSync3(join65(tmpdir2(), "nomad-crash-scan-"));
9224
+ const tmp = join65(dir, "crash.txt");
8879
9225
  writeFileSync10(tmp, text, { mode: 384 });
8880
9226
  const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
8881
9227
  if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
@@ -8885,7 +9231,7 @@ function redactWithGitleaks(text, scan = scanFile) {
8885
9231
  } finally {
8886
9232
  if (dir !== void 0) {
8887
9233
  try {
8888
- rmSync18(dir, { recursive: true, force: true });
9234
+ rmSync20(dir, { recursive: true, force: true });
8889
9235
  } catch {
8890
9236
  }
8891
9237
  }
@@ -8898,9 +9244,9 @@ init_utils();
8898
9244
  var CRASH_RETENTION_KEEP = 20;
8899
9245
  function listCrashFiles(dir = crashDir()) {
8900
9246
  try {
8901
- return readdirSync19(dir).flatMap((name) => {
9247
+ return readdirSync20(dir).flatMap((name) => {
8902
9248
  try {
8903
- return [{ name, mtimeMs: statSync12(join61(dir, name)).mtimeMs }];
9249
+ return [{ name, mtimeMs: statSync12(join66(dir, name)).mtimeMs }];
8904
9250
  } catch {
8905
9251
  return [];
8906
9252
  }
@@ -8914,7 +9260,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
8914
9260
  const targets = prunableByCount(files, keep);
8915
9261
  for (const name of targets) {
8916
9262
  try {
8917
- unlinkSync2(join61(dir, name));
9263
+ unlinkSync2(join66(dir, name));
8918
9264
  } catch {
8919
9265
  }
8920
9266
  }
@@ -8922,7 +9268,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
8922
9268
  function writeCrashReport(text, dir = crashDir()) {
8923
9269
  mkdirSync16(dir, { recursive: true, mode: 448 });
8924
9270
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes5(4).toString("hex")}`;
8925
- const path = join61(dir, `crash-${stamp}.txt`);
9271
+ const path = join66(dir, `crash-${stamp}.txt`);
8926
9272
  writeFileSync11(path, text, { mode: 384 });
8927
9273
  pruneCrashDir(dir);
8928
9274
  return path;
@@ -8954,18 +9300,18 @@ function handleCrash(err, argv, opts) {
8954
9300
 
8955
9301
  // src/diff.ts
8956
9302
  init_config();
8957
- import { existsSync as existsSync48 } from "node:fs";
8958
- import { join as join62 } from "node:path";
9303
+ import { existsSync as existsSync52 } from "node:fs";
9304
+ import { join as join67 } from "node:path";
8959
9305
  init_utils();
8960
9306
  init_utils_fs();
8961
9307
  init_utils_json();
8962
9308
  function cmdDiff() {
8963
9309
  try {
8964
9310
  const repo = repoHome();
8965
- if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
9311
+ if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
8966
9312
  const ts = freshBackupTs(backupBase());
8967
- const mapPath = join62(repo, "path-map.json");
8968
- const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
9313
+ const mapPath = join67(repo, "path-map.json");
9314
+ const map = existsSync52(mapPath) ? readPathMap(mapPath) : { projects: {} };
8969
9315
  divergenceCheckExtras(ts);
8970
9316
  computePreview(ts, map, "diff");
8971
9317
  } catch (err) {
@@ -8980,19 +9326,19 @@ function cmdDiff() {
8980
9326
 
8981
9327
  // src/init.ts
8982
9328
  init_config();
8983
- import { existsSync as existsSync50, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
8984
- import { join as join64 } from "node:path";
9329
+ import { existsSync as existsSync54, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
9330
+ import { join as join69 } from "node:path";
8985
9331
 
8986
9332
  // src/init.gh-onboard.ts
8987
9333
  init_config();
8988
- import { execFileSync as execFileSync24 } from "node:child_process";
9334
+ import { execFileSync as execFileSync25 } from "node:child_process";
8989
9335
  init_utils();
8990
9336
  var DEFAULT_REPO_NAME = "claude-nomad-config";
8991
9337
  function isValidRepoName(name) {
8992
9338
  return /^[A-Za-z0-9._-]{1,100}$/.test(name);
8993
9339
  }
8994
9340
  var GH_NETWORK_TIMEOUT_MS = 3e4;
8995
- function ensureOriginRepo(repoName, run = execFileSync24) {
9341
+ function ensureOriginRepo(repoName, run = execFileSync25) {
8996
9342
  if (!isValidRepoName(repoName)) {
8997
9343
  die(
8998
9344
  `invalid repo name: ${JSON.stringify(repoName)}. Use only letters, digits, hyphens, underscores, and dots (1-100 chars).`
@@ -9063,33 +9409,33 @@ init_config();
9063
9409
  init_utils();
9064
9410
  init_utils_fs();
9065
9411
  init_utils_json();
9066
- import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync49, rmSync as rmSync19, statSync as statSync13 } from "node:fs";
9067
- import { join as join63 } from "node:path";
9412
+ import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync53, rmSync as rmSync21, statSync as statSync13 } from "node:fs";
9413
+ import { join as join68 } from "node:path";
9068
9414
  function snapshotIntoShared(map) {
9069
9415
  const repo = repoHome();
9070
9416
  const claude = claudeHome();
9071
9417
  for (const name of allSharedLinks(map)) {
9072
- const src = join63(claude, name);
9073
- if (!existsSync49(src)) continue;
9074
- const dst = join63(repo, "shared", name);
9418
+ const src = join68(claude, name);
9419
+ if (!existsSync53(src)) continue;
9420
+ const dst = join68(repo, "shared", name);
9075
9421
  if (statSync13(src).isDirectory()) {
9076
- const gk = join63(dst, ".gitkeep");
9077
- if (existsSync49(gk)) rmSync19(gk);
9422
+ const gk = join68(dst, ".gitkeep");
9423
+ if (existsSync53(gk)) rmSync21(gk);
9078
9424
  cpSync10(src, dst, { recursive: true, force: false, errorOnExist: true });
9079
9425
  } else {
9080
9426
  copyFileSync2(src, dst);
9081
9427
  }
9082
9428
  log(`snapshotted shared/${name} from ${src}`);
9083
9429
  }
9084
- const userSettings = join63(claude, "settings.json");
9085
- if (existsSync49(userSettings)) {
9430
+ const userSettings = join68(claude, "settings.json");
9431
+ if (existsSync53(userSettings)) {
9086
9432
  let parsed;
9087
9433
  try {
9088
9434
  parsed = readJson(userSettings);
9089
9435
  } catch (err) {
9090
9436
  return die(`malformed ${userSettings}: ${err.message}`);
9091
9437
  }
9092
- const hostFile = join63(repo, "hosts", `${HOST}.json`);
9438
+ const hostFile = join68(repo, "hosts", `${HOST}.json`);
9093
9439
  writeJsonAtomic(hostFile, parsed);
9094
9440
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
9095
9441
  }
@@ -9103,14 +9449,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
9103
9449
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
9104
9450
  function preflightConflict(repoHome2) {
9105
9451
  const candidates = [
9106
- join64(repoHome2, "shared", "settings.base.json"),
9107
- join64(repoHome2, "shared", "CLAUDE.md"),
9108
- join64(repoHome2, "path-map.json"),
9109
- join64(repoHome2, "hosts"),
9110
- join64(repoHome2, "shared")
9452
+ join69(repoHome2, "shared", "settings.base.json"),
9453
+ join69(repoHome2, "shared", "CLAUDE.md"),
9454
+ join69(repoHome2, "path-map.json"),
9455
+ join69(repoHome2, "hosts"),
9456
+ join69(repoHome2, "shared")
9111
9457
  ];
9112
9458
  for (const c of candidates) {
9113
- if (existsSync50(c)) return c;
9459
+ if (existsSync54(c)) return c;
9114
9460
  }
9115
9461
  return null;
9116
9462
  }
@@ -9128,27 +9474,27 @@ function cmdInit(opts = {}) {
9128
9474
  die(`already initialized; refusing to clobber ${conflict}`);
9129
9475
  }
9130
9476
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
9131
- mkdirSync17(join64(repo, "shared"), { recursive: true });
9132
- mkdirSync17(join64(repo, "hosts"), { recursive: true });
9477
+ mkdirSync17(join69(repo, "shared"), { recursive: true });
9478
+ mkdirSync17(join69(repo, "hosts"), { recursive: true });
9133
9479
  for (const name of SHARED_KEEP_DIRS) {
9134
- mkdirSync17(join64(repo, "shared", name), { recursive: true });
9480
+ mkdirSync17(join69(repo, "shared", name), { recursive: true });
9135
9481
  }
9136
- const userClaudeMd = join64(claude, "CLAUDE.md");
9137
- if (!snapshot || !existsSync50(userClaudeMd)) {
9138
- writeFileSync12(join64(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
9482
+ const userClaudeMd = join69(claude, "CLAUDE.md");
9483
+ if (!snapshot || !existsSync54(userClaudeMd)) {
9484
+ writeFileSync12(join69(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
9139
9485
  item("created shared/CLAUDE.md");
9140
9486
  }
9141
9487
  for (const name of SHARED_KEEP_DIRS) {
9142
- writeFileSync12(join64(repo, "shared", name, ".gitkeep"), "");
9488
+ writeFileSync12(join69(repo, "shared", name, ".gitkeep"), "");
9143
9489
  item(`created shared/${name}/.gitkeep`);
9144
9490
  }
9145
- writeFileSync12(join64(repo, "hosts", ".gitkeep"), "");
9491
+ writeFileSync12(join69(repo, "hosts", ".gitkeep"), "");
9146
9492
  item("created hosts/.gitkeep");
9147
- writeJsonAtomic(join64(repo, "shared", "settings.base.json"), {});
9493
+ writeJsonAtomic(join69(repo, "shared", "settings.base.json"), {});
9148
9494
  item("created shared/settings.base.json");
9149
- writeJsonAtomic(join64(repo, "path-map.json"), { projects: {} });
9495
+ writeJsonAtomic(join69(repo, "path-map.json"), { projects: {} });
9150
9496
  item("created path-map.json");
9151
- writeFileSync12(join64(repo, ".gitattributes"), GITATTRIBUTES);
9497
+ writeFileSync12(join69(repo, ".gitattributes"), GITATTRIBUTES);
9152
9498
  item("created .gitattributes");
9153
9499
  if (snapshot) {
9154
9500
  snapshotIntoShared({ projects: {} });
@@ -9214,21 +9560,21 @@ function maybeDisableRepoActions(repoHome2, run) {
9214
9560
  // src/init.prompt.ts
9215
9561
  init_config();
9216
9562
  init_utils();
9217
- import { existsSync as existsSync51, readdirSync as readdirSync20, statSync as statSync14 } from "node:fs";
9218
- import { join as join65 } from "node:path";
9563
+ import { existsSync as existsSync55, readdirSync as readdirSync21, statSync as statSync14 } from "node:fs";
9564
+ import { join as join70 } from "node:path";
9219
9565
  import { createInterface as createInterface3 } from "node:readline/promises";
9220
9566
  function nonEmptyExists(path) {
9221
- if (!existsSync51(path)) return false;
9567
+ if (!existsSync55(path)) return false;
9222
9568
  try {
9223
- if (statSync14(path).isDirectory()) return readdirSync20(path).length > 0;
9569
+ if (statSync14(path).isDirectory()) return readdirSync21(path).length > 0;
9224
9570
  return true;
9225
9571
  } catch {
9226
9572
  return false;
9227
9573
  }
9228
9574
  }
9229
9575
  function hasExistingClaudeConfig(claudeHome2) {
9230
- if (existsSync51(join65(claudeHome2, "settings.json"))) return true;
9231
- return SHARED_LINKS.some((name) => nonEmptyExists(join65(claudeHome2, name)));
9576
+ if (existsSync55(join70(claudeHome2, "settings.json"))) return true;
9577
+ return SHARED_LINKS.some((name) => nonEmptyExists(join70(claudeHome2, name)));
9232
9578
  }
9233
9579
  async function confirmSnapshotDefault(claudeHome2) {
9234
9580
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -9535,7 +9881,7 @@ function parseSyncArgs(argv) {
9535
9881
  // package.json
9536
9882
  var package_default = {
9537
9883
  name: "claude-nomad",
9538
- version: "0.63.2",
9884
+ version: "0.64.0",
9539
9885
  type: "module",
9540
9886
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
9541
9887
  keywords: [
@@ -9779,15 +10125,15 @@ var DEFAULT_HELP = [
9779
10125
  init_config();
9780
10126
  init_utils();
9781
10127
  init_utils_json();
9782
- import { existsSync as existsSync52, readFileSync as readFileSync20, readdirSync as readdirSync21 } from "node:fs";
9783
- import { join as join66 } from "node:path";
10128
+ import { existsSync as existsSync56, readFileSync as readFileSync21, readdirSync as readdirSync22 } from "node:fs";
10129
+ import { join as join71 } from "node:path";
9784
10130
  function resumeCmd(sessionId) {
9785
10131
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
9786
10132
  fail(`invalid session id: ${sessionId}`);
9787
10133
  process.exit(1);
9788
10134
  }
9789
- const projectsRoot = join66(claudeHome(), "projects");
9790
- if (!existsSync52(projectsRoot)) {
10135
+ const projectsRoot = join71(claudeHome(), "projects");
10136
+ if (!existsSync56(projectsRoot)) {
9791
10137
  fail(`${projectsRoot} does not exist`);
9792
10138
  process.exit(1);
9793
10139
  }
@@ -9801,8 +10147,8 @@ function resumeCmd(sessionId) {
9801
10147
  fail(`no cwd field found in ${jsonlPath}`);
9802
10148
  process.exit(1);
9803
10149
  }
9804
- const mapPath = join66(repoHome(), "path-map.json");
9805
- if (!existsSync52(mapPath)) {
10150
+ const mapPath = join71(repoHome(), "path-map.json");
10151
+ if (!existsSync56(mapPath)) {
9806
10152
  fail("path-map.json missing");
9807
10153
  process.exit(1);
9808
10154
  }
@@ -9824,14 +10170,14 @@ function resumeCmd(sessionId) {
9824
10170
  console.log(`cd ${shQuote(hit.localPath)} && claude --resume ${shQuote(sessionId)}`);
9825
10171
  }
9826
10172
  function findTranscriptPath(projectsRoot, sessionId) {
9827
- for (const dir of readdirSync21(projectsRoot)) {
9828
- const candidate = join66(projectsRoot, dir, `${sessionId}.jsonl`);
9829
- if (existsSync52(candidate)) return candidate;
10173
+ for (const dir of readdirSync22(projectsRoot)) {
10174
+ const candidate = join71(projectsRoot, dir, `${sessionId}.jsonl`);
10175
+ if (existsSync56(candidate)) return candidate;
9830
10176
  }
9831
10177
  return null;
9832
10178
  }
9833
10179
  function extractRecordedCwd(jsonlPath) {
9834
- for (const line of readFileSync20(jsonlPath, "utf8").split("\n")) {
10180
+ for (const line of readFileSync21(jsonlPath, "utf8").split("\n")) {
9835
10181
  if (!line.trim()) continue;
9836
10182
  try {
9837
10183
  const obj = JSON.parse(line);