claude-nomad 0.63.1 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/nomad.mjs CHANGED
@@ -353,8 +353,11 @@ var init_settings_keys = __esm({
353
353
  "allowManagedPermissionRulesOnly",
354
354
  "alwaysThinkingEnabled",
355
355
  "apiKeyHelper",
356
+ "askUserQuestionTimeout",
356
357
  "attribution",
357
358
  "autoCompactEnabled",
359
+ "autoConnectIde",
360
+ "autoInstallIdeExtension",
358
361
  "autoMemoryDirectory",
359
362
  "autoMemoryEnabled",
360
363
  "autoMode",
@@ -366,6 +369,7 @@ var init_settings_keys = __esm({
366
369
  "awsCredentialExport",
367
370
  "axScreenReader",
368
371
  "blockedMarketplaces",
372
+ "browserExternalPageTools",
369
373
  "channelsEnabled",
370
374
  "claudeMd",
371
375
  "claudeMdExcludes",
@@ -373,24 +377,31 @@ var init_settings_keys = __esm({
373
377
  "companyAnnouncements",
374
378
  "defaultShell",
375
379
  "deniedMcpServers",
380
+ "diffTool",
376
381
  "disableAgentView",
377
382
  "disableAllHooks",
378
383
  "disableArtifact",
379
384
  "disableAutoMode",
385
+ "disableBrowserExternalNavigation",
380
386
  "disableBundledSkills",
381
387
  "disableClaudeAiConnectors",
382
388
  "disableDeepLinkRegistration",
383
389
  "disabledMcpjsonServers",
390
+ "disableMobileSimulatorTools",
384
391
  "disableRemoteControl",
392
+ "disableSideloadFlags",
385
393
  "disableSkillShellExecution",
386
394
  "disableWorkflows",
387
395
  "editorMode",
388
396
  "effortLevel",
397
+ "emojiCompletionEnabled",
389
398
  "enableAllProjectMcpServers",
399
+ "enableArtifact",
390
400
  "enabledMcpjsonServers",
391
401
  "enabledPlugins",
392
402
  "enforceAvailableModels",
393
403
  "env",
404
+ "externalEditorContext",
394
405
  "extraKnownMarketplaces",
395
406
  "fallbackModel",
396
407
  "fastMode",
@@ -399,6 +410,7 @@ var init_settings_keys = __esm({
399
410
  "fileCheckpointingEnabled",
400
411
  "fileSuggestion",
401
412
  "footerLinksRegexes",
413
+ "forceLoginGatewayUrl",
402
414
  "forceLoginMethod",
403
415
  "forceLoginOrgUUID",
404
416
  "forceRemoteSettingsRefresh",
@@ -409,15 +421,14 @@ var init_settings_keys = __esm({
409
421
  "includeGitInstructions",
410
422
  "inputNeededNotifEnabled",
411
423
  "language",
412
- "leftArrowOpensAgents",
413
424
  "managedMcpServers",
414
- "maxSkillDescriptionChars",
415
425
  "minimumVersion",
416
426
  "model",
417
427
  "modelOverrides",
418
428
  "otelHeadersHelper",
419
429
  "outputStyle",
420
430
  "parentSettingsBehavior",
431
+ "permissionExplainerEnabled",
421
432
  "permissions",
422
433
  "plansDirectory",
423
434
  "pluginConfigs",
@@ -426,8 +437,10 @@ var init_settings_keys = __esm({
426
437
  "policyHelper",
427
438
  "preferredNotifChannel",
428
439
  "prefersReducedMotion",
440
+ "processWrapper",
429
441
  "prUrlTemplate",
430
442
  "remoteControlAtStartup",
443
+ "requireCoworkFullVmSandbox",
431
444
  "requiredMaximumVersion",
432
445
  "requiredMinimumVersion",
433
446
  "respectGitignore",
@@ -437,6 +450,7 @@ var init_settings_keys = __esm({
437
450
  "showThinkingSummaries",
438
451
  "showTurnDuration",
439
452
  "skillListingBudgetFraction",
453
+ "skillListingMaxDescChars",
440
454
  "skillOverrides",
441
455
  "skipDangerousModePermissionPrompt",
442
456
  "skippedMarketplaces",
@@ -452,6 +466,7 @@ var init_settings_keys = __esm({
452
466
  "strictPluginOnlyCustomization",
453
467
  "subagentStatusLine",
454
468
  "syntaxHighlightingDisabled",
469
+ "teammateDefaultModel",
455
470
  "teammateMode",
456
471
  "terminalProgressBarEnabled",
457
472
  "theme",
@@ -459,29 +474,41 @@ var init_settings_keys = __esm({
459
474
  "useAutoModeDuringPlan",
460
475
  "verbose",
461
476
  "viewMode",
477
+ "vimInsertModeRemaps",
462
478
  "voice",
463
479
  "voiceEnabled",
464
480
  "wheelScrollAccelerationEnabled",
465
481
  "workflowKeywordTriggerEnabled",
482
+ "workflowSizeGuideline",
466
483
  "worktree",
467
484
  "wslInheritsWindowsSettings"
468
485
  ];
469
486
  APP_ONLY_KEYS = [
470
- "agents",
471
- "apiKeyHelperTimeoutMs",
472
- "awsLoginRefresh",
473
- "awsRegion",
474
- "awsRetryMode",
475
- "disableNonEssentialModelCalls",
476
- "enabledExperimentalFeatures",
477
- "installMethod",
478
- "pluginGroups",
479
- "pluginRepositoryEnabled",
480
- "pluginsLocalConfig",
481
- "proxy",
487
+ "autoCompactWindow",
488
+ "autoDreamEnabled",
489
+ "autoUploadSessions",
490
+ "breakReminder",
491
+ "daemonColdStart",
492
+ "defaultView",
493
+ "doneMeansMerged",
494
+ "enableWorkflows",
495
+ "feedbackDrafts",
496
+ "isolatePeerMachines",
497
+ "precomputeCompactionEnabled",
498
+ "promptSuggestionEnabled",
499
+ "proxyAuthHelper",
500
+ "quietHours",
501
+ "remote",
502
+ "showMessageTimestamps",
482
503
  "skipAutoPermissionPrompt",
483
- "statsig",
484
- "subagents"
504
+ "skipWorkflowUsageWarning",
505
+ "switchModelsOnFlag",
506
+ "terminalTitleFromRename",
507
+ "todoFeatureEnabled",
508
+ "totalTokensReminder",
509
+ "totalTokensReminderAfterUserTurn",
510
+ "totalTokensReminderBudget",
511
+ "ultracode"
485
512
  ];
486
513
  KNOWN_SETTINGS_KEYS = /* @__PURE__ */ new Set([...SCHEMA_KEYS, ...APP_ONLY_KEYS]);
487
514
  }
@@ -511,6 +538,9 @@ function crashDir() {
511
538
  function manifestPath() {
512
539
  return join(home(), ".cache", "claude-nomad", `push-manifest-${encodeURIComponent(HOST)}.json`);
513
540
  }
541
+ function sharedBaselinePath() {
542
+ return join(home(), ".cache", "claude-nomad", `shared-baseline-${encodeURIComponent(HOST)}.json`);
543
+ }
514
544
  function allSharedLinks(map) {
515
545
  const extras = [];
516
546
  for (const entry of map.sharedDirs ?? []) {
@@ -2715,7 +2745,7 @@ import { join as join45 } from "node:path";
2715
2745
  init_color();
2716
2746
  init_config();
2717
2747
  import { existsSync as existsSync11, lstatSync as lstatSync8, readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
2718
- import { join as join13 } from "node:path";
2748
+ import { basename as basename3, join as join13 } from "node:path";
2719
2749
 
2720
2750
  // src/commands.doctor.format.ts
2721
2751
  init_color();
@@ -2790,6 +2820,61 @@ function readJsonSafe(path, label, section2) {
2790
2820
  }
2791
2821
  }
2792
2822
 
2823
+ // src/extras-sync.diff.ts
2824
+ init_utils();
2825
+ import { execFileSync as execFileSync2 } from "node:child_process";
2826
+ import { relative as relative3 } from "node:path";
2827
+ var NAME_STATUS_DIFF_TIMEOUT_MS = 3e3;
2828
+ function parseNameStatus(stdout) {
2829
+ const fields = stdout.split("\0").filter((f) => f.length > 0);
2830
+ const entries = [];
2831
+ for (let i = 0; i + 1 < fields.length; i += 2) {
2832
+ entries.push({ status: fields[i], path: fields[i + 1] });
2833
+ }
2834
+ return entries;
2835
+ }
2836
+ function labelEntry(entry) {
2837
+ if (entry.status === "D") return `${entry.path} (local only)`;
2838
+ if (entry.status === "A") return `${entry.path} (repo only)`;
2839
+ return entry.path;
2840
+ }
2841
+ function parseDiffOutput(stdout) {
2842
+ return parseNameStatus(stdout).map(labelEntry);
2843
+ }
2844
+ function parseModifiedPaths(stdout, a) {
2845
+ return parseNameStatus(stdout).filter((entry) => entry.status === "M").map((entry) => relative3(a, entry.path));
2846
+ }
2847
+ function runNameStatusDiff(a, b, parse) {
2848
+ try {
2849
+ const stdout = execFileSync2(
2850
+ "git",
2851
+ ["diff", "--no-index", "--no-renames", "-z", "--name-status", a, b],
2852
+ {
2853
+ stdio: ["ignore", "pipe", "pipe"],
2854
+ timeout: NAME_STATUS_DIFF_TIMEOUT_MS
2855
+ }
2856
+ ).toString();
2857
+ return parse(stdout);
2858
+ } catch (err) {
2859
+ const e = err;
2860
+ if (e.status === 1 && e.stdout !== void 0) {
2861
+ return parse(e.stdout.toString());
2862
+ }
2863
+ if (e.code === "ENOENT") {
2864
+ warn(`git not on PATH; divergence check skipped for ${a}`);
2865
+ return [];
2866
+ }
2867
+ warn(`divergence check failed for ${a}: ${e.message ?? String(err)}`);
2868
+ return [];
2869
+ }
2870
+ }
2871
+ function listDivergingFiles(a, b) {
2872
+ return runNameStatusDiff(a, b, parseDiffOutput);
2873
+ }
2874
+ function listDivergingModified(a, b) {
2875
+ return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
2876
+ }
2877
+
2793
2878
  // src/init.classify.ts
2794
2879
  init_config();
2795
2880
  init_utils_json();
@@ -2914,6 +2999,25 @@ function reportRepoState(section2) {
2914
2999
  function repoHasSharedSource(name) {
2915
3000
  return existsSync11(join13(repoHome(), "shared", name));
2916
3001
  }
3002
+ function win32CopyOkRow(name) {
3003
+ return { line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)`, fail: false };
3004
+ }
3005
+ function divergingBasename(line) {
3006
+ return basename3(line.replace(/ \((?:local|repo) only\)$/, ""));
3007
+ }
3008
+ function classifyWin32Copy(name, p) {
3009
+ const sharedPath = join13(repoHome(), "shared", name);
3010
+ if (!existsSync11(sharedPath)) return win32CopyOkRow(name);
3011
+ const diverging = listDivergingFiles(p, sharedPath).filter(
3012
+ (line) => !isDeniedName(ALWAYS_NEVER_SYNC, divergingBasename(line))
3013
+ );
3014
+ if (diverging.length === 0) return win32CopyOkRow(name);
3015
+ return {
3016
+ line: `${yellow(warnGlyph)} ${name}: ${diverging.length} file(s) diverge from shared/${name}`,
3017
+ fail: false,
3018
+ children: diverging
3019
+ };
3020
+ }
2917
3021
  function classifySharedLink(name, p) {
2918
3022
  let stat;
2919
3023
  try {
@@ -2930,10 +3034,7 @@ function classifySharedLink(name, p) {
2930
3034
  }
2931
3035
  if (!stat.isSymbolicLink()) {
2932
3036
  if (process.platform === "win32") {
2933
- return {
2934
- line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)`,
2935
- fail: false
2936
- };
3037
+ return classifyWin32Copy(name, p);
2937
3038
  }
2938
3039
  return {
2939
3040
  line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync); run \`nomad adopt ${name}\` to fix`,
@@ -2967,9 +3068,12 @@ function reportSharedLinks(section2, map) {
2967
3068
  const claude = claudeHome();
2968
3069
  for (const name of allSharedLinks(map)) {
2969
3070
  const p = join13(claude, name);
2970
- const { line, fail: fail2 } = classifySharedLink(name, p);
3071
+ const { line, fail: fail2, children } = classifySharedLink(name, p);
2971
3072
  addItem(section2, line);
2972
3073
  if (fail2) process.exitCode = 1;
3074
+ if (children) {
3075
+ for (const child of children) addChildItem(section2, child);
3076
+ }
2973
3077
  }
2974
3078
  }
2975
3079
  function reportDroppedNamesMigration(section2) {
@@ -3151,61 +3255,6 @@ init_color();
3151
3255
  import { existsSync as existsSync14 } from "node:fs";
3152
3256
  import { join as join16 } from "node:path";
3153
3257
  init_config();
3154
-
3155
- // src/extras-sync.diff.ts
3156
- init_utils();
3157
- import { execFileSync as execFileSync2 } from "node:child_process";
3158
- import { relative as relative3 } from "node:path";
3159
- function parseNameStatus(stdout) {
3160
- const fields = stdout.split("\0").filter((f) => f.length > 0);
3161
- const entries = [];
3162
- for (let i = 0; i + 1 < fields.length; i += 2) {
3163
- entries.push({ status: fields[i], path: fields[i + 1] });
3164
- }
3165
- return entries;
3166
- }
3167
- function labelEntry(entry) {
3168
- if (entry.status === "D") return `${entry.path} (local only)`;
3169
- if (entry.status === "A") return `${entry.path} (repo only)`;
3170
- return entry.path;
3171
- }
3172
- function parseDiffOutput(stdout) {
3173
- return parseNameStatus(stdout).map(labelEntry);
3174
- }
3175
- function parseModifiedPaths(stdout, a) {
3176
- return parseNameStatus(stdout).filter((entry) => entry.status === "M").map((entry) => relative3(a, entry.path));
3177
- }
3178
- function runNameStatusDiff(a, b, parse) {
3179
- try {
3180
- const stdout = execFileSync2(
3181
- "git",
3182
- ["diff", "--no-index", "--no-renames", "-z", "--name-status", a, b],
3183
- {
3184
- stdio: ["ignore", "pipe", "pipe"]
3185
- }
3186
- ).toString();
3187
- return parse(stdout);
3188
- } catch (err) {
3189
- const e = err;
3190
- if (e.status === 1 && e.stdout !== void 0) {
3191
- return parse(e.stdout.toString());
3192
- }
3193
- if (e.code === "ENOENT") {
3194
- warn(`git not on PATH; divergence check skipped for ${a}`);
3195
- return [];
3196
- }
3197
- warn(`divergence check failed for ${a}: ${e.message ?? String(err)}`);
3198
- return [];
3199
- }
3200
- }
3201
- function listDivergingFiles(a, b) {
3202
- return runNameStatusDiff(a, b, parseDiffOutput);
3203
- }
3204
- function listDivergingModified(a, b) {
3205
- return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
3206
- }
3207
-
3208
- // src/commands.doctor.checks.skills.ts
3209
3258
  function stripSideIndicator(line) {
3210
3259
  if (line.endsWith(" (local only)")) return line.slice(0, -" (local only)".length);
3211
3260
  if (line.endsWith(" (repo only)")) return line.slice(0, -" (repo only)".length);
@@ -3213,15 +3262,15 @@ function stripSideIndicator(line) {
3213
3262
  }
3214
3263
  function isGsdDiffLine(line, localBase, sharedBase) {
3215
3264
  const bare = stripSideIndicator(line);
3216
- let relative9;
3265
+ let relative11;
3217
3266
  if (bare.startsWith(localBase + "/")) {
3218
- relative9 = bare.slice(localBase.length + 1);
3267
+ relative11 = bare.slice(localBase.length + 1);
3219
3268
  } else if (bare.startsWith(sharedBase + "/")) {
3220
- relative9 = bare.slice(sharedBase.length + 1);
3269
+ relative11 = bare.slice(sharedBase.length + 1);
3221
3270
  } else {
3222
- relative9 = bare;
3271
+ relative11 = bare;
3223
3272
  }
3224
- return relative9.split("/")[0].startsWith(GSD_PREFIX);
3273
+ return relative11.split("/")[0].startsWith(GSD_PREFIX);
3225
3274
  }
3226
3275
  function reportSkillsDivergence(section2) {
3227
3276
  const sharedSkills = join16(repoHome(), "shared", "skills");
@@ -5127,12 +5176,12 @@ import { join as join37 } from "node:path";
5127
5176
 
5128
5177
  // src/skills-sync.tracked.ts
5129
5178
  init_utils();
5130
- import { basename as basename3 } from "node:path";
5179
+ import { basename as basename4 } from "node:path";
5131
5180
  function trackedRootSkillsAt(ref, repo) {
5132
5181
  try {
5133
5182
  const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
5134
5183
  const entries = raw.split("\0").filter((entry) => entry !== "");
5135
- return new Set(entries.map((entry) => basename3(entry)));
5184
+ return new Set(entries.map((entry) => basename4(entry)));
5136
5185
  } catch {
5137
5186
  return /* @__PURE__ */ new Set();
5138
5187
  }
@@ -5848,8 +5897,8 @@ function sessionSuffix(finding) {
5848
5897
  const sid = sessionIdFromFinding(finding);
5849
5898
  if (sid === null) return "";
5850
5899
  const rawBasename = finding.File.replace(/^.*\//, "");
5851
- const basename4 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
5852
- return basename4 === sid ? "" : ` (session: ${sid})`;
5900
+ const basename5 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
5901
+ return basename5 === sid ? "" : ` (session: ${sid})`;
5853
5902
  }
5854
5903
  function renderFindingBlock(finding, readLine, siblings = []) {
5855
5904
  const lines = [` file: ${finding.File}:${finding.StartLine}${sessionSuffix(finding)}`];
@@ -6815,8 +6864,8 @@ function unstageOne(rel, repo) {
6815
6864
 
6816
6865
  // src/commands.pull.ts
6817
6866
  init_autostash_guard();
6818
- import { existsSync as existsSync42, mkdirSync as mkdirSync14 } from "node:fs";
6819
- import { join as join53 } from "node:path";
6867
+ import { existsSync as existsSync46, mkdirSync as mkdirSync14 } from "node:fs";
6868
+ import { join as join58 } from "node:path";
6820
6869
 
6821
6870
  // src/commands.push.sections.ts
6822
6871
  init_color();
@@ -7251,10 +7300,204 @@ function divergenceCheckExtras(ts, prePostHeads) {
7251
7300
  return divergedCount;
7252
7301
  }
7253
7302
 
7254
- // src/preview.ts
7303
+ // src/links.baseline.ts
7304
+ init_config();
7305
+ import { lstatSync as lstatSync15, readdirSync as readdirSync17 } from "node:fs";
7306
+ import { join as join51, relative as relative7, sep as sep11 } from "node:path";
7307
+ init_utils();
7308
+ var SHARED_BASELINE_KIND = "shared-links-baseline/1";
7309
+ var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
7310
+ function readSharedBaseline() {
7311
+ const parsed = readManifest(sharedBaselinePath());
7312
+ if (parsed === null) return null;
7313
+ if (parsed.scannerVersion !== SHARED_BASELINE_KIND) return null;
7314
+ return parsed;
7315
+ }
7316
+ function baselineKey(claude, abs) {
7317
+ return relative7(claude, abs).split(sep11).join("/");
7318
+ }
7319
+ function addLocalPath(abs, claude, scan) {
7320
+ const key = baselineKey(claude, abs);
7321
+ let st;
7322
+ try {
7323
+ st = lstatSync15(abs, { throwIfNoEntry: false });
7324
+ } catch {
7325
+ scan.declined.push(key);
7326
+ return;
7327
+ }
7328
+ if (st === void 0) return;
7329
+ if (st.isSymbolicLink()) {
7330
+ scan.declined.push(key);
7331
+ return;
7332
+ }
7333
+ if (st.isDirectory()) {
7334
+ collectSharedFiles(abs, claude, scan);
7335
+ return;
7336
+ }
7337
+ scan.files[key] = { size: st.size, mtime: st.mtimeMs };
7338
+ }
7339
+ function collectSharedFiles(dir, claude, scan) {
7340
+ let entries;
7341
+ try {
7342
+ entries = readdirSync17(dir);
7343
+ } catch {
7344
+ scan.declined.push(baselineKey(claude, dir));
7345
+ return;
7346
+ }
7347
+ for (const entry of entries) {
7348
+ const abs = join51(dir, entry);
7349
+ if (isDeniedName(ALWAYS_NEVER_SYNC, entry)) {
7350
+ scan.declined.push(baselineKey(claude, abs));
7351
+ continue;
7352
+ }
7353
+ addLocalPath(abs, claude, scan);
7354
+ }
7355
+ }
7356
+ function enumerateLocalSharedScan(map) {
7357
+ const claude = claudeHome();
7358
+ const scan = { files: {}, declined: [] };
7359
+ for (const name of allSharedLinks(map)) {
7360
+ if (isDeniedName(ALWAYS_NEVER_SYNC, name)) {
7361
+ scan.declined.push(name);
7362
+ continue;
7363
+ }
7364
+ addLocalPath(join51(claude, name), claude, scan);
7365
+ }
7366
+ return scan;
7367
+ }
7368
+ function enumerateLocalSharedFiles(map) {
7369
+ return enumerateLocalSharedScan(map).files;
7370
+ }
7371
+ function buildSharedBaseline(map) {
7372
+ const claude = claudeHome();
7373
+ const files = {};
7374
+ for (const [key, st] of Object.entries(enumerateLocalSharedFiles(map))) {
7375
+ try {
7376
+ files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join51(claude, key)) };
7377
+ } catch {
7378
+ }
7379
+ }
7380
+ return buildManifest(files, SHARED_BASELINE_KIND, SHARED_BASELINE_CONFIG_HASH);
7381
+ }
7382
+ function writeSharedBaseline(map) {
7383
+ if (process.platform !== "win32") return;
7384
+ try {
7385
+ writeManifest(sharedBaselinePath(), buildSharedBaseline(map));
7386
+ } catch (err) {
7387
+ warn(`could not record the shared-config baseline: ${err.message}`);
7388
+ }
7389
+ }
7390
+
7391
+ // src/commands.pull.win32.ts
7392
+ init_config();
7393
+ import { existsSync as existsSync44 } from "node:fs";
7394
+ import { join as join56 } from "node:path";
7395
+
7396
+ // src/git-probe.ts
7397
+ import { execFileSync as execFileSync21 } from "node:child_process";
7398
+ var PROBE_TIMEOUT_MS4 = 1e4;
7399
+ var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
7400
+ function gitProbe(args, repo) {
7401
+ try {
7402
+ return execFileSync21("git", args, {
7403
+ cwd: repo,
7404
+ stdio: ["ignore", "pipe", "pipe"],
7405
+ timeout: PROBE_TIMEOUT_MS4,
7406
+ maxBuffer: PROBE_MAX_BUFFER
7407
+ }).toString();
7408
+ } catch {
7409
+ return null;
7410
+ }
7411
+ }
7412
+
7413
+ // src/links.captures.ts
7255
7414
  init_config();
7256
- import { existsSync as existsSync41 } from "node:fs";
7415
+ import { existsSync as existsSync40, lstatSync as lstatSync16 } from "node:fs";
7257
7416
  import { join as join52 } from "node:path";
7417
+ function planSharedLinkCaptures(map) {
7418
+ if (process.platform !== "win32") return [];
7419
+ if (map === null) return [];
7420
+ const claude = claudeHome();
7421
+ const repo = repoHome();
7422
+ const plan = [];
7423
+ for (const name of allSharedLinks(map)) {
7424
+ const localPath = join52(claude, name);
7425
+ let stat;
7426
+ try {
7427
+ stat = lstatSync16(localPath, { throwIfNoEntry: false });
7428
+ } catch {
7429
+ continue;
7430
+ }
7431
+ if (stat === void 0) continue;
7432
+ if (stat.isSymbolicLink()) continue;
7433
+ const repoPath = join52(repo, "shared", name);
7434
+ if (!existsSync40(repoPath)) continue;
7435
+ plan.push({ name, localPath, repoPath });
7436
+ }
7437
+ return plan;
7438
+ }
7439
+
7440
+ // src/links.deletions.ts
7441
+ init_config();
7442
+ import { existsSync as existsSync41, lstatSync as lstatSync17, rmSync as rmSync17 } from "node:fs";
7443
+ import { join as join53, relative as relative8, resolve as resolve4, sep as sep12 } from "node:path";
7444
+ init_utils();
7445
+ init_utils_fs();
7446
+ function isUnknown(declined, key) {
7447
+ return declined.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
7448
+ }
7449
+ function deletionFor(key, names, claude, sharedRoot) {
7450
+ const segments = key.split("/");
7451
+ const name = segments[0];
7452
+ if (!names.has(name)) return null;
7453
+ if (!existsSync41(join53(claude, name))) return null;
7454
+ if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
7455
+ const repoPath = resolve4(sharedRoot, key);
7456
+ const rel = relative8(join53(sharedRoot, name), repoPath);
7457
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep12}`)) return null;
7458
+ if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
7459
+ return null;
7460
+ if (!existsSync41(repoPath)) return null;
7461
+ return { name, localPath: join53(claude, key), repoPath };
7462
+ }
7463
+ function planSharedLinkDeletions(map) {
7464
+ if (process.platform !== "win32") return [];
7465
+ if (map === null) return [];
7466
+ const baseline = readSharedBaseline();
7467
+ if (baseline === null) return [];
7468
+ const claude = claudeHome();
7469
+ const sharedRoot = join53(repoHome(), "shared");
7470
+ const scan = enumerateLocalSharedScan(map);
7471
+ const present = new Set(Object.keys(scan.files).map((key) => key.toLowerCase()));
7472
+ const declined = scan.declined.map((path) => path.toLowerCase());
7473
+ const names = new Set(allSharedLinks(map));
7474
+ const plan = [];
7475
+ for (const key of Object.keys(baseline.files)) {
7476
+ const folded = key.toLowerCase();
7477
+ if (present.has(folded)) continue;
7478
+ if (isUnknown(declined, folded)) continue;
7479
+ const entry = deletionFor(key, names, claude, sharedRoot);
7480
+ if (entry !== null) plan.push(entry);
7481
+ }
7482
+ return plan;
7483
+ }
7484
+ function applySharedLinkDeletions(map, ts) {
7485
+ const repo = repoHome();
7486
+ for (const entry of planSharedLinkDeletions(map)) {
7487
+ try {
7488
+ if (!lstatSync17(entry.repoPath).isFile()) continue;
7489
+ backupRepoWrite(entry.repoPath, ts, repo);
7490
+ rmSync17(entry.repoPath, { force: true });
7491
+ } catch (err) {
7492
+ warn(`could not remove ${entry.repoPath}: ${err.message}`);
7493
+ }
7494
+ }
7495
+ }
7496
+
7497
+ // src/preview.ts
7498
+ init_config();
7499
+ import { existsSync as existsSync43 } from "node:fs";
7500
+ import { join as join55 } from "node:path";
7258
7501
 
7259
7502
  // node_modules/diff/libesm/diff/base.js
7260
7503
  var Diff = class {
@@ -7529,16 +7772,16 @@ function diffLinesToUnified(oldStr, newStr) {
7529
7772
 
7530
7773
  // src/preview.skills.ts
7531
7774
  init_config();
7532
- import { existsSync as existsSync40, readdirSync as readdirSync17 } from "node:fs";
7533
- import { join as join51 } from "node:path";
7775
+ import { existsSync as existsSync42, readdirSync as readdirSync18 } from "node:fs";
7776
+ import { join as join54 } from "node:path";
7534
7777
  function buildSkillsPreviewSection() {
7535
7778
  const s = section("Skills");
7536
- const sharedSkills = join51(repoHome(), "shared", "skills");
7537
- if (!existsSync40(sharedSkills)) return s;
7538
- const sharedNames = readdirSync17(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
7779
+ const sharedSkills = join54(repoHome(), "shared", "skills");
7780
+ if (!existsSync42(sharedSkills)) return s;
7781
+ const sharedNames = readdirSync18(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
7539
7782
  for (const name of sharedNames) addItem(s, name);
7540
- const localSkills = join51(claudeHome(), "skills");
7541
- const localOnly = existsSync40(localSkills) ? readdirSync17(localSkills, { encoding: "utf8" }).filter(
7783
+ const localSkills = join54(claudeHome(), "skills");
7784
+ const localOnly = existsSync42(localSkills) ? readdirSync18(localSkills, { encoding: "utf8" }).filter(
7542
7785
  (name) => !isSkillExcluded(name) && !sharedNames.includes(name)
7543
7786
  ).length : 0;
7544
7787
  if (localOnly > 0) {
@@ -7560,7 +7803,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
7560
7803
  return lines.join("\n");
7561
7804
  }
7562
7805
  function readJsonOrNull(path) {
7563
- if (!existsSync41(path)) return null;
7806
+ if (!existsSync43(path)) return null;
7564
7807
  try {
7565
7808
  return readJson(path);
7566
7809
  } catch {
@@ -7574,12 +7817,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
7574
7817
  }
7575
7818
  const notes = [];
7576
7819
  const hostOverrides = readJsonOrNull(hostPath);
7577
- if (hostOverrides === null && existsSync41(hostPath)) {
7820
+ if (hostOverrides === null && existsSync43(hostPath)) {
7578
7821
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
7579
7822
  }
7580
7823
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
7581
7824
  const current = readJsonOrNull(settingsPath);
7582
- if (current === null && existsSync41(settingsPath)) {
7825
+ if (current === null && existsSync43(settingsPath)) {
7583
7826
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
7584
7827
  }
7585
7828
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -7595,6 +7838,12 @@ function formatLinkRow(e) {
7595
7838
  if (e.kind === "copy") return `would copy ${e.from} -> ${e.to}`;
7596
7839
  return `${e.kind} ${e.from} -> ${e.to}`;
7597
7840
  }
7841
+ function formatCaptureRow(c) {
7842
+ return `would capture ${c.localPath} -> ${c.repoPath}`;
7843
+ }
7844
+ function formatDeletionRow(d) {
7845
+ return `would remove ${d.repoPath} (gone from ${d.localPath})`;
7846
+ }
7598
7847
  function formatSessionRow(e) {
7599
7848
  return e.kind === "overwrite" ? `overwrite ${e.dst} (from ${e.src})` : e.text;
7600
7849
  }
@@ -7610,20 +7859,30 @@ function buildSettingsSectionForPreview(result) {
7610
7859
  }
7611
7860
  return s;
7612
7861
  }
7613
- function computePreview(ts, map, verb = "pull") {
7862
+ function computePreview(ts, map, verb = "pull", plans) {
7614
7863
  const repo = repoHome();
7615
7864
  const claude = claudeHome();
7616
7865
  console.log(`would pull on host=${HOST} (preview; nothing applied)`);
7617
7866
  console.log("");
7618
7867
  const links = section("Symlinks");
7868
+ const { captures, deletions } = plans ?? {
7869
+ captures: planSharedLinkCaptures(map),
7870
+ deletions: planSharedLinkDeletions(map)
7871
+ };
7872
+ for (const capture of captures) {
7873
+ addItem(links, formatCaptureRow(capture));
7874
+ }
7875
+ for (const deletion of deletions) {
7876
+ addItem(links, formatDeletionRow(deletion));
7877
+ }
7619
7878
  applySharedLinks(ts, map, {
7620
7879
  dryRun: true,
7621
7880
  onPreview: (e) => addItem(links, formatLinkRow(e))
7622
7881
  });
7623
7882
  const settingsResult = previewSettings(
7624
- join52(repo, "shared", "settings.base.json"),
7625
- join52(repo, "hosts", `${HOST}.json`),
7626
- join52(claude, "settings.json")
7883
+ join55(repo, "shared", "settings.base.json"),
7884
+ join55(repo, "hosts", `${HOST}.json`),
7885
+ join55(claude, "settings.json")
7627
7886
  );
7628
7887
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
7629
7888
  const sessions = section("Sessions");
@@ -7639,7 +7898,7 @@ function computePreview(ts, map, verb = "pull") {
7639
7898
  const extras = section("Extras");
7640
7899
  let extrasSkipped = 0;
7641
7900
  let extrasUnmapped = 0;
7642
- if (existsSync41(join52(repo, "path-map.json")) && existsSync41(join52(repo, "shared", "extras"))) {
7901
+ if (existsSync43(join55(repo, "path-map.json")) && existsSync43(join55(repo, "shared", "extras"))) {
7643
7902
  const extrasResult = remapExtrasPull(ts, { dryRun: true });
7644
7903
  for (const entry of extrasResult.wouldPull) {
7645
7904
  addItem(extras, entry);
@@ -7656,6 +7915,135 @@ function computePreview(ts, map, verb = "pull") {
7656
7915
  return { unmapped: remapResult.unmapped, collisions: 0, localOnly };
7657
7916
  }
7658
7917
 
7918
+ // src/commands.pull.win32.ts
7919
+ init_utils();
7920
+ init_utils_json();
7921
+ function readMapForMirror(mapPath) {
7922
+ if (!existsSync44(mapPath)) return { projects: {} };
7923
+ try {
7924
+ return readPathMap(mapPath);
7925
+ } catch {
7926
+ return null;
7927
+ }
7928
+ }
7929
+ function untrackedUnderShared(repo) {
7930
+ const out = gitProbe(["ls-files", "--others", "--exclude-standard", "-z", "--", "shared/"], repo);
7931
+ if (out === null) return null;
7932
+ return new Set(out.split("\0").filter((p) => p !== ""));
7933
+ }
7934
+ function newlyUntracked(before, after) {
7935
+ if (before === null || after === null) return [];
7936
+ return [...after].filter((p) => !before.has(p));
7937
+ }
7938
+ function reconcileSharedLinksBeforePull(repo, ts) {
7939
+ if (process.platform !== "win32") return [];
7940
+ const before = untrackedUnderShared(repo);
7941
+ try {
7942
+ const map = readMapForMirror(join56(repo, "path-map.json"));
7943
+ stageLocalSharedEdits(map, ts);
7944
+ applySharedLinkDeletions(map, ts);
7945
+ } catch (err) {
7946
+ warn(`could not reconcile local shared edits before the pull: ${err.message}`);
7947
+ }
7948
+ return newlyUntracked(before, untrackedUnderShared(repo));
7949
+ }
7950
+ function planSharedReconcileBeforePull(repo) {
7951
+ const map = readMapForMirror(join56(repo, "path-map.json"));
7952
+ return { captures: planSharedLinkCaptures(map), deletions: planSharedLinkDeletions(map) };
7953
+ }
7954
+
7955
+ // src/commands.pull.collision.ts
7956
+ init_config();
7957
+ init_exit_codes();
7958
+ import { existsSync as existsSync45, lstatSync as lstatSync18, readFileSync as readFileSync20, rmSync as rmSync18 } from "node:fs";
7959
+ import { join as join57 } from "node:path";
7960
+ init_utils();
7961
+ var SHARED_PREFIX = /^shared\//;
7962
+ var EXTENSION = /(\.[^.]+)?$/;
7963
+ function localOriginal(repoRel) {
7964
+ return `~/.claude/${repoRel.replace(SHARED_PREFIX, "")}`;
7965
+ }
7966
+ function renameSuggestion(localPath) {
7967
+ const base = localPath.slice(localPath.lastIndexOf("/") + 1);
7968
+ return base.replace(EXTENSION, ".local$1");
7969
+ }
7970
+ function collisionSteps(locals) {
7971
+ const single = locals.length === 1;
7972
+ const target = single ? locals[0] : "each file listed above";
7973
+ const rename = single ? renameSuggestion(locals[0]) : "mine.md becomes mine.local.md";
7974
+ const combine = single ? "combine the two files" : "combine each pair";
7975
+ return `Keep the incoming version (yours is set aside, not deleted):
7976
+ 1. move ${target} outside ~/.claude/
7977
+ 2. nomad pull
7978
+
7979
+ Keep both and merge them yourself:
7980
+ 1. rename ${target} to a name the repo does not use (${rename})
7981
+ 2. nomad pull (brings in the other machine's version)
7982
+ 3. ${combine}, then nomad push`;
7983
+ }
7984
+ function collisionAdvice(single, cleared) {
7985
+ const copies = single ? "nomad's copy" : "nomad's copies";
7986
+ const them = single ? "it" : "them";
7987
+ const fate = cleared ? "which nomad has now removed for you" : `which nomad could not remove (the warning above says why), so delete ${them} by hand if the next pull stops here again`;
7988
+ const moving = single ? "Moving only that copy would not have cleared this anyway: the next pull re-copies your local file over it before fetching. The file to move is the one under ~/.claude/." : "Moving only those copies would not have cleared this anyway: the next pull re-copies your local files over them before fetching. The files to move are the ones under ~/.claude/.";
7989
+ return `Git's advice above refers to ${copies} inside the sync repo, ${fate}. ${moving}`;
7990
+ }
7991
+ function untrackedCollisionRunbookText(repoRelPaths, cleared) {
7992
+ const locals = repoRelPaths.map(localOriginal);
7993
+ const single = locals.length === 1;
7994
+ const claim = single ? "One of those copies has the same name as a file the incoming update also adds:" : "Each of the copies below has the same name as a file the incoming update also adds:";
7995
+ const intact = single ? "Your file is still exactly as you left it." : "Your files are still exactly as you left them.";
7996
+ const listed = locals.map((p) => ` ${p}`).join("\n");
7997
+ return `nomad pull could not fetch. Your machine had unpublished shared-config edits, so nomad copied them into the sync repo first, the way an edit on macOS or Linux is already in the repo before a pull starts. ${claim}
7998
+
7999
+ ${listed}
8000
+
8001
+ Nothing changed. Your ~/.claude/ config is untouched, the update did not land, and nothing was published. ${intact}
8002
+
8003
+ ${collisionAdvice(single, cleared)}
8004
+
8005
+ ` + collisionSteps(locals);
8006
+ }
8007
+ function addedByIncomingUpdate(repo, created) {
8008
+ return created.filter((rel) => gitProbe(["cat-file", "-e", `FETCH_HEAD:${rel}`], repo) !== null);
8009
+ }
8010
+ function isContainedMirrorPath(rel) {
8011
+ return rel.startsWith("shared/") && isSafeRelPath(rel);
8012
+ }
8013
+ function matchesLocalOriginal(repo, rel) {
8014
+ try {
8015
+ const local = join57(claudeHome(), rel.replace(SHARED_PREFIX, ""));
8016
+ return readFileSync20(join57(repo, rel)).equals(readFileSync20(local));
8017
+ } catch {
8018
+ return false;
8019
+ }
8020
+ }
8021
+ function removeMirroredCopies(repo, repoRelPaths) {
8022
+ for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
8023
+ const abs = join57(repo, rel);
8024
+ try {
8025
+ if (lstatSync18(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
8026
+ if (!matchesLocalOriginal(repo, rel)) continue;
8027
+ rmSync18(abs, { force: true });
8028
+ } catch (err) {
8029
+ warn(`could not remove nomad's copy at ${abs}: ${err.message}`);
8030
+ }
8031
+ }
8032
+ return repoRelPaths.every((rel) => !existsSync45(join57(repo, rel)));
8033
+ }
8034
+ function pullWithCollisionRunbook(repo, mirrored) {
8035
+ try {
8036
+ gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
8037
+ } catch (err) {
8038
+ const colliding = addedByIncomingUpdate(repo, mirrored);
8039
+ if (colliding.length === 0) throw err;
8040
+ const cleared = removeMirroredCopies(repo, colliding);
8041
+ throw new NomadFatal(untrackedCollisionRunbookText(colliding, cleared), {
8042
+ code: EXIT.GENERIC_FAILURE
8043
+ });
8044
+ }
8045
+ }
8046
+
7659
8047
  // src/commands.pull.ts
7660
8048
  init_commands_pull_wedge();
7661
8049
 
@@ -7666,9 +8054,9 @@ init_utils_fs();
7666
8054
 
7667
8055
  // src/commands.pull.recovery.git.ts
7668
8056
  init_utils();
7669
- import { execFileSync as execFileSync21 } from "node:child_process";
8057
+ import { execFileSync as execFileSync22 } from "node:child_process";
7670
8058
  function gitCapture(args, cwd) {
7671
- return execFileSync21("git", args, {
8059
+ return execFileSync22("git", args, {
7672
8060
  cwd,
7673
8061
  stdio: ["ignore", "pipe", "pipe"],
7674
8062
  maxBuffer: 64 * 1024 * 1024
@@ -7821,6 +8209,7 @@ function capturePrePostHeads(repo, rebase) {
7821
8209
  }
7822
8210
  function buildWetPullSections(ts, map, prePostHeads) {
7823
8211
  applySharedLinks(ts, map);
8212
+ writeSharedBaseline(map);
7824
8213
  const { label } = regenerateSettings(ts);
7825
8214
  syncSkillsPull(ts, prePostHeads);
7826
8215
  const remapResult = withSpinner("Syncing sessions", () => remapPull(ts));
@@ -7860,22 +8249,6 @@ function handleWedge(repo, forceRemote) {
7860
8249
  const state = wedge === "rebase" ? "mid-rebase" : "mid-merge";
7861
8250
  die(wedgeMarkerRunbookText(state), { code: EXIT.CONFLICT });
7862
8251
  }
7863
- function readMapForMirror(mapPath) {
7864
- if (!existsSync42(mapPath)) return { projects: {} };
7865
- try {
7866
- return readPathMap(mapPath);
7867
- } catch {
7868
- return null;
7869
- }
7870
- }
7871
- function mirrorSharedLinksBeforePull(repo, ts) {
7872
- if (process.platform !== "win32") return;
7873
- try {
7874
- stageLocalSharedEdits(readMapForMirror(join53(repo, "path-map.json")), ts);
7875
- } catch (err) {
7876
- warn(`could not stage local shared edits before the pull: ${err.message}`);
7877
- }
7878
- }
7879
8252
  function runPullCore(opts = {}) {
7880
8253
  const dryRun = opts.dryRun === true;
7881
8254
  const forceRemote = opts.forceRemote === true;
@@ -7885,7 +8258,7 @@ function runPullCore(opts = {}) {
7885
8258
  const ts = freshBackupTs(backup);
7886
8259
  handleWedge(repo, forceRemote);
7887
8260
  if (!dryRun) {
7888
- const backupRoot = join53(backup, ts);
8261
+ const backupRoot = join58(backup, ts);
7889
8262
  try {
7890
8263
  mkdirSync14(backupRoot, { recursive: true });
7891
8264
  } catch (err) {
@@ -7897,16 +8270,17 @@ function runPullCore(opts = {}) {
7897
8270
  dryRun ? `pulling on host=${HOST} (backup=${ts}; dry-run)` : `pull on host=${HOST} (backup=${ts})`
7898
8271
  );
7899
8272
  }
7900
- if (!dryRun && !forceRemote) mirrorSharedLinksBeforePull(repo, ts);
8273
+ const mirrored = !dryRun && !forceRemote ? reconcileSharedLinksBeforePull(repo, ts) : [];
8274
+ const sharedPlans = dryRun ? planSharedReconcileBeforePull(repo) : void 0;
7901
8275
  const prePostHeads = capturePrePostHeads(repo, () => {
7902
- gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
8276
+ pullWithCollisionRunbook(repo, mirrored);
7903
8277
  });
7904
8278
  assertNoAutostashConflict(repo, "nomad pull");
7905
- const mapPath = join53(repo, "path-map.json");
7906
- const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
8279
+ const mapPath = join58(repo, "path-map.json");
8280
+ const map = existsSync46(mapPath) ? readPathMap(mapPath) : { projects: {} };
7907
8281
  const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
7908
8282
  if (dryRun) {
7909
- computePreview(ts, map, "pull");
8283
+ computePreview(ts, map, "pull", sharedPlans);
7910
8284
  return { tag: "dry" };
7911
8285
  }
7912
8286
  const { sections, localOnly, settingsLabel, unmapped, extrasSkipped } = buildWetPullSections(
@@ -7928,8 +8302,8 @@ function runPullCore(opts = {}) {
7928
8302
  }
7929
8303
  function cmdPull(opts = {}) {
7930
8304
  const repo = repoHome();
7931
- if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
7932
- if (!existsSync42(join53(repo, "shared", "settings.base.json"))) {
8305
+ if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
8306
+ if (!existsSync46(join58(repo, "shared", "settings.base.json"))) {
7933
8307
  die("repo not initialized; run 'nomad init' to scaffold");
7934
8308
  }
7935
8309
  const handle = acquireLock("pull");
@@ -7952,13 +8326,13 @@ function cmdPull(opts = {}) {
7952
8326
 
7953
8327
  // src/commands.push.ts
7954
8328
  init_config();
7955
- import { existsSync as existsSync46 } from "node:fs";
7956
- import { join as join58 } from "node:path";
8329
+ import { existsSync as existsSync50 } from "node:fs";
8330
+ import { join as join63 } from "node:path";
7957
8331
 
7958
8332
  // src/commands.push.selection.ts
7959
8333
  init_config();
7960
- import { existsSync as existsSync43, statSync as statSync11 } from "node:fs";
7961
- import { join as join54 } from "node:path";
8334
+ import { existsSync as existsSync47, statSync as statSync11 } from "node:fs";
8335
+ import { join as join59 } from "node:path";
7962
8336
  init_utils_json();
7963
8337
  function buildCurrentMap(map) {
7964
8338
  const current = {};
@@ -7967,8 +8341,8 @@ function buildCurrentMap(map) {
7967
8341
  for (const [, hostMap] of Object.entries(map.projects)) {
7968
8342
  const localPath = hostMap[HOST];
7969
8343
  if (!localPath) continue;
7970
- const localDir = join54(claude, "projects", encodePath(localPath));
7971
- if (!existsSync43(localDir)) continue;
8344
+ const localDir = join59(claude, "projects", encodePath(localPath));
8345
+ if (!existsSync47(localDir)) continue;
7972
8346
  for (const f of enumerateSourceFiles(localDir)) {
7973
8347
  const st = statSync11(f);
7974
8348
  current[f] = { size: st.size, mtime: st.mtimeMs };
@@ -7999,7 +8373,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
7999
8373
  };
8000
8374
  }
8001
8375
  function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
8002
- const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
8376
+ const map = existsSync47(mapPath) ? readPathMap(mapPath) : null;
8003
8377
  const { selection, newManifest } = computePushSelection(
8004
8378
  map,
8005
8379
  old,
@@ -8101,14 +8475,14 @@ function enforceAllowList(statusPorcelain, map) {
8101
8475
 
8102
8476
  // src/commands.push.settings.ts
8103
8477
  init_config();
8104
- import { existsSync as existsSync44 } from "node:fs";
8105
- import { join as join55 } from "node:path";
8478
+ import { existsSync as existsSync48 } from "node:fs";
8479
+ import { join as join60 } from "node:path";
8106
8480
  init_utils();
8107
8481
  init_utils_fs();
8108
8482
  init_utils_json();
8109
8483
  function stripGsdHooksFromBase(repo, backup) {
8110
- const basePath = join55(repo, "shared", "settings.base.json");
8111
- if (!existsSync44(basePath)) return;
8484
+ const basePath = join60(repo, "shared", "settings.base.json");
8485
+ if (!existsSync48(basePath)) return;
8112
8486
  let base;
8113
8487
  try {
8114
8488
  base = readJson(basePath);
@@ -8122,14 +8496,14 @@ function stripGsdHooksFromBase(repo, backup) {
8122
8496
  writeJsonAtomic(basePath, stripped);
8123
8497
  }
8124
8498
  function reportSettingsAheadDrift(repo) {
8125
- const basePath = join55(repo, "shared", "settings.base.json");
8126
- if (!existsSync44(basePath)) return;
8127
- const settingsPath = join55(claudeHome(), "settings.json");
8128
- if (!existsSync44(settingsPath)) return;
8499
+ const basePath = join60(repo, "shared", "settings.base.json");
8500
+ if (!existsSync48(basePath)) return;
8501
+ const settingsPath = join60(claudeHome(), "settings.json");
8502
+ if (!existsSync48(settingsPath)) return;
8129
8503
  try {
8130
8504
  const base = readJson(basePath);
8131
- const hostPath = join55(repo, "hosts", `${HOST}.json`);
8132
- const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
8505
+ const hostPath = join60(repo, "hosts", `${HOST}.json`);
8506
+ const overrides = existsSync48(hostPath) ? readJson(hostPath) : {};
8133
8507
  const merged = deepMerge(base, overrides);
8134
8508
  const settings = readJson(settingsPath);
8135
8509
  const { ahead } = classifySettingsDrift(merged, settings);
@@ -8146,12 +8520,12 @@ function reportSettingsAheadDrift(repo) {
8146
8520
  // src/commands.push.guards.ts
8147
8521
  init_push_checks();
8148
8522
  init_utils();
8149
- import { join as join56, relative as relative7 } from "node:path";
8523
+ import { join as join61, relative as relative9 } from "node:path";
8150
8524
  function guardGitlinks(repo) {
8151
- const gitlinks = findGitlinks(join56(repo, "shared"));
8525
+ const gitlinks = findGitlinks(join61(repo, "shared"));
8152
8526
  if (gitlinks.length === 0) return;
8153
8527
  for (const p of gitlinks) {
8154
- const rel = relative7(repo, p).replaceAll("\\", "/");
8528
+ const rel = relative9(repo, p).replaceAll("\\", "/");
8155
8529
  fail(`gitlink: ${rel} would push as submodule (run: rm -rf ${rel} or remove the nested repo)`);
8156
8530
  }
8157
8531
  const noun = gitlinks.length === 1 ? "entry" : "entries";
@@ -8180,7 +8554,7 @@ init_config();
8180
8554
 
8181
8555
  // src/push-global-config.ts
8182
8556
  init_config();
8183
- import { execFileSync as execFileSync22 } from "node:child_process";
8557
+ import { execFileSync as execFileSync23 } from "node:child_process";
8184
8558
  var STATUS_LABELS = {
8185
8559
  A: "add",
8186
8560
  M: "modify",
@@ -8220,7 +8594,7 @@ function isInScope(filePath, exactPrefixes, dirPrefixes) {
8220
8594
  }
8221
8595
  function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
8222
8596
  const args = opts.staged ? ["diff", "--cached", "--name-status", "-z"] : ["diff", "HEAD", "--name-status", "-z"];
8223
- const raw = execFileSync22("git", args, {
8597
+ const raw = execFileSync23("git", args, {
8224
8598
  cwd: repoHome2,
8225
8599
  stdio: ["ignore", "pipe", "pipe"]
8226
8600
  }).toString();
@@ -8258,9 +8632,9 @@ init_color();
8258
8632
  init_config();
8259
8633
  init_config_sharedDirs_guard();
8260
8634
  import { randomBytes as randomBytes4 } from "node:crypto";
8261
- import { copyFileSync, existsSync as existsSync45, lstatSync as lstatSync15, mkdirSync as mkdirSync15, readdirSync as readdirSync18, rmSync as rmSync17 } from "node:fs";
8635
+ import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync19, mkdirSync as mkdirSync15, readdirSync as readdirSync19, rmSync as rmSync19 } from "node:fs";
8262
8636
  import { homedir as homedir7 } from "node:os";
8263
- import { join as join57, relative as relative8, sep as sep11 } from "node:path";
8637
+ import { join as join62, relative as relative10, sep as sep13 } from "node:path";
8264
8638
  init_push_leak_verdict();
8265
8639
  init_push_gitleaks();
8266
8640
  init_utils_fs();
@@ -8268,11 +8642,11 @@ init_utils_json();
8268
8642
  var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
8269
8643
  function stageSessionDir(localDir, dstDir, changed) {
8270
8644
  if (changed !== void 0) {
8271
- const prefix = `${localDir}${sep11}`;
8645
+ const prefix = `${localDir}${sep13}`;
8272
8646
  const matching = [...changed].filter((p) => p.startsWith(prefix));
8273
8647
  if (matching.length === 0) return false;
8274
8648
  for (const src of matching) {
8275
- copyFileAtomic(src, join57(dstDir, relative8(localDir, src)));
8649
+ copyFileAtomic(src, join62(dstDir, relative10(localDir, src)));
8276
8650
  }
8277
8651
  return true;
8278
8652
  }
@@ -8288,14 +8662,14 @@ function stageSessions(tmpRoot, map, changed) {
8288
8662
  if (!p || p === "TBD") continue;
8289
8663
  reverse.set(encodePath(p), logical);
8290
8664
  }
8291
- const localProjects = join57(claudeHome(), "projects");
8292
- if (!existsSync45(localProjects)) return 0;
8665
+ const localProjects = join62(claudeHome(), "projects");
8666
+ if (!existsSync49(localProjects)) return 0;
8293
8667
  let staged = 0;
8294
- for (const dir of readdirSync18(localProjects)) {
8668
+ for (const dir of readdirSync19(localProjects)) {
8295
8669
  const logical = reverse.get(dir);
8296
8670
  if (!logical) continue;
8297
- const localDir = join57(localProjects, dir);
8298
- const dstDir = join57(tmpRoot, "shared", "projects", logical);
8671
+ const localDir = join62(localProjects, dir);
8672
+ const dstDir = join62(tmpRoot, "shared", "projects", logical);
8299
8673
  if (stageSessionDir(localDir, dstDir, changed)) staged++;
8300
8674
  }
8301
8675
  return staged;
@@ -8311,9 +8685,9 @@ function stageExtras(tmpRoot, map) {
8311
8685
  if (!localRoot || localRoot === "TBD") continue;
8312
8686
  for (const dirname14 of dirnames) {
8313
8687
  if (!whitelist.includes(dirname14)) continue;
8314
- const src = join57(localRoot, dirname14);
8315
- if (!existsSync45(src)) continue;
8316
- const dst = join57(tmpRoot, "shared", "extras", logical, dirname14);
8688
+ const src = join62(localRoot, dirname14);
8689
+ if (!existsSync49(src)) continue;
8690
+ const dst = join62(tmpRoot, "shared", "extras", logical, dirname14);
8317
8691
  copyExtras(src, dst);
8318
8692
  staged++;
8319
8693
  }
@@ -8321,19 +8695,19 @@ function stageExtras(tmpRoot, map) {
8321
8695
  return staged;
8322
8696
  }
8323
8697
  function stageSkills(tmpRoot) {
8324
- const localSkills = join57(claudeHome(), "skills");
8325
- const stat = lstatSync15(localSkills, { throwIfNoEntry: false });
8698
+ const localSkills = join62(claudeHome(), "skills");
8699
+ const stat = lstatSync19(localSkills, { throwIfNoEntry: false });
8326
8700
  if (stat === void 0 || stat.isSymbolicLink()) return 0;
8327
- const names = readdirSync18(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
8701
+ const names = readdirSync19(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
8328
8702
  if (names.length === 0) return 0;
8329
- copySkillsPush(localSkills, join57(tmpRoot, "shared", "skills"));
8703
+ copySkillsPush(localSkills, join62(tmpRoot, "shared", "skills"));
8330
8704
  return names.length;
8331
8705
  }
8332
8706
  function previewPushLeaks(map, opts = {}) {
8333
- const cacheDir = join57(homedir7(), ".cache", "claude-nomad");
8707
+ const cacheDir = join62(homedir7(), ".cache", "claude-nomad");
8334
8708
  mkdirSync15(cacheDir, { recursive: true });
8335
8709
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
8336
- const tmpRoot = join57(cacheDir, `push-preview-tree-${stamp}`);
8710
+ const tmpRoot = join62(cacheDir, `push-preview-tree-${stamp}`);
8337
8711
  try {
8338
8712
  const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
8339
8713
  const extrasCount = stageExtras(tmpRoot, map);
@@ -8341,9 +8715,9 @@ function previewPushLeaks(map, opts = {}) {
8341
8715
  if (sessionCount + extrasCount + skillCount === 0) {
8342
8716
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
8343
8717
  }
8344
- const ignoreFile = join57(repoHome(), ".gitleaksignore");
8345
- if (existsSync45(ignoreFile)) {
8346
- copyFileSync(ignoreFile, join57(tmpRoot, ".gitleaksignore"));
8718
+ const ignoreFile = join62(repoHome(), ".gitleaksignore");
8719
+ if (existsSync49(ignoreFile)) {
8720
+ copyFileSync(ignoreFile, join62(tmpRoot, ".gitleaksignore"));
8347
8721
  }
8348
8722
  let findings;
8349
8723
  try {
@@ -8356,7 +8730,7 @@ function previewPushLeaks(map, opts = {}) {
8356
8730
  }
8357
8731
  return verdictFromFindings(findings);
8358
8732
  } finally {
8359
- rmSync17(tmpRoot, { recursive: true, force: true });
8733
+ rmSync19(tmpRoot, { recursive: true, force: true });
8360
8734
  }
8361
8735
  }
8362
8736
 
@@ -8471,7 +8845,7 @@ async function runPushCore(opts = {}) {
8471
8845
  const scannerVersion = probeGitleaks();
8472
8846
  const configHash = computeConfigHash();
8473
8847
  const old = readManifest(manifestPath());
8474
- const mapPath = join58(repo, "path-map.json");
8848
+ const mapPath = join63(repo, "path-map.json");
8475
8849
  const { map, selection, newManifest } = loadSelectionForPush(
8476
8850
  mapPath,
8477
8851
  old,
@@ -8523,7 +8897,7 @@ async function cmdPush(opts = {}) {
8523
8897
  opts.allowRule
8524
8898
  );
8525
8899
  const repo = repoHome();
8526
- if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
8900
+ if (!existsSync50(repo)) die(`repo not cloned at ${repo}`);
8527
8901
  const handle = acquireLock("push");
8528
8902
  if (handle === null) process.exit(0);
8529
8903
  try {
@@ -8541,8 +8915,8 @@ async function cmdPush(opts = {}) {
8541
8915
  }
8542
8916
 
8543
8917
  // src/commands.sync.ts
8544
- import { existsSync as existsSync47 } from "node:fs";
8545
- import { join as join59 } from "node:path";
8918
+ import { existsSync as existsSync51 } from "node:fs";
8919
+ import { join as join64 } from "node:path";
8546
8920
  init_config();
8547
8921
  init_color();
8548
8922
  init_utils();
@@ -8676,8 +9050,8 @@ async function cmdSync(opts = {}) {
8676
9050
  const dryRun = opts.dryRun === true;
8677
9051
  const verbose = opts.verbose === true;
8678
9052
  const repo = repoHome();
8679
- if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
8680
- if (!existsSync47(join59(repo, "shared", "settings.base.json"))) {
9053
+ if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
9054
+ if (!existsSync51(join64(repo, "shared", "settings.base.json"))) {
8681
9055
  die("repo not initialized; run 'nomad init' to scaffold");
8682
9056
  }
8683
9057
  const handle = acquireLock("sync");
@@ -8701,9 +9075,9 @@ async function cmdSync(opts = {}) {
8701
9075
  }
8702
9076
 
8703
9077
  // src/commands.update.ts
8704
- import { execFileSync as execFileSync23 } from "node:child_process";
9078
+ import { execFileSync as execFileSync24 } from "node:child_process";
8705
9079
  init_utils();
8706
- function readInstalledVersion(run = execFileSync23) {
9080
+ function readInstalledVersion(run = execFileSync24) {
8707
9081
  const isWin = process.platform === "win32";
8708
9082
  try {
8709
9083
  return run(isWin ? "nomad.cmd" : "nomad", ["--version"], {
@@ -8714,7 +9088,7 @@ function readInstalledVersion(run = execFileSync23) {
8714
9088
  return null;
8715
9089
  }
8716
9090
  }
8717
- function cmdUpdate(currentVersion, run = execFileSync23) {
9091
+ function cmdUpdate(currentVersion, run = execFileSync24) {
8718
9092
  console.log(`Updating claude-nomad v${currentVersion}...`);
8719
9093
  const isWin = process.platform === "win32";
8720
9094
  try {
@@ -8753,8 +9127,8 @@ init_config();
8753
9127
  // src/crash-report.write.ts
8754
9128
  init_config();
8755
9129
  import { randomBytes as randomBytes5 } from "node:crypto";
8756
- import { mkdirSync as mkdirSync16, readdirSync as readdirSync19, statSync as statSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
8757
- import { join as join61 } from "node:path";
9130
+ import { mkdirSync as mkdirSync16, readdirSync as readdirSync20, statSync as statSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
9131
+ import { join as join66 } from "node:path";
8758
9132
 
8759
9133
  // src/crash-report.ts
8760
9134
  var CRASH_MAX_STACK_LINES = 50;
@@ -8837,17 +9211,17 @@ function buildCrashReport(input) {
8837
9211
  }
8838
9212
 
8839
9213
  // src/crash-report.redact.ts
8840
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync18, writeFileSync as writeFileSync10 } from "node:fs";
9214
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync20, writeFileSync as writeFileSync10 } from "node:fs";
8841
9215
  import { tmpdir as tmpdir2 } from "node:os";
8842
- import { join as join60 } from "node:path";
9216
+ import { join as join65 } from "node:path";
8843
9217
  init_push_gitleaks_scan();
8844
9218
  var CRASH_SCAN_TIMEOUT_MS = 3e3;
8845
9219
  var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
8846
9220
  function redactWithGitleaks(text, scan = scanFile) {
8847
9221
  let dir;
8848
9222
  try {
8849
- dir = mkdtempSync3(join60(tmpdir2(), "nomad-crash-scan-"));
8850
- const tmp = join60(dir, "crash.txt");
9223
+ dir = mkdtempSync3(join65(tmpdir2(), "nomad-crash-scan-"));
9224
+ const tmp = join65(dir, "crash.txt");
8851
9225
  writeFileSync10(tmp, text, { mode: 384 });
8852
9226
  const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
8853
9227
  if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
@@ -8857,7 +9231,7 @@ function redactWithGitleaks(text, scan = scanFile) {
8857
9231
  } finally {
8858
9232
  if (dir !== void 0) {
8859
9233
  try {
8860
- rmSync18(dir, { recursive: true, force: true });
9234
+ rmSync20(dir, { recursive: true, force: true });
8861
9235
  } catch {
8862
9236
  }
8863
9237
  }
@@ -8870,9 +9244,9 @@ init_utils();
8870
9244
  var CRASH_RETENTION_KEEP = 20;
8871
9245
  function listCrashFiles(dir = crashDir()) {
8872
9246
  try {
8873
- return readdirSync19(dir).flatMap((name) => {
9247
+ return readdirSync20(dir).flatMap((name) => {
8874
9248
  try {
8875
- return [{ name, mtimeMs: statSync12(join61(dir, name)).mtimeMs }];
9249
+ return [{ name, mtimeMs: statSync12(join66(dir, name)).mtimeMs }];
8876
9250
  } catch {
8877
9251
  return [];
8878
9252
  }
@@ -8886,7 +9260,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
8886
9260
  const targets = prunableByCount(files, keep);
8887
9261
  for (const name of targets) {
8888
9262
  try {
8889
- unlinkSync2(join61(dir, name));
9263
+ unlinkSync2(join66(dir, name));
8890
9264
  } catch {
8891
9265
  }
8892
9266
  }
@@ -8894,7 +9268,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
8894
9268
  function writeCrashReport(text, dir = crashDir()) {
8895
9269
  mkdirSync16(dir, { recursive: true, mode: 448 });
8896
9270
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes5(4).toString("hex")}`;
8897
- const path = join61(dir, `crash-${stamp}.txt`);
9271
+ const path = join66(dir, `crash-${stamp}.txt`);
8898
9272
  writeFileSync11(path, text, { mode: 384 });
8899
9273
  pruneCrashDir(dir);
8900
9274
  return path;
@@ -8926,18 +9300,18 @@ function handleCrash(err, argv, opts) {
8926
9300
 
8927
9301
  // src/diff.ts
8928
9302
  init_config();
8929
- import { existsSync as existsSync48 } from "node:fs";
8930
- import { join as join62 } from "node:path";
9303
+ import { existsSync as existsSync52 } from "node:fs";
9304
+ import { join as join67 } from "node:path";
8931
9305
  init_utils();
8932
9306
  init_utils_fs();
8933
9307
  init_utils_json();
8934
9308
  function cmdDiff() {
8935
9309
  try {
8936
9310
  const repo = repoHome();
8937
- if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
9311
+ if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
8938
9312
  const ts = freshBackupTs(backupBase());
8939
- const mapPath = join62(repo, "path-map.json");
8940
- const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
9313
+ const mapPath = join67(repo, "path-map.json");
9314
+ const map = existsSync52(mapPath) ? readPathMap(mapPath) : { projects: {} };
8941
9315
  divergenceCheckExtras(ts);
8942
9316
  computePreview(ts, map, "diff");
8943
9317
  } catch (err) {
@@ -8952,19 +9326,19 @@ function cmdDiff() {
8952
9326
 
8953
9327
  // src/init.ts
8954
9328
  init_config();
8955
- import { existsSync as existsSync50, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
8956
- import { join as join64 } from "node:path";
9329
+ import { existsSync as existsSync54, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
9330
+ import { join as join69 } from "node:path";
8957
9331
 
8958
9332
  // src/init.gh-onboard.ts
8959
9333
  init_config();
8960
- import { execFileSync as execFileSync24 } from "node:child_process";
9334
+ import { execFileSync as execFileSync25 } from "node:child_process";
8961
9335
  init_utils();
8962
9336
  var DEFAULT_REPO_NAME = "claude-nomad-config";
8963
9337
  function isValidRepoName(name) {
8964
9338
  return /^[A-Za-z0-9._-]{1,100}$/.test(name);
8965
9339
  }
8966
9340
  var GH_NETWORK_TIMEOUT_MS = 3e4;
8967
- function ensureOriginRepo(repoName, run = execFileSync24) {
9341
+ function ensureOriginRepo(repoName, run = execFileSync25) {
8968
9342
  if (!isValidRepoName(repoName)) {
8969
9343
  die(
8970
9344
  `invalid repo name: ${JSON.stringify(repoName)}. Use only letters, digits, hyphens, underscores, and dots (1-100 chars).`
@@ -9035,33 +9409,33 @@ init_config();
9035
9409
  init_utils();
9036
9410
  init_utils_fs();
9037
9411
  init_utils_json();
9038
- import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync49, rmSync as rmSync19, statSync as statSync13 } from "node:fs";
9039
- import { join as join63 } from "node:path";
9412
+ import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync53, rmSync as rmSync21, statSync as statSync13 } from "node:fs";
9413
+ import { join as join68 } from "node:path";
9040
9414
  function snapshotIntoShared(map) {
9041
9415
  const repo = repoHome();
9042
9416
  const claude = claudeHome();
9043
9417
  for (const name of allSharedLinks(map)) {
9044
- const src = join63(claude, name);
9045
- if (!existsSync49(src)) continue;
9046
- const dst = join63(repo, "shared", name);
9418
+ const src = join68(claude, name);
9419
+ if (!existsSync53(src)) continue;
9420
+ const dst = join68(repo, "shared", name);
9047
9421
  if (statSync13(src).isDirectory()) {
9048
- const gk = join63(dst, ".gitkeep");
9049
- if (existsSync49(gk)) rmSync19(gk);
9422
+ const gk = join68(dst, ".gitkeep");
9423
+ if (existsSync53(gk)) rmSync21(gk);
9050
9424
  cpSync10(src, dst, { recursive: true, force: false, errorOnExist: true });
9051
9425
  } else {
9052
9426
  copyFileSync2(src, dst);
9053
9427
  }
9054
9428
  log(`snapshotted shared/${name} from ${src}`);
9055
9429
  }
9056
- const userSettings = join63(claude, "settings.json");
9057
- if (existsSync49(userSettings)) {
9430
+ const userSettings = join68(claude, "settings.json");
9431
+ if (existsSync53(userSettings)) {
9058
9432
  let parsed;
9059
9433
  try {
9060
9434
  parsed = readJson(userSettings);
9061
9435
  } catch (err) {
9062
9436
  return die(`malformed ${userSettings}: ${err.message}`);
9063
9437
  }
9064
- const hostFile = join63(repo, "hosts", `${HOST}.json`);
9438
+ const hostFile = join68(repo, "hosts", `${HOST}.json`);
9065
9439
  writeJsonAtomic(hostFile, parsed);
9066
9440
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
9067
9441
  }
@@ -9075,14 +9449,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
9075
9449
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
9076
9450
  function preflightConflict(repoHome2) {
9077
9451
  const candidates = [
9078
- join64(repoHome2, "shared", "settings.base.json"),
9079
- join64(repoHome2, "shared", "CLAUDE.md"),
9080
- join64(repoHome2, "path-map.json"),
9081
- join64(repoHome2, "hosts"),
9082
- join64(repoHome2, "shared")
9452
+ join69(repoHome2, "shared", "settings.base.json"),
9453
+ join69(repoHome2, "shared", "CLAUDE.md"),
9454
+ join69(repoHome2, "path-map.json"),
9455
+ join69(repoHome2, "hosts"),
9456
+ join69(repoHome2, "shared")
9083
9457
  ];
9084
9458
  for (const c of candidates) {
9085
- if (existsSync50(c)) return c;
9459
+ if (existsSync54(c)) return c;
9086
9460
  }
9087
9461
  return null;
9088
9462
  }
@@ -9100,27 +9474,27 @@ function cmdInit(opts = {}) {
9100
9474
  die(`already initialized; refusing to clobber ${conflict}`);
9101
9475
  }
9102
9476
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
9103
- mkdirSync17(join64(repo, "shared"), { recursive: true });
9104
- mkdirSync17(join64(repo, "hosts"), { recursive: true });
9477
+ mkdirSync17(join69(repo, "shared"), { recursive: true });
9478
+ mkdirSync17(join69(repo, "hosts"), { recursive: true });
9105
9479
  for (const name of SHARED_KEEP_DIRS) {
9106
- mkdirSync17(join64(repo, "shared", name), { recursive: true });
9480
+ mkdirSync17(join69(repo, "shared", name), { recursive: true });
9107
9481
  }
9108
- const userClaudeMd = join64(claude, "CLAUDE.md");
9109
- if (!snapshot || !existsSync50(userClaudeMd)) {
9110
- writeFileSync12(join64(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
9482
+ const userClaudeMd = join69(claude, "CLAUDE.md");
9483
+ if (!snapshot || !existsSync54(userClaudeMd)) {
9484
+ writeFileSync12(join69(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
9111
9485
  item("created shared/CLAUDE.md");
9112
9486
  }
9113
9487
  for (const name of SHARED_KEEP_DIRS) {
9114
- writeFileSync12(join64(repo, "shared", name, ".gitkeep"), "");
9488
+ writeFileSync12(join69(repo, "shared", name, ".gitkeep"), "");
9115
9489
  item(`created shared/${name}/.gitkeep`);
9116
9490
  }
9117
- writeFileSync12(join64(repo, "hosts", ".gitkeep"), "");
9491
+ writeFileSync12(join69(repo, "hosts", ".gitkeep"), "");
9118
9492
  item("created hosts/.gitkeep");
9119
- writeJsonAtomic(join64(repo, "shared", "settings.base.json"), {});
9493
+ writeJsonAtomic(join69(repo, "shared", "settings.base.json"), {});
9120
9494
  item("created shared/settings.base.json");
9121
- writeJsonAtomic(join64(repo, "path-map.json"), { projects: {} });
9495
+ writeJsonAtomic(join69(repo, "path-map.json"), { projects: {} });
9122
9496
  item("created path-map.json");
9123
- writeFileSync12(join64(repo, ".gitattributes"), GITATTRIBUTES);
9497
+ writeFileSync12(join69(repo, ".gitattributes"), GITATTRIBUTES);
9124
9498
  item("created .gitattributes");
9125
9499
  if (snapshot) {
9126
9500
  snapshotIntoShared({ projects: {} });
@@ -9186,21 +9560,21 @@ function maybeDisableRepoActions(repoHome2, run) {
9186
9560
  // src/init.prompt.ts
9187
9561
  init_config();
9188
9562
  init_utils();
9189
- import { existsSync as existsSync51, readdirSync as readdirSync20, statSync as statSync14 } from "node:fs";
9190
- import { join as join65 } from "node:path";
9563
+ import { existsSync as existsSync55, readdirSync as readdirSync21, statSync as statSync14 } from "node:fs";
9564
+ import { join as join70 } from "node:path";
9191
9565
  import { createInterface as createInterface3 } from "node:readline/promises";
9192
9566
  function nonEmptyExists(path) {
9193
- if (!existsSync51(path)) return false;
9567
+ if (!existsSync55(path)) return false;
9194
9568
  try {
9195
- if (statSync14(path).isDirectory()) return readdirSync20(path).length > 0;
9569
+ if (statSync14(path).isDirectory()) return readdirSync21(path).length > 0;
9196
9570
  return true;
9197
9571
  } catch {
9198
9572
  return false;
9199
9573
  }
9200
9574
  }
9201
9575
  function hasExistingClaudeConfig(claudeHome2) {
9202
- if (existsSync51(join65(claudeHome2, "settings.json"))) return true;
9203
- return SHARED_LINKS.some((name) => nonEmptyExists(join65(claudeHome2, name)));
9576
+ if (existsSync55(join70(claudeHome2, "settings.json"))) return true;
9577
+ return SHARED_LINKS.some((name) => nonEmptyExists(join70(claudeHome2, name)));
9204
9578
  }
9205
9579
  async function confirmSnapshotDefault(claudeHome2) {
9206
9580
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -9507,7 +9881,7 @@ function parseSyncArgs(argv) {
9507
9881
  // package.json
9508
9882
  var package_default = {
9509
9883
  name: "claude-nomad",
9510
- version: "0.63.1",
9884
+ version: "0.64.0",
9511
9885
  type: "module",
9512
9886
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
9513
9887
  keywords: [
@@ -9751,15 +10125,15 @@ var DEFAULT_HELP = [
9751
10125
  init_config();
9752
10126
  init_utils();
9753
10127
  init_utils_json();
9754
- import { existsSync as existsSync52, readFileSync as readFileSync20, readdirSync as readdirSync21 } from "node:fs";
9755
- import { join as join66 } from "node:path";
10128
+ import { existsSync as existsSync56, readFileSync as readFileSync21, readdirSync as readdirSync22 } from "node:fs";
10129
+ import { join as join71 } from "node:path";
9756
10130
  function resumeCmd(sessionId) {
9757
10131
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
9758
10132
  fail(`invalid session id: ${sessionId}`);
9759
10133
  process.exit(1);
9760
10134
  }
9761
- const projectsRoot = join66(claudeHome(), "projects");
9762
- if (!existsSync52(projectsRoot)) {
10135
+ const projectsRoot = join71(claudeHome(), "projects");
10136
+ if (!existsSync56(projectsRoot)) {
9763
10137
  fail(`${projectsRoot} does not exist`);
9764
10138
  process.exit(1);
9765
10139
  }
@@ -9773,8 +10147,8 @@ function resumeCmd(sessionId) {
9773
10147
  fail(`no cwd field found in ${jsonlPath}`);
9774
10148
  process.exit(1);
9775
10149
  }
9776
- const mapPath = join66(repoHome(), "path-map.json");
9777
- if (!existsSync52(mapPath)) {
10150
+ const mapPath = join71(repoHome(), "path-map.json");
10151
+ if (!existsSync56(mapPath)) {
9778
10152
  fail("path-map.json missing");
9779
10153
  process.exit(1);
9780
10154
  }
@@ -9796,14 +10170,14 @@ function resumeCmd(sessionId) {
9796
10170
  console.log(`cd ${shQuote(hit.localPath)} && claude --resume ${shQuote(sessionId)}`);
9797
10171
  }
9798
10172
  function findTranscriptPath(projectsRoot, sessionId) {
9799
- for (const dir of readdirSync21(projectsRoot)) {
9800
- const candidate = join66(projectsRoot, dir, `${sessionId}.jsonl`);
9801
- if (existsSync52(candidate)) return candidate;
10173
+ for (const dir of readdirSync22(projectsRoot)) {
10174
+ const candidate = join71(projectsRoot, dir, `${sessionId}.jsonl`);
10175
+ if (existsSync56(candidate)) return candidate;
9802
10176
  }
9803
10177
  return null;
9804
10178
  }
9805
10179
  function extractRecordedCwd(jsonlPath) {
9806
- for (const line of readFileSync20(jsonlPath, "utf8").split("\n")) {
10180
+ for (const line of readFileSync21(jsonlPath, "utf8").split("\n")) {
9807
10181
  if (!line.trim()) continue;
9808
10182
  try {
9809
10183
  const obj = JSON.parse(line);