claude-nomad 0.64.0 → 0.64.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.64.1](https://github.com/funkadelic/claude-nomad/compare/v0.64.0...v0.64.1) (2026-08-07)
4
+
5
+ ### What's new
6
+
7
+ - **A stray dot or space at the end of a file name no longer hides a secret.** nomad never copies
8
+ password and key files (things like `.env`, `id_rsa`, or `server.pem`) out of the project folders
9
+ you sync. It spots them by name, and a name with an extra dot or space on the end, such as
10
+ `.env.`, did not match, so a file like that could end up in your sync repo. It matches now, and so
11
+ do the other files nomad always keeps out, such as `settings.local.json`. Nothing changes in how
12
+ you use nomad. It simply catches more ways of writing the same names it already blocked.
13
+ - **A `.claude` folder is recognized however it is capitalized.** If a project had a folder named
14
+ `.Claude` instead of `.claude`, nomad checked what was inside it less carefully than it should
15
+ have. Both are treated the same now.
16
+ - **A folder name nomad will never sync is turned down as soon as you add it.** You can ask nomad to
17
+ sync extra folders by listing their names in `path-map.json`. If a name you list is one nomad
18
+ refuses to sync, because it looks like a password file, or because it is a name that causes
19
+ trouble on one of the machines you sync to, you are told straight away and told why. Before,
20
+ nothing was said until a later `nomad push` stopped without explaining itself. `nomad doctor` also
21
+ lists any names in this situation and what to do about them.
22
+
23
+ All of this applies on macOS, Linux, WSL2, and native Windows.
24
+
25
+
26
+ ### Fixed
27
+
28
+ * **config:** close the trailing-dot bypass in the secret-name guard ([#495](https://github.com/funkadelic/claude-nomad/issues/495)) ([9aaf5d9](https://github.com/funkadelic/claude-nomad/commit/9aaf5d958027a3557974c1ab32254c6171aea631))
29
+ * reject a secret-shaped name as a shared dir ([#493](https://github.com/funkadelic/claude-nomad/issues/493)) ([3054464](https://github.com/funkadelic/claude-nomad/commit/3054464c91c974a7abdad24b2067bab8386e1dfd))
30
+
31
+
32
+ ### Changed
33
+
34
+ * **release:** publish the release body from CHANGELOG.md ([#496](https://github.com/funkadelic/claude-nomad/issues/496)) ([e7bff07](https://github.com/funkadelic/claude-nomad/commit/e7bff07504935e563936733eae1146dc6eeaca83))
35
+
3
36
  ## [0.64.0](https://github.com/funkadelic/claude-nomad/compare/v0.63.2...v0.64.0) (2026-08-05)
4
37
 
5
38
  ### What's new
package/dist/nomad.mjs CHANGED
@@ -42,11 +42,21 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
42
42
  ));
43
43
 
44
44
  // src/config.never-sync.ts
45
+ function stripTrailingDotsAndWhitespace(name) {
46
+ let end = name.length;
47
+ while (end > 0 && (name[end - 1] === "." || /\s/.test(name[end - 1]))) end -= 1;
48
+ return name.slice(0, end);
49
+ }
45
50
  function isSecretFileName(name) {
46
- return SECRET_FILE_PATTERNS.some((re) => re.test(name));
51
+ const stripped = stripTrailingDotsAndWhitespace(name);
52
+ return SECRET_FILE_PATTERNS.some((re) => re.test(stripped));
47
53
  }
48
54
  function isDeniedName(blockSet, name) {
49
- return blockSet.has(name) || blockSet.has(name.toLowerCase()) || isSecretFileName(name);
55
+ const stripped = stripTrailingDotsAndWhitespace(name);
56
+ return blockSet.has(name) || blockSet.has(name.toLowerCase()) || blockSet.has(stripped) || blockSet.has(stripped.toLowerCase()) || isSecretFileName(name);
57
+ }
58
+ function isClaudeExtraName(name) {
59
+ return stripTrailingDotsAndWhitespace(name).toLowerCase() === ".claude";
50
60
  }
51
61
  var NEVER_SYNC, CLAUDE_EXTRA_NEVER_SYNC, SECRET_FILE_PATTERNS;
52
62
  var init_config_never_sync = __esm({
@@ -302,14 +312,67 @@ function assertSafeLogical(logical) {
302
312
  );
303
313
  }
304
314
  }
315
+ function validateSharedDirEntry(entry) {
316
+ if (typeof entry !== "string") {
317
+ return { reason: "not-a-string", message: "not a string" };
318
+ }
319
+ if (!SAFE_SEGMENT.test(entry) || entry === "." || entry === "..") {
320
+ return {
321
+ reason: "not-a-segment",
322
+ message: 'not a single path segment (contains a path separator, an unsupported character, "." or "..")'
323
+ };
324
+ }
325
+ const addressed = stripTrailingDots(entry);
326
+ const named = classifyDeniedName(addressed);
327
+ if (named !== null) {
328
+ return addressed === entry ? named : {
329
+ reason: named.reason,
330
+ message: `a trailing-dot name shadowing ${named.message}`
331
+ };
332
+ }
333
+ if (entry.endsWith(".")) {
334
+ return {
335
+ reason: "win32-alias",
336
+ message: 'a trailing-dot name (git for Windows refuses to create or check out a path ending in ".")'
337
+ };
338
+ }
339
+ return null;
340
+ }
341
+ function stripTrailingDots(name) {
342
+ let end = name.length;
343
+ while (end > 0 && name[end - 1] === ".") end -= 1;
344
+ return name.slice(0, end);
345
+ }
346
+ function classifyDeniedName(name) {
347
+ const folded = name.toLowerCase();
348
+ if (NEVER_SYNC.has(name) || NEVER_SYNC.has(folded)) {
349
+ return { reason: "never-sync", message: "a never-sync name" };
350
+ }
351
+ if (RESERVED_SHARED.has(name) || RESERVED_SHARED_FOLDED.has(folded)) {
352
+ return { reason: "reserved", message: "a reserved shared/ name" };
353
+ }
354
+ const dot = folded.indexOf(".");
355
+ const stem = dot === -1 ? folded : folded.slice(0, dot);
356
+ if (WIN32_DEVICE_NAMES.has(stem)) {
357
+ return { reason: "reserved", message: "a reserved Windows device name" };
358
+ }
359
+ if (isSecretFileName(name)) {
360
+ return {
361
+ reason: "secret-shaped",
362
+ message: "a credential-shaped filename (.env, id_rsa, credentials, *.pem, *.key and similar)"
363
+ };
364
+ }
365
+ return null;
366
+ }
367
+ function mayJoinRefusedEntry(entry, reason, remediable) {
368
+ if (UNJOINABLE_REASONS.has(reason)) return false;
369
+ if (entry.endsWith(".")) return true;
370
+ return remediable.has(reason);
371
+ }
305
372
  function isValidSharedDir(entry) {
306
- if (typeof entry !== "string") return false;
307
- if (!SAFE_SEGMENT.test(entry) || entry === "." || entry === "..") return false;
308
- if (NEVER_SYNC.has(entry)) return false;
309
- if (RESERVED_SHARED.has(entry)) return false;
310
- return true;
373
+ return validateSharedDirEntry(entry) === null;
311
374
  }
312
- var SAFE_LOGICAL, SAFE_SEGMENT, RESERVED_SHARED;
375
+ var SAFE_LOGICAL, SAFE_SEGMENT, RESERVED_SHARED, RESERVED_SHARED_FOLDED, WIN32_DEVICE_NAMES, UNJOINABLE_REASONS;
313
376
  var init_config_sharedDirs_guard = __esm({
314
377
  "src/config.sharedDirs.guard.ts"() {
315
378
  "use strict";
@@ -331,6 +394,19 @@ var init_config_sharedDirs_guard = __esm({
331
394
  "extras",
332
395
  "projects"
333
396
  ]);
397
+ RESERVED_SHARED_FOLDED = new Set([...RESERVED_SHARED].map((name) => name.toLowerCase()));
398
+ WIN32_DEVICE_NAMES = /* @__PURE__ */ new Set([
399
+ "con",
400
+ "prn",
401
+ "aux",
402
+ "nul",
403
+ ...Array.from({ length: 9 }, (_unused, i) => `com${i + 1}`),
404
+ ...Array.from({ length: 9 }, (_unused, i) => `lpt${i + 1}`)
405
+ ]);
406
+ UNJOINABLE_REASONS = /* @__PURE__ */ new Set([
407
+ "not-a-string",
408
+ "not-a-segment"
409
+ ]);
334
410
  }
335
411
  });
336
412
 
@@ -541,19 +617,28 @@ function manifestPath() {
541
617
  function sharedBaselinePath() {
542
618
  return join(home(), ".cache", "claude-nomad", `shared-baseline-${encodeURIComponent(HOST)}.json`);
543
619
  }
544
- function allSharedLinks(map) {
620
+ function allSharedLinks(map, opts = {}) {
621
+ const emit = opts.quiet === true ? () => void 0 : warn;
622
+ const raw = map.sharedDirs;
623
+ if (raw !== void 0 && !Array.isArray(raw)) {
624
+ emit("sharedDirs in path-map.json is not an array; ignoring the whole field");
625
+ return [...SHARED_LINKS];
626
+ }
545
627
  const extras = [];
546
- for (const entry of map.sharedDirs ?? []) {
547
- if (isValidSharedDir(entry)) {
628
+ for (const entry of raw ?? []) {
629
+ const rejection = validateSharedDirEntry(entry);
630
+ if (rejection === null) {
548
631
  extras.push(entry);
549
632
  } else {
550
- warn(
551
- `sharedDirs entry ${JSON.stringify(entry)} is invalid (path separator, reserved name, or NEVER_SYNC); skipping`
552
- );
633
+ emit(`sharedDirs entry ${JSON.stringify(entry)} rejected: ${rejection.message}; skipping`);
553
634
  }
554
635
  }
555
636
  return [...SHARED_LINKS, ...extras];
556
637
  }
638
+ function sharedDirEntries(map) {
639
+ const raw = map.sharedDirs;
640
+ return Array.isArray(raw) ? raw : [];
641
+ }
557
642
  var SETTINGS_SCHEMA_URL, NPM_REGISTRY_LATEST_URL, GITLEAKS_PINNED_VERSION, GITLEAKS_SCAN_TIMEOUT_MS, HOST, SHARED_LINKS, GSD_PREFIX, GSD_DROPPED_NAMES, SUPPORTED_EXTRAS, ALWAYS_NEVER_SYNC, PUSH_ALLOWED_STATIC;
558
643
  var init_config = __esm({
559
644
  "src/config.ts"() {
@@ -1422,6 +1507,7 @@ var init_push_leak_verdict = __esm({
1422
1507
  // src/commands.adopt.ts
1423
1508
  init_config();
1424
1509
  init_config_sharedDirs_guard();
1510
+ init_exit_codes();
1425
1511
  import { cpSync as cpSync4, existsSync as existsSync5, lstatSync as lstatSync5, rmSync as rmSync4 } from "node:fs";
1426
1512
  import { join as join6 } from "node:path";
1427
1513
 
@@ -1892,7 +1978,7 @@ function copyExtrasFileSkipDiverged(src, dst) {
1892
1978
  copyExtras(src, dst);
1893
1979
  }
1894
1980
  function extrasDenySet(dirname14) {
1895
- return dirname14 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1981
+ return isClaudeExtraName(dirname14) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1896
1982
  }
1897
1983
  function copyExtrasFiltered(src, dst, blockSet) {
1898
1984
  rmSync2(dst, { recursive: true, force: true });
@@ -2133,7 +2219,7 @@ function readMapIfPresent(repoHome2) {
2133
2219
  return existsSync5(mapPath) ? readPathMap(mapPath) : { projects: {} };
2134
2220
  }
2135
2221
  function isConfiguredTarget(name, map) {
2136
- return SHARED_LINKS.includes(name) || (map.sharedDirs?.includes(name) ?? false);
2222
+ return SHARED_LINKS.includes(name) || sharedDirEntries(map).includes(name);
2137
2223
  }
2138
2224
  function isValidAdoptName(name) {
2139
2225
  if (SHARED_LINKS.includes(name)) return true;
@@ -2155,6 +2241,13 @@ function performAdoptMove(name, linkPath, sharedTarget, repo, backup) {
2155
2241
  }
2156
2242
  function cmdAdopt(name, opts = {}) {
2157
2243
  const dryRun = opts.dryRun === true;
2244
+ const rejection = validateSharedDirEntry(name);
2245
+ if (rejection !== null && rejection.reason === "secret-shaped") {
2246
+ throw new NomadFatal(
2247
+ `cannot adopt ${JSON.stringify(name)}: ${rejection.message}. If it is listed in sharedDirs in path-map.json, remove it there too.`,
2248
+ { code: EXIT.GENERIC_FAILURE }
2249
+ );
2250
+ }
2158
2251
  if (!isValidAdoptName(name)) {
2159
2252
  fail(`invalid name: ${JSON.stringify(name)}`);
2160
2253
  process.exit(1);
@@ -2568,6 +2661,7 @@ function cmdClean(opts, backupBase2 = backupBase()) {
2568
2661
 
2569
2662
  // src/commands.eject.ts
2570
2663
  init_config();
2664
+ init_config_sharedDirs_guard();
2571
2665
  init_utils();
2572
2666
  init_utils_fs();
2573
2667
  init_utils_json();
@@ -2705,6 +2799,19 @@ function materializeOneOrDie(name, linkPath, sharedRoot, done) {
2705
2799
  );
2706
2800
  }
2707
2801
  }
2802
+ var WIDENED_REASONS = /* @__PURE__ */ new Set([
2803
+ "never-sync",
2804
+ "reserved",
2805
+ "secret-shaped"
2806
+ ]);
2807
+ function ejectNames(map) {
2808
+ const alreadyMaterialized = sharedDirEntries(map).filter((entry) => {
2809
+ if (typeof entry !== "string") return false;
2810
+ const rejection = validateSharedDirEntry(entry);
2811
+ return rejection === null || mayJoinRefusedEntry(entry, rejection.reason, WIDENED_REASONS);
2812
+ });
2813
+ return [.../* @__PURE__ */ new Set([...allSharedLinks(map), ...alreadyMaterialized])];
2814
+ }
2708
2815
  function defaultEjectRoots() {
2709
2816
  return { claudeHome: claudeHome(), repoHome: repoHome() };
2710
2817
  }
@@ -2712,16 +2819,31 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
2712
2819
  const dryRun = opts.dryRun === true;
2713
2820
  const { claudeHome: claudeHome2, repoHome: repoHome2 } = roots;
2714
2821
  const map = readMapIfPresent2(repoHome2);
2715
- const names = allSharedLinks(map);
2822
+ const names = ejectNames(map);
2716
2823
  const classifications = /* @__PURE__ */ new Map();
2717
2824
  for (const name of names) {
2718
2825
  classifications.set(name, classifyName(join11(claudeHome2, name)));
2719
2826
  }
2827
+ const base = new Set(allSharedLinks(map, { quiet: true }));
2828
+ for (const name of names) {
2829
+ if (base.has(name)) continue;
2830
+ if (classifications.get(name) !== "materialize") continue;
2831
+ item(`processing rejected entry already present on this host: ${name}`);
2832
+ }
2720
2833
  const dangling = names.filter((n) => classifications.get(n) === "dangling");
2721
- if (dangling.length > 0) {
2834
+ const danglingBase = dangling.filter((n) => base.has(n));
2835
+ const danglingRefused = dangling.filter((n) => !base.has(n));
2836
+ if (danglingBase.length > 0) {
2837
+ fail(
2838
+ `dangling symlink(s): ${danglingBase.join(", ")}. run \`nomad pull\` first to restore the missing target, then re-run \`nomad eject\``
2839
+ );
2840
+ }
2841
+ if (danglingRefused.length > 0) {
2722
2842
  fail(
2723
- `dangling symlink(s): ${dangling.join(", ")}. run \`nomad pull\` first to restore the missing target, then re-run \`nomad eject\``
2843
+ `dangling symlink(s) for a refused name nomad cannot restore: ${danglingRefused.join(", ")}. recover the content by hand if you need it, then remove the dead link and re-run \`nomad eject\``
2724
2844
  );
2845
+ }
2846
+ if (dangling.length > 0) {
2725
2847
  process.exit(1);
2726
2848
  }
2727
2849
  const sharedRoot = resolveSharedRoot(repoHome2);
@@ -3066,7 +3188,7 @@ function classifySymlinkTarget(name, p) {
3066
3188
  }
3067
3189
  function reportSharedLinks(section2, map) {
3068
3190
  const claude = claudeHome();
3069
- for (const name of allSharedLinks(map)) {
3191
+ for (const name of allSharedLinks(map, { quiet: true })) {
3070
3192
  const p = join13(claude, name);
3071
3193
  const { line, fail: fail2, children } = classifySharedLink(name, p);
3072
3194
  addItem(section2, line);
@@ -3158,8 +3280,9 @@ function reportHostOverrides(section2, base, settings) {
3158
3280
  // src/commands.doctor.checks.pathmap.ts
3159
3281
  init_color();
3160
3282
  init_config();
3161
- import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
3162
- import { join as join15 } from "node:path";
3283
+ init_config_sharedDirs_guard();
3284
+ import { existsSync as existsSync13, lstatSync as lstatSync9, readdirSync as readdirSync6, realpathSync as realpathSync2 } from "node:fs";
3285
+ import { join as join15, sep as sep4 } from "node:path";
3163
3286
  init_utils_json();
3164
3287
  function reportMappedProjects(section2, map) {
3165
3288
  const mapped = Object.entries(map.projects).filter(([, hosts]) => hosts[HOST]);
@@ -3199,6 +3322,95 @@ function reportCurrentHostPathsMissing(section2, map) {
3199
3322
  }
3200
3323
  }
3201
3324
  }
3325
+ function hasSharedLeftover(entry) {
3326
+ try {
3327
+ return lstatSync9(join15(repoHome(), "shared", entry), { throwIfNoEntry: false }) !== void 0;
3328
+ } catch {
3329
+ return false;
3330
+ }
3331
+ }
3332
+ var PROBEABLE_REASONS = /* @__PURE__ */ new Set([
3333
+ "never-sync",
3334
+ "secret-shaped"
3335
+ ]);
3336
+ function classifyLocalLink(entry) {
3337
+ const linkPath = join15(claudeHome(), entry);
3338
+ let stat;
3339
+ try {
3340
+ stat = lstatSync9(linkPath, { throwIfNoEntry: false });
3341
+ } catch {
3342
+ return "absent";
3343
+ }
3344
+ if (stat?.isSymbolicLink() !== true) return "absent";
3345
+ let target;
3346
+ try {
3347
+ target = realpathSync2(linkPath);
3348
+ } catch {
3349
+ return "dangling";
3350
+ }
3351
+ let root;
3352
+ try {
3353
+ root = realpathSync2(join15(repoHome(), "shared"));
3354
+ } catch {
3355
+ return "foreign";
3356
+ }
3357
+ return target.startsWith(root + sep4) ? "managed" : "foreign";
3358
+ }
3359
+ function reportRejectedSharedDirs(section2, map) {
3360
+ const raw = map.sharedDirs;
3361
+ if (raw !== void 0 && !Array.isArray(raw)) {
3362
+ addItem(
3363
+ section2,
3364
+ `${yellow(warnGlyph)} path-map: sharedDirs is not an array (${typeof raw}); the whole field is ignored`
3365
+ );
3366
+ return;
3367
+ }
3368
+ const entries = Array.isArray(raw) ? raw : [];
3369
+ const probable = [];
3370
+ for (const entry of entries) {
3371
+ const rejection = validateSharedDirEntry(entry);
3372
+ if (rejection === null) continue;
3373
+ if (typeof entry === "string" && mayJoinRefusedEntry(entry, rejection.reason, PROBEABLE_REASONS)) {
3374
+ probable.push(entry);
3375
+ }
3376
+ addItem(
3377
+ section2,
3378
+ `${yellow(warnGlyph)} path-map: sharedDirs entry ${JSON.stringify(entry)} rejected: ${rejection.message}; skipping`
3379
+ );
3380
+ }
3381
+ reportRejectedLeftovers(section2, probable);
3382
+ }
3383
+ function reportRejectedLeftovers(section2, probable) {
3384
+ for (const entry of probable) {
3385
+ const link = classifyLocalLink(entry);
3386
+ if (link === "managed") {
3387
+ addItem(
3388
+ section2,
3389
+ `${yellow(warnGlyph)} path-map: entry ${JSON.stringify(entry)} is a symlink under ~/.claude/ pointing into shared/; copy the content out first (cp -RL), then remove both. nomad will not do it for you`
3390
+ );
3391
+ continue;
3392
+ }
3393
+ if (link === "dangling") {
3394
+ addItem(
3395
+ section2,
3396
+ `${yellow(warnGlyph)} path-map: entry ${JSON.stringify(entry)} is a DANGLING symlink under ~/.claude/ (its target does not resolve); do not delete anything under shared/ yet, since a copy there may be the only one left. nomad cannot restore this name, because the entry is refused: recover the content by hand if you need it, then remove the dead link`
3397
+ );
3398
+ continue;
3399
+ }
3400
+ if (link === "foreign") {
3401
+ addItem(
3402
+ section2,
3403
+ `${yellow(warnGlyph)} path-map: entry ${JSON.stringify(entry)} is a symlink under ~/.claude/ pointing OUTSIDE shared/; nomad does not manage it, so leave it alone and just drop the entry from sharedDirs`
3404
+ );
3405
+ }
3406
+ if (hasSharedLeftover(entry)) {
3407
+ addItem(
3408
+ section2,
3409
+ `${yellow(warnGlyph)} path-map: shared/ entry ${JSON.stringify(entry)} exists in the repo working tree; remove it by hand, nomad will not delete it`
3410
+ );
3411
+ }
3412
+ }
3413
+ }
3202
3414
  function reportPathCollisions(section2, map) {
3203
3415
  const seen = /* @__PURE__ */ new Map();
3204
3416
  let collisionCount = 0;
@@ -3230,6 +3442,7 @@ function reportPathMap(section2) {
3230
3442
  }
3231
3443
  const map = readJsonSafe(mapPath, mapPath, section2);
3232
3444
  if (map === null) return;
3445
+ reportRejectedSharedDirs(section2, map);
3233
3446
  const shapeError = validatePathMapShape(map);
3234
3447
  if (shapeError !== null) {
3235
3448
  addItem(section2, `${red(failGlyph)} ${shapeError}`);
@@ -3425,7 +3638,7 @@ function reportGitIdentity(section2) {
3425
3638
 
3426
3639
  // src/commands.doctor.checks.backups.ts
3427
3640
  init_color();
3428
- import { existsSync as existsSync18, lstatSync as lstatSync9, readdirSync as readdirSync8 } from "node:fs";
3641
+ import { existsSync as existsSync18, lstatSync as lstatSync10, readdirSync as readdirSync8 } from "node:fs";
3429
3642
  import { join as join21 } from "node:path";
3430
3643
  init_config();
3431
3644
  var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
@@ -3443,7 +3656,7 @@ function dirSizeBytes(dir) {
3443
3656
  let bytes = 0;
3444
3657
  for (const entry of safeReaddir(dir)) {
3445
3658
  const full = join21(dir, entry);
3446
- const st = lstatSync9(full, { throwIfNoEntry: false });
3659
+ const st = lstatSync10(full, { throwIfNoEntry: false });
3447
3660
  if (!st) continue;
3448
3661
  if (st.isSymbolicLink()) continue;
3449
3662
  if (st.isDirectory()) bytes += dirSizeBytes(full);
@@ -3922,8 +4135,8 @@ init_config();
3922
4135
 
3923
4136
  // src/remap.ts
3924
4137
  init_config_sharedDirs_guard();
3925
- import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync7, readdirSync as readdirSync10, rmSync as rmSync11, statSync as statSync5 } from "node:fs";
3926
- import { dirname as dirname6, join as join28, relative as relative5, sep as sep4 } from "node:path";
4138
+ import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync10, rmSync as rmSync11, statSync as statSync5 } from "node:fs";
4139
+ import { dirname as dirname6, join as join28, relative as relative5, sep as sep5 } from "node:path";
3927
4140
  init_config();
3928
4141
 
3929
4142
  // src/push-manifest.ts
@@ -4055,7 +4268,7 @@ function copyFileAtomic(src, dst) {
4055
4268
  renameAtomicRetry(tmp, dst);
4056
4269
  }
4057
4270
  function hasDeltaForDir(sel, localDir) {
4058
- const prefix = `${localDir}${sep4}`;
4271
+ const prefix = `${localDir}${sep5}`;
4059
4272
  for (const p of sel.changed) if (p.startsWith(prefix)) return true;
4060
4273
  for (const p of sel.deleted) if (p.startsWith(prefix)) return true;
4061
4274
  return false;
@@ -4064,7 +4277,7 @@ function skipForSelection(sel, localDir) {
4064
4277
  return sel !== void 0 && !hasDeltaForDir(sel, localDir);
4065
4278
  }
4066
4279
  function applySelective(sel, localDir, repoDst) {
4067
- const prefix = `${localDir}${sep4}`;
4280
+ const prefix = `${localDir}${sep5}`;
4068
4281
  for (const src of sel.changed) {
4069
4282
  if (!src.startsWith(prefix)) continue;
4070
4283
  copyFileAtomic(src, join28(repoDst, relative5(localDir, src)));
@@ -4082,7 +4295,7 @@ function copyDirJsonlOnly(src, dst) {
4082
4295
  filter: (srcPath) => {
4083
4296
  const rel = relative5(src, srcPath);
4084
4297
  if (rel === "") return true;
4085
- if (rel.split(sep4).length > 1) return true;
4298
+ if (rel.split(sep5).length > 1) return true;
4086
4299
  if (statSync5(srcPath).isDirectory()) return true;
4087
4300
  if (srcPath.endsWith(".jsonl")) return true;
4088
4301
  item(`skip ${rel}: extension not in allowlist`);
@@ -4142,9 +4355,9 @@ function countLocalOnly(src, dst) {
4142
4355
  for (const name of readdirSync10(dst)) {
4143
4356
  const dstPath = join28(dst, name);
4144
4357
  const srcPath = join28(src, name);
4145
- if (lstatSync10(dstPath).isDirectory()) {
4358
+ if (lstatSync11(dstPath).isDirectory()) {
4146
4359
  count += countLocalOnly(srcPath, dstPath);
4147
- } else if (lstatSync10(srcPath, { throwIfNoEntry: false }) === void 0) {
4360
+ } else if (lstatSync11(srcPath, { throwIfNoEntry: false }) === void 0) {
4148
4361
  count++;
4149
4362
  }
4150
4363
  }
@@ -4334,7 +4547,7 @@ function reportCheckShared(section2, gitleaksReady) {
4334
4547
 
4335
4548
  // src/commands.doctor.checks.hooks.scope.ts
4336
4549
  init_color();
4337
- import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync12, realpathSync as realpathSync2 } from "node:fs";
4550
+ import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync12, realpathSync as realpathSync3 } from "node:fs";
4338
4551
  import { dirname as dirname7, extname, join as join30 } from "node:path";
4339
4552
  init_config();
4340
4553
  function typeFromPackageJson(pkgPath) {
@@ -4351,7 +4564,7 @@ function effectiveType(hookPath) {
4351
4564
  if (ext === ".cjs") return "cjs";
4352
4565
  let real;
4353
4566
  try {
4354
- real = realpathSync2(hookPath);
4567
+ real = realpathSync3(hookPath);
4355
4568
  } catch {
4356
4569
  return null;
4357
4570
  }
@@ -4513,7 +4726,7 @@ import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:
4513
4726
  import { join as join33 } from "node:path";
4514
4727
 
4515
4728
  // src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
4516
- import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
4729
+ import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync4 } from "node:fs";
4517
4730
  import { dirname as dirname8, join as join32, resolve as resolve2 } from "node:path";
4518
4731
  function suppressedRanges(src) {
4519
4732
  const ranges = [];
@@ -4558,7 +4771,7 @@ function specifierIsMissing(specifier, baseDir) {
4558
4771
  function relativeRequireTargetsBroken(scriptPath) {
4559
4772
  let realPath;
4560
4773
  try {
4561
- realPath = realpathSync3(scriptPath);
4774
+ realPath = realpathSync4(scriptPath);
4562
4775
  } catch {
4563
4776
  return false;
4564
4777
  }
@@ -4702,7 +4915,7 @@ function reportPreserveSymlinksCheck(section2) {
4702
4915
  return;
4703
4916
  }
4704
4917
  const map = readPathMapSafe();
4705
- const sharedLinkNames = allSharedLinks(map);
4918
+ const sharedLinkNames = allSharedLinks(map, { quiet: true });
4706
4919
  let anyWarn = false;
4707
4920
  for (const [event, groups] of Object.entries(hooks)) {
4708
4921
  if (!Array.isArray(groups)) continue;
@@ -5052,21 +5265,21 @@ import { createInterface as createInterface2 } from "node:readline/promises";
5052
5265
  // src/commands.push.recovery.actions.ts
5053
5266
  init_config();
5054
5267
  import { readFileSync as readFileSync17 } from "node:fs";
5055
- import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep8 } from "node:path";
5268
+ import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep9 } from "node:path";
5056
5269
 
5057
5270
  // src/commands.push.recovery.memory.ts
5058
5271
  init_config();
5059
5272
  init_config_sharedDirs_guard();
5060
5273
  import {
5061
5274
  cpSync as cpSync7,
5062
- lstatSync as lstatSync11,
5275
+ lstatSync as lstatSync12,
5063
5276
  mkdirSync as mkdirSync9,
5064
5277
  readFileSync as readFileSync14,
5065
- realpathSync as realpathSync4,
5278
+ realpathSync as realpathSync5,
5066
5279
  statSync as statSync6,
5067
5280
  writeFileSync as writeFileSync6
5068
5281
  } from "node:fs";
5069
- import { dirname as dirname9, join as join36, sep as sep5 } from "node:path";
5282
+ import { dirname as dirname9, join as join36, sep as sep6 } from "node:path";
5070
5283
  init_push_gitleaks_scan();
5071
5284
 
5072
5285
  // src/rel-path-guard.ts
@@ -5097,12 +5310,12 @@ function resolveMemoryLocalPath(logical, relPath, map) {
5097
5310
  if (abs === void 0) return null;
5098
5311
  const memoryRoot = join36(claudeHome(), "projects", encodePath(abs), "memory");
5099
5312
  const localPath = join36(memoryRoot, ...relPath.split("/"));
5100
- if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep5)) return null;
5313
+ if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep6)) return null;
5101
5314
  try {
5102
- if (lstatSync11(localPath).isSymbolicLink()) return null;
5103
- const realLocal = realpathSync4(localPath);
5104
- const realRoot = realpathSync4(memoryRoot);
5105
- if (!realLocal.startsWith(realRoot + sep5)) return null;
5315
+ if (lstatSync12(localPath).isSymbolicLink()) return null;
5316
+ const realLocal = realpathSync5(localPath);
5317
+ const realRoot = realpathSync5(memoryRoot);
5318
+ if (!realLocal.startsWith(realRoot + sep6)) return null;
5106
5319
  if (!statSync6(realLocal).isFile()) return null;
5107
5320
  } catch {
5108
5321
  return null;
@@ -5160,18 +5373,18 @@ function applyMemoryRedact(f, ts, map, scan = scanFile) {
5160
5373
  init_config();
5161
5374
  import {
5162
5375
  cpSync as cpSync8,
5163
- lstatSync as lstatSync13,
5376
+ lstatSync as lstatSync14,
5164
5377
  mkdirSync as mkdirSync11,
5165
5378
  readFileSync as readFileSync15,
5166
- realpathSync as realpathSync5,
5379
+ realpathSync as realpathSync6,
5167
5380
  statSync as statSync7,
5168
5381
  writeFileSync as writeFileSync7
5169
5382
  } from "node:fs";
5170
- import { dirname as dirname10, join as join38, sep as sep6 } from "node:path";
5383
+ import { dirname as dirname10, join as join38, sep as sep7 } from "node:path";
5171
5384
 
5172
5385
  // src/skills-sync.ts
5173
5386
  init_config();
5174
- import { existsSync as existsSync29, lstatSync as lstatSync12, mkdirSync as mkdirSync10, readdirSync as readdirSync13, rmSync as rmSync13 } from "node:fs";
5387
+ import { existsSync as existsSync29, lstatSync as lstatSync13, mkdirSync as mkdirSync10, readdirSync as readdirSync13, rmSync as rmSync13 } from "node:fs";
5175
5388
  import { join as join37 } from "node:path";
5176
5389
 
5177
5390
  // src/skills-sync.tracked.ts
@@ -5211,7 +5424,7 @@ function syncSkillsPull(ts, prePostHeads) {
5211
5424
  if (!existsSync29(sharedSkills)) return;
5212
5425
  const localSkills = join37(claudeHome(), "skills");
5213
5426
  backupBeforeWrite(localSkills, ts);
5214
- const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
5427
+ const dstStat = lstatSync13(localSkills, { throwIfNoEntry: false });
5215
5428
  if (dstStat?.isSymbolicLink() === true) {
5216
5429
  rmSync13(localSkills, { recursive: true, force: true });
5217
5430
  mkdirSync10(localSkills, { recursive: true });
@@ -5221,7 +5434,7 @@ function syncSkillsPull(ts, prePostHeads) {
5221
5434
  }
5222
5435
  function syncSkillsPush() {
5223
5436
  const localSkills = join37(claudeHome(), "skills");
5224
- const stat = lstatSync12(localSkills, { throwIfNoEntry: false });
5437
+ const stat = lstatSync13(localSkills, { throwIfNoEntry: false });
5225
5438
  if (stat === void 0) return;
5226
5439
  if (stat.isSymbolicLink()) return;
5227
5440
  const sharedSkills = join37(repoHome(), "shared", "skills");
@@ -5250,12 +5463,12 @@ function resolveSkillLocalPath(name, relPath) {
5250
5463
  const skillsRoot = join38(claudeHome(), "skills");
5251
5464
  const skillRoot = join38(skillsRoot, name);
5252
5465
  const localPath = join38(skillRoot, ...relPath.split("/"));
5253
- if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep6)) return null;
5466
+ if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep7)) return null;
5254
5467
  try {
5255
- if (lstatSync13(localPath).isSymbolicLink()) return null;
5256
- const realLocal = realpathSync5(localPath);
5257
- const realRoot = realpathSync5(skillsRoot);
5258
- if (!realLocal.startsWith(realRoot + sep6)) return null;
5468
+ if (lstatSync14(localPath).isSymbolicLink()) return null;
5469
+ const realLocal = realpathSync6(localPath);
5470
+ const realRoot = realpathSync6(skillsRoot);
5471
+ if (!realLocal.startsWith(realRoot + sep7)) return null;
5259
5472
  if (!statSync7(realLocal).isFile()) return null;
5260
5473
  } catch {
5261
5474
  return null;
@@ -5380,7 +5593,7 @@ function applyDeferredAllows(findings, state, repo) {
5380
5593
  init_config();
5381
5594
  init_config_sharedDirs_guard();
5382
5595
  import { cpSync as cpSync9, existsSync as existsSync32, mkdirSync as mkdirSync12 } from "node:fs";
5383
- import { dirname as dirname12, join as join41, sep as sep7 } from "node:path";
5596
+ import { dirname as dirname12, join as join41, sep as sep8 } from "node:path";
5384
5597
 
5385
5598
  // src/commands.redact.ts
5386
5599
  init_config();
@@ -5388,17 +5601,17 @@ import { existsSync as existsSync31 } from "node:fs";
5388
5601
  import { dirname as dirname11, join as join40 } from "node:path";
5389
5602
 
5390
5603
  // src/commands.redact.subtree.ts
5391
- import { existsSync as existsSync30, lstatSync as lstatSync14, readFileSync as readFileSync16, readdirSync as readdirSync14, statSync as statSync8, writeFileSync as writeFileSync8 } from "node:fs";
5604
+ import { existsSync as existsSync30, lstatSync as lstatSync15, readFileSync as readFileSync16, readdirSync as readdirSync14, statSync as statSync8, writeFileSync as writeFileSync8 } from "node:fs";
5392
5605
  import { join as join39 } from "node:path";
5393
5606
  init_utils_fs();
5394
5607
  init_utils();
5395
5608
  function collectFiles(dir, out) {
5396
5609
  if (!existsSync30(dir)) return;
5397
- const st = lstatSync14(dir);
5610
+ const st = lstatSync15(dir);
5398
5611
  if (!st.isDirectory()) return;
5399
5612
  for (const entry of readdirSync14(dir)) {
5400
5613
  const abs = join39(dir, entry);
5401
- const lst = lstatSync14(abs);
5614
+ const lst = lstatSync15(abs);
5402
5615
  if (lst.isSymbolicLink()) continue;
5403
5616
  if (lst.isDirectory()) {
5404
5617
  collectFiles(abs, out);
@@ -5606,7 +5819,7 @@ function resolveStagedDir(localPath, map, claude, repo) {
5606
5819
  assertSafeLogical(logical);
5607
5820
  const abs = hostMap[HOST];
5608
5821
  if (abs === void 0) continue;
5609
- if (localPath.startsWith(join41(claude, "projects", encodePath(abs)) + sep7)) {
5822
+ if (localPath.startsWith(join41(claude, "projects", encodePath(abs)) + sep8)) {
5610
5823
  return join41(repo, "shared", "projects", logical);
5611
5824
  }
5612
5825
  }
@@ -5970,7 +6183,7 @@ function makeDefaultReadLine(repo) {
5970
6183
  try {
5971
6184
  const repoRoot = resolve3(repo);
5972
6185
  const target = resolve3(repoRoot, file);
5973
- if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep8)) {
6186
+ if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep9)) {
5974
6187
  return null;
5975
6188
  }
5976
6189
  let lines = cache.get(target);
@@ -6980,11 +7193,11 @@ init_utils_json();
6980
7193
 
6981
7194
  // src/extras-sync.remap.ts
6982
7195
  init_config();
6983
- import { existsSync as existsSync38, mkdirSync as mkdirSync13, readdirSync as readdirSync16, readFileSync as readFileSync19, realpathSync as realpathSync6, rmSync as rmSync16 } from "node:fs";
6984
- import { dirname as dirname13, join as join49, sep as sep10 } from "node:path";
7196
+ import { existsSync as existsSync38, mkdirSync as mkdirSync13, readdirSync as readdirSync16, readFileSync as readFileSync19, realpathSync as realpathSync7, rmSync as rmSync16 } from "node:fs";
7197
+ import { dirname as dirname13, join as join49, sep as sep11 } from "node:path";
6985
7198
 
6986
7199
  // src/extras-sync.planning-diff.ts
6987
- import { join as join48, normalize as normalize2, sep as sep9 } from "node:path";
7200
+ import { join as join48, normalize as normalize2, sep as sep10 } from "node:path";
6988
7201
  init_utils();
6989
7202
  function processRecord(fields, i, changed, deleted) {
6990
7203
  const status = fields[i];
@@ -7037,7 +7250,7 @@ function planningDeleteTargets(opts) {
7037
7250
  const logicalPrefix = "shared/extras/" + logical + "/";
7038
7251
  const prefix = logicalPrefix + ".planning/";
7039
7252
  const planningRoot = join48(localRoot, ".planning");
7040
- const planningRootBoundary = planningRoot + sep9;
7253
+ const planningRootBoundary = planningRoot + sep10;
7041
7254
  const targets = [];
7042
7255
  for (const repoPath of deleted) {
7043
7256
  if (!repoPath.startsWith(prefix)) {
@@ -7079,7 +7292,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
7079
7292
  }
7080
7293
  function pruneEmptyAncestors(target, planningRoot) {
7081
7294
  let dir = dirname13(target);
7082
- while (dir !== planningRoot && dir.startsWith(planningRoot + sep10)) {
7295
+ while (dir !== planningRoot && dir.startsWith(planningRoot + sep11)) {
7083
7296
  try {
7084
7297
  if (readdirSync16(dir).length > 0) break;
7085
7298
  rmSync16(dir, { recursive: true, force: true });
@@ -7091,13 +7304,13 @@ function pruneEmptyAncestors(target, planningRoot) {
7091
7304
  }
7092
7305
  function tryRealpath(dir) {
7093
7306
  try {
7094
- return realpathSync6(dir);
7307
+ return realpathSync7(dir);
7095
7308
  } catch {
7096
7309
  return void 0;
7097
7310
  }
7098
7311
  }
7099
7312
  function isInsidePlanningRoot(parentReal, rootReal) {
7100
- return parentReal === rootReal || parentReal.startsWith(rootReal + sep10);
7313
+ return parentReal === rootReal || parentReal.startsWith(rootReal + sep11);
7101
7314
  }
7102
7315
  function deletePlanningTarget(target, planningRoot, repoCounterpart) {
7103
7316
  if (existsSync38(repoCounterpart)) return;
@@ -7124,11 +7337,11 @@ function planningDiffArgs(pre, post, logical) {
7124
7337
  function deletePairsFor(t, raw) {
7125
7338
  return planningDeleteTargets({ raw, logical: t.logical, localRoot: t.localRoot }).map(
7126
7339
  (target) => {
7127
- const relToLocal = target.slice(t.localRoot.length + sep10.length);
7340
+ const relToLocal = target.slice(t.localRoot.length + sep11.length);
7128
7341
  return {
7129
7342
  target,
7130
7343
  relToLocal,
7131
- repoRel: `shared/extras/${t.logical}/${relToLocal.split(sep10).join("/")}`
7344
+ repoRel: `shared/extras/${t.logical}/${relToLocal.split(sep11).join("/")}`
7132
7345
  };
7133
7346
  }
7134
7347
  );
@@ -7302,8 +7515,8 @@ function divergenceCheckExtras(ts, prePostHeads) {
7302
7515
 
7303
7516
  // src/links.baseline.ts
7304
7517
  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";
7518
+ import { lstatSync as lstatSync16, readdirSync as readdirSync17 } from "node:fs";
7519
+ import { join as join51, relative as relative7, sep as sep12 } from "node:path";
7307
7520
  init_utils();
7308
7521
  var SHARED_BASELINE_KIND = "shared-links-baseline/1";
7309
7522
  var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
@@ -7314,13 +7527,13 @@ function readSharedBaseline() {
7314
7527
  return parsed;
7315
7528
  }
7316
7529
  function baselineKey(claude, abs) {
7317
- return relative7(claude, abs).split(sep11).join("/");
7530
+ return relative7(claude, abs).split(sep12).join("/");
7318
7531
  }
7319
7532
  function addLocalPath(abs, claude, scan) {
7320
7533
  const key = baselineKey(claude, abs);
7321
7534
  let st;
7322
7535
  try {
7323
- st = lstatSync15(abs, { throwIfNoEntry: false });
7536
+ st = lstatSync16(abs, { throwIfNoEntry: false });
7324
7537
  } catch {
7325
7538
  scan.declined.push(key);
7326
7539
  return;
@@ -7412,7 +7625,7 @@ function gitProbe(args, repo) {
7412
7625
 
7413
7626
  // src/links.captures.ts
7414
7627
  init_config();
7415
- import { existsSync as existsSync40, lstatSync as lstatSync16 } from "node:fs";
7628
+ import { existsSync as existsSync40, lstatSync as lstatSync17 } from "node:fs";
7416
7629
  import { join as join52 } from "node:path";
7417
7630
  function planSharedLinkCaptures(map) {
7418
7631
  if (process.platform !== "win32") return [];
@@ -7424,7 +7637,7 @@ function planSharedLinkCaptures(map) {
7424
7637
  const localPath = join52(claude, name);
7425
7638
  let stat;
7426
7639
  try {
7427
- stat = lstatSync16(localPath, { throwIfNoEntry: false });
7640
+ stat = lstatSync17(localPath, { throwIfNoEntry: false });
7428
7641
  } catch {
7429
7642
  continue;
7430
7643
  }
@@ -7439,8 +7652,8 @@ function planSharedLinkCaptures(map) {
7439
7652
 
7440
7653
  // src/links.deletions.ts
7441
7654
  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";
7655
+ import { existsSync as existsSync41, lstatSync as lstatSync18, rmSync as rmSync17 } from "node:fs";
7656
+ import { join as join53, relative as relative8, resolve as resolve4, sep as sep13 } from "node:path";
7444
7657
  init_utils();
7445
7658
  init_utils_fs();
7446
7659
  function isUnknown(declined, key) {
@@ -7454,7 +7667,7 @@ function deletionFor(key, names, claude, sharedRoot) {
7454
7667
  if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
7455
7668
  const repoPath = resolve4(sharedRoot, key);
7456
7669
  const rel = relative8(join53(sharedRoot, name), repoPath);
7457
- if (rel === "" || rel === ".." || rel.startsWith(`..${sep12}`)) return null;
7670
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep13}`)) return null;
7458
7671
  if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
7459
7672
  return null;
7460
7673
  if (!existsSync41(repoPath)) return null;
@@ -7485,7 +7698,7 @@ function applySharedLinkDeletions(map, ts) {
7485
7698
  const repo = repoHome();
7486
7699
  for (const entry of planSharedLinkDeletions(map)) {
7487
7700
  try {
7488
- if (!lstatSync17(entry.repoPath).isFile()) continue;
7701
+ if (!lstatSync18(entry.repoPath).isFile()) continue;
7489
7702
  backupRepoWrite(entry.repoPath, ts, repo);
7490
7703
  rmSync17(entry.repoPath, { force: true });
7491
7704
  } catch (err) {
@@ -7955,7 +8168,7 @@ function planSharedReconcileBeforePull(repo) {
7955
8168
  // src/commands.pull.collision.ts
7956
8169
  init_config();
7957
8170
  init_exit_codes();
7958
- import { existsSync as existsSync45, lstatSync as lstatSync18, readFileSync as readFileSync20, rmSync as rmSync18 } from "node:fs";
8171
+ import { existsSync as existsSync45, lstatSync as lstatSync19, readFileSync as readFileSync20, rmSync as rmSync18 } from "node:fs";
7959
8172
  import { join as join57 } from "node:path";
7960
8173
  init_utils();
7961
8174
  var SHARED_PREFIX = /^shared\//;
@@ -8022,7 +8235,7 @@ function removeMirroredCopies(repo, repoRelPaths) {
8022
8235
  for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
8023
8236
  const abs = join57(repo, rel);
8024
8237
  try {
8025
- if (lstatSync18(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
8238
+ if (lstatSync19(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
8026
8239
  if (!matchesLocalOriginal(repo, rel)) continue;
8027
8240
  rmSync18(abs, { force: true });
8028
8241
  } catch (err) {
@@ -8401,7 +8614,7 @@ function isAllowed(path, allowed) {
8401
8614
  }
8402
8615
  function blockSetFor(segments) {
8403
8616
  if (segments[0] !== "shared" || segments[1] !== "extras") return NEVER_SYNC;
8404
- return segments[3] === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
8617
+ return isClaudeExtraName(segments[3] ?? "") ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
8405
8618
  }
8406
8619
  function isNeverSync(path) {
8407
8620
  const segments = path.split("/");
@@ -8451,7 +8664,11 @@ function enforceAllowList(statusPorcelain, map) {
8451
8664
  ...Object.entries(map.extras ?? {}).flatMap(
8452
8665
  ([l, names]) => names.filter((n) => extrasWhitelist.includes(n)).flatMap((n) => [`shared/extras/${l}/${n}`, `shared/extras/${l}/${n}/`])
8453
8666
  ),
8454
- ...(map.sharedDirs ?? []).filter((d) => isValidSharedDir(d)).map((d) => `shared/${d}/`)
8667
+ // Predicate passed by reference, not wrapped in an arrow: `isValidSharedDir`
8668
+ // is a type guard, and an arrow returning its result is just a boolean, so
8669
+ // wrapping it would leave `d` as `unknown` and let a non-string reach the
8670
+ // template literal as "[object Object]".
8671
+ ...sharedDirEntries(map).filter(isValidSharedDir).map((d) => `shared/${d}/`)
8455
8672
  ];
8456
8673
  const neverSyncHits = [];
8457
8674
  const violations = [];
@@ -8632,9 +8849,9 @@ init_color();
8632
8849
  init_config();
8633
8850
  init_config_sharedDirs_guard();
8634
8851
  import { randomBytes as randomBytes4 } from "node:crypto";
8635
- import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync19, mkdirSync as mkdirSync15, readdirSync as readdirSync19, rmSync as rmSync19 } from "node:fs";
8852
+ import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync20, mkdirSync as mkdirSync15, readdirSync as readdirSync19, rmSync as rmSync19 } from "node:fs";
8636
8853
  import { homedir as homedir7 } from "node:os";
8637
- import { join as join62, relative as relative10, sep as sep13 } from "node:path";
8854
+ import { join as join62, relative as relative10, sep as sep14 } from "node:path";
8638
8855
  init_push_leak_verdict();
8639
8856
  init_push_gitleaks();
8640
8857
  init_utils_fs();
@@ -8642,7 +8859,7 @@ init_utils_json();
8642
8859
  var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
8643
8860
  function stageSessionDir(localDir, dstDir, changed) {
8644
8861
  if (changed !== void 0) {
8645
- const prefix = `${localDir}${sep13}`;
8862
+ const prefix = `${localDir}${sep14}`;
8646
8863
  const matching = [...changed].filter((p) => p.startsWith(prefix));
8647
8864
  if (matching.length === 0) return false;
8648
8865
  for (const src of matching) {
@@ -8696,7 +8913,7 @@ function stageExtras(tmpRoot, map) {
8696
8913
  }
8697
8914
  function stageSkills(tmpRoot) {
8698
8915
  const localSkills = join62(claudeHome(), "skills");
8699
- const stat = lstatSync19(localSkills, { throwIfNoEntry: false });
8916
+ const stat = lstatSync20(localSkills, { throwIfNoEntry: false });
8700
8917
  if (stat === void 0 || stat.isSymbolicLink()) return 0;
8701
8918
  const names = readdirSync19(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
8702
8919
  if (names.length === 0) return 0;
@@ -9881,7 +10098,7 @@ function parseSyncArgs(argv) {
9881
10098
  // package.json
9882
10099
  var package_default = {
9883
10100
  name: "claude-nomad",
9884
- version: "0.64.0",
10101
+ version: "0.64.1",
9885
10102
  type: "module",
9886
10103
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
9887
10104
  keywords: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nomad",
3
- "version": "0.64.0",
3
+ "version": "0.64.1",
4
4
  "type": "module",
5
5
  "description": "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
6
6
  "keywords": [