claude-nomad 0.67.3 → 0.69.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
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
41
41
  mod
42
42
  ));
43
43
 
44
- // src/config.never-sync.ts
44
+ // src/core/config.never-sync.ts
45
45
  function stripTrailingDotsAndWhitespace(name) {
46
46
  let end = name.length;
47
47
  while (end > 0 && (name[end - 1] === "." || /\s/.test(name[end - 1]))) end -= 1;
@@ -97,7 +97,7 @@ function isNeverSync(path) {
97
97
  }
98
98
  var NEVER_SYNC, CLAUDE_EXTRA_NEVER_SYNC, ALWAYS_NEVER_SYNC, SECRET_FILE_PATTERNS, UNFILTERED_SHARED_REGIONS;
99
99
  var init_config_never_sync = __esm({
100
- "src/config.never-sync.ts"() {
100
+ "src/core/config.never-sync.ts"() {
101
101
  "use strict";
102
102
  NEVER_SYNC = /* @__PURE__ */ new Set([
103
103
  ".claude.json",
@@ -222,10 +222,10 @@ var require_picocolors = __commonJS({
222
222
  }
223
223
  });
224
224
 
225
- // src/color.ts
225
+ // src/render/color.ts
226
226
  var import_picocolors, enabled, red, yellow, green, cyan, blue, dim, bold, wslNarrowPad, okGlyph, failGlyph, warnGlyph, infoGlyph;
227
227
  var init_color = __esm({
228
- "src/color.ts"() {
228
+ "src/render/color.ts"() {
229
229
  "use strict";
230
230
  import_picocolors = __toESM(require_picocolors(), 1);
231
231
  enabled = import_picocolors.default.isColorSupported;
@@ -244,10 +244,10 @@ var init_color = __esm({
244
244
  }
245
245
  });
246
246
 
247
- // src/exit-codes.ts
247
+ // src/core/exit-codes.ts
248
248
  var EXIT;
249
249
  var init_exit_codes = __esm({
250
- "src/exit-codes.ts"() {
250
+ "src/core/exit-codes.ts"() {
251
251
  "use strict";
252
252
  EXIT = {
253
253
  /** Completed successfully. Also returned when a held lock causes a no-op skip. */
@@ -269,7 +269,7 @@ var init_exit_codes = __esm({
269
269
  }
270
270
  });
271
271
 
272
- // src/utils.ts
272
+ // src/core/utils.ts
273
273
  import { execFileSync } from "node:child_process";
274
274
  function isProcessExit(err) {
275
275
  if (typeof err !== "object" || err === null) return false;
@@ -304,7 +304,7 @@ function gitOrFatal(args, context, cwd) {
304
304
  }
305
305
  var log, ok, warn, fail, item, NomadFatal, PROCESS_EXIT_BRAND, ProcessExit, die, gitStatusPorcelainZ;
306
306
  var init_utils = __esm({
307
- "src/utils.ts"() {
307
+ "src/core/utils.ts"() {
308
308
  "use strict";
309
309
  init_color();
310
310
  init_exit_codes();
@@ -349,7 +349,7 @@ var init_utils = __esm({
349
349
  }
350
350
  });
351
351
 
352
- // src/config.sharedDirs.guard.ts
352
+ // src/core/config.sharedDirs.guard.ts
353
353
  function assertSafeLogical(logical) {
354
354
  if (!SAFE_LOGICAL.test(logical) || logical === "." || logical === "..") {
355
355
  throw new NomadFatal(
@@ -419,7 +419,7 @@ function isValidSharedDir(entry) {
419
419
  }
420
420
  var SAFE_LOGICAL, SAFE_SEGMENT, RESERVED_SHARED, RESERVED_SHARED_FOLDED, WIN32_DEVICE_NAMES, UNJOINABLE_REASONS;
421
421
  var init_config_sharedDirs_guard = __esm({
422
- "src/config.sharedDirs.guard.ts"() {
422
+ "src/core/config.sharedDirs.guard.ts"() {
423
423
  "use strict";
424
424
  init_config_never_sync();
425
425
  init_utils();
@@ -455,10 +455,10 @@ var init_config_sharedDirs_guard = __esm({
455
455
  }
456
456
  });
457
457
 
458
- // src/settings-keys.ts
458
+ // src/core/settings-keys.ts
459
459
  var SCHEMA_KEYS, APP_ONLY_KEYS, KNOWN_SETTINGS_KEYS;
460
460
  var init_settings_keys = __esm({
461
- "src/settings-keys.ts"() {
461
+ "src/core/settings-keys.ts"() {
462
462
  "use strict";
463
463
  SCHEMA_KEYS = [
464
464
  "$schema",
@@ -635,7 +635,7 @@ var init_settings_keys = __esm({
635
635
  }
636
636
  });
637
637
 
638
- // src/config.ts
638
+ // src/core/config.ts
639
639
  import { homedir, hostname } from "node:os";
640
640
  import { join, resolve } from "node:path";
641
641
  function home() {
@@ -686,7 +686,7 @@ function sharedDirEntries(map) {
686
686
  }
687
687
  var SETTINGS_SCHEMA_URL, NPM_REGISTRY_LATEST_URL, GITLEAKS_PINNED_VERSION, GITLEAKS_SCAN_TIMEOUT_MS, HOST, SHARED_LINKS, GSD_PREFIX, GSD_DROPPED_NAMES, SUPPORTED_EXTRAS, PUSH_ALLOWED_STATIC;
688
688
  var init_config = __esm({
689
- "src/config.ts"() {
689
+ "src/core/config.ts"() {
690
690
  "use strict";
691
691
  init_config_sharedDirs_guard();
692
692
  init_utils();
@@ -721,7 +721,22 @@ var init_config = __esm({
721
721
  }
722
722
  });
723
723
 
724
- // src/utils.json.ts
724
+ // src/core/error-text.ts
725
+ function errorText(err) {
726
+ try {
727
+ const message = err?.message;
728
+ return typeof message === "string" ? message : String(err);
729
+ } catch {
730
+ return "unprintable error value";
731
+ }
732
+ }
733
+ var init_error_text = __esm({
734
+ "src/core/error-text.ts"() {
735
+ "use strict";
736
+ }
737
+ });
738
+
739
+ // src/core/utils.json.ts
725
740
  import { readFileSync } from "node:fs";
726
741
  function readJson(path) {
727
742
  const data = JSON.parse(readFileSync(path, "utf8"));
@@ -759,10 +774,13 @@ function validatePathMapShape(raw) {
759
774
  }
760
775
  return null;
761
776
  }
777
+ function isProtoPollutionKey(key) {
778
+ return key === "__proto__" || key === "constructor" || key === "prototype";
779
+ }
762
780
  function deepMerge(target, source) {
763
781
  const out = { ...target };
764
782
  for (const [key, value] of Object.entries(source)) {
765
- if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
783
+ if (isProtoPollutionKey(key)) continue;
766
784
  const existing = out[key];
767
785
  const bothObjects = value !== null && typeof value === "object" && !Array.isArray(value) && existing !== null && typeof existing === "object" && !Array.isArray(existing);
768
786
  out[key] = bothObjects ? deepMerge(existing, value) : value;
@@ -774,6 +792,7 @@ function sortKeysDeep(value) {
774
792
  if (value !== null && typeof value === "object") {
775
793
  const out = {};
776
794
  for (const key of Object.keys(value).sort((a, b) => a.localeCompare(b, "en"))) {
795
+ if (isProtoPollutionKey(key)) continue;
777
796
  out[key] = sortKeysDeep(value[key]);
778
797
  }
779
798
  return out;
@@ -782,7 +801,7 @@ function sortKeysDeep(value) {
782
801
  }
783
802
  var ENCODE_MAX, hashPath, encodePath;
784
803
  var init_utils_json = __esm({
785
- "src/utils.json.ts"() {
804
+ "src/core/utils.json.ts"() {
786
805
  "use strict";
787
806
  init_config();
788
807
  init_utils();
@@ -800,7 +819,7 @@ var init_utils_json = __esm({
800
819
  }
801
820
  });
802
821
 
803
- // src/utils.fs.ts
822
+ // src/core/utils.fs.ts
804
823
  import {
805
824
  closeSync,
806
825
  cpSync as cpSync3,
@@ -871,6 +890,12 @@ function freshBackupTs(backupRoot) {
871
890
  while (existsSync3(join5(backupRoot, `${base}-${n}`))) n++;
872
891
  return `${base}-${n}`;
873
892
  }
893
+ function prunableByAge(dirs, olderThanMs, nowMs) {
894
+ return dirs.filter((d) => nowMs - d.mtimeMs > olderThanMs).map((d) => d.name);
895
+ }
896
+ function prunableByCount(dirs, keep) {
897
+ return dirs.slice(keep).map((d) => d.name);
898
+ }
874
899
  function discardEmptyBackupDir(backupRoot) {
875
900
  try {
876
901
  rmdirSync(backupRoot);
@@ -886,17 +911,22 @@ function ensureSymlink(linkPath, target) {
886
911
  symlinkSync(target, linkPath);
887
912
  log(`linked ${linkPath} -> ${target}`);
888
913
  }
889
- function backupUnder(absPath, anchor, destRoot) {
914
+ function backupUnder(absPath, anchor, destRoot, hint) {
890
915
  if (!existsSync3(absPath)) return false;
891
916
  const rel = relative3(anchor, absPath);
892
917
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep2}`)) return false;
893
918
  const dst = join5(destRoot, rel);
894
- mkdirSync(dirname(dst), { recursive: true });
895
- cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
919
+ try {
920
+ mkdirSync(dirname(dst), { recursive: true });
921
+ cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
922
+ } catch (err) {
923
+ const fragment = `could not back up ${absPath} into ${dst} (${errorText(err)}); a partial copy may be left there`;
924
+ throw new NomadFatal(hint === void 0 ? fragment : `${fragment}. ${hint}`);
925
+ }
896
926
  return true;
897
927
  }
898
- function backupBeforeWrite(absPath, ts) {
899
- return backupUnder(absPath, claudeHome(), join5(backupBase(), ts));
928
+ function backupBeforeWrite(absPath, ts, hint) {
929
+ return backupUnder(absPath, claudeHome(), join5(backupBase(), ts), hint);
900
930
  }
901
931
  function backupRepoWrite(absPath, ts, repoHome2) {
902
932
  backupUnder(absPath, repoHome2, join5(backupBase(), ts, "repo"));
@@ -906,9 +936,10 @@ function backupExtrasWrite(absPath, ts, projectRoot) {
906
936
  }
907
937
  var RENAME_RETRY_MAX_ATTEMPTS, RENAME_RETRY_BACKOFF_MS;
908
938
  var init_utils_fs = __esm({
909
- "src/utils.fs.ts"() {
939
+ "src/core/utils.fs.ts"() {
910
940
  "use strict";
911
941
  init_config();
942
+ init_error_text();
912
943
  init_utils_json();
913
944
  init_utils();
914
945
  RENAME_RETRY_MAX_ATTEMPTS = 5;
@@ -916,7 +947,7 @@ var init_utils_fs = __esm({
916
947
  }
917
948
  });
918
949
 
919
- // src/commands.pull.wedge.ts
950
+ // src/commands/pull/wedge.ts
920
951
  import { execFileSync as execFileSync3 } from "node:child_process";
921
952
  import { existsSync as existsSync15 } from "node:fs";
922
953
  import { join as join21 } from "node:path";
@@ -985,23 +1016,54 @@ function orphanedAutostashPresent(repo) {
985
1016
  return raw.split("\n").some((line) => /:\s*autostash$/.test(line));
986
1017
  }
987
1018
  var INDEX_PROBE_TIMEOUT_MS;
988
- var init_commands_pull_wedge = __esm({
989
- "src/commands.pull.wedge.ts"() {
1019
+ var init_wedge = __esm({
1020
+ "src/commands/pull/wedge.ts"() {
990
1021
  "use strict";
991
1022
  INDEX_PROBE_TIMEOUT_MS = 3e4;
992
1023
  }
993
1024
  });
994
1025
 
995
- // src/push-gitleaks.config.ts
996
- import { existsSync as existsSync16, mkdtempSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
997
- import { tmpdir } from "node:os";
998
- import { join as join22 } from "node:path";
1026
+ // src/core/package-root.ts
1027
+ import { existsSync as existsSync16 } from "node:fs";
1028
+ import { basename as basename3, dirname as dirname4, join as join22 } from "node:path";
999
1029
  import { fileURLToPath } from "node:url";
1030
+ function packageRoot(from = HERE) {
1031
+ let dir = from;
1032
+ for (; ; ) {
1033
+ if (existsSync16(join22(dir, "package.json"))) return dir;
1034
+ if (basename3(dir) === "node_modules") {
1035
+ throw new Error(`no package.json above ${from} within its own package`);
1036
+ }
1037
+ const parent = dirname4(dir);
1038
+ if (parent === dir) throw new Error(`no package.json above ${from}`);
1039
+ dir = parent;
1040
+ }
1041
+ }
1042
+ var HERE;
1043
+ var init_package_root = __esm({
1044
+ "src/core/package-root.ts"() {
1045
+ "use strict";
1046
+ HERE = dirname4(fileURLToPath(import.meta.url));
1047
+ }
1048
+ });
1049
+
1050
+ // src/commands/push/gitleaks.config.ts
1051
+ import { existsSync as existsSync17, mkdtempSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
1052
+ import { tmpdir } from "node:os";
1053
+ import { join as join23 } from "node:path";
1000
1054
  function resolveTomlPath(repo = repoHome()) {
1001
- const repoToml = join22(repo, ".gitleaks.toml");
1002
- if (existsSync16(repoToml)) return repoToml;
1003
- const bundled = fileURLToPath(new URL("../.gitleaks.toml", import.meta.url));
1004
- return existsSync16(bundled) ? bundled : null;
1055
+ const repoToml = join23(repo, ".gitleaks.toml");
1056
+ if (existsSync17(repoToml)) return repoToml;
1057
+ let root;
1058
+ try {
1059
+ root = packageRoot();
1060
+ } catch {
1061
+ throw new NomadFatal(
1062
+ "claude-nomad install looks incomplete (no package.json above the CLI); reinstall with `npm i -g claude-nomad`"
1063
+ );
1064
+ }
1065
+ const bundled = join23(root, ".gitleaks.toml");
1066
+ return existsSync17(bundled) ? bundled : null;
1005
1067
  }
1006
1068
  function assertOverlayAllowlistsScoped(overlayBody) {
1007
1069
  if (OVERLAY_INLINE_ALLOWLIST_RE.test(overlayBody)) {
@@ -1039,17 +1101,17 @@ function buildOverlayTempConfig(overlayBody, bundled) {
1039
1101
  path = ${JSON.stringify(bundled)}
1040
1102
 
1041
1103
  ${overlayBody}`;
1042
- const tempPath = mkdtempSync(join22(tmpdir(), "nomad-gitleaks-cfg-"));
1043
- const configPath = join22(tempPath, "config.toml");
1104
+ const tempPath = mkdtempSync(join23(tmpdir(), "nomad-gitleaks-cfg-"));
1105
+ const configPath = join23(tempPath, "config.toml");
1044
1106
  writeFileSync3(configPath, tempBody, { mode: 384, flag: "wx" });
1045
1107
  return { configPath, tempPath };
1046
1108
  }
1047
1109
  function resolveTomlConfig() {
1048
1110
  const repo = repoHome();
1049
- const overlayPath = join22(repo, ".gitleaks.overlay.toml");
1050
- const repoToml = join22(repo, ".gitleaks.toml");
1111
+ const overlayPath = join23(repo, ".gitleaks.overlay.toml");
1112
+ const repoToml = join23(repo, ".gitleaks.toml");
1051
1113
  const bundled = resolveTomlPath(repo);
1052
- if (!existsSync16(overlayPath)) {
1114
+ if (!existsSync17(overlayPath)) {
1053
1115
  return { path: bundled, tempPath: null };
1054
1116
  }
1055
1117
  if (bundled === repoToml) {
@@ -1080,10 +1142,11 @@ function resolveTomlConfig() {
1080
1142
  }
1081
1143
  }
1082
1144
  var OVERLAY_EXTEND_RE, TABLE_HEADER_RE, OVERLAY_ALLOWLIST_HEADER_RE, OVERLAY_INLINE_ALLOWLIST_RE, PATHS_KEY_RE, CATCH_ALL_RE;
1083
- var init_push_gitleaks_config = __esm({
1084
- "src/push-gitleaks.config.ts"() {
1145
+ var init_gitleaks_config = __esm({
1146
+ "src/commands/push/gitleaks.config.ts"() {
1085
1147
  "use strict";
1086
1148
  init_config();
1149
+ init_package_root();
1087
1150
  init_utils();
1088
1151
  OVERLAY_EXTEND_RE = /^[ \t]*(?:\[[ \t]*extend[ \t]*\]|extend[ \t]*[.=])/m;
1089
1152
  TABLE_HEADER_RE = /^\s*\[/;
@@ -1094,7 +1157,7 @@ var init_push_gitleaks_config = __esm({
1094
1157
  }
1095
1158
  });
1096
1159
 
1097
- // src/autostash-guard.ts
1160
+ // src/commands/autostash-guard.ts
1098
1161
  function autostashConflictRunbookText(resumeCmd2, stashRetained) {
1099
1162
  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.`;
1100
1163
  if (stashRetained) {
@@ -1125,19 +1188,19 @@ function assertNoAutostashConflict(repo, resumeCmd2) {
1125
1188
  });
1126
1189
  }
1127
1190
  var init_autostash_guard = __esm({
1128
- "src/autostash-guard.ts"() {
1191
+ "src/commands/autostash-guard.ts"() {
1129
1192
  "use strict";
1130
- init_commands_pull_wedge();
1193
+ init_wedge();
1131
1194
  init_exit_codes();
1132
1195
  init_utils();
1133
1196
  }
1134
1197
  });
1135
1198
 
1136
- // src/push-checks.ts
1137
- import { execFileSync as execFileSync4 } from "node:child_process";
1199
+ // src/commands/push/checks.ts
1200
+ import { execFileSync as execFileSync5 } from "node:child_process";
1138
1201
  import { readdirSync as readdirSync8, rmSync as rmSync7 } from "node:fs";
1139
1202
  import { homedir as homedir2, platform } from "node:os";
1140
- import { delimiter, join as join23 } from "node:path";
1203
+ import { delimiter, join as join24 } from "node:path";
1141
1204
  function gitleaksInstallHint() {
1142
1205
  const head = "gitleaks not on PATH (required for nomad push). Install:";
1143
1206
  const plat = platform();
@@ -1185,7 +1248,7 @@ function findGitlinks(dir) {
1185
1248
  return;
1186
1249
  }
1187
1250
  for (const e of entries) {
1188
- const p = join23(current, e.name);
1251
+ const p = join24(current, e.name);
1189
1252
  if (e.name === ".git") {
1190
1253
  hits.push(p);
1191
1254
  continue;
@@ -1201,7 +1264,7 @@ function probeGitleaks() {
1201
1264
  const args = ["version"];
1202
1265
  if (toml !== null) args.push("--config", toml);
1203
1266
  try {
1204
- return execFileSync4("gitleaks", args, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
1267
+ return execFileSync5("gitleaks", args, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
1205
1268
  } catch (err) {
1206
1269
  const e = err;
1207
1270
  if (e.code === "ENOENT") throw new NomadFatal(gitleaksInstallHint());
@@ -1221,7 +1284,7 @@ function rebaseBeforePush(repo) {
1221
1284
  const wedge = classifyWedge(repo);
1222
1285
  if (wedge !== null) throw new NomadFatal(wedgePreflight(wedge), { code: EXIT.CONFLICT });
1223
1286
  try {
1224
- execFileSync4("git", ["pull", "--rebase", "--autostash"], {
1287
+ execFileSync5("git", ["pull", "--rebase", "--autostash"], {
1225
1288
  cwd: repo,
1226
1289
  stdio: ["ignore", "pipe", "pipe"]
1227
1290
  });
@@ -1234,22 +1297,22 @@ function rebaseBeforePush(repo) {
1234
1297
  }
1235
1298
  assertNoAutostashConflict(repo, "nomad push");
1236
1299
  }
1237
- var init_push_checks = __esm({
1238
- "src/push-checks.ts"() {
1300
+ var init_checks = __esm({
1301
+ "src/commands/push/checks.ts"() {
1239
1302
  "use strict";
1240
- init_push_gitleaks_config();
1303
+ init_gitleaks_config();
1241
1304
  init_autostash_guard();
1242
- init_commands_pull_wedge();
1305
+ init_wedge();
1243
1306
  init_exit_codes();
1244
1307
  init_utils();
1245
1308
  }
1246
1309
  });
1247
1310
 
1248
- // src/push-gitleaks.scan.ts
1249
- import { execFileSync as execFileSync8 } from "node:child_process";
1311
+ // src/commands/push/gitleaks.scan.ts
1312
+ import { execFileSync as execFileSync9 } from "node:child_process";
1250
1313
  import { mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync8 } from "node:fs";
1251
1314
  import { homedir as homedir3 } from "node:os";
1252
- import { join as join27 } from "node:path";
1315
+ import { join as join28 } from "node:path";
1253
1316
  function toFinding(entry) {
1254
1317
  if (typeof entry !== "object" || entry === null) return null;
1255
1318
  const e = entry;
@@ -1297,9 +1360,9 @@ function resolveScanFailure(reportPath, e, forwardStreams) {
1297
1360
  return report;
1298
1361
  }
1299
1362
  function scanStagedTree(repoDir, forwardStreams = false) {
1300
- const cacheDir = join27(homedir3(), ".cache", "claude-nomad");
1363
+ const cacheDir = join28(homedir3(), ".cache", "claude-nomad");
1301
1364
  mkdirSync3(cacheDir, { recursive: true });
1302
- const reportPath = join27(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
1365
+ const reportPath = join28(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
1303
1366
  const { path: toml, tempPath } = resolveTomlConfig();
1304
1367
  const args = [
1305
1368
  "protect",
@@ -1313,9 +1376,9 @@ function scanStagedTree(repoDir, forwardStreams = false) {
1313
1376
  const opts = { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] };
1314
1377
  const gitleaksOpts = { ...opts, timeout: GITLEAKS_SCAN_TIMEOUT_MS };
1315
1378
  try {
1316
- execFileSync8("git", ["init", "-q"], opts);
1317
- execFileSync8("git", ["add", "-A"], opts);
1318
- execFileSync8("gitleaks", args, gitleaksOpts);
1379
+ execFileSync9("git", ["init", "-q"], opts);
1380
+ execFileSync9("git", ["add", "-A"], opts);
1381
+ execFileSync9("gitleaks", args, gitleaksOpts);
1319
1382
  return [];
1320
1383
  } catch (err) {
1321
1384
  const e = err;
@@ -1327,14 +1390,14 @@ function scanStagedTree(repoDir, forwardStreams = false) {
1327
1390
  }
1328
1391
  }
1329
1392
  function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TIMEOUT_MS) {
1330
- const cacheDir = join27(homedir3(), ".cache", "claude-nomad");
1393
+ const cacheDir = join28(homedir3(), ".cache", "claude-nomad");
1331
1394
  mkdirSync3(cacheDir, { recursive: true });
1332
1395
  const { path: toml, tempPath } = resolveTomlConfig();
1333
1396
  let reportDir;
1334
1397
  let reportPath;
1335
1398
  try {
1336
- reportDir = mkdtempSync2(join27(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
1337
- reportPath = join27(reportDir, "report.json");
1399
+ reportDir = mkdtempSync2(join28(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
1400
+ reportPath = join28(reportDir, "report.json");
1338
1401
  const args = [
1339
1402
  "detect",
1340
1403
  "--no-git",
@@ -1348,7 +1411,7 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
1348
1411
  stdio: ["ignore", "pipe", "pipe"],
1349
1412
  timeout: timeoutMs
1350
1413
  };
1351
- execFileSync8("gitleaks", args, opts);
1414
+ execFileSync9("gitleaks", args, opts);
1352
1415
  return [];
1353
1416
  } catch (err) {
1354
1417
  const e = err;
@@ -1359,16 +1422,16 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
1359
1422
  if (reportDir !== void 0) rmSync8(reportDir, { recursive: true, force: true });
1360
1423
  }
1361
1424
  }
1362
- var init_push_gitleaks_scan = __esm({
1363
- "src/push-gitleaks.scan.ts"() {
1425
+ var init_gitleaks_scan = __esm({
1426
+ "src/commands/push/gitleaks.scan.ts"() {
1364
1427
  "use strict";
1365
1428
  init_config();
1366
- init_push_gitleaks_config();
1429
+ init_gitleaks_config();
1367
1430
  init_utils_fs();
1368
1431
  }
1369
1432
  });
1370
1433
 
1371
- // src/push-gitleaks.ts
1434
+ // src/commands/push/gitleaks.ts
1372
1435
  function findingIdentityKey(f) {
1373
1436
  const fp = f.Fingerprint;
1374
1437
  return typeof fp === "string" && fp.length > 0 ? fp : `${f.File}:${f.RuleID}:${f.StartLine}:${f.StartColumn}`;
@@ -1456,13 +1519,13 @@ function buildSessionAwareFatal(bySession, other) {
1456
1519
  return lines.join("\n");
1457
1520
  }
1458
1521
  var SESSION_PATH, SUBAGENT_SESSION_PATH, LEGACY_FATAL;
1459
- var init_push_gitleaks = __esm({
1460
- "src/push-gitleaks.ts"() {
1522
+ var init_gitleaks = __esm({
1523
+ "src/commands/push/gitleaks.ts"() {
1461
1524
  "use strict";
1462
1525
  init_config();
1463
1526
  init_exit_codes();
1464
- init_push_checks();
1465
- init_push_gitleaks_scan();
1527
+ init_checks();
1528
+ init_gitleaks_scan();
1466
1529
  init_utils();
1467
1530
  SESSION_PATH = /^shared\/projects\/[^/]+\/([^/]+)\.jsonl$/;
1468
1531
  SUBAGENT_SESSION_PATH = /^shared\/projects\/[^/]+\/([^/]+)\/.*\.jsonl$/;
@@ -1470,9 +1533,9 @@ var init_push_gitleaks = __esm({
1470
1533
  }
1471
1534
  });
1472
1535
 
1473
- // src/push-leak-verdict.ts
1474
- var push_leak_verdict_exports = {};
1475
- __export(push_leak_verdict_exports, {
1536
+ // src/commands/push/leak-verdict.ts
1537
+ var leak_verdict_exports = {};
1538
+ __export(leak_verdict_exports, {
1476
1539
  failRow: () => failRow,
1477
1540
  leakVerdictRow: () => leakVerdictRow,
1478
1541
  noLeaksRow: () => noLeaksRow,
@@ -1545,65 +1608,59 @@ function scanPushVerdict(repo) {
1545
1608
  return leakFound(findings);
1546
1609
  }
1547
1610
  var noLeaksRow, failRow;
1548
- var init_push_leak_verdict = __esm({
1549
- "src/push-leak-verdict.ts"() {
1611
+ var init_leak_verdict = __esm({
1612
+ "src/commands/push/leak-verdict.ts"() {
1550
1613
  "use strict";
1551
1614
  init_color();
1552
1615
  init_exit_codes();
1553
- init_push_checks();
1554
- init_push_gitleaks();
1616
+ init_checks();
1617
+ init_gitleaks();
1555
1618
  noLeaksRow = () => `${green(okGlyph)} no leaks`;
1556
1619
  failRow = (text) => `${red(failGlyph)} ${text}`;
1557
1620
  }
1558
1621
  });
1559
1622
 
1560
- // src/commands.adopt.ts
1623
+ // src/commands/adopt.ts
1561
1624
  import { existsSync as existsSync5, lstatSync as lstatSync7 } from "node:fs";
1562
1625
  import { join as join9 } from "node:path";
1563
1626
 
1564
- // src/commands.adopt.recover.ts
1627
+ // src/commands/adopt.recover.ts
1565
1628
  import { rmSync as rmSync4 } from "node:fs";
1566
1629
  import { dirname as dirname2, join as join7, resolve as resolve2 } from "node:path";
1567
1630
 
1568
- // src/commands.adopt.scan.ts
1631
+ // src/commands/adopt.scan.ts
1569
1632
  init_config();
1570
- import { lstatSync, readdirSync } from "node:fs";
1571
- import { join as join2, relative } from "node:path";
1572
-
1573
- // src/error-text.ts
1574
- function errorText(err) {
1575
- try {
1576
- const message = err?.message;
1577
- return typeof message === "string" ? message : String(err);
1578
- } catch {
1579
- return "unprintable error value";
1580
- }
1581
- }
1582
-
1583
- // src/commands.adopt.scan.ts
1633
+ init_error_text();
1584
1634
  init_exit_codes();
1585
1635
  init_utils();
1636
+ import { lstatSync, readdirSync } from "node:fs";
1637
+ import { join as join2, relative } from "node:path";
1586
1638
  function toForwardSlash(p) {
1587
1639
  return p.replaceAll("\\", "/");
1588
1640
  }
1589
- function walk(root, dir, out) {
1590
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
1591
- const match = matchDeniedName(ALWAYS_NEVER_SYNC, entry.name);
1592
- if (match !== null) {
1593
- out.push({
1594
- path: toForwardSlash(relative(root, join2(dir, entry.name))),
1595
- matched: match.axis === "name" ? match.entry : null
1596
- });
1597
- continue;
1641
+ function walk(root) {
1642
+ const out = [];
1643
+ const stack = [root];
1644
+ while (stack.length > 0) {
1645
+ const dir = stack.pop();
1646
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1647
+ const match = matchDeniedName(ALWAYS_NEVER_SYNC, entry.name);
1648
+ if (match !== null) {
1649
+ out.push({
1650
+ path: toForwardSlash(relative(root, join2(dir, entry.name))),
1651
+ matched: match.axis === "name" ? match.entry : null
1652
+ });
1653
+ continue;
1654
+ }
1655
+ if (entry.isDirectory()) stack.push(join2(dir, entry.name));
1598
1656
  }
1599
- if (entry.isDirectory()) walk(root, join2(dir, entry.name), out);
1600
1657
  }
1658
+ return out;
1601
1659
  }
1602
1660
  function scanDeniedEntries(root) {
1603
1661
  const stat = lstatSync(root, { throwIfNoEntry: false });
1604
1662
  if (!stat?.isDirectory()) return [];
1605
- const out = [];
1606
- walk(root, root, out);
1663
+ const out = walk(root);
1607
1664
  out.sort((a, b) => a.path.localeCompare(b.path));
1608
1665
  return out;
1609
1666
  }
@@ -1650,16 +1707,17 @@ function refuseDeniedEntries(name, root) {
1650
1707
  });
1651
1708
  }
1652
1709
 
1653
- // src/commands.adopt.recover.ts
1710
+ // src/commands/adopt.recover.ts
1654
1711
  init_config();
1712
+ init_error_text();
1655
1713
  init_exit_codes();
1656
1714
 
1657
- // src/extras-sync.core.ts
1715
+ // src/sync/extras/core.ts
1658
1716
  init_config();
1659
1717
  import { cpSync as cpSync2, existsSync as existsSync2, lstatSync as lstatSync3, readdirSync as readdirSync3, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
1660
1718
  import { basename as basename2, join as join4, relative as relative2 } from "node:path";
1661
1719
 
1662
- // src/extras-sync.collision.ts
1720
+ // src/sync/extras/collision.ts
1663
1721
  init_utils();
1664
1722
  import { cpSync, existsSync, lstatSync as lstatSync2, readdirSync as readdirSync2, rmSync } from "node:fs";
1665
1723
  import { basename, join as join3 } from "node:path";
@@ -1734,7 +1792,7 @@ function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved)
1734
1792
  });
1735
1793
  }
1736
1794
 
1737
- // src/extras-sync.guards.ts
1795
+ // src/sync/extras/guards.ts
1738
1796
  init_utils();
1739
1797
  init_config_sharedDirs_guard();
1740
1798
  import { isAbsolute, normalize, sep } from "node:path";
@@ -1752,7 +1810,7 @@ function assertSafeLocalRoot(localRoot, logical) {
1752
1810
  }
1753
1811
  }
1754
1812
 
1755
- // src/extras-sync.core.ts
1813
+ // src/sync/extras/core.ts
1756
1814
  init_utils();
1757
1815
  init_utils_json();
1758
1816
  function loadValidatedExtras(opts) {
@@ -1781,12 +1839,12 @@ function* eachExtrasTarget(v, counts) {
1781
1839
  counts.unmapped++;
1782
1840
  continue;
1783
1841
  }
1784
- for (const dirname15 of dirnames) {
1785
- if (!whitelist.includes(dirname15)) {
1842
+ for (const dirname16 of dirnames) {
1843
+ if (!whitelist.includes(dirname16)) {
1786
1844
  counts.skipped++;
1787
1845
  continue;
1788
1846
  }
1789
- yield { logical, localRoot, dirname: dirname15 };
1847
+ yield { logical, localRoot, dirname: dirname16 };
1790
1848
  }
1791
1849
  }
1792
1850
  }
@@ -1828,8 +1886,8 @@ function copyExtrasFileSkipDiverged(src, dst) {
1828
1886
  }
1829
1887
  copyExtras(src, dst);
1830
1888
  }
1831
- function extrasDenySet(dirname15) {
1832
- return isClaudeExtraName(dirname15) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1889
+ function extrasDenySet(dirname16) {
1890
+ return isClaudeExtraName(dirname16) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
1833
1891
  }
1834
1892
  function copyExtrasFiltered(src, dst, blockSet) {
1835
1893
  rmSync2(dst, { recursive: true, force: true });
@@ -1872,7 +1930,7 @@ function copyExtrasFilteredPreserving(src, dst, blockSet) {
1872
1930
  });
1873
1931
  }
1874
1932
 
1875
- // src/fs-presence.ts
1933
+ // src/core/fs-presence.ts
1876
1934
  import { lstatSync as lstatSync4, statSync } from "node:fs";
1877
1935
  var ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR", "ENAMETOOLONG"]);
1878
1936
  function classifyPresence(p) {
@@ -1897,14 +1955,110 @@ function lexists(p) {
1897
1955
  return classifyPresence(p) !== "absent";
1898
1956
  }
1899
1957
 
1900
- // src/links.ts
1958
+ // src/sync/links.ts
1901
1959
  init_config();
1902
1960
  import { existsSync as existsSync4, lstatSync as lstatSync6, rmSync as rmSync3 } from "node:fs";
1903
1961
  import { join as join6 } from "node:path";
1904
1962
 
1905
- // src/hooks-filter.ts
1963
+ // src/commands/capture-settings/core.ts
1964
+ init_utils_json();
1965
+
1966
+ // src/sync/hooks-filter.ts
1906
1967
  init_config();
1907
- var KNOWN_LAUNCHER_BASENAMES = /* @__PURE__ */ new Set(["node", "bash", "sh"]);
1968
+ init_utils_json();
1969
+
1970
+ // src/sync/hooks-filter.command-sub.ts
1971
+ var SUB_START = /^"?(?:\$\(|`)/;
1972
+ function opensSubstitution(token) {
1973
+ return SUB_START.test(token);
1974
+ }
1975
+ function stepChar(token, i, scan) {
1976
+ const c = token[i];
1977
+ const top = scan.stack.at(-1);
1978
+ if (top === "sq") {
1979
+ if (c === "'") scan.stack.pop();
1980
+ return i + 1;
1981
+ }
1982
+ if (scan.escaped) {
1983
+ scan.escaped = false;
1984
+ return i + 1;
1985
+ }
1986
+ if (c === "\\") {
1987
+ scan.escaped = true;
1988
+ return i + 1;
1989
+ }
1990
+ if (c === "$" && token[i + 1] === "(") {
1991
+ scan.stack.push("cmd");
1992
+ scan.depth++;
1993
+ return i + 2;
1994
+ }
1995
+ if (c === "'" && top === "cmd") scan.stack.push("sq");
1996
+ else if (c === "`") toggleBacktick(scan, top);
1997
+ else if (c === '"') toggleDoubleQuote(scan, top);
1998
+ else if (top === "cmd") stepParen(c, scan);
1999
+ return i + 1;
2000
+ }
2001
+ function toggleBacktick(scan, top) {
2002
+ if (top === "bt") scan.stack.pop();
2003
+ else scan.stack.push("bt");
2004
+ }
2005
+ function toggleDoubleQuote(scan, top) {
2006
+ if (top === "dq") scan.stack.pop();
2007
+ else scan.stack.push("dq");
2008
+ }
2009
+ function stepParen(c, scan) {
2010
+ if (c === "(") {
2011
+ scan.stack.push("cmd");
2012
+ scan.depth++;
2013
+ } else if (c === ")") {
2014
+ scan.stack.pop();
2015
+ scan.depth--;
2016
+ }
2017
+ }
2018
+ function skipDollarParen(tokens, start, from) {
2019
+ const scan = { stack: ["cmd"], depth: 1, escaped: false };
2020
+ let i = from;
2021
+ for (let j = start; j < tokens.length; j++) {
2022
+ const token = tokens[j];
2023
+ while (i < token.length) {
2024
+ i = stepChar(token, i, scan);
2025
+ if (scan.depth === 0) return { next: j + 1, rest: token.slice(i) };
2026
+ }
2027
+ i = 0;
2028
+ scan.escaped = false;
2029
+ }
2030
+ return unterminated(tokens, start);
2031
+ }
2032
+ function skipBacktick(tokens, start, from) {
2033
+ let i = from;
2034
+ for (let j = start; j < tokens.length; j++) {
2035
+ const token = tokens[j];
2036
+ while (i < token.length) {
2037
+ if (token[i] === "\\") i += 2;
2038
+ else if (token[i] === "`") return { next: j + 1, rest: token.slice(i + 1) };
2039
+ else i++;
2040
+ }
2041
+ i = 0;
2042
+ }
2043
+ return unterminated(tokens, start);
2044
+ }
2045
+ function unterminated(tokens, start) {
2046
+ return { next: start + 1, rest: tokens[start] };
2047
+ }
2048
+ function skipSubstitution(tokens, start) {
2049
+ const token = tokens[start] ?? "";
2050
+ const paren = token.indexOf("$(");
2051
+ const tick = token.indexOf("`");
2052
+ if (paren < 0 && tick < 0) return { next: start + 1, rest: token };
2053
+ if (tick >= 0 && (paren < 0 || tick < paren)) return skipBacktick(tokens, start, tick + 1);
2054
+ return skipDollarParen(tokens, start, paren + 2);
2055
+ }
2056
+
2057
+ // src/sync/hooks-filter.ts
2058
+ var KNOWN_LAUNCHER_BASENAMES = /* @__PURE__ */ new Set(["env", "node", "bash", "sh"]);
2059
+ var SUBSTITUTION_ARTIFACT = /[`()'"]/;
2060
+ var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/;
2061
+ var SHELL_OPERATORS = /* @__PURE__ */ new Set(["&&", "||", ";", "|", "&"]);
1908
2062
  function scriptBasename(token) {
1909
2063
  const lastSlash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
1910
2064
  return lastSlash >= 0 ? token.slice(lastSlash + 1) : token;
@@ -1919,25 +2073,56 @@ function stripQuotes(token) {
1919
2073
  }
1920
2074
  return token;
1921
2075
  }
2076
+ var NO_CANDIDATE = { index: -1, word: "" };
2077
+ function nextScriptWord(tokens, from, inScriptSlot) {
2078
+ let slot = inScriptSlot;
2079
+ let i = from;
2080
+ while (i < tokens.length) {
2081
+ const token = tokens[i];
2082
+ if (SHELL_OPERATORS.has(token)) {
2083
+ slot = false;
2084
+ i++;
2085
+ } else if (token.startsWith("-")) {
2086
+ i++;
2087
+ } else if (!opensSubstitution(token)) {
2088
+ return { index: i, word: token };
2089
+ } else {
2090
+ const end = skipSubstitution(tokens, i);
2091
+ if (end.rest !== "") return { index: end.next - 1, word: end.rest };
2092
+ if (slot && !SHELL_OPERATORS.has(tokens[end.next] ?? "")) return NO_CANDIDATE;
2093
+ i = end.next;
2094
+ }
2095
+ }
2096
+ return NO_CANDIDATE;
2097
+ }
2098
+ function resolveScriptWord(tokens, from, underEnv) {
2099
+ let candidate = nextScriptWord(tokens, from, true);
2100
+ while (underEnv && candidate.index >= 0) {
2101
+ const word = stripQuotes(candidate.word);
2102
+ const isBareInterpreter = !word.includes("/") && !word.includes("\\") && KNOWN_LAUNCHER_BASENAMES.has(word);
2103
+ if (!isBareInterpreter && !ENV_ASSIGNMENT.test(word)) return candidate;
2104
+ candidate = nextScriptWord(tokens, candidate.index + 1, true);
2105
+ }
2106
+ return candidate;
2107
+ }
1922
2108
  function isGsdHookEntry(command) {
1923
2109
  const tokens = command.trim().split(/\s+/);
1924
- if (tokens.length === 0 || tokens[0] === "") return false;
1925
- const envAssign = /^[A-Za-z_]\w*=/;
2110
+ if (tokens[0] === "") return false;
1926
2111
  let i = 0;
1927
- while (i < tokens.length && envAssign.test(tokens[i])) {
2112
+ while (i < tokens.length && ENV_ASSIGNMENT.test(tokens[i])) {
1928
2113
  i++;
1929
2114
  }
1930
- const first = stripQuotes(tokens[i] ?? "");
2115
+ const launcher = nextScriptWord(tokens, i, false);
2116
+ if (launcher.index < 0) return false;
2117
+ const first = stripQuotes(launcher.word);
1931
2118
  const firstBase = scriptBasename(first);
1932
2119
  const firstHasPath = first.includes("/") || first.includes("\\");
1933
- if (firstHasPath && !KNOWN_LAUNCHER_BASENAMES.has(firstBase) || first.startsWith(GSD_PREFIX)) {
2120
+ if (!KNOWN_LAUNCHER_BASENAMES.has(firstBase) && (firstHasPath || !SUBSTITUTION_ARTIFACT.test(first))) {
1934
2121
  return firstBase.startsWith(GSD_PREFIX);
1935
2122
  }
1936
- for (let j = i + 1; j < tokens.length; j++) {
1937
- if (tokens[j].startsWith("-")) continue;
1938
- return scriptBasename(stripQuotes(tokens[j])).startsWith(GSD_PREFIX);
1939
- }
1940
- return false;
2123
+ const script = resolveScriptWord(tokens, launcher.index + 1, firstBase === "env");
2124
+ if (script.index < 0) return false;
2125
+ return scriptBasename(stripQuotes(script.word)).startsWith(GSD_PREFIX);
1941
2126
  }
1942
2127
  function filterMatcherEntry(entry) {
1943
2128
  if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return entry;
@@ -1965,24 +2150,26 @@ function filterEventMatchers(matchers) {
1965
2150
  function stripGsdHookEntries(settings) {
1966
2151
  const out = {};
1967
2152
  for (const [key, value] of Object.entries(settings)) {
1968
- if (key !== "hooks") {
2153
+ if (isProtoPollutionKey(key)) continue;
2154
+ const hooksObj = key === "hooks" ? asPlainObject(value) : null;
2155
+ if (hooksObj === null) {
1969
2156
  out[key] = value;
1970
2157
  continue;
1971
2158
  }
1972
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
1973
- out[key] = value;
1974
- continue;
1975
- }
1976
- const hooksObj = value;
1977
- const filteredHooks = {};
1978
- for (const [event, matchers] of Object.entries(hooksObj)) {
1979
- const filtered = filterEventMatchers(matchers);
1980
- if (filtered !== null) filteredHooks[event] = filtered;
1981
- }
1982
- if (Object.keys(filteredHooks).length > 0) out[key] = filteredHooks;
2159
+ const filteredHooks = filterHooksBlock(hooksObj);
2160
+ if (filteredHooks !== null) out[key] = filteredHooks;
1983
2161
  }
1984
2162
  return out;
1985
2163
  }
2164
+ function filterHooksBlock(hooksObj) {
2165
+ const filteredHooks = {};
2166
+ for (const [event, matchers] of Object.entries(hooksObj)) {
2167
+ if (isProtoPollutionKey(event)) continue;
2168
+ const filtered = filterEventMatchers(matchers);
2169
+ if (filtered !== null) filteredHooks[event] = filtered;
2170
+ }
2171
+ return Object.keys(filteredHooks).length > 0 ? filteredHooks : null;
2172
+ }
1986
2173
  function keepMatcherEntry(entry) {
1987
2174
  if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return null;
1988
2175
  const entryObj = entry;
@@ -2012,6 +2199,7 @@ function keepGsdHookEntries(settings) {
2012
2199
  const hooksObj = hooksVal;
2013
2200
  const keptHooks = {};
2014
2201
  for (const [event, matchers] of Object.entries(hooksObj)) {
2202
+ if (isProtoPollutionKey(event)) continue;
2015
2203
  const kept = keepEventMatchers(matchers);
2016
2204
  if (kept !== null) keptHooks[event] = kept;
2017
2205
  }
@@ -2046,7 +2234,7 @@ function graftGsdHookEntries(base, gsdOnly) {
2046
2234
  const baseHooks = asPlainObject(base.hooks);
2047
2235
  const mergedHooks = baseHooks ? { ...baseHooks } : {};
2048
2236
  for (const [event, gsdMatchers] of Object.entries(gsdHooks)) {
2049
- if (!Array.isArray(gsdMatchers)) continue;
2237
+ if (isProtoPollutionKey(event) || !Array.isArray(gsdMatchers)) continue;
2050
2238
  const baseMatchers = mergedHooks[event];
2051
2239
  mergedHooks[event] = Array.isArray(baseMatchers) ? unionMatcherArrays(baseMatchers, gsdMatchers) : gsdMatchers;
2052
2240
  }
@@ -2077,7 +2265,7 @@ function baseHasGsdHookEntries(settings) {
2077
2265
  return false;
2078
2266
  }
2079
2267
 
2080
- // src/commands.capture-settings.core.ts
2268
+ // src/commands/capture-settings/core.ts
2081
2269
  function arraysEqual(a, b) {
2082
2270
  if (a.length !== b.length) return false;
2083
2271
  for (let i = 0; i < a.length; i++) {
@@ -2171,6 +2359,7 @@ function normalizeNodePathsDeep(value) {
2171
2359
  if (value !== null && typeof value === "object") {
2172
2360
  const out = {};
2173
2361
  for (const [k, v] of Object.entries(value)) {
2362
+ if (isProtoPollutionKey(k)) continue;
2174
2363
  out[k] = normalizeNodePathsDeep(v);
2175
2364
  }
2176
2365
  return out;
@@ -2189,7 +2378,7 @@ function buildCaptureSubset(merged, settings, opts) {
2189
2378
  return out;
2190
2379
  }
2191
2380
 
2192
- // src/links.ts
2381
+ // src/sync/links.ts
2193
2382
  init_utils();
2194
2383
  init_utils_fs();
2195
2384
  init_utils_json();
@@ -2375,7 +2564,7 @@ function regenerateSettings(ts, opts = {}) {
2375
2564
  return { label: overrideLabel };
2376
2565
  }
2377
2566
 
2378
- // src/commands.adopt.recover.ts
2567
+ // src/commands/adopt.recover.ts
2379
2568
  init_utils();
2380
2569
  function isDirectChildOf(root, abs) {
2381
2570
  return dirname2(resolve2(abs)) === resolve2(root);
@@ -2487,14 +2676,14 @@ function reportSourceRemovalFailure(name, linkPath, message, stage) {
2487
2676
  );
2488
2677
  }
2489
2678
 
2490
- // src/commands.adopt.ts
2679
+ // src/commands/adopt.ts
2491
2680
  init_config();
2492
2681
  init_config_sharedDirs_guard();
2493
2682
  init_exit_codes();
2494
2683
  init_utils();
2495
2684
  init_utils_fs();
2496
2685
 
2497
- // src/utils.lockfile.ts
2686
+ // src/core/utils.lockfile.ts
2498
2687
  init_config();
2499
2688
  init_utils();
2500
2689
  import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
@@ -2607,7 +2796,7 @@ function retryOnce(verb, lp) {
2607
2796
  }
2608
2797
  }
2609
2798
 
2610
- // src/commands.adopt.ts
2799
+ // src/commands/adopt.ts
2611
2800
  init_utils_json();
2612
2801
  var ADOPT_PUSH_HINT = "run `nomad push` to share with other hosts";
2613
2802
  function readMapIfPresent(repoHome2) {
@@ -2689,7 +2878,11 @@ function adoptStopsEarly(name, linkPath, sharedTarget) {
2689
2878
  }
2690
2879
  function performAdoptMove(name, linkPath, sharedTarget, repo, backup) {
2691
2880
  const ts = freshBackupTs(backup);
2692
- const snapshotted = backupBeforeWrite(linkPath, ts);
2881
+ const snapshotted = backupBeforeWrite(
2882
+ linkPath,
2883
+ ts,
2884
+ `Nothing was removed from ${linkPath} and nothing was written to the repo. Fix what the error names, then run \`nomad adopt ${name}\` again.`
2885
+ );
2693
2886
  const rel = join9("shared", name);
2694
2887
  const stage = () => gitOrFatal(["add", "--", rel], `git add shared/${name}`, repo);
2695
2888
  copyIntoSharedOrFatal(name, linkPath, sharedTarget, repo);
@@ -2767,11 +2960,11 @@ function adoptPreflightAndMove(name, linkPath, sharedTarget, repo, backup, dryRu
2767
2960
  }
2768
2961
  }
2769
2962
 
2770
- // src/commands.allow.ts
2963
+ // src/commands/allow.ts
2771
2964
  init_config();
2772
2965
  import { existsSync as existsSync6 } from "node:fs";
2773
2966
 
2774
- // src/commands.redact.core.ts
2967
+ // src/commands/redact/core.ts
2775
2968
  import { appendFileSync, readFileSync as readFileSync4 } from "node:fs";
2776
2969
  import { join as join10 } from "node:path";
2777
2970
  function collectMatchIntervals(content, findings) {
@@ -2845,7 +3038,7 @@ function appendGitleaksIgnore(fingerprint, repo) {
2845
3038
  appendFileSync(ignPath, prefix + formatFingerprint(sanitized), "utf8");
2846
3039
  }
2847
3040
 
2848
- // src/commands.allow.ts
3041
+ // src/commands/allow.ts
2849
3042
  init_exit_codes();
2850
3043
  init_utils();
2851
3044
  function cmdAllow(fingerprints) {
@@ -2865,7 +3058,7 @@ function cmdAllow(fingerprints) {
2865
3058
  log(`allowed ${fingerprints.length} fingerprint(s)`);
2866
3059
  }
2867
3060
 
2868
- // src/commands.capture-settings.ts
3061
+ // src/commands/capture-settings/capture-settings.ts
2869
3062
  init_config();
2870
3063
  import { existsSync as existsSync7 } from "node:fs";
2871
3064
  import { join as join11 } from "node:path";
@@ -2947,10 +3140,11 @@ async function cmdCaptureSettings(opts) {
2947
3140
  }
2948
3141
  }
2949
3142
 
2950
- // src/commands.clean.ts
3143
+ // src/commands/clean.ts
2951
3144
  init_config();
2952
3145
  init_exit_codes();
2953
3146
  init_utils();
3147
+ init_utils_fs();
2954
3148
  import { existsSync as existsSync8, lstatSync as lstatSync8, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync3 } from "node:fs";
2955
3149
  import { join as join12 } from "node:path";
2956
3150
  var TS_SHAPE = /^\d{8}-\d{6}(-\d+)?$/;
@@ -2985,12 +3179,6 @@ function holdsNoContent(dir) {
2985
3179
  }
2986
3180
  return true;
2987
3181
  }
2988
- function prunableByAge(dirs, olderThanMs, nowMs) {
2989
- return dirs.filter((d) => nowMs - d.mtimeMs > olderThanMs).map((d) => d.name);
2990
- }
2991
- function prunableByCount(dirs, keep) {
2992
- return dirs.slice(keep).map((d) => d.name);
2993
- }
2994
3182
  function safeDelete(backupBase2, name) {
2995
3183
  if (!isTsDir(name)) return;
2996
3184
  const full = join12(backupBase2, name);
@@ -3050,7 +3238,7 @@ function pruneBackups(opts, backupBase2) {
3050
3238
  log(`removed ${targets.length} backup(s)`);
3051
3239
  }
3052
3240
 
3053
- // src/commands.eject.ts
3241
+ // src/commands/eject.ts
3054
3242
  init_config();
3055
3243
  init_config_sharedDirs_guard();
3056
3244
  init_utils();
@@ -3250,20 +3438,20 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
3250
3438
  }
3251
3439
  }
3252
3440
 
3253
- // src/commands.doctor.ts
3254
- import { existsSync as existsSync35 } from "node:fs";
3255
- import { join as join49 } from "node:path";
3441
+ // src/commands/doctor/doctor.ts
3442
+ import { existsSync as existsSync36 } from "node:fs";
3443
+ import { join as join52 } from "node:path";
3256
3444
 
3257
- // src/commands.doctor.checks.repo.ts
3445
+ // src/commands/doctor/checks/repo.ts
3258
3446
  init_color();
3259
3447
  init_config();
3260
3448
  import { existsSync as existsSync11, lstatSync as lstatSync10, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
3261
3449
  import { join as join17 } from "node:path";
3262
3450
 
3263
- // src/commands.doctor.format.ts
3451
+ // src/commands/doctor/format.ts
3264
3452
  init_color();
3265
3453
 
3266
- // src/output-tree.ts
3454
+ // src/render/output-tree.ts
3267
3455
  init_color();
3268
3456
  var FAIL_GLYPH_BARE = "\u2717";
3269
3457
  function section(header, raw = false) {
@@ -3321,7 +3509,7 @@ function renderTree(sections) {
3321
3509
  }
3322
3510
  var renderDoctor = renderTree;
3323
3511
 
3324
- // src/commands.doctor.format.ts
3512
+ // src/commands/doctor/format.ts
3325
3513
  init_utils_json();
3326
3514
  function readJsonSafe(path, label, section2) {
3327
3515
  try {
@@ -3333,12 +3521,12 @@ function readJsonSafe(path, label, section2) {
3333
3521
  }
3334
3522
  }
3335
3523
 
3336
- // src/commands.doctor.checks.repo.win32.ts
3524
+ // src/commands/doctor/checks/repo.win32.ts
3337
3525
  init_color();
3338
3526
  init_config();
3339
3527
  import { join as join14, win32 as win32Path } from "node:path";
3340
3528
 
3341
- // src/extras-sync.diff.ts
3529
+ // src/sync/extras/diff.ts
3342
3530
  init_utils();
3343
3531
  import { execFileSync as execFileSync2 } from "node:child_process";
3344
3532
  import { relative as relative4 } from "node:path";
@@ -3393,7 +3581,7 @@ function listDivergingModified(a, b) {
3393
3581
  return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
3394
3582
  }
3395
3583
 
3396
- // src/commands.doctor.checks.repo.win32.ts
3584
+ // src/commands/doctor/checks/repo.win32.ts
3397
3585
  function win32CopyOkRow(name, exempt = 0) {
3398
3586
  const note = exempt > 0 ? dim(` (${exempt} never-synced path(s) not compared)`) : "";
3399
3587
  return { line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)${note}`, fail: false };
@@ -3445,7 +3633,7 @@ function classifyWin32Copy(name, p) {
3445
3633
  };
3446
3634
  }
3447
3635
 
3448
- // src/commands.doctor.checks.repo.source.ts
3636
+ // src/commands/doctor/checks/repo.source.ts
3449
3637
  init_color();
3450
3638
  init_config();
3451
3639
  import { join as join15 } from "node:path";
@@ -3496,7 +3684,7 @@ function repoSourceRow(name, present, absent) {
3496
3684
  return state === "resolves" ? present : absent;
3497
3685
  }
3498
3686
 
3499
- // src/init.classify.ts
3687
+ // src/commands/init/classify.ts
3500
3688
  init_config();
3501
3689
  init_utils_json();
3502
3690
  import { existsSync as existsSync10 } from "node:fs";
@@ -3539,7 +3727,7 @@ function reasonForPartial(repoHome2, host) {
3539
3727
  return "- partial state (unknown gap)";
3540
3728
  }
3541
3729
 
3542
- // src/commands.doctor.checks.repo.ts
3730
+ // src/commands/doctor/checks/repo.ts
3543
3731
  init_utils_json();
3544
3732
  function isOverrideActive() {
3545
3733
  return Boolean(process.env.NOMAD_REPO);
@@ -3698,7 +3886,7 @@ function reportDroppedNamesMigration(section2) {
3698
3886
  }
3699
3887
  }
3700
3888
 
3701
- // src/commands.doctor.checks.settings.ts
3889
+ // src/commands/doctor/checks/settings.ts
3702
3890
  init_color();
3703
3891
  init_config();
3704
3892
  import { existsSync as existsSync12, readdirSync as readdirSync6 } from "node:fs";
@@ -3759,7 +3947,7 @@ function reportHostOverrides(section2, base, settings) {
3759
3947
  }
3760
3948
  }
3761
3949
 
3762
- // src/commands.doctor.checks.pathmap.ts
3950
+ // src/commands/doctor/checks/pathmap.ts
3763
3951
  init_color();
3764
3952
  init_config();
3765
3953
  init_config_sharedDirs_guard();
@@ -3945,7 +4133,7 @@ function reportNeverSync(section2) {
3945
4133
  );
3946
4134
  }
3947
4135
 
3948
- // src/commands.doctor.checks.skills.ts
4136
+ // src/commands/doctor/checks/skills.ts
3949
4137
  init_color();
3950
4138
  import { existsSync as existsSync14 } from "node:fs";
3951
4139
  import { join as join20 } from "node:path";
@@ -3993,18 +4181,37 @@ function reportSkillsDivergence(section2) {
3993
4181
  }
3994
4182
  }
3995
4183
 
3996
- // src/commands.doctor.checks.git-state.ts
4184
+ // src/commands/doctor/checks/git-state.ts
3997
4185
  init_color();
3998
4186
  init_config();
3999
- import { execFileSync as execFileSync5 } from "node:child_process";
4000
- import { existsSync as existsSync17 } from "node:fs";
4001
- import { join as join24, relative as relative5 } from "node:path";
4002
- init_commands_pull_wedge();
4003
- init_push_checks();
4187
+ import { execFileSync as execFileSync6 } from "node:child_process";
4188
+ import { existsSync as existsSync18 } from "node:fs";
4189
+ import { join as join25, relative as relative5 } from "node:path";
4190
+ init_wedge();
4191
+
4192
+ // src/core/git-probe.ts
4193
+ import { execFileSync as execFileSync4 } from "node:child_process";
4194
+ var PROBE_TIMEOUT_MS = 1e4;
4195
+ var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
4196
+ function gitProbe(args, repo) {
4197
+ try {
4198
+ return execFileSync4("git", args, {
4199
+ cwd: repo,
4200
+ stdio: ["ignore", "pipe", "pipe"],
4201
+ timeout: PROBE_TIMEOUT_MS,
4202
+ maxBuffer: PROBE_MAX_BUFFER
4203
+ }).toString();
4204
+ } catch {
4205
+ return null;
4206
+ }
4207
+ }
4208
+
4209
+ // src/commands/doctor/checks/git-state.ts
4210
+ init_checks();
4004
4211
  init_utils();
4005
4212
  function reportGitleaksProbe(section2) {
4006
4213
  try {
4007
- execFileSync5("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4214
+ execFileSync6("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4008
4215
  return true;
4009
4216
  } catch (err) {
4010
4217
  if (err.code === "ENOENT") {
@@ -4021,8 +4228,8 @@ function reportGitleaksProbe(section2) {
4021
4228
  }
4022
4229
  function reportGitlinks(section2) {
4023
4230
  const repo = repoHome();
4024
- const sharedDir = join24(repo, "shared");
4025
- if (existsSync17(sharedDir)) {
4231
+ const sharedDir = join25(repo, "shared");
4232
+ if (existsSync18(sharedDir)) {
4026
4233
  const gitlinks = findGitlinks(sharedDir);
4027
4234
  for (const p of gitlinks) {
4028
4235
  const rel = relative5(repo, p).replaceAll("\\", "/");
@@ -4040,7 +4247,7 @@ function reportGitlinks(section2) {
4040
4247
  }
4041
4248
  function reportRemote(section2) {
4042
4249
  try {
4043
- const url = execFileSync5("git", ["remote", "get-url", "origin"], {
4250
+ const url = execFileSync6("git", ["remote", "get-url", "origin"], {
4044
4251
  cwd: repoHome(),
4045
4252
  stdio: ["ignore", "pipe", "pipe"]
4046
4253
  }).toString().trim();
@@ -4097,7 +4304,7 @@ function reportGitIdentity(section2) {
4097
4304
  const missing = [];
4098
4305
  for (const field of ["user.name", "user.email"]) {
4099
4306
  try {
4100
- const val = execFileSync5("git", ["config", field], {
4307
+ const val = execFileSync6("git", ["config", field], {
4101
4308
  cwd: repo,
4102
4309
  stdio: ["ignore", "pipe", "pipe"]
4103
4310
  }).toString().trim();
@@ -4117,11 +4324,50 @@ function reportGitIdentity(section2) {
4117
4324
  addItem(section2, `${green(okGlyph)} git identity: user.name and user.email configured`);
4118
4325
  }
4119
4326
  }
4327
+ var MAX_TRACKED_DENIED_ROWS = 5;
4328
+ var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
4329
+ function renderable(text) {
4330
+ return text.replace(CONTROL_CHARS, " ");
4331
+ }
4332
+ function reportTrackedDeniedShared(section2) {
4333
+ const out = gitProbe(["ls-files", "-z", "--", ":(icase)shared/"], repoHome());
4334
+ if (out === null) {
4335
+ addItem(
4336
+ section2,
4337
+ `${dim(infoGlyph)} never-sync scan: git could not list shared/, so nothing was scanned`
4338
+ );
4339
+ return;
4340
+ }
4341
+ const hits = /* @__PURE__ */ new Map();
4342
+ for (const path of out.split("\0")) {
4343
+ const segment = path === "" ? null : deniedSegmentFor(path);
4344
+ if (segment !== null) hits.set(path, segment);
4345
+ }
4346
+ if (hits.size === 0) {
4347
+ addItem(
4348
+ section2,
4349
+ `${green(okGlyph)} never-sync scan: no tracked path under shared/ is denylisted`
4350
+ );
4351
+ return;
4352
+ }
4353
+ for (const [path, segment] of [...hits].slice(0, MAX_TRACKED_DENIED_ROWS)) {
4354
+ addItem(
4355
+ section2,
4356
+ `${yellow(warnGlyph)} never-sync: git tracks ${blue(renderable(path))} (the path segment "${renderable(segment)}" is blocked by the never-sync boundary)`
4357
+ );
4358
+ }
4359
+ const overflow = hits.size - MAX_TRACKED_DENIED_ROWS;
4360
+ const more = overflow > 0 ? `${overflow} more not listed, run git ls-files -- ":(icase)shared/" in the sync repo to see them all. ` : "";
4361
+ addItem(
4362
+ section2,
4363
+ `${yellow(warnGlyph)} never-sync: ${more}Run git rm --cached -- "<path>" and commit to stop tracking each one; that leaves the file on disk, so move it outside shared/ if you want to keep it. If one holds a real secret, rotate it and rewrite history too, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent`
4364
+ );
4365
+ }
4120
4366
 
4121
- // src/commands.doctor.checks.backups.ts
4367
+ // src/commands/doctor/checks/backups.ts
4122
4368
  init_color();
4123
- import { existsSync as existsSync18, lstatSync as lstatSync12, readdirSync as readdirSync9 } from "node:fs";
4124
- import { join as join25 } from "node:path";
4369
+ import { existsSync as existsSync19, lstatSync as lstatSync12, readdirSync as readdirSync9 } from "node:fs";
4370
+ import { join as join26 } from "node:path";
4125
4371
  init_config();
4126
4372
  var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
4127
4373
  function safeReaddir(dir) {
@@ -4137,7 +4383,7 @@ var BYTES_PER_MB = 1024 * 1024;
4137
4383
  function dirSizeBytes(dir) {
4138
4384
  let bytes = 0;
4139
4385
  for (const entry of safeReaddir(dir)) {
4140
- const full = join25(dir, entry);
4386
+ const full = join26(dir, entry);
4141
4387
  const st = lstatSync12(full, { throwIfNoEntry: false });
4142
4388
  if (!st) continue;
4143
4389
  if (st.isSymbolicLink()) continue;
@@ -4148,11 +4394,11 @@ function dirSizeBytes(dir) {
4148
4394
  }
4149
4395
  function totalSizeMb(backupBase2, dirs) {
4150
4396
  let bytes = 0;
4151
- for (const name of dirs) bytes += dirSizeBytes(join25(backupBase2, name));
4397
+ for (const name of dirs) bytes += dirSizeBytes(join26(backupBase2, name));
4152
4398
  return bytes / BYTES_PER_MB;
4153
4399
  }
4154
4400
  function reportBackupsCheck(section2, backupBase2 = backupBase()) {
4155
- if (!existsSync18(backupBase2)) return;
4401
+ if (!existsSync19(backupBase2)) return;
4156
4402
  const dirs = safeReaddir(backupBase2).filter((n) => TS_SHAPE2.test(n));
4157
4403
  const count = dirs.length;
4158
4404
  const sizeMb = totalSizeMb(backupBase2, dirs);
@@ -4164,16 +4410,16 @@ function reportBackupsCheck(section2, backupBase2 = backupBase()) {
4164
4410
  }
4165
4411
  }
4166
4412
 
4167
- // src/commands.doctor.check-remote.ts
4413
+ // src/commands/doctor/checks/remote.ts
4168
4414
  init_color();
4169
- import { execFileSync as execFileSync6 } from "node:child_process";
4415
+ import { execFileSync as execFileSync7 } from "node:child_process";
4170
4416
  init_config();
4171
4417
  init_utils_json();
4172
4418
  var GIT_REMOTE_TIMEOUT_MS = 3e3;
4173
4419
  function readRemotePathMap(sec, repo) {
4174
4420
  let rawJson;
4175
4421
  try {
4176
- rawJson = execFileSync6("git", ["show", "origin/main:path-map.json"], {
4422
+ rawJson = execFileSync7("git", ["show", "origin/main:path-map.json"], {
4177
4423
  cwd: repo,
4178
4424
  stdio: ["ignore", "pipe", "pipe"],
4179
4425
  timeout: GIT_REMOTE_TIMEOUT_MS
@@ -4206,7 +4452,7 @@ function reportCheckRemote(section2) {
4206
4452
  const repo = repoHome();
4207
4453
  let names;
4208
4454
  try {
4209
- const out = execFileSync6("git", ["ls-tree", "--name-only", "origin/main"], {
4455
+ const out = execFileSync7("git", ["ls-tree", "--name-only", "origin/main"], {
4210
4456
  cwd: repo,
4211
4457
  stdio: ["ignore", "pipe", "pipe"],
4212
4458
  timeout: GIT_REMOTE_TIMEOUT_MS
@@ -4231,16 +4477,16 @@ function reportCheckRemote(section2) {
4231
4477
  addItem(section2, `${green(okGlyph)} remote: origin/main has shared/ and a valid path-map.json`);
4232
4478
  }
4233
4479
 
4234
- // src/commands.doctor.check-schema.ts
4480
+ // src/commands/doctor/checks/schema.ts
4235
4481
  init_color();
4236
- import { existsSync as existsSync19 } from "node:fs";
4237
- import { join as join26 } from "node:path";
4482
+ import { existsSync as existsSync20 } from "node:fs";
4483
+ import { join as join27 } from "node:path";
4238
4484
  init_config();
4239
4485
 
4240
- // src/http-fetch.ts
4241
- import { execFileSync as execFileSync7 } from "node:child_process";
4486
+ // src/core/http-fetch.ts
4487
+ import { execFileSync as execFileSync8 } from "node:child_process";
4242
4488
  var FETCH_TIMEOUT_MS = 3e3;
4243
- function fetchUrl(url, run = execFileSync7) {
4489
+ function fetchUrl(url, run = execFileSync8) {
4244
4490
  try {
4245
4491
  return run("curl", ["-fsSL", "-m", "3", url], {
4246
4492
  stdio: ["ignore", "pipe", "pipe"],
@@ -4258,7 +4504,7 @@ function fetchUrl(url, run = execFileSync7) {
4258
4504
  }
4259
4505
  }
4260
4506
 
4261
- // src/commands.doctor.check-schema.ts
4507
+ // src/commands/doctor/checks/schema.ts
4262
4508
  function fetchSchemaKeys() {
4263
4509
  try {
4264
4510
  const raw = fetchUrl(SETTINGS_SCHEMA_URL);
@@ -4271,8 +4517,8 @@ function fetchSchemaKeys() {
4271
4517
  }
4272
4518
  }
4273
4519
  function reportCheckSchema(section2) {
4274
- const settingsPath = join26(claudeHome(), "settings.json");
4275
- if (!existsSync19(settingsPath)) {
4520
+ const settingsPath = join27(claudeHome(), "settings.json");
4521
+ if (!existsSync20(settingsPath)) {
4276
4522
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json to check`);
4277
4523
  return;
4278
4524
  }
@@ -4298,29 +4544,29 @@ function reportCheckSchema(section2) {
4298
4544
  }
4299
4545
  }
4300
4546
 
4301
- // src/commands.doctor.check-shared.ts
4547
+ // src/commands/doctor/checks/shared.ts
4302
4548
  init_color();
4303
4549
  import { randomBytes as randomBytes3 } from "node:crypto";
4304
- import { execFileSync as execFileSync11 } from "node:child_process";
4305
- import { existsSync as existsSync22, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync12 } from "node:fs";
4550
+ import { execFileSync as execFileSync12 } from "node:child_process";
4551
+ import { existsSync as existsSync23, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync12 } from "node:fs";
4306
4552
  import { homedir as homedir6 } from "node:os";
4307
- import { join as join33 } from "node:path";
4553
+ import { join as join34 } from "node:path";
4308
4554
 
4309
- // src/commands.doctor.check-shared.memory.ts
4555
+ // src/commands/doctor/checks/shared.memory.ts
4310
4556
  init_color();
4311
- import { execFileSync as execFileSync9 } from "node:child_process";
4557
+ import { execFileSync as execFileSync10 } from "node:child_process";
4312
4558
  import { randomBytes } from "node:crypto";
4313
4559
  import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
4314
4560
  import { homedir as homedir4 } from "node:os";
4315
- import { dirname as dirname4, join as join28 } from "node:path";
4561
+ import { dirname as dirname5, join as join29 } from "node:path";
4316
4562
  init_config();
4317
- init_push_gitleaks();
4563
+ init_gitleaks();
4318
4564
  init_utils_fs();
4319
4565
  var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
4320
4566
  function buildMemoryScanTree(tmpRoot) {
4321
4567
  let paths;
4322
4568
  try {
4323
- const out = execFileSync9(
4569
+ const out = execFileSync10(
4324
4570
  "git",
4325
4571
  ["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
4326
4572
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
@@ -4336,7 +4582,7 @@ function buildMemoryScanTree(tmpRoot) {
4336
4582
  if (m?.[1] === void 0) continue;
4337
4583
  let blob;
4338
4584
  try {
4339
- blob = execFileSync9("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4585
+ blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4340
4586
  stdio: ["ignore", "pipe", "ignore"],
4341
4587
  maxBuffer: 67108864,
4342
4588
  timeout: 1e4
@@ -4345,8 +4591,8 @@ function buildMemoryScanTree(tmpRoot) {
4345
4591
  incomplete = true;
4346
4592
  continue;
4347
4593
  }
4348
- const dest = join28(tmpRoot, rel);
4349
- mkdirSync4(dirname4(dest), { recursive: true });
4594
+ const dest = join29(tmpRoot, rel);
4595
+ mkdirSync4(dirname5(dest), { recursive: true });
4350
4596
  writeFileSync4(dest, blob);
4351
4597
  logicals.add(m[1]);
4352
4598
  }
@@ -4364,10 +4610,10 @@ function reportMemoryFindings(section2, findings) {
4364
4610
  function reportCommittedMemory(section2) {
4365
4611
  let tmpRoot;
4366
4612
  try {
4367
- const cacheDir = join28(homedir4(), ".cache", "claude-nomad");
4613
+ const cacheDir = join29(homedir4(), ".cache", "claude-nomad");
4368
4614
  mkdirSync4(cacheDir, { recursive: true });
4369
4615
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
4370
- tmpRoot = join28(cacheDir, `check-shared-memory-tree-${stamp}`);
4616
+ tmpRoot = join29(cacheDir, `check-shared-memory-tree-${stamp}`);
4371
4617
  mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
4372
4618
  const { staged, incomplete } = buildMemoryScanTree(tmpRoot);
4373
4619
  if (incomplete) {
@@ -4412,14 +4658,14 @@ function reportCommittedMemory(section2) {
4412
4658
  }
4413
4659
  }
4414
4660
 
4415
- // src/commands.doctor.check-shared.scan.ts
4661
+ // src/commands/doctor/checks/shared.scan.ts
4416
4662
  init_color();
4417
- import { join as join29 } from "node:path";
4663
+ import { join as join30 } from "node:path";
4418
4664
  init_config();
4419
- init_push_gitleaks();
4665
+ init_gitleaks();
4420
4666
  function scrubPath(logical, sid, logicalToEncoded) {
4421
4667
  const encoded = logicalToEncoded.get(logical) ?? logical;
4422
- return join29(claudeHome(), "projects", encoded, `${sid}.jsonl`);
4668
+ return join30(claudeHome(), "projects", encoded, `${sid}.jsonl`);
4423
4669
  }
4424
4670
  function reportSessionFindings(section2, bySession) {
4425
4671
  for (const [sid, counts] of bySession) {
@@ -4502,21 +4748,21 @@ function scanAndReport(section2, tmpRoot, staged, logicalToEncoded) {
4502
4748
  emitDescriptionLegend(section2, findings);
4503
4749
  }
4504
4750
 
4505
- // src/commands.doctor.check-shared.skills.ts
4751
+ // src/commands/doctor/checks/shared.skills.ts
4506
4752
  init_color();
4507
- import { execFileSync as execFileSync10 } from "node:child_process";
4753
+ import { execFileSync as execFileSync11 } from "node:child_process";
4508
4754
  import { randomBytes as randomBytes2 } from "node:crypto";
4509
4755
  import { mkdirSync as mkdirSync5, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
4510
4756
  import { homedir as homedir5 } from "node:os";
4511
- import { dirname as dirname5, join as join30 } from "node:path";
4757
+ import { dirname as dirname6, join as join31 } from "node:path";
4512
4758
  init_config();
4513
- init_push_gitleaks();
4759
+ init_gitleaks();
4514
4760
  init_utils_fs();
4515
4761
  var SKILL_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
4516
4762
  function buildSkillScanTree(tmpRoot) {
4517
4763
  let paths;
4518
4764
  try {
4519
- const out = execFileSync10(
4765
+ const out = execFileSync11(
4520
4766
  "git",
4521
4767
  ["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/skills"],
4522
4768
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
@@ -4532,7 +4778,7 @@ function buildSkillScanTree(tmpRoot) {
4532
4778
  if (m?.[1] === void 0) continue;
4533
4779
  let blob;
4534
4780
  try {
4535
- blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4781
+ blob = execFileSync11("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4536
4782
  stdio: ["ignore", "pipe", "ignore"],
4537
4783
  maxBuffer: 67108864,
4538
4784
  timeout: 1e4
@@ -4541,8 +4787,8 @@ function buildSkillScanTree(tmpRoot) {
4541
4787
  incomplete = true;
4542
4788
  continue;
4543
4789
  }
4544
- const dest = join30(tmpRoot, rel);
4545
- mkdirSync5(dirname5(dest), { recursive: true });
4790
+ const dest = join31(tmpRoot, rel);
4791
+ mkdirSync5(dirname6(dest), { recursive: true });
4546
4792
  writeFileSync5(dest, blob);
4547
4793
  names.add(m[1]);
4548
4794
  }
@@ -4564,10 +4810,10 @@ function reportSkillFindings(section2, findings) {
4564
4810
  function reportCommittedSkills(section2) {
4565
4811
  let tmpRoot;
4566
4812
  try {
4567
- const cacheDir = join30(homedir5(), ".cache", "claude-nomad");
4813
+ const cacheDir = join31(homedir5(), ".cache", "claude-nomad");
4568
4814
  mkdirSync5(cacheDir, { recursive: true });
4569
4815
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
4570
- tmpRoot = join30(cacheDir, `check-shared-skills-tree-${stamp}`);
4816
+ tmpRoot = join31(cacheDir, `check-shared-skills-tree-${stamp}`);
4571
4817
  mkdirSync5(tmpRoot, { recursive: true, mode: 448 });
4572
4818
  const { staged, incomplete } = buildSkillScanTree(tmpRoot);
4573
4819
  if (incomplete) {
@@ -4612,22 +4858,22 @@ function reportCommittedSkills(section2) {
4612
4858
  }
4613
4859
  }
4614
4860
 
4615
- // src/commands.doctor.check-shared.ts
4861
+ // src/commands/doctor/checks/shared.ts
4616
4862
  init_config();
4617
4863
 
4618
- // src/remap.ts
4864
+ // src/sync/remap.ts
4619
4865
  init_config_sharedDirs_guard();
4620
- import { cpSync as cpSync5, existsSync as existsSync21, lstatSync as lstatSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11, statSync as statSync6 } from "node:fs";
4621
- import { dirname as dirname7, join as join32, relative as relative6, sep as sep5 } from "node:path";
4866
+ import { cpSync as cpSync5, existsSync as existsSync22, lstatSync as lstatSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11, statSync as statSync6 } from "node:fs";
4867
+ import { dirname as dirname8, join as join33, relative as relative6, sep as sep5 } from "node:path";
4622
4868
  init_config();
4623
4869
 
4624
- // src/push-manifest.ts
4870
+ // src/commands/push/manifest.ts
4625
4871
  init_config();
4626
- init_push_gitleaks_config();
4872
+ init_gitleaks_config();
4627
4873
  init_utils_fs();
4628
4874
  import { createHash } from "node:crypto";
4629
- import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync10, readFileSync as readFileSync7, statSync as statSync5 } from "node:fs";
4630
- import { dirname as dirname6, join as join31 } from "node:path";
4875
+ import { existsSync as existsSync21, mkdirSync as mkdirSync6, readdirSync as readdirSync10, readFileSync as readFileSync7, statSync as statSync5 } from "node:fs";
4876
+ import { dirname as dirname7, join as join32 } from "node:path";
4631
4877
  function isChanged(prev, cur, hash) {
4632
4878
  if (prev === void 0) return true;
4633
4879
  if (prev.size !== cur.size) return true;
@@ -4668,7 +4914,7 @@ function hashFile(absPath) {
4668
4914
  }
4669
4915
  function enumerateDir(dir, results) {
4670
4916
  for (const entry of readdirSync10(dir)) {
4671
- const fullPath = join31(dir, entry);
4917
+ const fullPath = join32(dir, entry);
4672
4918
  const st = statSync5(fullPath);
4673
4919
  if (st.isDirectory()) {
4674
4920
  enumerateDir(fullPath, results);
@@ -4680,7 +4926,7 @@ function enumerateDir(dir, results) {
4680
4926
  function enumerateSourceFiles(projectDir) {
4681
4927
  const results = [];
4682
4928
  for (const entry of readdirSync10(projectDir)) {
4683
- const fullPath = join31(projectDir, entry);
4929
+ const fullPath = join32(projectDir, entry);
4684
4930
  const st = statSync5(fullPath);
4685
4931
  if (st.isDirectory()) {
4686
4932
  enumerateDir(fullPath, results);
@@ -4692,15 +4938,15 @@ function enumerateSourceFiles(projectDir) {
4692
4938
  }
4693
4939
  function fileIdentity(p) {
4694
4940
  if (p === null) return "none::absent";
4695
- if (!existsSync20(p)) return `${p}::absent`;
4941
+ if (!existsSync21(p)) return `${p}::absent`;
4696
4942
  return `${p}::${createHash("sha256").update(readFileSync7(p)).digest("hex")}`;
4697
4943
  }
4698
4944
  function computeConfigHash() {
4699
4945
  const repo = repoHome();
4700
4946
  const parts = [
4701
4947
  fileIdentity(resolveTomlPath(repo)),
4702
- fileIdentity(join31(repo, ".gitleaks.overlay.toml")),
4703
- fileIdentity(join31(repo, ".gitleaksignore"))
4948
+ fileIdentity(join32(repo, ".gitleaks.overlay.toml")),
4949
+ fileIdentity(join32(repo, ".gitleaksignore"))
4704
4950
  ];
4705
4951
  return createHash("sha256").update(parts.join("\n")).digest("hex");
4706
4952
  }
@@ -4720,14 +4966,14 @@ function readManifest(path) {
4720
4966
  }
4721
4967
  }
4722
4968
  function writeManifest(path, manifest) {
4723
- mkdirSync6(dirname6(path), { recursive: true });
4969
+ mkdirSync6(dirname7(path), { recursive: true });
4724
4970
  writeJsonAtomic(path, manifest);
4725
4971
  }
4726
4972
  function buildManifest(files, scannerVersion, configHash) {
4727
4973
  return { schema: 1, scannerVersion, configHash, files };
4728
4974
  }
4729
4975
 
4730
- // src/remap.ts
4976
+ // src/sync/remap.ts
4731
4977
  init_utils();
4732
4978
  init_utils_fs();
4733
4979
  init_utils_json();
@@ -4744,16 +4990,15 @@ function overlaySessionDir(src, dst) {
4744
4990
  cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
4745
4991
  }
4746
4992
  function copyFileAtomic(src, dst) {
4747
- mkdirSync7(dirname7(dst), { recursive: true });
4993
+ mkdirSync7(dirname8(dst), { recursive: true });
4748
4994
  const tmp = `${dst}.tmp.${process.pid}`;
4749
4995
  cpSync5(src, tmp, { force: true, preserveTimestamps: true });
4750
4996
  renameAtomicRetry(tmp, dst);
4751
4997
  }
4752
4998
  function hasDeltaForDir(sel, localDir) {
4753
4999
  const prefix = `${localDir}${sep5}`;
4754
- for (const p of sel.changed) if (p.startsWith(prefix)) return true;
4755
- for (const p of sel.deleted) if (p.startsWith(prefix)) return true;
4756
- return false;
5000
+ const underDir = (p) => p.startsWith(prefix);
5001
+ return [...sel.changed].some(underDir) || sel.deleted.some(underDir);
4757
5002
  }
4758
5003
  function skipForSelection(sel, localDir) {
4759
5004
  return sel !== void 0 && !hasDeltaForDir(sel, localDir);
@@ -4762,12 +5007,12 @@ function applySelective(sel, localDir, repoDst) {
4762
5007
  const prefix = `${localDir}${sep5}`;
4763
5008
  for (const src of sel.changed) {
4764
5009
  if (!src.startsWith(prefix)) continue;
4765
- copyFileAtomic(src, join32(repoDst, relative6(localDir, src)));
5010
+ copyFileAtomic(src, join33(repoDst, relative6(localDir, src)));
4766
5011
  }
4767
5012
  for (const src of sel.deleted) {
4768
5013
  if (!src.startsWith(prefix)) continue;
4769
- const dst = join32(repoDst, relative6(localDir, src));
4770
- if (existsSync21(dst)) rmSync11(dst);
5014
+ const dst = join33(repoDst, relative6(localDir, src));
5015
+ if (existsSync22(dst)) rmSync11(dst);
4771
5016
  }
4772
5017
  }
4773
5018
  function copyDirJsonlOnly(src, dst) {
@@ -4796,15 +5041,15 @@ function remapPull(ts, opts = {}) {
4796
5041
  const wouldPull = [];
4797
5042
  const repo = repoHome();
4798
5043
  const claude = claudeHome();
4799
- const mapPath = join32(repo, "path-map.json");
4800
- const repoProjects = join32(repo, "shared", "projects");
4801
- if (!existsSync21(mapPath) || !existsSync21(repoProjects)) {
5044
+ const mapPath = join33(repo, "path-map.json");
5045
+ const repoProjects = join33(repo, "shared", "projects");
5046
+ if (!existsSync22(mapPath) || !existsSync22(repoProjects)) {
4802
5047
  const text = "no path-map or repo projects dir; skipping session remap";
4803
5048
  emitPreview(opts.onPreview, { kind: "note", text }, text);
4804
5049
  return { unmapped: 0, pulled, wouldPull };
4805
5050
  }
4806
5051
  const map = readPathMap(mapPath);
4807
- const localProjects = join32(claude, "projects");
5052
+ const localProjects = join33(claude, "projects");
4808
5053
  if (!dryRun) mkdirSync7(localProjects, { recursive: true });
4809
5054
  for (const [logical, hosts] of Object.entries(map.projects)) {
4810
5055
  assertSafeLogical(logical);
@@ -4814,9 +5059,9 @@ function remapPull(ts, opts = {}) {
4814
5059
  continue;
4815
5060
  }
4816
5061
  assertSafeLocalRoot(localPath, logical);
4817
- const src = join32(repoProjects, logical);
4818
- if (!existsSync21(src)) continue;
4819
- const dst = join32(localProjects, encodePath(localPath));
5062
+ const src = join33(repoProjects, logical);
5063
+ if (!existsSync22(src)) continue;
5064
+ const dst = join33(localProjects, encodePath(localPath));
4820
5065
  if (dryRun) {
4821
5066
  wouldPull.push(logical);
4822
5067
  emitPreview(
@@ -4835,8 +5080,8 @@ function remapPull(ts, opts = {}) {
4835
5080
  function countLocalOnly(src, dst) {
4836
5081
  let count = 0;
4837
5082
  for (const name of readdirSync11(dst)) {
4838
- const dstPath = join32(dst, name);
4839
- const srcPath = join32(src, name);
5083
+ const dstPath = join33(dst, name);
5084
+ const srcPath = join33(src, name);
4840
5085
  if (lstatSync13(dstPath).isDirectory()) {
4841
5086
  count += countLocalOnly(srcPath, dstPath);
4842
5087
  } else if (lstatSync13(srcPath, { throwIfNoEntry: false }) === void 0) {
@@ -4848,20 +5093,20 @@ function countLocalOnly(src, dst) {
4848
5093
  function scanLocalOnly() {
4849
5094
  const repo = repoHome();
4850
5095
  const claude = claudeHome();
4851
- const mapPath = join32(repo, "path-map.json");
4852
- const repoProjects = join32(repo, "shared", "projects");
4853
- if (!existsSync21(mapPath) || !existsSync21(repoProjects)) return 0;
5096
+ const mapPath = join33(repo, "path-map.json");
5097
+ const repoProjects = join33(repo, "shared", "projects");
5098
+ if (!existsSync22(mapPath) || !existsSync22(repoProjects)) return 0;
4854
5099
  const map = readPathMap(mapPath);
4855
- const localProjects = join32(claude, "projects");
5100
+ const localProjects = join33(claude, "projects");
4856
5101
  let count = 0;
4857
5102
  for (const [logical, hosts] of Object.entries(map.projects)) {
4858
5103
  assertSafeLogical(logical);
4859
5104
  const localPath = hosts[HOST];
4860
5105
  if (!localPath || localPath === "TBD") continue;
4861
5106
  assertSafeLocalRoot(localPath, logical);
4862
- const dst = join32(localProjects, encodePath(localPath));
4863
- if (!existsSync21(dst)) continue;
4864
- count += countLocalOnly(join32(repoProjects, logical), dst);
5107
+ const dst = join33(localProjects, encodePath(localPath));
5108
+ if (!existsSync22(dst)) continue;
5109
+ count += countLocalOnly(join33(repoProjects, logical), dst);
4865
5110
  }
4866
5111
  return count;
4867
5112
  }
@@ -4896,16 +5141,16 @@ function remapPush(ts, opts = {}) {
4896
5141
  const wouldPush = [];
4897
5142
  const repo = repoHome();
4898
5143
  const claude = claudeHome();
4899
- const mapPath = join32(repo, "path-map.json");
4900
- if (!existsSync21(mapPath)) {
5144
+ const mapPath = join33(repo, "path-map.json");
5145
+ if (!existsSync22(mapPath)) {
4901
5146
  log("no path-map.json; skipping session export");
4902
5147
  return { unmapped: 0, collisions: 0, pushed, wouldPush };
4903
5148
  }
4904
5149
  const map = readPathMap(mapPath);
4905
- const localProjects = join32(claude, "projects");
4906
- const repoProjects = join32(repo, "shared", "projects");
5150
+ const localProjects = join33(claude, "projects");
5151
+ const repoProjects = join33(repo, "shared", "projects");
4907
5152
  const reverse = buildReverseMap(map);
4908
- if (!existsSync21(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
5153
+ if (!existsSync22(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
4909
5154
  if (!dryRun) mkdirSync7(repoProjects, { recursive: true });
4910
5155
  for (const dir of readdirSync11(localProjects)) {
4911
5156
  if (dir.endsWith(TMP_SUFFIX)) continue;
@@ -4914,8 +5159,8 @@ function remapPush(ts, opts = {}) {
4914
5159
  unmapped++;
4915
5160
  continue;
4916
5161
  }
4917
- const localDir = join32(localProjects, dir);
4918
- const repoDst = join32(repoProjects, logical);
5162
+ const localDir = join33(localProjects, dir);
5163
+ const repoDst = join33(repoProjects, logical);
4919
5164
  if (skipForSelection(opts.selection, localDir)) continue;
4920
5165
  if (dryRun) {
4921
5166
  wouldPush.push(logical);
@@ -4932,15 +5177,15 @@ function remapPush(ts, opts = {}) {
4932
5177
  return { unmapped, collisions: 0, pushed, wouldPush };
4933
5178
  }
4934
5179
 
4935
- // src/commands.doctor.check-shared.ts
5180
+ // src/commands/doctor/checks/shared.ts
4936
5181
  init_utils_fs();
4937
5182
  init_utils_json();
4938
5183
  function buildScanTree(tmpRoot) {
4939
5184
  const logicalToEncoded = /* @__PURE__ */ new Map();
4940
5185
  let staged = 0;
4941
5186
  const repo = repoHome();
4942
- const mapPath = join33(repo, "path-map.json");
4943
- if (!existsSync22(mapPath)) return { logicalToEncoded, staged, malformed: false };
5187
+ const mapPath = join34(repo, "path-map.json");
5188
+ if (!existsSync23(mapPath)) return { logicalToEncoded, staged, malformed: false };
4944
5189
  let map;
4945
5190
  try {
4946
5191
  map = readJson(mapPath);
@@ -4957,12 +5202,12 @@ function buildScanTree(tmpRoot) {
4957
5202
  if (!p || p === "TBD") continue;
4958
5203
  reverse.set(encodePath(p), logical);
4959
5204
  }
4960
- const localProjects = join33(claudeHome(), "projects");
4961
- if (!existsSync22(localProjects)) return { logicalToEncoded, staged, malformed: false };
5205
+ const localProjects = join34(claudeHome(), "projects");
5206
+ if (!existsSync23(localProjects)) return { logicalToEncoded, staged, malformed: false };
4962
5207
  for (const dir of readdirSync12(localProjects)) {
4963
5208
  const logical = reverse.get(dir);
4964
5209
  if (!logical) continue;
4965
- copyDirJsonlOnly(join33(localProjects, dir), join33(tmpRoot, "shared", "projects", logical));
5210
+ copyDirJsonlOnly(join34(localProjects, dir), join34(tmpRoot, "shared", "projects", logical));
4966
5211
  logicalToEncoded.set(logical, dir);
4967
5212
  staged++;
4968
5213
  }
@@ -4970,7 +5215,7 @@ function buildScanTree(tmpRoot) {
4970
5215
  }
4971
5216
  function probeGitleaksForScan() {
4972
5217
  try {
4973
- execFileSync11("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
5218
+ execFileSync12("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4974
5219
  return "ok";
4975
5220
  } catch (err) {
4976
5221
  if (err.code === "ENOENT") return "missing";
@@ -4992,11 +5237,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
4992
5237
  return true;
4993
5238
  }
4994
5239
  function runLocalPreviewScan(section2) {
4995
- const cacheDir = join33(homedir6(), ".cache", "claude-nomad");
5240
+ const cacheDir = join34(homedir6(), ".cache", "claude-nomad");
4996
5241
  mkdirSync8(cacheDir, { recursive: true });
4997
5242
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
4998
- const reportPath = join33(cacheDir, `check-shared-${stamp}.json`);
4999
- const tmpRoot = join33(cacheDir, `check-shared-tree-${stamp}`);
5243
+ const reportPath = join34(cacheDir, `check-shared-${stamp}.json`);
5244
+ const tmpRoot = join34(cacheDir, `check-shared-tree-${stamp}`);
5000
5245
  try {
5001
5246
  const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
5002
5247
  if (malformed) {
@@ -5027,10 +5272,10 @@ function reportCheckShared(section2, gitleaksReady) {
5027
5272
  }
5028
5273
  }
5029
5274
 
5030
- // src/commands.doctor.checks.hooks.scope.ts
5275
+ // src/commands/doctor/checks/hooks.scope.ts
5031
5276
  init_color();
5032
- import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync13, realpathSync as realpathSync3 } from "node:fs";
5033
- import { dirname as dirname8, extname, join as join34 } from "node:path";
5277
+ import { existsSync as existsSync24, readFileSync as readFileSync8, readdirSync as readdirSync13, realpathSync as realpathSync3 } from "node:fs";
5278
+ import { dirname as dirname9, extname, join as join35 } from "node:path";
5034
5279
  init_config();
5035
5280
  function typeFromPackageJson(pkgPath) {
5036
5281
  try {
@@ -5050,11 +5295,11 @@ function effectiveType(hookPath) {
5050
5295
  } catch {
5051
5296
  return null;
5052
5297
  }
5053
- let dir = dirname8(real);
5298
+ let dir = dirname9(real);
5054
5299
  for (; ; ) {
5055
- const pkg = join34(dir, "package.json");
5056
- if (existsSync23(pkg)) return typeFromPackageJson(pkg);
5057
- const parent = dirname8(dir);
5300
+ const pkg = join35(dir, "package.json");
5301
+ if (existsSync24(pkg)) return typeFromPackageJson(pkg);
5302
+ const parent = dirname9(dir);
5058
5303
  if (parent === dir) return "cjs";
5059
5304
  dir = parent;
5060
5305
  }
@@ -5095,15 +5340,15 @@ function safeRead(path) {
5095
5340
  }
5096
5341
  }
5097
5342
  function reportHookScopeCheck(section2) {
5098
- const hooksDir = join34(claudeHome(), "hooks");
5099
- if (!existsSync23(hooksDir)) {
5343
+ const hooksDir = join35(claudeHome(), "hooks");
5344
+ if (!existsSync24(hooksDir)) {
5100
5345
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
5101
5346
  return;
5102
5347
  }
5103
5348
  let anyWarn = false;
5104
5349
  for (const name of safeReaddir2(hooksDir)) {
5105
5350
  if (extname(name) !== ".js") continue;
5106
- const abs = join34(hooksDir, name);
5351
+ const abs = join35(hooksDir, name);
5107
5352
  const eff = effectiveType(abs);
5108
5353
  if (eff === null) continue;
5109
5354
  const src = safeRead(abs);
@@ -5121,10 +5366,10 @@ function reportHookScopeCheck(section2) {
5121
5366
  }
5122
5367
  }
5123
5368
 
5124
- // src/commands.doctor.checks.hooks.ts
5369
+ // src/commands/doctor/checks/hooks.ts
5125
5370
  init_color();
5126
- import { existsSync as existsSync24 } from "node:fs";
5127
- import { join as join35 } from "node:path";
5371
+ import { existsSync as existsSync25 } from "node:fs";
5372
+ import { join as join36 } from "node:path";
5128
5373
  init_config();
5129
5374
  function expandHome(token) {
5130
5375
  const h2 = home();
@@ -5167,7 +5412,7 @@ function checkEventGroups(section2, event, groups) {
5167
5412
  for (const group of groups) {
5168
5413
  for (const cmd of commandsFromGroup(group)) {
5169
5414
  for (const resolved of claudePathsIn(cmd)) {
5170
- if (existsSync24(resolved)) continue;
5415
+ if (existsSync25(resolved)) continue;
5171
5416
  addItem(
5172
5417
  section2,
5173
5418
  `${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
@@ -5180,8 +5425,8 @@ function checkEventGroups(section2, event, groups) {
5180
5425
  return anyFail;
5181
5426
  }
5182
5427
  function reportHooksTargetCheck(section2) {
5183
- const settingsPath = join35(claudeHome(), "settings.json");
5184
- if (!existsSync24(settingsPath)) {
5428
+ const settingsPath = join36(claudeHome(), "settings.json");
5429
+ if (!existsSync25(settingsPath)) {
5185
5430
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
5186
5431
  return;
5187
5432
  }
@@ -5202,17 +5447,17 @@ function reportHooksTargetCheck(section2) {
5202
5447
  }
5203
5448
  }
5204
5449
 
5205
- // src/commands.doctor.checks.hooks.preserve-symlinks.ts
5450
+ // src/commands/doctor/checks/hooks.preserve-symlinks.ts
5206
5451
  init_color();
5207
- import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
5208
- import { join as join37 } from "node:path";
5452
+ import { existsSync as existsSync27, readFileSync as readFileSync9 } from "node:fs";
5453
+ import { join as join38 } from "node:path";
5209
5454
 
5210
- // src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
5211
- import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync4 } from "node:fs";
5212
- import { dirname as dirname9, join as join36, resolve as resolve3 } from "node:path";
5455
+ // src/commands/doctor/checks/hooks.preserve-symlinks.probe.ts
5456
+ import { closeSync as closeSync3, existsSync as existsSync26, openSync as openSync3, readSync, realpathSync as realpathSync4 } from "node:fs";
5457
+ import { dirname as dirname10, join as join37, resolve as resolve3 } from "node:path";
5213
5458
  function suppressedRanges(src) {
5214
5459
  const ranges = [];
5215
- const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
5460
+ const re = /\/\*[^]*?\*\/|\/\/[^\n]*|(['"`])(?:\\[^]|[^\\])*?\1/g;
5216
5461
  let m;
5217
5462
  while ((m = re.exec(src)) !== null) {
5218
5463
  ranges.push([m.index, m.index + m[0].length]);
@@ -5225,7 +5470,7 @@ function inSuppressedRange(pos, ranges) {
5225
5470
  }
5226
5471
  return false;
5227
5472
  }
5228
- function topRelativeSpecifiers(src) {
5473
+ function relativeSpecifiers(src) {
5229
5474
  const ranges = suppressedRanges(src);
5230
5475
  const specifiers = [];
5231
5476
  const reqRe = /\brequire\s*\(\s*(['"])(\.\.?\/[^'"]*)\1\s*\)/g;
@@ -5241,12 +5486,12 @@ function topRelativeSpecifiers(src) {
5241
5486
  }
5242
5487
  function specifierIsMissing(specifier, baseDir) {
5243
5488
  const base = resolve3(baseDir, specifier);
5244
- if (existsSync25(base)) return false;
5489
+ if (existsSync26(base)) return false;
5245
5490
  for (const ext of [".js", ".cjs", ".mjs"]) {
5246
- if (existsSync25(base + ext)) return false;
5491
+ if (existsSync26(base + ext)) return false;
5247
5492
  }
5248
5493
  for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
5249
- if (existsSync25(join36(base, idx))) return false;
5494
+ if (existsSync26(join37(base, idx))) return false;
5250
5495
  }
5251
5496
  return true;
5252
5497
  }
@@ -5270,16 +5515,16 @@ function relativeRequireTargetsBroken(scriptPath) {
5270
5515
  } catch {
5271
5516
  return false;
5272
5517
  }
5273
- const specifiers = topRelativeSpecifiers(raw);
5518
+ const specifiers = relativeSpecifiers(raw);
5274
5519
  if (specifiers.length === 0) return false;
5275
- const baseDir = dirname9(realPath);
5520
+ const baseDir = dirname10(realPath);
5276
5521
  for (const spec of specifiers) {
5277
5522
  if (specifierIsMissing(spec, baseDir)) return true;
5278
5523
  }
5279
5524
  return false;
5280
5525
  }
5281
5526
 
5282
- // src/commands.doctor.checks.hooks.preserve-symlinks.ts
5527
+ // src/commands/doctor/checks/hooks.preserve-symlinks.ts
5283
5528
  init_config();
5284
5529
  function expandHome2(token) {
5285
5530
  const h2 = home();
@@ -5301,8 +5546,8 @@ function commandTokens(command) {
5301
5546
  return tokens;
5302
5547
  }
5303
5548
  function readPathMapSafe() {
5304
- const mapPath = join37(repoHome(), "path-map.json");
5305
- if (!existsSync26(mapPath)) return { projects: {} };
5549
+ const mapPath = join38(repoHome(), "path-map.json");
5550
+ if (!existsSync27(mapPath)) return { projects: {} };
5306
5551
  try {
5307
5552
  return JSON.parse(readFileSync9(mapPath, "utf8"));
5308
5553
  } catch {
@@ -5376,8 +5621,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
5376
5621
  return anyWarn;
5377
5622
  }
5378
5623
  function reportPreserveSymlinksCheck(section2) {
5379
- const settingsPath = join37(claudeHome(), "settings.json");
5380
- if (!existsSync26(settingsPath)) {
5624
+ const settingsPath = join38(claudeHome(), "settings.json");
5625
+ if (!existsSync27(settingsPath)) {
5381
5626
  addItem(
5382
5627
  section2,
5383
5628
  `${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
@@ -5408,10 +5653,10 @@ function reportPreserveSymlinksCheck(section2) {
5408
5653
  }
5409
5654
  }
5410
5655
 
5411
- // src/commands.doctor.checks.settings-drift.ts
5656
+ // src/commands/doctor/checks/settings-drift.ts
5412
5657
  init_color();
5413
- import { existsSync as existsSync27, readFileSync as readFileSync10 } from "node:fs";
5414
- import { join as join38 } from "node:path";
5658
+ import { existsSync as existsSync28, readFileSync as readFileSync10 } from "node:fs";
5659
+ import { join as join39 } from "node:path";
5415
5660
  init_config();
5416
5661
  init_utils_json();
5417
5662
  function diffMergedSettings(merged, settings) {
@@ -5429,7 +5674,7 @@ function tryReadJson(filePath) {
5429
5674
  }
5430
5675
  }
5431
5676
  function reportHooksBaseSelfCleanNote(section2) {
5432
- const basePath = join38(repoHome(), "shared", "settings.base.json");
5677
+ const basePath = join39(repoHome(), "shared", "settings.base.json");
5433
5678
  const base = tryReadJson(basePath);
5434
5679
  if (base === null) return;
5435
5680
  if (!baseHasGsdHookEntries(base)) return;
@@ -5442,14 +5687,14 @@ function reportSettingsDriftCheck(section2) {
5442
5687
  const claude = claudeHome();
5443
5688
  const repo = repoHome();
5444
5689
  const host = HOST;
5445
- const settingsPath = join38(claude, "settings.json");
5446
- const basePath = join38(repo, "shared", "settings.base.json");
5447
- const hostPath = join38(repo, "hosts", `${host}.json`);
5448
- if (!existsSync27(settingsPath)) {
5690
+ const settingsPath = join39(claude, "settings.json");
5691
+ const basePath = join39(repo, "shared", "settings.base.json");
5692
+ const hostPath = join39(repo, "hosts", `${host}.json`);
5693
+ if (!existsSync28(settingsPath)) {
5449
5694
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
5450
5695
  return;
5451
5696
  }
5452
- if (!existsSync27(basePath)) {
5697
+ if (!existsSync28(basePath)) {
5453
5698
  addItem(
5454
5699
  section2,
5455
5700
  `${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
@@ -5468,7 +5713,7 @@ function reportSettingsDriftCheck(section2) {
5468
5713
  if (settings === null) {
5469
5714
  return;
5470
5715
  }
5471
- const hostExists = existsSync27(hostPath);
5716
+ const hostExists = existsSync28(hostPath);
5472
5717
  const hostObj = hostExists ? tryReadJson(hostPath) : null;
5473
5718
  if (hostExists && hostObj === null) {
5474
5719
  addItem(
@@ -5512,19 +5757,20 @@ function emitDriftRows(section2, missing, changed, promotable, excluded, hostFil
5512
5757
  }
5513
5758
  }
5514
5759
 
5515
- // src/commands.doctor.ts
5760
+ // src/commands/doctor/doctor.ts
5516
5761
  init_config();
5517
5762
 
5518
- // src/commands.doctor.engine.ts
5763
+ // src/commands/doctor/engine.ts
5519
5764
  init_color();
5520
5765
  import { readFileSync as readFileSync12 } from "node:fs";
5521
- import { fileURLToPath as fileURLToPath3 } from "node:url";
5766
+ import { join as join41 } from "node:path";
5522
5767
 
5523
- // src/commands.doctor.version.ts
5768
+ // src/commands/doctor/version.ts
5524
5769
  init_color();
5525
5770
  import { readFileSync as readFileSync11 } from "node:fs";
5526
- import { fileURLToPath as fileURLToPath2 } from "node:url";
5771
+ import { join as join40 } from "node:path";
5527
5772
  init_config();
5773
+ init_package_root();
5528
5774
  var STRICT_SEMVER = /^\d+\.\d+\.\d+$/;
5529
5775
  var STRICT_SEMVER_PREFIX = /^(\d+\.\d+\.\d+)(?:[-+]|$)/;
5530
5776
  function compareSemver(a, b) {
@@ -5538,7 +5784,7 @@ function compareSemver(a, b) {
5538
5784
  }
5539
5785
  function readLocalVersion() {
5540
5786
  try {
5541
- const pkgPath = fileURLToPath2(new URL("../package.json", import.meta.url));
5787
+ const pkgPath = join40(packageRoot(), "package.json");
5542
5788
  const parsed = JSON.parse(readFileSync11(pkgPath, "utf8"));
5543
5789
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
5544
5790
  return parsed.version;
@@ -5589,7 +5835,8 @@ function reportVersionCheck(section2) {
5589
5835
  }
5590
5836
  }
5591
5837
 
5592
- // src/commands.doctor.engine.ts
5838
+ // src/commands/doctor/engine.ts
5839
+ init_package_root();
5593
5840
  var ENGINES_GTE = /^>=\s*(\d+\.\d+\.\d+)$/;
5594
5841
  function parseMinVersion(spec) {
5595
5842
  const m = ENGINES_GTE.exec(spec);
@@ -5597,7 +5844,7 @@ function parseMinVersion(spec) {
5597
5844
  }
5598
5845
  function readEnginesNode() {
5599
5846
  try {
5600
- const pkgPath = fileURLToPath3(new URL("../package.json", import.meta.url));
5847
+ const pkgPath = join41(packageRoot(), "package.json");
5601
5848
  const parsed = JSON.parse(readFileSync12(pkgPath, "utf8"));
5602
5849
  const node = parsed.engines?.node;
5603
5850
  if (typeof node === "string" && node.length > 0) return node;
@@ -5624,18 +5871,18 @@ function reportNodeEngineCheck(section2) {
5624
5871
  addItem(section2, `${green(okGlyph)} node: ${process.version}`);
5625
5872
  }
5626
5873
 
5627
- // src/commands.doctor.checks.longpaths.ts
5874
+ // src/commands/doctor/checks/longpaths.ts
5628
5875
  init_color();
5629
- import { execFileSync as execFileSync12 } from "node:child_process";
5876
+ import { execFileSync as execFileSync13 } from "node:child_process";
5630
5877
  init_config();
5631
- var PROBE_TIMEOUT_MS = 3e3;
5878
+ var PROBE_TIMEOUT_MS2 = 3e3;
5632
5879
  var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
5633
5880
  var LONGPATHS_REG_VALUE = "LongPathsEnabled";
5634
5881
  function probeGitLongpaths(run) {
5635
5882
  try {
5636
5883
  const out = run("git", ["-C", repoHome(), "config", "--get", "core.longpaths"], {
5637
5884
  stdio: ["ignore", "pipe", "pipe"],
5638
- timeout: PROBE_TIMEOUT_MS
5885
+ timeout: PROBE_TIMEOUT_MS2
5639
5886
  }).toString().trim();
5640
5887
  return out === "true" || out === "1";
5641
5888
  } catch {
@@ -5646,7 +5893,7 @@ function probeRegistryLongpaths(run) {
5646
5893
  try {
5647
5894
  const out = run("reg", ["query", LONGPATHS_REG_KEY, "/v", LONGPATHS_REG_VALUE], {
5648
5895
  stdio: ["ignore", "pipe", "pipe"],
5649
- timeout: PROBE_TIMEOUT_MS
5896
+ timeout: PROBE_TIMEOUT_MS2
5650
5897
  }).toString();
5651
5898
  return /0x1\b/.test(out);
5652
5899
  } catch {
@@ -5660,7 +5907,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
5660
5907
  }
5661
5908
  addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
5662
5909
  }
5663
- function reportLongPathsCheck(section2, run = execFileSync12) {
5910
+ function reportLongPathsCheck(section2, run = execFileSync13) {
5664
5911
  if (process.platform !== "win32") return;
5665
5912
  addLongpathsRow(
5666
5913
  section2,
@@ -5682,18 +5929,18 @@ function reportSyncModality(section2) {
5682
5929
  addItem(section2, `${dim(infoGlyph)} sync modality: ${modality}`);
5683
5930
  }
5684
5931
 
5685
- // src/commands.doctor.checks.crlf.ts
5932
+ // src/commands/doctor/checks/crlf.ts
5686
5933
  init_color();
5687
- import { execFileSync as execFileSync13 } from "node:child_process";
5688
- import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
5689
- import { join as join39 } from "node:path";
5934
+ import { execFileSync as execFileSync14 } from "node:child_process";
5935
+ import { existsSync as existsSync29, readFileSync as readFileSync13 } from "node:fs";
5936
+ import { join as join42 } from "node:path";
5690
5937
  init_config();
5691
- var PROBE_TIMEOUT_MS2 = 3e3;
5938
+ var PROBE_TIMEOUT_MS3 = 3e3;
5692
5939
  var GUARD_LINE = /^\*\s+-text\b/;
5693
5940
  function hasGitattributesGuard(repo) {
5694
5941
  try {
5695
- const path = join39(repo, ".gitattributes");
5696
- if (!existsSync28(path)) return false;
5942
+ const path = join42(repo, ".gitattributes");
5943
+ if (!existsSync29(path)) return false;
5697
5944
  const content = readFileSync13(path, "utf8");
5698
5945
  return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
5699
5946
  } catch {
@@ -5704,7 +5951,7 @@ function probeAutocrlf(repo, run) {
5704
5951
  try {
5705
5952
  const out = run("git", ["-C", repo, "config", "core.autocrlf"], {
5706
5953
  stdio: ["ignore", "pipe", "pipe"],
5707
- timeout: PROBE_TIMEOUT_MS2
5954
+ timeout: PROBE_TIMEOUT_MS3
5708
5955
  }).toString().trim();
5709
5956
  if (out === "true" || out === "input") return "active";
5710
5957
  if (out === "false") return "disabled";
@@ -5723,7 +5970,7 @@ function addExposedRow(section2, repo, verdict) {
5723
5970
  const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
5724
5971
  addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
5725
5972
  }
5726
- function reportCrlfGuardCheck(section2, run = execFileSync13) {
5973
+ function reportCrlfGuardCheck(section2, run = execFileSync14) {
5727
5974
  const repo = repoHome();
5728
5975
  if (hasGitattributesGuard(repo)) {
5729
5976
  addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
@@ -5732,24 +5979,24 @@ function reportCrlfGuardCheck(section2, run = execFileSync13) {
5732
5979
  addExposedRow(section2, repo, probeAutocrlf(repo, run));
5733
5980
  }
5734
5981
 
5735
- // src/spinner.ts
5982
+ // src/render/spinner.ts
5736
5983
  init_color();
5737
- import { existsSync as existsSync33 } from "node:fs";
5738
- import { fileURLToPath as fileURLToPath4 } from "node:url";
5984
+ import { existsSync as existsSync34 } from "node:fs";
5985
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5739
5986
  import { Worker } from "node:worker_threads";
5740
5987
 
5741
- // src/commands.push.recovery.ts
5988
+ // src/commands/push/recovery/recovery.ts
5742
5989
  init_config();
5743
5990
  import { readFileSync as readFileSync18, rmSync as rmSync15, writeFileSync as writeFileSync9 } from "node:fs";
5744
- import { join as join47 } from "node:path";
5991
+ import { join as join50 } from "node:path";
5745
5992
  import { createInterface as createInterface2 } from "node:readline/promises";
5746
5993
 
5747
- // src/commands.push.recovery.actions.ts
5994
+ // src/commands/push/recovery/actions.ts
5748
5995
  init_config();
5749
5996
  import { readFileSync as readFileSync17 } from "node:fs";
5750
5997
  import { isAbsolute as isAbsolute2, resolve as resolve4, sep as sep9 } from "node:path";
5751
5998
 
5752
- // src/commands.push.recovery.memory.ts
5999
+ // src/commands/push/recovery/memory.ts
5753
6000
  init_config();
5754
6001
  init_config_sharedDirs_guard();
5755
6002
  import {
@@ -5761,17 +6008,17 @@ import {
5761
6008
  statSync as statSync7,
5762
6009
  writeFileSync as writeFileSync6
5763
6010
  } from "node:fs";
5764
- import { dirname as dirname10, join as join40, sep as sep6 } from "node:path";
5765
- init_push_gitleaks_scan();
6011
+ import { dirname as dirname11, join as join43, sep as sep6 } from "node:path";
6012
+ init_gitleaks_scan();
5766
6013
 
5767
- // src/rel-path-guard.ts
6014
+ // src/core/rel-path-guard.ts
5768
6015
  function isSafeRelPath(relPath) {
5769
6016
  if (relPath.length === 0 || relPath.startsWith("/") || relPath.includes("\\")) return false;
5770
6017
  const segments = relPath.split("/");
5771
6018
  return segments.every((s) => s.length > 0 && s !== "." && s !== "..");
5772
6019
  }
5773
6020
 
5774
- // src/commands.push.recovery.memory.ts
6021
+ // src/commands/push/recovery/memory.ts
5775
6022
  init_utils_fs();
5776
6023
  init_utils_json();
5777
6024
  init_utils();
@@ -5790,8 +6037,8 @@ function resolveMemoryLocalPath(logical, relPath, map) {
5790
6037
  if (!isSafeRelPath(relPath)) return null;
5791
6038
  const abs = map.projects[logical]?.[HOST];
5792
6039
  if (abs === void 0) return null;
5793
- const memoryRoot = join40(claudeHome(), "projects", encodePath(abs), "memory");
5794
- const localPath = join40(memoryRoot, ...relPath.split("/"));
6040
+ const memoryRoot = join43(claudeHome(), "projects", encodePath(abs), "memory");
6041
+ const localPath = join43(memoryRoot, ...relPath.split("/"));
5795
6042
  if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep6)) return null;
5796
6043
  try {
5797
6044
  if (lstatSync14(localPath).isSymbolicLink()) return null;
@@ -5845,13 +6092,13 @@ function applyMemoryRedact(f, ts, map, scan = scanFile) {
5845
6092
  );
5846
6093
  }
5847
6094
  writeFileSync6(localPath, after, "utf8");
5848
- const dest = join40(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
5849
- mkdirSync9(dirname10(dest), { recursive: true });
6095
+ const dest = join43(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
6096
+ mkdirSync9(dirname11(dest), { recursive: true });
5850
6097
  cpSync6(localPath, dest, { force: true });
5851
6098
  return true;
5852
6099
  }
5853
6100
 
5854
- // src/commands.push.recovery.skills.ts
6101
+ // src/commands/push/recovery/skills.ts
5855
6102
  init_config();
5856
6103
  import {
5857
6104
  cpSync as cpSync7,
@@ -5862,27 +6109,27 @@ import {
5862
6109
  statSync as statSync8,
5863
6110
  writeFileSync as writeFileSync7
5864
6111
  } from "node:fs";
5865
- import { dirname as dirname11, join as join42, sep as sep7 } from "node:path";
6112
+ import { dirname as dirname12, join as join45, sep as sep7 } from "node:path";
5866
6113
 
5867
- // src/skills-sync.ts
6114
+ // src/sync/skills-sync.ts
5868
6115
  init_config();
5869
- import { existsSync as existsSync29, lstatSync as lstatSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync14, rmSync as rmSync13 } from "node:fs";
5870
- import { join as join41 } from "node:path";
6116
+ import { existsSync as existsSync30, lstatSync as lstatSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync14, rmSync as rmSync13 } from "node:fs";
6117
+ import { join as join44 } from "node:path";
5871
6118
 
5872
- // src/skills-sync.tracked.ts
6119
+ // src/sync/skills-sync.tracked.ts
5873
6120
  init_utils();
5874
- import { basename as basename3 } from "node:path";
6121
+ import { basename as basename4 } from "node:path";
5875
6122
  function trackedRootSkillsAt(ref, repo) {
5876
6123
  try {
5877
6124
  const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
5878
6125
  const entries = raw.split("\0").filter((entry) => entry !== "");
5879
- return new Set(entries.map((entry) => basename3(entry)));
6126
+ return new Set(entries.map((entry) => basename4(entry)));
5880
6127
  } catch {
5881
6128
  return /* @__PURE__ */ new Set();
5882
6129
  }
5883
6130
  }
5884
6131
 
5885
- // src/skills-sync.ts
6132
+ // src/sync/skills-sync.ts
5886
6133
  init_utils_fs();
5887
6134
  function isGsdOwned(name) {
5888
6135
  return name.startsWith(GSD_PREFIX);
@@ -5902,9 +6149,9 @@ function copySkillsPull(src, dst, isRootPreserved) {
5902
6149
  copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved);
5903
6150
  }
5904
6151
  function syncSkillsPull(ts, prePostHeads) {
5905
- const sharedSkills = join41(repoHome(), "shared", "skills");
5906
- if (!existsSync29(sharedSkills)) return;
5907
- const localSkills = join41(claudeHome(), "skills");
6152
+ const sharedSkills = join44(repoHome(), "shared", "skills");
6153
+ if (!existsSync30(sharedSkills)) return;
6154
+ const localSkills = join44(claudeHome(), "skills");
5908
6155
  backupBeforeWrite(localSkills, ts);
5909
6156
  const dstStat = lstatSync15(localSkills, { throwIfNoEntry: false });
5910
6157
  if (dstStat?.isSymbolicLink() === true) {
@@ -5915,16 +6162,16 @@ function syncSkillsPull(ts, prePostHeads) {
5915
6162
  copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
5916
6163
  }
5917
6164
  function syncSkillsPush() {
5918
- const localSkills = join41(claudeHome(), "skills");
6165
+ const localSkills = join44(claudeHome(), "skills");
5919
6166
  const stat = lstatSync15(localSkills, { throwIfNoEntry: false });
5920
6167
  if (stat === void 0) return;
5921
6168
  if (stat.isSymbolicLink()) return;
5922
- const sharedSkills = join41(repoHome(), "shared", "skills");
6169
+ const sharedSkills = join44(repoHome(), "shared", "skills");
5923
6170
  copySkillsPush(localSkills, sharedSkills);
5924
6171
  }
5925
6172
 
5926
- // src/commands.push.recovery.skills.ts
5927
- init_push_gitleaks_scan();
6173
+ // src/commands/push/recovery/skills.ts
6174
+ init_gitleaks_scan();
5928
6175
  init_utils_fs();
5929
6176
  init_utils();
5930
6177
  var SKILL_FINDING_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
@@ -5942,9 +6189,9 @@ function resolveSkillLocalPath(name, relPath) {
5942
6189
  if (!SAFE_SKILL_NAME.test(name) || name === "." || name === "..") return null;
5943
6190
  if (isGsdOwned(name)) return null;
5944
6191
  if (!isSafeRelPath(relPath)) return null;
5945
- const skillsRoot = join42(claudeHome(), "skills");
5946
- const skillRoot = join42(skillsRoot, name);
5947
- const localPath = join42(skillRoot, ...relPath.split("/"));
6192
+ const skillsRoot = join45(claudeHome(), "skills");
6193
+ const skillRoot = join45(skillsRoot, name);
6194
+ const localPath = join45(skillRoot, ...relPath.split("/"));
5948
6195
  if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep7)) return null;
5949
6196
  try {
5950
6197
  if (lstatSync16(localPath).isSymbolicLink()) return null;
@@ -5996,17 +6243,17 @@ function applySkillRedact(f, ts, scan = scanFile) {
5996
6243
  );
5997
6244
  }
5998
6245
  writeFileSync7(localPath, after, "utf8");
5999
- const dest = join42(repoHome(), "shared", "skills", name, ...relPath.split("/"));
6000
- mkdirSync11(dirname11(dest), { recursive: true });
6246
+ const dest = join45(repoHome(), "shared", "skills", name, ...relPath.split("/"));
6247
+ mkdirSync11(dirname12(dest), { recursive: true });
6001
6248
  cpSync7(localPath, dest, { force: true });
6002
6249
  return true;
6003
6250
  }
6004
6251
 
6005
- // src/commands.push.recovery.allow-gate.ts
6252
+ // src/commands/push/recovery/allow-gate.ts
6006
6253
  init_utils();
6007
6254
 
6008
- // src/commands.push.recovery.seams.ts
6009
- init_push_gitleaks();
6255
+ // src/commands/push/recovery/seams.ts
6256
+ init_gitleaks();
6010
6257
  function findingKey(f) {
6011
6258
  return `${f.File}:${f.StartLine}:${f.StartColumn}:${f.RuleID}`;
6012
6259
  }
@@ -6027,7 +6274,7 @@ function parseAction(raw) {
6027
6274
  return "skip";
6028
6275
  }
6029
6276
 
6030
- // src/commands.push.recovery.allow-gate.ts
6277
+ // src/commands/push/recovery/allow-gate.ts
6031
6278
  function isCleared(f, state) {
6032
6279
  const action = state.actions.get(findingKey(f)) ?? "skip";
6033
6280
  if (action === "allow") return true;
@@ -6071,28 +6318,28 @@ function applyDeferredAllows(findings, state, repo) {
6071
6318
  }
6072
6319
  }
6073
6320
 
6074
- // src/commands.push.recovery.redact.ts
6321
+ // src/commands/push/recovery/redact.ts
6075
6322
  init_config();
6076
6323
  init_config_sharedDirs_guard();
6077
- import { cpSync as cpSync8, existsSync as existsSync32, mkdirSync as mkdirSync12 } from "node:fs";
6078
- import { dirname as dirname13, join as join45, sep as sep8 } from "node:path";
6324
+ import { cpSync as cpSync8, existsSync as existsSync33, mkdirSync as mkdirSync12 } from "node:fs";
6325
+ import { dirname as dirname14, join as join48, sep as sep8 } from "node:path";
6079
6326
 
6080
- // src/commands.redact.ts
6327
+ // src/commands/redact/redact.ts
6081
6328
  init_config();
6082
- import { existsSync as existsSync31 } from "node:fs";
6083
- import { dirname as dirname12, join as join44 } from "node:path";
6329
+ import { existsSync as existsSync32 } from "node:fs";
6330
+ import { dirname as dirname13, join as join47 } from "node:path";
6084
6331
 
6085
- // src/commands.redact.subtree.ts
6086
- import { existsSync as existsSync30, lstatSync as lstatSync17, readFileSync as readFileSync16, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
6087
- import { join as join43 } from "node:path";
6332
+ // src/commands/redact/subtree.ts
6333
+ import { existsSync as existsSync31, lstatSync as lstatSync17, readFileSync as readFileSync16, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
6334
+ import { join as join46 } from "node:path";
6088
6335
  init_utils_fs();
6089
6336
  init_utils();
6090
6337
  function collectFiles(dir, out) {
6091
- if (!existsSync30(dir)) return;
6338
+ if (!existsSync31(dir)) return;
6092
6339
  const st = lstatSync17(dir);
6093
6340
  if (!st.isDirectory()) return;
6094
6341
  for (const entry of readdirSync15(dir)) {
6095
- const abs = join43(dir, entry);
6342
+ const abs = join46(dir, entry);
6096
6343
  const lst = lstatSync17(abs);
6097
6344
  if (lst.isSymbolicLink()) continue;
6098
6345
  if (lst.isDirectory()) {
@@ -6146,12 +6393,12 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
6146
6393
  return { total, dirty };
6147
6394
  }
6148
6395
 
6149
- // src/commands.pushed-history.ts
6396
+ // src/commands/pushed-history.ts
6150
6397
  init_utils();
6151
- import { execFileSync as execFileSync14 } from "node:child_process";
6398
+ import { execFileSync as execFileSync15 } from "node:child_process";
6152
6399
  function pushedRef(repo) {
6153
6400
  try {
6154
- const ref = execFileSync14("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
6401
+ const ref = execFileSync15("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
6155
6402
  cwd: repo,
6156
6403
  stdio: ["ignore", "pipe", "ignore"]
6157
6404
  }).toString().trim();
@@ -6164,7 +6411,7 @@ function sessionInPushedHistory(id, repo) {
6164
6411
  const ref = pushedRef(repo);
6165
6412
  if (ref === null) return false;
6166
6413
  try {
6167
- const out = execFileSync14(
6414
+ const out = execFileSync15(
6168
6415
  "git",
6169
6416
  [
6170
6417
  "log",
@@ -6194,23 +6441,23 @@ function warnIfSessionPushed(id, repo) {
6194
6441
  );
6195
6442
  }
6196
6443
 
6197
- // src/commands.redact.ts
6198
- init_push_gitleaks_scan();
6444
+ // src/commands/redact/redact.ts
6445
+ init_gitleaks_scan();
6199
6446
  init_exit_codes();
6200
6447
  init_utils_fs();
6201
6448
  init_utils_json();
6202
6449
  init_utils();
6203
6450
  function resolveLiveTranscript(id) {
6204
6451
  try {
6205
- const mapPath = join44(repoHome(), "path-map.json");
6206
- if (!existsSync31(mapPath)) return null;
6452
+ const mapPath = join47(repoHome(), "path-map.json");
6453
+ if (!existsSync32(mapPath)) return null;
6207
6454
  const projects = readJson(mapPath).projects;
6208
6455
  const claude = claudeHome();
6209
6456
  for (const hostMap of Object.values(projects)) {
6210
6457
  const abs = hostMap[HOST];
6211
6458
  if (abs === void 0) continue;
6212
- const live = join44(claude, "projects", encodePath(abs), `${id}.jsonl`);
6213
- if (existsSync31(live)) return live;
6459
+ const live = join47(claude, "projects", encodePath(abs), `${id}.jsonl`);
6460
+ if (existsSync32(live)) return live;
6214
6461
  }
6215
6462
  return null;
6216
6463
  } catch {
@@ -6230,17 +6477,17 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
6230
6477
  }
6231
6478
  const repo = repoHome();
6232
6479
  const backup = backupBase();
6233
- if (!existsSync31(repo)) die(`repo not cloned at ${repo}`);
6480
+ if (!existsSync32(repo)) die(`repo not cloned at ${repo}`);
6234
6481
  const handle = acquireLock("redact");
6235
6482
  if (handle === null) process.exit(0);
6236
6483
  try {
6237
6484
  const localPath = resolveLiveTranscript(id);
6238
- if (localPath === null || !existsSync31(localPath)) {
6485
+ if (localPath === null || !existsSync32(localPath)) {
6239
6486
  fail(`could not resolve local transcript for session ${id} on this host`);
6240
6487
  process.exitCode = 1;
6241
6488
  return;
6242
6489
  }
6243
- const sessionDir = join44(dirname12(localPath), id);
6490
+ const sessionDir = join47(dirname13(localPath), id);
6244
6491
  const subtreeFiles = listSubtreeFiles(sessionDir);
6245
6492
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6246
6493
  log(
@@ -6292,8 +6539,8 @@ ${lines}`);
6292
6539
  }
6293
6540
  }
6294
6541
 
6295
- // src/commands.push.recovery.redact.ts
6296
- init_push_gitleaks_scan();
6542
+ // src/commands/push/recovery/redact.ts
6543
+ init_gitleaks_scan();
6297
6544
  init_utils_json();
6298
6545
  init_utils();
6299
6546
  function resolveStagedDir(localPath, map, claude, repo) {
@@ -6301,8 +6548,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
6301
6548
  assertSafeLogical(logical);
6302
6549
  const abs = hostMap[HOST];
6303
6550
  if (abs === void 0) continue;
6304
- if (localPath.startsWith(join45(claude, "projects", encodePath(abs)) + sep8)) {
6305
- return join45(repo, "shared", "projects", logical);
6551
+ if (localPath.startsWith(join48(claude, "projects", encodePath(abs)) + sep8)) {
6552
+ return join48(repo, "shared", "projects", logical);
6306
6553
  }
6307
6554
  }
6308
6555
  return null;
@@ -6312,7 +6559,7 @@ function preflightRedactable(f, map, nowMs) {
6312
6559
  if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
6313
6560
  const localPath = resolveLiveTranscript(sid);
6314
6561
  if (localPath === null) return `session ${sid}: local transcript not found`;
6315
- const sessionDir = join45(dirname13(localPath), sid);
6562
+ const sessionDir = join48(dirname14(localPath), sid);
6316
6563
  const subtreeFiles = listSubtreeFiles(sessionDir);
6317
6564
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6318
6565
  return `session ${sid}: looks active (modified within the last 5 minutes)`;
@@ -6341,7 +6588,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
6341
6588
  `could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
6342
6589
  );
6343
6590
  }
6344
- const sessionDir = join45(dirname13(localPath), sid);
6591
+ const sessionDir = join48(dirname14(localPath), sid);
6345
6592
  const subtreeFiles = listSubtreeFiles(sessionDir);
6346
6593
  if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
6347
6594
  return refuse(
@@ -6374,35 +6621,35 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
6374
6621
  );
6375
6622
  }
6376
6623
  mkdirSync12(stagedProjectDir, { recursive: true });
6377
- cpSync8(localPath, join45(stagedProjectDir, `${sid}.jsonl`), { force: true });
6378
- if (existsSync32(sessionDir)) {
6379
- cpSync8(sessionDir, join45(stagedProjectDir, sid), { force: true, recursive: true });
6624
+ cpSync8(localPath, join48(stagedProjectDir, `${sid}.jsonl`), { force: true });
6625
+ if (existsSync33(sessionDir)) {
6626
+ cpSync8(sessionDir, join48(stagedProjectDir, sid), { force: true, recursive: true });
6380
6627
  }
6381
6628
  return true;
6382
6629
  }
6383
6630
 
6384
- // src/commands.push.recovery.drop.ts
6631
+ // src/commands/push/recovery/drop.ts
6385
6632
  init_config();
6386
6633
  import { rmSync as rmSync14 } from "node:fs";
6387
- import { join as join46 } from "node:path";
6634
+ import { join as join49 } from "node:path";
6388
6635
  function dropSessionFromStaged(sid, map) {
6389
6636
  const logicals = Object.keys(map.projects);
6390
6637
  if (logicals.length === 0) return false;
6391
6638
  const repo = repoHome();
6392
6639
  for (const logical of logicals) {
6393
- const jsonl = join46(repo, "shared", "projects", logical, `${sid}.jsonl`);
6394
- const dir = join46(repo, "shared", "projects", logical, sid);
6640
+ const jsonl = join49(repo, "shared", "projects", logical, `${sid}.jsonl`);
6641
+ const dir = join49(repo, "shared", "projects", logical, sid);
6395
6642
  rmSync14(jsonl, { force: true });
6396
6643
  rmSync14(dir, { recursive: true, force: true });
6397
6644
  }
6398
6645
  return true;
6399
6646
  }
6400
6647
 
6401
- // src/commands.push.recovery.actions.ts
6402
- init_push_gitleaks_scan();
6648
+ // src/commands/push/recovery/actions.ts
6649
+ init_gitleaks_scan();
6403
6650
  init_utils();
6404
6651
 
6405
- // src/commands.push.recovery.span-align.ts
6652
+ // src/commands/push/recovery/span-align.ts
6406
6653
  var REDACTED_TOKEN = "REDACTED";
6407
6654
  var SHIFT_WINDOW = [0, -1, 1, -2, 2];
6408
6655
  var ENTROPY_TOLERANCE = 1e-4;
@@ -6489,7 +6736,7 @@ function alignFindingSpan(finding, buf) {
6489
6736
  return breakTie(hits, finding);
6490
6737
  }
6491
6738
 
6492
- // src/commands.push.recovery.secret-shape.ts
6739
+ // src/commands/push/recovery/secret-shape.ts
6493
6740
  var VALUE_HEAD = 4;
6494
6741
  var VALUE_TAIL = 4;
6495
6742
  var MIN_ELIDE_LEN = 16;
@@ -6497,7 +6744,7 @@ var FRAGMENT_SEP = "...";
6497
6744
  var NEAR_WINDOW = 40;
6498
6745
  var VALUE_PAD = 16;
6499
6746
  var SIBLING_PAD = 2;
6500
- var CONTROL_CHARS = /[\x00-\x1f\x7f]/g;
6747
+ var CONTROL_CHARS2 = /[\x00-\x1f\x7f]/g;
6501
6748
  function classifyCharset(value) {
6502
6749
  if (/^[0-9a-fA-F]+$/.test(value)) return "hex";
6503
6750
  if (/^[0-9a-zA-Z]+$/.test(value)) return "alphanumeric";
@@ -6571,7 +6818,7 @@ function resolveSpanContext(finding, readLine, siblings = []) {
6571
6818
  return { value: aligned.value, near: emptyToNull(near), exact: aligned.exact };
6572
6819
  }
6573
6820
  function formatNear(text) {
6574
- const stripped = elideTokenRuns(text.replace(CONTROL_CHARS, ""));
6821
+ const stripped = elideTokenRuns(text.replace(CONTROL_CHARS2, ""));
6575
6822
  const truncated = stripped.length > NEAR_WINDOW;
6576
6823
  const window = stripped.slice(-NEAR_WINDOW).trim();
6577
6824
  if (window.length === 0) return null;
@@ -6585,15 +6832,15 @@ function elideTokenRuns(text) {
6585
6832
  });
6586
6833
  }
6587
6834
 
6588
- // src/commands.push.recovery.display.ts
6835
+ // src/commands/push/recovery/display.ts
6589
6836
  var RULE_PAD = 24;
6590
6837
  var GROUP_SEP = "\0";
6591
6838
  function sessionSuffix(finding) {
6592
6839
  const sid = sessionIdFromFinding(finding);
6593
6840
  if (sid === null) return "";
6594
6841
  const rawBasename = finding.File.replace(/^.*\//, "");
6595
- const basename4 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
6596
- return basename4 === sid ? "" : ` (session: ${sid})`;
6842
+ const basename5 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
6843
+ return basename5 === sid ? "" : ` (session: ${sid})`;
6597
6844
  }
6598
6845
  function renderFindingBlock(finding, readLine, siblings = []) {
6599
6846
  const lines = [` file: ${finding.File}:${finding.StartLine}${sessionSuffix(finding)}`];
@@ -6643,7 +6890,7 @@ function buildPromptHeader(group, index, total, readLine, peers = 0, siblings =
6643
6890
  return ["", headerLine, ...body, menu, ""].join("\n");
6644
6891
  }
6645
6892
 
6646
- // src/commands.push.recovery.actions.ts
6893
+ // src/commands/push/recovery/actions.ts
6647
6894
  function allowAllFindings(findings, repo) {
6648
6895
  for (const f of findings) {
6649
6896
  appendGitleaksIgnore(f.Fingerprint, repo);
@@ -6776,8 +7023,8 @@ function dispatchActions(findings, actions, opts) {
6776
7023
  applyDeferredAllows(findings, ctx, repo);
6777
7024
  }
6778
7025
 
6779
- // src/commands.push.recovery.redact-all.ts
6780
- init_push_gitleaks_scan();
7026
+ // src/commands/push/recovery/redact-all.ts
7027
+ init_gitleaks_scan();
6781
7028
  init_utils();
6782
7029
  function redactAllDedupeKey(f) {
6783
7030
  const sid = sessionIdFromFinding(f);
@@ -6842,10 +7089,10 @@ function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
6842
7089
  }
6843
7090
  }
6844
7091
 
6845
- // src/commands.push.recovery.ts
7092
+ // src/commands/push/recovery/recovery.ts
6846
7093
  init_exit_codes();
6847
- init_push_gitleaks_scan();
6848
- init_push_gitleaks();
7094
+ init_gitleaks_scan();
7095
+ init_gitleaks();
6849
7096
  init_utils();
6850
7097
  function isTTY(stdin = process.stdin, stdout = process.stdout) {
6851
7098
  return stdin.isTTY === true && stdout.isTTY === true;
@@ -6876,7 +7123,7 @@ function applyThenRescan(scanVerdict, repoHome2) {
6876
7123
  return next;
6877
7124
  }
6878
7125
  function allowThenRescan(append, scanVerdict, repoHome2) {
6879
- const ignPath = join47(repoHome2, ".gitleaksignore");
7126
+ const ignPath = join50(repoHome2, ".gitleaksignore");
6880
7127
  let before;
6881
7128
  try {
6882
7129
  before = readFileSync18(ignPath, "utf8");
@@ -6920,7 +7167,7 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
6920
7167
  scan = scanFile,
6921
7168
  printLegend = printRecoveryLegend
6922
7169
  } = deps;
6923
- const scanVerdict = deps.scanVerdict ?? (await Promise.resolve().then(() => (init_push_leak_verdict(), push_leak_verdict_exports))).scanPushVerdict;
7170
+ const scanVerdict = deps.scanVerdict ?? (await Promise.resolve().then(() => (init_leak_verdict(), leak_verdict_exports))).scanPushVerdict;
6924
7171
  const repo = repoHome();
6925
7172
  let current = verdict;
6926
7173
  if (redactAll) {
@@ -6963,7 +7210,7 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
6963
7210
  return current;
6964
7211
  }
6965
7212
 
6966
- // src/spinner.ts
7213
+ // src/render/spinner.ts
6967
7214
  function formatElapsed(ms) {
6968
7215
  return `${(ms / 1e3).toFixed(1)}s`;
6969
7216
  }
@@ -6982,11 +7229,11 @@ function writeAnimatedDone(out, label, ms, useTTY) {
6982
7229
  `);
6983
7230
  }
6984
7231
  function resolveWorkerPath(deps = {}) {
6985
- const check = deps.existsSyncFn ?? existsSync33;
7232
+ const check = deps.existsSyncFn ?? existsSync34;
6986
7233
  const base = deps.baseUrl ?? import.meta.url;
6987
- const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
7234
+ const mjs = fileURLToPath2(new URL("./nomad.worker.mjs", base));
6988
7235
  if (check(mjs)) return mjs;
6989
- return fileURLToPath4(new URL("./spinner.worker.ts", base));
7236
+ return fileURLToPath2(new URL("./spinner.worker.ts", base));
6990
7237
  }
6991
7238
  function makeRealWorker() {
6992
7239
  return new Worker(resolveWorkerPath());
@@ -7046,11 +7293,11 @@ function withSpinner(label, fn, deps) {
7046
7293
  }
7047
7294
  }
7048
7295
 
7049
- // src/commands.doctor.gitleaks-version.ts
7296
+ // src/commands/doctor/gitleaks-version.ts
7050
7297
  init_color();
7051
- import { execFileSync as execFileSync15 } from "node:child_process";
7052
- import { existsSync as existsSync34 } from "node:fs";
7053
- import { join as join48 } from "node:path";
7298
+ import { execFileSync as execFileSync16 } from "node:child_process";
7299
+ import { existsSync as existsSync35 } from "node:fs";
7300
+ import { join as join51 } from "node:path";
7054
7301
  init_config();
7055
7302
  var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
7056
7303
  var GITLEAKS_TIMEOUT_MS = 5e3;
@@ -7059,7 +7306,7 @@ function majorMinorOf(value) {
7059
7306
  return m === null ? null : [m[1], m[2]];
7060
7307
  }
7061
7308
  function readGitleaksVersion(run, tomlExists) {
7062
- const tomlPath = join48(repoHome(), ".gitleaks.toml");
7309
+ const tomlPath = join51(repoHome(), ".gitleaks.toml");
7063
7310
  const args = ["version"];
7064
7311
  if (tomlExists(tomlPath)) args.push("--config", tomlPath);
7065
7312
  try {
@@ -7071,7 +7318,7 @@ function readGitleaksVersion(run, tomlExists) {
7071
7318
  return null;
7072
7319
  }
7073
7320
  }
7074
- function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists = existsSync34) {
7321
+ function reportGitleaksVersionCheck(section2, run = execFileSync16, tomlExists = existsSync35) {
7075
7322
  const raw = readGitleaksVersion(run, tomlExists);
7076
7323
  if (raw === null) return;
7077
7324
  const local = majorMinorOf(raw);
@@ -7089,11 +7336,11 @@ function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists =
7089
7336
  );
7090
7337
  }
7091
7338
 
7092
- // src/commands.doctor.checks.deps.ts
7339
+ // src/commands/doctor/checks/deps.ts
7093
7340
  init_color();
7094
- import { execFileSync as execFileSync16 } from "node:child_process";
7341
+ import { execFileSync as execFileSync17 } from "node:child_process";
7095
7342
  var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
7096
- var PROBE_TIMEOUT_MS3 = 3e3;
7343
+ var PROBE_TIMEOUT_MS4 = 3e3;
7097
7344
  var FETCHER_BASE = "HTTP fetcher";
7098
7345
  function parseFirstVersion(line) {
7099
7346
  const m = VERSION_TOKEN.exec(line);
@@ -7103,7 +7350,7 @@ function probeOptionalDep(bin, run) {
7103
7350
  try {
7104
7351
  const firstLine = run(bin, ["--version"], {
7105
7352
  stdio: ["ignore", "pipe", "pipe"],
7106
- timeout: PROBE_TIMEOUT_MS3
7353
+ timeout: PROBE_TIMEOUT_MS4
7107
7354
  }).toString().split("\n")[0].trim();
7108
7355
  const version = parseFirstVersion(firstLine);
7109
7356
  return { status: "present", version };
@@ -7128,7 +7375,7 @@ function reportFetcherRow(section2, run) {
7128
7375
  );
7129
7376
  }
7130
7377
  }
7131
- function reportOptionalDeps(section2, run = execFileSync16) {
7378
+ function reportOptionalDeps(section2, run = execFileSync17) {
7132
7379
  const gh = probeOptionalDep("gh", run);
7133
7380
  if (gh.status === "present") {
7134
7381
  addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
@@ -7141,13 +7388,13 @@ function reportOptionalDeps(section2, run = execFileSync16) {
7141
7388
  reportFetcherRow(section2, run);
7142
7389
  }
7143
7390
 
7144
- // src/commands.doctor.actions-drift.ts
7391
+ // src/commands/doctor/actions-drift.ts
7145
7392
  init_color();
7146
- import { execFileSync as execFileSync18 } from "node:child_process";
7393
+ import { execFileSync as execFileSync19 } from "node:child_process";
7147
7394
  init_config();
7148
7395
 
7149
- // src/gh-actions.ts
7150
- import { execFileSync as execFileSync17 } from "node:child_process";
7396
+ // src/commands/init/gh-actions.ts
7397
+ import { execFileSync as execFileSync18 } from "node:child_process";
7151
7398
  var GH_TIMEOUT_MS = 5e3;
7152
7399
  function parseGitHubRemote(remoteUrl) {
7153
7400
  const normalized = remoteUrl.trim().replace(/\/$/, "");
@@ -7155,7 +7402,7 @@ function parseGitHubRemote(remoteUrl) {
7155
7402
  if (m === null) return null;
7156
7403
  return { owner: m[1], repo: m[2] };
7157
7404
  }
7158
- function ghAuthStatus(run = execFileSync17) {
7405
+ function ghAuthStatus(run = execFileSync18) {
7159
7406
  try {
7160
7407
  run("gh", ["auth", "status"], {
7161
7408
  stdio: ["ignore", "ignore", "ignore"],
@@ -7169,7 +7416,7 @@ function ghAuthStatus(run = execFileSync17) {
7169
7416
  return "gh-probe-error";
7170
7417
  }
7171
7418
  }
7172
- function isRepoPrivate(ref, run = execFileSync17) {
7419
+ function isRepoPrivate(ref, run = execFileSync18) {
7173
7420
  const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
7174
7421
  stdio: ["ignore", "pipe", "ignore"],
7175
7422
  timeout: GH_TIMEOUT_MS
@@ -7177,7 +7424,7 @@ function isRepoPrivate(ref, run = execFileSync17) {
7177
7424
  const parsed = JSON.parse(out);
7178
7425
  return parsed.isPrivate === true;
7179
7426
  }
7180
- function isActionsEnabled(ref, run = execFileSync17) {
7427
+ function isActionsEnabled(ref, run = execFileSync18) {
7181
7428
  const out = run(
7182
7429
  "gh",
7183
7430
  ["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
@@ -7185,7 +7432,7 @@ function isActionsEnabled(ref, run = execFileSync17) {
7185
7432
  ).toString().trim();
7186
7433
  return out === "true";
7187
7434
  }
7188
- function disableActions(ref, run = execFileSync17) {
7435
+ function disableActions(ref, run = execFileSync18) {
7189
7436
  run(
7190
7437
  "gh",
7191
7438
  [
@@ -7199,15 +7446,15 @@ function disableActions(ref, run = execFileSync17) {
7199
7446
  { stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
7200
7447
  );
7201
7448
  }
7202
- function readOriginRemote(cwd, run = execFileSync17) {
7449
+ function readOriginRemote(cwd, run = execFileSync18) {
7203
7450
  return run("git", ["remote", "get-url", "origin"], {
7204
7451
  cwd,
7205
7452
  stdio: ["ignore", "pipe", "ignore"]
7206
7453
  }).toString().trim();
7207
7454
  }
7208
7455
 
7209
- // src/commands.doctor.actions-drift.ts
7210
- function reportActionsDrift(section2, run = execFileSync18) {
7456
+ // src/commands/doctor/actions-drift.ts
7457
+ function reportActionsDrift(section2, run = execFileSync19) {
7211
7458
  let remote;
7212
7459
  try {
7213
7460
  remote = readOriginRemote(repoHome(), run);
@@ -7238,7 +7485,7 @@ function reportActionsDrift(section2, run = execFileSync18) {
7238
7485
  );
7239
7486
  }
7240
7487
 
7241
- // src/commands.doctor.verdict.ts
7488
+ // src/commands/doctor/verdict.ts
7242
7489
  init_color();
7243
7490
  function isFailLine(item2) {
7244
7491
  return item2.includes(failGlyph);
@@ -7262,7 +7509,7 @@ function buildVerdictSection(sections) {
7262
7509
  return summary;
7263
7510
  }
7264
7511
 
7265
- // src/commands.doctor.banner.ts
7512
+ // src/commands/doctor/banner.ts
7266
7513
  var NOMAD_BANNER = [
7267
7514
  " __",
7268
7515
  " ____ ____ ____ ___ ____ _ ____/ /",
@@ -7276,7 +7523,7 @@ function printBanner(log2, stdout) {
7276
7523
  log2("");
7277
7524
  }
7278
7525
 
7279
- // src/commands.doctor.compact.ts
7526
+ // src/commands/doctor/compact.ts
7280
7527
  init_color();
7281
7528
  var ALWAYS_FULL = /* @__PURE__ */ new Set([
7282
7529
  "Nomad Version",
@@ -7318,7 +7565,7 @@ function compactSections(sections) {
7318
7565
  });
7319
7566
  }
7320
7567
 
7321
- // src/commands.doctor.ts
7568
+ // src/commands/doctor/doctor.ts
7322
7569
  function gatherDoctorSections(opts) {
7323
7570
  const host = section("Environment");
7324
7571
  reportHostAndPaths(host);
@@ -7328,8 +7575,8 @@ function gatherDoctorSections(opts) {
7328
7575
  reportLongPathsCheck(host);
7329
7576
  reportCrlfGuardCheck(host);
7330
7577
  const links = section("Shared links");
7331
- const mapPath = join49(repoHome(), "path-map.json");
7332
- const rawMap = existsSync35(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
7578
+ const mapPath = join52(repoHome(), "path-map.json");
7579
+ const rawMap = existsSync36(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
7333
7580
  const map = rawMap ?? { projects: {} };
7334
7581
  reportSharedLinks(links, map);
7335
7582
  reportDroppedNamesMigration(links);
@@ -7347,6 +7594,7 @@ function gatherDoctorSections(opts) {
7347
7594
  reportPathMap(pathMap);
7348
7595
  const neverSync = section("Never-sync");
7349
7596
  reportNeverSync(neverSync);
7597
+ reportTrackedDeniedShared(neverSync);
7350
7598
  const skills = section("Skills");
7351
7599
  reportSkillsDivergence(skills);
7352
7600
  const repository = section("Repository");
@@ -7403,7 +7651,7 @@ function cmdDoctor(opts = {}) {
7403
7651
  renderDoctor(opts.verbose === true ? report : compactSections(report));
7404
7652
  }
7405
7653
 
7406
- // src/nomad.dispatch.doctor.ts
7654
+ // src/cli/dispatch.doctor.ts
7407
7655
  function parseDoctorArgs(args) {
7408
7656
  if (args[0] === "--resume-cmd") {
7409
7657
  const id = args[1];
@@ -7424,17 +7672,17 @@ function parseDoctorArgs(args) {
7424
7672
  return { kind: "run", checkShared, checkSchema, checkRemote, verbose };
7425
7673
  }
7426
7674
 
7427
- // src/commands.drop-session.ts
7675
+ // src/commands/drop-session/drop-session.ts
7428
7676
  init_config();
7429
- import { execFileSync as execFileSync20 } from "node:child_process";
7430
- import { existsSync as existsSync37, readdirSync as readdirSync16, statSync as statSync10 } from "node:fs";
7431
- import { join as join51, relative as relative7 } from "node:path";
7677
+ import { execFileSync as execFileSync21 } from "node:child_process";
7678
+ import { existsSync as existsSync38, readdirSync as readdirSync16, statSync as statSync10 } from "node:fs";
7679
+ import { join as join54, relative as relative7 } from "node:path";
7432
7680
 
7433
- // src/commands.drop-session.git.ts
7434
- import { execFileSync as execFileSync19 } from "node:child_process";
7681
+ // src/commands/drop-session/git.ts
7682
+ import { execFileSync as execFileSync20 } from "node:child_process";
7435
7683
  function expandStagedDir(dirRel, repo) {
7436
7684
  try {
7437
- const out = execFileSync19("git", ["ls-files", "-z", "--", dirRel], {
7685
+ const out = execFileSync20("git", ["ls-files", "-z", "--", dirRel], {
7438
7686
  cwd: repo,
7439
7687
  stdio: ["ignore", "pipe", "pipe"]
7440
7688
  });
@@ -7446,7 +7694,7 @@ function expandStagedDir(dirRel, repo) {
7446
7694
  function isTrackedInHead(rel, repo) {
7447
7695
  try {
7448
7696
  const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
7449
- execFileSync19("git", ["cat-file", "-e", `HEAD:${treePath}`], {
7697
+ execFileSync20("git", ["cat-file", "-e", `HEAD:${treePath}`], {
7450
7698
  cwd: repo,
7451
7699
  stdio: ["ignore", "pipe", "pipe"]
7452
7700
  });
@@ -7457,7 +7705,7 @@ function isTrackedInHead(rel, repo) {
7457
7705
  }
7458
7706
  function isInIndex(rel, repo) {
7459
7707
  try {
7460
- const out = execFileSync19("git", ["ls-files", "--", rel], {
7708
+ const out = execFileSync20("git", ["ls-files", "--", rel], {
7461
7709
  cwd: repo,
7462
7710
  stdio: ["ignore", "pipe", "pipe"]
7463
7711
  });
@@ -7467,12 +7715,12 @@ function isInIndex(rel, repo) {
7467
7715
  }
7468
7716
  }
7469
7717
 
7470
- // src/commands.drop-session.scrub-hint.ts
7718
+ // src/commands/drop-session/scrub-hint.ts
7471
7719
  init_config();
7472
7720
  init_utils();
7473
7721
  init_utils_json();
7474
- import { existsSync as existsSync36 } from "node:fs";
7475
- import { join as join50 } from "node:path";
7722
+ import { existsSync as existsSync37 } from "node:fs";
7723
+ import { join as join53 } from "node:path";
7476
7724
  var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
7477
7725
  function reportScrubHint(id, matches) {
7478
7726
  const live = resolveLiveTranscript2(id, matches);
@@ -7488,8 +7736,8 @@ function reportScrubHint(id, matches) {
7488
7736
  }
7489
7737
  function resolveLiveTranscript2(id, matches) {
7490
7738
  try {
7491
- const mapPath = join50(repoHome(), "path-map.json");
7492
- if (!existsSync36(mapPath)) return null;
7739
+ const mapPath = join53(repoHome(), "path-map.json");
7740
+ if (!existsSync37(mapPath)) return null;
7493
7741
  const projects = readJson(mapPath).projects;
7494
7742
  const claude = claudeHome();
7495
7743
  for (const rel of matches) {
@@ -7497,8 +7745,8 @@ function resolveLiveTranscript2(id, matches) {
7497
7745
  if (logical === void 0) continue;
7498
7746
  const abs = projects[logical]?.[HOST];
7499
7747
  if (abs === void 0) continue;
7500
- const live = join50(claude, "projects", encodePath(abs), `${id}.jsonl`);
7501
- if (existsSync36(live)) return live;
7748
+ const live = join53(claude, "projects", encodePath(abs), `${id}.jsonl`);
7749
+ if (existsSync37(live)) return live;
7502
7750
  }
7503
7751
  return null;
7504
7752
  } catch {
@@ -7506,7 +7754,7 @@ function resolveLiveTranscript2(id, matches) {
7506
7754
  }
7507
7755
  }
7508
7756
 
7509
- // src/commands.drop-session.ts
7757
+ // src/commands/drop-session/drop-session.ts
7510
7758
  init_utils();
7511
7759
  function cmdDropSession(id) {
7512
7760
  if (id.length === 0 || id.length > 128 || !/^[A-Za-z0-9_-]+$/.test(id)) {
@@ -7514,12 +7762,12 @@ function cmdDropSession(id) {
7514
7762
  process.exit(1);
7515
7763
  }
7516
7764
  const repo = repoHome();
7517
- if (!existsSync37(repo)) die(`repo not cloned at ${repo}`);
7765
+ if (!existsSync38(repo)) die(`repo not cloned at ${repo}`);
7518
7766
  const handle = acquireLock("drop-session");
7519
7767
  if (handle === null) process.exit(0);
7520
7768
  try {
7521
- const repoProjects = join51(repo, "shared", "projects");
7522
- if (!existsSync37(repoProjects)) {
7769
+ const repoProjects = join54(repo, "shared", "projects");
7770
+ if (!existsSync38(repoProjects)) {
7523
7771
  throw new NomadFatal(`no staged session matches ${id}`);
7524
7772
  }
7525
7773
  const matches = collectMatches(repoProjects, id, repo);
@@ -7542,12 +7790,12 @@ function cmdDropSession(id) {
7542
7790
  function collectMatches(repoProjects, id, repo) {
7543
7791
  const matches = [];
7544
7792
  for (const logical of readdirSync16(repoProjects)) {
7545
- const candidate = join51(repoProjects, logical, `${id}.jsonl`);
7546
- if (existsSync37(candidate)) {
7793
+ const candidate = join54(repoProjects, logical, `${id}.jsonl`);
7794
+ if (existsSync38(candidate)) {
7547
7795
  matches.push(relative7(repo, candidate));
7548
7796
  }
7549
- const dir = join51(repoProjects, logical, id);
7550
- if (existsSync37(dir) && statSync10(dir).isDirectory()) {
7797
+ const dir = join54(repoProjects, logical, id);
7798
+ if (existsSync38(dir) && statSync10(dir).isDirectory()) {
7551
7799
  const dirRel = relative7(repo, dir);
7552
7800
  const staged = expandStagedDir(dirRel, repo);
7553
7801
  if (staged.length > 0) matches.push(...staged);
@@ -7563,12 +7811,12 @@ function unstageOne(rel, repo) {
7563
7811
  }
7564
7812
  try {
7565
7813
  if (isTrackedInHead(rel, repo)) {
7566
- execFileSync20("git", ["restore", "--staged", "--worktree", "--", rel], {
7814
+ execFileSync21("git", ["restore", "--staged", "--worktree", "--", rel], {
7567
7815
  cwd: repo,
7568
7816
  stdio: ["ignore", "pipe", "pipe"]
7569
7817
  });
7570
7818
  } else {
7571
- execFileSync20("git", ["rm", "--cached", "-f", "--", rel], {
7819
+ execFileSync21("git", ["rm", "--cached", "-f", "--", rel], {
7572
7820
  cwd: repo,
7573
7821
  stdio: ["ignore", "pipe", "pipe"]
7574
7822
  });
@@ -7581,15 +7829,15 @@ function unstageOne(rel, repo) {
7581
7829
  item(`dropped ${rel}`);
7582
7830
  }
7583
7831
 
7584
- // src/commands.pull.ts
7832
+ // src/commands/pull/pull.ts
7585
7833
  init_autostash_guard();
7586
- import { existsSync as existsSync46, mkdirSync as mkdirSync14 } from "node:fs";
7587
- import { join as join62 } from "node:path";
7834
+ import { existsSync as existsSync47, mkdirSync as mkdirSync14 } from "node:fs";
7835
+ import { join as join65 } from "node:path";
7588
7836
 
7589
- // src/commands.push.sections.ts
7837
+ // src/commands/push/sections.ts
7590
7838
  init_color();
7591
7839
 
7592
- // src/summary.ts
7840
+ // src/render/summary.ts
7593
7841
  init_utils();
7594
7842
  function summaryText(verb, unmapped, collisions = 0, extrasSkipped = 0, localOnly = 0) {
7595
7843
  const extras = extrasSkipped > 0 ? `, ${extrasSkipped} extras skipped` : "";
@@ -7614,7 +7862,7 @@ function summaryRow(verb, unmapped, collisions = 0, extrasSkipped = 0, localOnly
7614
7862
  return text.replace(/^summary: /, "");
7615
7863
  }
7616
7864
 
7617
- // src/commands.push.sections.ts
7865
+ // src/commands/push/sections.ts
7618
7866
  function collapsedSkipRow(n, noun) {
7619
7867
  if (n <= 0) return null;
7620
7868
  return `${dim(infoGlyph)} ${n} ${noun}`;
@@ -7687,23 +7935,23 @@ function renderNoScanTree(st, opts = {}) {
7687
7935
  renderTree(buildNoScanSections(st, opts));
7688
7936
  }
7689
7937
 
7690
- // src/commands.pull.ts
7938
+ // src/commands/pull/pull.ts
7691
7939
  init_config();
7692
7940
 
7693
- // src/extras-sync.ts
7941
+ // src/sync/extras/extras.ts
7694
7942
  init_config();
7695
- import { existsSync as existsSync39, statSync as statSync11 } from "node:fs";
7696
- import { join as join54 } from "node:path";
7943
+ import { existsSync as existsSync40, statSync as statSync11 } from "node:fs";
7944
+ import { join as join57 } from "node:path";
7697
7945
  init_utils();
7698
7946
  init_utils_json();
7699
7947
 
7700
- // src/extras-sync.remap.ts
7948
+ // src/sync/extras/remap.ts
7701
7949
  init_config();
7702
- import { existsSync as existsSync38, mkdirSync as mkdirSync13, readdirSync as readdirSync17, readFileSync as readFileSync19, realpathSync as realpathSync7, rmSync as rmSync16 } from "node:fs";
7703
- import { dirname as dirname14, join as join53, sep as sep11 } from "node:path";
7950
+ import { existsSync as existsSync39, mkdirSync as mkdirSync13, readdirSync as readdirSync17, readFileSync as readFileSync19, realpathSync as realpathSync7, rmSync as rmSync16 } from "node:fs";
7951
+ import { dirname as dirname15, join as join56, sep as sep11 } from "node:path";
7704
7952
 
7705
- // src/extras-sync.planning-diff.ts
7706
- import { join as join52, normalize as normalize2, sep as sep10 } from "node:path";
7953
+ // src/sync/extras/planning-diff.ts
7954
+ import { join as join55, normalize as normalize2, sep as sep10 } from "node:path";
7707
7955
  init_utils();
7708
7956
  function processRecord(fields, i, changed, deleted) {
7709
7957
  const status = fields[i];
@@ -7755,7 +8003,7 @@ function planningDeleteTargets(opts) {
7755
8003
  const { deleted } = parsePlanningDiff(raw);
7756
8004
  const logicalPrefix = "shared/extras/" + logical + "/";
7757
8005
  const prefix = logicalPrefix + ".planning/";
7758
- const planningRoot = join52(localRoot, ".planning");
8006
+ const planningRoot = join55(localRoot, ".planning");
7759
8007
  const planningRootBoundary = planningRoot + sep10;
7760
8008
  const targets = [];
7761
8009
  for (const repoPath of deleted) {
@@ -7763,7 +8011,7 @@ function planningDeleteTargets(opts) {
7763
8011
  continue;
7764
8012
  }
7765
8013
  const remainder = repoPath.slice(logicalPrefix.length);
7766
- const candidate = join52(localRoot, remainder);
8014
+ const candidate = join55(localRoot, remainder);
7767
8015
  const resolved = normalize2(candidate);
7768
8016
  if (!resolved.startsWith(planningRootBoundary)) {
7769
8017
  throw new NomadFatal(
@@ -7775,7 +8023,7 @@ function planningDeleteTargets(opts) {
7775
8023
  return targets;
7776
8024
  }
7777
8025
 
7778
- // src/extras-sync.remap.ts
8026
+ // src/sync/extras/remap.ts
7779
8027
  init_utils_fs();
7780
8028
  init_utils();
7781
8029
  function runExtrasOp(v, dryRun, paths, backup, copy) {
@@ -7784,7 +8032,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
7784
8032
  const would = [];
7785
8033
  for (const t of eachExtrasTarget(v, counts)) {
7786
8034
  const { src, dst } = paths(t);
7787
- if (!existsSync38(src)) continue;
8035
+ if (!existsSync39(src)) continue;
7788
8036
  const item2 = `${t.logical}/${t.dirname}`;
7789
8037
  if (dryRun) {
7790
8038
  would.push(item2);
@@ -7797,7 +8045,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
7797
8045
  return { ...counts, done, would };
7798
8046
  }
7799
8047
  function pruneEmptyAncestors(target, planningRoot) {
7800
- let dir = dirname14(target);
8048
+ let dir = dirname15(target);
7801
8049
  while (dir !== planningRoot && dir.startsWith(planningRoot + sep11)) {
7802
8050
  try {
7803
8051
  if (readdirSync17(dir).length > 0) break;
@@ -7805,7 +8053,7 @@ function pruneEmptyAncestors(target, planningRoot) {
7805
8053
  } catch {
7806
8054
  break;
7807
8055
  }
7808
- dir = dirname14(dir);
8056
+ dir = dirname15(dir);
7809
8057
  }
7810
8058
  }
7811
8059
  function tryRealpath(dir) {
@@ -7819,8 +8067,8 @@ function isInsidePlanningRoot(parentReal, rootReal) {
7819
8067
  return parentReal === rootReal || parentReal.startsWith(rootReal + sep11);
7820
8068
  }
7821
8069
  function deletePlanningTarget(target, planningRoot, repoCounterpart) {
7822
- if (existsSync38(repoCounterpart)) return;
7823
- const parentReal = tryRealpath(dirname14(target));
8070
+ if (existsSync39(repoCounterpart)) return;
8071
+ const parentReal = tryRealpath(dirname15(target));
7824
8072
  if (parentReal === void 0) return;
7825
8073
  const rootReal = tryRealpath(planningRoot);
7826
8074
  if (rootReal === void 0) return;
@@ -7829,7 +8077,7 @@ function deletePlanningTarget(target, planningRoot, repoCounterpart) {
7829
8077
  pruneEmptyAncestors(target, planningRoot);
7830
8078
  }
7831
8079
  function localDivergesFromPreDelete(target, pre, repoRel, repo) {
7832
- if (!existsSync38(target)) return false;
8080
+ if (!existsSync39(target)) return false;
7833
8081
  try {
7834
8082
  const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
7835
8083
  return !readFileSync19(target).equals(preBlob);
@@ -7874,7 +8122,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
7874
8122
  return kept;
7875
8123
  }
7876
8124
  function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
7877
- const repoExtras = join53(repo, "shared", "extras");
8125
+ const repoExtras = join56(repo, "shared", "extras");
7878
8126
  for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
7879
8127
  if (t.dirname !== ".planning") continue;
7880
8128
  let raw;
@@ -7889,14 +8137,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
7889
8137
  }
7890
8138
  const pairs = deletePairsFor(t, raw);
7891
8139
  if (pairs.length === 0) continue;
7892
- backupExtrasWrite(join53(t.localRoot, t.dirname), ts, t.localRoot);
7893
- const planningRoot = join53(t.localRoot, ".planning");
8140
+ backupExtrasWrite(join56(t.localRoot, t.dirname), ts, t.localRoot);
8141
+ const planningRoot = join56(t.localRoot, ".planning");
7894
8142
  for (const { target, relToLocal, repoRel } of pairs) {
7895
8143
  if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
7896
8144
  warn(keptDeleteWarnLine(t.logical, relToLocal));
7897
8145
  continue;
7898
8146
  }
7899
- deletePlanningTarget(target, planningRoot, join53(repoExtras, t.logical, relToLocal));
8147
+ deletePlanningTarget(target, planningRoot, join56(repoExtras, t.logical, relToLocal));
7900
8148
  }
7901
8149
  }
7902
8150
  }
@@ -7905,14 +8153,14 @@ function remapExtrasPush(ts, opts = {}) {
7905
8153
  const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
7906
8154
  if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
7907
8155
  const repo = repoHome();
7908
- const repoExtras = join53(repo, "shared", "extras");
8156
+ const repoExtras = join56(repo, "shared", "extras");
7909
8157
  if (!dryRun) mkdirSync13(repoExtras, { recursive: true });
7910
8158
  const { unmapped, skipped, done, would } = runExtrasOp(
7911
8159
  v,
7912
8160
  dryRun,
7913
- ({ localRoot, logical, dirname: dirname15 }) => ({
7914
- src: join53(localRoot, dirname15),
7915
- dst: join53(repoExtras, logical, dirname15)
8161
+ ({ localRoot, logical, dirname: dirname16 }) => ({
8162
+ src: join56(localRoot, dirname16),
8163
+ dst: join56(repoExtras, logical, dirname16)
7916
8164
  }),
7917
8165
  (dst) => backupRepoWrite(dst, ts, repo),
7918
8166
  // Push copy routing per extra type:
@@ -7921,11 +8169,11 @@ function remapExtrasPush(ts, opts = {}) {
7921
8169
  // The filter prevents ALWAYS_NEVER_SYNC files from landing in the repo
7922
8170
  // working tree before the allow-list gate fires, eliminating the
7923
8171
  // "residue wedges repeat push" regression. The allow-list gate
7924
- // (enforceAllowList in commands.push.allowlist.ts, blockSetFor in
8172
+ // (enforceAllowList in commands/push/allowlist.ts, blockSetFor in
7925
8173
  // config.never-sync.ts)
7926
8174
  // remains the hard security boundary.
7927
8175
  // All others: copyExtrasFiltered with per-extra denylist.
7928
- (src, dst, dirname15) => dirname15 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname15)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname15))
8176
+ (src, dst, dirname16) => dirname16 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname16)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname16))
7929
8177
  );
7930
8178
  return { unmapped, skipped, pushed: done, wouldPush: would };
7931
8179
  }
@@ -7941,9 +8189,9 @@ function remapExtrasPull(ts, opts = {}) {
7941
8189
  const { unmapped, skipped, done, would } = runExtrasOp(
7942
8190
  v,
7943
8191
  dryRun,
7944
- ({ localRoot, logical, dirname: dirname15 }) => ({
7945
- src: join53(repo, "shared", "extras", logical, dirname15),
7946
- dst: join53(localRoot, dirname15)
8192
+ ({ localRoot, logical, dirname: dirname16 }) => ({
8193
+ src: join56(repo, "shared", "extras", logical, dirname16),
8194
+ dst: join56(localRoot, dirname16)
7947
8195
  }),
7948
8196
  // Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
7949
8197
  // localRoot so the backup tree mirrors the project layout.
@@ -7961,12 +8209,12 @@ function remapExtrasPull(ts, opts = {}) {
7961
8209
  // copyExtrasFileSkipDiverged keeps a locally-edited file rather than
7962
8210
  // clobbering it, so the divergence WARN's keep-local promise holds for
7963
8211
  // every extra, not just `.planning`.
7964
- (src, dst, dirname15) => {
7965
- if (dirname15 === ".claude")
7966
- return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname15));
7967
- if (dirname15 === ".planning") {
8212
+ (src, dst, dirname16) => {
8213
+ if (dirname16 === ".claude")
8214
+ return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname16));
8215
+ if (dirname16 === ".planning") {
7968
8216
  const divergedSet = new Set(listDivergingModified(dst, src));
7969
- return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname15), divergedSet);
8217
+ return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname16), divergedSet);
7970
8218
  }
7971
8219
  return copyExtrasFileSkipDiverged(src, dst);
7972
8220
  }
@@ -7977,7 +8225,7 @@ function remapExtrasPull(ts, opts = {}) {
7977
8225
  return { unmapped, skipped, pulled: done, wouldPull: would };
7978
8226
  }
7979
8227
 
7980
- // src/extras-sync.ts
8228
+ // src/sync/extras/extras.ts
7981
8229
  function divergenceWarnLine(o) {
7982
8230
  const kind = o.isDir ? "folder" : "file";
7983
8231
  const name = o.isDir ? `${o.dirname}/` : o.dirname;
@@ -7990,20 +8238,20 @@ function divergenceCheckExtras(ts, prePostHeads) {
7990
8238
  const v = loadValidatedExtras({});
7991
8239
  if (v === null) return 0;
7992
8240
  const counts = { unmapped: 0, skipped: 0 };
7993
- const backupRoot = join54(backupBase(), ts, "extras");
8241
+ const backupRoot = join57(backupBase(), ts, "extras");
7994
8242
  const repo = repoHome();
7995
8243
  let divergedCount = 0;
7996
- for (const { logical, localRoot, dirname: dirname15 } of eachExtrasTarget(v, counts)) {
7997
- const local = join54(localRoot, dirname15);
7998
- const repoEntry = join54(repo, "shared", "extras", logical, dirname15);
7999
- if (!existsSync39(local) || !existsSync39(repoEntry)) continue;
8244
+ for (const { logical, localRoot, dirname: dirname16 } of eachExtrasTarget(v, counts)) {
8245
+ const local = join57(localRoot, dirname16);
8246
+ const repoEntry = join57(repo, "shared", "extras", logical, dirname16);
8247
+ if (!existsSync40(local) || !existsSync40(repoEntry)) continue;
8000
8248
  const modified = listDivergingModified(local, repoEntry);
8001
8249
  if (modified.length === 0) continue;
8002
8250
  divergedCount += modified.length;
8003
- const projectBackupRoot = join54(backupRoot, encodePath(localRoot));
8251
+ const projectBackupRoot = join57(backupRoot, encodePath(localRoot));
8004
8252
  warn(
8005
8253
  divergenceWarnLine({
8006
- dirname: dirname15,
8254
+ dirname: dirname16,
8007
8255
  logical,
8008
8256
  isDir: statSync11(local).isDirectory(),
8009
8257
  count: modified.length,
@@ -8020,10 +8268,10 @@ function divergenceCheckExtras(ts, prePostHeads) {
8020
8268
  return divergedCount;
8021
8269
  }
8022
8270
 
8023
- // src/links.baseline.ts
8271
+ // src/sync/links.baseline.ts
8024
8272
  init_config();
8025
8273
  import { lstatSync as lstatSync18, readdirSync as readdirSync18 } from "node:fs";
8026
- import { join as join55, relative as relative8, sep as sep12 } from "node:path";
8274
+ import { join as join58, relative as relative8, sep as sep12 } from "node:path";
8027
8275
  init_utils();
8028
8276
  var SHARED_BASELINE_KIND = "shared-links-baseline/1";
8029
8277
  var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
@@ -8065,7 +8313,7 @@ function collectSharedFiles(dir, claude, scan) {
8065
8313
  return;
8066
8314
  }
8067
8315
  for (const entry of entries) {
8068
- const abs = join55(dir, entry);
8316
+ const abs = join58(dir, entry);
8069
8317
  if (isDeniedName(ALWAYS_NEVER_SYNC, entry)) {
8070
8318
  scan.declined.push(baselineKey(claude, abs));
8071
8319
  continue;
@@ -8081,7 +8329,7 @@ function enumerateLocalSharedScan(map, opts = {}) {
8081
8329
  scan.declined.push(name);
8082
8330
  continue;
8083
8331
  }
8084
- addLocalPath(join55(claude, name), claude, scan);
8332
+ addLocalPath(join58(claude, name), claude, scan);
8085
8333
  }
8086
8334
  return scan;
8087
8335
  }
@@ -8093,7 +8341,7 @@ function buildSharedBaseline(map, opts = {}) {
8093
8341
  const files = {};
8094
8342
  for (const [key, st] of Object.entries(enumerateLocalSharedFiles(map, opts))) {
8095
8343
  try {
8096
- files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join55(claude, key)) };
8344
+ files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join58(claude, key)) };
8097
8345
  } catch {
8098
8346
  }
8099
8347
  }
@@ -8108,16 +8356,16 @@ function writeSharedBaseline(map, opts = {}) {
8108
8356
  }
8109
8357
  }
8110
8358
 
8111
- // src/commands.pull.win32.ts
8359
+ // src/commands/pull/win32.ts
8112
8360
  init_color();
8113
- import { existsSync as existsSync44 } from "node:fs";
8114
- import { join as join60 } from "node:path";
8361
+ import { existsSync as existsSync45 } from "node:fs";
8362
+ import { join as join63 } from "node:path";
8115
8363
 
8116
- // src/commands.pull.recovery.git.ts
8364
+ // src/commands/pull/recovery.git.ts
8117
8365
  init_utils();
8118
- import { execFileSync as execFileSync21 } from "node:child_process";
8366
+ import { execFileSync as execFileSync22 } from "node:child_process";
8119
8367
  function gitCapture(args, cwd) {
8120
- return execFileSync21("git", args, {
8368
+ return execFileSync22("git", args, {
8121
8369
  cwd,
8122
8370
  stdio: ["ignore", "pipe", "pipe"],
8123
8371
  maxBuffer: 64 * 1024 * 1024
@@ -8152,30 +8400,13 @@ function parseDirtyPaths(repo, opts = {}) {
8152
8400
  return parsePorcelainZ(gitStatusPorcelainZ(repo, opts));
8153
8401
  }
8154
8402
 
8155
- // src/commands.pull.win32.ts
8403
+ // src/commands/pull/win32.ts
8156
8404
  init_config();
8157
8405
 
8158
- // src/git-probe.ts
8159
- import { execFileSync as execFileSync22 } from "node:child_process";
8160
- var PROBE_TIMEOUT_MS4 = 1e4;
8161
- var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
8162
- function gitProbe(args, repo) {
8163
- try {
8164
- return execFileSync22("git", args, {
8165
- cwd: repo,
8166
- stdio: ["ignore", "pipe", "pipe"],
8167
- timeout: PROBE_TIMEOUT_MS4,
8168
- maxBuffer: PROBE_MAX_BUFFER
8169
- }).toString();
8170
- } catch {
8171
- return null;
8172
- }
8173
- }
8174
-
8175
- // src/links.deletions.ts
8406
+ // src/sync/links.deletions.ts
8176
8407
  init_config();
8177
- import { existsSync as existsSync40, lstatSync as lstatSync19, rmSync as rmSync17 } from "node:fs";
8178
- import { join as join56, relative as relative9, resolve as resolve5, sep as sep13 } from "node:path";
8408
+ import { existsSync as existsSync41, lstatSync as lstatSync19, rmSync as rmSync17 } from "node:fs";
8409
+ import { join as join59, relative as relative9, resolve as resolve5, sep as sep13 } from "node:path";
8179
8410
  init_utils();
8180
8411
  init_utils_fs();
8181
8412
  function isUnknown(declined, key) {
@@ -8185,15 +8416,15 @@ function deletionFor(key, names, claude, sharedRoot) {
8185
8416
  const segments = key.split("/");
8186
8417
  const name = segments[0];
8187
8418
  if (!names.has(name)) return null;
8188
- if (!existsSync40(join56(claude, name))) return null;
8419
+ if (!existsSync41(join59(claude, name))) return null;
8189
8420
  if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
8190
8421
  const repoPath = resolve5(sharedRoot, key);
8191
- const rel = relative9(join56(sharedRoot, name), repoPath);
8422
+ const rel = relative9(join59(sharedRoot, name), repoPath);
8192
8423
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep13}`)) return null;
8193
8424
  if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
8194
8425
  return null;
8195
- if (!existsSync40(repoPath)) return null;
8196
- return { kind: "deletion", name, localPath: join56(claude, key), repoPath };
8426
+ if (!existsSync41(repoPath)) return null;
8427
+ return { kind: "deletion", name, localPath: join59(claude, key), repoPath };
8197
8428
  }
8198
8429
  function planSharedLinkDeletions(map, opts = {}) {
8199
8430
  if (process.platform !== "win32") return [];
@@ -8201,7 +8432,7 @@ function planSharedLinkDeletions(map, opts = {}) {
8201
8432
  const baseline = readSharedBaseline();
8202
8433
  if (baseline === null) return [];
8203
8434
  const claude = claudeHome();
8204
- const sharedRoot = join56(repoHome(), "shared");
8435
+ const sharedRoot = join59(repoHome(), "shared");
8205
8436
  const linkNames = opts.linkNames ?? allSharedLinks(map);
8206
8437
  const scan = enumerateLocalSharedScan(map, { linkNames });
8207
8438
  const present = new Set(Object.keys(scan.files).map((key) => key.toLowerCase()));
@@ -8233,10 +8464,11 @@ function applySharedLinkDeletions(map, ts, opts = {}) {
8233
8464
  return removed;
8234
8465
  }
8235
8466
 
8236
- // src/links.mirror.ts
8467
+ // src/sync/links.mirror.ts
8237
8468
  init_config();
8238
- import { existsSync as existsSync41, lstatSync as lstatSync20, rmSync as rmSync18, statSync as statSync12 } from "node:fs";
8239
- import { join as join57 } from "node:path";
8469
+ init_error_text();
8470
+ import { existsSync as existsSync42, lstatSync as lstatSync20, rmSync as rmSync18, statSync as statSync12 } from "node:fs";
8471
+ import { join as join60 } from "node:path";
8240
8472
  init_utils();
8241
8473
  init_utils_fs();
8242
8474
  function emitMirror(onPreview, name, localPath, repoPath) {
@@ -8250,7 +8482,7 @@ function emitMirrorWet(onPreview, name, localPath, repoPath) {
8250
8482
  if (onPreview) onPreview({ kind: "mirror", name, localPath, repoPath });
8251
8483
  }
8252
8484
  function notShared(target) {
8253
- return !existsSync41(target);
8485
+ return !existsSync42(target);
8254
8486
  }
8255
8487
  function warnUnusableSharedTarget(name, dryRun, state) {
8256
8488
  const reason = state === "dangling" ? `shared/${name} does not resolve to anything usable in the sync repo. Remove it, or restore what it points at` : `shared/${name} could not be read in the sync repo, so whether it is usable could not be determined. Check its permissions there`;
@@ -8278,8 +8510,8 @@ function warnUnreadableLocalName(name, target, err, dryRun) {
8278
8510
  );
8279
8511
  }
8280
8512
  function mirrorOneSharedName(name, claude, repo, policy, opts) {
8281
- const localPath = join57(claude, name);
8282
- const target = join57(repo, "shared", name);
8513
+ const localPath = join60(claude, name);
8514
+ const target = join60(repo, "shared", name);
8283
8515
  let stat;
8284
8516
  try {
8285
8517
  stat = lstatSync20(localPath, { throwIfNoEntry: false });
@@ -8339,7 +8571,7 @@ function mirrorSharedNames(map, policy, opts = {}) {
8339
8571
  if (map === null) return;
8340
8572
  const claude = claudeHome();
8341
8573
  const repo = repoHome();
8342
- const sharedPath = join57(repo, "shared");
8574
+ const sharedPath = join60(repo, "shared");
8343
8575
  const sharedState = classifyPresence(sharedPath);
8344
8576
  if (isUnusableTarget(sharedState)) {
8345
8577
  warnUnusableSharedDir(opts.dryRun === true, sharedState);
@@ -8368,7 +8600,7 @@ function presentAt(abs) {
8368
8600
  }
8369
8601
  }
8370
8602
  function removeUntrackedDenied(repo, path, segment, ts) {
8371
- const abs = join57(repo, path);
8603
+ const abs = join60(repo, path);
8372
8604
  const denied = `the path segment "${segment}" is on the never-sync list`;
8373
8605
  if (!presentAt(abs)) {
8374
8606
  warn(
@@ -8376,7 +8608,7 @@ function removeUntrackedDenied(repo, path, segment, ts) {
8376
8608
  );
8377
8609
  return;
8378
8610
  }
8379
- const snapshotted = existsSync41(abs);
8611
+ const snapshotted = existsSync42(abs);
8380
8612
  try {
8381
8613
  backupRepoWrite(abs, ts, repo);
8382
8614
  } catch (err) {
@@ -8402,19 +8634,19 @@ function reportTrackedDenied(repo, path, segment) {
8402
8634
  const denied = `the path segment "${segment}" is on the never-sync list`;
8403
8635
  if (inHead === null) {
8404
8636
  warn(
8405
- `could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it with "git status -- ${path}" and take it out of shared/ before committing`
8637
+ `could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it by running git status -- "${path}" and take it out of shared/ before committing`
8406
8638
  );
8407
8639
  return;
8408
8640
  }
8409
8641
  if (inHead.trim() === "") {
8410
8642
  warn(
8411
- `${path} is staged and has no committed version: ${denied}. Nothing was changed. Run "git rm --cached -- ${path}" to take it out of the index; that leaves the file on disk but makes it untracked, which the next nomad pull removes from the sync repo working tree (snapshotting it into the backup cache first), so move it outside shared/ instead if you want to keep it. If it is the destination half of a staged rename, "git diff --cached --name-status" names the source, whose content IS committed, so restore that half with "git checkout HEAD -- <source>" rather than leaving its deletion staged`
8643
+ `${path} is staged and has no committed version: ${denied}. Nothing was changed. Run git rm --cached -- "${path}" to take it out of the index; that leaves the file on disk but makes it untracked, which the next nomad pull removes from the sync repo working tree (snapshotting it into the backup cache first), so move it outside shared/ instead if you want to keep it. If it is the destination half of a staged rename, run git diff --cached --name-status to name the source, whose content IS committed, so restore that half with git checkout HEAD -- "<source>" rather than leaving its deletion staged`
8412
8644
  );
8413
8645
  return;
8414
8646
  }
8415
- if (!existsSync41(join57(repo, path))) return;
8647
+ if (!existsSync42(join60(repo, path))) return;
8416
8648
  warn(
8417
- `${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run "git checkout HEAD -- ${path}" to put the committed content back, or move the file outside shared/ if you want to keep it. Neither of those takes the committed copy out of the repo: "git rm -- ${path}" and a commit does that going forward, and if it holds a real secret, rotate it and rewrite history, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent to the remote`
8649
+ `${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run git checkout HEAD -- "${path}" to put the committed content back, or move the file outside shared/ if you want to keep it. Neither of those takes the committed copy out of the repo: git rm -- "${path}" and a commit does that going forward, and if it holds a real secret, rotate it and rewrite history, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent to the remote`
8418
8650
  );
8419
8651
  }
8420
8652
  function revertDeniedMirrorPaths(repo, status, ts) {
@@ -8428,10 +8660,10 @@ function revertDeniedMirrorPaths(repo, status, ts) {
8428
8660
  }
8429
8661
  }
8430
8662
 
8431
- // src/preview.ts
8663
+ // src/render/preview.ts
8432
8664
  init_config();
8433
- import { existsSync as existsSync43 } from "node:fs";
8434
- import { join as join59 } from "node:path";
8665
+ import { existsSync as existsSync44 } from "node:fs";
8666
+ import { join as join62 } from "node:path";
8435
8667
 
8436
8668
  // node_modules/diff/libesm/diff/base.js
8437
8669
  var Diff = class {
@@ -8683,7 +8915,7 @@ function tokenize(value, options) {
8683
8915
  return retLines;
8684
8916
  }
8685
8917
 
8686
- // src/diff-lines.ts
8918
+ // src/render/diff-lines.ts
8687
8919
  init_color();
8688
8920
  function diffLinesToUnified(oldStr, newStr) {
8689
8921
  const parts = diffLines(oldStr, newStr);
@@ -8704,18 +8936,18 @@ function diffLinesToUnified(oldStr, newStr) {
8704
8936
  return lines;
8705
8937
  }
8706
8938
 
8707
- // src/preview.skills.ts
8939
+ // src/render/preview.skills.ts
8708
8940
  init_config();
8709
- import { existsSync as existsSync42, readdirSync as readdirSync19 } from "node:fs";
8710
- import { join as join58 } from "node:path";
8941
+ import { existsSync as existsSync43, readdirSync as readdirSync19 } from "node:fs";
8942
+ import { join as join61 } from "node:path";
8711
8943
  function buildSkillsPreviewSection() {
8712
8944
  const s = section("Skills");
8713
- const sharedSkills = join58(repoHome(), "shared", "skills");
8714
- if (!existsSync42(sharedSkills)) return s;
8945
+ const sharedSkills = join61(repoHome(), "shared", "skills");
8946
+ if (!existsSync43(sharedSkills)) return s;
8715
8947
  const sharedNames = readdirSync19(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
8716
8948
  for (const name of sharedNames) addItem(s, name);
8717
- const localSkills = join58(claudeHome(), "skills");
8718
- const localOnly = existsSync42(localSkills) ? readdirSync19(localSkills, { encoding: "utf8" }).filter(
8949
+ const localSkills = join61(claudeHome(), "skills");
8950
+ const localOnly = existsSync43(localSkills) ? readdirSync19(localSkills, { encoding: "utf8" }).filter(
8719
8951
  (name) => !isSkillExcluded(name) && !sharedNames.includes(name)
8720
8952
  ).length : 0;
8721
8953
  if (localOnly > 0) {
@@ -8724,7 +8956,7 @@ function buildSkillsPreviewSection() {
8724
8956
  return s;
8725
8957
  }
8726
8958
 
8727
- // src/preview.ts
8959
+ // src/render/preview.ts
8728
8960
  init_utils_json();
8729
8961
  var CANONICAL_ORDER_NOTE = "settings.json will be rewritten in canonical key order; no value changes";
8730
8962
  function diffJsonStrings(currentJsonText, newJsonText) {
@@ -8737,7 +8969,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
8737
8969
  return lines.join("\n");
8738
8970
  }
8739
8971
  function readJsonOrNull(path) {
8740
- if (!existsSync43(path)) return null;
8972
+ if (!existsSync44(path)) return null;
8741
8973
  try {
8742
8974
  return readJson(path);
8743
8975
  } catch {
@@ -8751,12 +8983,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
8751
8983
  }
8752
8984
  const notes = [];
8753
8985
  const hostOverrides = readJsonOrNull(hostPath);
8754
- if (hostOverrides === null && existsSync43(hostPath)) {
8986
+ if (hostOverrides === null && existsSync44(hostPath)) {
8755
8987
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
8756
8988
  }
8757
8989
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
8758
8990
  const current = readJsonOrNull(settingsPath);
8759
- if (current === null && existsSync43(settingsPath)) {
8991
+ if (current === null && existsSync44(settingsPath)) {
8760
8992
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
8761
8993
  }
8762
8994
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -8823,9 +9055,9 @@ function computePreview(ts, map, verb = "pull", plans) {
8823
9055
  onPreview: (e) => addItem(links, formatLinkRow(e))
8824
9056
  });
8825
9057
  const settingsResult = previewSettings(
8826
- join59(repo, "shared", "settings.base.json"),
8827
- join59(repo, "hosts", `${HOST}.json`),
8828
- join59(claude, "settings.json")
9058
+ join62(repo, "shared", "settings.base.json"),
9059
+ join62(repo, "hosts", `${HOST}.json`),
9060
+ join62(claude, "settings.json")
8829
9061
  );
8830
9062
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
8831
9063
  const sessions = section("Sessions");
@@ -8841,7 +9073,7 @@ function computePreview(ts, map, verb = "pull", plans) {
8841
9073
  const extras = section("Extras");
8842
9074
  let extrasSkipped = 0;
8843
9075
  let extrasUnmapped = 0;
8844
- if (existsSync43(join59(repo, "path-map.json")) && existsSync43(join59(repo, "shared", "extras"))) {
9076
+ if (existsSync44(join62(repo, "path-map.json")) && existsSync44(join62(repo, "shared", "extras"))) {
8845
9077
  const extrasResult = remapExtrasPull(ts, { dryRun: true });
8846
9078
  for (const entry of extrasResult.wouldPull) {
8847
9079
  addItem(extras, entry);
@@ -8858,11 +9090,11 @@ function computePreview(ts, map, verb = "pull", plans) {
8858
9090
  return { unmapped: remapResult.unmapped, collisions: 0, localOnly };
8859
9091
  }
8860
9092
 
8861
- // src/commands.pull.win32.ts
9093
+ // src/commands/pull/win32.ts
8862
9094
  init_utils();
8863
9095
  init_utils_json();
8864
9096
  function readMapForMirror(mapPath) {
8865
- if (!existsSync44(mapPath)) return { projects: {} };
9097
+ if (!existsSync45(mapPath)) return { projects: {} };
8866
9098
  try {
8867
9099
  return readPathMap(mapPath);
8868
9100
  } catch {
@@ -8926,7 +9158,7 @@ function reconcileSharedLinksBeforePull(repo, ts) {
8926
9158
  let namesDerived = false;
8927
9159
  let derivedSharedDirs;
8928
9160
  try {
8929
- const map = readMapForMirror(join60(repo, "path-map.json"));
9161
+ const map = readMapForMirror(join63(repo, "path-map.json"));
8930
9162
  if (map !== null) {
8931
9163
  linkNames = allSharedLinks(map);
8932
9164
  namesDerived = true;
@@ -8965,7 +9197,7 @@ function buildMirrorSection(events, discard) {
8965
9197
  function describeSkippedMirrorDiscard(repo, ts) {
8966
9198
  if (process.platform !== "win32") return null;
8967
9199
  try {
8968
- const map = readMapForMirror(join60(repo, "path-map.json"));
9200
+ const map = readMapForMirror(join63(repo, "path-map.json"));
8969
9201
  const linkNames = map !== null ? allSharedLinks(map, { quiet: true }) : void 0;
8970
9202
  const captures = [];
8971
9203
  stageLocalSharedEdits(map, ts, {
@@ -8974,13 +9206,13 @@ function describeSkippedMirrorDiscard(repo, ts) {
8974
9206
  linkNames
8975
9207
  });
8976
9208
  if (captures.length === 0) return null;
8977
- return { count: captures.length, backupPath: join60(backupBase(), ts) };
9209
+ return { count: captures.length, backupPath: join63(backupBase(), ts) };
8978
9210
  } catch {
8979
9211
  return null;
8980
9212
  }
8981
9213
  }
8982
9214
  function planSharedReconcileBeforePull(repo, ts) {
8983
- const map = readMapForMirror(join60(repo, "path-map.json"));
9215
+ const map = readMapForMirror(join63(repo, "path-map.json"));
8984
9216
  const captures = [];
8985
9217
  const linkNames = process.platform === "win32" && map !== null ? allSharedLinks(map) : void 0;
8986
9218
  stageLocalSharedEdits(map, ts, { dryRun: true, onPreview: (e) => captures.push(e), linkNames });
@@ -8992,11 +9224,11 @@ function planSharedReconcileBeforePull(repo, ts) {
8992
9224
  };
8993
9225
  }
8994
9226
 
8995
- // src/commands.pull.collision.ts
9227
+ // src/commands/pull/collision.ts
8996
9228
  init_config();
8997
9229
  init_exit_codes();
8998
- import { existsSync as existsSync45, lstatSync as lstatSync21, readFileSync as readFileSync20, rmSync as rmSync19 } from "node:fs";
8999
- import { join as join61 } from "node:path";
9230
+ import { existsSync as existsSync46, lstatSync as lstatSync21, readFileSync as readFileSync20, rmSync as rmSync19 } from "node:fs";
9231
+ import { join as join64 } from "node:path";
9000
9232
  init_utils();
9001
9233
  var SHARED_PREFIX = /^shared\//;
9002
9234
  var EXTENSION = /(\.[^.]+)?$/;
@@ -9052,15 +9284,15 @@ function isContainedMirrorPath(rel) {
9052
9284
  }
9053
9285
  function matchesLocalOriginal(repo, rel) {
9054
9286
  try {
9055
- const local = join61(claudeHome(), rel.replace(SHARED_PREFIX, ""));
9056
- return readFileSync20(join61(repo, rel)).equals(readFileSync20(local));
9287
+ const local = join64(claudeHome(), rel.replace(SHARED_PREFIX, ""));
9288
+ return readFileSync20(join64(repo, rel)).equals(readFileSync20(local));
9057
9289
  } catch {
9058
9290
  return false;
9059
9291
  }
9060
9292
  }
9061
9293
  function removeMirroredCopies(repo, repoRelPaths) {
9062
9294
  for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
9063
- const abs = join61(repo, rel);
9295
+ const abs = join64(repo, rel);
9064
9296
  try {
9065
9297
  if (lstatSync21(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
9066
9298
  if (!matchesLocalOriginal(repo, rel)) continue;
@@ -9069,7 +9301,7 @@ function removeMirroredCopies(repo, repoRelPaths) {
9069
9301
  warn(`could not remove nomad's copy at ${abs}: ${err.message}`);
9070
9302
  }
9071
9303
  }
9072
- return repoRelPaths.every((rel) => !existsSync45(join61(repo, rel)));
9304
+ return repoRelPaths.every((rel) => !existsSync46(join64(repo, rel)));
9073
9305
  }
9074
9306
  function pullWithCollisionRunbook(repo, mirrored) {
9075
9307
  try {
@@ -9084,10 +9316,10 @@ function pullWithCollisionRunbook(repo, mirrored) {
9084
9316
  }
9085
9317
  }
9086
9318
 
9087
- // src/commands.pull.ts
9088
- init_commands_pull_wedge();
9319
+ // src/commands/pull/pull.ts
9320
+ init_wedge();
9089
9321
 
9090
- // src/commands.pull.recovery.ts
9322
+ // src/commands/pull/recovery.ts
9091
9323
  init_config();
9092
9324
  init_utils();
9093
9325
  init_utils_fs();
@@ -9181,9 +9413,9 @@ an ordinary pull.
9181
9413
  log(buildRecoverySummary(branchName, strandedLog, untracked));
9182
9414
  }
9183
9415
 
9184
- // src/commands.pull.recovery.unmerged.ts
9416
+ // src/commands/pull/recovery.unmerged.ts
9185
9417
  init_exit_codes();
9186
- init_commands_pull_wedge();
9418
+ init_wedge();
9187
9419
  init_utils();
9188
9420
  function recoverUnmergedIndex(repo) {
9189
9421
  const conflicted = new Set(
@@ -9206,7 +9438,7 @@ function recoverUnmergedIndex(repo) {
9206
9438
  }
9207
9439
  }
9208
9440
 
9209
- // src/commands.pull.ts
9441
+ // src/commands/pull/pull.ts
9210
9442
  init_exit_codes();
9211
9443
  init_utils();
9212
9444
  init_utils_fs();
@@ -9276,7 +9508,7 @@ function runPullCore(opts = {}) {
9276
9508
  try {
9277
9509
  return runPullWithBackupTs(ts, opts);
9278
9510
  } finally {
9279
- discardEmptyBackupDir(join62(backup, ts));
9511
+ discardEmptyBackupDir(join65(backup, ts));
9280
9512
  }
9281
9513
  }
9282
9514
  function runPullWithBackupTs(ts, opts) {
@@ -9287,7 +9519,7 @@ function runPullWithBackupTs(ts, opts) {
9287
9519
  const backup = backupBase();
9288
9520
  const recovered = handleWedge(repo, forceRemote);
9289
9521
  if (!dryRun) {
9290
- const backupRoot = join62(backup, ts);
9522
+ const backupRoot = join65(backup, ts);
9291
9523
  try {
9292
9524
  mkdirSync14(backupRoot, { recursive: true });
9293
9525
  } catch (err) {
@@ -9310,8 +9542,8 @@ function runPullWithBackupTs(ts, opts) {
9310
9542
  pullWithCollisionRunbook(repo, mirrored);
9311
9543
  });
9312
9544
  assertNoAutostashConflict(repo, "nomad pull");
9313
- const mapPath = join62(repo, "path-map.json");
9314
- const map = existsSync46(mapPath) ? readPathMap(mapPath) : { projects: {} };
9545
+ const mapPath = join65(repo, "path-map.json");
9546
+ const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
9315
9547
  const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
9316
9548
  if (dryRun) {
9317
9549
  computePreview(ts, map, "pull", plansAgainst(sharedPlans, map));
@@ -9338,8 +9570,8 @@ function runPullWithBackupTs(ts, opts) {
9338
9570
  }
9339
9571
  function cmdPull(opts = {}) {
9340
9572
  const repo = repoHome();
9341
- if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
9342
- if (!existsSync46(join62(repo, "shared", "settings.base.json"))) {
9573
+ if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
9574
+ if (!existsSync47(join65(repo, "shared", "settings.base.json"))) {
9343
9575
  die("repo not initialized; run 'nomad init' to scaffold");
9344
9576
  }
9345
9577
  const handle = acquireLock("pull");
@@ -9360,15 +9592,15 @@ function cmdPull(opts = {}) {
9360
9592
  }
9361
9593
  }
9362
9594
 
9363
- // src/commands.push.ts
9595
+ // src/commands/push/push.ts
9364
9596
  init_config();
9365
- import { existsSync as existsSync50 } from "node:fs";
9366
- import { join as join67 } from "node:path";
9597
+ import { existsSync as existsSync51 } from "node:fs";
9598
+ import { join as join70 } from "node:path";
9367
9599
 
9368
- // src/commands.push.selection.ts
9600
+ // src/commands/push/selection.ts
9369
9601
  init_config();
9370
- import { existsSync as existsSync47, statSync as statSync13 } from "node:fs";
9371
- import { join as join63 } from "node:path";
9602
+ import { existsSync as existsSync48, statSync as statSync13 } from "node:fs";
9603
+ import { join as join66 } from "node:path";
9372
9604
  init_utils_json();
9373
9605
  function buildCurrentMap(map) {
9374
9606
  const current = {};
@@ -9377,8 +9609,8 @@ function buildCurrentMap(map) {
9377
9609
  for (const [, hostMap] of Object.entries(map.projects)) {
9378
9610
  const localPath = hostMap[HOST];
9379
9611
  if (!localPath) continue;
9380
- const localDir = join63(claude, "projects", encodePath(localPath));
9381
- if (!existsSync47(localDir)) continue;
9612
+ const localDir = join66(claude, "projects", encodePath(localPath));
9613
+ if (!existsSync48(localDir)) continue;
9382
9614
  for (const f of enumerateSourceFiles(localDir)) {
9383
9615
  const st = statSync13(f);
9384
9616
  current[f] = { size: st.size, mtime: st.mtimeMs };
@@ -9409,7 +9641,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
9409
9641
  };
9410
9642
  }
9411
9643
  function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
9412
- const map = existsSync47(mapPath) ? readPathMap(mapPath) : null;
9644
+ const map = existsSync48(mapPath) ? readPathMap(mapPath) : null;
9413
9645
  const { selection, newManifest } = computePushSelection(
9414
9646
  map,
9415
9647
  old,
@@ -9420,7 +9652,7 @@ function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan
9420
9652
  return { map, selection, newManifest };
9421
9653
  }
9422
9654
 
9423
- // src/commands.push.allowlist.ts
9655
+ // src/commands/push/allowlist.ts
9424
9656
  init_config();
9425
9657
  init_config_sharedDirs_guard();
9426
9658
  init_utils();
@@ -9495,21 +9727,21 @@ function enforceAllowList(statusPorcelain, map) {
9495
9727
  fail(`${p} is in NEVER_SYNC and must never be pushed`);
9496
9728
  }
9497
9729
  for (const p of violations) {
9498
- fail(`to sync ${p}, add to PUSH_ALLOWED in src/config.ts`);
9730
+ fail(`to sync ${p}, add to PUSH_ALLOWED in src/core/config.ts`);
9499
9731
  }
9500
9732
  throw new NomadFatal("push allow-list violations");
9501
9733
  }
9502
9734
 
9503
- // src/commands.push.settings.ts
9735
+ // src/commands/push/settings.ts
9504
9736
  init_config();
9505
- import { existsSync as existsSync48 } from "node:fs";
9506
- import { join as join64 } from "node:path";
9737
+ import { existsSync as existsSync49 } from "node:fs";
9738
+ import { join as join67 } from "node:path";
9507
9739
  init_utils();
9508
9740
  init_utils_fs();
9509
9741
  init_utils_json();
9510
9742
  function stripGsdHooksFromBase(repo, backup) {
9511
- const basePath = join64(repo, "shared", "settings.base.json");
9512
- if (!existsSync48(basePath)) return;
9743
+ const basePath = join67(repo, "shared", "settings.base.json");
9744
+ if (!existsSync49(basePath)) return;
9513
9745
  let base;
9514
9746
  try {
9515
9747
  base = readJson(basePath);
@@ -9523,14 +9755,14 @@ function stripGsdHooksFromBase(repo, backup) {
9523
9755
  writeJsonAtomic(basePath, stripped);
9524
9756
  }
9525
9757
  function reportSettingsAheadDrift(repo) {
9526
- const basePath = join64(repo, "shared", "settings.base.json");
9527
- if (!existsSync48(basePath)) return;
9528
- const settingsPath = join64(claudeHome(), "settings.json");
9529
- if (!existsSync48(settingsPath)) return;
9758
+ const basePath = join67(repo, "shared", "settings.base.json");
9759
+ if (!existsSync49(basePath)) return;
9760
+ const settingsPath = join67(claudeHome(), "settings.json");
9761
+ if (!existsSync49(settingsPath)) return;
9530
9762
  try {
9531
9763
  const base = readJson(basePath);
9532
- const hostPath = join64(repo, "hosts", `${HOST}.json`);
9533
- const overrides = existsSync48(hostPath) ? readJson(hostPath) : {};
9764
+ const hostPath = join67(repo, "hosts", `${HOST}.json`);
9765
+ const overrides = existsSync49(hostPath) ? readJson(hostPath) : {};
9534
9766
  const merged = deepMerge(base, overrides);
9535
9767
  const settings = readJson(settingsPath);
9536
9768
  const { ahead } = classifySettingsDrift(merged, settings);
@@ -9544,12 +9776,12 @@ function reportSettingsAheadDrift(repo) {
9544
9776
  }
9545
9777
  }
9546
9778
 
9547
- // src/commands.push.guards.ts
9548
- init_push_checks();
9779
+ // src/commands/push/guards.ts
9780
+ init_checks();
9549
9781
  init_utils();
9550
- import { join as join65, relative as relative10 } from "node:path";
9782
+ import { join as join68, relative as relative10 } from "node:path";
9551
9783
  function guardGitlinks(repo) {
9552
- const gitlinks = findGitlinks(join65(repo, "shared"));
9784
+ const gitlinks = findGitlinks(join68(repo, "shared"));
9553
9785
  if (gitlinks.length === 0) return;
9554
9786
  for (const p of gitlinks) {
9555
9787
  const rel = relative10(repo, p).replaceAll("\\", "/");
@@ -9576,10 +9808,10 @@ function guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule) {
9576
9808
  }
9577
9809
  }
9578
9810
 
9579
- // src/commands.push.steps.ts
9811
+ // src/commands/push/steps.ts
9580
9812
  init_config();
9581
9813
 
9582
- // src/push-global-config.ts
9814
+ // src/commands/push/global-config.ts
9583
9815
  init_config();
9584
9816
  import { execFileSync as execFileSync23 } from "node:child_process";
9585
9817
  var STATUS_LABELS = {
@@ -9651,19 +9883,19 @@ function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
9651
9883
  return changes;
9652
9884
  }
9653
9885
 
9654
- // src/commands.push.steps.ts
9655
- init_push_leak_verdict();
9886
+ // src/commands/push/steps.ts
9887
+ init_leak_verdict();
9656
9888
 
9657
- // src/push-preview.ts
9889
+ // src/commands/push/preview.ts
9658
9890
  init_color();
9659
9891
  init_config();
9660
9892
  init_config_sharedDirs_guard();
9661
9893
  import { randomBytes as randomBytes4 } from "node:crypto";
9662
- import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync20, rmSync as rmSync20 } from "node:fs";
9894
+ import { copyFileSync, existsSync as existsSync50, lstatSync as lstatSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync20, rmSync as rmSync20 } from "node:fs";
9663
9895
  import { homedir as homedir7 } from "node:os";
9664
- import { join as join66, relative as relative11, sep as sep14 } from "node:path";
9665
- init_push_leak_verdict();
9666
- init_push_gitleaks();
9896
+ import { join as join69, relative as relative11, sep as sep14 } from "node:path";
9897
+ init_leak_verdict();
9898
+ init_gitleaks();
9667
9899
  init_utils_fs();
9668
9900
  init_utils_json();
9669
9901
  var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
@@ -9673,7 +9905,7 @@ function stageSessionDir(localDir, dstDir, changed) {
9673
9905
  const matching = [...changed].filter((p) => p.startsWith(prefix));
9674
9906
  if (matching.length === 0) return false;
9675
9907
  for (const src of matching) {
9676
- copyFileAtomic(src, join66(dstDir, relative11(localDir, src)));
9908
+ copyFileAtomic(src, join69(dstDir, relative11(localDir, src)));
9677
9909
  }
9678
9910
  return true;
9679
9911
  }
@@ -9689,14 +9921,14 @@ function stageSessions(tmpRoot, map, changed) {
9689
9921
  if (!p || p === "TBD") continue;
9690
9922
  reverse.set(encodePath(p), logical);
9691
9923
  }
9692
- const localProjects = join66(claudeHome(), "projects");
9693
- if (!existsSync49(localProjects)) return 0;
9924
+ const localProjects = join69(claudeHome(), "projects");
9925
+ if (!existsSync50(localProjects)) return 0;
9694
9926
  let staged = 0;
9695
9927
  for (const dir of readdirSync20(localProjects)) {
9696
9928
  const logical = reverse.get(dir);
9697
9929
  if (!logical) continue;
9698
- const localDir = join66(localProjects, dir);
9699
- const dstDir = join66(tmpRoot, "shared", "projects", logical);
9930
+ const localDir = join69(localProjects, dir);
9931
+ const dstDir = join69(tmpRoot, "shared", "projects", logical);
9700
9932
  if (stageSessionDir(localDir, dstDir, changed)) staged++;
9701
9933
  }
9702
9934
  return staged;
@@ -9710,11 +9942,11 @@ function stageExtras(tmpRoot, map) {
9710
9942
  assertSafeLogical(logical);
9711
9943
  const localRoot = map.projects[logical]?.[HOST];
9712
9944
  if (!localRoot || localRoot === "TBD") continue;
9713
- for (const dirname15 of dirnames) {
9714
- if (!whitelist.includes(dirname15)) continue;
9715
- const src = join66(localRoot, dirname15);
9716
- if (!existsSync49(src)) continue;
9717
- const dst = join66(tmpRoot, "shared", "extras", logical, dirname15);
9945
+ for (const dirname16 of dirnames) {
9946
+ if (!whitelist.includes(dirname16)) continue;
9947
+ const src = join69(localRoot, dirname16);
9948
+ if (!existsSync50(src)) continue;
9949
+ const dst = join69(tmpRoot, "shared", "extras", logical, dirname16);
9718
9950
  copyExtras(src, dst);
9719
9951
  staged++;
9720
9952
  }
@@ -9722,19 +9954,19 @@ function stageExtras(tmpRoot, map) {
9722
9954
  return staged;
9723
9955
  }
9724
9956
  function stageSkills(tmpRoot) {
9725
- const localSkills = join66(claudeHome(), "skills");
9957
+ const localSkills = join69(claudeHome(), "skills");
9726
9958
  const stat = lstatSync22(localSkills, { throwIfNoEntry: false });
9727
9959
  if (stat === void 0 || stat.isSymbolicLink()) return 0;
9728
9960
  const names = readdirSync20(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
9729
9961
  if (names.length === 0) return 0;
9730
- copySkillsPush(localSkills, join66(tmpRoot, "shared", "skills"));
9962
+ copySkillsPush(localSkills, join69(tmpRoot, "shared", "skills"));
9731
9963
  return names.length;
9732
9964
  }
9733
9965
  function previewPushLeaks(map, opts = {}) {
9734
- const cacheDir = join66(homedir7(), ".cache", "claude-nomad");
9966
+ const cacheDir = join69(homedir7(), ".cache", "claude-nomad");
9735
9967
  mkdirSync15(cacheDir, { recursive: true });
9736
9968
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
9737
- const tmpRoot = join66(cacheDir, `push-preview-tree-${stamp}`);
9969
+ const tmpRoot = join69(cacheDir, `push-preview-tree-${stamp}`);
9738
9970
  try {
9739
9971
  const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
9740
9972
  const extrasCount = stageExtras(tmpRoot, map);
@@ -9742,9 +9974,9 @@ function previewPushLeaks(map, opts = {}) {
9742
9974
  if (sessionCount + extrasCount + skillCount === 0) {
9743
9975
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
9744
9976
  }
9745
- const ignoreFile = join66(repoHome(), ".gitleaksignore");
9746
- if (existsSync49(ignoreFile)) {
9747
- copyFileSync(ignoreFile, join66(tmpRoot, ".gitleaksignore"));
9977
+ const ignoreFile = join69(repoHome(), ".gitleaksignore");
9978
+ if (existsSync50(ignoreFile)) {
9979
+ copyFileSync(ignoreFile, join69(tmpRoot, ".gitleaksignore"));
9748
9980
  }
9749
9981
  let findings;
9750
9982
  try {
@@ -9761,7 +9993,7 @@ function previewPushLeaks(map, opts = {}) {
9761
9993
  }
9762
9994
  }
9763
9995
 
9764
- // src/commands.push.steps.ts
9996
+ // src/commands/push/steps.ts
9765
9997
  init_utils();
9766
9998
  async function commitAndPush(st, ts, map, resolution, repo, newManifest, render) {
9767
9999
  gitOrFatal(["add", "-A"], "git add", repo);
@@ -9813,8 +10045,8 @@ function runDryRunPreview(st, map, repo, selection) {
9813
10045
  if (verdict.recovery !== null) fail(verdict.recovery);
9814
10046
  }
9815
10047
 
9816
- // src/commands.push.ts
9817
- init_push_checks();
10048
+ // src/commands/push/push.ts
10049
+ init_checks();
9818
10050
  init_utils();
9819
10051
  init_utils_fs();
9820
10052
  function aheadOfUpstream(repo) {
@@ -9872,7 +10104,7 @@ async function runPushCore(opts = {}) {
9872
10104
  const scannerVersion = probeGitleaks();
9873
10105
  const configHash = computeConfigHash();
9874
10106
  const old = readManifest(manifestPath());
9875
- const mapPath = join67(repo, "path-map.json");
10107
+ const mapPath = join70(repo, "path-map.json");
9876
10108
  const { map, selection, newManifest } = loadSelectionForPush(
9877
10109
  mapPath,
9878
10110
  old,
@@ -9925,7 +10157,7 @@ async function cmdPush(opts = {}) {
9925
10157
  opts.allowRule
9926
10158
  );
9927
10159
  const repo = repoHome();
9928
- if (!existsSync50(repo)) die(`repo not cloned at ${repo}`);
10160
+ if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
9929
10161
  const handle = acquireLock("push");
9930
10162
  if (handle === null) process.exit(0);
9931
10163
  try {
@@ -9942,9 +10174,9 @@ async function cmdPush(opts = {}) {
9942
10174
  }
9943
10175
  }
9944
10176
 
9945
- // src/commands.sync.ts
9946
- import { existsSync as existsSync51 } from "node:fs";
9947
- import { join as join68 } from "node:path";
10177
+ // src/commands/sync.ts
10178
+ import { existsSync as existsSync52 } from "node:fs";
10179
+ import { join as join71 } from "node:path";
9948
10180
  init_config();
9949
10181
  init_color();
9950
10182
  init_utils();
@@ -10085,8 +10317,8 @@ async function cmdSync(opts = {}) {
10085
10317
  const dryRun = opts.dryRun === true;
10086
10318
  const verbose = opts.verbose === true;
10087
10319
  const repo = repoHome();
10088
- if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
10089
- if (!existsSync51(join68(repo, "shared", "settings.base.json"))) {
10320
+ if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
10321
+ if (!existsSync52(join71(repo, "shared", "settings.base.json"))) {
10090
10322
  die("repo not initialized; run 'nomad init' to scaffold");
10091
10323
  }
10092
10324
  const handle = acquireLock("sync");
@@ -10109,9 +10341,11 @@ async function cmdSync(opts = {}) {
10109
10341
  }
10110
10342
  }
10111
10343
 
10112
- // src/commands.update.ts
10113
- import { execFileSync as execFileSync24 } from "node:child_process";
10344
+ // src/commands/update.ts
10114
10345
  init_utils();
10346
+ import { execFileSync as execFileSync24 } from "node:child_process";
10347
+ var RELEASE_NOTES_TAG_URL = "https://github.com/funkadelic/claude-nomad/releases/tag";
10348
+ var SEMVER = /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/;
10115
10349
  function readInstalledVersion(run = execFileSync24) {
10116
10350
  const isWin = process.platform === "win32";
10117
10351
  try {
@@ -10123,6 +10357,10 @@ function readInstalledVersion(run = execFileSync24) {
10123
10357
  return null;
10124
10358
  }
10125
10359
  }
10360
+ function readOwnVersion(run) {
10361
+ const version = readInstalledVersion(run);
10362
+ return version !== null && SEMVER.test(version) ? version : null;
10363
+ }
10126
10364
  function cmdUpdate(currentVersion, run = execFileSync24) {
10127
10365
  console.log(`Updating claude-nomad v${currentVersion}...`);
10128
10366
  const isWin = process.platform === "win32";
@@ -10146,26 +10384,27 @@ function cmdUpdate(currentVersion, run = execFileSync24) {
10146
10384
  ${detail}` : "";
10147
10385
  throw new NomadFatal(`npm update -g claude-nomad failed: ${e.message}${suffix}`);
10148
10386
  }
10149
- const version = readInstalledVersion(run);
10387
+ const version = readOwnVersion(run);
10150
10388
  if (!version) {
10151
10389
  console.log('Update complete. Run "nomad --version" to confirm the new version.');
10152
- } else if (version === currentVersion) {
10153
- console.log(`claude-nomad is already at the latest version (v${version}).`);
10154
- } else {
10155
- console.log(`claude-nomad is now at v${version}`);
10390
+ return;
10156
10391
  }
10392
+ console.log(
10393
+ version === currentVersion ? `claude-nomad is already at the latest version (v${version}).` : `claude-nomad is now at v${version}`
10394
+ );
10395
+ console.log(`Release notes: ${RELEASE_NOTES_TAG_URL}/v${version}`);
10157
10396
  }
10158
10397
 
10159
10398
  // src/nomad.ts
10160
10399
  init_config();
10161
10400
 
10162
- // src/crash-report.write.ts
10401
+ // src/core/crash-report.write.ts
10163
10402
  init_config();
10164
10403
  import { randomBytes as randomBytes5 } from "node:crypto";
10165
10404
  import { mkdirSync as mkdirSync16, readdirSync as readdirSync21, statSync as statSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
10166
- import { join as join70 } from "node:path";
10405
+ import { join as join73 } from "node:path";
10167
10406
 
10168
- // src/crash-report.ts
10407
+ // src/core/crash-report.ts
10169
10408
  var CRASH_MAX_STACK_LINES = 50;
10170
10409
  var CRASH_MAX_ARGV = 32;
10171
10410
  var CRASH_MAX_ARGV_TOKEN_LENGTH = 200;
@@ -10245,18 +10484,18 @@ function buildCrashReport(input) {
10245
10484
  return truncateToBytes(scrubbed, CRASH_MAX_REPORT_BYTES);
10246
10485
  }
10247
10486
 
10248
- // src/crash-report.redact.ts
10487
+ // src/core/crash-report.redact.ts
10249
10488
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync21, writeFileSync as writeFileSync10 } from "node:fs";
10250
10489
  import { tmpdir as tmpdir2 } from "node:os";
10251
- import { join as join69 } from "node:path";
10252
- init_push_gitleaks_scan();
10490
+ import { join as join72 } from "node:path";
10491
+ init_gitleaks_scan();
10253
10492
  var CRASH_SCAN_TIMEOUT_MS = 3e3;
10254
10493
  var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
10255
10494
  function redactWithGitleaks(text, scan = scanFile) {
10256
10495
  let dir;
10257
10496
  try {
10258
- dir = mkdtempSync3(join69(tmpdir2(), "nomad-crash-scan-"));
10259
- const tmp = join69(dir, "crash.txt");
10497
+ dir = mkdtempSync3(join72(tmpdir2(), "nomad-crash-scan-"));
10498
+ const tmp = join72(dir, "crash.txt");
10260
10499
  writeFileSync10(tmp, text, { mode: 384 });
10261
10500
  const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
10262
10501
  if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
@@ -10273,7 +10512,7 @@ function redactWithGitleaks(text, scan = scanFile) {
10273
10512
  }
10274
10513
  }
10275
10514
 
10276
- // src/crash-report.write.ts
10515
+ // src/core/crash-report.write.ts
10277
10516
  init_utils_fs();
10278
10517
  init_utils();
10279
10518
  var CRASH_RETENTION_KEEP = 20;
@@ -10281,7 +10520,7 @@ function listCrashFiles(dir = crashDir()) {
10281
10520
  try {
10282
10521
  return readdirSync21(dir).flatMap((name) => {
10283
10522
  try {
10284
- return [{ name, mtimeMs: statSync14(join70(dir, name)).mtimeMs }];
10523
+ return [{ name, mtimeMs: statSync14(join73(dir, name)).mtimeMs }];
10285
10524
  } catch {
10286
10525
  return [];
10287
10526
  }
@@ -10295,7 +10534,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
10295
10534
  const targets = prunableByCount(files, keep);
10296
10535
  for (const name of targets) {
10297
10536
  try {
10298
- unlinkSync2(join70(dir, name));
10537
+ unlinkSync2(join73(dir, name));
10299
10538
  } catch {
10300
10539
  }
10301
10540
  }
@@ -10303,7 +10542,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
10303
10542
  function writeCrashReport(text, dir = crashDir()) {
10304
10543
  mkdirSync16(dir, { recursive: true, mode: 448 });
10305
10544
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes5(4).toString("hex")}`;
10306
- const path = join70(dir, `crash-${stamp}.txt`);
10545
+ const path = join73(dir, `crash-${stamp}.txt`);
10307
10546
  writeFileSync11(path, text, { mode: 384 });
10308
10547
  pruneCrashDir(dir);
10309
10548
  return path;
@@ -10333,20 +10572,20 @@ function handleCrash(err, argv, opts) {
10333
10572
  }
10334
10573
  }
10335
10574
 
10336
- // src/diff.ts
10575
+ // src/commands/diff.ts
10337
10576
  init_config();
10338
- import { existsSync as existsSync52 } from "node:fs";
10339
- import { join as join71 } from "node:path";
10577
+ import { existsSync as existsSync53 } from "node:fs";
10578
+ import { join as join74 } from "node:path";
10340
10579
  init_utils();
10341
10580
  init_utils_fs();
10342
10581
  init_utils_json();
10343
10582
  function cmdDiff() {
10344
10583
  try {
10345
10584
  const repo = repoHome();
10346
- if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
10585
+ if (!existsSync53(repo)) die(`repo not cloned at ${repo}`);
10347
10586
  const ts = freshBackupTs(backupBase());
10348
- const mapPath = join71(repo, "path-map.json");
10349
- const map = existsSync52(mapPath) ? readPathMap(mapPath) : { projects: {} };
10587
+ const mapPath = join74(repo, "path-map.json");
10588
+ const map = existsSync53(mapPath) ? readPathMap(mapPath) : { projects: {} };
10350
10589
  divergenceCheckExtras(ts);
10351
10590
  computePreview(ts, map, "diff");
10352
10591
  } catch (err) {
@@ -10359,12 +10598,12 @@ function cmdDiff() {
10359
10598
  }
10360
10599
  }
10361
10600
 
10362
- // src/init.ts
10601
+ // src/commands/init/init.ts
10363
10602
  init_config();
10364
- import { existsSync as existsSync54, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
10365
- import { join as join73 } from "node:path";
10603
+ import { existsSync as existsSync55, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
10604
+ import { join as join76 } from "node:path";
10366
10605
 
10367
- // src/init.gh-onboard.ts
10606
+ // src/commands/init/gh-onboard.ts
10368
10607
  init_config();
10369
10608
  import { execFileSync as execFileSync25 } from "node:child_process";
10370
10609
  init_utils();
@@ -10439,44 +10678,44 @@ function ensureOriginRepo(repoName, run = execFileSync25) {
10439
10678
  log(`created private repo ${owner}/${repoName} and wired origin`);
10440
10679
  }
10441
10680
 
10442
- // src/init.snapshot.ts
10681
+ // src/commands/init/snapshot.ts
10443
10682
  init_config();
10444
10683
  init_utils();
10445
10684
  init_utils_fs();
10446
10685
  init_utils_json();
10447
- import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync53, rmSync as rmSync22, statSync as statSync15 } from "node:fs";
10448
- import { join as join72 } from "node:path";
10686
+ import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync54, rmSync as rmSync22, statSync as statSync15 } from "node:fs";
10687
+ import { join as join75 } from "node:path";
10449
10688
  function snapshotIntoShared(map) {
10450
10689
  const repo = repoHome();
10451
10690
  const claude = claudeHome();
10452
10691
  for (const name of allSharedLinks(map)) {
10453
- const src = join72(claude, name);
10454
- if (!existsSync53(src)) continue;
10455
- const dst = join72(repo, "shared", name);
10692
+ const src = join75(claude, name);
10693
+ if (!existsSync54(src)) continue;
10694
+ const dst = join75(repo, "shared", name);
10456
10695
  if (statSync15(src).isDirectory()) {
10457
- const gk = join72(dst, ".gitkeep");
10458
- if (existsSync53(gk)) rmSync22(gk);
10696
+ const gk = join75(dst, ".gitkeep");
10697
+ if (existsSync54(gk)) rmSync22(gk);
10459
10698
  cpSync9(src, dst, { recursive: true, force: false, errorOnExist: true });
10460
10699
  } else {
10461
10700
  copyFileSync2(src, dst);
10462
10701
  }
10463
10702
  log(`snapshotted shared/${name} from ${src}`);
10464
10703
  }
10465
- const userSettings = join72(claude, "settings.json");
10466
- if (existsSync53(userSettings)) {
10704
+ const userSettings = join75(claude, "settings.json");
10705
+ if (existsSync54(userSettings)) {
10467
10706
  let parsed;
10468
10707
  try {
10469
10708
  parsed = readJson(userSettings);
10470
10709
  } catch (err) {
10471
10710
  return die(`malformed ${userSettings}: ${err.message}`);
10472
10711
  }
10473
- const hostFile = join72(repo, "hosts", `${HOST}.json`);
10712
+ const hostFile = join75(repo, "hosts", `${HOST}.json`);
10474
10713
  writeJsonAtomic(hostFile, parsed);
10475
10714
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
10476
10715
  }
10477
10716
  }
10478
10717
 
10479
- // src/init.ts
10718
+ // src/commands/init/init.ts
10480
10719
  init_utils();
10481
10720
  init_utils_fs();
10482
10721
  var SHARED_CLAUDE_MD = "<!-- claude-nomad shared CLAUDE.md; symlinked into ~/.claude/CLAUDE.md by nomad pull -->\n";
@@ -10484,14 +10723,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
10484
10723
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
10485
10724
  function preflightConflict(repoHome2) {
10486
10725
  const candidates = [
10487
- join73(repoHome2, "shared", "settings.base.json"),
10488
- join73(repoHome2, "shared", "CLAUDE.md"),
10489
- join73(repoHome2, "path-map.json"),
10490
- join73(repoHome2, "hosts"),
10491
- join73(repoHome2, "shared")
10726
+ join76(repoHome2, "shared", "settings.base.json"),
10727
+ join76(repoHome2, "shared", "CLAUDE.md"),
10728
+ join76(repoHome2, "path-map.json"),
10729
+ join76(repoHome2, "hosts"),
10730
+ join76(repoHome2, "shared")
10492
10731
  ];
10493
10732
  for (const c of candidates) {
10494
- if (existsSync54(c)) return c;
10733
+ if (existsSync55(c)) return c;
10495
10734
  }
10496
10735
  return null;
10497
10736
  }
@@ -10509,27 +10748,27 @@ function cmdInit(opts = {}) {
10509
10748
  die(`already initialized; refusing to clobber ${conflict}`);
10510
10749
  }
10511
10750
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
10512
- mkdirSync17(join73(repo, "shared"), { recursive: true });
10513
- mkdirSync17(join73(repo, "hosts"), { recursive: true });
10751
+ mkdirSync17(join76(repo, "shared"), { recursive: true });
10752
+ mkdirSync17(join76(repo, "hosts"), { recursive: true });
10514
10753
  for (const name of SHARED_KEEP_DIRS) {
10515
- mkdirSync17(join73(repo, "shared", name), { recursive: true });
10754
+ mkdirSync17(join76(repo, "shared", name), { recursive: true });
10516
10755
  }
10517
- const userClaudeMd = join73(claude, "CLAUDE.md");
10518
- if (!snapshot || !existsSync54(userClaudeMd)) {
10519
- writeFileSync12(join73(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
10756
+ const userClaudeMd = join76(claude, "CLAUDE.md");
10757
+ if (!snapshot || !existsSync55(userClaudeMd)) {
10758
+ writeFileSync12(join76(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
10520
10759
  item("created shared/CLAUDE.md");
10521
10760
  }
10522
10761
  for (const name of SHARED_KEEP_DIRS) {
10523
- writeFileSync12(join73(repo, "shared", name, ".gitkeep"), "");
10762
+ writeFileSync12(join76(repo, "shared", name, ".gitkeep"), "");
10524
10763
  item(`created shared/${name}/.gitkeep`);
10525
10764
  }
10526
- writeFileSync12(join73(repo, "hosts", ".gitkeep"), "");
10765
+ writeFileSync12(join76(repo, "hosts", ".gitkeep"), "");
10527
10766
  item("created hosts/.gitkeep");
10528
- writeJsonAtomic(join73(repo, "shared", "settings.base.json"), {});
10767
+ writeJsonAtomic(join76(repo, "shared", "settings.base.json"), {});
10529
10768
  item("created shared/settings.base.json");
10530
- writeJsonAtomic(join73(repo, "path-map.json"), { projects: {} });
10769
+ writeJsonAtomic(join76(repo, "path-map.json"), { projects: {} });
10531
10770
  item("created path-map.json");
10532
- writeFileSync12(join73(repo, ".gitattributes"), GITATTRIBUTES);
10771
+ writeFileSync12(join76(repo, ".gitattributes"), GITATTRIBUTES);
10533
10772
  item("created .gitattributes");
10534
10773
  if (snapshot) {
10535
10774
  snapshotIntoShared({ projects: {} });
@@ -10592,14 +10831,14 @@ function maybeDisableRepoActions(repoHome2, run) {
10592
10831
  }
10593
10832
  }
10594
10833
 
10595
- // src/init.prompt.ts
10834
+ // src/commands/init/prompt.ts
10596
10835
  init_config();
10597
10836
  init_utils();
10598
- import { existsSync as existsSync55, readdirSync as readdirSync22, statSync as statSync16 } from "node:fs";
10599
- import { join as join74 } from "node:path";
10837
+ import { existsSync as existsSync56, readdirSync as readdirSync22, statSync as statSync16 } from "node:fs";
10838
+ import { join as join77 } from "node:path";
10600
10839
  import { createInterface as createInterface3 } from "node:readline/promises";
10601
10840
  function nonEmptyExists(path) {
10602
- if (!existsSync55(path)) return false;
10841
+ if (!existsSync56(path)) return false;
10603
10842
  try {
10604
10843
  if (statSync16(path).isDirectory()) return readdirSync22(path).length > 0;
10605
10844
  return true;
@@ -10608,8 +10847,8 @@ function nonEmptyExists(path) {
10608
10847
  }
10609
10848
  }
10610
10849
  function hasExistingClaudeConfig(claudeHome2) {
10611
- if (existsSync55(join74(claudeHome2, "settings.json"))) return true;
10612
- return SHARED_LINKS.some((name) => nonEmptyExists(join74(claudeHome2, name)));
10850
+ if (existsSync56(join77(claudeHome2, "settings.json"))) return true;
10851
+ return SHARED_LINKS.some((name) => nonEmptyExists(join77(claudeHome2, name)));
10613
10852
  }
10614
10853
  async function confirmSnapshotDefault(claudeHome2) {
10615
10854
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -10633,7 +10872,7 @@ async function resolveSnapshotChoice(flagSnapshot, claudeHome2, confirm = confir
10633
10872
  return confirm(claudeHome2);
10634
10873
  }
10635
10874
 
10636
- // src/nomad.dispatch.helpers.ts
10875
+ // src/cli/dispatch.helpers.ts
10637
10876
  var REJECT = { ok: false, advance: 0 };
10638
10877
  function applyBool(seen, set) {
10639
10878
  if (seen) return REJECT;
@@ -10646,7 +10885,7 @@ function extractFlagValue(argv, i) {
10646
10885
  return val;
10647
10886
  }
10648
10887
 
10649
- // src/nomad.dispatch.clean.ts
10888
+ // src/cli/dispatch.clean.ts
10650
10889
  function applyOlderThan(argv, i, st) {
10651
10890
  if (st.olderThan !== void 0) return REJECT;
10652
10891
  const val = extractFlagValue(argv, i);
@@ -10693,7 +10932,7 @@ function parseCleanArgs(argv) {
10693
10932
  return { dryRun: st.dryRun, olderThan: st.olderThan, keep: st.keep };
10694
10933
  }
10695
10934
 
10696
- // src/nomad.dispatch.capture-settings.ts
10935
+ // src/cli/dispatch.capture-settings.ts
10697
10936
  function parseCaptureSettingsArgs(argv) {
10698
10937
  let host = false;
10699
10938
  let dryRun = false;
@@ -10718,7 +10957,7 @@ function parseCaptureSettingsArgs(argv) {
10718
10957
  return { host, dryRun, yes };
10719
10958
  }
10720
10959
 
10721
- // src/nomad.dispatch.eject.ts
10960
+ // src/cli/dispatch.eject.ts
10722
10961
  function parseEjectArgs(argv) {
10723
10962
  let dryRun = false;
10724
10963
  let i = 3;
@@ -10735,7 +10974,7 @@ function parseEjectArgs(argv) {
10735
10974
  return { dryRun };
10736
10975
  }
10737
10976
 
10738
- // src/nomad.dispatch.ts
10977
+ // src/cli/dispatch.ts
10739
10978
  function applyInitToken(argv, i, st) {
10740
10979
  const token = argv[i];
10741
10980
  if (token === "--snapshot") {
@@ -10808,7 +11047,7 @@ function parseRedactArgs(argv) {
10808
11047
  return { id, rule, dryRun };
10809
11048
  }
10810
11049
 
10811
- // src/nomad.dispatch.allow.ts
11050
+ // src/cli/dispatch.allow.ts
10812
11051
  function parseAllowArgs(argv) {
10813
11052
  const positionals = argv.slice(3);
10814
11053
  if (positionals.length === 0) return null;
@@ -10818,7 +11057,7 @@ function parseAllowArgs(argv) {
10818
11057
  return positionals;
10819
11058
  }
10820
11059
 
10821
- // src/nomad.dispatch.pull.ts
11060
+ // src/cli/dispatch.pull.ts
10822
11061
  function parsePullArgs(argv) {
10823
11062
  let dryRun = false;
10824
11063
  let forceRemote = false;
@@ -10840,7 +11079,7 @@ function parsePullArgs(argv) {
10840
11079
  return { dryRun, forceRemote };
10841
11080
  }
10842
11081
 
10843
- // src/nomad.dispatch.push.ts
11082
+ // src/cli/dispatch.push.ts
10844
11083
  var RULE_ID_RE = /^\w[\w-]*$/;
10845
11084
  function applyAllow(argv, i, st) {
10846
11085
  if (st.allowRule !== void 0) return REJECT;
@@ -10893,7 +11132,7 @@ function parsePushArgs(argv) {
10893
11132
  };
10894
11133
  }
10895
11134
 
10896
- // src/nomad.dispatch.sync.ts
11135
+ // src/cli/dispatch.sync.ts
10897
11136
  function parseSyncArgs(argv) {
10898
11137
  let dryRun = false;
10899
11138
  let verbose = false;
@@ -10916,7 +11155,7 @@ function parseSyncArgs(argv) {
10916
11155
  // package.json
10917
11156
  var package_default = {
10918
11157
  name: "claude-nomad",
10919
- version: "0.67.3",
11158
+ version: "0.69.0",
10920
11159
  type: "module",
10921
11160
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
10922
11161
  keywords: [
@@ -10983,7 +11222,8 @@ var package_default = {
10983
11222
  overrides: {
10984
11223
  "@conventional-changelog/git-client": {
10985
11224
  "conventional-commits-parser": "^7.0.0"
10986
- }
11225
+ },
11226
+ "smol-toml": "^1.7.1"
10987
11227
  },
10988
11228
  devDependencies: {
10989
11229
  "@commitlint/cli": "^21.0.1",
@@ -10992,7 +11232,7 @@ var package_default = {
10992
11232
  "@stryker-mutator/core": "10.0.0",
10993
11233
  "@stryker-mutator/vitest-runner": "10.0.0",
10994
11234
  "@types/node": "^22.0.0",
10995
- "@vitest/coverage-v8": "^4.1.6",
11235
+ "@vitest/coverage-v8": "^5.0.0",
10996
11236
  "conventional-changelog-conventionalcommits": "^10.2.1",
10997
11237
  diff: "^9.0.0",
10998
11238
  esbuild: "^0.28.0",
@@ -11008,11 +11248,11 @@ var package_default = {
11008
11248
  prettier: "^3.8.3",
11009
11249
  typescript: "^6.0.3",
11010
11250
  "typescript-eslint": "^8.59.4",
11011
- vitest: "^4.1.6"
11251
+ vitest: "^5.0.0"
11012
11252
  }
11013
11253
  };
11014
11254
 
11015
- // src/nomad.help.ts
11255
+ // src/cli/help.ts
11016
11256
  var DESC_COL = 26;
11017
11257
  var row = (label, desc) => label.padEnd(DESC_COL) + desc;
11018
11258
  var cont = (text) => " ".repeat(DESC_COL) + text;
@@ -11160,19 +11400,19 @@ var DEFAULT_HELP = [
11160
11400
  "every pull)."
11161
11401
  ].join("\n");
11162
11402
 
11163
- // src/resume.ts
11403
+ // src/sync/resume.ts
11164
11404
  init_config();
11165
11405
  init_utils();
11166
11406
  init_utils_json();
11167
- import { existsSync as existsSync56, readFileSync as readFileSync21, readdirSync as readdirSync23 } from "node:fs";
11168
- import { join as join75 } from "node:path";
11407
+ import { existsSync as existsSync57, readFileSync as readFileSync21, readdirSync as readdirSync23 } from "node:fs";
11408
+ import { join as join78 } from "node:path";
11169
11409
  function resumeCmd(sessionId) {
11170
11410
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
11171
11411
  fail(`invalid session id: ${sessionId}`);
11172
11412
  process.exit(1);
11173
11413
  }
11174
- const projectsRoot = join75(claudeHome(), "projects");
11175
- if (!existsSync56(projectsRoot)) {
11414
+ const projectsRoot = join78(claudeHome(), "projects");
11415
+ if (!existsSync57(projectsRoot)) {
11176
11416
  fail(`${projectsRoot} does not exist`);
11177
11417
  process.exit(1);
11178
11418
  }
@@ -11186,8 +11426,8 @@ function resumeCmd(sessionId) {
11186
11426
  fail(`no cwd field found in ${jsonlPath}`);
11187
11427
  process.exit(1);
11188
11428
  }
11189
- const mapPath = join75(repoHome(), "path-map.json");
11190
- if (!existsSync56(mapPath)) {
11429
+ const mapPath = join78(repoHome(), "path-map.json");
11430
+ if (!existsSync57(mapPath)) {
11191
11431
  fail("path-map.json missing");
11192
11432
  process.exit(1);
11193
11433
  }
@@ -11210,8 +11450,8 @@ function resumeCmd(sessionId) {
11210
11450
  }
11211
11451
  function findTranscriptPath(projectsRoot, sessionId) {
11212
11452
  for (const dir of readdirSync23(projectsRoot)) {
11213
- const candidate = join75(projectsRoot, dir, `${sessionId}.jsonl`);
11214
- if (existsSync56(candidate)) return candidate;
11453
+ const candidate = join78(projectsRoot, dir, `${sessionId}.jsonl`);
11454
+ if (existsSync57(candidate)) return candidate;
11215
11455
  }
11216
11456
  return null;
11217
11457
  }
@@ -11244,7 +11484,7 @@ function shQuote(s) {
11244
11484
  return `'${escaped}'`;
11245
11485
  }
11246
11486
 
11247
- // src/user-abort.ts
11487
+ // src/core/user-abort.ts
11248
11488
  var ABORT_NAMES = /* @__PURE__ */ new Set(["AbortError", "ExitPromptError"]);
11249
11489
  var ABORT_CODE = "ABORT_ERR";
11250
11490
  function isUserAbort(err) {