claude-nomad 0.60.0 → 0.61.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
@@ -1686,12 +1686,12 @@ function* eachExtrasTarget(v, counts) {
1686
1686
  counts.unmapped++;
1687
1687
  continue;
1688
1688
  }
1689
- for (const dirname10 of dirnames) {
1690
- if (!whitelist.includes(dirname10)) {
1689
+ for (const dirname11 of dirnames) {
1690
+ if (!whitelist.includes(dirname11)) {
1691
1691
  counts.skipped++;
1692
1692
  continue;
1693
1693
  }
1694
- yield { logical, localRoot, dirname: dirname10 };
1694
+ yield { logical, localRoot, dirname: dirname11 };
1695
1695
  }
1696
1696
  }
1697
1697
  }
@@ -1729,8 +1729,8 @@ function copyExtrasFileSkipDiverged(src, dst) {
1729
1729
  }
1730
1730
  copyExtras(src, dst);
1731
1731
  }
1732
- function extrasDenySet(dirname10) {
1733
- return dirname10 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1732
+ function extrasDenySet(dirname11) {
1733
+ return dirname11 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1734
1734
  }
1735
1735
  function copyExtrasFiltered(src, dst, blockSet) {
1736
1736
  rmSync2(dst, { recursive: true, force: true });
@@ -2558,8 +2558,8 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
2558
2558
  }
2559
2559
 
2560
2560
  // src/commands.doctor.ts
2561
- import { existsSync as existsSync34 } from "node:fs";
2562
- import { join as join40 } from "node:path";
2561
+ import { existsSync as existsSync35 } from "node:fs";
2562
+ import { join as join42 } from "node:path";
2563
2563
 
2564
2564
  // src/commands.doctor.checks.repo.ts
2565
2565
  init_color();
@@ -3406,20 +3406,117 @@ function reportCheckSchema(section2) {
3406
3406
 
3407
3407
  // src/commands.doctor.check-shared.ts
3408
3408
  init_color();
3409
- import { randomBytes } from "node:crypto";
3409
+ import { randomBytes as randomBytes2 } from "node:crypto";
3410
+ import { execFileSync as execFileSync10 } from "node:child_process";
3411
+ import { existsSync as existsSync22, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11 } from "node:fs";
3412
+ import { homedir as homedir5 } from "node:os";
3413
+ import { join as join28 } from "node:path";
3414
+
3415
+ // src/commands.doctor.check-shared.memory.ts
3416
+ init_color();
3410
3417
  import { execFileSync as execFileSync9 } from "node:child_process";
3411
- import { existsSync as existsSync22, mkdirSync as mkdirSync6, readdirSync as readdirSync11, rmSync as rmSync10 } from "node:fs";
3418
+ import { randomBytes } from "node:crypto";
3419
+ import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
3412
3420
  import { homedir as homedir4 } from "node:os";
3413
- import { join as join27 } from "node:path";
3421
+ import { dirname as dirname3, join as join24 } from "node:path";
3422
+ init_config();
3423
+ init_push_gitleaks();
3424
+ init_utils_fs();
3425
+ var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
3426
+ function buildMemoryScanTree(tmpRoot) {
3427
+ let paths;
3428
+ try {
3429
+ const out = execFileSync9(
3430
+ "git",
3431
+ ["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
3432
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
3433
+ );
3434
+ paths = out.split("\0").filter((p) => p.length > 0);
3435
+ } catch {
3436
+ return 0;
3437
+ }
3438
+ const logicals = /* @__PURE__ */ new Set();
3439
+ for (const rel of paths) {
3440
+ const m = MEMORY_MD_PATH.exec(rel);
3441
+ if (m?.[1] === void 0) continue;
3442
+ let blob;
3443
+ try {
3444
+ blob = execFileSync9("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
3445
+ stdio: ["ignore", "pipe", "ignore"],
3446
+ maxBuffer: 67108864,
3447
+ timeout: 1e4
3448
+ });
3449
+ } catch {
3450
+ continue;
3451
+ }
3452
+ const dest = join24(tmpRoot, rel);
3453
+ mkdirSync4(dirname3(dest), { recursive: true });
3454
+ writeFileSync4(dest, blob);
3455
+ logicals.add(m[1]);
3456
+ }
3457
+ return logicals.size;
3458
+ }
3459
+ function reportMemoryFindings(section2, findings) {
3460
+ for (const f of findings) {
3461
+ addItem(section2, `${yellow(warnGlyph)} ${f.RuleID} in ${f.File}`);
3462
+ }
3463
+ addItem(
3464
+ section2,
3465
+ ` ${dim("run `nomad push` and choose Redact in the recovery menu to scrub these")}`
3466
+ );
3467
+ }
3468
+ function reportCommittedMemory(section2) {
3469
+ let tmpRoot;
3470
+ try {
3471
+ const cacheDir = join24(homedir4(), ".cache", "claude-nomad");
3472
+ mkdirSync4(cacheDir, { recursive: true });
3473
+ const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
3474
+ tmpRoot = join24(cacheDir, `check-shared-memory-tree-${stamp}`);
3475
+ mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
3476
+ const staged = buildMemoryScanTree(tmpRoot);
3477
+ if (staged === 0) return;
3478
+ let findings;
3479
+ try {
3480
+ findings = scanStagedTree(tmpRoot);
3481
+ } catch (err) {
3482
+ addItem(
3483
+ section2,
3484
+ `${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
3485
+ );
3486
+ return;
3487
+ }
3488
+ if (findings === null) {
3489
+ addItem(
3490
+ section2,
3491
+ `${yellow(warnGlyph)} committed-memory scan skipped: no parseable gitleaks report`
3492
+ );
3493
+ return;
3494
+ }
3495
+ if (findings.length === 0) return;
3496
+ reportMemoryFindings(section2, findings);
3497
+ } catch (err) {
3498
+ addItem(
3499
+ section2,
3500
+ `${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
3501
+ );
3502
+ } finally {
3503
+ if (tmpRoot !== void 0) {
3504
+ try {
3505
+ rmSync9(tmpRoot, { recursive: true, force: true });
3506
+ } catch {
3507
+ }
3508
+ }
3509
+ }
3510
+ }
3414
3511
 
3415
3512
  // src/commands.doctor.check-shared.scan.ts
3416
3513
  init_color();
3417
- import { join as join24 } from "node:path";
3514
+ import { join as join25 } from "node:path";
3418
3515
  init_config();
3419
3516
  init_push_gitleaks();
3420
3517
  function scrubPath(logical, sid, logicalToEncoded) {
3421
3518
  const encoded = logicalToEncoded.get(logical) ?? logical;
3422
- return join24(claudeHome(), "projects", encoded, `${sid}.jsonl`);
3519
+ return join25(claudeHome(), "projects", encoded, `${sid}.jsonl`);
3423
3520
  }
3424
3521
  function reportSessionFindings(section2, bySession) {
3425
3522
  for (const [sid, counts] of bySession) {
@@ -3507,8 +3604,8 @@ init_config();
3507
3604
 
3508
3605
  // src/remap.ts
3509
3606
  init_config_sharedDirs_guard();
3510
- import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync10, rmSync as rmSync9, statSync as statSync5 } from "node:fs";
3511
- import { dirname as dirname4, join as join26, relative as relative5, sep as sep4 } from "node:path";
3607
+ import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync10, statSync as statSync5 } from "node:fs";
3608
+ import { dirname as dirname5, join as join27, relative as relative5, sep as sep4 } from "node:path";
3512
3609
  init_config();
3513
3610
 
3514
3611
  // src/push-manifest.ts
@@ -3516,8 +3613,8 @@ init_config();
3516
3613
  init_push_gitleaks_config();
3517
3614
  init_utils_fs();
3518
3615
  import { createHash } from "node:crypto";
3519
- import { existsSync as existsSync20, mkdirSync as mkdirSync4, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
3520
- import { dirname as dirname3, join as join25 } from "node:path";
3616
+ import { existsSync as existsSync20, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
3617
+ import { dirname as dirname4, join as join26 } from "node:path";
3521
3618
  function isChanged(prev, cur, hash) {
3522
3619
  if (prev === void 0) return true;
3523
3620
  if (prev.size !== cur.size) return true;
@@ -3558,7 +3655,7 @@ function hashFile(absPath) {
3558
3655
  }
3559
3656
  function enumerateDir(dir, results) {
3560
3657
  for (const entry of readdirSync9(dir)) {
3561
- const fullPath = join25(dir, entry);
3658
+ const fullPath = join26(dir, entry);
3562
3659
  const st = statSync4(fullPath);
3563
3660
  if (st.isDirectory()) {
3564
3661
  enumerateDir(fullPath, results);
@@ -3570,7 +3667,7 @@ function enumerateDir(dir, results) {
3570
3667
  function enumerateSourceFiles(projectDir) {
3571
3668
  const results = [];
3572
3669
  for (const entry of readdirSync9(projectDir)) {
3573
- const fullPath = join25(projectDir, entry);
3670
+ const fullPath = join26(projectDir, entry);
3574
3671
  const st = statSync4(fullPath);
3575
3672
  if (st.isDirectory()) {
3576
3673
  enumerateDir(fullPath, results);
@@ -3589,8 +3686,8 @@ function computeConfigHash() {
3589
3686
  const repo = repoHome();
3590
3687
  const parts = [
3591
3688
  fileIdentity(resolveTomlPath(repo)),
3592
- fileIdentity(join25(repo, ".gitleaks.overlay.toml")),
3593
- fileIdentity(join25(repo, ".gitleaksignore"))
3689
+ fileIdentity(join26(repo, ".gitleaks.overlay.toml")),
3690
+ fileIdentity(join26(repo, ".gitleaksignore"))
3594
3691
  ];
3595
3692
  return createHash("sha256").update(parts.join("\n")).digest("hex");
3596
3693
  }
@@ -3610,7 +3707,7 @@ function readManifest(path) {
3610
3707
  }
3611
3708
  }
3612
3709
  function writeManifest(path, manifest) {
3613
- mkdirSync4(dirname3(path), { recursive: true });
3710
+ mkdirSync5(dirname4(path), { recursive: true });
3614
3711
  writeJsonAtomic(path, manifest);
3615
3712
  }
3616
3713
  function buildManifest(files, scannerVersion, configHash) {
@@ -3624,9 +3721,9 @@ init_utils_json();
3624
3721
  var TMP_SUFFIX = ".nomad-tmp";
3625
3722
  function atomicMirror(src, dst, options) {
3626
3723
  const tmp = `${dst}${TMP_SUFFIX}`;
3627
- rmSync9(tmp, { recursive: true, force: true });
3724
+ rmSync10(tmp, { recursive: true, force: true });
3628
3725
  cpSync6(src, tmp, options);
3629
- rmSync9(dst, { recursive: true, force: true });
3726
+ rmSync10(dst, { recursive: true, force: true });
3630
3727
  renameAtomicRetry(tmp, dst);
3631
3728
  }
3632
3729
  function overlaySessionDir(src, dst) {
@@ -3634,7 +3731,7 @@ function overlaySessionDir(src, dst) {
3634
3731
  cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
3635
3732
  }
3636
3733
  function copyFileAtomic(src, dst) {
3637
- mkdirSync5(dirname4(dst), { recursive: true });
3734
+ mkdirSync6(dirname5(dst), { recursive: true });
3638
3735
  const tmp = `${dst}.tmp.${process.pid}`;
3639
3736
  cpSync6(src, tmp, { force: true, preserveTimestamps: true });
3640
3737
  renameAtomicRetry(tmp, dst);
@@ -3652,12 +3749,12 @@ function applySelective(sel, localDir, repoDst) {
3652
3749
  const prefix = `${localDir}${sep4}`;
3653
3750
  for (const src of sel.changed) {
3654
3751
  if (!src.startsWith(prefix)) continue;
3655
- copyFileAtomic(src, join26(repoDst, relative5(localDir, src)));
3752
+ copyFileAtomic(src, join27(repoDst, relative5(localDir, src)));
3656
3753
  }
3657
3754
  for (const src of sel.deleted) {
3658
3755
  if (!src.startsWith(prefix)) continue;
3659
- const dst = join26(repoDst, relative5(localDir, src));
3660
- if (existsSync21(dst)) rmSync9(dst);
3756
+ const dst = join27(repoDst, relative5(localDir, src));
3757
+ if (existsSync21(dst)) rmSync10(dst);
3661
3758
  }
3662
3759
  }
3663
3760
  function copyDirJsonlOnly(src, dst) {
@@ -3686,16 +3783,16 @@ function remapPull(ts, opts = {}) {
3686
3783
  const wouldPull = [];
3687
3784
  const repo = repoHome();
3688
3785
  const claude = claudeHome();
3689
- const mapPath = join26(repo, "path-map.json");
3690
- const repoProjects = join26(repo, "shared", "projects");
3786
+ const mapPath = join27(repo, "path-map.json");
3787
+ const repoProjects = join27(repo, "shared", "projects");
3691
3788
  if (!existsSync21(mapPath) || !existsSync21(repoProjects)) {
3692
3789
  const text = "no path-map or repo projects dir; skipping session remap";
3693
3790
  emitPreview(opts.onPreview, { kind: "note", text }, text);
3694
3791
  return { unmapped: 0, pulled, wouldPull };
3695
3792
  }
3696
3793
  const map = readPathMap(mapPath);
3697
- const localProjects = join26(claude, "projects");
3698
- if (!dryRun) mkdirSync5(localProjects, { recursive: true });
3794
+ const localProjects = join27(claude, "projects");
3795
+ if (!dryRun) mkdirSync6(localProjects, { recursive: true });
3699
3796
  for (const [logical, hosts] of Object.entries(map.projects)) {
3700
3797
  assertSafeLogical(logical);
3701
3798
  const localPath = hosts[HOST];
@@ -3704,9 +3801,9 @@ function remapPull(ts, opts = {}) {
3704
3801
  continue;
3705
3802
  }
3706
3803
  assertSafeLocalRoot(localPath, logical);
3707
- const src = join26(repoProjects, logical);
3804
+ const src = join27(repoProjects, logical);
3708
3805
  if (!existsSync21(src)) continue;
3709
- const dst = join26(localProjects, encodePath(localPath));
3806
+ const dst = join27(localProjects, encodePath(localPath));
3710
3807
  if (dryRun) {
3711
3808
  wouldPull.push(logical);
3712
3809
  emitPreview(
@@ -3725,8 +3822,8 @@ function remapPull(ts, opts = {}) {
3725
3822
  function countLocalOnly(src, dst) {
3726
3823
  let count = 0;
3727
3824
  for (const name of readdirSync10(dst)) {
3728
- const dstPath = join26(dst, name);
3729
- const srcPath = join26(src, name);
3825
+ const dstPath = join27(dst, name);
3826
+ const srcPath = join27(src, name);
3730
3827
  if (lstatSync10(dstPath).isDirectory()) {
3731
3828
  count += countLocalOnly(srcPath, dstPath);
3732
3829
  } else if (lstatSync10(srcPath, { throwIfNoEntry: false }) === void 0) {
@@ -3738,20 +3835,20 @@ function countLocalOnly(src, dst) {
3738
3835
  function scanLocalOnly() {
3739
3836
  const repo = repoHome();
3740
3837
  const claude = claudeHome();
3741
- const mapPath = join26(repo, "path-map.json");
3742
- const repoProjects = join26(repo, "shared", "projects");
3838
+ const mapPath = join27(repo, "path-map.json");
3839
+ const repoProjects = join27(repo, "shared", "projects");
3743
3840
  if (!existsSync21(mapPath) || !existsSync21(repoProjects)) return 0;
3744
3841
  const map = readPathMap(mapPath);
3745
- const localProjects = join26(claude, "projects");
3842
+ const localProjects = join27(claude, "projects");
3746
3843
  let count = 0;
3747
3844
  for (const [logical, hosts] of Object.entries(map.projects)) {
3748
3845
  assertSafeLogical(logical);
3749
3846
  const localPath = hosts[HOST];
3750
3847
  if (!localPath || localPath === "TBD") continue;
3751
3848
  assertSafeLocalRoot(localPath, logical);
3752
- const dst = join26(localProjects, encodePath(localPath));
3849
+ const dst = join27(localProjects, encodePath(localPath));
3753
3850
  if (!existsSync21(dst)) continue;
3754
- count += countLocalOnly(join26(repoProjects, logical), dst);
3851
+ count += countLocalOnly(join27(repoProjects, logical), dst);
3755
3852
  }
3756
3853
  return count;
3757
3854
  }
@@ -3786,17 +3883,17 @@ function remapPush(ts, opts = {}) {
3786
3883
  const wouldPush = [];
3787
3884
  const repo = repoHome();
3788
3885
  const claude = claudeHome();
3789
- const mapPath = join26(repo, "path-map.json");
3886
+ const mapPath = join27(repo, "path-map.json");
3790
3887
  if (!existsSync21(mapPath)) {
3791
3888
  log("no path-map.json; skipping session export");
3792
3889
  return { unmapped: 0, collisions: 0, pushed, wouldPush };
3793
3890
  }
3794
3891
  const map = readPathMap(mapPath);
3795
- const localProjects = join26(claude, "projects");
3796
- const repoProjects = join26(repo, "shared", "projects");
3892
+ const localProjects = join27(claude, "projects");
3893
+ const repoProjects = join27(repo, "shared", "projects");
3797
3894
  const reverse = buildReverseMap(map);
3798
3895
  if (!existsSync21(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
3799
- if (!dryRun) mkdirSync5(repoProjects, { recursive: true });
3896
+ if (!dryRun) mkdirSync6(repoProjects, { recursive: true });
3800
3897
  for (const dir of readdirSync10(localProjects)) {
3801
3898
  if (dir.endsWith(TMP_SUFFIX)) continue;
3802
3899
  const logical = reverse.get(dir);
@@ -3804,8 +3901,8 @@ function remapPush(ts, opts = {}) {
3804
3901
  unmapped++;
3805
3902
  continue;
3806
3903
  }
3807
- const localDir = join26(localProjects, dir);
3808
- const repoDst = join26(repoProjects, logical);
3904
+ const localDir = join27(localProjects, dir);
3905
+ const repoDst = join27(repoProjects, logical);
3809
3906
  if (skipForSelection(opts.selection, localDir)) continue;
3810
3907
  if (dryRun) {
3811
3908
  wouldPush.push(logical);
@@ -3829,7 +3926,7 @@ function buildScanTree(tmpRoot) {
3829
3926
  const logicalToEncoded = /* @__PURE__ */ new Map();
3830
3927
  let staged = 0;
3831
3928
  const repo = repoHome();
3832
- const mapPath = join27(repo, "path-map.json");
3929
+ const mapPath = join28(repo, "path-map.json");
3833
3930
  if (!existsSync22(mapPath)) return { logicalToEncoded, staged, malformed: false };
3834
3931
  let map;
3835
3932
  try {
@@ -3847,12 +3944,12 @@ function buildScanTree(tmpRoot) {
3847
3944
  if (!p || p === "TBD") continue;
3848
3945
  reverse.set(encodePath(p), logical);
3849
3946
  }
3850
- const localProjects = join27(claudeHome(), "projects");
3947
+ const localProjects = join28(claudeHome(), "projects");
3851
3948
  if (!existsSync22(localProjects)) return { logicalToEncoded, staged, malformed: false };
3852
3949
  for (const dir of readdirSync11(localProjects)) {
3853
3950
  const logical = reverse.get(dir);
3854
3951
  if (!logical) continue;
3855
- copyDirJsonlOnly(join27(localProjects, dir), join27(tmpRoot, "shared", "projects", logical));
3952
+ copyDirJsonlOnly(join28(localProjects, dir), join28(tmpRoot, "shared", "projects", logical));
3856
3953
  logicalToEncoded.set(logical, dir);
3857
3954
  staged++;
3858
3955
  }
@@ -3860,7 +3957,7 @@ function buildScanTree(tmpRoot) {
3860
3957
  }
3861
3958
  function probeGitleaksForScan() {
3862
3959
  try {
3863
- execFileSync9("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
3960
+ execFileSync10("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
3864
3961
  return "ok";
3865
3962
  } catch (err) {
3866
3963
  if (err.code === "ENOENT") return "missing";
@@ -3881,13 +3978,12 @@ function ensureGitleaksReady(section2, gitleaksReady) {
3881
3978
  }
3882
3979
  return true;
3883
3980
  }
3884
- function reportCheckShared(section2, gitleaksReady) {
3885
- if (!ensureGitleaksReady(section2, gitleaksReady)) return;
3886
- const cacheDir = join27(homedir4(), ".cache", "claude-nomad");
3887
- mkdirSync6(cacheDir, { recursive: true });
3888
- const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
3889
- const reportPath = join27(cacheDir, `check-shared-${stamp}.json`);
3890
- const tmpRoot = join27(cacheDir, `check-shared-tree-${stamp}`);
3981
+ function runLocalPreviewScan(section2) {
3982
+ const cacheDir = join28(homedir5(), ".cache", "claude-nomad");
3983
+ mkdirSync7(cacheDir, { recursive: true });
3984
+ const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
3985
+ const reportPath = join28(cacheDir, `check-shared-${stamp}.json`);
3986
+ const tmpRoot = join28(cacheDir, `check-shared-tree-${stamp}`);
3891
3987
  try {
3892
3988
  const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
3893
3989
  if (malformed) {
@@ -3901,15 +3997,26 @@ function reportCheckShared(section2, gitleaksReady) {
3901
3997
  }
3902
3998
  scanAndReport(section2, tmpRoot, staged, logicalToEncoded);
3903
3999
  } finally {
3904
- rmSync10(reportPath, { force: true });
3905
- rmSync10(tmpRoot, { recursive: true, force: true });
4000
+ rmSync11(reportPath, { force: true });
4001
+ rmSync11(tmpRoot, { recursive: true, force: true });
4002
+ }
4003
+ }
4004
+ function reportCheckShared(section2, gitleaksReady) {
4005
+ if (!ensureGitleaksReady(section2, gitleaksReady)) return;
4006
+ try {
4007
+ runLocalPreviewScan(section2);
4008
+ } catch (err) {
4009
+ addItem(section2, `${red(failGlyph)} shared scan failed: ${err.message}`);
4010
+ process.exitCode = 1;
4011
+ } finally {
4012
+ reportCommittedMemory(section2);
3906
4013
  }
3907
4014
  }
3908
4015
 
3909
4016
  // src/commands.doctor.checks.hooks.scope.ts
3910
4017
  init_color();
3911
4018
  import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync12, realpathSync as realpathSync2 } from "node:fs";
3912
- import { dirname as dirname5, extname, join as join28 } from "node:path";
4019
+ import { dirname as dirname6, extname, join as join29 } from "node:path";
3913
4020
  init_config();
3914
4021
  function typeFromPackageJson(pkgPath) {
3915
4022
  try {
@@ -3929,11 +4036,11 @@ function effectiveType(hookPath) {
3929
4036
  } catch {
3930
4037
  return null;
3931
4038
  }
3932
- let dir = dirname5(real);
4039
+ let dir = dirname6(real);
3933
4040
  for (; ; ) {
3934
- const pkg = join28(dir, "package.json");
4041
+ const pkg = join29(dir, "package.json");
3935
4042
  if (existsSync23(pkg)) return typeFromPackageJson(pkg);
3936
- const parent = dirname5(dir);
4043
+ const parent = dirname6(dir);
3937
4044
  if (parent === dir) return "cjs";
3938
4045
  dir = parent;
3939
4046
  }
@@ -3974,7 +4081,7 @@ function safeRead(path) {
3974
4081
  }
3975
4082
  }
3976
4083
  function reportHookScopeCheck(section2) {
3977
- const hooksDir = join28(claudeHome(), "hooks");
4084
+ const hooksDir = join29(claudeHome(), "hooks");
3978
4085
  if (!existsSync23(hooksDir)) {
3979
4086
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
3980
4087
  return;
@@ -3982,7 +4089,7 @@ function reportHookScopeCheck(section2) {
3982
4089
  let anyWarn = false;
3983
4090
  for (const name of safeReaddir2(hooksDir)) {
3984
4091
  if (extname(name) !== ".js") continue;
3985
- const abs = join28(hooksDir, name);
4092
+ const abs = join29(hooksDir, name);
3986
4093
  const eff = effectiveType(abs);
3987
4094
  if (eff === null) continue;
3988
4095
  const src = safeRead(abs);
@@ -4003,7 +4110,7 @@ function reportHookScopeCheck(section2) {
4003
4110
  // src/commands.doctor.checks.hooks.ts
4004
4111
  init_color();
4005
4112
  import { existsSync as existsSync24 } from "node:fs";
4006
- import { join as join29 } from "node:path";
4113
+ import { join as join30 } from "node:path";
4007
4114
  init_config();
4008
4115
  function expandHome(token) {
4009
4116
  const h2 = home();
@@ -4059,7 +4166,7 @@ function checkEventGroups(section2, event, groups) {
4059
4166
  return anyFail;
4060
4167
  }
4061
4168
  function reportHooksTargetCheck(section2) {
4062
- const settingsPath = join29(claudeHome(), "settings.json");
4169
+ const settingsPath = join30(claudeHome(), "settings.json");
4063
4170
  if (!existsSync24(settingsPath)) {
4064
4171
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
4065
4172
  return;
@@ -4084,11 +4191,11 @@ function reportHooksTargetCheck(section2) {
4084
4191
  // src/commands.doctor.checks.hooks.preserve-symlinks.ts
4085
4192
  init_color();
4086
4193
  import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
4087
- import { join as join31 } from "node:path";
4194
+ import { join as join32 } from "node:path";
4088
4195
 
4089
4196
  // src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
4090
4197
  import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
4091
- import { dirname as dirname6, join as join30, resolve as resolve2 } from "node:path";
4198
+ import { dirname as dirname7, join as join31, resolve as resolve2 } from "node:path";
4092
4199
  function suppressedRanges(src) {
4093
4200
  const ranges = [];
4094
4201
  const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
@@ -4125,7 +4232,7 @@ function specifierIsMissing(specifier, baseDir) {
4125
4232
  if (existsSync25(base + ext)) return false;
4126
4233
  }
4127
4234
  for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
4128
- if (existsSync25(join30(base, idx))) return false;
4235
+ if (existsSync25(join31(base, idx))) return false;
4129
4236
  }
4130
4237
  return true;
4131
4238
  }
@@ -4151,7 +4258,7 @@ function relativeRequireTargetsBroken(scriptPath) {
4151
4258
  }
4152
4259
  const specifiers = topRelativeSpecifiers(raw);
4153
4260
  if (specifiers.length === 0) return false;
4154
- const baseDir = dirname6(realPath);
4261
+ const baseDir = dirname7(realPath);
4155
4262
  for (const spec of specifiers) {
4156
4263
  if (specifierIsMissing(spec, baseDir)) return true;
4157
4264
  }
@@ -4180,7 +4287,7 @@ function commandTokens(command) {
4180
4287
  return tokens;
4181
4288
  }
4182
4289
  function readPathMapSafe() {
4183
- const mapPath = join31(repoHome(), "path-map.json");
4290
+ const mapPath = join32(repoHome(), "path-map.json");
4184
4291
  if (!existsSync26(mapPath)) return { projects: {} };
4185
4292
  try {
4186
4293
  return JSON.parse(readFileSync9(mapPath, "utf8"));
@@ -4255,7 +4362,7 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
4255
4362
  return anyWarn;
4256
4363
  }
4257
4364
  function reportPreserveSymlinksCheck(section2) {
4258
- const settingsPath = join31(claudeHome(), "settings.json");
4365
+ const settingsPath = join32(claudeHome(), "settings.json");
4259
4366
  if (!existsSync26(settingsPath)) {
4260
4367
  addItem(
4261
4368
  section2,
@@ -4290,7 +4397,7 @@ function reportPreserveSymlinksCheck(section2) {
4290
4397
  // src/commands.doctor.checks.settings-drift.ts
4291
4398
  init_color();
4292
4399
  import { existsSync as existsSync27, readFileSync as readFileSync10 } from "node:fs";
4293
- import { join as join32 } from "node:path";
4400
+ import { join as join33 } from "node:path";
4294
4401
  init_config();
4295
4402
  init_utils_json();
4296
4403
  function diffMergedSettings(merged, settings) {
@@ -4308,7 +4415,7 @@ function tryReadJson(filePath) {
4308
4415
  }
4309
4416
  }
4310
4417
  function reportHooksBaseSelfCleanNote(section2) {
4311
- const basePath = join32(repoHome(), "shared", "settings.base.json");
4418
+ const basePath = join33(repoHome(), "shared", "settings.base.json");
4312
4419
  const base = tryReadJson(basePath);
4313
4420
  if (base === null) return;
4314
4421
  if (!baseHasGsdHookEntries(base)) return;
@@ -4321,9 +4428,9 @@ function reportSettingsDriftCheck(section2) {
4321
4428
  const claude = claudeHome();
4322
4429
  const repo = repoHome();
4323
4430
  const host = HOST;
4324
- const settingsPath = join32(claude, "settings.json");
4325
- const basePath = join32(repo, "shared", "settings.base.json");
4326
- const hostPath = join32(repo, "hosts", `${host}.json`);
4431
+ const settingsPath = join33(claude, "settings.json");
4432
+ const basePath = join33(repo, "shared", "settings.base.json");
4433
+ const hostPath = join33(repo, "hosts", `${host}.json`);
4327
4434
  if (!existsSync27(settingsPath)) {
4328
4435
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
4329
4436
  return;
@@ -4505,7 +4612,7 @@ function reportNodeEngineCheck(section2) {
4505
4612
 
4506
4613
  // src/commands.doctor.checks.longpaths.ts
4507
4614
  init_color();
4508
- import { execFileSync as execFileSync10 } from "node:child_process";
4615
+ import { execFileSync as execFileSync11 } from "node:child_process";
4509
4616
  init_config();
4510
4617
  var PROBE_TIMEOUT_MS = 3e3;
4511
4618
  var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
@@ -4539,7 +4646,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
4539
4646
  }
4540
4647
  addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
4541
4648
  }
4542
- function reportLongPathsCheck(section2, run = execFileSync10) {
4649
+ function reportLongPathsCheck(section2, run = execFileSync11) {
4543
4650
  if (process.platform !== "win32") return;
4544
4651
  addLongpathsRow(
4545
4652
  section2,
@@ -4561,15 +4668,15 @@ function reportSyncModality(section2) {
4561
4668
 
4562
4669
  // src/commands.doctor.checks.crlf.ts
4563
4670
  init_color();
4564
- import { execFileSync as execFileSync11 } from "node:child_process";
4671
+ import { execFileSync as execFileSync12 } from "node:child_process";
4565
4672
  import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
4566
- import { join as join33 } from "node:path";
4673
+ import { join as join34 } from "node:path";
4567
4674
  init_config();
4568
4675
  var PROBE_TIMEOUT_MS2 = 3e3;
4569
4676
  var GUARD_LINE = /^\*\s+-text\b/;
4570
4677
  function hasGitattributesGuard(repo) {
4571
4678
  try {
4572
- const path = join33(repo, ".gitattributes");
4679
+ const path = join34(repo, ".gitattributes");
4573
4680
  if (!existsSync28(path)) return false;
4574
4681
  const content = readFileSync13(path, "utf8");
4575
4682
  return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
@@ -4600,7 +4707,7 @@ function addExposedRow(section2, repo, verdict) {
4600
4707
  const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
4601
4708
  addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
4602
4709
  }
4603
- function reportCrlfGuardCheck(section2, run = execFileSync11) {
4710
+ function reportCrlfGuardCheck(section2, run = execFileSync12) {
4604
4711
  const repo = repoHome();
4605
4712
  if (hasGitattributesGuard(repo)) {
4606
4713
  addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
@@ -4611,35 +4718,35 @@ function reportCrlfGuardCheck(section2, run = execFileSync11) {
4611
4718
 
4612
4719
  // src/spinner.ts
4613
4720
  init_color();
4614
- import { existsSync as existsSync32 } from "node:fs";
4721
+ import { existsSync as existsSync33 } from "node:fs";
4615
4722
  import { fileURLToPath as fileURLToPath4 } from "node:url";
4616
4723
  import { Worker } from "node:worker_threads";
4617
4724
 
4618
4725
  // src/commands.push.recovery.ts
4619
4726
  init_config();
4620
- import { readFileSync as readFileSync16, rmSync as rmSync12, writeFileSync as writeFileSync5 } from "node:fs";
4621
- import { join as join38 } from "node:path";
4727
+ import { readFileSync as readFileSync17, rmSync as rmSync13, writeFileSync as writeFileSync7 } from "node:fs";
4728
+ import { join as join40 } from "node:path";
4622
4729
  import { createInterface as createInterface2 } from "node:readline/promises";
4623
4730
 
4624
4731
  // src/commands.push.recovery.actions.ts
4625
4732
  init_config();
4626
- import { readFileSync as readFileSync15 } from "node:fs";
4733
+ import { readFileSync as readFileSync16 } from "node:fs";
4627
4734
  import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep6 } from "node:path";
4628
4735
 
4629
4736
  // src/commands.push.recovery.redact.ts
4630
4737
  init_config();
4631
4738
  init_config_sharedDirs_guard();
4632
- import { cpSync as cpSync7, existsSync as existsSync31, mkdirSync as mkdirSync7, statSync as statSync8 } from "node:fs";
4633
- import { dirname as dirname8, join as join36, sep as sep5 } from "node:path";
4739
+ import { cpSync as cpSync8, existsSync as existsSync32, mkdirSync as mkdirSync9, statSync as statSync8 } from "node:fs";
4740
+ import { dirname as dirname9, join as join38, sep as sep5 } from "node:path";
4634
4741
 
4635
4742
  // src/commands.redact.ts
4636
4743
  init_config();
4637
4744
  import { existsSync as existsSync30, statSync as statSync7 } from "node:fs";
4638
- import { dirname as dirname7, join as join35 } from "node:path";
4745
+ import { dirname as dirname8, join as join36 } from "node:path";
4639
4746
 
4640
4747
  // src/commands.redact.subtree.ts
4641
- import { existsSync as existsSync29, lstatSync as lstatSync11, readFileSync as readFileSync14, readdirSync as readdirSync13, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
4642
- import { join as join34 } from "node:path";
4748
+ import { existsSync as existsSync29, lstatSync as lstatSync11, readFileSync as readFileSync14, readdirSync as readdirSync13, statSync as statSync6, writeFileSync as writeFileSync5 } from "node:fs";
4749
+ import { join as join35 } from "node:path";
4643
4750
  init_utils_fs();
4644
4751
  init_utils();
4645
4752
  function collectFiles(dir, out) {
@@ -4647,7 +4754,7 @@ function collectFiles(dir, out) {
4647
4754
  const st = lstatSync11(dir);
4648
4755
  if (!st.isDirectory()) return;
4649
4756
  for (const entry of readdirSync13(dir)) {
4650
- const abs = join34(dir, entry);
4757
+ const abs = join35(dir, entry);
4651
4758
  const lst = lstatSync11(abs);
4652
4759
  if (lst.isSymbolicLink()) continue;
4653
4760
  if (lst.isDirectory()) {
@@ -4691,7 +4798,7 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
4691
4798
  `warning: no redaction applied to ${filePath}: finding match values were not located in the file. Inspect it manually; the push re-scan still blocks a real leak.`
4692
4799
  );
4693
4800
  }
4694
- writeFileSync4(filePath, after, "utf8");
4801
+ writeFileSync5(filePath, after, "utf8");
4695
4802
  }
4696
4803
  }
4697
4804
  return { total, dirty };
@@ -4699,10 +4806,10 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
4699
4806
 
4700
4807
  // src/commands.pushed-history.ts
4701
4808
  init_utils();
4702
- import { execFileSync as execFileSync12 } from "node:child_process";
4809
+ import { execFileSync as execFileSync13 } from "node:child_process";
4703
4810
  function pushedRef(repo) {
4704
4811
  try {
4705
- const ref = execFileSync12("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
4812
+ const ref = execFileSync13("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
4706
4813
  cwd: repo,
4707
4814
  stdio: ["ignore", "pipe", "ignore"]
4708
4815
  }).toString().trim();
@@ -4715,7 +4822,7 @@ function sessionInPushedHistory(id, repo) {
4715
4822
  const ref = pushedRef(repo);
4716
4823
  if (ref === null) return false;
4717
4824
  try {
4718
- const out = execFileSync12(
4825
+ const out = execFileSync13(
4719
4826
  "git",
4720
4827
  [
4721
4828
  "log",
@@ -4752,14 +4859,14 @@ init_utils_json();
4752
4859
  init_utils();
4753
4860
  function resolveLiveTranscript(id) {
4754
4861
  try {
4755
- const mapPath = join35(repoHome(), "path-map.json");
4862
+ const mapPath = join36(repoHome(), "path-map.json");
4756
4863
  if (!existsSync30(mapPath)) return null;
4757
4864
  const projects = readJson(mapPath).projects;
4758
4865
  const claude = claudeHome();
4759
4866
  for (const hostMap of Object.values(projects)) {
4760
4867
  const abs = hostMap[HOST];
4761
4868
  if (abs === void 0) continue;
4762
- const live = join35(claude, "projects", encodePath(abs), `${id}.jsonl`);
4869
+ const live = join36(claude, "projects", encodePath(abs), `${id}.jsonl`);
4763
4870
  if (existsSync30(live)) return live;
4764
4871
  }
4765
4872
  return null;
@@ -4790,7 +4897,7 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
4790
4897
  process.exitCode = 1;
4791
4898
  return;
4792
4899
  }
4793
- const sessionDir = join35(dirname7(localPath), id);
4900
+ const sessionDir = join36(dirname8(localPath), id);
4794
4901
  const subtreeFiles = listSubtreeFiles(sessionDir);
4795
4902
  const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
4796
4903
  if (isRecentlyModified(subtreeMtime, nowMs())) {
@@ -4850,6 +4957,78 @@ init_utils();
4850
4957
 
4851
4958
  // src/commands.push.recovery.seams.ts
4852
4959
  init_push_gitleaks();
4960
+
4961
+ // src/commands.push.recovery.memory.ts
4962
+ init_config();
4963
+ init_config_sharedDirs_guard();
4964
+ import { cpSync as cpSync7, existsSync as existsSync31, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "node:fs";
4965
+ import { join as join37 } from "node:path";
4966
+ init_push_gitleaks_scan();
4967
+ init_utils_fs();
4968
+ init_utils_json();
4969
+ init_utils();
4970
+ var MEMORY_FINDING_PATH = /^shared\/projects\/([^/]+)\/memory\/([^/]+\.md)$/;
4971
+ var SAFE_MEMORY_FILENAME = /^[^/\\]+\.md$/;
4972
+ var MEMORY_DIR_PATH = /^shared\/projects\/[^/]+\/memory\//;
4973
+ function memoryFileFromFinding(f) {
4974
+ const m = MEMORY_FINDING_PATH.exec(f.File);
4975
+ if (m?.[1] === void 0 || m[2] === void 0) return null;
4976
+ return { logical: m[1], filename: m[2] };
4977
+ }
4978
+ function isMemoryFindingPath(f) {
4979
+ return MEMORY_DIR_PATH.test(f.File);
4980
+ }
4981
+ function resolveMemoryLocalPath(logical, filename, map) {
4982
+ assertSafeLogical(logical);
4983
+ if (!SAFE_MEMORY_FILENAME.test(filename) || filename.includes("..")) return null;
4984
+ const abs = map.projects[logical]?.[HOST];
4985
+ if (abs === void 0) return null;
4986
+ const localPath = join37(claudeHome(), "projects", encodePath(abs), "memory", filename);
4987
+ return existsSync31(localPath) ? localPath : null;
4988
+ }
4989
+ function preflightMemoryRedactable(f, map) {
4990
+ const parsed = memoryFileFromFinding(f);
4991
+ if (parsed === null) return "a finding is not a project-level memory file";
4992
+ const localPath = resolveMemoryLocalPath(parsed.logical, parsed.filename, map);
4993
+ if (localPath === null) {
4994
+ return `memory file ${parsed.logical}/memory/${parsed.filename}: local file not found or unmapped`;
4995
+ }
4996
+ return null;
4997
+ }
4998
+ function applyMemoryRedact(f, ts, map, scan = scanFile) {
4999
+ const refuse = (msg) => {
5000
+ log(msg);
5001
+ return false;
5002
+ };
5003
+ const parsed = memoryFileFromFinding(f);
5004
+ if (parsed === null) {
5005
+ return refuse("could not parse this finding as a project-level memory file; choose Skip.");
5006
+ }
5007
+ const { logical, filename } = parsed;
5008
+ const localPath = resolveMemoryLocalPath(logical, filename, map);
5009
+ if (localPath === null) {
5010
+ return refuse(
5011
+ `could not locate the local memory file for ${logical}/memory/${filename}; choose Skip.`
5012
+ );
5013
+ }
5014
+ const findings = scan(localPath);
5015
+ if (findings === null) {
5016
+ return refuse(`re-scan of ${logical}/memory/${filename} failed; choose Skip.`);
5017
+ }
5018
+ if (findings.length === 0) {
5019
+ return refuse(`nothing to redact in ${logical}/memory/${filename}; choose Skip.`);
5020
+ }
5021
+ backupBeforeWrite(localPath, ts);
5022
+ const before = readFileSync15(localPath, "utf8");
5023
+ const after = applyRedactions(before, findings);
5024
+ writeFileSync6(localPath, after, "utf8");
5025
+ const stagedMemoryDir = join37(repoHome(), "shared", "projects", logical, "memory");
5026
+ mkdirSync8(stagedMemoryDir, { recursive: true });
5027
+ cpSync7(localPath, join37(stagedMemoryDir, filename), { force: true });
5028
+ return true;
5029
+ }
5030
+
5031
+ // src/commands.push.recovery.seams.ts
4853
5032
  var MASK_LEAD = 4;
4854
5033
  var MASK_BODY = "************";
4855
5034
  var CONTEXT_WINDOW = 40;
@@ -4858,8 +5037,10 @@ function findingKey(f) {
4858
5037
  return `${f.File}:${f.StartLine}:${f.StartColumn}:${f.RuleID}`;
4859
5038
  }
4860
5039
  var VALID_SID = /^[A-Za-z0-9_-]+$/;
5040
+ var SUBTREE_PATH = /^shared\/projects\/[^/]+\/([^/]+)\/.+$/;
4861
5041
  function sessionIdFromFinding(f) {
4862
- const m = SESSION_PATH.exec(f.File) ?? /^shared\/projects\/[^/]+\/([^/]+)\//.exec(f.File);
5042
+ if (isMemoryFindingPath(f)) return null;
5043
+ const m = SESSION_PATH.exec(f.File) ?? SUBTREE_PATH.exec(f.File);
4863
5044
  if (m === null) return null;
4864
5045
  const sid = m[1];
4865
5046
  return VALID_SID.test(sid) ? sid : null;
@@ -4906,8 +5087,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
4906
5087
  assertSafeLogical(logical);
4907
5088
  const abs = hostMap[HOST];
4908
5089
  if (abs === void 0) continue;
4909
- if (localPath.startsWith(join36(claude, "projects", encodePath(abs)) + sep5)) {
4910
- return join36(repo, "shared", "projects", logical);
5090
+ if (localPath.startsWith(join38(claude, "projects", encodePath(abs)) + sep5)) {
5091
+ return join38(repo, "shared", "projects", logical);
4911
5092
  }
4912
5093
  }
4913
5094
  return null;
@@ -4917,7 +5098,7 @@ function preflightRedactable(f, map, nowMs) {
4917
5098
  if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
4918
5099
  const localPath = resolveLiveTranscript(sid);
4919
5100
  if (localPath === null) return `session ${sid}: local transcript not found`;
4920
- const sessionDir = join36(dirname8(localPath), sid);
5101
+ const sessionDir = join38(dirname9(localPath), sid);
4921
5102
  const subtreeFiles = listSubtreeFiles(sessionDir);
4922
5103
  const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
4923
5104
  if (isRecentlyModified(subtreeMtime, nowMs())) {
@@ -4947,7 +5128,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
4947
5128
  `could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
4948
5129
  );
4949
5130
  }
4950
- const sessionDir = join36(dirname8(localPath), sid);
5131
+ const sessionDir = join38(dirname9(localPath), sid);
4951
5132
  const subtreeFiles = listSubtreeFiles(sessionDir);
4952
5133
  const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
4953
5134
  if (isRecentlyModified(subtreeMtime, nowMs())) {
@@ -4980,27 +5161,27 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
4980
5161
  `nothing to redact in the local transcript for session ${sid}; choose Skip or Drop session.`
4981
5162
  );
4982
5163
  }
4983
- mkdirSync7(stagedProjectDir, { recursive: true });
4984
- cpSync7(localPath, join36(stagedProjectDir, `${sid}.jsonl`), { force: true });
4985
- if (existsSync31(sessionDir)) {
4986
- cpSync7(sessionDir, join36(stagedProjectDir, sid), { force: true, recursive: true });
5164
+ mkdirSync9(stagedProjectDir, { recursive: true });
5165
+ cpSync8(localPath, join38(stagedProjectDir, `${sid}.jsonl`), { force: true });
5166
+ if (existsSync32(sessionDir)) {
5167
+ cpSync8(sessionDir, join38(stagedProjectDir, sid), { force: true, recursive: true });
4987
5168
  }
4988
5169
  return true;
4989
5170
  }
4990
5171
 
4991
5172
  // src/commands.push.recovery.drop.ts
4992
5173
  init_config();
4993
- import { rmSync as rmSync11 } from "node:fs";
4994
- import { join as join37 } from "node:path";
5174
+ import { rmSync as rmSync12 } from "node:fs";
5175
+ import { join as join39 } from "node:path";
4995
5176
  function dropSessionFromStaged(sid, map) {
4996
5177
  const logicals = Object.keys(map.projects);
4997
5178
  if (logicals.length === 0) return false;
4998
5179
  const repo = repoHome();
4999
5180
  for (const logical of logicals) {
5000
- const jsonl = join37(repo, "shared", "projects", logical, `${sid}.jsonl`);
5001
- const dir = join37(repo, "shared", "projects", logical, sid);
5002
- rmSync11(jsonl, { force: true });
5003
- rmSync11(dir, { recursive: true, force: true });
5181
+ const jsonl = join39(repo, "shared", "projects", logical, `${sid}.jsonl`);
5182
+ const dir = join39(repo, "shared", "projects", logical, sid);
5183
+ rmSync12(jsonl, { force: true });
5184
+ rmSync12(dir, { recursive: true, force: true });
5004
5185
  }
5005
5186
  return true;
5006
5187
  }
@@ -5034,7 +5215,7 @@ function makeDefaultReadLine(repo) {
5034
5215
  if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep6)) {
5035
5216
  return null;
5036
5217
  }
5037
- const content = readFileSync15(target, "utf8");
5218
+ const content = readFileSync16(target, "utf8");
5038
5219
  const lines = content.split(/\r?\n/);
5039
5220
  const idx = line - 1;
5040
5221
  if (idx < 0 || idx >= lines.length) return null;
@@ -5057,6 +5238,22 @@ Finding: ${f.RuleID} in ${f.File} line ${f.StartLine}` + (sid === null ? "" : `
5057
5238
  }
5058
5239
  return actions;
5059
5240
  }
5241
+ function dispatchMemory(f, action, ctx) {
5242
+ const parsed = memoryFileFromFinding(f);
5243
+ if (parsed === null) {
5244
+ if (isMemoryFindingPath(f)) {
5245
+ log(`memory path not auto-redactable: ${f.File}; scrub it by hand or choose Skip`);
5246
+ }
5247
+ return;
5248
+ }
5249
+ if (action === "drop") {
5250
+ log("memory files cannot be dropped; use Redact or Skip");
5251
+ return;
5252
+ }
5253
+ const memKey = `${parsed.logical}/${parsed.filename}`;
5254
+ if (ctx.redactedMemory.has(memKey)) return;
5255
+ if (applyMemoryRedact(f, ctx.ts, ctx.map, ctx.scan)) ctx.redactedMemory.add(memKey);
5256
+ }
5060
5257
  function dispatchOne(f, ctx) {
5061
5258
  const action = ctx.actions.get(findingKey(f)) ?? "skip";
5062
5259
  if (action === "skip") return;
@@ -5066,7 +5263,10 @@ function dispatchOne(f, ctx) {
5066
5263
  applyAllow(f, ctx.repo);
5067
5264
  return;
5068
5265
  }
5069
- if (sid === null) return;
5266
+ if (sid === null) {
5267
+ dispatchMemory(f, action, ctx);
5268
+ return;
5269
+ }
5070
5270
  if (action === "drop") {
5071
5271
  ctx.droppedSids.add(sid);
5072
5272
  if (ctx.drop(sid, ctx.map)) {
@@ -5091,20 +5291,46 @@ function dispatchActions(findings, actions, opts) {
5091
5291
  scan,
5092
5292
  drop,
5093
5293
  redactedSids: /* @__PURE__ */ new Set(),
5094
- droppedSids: /* @__PURE__ */ new Set()
5294
+ droppedSids: /* @__PURE__ */ new Set(),
5295
+ redactedMemory: /* @__PURE__ */ new Set()
5095
5296
  };
5096
5297
  for (const f of findings) {
5097
5298
  dispatchOne(f, ctx);
5098
5299
  }
5099
5300
  }
5301
+ function redactAllDedupeKey(f) {
5302
+ const sid = sessionIdFromFinding(f);
5303
+ if (sid !== null) return sid;
5304
+ const parsed = memoryFileFromFinding(f);
5305
+ return parsed !== null ? `${parsed.logical}/${parsed.filename}` : findingKey(f);
5306
+ }
5307
+ function redactAllPreflightOne(f, map, nowMs) {
5308
+ if (sessionIdFromFinding(f) === null && memoryFileFromFinding(f) !== null) {
5309
+ return preflightMemoryRedactable(f, map);
5310
+ }
5311
+ return preflightRedactable(f, map, nowMs);
5312
+ }
5313
+ function redactAllOne(f, ts, map, nowMs, scan, redactedSids, redactedMemory) {
5314
+ const sid = sessionIdFromFinding(f);
5315
+ if (sid !== null) {
5316
+ if (redactedSids.has(sid)) return;
5317
+ if (applyRedact(f, ts, map, nowMs, scan)) redactedSids.add(sid);
5318
+ return;
5319
+ }
5320
+ const parsed = memoryFileFromFinding(f);
5321
+ if (parsed === null) return;
5322
+ const memKey = `${parsed.logical}/${parsed.filename}`;
5323
+ if (redactedMemory.has(memKey)) return;
5324
+ if (applyMemoryRedact(f, ts, map, scan)) redactedMemory.add(memKey);
5325
+ }
5100
5326
  function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
5101
5327
  const refusals = [];
5102
5328
  const preflighted = /* @__PURE__ */ new Set();
5103
5329
  for (const f of findings) {
5104
- const dedupeKey = sessionIdFromFinding(f) ?? findingKey(f);
5330
+ const dedupeKey = redactAllDedupeKey(f);
5105
5331
  if (preflighted.has(dedupeKey)) continue;
5106
5332
  preflighted.add(dedupeKey);
5107
- const reason = preflightRedactable(f, map, nowMs);
5333
+ const reason = redactAllPreflightOne(f, map, nowMs);
5108
5334
  if (reason !== null) refusals.push(reason);
5109
5335
  }
5110
5336
  if (refusals.length > 0) {
@@ -5115,10 +5341,9 @@ function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
5115
5341
  );
5116
5342
  }
5117
5343
  const redactedSids = /* @__PURE__ */ new Set();
5344
+ const redactedMemory = /* @__PURE__ */ new Set();
5118
5345
  for (const f of findings) {
5119
- const sid = sessionIdFromFinding(f);
5120
- if (sid === null || redactedSids.has(sid)) continue;
5121
- if (applyRedact(f, ts, map, nowMs, scan)) redactedSids.add(sid);
5346
+ redactAllOne(f, ts, map, nowMs, scan, redactedSids, redactedMemory);
5122
5347
  }
5123
5348
  }
5124
5349
 
@@ -5155,10 +5380,10 @@ function applyThenRescan(scanVerdict, repoHome2) {
5155
5380
  return next;
5156
5381
  }
5157
5382
  function allowThenRescan(append, scanVerdict, repoHome2) {
5158
- const ignPath = join38(repoHome2, ".gitleaksignore");
5383
+ const ignPath = join40(repoHome2, ".gitleaksignore");
5159
5384
  let before;
5160
5385
  try {
5161
- before = readFileSync16(ignPath, "utf8");
5386
+ before = readFileSync17(ignPath, "utf8");
5162
5387
  } catch {
5163
5388
  before = null;
5164
5389
  }
@@ -5166,8 +5391,8 @@ function allowThenRescan(append, scanVerdict, repoHome2) {
5166
5391
  try {
5167
5392
  return applyThenRescan(scanVerdict, repoHome2);
5168
5393
  } catch (err) {
5169
- if (before === null) rmSync12(ignPath, { force: true });
5170
- else writeFileSync5(ignPath, before, "utf8");
5394
+ if (before === null) rmSync13(ignPath, { force: true });
5395
+ else writeFileSync7(ignPath, before, "utf8");
5171
5396
  throw err;
5172
5397
  }
5173
5398
  }
@@ -5254,7 +5479,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
5254
5479
  `);
5255
5480
  }
5256
5481
  function resolveWorkerPath(deps = {}) {
5257
- const check = deps.existsSyncFn ?? existsSync32;
5482
+ const check = deps.existsSyncFn ?? existsSync33;
5258
5483
  const base = deps.baseUrl ?? import.meta.url;
5259
5484
  const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
5260
5485
  if (check(mjs)) return mjs;
@@ -5320,9 +5545,9 @@ function withSpinner(label, fn, deps) {
5320
5545
 
5321
5546
  // src/commands.doctor.gitleaks-version.ts
5322
5547
  init_color();
5323
- import { execFileSync as execFileSync13 } from "node:child_process";
5324
- import { existsSync as existsSync33 } from "node:fs";
5325
- import { join as join39 } from "node:path";
5548
+ import { execFileSync as execFileSync14 } from "node:child_process";
5549
+ import { existsSync as existsSync34 } from "node:fs";
5550
+ import { join as join41 } from "node:path";
5326
5551
  init_config();
5327
5552
  var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
5328
5553
  var GITLEAKS_TIMEOUT_MS = 5e3;
@@ -5331,7 +5556,7 @@ function majorMinorOf(value) {
5331
5556
  return m === null ? null : [m[1], m[2]];
5332
5557
  }
5333
5558
  function readGitleaksVersion(run, tomlExists) {
5334
- const tomlPath = join39(repoHome(), ".gitleaks.toml");
5559
+ const tomlPath = join41(repoHome(), ".gitleaks.toml");
5335
5560
  const args = ["version"];
5336
5561
  if (tomlExists(tomlPath)) args.push("--config", tomlPath);
5337
5562
  try {
@@ -5343,7 +5568,7 @@ function readGitleaksVersion(run, tomlExists) {
5343
5568
  return null;
5344
5569
  }
5345
5570
  }
5346
- function reportGitleaksVersionCheck(section2, run = execFileSync13, tomlExists = existsSync33) {
5571
+ function reportGitleaksVersionCheck(section2, run = execFileSync14, tomlExists = existsSync34) {
5347
5572
  const raw = readGitleaksVersion(run, tomlExists);
5348
5573
  if (raw === null) return;
5349
5574
  const local = majorMinorOf(raw);
@@ -5363,7 +5588,7 @@ function reportGitleaksVersionCheck(section2, run = execFileSync13, tomlExists =
5363
5588
 
5364
5589
  // src/commands.doctor.checks.deps.ts
5365
5590
  init_color();
5366
- import { execFileSync as execFileSync14 } from "node:child_process";
5591
+ import { execFileSync as execFileSync15 } from "node:child_process";
5367
5592
  var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
5368
5593
  var PROBE_TIMEOUT_MS3 = 3e3;
5369
5594
  var FETCHER_BASE = "HTTP fetcher";
@@ -5400,7 +5625,7 @@ function reportFetcherRow(section2, run) {
5400
5625
  );
5401
5626
  }
5402
5627
  }
5403
- function reportOptionalDeps(section2, run = execFileSync14) {
5628
+ function reportOptionalDeps(section2, run = execFileSync15) {
5404
5629
  const gh = probeOptionalDep("gh", run);
5405
5630
  if (gh.status === "present") {
5406
5631
  addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
@@ -5415,11 +5640,11 @@ function reportOptionalDeps(section2, run = execFileSync14) {
5415
5640
 
5416
5641
  // src/commands.doctor.actions-drift.ts
5417
5642
  init_color();
5418
- import { execFileSync as execFileSync16 } from "node:child_process";
5643
+ import { execFileSync as execFileSync17 } from "node:child_process";
5419
5644
  init_config();
5420
5645
 
5421
5646
  // src/gh-actions.ts
5422
- import { execFileSync as execFileSync15 } from "node:child_process";
5647
+ import { execFileSync as execFileSync16 } from "node:child_process";
5423
5648
  var GH_TIMEOUT_MS = 5e3;
5424
5649
  function parseGitHubRemote(remoteUrl) {
5425
5650
  const normalized = remoteUrl.trim().replace(/\/$/, "");
@@ -5427,7 +5652,7 @@ function parseGitHubRemote(remoteUrl) {
5427
5652
  if (m === null) return null;
5428
5653
  return { owner: m[1], repo: m[2] };
5429
5654
  }
5430
- function ghAuthStatus(run = execFileSync15) {
5655
+ function ghAuthStatus(run = execFileSync16) {
5431
5656
  try {
5432
5657
  run("gh", ["auth", "status"], {
5433
5658
  stdio: ["ignore", "ignore", "ignore"],
@@ -5441,7 +5666,7 @@ function ghAuthStatus(run = execFileSync15) {
5441
5666
  return "gh-probe-error";
5442
5667
  }
5443
5668
  }
5444
- function isRepoPrivate(ref, run = execFileSync15) {
5669
+ function isRepoPrivate(ref, run = execFileSync16) {
5445
5670
  const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
5446
5671
  stdio: ["ignore", "pipe", "ignore"],
5447
5672
  timeout: GH_TIMEOUT_MS
@@ -5449,7 +5674,7 @@ function isRepoPrivate(ref, run = execFileSync15) {
5449
5674
  const parsed = JSON.parse(out);
5450
5675
  return parsed.isPrivate === true;
5451
5676
  }
5452
- function isActionsEnabled(ref, run = execFileSync15) {
5677
+ function isActionsEnabled(ref, run = execFileSync16) {
5453
5678
  const out = run(
5454
5679
  "gh",
5455
5680
  ["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
@@ -5457,7 +5682,7 @@ function isActionsEnabled(ref, run = execFileSync15) {
5457
5682
  ).toString().trim();
5458
5683
  return out === "true";
5459
5684
  }
5460
- function disableActions(ref, run = execFileSync15) {
5685
+ function disableActions(ref, run = execFileSync16) {
5461
5686
  run(
5462
5687
  "gh",
5463
5688
  [
@@ -5471,7 +5696,7 @@ function disableActions(ref, run = execFileSync15) {
5471
5696
  { stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
5472
5697
  );
5473
5698
  }
5474
- function readOriginRemote(cwd, run = execFileSync15) {
5699
+ function readOriginRemote(cwd, run = execFileSync16) {
5475
5700
  return run("git", ["remote", "get-url", "origin"], {
5476
5701
  cwd,
5477
5702
  stdio: ["ignore", "pipe", "ignore"]
@@ -5479,7 +5704,7 @@ function readOriginRemote(cwd, run = execFileSync15) {
5479
5704
  }
5480
5705
 
5481
5706
  // src/commands.doctor.actions-drift.ts
5482
- function reportActionsDrift(section2, run = execFileSync16) {
5707
+ function reportActionsDrift(section2, run = execFileSync17) {
5483
5708
  let remote;
5484
5709
  try {
5485
5710
  remote = readOriginRemote(repoHome(), run);
@@ -5569,8 +5794,8 @@ function gatherDoctorSections(opts) {
5569
5794
  reportLongPathsCheck(host);
5570
5795
  reportCrlfGuardCheck(host);
5571
5796
  const links = section("Shared links");
5572
- const mapPath = join40(repoHome(), "path-map.json");
5573
- const rawMap = existsSync34(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
5797
+ const mapPath = join42(repoHome(), "path-map.json");
5798
+ const rawMap = existsSync35(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
5574
5799
  const map = rawMap ?? { projects: {} };
5575
5800
  reportSharedLinks(links, map);
5576
5801
  reportDroppedNamesMigration(links);
@@ -5666,15 +5891,15 @@ function parseDoctorArgs(args) {
5666
5891
 
5667
5892
  // src/commands.drop-session.ts
5668
5893
  init_config();
5669
- import { execFileSync as execFileSync18 } from "node:child_process";
5670
- import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync9 } from "node:fs";
5671
- import { join as join42, relative as relative6 } from "node:path";
5894
+ import { execFileSync as execFileSync19 } from "node:child_process";
5895
+ import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync9 } from "node:fs";
5896
+ import { join as join44, relative as relative6 } from "node:path";
5672
5897
 
5673
5898
  // src/commands.drop-session.git.ts
5674
- import { execFileSync as execFileSync17 } from "node:child_process";
5899
+ import { execFileSync as execFileSync18 } from "node:child_process";
5675
5900
  function expandStagedDir(dirRel, repo) {
5676
5901
  try {
5677
- const out = execFileSync17("git", ["ls-files", "-z", "--", dirRel], {
5902
+ const out = execFileSync18("git", ["ls-files", "-z", "--", dirRel], {
5678
5903
  cwd: repo,
5679
5904
  stdio: ["ignore", "pipe", "pipe"]
5680
5905
  });
@@ -5686,7 +5911,7 @@ function expandStagedDir(dirRel, repo) {
5686
5911
  function isTrackedInHead(rel, repo) {
5687
5912
  try {
5688
5913
  const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
5689
- execFileSync17("git", ["cat-file", "-e", `HEAD:${treePath}`], {
5914
+ execFileSync18("git", ["cat-file", "-e", `HEAD:${treePath}`], {
5690
5915
  cwd: repo,
5691
5916
  stdio: ["ignore", "pipe", "pipe"]
5692
5917
  });
@@ -5697,7 +5922,7 @@ function isTrackedInHead(rel, repo) {
5697
5922
  }
5698
5923
  function isInIndex(rel, repo) {
5699
5924
  try {
5700
- const out = execFileSync17("git", ["ls-files", "--", rel], {
5925
+ const out = execFileSync18("git", ["ls-files", "--", rel], {
5701
5926
  cwd: repo,
5702
5927
  stdio: ["ignore", "pipe", "pipe"]
5703
5928
  });
@@ -5711,8 +5936,8 @@ function isInIndex(rel, repo) {
5711
5936
  init_config();
5712
5937
  init_utils();
5713
5938
  init_utils_json();
5714
- import { existsSync as existsSync35 } from "node:fs";
5715
- import { join as join41 } from "node:path";
5939
+ import { existsSync as existsSync36 } from "node:fs";
5940
+ import { join as join43 } from "node:path";
5716
5941
  var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
5717
5942
  function reportScrubHint(id, matches) {
5718
5943
  const live = resolveLiveTranscript2(id, matches);
@@ -5728,8 +5953,8 @@ function reportScrubHint(id, matches) {
5728
5953
  }
5729
5954
  function resolveLiveTranscript2(id, matches) {
5730
5955
  try {
5731
- const mapPath = join41(repoHome(), "path-map.json");
5732
- if (!existsSync35(mapPath)) return null;
5956
+ const mapPath = join43(repoHome(), "path-map.json");
5957
+ if (!existsSync36(mapPath)) return null;
5733
5958
  const projects = readJson(mapPath).projects;
5734
5959
  const claude = claudeHome();
5735
5960
  for (const rel of matches) {
@@ -5737,8 +5962,8 @@ function resolveLiveTranscript2(id, matches) {
5737
5962
  if (logical === void 0) continue;
5738
5963
  const abs = projects[logical]?.[HOST];
5739
5964
  if (abs === void 0) continue;
5740
- const live = join41(claude, "projects", encodePath(abs), `${id}.jsonl`);
5741
- if (existsSync35(live)) return live;
5965
+ const live = join43(claude, "projects", encodePath(abs), `${id}.jsonl`);
5966
+ if (existsSync36(live)) return live;
5742
5967
  }
5743
5968
  return null;
5744
5969
  } catch {
@@ -5754,12 +5979,12 @@ function cmdDropSession(id) {
5754
5979
  process.exit(1);
5755
5980
  }
5756
5981
  const repo = repoHome();
5757
- if (!existsSync36(repo)) die(`repo not cloned at ${repo}`);
5982
+ if (!existsSync37(repo)) die(`repo not cloned at ${repo}`);
5758
5983
  const handle = acquireLock("drop-session");
5759
5984
  if (handle === null) process.exit(0);
5760
5985
  try {
5761
- const repoProjects = join42(repo, "shared", "projects");
5762
- if (!existsSync36(repoProjects)) {
5986
+ const repoProjects = join44(repo, "shared", "projects");
5987
+ if (!existsSync37(repoProjects)) {
5763
5988
  throw new NomadFatal(`no staged session matches ${id}`);
5764
5989
  }
5765
5990
  const matches = collectMatches(repoProjects, id, repo);
@@ -5782,12 +6007,12 @@ function cmdDropSession(id) {
5782
6007
  function collectMatches(repoProjects, id, repo) {
5783
6008
  const matches = [];
5784
6009
  for (const logical of readdirSync14(repoProjects)) {
5785
- const candidate = join42(repoProjects, logical, `${id}.jsonl`);
5786
- if (existsSync36(candidate)) {
6010
+ const candidate = join44(repoProjects, logical, `${id}.jsonl`);
6011
+ if (existsSync37(candidate)) {
5787
6012
  matches.push(relative6(repo, candidate));
5788
6013
  }
5789
- const dir = join42(repoProjects, logical, id);
5790
- if (existsSync36(dir) && statSync9(dir).isDirectory()) {
6014
+ const dir = join44(repoProjects, logical, id);
6015
+ if (existsSync37(dir) && statSync9(dir).isDirectory()) {
5791
6016
  const dirRel = relative6(repo, dir);
5792
6017
  const staged = expandStagedDir(dirRel, repo);
5793
6018
  if (staged.length > 0) matches.push(...staged);
@@ -5803,12 +6028,12 @@ function unstageOne(rel, repo) {
5803
6028
  }
5804
6029
  try {
5805
6030
  if (isTrackedInHead(rel, repo)) {
5806
- execFileSync18("git", ["restore", "--staged", "--worktree", "--", rel], {
6031
+ execFileSync19("git", ["restore", "--staged", "--worktree", "--", rel], {
5807
6032
  cwd: repo,
5808
6033
  stdio: ["ignore", "pipe", "pipe"]
5809
6034
  });
5810
6035
  } else {
5811
- execFileSync18("git", ["rm", "--cached", "-f", "--", rel], {
6036
+ execFileSync19("git", ["rm", "--cached", "-f", "--", rel], {
5812
6037
  cwd: repo,
5813
6038
  stdio: ["ignore", "pipe", "pipe"]
5814
6039
  });
@@ -5822,8 +6047,8 @@ function unstageOne(rel, repo) {
5822
6047
  }
5823
6048
 
5824
6049
  // src/commands.pull.ts
5825
- import { existsSync as existsSync41, mkdirSync as mkdirSync10 } from "node:fs";
5826
- import { join as join48 } from "node:path";
6050
+ import { existsSync as existsSync42, mkdirSync as mkdirSync12 } from "node:fs";
6051
+ import { join as join50 } from "node:path";
5827
6052
 
5828
6053
  // src/commands.push.sections.ts
5829
6054
  init_color();
@@ -5931,18 +6156,18 @@ init_config();
5931
6156
 
5932
6157
  // src/extras-sync.ts
5933
6158
  init_config();
5934
- import { existsSync as existsSync38, statSync as statSync10 } from "node:fs";
5935
- import { join as join45 } from "node:path";
6159
+ import { existsSync as existsSync39, statSync as statSync10 } from "node:fs";
6160
+ import { join as join47 } from "node:path";
5936
6161
  init_utils();
5937
6162
  init_utils_json();
5938
6163
 
5939
6164
  // src/extras-sync.remap.ts
5940
6165
  init_config();
5941
- import { existsSync as existsSync37, mkdirSync as mkdirSync8, readdirSync as readdirSync15, readFileSync as readFileSync17, realpathSync as realpathSync4, rmSync as rmSync13 } from "node:fs";
5942
- import { dirname as dirname9, join as join44, sep as sep8 } from "node:path";
6166
+ import { existsSync as existsSync38, mkdirSync as mkdirSync10, readdirSync as readdirSync15, readFileSync as readFileSync18, realpathSync as realpathSync4, rmSync as rmSync14 } from "node:fs";
6167
+ import { dirname as dirname10, join as join46, sep as sep8 } from "node:path";
5943
6168
 
5944
6169
  // src/extras-sync.planning-diff.ts
5945
- import { join as join43, normalize as normalize2, sep as sep7 } from "node:path";
6170
+ import { join as join45, normalize as normalize2, sep as sep7 } from "node:path";
5946
6171
  init_utils();
5947
6172
  function processRecord(fields, i, changed, deleted) {
5948
6173
  const status = fields[i];
@@ -5994,7 +6219,7 @@ function planningDeleteTargets(opts) {
5994
6219
  const { deleted } = parsePlanningDiff(raw);
5995
6220
  const logicalPrefix = "shared/extras/" + logical + "/";
5996
6221
  const prefix = logicalPrefix + ".planning/";
5997
- const planningRoot = join43(localRoot, ".planning");
6222
+ const planningRoot = join45(localRoot, ".planning");
5998
6223
  const planningRootBoundary = planningRoot + sep7;
5999
6224
  const targets = [];
6000
6225
  for (const repoPath of deleted) {
@@ -6002,7 +6227,7 @@ function planningDeleteTargets(opts) {
6002
6227
  continue;
6003
6228
  }
6004
6229
  const remainder = repoPath.slice(logicalPrefix.length);
6005
- const candidate = join43(localRoot, remainder);
6230
+ const candidate = join45(localRoot, remainder);
6006
6231
  const resolved = normalize2(candidate);
6007
6232
  if (!resolved.startsWith(planningRootBoundary)) {
6008
6233
  throw new NomadFatal(
@@ -6023,7 +6248,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
6023
6248
  const would = [];
6024
6249
  for (const t of eachExtrasTarget(v, counts)) {
6025
6250
  const { src, dst } = paths(t);
6026
- if (!existsSync37(src)) continue;
6251
+ if (!existsSync38(src)) continue;
6027
6252
  const item2 = `${t.logical}/${t.dirname}`;
6028
6253
  if (dryRun) {
6029
6254
  would.push(item2);
@@ -6036,15 +6261,15 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
6036
6261
  return { ...counts, done, would };
6037
6262
  }
6038
6263
  function pruneEmptyAncestors(target, planningRoot) {
6039
- let dir = dirname9(target);
6264
+ let dir = dirname10(target);
6040
6265
  while (dir !== planningRoot && dir.startsWith(planningRoot + sep8)) {
6041
6266
  try {
6042
6267
  if (readdirSync15(dir).length > 0) break;
6043
- rmSync13(dir, { recursive: true, force: true });
6268
+ rmSync14(dir, { recursive: true, force: true });
6044
6269
  } catch {
6045
6270
  break;
6046
6271
  }
6047
- dir = dirname9(dir);
6272
+ dir = dirname10(dir);
6048
6273
  }
6049
6274
  }
6050
6275
  function tryRealpath(dir) {
@@ -6058,20 +6283,20 @@ function isInsidePlanningRoot(parentReal, rootReal) {
6058
6283
  return parentReal === rootReal || parentReal.startsWith(rootReal + sep8);
6059
6284
  }
6060
6285
  function deletePlanningTarget(target, planningRoot, repoCounterpart) {
6061
- if (existsSync37(repoCounterpart)) return;
6062
- const parentReal = tryRealpath(dirname9(target));
6286
+ if (existsSync38(repoCounterpart)) return;
6287
+ const parentReal = tryRealpath(dirname10(target));
6063
6288
  if (parentReal === void 0) return;
6064
6289
  const rootReal = tryRealpath(planningRoot);
6065
6290
  if (rootReal === void 0) return;
6066
6291
  if (!isInsidePlanningRoot(parentReal, rootReal)) return;
6067
- rmSync13(target, { recursive: true, force: true });
6292
+ rmSync14(target, { recursive: true, force: true });
6068
6293
  pruneEmptyAncestors(target, planningRoot);
6069
6294
  }
6070
6295
  function localDivergesFromPreDelete(target, pre, repoRel, repo) {
6071
- if (!existsSync37(target)) return false;
6296
+ if (!existsSync38(target)) return false;
6072
6297
  try {
6073
6298
  const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
6074
- return !readFileSync17(target).equals(preBlob);
6299
+ return !readFileSync18(target).equals(preBlob);
6075
6300
  } catch {
6076
6301
  return true;
6077
6302
  }
@@ -6113,7 +6338,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
6113
6338
  return kept;
6114
6339
  }
6115
6340
  function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
6116
- const repoExtras = join44(repo, "shared", "extras");
6341
+ const repoExtras = join46(repo, "shared", "extras");
6117
6342
  for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
6118
6343
  if (t.dirname !== ".planning") continue;
6119
6344
  let raw;
@@ -6128,14 +6353,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
6128
6353
  }
6129
6354
  const pairs = deletePairsFor(t, raw);
6130
6355
  if (pairs.length === 0) continue;
6131
- backupExtrasWrite(join44(t.localRoot, t.dirname), ts, t.localRoot);
6132
- const planningRoot = join44(t.localRoot, ".planning");
6356
+ backupExtrasWrite(join46(t.localRoot, t.dirname), ts, t.localRoot);
6357
+ const planningRoot = join46(t.localRoot, ".planning");
6133
6358
  for (const { target, relToLocal, repoRel } of pairs) {
6134
6359
  if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
6135
6360
  warn(keptDeleteWarnLine(t.logical, relToLocal));
6136
6361
  continue;
6137
6362
  }
6138
- deletePlanningTarget(target, planningRoot, join44(repoExtras, t.logical, relToLocal));
6363
+ deletePlanningTarget(target, planningRoot, join46(repoExtras, t.logical, relToLocal));
6139
6364
  }
6140
6365
  }
6141
6366
  }
@@ -6144,14 +6369,14 @@ function remapExtrasPush(ts, opts = {}) {
6144
6369
  const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
6145
6370
  if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
6146
6371
  const repo = repoHome();
6147
- const repoExtras = join44(repo, "shared", "extras");
6148
- if (!dryRun) mkdirSync8(repoExtras, { recursive: true });
6372
+ const repoExtras = join46(repo, "shared", "extras");
6373
+ if (!dryRun) mkdirSync10(repoExtras, { recursive: true });
6149
6374
  const { unmapped, skipped, done, would } = runExtrasOp(
6150
6375
  v,
6151
6376
  dryRun,
6152
- ({ localRoot, logical, dirname: dirname10 }) => ({
6153
- src: join44(localRoot, dirname10),
6154
- dst: join44(repoExtras, logical, dirname10)
6377
+ ({ localRoot, logical, dirname: dirname11 }) => ({
6378
+ src: join46(localRoot, dirname11),
6379
+ dst: join46(repoExtras, logical, dirname11)
6155
6380
  }),
6156
6381
  (dst) => backupRepoWrite(dst, ts, repo),
6157
6382
  // Push copy routing per extra type:
@@ -6163,7 +6388,7 @@ function remapExtrasPush(ts, opts = {}) {
6163
6388
  // (enforceAllowList / blockSetFor in commands.push.allowlist.ts)
6164
6389
  // remains the hard security boundary.
6165
6390
  // All others: copyExtrasFiltered with per-extra denylist.
6166
- (src, dst, dirname10) => dirname10 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname10)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname10))
6391
+ (src, dst, dirname11) => dirname11 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname11)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname11))
6167
6392
  );
6168
6393
  return { unmapped, skipped, pushed: done, wouldPush: would };
6169
6394
  }
@@ -6179,9 +6404,9 @@ function remapExtrasPull(ts, opts = {}) {
6179
6404
  const { unmapped, skipped, done, would } = runExtrasOp(
6180
6405
  v,
6181
6406
  dryRun,
6182
- ({ localRoot, logical, dirname: dirname10 }) => ({
6183
- src: join44(repo, "shared", "extras", logical, dirname10),
6184
- dst: join44(localRoot, dirname10)
6407
+ ({ localRoot, logical, dirname: dirname11 }) => ({
6408
+ src: join46(repo, "shared", "extras", logical, dirname11),
6409
+ dst: join46(localRoot, dirname11)
6185
6410
  }),
6186
6411
  // Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
6187
6412
  // localRoot so the backup tree mirrors the project layout.
@@ -6199,12 +6424,12 @@ function remapExtrasPull(ts, opts = {}) {
6199
6424
  // copyExtrasFileSkipDiverged keeps a locally-edited file rather than
6200
6425
  // clobbering it, so the divergence WARN's keep-local promise holds for
6201
6426
  // every extra, not just `.planning`.
6202
- (src, dst, dirname10) => {
6203
- if (dirname10 === ".claude")
6204
- return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname10));
6205
- if (dirname10 === ".planning") {
6427
+ (src, dst, dirname11) => {
6428
+ if (dirname11 === ".claude")
6429
+ return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname11));
6430
+ if (dirname11 === ".planning") {
6206
6431
  const divergedSet = new Set(listDivergingModified(dst, src));
6207
- return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname10), divergedSet);
6432
+ return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname11), divergedSet);
6208
6433
  }
6209
6434
  return copyExtrasFileSkipDiverged(src, dst);
6210
6435
  }
@@ -6228,20 +6453,20 @@ function divergenceCheckExtras(ts, prePostHeads) {
6228
6453
  const v = loadValidatedExtras({});
6229
6454
  if (v === null) return 0;
6230
6455
  const counts = { unmapped: 0, skipped: 0 };
6231
- const backupRoot = join45(backupBase(), ts, "extras");
6456
+ const backupRoot = join47(backupBase(), ts, "extras");
6232
6457
  const repo = repoHome();
6233
6458
  let divergedCount = 0;
6234
- for (const { logical, localRoot, dirname: dirname10 } of eachExtrasTarget(v, counts)) {
6235
- const local = join45(localRoot, dirname10);
6236
- const repoEntry = join45(repo, "shared", "extras", logical, dirname10);
6237
- if (!existsSync38(local) || !existsSync38(repoEntry)) continue;
6459
+ for (const { logical, localRoot, dirname: dirname11 } of eachExtrasTarget(v, counts)) {
6460
+ const local = join47(localRoot, dirname11);
6461
+ const repoEntry = join47(repo, "shared", "extras", logical, dirname11);
6462
+ if (!existsSync39(local) || !existsSync39(repoEntry)) continue;
6238
6463
  const modified = listDivergingModified(local, repoEntry);
6239
6464
  if (modified.length === 0) continue;
6240
6465
  divergedCount += modified.length;
6241
- const projectBackupRoot = join45(backupRoot, encodePath(localRoot));
6466
+ const projectBackupRoot = join47(backupRoot, encodePath(localRoot));
6242
6467
  warn(
6243
6468
  divergenceWarnLine({
6244
- dirname: dirname10,
6469
+ dirname: dirname11,
6245
6470
  logical,
6246
6471
  isDir: statSync10(local).isDirectory(),
6247
6472
  count: modified.length,
@@ -6260,8 +6485,8 @@ function divergenceCheckExtras(ts, prePostHeads) {
6260
6485
 
6261
6486
  // src/skills-sync.ts
6262
6487
  init_config();
6263
- import { existsSync as existsSync39, lstatSync as lstatSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync16, rmSync as rmSync14 } from "node:fs";
6264
- import { join as join46 } from "node:path";
6488
+ import { existsSync as existsSync40, lstatSync as lstatSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync16, rmSync as rmSync15 } from "node:fs";
6489
+ import { join as join48 } from "node:path";
6265
6490
  init_utils_fs();
6266
6491
  function isGsdOwned(name) {
6267
6492
  return name.startsWith(GSD_PREFIX);
@@ -6281,30 +6506,30 @@ function copySkillsPull(src, dst) {
6281
6506
  copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
6282
6507
  }
6283
6508
  function syncSkillsPull(ts) {
6284
- const sharedSkills = join46(repoHome(), "shared", "skills");
6285
- if (!existsSync39(sharedSkills)) return;
6286
- const localSkills = join46(claudeHome(), "skills");
6509
+ const sharedSkills = join48(repoHome(), "shared", "skills");
6510
+ if (!existsSync40(sharedSkills)) return;
6511
+ const localSkills = join48(claudeHome(), "skills");
6287
6512
  const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
6288
6513
  if (dstStat?.isSymbolicLink() === true) {
6289
6514
  backupBeforeWrite(localSkills, ts);
6290
- rmSync14(localSkills, { recursive: true, force: true });
6291
- mkdirSync9(localSkills, { recursive: true });
6515
+ rmSync15(localSkills, { recursive: true, force: true });
6516
+ mkdirSync11(localSkills, { recursive: true });
6292
6517
  }
6293
6518
  copySkillsPull(sharedSkills, localSkills);
6294
6519
  }
6295
6520
  function syncSkillsPush() {
6296
- const localSkills = join46(claudeHome(), "skills");
6521
+ const localSkills = join48(claudeHome(), "skills");
6297
6522
  const stat = lstatSync12(localSkills, { throwIfNoEntry: false });
6298
6523
  if (stat === void 0) return;
6299
6524
  if (stat.isSymbolicLink()) return;
6300
- const sharedSkills = join46(repoHome(), "shared", "skills");
6525
+ const sharedSkills = join48(repoHome(), "shared", "skills");
6301
6526
  copySkillsPush(localSkills, sharedSkills);
6302
6527
  }
6303
6528
 
6304
6529
  // src/preview.ts
6305
6530
  init_config();
6306
- import { existsSync as existsSync40 } from "node:fs";
6307
- import { join as join47 } from "node:path";
6531
+ import { existsSync as existsSync41 } from "node:fs";
6532
+ import { join as join49 } from "node:path";
6308
6533
 
6309
6534
  // node_modules/diff/libesm/diff/base.js
6310
6535
  var Diff = class {
@@ -6590,7 +6815,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
6590
6815
  return lines.join("\n");
6591
6816
  }
6592
6817
  function readJsonOrNull(path) {
6593
- if (!existsSync40(path)) return null;
6818
+ if (!existsSync41(path)) return null;
6594
6819
  try {
6595
6820
  return readJson(path);
6596
6821
  } catch {
@@ -6604,12 +6829,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
6604
6829
  }
6605
6830
  const notes = [];
6606
6831
  const hostOverrides = readJsonOrNull(hostPath);
6607
- if (hostOverrides === null && existsSync40(hostPath)) {
6832
+ if (hostOverrides === null && existsSync41(hostPath)) {
6608
6833
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
6609
6834
  }
6610
6835
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
6611
6836
  const current = readJsonOrNull(settingsPath);
6612
- if (current === null && existsSync40(settingsPath)) {
6837
+ if (current === null && existsSync41(settingsPath)) {
6613
6838
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
6614
6839
  }
6615
6840
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -6651,9 +6876,9 @@ function computePreview(ts, map, verb = "pull") {
6651
6876
  onPreview: (e) => addItem(links, formatLinkRow(e))
6652
6877
  });
6653
6878
  const settingsResult = previewSettings(
6654
- join47(repo, "shared", "settings.base.json"),
6655
- join47(repo, "hosts", `${HOST}.json`),
6656
- join47(claude, "settings.json")
6879
+ join49(repo, "shared", "settings.base.json"),
6880
+ join49(repo, "hosts", `${HOST}.json`),
6881
+ join49(claude, "settings.json")
6657
6882
  );
6658
6883
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
6659
6884
  const sessions = section("Sessions");
@@ -6668,7 +6893,7 @@ function computePreview(ts, map, verb = "pull") {
6668
6893
  const extras = section("Extras");
6669
6894
  let extrasSkipped = 0;
6670
6895
  let extrasUnmapped = 0;
6671
- if (existsSync40(join47(repo, "path-map.json")) && existsSync40(join47(repo, "shared", "extras"))) {
6896
+ if (existsSync41(join49(repo, "path-map.json")) && existsSync41(join49(repo, "shared", "extras"))) {
6672
6897
  const extrasResult = remapExtrasPull(ts, { dryRun: true });
6673
6898
  for (const entry of extrasResult.wouldPull) {
6674
6899
  addItem(extras, entry);
@@ -6693,9 +6918,9 @@ init_config();
6693
6918
  init_commands_pull_wedge();
6694
6919
  init_utils();
6695
6920
  init_utils_fs();
6696
- import { execFileSync as execFileSync19 } from "node:child_process";
6921
+ import { execFileSync as execFileSync20 } from "node:child_process";
6697
6922
  function gitCapture(args, cwd) {
6698
- return execFileSync19("git", args, {
6923
+ return execFileSync20("git", args, {
6699
6924
  cwd,
6700
6925
  stdio: ["ignore", "pipe", "pipe"],
6701
6926
  maxBuffer: 64 * 1024 * 1024
@@ -6882,9 +7107,9 @@ function runPullCore(opts = {}) {
6882
7107
  const ts = freshBackupTs(backup);
6883
7108
  handleWedge(repo, forceRemote);
6884
7109
  if (!dryRun) {
6885
- const backupRoot = join48(backup, ts);
7110
+ const backupRoot = join50(backup, ts);
6886
7111
  try {
6887
- mkdirSync10(backupRoot, { recursive: true });
7112
+ mkdirSync12(backupRoot, { recursive: true });
6888
7113
  } catch (err) {
6889
7114
  die(`could not create backup dir: ${err.message}`);
6890
7115
  }
@@ -6897,8 +7122,8 @@ function runPullCore(opts = {}) {
6897
7122
  const prePostHeads = capturePrePostHeads(repo, () => {
6898
7123
  gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
6899
7124
  });
6900
- const mapPath = join48(repo, "path-map.json");
6901
- const map = existsSync41(mapPath) ? readPathMap(mapPath) : { projects: {} };
7125
+ const mapPath = join50(repo, "path-map.json");
7126
+ const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
6902
7127
  const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
6903
7128
  if (dryRun) {
6904
7129
  computePreview(ts, map, "pull");
@@ -6924,8 +7149,8 @@ function runPullCore(opts = {}) {
6924
7149
  }
6925
7150
  function cmdPull(opts = {}) {
6926
7151
  const repo = repoHome();
6927
- if (!existsSync41(repo)) die(`repo not cloned at ${repo}`);
6928
- if (!existsSync41(join48(repo, "shared", "settings.base.json"))) {
7152
+ if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
7153
+ if (!existsSync42(join50(repo, "shared", "settings.base.json"))) {
6929
7154
  die("repo not initialized; run 'nomad init' to scaffold");
6930
7155
  }
6931
7156
  const handle = acquireLock("pull");
@@ -6947,13 +7172,13 @@ function cmdPull(opts = {}) {
6947
7172
 
6948
7173
  // src/commands.push.ts
6949
7174
  init_config();
6950
- import { existsSync as existsSync45 } from "node:fs";
6951
- import { join as join53 } from "node:path";
7175
+ import { existsSync as existsSync46 } from "node:fs";
7176
+ import { join as join55 } from "node:path";
6952
7177
 
6953
7178
  // src/commands.push.selection.ts
6954
7179
  init_config();
6955
- import { existsSync as existsSync42, statSync as statSync11 } from "node:fs";
6956
- import { join as join49 } from "node:path";
7180
+ import { existsSync as existsSync43, statSync as statSync11 } from "node:fs";
7181
+ import { join as join51 } from "node:path";
6957
7182
  init_utils_json();
6958
7183
  function buildCurrentMap(map) {
6959
7184
  const current = {};
@@ -6962,8 +7187,8 @@ function buildCurrentMap(map) {
6962
7187
  for (const [, hostMap] of Object.entries(map.projects)) {
6963
7188
  const localPath = hostMap[HOST];
6964
7189
  if (!localPath) continue;
6965
- const localDir = join49(claude, "projects", encodePath(localPath));
6966
- if (!existsSync42(localDir)) continue;
7190
+ const localDir = join51(claude, "projects", encodePath(localPath));
7191
+ if (!existsSync43(localDir)) continue;
6967
7192
  for (const f of enumerateSourceFiles(localDir)) {
6968
7193
  const st = statSync11(f);
6969
7194
  current[f] = { size: st.size, mtime: st.mtimeMs };
@@ -6994,7 +7219,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
6994
7219
  };
6995
7220
  }
6996
7221
  function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
6997
- const map = existsSync42(mapPath) ? readPathMap(mapPath) : null;
7222
+ const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
6998
7223
  const { selection, newManifest } = computePushSelection(
6999
7224
  map,
7000
7225
  old,
@@ -7096,14 +7321,14 @@ function enforceAllowList(statusPorcelain, map) {
7096
7321
 
7097
7322
  // src/commands.push.settings.ts
7098
7323
  init_config();
7099
- import { existsSync as existsSync43 } from "node:fs";
7100
- import { join as join50 } from "node:path";
7324
+ import { existsSync as existsSync44 } from "node:fs";
7325
+ import { join as join52 } from "node:path";
7101
7326
  init_utils();
7102
7327
  init_utils_fs();
7103
7328
  init_utils_json();
7104
7329
  function stripGsdHooksFromBase(repo, backup) {
7105
- const basePath = join50(repo, "shared", "settings.base.json");
7106
- if (!existsSync43(basePath)) return;
7330
+ const basePath = join52(repo, "shared", "settings.base.json");
7331
+ if (!existsSync44(basePath)) return;
7107
7332
  let base;
7108
7333
  try {
7109
7334
  base = readJson(basePath);
@@ -7117,14 +7342,14 @@ function stripGsdHooksFromBase(repo, backup) {
7117
7342
  writeJsonAtomic(basePath, stripped);
7118
7343
  }
7119
7344
  function reportSettingsAheadDrift(repo) {
7120
- const basePath = join50(repo, "shared", "settings.base.json");
7121
- if (!existsSync43(basePath)) return;
7122
- const settingsPath = join50(claudeHome(), "settings.json");
7123
- if (!existsSync43(settingsPath)) return;
7345
+ const basePath = join52(repo, "shared", "settings.base.json");
7346
+ if (!existsSync44(basePath)) return;
7347
+ const settingsPath = join52(claudeHome(), "settings.json");
7348
+ if (!existsSync44(settingsPath)) return;
7124
7349
  try {
7125
7350
  const base = readJson(basePath);
7126
- const hostPath = join50(repo, "hosts", `${HOST}.json`);
7127
- const overrides = existsSync43(hostPath) ? readJson(hostPath) : {};
7351
+ const hostPath = join52(repo, "hosts", `${HOST}.json`);
7352
+ const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
7128
7353
  const merged = deepMerge(base, overrides);
7129
7354
  const settings = readJson(settingsPath);
7130
7355
  const { ahead } = classifySettingsDrift(merged, settings);
@@ -7141,9 +7366,9 @@ function reportSettingsAheadDrift(repo) {
7141
7366
  // src/commands.push.guards.ts
7142
7367
  init_push_checks();
7143
7368
  init_utils();
7144
- import { join as join51, relative as relative7 } from "node:path";
7369
+ import { join as join53, relative as relative7 } from "node:path";
7145
7370
  function guardGitlinks(repo) {
7146
- const gitlinks = findGitlinks(join51(repo, "shared"));
7371
+ const gitlinks = findGitlinks(join53(repo, "shared"));
7147
7372
  if (gitlinks.length === 0) return;
7148
7373
  for (const p of gitlinks) {
7149
7374
  const rel = relative7(repo, p).replaceAll("\\", "/");
@@ -7175,7 +7400,7 @@ init_config();
7175
7400
 
7176
7401
  // src/push-global-config.ts
7177
7402
  init_config();
7178
- import { execFileSync as execFileSync20 } from "node:child_process";
7403
+ import { execFileSync as execFileSync21 } from "node:child_process";
7179
7404
  var STATUS_LABELS = {
7180
7405
  A: "add",
7181
7406
  M: "modify",
@@ -7215,7 +7440,7 @@ function isInScope(filePath, exactPrefixes, dirPrefixes) {
7215
7440
  }
7216
7441
  function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
7217
7442
  const args = opts.staged ? ["diff", "--cached", "--name-status", "-z"] : ["diff", "HEAD", "--name-status", "-z"];
7218
- const raw = execFileSync20("git", args, {
7443
+ const raw = execFileSync21("git", args, {
7219
7444
  cwd: repoHome2,
7220
7445
  stdio: ["ignore", "pipe", "pipe"]
7221
7446
  }).toString();
@@ -7252,10 +7477,10 @@ init_push_leak_verdict();
7252
7477
  init_color();
7253
7478
  init_config();
7254
7479
  init_config_sharedDirs_guard();
7255
- import { randomBytes as randomBytes2 } from "node:crypto";
7256
- import { copyFileSync, existsSync as existsSync44, mkdirSync as mkdirSync11, readdirSync as readdirSync17, rmSync as rmSync15 } from "node:fs";
7257
- import { homedir as homedir5 } from "node:os";
7258
- import { join as join52, relative as relative8, sep as sep9 } from "node:path";
7480
+ import { randomBytes as randomBytes3 } from "node:crypto";
7481
+ import { copyFileSync, existsSync as existsSync45, mkdirSync as mkdirSync13, readdirSync as readdirSync17, rmSync as rmSync16 } from "node:fs";
7482
+ import { homedir as homedir6 } from "node:os";
7483
+ import { join as join54, relative as relative8, sep as sep9 } from "node:path";
7259
7484
  init_push_leak_verdict();
7260
7485
  init_push_gitleaks();
7261
7486
  init_utils_fs();
@@ -7267,7 +7492,7 @@ function stageSessionDir(localDir, dstDir, changed) {
7267
7492
  const matching = [...changed].filter((p) => p.startsWith(prefix));
7268
7493
  if (matching.length === 0) return false;
7269
7494
  for (const src of matching) {
7270
- copyFileAtomic(src, join52(dstDir, relative8(localDir, src)));
7495
+ copyFileAtomic(src, join54(dstDir, relative8(localDir, src)));
7271
7496
  }
7272
7497
  return true;
7273
7498
  }
@@ -7283,14 +7508,14 @@ function stageSessions(tmpRoot, map, changed) {
7283
7508
  if (!p || p === "TBD") continue;
7284
7509
  reverse.set(encodePath(p), logical);
7285
7510
  }
7286
- const localProjects = join52(claudeHome(), "projects");
7287
- if (!existsSync44(localProjects)) return 0;
7511
+ const localProjects = join54(claudeHome(), "projects");
7512
+ if (!existsSync45(localProjects)) return 0;
7288
7513
  let staged = 0;
7289
7514
  for (const dir of readdirSync17(localProjects)) {
7290
7515
  const logical = reverse.get(dir);
7291
7516
  if (!logical) continue;
7292
- const localDir = join52(localProjects, dir);
7293
- const dstDir = join52(tmpRoot, "shared", "projects", logical);
7517
+ const localDir = join54(localProjects, dir);
7518
+ const dstDir = join54(tmpRoot, "shared", "projects", logical);
7294
7519
  if (stageSessionDir(localDir, dstDir, changed)) staged++;
7295
7520
  }
7296
7521
  return staged;
@@ -7304,11 +7529,11 @@ function stageExtras(tmpRoot, map) {
7304
7529
  assertSafeLogical(logical);
7305
7530
  const localRoot = map.projects[logical]?.[HOST];
7306
7531
  if (!localRoot || localRoot === "TBD") continue;
7307
- for (const dirname10 of dirnames) {
7308
- if (!whitelist.includes(dirname10)) continue;
7309
- const src = join52(localRoot, dirname10);
7310
- if (!existsSync44(src)) continue;
7311
- const dst = join52(tmpRoot, "shared", "extras", logical, dirname10);
7532
+ for (const dirname11 of dirnames) {
7533
+ if (!whitelist.includes(dirname11)) continue;
7534
+ const src = join54(localRoot, dirname11);
7535
+ if (!existsSync45(src)) continue;
7536
+ const dst = join54(tmpRoot, "shared", "extras", logical, dirname11);
7312
7537
  copyExtras(src, dst);
7313
7538
  staged++;
7314
7539
  }
@@ -7316,19 +7541,19 @@ function stageExtras(tmpRoot, map) {
7316
7541
  return staged;
7317
7542
  }
7318
7543
  function previewPushLeaks(map, opts = {}) {
7319
- const cacheDir = join52(homedir5(), ".cache", "claude-nomad");
7320
- mkdirSync11(cacheDir, { recursive: true });
7321
- const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
7322
- const tmpRoot = join52(cacheDir, `push-preview-tree-${stamp}`);
7544
+ const cacheDir = join54(homedir6(), ".cache", "claude-nomad");
7545
+ mkdirSync13(cacheDir, { recursive: true });
7546
+ const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
7547
+ const tmpRoot = join54(cacheDir, `push-preview-tree-${stamp}`);
7323
7548
  try {
7324
7549
  const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
7325
7550
  const extrasCount = stageExtras(tmpRoot, map);
7326
7551
  if (sessionCount + extrasCount === 0) {
7327
7552
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
7328
7553
  }
7329
- const ignoreFile = join52(repoHome(), ".gitleaksignore");
7330
- if (existsSync44(ignoreFile)) {
7331
- copyFileSync(ignoreFile, join52(tmpRoot, ".gitleaksignore"));
7554
+ const ignoreFile = join54(repoHome(), ".gitleaksignore");
7555
+ if (existsSync45(ignoreFile)) {
7556
+ copyFileSync(ignoreFile, join54(tmpRoot, ".gitleaksignore"));
7332
7557
  }
7333
7558
  let findings;
7334
7559
  try {
@@ -7341,7 +7566,7 @@ function previewPushLeaks(map, opts = {}) {
7341
7566
  }
7342
7567
  return verdictFromFindings(findings);
7343
7568
  } finally {
7344
- rmSync15(tmpRoot, { recursive: true, force: true });
7569
+ rmSync16(tmpRoot, { recursive: true, force: true });
7345
7570
  }
7346
7571
  }
7347
7572
 
@@ -7454,7 +7679,7 @@ async function runPushCore(opts = {}) {
7454
7679
  const scannerVersion = probeGitleaks();
7455
7680
  const configHash = computeConfigHash();
7456
7681
  const old = readManifest(manifestPath());
7457
- const mapPath = join53(repo, "path-map.json");
7682
+ const mapPath = join55(repo, "path-map.json");
7458
7683
  const { map, selection, newManifest } = loadSelectionForPush(
7459
7684
  mapPath,
7460
7685
  old,
@@ -7505,7 +7730,7 @@ async function cmdPush(opts = {}) {
7505
7730
  const allowRule = opts.allowRule;
7506
7731
  guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
7507
7732
  const repo = repoHome();
7508
- if (!existsSync45(repo)) die(`repo not cloned at ${repo}`);
7733
+ if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
7509
7734
  const handle = acquireLock("push");
7510
7735
  if (handle === null) process.exit(0);
7511
7736
  try {
@@ -7523,8 +7748,8 @@ async function cmdPush(opts = {}) {
7523
7748
  }
7524
7749
 
7525
7750
  // src/commands.sync.ts
7526
- import { existsSync as existsSync46 } from "node:fs";
7527
- import { join as join54 } from "node:path";
7751
+ import { existsSync as existsSync47 } from "node:fs";
7752
+ import { join as join56 } from "node:path";
7528
7753
  init_config();
7529
7754
  init_color();
7530
7755
  init_utils();
@@ -7652,8 +7877,8 @@ async function runSyncWet(verbose) {
7652
7877
  }
7653
7878
  async function runSyncDryRun(repo, backup) {
7654
7879
  const ts = freshBackupTs(backup);
7655
- const mapPath = join54(repo, "path-map.json");
7656
- const map = existsSync46(mapPath) ? readPathMap(mapPath) : { projects: {} };
7880
+ const mapPath = join56(repo, "path-map.json");
7881
+ const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
7657
7882
  computePreview(ts, map, "pull");
7658
7883
  log("push preview below is computed against pre-pull state (a real sync pushes after pull)");
7659
7884
  await runPushCore({ dryRun: true });
@@ -7663,8 +7888,8 @@ async function cmdSync(opts = {}) {
7663
7888
  const verbose = opts.verbose === true;
7664
7889
  const repo = repoHome();
7665
7890
  const backup = backupBase();
7666
- if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
7667
- if (!existsSync46(join54(repo, "shared", "settings.base.json"))) {
7891
+ if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
7892
+ if (!existsSync47(join56(repo, "shared", "settings.base.json"))) {
7668
7893
  die("repo not initialized; run 'nomad init' to scaffold");
7669
7894
  }
7670
7895
  const handle = acquireLock("sync");
@@ -7688,9 +7913,9 @@ async function cmdSync(opts = {}) {
7688
7913
  }
7689
7914
 
7690
7915
  // src/commands.update.ts
7691
- import { execFileSync as execFileSync21 } from "node:child_process";
7916
+ import { execFileSync as execFileSync22 } from "node:child_process";
7692
7917
  init_utils();
7693
- function readInstalledVersion(run = execFileSync21) {
7918
+ function readInstalledVersion(run = execFileSync22) {
7694
7919
  const isWin = process.platform === "win32";
7695
7920
  try {
7696
7921
  return run(isWin ? "nomad.cmd" : "nomad", ["--version"], {
@@ -7701,7 +7926,7 @@ function readInstalledVersion(run = execFileSync21) {
7701
7926
  return null;
7702
7927
  }
7703
7928
  }
7704
- function cmdUpdate(currentVersion, run = execFileSync21) {
7929
+ function cmdUpdate(currentVersion, run = execFileSync22) {
7705
7930
  console.log(`Updating claude-nomad v${currentVersion}...`);
7706
7931
  const isWin = process.platform === "win32";
7707
7932
  try {
@@ -7737,18 +7962,18 @@ init_config();
7737
7962
 
7738
7963
  // src/diff.ts
7739
7964
  init_config();
7740
- import { existsSync as existsSync47 } from "node:fs";
7741
- import { join as join55 } from "node:path";
7965
+ import { existsSync as existsSync48 } from "node:fs";
7966
+ import { join as join57 } from "node:path";
7742
7967
  init_utils();
7743
7968
  init_utils_fs();
7744
7969
  init_utils_json();
7745
7970
  function cmdDiff() {
7746
7971
  try {
7747
7972
  const repo = repoHome();
7748
- if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
7973
+ if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
7749
7974
  const ts = freshBackupTs(backupBase());
7750
- const mapPath = join55(repo, "path-map.json");
7751
- const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
7975
+ const mapPath = join57(repo, "path-map.json");
7976
+ const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
7752
7977
  divergenceCheckExtras(ts);
7753
7978
  computePreview(ts, map, "diff");
7754
7979
  } catch (err) {
@@ -7763,19 +7988,19 @@ function cmdDiff() {
7763
7988
 
7764
7989
  // src/init.ts
7765
7990
  init_config();
7766
- import { existsSync as existsSync49, mkdirSync as mkdirSync12, writeFileSync as writeFileSync6 } from "node:fs";
7767
- import { join as join57 } from "node:path";
7991
+ import { existsSync as existsSync50, mkdirSync as mkdirSync14, writeFileSync as writeFileSync8 } from "node:fs";
7992
+ import { join as join59 } from "node:path";
7768
7993
 
7769
7994
  // src/init.gh-onboard.ts
7770
7995
  init_config();
7771
- import { execFileSync as execFileSync22 } from "node:child_process";
7996
+ import { execFileSync as execFileSync23 } from "node:child_process";
7772
7997
  init_utils();
7773
7998
  var DEFAULT_REPO_NAME = "claude-nomad-config";
7774
7999
  function isValidRepoName(name) {
7775
8000
  return /^[A-Za-z0-9._-]{1,100}$/.test(name);
7776
8001
  }
7777
8002
  var GH_NETWORK_TIMEOUT_MS = 3e4;
7778
- function ensureOriginRepo(repoName, run = execFileSync22) {
8003
+ function ensureOriginRepo(repoName, run = execFileSync23) {
7779
8004
  if (!isValidRepoName(repoName)) {
7780
8005
  die(
7781
8006
  `invalid repo name: ${JSON.stringify(repoName)}. Use only letters, digits, hyphens, underscores, and dots (1-100 chars).`
@@ -7846,33 +8071,33 @@ init_config();
7846
8071
  init_utils();
7847
8072
  init_utils_fs();
7848
8073
  init_utils_json();
7849
- import { copyFileSync as copyFileSync2, cpSync as cpSync8, existsSync as existsSync48, rmSync as rmSync16, statSync as statSync12 } from "node:fs";
7850
- import { join as join56 } from "node:path";
8074
+ import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync49, rmSync as rmSync17, statSync as statSync12 } from "node:fs";
8075
+ import { join as join58 } from "node:path";
7851
8076
  function snapshotIntoShared(map) {
7852
8077
  const repo = repoHome();
7853
8078
  const claude = claudeHome();
7854
8079
  for (const name of allSharedLinks(map)) {
7855
- const src = join56(claude, name);
7856
- if (!existsSync48(src)) continue;
7857
- const dst = join56(repo, "shared", name);
8080
+ const src = join58(claude, name);
8081
+ if (!existsSync49(src)) continue;
8082
+ const dst = join58(repo, "shared", name);
7858
8083
  if (statSync12(src).isDirectory()) {
7859
- const gk = join56(dst, ".gitkeep");
7860
- if (existsSync48(gk)) rmSync16(gk);
7861
- cpSync8(src, dst, { recursive: true, force: false, errorOnExist: true });
8084
+ const gk = join58(dst, ".gitkeep");
8085
+ if (existsSync49(gk)) rmSync17(gk);
8086
+ cpSync9(src, dst, { recursive: true, force: false, errorOnExist: true });
7862
8087
  } else {
7863
8088
  copyFileSync2(src, dst);
7864
8089
  }
7865
8090
  log(`snapshotted shared/${name} from ${src}`);
7866
8091
  }
7867
- const userSettings = join56(claude, "settings.json");
7868
- if (existsSync48(userSettings)) {
8092
+ const userSettings = join58(claude, "settings.json");
8093
+ if (existsSync49(userSettings)) {
7869
8094
  let parsed;
7870
8095
  try {
7871
8096
  parsed = readJson(userSettings);
7872
8097
  } catch (err) {
7873
8098
  return die(`malformed ${userSettings}: ${err.message}`);
7874
8099
  }
7875
- const hostFile = join56(repo, "hosts", `${HOST}.json`);
8100
+ const hostFile = join58(repo, "hosts", `${HOST}.json`);
7876
8101
  writeJsonAtomic(hostFile, parsed);
7877
8102
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
7878
8103
  }
@@ -7886,14 +8111,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
7886
8111
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
7887
8112
  function preflightConflict(repoHome2) {
7888
8113
  const candidates = [
7889
- join57(repoHome2, "shared", "settings.base.json"),
7890
- join57(repoHome2, "shared", "CLAUDE.md"),
7891
- join57(repoHome2, "path-map.json"),
7892
- join57(repoHome2, "hosts"),
7893
- join57(repoHome2, "shared")
8114
+ join59(repoHome2, "shared", "settings.base.json"),
8115
+ join59(repoHome2, "shared", "CLAUDE.md"),
8116
+ join59(repoHome2, "path-map.json"),
8117
+ join59(repoHome2, "hosts"),
8118
+ join59(repoHome2, "shared")
7894
8119
  ];
7895
8120
  for (const c of candidates) {
7896
- if (existsSync49(c)) return c;
8121
+ if (existsSync50(c)) return c;
7897
8122
  }
7898
8123
  return null;
7899
8124
  }
@@ -7905,33 +8130,33 @@ function cmdInit(opts = {}) {
7905
8130
  const keepActions = opts.keepActions === true;
7906
8131
  const repo = repoHome();
7907
8132
  const claude = claudeHome();
7908
- mkdirSync12(repo, { recursive: true });
8133
+ mkdirSync14(repo, { recursive: true });
7909
8134
  const conflict = preflightConflict(repo);
7910
8135
  if (conflict !== null) {
7911
8136
  die(`already initialized; refusing to clobber ${conflict}`);
7912
8137
  }
7913
8138
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
7914
- mkdirSync12(join57(repo, "shared"), { recursive: true });
7915
- mkdirSync12(join57(repo, "hosts"), { recursive: true });
8139
+ mkdirSync14(join59(repo, "shared"), { recursive: true });
8140
+ mkdirSync14(join59(repo, "hosts"), { recursive: true });
7916
8141
  for (const name of SHARED_KEEP_DIRS) {
7917
- mkdirSync12(join57(repo, "shared", name), { recursive: true });
8142
+ mkdirSync14(join59(repo, "shared", name), { recursive: true });
7918
8143
  }
7919
- const userClaudeMd = join57(claude, "CLAUDE.md");
7920
- if (!snapshot || !existsSync49(userClaudeMd)) {
7921
- writeFileSync6(join57(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
8144
+ const userClaudeMd = join59(claude, "CLAUDE.md");
8145
+ if (!snapshot || !existsSync50(userClaudeMd)) {
8146
+ writeFileSync8(join59(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
7922
8147
  item("created shared/CLAUDE.md");
7923
8148
  }
7924
8149
  for (const name of SHARED_KEEP_DIRS) {
7925
- writeFileSync6(join57(repo, "shared", name, ".gitkeep"), "");
8150
+ writeFileSync8(join59(repo, "shared", name, ".gitkeep"), "");
7926
8151
  item(`created shared/${name}/.gitkeep`);
7927
8152
  }
7928
- writeFileSync6(join57(repo, "hosts", ".gitkeep"), "");
8153
+ writeFileSync8(join59(repo, "hosts", ".gitkeep"), "");
7929
8154
  item("created hosts/.gitkeep");
7930
- writeJsonAtomic(join57(repo, "shared", "settings.base.json"), {});
8155
+ writeJsonAtomic(join59(repo, "shared", "settings.base.json"), {});
7931
8156
  item("created shared/settings.base.json");
7932
- writeJsonAtomic(join57(repo, "path-map.json"), { projects: {} });
8157
+ writeJsonAtomic(join59(repo, "path-map.json"), { projects: {} });
7933
8158
  item("created path-map.json");
7934
- writeFileSync6(join57(repo, ".gitattributes"), GITATTRIBUTES);
8159
+ writeFileSync8(join59(repo, ".gitattributes"), GITATTRIBUTES);
7935
8160
  item("created .gitattributes");
7936
8161
  if (snapshot) {
7937
8162
  snapshotIntoShared({ projects: {} });
@@ -7997,11 +8222,11 @@ function maybeDisableRepoActions(repoHome2, run) {
7997
8222
  // src/init.prompt.ts
7998
8223
  init_config();
7999
8224
  init_utils();
8000
- import { existsSync as existsSync50, readdirSync as readdirSync18, statSync as statSync13 } from "node:fs";
8001
- import { join as join58 } from "node:path";
8225
+ import { existsSync as existsSync51, readdirSync as readdirSync18, statSync as statSync13 } from "node:fs";
8226
+ import { join as join60 } from "node:path";
8002
8227
  import { createInterface as createInterface3 } from "node:readline/promises";
8003
8228
  function nonEmptyExists(path) {
8004
- if (!existsSync50(path)) return false;
8229
+ if (!existsSync51(path)) return false;
8005
8230
  try {
8006
8231
  if (statSync13(path).isDirectory()) return readdirSync18(path).length > 0;
8007
8232
  return true;
@@ -8010,8 +8235,8 @@ function nonEmptyExists(path) {
8010
8235
  }
8011
8236
  }
8012
8237
  function hasExistingClaudeConfig(claudeHome2) {
8013
- if (existsSync50(join58(claudeHome2, "settings.json"))) return true;
8014
- return SHARED_LINKS.some((name) => nonEmptyExists(join58(claudeHome2, name)));
8238
+ if (existsSync51(join60(claudeHome2, "settings.json"))) return true;
8239
+ return SHARED_LINKS.some((name) => nonEmptyExists(join60(claudeHome2, name)));
8015
8240
  }
8016
8241
  async function confirmSnapshotDefault(claudeHome2) {
8017
8242
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -8318,7 +8543,7 @@ function parseSyncArgs(argv) {
8318
8543
  // package.json
8319
8544
  var package_default = {
8320
8545
  name: "claude-nomad",
8321
- version: "0.60.0",
8546
+ version: "0.61.0",
8322
8547
  type: "module",
8323
8548
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
8324
8549
  keywords: [
@@ -8559,15 +8784,15 @@ var DEFAULT_HELP = [
8559
8784
  init_config();
8560
8785
  init_utils();
8561
8786
  init_utils_json();
8562
- import { existsSync as existsSync51, readFileSync as readFileSync18, readdirSync as readdirSync19 } from "node:fs";
8563
- import { join as join59 } from "node:path";
8787
+ import { existsSync as existsSync52, readFileSync as readFileSync19, readdirSync as readdirSync19 } from "node:fs";
8788
+ import { join as join61 } from "node:path";
8564
8789
  function resumeCmd(sessionId) {
8565
8790
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
8566
8791
  fail(`invalid session id: ${sessionId}`);
8567
8792
  process.exit(1);
8568
8793
  }
8569
- const projectsRoot = join59(claudeHome(), "projects");
8570
- if (!existsSync51(projectsRoot)) {
8794
+ const projectsRoot = join61(claudeHome(), "projects");
8795
+ if (!existsSync52(projectsRoot)) {
8571
8796
  fail(`${projectsRoot} does not exist`);
8572
8797
  process.exit(1);
8573
8798
  }
@@ -8581,8 +8806,8 @@ function resumeCmd(sessionId) {
8581
8806
  fail(`no cwd field found in ${jsonlPath}`);
8582
8807
  process.exit(1);
8583
8808
  }
8584
- const mapPath = join59(repoHome(), "path-map.json");
8585
- if (!existsSync51(mapPath)) {
8809
+ const mapPath = join61(repoHome(), "path-map.json");
8810
+ if (!existsSync52(mapPath)) {
8586
8811
  fail("path-map.json missing");
8587
8812
  process.exit(1);
8588
8813
  }
@@ -8605,13 +8830,13 @@ function resumeCmd(sessionId) {
8605
8830
  }
8606
8831
  function findTranscriptPath(projectsRoot, sessionId) {
8607
8832
  for (const dir of readdirSync19(projectsRoot)) {
8608
- const candidate = join59(projectsRoot, dir, `${sessionId}.jsonl`);
8609
- if (existsSync51(candidate)) return candidate;
8833
+ const candidate = join61(projectsRoot, dir, `${sessionId}.jsonl`);
8834
+ if (existsSync52(candidate)) return candidate;
8610
8835
  }
8611
8836
  return null;
8612
8837
  }
8613
8838
  function extractRecordedCwd(jsonlPath) {
8614
- for (const line of readFileSync18(jsonlPath, "utf8").split("\n")) {
8839
+ for (const line of readFileSync19(jsonlPath, "utf8").split("\n")) {
8615
8840
  if (!line.trim()) continue;
8616
8841
  try {
8617
8842
  const obj = JSON.parse(line);