claude-nomad 0.55.0 → 0.56.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
@@ -410,6 +410,9 @@ function repoHome() {
410
410
  function backupBase() {
411
411
  return join(home(), ".cache", "claude-nomad", "backup");
412
412
  }
413
+ function manifestPath() {
414
+ return join(home(), ".cache", "claude-nomad", `push-manifest-${encodeURIComponent(HOST)}.json`);
415
+ }
413
416
  function allSharedLinks(map) {
414
417
  const extras = [];
415
418
  for (const entry of map.sharedDirs ?? []) {
@@ -423,7 +426,7 @@ function allSharedLinks(map) {
423
426
  }
424
427
  return [...SHARED_LINKS, ...extras];
425
428
  }
426
- var SETTINGS_SCHEMA_URL, NPM_REGISTRY_LATEST_URL, GITLEAKS_PINNED_VERSION, HOST, SHARED_LINKS, GSD_PREFIX, GSD_DROPPED_NAMES, SUPPORTED_EXTRAS, ALWAYS_NEVER_SYNC, PUSH_ALLOWED_STATIC;
429
+ var SETTINGS_SCHEMA_URL, NPM_REGISTRY_LATEST_URL, GITLEAKS_PINNED_VERSION, GITLEAKS_SCAN_TIMEOUT_MS, HOST, SHARED_LINKS, GSD_PREFIX, GSD_DROPPED_NAMES, SUPPORTED_EXTRAS, ALWAYS_NEVER_SYNC, PUSH_ALLOWED_STATIC;
427
430
  var init_config = __esm({
428
431
  "src/config.ts"() {
429
432
  "use strict";
@@ -434,6 +437,7 @@ var init_config = __esm({
434
437
  SETTINGS_SCHEMA_URL = "https://json.schemastore.org/claude-code-settings.json";
435
438
  NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org/claude-nomad/latest";
436
439
  GITLEAKS_PINNED_VERSION = "8.30.1";
440
+ GITLEAKS_SCAN_TIMEOUT_MS = 9e5;
437
441
  HOST = (process.env.NOMAD_HOST || hostname()).toLowerCase();
438
442
  SHARED_LINKS = ["CLAUDE.md", "commands", "rules", "my-statusline.cjs"];
439
443
  GSD_PREFIX = "gsd-";
@@ -523,13 +527,23 @@ function sortKeysDeep(value) {
523
527
  }
524
528
  return value;
525
529
  }
526
- var encodePath;
530
+ var ENCODE_MAX, hashPath, encodePath;
527
531
  var init_utils_json = __esm({
528
532
  "src/utils.json.ts"() {
529
533
  "use strict";
530
534
  init_config();
531
535
  init_utils();
532
- encodePath = (absPath) => absPath.replaceAll("/", "-");
536
+ ENCODE_MAX = 200;
537
+ hashPath = (s) => {
538
+ let h2 = 0;
539
+ for (let i = 0; i < s.length; i++) h2 = (h2 << 5) - h2 + s.charCodeAt(i) | 0;
540
+ return h2;
541
+ };
542
+ encodePath = (absPath) => {
543
+ const enc = absPath.replace(/[^a-zA-Z0-9]/g, "-");
544
+ if (enc.length <= ENCODE_MAX) return enc;
545
+ return `${enc.slice(0, ENCODE_MAX)}-${Math.abs(hashPath(absPath)).toString(36)}`;
546
+ };
533
547
  }
534
548
  });
535
549
 
@@ -769,7 +783,7 @@ var init_push_gitleaks_config = __esm({
769
783
  "use strict";
770
784
  init_config();
771
785
  init_utils();
772
- OVERLAY_EXTEND_RE = /^\s*(?:\[\s*extend\s*\]|extend\s*[.=])/m;
786
+ OVERLAY_EXTEND_RE = /^[ \t]*(?:\[[ \t]*extend[ \t]*\]|extend[ \t]*[.=])/m;
773
787
  TABLE_HEADER_RE = /^\s*\[/;
774
788
  OVERLAY_ALLOWLIST_HEADER_RE = /^\s*\[\[?\s*allowlists?\s*\]\]?/;
775
789
  OVERLAY_INLINE_ALLOWLIST_RE = /^[ \t]*allowlists?[ \t]*[.=]/m;
@@ -912,10 +926,11 @@ function scanStagedTree(repoDir, forwardStreams = false) {
912
926
  ];
913
927
  if (toml !== null) args.push("--config", toml);
914
928
  const opts = { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] };
929
+ const gitleaksOpts = { ...opts, timeout: GITLEAKS_SCAN_TIMEOUT_MS };
915
930
  try {
916
931
  execFileSync8("git", ["init", "-q"], opts);
917
932
  execFileSync8("git", ["add", "-A"], opts);
918
- execFileSync8("gitleaks", args, opts);
933
+ execFileSync8("gitleaks", args, gitleaksOpts);
919
934
  return [];
920
935
  } catch (err) {
921
936
  const e = err;
@@ -945,7 +960,10 @@ function scanFile(filePath, forwardStreams = false) {
945
960
  `--report-path=${reportPath}`
946
961
  ];
947
962
  if (toml !== null) args.push("--config", toml);
948
- const opts = { stdio: ["ignore", "pipe", "pipe"] };
963
+ const opts = {
964
+ stdio: ["ignore", "pipe", "pipe"],
965
+ timeout: GITLEAKS_SCAN_TIMEOUT_MS
966
+ };
949
967
  try {
950
968
  execFileSync8("gitleaks", args, opts);
951
969
  return [];
@@ -966,6 +984,7 @@ function scanFile(filePath, forwardStreams = false) {
966
984
  var init_push_gitleaks_scan = __esm({
967
985
  "src/push-gitleaks.scan.ts"() {
968
986
  "use strict";
987
+ init_config();
969
988
  init_push_gitleaks_config();
970
989
  init_utils_fs();
971
990
  }
@@ -2073,8 +2092,8 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
2073
2092
  }
2074
2093
 
2075
2094
  // src/commands.doctor.ts
2076
- import { existsSync as existsSync30 } from "node:fs";
2077
- import { join as join36 } from "node:path";
2095
+ import { existsSync as existsSync31 } from "node:fs";
2096
+ import { join as join37 } from "node:path";
2078
2097
 
2079
2098
  // src/commands.doctor.checks.repo.ts
2080
2099
  init_color();
@@ -2554,15 +2573,15 @@ function stripSideIndicator(line) {
2554
2573
  }
2555
2574
  function isGsdDiffLine(line, localBase, sharedBase) {
2556
2575
  const bare = stripSideIndicator(line);
2557
- let relative6;
2576
+ let relative7;
2558
2577
  if (bare.startsWith(localBase + "/")) {
2559
- relative6 = bare.slice(localBase.length + 1);
2578
+ relative7 = bare.slice(localBase.length + 1);
2560
2579
  } else if (bare.startsWith(sharedBase + "/")) {
2561
- relative6 = bare.slice(sharedBase.length + 1);
2580
+ relative7 = bare.slice(sharedBase.length + 1);
2562
2581
  } else {
2563
- relative6 = bare;
2582
+ relative7 = bare;
2564
2583
  }
2565
- return relative6.split("/")[0].startsWith(GSD_PREFIX);
2584
+ return relative7.split("/")[0].startsWith(GSD_PREFIX);
2566
2585
  }
2567
2586
  function reportSkillsDivergence(section2) {
2568
2587
  const sharedSkills = join14(repoHome(), "shared", "skills");
@@ -2899,9 +2918,9 @@ function reportCheckSchema(section2) {
2899
2918
  init_color();
2900
2919
  import { randomBytes } from "node:crypto";
2901
2920
  import { execFileSync as execFileSync9 } from "node:child_process";
2902
- import { existsSync as existsSync19, mkdirSync as mkdirSync5, readdirSync as readdirSync8, rmSync as rmSync8 } from "node:fs";
2921
+ import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync9, rmSync as rmSync8 } from "node:fs";
2903
2922
  import { homedir as homedir4 } from "node:os";
2904
- import { join as join24 } from "node:path";
2923
+ import { join as join25 } from "node:path";
2905
2924
 
2906
2925
  // src/commands.doctor.check-shared.scan.ts
2907
2926
  init_color();
@@ -2998,8 +3017,8 @@ init_config();
2998
3017
 
2999
3018
  // src/remap.ts
3000
3019
  init_config_sharedDirs_guard();
3001
- import { cpSync as cpSync4, existsSync as existsSync18, mkdirSync as mkdirSync4, readdirSync as readdirSync7, renameSync as renameSync3, rmSync as rmSync7, statSync as statSync4 } from "node:fs";
3002
- import { join as join23, relative as relative3, sep as sep3 } from "node:path";
3020
+ import { cpSync as cpSync4, existsSync as existsSync19, mkdirSync as mkdirSync5, readdirSync as readdirSync8, renameSync as renameSync3, rmSync as rmSync7, statSync as statSync5 } from "node:fs";
3021
+ import { dirname as dirname4, join as join24, relative as relative3, sep as sep3 } from "node:path";
3003
3022
 
3004
3023
  // src/extras-sync.guards.ts
3005
3024
  init_utils();
@@ -3020,6 +3039,114 @@ function assertSafeLocalRoot(localRoot, logical) {
3020
3039
 
3021
3040
  // src/remap.ts
3022
3041
  init_config();
3042
+
3043
+ // src/push-manifest.ts
3044
+ init_config();
3045
+ init_push_gitleaks_config();
3046
+ init_utils_fs();
3047
+ import { createHash } from "node:crypto";
3048
+ import { existsSync as existsSync18, mkdirSync as mkdirSync4, readdirSync as readdirSync7, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
3049
+ import { dirname as dirname3, join as join23 } from "node:path";
3050
+ function isChanged(prev, cur, hash) {
3051
+ if (prev === void 0) return true;
3052
+ if (prev.size !== cur.size) return true;
3053
+ if (prev.mtime === cur.mtime) return false;
3054
+ return prev.hash !== hash();
3055
+ }
3056
+ function diffManifest(old, current, hashFor) {
3057
+ const changed = /* @__PURE__ */ new Set();
3058
+ const deleted = [];
3059
+ if (old === null) {
3060
+ for (const key of Object.keys(current)) {
3061
+ changed.add(key);
3062
+ }
3063
+ return { changed, deleted };
3064
+ }
3065
+ for (const [key, cur] of Object.entries(current)) {
3066
+ const prev = old.files[key];
3067
+ if (isChanged(prev, cur, () => hashFor(key))) {
3068
+ changed.add(key);
3069
+ }
3070
+ }
3071
+ for (const key of Object.keys(old.files)) {
3072
+ if (!(key in current)) {
3073
+ deleted.push(key);
3074
+ }
3075
+ }
3076
+ return { changed, deleted };
3077
+ }
3078
+ function shouldFullRescan(old, scannerVersion, configHash, forceFlag) {
3079
+ if (forceFlag) return true;
3080
+ if (old === null) return true;
3081
+ if (old.scannerVersion !== scannerVersion) return true;
3082
+ if (old.configHash !== configHash) return true;
3083
+ return false;
3084
+ }
3085
+ function hashFile(absPath) {
3086
+ return createHash("sha256").update(readFileSync6(absPath)).digest("hex");
3087
+ }
3088
+ function enumerateDir(dir, results) {
3089
+ for (const entry of readdirSync7(dir)) {
3090
+ const fullPath = join23(dir, entry);
3091
+ const st = statSync4(fullPath);
3092
+ if (st.isDirectory()) {
3093
+ enumerateDir(fullPath, results);
3094
+ } else {
3095
+ results.push(fullPath);
3096
+ }
3097
+ }
3098
+ }
3099
+ function enumerateSourceFiles(projectDir) {
3100
+ const results = [];
3101
+ for (const entry of readdirSync7(projectDir)) {
3102
+ const fullPath = join23(projectDir, entry);
3103
+ const st = statSync4(fullPath);
3104
+ if (st.isDirectory()) {
3105
+ enumerateDir(fullPath, results);
3106
+ } else if (fullPath.endsWith(".jsonl")) {
3107
+ results.push(fullPath);
3108
+ }
3109
+ }
3110
+ return results;
3111
+ }
3112
+ function fileIdentity(p) {
3113
+ if (p === null) return "none::absent";
3114
+ if (!existsSync18(p)) return `${p}::absent`;
3115
+ return `${p}::${createHash("sha256").update(readFileSync6(p)).digest("hex")}`;
3116
+ }
3117
+ function computeConfigHash() {
3118
+ const repo = repoHome();
3119
+ const parts = [
3120
+ fileIdentity(resolveTomlPath(repo)),
3121
+ fileIdentity(join23(repo, ".gitleaks.overlay.toml")),
3122
+ fileIdentity(join23(repo, ".gitleaksignore"))
3123
+ ];
3124
+ return createHash("sha256").update(parts.join("\n")).digest("hex");
3125
+ }
3126
+ function isManifestShape(raw) {
3127
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
3128
+ const m = raw;
3129
+ return m.schema === 1 && typeof m.scannerVersion === "string" && typeof m.configHash === "string" && m.files !== null && typeof m.files === "object" && !Array.isArray(m.files);
3130
+ }
3131
+ function readManifest(path) {
3132
+ try {
3133
+ const raw = readFileSync6(path, "utf8");
3134
+ const parsed = JSON.parse(raw);
3135
+ if (!isManifestShape(parsed)) return null;
3136
+ return parsed;
3137
+ } catch {
3138
+ return null;
3139
+ }
3140
+ }
3141
+ function writeManifest(path, manifest) {
3142
+ mkdirSync4(dirname3(path), { recursive: true });
3143
+ writeJsonAtomic(path, manifest);
3144
+ }
3145
+ function buildManifest(files, scannerVersion, configHash) {
3146
+ return { schema: 1, scannerVersion, configHash, files };
3147
+ }
3148
+
3149
+ // src/remap.ts
3023
3150
  init_utils();
3024
3151
  init_utils_fs();
3025
3152
  init_utils_json();
@@ -3034,6 +3161,33 @@ function atomicMirror(src, dst, options) {
3034
3161
  function copyDir(src, dst) {
3035
3162
  atomicMirror(src, dst, { recursive: true, force: true });
3036
3163
  }
3164
+ function copyFileAtomic(src, dst) {
3165
+ mkdirSync5(dirname4(dst), { recursive: true });
3166
+ const tmp = `${dst}.tmp.${process.pid}`;
3167
+ cpSync4(src, tmp, { force: true, preserveTimestamps: true });
3168
+ renameSync3(tmp, dst);
3169
+ }
3170
+ function hasDeltaForDir(sel, localDir) {
3171
+ const prefix = `${localDir}${sep3}`;
3172
+ for (const p of sel.changed) if (p.startsWith(prefix)) return true;
3173
+ for (const p of sel.deleted) if (p.startsWith(prefix)) return true;
3174
+ return false;
3175
+ }
3176
+ function skipForSelection(sel, localDir) {
3177
+ return sel !== void 0 && !hasDeltaForDir(sel, localDir);
3178
+ }
3179
+ function applySelective(sel, localDir, repoDst) {
3180
+ const prefix = `${localDir}${sep3}`;
3181
+ for (const src of sel.changed) {
3182
+ if (!src.startsWith(prefix)) continue;
3183
+ copyFileAtomic(src, join24(repoDst, relative3(localDir, src)));
3184
+ }
3185
+ for (const src of sel.deleted) {
3186
+ if (!src.startsWith(prefix)) continue;
3187
+ const dst = join24(repoDst, relative3(localDir, src));
3188
+ if (existsSync19(dst)) rmSync7(dst);
3189
+ }
3190
+ }
3037
3191
  function copyDirJsonlOnly(src, dst) {
3038
3192
  atomicMirror(src, dst, {
3039
3193
  recursive: true,
@@ -3042,7 +3196,7 @@ function copyDirJsonlOnly(src, dst) {
3042
3196
  const rel = relative3(src, srcPath);
3043
3197
  if (rel === "") return true;
3044
3198
  if (rel.split(sep3).length > 1) return true;
3045
- if (statSync4(srcPath).isDirectory()) return true;
3199
+ if (statSync5(srcPath).isDirectory()) return true;
3046
3200
  if (srcPath.endsWith(".jsonl")) return true;
3047
3201
  item(`skip ${rel}: extension not in allowlist`);
3048
3202
  return false;
@@ -3060,16 +3214,16 @@ function remapPull(ts, opts = {}) {
3060
3214
  const wouldPull = [];
3061
3215
  const repo = repoHome();
3062
3216
  const claude = claudeHome();
3063
- const mapPath = join23(repo, "path-map.json");
3064
- const repoProjects = join23(repo, "shared", "projects");
3065
- if (!existsSync18(mapPath) || !existsSync18(repoProjects)) {
3217
+ const mapPath = join24(repo, "path-map.json");
3218
+ const repoProjects = join24(repo, "shared", "projects");
3219
+ if (!existsSync19(mapPath) || !existsSync19(repoProjects)) {
3066
3220
  const text = "no path-map or repo projects dir; skipping session remap";
3067
3221
  emitPreview(opts.onPreview, { kind: "note", text }, text);
3068
3222
  return { unmapped: 0, pulled, wouldPull };
3069
3223
  }
3070
3224
  const map = readPathMap(mapPath);
3071
- const localProjects = join23(claude, "projects");
3072
- if (!dryRun) mkdirSync4(localProjects, { recursive: true });
3225
+ const localProjects = join24(claude, "projects");
3226
+ if (!dryRun) mkdirSync5(localProjects, { recursive: true });
3073
3227
  for (const [logical, hosts] of Object.entries(map.projects)) {
3074
3228
  assertSafeLogical(logical);
3075
3229
  const localPath = hosts[HOST];
@@ -3078,9 +3232,9 @@ function remapPull(ts, opts = {}) {
3078
3232
  continue;
3079
3233
  }
3080
3234
  assertSafeLocalRoot(localPath, logical);
3081
- const src = join23(repoProjects, logical);
3082
- if (!existsSync18(src)) continue;
3083
- const dst = join23(localProjects, encodePath(localPath));
3235
+ const src = join24(repoProjects, logical);
3236
+ if (!existsSync19(src)) continue;
3237
+ const dst = join24(localProjects, encodePath(localPath));
3084
3238
  if (dryRun) {
3085
3239
  wouldPull.push(logical);
3086
3240
  emitPreview(
@@ -3127,31 +3281,37 @@ function remapPush(ts, opts = {}) {
3127
3281
  const wouldPush = [];
3128
3282
  const repo = repoHome();
3129
3283
  const claude = claudeHome();
3130
- const mapPath = join23(repo, "path-map.json");
3131
- if (!existsSync18(mapPath)) {
3284
+ const mapPath = join24(repo, "path-map.json");
3285
+ if (!existsSync19(mapPath)) {
3132
3286
  log("no path-map.json; skipping session export");
3133
3287
  return { unmapped: 0, collisions: 0, pushed, wouldPush };
3134
3288
  }
3135
3289
  const map = readPathMap(mapPath);
3136
- const localProjects = join23(claude, "projects");
3137
- const repoProjects = join23(repo, "shared", "projects");
3290
+ const localProjects = join24(claude, "projects");
3291
+ const repoProjects = join24(repo, "shared", "projects");
3138
3292
  const reverse = buildReverseMap(map);
3139
- if (!existsSync18(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
3140
- if (!dryRun) mkdirSync4(repoProjects, { recursive: true });
3141
- for (const dir of readdirSync7(localProjects)) {
3293
+ if (!existsSync19(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
3294
+ if (!dryRun) mkdirSync5(repoProjects, { recursive: true });
3295
+ for (const dir of readdirSync8(localProjects)) {
3142
3296
  if (dir.endsWith(TMP_SUFFIX)) continue;
3143
3297
  const logical = reverse.get(dir);
3144
3298
  if (!logical) {
3145
3299
  unmapped++;
3146
3300
  continue;
3147
3301
  }
3148
- const repoDst = join23(repoProjects, logical);
3302
+ const localDir = join24(localProjects, dir);
3303
+ const repoDst = join24(repoProjects, logical);
3304
+ if (skipForSelection(opts.selection, localDir)) continue;
3149
3305
  if (dryRun) {
3150
3306
  wouldPush.push(logical);
3151
3307
  continue;
3152
3308
  }
3153
3309
  backupRepoWrite(repoDst, ts, repo);
3154
- copyDirJsonlOnly(join23(localProjects, dir), repoDst);
3310
+ if (opts.selection) {
3311
+ applySelective(opts.selection, localDir, repoDst);
3312
+ } else {
3313
+ copyDirJsonlOnly(localDir, repoDst);
3314
+ }
3155
3315
  pushed.push(logical);
3156
3316
  }
3157
3317
  return { unmapped, collisions: 0, pushed, wouldPush };
@@ -3164,8 +3324,8 @@ function buildScanTree(tmpRoot) {
3164
3324
  const logicalToEncoded = /* @__PURE__ */ new Map();
3165
3325
  let staged = 0;
3166
3326
  const repo = repoHome();
3167
- const mapPath = join24(repo, "path-map.json");
3168
- if (!existsSync19(mapPath)) return { logicalToEncoded, staged, malformed: false };
3327
+ const mapPath = join25(repo, "path-map.json");
3328
+ if (!existsSync20(mapPath)) return { logicalToEncoded, staged, malformed: false };
3169
3329
  let map;
3170
3330
  try {
3171
3331
  map = readJson(mapPath);
@@ -3182,12 +3342,12 @@ function buildScanTree(tmpRoot) {
3182
3342
  if (!p || p === "TBD") continue;
3183
3343
  reverse.set(encodePath(p), logical);
3184
3344
  }
3185
- const localProjects = join24(claudeHome(), "projects");
3186
- if (!existsSync19(localProjects)) return { logicalToEncoded, staged, malformed: false };
3187
- for (const dir of readdirSync8(localProjects)) {
3345
+ const localProjects = join25(claudeHome(), "projects");
3346
+ if (!existsSync20(localProjects)) return { logicalToEncoded, staged, malformed: false };
3347
+ for (const dir of readdirSync9(localProjects)) {
3188
3348
  const logical = reverse.get(dir);
3189
3349
  if (!logical) continue;
3190
- copyDirJsonlOnly(join24(localProjects, dir), join24(tmpRoot, "shared", "projects", logical));
3350
+ copyDirJsonlOnly(join25(localProjects, dir), join25(tmpRoot, "shared", "projects", logical));
3191
3351
  logicalToEncoded.set(logical, dir);
3192
3352
  staged++;
3193
3353
  }
@@ -3218,11 +3378,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
3218
3378
  }
3219
3379
  function reportCheckShared(section2, gitleaksReady) {
3220
3380
  if (!ensureGitleaksReady(section2, gitleaksReady)) return;
3221
- const cacheDir = join24(homedir4(), ".cache", "claude-nomad");
3222
- mkdirSync5(cacheDir, { recursive: true });
3381
+ const cacheDir = join25(homedir4(), ".cache", "claude-nomad");
3382
+ mkdirSync6(cacheDir, { recursive: true });
3223
3383
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
3224
- const reportPath = join24(cacheDir, `check-shared-${stamp}.json`);
3225
- const tmpRoot = join24(cacheDir, `check-shared-tree-${stamp}`);
3384
+ const reportPath = join25(cacheDir, `check-shared-${stamp}.json`);
3385
+ const tmpRoot = join25(cacheDir, `check-shared-tree-${stamp}`);
3226
3386
  try {
3227
3387
  const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
3228
3388
  if (malformed) {
@@ -3243,12 +3403,12 @@ function reportCheckShared(section2, gitleaksReady) {
3243
3403
 
3244
3404
  // src/commands.doctor.checks.hooks.scope.ts
3245
3405
  init_color();
3246
- import { existsSync as existsSync20, readFileSync as readFileSync6, readdirSync as readdirSync9, realpathSync as realpathSync2 } from "node:fs";
3247
- import { dirname as dirname3, extname, join as join25 } from "node:path";
3406
+ import { existsSync as existsSync21, readFileSync as readFileSync7, readdirSync as readdirSync10, realpathSync as realpathSync2 } from "node:fs";
3407
+ import { dirname as dirname5, extname, join as join26 } from "node:path";
3248
3408
  init_config();
3249
3409
  function typeFromPackageJson(pkgPath) {
3250
3410
  try {
3251
- const parsed = JSON.parse(readFileSync6(pkgPath, "utf8"));
3411
+ const parsed = JSON.parse(readFileSync7(pkgPath, "utf8"));
3252
3412
  return parsed.type === "module" ? "esm" : "cjs";
3253
3413
  } catch {
3254
3414
  return "cjs";
@@ -3264,11 +3424,11 @@ function effectiveType(hookPath) {
3264
3424
  } catch {
3265
3425
  return null;
3266
3426
  }
3267
- let dir = dirname3(real);
3427
+ let dir = dirname5(real);
3268
3428
  for (; ; ) {
3269
- const pkg = join25(dir, "package.json");
3270
- if (existsSync20(pkg)) return typeFromPackageJson(pkg);
3271
- const parent = dirname3(dir);
3429
+ const pkg = join26(dir, "package.json");
3430
+ if (existsSync21(pkg)) return typeFromPackageJson(pkg);
3431
+ const parent = dirname5(dir);
3272
3432
  if (parent === dir) return "cjs";
3273
3433
  dir = parent;
3274
3434
  }
@@ -3285,7 +3445,7 @@ function stripCommentsAndStrings(src) {
3285
3445
  function classifySource(src) {
3286
3446
  const code = stripCommentsAndStrings(src);
3287
3447
  const cjs = /\brequire\s*\(/.test(code) || /\bmodule\.exports\b/.test(code) || /\bexports\.\w/.test(code);
3288
- const esm = /^[^\S\r\n]*import\s/m.test(code) || /^[^\S\r\n]*export\s/m.test(code) || /\bimport\.meta\b/.test(code);
3448
+ const esm = /^[ \t]*import\s/m.test(code) || /^[ \t]*export\s/m.test(code) || /\bimport\.meta\b/.test(code);
3289
3449
  if (cjs && !esm) return "cjs";
3290
3450
  if (esm && !cjs) return "esm";
3291
3451
  if (cjs && esm) return "cjs";
@@ -3296,28 +3456,28 @@ function remedy(family) {
3296
3456
  }
3297
3457
  function safeReaddir2(dir) {
3298
3458
  try {
3299
- return readdirSync9(dir);
3459
+ return readdirSync10(dir);
3300
3460
  } catch {
3301
3461
  return [];
3302
3462
  }
3303
3463
  }
3304
3464
  function safeRead(path) {
3305
3465
  try {
3306
- return readFileSync6(path, "utf8");
3466
+ return readFileSync7(path, "utf8");
3307
3467
  } catch {
3308
3468
  return null;
3309
3469
  }
3310
3470
  }
3311
3471
  function reportHookScopeCheck(section2) {
3312
- const hooksDir = join25(claudeHome(), "hooks");
3313
- if (!existsSync20(hooksDir)) {
3472
+ const hooksDir = join26(claudeHome(), "hooks");
3473
+ if (!existsSync21(hooksDir)) {
3314
3474
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
3315
3475
  return;
3316
3476
  }
3317
3477
  let anyWarn = false;
3318
3478
  for (const name of safeReaddir2(hooksDir)) {
3319
3479
  if (extname(name) !== ".js") continue;
3320
- const abs = join25(hooksDir, name);
3480
+ const abs = join26(hooksDir, name);
3321
3481
  const eff = effectiveType(abs);
3322
3482
  if (eff === null) continue;
3323
3483
  const src = safeRead(abs);
@@ -3337,15 +3497,19 @@ function reportHookScopeCheck(section2) {
3337
3497
 
3338
3498
  // src/commands.doctor.checks.hooks.ts
3339
3499
  init_color();
3340
- import { existsSync as existsSync21 } from "node:fs";
3341
- import { join as join26 } from "node:path";
3500
+ import { existsSync as existsSync22 } from "node:fs";
3501
+ import { join as join27 } from "node:path";
3342
3502
  init_config();
3343
3503
  function expandHome(token) {
3344
3504
  const h2 = home();
3345
3505
  return token.replace(/^\$\{HOME\}/, h2).replace(/^\$HOME/, h2).replace(/^~/, h2);
3346
3506
  }
3507
+ var TRAILING_SHELL_PUNCT = /* @__PURE__ */ new Set(["'", '"', "`", ";", ")", "|", "&", ">"]);
3347
3508
  function stripShellPunctuation(token) {
3348
- return token.replace(/^['"]+/, "").replace(/['"`;)|&>]+$/, "");
3509
+ const stripped = token.replace(/^['"]+/, "");
3510
+ let end = stripped.length;
3511
+ while (end > 0 && TRAILING_SHELL_PUNCT.has(stripped[end - 1])) end--;
3512
+ return stripped.slice(0, end);
3349
3513
  }
3350
3514
  function* claudePathsIn(command) {
3351
3515
  const claudePrefix = `${claudeHome()}/`;
@@ -3377,7 +3541,7 @@ function checkEventGroups(section2, event, groups) {
3377
3541
  for (const group of groups) {
3378
3542
  for (const cmd of commandsFromGroup(group)) {
3379
3543
  for (const resolved of claudePathsIn(cmd)) {
3380
- if (existsSync21(resolved)) continue;
3544
+ if (existsSync22(resolved)) continue;
3381
3545
  addItem(
3382
3546
  section2,
3383
3547
  `${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
@@ -3390,8 +3554,8 @@ function checkEventGroups(section2, event, groups) {
3390
3554
  return anyFail;
3391
3555
  }
3392
3556
  function reportHooksTargetCheck(section2) {
3393
- const settingsPath = join26(claudeHome(), "settings.json");
3394
- if (!existsSync21(settingsPath)) {
3557
+ const settingsPath = join27(claudeHome(), "settings.json");
3558
+ if (!existsSync22(settingsPath)) {
3395
3559
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
3396
3560
  return;
3397
3561
  }
@@ -3414,12 +3578,12 @@ function reportHooksTargetCheck(section2) {
3414
3578
 
3415
3579
  // src/commands.doctor.checks.hooks.preserve-symlinks.ts
3416
3580
  init_color();
3417
- import { existsSync as existsSync23, readFileSync as readFileSync7 } from "node:fs";
3418
- import { join as join28 } from "node:path";
3581
+ import { existsSync as existsSync24, readFileSync as readFileSync8 } from "node:fs";
3582
+ import { join as join29 } from "node:path";
3419
3583
 
3420
3584
  // src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
3421
- import { closeSync as closeSync3, existsSync as existsSync22, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
3422
- import { dirname as dirname4, join as join27, resolve as resolve2 } from "node:path";
3585
+ import { closeSync as closeSync3, existsSync as existsSync23, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
3586
+ import { dirname as dirname6, join as join28, resolve as resolve2 } from "node:path";
3423
3587
  function suppressedRanges(src) {
3424
3588
  const ranges = [];
3425
3589
  const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
@@ -3451,12 +3615,12 @@ function topRelativeSpecifiers(src) {
3451
3615
  }
3452
3616
  function specifierIsMissing(specifier, baseDir) {
3453
3617
  const base = resolve2(baseDir, specifier);
3454
- if (existsSync22(base)) return false;
3618
+ if (existsSync23(base)) return false;
3455
3619
  for (const ext of [".js", ".cjs", ".mjs"]) {
3456
- if (existsSync22(base + ext)) return false;
3620
+ if (existsSync23(base + ext)) return false;
3457
3621
  }
3458
3622
  for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
3459
- if (existsSync22(join27(base, idx))) return false;
3623
+ if (existsSync23(join28(base, idx))) return false;
3460
3624
  }
3461
3625
  return true;
3462
3626
  }
@@ -3482,7 +3646,7 @@ function relativeRequireTargetsBroken(scriptPath) {
3482
3646
  }
3483
3647
  const specifiers = topRelativeSpecifiers(raw);
3484
3648
  if (specifiers.length === 0) return false;
3485
- const baseDir = dirname4(realPath);
3649
+ const baseDir = dirname6(realPath);
3486
3650
  for (const spec of specifiers) {
3487
3651
  if (specifierIsMissing(spec, baseDir)) return true;
3488
3652
  }
@@ -3511,10 +3675,10 @@ function commandTokens(command) {
3511
3675
  return tokens;
3512
3676
  }
3513
3677
  function readPathMapSafe() {
3514
- const mapPath = join28(repoHome(), "path-map.json");
3515
- if (!existsSync23(mapPath)) return { projects: {} };
3678
+ const mapPath = join29(repoHome(), "path-map.json");
3679
+ if (!existsSync24(mapPath)) return { projects: {} };
3516
3680
  try {
3517
- return JSON.parse(readFileSync7(mapPath, "utf8"));
3681
+ return JSON.parse(readFileSync8(mapPath, "utf8"));
3518
3682
  } catch {
3519
3683
  return { projects: {} };
3520
3684
  }
@@ -3585,8 +3749,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
3585
3749
  return anyWarn;
3586
3750
  }
3587
3751
  function reportPreserveSymlinksCheck(section2) {
3588
- const settingsPath = join28(claudeHome(), "settings.json");
3589
- if (!existsSync23(settingsPath)) {
3752
+ const settingsPath = join29(claudeHome(), "settings.json");
3753
+ if (!existsSync24(settingsPath)) {
3590
3754
  addItem(
3591
3755
  section2,
3592
3756
  `${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
@@ -3595,7 +3759,7 @@ function reportPreserveSymlinksCheck(section2) {
3595
3759
  }
3596
3760
  let settings;
3597
3761
  try {
3598
- settings = JSON.parse(readFileSync7(settingsPath, "utf8"));
3762
+ settings = JSON.parse(readFileSync8(settingsPath, "utf8"));
3599
3763
  } catch {
3600
3764
  return;
3601
3765
  }
@@ -3619,8 +3783,8 @@ function reportPreserveSymlinksCheck(section2) {
3619
3783
 
3620
3784
  // src/commands.doctor.checks.settings-drift.ts
3621
3785
  init_color();
3622
- import { existsSync as existsSync24, readFileSync as readFileSync8 } from "node:fs";
3623
- import { join as join29 } from "node:path";
3786
+ import { existsSync as existsSync25, readFileSync as readFileSync9 } from "node:fs";
3787
+ import { join as join30 } from "node:path";
3624
3788
  init_config();
3625
3789
  init_utils_json();
3626
3790
  function diffMergedSettings(merged, settings) {
@@ -3629,7 +3793,7 @@ function diffMergedSettings(merged, settings) {
3629
3793
  }
3630
3794
  function tryReadJson(filePath) {
3631
3795
  try {
3632
- const raw = readFileSync8(filePath, "utf8");
3796
+ const raw = readFileSync9(filePath, "utf8");
3633
3797
  const parsed = JSON.parse(raw);
3634
3798
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
3635
3799
  return parsed;
@@ -3638,7 +3802,7 @@ function tryReadJson(filePath) {
3638
3802
  }
3639
3803
  }
3640
3804
  function reportHooksBaseSelfCleanNote(section2) {
3641
- const basePath = join29(repoHome(), "shared", "settings.base.json");
3805
+ const basePath = join30(repoHome(), "shared", "settings.base.json");
3642
3806
  const base = tryReadJson(basePath);
3643
3807
  if (base === null) return;
3644
3808
  if (!baseHasGsdHookEntries(base)) return;
@@ -3651,14 +3815,14 @@ function reportSettingsDriftCheck(section2) {
3651
3815
  const claude = claudeHome();
3652
3816
  const repo = repoHome();
3653
3817
  const host = HOST;
3654
- const settingsPath = join29(claude, "settings.json");
3655
- const basePath = join29(repo, "shared", "settings.base.json");
3656
- const hostPath = join29(repo, "hosts", `${host}.json`);
3657
- if (!existsSync24(settingsPath)) {
3818
+ const settingsPath = join30(claude, "settings.json");
3819
+ const basePath = join30(repo, "shared", "settings.base.json");
3820
+ const hostPath = join30(repo, "hosts", `${host}.json`);
3821
+ if (!existsSync25(settingsPath)) {
3658
3822
  addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
3659
3823
  return;
3660
3824
  }
3661
- if (!existsSync24(basePath)) {
3825
+ if (!existsSync25(basePath)) {
3662
3826
  addItem(
3663
3827
  section2,
3664
3828
  `${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
@@ -3677,7 +3841,7 @@ function reportSettingsDriftCheck(section2) {
3677
3841
  if (settings === null) {
3678
3842
  return;
3679
3843
  }
3680
- const hostExists = existsSync24(hostPath);
3844
+ const hostExists = existsSync25(hostPath);
3681
3845
  const hostObj = hostExists ? tryReadJson(hostPath) : null;
3682
3846
  if (hostExists && hostObj === null) {
3683
3847
  addItem(
@@ -3726,12 +3890,12 @@ init_config();
3726
3890
 
3727
3891
  // src/commands.doctor.engine.ts
3728
3892
  init_color();
3729
- import { readFileSync as readFileSync10 } from "node:fs";
3893
+ import { readFileSync as readFileSync11 } from "node:fs";
3730
3894
  import { fileURLToPath as fileURLToPath3 } from "node:url";
3731
3895
 
3732
3896
  // src/commands.doctor.version.ts
3733
3897
  init_color();
3734
- import { readFileSync as readFileSync9 } from "node:fs";
3898
+ import { readFileSync as readFileSync10 } from "node:fs";
3735
3899
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3736
3900
  init_config();
3737
3901
  var STRICT_SEMVER = /^\d+\.\d+\.\d+$/;
@@ -3748,7 +3912,7 @@ function compareSemver(a, b) {
3748
3912
  function readLocalVersion() {
3749
3913
  try {
3750
3914
  const pkgPath = fileURLToPath2(new URL("../package.json", import.meta.url));
3751
- const parsed = JSON.parse(readFileSync9(pkgPath, "utf8"));
3915
+ const parsed = JSON.parse(readFileSync10(pkgPath, "utf8"));
3752
3916
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
3753
3917
  return parsed.version;
3754
3918
  }
@@ -3807,7 +3971,7 @@ function parseMinVersion(spec) {
3807
3971
  function readEnginesNode() {
3808
3972
  try {
3809
3973
  const pkgPath = fileURLToPath3(new URL("../package.json", import.meta.url));
3810
- const parsed = JSON.parse(readFileSync10(pkgPath, "utf8"));
3974
+ const parsed = JSON.parse(readFileSync11(pkgPath, "utf8"));
3811
3975
  const node = parsed.engines?.node;
3812
3976
  if (typeof node === "string" && node.length > 0) return node;
3813
3977
  return null;
@@ -3835,43 +3999,43 @@ function reportNodeEngineCheck(section2) {
3835
3999
 
3836
4000
  // src/spinner.ts
3837
4001
  init_color();
3838
- import { existsSync as existsSync28 } from "node:fs";
4002
+ import { existsSync as existsSync29 } from "node:fs";
3839
4003
  import { fileURLToPath as fileURLToPath4 } from "node:url";
3840
4004
  import { Worker } from "node:worker_threads";
3841
4005
 
3842
4006
  // src/commands.push.recovery.ts
3843
4007
  init_config();
3844
- import { readFileSync as readFileSync13, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
3845
- import { join as join34 } from "node:path";
4008
+ import { readFileSync as readFileSync14, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
4009
+ import { join as join35 } from "node:path";
3846
4010
  import { createInterface as createInterface2 } from "node:readline/promises";
3847
4011
 
3848
4012
  // src/commands.push.recovery.actions.ts
3849
4013
  init_config();
3850
- import { readFileSync as readFileSync12 } from "node:fs";
4014
+ import { readFileSync as readFileSync13 } from "node:fs";
3851
4015
  import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep5 } from "node:path";
3852
4016
 
3853
4017
  // src/commands.push.recovery.redact.ts
3854
4018
  init_config();
3855
4019
  init_config_sharedDirs_guard();
3856
- import { cpSync as cpSync5, existsSync as existsSync27, mkdirSync as mkdirSync6, statSync as statSync7 } from "node:fs";
3857
- import { dirname as dirname6, join as join32, sep as sep4 } from "node:path";
4020
+ import { cpSync as cpSync5, existsSync as existsSync28, mkdirSync as mkdirSync7, statSync as statSync8 } from "node:fs";
4021
+ import { dirname as dirname8, join as join33, sep as sep4 } from "node:path";
3858
4022
 
3859
4023
  // src/commands.redact.ts
3860
4024
  init_config();
3861
- import { existsSync as existsSync26, statSync as statSync6 } from "node:fs";
3862
- import { dirname as dirname5, join as join31 } from "node:path";
4025
+ import { existsSync as existsSync27, statSync as statSync7 } from "node:fs";
4026
+ import { dirname as dirname7, join as join32 } from "node:path";
3863
4027
 
3864
4028
  // src/commands.redact.subtree.ts
3865
- import { existsSync as existsSync25, lstatSync as lstatSync8, readFileSync as readFileSync11, readdirSync as readdirSync10, statSync as statSync5, writeFileSync as writeFileSync4 } from "node:fs";
3866
- import { join as join30 } from "node:path";
4029
+ import { existsSync as existsSync26, lstatSync as lstatSync8, readFileSync as readFileSync12, readdirSync as readdirSync11, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
4030
+ import { join as join31 } from "node:path";
3867
4031
  init_utils_fs();
3868
4032
  init_utils();
3869
4033
  function collectFiles(dir, out) {
3870
- if (!existsSync25(dir)) return;
4034
+ if (!existsSync26(dir)) return;
3871
4035
  const st = lstatSync8(dir);
3872
4036
  if (!st.isDirectory()) return;
3873
- for (const entry of readdirSync10(dir)) {
3874
- const abs = join30(dir, entry);
4037
+ for (const entry of readdirSync11(dir)) {
4038
+ const abs = join31(dir, entry);
3875
4039
  const lst = lstatSync8(abs);
3876
4040
  if (lst.isSymbolicLink()) continue;
3877
4041
  if (lst.isDirectory()) {
@@ -3886,7 +4050,7 @@ function listSubtreeFiles(sessionDir) {
3886
4050
  collectFiles(sessionDir, out);
3887
4051
  return out.sort((a, b) => a.localeCompare(b));
3888
4052
  }
3889
- function newestSubtreeMtimeMs(mainPath, subtreeFiles, statMtime = (p) => statSync5(p).mtimeMs) {
4053
+ function newestSubtreeMtimeMs(mainPath, subtreeFiles, statMtime = (p) => statSync6(p).mtimeMs) {
3890
4054
  let newest = statMtime(mainPath);
3891
4055
  for (const filePath of subtreeFiles) {
3892
4056
  const t = statMtime(filePath);
@@ -3908,7 +4072,7 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
3908
4072
  if (!dryRun && total > 0) {
3909
4073
  for (const { path: filePath, findings } of dirty) {
3910
4074
  backupBeforeWrite(filePath, ts);
3911
- const before = readFileSync11(filePath, "utf8");
4075
+ const before = readFileSync12(filePath, "utf8");
3912
4076
  const after = applyRedactions(before, findings);
3913
4077
  if (after === before) {
3914
4078
  log(
@@ -3976,15 +4140,15 @@ init_utils_json();
3976
4140
  init_utils();
3977
4141
  function resolveLiveTranscript(id) {
3978
4142
  try {
3979
- const mapPath = join31(repoHome(), "path-map.json");
3980
- if (!existsSync26(mapPath)) return null;
4143
+ const mapPath = join32(repoHome(), "path-map.json");
4144
+ if (!existsSync27(mapPath)) return null;
3981
4145
  const projects = readJson(mapPath).projects;
3982
4146
  const claude = claudeHome();
3983
4147
  for (const hostMap of Object.values(projects)) {
3984
4148
  const abs = hostMap[HOST];
3985
4149
  if (abs === void 0) continue;
3986
- const live = join31(claude, "projects", encodePath(abs), `${id}.jsonl`);
3987
- if (existsSync26(live)) return live;
4150
+ const live = join32(claude, "projects", encodePath(abs), `${id}.jsonl`);
4151
+ if (existsSync27(live)) return live;
3988
4152
  }
3989
4153
  return null;
3990
4154
  } catch {
@@ -4004,19 +4168,19 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
4004
4168
  }
4005
4169
  const repo = repoHome();
4006
4170
  const backup = backupBase();
4007
- if (!existsSync26(repo)) die(`repo not cloned at ${repo}`);
4171
+ if (!existsSync27(repo)) die(`repo not cloned at ${repo}`);
4008
4172
  const handle = acquireLock("redact");
4009
4173
  if (handle === null) process.exit(0);
4010
4174
  try {
4011
4175
  const localPath = resolveLiveTranscript(id);
4012
- if (localPath === null || !existsSync26(localPath)) {
4176
+ if (localPath === null || !existsSync27(localPath)) {
4013
4177
  fail(`could not resolve local transcript for session ${id} on this host`);
4014
4178
  process.exitCode = 1;
4015
4179
  return;
4016
4180
  }
4017
- const sessionDir = join31(dirname5(localPath), id);
4181
+ const sessionDir = join32(dirname7(localPath), id);
4018
4182
  const subtreeFiles = listSubtreeFiles(sessionDir);
4019
- const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync6(p).mtimeMs);
4183
+ const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
4020
4184
  if (isRecentlyModified(subtreeMtime, nowMs())) {
4021
4185
  log(
4022
4186
  `session ${id} was modified recently and may be active.
@@ -4130,8 +4294,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
4130
4294
  assertSafeLogical(logical);
4131
4295
  const abs = hostMap[HOST];
4132
4296
  if (abs === void 0) continue;
4133
- if (localPath.startsWith(join32(claude, "projects", encodePath(abs)) + sep4)) {
4134
- return join32(repo, "shared", "projects", logical);
4297
+ if (localPath.startsWith(join33(claude, "projects", encodePath(abs)) + sep4)) {
4298
+ return join33(repo, "shared", "projects", logical);
4135
4299
  }
4136
4300
  }
4137
4301
  return null;
@@ -4141,9 +4305,9 @@ function preflightRedactable(f, map, nowMs) {
4141
4305
  if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
4142
4306
  const localPath = resolveLiveTranscript(sid);
4143
4307
  if (localPath === null) return `session ${sid}: local transcript not found`;
4144
- const sessionDir = join32(dirname6(localPath), sid);
4308
+ const sessionDir = join33(dirname8(localPath), sid);
4145
4309
  const subtreeFiles = listSubtreeFiles(sessionDir);
4146
- const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
4310
+ const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
4147
4311
  if (isRecentlyModified(subtreeMtime, nowMs())) {
4148
4312
  return `session ${sid}: looks active (modified within the last 5 minutes)`;
4149
4313
  }
@@ -4171,9 +4335,9 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
4171
4335
  `could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
4172
4336
  );
4173
4337
  }
4174
- const sessionDir = join32(dirname6(localPath), sid);
4338
+ const sessionDir = join33(dirname8(localPath), sid);
4175
4339
  const subtreeFiles = listSubtreeFiles(sessionDir);
4176
- const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
4340
+ const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
4177
4341
  if (isRecentlyModified(subtreeMtime, nowMs())) {
4178
4342
  return refuse(
4179
4343
  `session ${sid} looks active (modified within the last 5 minutes); refusing to redact, no changes made.
@@ -4204,10 +4368,10 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
4204
4368
  `nothing to redact in the local transcript for session ${sid}; choose Skip or Drop session.`
4205
4369
  );
4206
4370
  }
4207
- mkdirSync6(stagedProjectDir, { recursive: true });
4208
- cpSync5(localPath, join32(stagedProjectDir, `${sid}.jsonl`), { force: true });
4209
- if (existsSync27(sessionDir)) {
4210
- cpSync5(sessionDir, join32(stagedProjectDir, sid), { force: true, recursive: true });
4371
+ mkdirSync7(stagedProjectDir, { recursive: true });
4372
+ cpSync5(localPath, join33(stagedProjectDir, `${sid}.jsonl`), { force: true });
4373
+ if (existsSync28(sessionDir)) {
4374
+ cpSync5(sessionDir, join33(stagedProjectDir, sid), { force: true, recursive: true });
4211
4375
  }
4212
4376
  return true;
4213
4377
  }
@@ -4215,14 +4379,14 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
4215
4379
  // src/commands.push.recovery.drop.ts
4216
4380
  init_config();
4217
4381
  import { rmSync as rmSync9 } from "node:fs";
4218
- import { join as join33 } from "node:path";
4382
+ import { join as join34 } from "node:path";
4219
4383
  function dropSessionFromStaged(sid, map) {
4220
4384
  const logicals = Object.keys(map.projects);
4221
4385
  if (logicals.length === 0) return false;
4222
4386
  const repo = repoHome();
4223
4387
  for (const logical of logicals) {
4224
- const jsonl = join33(repo, "shared", "projects", logical, `${sid}.jsonl`);
4225
- const dir = join33(repo, "shared", "projects", logical, sid);
4388
+ const jsonl = join34(repo, "shared", "projects", logical, `${sid}.jsonl`);
4389
+ const dir = join34(repo, "shared", "projects", logical, sid);
4226
4390
  rmSync9(jsonl, { force: true });
4227
4391
  rmSync9(dir, { recursive: true, force: true });
4228
4392
  }
@@ -4258,7 +4422,7 @@ function makeDefaultReadLine(repo) {
4258
4422
  if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep5)) {
4259
4423
  return null;
4260
4424
  }
4261
- const content = readFileSync12(target, "utf8");
4425
+ const content = readFileSync13(target, "utf8");
4262
4426
  const lines = content.split(/\r?\n/);
4263
4427
  const idx = line - 1;
4264
4428
  if (idx < 0 || idx >= lines.length) return null;
@@ -4379,10 +4543,10 @@ function applyThenRescan(scanVerdict, repoHome2) {
4379
4543
  return next;
4380
4544
  }
4381
4545
  function allowThenRescan(append, scanVerdict, repoHome2) {
4382
- const ignPath = join34(repoHome2, ".gitleaksignore");
4546
+ const ignPath = join35(repoHome2, ".gitleaksignore");
4383
4547
  let before;
4384
4548
  try {
4385
- before = readFileSync13(ignPath, "utf8");
4549
+ before = readFileSync14(ignPath, "utf8");
4386
4550
  } catch {
4387
4551
  before = null;
4388
4552
  }
@@ -4478,7 +4642,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
4478
4642
  `);
4479
4643
  }
4480
4644
  function resolveWorkerPath(deps = {}) {
4481
- const check = deps.existsSyncFn ?? existsSync28;
4645
+ const check = deps.existsSyncFn ?? existsSync29;
4482
4646
  const base = deps.baseUrl ?? import.meta.url;
4483
4647
  const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
4484
4648
  if (check(mjs)) return mjs;
@@ -4545,8 +4709,8 @@ function withSpinner(label, fn, deps) {
4545
4709
  // src/commands.doctor.gitleaks-version.ts
4546
4710
  init_color();
4547
4711
  import { execFileSync as execFileSync11 } from "node:child_process";
4548
- import { existsSync as existsSync29 } from "node:fs";
4549
- import { join as join35 } from "node:path";
4712
+ import { existsSync as existsSync30 } from "node:fs";
4713
+ import { join as join36 } from "node:path";
4550
4714
  init_config();
4551
4715
  var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
4552
4716
  var GITLEAKS_TIMEOUT_MS = 5e3;
@@ -4555,7 +4719,7 @@ function majorMinorOf(value) {
4555
4719
  return m === null ? null : [m[1], m[2]];
4556
4720
  }
4557
4721
  function readGitleaksVersion(run, tomlExists) {
4558
- const tomlPath = join35(repoHome(), ".gitleaks.toml");
4722
+ const tomlPath = join36(repoHome(), ".gitleaks.toml");
4559
4723
  const args = ["version"];
4560
4724
  if (tomlExists(tomlPath)) args.push("--config", tomlPath);
4561
4725
  try {
@@ -4567,7 +4731,7 @@ function readGitleaksVersion(run, tomlExists) {
4567
4731
  return null;
4568
4732
  }
4569
4733
  }
4570
- function reportGitleaksVersionCheck(section2, run = execFileSync11, tomlExists = existsSync29) {
4734
+ function reportGitleaksVersionCheck(section2, run = execFileSync11, tomlExists = existsSync30) {
4571
4735
  const raw = readGitleaksVersion(run, tomlExists);
4572
4736
  if (raw === null) return;
4573
4737
  const local = majorMinorOf(raw);
@@ -4790,8 +4954,8 @@ function gatherDoctorSections(opts) {
4790
4954
  reportHostKeyAlignment(host);
4791
4955
  reportRepoState(host);
4792
4956
  const links = section("Shared links");
4793
- const mapPath = join36(repoHome(), "path-map.json");
4794
- const rawMap = existsSync30(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
4957
+ const mapPath = join37(repoHome(), "path-map.json");
4958
+ const rawMap = existsSync31(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
4795
4959
  const map = rawMap ?? { projects: {} };
4796
4960
  reportSharedLinks(links, map);
4797
4961
  reportDroppedNamesMigration(links);
@@ -4888,8 +5052,8 @@ function parseDoctorArgs(args) {
4888
5052
  // src/commands.drop-session.ts
4889
5053
  init_config();
4890
5054
  import { execFileSync as execFileSync16 } from "node:child_process";
4891
- import { existsSync as existsSync32, readdirSync as readdirSync11, statSync as statSync8 } from "node:fs";
4892
- import { join as join38, relative as relative4 } from "node:path";
5055
+ import { existsSync as existsSync33, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
5056
+ import { join as join39, relative as relative4 } from "node:path";
4893
5057
 
4894
5058
  // src/commands.drop-session.git.ts
4895
5059
  import { execFileSync as execFileSync15 } from "node:child_process";
@@ -4931,8 +5095,8 @@ function isInIndex(rel, repo) {
4931
5095
  init_config();
4932
5096
  init_utils();
4933
5097
  init_utils_json();
4934
- import { existsSync as existsSync31 } from "node:fs";
4935
- import { join as join37 } from "node:path";
5098
+ import { existsSync as existsSync32 } from "node:fs";
5099
+ import { join as join38 } from "node:path";
4936
5100
  var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
4937
5101
  function reportScrubHint(id, matches) {
4938
5102
  const live = resolveLiveTranscript2(id, matches);
@@ -4948,8 +5112,8 @@ function reportScrubHint(id, matches) {
4948
5112
  }
4949
5113
  function resolveLiveTranscript2(id, matches) {
4950
5114
  try {
4951
- const mapPath = join37(repoHome(), "path-map.json");
4952
- if (!existsSync31(mapPath)) return null;
5115
+ const mapPath = join38(repoHome(), "path-map.json");
5116
+ if (!existsSync32(mapPath)) return null;
4953
5117
  const projects = readJson(mapPath).projects;
4954
5118
  const claude = claudeHome();
4955
5119
  for (const rel of matches) {
@@ -4957,8 +5121,8 @@ function resolveLiveTranscript2(id, matches) {
4957
5121
  if (logical === void 0) continue;
4958
5122
  const abs = projects[logical]?.[HOST];
4959
5123
  if (abs === void 0) continue;
4960
- const live = join37(claude, "projects", encodePath(abs), `${id}.jsonl`);
4961
- if (existsSync31(live)) return live;
5124
+ const live = join38(claude, "projects", encodePath(abs), `${id}.jsonl`);
5125
+ if (existsSync32(live)) return live;
4962
5126
  }
4963
5127
  return null;
4964
5128
  } catch {
@@ -4974,12 +5138,12 @@ function cmdDropSession(id) {
4974
5138
  process.exit(1);
4975
5139
  }
4976
5140
  const repo = repoHome();
4977
- if (!existsSync32(repo)) die(`repo not cloned at ${repo}`);
5141
+ if (!existsSync33(repo)) die(`repo not cloned at ${repo}`);
4978
5142
  const handle = acquireLock("drop-session");
4979
5143
  if (handle === null) process.exit(0);
4980
5144
  try {
4981
- const repoProjects = join38(repo, "shared", "projects");
4982
- if (!existsSync32(repoProjects)) {
5145
+ const repoProjects = join39(repo, "shared", "projects");
5146
+ if (!existsSync33(repoProjects)) {
4983
5147
  throw new NomadFatal(`no staged session matches ${id}`);
4984
5148
  }
4985
5149
  const matches = collectMatches(repoProjects, id, repo);
@@ -5001,13 +5165,13 @@ function cmdDropSession(id) {
5001
5165
  }
5002
5166
  function collectMatches(repoProjects, id, repo) {
5003
5167
  const matches = [];
5004
- for (const logical of readdirSync11(repoProjects)) {
5005
- const candidate = join38(repoProjects, logical, `${id}.jsonl`);
5006
- if (existsSync32(candidate)) {
5168
+ for (const logical of readdirSync12(repoProjects)) {
5169
+ const candidate = join39(repoProjects, logical, `${id}.jsonl`);
5170
+ if (existsSync33(candidate)) {
5007
5171
  matches.push(relative4(repo, candidate));
5008
5172
  }
5009
- const dir = join38(repoProjects, logical, id);
5010
- if (existsSync32(dir) && statSync8(dir).isDirectory()) {
5173
+ const dir = join39(repoProjects, logical, id);
5174
+ if (existsSync33(dir) && statSync9(dir).isDirectory()) {
5011
5175
  const dirRel = relative4(repo, dir);
5012
5176
  const staged = expandStagedDir(dirRel, repo);
5013
5177
  if (staged.length > 0) matches.push(...staged);
@@ -5042,8 +5206,8 @@ function unstageOne(rel, repo) {
5042
5206
  }
5043
5207
 
5044
5208
  // src/commands.pull.ts
5045
- import { existsSync as existsSync38, mkdirSync as mkdirSync9 } from "node:fs";
5046
- import { join as join45 } from "node:path";
5209
+ import { existsSync as existsSync39, mkdirSync as mkdirSync10 } from "node:fs";
5210
+ import { join as join46 } from "node:path";
5047
5211
 
5048
5212
  // src/commands.push.sections.ts
5049
5213
  init_color();
@@ -5138,20 +5302,20 @@ init_config();
5138
5302
 
5139
5303
  // src/extras-sync.ts
5140
5304
  init_config();
5141
- import { existsSync as existsSync35, statSync as statSync9 } from "node:fs";
5142
- import { join as join42 } from "node:path";
5305
+ import { existsSync as existsSync36, statSync as statSync10 } from "node:fs";
5306
+ import { join as join43 } from "node:path";
5143
5307
 
5144
5308
  // src/extras-sync.core.ts
5145
5309
  init_config();
5146
- import { cpSync as cpSync6, existsSync as existsSync33, lstatSync as lstatSync9, readdirSync as readdirSync12, rmSync as rmSync11 } from "node:fs";
5147
- import { basename, join as join39 } from "node:path";
5310
+ import { cpSync as cpSync6, existsSync as existsSync34, lstatSync as lstatSync9, readdirSync as readdirSync13, rmSync as rmSync11 } from "node:fs";
5311
+ import { basename, join as join40 } from "node:path";
5148
5312
  init_utils();
5149
5313
  init_utils_json();
5150
5314
  function loadValidatedExtras(opts) {
5151
5315
  const repo = repoHome();
5152
- const mapPath = join39(repo, "path-map.json");
5153
- const repoExtras = join39(repo, "shared", "extras");
5154
- if (!existsSync33(mapPath) || opts.requireRepoExtras === true && !existsSync33(repoExtras)) {
5316
+ const mapPath = join40(repo, "path-map.json");
5317
+ const repoExtras = join40(repo, "shared", "extras");
5318
+ if (!existsSync34(mapPath) || opts.requireRepoExtras === true && !existsSync34(repoExtras)) {
5155
5319
  if (opts.missingMsg !== void 0) log(opts.missingMsg);
5156
5320
  return null;
5157
5321
  }
@@ -5173,26 +5337,26 @@ function* eachExtrasTarget(v, counts) {
5173
5337
  counts.unmapped++;
5174
5338
  continue;
5175
5339
  }
5176
- for (const dirname8 of dirnames) {
5177
- if (!whitelist.includes(dirname8)) {
5340
+ for (const dirname10 of dirnames) {
5341
+ if (!whitelist.includes(dirname10)) {
5178
5342
  counts.skipped++;
5179
5343
  continue;
5180
5344
  }
5181
- yield { logical, localRoot, dirname: dirname8 };
5345
+ yield { logical, localRoot, dirname: dirname10 };
5182
5346
  }
5183
5347
  }
5184
5348
  }
5185
5349
  function stripCollidingDstSymlinks(src, dst, isExcluded) {
5186
- if (!existsSync33(dst)) return;
5187
- for (const name of readdirSync12(src)) {
5350
+ if (!existsSync34(dst)) return;
5351
+ for (const name of readdirSync13(src)) {
5188
5352
  if (isExcluded(name)) continue;
5189
- const dstPath = join39(dst, name);
5353
+ const dstPath = join40(dst, name);
5190
5354
  const dstStat = lstatSync9(dstPath, { throwIfNoEntry: false });
5191
5355
  if (dstStat === void 0) continue;
5192
5356
  if (dstStat.isSymbolicLink()) {
5193
5357
  rmSync11(dstPath, { recursive: true, force: true });
5194
- } else if (dstStat.isDirectory() && lstatSync9(join39(src, name)).isDirectory()) {
5195
- stripCollidingDstSymlinks(join39(src, name), dstPath, isExcluded);
5358
+ } else if (dstStat.isDirectory() && lstatSync9(join40(src, name)).isDirectory()) {
5359
+ stripCollidingDstSymlinks(join40(src, name), dstPath, isExcluded);
5196
5360
  }
5197
5361
  }
5198
5362
  }
@@ -5219,8 +5383,8 @@ function copyExtras(src, dst) {
5219
5383
  rmSync11(dst, { recursive: true, force: true });
5220
5384
  cpSync6(src, dst, { recursive: true, force: true, verbatimSymlinks: true });
5221
5385
  }
5222
- function extrasDenySet(dirname8) {
5223
- return dirname8 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
5386
+ function extrasDenySet(dirname10) {
5387
+ return dirname10 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
5224
5388
  }
5225
5389
  function copyExtrasFiltered(src, dst, blockSet) {
5226
5390
  rmSync11(dst, { recursive: true, force: true });
@@ -5232,17 +5396,17 @@ function copyExtrasFiltered(src, dst, blockSet) {
5232
5396
  });
5233
5397
  }
5234
5398
  function prunePreservingDenied(src, dst, blockSet) {
5235
- for (const name of readdirSync12(dst)) {
5399
+ for (const name of readdirSync13(dst)) {
5236
5400
  if (isDeniedName(blockSet, name)) continue;
5237
- const dstPath = join39(dst, name);
5238
- const srcStat = lstatSync9(join39(src, name), { throwIfNoEntry: false });
5401
+ const dstPath = join40(dst, name);
5402
+ const srcStat = lstatSync9(join40(src, name), { throwIfNoEntry: false });
5239
5403
  if (srcStat === void 0) {
5240
5404
  rmSync11(dstPath, { recursive: true, force: true });
5241
5405
  continue;
5242
5406
  }
5243
5407
  const dstStat = lstatSync9(dstPath);
5244
5408
  if (srcStat.isDirectory() && dstStat.isDirectory()) {
5245
- prunePreservingDenied(join39(src, name), dstPath, blockSet);
5409
+ prunePreservingDenied(join40(src, name), dstPath, blockSet);
5246
5410
  } else if (srcStat.isDirectory() !== dstStat.isDirectory()) {
5247
5411
  rmSync11(dstPath, { recursive: true, force: true });
5248
5412
  }
@@ -5263,17 +5427,17 @@ function copyExtrasFilteredPreserving(src, dst, blockSet) {
5263
5427
  });
5264
5428
  }
5265
5429
  function prunePreservingBy(src, dst, isPreserved) {
5266
- for (const name of readdirSync12(dst)) {
5430
+ for (const name of readdirSync13(dst)) {
5267
5431
  if (isPreserved(name)) continue;
5268
- const dstPath = join39(dst, name);
5269
- const srcStat = lstatSync9(join39(src, name), { throwIfNoEntry: false });
5432
+ const dstPath = join40(dst, name);
5433
+ const srcStat = lstatSync9(join40(src, name), { throwIfNoEntry: false });
5270
5434
  if (srcStat === void 0) {
5271
5435
  rmSync11(dstPath, { recursive: true, force: true });
5272
5436
  continue;
5273
5437
  }
5274
5438
  const dstStat = lstatSync9(dstPath);
5275
5439
  if (srcStat.isDirectory() && dstStat.isDirectory()) {
5276
- prunePreservingBy(join39(src, name), dstPath, isPreserved);
5440
+ prunePreservingBy(join40(src, name), dstPath, isPreserved);
5277
5441
  } else if (srcStat.isDirectory() !== dstStat.isDirectory()) {
5278
5442
  rmSync11(dstPath, { recursive: true, force: true });
5279
5443
  }
@@ -5300,11 +5464,11 @@ init_utils_json();
5300
5464
 
5301
5465
  // src/extras-sync.remap.ts
5302
5466
  init_config();
5303
- import { existsSync as existsSync34, mkdirSync as mkdirSync7, readdirSync as readdirSync13, realpathSync as realpathSync4, rmSync as rmSync12 } from "node:fs";
5304
- import { dirname as dirname7, join as join41, sep as sep7 } from "node:path";
5467
+ import { existsSync as existsSync35, mkdirSync as mkdirSync8, readdirSync as readdirSync14, realpathSync as realpathSync4, rmSync as rmSync12 } from "node:fs";
5468
+ import { dirname as dirname9, join as join42, sep as sep7 } from "node:path";
5305
5469
 
5306
5470
  // src/extras-sync.planning-diff.ts
5307
- import { join as join40, normalize as normalize2, sep as sep6 } from "node:path";
5471
+ import { join as join41, normalize as normalize2, sep as sep6 } from "node:path";
5308
5472
  init_utils();
5309
5473
  function processRecord(fields, i, changed, deleted) {
5310
5474
  const status = fields[i];
@@ -5356,7 +5520,7 @@ function planningDeleteTargets(opts) {
5356
5520
  const { deleted } = parsePlanningDiff(raw);
5357
5521
  const logicalPrefix = "shared/extras/" + logical + "/";
5358
5522
  const prefix = logicalPrefix + ".planning/";
5359
- const planningRoot = join40(localRoot, ".planning");
5523
+ const planningRoot = join41(localRoot, ".planning");
5360
5524
  const planningRootBoundary = planningRoot + sep6;
5361
5525
  const targets = [];
5362
5526
  for (const repoPath of deleted) {
@@ -5364,7 +5528,7 @@ function planningDeleteTargets(opts) {
5364
5528
  continue;
5365
5529
  }
5366
5530
  const remainder = repoPath.slice(logicalPrefix.length);
5367
- const candidate = join40(localRoot, remainder);
5531
+ const candidate = join41(localRoot, remainder);
5368
5532
  const resolved = normalize2(candidate);
5369
5533
  if (!resolved.startsWith(planningRootBoundary)) {
5370
5534
  throw new NomadFatal(
@@ -5385,7 +5549,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
5385
5549
  const would = [];
5386
5550
  for (const t of eachExtrasTarget(v, counts)) {
5387
5551
  const { src, dst } = paths(t);
5388
- if (!existsSync34(src)) continue;
5552
+ if (!existsSync35(src)) continue;
5389
5553
  const item2 = `${t.logical}/${t.dirname}`;
5390
5554
  if (dryRun) {
5391
5555
  would.push(item2);
@@ -5398,15 +5562,15 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
5398
5562
  return { ...counts, done, would };
5399
5563
  }
5400
5564
  function pruneEmptyAncestors(target, planningRoot) {
5401
- let dir = dirname7(target);
5565
+ let dir = dirname9(target);
5402
5566
  while (dir !== planningRoot && dir.startsWith(planningRoot + sep7)) {
5403
5567
  try {
5404
- if (readdirSync13(dir).length > 0) break;
5568
+ if (readdirSync14(dir).length > 0) break;
5405
5569
  rmSync12(dir, { recursive: true, force: true });
5406
5570
  } catch {
5407
5571
  break;
5408
5572
  }
5409
- dir = dirname7(dir);
5573
+ dir = dirname9(dir);
5410
5574
  }
5411
5575
  }
5412
5576
  function tryRealpath(dir) {
@@ -5420,8 +5584,8 @@ function isInsidePlanningRoot(parentReal, rootReal) {
5420
5584
  return parentReal === rootReal || parentReal.startsWith(rootReal + sep7);
5421
5585
  }
5422
5586
  function deletePlanningTarget(target, planningRoot, repoCounterpart) {
5423
- if (existsSync34(repoCounterpart)) return;
5424
- const parentReal = tryRealpath(dirname7(target));
5587
+ if (existsSync35(repoCounterpart)) return;
5588
+ const parentReal = tryRealpath(dirname9(target));
5425
5589
  if (parentReal === void 0) return;
5426
5590
  const rootReal = tryRealpath(planningRoot);
5427
5591
  if (rootReal === void 0) return;
@@ -5430,7 +5594,7 @@ function deletePlanningTarget(target, planningRoot, repoCounterpart) {
5430
5594
  pruneEmptyAncestors(target, planningRoot);
5431
5595
  }
5432
5596
  function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
5433
- const repoExtras = join41(repo, "shared", "extras");
5597
+ const repoExtras = join42(repo, "shared", "extras");
5434
5598
  for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
5435
5599
  if (t.dirname !== ".planning") continue;
5436
5600
  let raw;
@@ -5456,11 +5620,11 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
5456
5620
  }
5457
5621
  const targets = planningDeleteTargets({ raw, logical: t.logical, localRoot: t.localRoot });
5458
5622
  if (targets.length === 0) continue;
5459
- backupExtrasWrite(join41(t.localRoot, t.dirname), ts, t.localRoot);
5460
- const planningRoot = join41(t.localRoot, ".planning");
5623
+ backupExtrasWrite(join42(t.localRoot, t.dirname), ts, t.localRoot);
5624
+ const planningRoot = join42(t.localRoot, ".planning");
5461
5625
  for (const target of targets) {
5462
5626
  const relToLocal = target.slice(t.localRoot.length + sep7.length);
5463
- deletePlanningTarget(target, planningRoot, join41(repoExtras, t.logical, relToLocal));
5627
+ deletePlanningTarget(target, planningRoot, join42(repoExtras, t.logical, relToLocal));
5464
5628
  }
5465
5629
  }
5466
5630
  }
@@ -5469,14 +5633,14 @@ function remapExtrasPush(ts, opts = {}) {
5469
5633
  const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
5470
5634
  if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
5471
5635
  const repo = repoHome();
5472
- const repoExtras = join41(repo, "shared", "extras");
5473
- if (!dryRun) mkdirSync7(repoExtras, { recursive: true });
5636
+ const repoExtras = join42(repo, "shared", "extras");
5637
+ if (!dryRun) mkdirSync8(repoExtras, { recursive: true });
5474
5638
  const { unmapped, skipped, done, would } = runExtrasOp(
5475
5639
  v,
5476
5640
  dryRun,
5477
- ({ localRoot, logical, dirname: dirname8 }) => ({
5478
- src: join41(localRoot, dirname8),
5479
- dst: join41(repoExtras, logical, dirname8)
5641
+ ({ localRoot, logical, dirname: dirname10 }) => ({
5642
+ src: join42(localRoot, dirname10),
5643
+ dst: join42(repoExtras, logical, dirname10)
5480
5644
  }),
5481
5645
  (dst) => backupRepoWrite(dst, ts, repo),
5482
5646
  // Push copy routing per extra type:
@@ -5488,7 +5652,7 @@ function remapExtrasPush(ts, opts = {}) {
5488
5652
  // (enforceAllowList / blockSetFor in commands.push.allowlist.ts)
5489
5653
  // remains the hard security boundary.
5490
5654
  // All others: copyExtrasFiltered with per-extra denylist.
5491
- (src, dst, dirname8) => dirname8 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname8)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname8))
5655
+ (src, dst, dirname10) => dirname10 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname10)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname10))
5492
5656
  );
5493
5657
  return { unmapped, skipped, pushed: done, wouldPush: would };
5494
5658
  }
@@ -5504,9 +5668,9 @@ function remapExtrasPull(ts, opts = {}) {
5504
5668
  const { unmapped, skipped, done, would } = runExtrasOp(
5505
5669
  v,
5506
5670
  dryRun,
5507
- ({ localRoot, logical, dirname: dirname8 }) => ({
5508
- src: join41(repo, "shared", "extras", logical, dirname8),
5509
- dst: join41(localRoot, dirname8)
5671
+ ({ localRoot, logical, dirname: dirname10 }) => ({
5672
+ src: join42(repo, "shared", "extras", logical, dirname10),
5673
+ dst: join42(localRoot, dirname10)
5510
5674
  }),
5511
5675
  // Snapshot the host-side dst BEFORE copyExtras clobbers it. Anchor on
5512
5676
  // localRoot so the backup tree mirrors the project layout.
@@ -5519,11 +5683,11 @@ function remapExtrasPull(ts, opts = {}) {
5519
5683
  // removals via the git-diff D set. The filter is defense-in-depth
5520
5684
  // against a repo poisoned out-of-band.
5521
5685
  // All others: copyExtras (exact mirror; rarely carry host-local files).
5522
- (src, dst, dirname8) => {
5523
- if (dirname8 === ".claude")
5524
- return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname8));
5525
- if (dirname8 === ".planning")
5526
- return copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname8));
5686
+ (src, dst, dirname10) => {
5687
+ if (dirname10 === ".claude")
5688
+ return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname10));
5689
+ if (dirname10 === ".planning")
5690
+ return copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname10));
5527
5691
  return copyExtras(src, dst);
5528
5692
  }
5529
5693
  );
@@ -5547,20 +5711,20 @@ function divergenceCheckExtras(ts) {
5547
5711
  const v = loadValidatedExtras({});
5548
5712
  if (v === null) return;
5549
5713
  const counts = { unmapped: 0, skipped: 0 };
5550
- const backupRoot = join42(backupBase(), ts, "extras");
5714
+ const backupRoot = join43(backupBase(), ts, "extras");
5551
5715
  const repo = repoHome();
5552
- for (const { logical, localRoot, dirname: dirname8 } of eachExtrasTarget(v, counts)) {
5553
- const local = join42(localRoot, dirname8);
5554
- const repoEntry = join42(repo, "shared", "extras", logical, dirname8);
5555
- if (!existsSync35(local) || !existsSync35(repoEntry)) continue;
5716
+ for (const { logical, localRoot, dirname: dirname10 } of eachExtrasTarget(v, counts)) {
5717
+ const local = join43(localRoot, dirname10);
5718
+ const repoEntry = join43(repo, "shared", "extras", logical, dirname10);
5719
+ if (!existsSync36(local) || !existsSync36(repoEntry)) continue;
5556
5720
  const diff = listDivergingFiles(local, repoEntry);
5557
5721
  if (diff.length === 0) continue;
5558
- const projectBackupRoot = join42(backupRoot, encodePath(localRoot));
5722
+ const projectBackupRoot = join43(backupRoot, encodePath(localRoot));
5559
5723
  warn(
5560
5724
  divergenceWarnLine({
5561
- dirname: dirname8,
5725
+ dirname: dirname10,
5562
5726
  logical,
5563
- isDir: statSync9(local).isDirectory(),
5727
+ isDir: statSync10(local).isDirectory(),
5564
5728
  count: diff.length,
5565
5729
  projectBackupRoot
5566
5730
  })
@@ -5571,8 +5735,8 @@ function divergenceCheckExtras(ts) {
5571
5735
 
5572
5736
  // src/skills-sync.ts
5573
5737
  init_config();
5574
- import { existsSync as existsSync36, lstatSync as lstatSync10, mkdirSync as mkdirSync8, readdirSync as readdirSync14, rmSync as rmSync13 } from "node:fs";
5575
- import { join as join43 } from "node:path";
5738
+ import { existsSync as existsSync37, lstatSync as lstatSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync15, rmSync as rmSync13 } from "node:fs";
5739
+ import { join as join44 } from "node:path";
5576
5740
  init_utils_fs();
5577
5741
  function isGsdOwned(name) {
5578
5742
  return name.startsWith(GSD_PREFIX);
@@ -5581,7 +5745,7 @@ function isSkillExcluded(name) {
5581
5745
  return isGsdOwned(name) || isDeniedName(ALWAYS_NEVER_SYNC, name);
5582
5746
  }
5583
5747
  function copySkillsPush(src, dst) {
5584
- const srcNames = readdirSync14(src, { encoding: "utf8" });
5748
+ const srcNames = readdirSync15(src, { encoding: "utf8" });
5585
5749
  const blockSet = /* @__PURE__ */ new Set([
5586
5750
  ...srcNames.filter((n) => isGsdOwned(n)),
5587
5751
  ...ALWAYS_NEVER_SYNC
@@ -5592,30 +5756,30 @@ function copySkillsPull(src, dst) {
5592
5756
  copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
5593
5757
  }
5594
5758
  function syncSkillsPull(ts) {
5595
- const sharedSkills = join43(repoHome(), "shared", "skills");
5596
- if (!existsSync36(sharedSkills)) return;
5597
- const localSkills = join43(claudeHome(), "skills");
5759
+ const sharedSkills = join44(repoHome(), "shared", "skills");
5760
+ if (!existsSync37(sharedSkills)) return;
5761
+ const localSkills = join44(claudeHome(), "skills");
5598
5762
  const dstStat = lstatSync10(localSkills, { throwIfNoEntry: false });
5599
5763
  if (dstStat?.isSymbolicLink() === true) {
5600
5764
  backupBeforeWrite(localSkills, ts);
5601
5765
  rmSync13(localSkills, { recursive: true, force: true });
5602
- mkdirSync8(localSkills, { recursive: true });
5766
+ mkdirSync9(localSkills, { recursive: true });
5603
5767
  }
5604
5768
  copySkillsPull(sharedSkills, localSkills);
5605
5769
  }
5606
5770
  function syncSkillsPush() {
5607
- const localSkills = join43(claudeHome(), "skills");
5771
+ const localSkills = join44(claudeHome(), "skills");
5608
5772
  const stat = lstatSync10(localSkills, { throwIfNoEntry: false });
5609
5773
  if (stat === void 0) return;
5610
5774
  if (stat.isSymbolicLink()) return;
5611
- const sharedSkills = join43(repoHome(), "shared", "skills");
5775
+ const sharedSkills = join44(repoHome(), "shared", "skills");
5612
5776
  copySkillsPush(localSkills, sharedSkills);
5613
5777
  }
5614
5778
 
5615
5779
  // src/preview.ts
5616
5780
  init_config();
5617
- import { existsSync as existsSync37 } from "node:fs";
5618
- import { join as join44 } from "node:path";
5781
+ import { existsSync as existsSync38 } from "node:fs";
5782
+ import { join as join45 } from "node:path";
5619
5783
 
5620
5784
  // node_modules/diff/libesm/diff/base.js
5621
5785
  var Diff = class {
@@ -5901,7 +6065,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
5901
6065
  return lines.join("\n");
5902
6066
  }
5903
6067
  function readJsonOrNull(path) {
5904
- if (!existsSync37(path)) return null;
6068
+ if (!existsSync38(path)) return null;
5905
6069
  try {
5906
6070
  return readJson(path);
5907
6071
  } catch {
@@ -5915,12 +6079,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
5915
6079
  }
5916
6080
  const notes = [];
5917
6081
  const hostOverrides = readJsonOrNull(hostPath);
5918
- if (hostOverrides === null && existsSync37(hostPath)) {
6082
+ if (hostOverrides === null && existsSync38(hostPath)) {
5919
6083
  notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
5920
6084
  }
5921
6085
  const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
5922
6086
  const current = readJsonOrNull(settingsPath);
5923
- if (current === null && existsSync37(settingsPath)) {
6087
+ if (current === null && existsSync38(settingsPath)) {
5924
6088
  return { diff: "", notes: [...notes, "malformed; skipping diff"] };
5925
6089
  }
5926
6090
  const strippedCurrent = stripGsdHookEntries(current ?? {});
@@ -5961,9 +6125,9 @@ function computePreview(ts, map, verb = "pull") {
5961
6125
  onPreview: (e) => addItem(links, formatLinkRow(e))
5962
6126
  });
5963
6127
  const settingsResult = previewSettings(
5964
- join44(repo, "shared", "settings.base.json"),
5965
- join44(repo, "hosts", `${HOST}.json`),
5966
- join44(claude, "settings.json")
6128
+ join45(repo, "shared", "settings.base.json"),
6129
+ join45(repo, "hosts", `${HOST}.json`),
6130
+ join45(claude, "settings.json")
5967
6131
  );
5968
6132
  const settingsSection = buildSettingsSectionForPreview(settingsResult);
5969
6133
  const sessions = section("Sessions");
@@ -6164,8 +6328,8 @@ function cmdPull(opts = {}) {
6164
6328
  const forceRemote = opts.forceRemote === true;
6165
6329
  const repo = repoHome();
6166
6330
  const backup = backupBase();
6167
- if (!existsSync38(repo)) die(`repo not cloned at ${repo}`);
6168
- if (!existsSync38(join45(repo, "shared", "settings.base.json"))) {
6331
+ if (!existsSync39(repo)) die(`repo not cloned at ${repo}`);
6332
+ if (!existsSync39(join46(repo, "shared", "settings.base.json"))) {
6169
6333
  die("repo not initialized; run 'nomad init' to scaffold");
6170
6334
  }
6171
6335
  const handle = acquireLock("pull");
@@ -6174,9 +6338,9 @@ function cmdPull(opts = {}) {
6174
6338
  const ts = freshBackupTs(backup);
6175
6339
  handleWedge(repo, forceRemote);
6176
6340
  if (!dryRun) {
6177
- const backupRoot = join45(backup, ts);
6341
+ const backupRoot = join46(backup, ts);
6178
6342
  try {
6179
- mkdirSync9(backupRoot, { recursive: true });
6343
+ mkdirSync10(backupRoot, { recursive: true });
6180
6344
  } catch (err) {
6181
6345
  die(`could not create backup dir: ${err.message}`);
6182
6346
  }
@@ -6187,8 +6351,8 @@ function cmdPull(opts = {}) {
6187
6351
  const prePostHeads = capturePrePostHeads(repo, () => {
6188
6352
  gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
6189
6353
  });
6190
- const mapPath = join45(repo, "path-map.json");
6191
- const map = existsSync38(mapPath) ? readPathMap(mapPath) : { projects: {} };
6354
+ const mapPath = join46(repo, "path-map.json");
6355
+ const map = existsSync39(mapPath) ? readPathMap(mapPath) : { projects: {} };
6192
6356
  divergenceCheckExtras(ts);
6193
6357
  if (dryRun) {
6194
6358
  computePreview(ts, map, "pull");
@@ -6210,8 +6374,63 @@ function cmdPull(opts = {}) {
6210
6374
 
6211
6375
  // src/commands.push.ts
6212
6376
  init_config();
6213
- import { existsSync as existsSync40 } from "node:fs";
6214
- import { join as join47, relative as relative5 } from "node:path";
6377
+ import { existsSync as existsSync43 } from "node:fs";
6378
+ import { join as join51 } from "node:path";
6379
+
6380
+ // src/commands.push.selection.ts
6381
+ init_config();
6382
+ import { existsSync as existsSync40, statSync as statSync11 } from "node:fs";
6383
+ import { join as join47 } from "node:path";
6384
+ init_utils_json();
6385
+ function buildCurrentMap(map) {
6386
+ const current = {};
6387
+ if (map === null) return current;
6388
+ const claude = claudeHome();
6389
+ for (const [, hostMap] of Object.entries(map.projects)) {
6390
+ const localPath = hostMap[HOST];
6391
+ if (!localPath) continue;
6392
+ const localDir = join47(claude, "projects", encodePath(localPath));
6393
+ if (!existsSync40(localDir)) continue;
6394
+ for (const f of enumerateSourceFiles(localDir)) {
6395
+ const st = statSync11(f);
6396
+ current[f] = { size: st.size, mtime: st.mtimeMs };
6397
+ }
6398
+ }
6399
+ return current;
6400
+ }
6401
+ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
6402
+ const current = buildCurrentMap(map);
6403
+ const fullRescan = shouldFullRescan(old, scannerVersion, configHash, fullScan);
6404
+ const hashCache = /* @__PURE__ */ new Map();
6405
+ const cachedHash = (p) => {
6406
+ const hit = hashCache.get(p);
6407
+ if (hit !== void 0) return hit;
6408
+ const h2 = hashFile(p);
6409
+ hashCache.set(p, h2);
6410
+ return h2;
6411
+ };
6412
+ const delta = fullRescan ? { changed: new Set(Object.keys(current)), deleted: [] } : diffManifest(old, current, cachedHash);
6413
+ const files = {};
6414
+ for (const [key, meta] of Object.entries(current)) {
6415
+ const hash = delta.changed.has(key) ? cachedHash(key) : old.files[key].hash;
6416
+ files[key] = { size: meta.size, mtime: meta.mtime, hash };
6417
+ }
6418
+ return {
6419
+ selection: fullRescan ? void 0 : delta,
6420
+ newManifest: buildManifest(files, scannerVersion, configHash)
6421
+ };
6422
+ }
6423
+ function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
6424
+ const map = existsSync40(mapPath) ? readPathMap(mapPath) : null;
6425
+ const { selection, newManifest } = computePushSelection(
6426
+ map,
6427
+ old,
6428
+ scannerVersion,
6429
+ configHash,
6430
+ fullScan
6431
+ );
6432
+ return { map, selection, newManifest };
6433
+ }
6215
6434
 
6216
6435
  // src/commands.push.allowlist.ts
6217
6436
  init_config();
@@ -6302,6 +6521,85 @@ function enforceAllowList(statusPorcelain, map) {
6302
6521
  throw new NomadFatal("push allow-list violations");
6303
6522
  }
6304
6523
 
6524
+ // src/commands.push.settings.ts
6525
+ init_config();
6526
+ import { existsSync as existsSync41 } from "node:fs";
6527
+ import { join as join48 } from "node:path";
6528
+ init_utils();
6529
+ init_utils_fs();
6530
+ init_utils_json();
6531
+ function stripGsdHooksFromBase(repo, backup) {
6532
+ const basePath = join48(repo, "shared", "settings.base.json");
6533
+ if (!existsSync41(basePath)) return;
6534
+ let base;
6535
+ try {
6536
+ base = readJson(basePath);
6537
+ } catch {
6538
+ return;
6539
+ }
6540
+ if (!baseHasGsdHookEntries(base)) return;
6541
+ const stripped = stripGsdHookEntries(base);
6542
+ const ts = freshBackupTs(backup);
6543
+ backupRepoWrite(basePath, ts, repo);
6544
+ writeJsonAtomic(basePath, stripped);
6545
+ }
6546
+ function reportSettingsAheadDrift(repo) {
6547
+ const basePath = join48(repo, "shared", "settings.base.json");
6548
+ if (!existsSync41(basePath)) return;
6549
+ const settingsPath = join48(claudeHome(), "settings.json");
6550
+ if (!existsSync41(settingsPath)) return;
6551
+ try {
6552
+ const base = readJson(basePath);
6553
+ const hostPath = join48(repo, "hosts", `${HOST}.json`);
6554
+ const overrides = existsSync41(hostPath) ? readJson(hostPath) : {};
6555
+ const merged = deepMerge(base, overrides);
6556
+ const settings = readJson(settingsPath);
6557
+ const { ahead } = classifySettingsDrift(merged, settings);
6558
+ const { promotable } = partitionByCaptureExclusion(ahead);
6559
+ if (promotable.length === 0) return;
6560
+ const { phrase, pronoun, verb } = describeSettings(promotable);
6561
+ warn(
6562
+ `your settings.json has ${phrase} that ${verb} not yet in the repo; run 'nomad capture-settings' to save ${pronoun} (or 'nomad capture-settings --host' for host-specific values).`
6563
+ );
6564
+ } catch {
6565
+ }
6566
+ }
6567
+
6568
+ // src/commands.push.guards.ts
6569
+ init_push_checks();
6570
+ init_utils();
6571
+ import { join as join49, relative as relative5 } from "node:path";
6572
+ function guardGitlinks(repo) {
6573
+ const gitlinks = findGitlinks(join49(repo, "shared"));
6574
+ if (gitlinks.length === 0) return;
6575
+ for (const p of gitlinks) {
6576
+ const rel = relative5(repo, p);
6577
+ fail(`gitlink: ${rel} would push as submodule (run: rm -rf ${rel} or remove the nested repo)`);
6578
+ }
6579
+ const noun = gitlinks.length === 1 ? "entry" : "entries";
6580
+ throw new NomadFatal(
6581
+ `gitlink trap: ${gitlinks.length} nested .git ${noun} in shared/; remove before retry`
6582
+ );
6583
+ }
6584
+ function guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule) {
6585
+ const hasAllow = allowAll || allowRule !== void 0;
6586
+ const wantsResolution = redactAll || hasAllow;
6587
+ if (redactAll && hasAllow) {
6588
+ die("--redact-all, --allow-all, and --allow are mutually exclusive resolution modes");
6589
+ }
6590
+ if (allowAll && allowRule !== void 0) {
6591
+ die("--redact-all, --allow-all, and --allow are mutually exclusive resolution modes");
6592
+ }
6593
+ if (dryRun && wantsResolution) {
6594
+ die(
6595
+ "--redact-all, --allow-all, and --allow cannot be combined with --dry-run (dry-run resolves nothing)"
6596
+ );
6597
+ }
6598
+ }
6599
+
6600
+ // src/commands.push.steps.ts
6601
+ init_config();
6602
+
6305
6603
  // src/push-global-config.ts
6306
6604
  init_config();
6307
6605
  import { execFileSync as execFileSync18 } from "node:child_process";
@@ -6374,24 +6672,36 @@ function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
6374
6672
  return changes;
6375
6673
  }
6376
6674
 
6377
- // src/commands.push.ts
6675
+ // src/commands.push.steps.ts
6378
6676
  init_push_leak_verdict();
6379
- init_push_checks();
6380
6677
 
6381
6678
  // src/push-preview.ts
6382
6679
  init_color();
6383
6680
  init_config();
6384
6681
  init_config_sharedDirs_guard();
6385
6682
  import { randomBytes as randomBytes2 } from "node:crypto";
6386
- import { copyFileSync, existsSync as existsSync39, mkdirSync as mkdirSync10, readdirSync as readdirSync15, rmSync as rmSync14 } from "node:fs";
6683
+ import { copyFileSync, existsSync as existsSync42, mkdirSync as mkdirSync11, readdirSync as readdirSync16, rmSync as rmSync14 } from "node:fs";
6387
6684
  import { homedir as homedir5 } from "node:os";
6388
- import { join as join46 } from "node:path";
6685
+ import { join as join50, relative as relative6, sep as sep8 } from "node:path";
6389
6686
  init_push_leak_verdict();
6390
6687
  init_push_gitleaks();
6391
6688
  init_utils_fs();
6392
6689
  init_utils_json();
6393
6690
  var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
6394
- function stageSessions(tmpRoot, map) {
6691
+ function stageSessionDir(localDir, dstDir, changed) {
6692
+ if (changed !== void 0) {
6693
+ const prefix = `${localDir}${sep8}`;
6694
+ const matching = [...changed].filter((p) => p.startsWith(prefix));
6695
+ if (matching.length === 0) return false;
6696
+ for (const src of matching) {
6697
+ copyFileAtomic(src, join50(dstDir, relative6(localDir, src)));
6698
+ }
6699
+ return true;
6700
+ }
6701
+ copyDirJsonlOnly(localDir, dstDir);
6702
+ return true;
6703
+ }
6704
+ function stageSessions(tmpRoot, map, changed) {
6395
6705
  if (typeof map.projects !== "object" || map.projects === null) return 0;
6396
6706
  const reverse = /* @__PURE__ */ new Map();
6397
6707
  for (const [logical, hosts] of Object.entries(map.projects)) {
@@ -6400,14 +6710,15 @@ function stageSessions(tmpRoot, map) {
6400
6710
  if (!p || p === "TBD") continue;
6401
6711
  reverse.set(encodePath(p), logical);
6402
6712
  }
6403
- const localProjects = join46(claudeHome(), "projects");
6404
- if (!existsSync39(localProjects)) return 0;
6713
+ const localProjects = join50(claudeHome(), "projects");
6714
+ if (!existsSync42(localProjects)) return 0;
6405
6715
  let staged = 0;
6406
- for (const dir of readdirSync15(localProjects)) {
6716
+ for (const dir of readdirSync16(localProjects)) {
6407
6717
  const logical = reverse.get(dir);
6408
6718
  if (!logical) continue;
6409
- copyDirJsonlOnly(join46(localProjects, dir), join46(tmpRoot, "shared", "projects", logical));
6410
- staged++;
6719
+ const localDir = join50(localProjects, dir);
6720
+ const dstDir = join50(tmpRoot, "shared", "projects", logical);
6721
+ if (stageSessionDir(localDir, dstDir, changed)) staged++;
6411
6722
  }
6412
6723
  return staged;
6413
6724
  }
@@ -6420,31 +6731,31 @@ function stageExtras(tmpRoot, map) {
6420
6731
  assertSafeLogical(logical);
6421
6732
  const localRoot = map.projects[logical]?.[HOST];
6422
6733
  if (!localRoot || localRoot === "TBD") continue;
6423
- for (const dirname8 of dirnames) {
6424
- if (!whitelist.includes(dirname8)) continue;
6425
- const src = join46(localRoot, dirname8);
6426
- if (!existsSync39(src)) continue;
6427
- const dst = join46(tmpRoot, "shared", "extras", logical, dirname8);
6734
+ for (const dirname10 of dirnames) {
6735
+ if (!whitelist.includes(dirname10)) continue;
6736
+ const src = join50(localRoot, dirname10);
6737
+ if (!existsSync42(src)) continue;
6738
+ const dst = join50(tmpRoot, "shared", "extras", logical, dirname10);
6428
6739
  copyExtras(src, dst);
6429
6740
  staged++;
6430
6741
  }
6431
6742
  }
6432
6743
  return staged;
6433
6744
  }
6434
- function previewPushLeaks(map) {
6435
- const cacheDir = join46(homedir5(), ".cache", "claude-nomad");
6436
- mkdirSync10(cacheDir, { recursive: true });
6745
+ function previewPushLeaks(map, opts = {}) {
6746
+ const cacheDir = join50(homedir5(), ".cache", "claude-nomad");
6747
+ mkdirSync11(cacheDir, { recursive: true });
6437
6748
  const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
6438
- const tmpRoot = join46(cacheDir, `push-preview-tree-${stamp}`);
6749
+ const tmpRoot = join50(cacheDir, `push-preview-tree-${stamp}`);
6439
6750
  try {
6440
- const sessionCount = stageSessions(tmpRoot, map);
6751
+ const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
6441
6752
  const extrasCount = stageExtras(tmpRoot, map);
6442
6753
  if (sessionCount + extrasCount === 0) {
6443
6754
  return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
6444
6755
  }
6445
- const ignoreFile = join46(repoHome(), ".gitleaksignore");
6446
- if (existsSync39(ignoreFile)) {
6447
- copyFileSync(ignoreFile, join46(tmpRoot, ".gitleaksignore"));
6756
+ const ignoreFile = join50(repoHome(), ".gitleaksignore");
6757
+ if (existsSync42(ignoreFile)) {
6758
+ copyFileSync(ignoreFile, join50(tmpRoot, ".gitleaksignore"));
6448
6759
  }
6449
6760
  let findings;
6450
6761
  try {
@@ -6461,59 +6772,9 @@ function previewPushLeaks(map) {
6461
6772
  }
6462
6773
  }
6463
6774
 
6464
- // src/commands.push.ts
6775
+ // src/commands.push.steps.ts
6465
6776
  init_utils();
6466
- init_utils_fs();
6467
- init_utils_json();
6468
- function stripGsdHooksFromBase(repo, backup) {
6469
- const basePath = join47(repo, "shared", "settings.base.json");
6470
- if (!existsSync40(basePath)) return;
6471
- let base;
6472
- try {
6473
- base = readJson(basePath);
6474
- } catch {
6475
- return;
6476
- }
6477
- if (!baseHasGsdHookEntries(base)) return;
6478
- const stripped = stripGsdHookEntries(base);
6479
- const ts = freshBackupTs(backup);
6480
- backupRepoWrite(basePath, ts, repo);
6481
- writeJsonAtomic(basePath, stripped);
6482
- }
6483
- function reportSettingsAheadDrift(repo) {
6484
- const basePath = join47(repo, "shared", "settings.base.json");
6485
- if (!existsSync40(basePath)) return;
6486
- const settingsPath = join47(claudeHome(), "settings.json");
6487
- if (!existsSync40(settingsPath)) return;
6488
- try {
6489
- const base = readJson(basePath);
6490
- const hostPath = join47(repo, "hosts", `${HOST}.json`);
6491
- const overrides = existsSync40(hostPath) ? readJson(hostPath) : {};
6492
- const merged = deepMerge(base, overrides);
6493
- const settings = readJson(settingsPath);
6494
- const { ahead } = classifySettingsDrift(merged, settings);
6495
- const { promotable } = partitionByCaptureExclusion(ahead);
6496
- if (promotable.length === 0) return;
6497
- const { phrase, pronoun, verb } = describeSettings(promotable);
6498
- warn(
6499
- `your settings.json has ${phrase} that ${verb} not yet in the repo; run 'nomad capture-settings' to save ${pronoun} (or 'nomad capture-settings --host' for host-specific values).`
6500
- );
6501
- } catch {
6502
- }
6503
- }
6504
- function guardGitlinks(repo) {
6505
- const gitlinks = findGitlinks(join47(repo, "shared"));
6506
- if (gitlinks.length === 0) return;
6507
- for (const p of gitlinks) {
6508
- const rel = relative5(repo, p);
6509
- fail(`gitlink: ${rel} would push as submodule (run: rm -rf ${rel} or remove the nested repo)`);
6510
- }
6511
- const noun = gitlinks.length === 1 ? "entry" : "entries";
6512
- throw new NomadFatal(
6513
- `gitlink trap: ${gitlinks.length} nested .git ${noun} in shared/; remove before retry`
6514
- );
6515
- }
6516
- async function commitAndPush(st, ts, map, redactAll, allowAll, allowRule, repo) {
6777
+ async function commitAndPush(st, ts, map, resolution, repo, newManifest) {
6517
6778
  gitOrFatal(["add", "-A"], "git add", repo);
6518
6779
  const staged = parsePorcelainZ2(gitStatusPorcelainZ(repo));
6519
6780
  const toDrop = staged.filter((p) => isGsdDropped(p));
@@ -6529,55 +6790,61 @@ async function commitAndPush(st, ts, map, redactAll, allowAll, allowRule, repo)
6529
6790
  let verdict = withSpinner("Scanning for secrets", () => scanPushVerdict(repo));
6530
6791
  if (verdict.leak) {
6531
6792
  renderPushTree(st, verdict);
6532
- verdict = await resolveLeakFindings(verdict, ts, map, { redactAll, allowAll, allowRule });
6793
+ verdict = await resolveLeakFindings(verdict, ts, map, resolution);
6533
6794
  }
6534
6795
  gitOrFatal(["commit", "-m", `chore: sync from ${HOST}`], "git commit", repo);
6535
6796
  withSpinner("Pushing", () => gitOrFatal(["push"], "git push", repo));
6797
+ try {
6798
+ writeManifest(manifestPath(), newManifest);
6799
+ } catch (err) {
6800
+ warn(`could not write push manifest (next push will full-rescan): ${String(err)}`);
6801
+ }
6536
6802
  renderPushTree(st, verdict);
6537
6803
  }
6538
- function runDryRunPreview(st, map, repo) {
6804
+ function runDryRunPreview(st, map, repo, selection) {
6539
6805
  st.globalConfig = collectGlobalConfigChanges(repo, HOST, { staged: false });
6540
6806
  if (map === null) {
6541
6807
  renderNoScanTree(st, { noMapHint: true });
6542
6808
  return;
6543
6809
  }
6544
- const verdict = previewPushLeaks(map);
6810
+ const verdict = withSpinner("Scanning for secrets", () => previewPushLeaks(map, { selection }));
6545
6811
  renderPushTree(st, verdict);
6546
6812
  if (verdict.recovery !== null) fail(verdict.recovery);
6547
6813
  }
6548
- function guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule) {
6549
- const hasAllow = allowAll || allowRule !== void 0;
6550
- const wantsResolution = redactAll || hasAllow;
6551
- if (redactAll && hasAllow) {
6552
- die("--redact-all, --allow-all, and --allow are mutually exclusive resolution modes");
6553
- }
6554
- if (allowAll && allowRule !== void 0) {
6555
- die("--redact-all, --allow-all, and --allow are mutually exclusive resolution modes");
6556
- }
6557
- if (dryRun && wantsResolution) {
6558
- die(
6559
- "--redact-all, --allow-all, and --allow cannot be combined with --dry-run (dry-run resolves nothing)"
6560
- );
6561
- }
6562
- }
6814
+
6815
+ // src/commands.push.ts
6816
+ init_push_checks();
6817
+ init_utils();
6818
+ init_utils_fs();
6563
6819
  async function cmdPush(opts = {}) {
6564
6820
  const dryRun = opts.dryRun === true;
6565
6821
  const redactAll = opts.redactAll === true;
6566
6822
  const allowAll = opts.allowAll === true;
6567
6823
  const allowRule = opts.allowRule;
6824
+ const fullScan = opts.fullScan === true;
6568
6825
  guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
6569
6826
  const repo = repoHome();
6570
6827
  const backup = backupBase();
6571
- if (!existsSync40(repo)) die(`repo not cloned at ${repo}`);
6828
+ if (!existsSync43(repo)) die(`repo not cloned at ${repo}`);
6572
6829
  const handle = acquireLock("push");
6573
6830
  if (handle === null) process.exit(0);
6574
6831
  try {
6575
6832
  console.log(dryRun ? `push on host=${HOST} (dry-run)` : `push on host=${HOST}`);
6576
6833
  reportSettingsAheadDrift(repo);
6577
- probeGitleaks();
6834
+ const scannerVersion = probeGitleaks();
6835
+ const configHash = computeConfigHash();
6836
+ const old = readManifest(manifestPath());
6837
+ const mapPath = join51(repo, "path-map.json");
6838
+ const { map, selection, newManifest } = loadSelectionForPush(
6839
+ mapPath,
6840
+ old,
6841
+ scannerVersion,
6842
+ configHash,
6843
+ fullScan
6844
+ );
6578
6845
  withSpinner("Rebasing onto origin", () => rebaseBeforePush(repo));
6579
6846
  const ts = freshBackupTs(backup);
6580
- const remap = withSpinner("Syncing sessions", () => remapPush(ts, { dryRun }));
6847
+ const remap = withSpinner("Syncing sessions", () => remapPush(ts, { dryRun, selection }));
6581
6848
  const extras = withSpinner("Syncing extras", () => remapExtrasPush(ts, { dryRun }));
6582
6849
  if (!dryRun) {
6583
6850
  syncSkillsPush();
@@ -6591,15 +6858,13 @@ async function cmdPush(opts = {}) {
6591
6858
  renderNoScanTree(st);
6592
6859
  return;
6593
6860
  }
6594
- const mapPath = join47(repo, "path-map.json");
6595
- if (!existsSync40(mapPath)) {
6596
- if (dryRun) return runDryRunPreview(st, null, repo);
6597
- die("path-map.json missing, cannot enforce push allow-list");
6861
+ if (map === null) {
6862
+ if (dryRun) return runDryRunPreview(st, null, repo, selection);
6863
+ return die("path-map.json missing, cannot enforce push allow-list");
6598
6864
  }
6599
- const map = readPathMap(mapPath);
6600
6865
  if (status) enforceAllowList(status, map);
6601
- if (dryRun) return runDryRunPreview(st, map, repo);
6602
- await commitAndPush(st, ts, map, redactAll, allowAll, allowRule, repo);
6866
+ if (dryRun) return runDryRunPreview(st, map, repo, selection);
6867
+ await commitAndPush(st, ts, map, { redactAll, allowAll, allowRule }, repo, newManifest);
6603
6868
  } catch (err) {
6604
6869
  if (err instanceof NomadFatal) {
6605
6870
  fail(err.message);
@@ -6656,18 +6921,18 @@ init_config();
6656
6921
 
6657
6922
  // src/diff.ts
6658
6923
  init_config();
6659
- import { existsSync as existsSync41 } from "node:fs";
6660
- import { join as join48 } from "node:path";
6924
+ import { existsSync as existsSync44 } from "node:fs";
6925
+ import { join as join52 } from "node:path";
6661
6926
  init_utils();
6662
6927
  init_utils_fs();
6663
6928
  init_utils_json();
6664
6929
  function cmdDiff() {
6665
6930
  try {
6666
6931
  const repo = repoHome();
6667
- if (!existsSync41(repo)) die(`repo not cloned at ${repo}`);
6932
+ if (!existsSync44(repo)) die(`repo not cloned at ${repo}`);
6668
6933
  const ts = freshBackupTs(backupBase());
6669
- const mapPath = join48(repo, "path-map.json");
6670
- const map = existsSync41(mapPath) ? readPathMap(mapPath) : { projects: {} };
6934
+ const mapPath = join52(repo, "path-map.json");
6935
+ const map = existsSync44(mapPath) ? readPathMap(mapPath) : { projects: {} };
6671
6936
  computePreview(ts, map, "diff");
6672
6937
  } catch (err) {
6673
6938
  if (err instanceof NomadFatal) {
@@ -6681,8 +6946,8 @@ function cmdDiff() {
6681
6946
 
6682
6947
  // src/init.ts
6683
6948
  init_config();
6684
- import { existsSync as existsSync43, mkdirSync as mkdirSync11, writeFileSync as writeFileSync6 } from "node:fs";
6685
- import { join as join50 } from "node:path";
6949
+ import { existsSync as existsSync46, mkdirSync as mkdirSync12, writeFileSync as writeFileSync6 } from "node:fs";
6950
+ import { join as join54 } from "node:path";
6686
6951
 
6687
6952
  // src/init.gh-onboard.ts
6688
6953
  init_config();
@@ -6764,33 +7029,33 @@ init_config();
6764
7029
  init_utils();
6765
7030
  init_utils_fs();
6766
7031
  init_utils_json();
6767
- import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync42, rmSync as rmSync15, statSync as statSync10 } from "node:fs";
6768
- import { join as join49 } from "node:path";
7032
+ import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync45, rmSync as rmSync15, statSync as statSync12 } from "node:fs";
7033
+ import { join as join53 } from "node:path";
6769
7034
  function snapshotIntoShared(map) {
6770
7035
  const repo = repoHome();
6771
7036
  const claude = claudeHome();
6772
7037
  for (const name of allSharedLinks(map)) {
6773
- const src = join49(claude, name);
6774
- if (!existsSync42(src)) continue;
6775
- const dst = join49(repo, "shared", name);
6776
- if (statSync10(src).isDirectory()) {
6777
- const gk = join49(dst, ".gitkeep");
6778
- if (existsSync42(gk)) rmSync15(gk);
7038
+ const src = join53(claude, name);
7039
+ if (!existsSync45(src)) continue;
7040
+ const dst = join53(repo, "shared", name);
7041
+ if (statSync12(src).isDirectory()) {
7042
+ const gk = join53(dst, ".gitkeep");
7043
+ if (existsSync45(gk)) rmSync15(gk);
6779
7044
  cpSync7(src, dst, { recursive: true, force: false, errorOnExist: true });
6780
7045
  } else {
6781
7046
  copyFileSync2(src, dst);
6782
7047
  }
6783
7048
  log(`snapshotted shared/${name} from ${src}`);
6784
7049
  }
6785
- const userSettings = join49(claude, "settings.json");
6786
- if (existsSync42(userSettings)) {
7050
+ const userSettings = join53(claude, "settings.json");
7051
+ if (existsSync45(userSettings)) {
6787
7052
  let parsed;
6788
7053
  try {
6789
7054
  parsed = readJson(userSettings);
6790
7055
  } catch (err) {
6791
7056
  return die(`malformed ${userSettings}: ${err.message}`);
6792
7057
  }
6793
- const hostFile = join49(repo, "hosts", `${HOST}.json`);
7058
+ const hostFile = join53(repo, "hosts", `${HOST}.json`);
6794
7059
  writeJsonAtomic(hostFile, parsed);
6795
7060
  log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
6796
7061
  }
@@ -6803,14 +7068,14 @@ var SHARED_CLAUDE_MD = "<!-- claude-nomad shared CLAUDE.md; symlinked into ~/.cl
6803
7068
  var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
6804
7069
  function preflightConflict(repoHome2) {
6805
7070
  const candidates = [
6806
- join50(repoHome2, "shared", "settings.base.json"),
6807
- join50(repoHome2, "shared", "CLAUDE.md"),
6808
- join50(repoHome2, "path-map.json"),
6809
- join50(repoHome2, "hosts"),
6810
- join50(repoHome2, "shared")
7071
+ join54(repoHome2, "shared", "settings.base.json"),
7072
+ join54(repoHome2, "shared", "CLAUDE.md"),
7073
+ join54(repoHome2, "path-map.json"),
7074
+ join54(repoHome2, "hosts"),
7075
+ join54(repoHome2, "shared")
6811
7076
  ];
6812
7077
  for (const c of candidates) {
6813
- if (existsSync43(c)) return c;
7078
+ if (existsSync46(c)) return c;
6814
7079
  }
6815
7080
  return null;
6816
7081
  }
@@ -6822,31 +7087,31 @@ function cmdInit(opts = {}) {
6822
7087
  const keepActions = opts.keepActions === true;
6823
7088
  const repo = repoHome();
6824
7089
  const claude = claudeHome();
6825
- mkdirSync11(repo, { recursive: true });
7090
+ mkdirSync12(repo, { recursive: true });
6826
7091
  const conflict = preflightConflict(repo);
6827
7092
  if (conflict !== null) {
6828
7093
  die(`already initialized; refusing to clobber ${conflict}`);
6829
7094
  }
6830
7095
  ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
6831
- mkdirSync11(join50(repo, "shared"), { recursive: true });
6832
- mkdirSync11(join50(repo, "hosts"), { recursive: true });
7096
+ mkdirSync12(join54(repo, "shared"), { recursive: true });
7097
+ mkdirSync12(join54(repo, "hosts"), { recursive: true });
6833
7098
  for (const name of SHARED_KEEP_DIRS) {
6834
- mkdirSync11(join50(repo, "shared", name), { recursive: true });
7099
+ mkdirSync12(join54(repo, "shared", name), { recursive: true });
6835
7100
  }
6836
- const userClaudeMd = join50(claude, "CLAUDE.md");
6837
- if (!snapshot || !existsSync43(userClaudeMd)) {
6838
- writeFileSync6(join50(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
7101
+ const userClaudeMd = join54(claude, "CLAUDE.md");
7102
+ if (!snapshot || !existsSync46(userClaudeMd)) {
7103
+ writeFileSync6(join54(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
6839
7104
  item("created shared/CLAUDE.md");
6840
7105
  }
6841
7106
  for (const name of SHARED_KEEP_DIRS) {
6842
- writeFileSync6(join50(repo, "shared", name, ".gitkeep"), "");
7107
+ writeFileSync6(join54(repo, "shared", name, ".gitkeep"), "");
6843
7108
  item(`created shared/${name}/.gitkeep`);
6844
7109
  }
6845
- writeFileSync6(join50(repo, "hosts", ".gitkeep"), "");
7110
+ writeFileSync6(join54(repo, "hosts", ".gitkeep"), "");
6846
7111
  item("created hosts/.gitkeep");
6847
- writeJsonAtomic(join50(repo, "shared", "settings.base.json"), {});
7112
+ writeJsonAtomic(join54(repo, "shared", "settings.base.json"), {});
6848
7113
  item("created shared/settings.base.json");
6849
- writeJsonAtomic(join50(repo, "path-map.json"), { projects: {} });
7114
+ writeJsonAtomic(join54(repo, "path-map.json"), { projects: {} });
6850
7115
  item("created path-map.json");
6851
7116
  if (snapshot) {
6852
7117
  snapshotIntoShared({ projects: {} });
@@ -6912,21 +7177,21 @@ function maybeDisableRepoActions(repoHome2, run) {
6912
7177
  // src/init.prompt.ts
6913
7178
  init_config();
6914
7179
  init_utils();
6915
- import { existsSync as existsSync44, readdirSync as readdirSync16, statSync as statSync11 } from "node:fs";
6916
- import { join as join51 } from "node:path";
7180
+ import { existsSync as existsSync47, readdirSync as readdirSync17, statSync as statSync13 } from "node:fs";
7181
+ import { join as join55 } from "node:path";
6917
7182
  import { createInterface as createInterface3 } from "node:readline/promises";
6918
7183
  function nonEmptyExists(path) {
6919
- if (!existsSync44(path)) return false;
7184
+ if (!existsSync47(path)) return false;
6920
7185
  try {
6921
- if (statSync11(path).isDirectory()) return readdirSync16(path).length > 0;
7186
+ if (statSync13(path).isDirectory()) return readdirSync17(path).length > 0;
6922
7187
  return true;
6923
7188
  } catch {
6924
7189
  return false;
6925
7190
  }
6926
7191
  }
6927
7192
  function hasExistingClaudeConfig(claudeHome2) {
6928
- if (existsSync44(join51(claudeHome2, "settings.json"))) return true;
6929
- return SHARED_LINKS.some((name) => nonEmptyExists(join51(claudeHome2, name)));
7193
+ if (existsSync47(join55(claudeHome2, "settings.json"))) return true;
7194
+ return SHARED_LINKS.some((name) => nonEmptyExists(join55(claudeHome2, name)));
6930
7195
  }
6931
7196
  async function confirmSnapshotDefault(claudeHome2) {
6932
7197
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
@@ -7176,6 +7441,8 @@ function applyPushToken(argv, i, st) {
7176
7441
  return applyBool(st.allowAll, () => st.allowAll = true);
7177
7442
  case "--allow":
7178
7443
  return applyAllow2(argv, i, st);
7444
+ case "--full-scan":
7445
+ return applyBool(st.fullScan, () => st.fullScan = true);
7179
7446
  default:
7180
7447
  return REJECT;
7181
7448
  }
@@ -7185,7 +7452,8 @@ function parsePushArgs(argv) {
7185
7452
  dryRun: false,
7186
7453
  redactAll: false,
7187
7454
  allowAll: false,
7188
- allowRule: void 0
7455
+ allowRule: void 0,
7456
+ fullScan: false
7189
7457
  };
7190
7458
  let i = 3;
7191
7459
  while (i < argv.length) {
@@ -7202,14 +7470,15 @@ function parsePushArgs(argv) {
7202
7470
  dryRun: st.dryRun,
7203
7471
  redactAll: st.redactAll,
7204
7472
  allowAll: st.allowAll,
7205
- allowRule: st.allowRule
7473
+ allowRule: st.allowRule,
7474
+ fullScan: st.fullScan
7206
7475
  };
7207
7476
  }
7208
7477
 
7209
7478
  // package.json
7210
7479
  var package_default = {
7211
7480
  name: "claude-nomad",
7212
- version: "0.55.0",
7481
+ version: "0.56.0",
7213
7482
  type: "module",
7214
7483
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
7215
7484
  keywords: [
@@ -7317,6 +7586,10 @@ var DEFAULT_HELP = [
7317
7586
  row(" push", "Rebase, run safety checks (gitleaks, gitlinks, allow-list), commit, push."),
7318
7587
  row(" --dry-run", "Run pre-checks (rebase, gitleaks probe, gitlink scan) and preview"),
7319
7588
  cont("remap, without staging or pushing."),
7589
+ row(" --full-scan", "Ignore the per-host push manifest and rescan all transcripts."),
7590
+ cont("On a successful non-dry-run push, rewrite the manifest from that full"),
7591
+ cont("rescan. Use after a gitleaks upgrade or when in doubt. Composes freely"),
7592
+ cont("with --dry-run and all resolution modes."),
7320
7593
  row(" --redact-all", "Redact all findings non-interactively (backup, no prompt); no TTY"),
7321
7594
  cont("required. Does not auto-Allow. Mutually exclusive with --allow*."),
7322
7595
  cont("Cannot combine with --dry-run."),
@@ -7430,15 +7703,15 @@ var DEFAULT_HELP = [
7430
7703
  init_config();
7431
7704
  init_utils();
7432
7705
  init_utils_json();
7433
- import { existsSync as existsSync45, readFileSync as readFileSync14, readdirSync as readdirSync17 } from "node:fs";
7434
- import { join as join52 } from "node:path";
7706
+ import { existsSync as existsSync48, readFileSync as readFileSync15, readdirSync as readdirSync18 } from "node:fs";
7707
+ import { join as join56 } from "node:path";
7435
7708
  function resumeCmd(sessionId) {
7436
7709
  if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
7437
7710
  fail(`invalid session id: ${sessionId}`);
7438
7711
  process.exit(1);
7439
7712
  }
7440
- const projectsRoot = join52(claudeHome(), "projects");
7441
- if (!existsSync45(projectsRoot)) {
7713
+ const projectsRoot = join56(claudeHome(), "projects");
7714
+ if (!existsSync48(projectsRoot)) {
7442
7715
  fail(`${projectsRoot} does not exist`);
7443
7716
  process.exit(1);
7444
7717
  }
@@ -7452,8 +7725,8 @@ function resumeCmd(sessionId) {
7452
7725
  fail(`no cwd field found in ${jsonlPath}`);
7453
7726
  process.exit(1);
7454
7727
  }
7455
- const mapPath = join52(repoHome(), "path-map.json");
7456
- if (!existsSync45(mapPath)) {
7728
+ const mapPath = join56(repoHome(), "path-map.json");
7729
+ if (!existsSync48(mapPath)) {
7457
7730
  fail("path-map.json missing");
7458
7731
  process.exit(1);
7459
7732
  }
@@ -7475,14 +7748,14 @@ function resumeCmd(sessionId) {
7475
7748
  console.log(`cd ${shQuote(hit.localPath)} && claude --resume ${shQuote(sessionId)}`);
7476
7749
  }
7477
7750
  function findTranscriptPath(projectsRoot, sessionId) {
7478
- for (const dir of readdirSync17(projectsRoot)) {
7479
- const candidate = join52(projectsRoot, dir, `${sessionId}.jsonl`);
7480
- if (existsSync45(candidate)) return candidate;
7751
+ for (const dir of readdirSync18(projectsRoot)) {
7752
+ const candidate = join56(projectsRoot, dir, `${sessionId}.jsonl`);
7753
+ if (existsSync48(candidate)) return candidate;
7481
7754
  }
7482
7755
  return null;
7483
7756
  }
7484
7757
  function extractRecordedCwd(jsonlPath) {
7485
- for (const line of readFileSync14(jsonlPath, "utf8").split("\n")) {
7758
+ for (const line of readFileSync15(jsonlPath, "utf8").split("\n")) {
7486
7759
  if (!line.trim()) continue;
7487
7760
  try {
7488
7761
  const obj = JSON.parse(line);
@@ -7542,7 +7815,7 @@ try {
7542
7815
  const pushArgs = parsePushArgs(process.argv);
7543
7816
  if (pushArgs === null) {
7544
7817
  console.error(
7545
- "usage: nomad push [--dry-run] [--redact-all] [--allow <rule>] [--allow-all]"
7818
+ "usage: nomad push [--dry-run] [--full-scan] [--redact-all] [--allow <rule>] [--allow-all]"
7546
7819
  );
7547
7820
  process.exit(1);
7548
7821
  }
@@ -7550,7 +7823,8 @@ try {
7550
7823
  dryRun: pushArgs.dryRun,
7551
7824
  redactAll: pushArgs.redactAll,
7552
7825
  allowAll: pushArgs.allowAll,
7553
- allowRule: pushArgs.allowRule
7826
+ allowRule: pushArgs.allowRule,
7827
+ fullScan: pushArgs.fullScan
7554
7828
  });
7555
7829
  break;
7556
7830
  }