claude-nomad 0.62.2 → 0.62.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.62.3](https://github.com/funkadelic/claude-nomad/compare/v0.62.2...v0.62.3) (2026-07-21)
4
+
5
+
6
+ ### Fixed
7
+
8
+ * abort when an autostash pop conflicts during rebase ([#448](https://github.com/funkadelic/claude-nomad/issues/448)) ([a22b3df](https://github.com/funkadelic/claude-nomad/commit/a22b3dfdc95478ba09a6cf6bf9994c9d8d9ac416))
9
+ * **pull:** stop --force-remote applying conflict-markered config ([#452](https://github.com/funkadelic/claude-nomad/issues/452)) ([5612ac9](https://github.com/funkadelic/claude-nomad/commit/5612ac9c25e25118a73aaa465d2ccd9b16604e8a))
10
+ * retain never-pushed local skills on pull ([#449](https://github.com/funkadelic/claude-nomad/issues/449)) ([0faf4f7](https://github.com/funkadelic/claude-nomad/commit/0faf4f726d2982ba9e4b6da9be58b17b838bcd37))
11
+
3
12
  ## [0.62.2](https://github.com/funkadelic/claude-nomad/compare/v0.62.1...v0.62.2) (2026-07-21)
4
13
 
5
14
 
package/README.md CHANGED
@@ -253,9 +253,10 @@ operation, parks stranded commits on a `nomad/stranded-<ts>` branch, resets to `
253
253
  re-pulls; refuses if stranded or dirty tracked changes touch synced config), and a repo where the
254
254
  rebase was torn down but the git index still has unmerged entries with no active rebase or merge in
255
255
  progress (clears the stuck index via `git reset --mixed HEAD`, preserving working-tree edits,
256
- surfaces any orphaned autostash entry, then re-pulls). Run `nomad doctor` first if you are unsure
257
- which state you are in; the Repository section names the specific problem and points at the right
258
- fix.
256
+ surfaces any orphaned autostash entry, then re-pulls; stops after the index repair if any conflicted
257
+ file still carries conflict markers, so they are never copied into your live config). Run
258
+ `nomad doctor` first if you are unsure which state you are in; the Repository section names the
259
+ specific problem and points at the right fix.
259
260
 
260
261
  If an external tool (such as Claude Code or GSD) wrote new keys into your `~/.claude/settings.json`
261
262
  that are not yet in your shared repo, run `nomad capture-settings` to promote them before the next
package/dist/nomad.mjs CHANGED
@@ -779,7 +779,7 @@ function classifyWedge(repo) {
779
779
  return unmergedIndexPresent(repo) ? "unmerged-index" : null;
780
780
  }
781
781
  function unmergedIndexRunbookText(resumeCmd2) {
782
- return `repo has an unmerged index with no active rebase or merge in progress (torn-down rebase left stage-2/3 entries behind).
782
+ return `repo has an unmerged index with no active rebase or merge in progress (torn-down rebase or merge left stage-2/3 entries behind).
783
783
 
784
784
  Manual recovery:
785
785
  1. git reset --mixed HEAD (clears the stuck index; preserves working-tree files)
@@ -915,6 +915,43 @@ var init_push_gitleaks_config = __esm({
915
915
  }
916
916
  });
917
917
 
918
+ // src/autostash-guard.ts
919
+ function autostashConflictRunbookText(resumeCmd2, stashRetained) {
920
+ const head = `${resumeCmd2} reported success, but the autostash pop it ran internally conflicted. The working tree now holds conflict markers and the index is unmerged. Nothing has been copied to ~/.claude/ yet, and there is no rebase or merge in progress to abort.`;
921
+ if (stashRetained) {
922
+ return `${head}
923
+
924
+ Recovery (the pre-conflict content is retained in the stash; nothing is lost):
925
+ 1. git stash list (confirm stash@{0}: autostash is present)
926
+ 2. git show 'stash@{0}:<path>' (view the pre-conflict content; does not re-trigger the conflict)
927
+ 3. git reset --hard HEAD (discard the markered working tree)
928
+ 4. re-apply the content from step 2 into <path>, as needed
929
+ 5. git stash drop (once you are done with the stash entry)
930
+ 6. ${resumeCmd2}`;
931
+ }
932
+ return `${head}
933
+
934
+ No autostash entry was found to recover from, so the working tree is the only copy of this edit. Resolve the conflict markers by hand in the affected file(s), then:
935
+ 1. git add <path> (mark resolved)
936
+ 2. ${resumeCmd2}
937
+
938
+ Do not run "git reset --hard": with no stash entry to fall back on, that would discard your only copy of the edit.`;
939
+ }
940
+ function assertNoAutostashConflict(repo, resumeCmd2) {
941
+ if (!unmergedIndexPresent(repo)) return;
942
+ throw new NomadFatal(autostashConflictRunbookText(resumeCmd2, orphanedAutostashPresent(repo)), {
943
+ code: EXIT.CONFLICT
944
+ });
945
+ }
946
+ var init_autostash_guard = __esm({
947
+ "src/autostash-guard.ts"() {
948
+ "use strict";
949
+ init_commands_pull_wedge();
950
+ init_exit_codes();
951
+ init_utils();
952
+ }
953
+ });
954
+
918
955
  // src/push-checks.ts
919
956
  import { execFileSync as execFileSync4 } from "node:child_process";
920
957
  import { readdirSync as readdirSync7, rmSync as rmSync7 } from "node:fs";
@@ -1014,11 +1051,13 @@ function rebaseBeforePush(repo) {
1014
1051
  `rebase failed; if a conflict was reported, resolve it in ${repo} and run "git rebase --continue" (or "git rebase --abort" to give up). Re-run nomad push after resolution.`
1015
1052
  );
1016
1053
  }
1054
+ assertNoAutostashConflict(repo, "nomad push");
1017
1055
  }
1018
1056
  var init_push_checks = __esm({
1019
1057
  "src/push-checks.ts"() {
1020
1058
  "use strict";
1021
1059
  init_push_gitleaks_config();
1060
+ init_autostash_guard();
1022
1061
  init_commands_pull_wedge();
1023
1062
  init_exit_codes();
1024
1063
  init_utils();
@@ -1644,9 +1683,9 @@ function cpSyncGuarded(src, dst, filter, label) {
1644
1683
  throw err;
1645
1684
  }
1646
1685
  }
1647
- function prunePreservingBy(src, dst, isPreserved) {
1686
+ function prunePreservingBy(src, dst, isPreserved, isRootPreserved) {
1648
1687
  for (const name of readdirSync(dst)) {
1649
- if (isPreserved(name)) continue;
1688
+ if (isPreserved(name) || isRootPreserved?.(name) === true) continue;
1650
1689
  const dstPath = join2(dst, name);
1651
1690
  const srcStat = lstatSync(join2(src, name), { throwIfNoEntry: false });
1652
1691
  if (srcStat === void 0) {
@@ -1661,7 +1700,7 @@ function prunePreservingBy(src, dst, isPreserved) {
1661
1700
  }
1662
1701
  }
1663
1702
  }
1664
- function copyExtrasFilteredPreservingBy(src, dst, isPreserved) {
1703
+ function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved) {
1665
1704
  const dstStat = lstatSync(dst, { throwIfNoEntry: false });
1666
1705
  if (dstStat !== void 0) {
1667
1706
  if (dstStat.isDirectory()) {
@@ -1671,7 +1710,7 @@ function copyExtrasFilteredPreservingBy(src, dst, isPreserved) {
1671
1710
  `copyExtrasFilteredPreservingBy: type mismatch copying ${quoted(src)} -> ${quoted(dst)}: the repo entry is a file but the local entry is a directory; run nomad pull --force-remote to recover`
1672
1711
  );
1673
1712
  }
1674
- prunePreservingBy(src, dst, isPreserved);
1713
+ prunePreservingBy(src, dst, isPreserved, isRootPreserved);
1675
1714
  } else {
1676
1715
  rmSync(dst, { recursive: true, force: true });
1677
1716
  }
@@ -5364,6 +5403,21 @@ import { dirname as dirname11, join as join42, sep as sep6 } from "node:path";
5364
5403
  init_config();
5365
5404
  import { existsSync as existsSync33, lstatSync as lstatSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync14, rmSync as rmSync14 } from "node:fs";
5366
5405
  import { join as join41 } from "node:path";
5406
+
5407
+ // src/skills-sync.tracked.ts
5408
+ init_utils();
5409
+ import { basename as basename3 } from "node:path";
5410
+ function trackedRootSkillsAt(ref, repo) {
5411
+ try {
5412
+ const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
5413
+ const entries = raw.split("\0").filter((entry) => entry !== "");
5414
+ return new Set(entries.map((entry) => basename3(entry)));
5415
+ } catch {
5416
+ return /* @__PURE__ */ new Set();
5417
+ }
5418
+ }
5419
+
5420
+ // src/skills-sync.ts
5367
5421
  init_utils_fs();
5368
5422
  function isGsdOwned(name) {
5369
5423
  return name.startsWith(GSD_PREFIX);
@@ -5379,20 +5433,21 @@ function copySkillsPush(src, dst) {
5379
5433
  ]);
5380
5434
  copyExtrasFiltered(src, dst, blockSet);
5381
5435
  }
5382
- function copySkillsPull(src, dst) {
5383
- copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
5436
+ function copySkillsPull(src, dst, isRootPreserved) {
5437
+ copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved);
5384
5438
  }
5385
- function syncSkillsPull(ts) {
5439
+ function syncSkillsPull(ts, prePostHeads) {
5386
5440
  const sharedSkills = join41(repoHome(), "shared", "skills");
5387
5441
  if (!existsSync33(sharedSkills)) return;
5388
5442
  const localSkills = join41(claudeHome(), "skills");
5443
+ backupBeforeWrite(localSkills, ts);
5389
5444
  const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
5390
5445
  if (dstStat?.isSymbolicLink() === true) {
5391
- backupBeforeWrite(localSkills, ts);
5392
5446
  rmSync14(localSkills, { recursive: true, force: true });
5393
5447
  mkdirSync11(localSkills, { recursive: true });
5394
5448
  }
5395
- copySkillsPull(sharedSkills, localSkills);
5449
+ const tracked = prePostHeads === void 0 ? /* @__PURE__ */ new Set() : trackedRootSkillsAt(prePostHeads.pre, repoHome());
5450
+ copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
5396
5451
  }
5397
5452
  function syncSkillsPush() {
5398
5453
  const localSkills = join41(claudeHome(), "skills");
@@ -6384,8 +6439,9 @@ function unstageOne(rel, repo) {
6384
6439
  }
6385
6440
 
6386
6441
  // src/commands.pull.ts
6387
- import { existsSync as existsSync42, mkdirSync as mkdirSync14 } from "node:fs";
6388
- import { join as join52 } from "node:path";
6442
+ init_autostash_guard();
6443
+ import { existsSync as existsSync43, mkdirSync as mkdirSync14 } from "node:fs";
6444
+ import { join as join53 } from "node:path";
6389
6445
 
6390
6446
  // src/commands.push.sections.ts
6391
6447
  init_color();
@@ -6822,8 +6878,8 @@ function divergenceCheckExtras(ts, prePostHeads) {
6822
6878
 
6823
6879
  // src/preview.ts
6824
6880
  init_config();
6825
- import { existsSync as existsSync41 } from "node:fs";
6826
- import { join as join51 } from "node:path";
6881
+ import { existsSync as existsSync42 } from "node:fs";
6882
+ import { join as join52 } from "node:path";
6827
6883
 
6828
6884
  // node_modules/diff/libesm/diff/base.js
6829
6885
  var Diff = class {
@@ -7096,6 +7152,26 @@ function diffLinesToUnified(oldStr, newStr) {
7096
7152
  return lines;
7097
7153
  }
7098
7154
 
7155
+ // src/preview.skills.ts
7156
+ init_config();
7157
+ import { existsSync as existsSync41, readdirSync as readdirSync17 } from "node:fs";
7158
+ import { join as join51 } from "node:path";
7159
+ function buildSkillsPreviewSection() {
7160
+ const s = section("Skills");
7161
+ const sharedSkills = join51(repoHome(), "shared", "skills");
7162
+ if (!existsSync41(sharedSkills)) return s;
7163
+ const sharedNames = readdirSync17(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
7164
+ for (const name of sharedNames) addItem(s, name);
7165
+ const localSkills = join51(claudeHome(), "skills");
7166
+ const localOnly = existsSync41(localSkills) ? readdirSync17(localSkills, { encoding: "utf8" }).filter(
7167
+ (name) => !isSkillExcluded(name) && !sharedNames.includes(name)
7168
+ ).length : 0;
7169
+ if (localOnly > 0) {
7170
+ addItem(s, `${localOnly} local-only present, not in repo (push to reconcile)`);
7171
+ }
7172
+ return s;
7173
+ }
7174
+
7099
7175
  // src/preview.ts
7100
7176
  init_utils_json();
7101
7177
  var CANONICAL_ORDER_NOTE = "settings.json will be rewritten in canonical key order; no value changes";
@@ -7109,7 +7185,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
7109
7185
  return lines.join("\n");
7110
7186
  }
7111
7187
  function readJsonOrNull(path) {
7112
- if (!existsSync41(path)) return null;
7188
+ if (!existsSync42(path)) return null;
7113
7189
  try {
7114
7190
  return readJson(path);
7115
7191
  } catch {
@@ -7123,12 +7199,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
7123
7199
  }
7124
7200
  const notes = [];
7125
7201
  const hostOverrides = readJsonOrNull(hostPath);
7126
- if (hostOverrides === null && existsSync41(hostPath)) {
7202
+ if (hostOverrides === null && existsSync42(hostPath)) {
7127
7203
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
7128
7204
  }
7129
7205
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
7130
7206
  const current = readJsonOrNull(settingsPath);
7131
- if (current === null && existsSync41(settingsPath)) {
7207
+ if (current === null && existsSync42(settingsPath)) {
7132
7208
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
7133
7209
  }
7134
7210
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -7170,9 +7246,9 @@ function computePreview(ts, map, verb = "pull") {
7170
7246
  onPreview: (e) => addItem(links, formatLinkRow(e))
7171
7247
  });
7172
7248
  const settingsResult = previewSettings(
7173
- join51(repo, "shared", "settings.base.json"),
7174
- join51(repo, "hosts", `${HOST}.json`),
7175
- join51(claude, "settings.json")
7249
+ join52(repo, "shared", "settings.base.json"),
7250
+ join52(repo, "hosts", `${HOST}.json`),
7251
+ join52(claude, "settings.json")
7176
7252
  );
7177
7253
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
7178
7254
  const sessions = section("Sessions");
@@ -7184,10 +7260,11 @@ function computePreview(ts, map, verb = "pull") {
7184
7260
  if (localOnly > 0) {
7185
7261
  addItem(sessions, `${localOnly} local-only present, not in repo (push to reconcile)`);
7186
7262
  }
7263
+ const skills = buildSkillsPreviewSection();
7187
7264
  const extras = section("Extras");
7188
7265
  let extrasSkipped = 0;
7189
7266
  let extrasUnmapped = 0;
7190
- if (existsSync41(join51(repo, "path-map.json")) && existsSync41(join51(repo, "shared", "extras"))) {
7267
+ if (existsSync42(join52(repo, "path-map.json")) && existsSync42(join52(repo, "shared", "extras"))) {
7191
7268
  const extrasResult = remapExtrasPull(ts, { dryRun: true });
7192
7269
  for (const entry of extrasResult.wouldPull) {
7193
7270
  addItem(extras, entry);
@@ -7200,7 +7277,7 @@ function computePreview(ts, map, verb = "pull") {
7200
7277
  summary,
7201
7278
  summaryRow(verb, remapResult.unmapped + extrasUnmapped, 0, extrasSkipped, localOnly)
7202
7279
  );
7203
- renderTree([links, settingsSection, sessions, extras, summary]);
7280
+ renderTree([links, settingsSection, sessions, skills, extras, summary]);
7204
7281
  return { unmapped: remapResult.unmapped, collisions: 0, localOnly };
7205
7282
  }
7206
7283
 
@@ -7210,6 +7287,7 @@ init_commands_pull_wedge();
7210
7287
  // src/commands.pull.recovery.ts
7211
7288
  init_config();
7212
7289
  init_commands_pull_wedge();
7290
+ init_exit_codes();
7213
7291
  init_utils();
7214
7292
  init_utils_fs();
7215
7293
  import { execFileSync as execFileSync21 } from "node:child_process";
@@ -7262,8 +7340,8 @@ function parsePorcelainZ(raw) {
7262
7340
  }
7263
7341
  return { tracked, untracked };
7264
7342
  }
7265
- function parseDirtyPaths(repo) {
7266
- return parsePorcelainZ(gitStatusPorcelainZ(repo));
7343
+ function parseDirtyPaths(repo, opts = {}) {
7344
+ return parsePorcelainZ(gitStatusPorcelainZ(repo, opts));
7267
7345
  }
7268
7346
  function buildRecoverySummary(branchName, strandedLog, untracked) {
7269
7347
  const strandedLines = strandedLog.split("\n").filter(Boolean).map((l) => ` ${l}`).join("\n");
@@ -7290,18 +7368,24 @@ function freshStrandedBranch(repo) {
7290
7368
  return `${base}-${n}`;
7291
7369
  }
7292
7370
  function recoverUnmergedIndex(repo) {
7371
+ const conflicted = new Set(
7372
+ gitCapture(["diff", "--diff-filter=U", "--name-only", "-z"], repo).split("\0").filter(Boolean)
7373
+ );
7293
7374
  gitOrFatal(["reset", "--mixed", "HEAD"], "git reset --mixed HEAD", repo);
7294
- const dirty = gitCapture(["diff", "--name-only", "-z"], repo).split("\0").filter(Boolean);
7295
- if (dirty.length > 0) {
7296
- log(
7297
- "index cleared, but these files still carry conflict content from the torn-down rebase; review and resolve before the next pull:\n" + dirty.map((p) => ` ${p}`).join("\n")
7298
- );
7299
- }
7375
+ const { tracked, untracked } = parseDirtyPaths(repo, { untrackedAll: true });
7376
+ const present = /* @__PURE__ */ new Set([...tracked, ...untracked]);
7377
+ const residual = [...conflicted].filter((p) => present.has(p));
7300
7378
  if (orphanedAutostashPresent(repo)) {
7301
7379
  log(
7302
7380
  'orphaned autostash preserved in the stash list; run "git stash pop" to restore or "git stash drop" to discard it, then re-run "nomad pull"'
7303
7381
  );
7304
7382
  }
7383
+ if (residual.length > 0) {
7384
+ die(
7385
+ "index cleared, but these files still carry unresolved conflict content from the torn-down rebase or merge:\n" + residual.map((p) => ` ${p}`).join("\n") + '\n\nThe repo is no longer wedged, so nothing else is blocked. Nothing was applied to ~/.claude/: pulling now would publish that content to your live config.\n\nResolve each file above (remove any <<<<<<< / ======= / >>>>>>> markers and keep the content you want; a file left untracked was deleted upstream, so keep or delete it deliberately), commit or checkout the result, then re-run "nomad pull".',
7386
+ { code: EXIT.CONFLICT }
7387
+ );
7388
+ }
7305
7389
  }
7306
7390
  function recoverForceRemote(mode, repo) {
7307
7391
  if (mode === "merge") {
@@ -7355,7 +7439,7 @@ function capturePrePostHeads(repo, rebase) {
7355
7439
  function buildWetPullSections(ts, map, prePostHeads) {
7356
7440
  applySharedLinks(ts, map);
7357
7441
  const { label } = regenerateSettings(ts);
7358
- syncSkillsPull(ts);
7442
+ syncSkillsPull(ts, prePostHeads);
7359
7443
  const remapResult = withSpinner("Syncing sessions", () => remapPull(ts));
7360
7444
  const extrasResult = remapExtrasPull(ts, { prePostHeads });
7361
7445
  const localOnly = scanLocalOnly();
@@ -7402,7 +7486,7 @@ function runPullCore(opts = {}) {
7402
7486
  const ts = freshBackupTs(backup);
7403
7487
  handleWedge(repo, forceRemote);
7404
7488
  if (!dryRun) {
7405
- const backupRoot = join52(backup, ts);
7489
+ const backupRoot = join53(backup, ts);
7406
7490
  try {
7407
7491
  mkdirSync14(backupRoot, { recursive: true });
7408
7492
  } catch (err) {
@@ -7417,8 +7501,9 @@ function runPullCore(opts = {}) {
7417
7501
  const prePostHeads = capturePrePostHeads(repo, () => {
7418
7502
  gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
7419
7503
  });
7420
- const mapPath = join52(repo, "path-map.json");
7421
- const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
7504
+ assertNoAutostashConflict(repo, "nomad pull");
7505
+ const mapPath = join53(repo, "path-map.json");
7506
+ const map = existsSync43(mapPath) ? readPathMap(mapPath) : { projects: {} };
7422
7507
  const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
7423
7508
  if (dryRun) {
7424
7509
  computePreview(ts, map, "pull");
@@ -7444,8 +7529,8 @@ function runPullCore(opts = {}) {
7444
7529
  }
7445
7530
  function cmdPull(opts = {}) {
7446
7531
  const repo = repoHome();
7447
- if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
7448
- if (!existsSync42(join52(repo, "shared", "settings.base.json"))) {
7532
+ if (!existsSync43(repo)) die(`repo not cloned at ${repo}`);
7533
+ if (!existsSync43(join53(repo, "shared", "settings.base.json"))) {
7449
7534
  die("repo not initialized; run 'nomad init' to scaffold");
7450
7535
  }
7451
7536
  const handle = acquireLock("pull");
@@ -7467,13 +7552,13 @@ function cmdPull(opts = {}) {
7467
7552
 
7468
7553
  // src/commands.push.ts
7469
7554
  init_config();
7470
- import { existsSync as existsSync46 } from "node:fs";
7471
- import { join as join57 } from "node:path";
7555
+ import { existsSync as existsSync47 } from "node:fs";
7556
+ import { join as join58 } from "node:path";
7472
7557
 
7473
7558
  // src/commands.push.selection.ts
7474
7559
  init_config();
7475
- import { existsSync as existsSync43, statSync as statSync10 } from "node:fs";
7476
- import { join as join53 } from "node:path";
7560
+ import { existsSync as existsSync44, statSync as statSync10 } from "node:fs";
7561
+ import { join as join54 } from "node:path";
7477
7562
  init_utils_json();
7478
7563
  function buildCurrentMap(map) {
7479
7564
  const current = {};
@@ -7482,8 +7567,8 @@ function buildCurrentMap(map) {
7482
7567
  for (const [, hostMap] of Object.entries(map.projects)) {
7483
7568
  const localPath = hostMap[HOST];
7484
7569
  if (!localPath) continue;
7485
- const localDir = join53(claude, "projects", encodePath(localPath));
7486
- if (!existsSync43(localDir)) continue;
7570
+ const localDir = join54(claude, "projects", encodePath(localPath));
7571
+ if (!existsSync44(localDir)) continue;
7487
7572
  for (const f of enumerateSourceFiles(localDir)) {
7488
7573
  const st = statSync10(f);
7489
7574
  current[f] = { size: st.size, mtime: st.mtimeMs };
@@ -7514,7 +7599,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
7514
7599
  };
7515
7600
  }
7516
7601
  function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
7517
- const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
7602
+ const map = existsSync44(mapPath) ? readPathMap(mapPath) : null;
7518
7603
  const { selection, newManifest } = computePushSelection(
7519
7604
  map,
7520
7605
  old,
@@ -7616,14 +7701,14 @@ function enforceAllowList(statusPorcelain, map) {
7616
7701
 
7617
7702
  // src/commands.push.settings.ts
7618
7703
  init_config();
7619
- import { existsSync as existsSync44 } from "node:fs";
7620
- import { join as join54 } from "node:path";
7704
+ import { existsSync as existsSync45 } from "node:fs";
7705
+ import { join as join55 } from "node:path";
7621
7706
  init_utils();
7622
7707
  init_utils_fs();
7623
7708
  init_utils_json();
7624
7709
  function stripGsdHooksFromBase(repo, backup) {
7625
- const basePath = join54(repo, "shared", "settings.base.json");
7626
- if (!existsSync44(basePath)) return;
7710
+ const basePath = join55(repo, "shared", "settings.base.json");
7711
+ if (!existsSync45(basePath)) return;
7627
7712
  let base;
7628
7713
  try {
7629
7714
  base = readJson(basePath);
@@ -7637,14 +7722,14 @@ function stripGsdHooksFromBase(repo, backup) {
7637
7722
  writeJsonAtomic(basePath, stripped);
7638
7723
  }
7639
7724
  function reportSettingsAheadDrift(repo) {
7640
- const basePath = join54(repo, "shared", "settings.base.json");
7641
- if (!existsSync44(basePath)) return;
7642
- const settingsPath = join54(claudeHome(), "settings.json");
7643
- if (!existsSync44(settingsPath)) return;
7725
+ const basePath = join55(repo, "shared", "settings.base.json");
7726
+ if (!existsSync45(basePath)) return;
7727
+ const settingsPath = join55(claudeHome(), "settings.json");
7728
+ if (!existsSync45(settingsPath)) return;
7644
7729
  try {
7645
7730
  const base = readJson(basePath);
7646
- const hostPath = join54(repo, "hosts", `${HOST}.json`);
7647
- const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
7731
+ const hostPath = join55(repo, "hosts", `${HOST}.json`);
7732
+ const overrides = existsSync45(hostPath) ? readJson(hostPath) : {};
7648
7733
  const merged = deepMerge(base, overrides);
7649
7734
  const settings = readJson(settingsPath);
7650
7735
  const { ahead } = classifySettingsDrift(merged, settings);
@@ -7661,9 +7746,9 @@ function reportSettingsAheadDrift(repo) {
7661
7746
  // src/commands.push.guards.ts
7662
7747
  init_push_checks();
7663
7748
  init_utils();
7664
- import { join as join55, relative as relative7 } from "node:path";
7749
+ import { join as join56, relative as relative7 } from "node:path";
7665
7750
  function guardGitlinks(repo) {
7666
- const gitlinks = findGitlinks(join55(repo, "shared"));
7751
+ const gitlinks = findGitlinks(join56(repo, "shared"));
7667
7752
  if (gitlinks.length === 0) return;
7668
7753
  for (const p of gitlinks) {
7669
7754
  const rel = relative7(repo, p).replaceAll("\\", "/");
@@ -7773,9 +7858,9 @@ init_color();
7773
7858
  init_config();
7774
7859
  init_config_sharedDirs_guard();
7775
7860
  import { randomBytes as randomBytes4 } from "node:crypto";
7776
- import { copyFileSync, existsSync as existsSync45, mkdirSync as mkdirSync15, readdirSync as readdirSync17, rmSync as rmSync17 } from "node:fs";
7861
+ import { copyFileSync, existsSync as existsSync46, mkdirSync as mkdirSync15, readdirSync as readdirSync18, rmSync as rmSync17 } from "node:fs";
7777
7862
  import { homedir as homedir7 } from "node:os";
7778
- import { join as join56, relative as relative8, sep as sep10 } from "node:path";
7863
+ import { join as join57, relative as relative8, sep as sep10 } from "node:path";
7779
7864
  init_push_leak_verdict();
7780
7865
  init_push_gitleaks();
7781
7866
  init_utils_fs();
@@ -7787,7 +7872,7 @@ function stageSessionDir(localDir, dstDir, changed) {
7787
7872
  const matching = [...changed].filter((p) => p.startsWith(prefix));
7788
7873
  if (matching.length === 0) return false;
7789
7874
  for (const src of matching) {
7790
- copyFileAtomic(src, join56(dstDir, relative8(localDir, src)));
7875
+ copyFileAtomic(src, join57(dstDir, relative8(localDir, src)));
7791
7876
  }
7792
7877
  return true;
7793
7878
  }
@@ -7803,14 +7888,14 @@ function stageSessions(tmpRoot, map, changed) {
7803
7888
  if (!p || p === "TBD") continue;
7804
7889
  reverse.set(encodePath(p), logical);
7805
7890
  }
7806
- const localProjects = join56(claudeHome(), "projects");
7807
- if (!existsSync45(localProjects)) return 0;
7891
+ const localProjects = join57(claudeHome(), "projects");
7892
+ if (!existsSync46(localProjects)) return 0;
7808
7893
  let staged = 0;
7809
- for (const dir of readdirSync17(localProjects)) {
7894
+ for (const dir of readdirSync18(localProjects)) {
7810
7895
  const logical = reverse.get(dir);
7811
7896
  if (!logical) continue;
7812
- const localDir = join56(localProjects, dir);
7813
- const dstDir = join56(tmpRoot, "shared", "projects", logical);
7897
+ const localDir = join57(localProjects, dir);
7898
+ const dstDir = join57(tmpRoot, "shared", "projects", logical);
7814
7899
  if (stageSessionDir(localDir, dstDir, changed)) staged++;
7815
7900
  }
7816
7901
  return staged;
@@ -7826,9 +7911,9 @@ function stageExtras(tmpRoot, map) {
7826
7911
  if (!localRoot || localRoot === "TBD") continue;
7827
7912
  for (const dirname13 of dirnames) {
7828
7913
  if (!whitelist.includes(dirname13)) continue;
7829
- const src = join56(localRoot, dirname13);
7830
- if (!existsSync45(src)) continue;
7831
- const dst = join56(tmpRoot, "shared", "extras", logical, dirname13);
7914
+ const src = join57(localRoot, dirname13);
7915
+ if (!existsSync46(src)) continue;
7916
+ const dst = join57(tmpRoot, "shared", "extras", logical, dirname13);
7832
7917
  copyExtras(src, dst);
7833
7918
  staged++;
7834
7919
  }
@@ -7836,19 +7921,19 @@ function stageExtras(tmpRoot, map) {
7836
7921
  return staged;
7837
7922
  }
7838
7923
  function previewPushLeaks(map, opts = {}) {
7839
- const cacheDir = join56(homedir7(), ".cache", "claude-nomad");
7924
+ const cacheDir = join57(homedir7(), ".cache", "claude-nomad");
7840
7925
  mkdirSync15(cacheDir, { recursive: true });
7841
7926
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
7842
- const tmpRoot = join56(cacheDir, `push-preview-tree-${stamp}`);
7927
+ const tmpRoot = join57(cacheDir, `push-preview-tree-${stamp}`);
7843
7928
  try {
7844
7929
  const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
7845
7930
  const extrasCount = stageExtras(tmpRoot, map);
7846
7931
  if (sessionCount + extrasCount === 0) {
7847
7932
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
7848
7933
  }
7849
- const ignoreFile = join56(repoHome(), ".gitleaksignore");
7850
- if (existsSync45(ignoreFile)) {
7851
- copyFileSync(ignoreFile, join56(tmpRoot, ".gitleaksignore"));
7934
+ const ignoreFile = join57(repoHome(), ".gitleaksignore");
7935
+ if (existsSync46(ignoreFile)) {
7936
+ copyFileSync(ignoreFile, join57(tmpRoot, ".gitleaksignore"));
7852
7937
  }
7853
7938
  let findings;
7854
7939
  try {
@@ -7975,7 +8060,7 @@ async function runPushCore(opts = {}) {
7975
8060
  const scannerVersion = probeGitleaks();
7976
8061
  const configHash = computeConfigHash();
7977
8062
  const old = readManifest(manifestPath());
7978
- const mapPath = join57(repo, "path-map.json");
8063
+ const mapPath = join58(repo, "path-map.json");
7979
8064
  const { map, selection, newManifest } = loadSelectionForPush(
7980
8065
  mapPath,
7981
8066
  old,
@@ -8026,7 +8111,7 @@ async function cmdPush(opts = {}) {
8026
8111
  const allowRule = opts.allowRule;
8027
8112
  guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
8028
8113
  const repo = repoHome();
8029
- if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
8114
+ if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
8030
8115
  const handle = acquireLock("push");
8031
8116
  if (handle === null) process.exit(0);
8032
8117
  try {
@@ -8044,8 +8129,8 @@ async function cmdPush(opts = {}) {
8044
8129
  }
8045
8130
 
8046
8131
  // src/commands.sync.ts
8047
- import { existsSync as existsSync47 } from "node:fs";
8048
- import { join as join58 } from "node:path";
8132
+ import { existsSync as existsSync48 } from "node:fs";
8133
+ import { join as join59 } from "node:path";
8049
8134
  init_config();
8050
8135
  init_color();
8051
8136
  init_utils();
@@ -8173,8 +8258,8 @@ async function runSyncWet(verbose) {
8173
8258
  }
8174
8259
  async function runSyncDryRun(repo, backup) {
8175
8260
  const ts = freshBackupTs(backup);
8176
- const mapPath = join58(repo, "path-map.json");
8177
- const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
8261
+ const mapPath = join59(repo, "path-map.json");
8262
+ const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
8178
8263
  computePreview(ts, map, "pull");
8179
8264
  log("push preview below is computed against pre-pull state (a real sync pushes after pull)");
8180
8265
  await runPushCore({ dryRun: true });
@@ -8184,8 +8269,8 @@ async function cmdSync(opts = {}) {
8184
8269
  const verbose = opts.verbose === true;
8185
8270
  const repo = repoHome();
8186
8271
  const backup = backupBase();
8187
- if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
8188
- if (!existsSync47(join58(repo, "shared", "settings.base.json"))) {
8272
+ if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
8273
+ if (!existsSync48(join59(repo, "shared", "settings.base.json"))) {
8189
8274
  die("repo not initialized; run 'nomad init' to scaffold");
8190
8275
  }
8191
8276
  const handle = acquireLock("sync");
@@ -8258,8 +8343,8 @@ init_config();
8258
8343
 
8259
8344
  // src/crash-report.write.ts
8260
8345
  init_config();
8261
- import { mkdirSync as mkdirSync16, readdirSync as readdirSync18, statSync as statSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
8262
- import { join as join60 } from "node:path";
8346
+ import { mkdirSync as mkdirSync16, readdirSync as readdirSync19, statSync as statSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
8347
+ import { join as join61 } from "node:path";
8263
8348
 
8264
8349
  // src/crash-report.ts
8265
8350
  var CRASH_MAX_STACK_LINES = 50;
@@ -8338,15 +8423,15 @@ function buildCrashReport(input) {
8338
8423
  // src/crash-report.redact.ts
8339
8424
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync18, writeFileSync as writeFileSync10 } from "node:fs";
8340
8425
  import { tmpdir as tmpdir2 } from "node:os";
8341
- import { join as join59 } from "node:path";
8426
+ import { join as join60 } from "node:path";
8342
8427
  init_push_gitleaks_scan();
8343
8428
  var CRASH_SCAN_TIMEOUT_MS = 3e3;
8344
8429
  var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
8345
8430
  function redactWithGitleaks(text, scan = scanFile) {
8346
8431
  let dir;
8347
8432
  try {
8348
- dir = mkdtempSync2(join59(tmpdir2(), "nomad-crash-scan-"));
8349
- const tmp = join59(dir, "crash.txt");
8433
+ dir = mkdtempSync2(join60(tmpdir2(), "nomad-crash-scan-"));
8434
+ const tmp = join60(dir, "crash.txt");
8350
8435
  writeFileSync10(tmp, text, { mode: 384 });
8351
8436
  const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
8352
8437
  if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
@@ -8369,9 +8454,9 @@ init_utils();
8369
8454
  var CRASH_RETENTION_KEEP = 20;
8370
8455
  function listCrashFiles(dir = crashDir()) {
8371
8456
  try {
8372
- return readdirSync18(dir).flatMap((name) => {
8457
+ return readdirSync19(dir).flatMap((name) => {
8373
8458
  try {
8374
- return [{ name, mtimeMs: statSync11(join60(dir, name)).mtimeMs }];
8459
+ return [{ name, mtimeMs: statSync11(join61(dir, name)).mtimeMs }];
8375
8460
  } catch {
8376
8461
  return [];
8377
8462
  }
@@ -8385,14 +8470,14 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
8385
8470
  const targets = prunableByCount(files, keep);
8386
8471
  for (const name of targets) {
8387
8472
  try {
8388
- unlinkSync2(join60(dir, name));
8473
+ unlinkSync2(join61(dir, name));
8389
8474
  } catch {
8390
8475
  }
8391
8476
  }
8392
8477
  }
8393
8478
  function writeCrashReport(text, dir = crashDir()) {
8394
8479
  mkdirSync16(dir, { recursive: true, mode: 448 });
8395
- const path = join60(dir, `crash-${nowTimestamp()}-${process.pid}.txt`);
8480
+ const path = join61(dir, `crash-${nowTimestamp()}-${process.pid}.txt`);
8396
8481
  writeFileSync11(path, text, { mode: 384 });
8397
8482
  pruneCrashDir(dir);
8398
8483
  return path;
@@ -8423,18 +8508,18 @@ function handleCrash(err, argv, opts) {
8423
8508
 
8424
8509
  // src/diff.ts
8425
8510
  init_config();
8426
- import { existsSync as existsSync48 } from "node:fs";
8427
- import { join as join61 } from "node:path";
8511
+ import { existsSync as existsSync49 } from "node:fs";
8512
+ import { join as join62 } from "node:path";
8428
8513
  init_utils();
8429
8514
  init_utils_fs();
8430
8515
  init_utils_json();
8431
8516
  function cmdDiff() {
8432
8517
  try {
8433
8518
  const repo = repoHome();
8434
- if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
8519
+ if (!existsSync49(repo)) die(`repo not cloned at ${repo}`);
8435
8520
  const ts = freshBackupTs(backupBase());
8436
- const mapPath = join61(repo, "path-map.json");
8437
- const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
8521
+ const mapPath = join62(repo, "path-map.json");
8522
+ const map = existsSync49(mapPath) ? readPathMap(mapPath) : { projects: {} };
8438
8523
  divergenceCheckExtras(ts);
8439
8524
  computePreview(ts, map, "diff");
8440
8525
  } catch (err) {
@@ -8449,8 +8534,8 @@ function cmdDiff() {
8449
8534
 
8450
8535
  // src/init.ts
8451
8536
  init_config();
8452
- import { existsSync as existsSync50, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
8453
- import { join as join63 } from "node:path";
8537
+ import { existsSync as existsSync51, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
8538
+ import { join as join64 } from "node:path";
8454
8539
 
8455
8540
  // src/init.gh-onboard.ts
8456
8541
  init_config();
@@ -8532,33 +8617,33 @@ init_config();
8532
8617
  init_utils();
8533
8618
  init_utils_fs();
8534
8619
  init_utils_json();
8535
- import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync49, rmSync as rmSync19, statSync as statSync12 } from "node:fs";
8536
- import { join as join62 } from "node:path";
8620
+ import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync50, rmSync as rmSync19, statSync as statSync12 } from "node:fs";
8621
+ import { join as join63 } from "node:path";
8537
8622
  function snapshotIntoShared(map) {
8538
8623
  const repo = repoHome();
8539
8624
  const claude = claudeHome();
8540
8625
  for (const name of allSharedLinks(map)) {
8541
- const src = join62(claude, name);
8542
- if (!existsSync49(src)) continue;
8543
- const dst = join62(repo, "shared", name);
8626
+ const src = join63(claude, name);
8627
+ if (!existsSync50(src)) continue;
8628
+ const dst = join63(repo, "shared", name);
8544
8629
  if (statSync12(src).isDirectory()) {
8545
- const gk = join62(dst, ".gitkeep");
8546
- if (existsSync49(gk)) rmSync19(gk);
8630
+ const gk = join63(dst, ".gitkeep");
8631
+ if (existsSync50(gk)) rmSync19(gk);
8547
8632
  cpSync10(src, dst, { recursive: true, force: false, errorOnExist: true });
8548
8633
  } else {
8549
8634
  copyFileSync2(src, dst);
8550
8635
  }
8551
8636
  log(`snapshotted shared/${name} from ${src}`);
8552
8637
  }
8553
- const userSettings = join62(claude, "settings.json");
8554
- if (existsSync49(userSettings)) {
8638
+ const userSettings = join63(claude, "settings.json");
8639
+ if (existsSync50(userSettings)) {
8555
8640
  let parsed;
8556
8641
  try {
8557
8642
  parsed = readJson(userSettings);
8558
8643
  } catch (err) {
8559
8644
  return die(`malformed ${userSettings}: ${err.message}`);
8560
8645
  }
8561
- const hostFile = join62(repo, "hosts", `${HOST}.json`);
8646
+ const hostFile = join63(repo, "hosts", `${HOST}.json`);
8562
8647
  writeJsonAtomic(hostFile, parsed);
8563
8648
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
8564
8649
  }
@@ -8572,14 +8657,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
8572
8657
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
8573
8658
  function preflightConflict(repoHome2) {
8574
8659
  const candidates = [
8575
- join63(repoHome2, "shared", "settings.base.json"),
8576
- join63(repoHome2, "shared", "CLAUDE.md"),
8577
- join63(repoHome2, "path-map.json"),
8578
- join63(repoHome2, "hosts"),
8579
- join63(repoHome2, "shared")
8660
+ join64(repoHome2, "shared", "settings.base.json"),
8661
+ join64(repoHome2, "shared", "CLAUDE.md"),
8662
+ join64(repoHome2, "path-map.json"),
8663
+ join64(repoHome2, "hosts"),
8664
+ join64(repoHome2, "shared")
8580
8665
  ];
8581
8666
  for (const c of candidates) {
8582
- if (existsSync50(c)) return c;
8667
+ if (existsSync51(c)) return c;
8583
8668
  }
8584
8669
  return null;
8585
8670
  }
@@ -8597,27 +8682,27 @@ function cmdInit(opts = {}) {
8597
8682
  die(`already initialized; refusing to clobber ${conflict}`);
8598
8683
  }
8599
8684
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
8600
- mkdirSync17(join63(repo, "shared"), { recursive: true });
8601
- mkdirSync17(join63(repo, "hosts"), { recursive: true });
8685
+ mkdirSync17(join64(repo, "shared"), { recursive: true });
8686
+ mkdirSync17(join64(repo, "hosts"), { recursive: true });
8602
8687
  for (const name of SHARED_KEEP_DIRS) {
8603
- mkdirSync17(join63(repo, "shared", name), { recursive: true });
8688
+ mkdirSync17(join64(repo, "shared", name), { recursive: true });
8604
8689
  }
8605
- const userClaudeMd = join63(claude, "CLAUDE.md");
8606
- if (!snapshot || !existsSync50(userClaudeMd)) {
8607
- writeFileSync12(join63(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
8690
+ const userClaudeMd = join64(claude, "CLAUDE.md");
8691
+ if (!snapshot || !existsSync51(userClaudeMd)) {
8692
+ writeFileSync12(join64(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
8608
8693
  item("created shared/CLAUDE.md");
8609
8694
  }
8610
8695
  for (const name of SHARED_KEEP_DIRS) {
8611
- writeFileSync12(join63(repo, "shared", name, ".gitkeep"), "");
8696
+ writeFileSync12(join64(repo, "shared", name, ".gitkeep"), "");
8612
8697
  item(`created shared/${name}/.gitkeep`);
8613
8698
  }
8614
- writeFileSync12(join63(repo, "hosts", ".gitkeep"), "");
8699
+ writeFileSync12(join64(repo, "hosts", ".gitkeep"), "");
8615
8700
  item("created hosts/.gitkeep");
8616
- writeJsonAtomic(join63(repo, "shared", "settings.base.json"), {});
8701
+ writeJsonAtomic(join64(repo, "shared", "settings.base.json"), {});
8617
8702
  item("created shared/settings.base.json");
8618
- writeJsonAtomic(join63(repo, "path-map.json"), { projects: {} });
8703
+ writeJsonAtomic(join64(repo, "path-map.json"), { projects: {} });
8619
8704
  item("created path-map.json");
8620
- writeFileSync12(join63(repo, ".gitattributes"), GITATTRIBUTES);
8705
+ writeFileSync12(join64(repo, ".gitattributes"), GITATTRIBUTES);
8621
8706
  item("created .gitattributes");
8622
8707
  if (snapshot) {
8623
8708
  snapshotIntoShared({ projects: {} });
@@ -8683,21 +8768,21 @@ function maybeDisableRepoActions(repoHome2, run) {
8683
8768
  // src/init.prompt.ts
8684
8769
  init_config();
8685
8770
  init_utils();
8686
- import { existsSync as existsSync51, readdirSync as readdirSync19, statSync as statSync13 } from "node:fs";
8687
- import { join as join64 } from "node:path";
8771
+ import { existsSync as existsSync52, readdirSync as readdirSync20, statSync as statSync13 } from "node:fs";
8772
+ import { join as join65 } from "node:path";
8688
8773
  import { createInterface as createInterface3 } from "node:readline/promises";
8689
8774
  function nonEmptyExists(path) {
8690
- if (!existsSync51(path)) return false;
8775
+ if (!existsSync52(path)) return false;
8691
8776
  try {
8692
- if (statSync13(path).isDirectory()) return readdirSync19(path).length > 0;
8777
+ if (statSync13(path).isDirectory()) return readdirSync20(path).length > 0;
8693
8778
  return true;
8694
8779
  } catch {
8695
8780
  return false;
8696
8781
  }
8697
8782
  }
8698
8783
  function hasExistingClaudeConfig(claudeHome2) {
8699
- if (existsSync51(join64(claudeHome2, "settings.json"))) return true;
8700
- return SHARED_LINKS.some((name) => nonEmptyExists(join64(claudeHome2, name)));
8784
+ if (existsSync52(join65(claudeHome2, "settings.json"))) return true;
8785
+ return SHARED_LINKS.some((name) => nonEmptyExists(join65(claudeHome2, name)));
8701
8786
  }
8702
8787
  async function confirmSnapshotDefault(claudeHome2) {
8703
8788
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -9004,7 +9089,7 @@ function parseSyncArgs(argv) {
9004
9089
  // package.json
9005
9090
  var package_default = {
9006
9091
  name: "claude-nomad",
9007
- version: "0.62.2",
9092
+ version: "0.62.3",
9008
9093
  type: "module",
9009
9094
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
9010
9095
  keywords: [
@@ -9245,15 +9330,15 @@ var DEFAULT_HELP = [
9245
9330
  init_config();
9246
9331
  init_utils();
9247
9332
  init_utils_json();
9248
- import { existsSync as existsSync52, readFileSync as readFileSync20, readdirSync as readdirSync20 } from "node:fs";
9249
- import { join as join65 } from "node:path";
9333
+ import { existsSync as existsSync53, readFileSync as readFileSync20, readdirSync as readdirSync21 } from "node:fs";
9334
+ import { join as join66 } from "node:path";
9250
9335
  function resumeCmd(sessionId) {
9251
9336
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
9252
9337
  fail(`invalid session id: ${sessionId}`);
9253
9338
  process.exit(1);
9254
9339
  }
9255
- const projectsRoot = join65(claudeHome(), "projects");
9256
- if (!existsSync52(projectsRoot)) {
9340
+ const projectsRoot = join66(claudeHome(), "projects");
9341
+ if (!existsSync53(projectsRoot)) {
9257
9342
  fail(`${projectsRoot} does not exist`);
9258
9343
  process.exit(1);
9259
9344
  }
@@ -9267,8 +9352,8 @@ function resumeCmd(sessionId) {
9267
9352
  fail(`no cwd field found in ${jsonlPath}`);
9268
9353
  process.exit(1);
9269
9354
  }
9270
- const mapPath = join65(repoHome(), "path-map.json");
9271
- if (!existsSync52(mapPath)) {
9355
+ const mapPath = join66(repoHome(), "path-map.json");
9356
+ if (!existsSync53(mapPath)) {
9272
9357
  fail("path-map.json missing");
9273
9358
  process.exit(1);
9274
9359
  }
@@ -9290,9 +9375,9 @@ function resumeCmd(sessionId) {
9290
9375
  console.log(`cd ${shQuote(hit.localPath)} && claude --resume ${shQuote(sessionId)}`);
9291
9376
  }
9292
9377
  function findTranscriptPath(projectsRoot, sessionId) {
9293
- for (const dir of readdirSync20(projectsRoot)) {
9294
- const candidate = join65(projectsRoot, dir, `${sessionId}.jsonl`);
9295
- if (existsSync52(candidate)) return candidate;
9378
+ for (const dir of readdirSync21(projectsRoot)) {
9379
+ const candidate = join66(projectsRoot, dir, `${sessionId}.jsonl`);
9380
+ if (existsSync53(candidate)) return candidate;
9296
9381
  }
9297
9382
  return null;
9298
9383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nomad",
3
- "version": "0.62.2",
3
+ "version": "0.62.3",
4
4
  "type": "module",
5
5
  "description": "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
6
6
  "keywords": [