claude-nomad 0.67.3 → 0.68.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/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.68.0](https://github.com/funkadelic/claude-nomad/compare/v0.67.3...v0.68.0) (2026-09-03)
4
+
5
+
6
+ ### What's new
7
+
8
+ **On native Windows (PowerShell or cmd).** Note: WSL2 behaves like Linux and this does not apply to it.
9
+
10
+ - **A never-sync warning during `nomad pull` now suggests commands you can paste as they are.** Before a pull, nomad warns when a file that should never travel between machines turns up in your sync repo, and the warning names the git commands to deal with it. A filename containing a space split the suggested command in two, so pasting it acted on neither half. The paths are now quoted.
11
+
12
+ **On every platform.**
13
+
14
+ - **`nomad doctor` now warns when your sync repo is carrying a file that should never sync**, such as a credential or a per-machine settings file. Until now one of those was only noticed while it was new or changed, so a file that was committed and then left alone went unmentioned, and the check that did look ran only on Windows. Doctor names the file and the command that stops tracking it without deleting your copy. It only warns.
15
+ - **`nomad adopt` no longer fails on a deeply nested folder.** Adopting a shared folder scans it first, and a very deep tree could crash that scan. The scan handles any depth now.
16
+ - **A backup nomad cannot write is now reported as a plain error.** Nomad copies anything it is about to overwrite into a backup first. When that copy fails, say a full disk or a permissions problem, you get one message naming what it was copying, where it was going, and why it failed, instead of a crash report file. The message also says a partial copy may be left behind.
17
+
18
+ Everything else in this release is dependency updates.
19
+
20
+
21
+ ### Added
22
+
23
+ * **doctor:** warn when the sync repo tracks a never-synced file ([#562](https://github.com/funkadelic/claude-nomad/issues/562)) ([a1b3f71](https://github.com/funkadelic/claude-nomad/commit/a1b3f71a0a3deecac81e0bcc9e8ee214c1e50a4e))
24
+
25
+
26
+ ### Fixed
27
+
28
+ * **adopt:** handle deep directories and report a failed backup ([#560](https://github.com/funkadelic/claude-nomad/issues/560)) ([2b19ccb](https://github.com/funkadelic/claude-nomad/commit/2b19ccba5959efb7b42d16ee0db862df4d92a0ac))
29
+ * **backup:** report a failed backup instead of writing a crash report ([#561](https://github.com/funkadelic/claude-nomad/issues/561)) ([9d8484c](https://github.com/funkadelic/claude-nomad/commit/9d8484cdb59eb5a5905dfb0a3e16ae952acead5f))
30
+ * **pull:** quote file paths in the never-sync warning's suggested commands ([#559](https://github.com/funkadelic/claude-nomad/issues/559)) ([070cc17](https://github.com/funkadelic/claude-nomad/commit/070cc1709263d46cc50d01e99c345b3955d729ed))
31
+
32
+
33
+ ### Dependencies
34
+
35
+ * bump fast-uri from 3.1.5 to 3.1.7 ([#563](https://github.com/funkadelic/claude-nomad/issues/563)) ([7bf62e3](https://github.com/funkadelic/claude-nomad/commit/7bf62e3e481246b7c8125e31fb2df6654241dd54))
36
+ * bump the codeql-action group across 1 directory with 2 updates ([#556](https://github.com/funkadelic/claude-nomad/issues/556)) ([ad3ae73](https://github.com/funkadelic/claude-nomad/commit/ad3ae739043ae24f560d0ddd56baff79bb6b6ad6))
37
+ * bump the dev-dependencies group across 1 directory with 3 updates ([#555](https://github.com/funkadelic/claude-nomad/issues/555)) ([02aebe2](https://github.com/funkadelic/claude-nomad/commit/02aebe2a6407dcd4be67fe9e33723c735a1c44cd))
38
+ * bump the prod-dependencies group in /docs-site with 3 updates ([#557](https://github.com/funkadelic/claude-nomad/issues/557)) ([b6b5c8a](https://github.com/funkadelic/claude-nomad/commit/b6b5c8aebebcc8e4c73ac12d511bc972bda553d7))
39
+
3
40
  ## [0.67.3](https://github.com/funkadelic/claude-nomad/compare/v0.67.2...v0.67.3) (2026-08-29)
4
41
 
5
42
  ### What's new
@@ -560,14 +597,7 @@ All of this applies on macOS, Linux, WSL2, and native Windows.
560
597
 
561
598
  * **pull:** retain unpushed local work and surface it in the preview ([#376](https://github.com/funkadelic/claude-nomad/issues/376)) ([3f70019](https://github.com/funkadelic/claude-nomad/commit/3f70019c89ccd77726e6330d9623e106bb9ff69c))
562
599
 
563
- What this means: `nomad pull` no longer discards work you have not pushed
564
- yet. Chat sessions, subagent history, and memory files that exist only on
565
- this machine now survive a pull, and a synced file you have edited locally
566
- (a project's `.planning` notes or its `CLAUDE.md`) is kept instead of being
567
- overwritten when it differs from the incoming copy, with a note to push and
568
- reconcile. `nomad diff` and `pull --dry-run` now show the count of retained
569
- local-only items and the keep-local warning, where before they reported
570
- `clean` / `0 collisions`.
600
+ What this means: `nomad pull` no longer discards work you have not pushed yet. Chat sessions, subagent history, and memory files that exist only on this machine now survive a pull, and a synced file you have edited locally (a project's `.planning` notes or its `CLAUDE.md`) is kept instead of being overwritten when it differs from the incoming copy, with a note to push and reconcile. `nomad diff` and `pull --dry-run` now show the count of retained local-only items and the keep-local warning, where before they reported `clean` / `0 collisions`.
571
601
 
572
602
  ## [0.56.1](https://github.com/funkadelic/claude-nomad/compare/v0.56.0...v0.56.1) (2026-06-30)
573
603
 
package/README.md CHANGED
@@ -54,18 +54,20 @@ session history survives different file paths and your secrets never ride along.
54
54
  - **One command tells you what is wrong.** `nomad doctor` is a read-only health check: wedged sync
55
55
  repo, broken hook references, hooks that would crash on session start because of a missing
56
56
  `--preserve-symlinks-main` flag, version drift, oversized backup cache, missing git committer
57
- identity in the sync repo (a push fails at commit time without one), path-map entries whose local
58
- project folder no longer exists on this machine, a multi-host repo where this machine's
59
- hostname-derived key matches no `hosts/<HOST>.json` or path-map entry (a sign `NOMAD_HOST` is
60
- unset here, so per-host settings and session sync will not line up with the other hosts), synced
61
- skills with local edits that differ from the shared copy, a native Windows shared-config copy that
62
- has drifted from the repo's copy (a warning, not a failure), and settings drift in both
63
- directions: keys present in the repo merge but absent from your live `settings.json` (behind; the
64
- next `nomad pull` will restore them, fix: `nomad pull`) and keys present locally but not yet in
65
- the repo (ahead; local-only additions, fix: `nomad capture-settings`). Each issue includes a fix
66
- hint. By default the report is compact: it shows only checks that need action plus a one-line
67
- verdict. Add `--verbose` (or `--all` / `-v`) to see the full per-check tree, including everything
68
- that passed.
57
+ identity in the sync repo (a push fails at commit time without one), a never-synced file such as a
58
+ credential or a per-host settings file that Git already tracks inside the repo's `shared/` folder
59
+ (once it is committed the pull-side guard can no longer see it, so doctor names the file and the
60
+ folder name that blocks it), path-map entries whose local project folder no longer exists on this
61
+ machine, a multi-host repo where this machine's hostname-derived key matches no
62
+ `hosts/<HOST>.json` or path-map entry (a sign `NOMAD_HOST` is unset here, so per-host settings and
63
+ session sync will not line up with the other hosts), synced skills with local edits that differ
64
+ from the shared copy, a native Windows shared-config copy that has drifted from the repo's copy (a
65
+ warning, not a failure), and settings drift in both directions: keys present in the repo merge but
66
+ absent from your live `settings.json` (behind; the next `nomad pull` will restore them, fix:
67
+ `nomad pull`) and keys present locally but not yet in the repo (ahead; local-only additions, fix:
68
+ `nomad capture-settings`). Each issue includes a fix hint. By default the report is compact: it
69
+ shows only checks that need action plus a one-line verdict. Add `--verbose` (or `--all` / `-v`) to
70
+ see the full per-check tree, including everything that passed.
69
71
  - **Self-healing sync.** Every overwrite is backed up first, and `nomad pull --force-remote`
70
72
  recovers two kinds of stuck sync repo: a repo stuck mid-rebase or mid-merge (aborts the operation,
71
73
  parks stranded work on a branch, refuses if shared config is at risk), and a repo where the rebase
package/dist/nomad.mjs CHANGED
@@ -721,6 +721,21 @@ var init_config = __esm({
721
721
  }
722
722
  });
723
723
 
724
+ // src/error-text.ts
725
+ function errorText(err) {
726
+ try {
727
+ const message = err?.message;
728
+ return typeof message === "string" ? message : String(err);
729
+ } catch {
730
+ return "unprintable error value";
731
+ }
732
+ }
733
+ var init_error_text = __esm({
734
+ "src/error-text.ts"() {
735
+ "use strict";
736
+ }
737
+ });
738
+
724
739
  // src/utils.json.ts
725
740
  import { readFileSync } from "node:fs";
726
741
  function readJson(path) {
@@ -886,17 +901,22 @@ function ensureSymlink(linkPath, target) {
886
901
  symlinkSync(target, linkPath);
887
902
  log(`linked ${linkPath} -> ${target}`);
888
903
  }
889
- function backupUnder(absPath, anchor, destRoot) {
904
+ function backupUnder(absPath, anchor, destRoot, hint) {
890
905
  if (!existsSync3(absPath)) return false;
891
906
  const rel = relative3(anchor, absPath);
892
907
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep2}`)) return false;
893
908
  const dst = join5(destRoot, rel);
894
- mkdirSync(dirname(dst), { recursive: true });
895
- cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
909
+ try {
910
+ mkdirSync(dirname(dst), { recursive: true });
911
+ cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
912
+ } catch (err) {
913
+ const fragment = `could not back up ${absPath} into ${dst} (${errorText(err)}); a partial copy may be left there`;
914
+ throw new NomadFatal(hint === void 0 ? fragment : `${fragment}. ${hint}`);
915
+ }
896
916
  return true;
897
917
  }
898
- function backupBeforeWrite(absPath, ts) {
899
- return backupUnder(absPath, claudeHome(), join5(backupBase(), ts));
918
+ function backupBeforeWrite(absPath, ts, hint) {
919
+ return backupUnder(absPath, claudeHome(), join5(backupBase(), ts), hint);
900
920
  }
901
921
  function backupRepoWrite(absPath, ts, repoHome2) {
902
922
  backupUnder(absPath, repoHome2, join5(backupBase(), ts, "repo"));
@@ -909,6 +929,7 @@ var init_utils_fs = __esm({
909
929
  "src/utils.fs.ts"() {
910
930
  "use strict";
911
931
  init_config();
932
+ init_error_text();
912
933
  init_utils_json();
913
934
  init_utils();
914
935
  RENAME_RETRY_MAX_ATTEMPTS = 5;
@@ -1134,7 +1155,7 @@ var init_autostash_guard = __esm({
1134
1155
  });
1135
1156
 
1136
1157
  // src/push-checks.ts
1137
- import { execFileSync as execFileSync4 } from "node:child_process";
1158
+ import { execFileSync as execFileSync5 } from "node:child_process";
1138
1159
  import { readdirSync as readdirSync8, rmSync as rmSync7 } from "node:fs";
1139
1160
  import { homedir as homedir2, platform } from "node:os";
1140
1161
  import { delimiter, join as join23 } from "node:path";
@@ -1201,7 +1222,7 @@ function probeGitleaks() {
1201
1222
  const args = ["version"];
1202
1223
  if (toml !== null) args.push("--config", toml);
1203
1224
  try {
1204
- return execFileSync4("gitleaks", args, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
1225
+ return execFileSync5("gitleaks", args, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
1205
1226
  } catch (err) {
1206
1227
  const e = err;
1207
1228
  if (e.code === "ENOENT") throw new NomadFatal(gitleaksInstallHint());
@@ -1221,7 +1242,7 @@ function rebaseBeforePush(repo) {
1221
1242
  const wedge = classifyWedge(repo);
1222
1243
  if (wedge !== null) throw new NomadFatal(wedgePreflight(wedge), { code: EXIT.CONFLICT });
1223
1244
  try {
1224
- execFileSync4("git", ["pull", "--rebase", "--autostash"], {
1245
+ execFileSync5("git", ["pull", "--rebase", "--autostash"], {
1225
1246
  cwd: repo,
1226
1247
  stdio: ["ignore", "pipe", "pipe"]
1227
1248
  });
@@ -1246,7 +1267,7 @@ var init_push_checks = __esm({
1246
1267
  });
1247
1268
 
1248
1269
  // src/push-gitleaks.scan.ts
1249
- import { execFileSync as execFileSync8 } from "node:child_process";
1270
+ import { execFileSync as execFileSync9 } from "node:child_process";
1250
1271
  import { mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync8 } from "node:fs";
1251
1272
  import { homedir as homedir3 } from "node:os";
1252
1273
  import { join as join27 } from "node:path";
@@ -1313,9 +1334,9 @@ function scanStagedTree(repoDir, forwardStreams = false) {
1313
1334
  const opts = { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] };
1314
1335
  const gitleaksOpts = { ...opts, timeout: GITLEAKS_SCAN_TIMEOUT_MS };
1315
1336
  try {
1316
- execFileSync8("git", ["init", "-q"], opts);
1317
- execFileSync8("git", ["add", "-A"], opts);
1318
- execFileSync8("gitleaks", args, gitleaksOpts);
1337
+ execFileSync9("git", ["init", "-q"], opts);
1338
+ execFileSync9("git", ["add", "-A"], opts);
1339
+ execFileSync9("gitleaks", args, gitleaksOpts);
1319
1340
  return [];
1320
1341
  } catch (err) {
1321
1342
  const e = err;
@@ -1348,7 +1369,7 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
1348
1369
  stdio: ["ignore", "pipe", "pipe"],
1349
1370
  timeout: timeoutMs
1350
1371
  };
1351
- execFileSync8("gitleaks", args, opts);
1372
+ execFileSync9("gitleaks", args, opts);
1352
1373
  return [];
1353
1374
  } catch (err) {
1354
1375
  const e = err;
@@ -1567,43 +1588,37 @@ import { dirname as dirname2, join as join7, resolve as resolve2 } from "node:pa
1567
1588
 
1568
1589
  // src/commands.adopt.scan.ts
1569
1590
  init_config();
1570
- import { lstatSync, readdirSync } from "node:fs";
1571
- import { join as join2, relative } from "node:path";
1572
-
1573
- // src/error-text.ts
1574
- function errorText(err) {
1575
- try {
1576
- const message = err?.message;
1577
- return typeof message === "string" ? message : String(err);
1578
- } catch {
1579
- return "unprintable error value";
1580
- }
1581
- }
1582
-
1583
- // src/commands.adopt.scan.ts
1591
+ init_error_text();
1584
1592
  init_exit_codes();
1585
1593
  init_utils();
1594
+ import { lstatSync, readdirSync } from "node:fs";
1595
+ import { join as join2, relative } from "node:path";
1586
1596
  function toForwardSlash(p) {
1587
1597
  return p.replaceAll("\\", "/");
1588
1598
  }
1589
- function walk(root, dir, out) {
1590
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
1591
- const match = matchDeniedName(ALWAYS_NEVER_SYNC, entry.name);
1592
- if (match !== null) {
1593
- out.push({
1594
- path: toForwardSlash(relative(root, join2(dir, entry.name))),
1595
- matched: match.axis === "name" ? match.entry : null
1596
- });
1597
- continue;
1599
+ function walk(root) {
1600
+ const out = [];
1601
+ const stack = [root];
1602
+ while (stack.length > 0) {
1603
+ const dir = stack.pop();
1604
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1605
+ const match = matchDeniedName(ALWAYS_NEVER_SYNC, entry.name);
1606
+ if (match !== null) {
1607
+ out.push({
1608
+ path: toForwardSlash(relative(root, join2(dir, entry.name))),
1609
+ matched: match.axis === "name" ? match.entry : null
1610
+ });
1611
+ continue;
1612
+ }
1613
+ if (entry.isDirectory()) stack.push(join2(dir, entry.name));
1598
1614
  }
1599
- if (entry.isDirectory()) walk(root, join2(dir, entry.name), out);
1600
1615
  }
1616
+ return out;
1601
1617
  }
1602
1618
  function scanDeniedEntries(root) {
1603
1619
  const stat = lstatSync(root, { throwIfNoEntry: false });
1604
1620
  if (!stat?.isDirectory()) return [];
1605
- const out = [];
1606
- walk(root, root, out);
1621
+ const out = walk(root);
1607
1622
  out.sort((a, b) => a.path.localeCompare(b.path));
1608
1623
  return out;
1609
1624
  }
@@ -1652,6 +1667,7 @@ function refuseDeniedEntries(name, root) {
1652
1667
 
1653
1668
  // src/commands.adopt.recover.ts
1654
1669
  init_config();
1670
+ init_error_text();
1655
1671
  init_exit_codes();
1656
1672
 
1657
1673
  // src/extras-sync.core.ts
@@ -2689,7 +2705,11 @@ function adoptStopsEarly(name, linkPath, sharedTarget) {
2689
2705
  }
2690
2706
  function performAdoptMove(name, linkPath, sharedTarget, repo, backup) {
2691
2707
  const ts = freshBackupTs(backup);
2692
- const snapshotted = backupBeforeWrite(linkPath, ts);
2708
+ const snapshotted = backupBeforeWrite(
2709
+ linkPath,
2710
+ ts,
2711
+ `Nothing was removed from ${linkPath} and nothing was written to the repo. Fix what the error names, then run \`nomad adopt ${name}\` again.`
2712
+ );
2693
2713
  const rel = join9("shared", name);
2694
2714
  const stage = () => gitOrFatal(["add", "--", rel], `git add shared/${name}`, repo);
2695
2715
  copyIntoSharedOrFatal(name, linkPath, sharedTarget, repo);
@@ -3996,15 +4016,34 @@ function reportSkillsDivergence(section2) {
3996
4016
  // src/commands.doctor.checks.git-state.ts
3997
4017
  init_color();
3998
4018
  init_config();
3999
- import { execFileSync as execFileSync5 } from "node:child_process";
4019
+ import { execFileSync as execFileSync6 } from "node:child_process";
4000
4020
  import { existsSync as existsSync17 } from "node:fs";
4001
4021
  import { join as join24, relative as relative5 } from "node:path";
4002
4022
  init_commands_pull_wedge();
4023
+
4024
+ // src/git-probe.ts
4025
+ import { execFileSync as execFileSync4 } from "node:child_process";
4026
+ var PROBE_TIMEOUT_MS = 1e4;
4027
+ var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
4028
+ function gitProbe(args, repo) {
4029
+ try {
4030
+ return execFileSync4("git", args, {
4031
+ cwd: repo,
4032
+ stdio: ["ignore", "pipe", "pipe"],
4033
+ timeout: PROBE_TIMEOUT_MS,
4034
+ maxBuffer: PROBE_MAX_BUFFER
4035
+ }).toString();
4036
+ } catch {
4037
+ return null;
4038
+ }
4039
+ }
4040
+
4041
+ // src/commands.doctor.checks.git-state.ts
4003
4042
  init_push_checks();
4004
4043
  init_utils();
4005
4044
  function reportGitleaksProbe(section2) {
4006
4045
  try {
4007
- execFileSync5("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4046
+ execFileSync6("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4008
4047
  return true;
4009
4048
  } catch (err) {
4010
4049
  if (err.code === "ENOENT") {
@@ -4040,7 +4079,7 @@ function reportGitlinks(section2) {
4040
4079
  }
4041
4080
  function reportRemote(section2) {
4042
4081
  try {
4043
- const url = execFileSync5("git", ["remote", "get-url", "origin"], {
4082
+ const url = execFileSync6("git", ["remote", "get-url", "origin"], {
4044
4083
  cwd: repoHome(),
4045
4084
  stdio: ["ignore", "pipe", "pipe"]
4046
4085
  }).toString().trim();
@@ -4097,7 +4136,7 @@ function reportGitIdentity(section2) {
4097
4136
  const missing = [];
4098
4137
  for (const field of ["user.name", "user.email"]) {
4099
4138
  try {
4100
- const val = execFileSync5("git", ["config", field], {
4139
+ const val = execFileSync6("git", ["config", field], {
4101
4140
  cwd: repo,
4102
4141
  stdio: ["ignore", "pipe", "pipe"]
4103
4142
  }).toString().trim();
@@ -4117,6 +4156,45 @@ function reportGitIdentity(section2) {
4117
4156
  addItem(section2, `${green(okGlyph)} git identity: user.name and user.email configured`);
4118
4157
  }
4119
4158
  }
4159
+ var MAX_TRACKED_DENIED_ROWS = 5;
4160
+ var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
4161
+ function renderable(text) {
4162
+ return text.replace(CONTROL_CHARS, " ");
4163
+ }
4164
+ function reportTrackedDeniedShared(section2) {
4165
+ const out = gitProbe(["ls-files", "-z", "--", ":(icase)shared/"], repoHome());
4166
+ if (out === null) {
4167
+ addItem(
4168
+ section2,
4169
+ `${dim(infoGlyph)} never-sync scan: git could not list shared/, so nothing was scanned`
4170
+ );
4171
+ return;
4172
+ }
4173
+ const hits = /* @__PURE__ */ new Map();
4174
+ for (const path of out.split("\0")) {
4175
+ const segment = path === "" ? null : deniedSegmentFor(path);
4176
+ if (segment !== null) hits.set(path, segment);
4177
+ }
4178
+ if (hits.size === 0) {
4179
+ addItem(
4180
+ section2,
4181
+ `${green(okGlyph)} never-sync scan: no tracked path under shared/ is denylisted`
4182
+ );
4183
+ return;
4184
+ }
4185
+ for (const [path, segment] of [...hits].slice(0, MAX_TRACKED_DENIED_ROWS)) {
4186
+ addItem(
4187
+ section2,
4188
+ `${yellow(warnGlyph)} never-sync: git tracks ${blue(renderable(path))} (the path segment "${renderable(segment)}" is blocked by the never-sync boundary)`
4189
+ );
4190
+ }
4191
+ const overflow = hits.size - MAX_TRACKED_DENIED_ROWS;
4192
+ const more = overflow > 0 ? `${overflow} more not listed, run git ls-files -- ":(icase)shared/" in the sync repo to see them all. ` : "";
4193
+ addItem(
4194
+ section2,
4195
+ `${yellow(warnGlyph)} never-sync: ${more}Run git rm --cached -- "<path>" and commit to stop tracking each one; that leaves the file on disk, so move it outside shared/ if you want to keep it. If one holds a real secret, rotate it and rewrite history too, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent`
4196
+ );
4197
+ }
4120
4198
 
4121
4199
  // src/commands.doctor.checks.backups.ts
4122
4200
  init_color();
@@ -4166,14 +4244,14 @@ function reportBackupsCheck(section2, backupBase2 = backupBase()) {
4166
4244
 
4167
4245
  // src/commands.doctor.check-remote.ts
4168
4246
  init_color();
4169
- import { execFileSync as execFileSync6 } from "node:child_process";
4247
+ import { execFileSync as execFileSync7 } from "node:child_process";
4170
4248
  init_config();
4171
4249
  init_utils_json();
4172
4250
  var GIT_REMOTE_TIMEOUT_MS = 3e3;
4173
4251
  function readRemotePathMap(sec, repo) {
4174
4252
  let rawJson;
4175
4253
  try {
4176
- rawJson = execFileSync6("git", ["show", "origin/main:path-map.json"], {
4254
+ rawJson = execFileSync7("git", ["show", "origin/main:path-map.json"], {
4177
4255
  cwd: repo,
4178
4256
  stdio: ["ignore", "pipe", "pipe"],
4179
4257
  timeout: GIT_REMOTE_TIMEOUT_MS
@@ -4206,7 +4284,7 @@ function reportCheckRemote(section2) {
4206
4284
  const repo = repoHome();
4207
4285
  let names;
4208
4286
  try {
4209
- const out = execFileSync6("git", ["ls-tree", "--name-only", "origin/main"], {
4287
+ const out = execFileSync7("git", ["ls-tree", "--name-only", "origin/main"], {
4210
4288
  cwd: repo,
4211
4289
  stdio: ["ignore", "pipe", "pipe"],
4212
4290
  timeout: GIT_REMOTE_TIMEOUT_MS
@@ -4238,9 +4316,9 @@ import { join as join26 } from "node:path";
4238
4316
  init_config();
4239
4317
 
4240
4318
  // src/http-fetch.ts
4241
- import { execFileSync as execFileSync7 } from "node:child_process";
4319
+ import { execFileSync as execFileSync8 } from "node:child_process";
4242
4320
  var FETCH_TIMEOUT_MS = 3e3;
4243
- function fetchUrl(url, run = execFileSync7) {
4321
+ function fetchUrl(url, run = execFileSync8) {
4244
4322
  try {
4245
4323
  return run("curl", ["-fsSL", "-m", "3", url], {
4246
4324
  stdio: ["ignore", "pipe", "pipe"],
@@ -4301,14 +4379,14 @@ function reportCheckSchema(section2) {
4301
4379
  // src/commands.doctor.check-shared.ts
4302
4380
  init_color();
4303
4381
  import { randomBytes as randomBytes3 } from "node:crypto";
4304
- import { execFileSync as execFileSync11 } from "node:child_process";
4382
+ import { execFileSync as execFileSync12 } from "node:child_process";
4305
4383
  import { existsSync as existsSync22, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync12 } from "node:fs";
4306
4384
  import { homedir as homedir6 } from "node:os";
4307
4385
  import { join as join33 } from "node:path";
4308
4386
 
4309
4387
  // src/commands.doctor.check-shared.memory.ts
4310
4388
  init_color();
4311
- import { execFileSync as execFileSync9 } from "node:child_process";
4389
+ import { execFileSync as execFileSync10 } from "node:child_process";
4312
4390
  import { randomBytes } from "node:crypto";
4313
4391
  import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
4314
4392
  import { homedir as homedir4 } from "node:os";
@@ -4320,7 +4398,7 @@ var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
4320
4398
  function buildMemoryScanTree(tmpRoot) {
4321
4399
  let paths;
4322
4400
  try {
4323
- const out = execFileSync9(
4401
+ const out = execFileSync10(
4324
4402
  "git",
4325
4403
  ["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
4326
4404
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
@@ -4336,7 +4414,7 @@ function buildMemoryScanTree(tmpRoot) {
4336
4414
  if (m?.[1] === void 0) continue;
4337
4415
  let blob;
4338
4416
  try {
4339
- blob = execFileSync9("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4417
+ blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4340
4418
  stdio: ["ignore", "pipe", "ignore"],
4341
4419
  maxBuffer: 67108864,
4342
4420
  timeout: 1e4
@@ -4504,7 +4582,7 @@ function scanAndReport(section2, tmpRoot, staged, logicalToEncoded) {
4504
4582
 
4505
4583
  // src/commands.doctor.check-shared.skills.ts
4506
4584
  init_color();
4507
- import { execFileSync as execFileSync10 } from "node:child_process";
4585
+ import { execFileSync as execFileSync11 } from "node:child_process";
4508
4586
  import { randomBytes as randomBytes2 } from "node:crypto";
4509
4587
  import { mkdirSync as mkdirSync5, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
4510
4588
  import { homedir as homedir5 } from "node:os";
@@ -4516,7 +4594,7 @@ var SKILL_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
4516
4594
  function buildSkillScanTree(tmpRoot) {
4517
4595
  let paths;
4518
4596
  try {
4519
- const out = execFileSync10(
4597
+ const out = execFileSync11(
4520
4598
  "git",
4521
4599
  ["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/skills"],
4522
4600
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
@@ -4532,7 +4610,7 @@ function buildSkillScanTree(tmpRoot) {
4532
4610
  if (m?.[1] === void 0) continue;
4533
4611
  let blob;
4534
4612
  try {
4535
- blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4613
+ blob = execFileSync11("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
4536
4614
  stdio: ["ignore", "pipe", "ignore"],
4537
4615
  maxBuffer: 67108864,
4538
4616
  timeout: 1e4
@@ -4970,7 +5048,7 @@ function buildScanTree(tmpRoot) {
4970
5048
  }
4971
5049
  function probeGitleaksForScan() {
4972
5050
  try {
4973
- execFileSync11("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
5051
+ execFileSync12("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
4974
5052
  return "ok";
4975
5053
  } catch (err) {
4976
5054
  if (err.code === "ENOENT") return "missing";
@@ -5626,16 +5704,16 @@ function reportNodeEngineCheck(section2) {
5626
5704
 
5627
5705
  // src/commands.doctor.checks.longpaths.ts
5628
5706
  init_color();
5629
- import { execFileSync as execFileSync12 } from "node:child_process";
5707
+ import { execFileSync as execFileSync13 } from "node:child_process";
5630
5708
  init_config();
5631
- var PROBE_TIMEOUT_MS = 3e3;
5709
+ var PROBE_TIMEOUT_MS2 = 3e3;
5632
5710
  var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
5633
5711
  var LONGPATHS_REG_VALUE = "LongPathsEnabled";
5634
5712
  function probeGitLongpaths(run) {
5635
5713
  try {
5636
5714
  const out = run("git", ["-C", repoHome(), "config", "--get", "core.longpaths"], {
5637
5715
  stdio: ["ignore", "pipe", "pipe"],
5638
- timeout: PROBE_TIMEOUT_MS
5716
+ timeout: PROBE_TIMEOUT_MS2
5639
5717
  }).toString().trim();
5640
5718
  return out === "true" || out === "1";
5641
5719
  } catch {
@@ -5646,7 +5724,7 @@ function probeRegistryLongpaths(run) {
5646
5724
  try {
5647
5725
  const out = run("reg", ["query", LONGPATHS_REG_KEY, "/v", LONGPATHS_REG_VALUE], {
5648
5726
  stdio: ["ignore", "pipe", "pipe"],
5649
- timeout: PROBE_TIMEOUT_MS
5727
+ timeout: PROBE_TIMEOUT_MS2
5650
5728
  }).toString();
5651
5729
  return /0x1\b/.test(out);
5652
5730
  } catch {
@@ -5660,7 +5738,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
5660
5738
  }
5661
5739
  addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
5662
5740
  }
5663
- function reportLongPathsCheck(section2, run = execFileSync12) {
5741
+ function reportLongPathsCheck(section2, run = execFileSync13) {
5664
5742
  if (process.platform !== "win32") return;
5665
5743
  addLongpathsRow(
5666
5744
  section2,
@@ -5684,11 +5762,11 @@ function reportSyncModality(section2) {
5684
5762
 
5685
5763
  // src/commands.doctor.checks.crlf.ts
5686
5764
  init_color();
5687
- import { execFileSync as execFileSync13 } from "node:child_process";
5765
+ import { execFileSync as execFileSync14 } from "node:child_process";
5688
5766
  import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
5689
5767
  import { join as join39 } from "node:path";
5690
5768
  init_config();
5691
- var PROBE_TIMEOUT_MS2 = 3e3;
5769
+ var PROBE_TIMEOUT_MS3 = 3e3;
5692
5770
  var GUARD_LINE = /^\*\s+-text\b/;
5693
5771
  function hasGitattributesGuard(repo) {
5694
5772
  try {
@@ -5704,7 +5782,7 @@ function probeAutocrlf(repo, run) {
5704
5782
  try {
5705
5783
  const out = run("git", ["-C", repo, "config", "core.autocrlf"], {
5706
5784
  stdio: ["ignore", "pipe", "pipe"],
5707
- timeout: PROBE_TIMEOUT_MS2
5785
+ timeout: PROBE_TIMEOUT_MS3
5708
5786
  }).toString().trim();
5709
5787
  if (out === "true" || out === "input") return "active";
5710
5788
  if (out === "false") return "disabled";
@@ -5723,7 +5801,7 @@ function addExposedRow(section2, repo, verdict) {
5723
5801
  const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
5724
5802
  addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
5725
5803
  }
5726
- function reportCrlfGuardCheck(section2, run = execFileSync13) {
5804
+ function reportCrlfGuardCheck(section2, run = execFileSync14) {
5727
5805
  const repo = repoHome();
5728
5806
  if (hasGitattributesGuard(repo)) {
5729
5807
  addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
@@ -6148,10 +6226,10 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
6148
6226
 
6149
6227
  // src/commands.pushed-history.ts
6150
6228
  init_utils();
6151
- import { execFileSync as execFileSync14 } from "node:child_process";
6229
+ import { execFileSync as execFileSync15 } from "node:child_process";
6152
6230
  function pushedRef(repo) {
6153
6231
  try {
6154
- const ref = execFileSync14("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
6232
+ const ref = execFileSync15("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
6155
6233
  cwd: repo,
6156
6234
  stdio: ["ignore", "pipe", "ignore"]
6157
6235
  }).toString().trim();
@@ -6164,7 +6242,7 @@ function sessionInPushedHistory(id, repo) {
6164
6242
  const ref = pushedRef(repo);
6165
6243
  if (ref === null) return false;
6166
6244
  try {
6167
- const out = execFileSync14(
6245
+ const out = execFileSync15(
6168
6246
  "git",
6169
6247
  [
6170
6248
  "log",
@@ -6497,7 +6575,7 @@ var FRAGMENT_SEP = "...";
6497
6575
  var NEAR_WINDOW = 40;
6498
6576
  var VALUE_PAD = 16;
6499
6577
  var SIBLING_PAD = 2;
6500
- var CONTROL_CHARS = /[\x00-\x1f\x7f]/g;
6578
+ var CONTROL_CHARS2 = /[\x00-\x1f\x7f]/g;
6501
6579
  function classifyCharset(value) {
6502
6580
  if (/^[0-9a-fA-F]+$/.test(value)) return "hex";
6503
6581
  if (/^[0-9a-zA-Z]+$/.test(value)) return "alphanumeric";
@@ -6571,7 +6649,7 @@ function resolveSpanContext(finding, readLine, siblings = []) {
6571
6649
  return { value: aligned.value, near: emptyToNull(near), exact: aligned.exact };
6572
6650
  }
6573
6651
  function formatNear(text) {
6574
- const stripped = elideTokenRuns(text.replace(CONTROL_CHARS, ""));
6652
+ const stripped = elideTokenRuns(text.replace(CONTROL_CHARS2, ""));
6575
6653
  const truncated = stripped.length > NEAR_WINDOW;
6576
6654
  const window = stripped.slice(-NEAR_WINDOW).trim();
6577
6655
  if (window.length === 0) return null;
@@ -7048,7 +7126,7 @@ function withSpinner(label, fn, deps) {
7048
7126
 
7049
7127
  // src/commands.doctor.gitleaks-version.ts
7050
7128
  init_color();
7051
- import { execFileSync as execFileSync15 } from "node:child_process";
7129
+ import { execFileSync as execFileSync16 } from "node:child_process";
7052
7130
  import { existsSync as existsSync34 } from "node:fs";
7053
7131
  import { join as join48 } from "node:path";
7054
7132
  init_config();
@@ -7071,7 +7149,7 @@ function readGitleaksVersion(run, tomlExists) {
7071
7149
  return null;
7072
7150
  }
7073
7151
  }
7074
- function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists = existsSync34) {
7152
+ function reportGitleaksVersionCheck(section2, run = execFileSync16, tomlExists = existsSync34) {
7075
7153
  const raw = readGitleaksVersion(run, tomlExists);
7076
7154
  if (raw === null) return;
7077
7155
  const local = majorMinorOf(raw);
@@ -7091,9 +7169,9 @@ function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists =
7091
7169
 
7092
7170
  // src/commands.doctor.checks.deps.ts
7093
7171
  init_color();
7094
- import { execFileSync as execFileSync16 } from "node:child_process";
7172
+ import { execFileSync as execFileSync17 } from "node:child_process";
7095
7173
  var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
7096
- var PROBE_TIMEOUT_MS3 = 3e3;
7174
+ var PROBE_TIMEOUT_MS4 = 3e3;
7097
7175
  var FETCHER_BASE = "HTTP fetcher";
7098
7176
  function parseFirstVersion(line) {
7099
7177
  const m = VERSION_TOKEN.exec(line);
@@ -7103,7 +7181,7 @@ function probeOptionalDep(bin, run) {
7103
7181
  try {
7104
7182
  const firstLine = run(bin, ["--version"], {
7105
7183
  stdio: ["ignore", "pipe", "pipe"],
7106
- timeout: PROBE_TIMEOUT_MS3
7184
+ timeout: PROBE_TIMEOUT_MS4
7107
7185
  }).toString().split("\n")[0].trim();
7108
7186
  const version = parseFirstVersion(firstLine);
7109
7187
  return { status: "present", version };
@@ -7128,7 +7206,7 @@ function reportFetcherRow(section2, run) {
7128
7206
  );
7129
7207
  }
7130
7208
  }
7131
- function reportOptionalDeps(section2, run = execFileSync16) {
7209
+ function reportOptionalDeps(section2, run = execFileSync17) {
7132
7210
  const gh = probeOptionalDep("gh", run);
7133
7211
  if (gh.status === "present") {
7134
7212
  addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
@@ -7143,11 +7221,11 @@ function reportOptionalDeps(section2, run = execFileSync16) {
7143
7221
 
7144
7222
  // src/commands.doctor.actions-drift.ts
7145
7223
  init_color();
7146
- import { execFileSync as execFileSync18 } from "node:child_process";
7224
+ import { execFileSync as execFileSync19 } from "node:child_process";
7147
7225
  init_config();
7148
7226
 
7149
7227
  // src/gh-actions.ts
7150
- import { execFileSync as execFileSync17 } from "node:child_process";
7228
+ import { execFileSync as execFileSync18 } from "node:child_process";
7151
7229
  var GH_TIMEOUT_MS = 5e3;
7152
7230
  function parseGitHubRemote(remoteUrl) {
7153
7231
  const normalized = remoteUrl.trim().replace(/\/$/, "");
@@ -7155,7 +7233,7 @@ function parseGitHubRemote(remoteUrl) {
7155
7233
  if (m === null) return null;
7156
7234
  return { owner: m[1], repo: m[2] };
7157
7235
  }
7158
- function ghAuthStatus(run = execFileSync17) {
7236
+ function ghAuthStatus(run = execFileSync18) {
7159
7237
  try {
7160
7238
  run("gh", ["auth", "status"], {
7161
7239
  stdio: ["ignore", "ignore", "ignore"],
@@ -7169,7 +7247,7 @@ function ghAuthStatus(run = execFileSync17) {
7169
7247
  return "gh-probe-error";
7170
7248
  }
7171
7249
  }
7172
- function isRepoPrivate(ref, run = execFileSync17) {
7250
+ function isRepoPrivate(ref, run = execFileSync18) {
7173
7251
  const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
7174
7252
  stdio: ["ignore", "pipe", "ignore"],
7175
7253
  timeout: GH_TIMEOUT_MS
@@ -7177,7 +7255,7 @@ function isRepoPrivate(ref, run = execFileSync17) {
7177
7255
  const parsed = JSON.parse(out);
7178
7256
  return parsed.isPrivate === true;
7179
7257
  }
7180
- function isActionsEnabled(ref, run = execFileSync17) {
7258
+ function isActionsEnabled(ref, run = execFileSync18) {
7181
7259
  const out = run(
7182
7260
  "gh",
7183
7261
  ["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
@@ -7185,7 +7263,7 @@ function isActionsEnabled(ref, run = execFileSync17) {
7185
7263
  ).toString().trim();
7186
7264
  return out === "true";
7187
7265
  }
7188
- function disableActions(ref, run = execFileSync17) {
7266
+ function disableActions(ref, run = execFileSync18) {
7189
7267
  run(
7190
7268
  "gh",
7191
7269
  [
@@ -7199,7 +7277,7 @@ function disableActions(ref, run = execFileSync17) {
7199
7277
  { stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
7200
7278
  );
7201
7279
  }
7202
- function readOriginRemote(cwd, run = execFileSync17) {
7280
+ function readOriginRemote(cwd, run = execFileSync18) {
7203
7281
  return run("git", ["remote", "get-url", "origin"], {
7204
7282
  cwd,
7205
7283
  stdio: ["ignore", "pipe", "ignore"]
@@ -7207,7 +7285,7 @@ function readOriginRemote(cwd, run = execFileSync17) {
7207
7285
  }
7208
7286
 
7209
7287
  // src/commands.doctor.actions-drift.ts
7210
- function reportActionsDrift(section2, run = execFileSync18) {
7288
+ function reportActionsDrift(section2, run = execFileSync19) {
7211
7289
  let remote;
7212
7290
  try {
7213
7291
  remote = readOriginRemote(repoHome(), run);
@@ -7347,6 +7425,7 @@ function gatherDoctorSections(opts) {
7347
7425
  reportPathMap(pathMap);
7348
7426
  const neverSync = section("Never-sync");
7349
7427
  reportNeverSync(neverSync);
7428
+ reportTrackedDeniedShared(neverSync);
7350
7429
  const skills = section("Skills");
7351
7430
  reportSkillsDivergence(skills);
7352
7431
  const repository = section("Repository");
@@ -7426,15 +7505,15 @@ function parseDoctorArgs(args) {
7426
7505
 
7427
7506
  // src/commands.drop-session.ts
7428
7507
  init_config();
7429
- import { execFileSync as execFileSync20 } from "node:child_process";
7508
+ import { execFileSync as execFileSync21 } from "node:child_process";
7430
7509
  import { existsSync as existsSync37, readdirSync as readdirSync16, statSync as statSync10 } from "node:fs";
7431
7510
  import { join as join51, relative as relative7 } from "node:path";
7432
7511
 
7433
7512
  // src/commands.drop-session.git.ts
7434
- import { execFileSync as execFileSync19 } from "node:child_process";
7513
+ import { execFileSync as execFileSync20 } from "node:child_process";
7435
7514
  function expandStagedDir(dirRel, repo) {
7436
7515
  try {
7437
- const out = execFileSync19("git", ["ls-files", "-z", "--", dirRel], {
7516
+ const out = execFileSync20("git", ["ls-files", "-z", "--", dirRel], {
7438
7517
  cwd: repo,
7439
7518
  stdio: ["ignore", "pipe", "pipe"]
7440
7519
  });
@@ -7446,7 +7525,7 @@ function expandStagedDir(dirRel, repo) {
7446
7525
  function isTrackedInHead(rel, repo) {
7447
7526
  try {
7448
7527
  const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
7449
- execFileSync19("git", ["cat-file", "-e", `HEAD:${treePath}`], {
7528
+ execFileSync20("git", ["cat-file", "-e", `HEAD:${treePath}`], {
7450
7529
  cwd: repo,
7451
7530
  stdio: ["ignore", "pipe", "pipe"]
7452
7531
  });
@@ -7457,7 +7536,7 @@ function isTrackedInHead(rel, repo) {
7457
7536
  }
7458
7537
  function isInIndex(rel, repo) {
7459
7538
  try {
7460
- const out = execFileSync19("git", ["ls-files", "--", rel], {
7539
+ const out = execFileSync20("git", ["ls-files", "--", rel], {
7461
7540
  cwd: repo,
7462
7541
  stdio: ["ignore", "pipe", "pipe"]
7463
7542
  });
@@ -7563,12 +7642,12 @@ function unstageOne(rel, repo) {
7563
7642
  }
7564
7643
  try {
7565
7644
  if (isTrackedInHead(rel, repo)) {
7566
- execFileSync20("git", ["restore", "--staged", "--worktree", "--", rel], {
7645
+ execFileSync21("git", ["restore", "--staged", "--worktree", "--", rel], {
7567
7646
  cwd: repo,
7568
7647
  stdio: ["ignore", "pipe", "pipe"]
7569
7648
  });
7570
7649
  } else {
7571
- execFileSync20("git", ["rm", "--cached", "-f", "--", rel], {
7650
+ execFileSync21("git", ["rm", "--cached", "-f", "--", rel], {
7572
7651
  cwd: repo,
7573
7652
  stdio: ["ignore", "pipe", "pipe"]
7574
7653
  });
@@ -8115,9 +8194,9 @@ import { join as join60 } from "node:path";
8115
8194
 
8116
8195
  // src/commands.pull.recovery.git.ts
8117
8196
  init_utils();
8118
- import { execFileSync as execFileSync21 } from "node:child_process";
8197
+ import { execFileSync as execFileSync22 } from "node:child_process";
8119
8198
  function gitCapture(args, cwd) {
8120
- return execFileSync21("git", args, {
8199
+ return execFileSync22("git", args, {
8121
8200
  cwd,
8122
8201
  stdio: ["ignore", "pipe", "pipe"],
8123
8202
  maxBuffer: 64 * 1024 * 1024
@@ -8155,23 +8234,6 @@ function parseDirtyPaths(repo, opts = {}) {
8155
8234
  // src/commands.pull.win32.ts
8156
8235
  init_config();
8157
8236
 
8158
- // src/git-probe.ts
8159
- import { execFileSync as execFileSync22 } from "node:child_process";
8160
- var PROBE_TIMEOUT_MS4 = 1e4;
8161
- var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
8162
- function gitProbe(args, repo) {
8163
- try {
8164
- return execFileSync22("git", args, {
8165
- cwd: repo,
8166
- stdio: ["ignore", "pipe", "pipe"],
8167
- timeout: PROBE_TIMEOUT_MS4,
8168
- maxBuffer: PROBE_MAX_BUFFER
8169
- }).toString();
8170
- } catch {
8171
- return null;
8172
- }
8173
- }
8174
-
8175
8237
  // src/links.deletions.ts
8176
8238
  init_config();
8177
8239
  import { existsSync as existsSync40, lstatSync as lstatSync19, rmSync as rmSync17 } from "node:fs";
@@ -8235,6 +8297,7 @@ function applySharedLinkDeletions(map, ts, opts = {}) {
8235
8297
 
8236
8298
  // src/links.mirror.ts
8237
8299
  init_config();
8300
+ init_error_text();
8238
8301
  import { existsSync as existsSync41, lstatSync as lstatSync20, rmSync as rmSync18, statSync as statSync12 } from "node:fs";
8239
8302
  import { join as join57 } from "node:path";
8240
8303
  init_utils();
@@ -8402,19 +8465,19 @@ function reportTrackedDenied(repo, path, segment) {
8402
8465
  const denied = `the path segment "${segment}" is on the never-sync list`;
8403
8466
  if (inHead === null) {
8404
8467
  warn(
8405
- `could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it with "git status -- ${path}" and take it out of shared/ before committing`
8468
+ `could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it by running git status -- "${path}" and take it out of shared/ before committing`
8406
8469
  );
8407
8470
  return;
8408
8471
  }
8409
8472
  if (inHead.trim() === "") {
8410
8473
  warn(
8411
- `${path} is staged and has no committed version: ${denied}. Nothing was changed. Run "git rm --cached -- ${path}" to take it out of the index; that leaves the file on disk but makes it untracked, which the next nomad pull removes from the sync repo working tree (snapshotting it into the backup cache first), so move it outside shared/ instead if you want to keep it. If it is the destination half of a staged rename, "git diff --cached --name-status" names the source, whose content IS committed, so restore that half with "git checkout HEAD -- <source>" rather than leaving its deletion staged`
8474
+ `${path} is staged and has no committed version: ${denied}. Nothing was changed. Run git rm --cached -- "${path}" to take it out of the index; that leaves the file on disk but makes it untracked, which the next nomad pull removes from the sync repo working tree (snapshotting it into the backup cache first), so move it outside shared/ instead if you want to keep it. If it is the destination half of a staged rename, run git diff --cached --name-status to name the source, whose content IS committed, so restore that half with git checkout HEAD -- "<source>" rather than leaving its deletion staged`
8412
8475
  );
8413
8476
  return;
8414
8477
  }
8415
8478
  if (!existsSync41(join57(repo, path))) return;
8416
8479
  warn(
8417
- `${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run "git checkout HEAD -- ${path}" to put the committed content back, or move the file outside shared/ if you want to keep it. Neither of those takes the committed copy out of the repo: "git rm -- ${path}" and a commit does that going forward, and if it holds a real secret, rotate it and rewrite history, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent to the remote`
8480
+ `${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run git checkout HEAD -- "${path}" to put the committed content back, or move the file outside shared/ if you want to keep it. Neither of those takes the committed copy out of the repo: git rm -- "${path}" and a commit does that going forward, and if it holds a real secret, rotate it and rewrite history, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent to the remote`
8418
8481
  );
8419
8482
  }
8420
8483
  function revertDeniedMirrorPaths(repo, status, ts) {
@@ -10916,7 +10979,7 @@ function parseSyncArgs(argv) {
10916
10979
  // package.json
10917
10980
  var package_default = {
10918
10981
  name: "claude-nomad",
10919
- version: "0.67.3",
10982
+ version: "0.68.0",
10920
10983
  type: "module",
10921
10984
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
10922
10985
  keywords: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nomad",
3
- "version": "0.67.3",
3
+ "version": "0.68.0",
4
4
  "type": "module",
5
5
  "description": "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
6
6
  "keywords": [