claude-nomad 0.67.0 → 0.67.2
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 +106 -156
- package/README.md +7 -1
- package/dist/nomad.mjs +710 -514
- package/package.json +4 -3
package/dist/nomad.mjs
CHANGED
|
@@ -70,6 +70,9 @@ function regionKey(segment) {
|
|
|
70
70
|
function isExtrasScoped(segments) {
|
|
71
71
|
return segments[0] === "shared" && segments.length > 1 && regionKey(segments[1]) === "extras";
|
|
72
72
|
}
|
|
73
|
+
function isLogicalNameScoped(segments) {
|
|
74
|
+
return segments[0] === "shared" && segments.length > 1 && UNFILTERED_SHARED_REGIONS.has(regionKey(segments[1]));
|
|
75
|
+
}
|
|
73
76
|
function isSharedNameScoped(segments) {
|
|
74
77
|
return segments[0] === "shared" && segments.length > 1 && !UNFILTERED_SHARED_REGIONS.has(regionKey(segments[1]));
|
|
75
78
|
}
|
|
@@ -83,7 +86,7 @@ function blockSetFor(segments) {
|
|
|
83
86
|
function deniedSegmentFor(path) {
|
|
84
87
|
const segments = path.split("/");
|
|
85
88
|
const blockSet = blockSetFor(segments);
|
|
86
|
-
const scan =
|
|
89
|
+
const scan = isLogicalNameScoped(segments) && segments.length > 3 ? segments.slice(3) : segments;
|
|
87
90
|
for (const segment of scan) {
|
|
88
91
|
if (isDeniedName(blockSet, segment)) return segment;
|
|
89
92
|
}
|
|
@@ -803,12 +806,12 @@ import {
|
|
|
803
806
|
cpSync as cpSync3,
|
|
804
807
|
existsSync as existsSync3,
|
|
805
808
|
fsyncSync,
|
|
806
|
-
lstatSync as
|
|
809
|
+
lstatSync as lstatSync5,
|
|
807
810
|
mkdirSync,
|
|
808
811
|
openSync,
|
|
809
812
|
renameSync,
|
|
810
813
|
rmdirSync,
|
|
811
|
-
statSync,
|
|
814
|
+
statSync as statSync2,
|
|
812
815
|
symlinkSync,
|
|
813
816
|
writeFileSync
|
|
814
817
|
} from "node:fs";
|
|
@@ -837,7 +840,7 @@ function renameAtomicRetry(tmp, dst, renameFn = renameSync) {
|
|
|
837
840
|
}
|
|
838
841
|
}
|
|
839
842
|
function writeJsonAtomic(path, data) {
|
|
840
|
-
const mode = existsSync3(path) ?
|
|
843
|
+
const mode = existsSync3(path) ? statSync2(path).mode & 511 : 384;
|
|
841
844
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
842
845
|
const fd = openSync(tmp, "w", mode);
|
|
843
846
|
try {
|
|
@@ -876,7 +879,7 @@ function discardEmptyBackupDir(backupRoot) {
|
|
|
876
879
|
}
|
|
877
880
|
function ensureSymlink(linkPath, target) {
|
|
878
881
|
if (existsSync3(linkPath)) {
|
|
879
|
-
if (
|
|
882
|
+
if (lstatSync5(linkPath).isSymbolicLink()) return;
|
|
880
883
|
die(`${linkPath} exists and is not a symlink. Move it aside first.`);
|
|
881
884
|
}
|
|
882
885
|
mkdirSync(dirname(linkPath), { recursive: true });
|
|
@@ -915,12 +918,12 @@ var init_utils_fs = __esm({
|
|
|
915
918
|
|
|
916
919
|
// src/commands.pull.wedge.ts
|
|
917
920
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
918
|
-
import { existsSync as
|
|
919
|
-
import { join as
|
|
921
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
922
|
+
import { join as join21 } from "node:path";
|
|
920
923
|
function detectWedge(repo) {
|
|
921
|
-
const g =
|
|
922
|
-
if (
|
|
923
|
-
if (
|
|
924
|
+
const g = join21(repo, ".git");
|
|
925
|
+
if (existsSync15(join21(g, "rebase-merge")) || existsSync15(join21(g, "rebase-apply"))) return "rebase";
|
|
926
|
+
if (existsSync15(join21(g, "MERGE_HEAD"))) return "merge";
|
|
924
927
|
return null;
|
|
925
928
|
}
|
|
926
929
|
function probeUnmergedIndex(repo) {
|
|
@@ -990,15 +993,15 @@ var init_commands_pull_wedge = __esm({
|
|
|
990
993
|
});
|
|
991
994
|
|
|
992
995
|
// src/push-gitleaks.config.ts
|
|
993
|
-
import { existsSync as
|
|
996
|
+
import { existsSync as existsSync16, mkdtempSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
994
997
|
import { tmpdir } from "node:os";
|
|
995
|
-
import { join as
|
|
998
|
+
import { join as join22 } from "node:path";
|
|
996
999
|
import { fileURLToPath } from "node:url";
|
|
997
1000
|
function resolveTomlPath(repo = repoHome()) {
|
|
998
|
-
const repoToml =
|
|
999
|
-
if (
|
|
1001
|
+
const repoToml = join22(repo, ".gitleaks.toml");
|
|
1002
|
+
if (existsSync16(repoToml)) return repoToml;
|
|
1000
1003
|
const bundled = fileURLToPath(new URL("../.gitleaks.toml", import.meta.url));
|
|
1001
|
-
return
|
|
1004
|
+
return existsSync16(bundled) ? bundled : null;
|
|
1002
1005
|
}
|
|
1003
1006
|
function assertOverlayAllowlistsScoped(overlayBody) {
|
|
1004
1007
|
if (OVERLAY_INLINE_ALLOWLIST_RE.test(overlayBody)) {
|
|
@@ -1036,17 +1039,17 @@ function buildOverlayTempConfig(overlayBody, bundled) {
|
|
|
1036
1039
|
path = ${JSON.stringify(bundled)}
|
|
1037
1040
|
|
|
1038
1041
|
${overlayBody}`;
|
|
1039
|
-
const tempPath = mkdtempSync(
|
|
1040
|
-
const configPath =
|
|
1042
|
+
const tempPath = mkdtempSync(join22(tmpdir(), "nomad-gitleaks-cfg-"));
|
|
1043
|
+
const configPath = join22(tempPath, "config.toml");
|
|
1041
1044
|
writeFileSync3(configPath, tempBody, { mode: 384, flag: "wx" });
|
|
1042
1045
|
return { configPath, tempPath };
|
|
1043
1046
|
}
|
|
1044
1047
|
function resolveTomlConfig() {
|
|
1045
1048
|
const repo = repoHome();
|
|
1046
|
-
const overlayPath =
|
|
1047
|
-
const repoToml =
|
|
1049
|
+
const overlayPath = join22(repo, ".gitleaks.overlay.toml");
|
|
1050
|
+
const repoToml = join22(repo, ".gitleaks.toml");
|
|
1048
1051
|
const bundled = resolveTomlPath(repo);
|
|
1049
|
-
if (!
|
|
1052
|
+
if (!existsSync16(overlayPath)) {
|
|
1050
1053
|
return { path: bundled, tempPath: null };
|
|
1051
1054
|
}
|
|
1052
1055
|
if (bundled === repoToml) {
|
|
@@ -1134,7 +1137,7 @@ var init_autostash_guard = __esm({
|
|
|
1134
1137
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
1135
1138
|
import { readdirSync as readdirSync8, rmSync as rmSync7 } from "node:fs";
|
|
1136
1139
|
import { homedir as homedir2, platform } from "node:os";
|
|
1137
|
-
import { delimiter, join as
|
|
1140
|
+
import { delimiter, join as join23 } from "node:path";
|
|
1138
1141
|
function gitleaksInstallHint() {
|
|
1139
1142
|
const head = "gitleaks not on PATH (required for nomad push). Install:";
|
|
1140
1143
|
const plat = platform();
|
|
@@ -1182,7 +1185,7 @@ function findGitlinks(dir) {
|
|
|
1182
1185
|
return;
|
|
1183
1186
|
}
|
|
1184
1187
|
for (const e of entries) {
|
|
1185
|
-
const p =
|
|
1188
|
+
const p = join23(current, e.name);
|
|
1186
1189
|
if (e.name === ".git") {
|
|
1187
1190
|
hits.push(p);
|
|
1188
1191
|
continue;
|
|
@@ -1246,7 +1249,7 @@ var init_push_checks = __esm({
|
|
|
1246
1249
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
1247
1250
|
import { mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync8 } from "node:fs";
|
|
1248
1251
|
import { homedir as homedir3 } from "node:os";
|
|
1249
|
-
import { join as
|
|
1252
|
+
import { join as join27 } from "node:path";
|
|
1250
1253
|
function toFinding(entry) {
|
|
1251
1254
|
if (typeof entry !== "object" || entry === null) return null;
|
|
1252
1255
|
const e = entry;
|
|
@@ -1294,9 +1297,9 @@ function resolveScanFailure(reportPath, e, forwardStreams) {
|
|
|
1294
1297
|
return report;
|
|
1295
1298
|
}
|
|
1296
1299
|
function scanStagedTree(repoDir, forwardStreams = false) {
|
|
1297
|
-
const cacheDir =
|
|
1300
|
+
const cacheDir = join27(homedir3(), ".cache", "claude-nomad");
|
|
1298
1301
|
mkdirSync3(cacheDir, { recursive: true });
|
|
1299
|
-
const reportPath =
|
|
1302
|
+
const reportPath = join27(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
|
|
1300
1303
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
1301
1304
|
const args = [
|
|
1302
1305
|
"protect",
|
|
@@ -1324,14 +1327,14 @@ function scanStagedTree(repoDir, forwardStreams = false) {
|
|
|
1324
1327
|
}
|
|
1325
1328
|
}
|
|
1326
1329
|
function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TIMEOUT_MS) {
|
|
1327
|
-
const cacheDir =
|
|
1330
|
+
const cacheDir = join27(homedir3(), ".cache", "claude-nomad");
|
|
1328
1331
|
mkdirSync3(cacheDir, { recursive: true });
|
|
1329
1332
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
1330
1333
|
let reportDir;
|
|
1331
1334
|
let reportPath;
|
|
1332
1335
|
try {
|
|
1333
|
-
reportDir = mkdtempSync2(
|
|
1334
|
-
reportPath =
|
|
1336
|
+
reportDir = mkdtempSync2(join27(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
|
|
1337
|
+
reportPath = join27(reportDir, "report.json");
|
|
1335
1338
|
const args = [
|
|
1336
1339
|
"detect",
|
|
1337
1340
|
"--no-git",
|
|
@@ -1559,15 +1562,27 @@ import { existsSync as existsSync5, lstatSync as lstatSync7 } from "node:fs";
|
|
|
1559
1562
|
import { join as join9 } from "node:path";
|
|
1560
1563
|
|
|
1561
1564
|
// src/commands.adopt.recover.ts
|
|
1562
|
-
import {
|
|
1565
|
+
import { rmSync as rmSync4 } from "node:fs";
|
|
1563
1566
|
import { dirname as dirname2, join as join7, resolve as resolve2 } from "node:path";
|
|
1564
1567
|
|
|
1565
1568
|
// src/commands.adopt.scan.ts
|
|
1566
1569
|
init_config();
|
|
1567
|
-
init_exit_codes();
|
|
1568
|
-
init_utils();
|
|
1569
1570
|
import { lstatSync, readdirSync } from "node:fs";
|
|
1570
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
|
|
1584
|
+
init_exit_codes();
|
|
1585
|
+
init_utils();
|
|
1571
1586
|
function toForwardSlash(p) {
|
|
1572
1587
|
return p.replaceAll("\\", "/");
|
|
1573
1588
|
}
|
|
@@ -1596,10 +1611,8 @@ function scanOrFatal(name, root, state) {
|
|
|
1596
1611
|
try {
|
|
1597
1612
|
return scanDeniedEntries(root);
|
|
1598
1613
|
} catch (err) {
|
|
1599
|
-
const message = err?.message;
|
|
1600
|
-
const text = typeof message === "string" ? message : String(err);
|
|
1601
1614
|
throw new NomadFatal(
|
|
1602
|
-
`cannot adopt ${name}: could not scan ${root} for never-sync content (${
|
|
1615
|
+
`cannot adopt ${name}: could not scan ${root} for never-sync content (${errorText(err)}). ${state()} Check that it is readable and not being written to, then run \`nomad adopt ${name}\` again.`,
|
|
1603
1616
|
{ code: EXIT.GENERIC_FAILURE }
|
|
1604
1617
|
);
|
|
1605
1618
|
}
|
|
@@ -1859,9 +1872,34 @@ function copyExtrasFilteredPreserving(src, dst, blockSet) {
|
|
|
1859
1872
|
});
|
|
1860
1873
|
}
|
|
1861
1874
|
|
|
1875
|
+
// src/fs-presence.ts
|
|
1876
|
+
import { lstatSync as lstatSync4, statSync } from "node:fs";
|
|
1877
|
+
var ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR", "ENAMETOOLONG"]);
|
|
1878
|
+
function classifyPresence(p) {
|
|
1879
|
+
let stat;
|
|
1880
|
+
try {
|
|
1881
|
+
stat = lstatSync4(p, { throwIfNoEntry: false });
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
return ABSENT_CODES.has(String(err.code)) ? "absent" : "unknown";
|
|
1884
|
+
}
|
|
1885
|
+
if (stat === void 0) return "absent";
|
|
1886
|
+
try {
|
|
1887
|
+
statSync(p);
|
|
1888
|
+
} catch (err) {
|
|
1889
|
+
return ABSENT_CODES.has(String(err.code)) ? "dangling" : "unknown";
|
|
1890
|
+
}
|
|
1891
|
+
return "resolves";
|
|
1892
|
+
}
|
|
1893
|
+
function isUnusableTarget(state) {
|
|
1894
|
+
return state === "dangling" || state === "unknown";
|
|
1895
|
+
}
|
|
1896
|
+
function lexists(p) {
|
|
1897
|
+
return classifyPresence(p) !== "absent";
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1862
1900
|
// src/links.ts
|
|
1863
1901
|
init_config();
|
|
1864
|
-
import { existsSync as existsSync4, lstatSync as
|
|
1902
|
+
import { existsSync as existsSync4, lstatSync as lstatSync6, rmSync as rmSync3 } from "node:fs";
|
|
1865
1903
|
import { join as join6 } from "node:path";
|
|
1866
1904
|
|
|
1867
1905
|
// src/hooks-filter.ts
|
|
@@ -2177,14 +2215,14 @@ function emitCopy(onPreview, from, to) {
|
|
|
2177
2215
|
}
|
|
2178
2216
|
}
|
|
2179
2217
|
function isAlreadySymlink(linkPath) {
|
|
2180
|
-
return existsSync4(linkPath) &&
|
|
2218
|
+
return existsSync4(linkPath) && lstatSync6(linkPath).isSymbolicLink();
|
|
2181
2219
|
}
|
|
2182
2220
|
function runAutoMovePasses(linkNames, claude, repo, ts, dryRun, onPreview) {
|
|
2183
2221
|
for (const name of linkNames) {
|
|
2184
2222
|
const linkPath = join6(claude, name);
|
|
2185
2223
|
const target = join6(repo, "shared", name);
|
|
2186
2224
|
if (!existsSync4(linkPath)) continue;
|
|
2187
|
-
if (
|
|
2225
|
+
if (lstatSync6(linkPath).isSymbolicLink()) continue;
|
|
2188
2226
|
if (!existsSync4(target)) continue;
|
|
2189
2227
|
if (dryRun) {
|
|
2190
2228
|
emitAutoMove(onPreview, linkPath, ts, name);
|
|
@@ -2199,7 +2237,7 @@ function copySharedLinkPull(src, dst) {
|
|
|
2199
2237
|
}
|
|
2200
2238
|
function stillOccupied(abs) {
|
|
2201
2239
|
try {
|
|
2202
|
-
return
|
|
2240
|
+
return lstatSync6(abs, { throwIfNoEntry: false }) !== void 0;
|
|
2203
2241
|
} catch {
|
|
2204
2242
|
return true;
|
|
2205
2243
|
}
|
|
@@ -2224,7 +2262,7 @@ function warnWin32ApplyFailed(linkPath, ts, err, snapshotted) {
|
|
|
2224
2262
|
function applyOneSharedLinkWin32(target, linkPath, ts) {
|
|
2225
2263
|
let snapshotted = false;
|
|
2226
2264
|
try {
|
|
2227
|
-
const stat =
|
|
2265
|
+
const stat = lstatSync6(linkPath, { throwIfNoEntry: false });
|
|
2228
2266
|
if (stat !== void 0) {
|
|
2229
2267
|
const snapshot = snapshotBeforeWin32Copy(linkPath, ts);
|
|
2230
2268
|
if (snapshot === "failed") return;
|
|
@@ -2339,18 +2377,6 @@ function regenerateSettings(ts, opts = {}) {
|
|
|
2339
2377
|
|
|
2340
2378
|
// src/commands.adopt.recover.ts
|
|
2341
2379
|
init_utils();
|
|
2342
|
-
function errorText(err) {
|
|
2343
|
-
const message = err?.message;
|
|
2344
|
-
return typeof message === "string" ? message : String(err);
|
|
2345
|
-
}
|
|
2346
|
-
function lexists(p) {
|
|
2347
|
-
try {
|
|
2348
|
-
lstatSync6(p);
|
|
2349
|
-
return true;
|
|
2350
|
-
} catch {
|
|
2351
|
-
return false;
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
2380
|
function isDirectChildOf(root, abs) {
|
|
2355
2381
|
return dirname2(resolve2(abs)) === resolve2(root);
|
|
2356
2382
|
}
|
|
@@ -2595,11 +2621,56 @@ function isValidAdoptName(name) {
|
|
|
2595
2621
|
if (SHARED_LINKS.includes(name)) return true;
|
|
2596
2622
|
return isValidSharedDir(name);
|
|
2597
2623
|
}
|
|
2624
|
+
function refuseUnusableSharedTarget(name) {
|
|
2625
|
+
throw new NomadFatal(
|
|
2626
|
+
`${name}: shared/${name} does not resolve to anything usable, so adopting would publish nothing. Remove shared/${name} from the repo, or restore what it points at, then re-run adopt.`,
|
|
2627
|
+
{ code: EXIT.GENERIC_FAILURE }
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
function clobberMessage(name, state) {
|
|
2631
|
+
if (state === "dangling") {
|
|
2632
|
+
return `${name}: shared/${name} is a pointer that does not resolve; would clobber. Remove it, or restore what it points at, first.`;
|
|
2633
|
+
}
|
|
2634
|
+
if (state === "unknown") {
|
|
2635
|
+
return `${name}: shared/${name} could not be read, so adopt cannot tell whether it would clobber existing content. Check its permissions in the sync repo.`;
|
|
2636
|
+
}
|
|
2637
|
+
return `${name}: shared/${name} already exists; would clobber. Remove it first.`;
|
|
2638
|
+
}
|
|
2639
|
+
function refuseClobber(name, sharedTarget) {
|
|
2640
|
+
const state = classifyPresence(sharedTarget);
|
|
2641
|
+
if (state === "absent") return;
|
|
2642
|
+
throw new NomadFatal(clobberMessage(name, state), { code: EXIT.GENERIC_FAILURE });
|
|
2643
|
+
}
|
|
2598
2644
|
function reportWin32AlreadyAdopted(name, sharedTarget) {
|
|
2599
|
-
if (process.platform !== "win32"
|
|
2645
|
+
if (process.platform !== "win32") return false;
|
|
2646
|
+
const state = classifyPresence(sharedTarget);
|
|
2647
|
+
if (state === "absent") return false;
|
|
2648
|
+
if (isUnusableTarget(state)) refuseUnusableSharedTarget(name);
|
|
2600
2649
|
log(`${name}: already adopted (win32 copy-sync); run \`nomad pull\` to refresh the local copy`);
|
|
2601
2650
|
return true;
|
|
2602
2651
|
}
|
|
2652
|
+
function alreadySymlinkMessage(name, sharedTarget) {
|
|
2653
|
+
const state = classifyPresence(sharedTarget);
|
|
2654
|
+
if (state === "dangling") {
|
|
2655
|
+
return {
|
|
2656
|
+
text: `${name}: already a symlink, but shared/${name} does not resolve, so the link is broken. Restore shared/${name} in the repo, or remove ~/.claude/${name} and re-run adopt`,
|
|
2657
|
+
healthy: false
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
if (state === "unknown") {
|
|
2661
|
+
return {
|
|
2662
|
+
text: `${name}: already a symlink, but shared/${name} could not be read, so whether the link works could not be determined. Check its permissions in the sync repo`,
|
|
2663
|
+
healthy: false
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
if (state === "absent") {
|
|
2667
|
+
return {
|
|
2668
|
+
text: `${name}: already a symlink, but there is no shared/${name} in the repo, so it is not adopted. Remove ~/.claude/${name} and re-run adopt, or run \`nomad pull\` if another host shares it`,
|
|
2669
|
+
healthy: false
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
return { text: `${name}: already adopted (already a symlink)`, healthy: true };
|
|
2673
|
+
}
|
|
2603
2674
|
function adoptStopsEarly(name, linkPath, sharedTarget) {
|
|
2604
2675
|
if (!existsSync5(linkPath)) {
|
|
2605
2676
|
if (reportWin32AlreadyAdopted(name, sharedTarget)) return true;
|
|
@@ -2607,14 +2678,13 @@ function adoptStopsEarly(name, linkPath, sharedTarget) {
|
|
|
2607
2678
|
return true;
|
|
2608
2679
|
}
|
|
2609
2680
|
if (lstatSync7(linkPath).isSymbolicLink()) {
|
|
2610
|
-
|
|
2681
|
+
const { text, healthy } = alreadySymlinkMessage(name, sharedTarget);
|
|
2682
|
+
if (healthy) log(text);
|
|
2683
|
+
else warn(text);
|
|
2611
2684
|
return true;
|
|
2612
2685
|
}
|
|
2613
2686
|
if (reportWin32AlreadyAdopted(name, sharedTarget)) return true;
|
|
2614
|
-
|
|
2615
|
-
fail(`${name}: shared/${name} already exists; would clobber. Remove it first.`);
|
|
2616
|
-
process.exit(1);
|
|
2617
|
-
}
|
|
2687
|
+
refuseClobber(name, sharedTarget);
|
|
2618
2688
|
return false;
|
|
2619
2689
|
}
|
|
2620
2690
|
function performAdoptMove(name, linkPath, sharedTarget, repo, backup) {
|
|
@@ -2647,18 +2717,19 @@ function cmdAdopt(name, opts = {}) {
|
|
|
2647
2717
|
);
|
|
2648
2718
|
}
|
|
2649
2719
|
if (!isValidAdoptName(name)) {
|
|
2650
|
-
|
|
2651
|
-
|
|
2720
|
+
throw new NomadFatal(`invalid name: ${JSON.stringify(name)}`, {
|
|
2721
|
+
code: EXIT.GENERIC_FAILURE
|
|
2722
|
+
});
|
|
2652
2723
|
}
|
|
2653
2724
|
const repo = repoHome();
|
|
2654
2725
|
const claude = claudeHome();
|
|
2655
2726
|
const backup = backupBase();
|
|
2656
2727
|
const map = readMapIfPresent(repo);
|
|
2657
2728
|
if (!isConfiguredTarget(name, map)) {
|
|
2658
|
-
|
|
2659
|
-
`${name}: not a configured shared target. Add it to sharedDirs in path-map.json first, then re-run adopt
|
|
2729
|
+
throw new NomadFatal(
|
|
2730
|
+
`${name}: not a configured shared target. Add it to sharedDirs in path-map.json first, then re-run adopt.`,
|
|
2731
|
+
{ code: EXIT.GENERIC_FAILURE }
|
|
2660
2732
|
);
|
|
2661
|
-
process.exit(1);
|
|
2662
2733
|
}
|
|
2663
2734
|
const linkPath = join9(claude, name);
|
|
2664
2735
|
const sharedTarget = join9(repo, "shared", name);
|
|
@@ -2880,7 +2951,7 @@ async function cmdCaptureSettings(opts) {
|
|
|
2880
2951
|
init_config();
|
|
2881
2952
|
init_exit_codes();
|
|
2882
2953
|
init_utils();
|
|
2883
|
-
import { existsSync as existsSync8, lstatSync as lstatSync8, readdirSync as readdirSync4, rmSync as rmSync5, statSync as
|
|
2954
|
+
import { existsSync as existsSync8, lstatSync as lstatSync8, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync3 } from "node:fs";
|
|
2884
2955
|
import { join as join12 } from "node:path";
|
|
2885
2956
|
var TS_SHAPE = /^\d{8}-\d{6}(-\d+)?$/;
|
|
2886
2957
|
var DURATION_RE = /^(\d+)([dhm])$/;
|
|
@@ -2899,7 +2970,7 @@ function parseDuration(s) {
|
|
|
2899
2970
|
}
|
|
2900
2971
|
function listBackupDirs(backupBase2) {
|
|
2901
2972
|
if (!existsSync8(backupBase2)) return [];
|
|
2902
|
-
return readdirSync4(backupBase2).filter(isTsDir).map((name) => ({ name, mtimeMs:
|
|
2973
|
+
return readdirSync4(backupBase2).filter(isTsDir).map((name) => ({ name, mtimeMs: statSync3(join12(backupBase2, name)).mtimeMs })).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
2903
2974
|
}
|
|
2904
2975
|
function holdsNoContent(dir) {
|
|
2905
2976
|
let entries;
|
|
@@ -3180,14 +3251,14 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
|
|
|
3180
3251
|
}
|
|
3181
3252
|
|
|
3182
3253
|
// src/commands.doctor.ts
|
|
3183
|
-
import { existsSync as
|
|
3184
|
-
import { join as
|
|
3254
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
3255
|
+
import { join as join49 } from "node:path";
|
|
3185
3256
|
|
|
3186
3257
|
// src/commands.doctor.checks.repo.ts
|
|
3187
3258
|
init_color();
|
|
3188
3259
|
init_config();
|
|
3189
|
-
import { existsSync as
|
|
3190
|
-
import { join as
|
|
3260
|
+
import { existsSync as existsSync11, lstatSync as lstatSync10, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
3261
|
+
import { join as join17 } from "node:path";
|
|
3191
3262
|
|
|
3192
3263
|
// src/commands.doctor.format.ts
|
|
3193
3264
|
init_color();
|
|
@@ -3265,7 +3336,6 @@ function readJsonSafe(path, label, section2) {
|
|
|
3265
3336
|
// src/commands.doctor.checks.repo.win32.ts
|
|
3266
3337
|
init_color();
|
|
3267
3338
|
init_config();
|
|
3268
|
-
import { existsSync as existsSync10 } from "node:fs";
|
|
3269
3339
|
import { join as join14, win32 as win32Path } from "node:path";
|
|
3270
3340
|
|
|
3271
3341
|
// src/extras-sync.diff.ts
|
|
@@ -3334,6 +3404,18 @@ function win32CopyUnpublishedRow(name) {
|
|
|
3334
3404
|
fail: false
|
|
3335
3405
|
};
|
|
3336
3406
|
}
|
|
3407
|
+
function win32CopyUnusableRow(name, state) {
|
|
3408
|
+
if (state === "dangling") {
|
|
3409
|
+
return {
|
|
3410
|
+
line: `${yellow(warnGlyph)} ${name}: real local copy, but shared/${name} does not resolve in the repo (remove shared/${name}, or restore what it points at)`,
|
|
3411
|
+
fail: false
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
return {
|
|
3415
|
+
line: `${yellow(warnGlyph)} ${name}: real local copy, but shared/${name} could not be read in the repo, so whether it is usable could not be determined (check its permissions in the sync repo)`,
|
|
3416
|
+
fail: false
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3337
3419
|
function relativeUnder(root, path) {
|
|
3338
3420
|
const rel = win32Path.relative(root, path);
|
|
3339
3421
|
const segments = rel.split(win32Path.sep);
|
|
@@ -3347,7 +3429,9 @@ function divergingRepoPath(line, name, local, shared) {
|
|
|
3347
3429
|
}
|
|
3348
3430
|
function classifyWin32Copy(name, p) {
|
|
3349
3431
|
const sharedPath = join14(repoHome(), "shared", name);
|
|
3350
|
-
|
|
3432
|
+
const sharedState = classifyPresence(sharedPath);
|
|
3433
|
+
if (sharedState === "absent") return win32CopyUnpublishedRow(name);
|
|
3434
|
+
if (isUnusableTarget(sharedState)) return win32CopyUnusableRow(name, sharedState);
|
|
3351
3435
|
const compared = listDivergingFiles(p, sharedPath);
|
|
3352
3436
|
const diverging = compared.filter(
|
|
3353
3437
|
(line) => deniedSegmentFor(divergingRepoPath(line, name, p, sharedPath)) === null
|
|
@@ -3361,18 +3445,69 @@ function classifyWin32Copy(name, p) {
|
|
|
3361
3445
|
};
|
|
3362
3446
|
}
|
|
3363
3447
|
|
|
3448
|
+
// src/commands.doctor.checks.repo.source.ts
|
|
3449
|
+
init_color();
|
|
3450
|
+
init_config();
|
|
3451
|
+
import { join as join15 } from "node:path";
|
|
3452
|
+
function repoSourceState(name) {
|
|
3453
|
+
return classifyPresence(join15(repoHome(), "shared", name));
|
|
3454
|
+
}
|
|
3455
|
+
function adoptRefusalClause(name, state) {
|
|
3456
|
+
if (state === "dangling") {
|
|
3457
|
+
return `shared/${name} does not resolve, so \`nomad adopt ${name}\` would refuse (remove shared/${name}, or restore what it points at, first)`;
|
|
3458
|
+
}
|
|
3459
|
+
return `shared/${name} could not be read, so \`nomad adopt ${name}\` would refuse (check its permissions in the sync repo first)`;
|
|
3460
|
+
}
|
|
3461
|
+
function adoptWouldClobberRow(name) {
|
|
3462
|
+
return {
|
|
3463
|
+
line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync), and shared/${name} already holds content, so \`nomad adopt ${name}\` would refuse rather than choose between them (compare the two, then either remove ~/.claude/${name} and run \`nomad pull\`, or remove shared/${name} and run \`nomad adopt ${name}\`)`,
|
|
3464
|
+
fail: true
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
function posixNonSymlinkRow(name) {
|
|
3468
|
+
const state = repoSourceState(name);
|
|
3469
|
+
if (isUnusableTarget(state)) {
|
|
3470
|
+
return {
|
|
3471
|
+
line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync), and ${adoptRefusalClause(name, state)}`,
|
|
3472
|
+
fail: true
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
if (state === "resolves") return adoptWouldClobberRow(name);
|
|
3476
|
+
return {
|
|
3477
|
+
line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync); run \`nomad adopt ${name}\` to fix`,
|
|
3478
|
+
fail: true
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
function repoSourceUnusableRow(name, state) {
|
|
3482
|
+
if (state === "dangling") {
|
|
3483
|
+
return {
|
|
3484
|
+
line: `${yellow(warnGlyph)} ${name}: shared/${name} in the repo does not resolve, so there is nothing to restore from (remove shared/${name}, or restore what it points at)`,
|
|
3485
|
+
fail: false
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
return {
|
|
3489
|
+
line: `${yellow(warnGlyph)} ${name}: shared/${name} in the repo could not be read, so whether there is anything to restore from could not be determined (check its permissions in the sync repo)`,
|
|
3490
|
+
fail: false
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
function repoSourceRow(name, present, absent) {
|
|
3494
|
+
const state = repoSourceState(name);
|
|
3495
|
+
if (isUnusableTarget(state)) return repoSourceUnusableRow(name, state);
|
|
3496
|
+
return state === "resolves" ? present : absent;
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3364
3499
|
// src/init.classify.ts
|
|
3365
3500
|
init_config();
|
|
3366
3501
|
init_utils_json();
|
|
3367
|
-
import { existsSync as
|
|
3368
|
-
import { join as
|
|
3502
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
3503
|
+
import { join as join16 } from "node:path";
|
|
3369
3504
|
function classifyRepoState(repoHome2, host) {
|
|
3370
|
-
const basePath =
|
|
3371
|
-
const mapPath =
|
|
3372
|
-
const hostPath =
|
|
3373
|
-
const hasBase =
|
|
3374
|
-
const hasMap =
|
|
3375
|
-
const hasHost =
|
|
3505
|
+
const basePath = join16(repoHome2, "shared", "settings.base.json");
|
|
3506
|
+
const mapPath = join16(repoHome2, "path-map.json");
|
|
3507
|
+
const hostPath = join16(repoHome2, "hosts", `${host}.json`);
|
|
3508
|
+
const hasBase = existsSync10(basePath);
|
|
3509
|
+
const hasMap = existsSync10(mapPath);
|
|
3510
|
+
const hasHost = existsSync10(hostPath);
|
|
3376
3511
|
let mapEntryCount = 0;
|
|
3377
3512
|
if (hasMap) {
|
|
3378
3513
|
try {
|
|
@@ -3387,11 +3522,11 @@ function classifyRepoState(repoHome2, host) {
|
|
|
3387
3522
|
return "partial";
|
|
3388
3523
|
}
|
|
3389
3524
|
function reasonForPartial(repoHome2, host) {
|
|
3390
|
-
const basePath =
|
|
3391
|
-
const mapPath =
|
|
3392
|
-
const hostPath =
|
|
3393
|
-
if (!
|
|
3394
|
-
if (!
|
|
3525
|
+
const basePath = join16(repoHome2, "shared", "settings.base.json");
|
|
3526
|
+
const mapPath = join16(repoHome2, "path-map.json");
|
|
3527
|
+
const hostPath = join16(repoHome2, "hosts", `${host}.json`);
|
|
3528
|
+
if (!existsSync10(basePath)) return "- shared/settings.base.json missing";
|
|
3529
|
+
if (!existsSync10(mapPath)) return "- path-map.json missing";
|
|
3395
3530
|
let mapEntryCount;
|
|
3396
3531
|
try {
|
|
3397
3532
|
const map = readJson(mapPath);
|
|
@@ -3400,7 +3535,7 @@ function reasonForPartial(repoHome2, host) {
|
|
|
3400
3535
|
mapEntryCount = 0;
|
|
3401
3536
|
}
|
|
3402
3537
|
if (mapEntryCount === 0) return "- path-map.json.projects has no entries";
|
|
3403
|
-
if (!
|
|
3538
|
+
if (!existsSync10(hostPath)) return `- hosts/${host}.json missing`;
|
|
3404
3539
|
return "- partial state (unknown gap)";
|
|
3405
3540
|
}
|
|
3406
3541
|
|
|
@@ -3417,15 +3552,15 @@ function reportHostAndPaths(section2) {
|
|
|
3417
3552
|
if (isOverrideActive()) {
|
|
3418
3553
|
addItem(section2, `${dim(infoGlyph)} NOMAD_REPO: ${blue(repo)}`);
|
|
3419
3554
|
}
|
|
3420
|
-
addItem(section2, `${
|
|
3555
|
+
addItem(section2, `${existsSync11(repo) ? green(okGlyph) : yellow(warnGlyph)} repo: ${blue(repo)}`);
|
|
3421
3556
|
addItem(
|
|
3422
3557
|
section2,
|
|
3423
|
-
`${
|
|
3558
|
+
`${existsSync11(claude) ? green(okGlyph) : yellow(warnGlyph)} claude home: ${blue(claude)}`
|
|
3424
3559
|
);
|
|
3425
3560
|
}
|
|
3426
3561
|
function pathMapHostKeys() {
|
|
3427
|
-
const mapPath =
|
|
3428
|
-
if (!
|
|
3562
|
+
const mapPath = join17(repoHome(), "path-map.json");
|
|
3563
|
+
if (!existsSync11(mapPath)) return /* @__PURE__ */ new Set();
|
|
3429
3564
|
let raw;
|
|
3430
3565
|
try {
|
|
3431
3566
|
raw = readJson(mapPath);
|
|
@@ -3442,7 +3577,7 @@ function pathMapHostKeys() {
|
|
|
3442
3577
|
function hostOverrideLabels() {
|
|
3443
3578
|
let entries;
|
|
3444
3579
|
try {
|
|
3445
|
-
entries = readdirSync5(
|
|
3580
|
+
entries = readdirSync5(join17(repoHome(), "hosts"));
|
|
3446
3581
|
} catch {
|
|
3447
3582
|
return /* @__PURE__ */ new Set();
|
|
3448
3583
|
}
|
|
@@ -3482,9 +3617,6 @@ function reportRepoState(section2) {
|
|
|
3482
3617
|
process.exitCode = 1;
|
|
3483
3618
|
}
|
|
3484
3619
|
}
|
|
3485
|
-
function repoHasSharedSource(name) {
|
|
3486
|
-
return existsSync12(join16(repoHome(), "shared", name));
|
|
3487
|
-
}
|
|
3488
3620
|
function classifySharedLink(name, p) {
|
|
3489
3621
|
let stat;
|
|
3490
3622
|
try {
|
|
@@ -3492,10 +3624,14 @@ function classifySharedLink(name, p) {
|
|
|
3492
3624
|
} catch (err) {
|
|
3493
3625
|
const code = err.code;
|
|
3494
3626
|
if (code === "ENOENT") {
|
|
3495
|
-
return
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3627
|
+
return repoSourceRow(
|
|
3628
|
+
name,
|
|
3629
|
+
{
|
|
3630
|
+
line: `${yellow(warnGlyph)} ${name}: missing (run \`nomad pull\` to restore)`,
|
|
3631
|
+
fail: false
|
|
3632
|
+
},
|
|
3633
|
+
{ line: `${dim(infoGlyph)} ${name}: not synced (nothing in shared/)`, fail: false }
|
|
3634
|
+
);
|
|
3499
3635
|
}
|
|
3500
3636
|
return { line: `${red(failGlyph)} ${name}: could not stat (${String(code)})`, fail: true };
|
|
3501
3637
|
}
|
|
@@ -3503,27 +3639,28 @@ function classifySharedLink(name, p) {
|
|
|
3503
3639
|
if (process.platform === "win32") {
|
|
3504
3640
|
return classifyWin32Copy(name, p);
|
|
3505
3641
|
}
|
|
3506
|
-
return
|
|
3507
|
-
line: `${red(failGlyph)} ${name}: NOT a symlink (blocks sync); run \`nomad adopt ${name}\` to fix`,
|
|
3508
|
-
fail: true
|
|
3509
|
-
};
|
|
3642
|
+
return posixNonSymlinkRow(name);
|
|
3510
3643
|
}
|
|
3511
3644
|
return classifySymlinkTarget(name, p);
|
|
3512
3645
|
}
|
|
3513
3646
|
function classifySymlinkTarget(name, p) {
|
|
3514
3647
|
try {
|
|
3515
|
-
|
|
3648
|
+
statSync4(p);
|
|
3516
3649
|
return { line: `${green(okGlyph)} ${name}: symlink`, fail: false };
|
|
3517
3650
|
} catch (err) {
|
|
3518
3651
|
const code = err.code;
|
|
3519
3652
|
if (code === "ENOENT") {
|
|
3520
|
-
return
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3653
|
+
return repoSourceRow(
|
|
3654
|
+
name,
|
|
3655
|
+
{
|
|
3656
|
+
line: `${yellow(warnGlyph)} ${name}: broken symlink (target missing, run \`nomad pull\`)`,
|
|
3657
|
+
fail: false
|
|
3658
|
+
},
|
|
3659
|
+
{
|
|
3660
|
+
line: `${dim(infoGlyph)} ${name}: stale symlink (no longer in shared/, safe to remove)`,
|
|
3661
|
+
fail: false
|
|
3662
|
+
}
|
|
3663
|
+
);
|
|
3527
3664
|
}
|
|
3528
3665
|
return {
|
|
3529
3666
|
line: `${yellow(warnGlyph)} ${name}: symlink target unreadable (${String(code)})`,
|
|
@@ -3534,7 +3671,7 @@ function classifySymlinkTarget(name, p) {
|
|
|
3534
3671
|
function reportSharedLinks(section2, map) {
|
|
3535
3672
|
const claude = claudeHome();
|
|
3536
3673
|
for (const name of allSharedLinks(map, { quiet: true })) {
|
|
3537
|
-
const p =
|
|
3674
|
+
const p = join17(claude, name);
|
|
3538
3675
|
const { line, fail: fail2, children } = classifySharedLink(name, p);
|
|
3539
3676
|
addItem(section2, line);
|
|
3540
3677
|
if (fail2) process.exitCode = 1;
|
|
@@ -3546,7 +3683,7 @@ function reportSharedLinks(section2, map) {
|
|
|
3546
3683
|
function reportDroppedNamesMigration(section2) {
|
|
3547
3684
|
const claude = claudeHome();
|
|
3548
3685
|
for (const name of GSD_DROPPED_NAMES) {
|
|
3549
|
-
const p =
|
|
3686
|
+
const p = join17(claude, name);
|
|
3550
3687
|
let stat;
|
|
3551
3688
|
try {
|
|
3552
3689
|
stat = lstatSync10(p);
|
|
@@ -3564,11 +3701,11 @@ function reportDroppedNamesMigration(section2) {
|
|
|
3564
3701
|
// src/commands.doctor.checks.settings.ts
|
|
3565
3702
|
init_color();
|
|
3566
3703
|
init_config();
|
|
3567
|
-
import { existsSync as
|
|
3568
|
-
import { join as
|
|
3704
|
+
import { existsSync as existsSync12, readdirSync as readdirSync6 } from "node:fs";
|
|
3705
|
+
import { join as join18 } from "node:path";
|
|
3569
3706
|
function loadBaseSettings(section2) {
|
|
3570
|
-
const basePath =
|
|
3571
|
-
if (!
|
|
3707
|
+
const basePath = join18(repoHome(), "shared", "settings.base.json");
|
|
3708
|
+
if (!existsSync12(basePath)) {
|
|
3572
3709
|
addItem(section2, `${red(failGlyph)} shared/settings.base.json missing at ${blue(basePath)}`);
|
|
3573
3710
|
process.exitCode = 1;
|
|
3574
3711
|
return null;
|
|
@@ -3576,8 +3713,8 @@ function loadBaseSettings(section2) {
|
|
|
3576
3713
|
return readJsonSafe(basePath, basePath, section2);
|
|
3577
3714
|
}
|
|
3578
3715
|
function loadAndReportSettings(section2) {
|
|
3579
|
-
const settingsPath =
|
|
3580
|
-
if (!
|
|
3716
|
+
const settingsPath = join18(claudeHome(), "settings.json");
|
|
3717
|
+
if (!existsSync12(settingsPath)) return null;
|
|
3581
3718
|
const settings = readJsonSafe(settingsPath, settingsPath, section2);
|
|
3582
3719
|
if (settings === null) return null;
|
|
3583
3720
|
const unknownKeys = Object.keys(settings).filter((k) => !KNOWN_SETTINGS_KEYS.has(k));
|
|
@@ -3593,13 +3730,13 @@ function loadAndReportSettings(section2) {
|
|
|
3593
3730
|
}
|
|
3594
3731
|
function reportHostOverrides(section2, base, settings) {
|
|
3595
3732
|
const repo = repoHome();
|
|
3596
|
-
const hostFile =
|
|
3733
|
+
const hostFile = join18(repo, "hosts", `${HOST}.json`);
|
|
3597
3734
|
let drift = [];
|
|
3598
3735
|
if (base !== null && settings !== null) {
|
|
3599
3736
|
const baseKeys = new Set(Object.keys(base));
|
|
3600
3737
|
drift = Object.keys(settings).filter((k) => !baseKeys.has(k));
|
|
3601
3738
|
}
|
|
3602
|
-
if (
|
|
3739
|
+
if (existsSync12(hostFile)) {
|
|
3603
3740
|
if (readJsonSafe(hostFile, hostFile, section2) !== null) {
|
|
3604
3741
|
addItem(section2, `${green(okGlyph)} host overrides: ${blue(hostFile)}`);
|
|
3605
3742
|
}
|
|
@@ -3608,8 +3745,8 @@ function reportHostOverrides(section2, base, settings) {
|
|
|
3608
3745
|
section2,
|
|
3609
3746
|
`${red(failGlyph)} no hosts/${HOST}.json AND settings.json has unbased keys ${JSON.stringify(drift)}`
|
|
3610
3747
|
);
|
|
3611
|
-
const hostsDir =
|
|
3612
|
-
if (
|
|
3748
|
+
const hostsDir = join18(repo, "hosts");
|
|
3749
|
+
if (existsSync12(hostsDir)) {
|
|
3613
3750
|
const cands = readdirSync6(hostsDir).filter((f) => f.endsWith(".json"));
|
|
3614
3751
|
if (cands.length > 0) addItem(section2, `${dim(infoGlyph)} candidates: ${cands.join(", ")}`);
|
|
3615
3752
|
}
|
|
@@ -3626,8 +3763,8 @@ function reportHostOverrides(section2, base, settings) {
|
|
|
3626
3763
|
init_color();
|
|
3627
3764
|
init_config();
|
|
3628
3765
|
init_config_sharedDirs_guard();
|
|
3629
|
-
import { existsSync as
|
|
3630
|
-
import { join as
|
|
3766
|
+
import { existsSync as existsSync13, lstatSync as lstatSync11, readdirSync as readdirSync7, realpathSync as realpathSync2 } from "node:fs";
|
|
3767
|
+
import { join as join19, sep as sep4 } from "node:path";
|
|
3631
3768
|
init_utils_json();
|
|
3632
3769
|
function reportMappedProjects(section2, map) {
|
|
3633
3770
|
const mapped = Object.entries(map.projects).filter(([, hosts]) => hosts[HOST]);
|
|
@@ -3637,8 +3774,8 @@ function reportMappedProjects(section2, map) {
|
|
|
3637
3774
|
}
|
|
3638
3775
|
}
|
|
3639
3776
|
function reportUnmappedProjects(section2, map) {
|
|
3640
|
-
const localProjects =
|
|
3641
|
-
if (!
|
|
3777
|
+
const localProjects = join19(claudeHome(), "projects");
|
|
3778
|
+
if (!existsSync13(localProjects)) return;
|
|
3642
3779
|
let localDirs;
|
|
3643
3780
|
try {
|
|
3644
3781
|
localDirs = readdirSync7(localProjects);
|
|
@@ -3659,7 +3796,7 @@ function reportCurrentHostPathsMissing(section2, map) {
|
|
|
3659
3796
|
for (const [name, hosts] of Object.entries(map.projects)) {
|
|
3660
3797
|
const abspath = hosts[HOST];
|
|
3661
3798
|
if (!abspath || abspath === "TBD") continue;
|
|
3662
|
-
if (!
|
|
3799
|
+
if (!existsSync13(abspath)) {
|
|
3663
3800
|
addItem(
|
|
3664
3801
|
section2,
|
|
3665
3802
|
`${yellow(warnGlyph)} path-map: ${name} local path missing on ${HOST}: ${blue(abspath)}`
|
|
@@ -3669,7 +3806,7 @@ function reportCurrentHostPathsMissing(section2, map) {
|
|
|
3669
3806
|
}
|
|
3670
3807
|
function hasSharedLeftover(entry) {
|
|
3671
3808
|
try {
|
|
3672
|
-
return lstatSync11(
|
|
3809
|
+
return lstatSync11(join19(repoHome(), "shared", entry), { throwIfNoEntry: false }) !== void 0;
|
|
3673
3810
|
} catch {
|
|
3674
3811
|
return false;
|
|
3675
3812
|
}
|
|
@@ -3679,7 +3816,7 @@ var PROBEABLE_REASONS = /* @__PURE__ */ new Set([
|
|
|
3679
3816
|
"secret-shaped"
|
|
3680
3817
|
]);
|
|
3681
3818
|
function classifyLocalLink(entry) {
|
|
3682
|
-
const linkPath =
|
|
3819
|
+
const linkPath = join19(claudeHome(), entry);
|
|
3683
3820
|
let stat;
|
|
3684
3821
|
try {
|
|
3685
3822
|
stat = lstatSync11(linkPath, { throwIfNoEntry: false });
|
|
@@ -3695,7 +3832,7 @@ function classifyLocalLink(entry) {
|
|
|
3695
3832
|
}
|
|
3696
3833
|
let root;
|
|
3697
3834
|
try {
|
|
3698
|
-
root = realpathSync2(
|
|
3835
|
+
root = realpathSync2(join19(repoHome(), "shared"));
|
|
3699
3836
|
} catch {
|
|
3700
3837
|
return "foreign";
|
|
3701
3838
|
}
|
|
@@ -3779,8 +3916,8 @@ function reportPathCollisions(section2, map) {
|
|
|
3779
3916
|
else addItem(section2, `${green(okGlyph)} path-encoding: no collisions`);
|
|
3780
3917
|
}
|
|
3781
3918
|
function reportPathMap(section2) {
|
|
3782
|
-
const mapPath =
|
|
3783
|
-
if (!
|
|
3919
|
+
const mapPath = join19(repoHome(), "path-map.json");
|
|
3920
|
+
if (!existsSync13(mapPath)) {
|
|
3784
3921
|
addItem(section2, `${red(failGlyph)} path-map.json missing at ${blue(mapPath)}`);
|
|
3785
3922
|
process.exitCode = 1;
|
|
3786
3923
|
return;
|
|
@@ -3810,8 +3947,8 @@ function reportNeverSync(section2) {
|
|
|
3810
3947
|
|
|
3811
3948
|
// src/commands.doctor.checks.skills.ts
|
|
3812
3949
|
init_color();
|
|
3813
|
-
import { existsSync as
|
|
3814
|
-
import { join as
|
|
3950
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
3951
|
+
import { join as join20 } from "node:path";
|
|
3815
3952
|
init_config();
|
|
3816
3953
|
function stripSideIndicator(line) {
|
|
3817
3954
|
if (line.endsWith(" (local only)")) return line.slice(0, -" (local only)".length);
|
|
@@ -3831,13 +3968,13 @@ function isGsdDiffLine(line, localBase, sharedBase) {
|
|
|
3831
3968
|
return relative12.split("/")[0].startsWith(GSD_PREFIX);
|
|
3832
3969
|
}
|
|
3833
3970
|
function reportSkillsDivergence(section2) {
|
|
3834
|
-
const sharedSkills =
|
|
3835
|
-
const localSkills =
|
|
3836
|
-
if (!
|
|
3971
|
+
const sharedSkills = join20(repoHome(), "shared", "skills");
|
|
3972
|
+
const localSkills = join20(claudeHome(), "skills");
|
|
3973
|
+
if (!existsSync14(sharedSkills)) {
|
|
3837
3974
|
addItem(section2, `${dim(infoGlyph)} skills: no shared/skills/ to compare`);
|
|
3838
3975
|
return;
|
|
3839
3976
|
}
|
|
3840
|
-
if (!
|
|
3977
|
+
if (!existsSync14(localSkills)) {
|
|
3841
3978
|
addItem(section2, `${dim(infoGlyph)} skills: no local skills/ to compare`);
|
|
3842
3979
|
return;
|
|
3843
3980
|
}
|
|
@@ -3860,8 +3997,8 @@ function reportSkillsDivergence(section2) {
|
|
|
3860
3997
|
init_color();
|
|
3861
3998
|
init_config();
|
|
3862
3999
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3863
|
-
import { existsSync as
|
|
3864
|
-
import { join as
|
|
4000
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
4001
|
+
import { join as join24, relative as relative5 } from "node:path";
|
|
3865
4002
|
init_commands_pull_wedge();
|
|
3866
4003
|
init_push_checks();
|
|
3867
4004
|
init_utils();
|
|
@@ -3884,8 +4021,8 @@ function reportGitleaksProbe(section2) {
|
|
|
3884
4021
|
}
|
|
3885
4022
|
function reportGitlinks(section2) {
|
|
3886
4023
|
const repo = repoHome();
|
|
3887
|
-
const sharedDir =
|
|
3888
|
-
if (
|
|
4024
|
+
const sharedDir = join24(repo, "shared");
|
|
4025
|
+
if (existsSync17(sharedDir)) {
|
|
3889
4026
|
const gitlinks = findGitlinks(sharedDir);
|
|
3890
4027
|
for (const p of gitlinks) {
|
|
3891
4028
|
const rel = relative5(repo, p).replaceAll("\\", "/");
|
|
@@ -3983,8 +4120,8 @@ function reportGitIdentity(section2) {
|
|
|
3983
4120
|
|
|
3984
4121
|
// src/commands.doctor.checks.backups.ts
|
|
3985
4122
|
init_color();
|
|
3986
|
-
import { existsSync as
|
|
3987
|
-
import { join as
|
|
4123
|
+
import { existsSync as existsSync18, lstatSync as lstatSync12, readdirSync as readdirSync9 } from "node:fs";
|
|
4124
|
+
import { join as join25 } from "node:path";
|
|
3988
4125
|
init_config();
|
|
3989
4126
|
var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
|
|
3990
4127
|
function safeReaddir(dir) {
|
|
@@ -4000,7 +4137,7 @@ var BYTES_PER_MB = 1024 * 1024;
|
|
|
4000
4137
|
function dirSizeBytes(dir) {
|
|
4001
4138
|
let bytes = 0;
|
|
4002
4139
|
for (const entry of safeReaddir(dir)) {
|
|
4003
|
-
const full =
|
|
4140
|
+
const full = join25(dir, entry);
|
|
4004
4141
|
const st = lstatSync12(full, { throwIfNoEntry: false });
|
|
4005
4142
|
if (!st) continue;
|
|
4006
4143
|
if (st.isSymbolicLink()) continue;
|
|
@@ -4011,11 +4148,11 @@ function dirSizeBytes(dir) {
|
|
|
4011
4148
|
}
|
|
4012
4149
|
function totalSizeMb(backupBase2, dirs) {
|
|
4013
4150
|
let bytes = 0;
|
|
4014
|
-
for (const name of dirs) bytes += dirSizeBytes(
|
|
4151
|
+
for (const name of dirs) bytes += dirSizeBytes(join25(backupBase2, name));
|
|
4015
4152
|
return bytes / BYTES_PER_MB;
|
|
4016
4153
|
}
|
|
4017
4154
|
function reportBackupsCheck(section2, backupBase2 = backupBase()) {
|
|
4018
|
-
if (!
|
|
4155
|
+
if (!existsSync18(backupBase2)) return;
|
|
4019
4156
|
const dirs = safeReaddir(backupBase2).filter((n) => TS_SHAPE2.test(n));
|
|
4020
4157
|
const count = dirs.length;
|
|
4021
4158
|
const sizeMb = totalSizeMb(backupBase2, dirs);
|
|
@@ -4096,8 +4233,8 @@ function reportCheckRemote(section2) {
|
|
|
4096
4233
|
|
|
4097
4234
|
// src/commands.doctor.check-schema.ts
|
|
4098
4235
|
init_color();
|
|
4099
|
-
import { existsSync as
|
|
4100
|
-
import { join as
|
|
4236
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
4237
|
+
import { join as join26 } from "node:path";
|
|
4101
4238
|
init_config();
|
|
4102
4239
|
|
|
4103
4240
|
// src/http-fetch.ts
|
|
@@ -4134,8 +4271,8 @@ function fetchSchemaKeys() {
|
|
|
4134
4271
|
}
|
|
4135
4272
|
}
|
|
4136
4273
|
function reportCheckSchema(section2) {
|
|
4137
|
-
const settingsPath =
|
|
4138
|
-
if (!
|
|
4274
|
+
const settingsPath = join26(claudeHome(), "settings.json");
|
|
4275
|
+
if (!existsSync19(settingsPath)) {
|
|
4139
4276
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json to check`);
|
|
4140
4277
|
return;
|
|
4141
4278
|
}
|
|
@@ -4165,9 +4302,9 @@ function reportCheckSchema(section2) {
|
|
|
4165
4302
|
init_color();
|
|
4166
4303
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
4167
4304
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
4168
|
-
import { existsSync as
|
|
4305
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync12 } from "node:fs";
|
|
4169
4306
|
import { homedir as homedir6 } from "node:os";
|
|
4170
|
-
import { join as
|
|
4307
|
+
import { join as join33 } from "node:path";
|
|
4171
4308
|
|
|
4172
4309
|
// src/commands.doctor.check-shared.memory.ts
|
|
4173
4310
|
init_color();
|
|
@@ -4175,7 +4312,7 @@ import { execFileSync as execFileSync9 } from "node:child_process";
|
|
|
4175
4312
|
import { randomBytes } from "node:crypto";
|
|
4176
4313
|
import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4177
4314
|
import { homedir as homedir4 } from "node:os";
|
|
4178
|
-
import { dirname as dirname4, join as
|
|
4315
|
+
import { dirname as dirname4, join as join28 } from "node:path";
|
|
4179
4316
|
init_config();
|
|
4180
4317
|
init_push_gitleaks();
|
|
4181
4318
|
init_utils_fs();
|
|
@@ -4208,7 +4345,7 @@ function buildMemoryScanTree(tmpRoot) {
|
|
|
4208
4345
|
incomplete = true;
|
|
4209
4346
|
continue;
|
|
4210
4347
|
}
|
|
4211
|
-
const dest =
|
|
4348
|
+
const dest = join28(tmpRoot, rel);
|
|
4212
4349
|
mkdirSync4(dirname4(dest), { recursive: true });
|
|
4213
4350
|
writeFileSync4(dest, blob);
|
|
4214
4351
|
logicals.add(m[1]);
|
|
@@ -4227,10 +4364,10 @@ function reportMemoryFindings(section2, findings) {
|
|
|
4227
4364
|
function reportCommittedMemory(section2) {
|
|
4228
4365
|
let tmpRoot;
|
|
4229
4366
|
try {
|
|
4230
|
-
const cacheDir =
|
|
4367
|
+
const cacheDir = join28(homedir4(), ".cache", "claude-nomad");
|
|
4231
4368
|
mkdirSync4(cacheDir, { recursive: true });
|
|
4232
4369
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
4233
|
-
tmpRoot =
|
|
4370
|
+
tmpRoot = join28(cacheDir, `check-shared-memory-tree-${stamp}`);
|
|
4234
4371
|
mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
|
|
4235
4372
|
const { staged, incomplete } = buildMemoryScanTree(tmpRoot);
|
|
4236
4373
|
if (incomplete) {
|
|
@@ -4277,12 +4414,12 @@ function reportCommittedMemory(section2) {
|
|
|
4277
4414
|
|
|
4278
4415
|
// src/commands.doctor.check-shared.scan.ts
|
|
4279
4416
|
init_color();
|
|
4280
|
-
import { join as
|
|
4417
|
+
import { join as join29 } from "node:path";
|
|
4281
4418
|
init_config();
|
|
4282
4419
|
init_push_gitleaks();
|
|
4283
4420
|
function scrubPath(logical, sid, logicalToEncoded) {
|
|
4284
4421
|
const encoded = logicalToEncoded.get(logical) ?? logical;
|
|
4285
|
-
return
|
|
4422
|
+
return join29(claudeHome(), "projects", encoded, `${sid}.jsonl`);
|
|
4286
4423
|
}
|
|
4287
4424
|
function reportSessionFindings(section2, bySession) {
|
|
4288
4425
|
for (const [sid, counts] of bySession) {
|
|
@@ -4371,7 +4508,7 @@ import { execFileSync as execFileSync10 } from "node:child_process";
|
|
|
4371
4508
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4372
4509
|
import { mkdirSync as mkdirSync5, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4373
4510
|
import { homedir as homedir5 } from "node:os";
|
|
4374
|
-
import { dirname as dirname5, join as
|
|
4511
|
+
import { dirname as dirname5, join as join30 } from "node:path";
|
|
4375
4512
|
init_config();
|
|
4376
4513
|
init_push_gitleaks();
|
|
4377
4514
|
init_utils_fs();
|
|
@@ -4404,7 +4541,7 @@ function buildSkillScanTree(tmpRoot) {
|
|
|
4404
4541
|
incomplete = true;
|
|
4405
4542
|
continue;
|
|
4406
4543
|
}
|
|
4407
|
-
const dest =
|
|
4544
|
+
const dest = join30(tmpRoot, rel);
|
|
4408
4545
|
mkdirSync5(dirname5(dest), { recursive: true });
|
|
4409
4546
|
writeFileSync5(dest, blob);
|
|
4410
4547
|
names.add(m[1]);
|
|
@@ -4427,10 +4564,10 @@ function reportSkillFindings(section2, findings) {
|
|
|
4427
4564
|
function reportCommittedSkills(section2) {
|
|
4428
4565
|
let tmpRoot;
|
|
4429
4566
|
try {
|
|
4430
|
-
const cacheDir =
|
|
4567
|
+
const cacheDir = join30(homedir5(), ".cache", "claude-nomad");
|
|
4431
4568
|
mkdirSync5(cacheDir, { recursive: true });
|
|
4432
4569
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
4433
|
-
tmpRoot =
|
|
4570
|
+
tmpRoot = join30(cacheDir, `check-shared-skills-tree-${stamp}`);
|
|
4434
4571
|
mkdirSync5(tmpRoot, { recursive: true, mode: 448 });
|
|
4435
4572
|
const { staged, incomplete } = buildSkillScanTree(tmpRoot);
|
|
4436
4573
|
if (incomplete) {
|
|
@@ -4480,8 +4617,8 @@ init_config();
|
|
|
4480
4617
|
|
|
4481
4618
|
// src/remap.ts
|
|
4482
4619
|
init_config_sharedDirs_guard();
|
|
4483
|
-
import { cpSync as cpSync5, existsSync as
|
|
4484
|
-
import { dirname as dirname7, join as
|
|
4620
|
+
import { cpSync as cpSync5, existsSync as existsSync21, lstatSync as lstatSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11, statSync as statSync6 } from "node:fs";
|
|
4621
|
+
import { dirname as dirname7, join as join32, relative as relative6, sep as sep5 } from "node:path";
|
|
4485
4622
|
init_config();
|
|
4486
4623
|
|
|
4487
4624
|
// src/push-manifest.ts
|
|
@@ -4489,8 +4626,8 @@ init_config();
|
|
|
4489
4626
|
init_push_gitleaks_config();
|
|
4490
4627
|
init_utils_fs();
|
|
4491
4628
|
import { createHash } from "node:crypto";
|
|
4492
|
-
import { existsSync as
|
|
4493
|
-
import { dirname as dirname6, join as
|
|
4629
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync10, readFileSync as readFileSync7, statSync as statSync5 } from "node:fs";
|
|
4630
|
+
import { dirname as dirname6, join as join31 } from "node:path";
|
|
4494
4631
|
function isChanged(prev, cur, hash) {
|
|
4495
4632
|
if (prev === void 0) return true;
|
|
4496
4633
|
if (prev.size !== cur.size) return true;
|
|
@@ -4531,8 +4668,8 @@ function hashFile(absPath) {
|
|
|
4531
4668
|
}
|
|
4532
4669
|
function enumerateDir(dir, results) {
|
|
4533
4670
|
for (const entry of readdirSync10(dir)) {
|
|
4534
|
-
const fullPath =
|
|
4535
|
-
const st =
|
|
4671
|
+
const fullPath = join31(dir, entry);
|
|
4672
|
+
const st = statSync5(fullPath);
|
|
4536
4673
|
if (st.isDirectory()) {
|
|
4537
4674
|
enumerateDir(fullPath, results);
|
|
4538
4675
|
} else {
|
|
@@ -4543,8 +4680,8 @@ function enumerateDir(dir, results) {
|
|
|
4543
4680
|
function enumerateSourceFiles(projectDir) {
|
|
4544
4681
|
const results = [];
|
|
4545
4682
|
for (const entry of readdirSync10(projectDir)) {
|
|
4546
|
-
const fullPath =
|
|
4547
|
-
const st =
|
|
4683
|
+
const fullPath = join31(projectDir, entry);
|
|
4684
|
+
const st = statSync5(fullPath);
|
|
4548
4685
|
if (st.isDirectory()) {
|
|
4549
4686
|
enumerateDir(fullPath, results);
|
|
4550
4687
|
} else if (fullPath.endsWith(".jsonl")) {
|
|
@@ -4555,15 +4692,15 @@ function enumerateSourceFiles(projectDir) {
|
|
|
4555
4692
|
}
|
|
4556
4693
|
function fileIdentity(p) {
|
|
4557
4694
|
if (p === null) return "none::absent";
|
|
4558
|
-
if (!
|
|
4695
|
+
if (!existsSync20(p)) return `${p}::absent`;
|
|
4559
4696
|
return `${p}::${createHash("sha256").update(readFileSync7(p)).digest("hex")}`;
|
|
4560
4697
|
}
|
|
4561
4698
|
function computeConfigHash() {
|
|
4562
4699
|
const repo = repoHome();
|
|
4563
4700
|
const parts = [
|
|
4564
4701
|
fileIdentity(resolveTomlPath(repo)),
|
|
4565
|
-
fileIdentity(
|
|
4566
|
-
fileIdentity(
|
|
4702
|
+
fileIdentity(join31(repo, ".gitleaks.overlay.toml")),
|
|
4703
|
+
fileIdentity(join31(repo, ".gitleaksignore"))
|
|
4567
4704
|
];
|
|
4568
4705
|
return createHash("sha256").update(parts.join("\n")).digest("hex");
|
|
4569
4706
|
}
|
|
@@ -4625,12 +4762,12 @@ function applySelective(sel, localDir, repoDst) {
|
|
|
4625
4762
|
const prefix = `${localDir}${sep5}`;
|
|
4626
4763
|
for (const src of sel.changed) {
|
|
4627
4764
|
if (!src.startsWith(prefix)) continue;
|
|
4628
|
-
copyFileAtomic(src,
|
|
4765
|
+
copyFileAtomic(src, join32(repoDst, relative6(localDir, src)));
|
|
4629
4766
|
}
|
|
4630
4767
|
for (const src of sel.deleted) {
|
|
4631
4768
|
if (!src.startsWith(prefix)) continue;
|
|
4632
|
-
const dst =
|
|
4633
|
-
if (
|
|
4769
|
+
const dst = join32(repoDst, relative6(localDir, src));
|
|
4770
|
+
if (existsSync21(dst)) rmSync11(dst);
|
|
4634
4771
|
}
|
|
4635
4772
|
}
|
|
4636
4773
|
function copyDirJsonlOnly(src, dst) {
|
|
@@ -4641,7 +4778,7 @@ function copyDirJsonlOnly(src, dst) {
|
|
|
4641
4778
|
const rel = relative6(src, srcPath);
|
|
4642
4779
|
if (rel === "") return true;
|
|
4643
4780
|
if (rel.split(sep5).length > 1) return true;
|
|
4644
|
-
if (
|
|
4781
|
+
if (statSync6(srcPath).isDirectory()) return true;
|
|
4645
4782
|
if (srcPath.endsWith(".jsonl")) return true;
|
|
4646
4783
|
item(`skip ${rel}: extension not in allowlist`);
|
|
4647
4784
|
return false;
|
|
@@ -4659,15 +4796,15 @@ function remapPull(ts, opts = {}) {
|
|
|
4659
4796
|
const wouldPull = [];
|
|
4660
4797
|
const repo = repoHome();
|
|
4661
4798
|
const claude = claudeHome();
|
|
4662
|
-
const mapPath =
|
|
4663
|
-
const repoProjects =
|
|
4664
|
-
if (!
|
|
4799
|
+
const mapPath = join32(repo, "path-map.json");
|
|
4800
|
+
const repoProjects = join32(repo, "shared", "projects");
|
|
4801
|
+
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) {
|
|
4665
4802
|
const text = "no path-map or repo projects dir; skipping session remap";
|
|
4666
4803
|
emitPreview(opts.onPreview, { kind: "note", text }, text);
|
|
4667
4804
|
return { unmapped: 0, pulled, wouldPull };
|
|
4668
4805
|
}
|
|
4669
4806
|
const map = readPathMap(mapPath);
|
|
4670
|
-
const localProjects =
|
|
4807
|
+
const localProjects = join32(claude, "projects");
|
|
4671
4808
|
if (!dryRun) mkdirSync7(localProjects, { recursive: true });
|
|
4672
4809
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
4673
4810
|
assertSafeLogical(logical);
|
|
@@ -4677,9 +4814,9 @@ function remapPull(ts, opts = {}) {
|
|
|
4677
4814
|
continue;
|
|
4678
4815
|
}
|
|
4679
4816
|
assertSafeLocalRoot(localPath, logical);
|
|
4680
|
-
const src =
|
|
4681
|
-
if (!
|
|
4682
|
-
const dst =
|
|
4817
|
+
const src = join32(repoProjects, logical);
|
|
4818
|
+
if (!existsSync21(src)) continue;
|
|
4819
|
+
const dst = join32(localProjects, encodePath(localPath));
|
|
4683
4820
|
if (dryRun) {
|
|
4684
4821
|
wouldPull.push(logical);
|
|
4685
4822
|
emitPreview(
|
|
@@ -4698,8 +4835,8 @@ function remapPull(ts, opts = {}) {
|
|
|
4698
4835
|
function countLocalOnly(src, dst) {
|
|
4699
4836
|
let count = 0;
|
|
4700
4837
|
for (const name of readdirSync11(dst)) {
|
|
4701
|
-
const dstPath =
|
|
4702
|
-
const srcPath =
|
|
4838
|
+
const dstPath = join32(dst, name);
|
|
4839
|
+
const srcPath = join32(src, name);
|
|
4703
4840
|
if (lstatSync13(dstPath).isDirectory()) {
|
|
4704
4841
|
count += countLocalOnly(srcPath, dstPath);
|
|
4705
4842
|
} else if (lstatSync13(srcPath, { throwIfNoEntry: false }) === void 0) {
|
|
@@ -4711,20 +4848,20 @@ function countLocalOnly(src, dst) {
|
|
|
4711
4848
|
function scanLocalOnly() {
|
|
4712
4849
|
const repo = repoHome();
|
|
4713
4850
|
const claude = claudeHome();
|
|
4714
|
-
const mapPath =
|
|
4715
|
-
const repoProjects =
|
|
4716
|
-
if (!
|
|
4851
|
+
const mapPath = join32(repo, "path-map.json");
|
|
4852
|
+
const repoProjects = join32(repo, "shared", "projects");
|
|
4853
|
+
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) return 0;
|
|
4717
4854
|
const map = readPathMap(mapPath);
|
|
4718
|
-
const localProjects =
|
|
4855
|
+
const localProjects = join32(claude, "projects");
|
|
4719
4856
|
let count = 0;
|
|
4720
4857
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
4721
4858
|
assertSafeLogical(logical);
|
|
4722
4859
|
const localPath = hosts[HOST];
|
|
4723
4860
|
if (!localPath || localPath === "TBD") continue;
|
|
4724
4861
|
assertSafeLocalRoot(localPath, logical);
|
|
4725
|
-
const dst =
|
|
4726
|
-
if (!
|
|
4727
|
-
count += countLocalOnly(
|
|
4862
|
+
const dst = join32(localProjects, encodePath(localPath));
|
|
4863
|
+
if (!existsSync21(dst)) continue;
|
|
4864
|
+
count += countLocalOnly(join32(repoProjects, logical), dst);
|
|
4728
4865
|
}
|
|
4729
4866
|
return count;
|
|
4730
4867
|
}
|
|
@@ -4759,16 +4896,16 @@ function remapPush(ts, opts = {}) {
|
|
|
4759
4896
|
const wouldPush = [];
|
|
4760
4897
|
const repo = repoHome();
|
|
4761
4898
|
const claude = claudeHome();
|
|
4762
|
-
const mapPath =
|
|
4763
|
-
if (!
|
|
4899
|
+
const mapPath = join32(repo, "path-map.json");
|
|
4900
|
+
if (!existsSync21(mapPath)) {
|
|
4764
4901
|
log("no path-map.json; skipping session export");
|
|
4765
4902
|
return { unmapped: 0, collisions: 0, pushed, wouldPush };
|
|
4766
4903
|
}
|
|
4767
4904
|
const map = readPathMap(mapPath);
|
|
4768
|
-
const localProjects =
|
|
4769
|
-
const repoProjects =
|
|
4905
|
+
const localProjects = join32(claude, "projects");
|
|
4906
|
+
const repoProjects = join32(repo, "shared", "projects");
|
|
4770
4907
|
const reverse = buildReverseMap(map);
|
|
4771
|
-
if (!
|
|
4908
|
+
if (!existsSync21(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
|
|
4772
4909
|
if (!dryRun) mkdirSync7(repoProjects, { recursive: true });
|
|
4773
4910
|
for (const dir of readdirSync11(localProjects)) {
|
|
4774
4911
|
if (dir.endsWith(TMP_SUFFIX)) continue;
|
|
@@ -4777,8 +4914,8 @@ function remapPush(ts, opts = {}) {
|
|
|
4777
4914
|
unmapped++;
|
|
4778
4915
|
continue;
|
|
4779
4916
|
}
|
|
4780
|
-
const localDir =
|
|
4781
|
-
const repoDst =
|
|
4917
|
+
const localDir = join32(localProjects, dir);
|
|
4918
|
+
const repoDst = join32(repoProjects, logical);
|
|
4782
4919
|
if (skipForSelection(opts.selection, localDir)) continue;
|
|
4783
4920
|
if (dryRun) {
|
|
4784
4921
|
wouldPush.push(logical);
|
|
@@ -4802,8 +4939,8 @@ function buildScanTree(tmpRoot) {
|
|
|
4802
4939
|
const logicalToEncoded = /* @__PURE__ */ new Map();
|
|
4803
4940
|
let staged = 0;
|
|
4804
4941
|
const repo = repoHome();
|
|
4805
|
-
const mapPath =
|
|
4806
|
-
if (!
|
|
4942
|
+
const mapPath = join33(repo, "path-map.json");
|
|
4943
|
+
if (!existsSync22(mapPath)) return { logicalToEncoded, staged, malformed: false };
|
|
4807
4944
|
let map;
|
|
4808
4945
|
try {
|
|
4809
4946
|
map = readJson(mapPath);
|
|
@@ -4820,12 +4957,12 @@ function buildScanTree(tmpRoot) {
|
|
|
4820
4957
|
if (!p || p === "TBD") continue;
|
|
4821
4958
|
reverse.set(encodePath(p), logical);
|
|
4822
4959
|
}
|
|
4823
|
-
const localProjects =
|
|
4824
|
-
if (!
|
|
4960
|
+
const localProjects = join33(claudeHome(), "projects");
|
|
4961
|
+
if (!existsSync22(localProjects)) return { logicalToEncoded, staged, malformed: false };
|
|
4825
4962
|
for (const dir of readdirSync12(localProjects)) {
|
|
4826
4963
|
const logical = reverse.get(dir);
|
|
4827
4964
|
if (!logical) continue;
|
|
4828
|
-
copyDirJsonlOnly(
|
|
4965
|
+
copyDirJsonlOnly(join33(localProjects, dir), join33(tmpRoot, "shared", "projects", logical));
|
|
4829
4966
|
logicalToEncoded.set(logical, dir);
|
|
4830
4967
|
staged++;
|
|
4831
4968
|
}
|
|
@@ -4855,11 +4992,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
|
|
|
4855
4992
|
return true;
|
|
4856
4993
|
}
|
|
4857
4994
|
function runLocalPreviewScan(section2) {
|
|
4858
|
-
const cacheDir =
|
|
4995
|
+
const cacheDir = join33(homedir6(), ".cache", "claude-nomad");
|
|
4859
4996
|
mkdirSync8(cacheDir, { recursive: true });
|
|
4860
4997
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
4861
|
-
const reportPath =
|
|
4862
|
-
const tmpRoot =
|
|
4998
|
+
const reportPath = join33(cacheDir, `check-shared-${stamp}.json`);
|
|
4999
|
+
const tmpRoot = join33(cacheDir, `check-shared-tree-${stamp}`);
|
|
4863
5000
|
try {
|
|
4864
5001
|
const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
|
|
4865
5002
|
if (malformed) {
|
|
@@ -4892,8 +5029,8 @@ function reportCheckShared(section2, gitleaksReady) {
|
|
|
4892
5029
|
|
|
4893
5030
|
// src/commands.doctor.checks.hooks.scope.ts
|
|
4894
5031
|
init_color();
|
|
4895
|
-
import { existsSync as
|
|
4896
|
-
import { dirname as dirname8, extname, join as
|
|
5032
|
+
import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync13, realpathSync as realpathSync3 } from "node:fs";
|
|
5033
|
+
import { dirname as dirname8, extname, join as join34 } from "node:path";
|
|
4897
5034
|
init_config();
|
|
4898
5035
|
function typeFromPackageJson(pkgPath) {
|
|
4899
5036
|
try {
|
|
@@ -4915,8 +5052,8 @@ function effectiveType(hookPath) {
|
|
|
4915
5052
|
}
|
|
4916
5053
|
let dir = dirname8(real);
|
|
4917
5054
|
for (; ; ) {
|
|
4918
|
-
const pkg =
|
|
4919
|
-
if (
|
|
5055
|
+
const pkg = join34(dir, "package.json");
|
|
5056
|
+
if (existsSync23(pkg)) return typeFromPackageJson(pkg);
|
|
4920
5057
|
const parent = dirname8(dir);
|
|
4921
5058
|
if (parent === dir) return "cjs";
|
|
4922
5059
|
dir = parent;
|
|
@@ -4958,15 +5095,15 @@ function safeRead(path) {
|
|
|
4958
5095
|
}
|
|
4959
5096
|
}
|
|
4960
5097
|
function reportHookScopeCheck(section2) {
|
|
4961
|
-
const hooksDir =
|
|
4962
|
-
if (!
|
|
5098
|
+
const hooksDir = join34(claudeHome(), "hooks");
|
|
5099
|
+
if (!existsSync23(hooksDir)) {
|
|
4963
5100
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
|
|
4964
5101
|
return;
|
|
4965
5102
|
}
|
|
4966
5103
|
let anyWarn = false;
|
|
4967
5104
|
for (const name of safeReaddir2(hooksDir)) {
|
|
4968
5105
|
if (extname(name) !== ".js") continue;
|
|
4969
|
-
const abs =
|
|
5106
|
+
const abs = join34(hooksDir, name);
|
|
4970
5107
|
const eff = effectiveType(abs);
|
|
4971
5108
|
if (eff === null) continue;
|
|
4972
5109
|
const src = safeRead(abs);
|
|
@@ -4986,8 +5123,8 @@ function reportHookScopeCheck(section2) {
|
|
|
4986
5123
|
|
|
4987
5124
|
// src/commands.doctor.checks.hooks.ts
|
|
4988
5125
|
init_color();
|
|
4989
|
-
import { existsSync as
|
|
4990
|
-
import { join as
|
|
5126
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
5127
|
+
import { join as join35 } from "node:path";
|
|
4991
5128
|
init_config();
|
|
4992
5129
|
function expandHome(token) {
|
|
4993
5130
|
const h2 = home();
|
|
@@ -5030,7 +5167,7 @@ function checkEventGroups(section2, event, groups) {
|
|
|
5030
5167
|
for (const group of groups) {
|
|
5031
5168
|
for (const cmd of commandsFromGroup(group)) {
|
|
5032
5169
|
for (const resolved of claudePathsIn(cmd)) {
|
|
5033
|
-
if (
|
|
5170
|
+
if (existsSync24(resolved)) continue;
|
|
5034
5171
|
addItem(
|
|
5035
5172
|
section2,
|
|
5036
5173
|
`${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
|
|
@@ -5043,8 +5180,8 @@ function checkEventGroups(section2, event, groups) {
|
|
|
5043
5180
|
return anyFail;
|
|
5044
5181
|
}
|
|
5045
5182
|
function reportHooksTargetCheck(section2) {
|
|
5046
|
-
const settingsPath =
|
|
5047
|
-
if (!
|
|
5183
|
+
const settingsPath = join35(claudeHome(), "settings.json");
|
|
5184
|
+
if (!existsSync24(settingsPath)) {
|
|
5048
5185
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
|
|
5049
5186
|
return;
|
|
5050
5187
|
}
|
|
@@ -5067,12 +5204,12 @@ function reportHooksTargetCheck(section2) {
|
|
|
5067
5204
|
|
|
5068
5205
|
// src/commands.doctor.checks.hooks.preserve-symlinks.ts
|
|
5069
5206
|
init_color();
|
|
5070
|
-
import { existsSync as
|
|
5071
|
-
import { join as
|
|
5207
|
+
import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
|
|
5208
|
+
import { join as join37 } from "node:path";
|
|
5072
5209
|
|
|
5073
5210
|
// src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
|
|
5074
|
-
import { closeSync as closeSync3, existsSync as
|
|
5075
|
-
import { dirname as dirname9, join as
|
|
5211
|
+
import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync4 } from "node:fs";
|
|
5212
|
+
import { dirname as dirname9, join as join36, resolve as resolve3 } from "node:path";
|
|
5076
5213
|
function suppressedRanges(src) {
|
|
5077
5214
|
const ranges = [];
|
|
5078
5215
|
const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
|
|
@@ -5104,12 +5241,12 @@ function topRelativeSpecifiers(src) {
|
|
|
5104
5241
|
}
|
|
5105
5242
|
function specifierIsMissing(specifier, baseDir) {
|
|
5106
5243
|
const base = resolve3(baseDir, specifier);
|
|
5107
|
-
if (
|
|
5244
|
+
if (existsSync25(base)) return false;
|
|
5108
5245
|
for (const ext of [".js", ".cjs", ".mjs"]) {
|
|
5109
|
-
if (
|
|
5246
|
+
if (existsSync25(base + ext)) return false;
|
|
5110
5247
|
}
|
|
5111
5248
|
for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
|
|
5112
|
-
if (
|
|
5249
|
+
if (existsSync25(join36(base, idx))) return false;
|
|
5113
5250
|
}
|
|
5114
5251
|
return true;
|
|
5115
5252
|
}
|
|
@@ -5164,8 +5301,8 @@ function commandTokens(command) {
|
|
|
5164
5301
|
return tokens;
|
|
5165
5302
|
}
|
|
5166
5303
|
function readPathMapSafe() {
|
|
5167
|
-
const mapPath =
|
|
5168
|
-
if (!
|
|
5304
|
+
const mapPath = join37(repoHome(), "path-map.json");
|
|
5305
|
+
if (!existsSync26(mapPath)) return { projects: {} };
|
|
5169
5306
|
try {
|
|
5170
5307
|
return JSON.parse(readFileSync9(mapPath, "utf8"));
|
|
5171
5308
|
} catch {
|
|
@@ -5239,8 +5376,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
|
|
|
5239
5376
|
return anyWarn;
|
|
5240
5377
|
}
|
|
5241
5378
|
function reportPreserveSymlinksCheck(section2) {
|
|
5242
|
-
const settingsPath =
|
|
5243
|
-
if (!
|
|
5379
|
+
const settingsPath = join37(claudeHome(), "settings.json");
|
|
5380
|
+
if (!existsSync26(settingsPath)) {
|
|
5244
5381
|
addItem(
|
|
5245
5382
|
section2,
|
|
5246
5383
|
`${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
|
|
@@ -5273,8 +5410,8 @@ function reportPreserveSymlinksCheck(section2) {
|
|
|
5273
5410
|
|
|
5274
5411
|
// src/commands.doctor.checks.settings-drift.ts
|
|
5275
5412
|
init_color();
|
|
5276
|
-
import { existsSync as
|
|
5277
|
-
import { join as
|
|
5413
|
+
import { existsSync as existsSync27, readFileSync as readFileSync10 } from "node:fs";
|
|
5414
|
+
import { join as join38 } from "node:path";
|
|
5278
5415
|
init_config();
|
|
5279
5416
|
init_utils_json();
|
|
5280
5417
|
function diffMergedSettings(merged, settings) {
|
|
@@ -5292,7 +5429,7 @@ function tryReadJson(filePath) {
|
|
|
5292
5429
|
}
|
|
5293
5430
|
}
|
|
5294
5431
|
function reportHooksBaseSelfCleanNote(section2) {
|
|
5295
|
-
const basePath =
|
|
5432
|
+
const basePath = join38(repoHome(), "shared", "settings.base.json");
|
|
5296
5433
|
const base = tryReadJson(basePath);
|
|
5297
5434
|
if (base === null) return;
|
|
5298
5435
|
if (!baseHasGsdHookEntries(base)) return;
|
|
@@ -5305,14 +5442,14 @@ function reportSettingsDriftCheck(section2) {
|
|
|
5305
5442
|
const claude = claudeHome();
|
|
5306
5443
|
const repo = repoHome();
|
|
5307
5444
|
const host = HOST;
|
|
5308
|
-
const settingsPath =
|
|
5309
|
-
const basePath =
|
|
5310
|
-
const hostPath =
|
|
5311
|
-
if (!
|
|
5445
|
+
const settingsPath = join38(claude, "settings.json");
|
|
5446
|
+
const basePath = join38(repo, "shared", "settings.base.json");
|
|
5447
|
+
const hostPath = join38(repo, "hosts", `${host}.json`);
|
|
5448
|
+
if (!existsSync27(settingsPath)) {
|
|
5312
5449
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
|
|
5313
5450
|
return;
|
|
5314
5451
|
}
|
|
5315
|
-
if (!
|
|
5452
|
+
if (!existsSync27(basePath)) {
|
|
5316
5453
|
addItem(
|
|
5317
5454
|
section2,
|
|
5318
5455
|
`${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
|
|
@@ -5331,7 +5468,7 @@ function reportSettingsDriftCheck(section2) {
|
|
|
5331
5468
|
if (settings === null) {
|
|
5332
5469
|
return;
|
|
5333
5470
|
}
|
|
5334
|
-
const hostExists =
|
|
5471
|
+
const hostExists = existsSync27(hostPath);
|
|
5335
5472
|
const hostObj = hostExists ? tryReadJson(hostPath) : null;
|
|
5336
5473
|
if (hostExists && hostObj === null) {
|
|
5337
5474
|
addItem(
|
|
@@ -5548,15 +5685,15 @@ function reportSyncModality(section2) {
|
|
|
5548
5685
|
// src/commands.doctor.checks.crlf.ts
|
|
5549
5686
|
init_color();
|
|
5550
5687
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
5551
|
-
import { existsSync as
|
|
5552
|
-
import { join as
|
|
5688
|
+
import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
|
|
5689
|
+
import { join as join39 } from "node:path";
|
|
5553
5690
|
init_config();
|
|
5554
5691
|
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
5555
5692
|
var GUARD_LINE = /^\*\s+-text\b/;
|
|
5556
5693
|
function hasGitattributesGuard(repo) {
|
|
5557
5694
|
try {
|
|
5558
|
-
const path =
|
|
5559
|
-
if (!
|
|
5695
|
+
const path = join39(repo, ".gitattributes");
|
|
5696
|
+
if (!existsSync28(path)) return false;
|
|
5560
5697
|
const content = readFileSync13(path, "utf8");
|
|
5561
5698
|
return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
|
|
5562
5699
|
} catch {
|
|
@@ -5597,14 +5734,14 @@ function reportCrlfGuardCheck(section2, run = execFileSync13) {
|
|
|
5597
5734
|
|
|
5598
5735
|
// src/spinner.ts
|
|
5599
5736
|
init_color();
|
|
5600
|
-
import { existsSync as
|
|
5737
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
5601
5738
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
5602
5739
|
import { Worker } from "node:worker_threads";
|
|
5603
5740
|
|
|
5604
5741
|
// src/commands.push.recovery.ts
|
|
5605
5742
|
init_config();
|
|
5606
5743
|
import { readFileSync as readFileSync18, rmSync as rmSync15, writeFileSync as writeFileSync9 } from "node:fs";
|
|
5607
|
-
import { join as
|
|
5744
|
+
import { join as join47 } from "node:path";
|
|
5608
5745
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5609
5746
|
|
|
5610
5747
|
// src/commands.push.recovery.actions.ts
|
|
@@ -5621,10 +5758,10 @@ import {
|
|
|
5621
5758
|
mkdirSync as mkdirSync9,
|
|
5622
5759
|
readFileSync as readFileSync14,
|
|
5623
5760
|
realpathSync as realpathSync5,
|
|
5624
|
-
statSync as
|
|
5761
|
+
statSync as statSync7,
|
|
5625
5762
|
writeFileSync as writeFileSync6
|
|
5626
5763
|
} from "node:fs";
|
|
5627
|
-
import { dirname as dirname10, join as
|
|
5764
|
+
import { dirname as dirname10, join as join40, sep as sep6 } from "node:path";
|
|
5628
5765
|
init_push_gitleaks_scan();
|
|
5629
5766
|
|
|
5630
5767
|
// src/rel-path-guard.ts
|
|
@@ -5653,15 +5790,15 @@ function resolveMemoryLocalPath(logical, relPath, map) {
|
|
|
5653
5790
|
if (!isSafeRelPath(relPath)) return null;
|
|
5654
5791
|
const abs = map.projects[logical]?.[HOST];
|
|
5655
5792
|
if (abs === void 0) return null;
|
|
5656
|
-
const memoryRoot =
|
|
5657
|
-
const localPath =
|
|
5793
|
+
const memoryRoot = join40(claudeHome(), "projects", encodePath(abs), "memory");
|
|
5794
|
+
const localPath = join40(memoryRoot, ...relPath.split("/"));
|
|
5658
5795
|
if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep6)) return null;
|
|
5659
5796
|
try {
|
|
5660
5797
|
if (lstatSync14(localPath).isSymbolicLink()) return null;
|
|
5661
5798
|
const realLocal = realpathSync5(localPath);
|
|
5662
5799
|
const realRoot = realpathSync5(memoryRoot);
|
|
5663
5800
|
if (!realLocal.startsWith(realRoot + sep6)) return null;
|
|
5664
|
-
if (!
|
|
5801
|
+
if (!statSync7(realLocal).isFile()) return null;
|
|
5665
5802
|
} catch {
|
|
5666
5803
|
return null;
|
|
5667
5804
|
}
|
|
@@ -5708,7 +5845,7 @@ function applyMemoryRedact(f, ts, map, scan = scanFile) {
|
|
|
5708
5845
|
);
|
|
5709
5846
|
}
|
|
5710
5847
|
writeFileSync6(localPath, after, "utf8");
|
|
5711
|
-
const dest =
|
|
5848
|
+
const dest = join40(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
|
|
5712
5849
|
mkdirSync9(dirname10(dest), { recursive: true });
|
|
5713
5850
|
cpSync6(localPath, dest, { force: true });
|
|
5714
5851
|
return true;
|
|
@@ -5722,15 +5859,15 @@ import {
|
|
|
5722
5859
|
mkdirSync as mkdirSync11,
|
|
5723
5860
|
readFileSync as readFileSync15,
|
|
5724
5861
|
realpathSync as realpathSync6,
|
|
5725
|
-
statSync as
|
|
5862
|
+
statSync as statSync8,
|
|
5726
5863
|
writeFileSync as writeFileSync7
|
|
5727
5864
|
} from "node:fs";
|
|
5728
|
-
import { dirname as dirname11, join as
|
|
5865
|
+
import { dirname as dirname11, join as join42, sep as sep7 } from "node:path";
|
|
5729
5866
|
|
|
5730
5867
|
// src/skills-sync.ts
|
|
5731
5868
|
init_config();
|
|
5732
|
-
import { existsSync as
|
|
5733
|
-
import { join as
|
|
5869
|
+
import { existsSync as existsSync29, lstatSync as lstatSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync14, rmSync as rmSync13 } from "node:fs";
|
|
5870
|
+
import { join as join41 } from "node:path";
|
|
5734
5871
|
|
|
5735
5872
|
// src/skills-sync.tracked.ts
|
|
5736
5873
|
init_utils();
|
|
@@ -5765,9 +5902,9 @@ function copySkillsPull(src, dst, isRootPreserved) {
|
|
|
5765
5902
|
copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved);
|
|
5766
5903
|
}
|
|
5767
5904
|
function syncSkillsPull(ts, prePostHeads) {
|
|
5768
|
-
const sharedSkills =
|
|
5769
|
-
if (!
|
|
5770
|
-
const localSkills =
|
|
5905
|
+
const sharedSkills = join41(repoHome(), "shared", "skills");
|
|
5906
|
+
if (!existsSync29(sharedSkills)) return;
|
|
5907
|
+
const localSkills = join41(claudeHome(), "skills");
|
|
5771
5908
|
backupBeforeWrite(localSkills, ts);
|
|
5772
5909
|
const dstStat = lstatSync15(localSkills, { throwIfNoEntry: false });
|
|
5773
5910
|
if (dstStat?.isSymbolicLink() === true) {
|
|
@@ -5778,11 +5915,11 @@ function syncSkillsPull(ts, prePostHeads) {
|
|
|
5778
5915
|
copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
|
|
5779
5916
|
}
|
|
5780
5917
|
function syncSkillsPush() {
|
|
5781
|
-
const localSkills =
|
|
5918
|
+
const localSkills = join41(claudeHome(), "skills");
|
|
5782
5919
|
const stat = lstatSync15(localSkills, { throwIfNoEntry: false });
|
|
5783
5920
|
if (stat === void 0) return;
|
|
5784
5921
|
if (stat.isSymbolicLink()) return;
|
|
5785
|
-
const sharedSkills =
|
|
5922
|
+
const sharedSkills = join41(repoHome(), "shared", "skills");
|
|
5786
5923
|
copySkillsPush(localSkills, sharedSkills);
|
|
5787
5924
|
}
|
|
5788
5925
|
|
|
@@ -5805,16 +5942,16 @@ function resolveSkillLocalPath(name, relPath) {
|
|
|
5805
5942
|
if (!SAFE_SKILL_NAME.test(name) || name === "." || name === "..") return null;
|
|
5806
5943
|
if (isGsdOwned(name)) return null;
|
|
5807
5944
|
if (!isSafeRelPath(relPath)) return null;
|
|
5808
|
-
const skillsRoot =
|
|
5809
|
-
const skillRoot =
|
|
5810
|
-
const localPath =
|
|
5945
|
+
const skillsRoot = join42(claudeHome(), "skills");
|
|
5946
|
+
const skillRoot = join42(skillsRoot, name);
|
|
5947
|
+
const localPath = join42(skillRoot, ...relPath.split("/"));
|
|
5811
5948
|
if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep7)) return null;
|
|
5812
5949
|
try {
|
|
5813
5950
|
if (lstatSync16(localPath).isSymbolicLink()) return null;
|
|
5814
5951
|
const realLocal = realpathSync6(localPath);
|
|
5815
5952
|
const realRoot = realpathSync6(skillsRoot);
|
|
5816
5953
|
if (!realLocal.startsWith(realRoot + sep7)) return null;
|
|
5817
|
-
if (!
|
|
5954
|
+
if (!statSync8(realLocal).isFile()) return null;
|
|
5818
5955
|
} catch {
|
|
5819
5956
|
return null;
|
|
5820
5957
|
}
|
|
@@ -5859,7 +5996,7 @@ function applySkillRedact(f, ts, scan = scanFile) {
|
|
|
5859
5996
|
);
|
|
5860
5997
|
}
|
|
5861
5998
|
writeFileSync7(localPath, after, "utf8");
|
|
5862
|
-
const dest =
|
|
5999
|
+
const dest = join42(repoHome(), "shared", "skills", name, ...relPath.split("/"));
|
|
5863
6000
|
mkdirSync11(dirname11(dest), { recursive: true });
|
|
5864
6001
|
cpSync7(localPath, dest, { force: true });
|
|
5865
6002
|
return true;
|
|
@@ -5937,25 +6074,25 @@ function applyDeferredAllows(findings, state, repo) {
|
|
|
5937
6074
|
// src/commands.push.recovery.redact.ts
|
|
5938
6075
|
init_config();
|
|
5939
6076
|
init_config_sharedDirs_guard();
|
|
5940
|
-
import { cpSync as cpSync8, existsSync as
|
|
5941
|
-
import { dirname as dirname13, join as
|
|
6077
|
+
import { cpSync as cpSync8, existsSync as existsSync32, mkdirSync as mkdirSync12 } from "node:fs";
|
|
6078
|
+
import { dirname as dirname13, join as join45, sep as sep8 } from "node:path";
|
|
5942
6079
|
|
|
5943
6080
|
// src/commands.redact.ts
|
|
5944
6081
|
init_config();
|
|
5945
|
-
import { existsSync as
|
|
5946
|
-
import { dirname as dirname12, join as
|
|
6082
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
6083
|
+
import { dirname as dirname12, join as join44 } from "node:path";
|
|
5947
6084
|
|
|
5948
6085
|
// src/commands.redact.subtree.ts
|
|
5949
|
-
import { existsSync as
|
|
5950
|
-
import { join as
|
|
6086
|
+
import { existsSync as existsSync30, lstatSync as lstatSync17, readFileSync as readFileSync16, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
6087
|
+
import { join as join43 } from "node:path";
|
|
5951
6088
|
init_utils_fs();
|
|
5952
6089
|
init_utils();
|
|
5953
6090
|
function collectFiles(dir, out) {
|
|
5954
|
-
if (!
|
|
6091
|
+
if (!existsSync30(dir)) return;
|
|
5955
6092
|
const st = lstatSync17(dir);
|
|
5956
6093
|
if (!st.isDirectory()) return;
|
|
5957
6094
|
for (const entry of readdirSync15(dir)) {
|
|
5958
|
-
const abs =
|
|
6095
|
+
const abs = join43(dir, entry);
|
|
5959
6096
|
const lst = lstatSync17(abs);
|
|
5960
6097
|
if (lst.isSymbolicLink()) continue;
|
|
5961
6098
|
if (lst.isDirectory()) {
|
|
@@ -5970,7 +6107,7 @@ function listSubtreeFiles(sessionDir) {
|
|
|
5970
6107
|
collectFiles(sessionDir, out);
|
|
5971
6108
|
return out.sort((a, b) => a.localeCompare(b));
|
|
5972
6109
|
}
|
|
5973
|
-
function newestSubtreeMtimeMs(mainPath, subtreeFiles, statMtime = (p) =>
|
|
6110
|
+
function newestSubtreeMtimeMs(mainPath, subtreeFiles, statMtime = (p) => statSync9(p).mtimeMs) {
|
|
5974
6111
|
let newest = statMtime(mainPath);
|
|
5975
6112
|
for (const filePath of subtreeFiles) {
|
|
5976
6113
|
const t = statMtime(filePath);
|
|
@@ -5979,7 +6116,7 @@ function newestSubtreeMtimeMs(mainPath, subtreeFiles, statMtime = (p) => statSyn
|
|
|
5979
6116
|
return newest;
|
|
5980
6117
|
}
|
|
5981
6118
|
function isSubtreeActive(mainPath, subtreeFiles, now) {
|
|
5982
|
-
const subtreeMtime = newestSubtreeMtimeMs(mainPath, subtreeFiles, (p) =>
|
|
6119
|
+
const subtreeMtime = newestSubtreeMtimeMs(mainPath, subtreeFiles, (p) => statSync9(p).mtimeMs);
|
|
5983
6120
|
return isRecentlyModified(subtreeMtime, now);
|
|
5984
6121
|
}
|
|
5985
6122
|
function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts, scan, dryRun) {
|
|
@@ -6065,15 +6202,15 @@ init_utils_json();
|
|
|
6065
6202
|
init_utils();
|
|
6066
6203
|
function resolveLiveTranscript(id) {
|
|
6067
6204
|
try {
|
|
6068
|
-
const mapPath =
|
|
6069
|
-
if (!
|
|
6205
|
+
const mapPath = join44(repoHome(), "path-map.json");
|
|
6206
|
+
if (!existsSync31(mapPath)) return null;
|
|
6070
6207
|
const projects = readJson(mapPath).projects;
|
|
6071
6208
|
const claude = claudeHome();
|
|
6072
6209
|
for (const hostMap of Object.values(projects)) {
|
|
6073
6210
|
const abs = hostMap[HOST];
|
|
6074
6211
|
if (abs === void 0) continue;
|
|
6075
|
-
const live =
|
|
6076
|
-
if (
|
|
6212
|
+
const live = join44(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
6213
|
+
if (existsSync31(live)) return live;
|
|
6077
6214
|
}
|
|
6078
6215
|
return null;
|
|
6079
6216
|
} catch {
|
|
@@ -6093,17 +6230,17 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
6093
6230
|
}
|
|
6094
6231
|
const repo = repoHome();
|
|
6095
6232
|
const backup = backupBase();
|
|
6096
|
-
if (!
|
|
6233
|
+
if (!existsSync31(repo)) die(`repo not cloned at ${repo}`);
|
|
6097
6234
|
const handle = acquireLock("redact");
|
|
6098
6235
|
if (handle === null) process.exit(0);
|
|
6099
6236
|
try {
|
|
6100
6237
|
const localPath = resolveLiveTranscript(id);
|
|
6101
|
-
if (localPath === null || !
|
|
6238
|
+
if (localPath === null || !existsSync31(localPath)) {
|
|
6102
6239
|
fail(`could not resolve local transcript for session ${id} on this host`);
|
|
6103
6240
|
process.exitCode = 1;
|
|
6104
6241
|
return;
|
|
6105
6242
|
}
|
|
6106
|
-
const sessionDir =
|
|
6243
|
+
const sessionDir = join44(dirname12(localPath), id);
|
|
6107
6244
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6108
6245
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6109
6246
|
log(
|
|
@@ -6164,8 +6301,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
|
|
|
6164
6301
|
assertSafeLogical(logical);
|
|
6165
6302
|
const abs = hostMap[HOST];
|
|
6166
6303
|
if (abs === void 0) continue;
|
|
6167
|
-
if (localPath.startsWith(
|
|
6168
|
-
return
|
|
6304
|
+
if (localPath.startsWith(join45(claude, "projects", encodePath(abs)) + sep8)) {
|
|
6305
|
+
return join45(repo, "shared", "projects", logical);
|
|
6169
6306
|
}
|
|
6170
6307
|
}
|
|
6171
6308
|
return null;
|
|
@@ -6175,7 +6312,7 @@ function preflightRedactable(f, map, nowMs) {
|
|
|
6175
6312
|
if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
|
|
6176
6313
|
const localPath = resolveLiveTranscript(sid);
|
|
6177
6314
|
if (localPath === null) return `session ${sid}: local transcript not found`;
|
|
6178
|
-
const sessionDir =
|
|
6315
|
+
const sessionDir = join45(dirname13(localPath), sid);
|
|
6179
6316
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6180
6317
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6181
6318
|
return `session ${sid}: looks active (modified within the last 5 minutes)`;
|
|
@@ -6204,7 +6341,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
6204
6341
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
6205
6342
|
);
|
|
6206
6343
|
}
|
|
6207
|
-
const sessionDir =
|
|
6344
|
+
const sessionDir = join45(dirname13(localPath), sid);
|
|
6208
6345
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6209
6346
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6210
6347
|
return refuse(
|
|
@@ -6237,9 +6374,9 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
6237
6374
|
);
|
|
6238
6375
|
}
|
|
6239
6376
|
mkdirSync12(stagedProjectDir, { recursive: true });
|
|
6240
|
-
cpSync8(localPath,
|
|
6241
|
-
if (
|
|
6242
|
-
cpSync8(sessionDir,
|
|
6377
|
+
cpSync8(localPath, join45(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
6378
|
+
if (existsSync32(sessionDir)) {
|
|
6379
|
+
cpSync8(sessionDir, join45(stagedProjectDir, sid), { force: true, recursive: true });
|
|
6243
6380
|
}
|
|
6244
6381
|
return true;
|
|
6245
6382
|
}
|
|
@@ -6247,14 +6384,14 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
6247
6384
|
// src/commands.push.recovery.drop.ts
|
|
6248
6385
|
init_config();
|
|
6249
6386
|
import { rmSync as rmSync14 } from "node:fs";
|
|
6250
|
-
import { join as
|
|
6387
|
+
import { join as join46 } from "node:path";
|
|
6251
6388
|
function dropSessionFromStaged(sid, map) {
|
|
6252
6389
|
const logicals = Object.keys(map.projects);
|
|
6253
6390
|
if (logicals.length === 0) return false;
|
|
6254
6391
|
const repo = repoHome();
|
|
6255
6392
|
for (const logical of logicals) {
|
|
6256
|
-
const jsonl =
|
|
6257
|
-
const dir =
|
|
6393
|
+
const jsonl = join46(repo, "shared", "projects", logical, `${sid}.jsonl`);
|
|
6394
|
+
const dir = join46(repo, "shared", "projects", logical, sid);
|
|
6258
6395
|
rmSync14(jsonl, { force: true });
|
|
6259
6396
|
rmSync14(dir, { recursive: true, force: true });
|
|
6260
6397
|
}
|
|
@@ -6739,7 +6876,7 @@ function applyThenRescan(scanVerdict, repoHome2) {
|
|
|
6739
6876
|
return next;
|
|
6740
6877
|
}
|
|
6741
6878
|
function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
6742
|
-
const ignPath =
|
|
6879
|
+
const ignPath = join47(repoHome2, ".gitleaksignore");
|
|
6743
6880
|
let before;
|
|
6744
6881
|
try {
|
|
6745
6882
|
before = readFileSync18(ignPath, "utf8");
|
|
@@ -6845,7 +6982,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
6845
6982
|
`);
|
|
6846
6983
|
}
|
|
6847
6984
|
function resolveWorkerPath(deps = {}) {
|
|
6848
|
-
const check = deps.existsSyncFn ??
|
|
6985
|
+
const check = deps.existsSyncFn ?? existsSync33;
|
|
6849
6986
|
const base = deps.baseUrl ?? import.meta.url;
|
|
6850
6987
|
const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
|
|
6851
6988
|
if (check(mjs)) return mjs;
|
|
@@ -6912,8 +7049,8 @@ function withSpinner(label, fn, deps) {
|
|
|
6912
7049
|
// src/commands.doctor.gitleaks-version.ts
|
|
6913
7050
|
init_color();
|
|
6914
7051
|
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
6915
|
-
import { existsSync as
|
|
6916
|
-
import { join as
|
|
7052
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
7053
|
+
import { join as join48 } from "node:path";
|
|
6917
7054
|
init_config();
|
|
6918
7055
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
6919
7056
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -6922,7 +7059,7 @@ function majorMinorOf(value) {
|
|
|
6922
7059
|
return m === null ? null : [m[1], m[2]];
|
|
6923
7060
|
}
|
|
6924
7061
|
function readGitleaksVersion(run, tomlExists) {
|
|
6925
|
-
const tomlPath =
|
|
7062
|
+
const tomlPath = join48(repoHome(), ".gitleaks.toml");
|
|
6926
7063
|
const args = ["version"];
|
|
6927
7064
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
6928
7065
|
try {
|
|
@@ -6934,7 +7071,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
6934
7071
|
return null;
|
|
6935
7072
|
}
|
|
6936
7073
|
}
|
|
6937
|
-
function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists =
|
|
7074
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists = existsSync34) {
|
|
6938
7075
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
6939
7076
|
if (raw === null) return;
|
|
6940
7077
|
const local = majorMinorOf(raw);
|
|
@@ -7191,8 +7328,8 @@ function gatherDoctorSections(opts) {
|
|
|
7191
7328
|
reportLongPathsCheck(host);
|
|
7192
7329
|
reportCrlfGuardCheck(host);
|
|
7193
7330
|
const links = section("Shared links");
|
|
7194
|
-
const mapPath =
|
|
7195
|
-
const rawMap =
|
|
7331
|
+
const mapPath = join49(repoHome(), "path-map.json");
|
|
7332
|
+
const rawMap = existsSync35(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
7196
7333
|
const map = rawMap ?? { projects: {} };
|
|
7197
7334
|
reportSharedLinks(links, map);
|
|
7198
7335
|
reportDroppedNamesMigration(links);
|
|
@@ -7290,8 +7427,8 @@ function parseDoctorArgs(args) {
|
|
|
7290
7427
|
// src/commands.drop-session.ts
|
|
7291
7428
|
init_config();
|
|
7292
7429
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
7293
|
-
import { existsSync as
|
|
7294
|
-
import { join as
|
|
7430
|
+
import { existsSync as existsSync37, readdirSync as readdirSync16, statSync as statSync10 } from "node:fs";
|
|
7431
|
+
import { join as join51, relative as relative7 } from "node:path";
|
|
7295
7432
|
|
|
7296
7433
|
// src/commands.drop-session.git.ts
|
|
7297
7434
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
@@ -7334,8 +7471,8 @@ function isInIndex(rel, repo) {
|
|
|
7334
7471
|
init_config();
|
|
7335
7472
|
init_utils();
|
|
7336
7473
|
init_utils_json();
|
|
7337
|
-
import { existsSync as
|
|
7338
|
-
import { join as
|
|
7474
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
7475
|
+
import { join as join50 } from "node:path";
|
|
7339
7476
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
7340
7477
|
function reportScrubHint(id, matches) {
|
|
7341
7478
|
const live = resolveLiveTranscript2(id, matches);
|
|
@@ -7351,8 +7488,8 @@ function reportScrubHint(id, matches) {
|
|
|
7351
7488
|
}
|
|
7352
7489
|
function resolveLiveTranscript2(id, matches) {
|
|
7353
7490
|
try {
|
|
7354
|
-
const mapPath =
|
|
7355
|
-
if (!
|
|
7491
|
+
const mapPath = join50(repoHome(), "path-map.json");
|
|
7492
|
+
if (!existsSync36(mapPath)) return null;
|
|
7356
7493
|
const projects = readJson(mapPath).projects;
|
|
7357
7494
|
const claude = claudeHome();
|
|
7358
7495
|
for (const rel of matches) {
|
|
@@ -7360,8 +7497,8 @@ function resolveLiveTranscript2(id, matches) {
|
|
|
7360
7497
|
if (logical === void 0) continue;
|
|
7361
7498
|
const abs = projects[logical]?.[HOST];
|
|
7362
7499
|
if (abs === void 0) continue;
|
|
7363
|
-
const live =
|
|
7364
|
-
if (
|
|
7500
|
+
const live = join50(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
7501
|
+
if (existsSync36(live)) return live;
|
|
7365
7502
|
}
|
|
7366
7503
|
return null;
|
|
7367
7504
|
} catch {
|
|
@@ -7377,12 +7514,12 @@ function cmdDropSession(id) {
|
|
|
7377
7514
|
process.exit(1);
|
|
7378
7515
|
}
|
|
7379
7516
|
const repo = repoHome();
|
|
7380
|
-
if (!
|
|
7517
|
+
if (!existsSync37(repo)) die(`repo not cloned at ${repo}`);
|
|
7381
7518
|
const handle = acquireLock("drop-session");
|
|
7382
7519
|
if (handle === null) process.exit(0);
|
|
7383
7520
|
try {
|
|
7384
|
-
const repoProjects =
|
|
7385
|
-
if (!
|
|
7521
|
+
const repoProjects = join51(repo, "shared", "projects");
|
|
7522
|
+
if (!existsSync37(repoProjects)) {
|
|
7386
7523
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
7387
7524
|
}
|
|
7388
7525
|
const matches = collectMatches(repoProjects, id, repo);
|
|
@@ -7405,12 +7542,12 @@ function cmdDropSession(id) {
|
|
|
7405
7542
|
function collectMatches(repoProjects, id, repo) {
|
|
7406
7543
|
const matches = [];
|
|
7407
7544
|
for (const logical of readdirSync16(repoProjects)) {
|
|
7408
|
-
const candidate =
|
|
7409
|
-
if (
|
|
7545
|
+
const candidate = join51(repoProjects, logical, `${id}.jsonl`);
|
|
7546
|
+
if (existsSync37(candidate)) {
|
|
7410
7547
|
matches.push(relative7(repo, candidate));
|
|
7411
7548
|
}
|
|
7412
|
-
const dir =
|
|
7413
|
-
if (
|
|
7549
|
+
const dir = join51(repoProjects, logical, id);
|
|
7550
|
+
if (existsSync37(dir) && statSync10(dir).isDirectory()) {
|
|
7414
7551
|
const dirRel = relative7(repo, dir);
|
|
7415
7552
|
const staged = expandStagedDir(dirRel, repo);
|
|
7416
7553
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -7446,8 +7583,8 @@ function unstageOne(rel, repo) {
|
|
|
7446
7583
|
|
|
7447
7584
|
// src/commands.pull.ts
|
|
7448
7585
|
init_autostash_guard();
|
|
7449
|
-
import { existsSync as
|
|
7450
|
-
import { join as
|
|
7586
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync14 } from "node:fs";
|
|
7587
|
+
import { join as join62 } from "node:path";
|
|
7451
7588
|
|
|
7452
7589
|
// src/commands.push.sections.ts
|
|
7453
7590
|
init_color();
|
|
@@ -7555,18 +7692,18 @@ init_config();
|
|
|
7555
7692
|
|
|
7556
7693
|
// src/extras-sync.ts
|
|
7557
7694
|
init_config();
|
|
7558
|
-
import { existsSync as
|
|
7559
|
-
import { join as
|
|
7695
|
+
import { existsSync as existsSync39, statSync as statSync11 } from "node:fs";
|
|
7696
|
+
import { join as join54 } from "node:path";
|
|
7560
7697
|
init_utils();
|
|
7561
7698
|
init_utils_json();
|
|
7562
7699
|
|
|
7563
7700
|
// src/extras-sync.remap.ts
|
|
7564
7701
|
init_config();
|
|
7565
|
-
import { existsSync as
|
|
7566
|
-
import { dirname as dirname14, join as
|
|
7702
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync13, readdirSync as readdirSync17, readFileSync as readFileSync19, realpathSync as realpathSync7, rmSync as rmSync16 } from "node:fs";
|
|
7703
|
+
import { dirname as dirname14, join as join53, sep as sep11 } from "node:path";
|
|
7567
7704
|
|
|
7568
7705
|
// src/extras-sync.planning-diff.ts
|
|
7569
|
-
import { join as
|
|
7706
|
+
import { join as join52, normalize as normalize2, sep as sep10 } from "node:path";
|
|
7570
7707
|
init_utils();
|
|
7571
7708
|
function processRecord(fields, i, changed, deleted) {
|
|
7572
7709
|
const status = fields[i];
|
|
@@ -7618,7 +7755,7 @@ function planningDeleteTargets(opts) {
|
|
|
7618
7755
|
const { deleted } = parsePlanningDiff(raw);
|
|
7619
7756
|
const logicalPrefix = "shared/extras/" + logical + "/";
|
|
7620
7757
|
const prefix = logicalPrefix + ".planning/";
|
|
7621
|
-
const planningRoot =
|
|
7758
|
+
const planningRoot = join52(localRoot, ".planning");
|
|
7622
7759
|
const planningRootBoundary = planningRoot + sep10;
|
|
7623
7760
|
const targets = [];
|
|
7624
7761
|
for (const repoPath of deleted) {
|
|
@@ -7626,7 +7763,7 @@ function planningDeleteTargets(opts) {
|
|
|
7626
7763
|
continue;
|
|
7627
7764
|
}
|
|
7628
7765
|
const remainder = repoPath.slice(logicalPrefix.length);
|
|
7629
|
-
const candidate =
|
|
7766
|
+
const candidate = join52(localRoot, remainder);
|
|
7630
7767
|
const resolved = normalize2(candidate);
|
|
7631
7768
|
if (!resolved.startsWith(planningRootBoundary)) {
|
|
7632
7769
|
throw new NomadFatal(
|
|
@@ -7647,7 +7784,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
7647
7784
|
const would = [];
|
|
7648
7785
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
7649
7786
|
const { src, dst } = paths(t);
|
|
7650
|
-
if (!
|
|
7787
|
+
if (!existsSync38(src)) continue;
|
|
7651
7788
|
const item2 = `${t.logical}/${t.dirname}`;
|
|
7652
7789
|
if (dryRun) {
|
|
7653
7790
|
would.push(item2);
|
|
@@ -7682,7 +7819,7 @@ function isInsidePlanningRoot(parentReal, rootReal) {
|
|
|
7682
7819
|
return parentReal === rootReal || parentReal.startsWith(rootReal + sep11);
|
|
7683
7820
|
}
|
|
7684
7821
|
function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
7685
|
-
if (
|
|
7822
|
+
if (existsSync38(repoCounterpart)) return;
|
|
7686
7823
|
const parentReal = tryRealpath(dirname14(target));
|
|
7687
7824
|
if (parentReal === void 0) return;
|
|
7688
7825
|
const rootReal = tryRealpath(planningRoot);
|
|
@@ -7692,7 +7829,7 @@ function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
|
7692
7829
|
pruneEmptyAncestors(target, planningRoot);
|
|
7693
7830
|
}
|
|
7694
7831
|
function localDivergesFromPreDelete(target, pre, repoRel, repo) {
|
|
7695
|
-
if (!
|
|
7832
|
+
if (!existsSync38(target)) return false;
|
|
7696
7833
|
try {
|
|
7697
7834
|
const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
|
|
7698
7835
|
return !readFileSync19(target).equals(preBlob);
|
|
@@ -7737,7 +7874,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
|
|
|
7737
7874
|
return kept;
|
|
7738
7875
|
}
|
|
7739
7876
|
function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
7740
|
-
const repoExtras =
|
|
7877
|
+
const repoExtras = join53(repo, "shared", "extras");
|
|
7741
7878
|
for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
|
|
7742
7879
|
if (t.dirname !== ".planning") continue;
|
|
7743
7880
|
let raw;
|
|
@@ -7752,14 +7889,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
|
7752
7889
|
}
|
|
7753
7890
|
const pairs = deletePairsFor(t, raw);
|
|
7754
7891
|
if (pairs.length === 0) continue;
|
|
7755
|
-
backupExtrasWrite(
|
|
7756
|
-
const planningRoot =
|
|
7892
|
+
backupExtrasWrite(join53(t.localRoot, t.dirname), ts, t.localRoot);
|
|
7893
|
+
const planningRoot = join53(t.localRoot, ".planning");
|
|
7757
7894
|
for (const { target, relToLocal, repoRel } of pairs) {
|
|
7758
7895
|
if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
|
|
7759
7896
|
warn(keptDeleteWarnLine(t.logical, relToLocal));
|
|
7760
7897
|
continue;
|
|
7761
7898
|
}
|
|
7762
|
-
deletePlanningTarget(target, planningRoot,
|
|
7899
|
+
deletePlanningTarget(target, planningRoot, join53(repoExtras, t.logical, relToLocal));
|
|
7763
7900
|
}
|
|
7764
7901
|
}
|
|
7765
7902
|
}
|
|
@@ -7768,14 +7905,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
7768
7905
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
7769
7906
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
7770
7907
|
const repo = repoHome();
|
|
7771
|
-
const repoExtras =
|
|
7908
|
+
const repoExtras = join53(repo, "shared", "extras");
|
|
7772
7909
|
if (!dryRun) mkdirSync13(repoExtras, { recursive: true });
|
|
7773
7910
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
7774
7911
|
v,
|
|
7775
7912
|
dryRun,
|
|
7776
7913
|
({ localRoot, logical, dirname: dirname15 }) => ({
|
|
7777
|
-
src:
|
|
7778
|
-
dst:
|
|
7914
|
+
src: join53(localRoot, dirname15),
|
|
7915
|
+
dst: join53(repoExtras, logical, dirname15)
|
|
7779
7916
|
}),
|
|
7780
7917
|
(dst) => backupRepoWrite(dst, ts, repo),
|
|
7781
7918
|
// Push copy routing per extra type:
|
|
@@ -7805,8 +7942,8 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
7805
7942
|
v,
|
|
7806
7943
|
dryRun,
|
|
7807
7944
|
({ localRoot, logical, dirname: dirname15 }) => ({
|
|
7808
|
-
src:
|
|
7809
|
-
dst:
|
|
7945
|
+
src: join53(repo, "shared", "extras", logical, dirname15),
|
|
7946
|
+
dst: join53(localRoot, dirname15)
|
|
7810
7947
|
}),
|
|
7811
7948
|
// Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
|
|
7812
7949
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -7853,22 +7990,22 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
7853
7990
|
const v = loadValidatedExtras({});
|
|
7854
7991
|
if (v === null) return 0;
|
|
7855
7992
|
const counts = { unmapped: 0, skipped: 0 };
|
|
7856
|
-
const backupRoot =
|
|
7993
|
+
const backupRoot = join54(backupBase(), ts, "extras");
|
|
7857
7994
|
const repo = repoHome();
|
|
7858
7995
|
let divergedCount = 0;
|
|
7859
7996
|
for (const { logical, localRoot, dirname: dirname15 } of eachExtrasTarget(v, counts)) {
|
|
7860
|
-
const local =
|
|
7861
|
-
const repoEntry =
|
|
7862
|
-
if (!
|
|
7997
|
+
const local = join54(localRoot, dirname15);
|
|
7998
|
+
const repoEntry = join54(repo, "shared", "extras", logical, dirname15);
|
|
7999
|
+
if (!existsSync39(local) || !existsSync39(repoEntry)) continue;
|
|
7863
8000
|
const modified = listDivergingModified(local, repoEntry);
|
|
7864
8001
|
if (modified.length === 0) continue;
|
|
7865
8002
|
divergedCount += modified.length;
|
|
7866
|
-
const projectBackupRoot =
|
|
8003
|
+
const projectBackupRoot = join54(backupRoot, encodePath(localRoot));
|
|
7867
8004
|
warn(
|
|
7868
8005
|
divergenceWarnLine({
|
|
7869
8006
|
dirname: dirname15,
|
|
7870
8007
|
logical,
|
|
7871
|
-
isDir:
|
|
8008
|
+
isDir: statSync11(local).isDirectory(),
|
|
7872
8009
|
count: modified.length,
|
|
7873
8010
|
projectBackupRoot
|
|
7874
8011
|
})
|
|
@@ -7886,7 +8023,7 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
7886
8023
|
// src/links.baseline.ts
|
|
7887
8024
|
init_config();
|
|
7888
8025
|
import { lstatSync as lstatSync18, readdirSync as readdirSync18 } from "node:fs";
|
|
7889
|
-
import { join as
|
|
8026
|
+
import { join as join55, relative as relative8, sep as sep12 } from "node:path";
|
|
7890
8027
|
init_utils();
|
|
7891
8028
|
var SHARED_BASELINE_KIND = "shared-links-baseline/1";
|
|
7892
8029
|
var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
|
|
@@ -7928,7 +8065,7 @@ function collectSharedFiles(dir, claude, scan) {
|
|
|
7928
8065
|
return;
|
|
7929
8066
|
}
|
|
7930
8067
|
for (const entry of entries) {
|
|
7931
|
-
const abs =
|
|
8068
|
+
const abs = join55(dir, entry);
|
|
7932
8069
|
if (isDeniedName(ALWAYS_NEVER_SYNC, entry)) {
|
|
7933
8070
|
scan.declined.push(baselineKey(claude, abs));
|
|
7934
8071
|
continue;
|
|
@@ -7944,7 +8081,7 @@ function enumerateLocalSharedScan(map, opts = {}) {
|
|
|
7944
8081
|
scan.declined.push(name);
|
|
7945
8082
|
continue;
|
|
7946
8083
|
}
|
|
7947
|
-
addLocalPath(
|
|
8084
|
+
addLocalPath(join55(claude, name), claude, scan);
|
|
7948
8085
|
}
|
|
7949
8086
|
return scan;
|
|
7950
8087
|
}
|
|
@@ -7956,7 +8093,7 @@ function buildSharedBaseline(map, opts = {}) {
|
|
|
7956
8093
|
const files = {};
|
|
7957
8094
|
for (const [key, st] of Object.entries(enumerateLocalSharedFiles(map, opts))) {
|
|
7958
8095
|
try {
|
|
7959
|
-
files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(
|
|
8096
|
+
files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join55(claude, key)) };
|
|
7960
8097
|
} catch {
|
|
7961
8098
|
}
|
|
7962
8099
|
}
|
|
@@ -7973,8 +8110,8 @@ function writeSharedBaseline(map, opts = {}) {
|
|
|
7973
8110
|
|
|
7974
8111
|
// src/commands.pull.win32.ts
|
|
7975
8112
|
init_color();
|
|
7976
|
-
import { existsSync as
|
|
7977
|
-
import { join as
|
|
8113
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
8114
|
+
import { join as join60 } from "node:path";
|
|
7978
8115
|
|
|
7979
8116
|
// src/commands.pull.recovery.git.ts
|
|
7980
8117
|
init_utils();
|
|
@@ -8037,8 +8174,8 @@ function gitProbe(args, repo) {
|
|
|
8037
8174
|
|
|
8038
8175
|
// src/links.deletions.ts
|
|
8039
8176
|
init_config();
|
|
8040
|
-
import { existsSync as
|
|
8041
|
-
import { join as
|
|
8177
|
+
import { existsSync as existsSync40, lstatSync as lstatSync19, rmSync as rmSync17 } from "node:fs";
|
|
8178
|
+
import { join as join56, relative as relative9, resolve as resolve5, sep as sep13 } from "node:path";
|
|
8042
8179
|
init_utils();
|
|
8043
8180
|
init_utils_fs();
|
|
8044
8181
|
function isUnknown(declined, key) {
|
|
@@ -8048,15 +8185,15 @@ function deletionFor(key, names, claude, sharedRoot) {
|
|
|
8048
8185
|
const segments = key.split("/");
|
|
8049
8186
|
const name = segments[0];
|
|
8050
8187
|
if (!names.has(name)) return null;
|
|
8051
|
-
if (!
|
|
8188
|
+
if (!existsSync40(join56(claude, name))) return null;
|
|
8052
8189
|
if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
|
|
8053
8190
|
const repoPath = resolve5(sharedRoot, key);
|
|
8054
|
-
const rel = relative9(
|
|
8191
|
+
const rel = relative9(join56(sharedRoot, name), repoPath);
|
|
8055
8192
|
if (rel === "" || rel === ".." || rel.startsWith(`..${sep13}`)) return null;
|
|
8056
8193
|
if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
8057
8194
|
return null;
|
|
8058
|
-
if (!
|
|
8059
|
-
return { kind: "deletion", name, localPath:
|
|
8195
|
+
if (!existsSync40(repoPath)) return null;
|
|
8196
|
+
return { kind: "deletion", name, localPath: join56(claude, key), repoPath };
|
|
8060
8197
|
}
|
|
8061
8198
|
function planSharedLinkDeletions(map, opts = {}) {
|
|
8062
8199
|
if (process.platform !== "win32") return [];
|
|
@@ -8064,7 +8201,7 @@ function planSharedLinkDeletions(map, opts = {}) {
|
|
|
8064
8201
|
const baseline = readSharedBaseline();
|
|
8065
8202
|
if (baseline === null) return [];
|
|
8066
8203
|
const claude = claudeHome();
|
|
8067
|
-
const sharedRoot =
|
|
8204
|
+
const sharedRoot = join56(repoHome(), "shared");
|
|
8068
8205
|
const linkNames = opts.linkNames ?? allSharedLinks(map);
|
|
8069
8206
|
const scan = enumerateLocalSharedScan(map, { linkNames });
|
|
8070
8207
|
const present = new Set(Object.keys(scan.files).map((key) => key.toLowerCase()));
|
|
@@ -8098,8 +8235,8 @@ function applySharedLinkDeletions(map, ts, opts = {}) {
|
|
|
8098
8235
|
|
|
8099
8236
|
// src/links.mirror.ts
|
|
8100
8237
|
init_config();
|
|
8101
|
-
import { existsSync as
|
|
8102
|
-
import { join as
|
|
8238
|
+
import { existsSync as existsSync41, lstatSync as lstatSync20, rmSync as rmSync18, statSync as statSync12 } from "node:fs";
|
|
8239
|
+
import { join as join57 } from "node:path";
|
|
8103
8240
|
init_utils();
|
|
8104
8241
|
init_utils_fs();
|
|
8105
8242
|
function emitMirror(onPreview, name, localPath, repoPath) {
|
|
@@ -8112,31 +8249,52 @@ function emitMirror(onPreview, name, localPath, repoPath) {
|
|
|
8112
8249
|
function emitMirrorWet(onPreview, name, localPath, repoPath) {
|
|
8113
8250
|
if (onPreview) onPreview({ kind: "mirror", name, localPath, repoPath });
|
|
8114
8251
|
}
|
|
8115
|
-
function notShared(
|
|
8116
|
-
return !
|
|
8252
|
+
function notShared(target) {
|
|
8253
|
+
return !existsSync41(target);
|
|
8254
|
+
}
|
|
8255
|
+
function warnUnusableSharedTarget(name, dryRun, state) {
|
|
8256
|
+
const reason = state === "dangling" ? `shared/${name} does not resolve to anything usable in the sync repo. Remove it, or restore what it points at` : `shared/${name} could not be read in the sync repo, so whether it is usable could not be determined. Check its permissions there`;
|
|
8257
|
+
if (dryRun) {
|
|
8258
|
+
warn(`${name} would not be captured into shared/: ${reason}`);
|
|
8259
|
+
return;
|
|
8260
|
+
}
|
|
8261
|
+
warn(`${name} was not captured into shared/ this run: ${reason}, then run \`nomad push\` again`);
|
|
8262
|
+
}
|
|
8263
|
+
function warnUnreadableLocalName(name, target, err, dryRun) {
|
|
8264
|
+
const compoundState = classifyPresence(target);
|
|
8265
|
+
if (isUnusableTarget(compoundState)) {
|
|
8266
|
+
warnUnusableSharedTarget(name, dryRun, compoundState);
|
|
8267
|
+
return;
|
|
8268
|
+
}
|
|
8269
|
+
if (notShared(target)) return;
|
|
8270
|
+
if (dryRun) {
|
|
8271
|
+
warn(
|
|
8272
|
+
`${name} could not be read (${errorText(err)}), so nothing was captured for it and nothing was written. A pull that captures shared edits would skip it too. Check its permissions, or whether another program has it open`
|
|
8273
|
+
);
|
|
8274
|
+
return;
|
|
8275
|
+
}
|
|
8276
|
+
warn(
|
|
8277
|
+
`${name} could not be read (${errorText(err)}), so it was left out of shared/ this run. Check its permissions, or whether another program has it open`
|
|
8278
|
+
);
|
|
8117
8279
|
}
|
|
8118
8280
|
function mirrorOneSharedName(name, claude, repo, policy, opts) {
|
|
8119
|
-
const localPath =
|
|
8120
|
-
const target =
|
|
8281
|
+
const localPath = join57(claude, name);
|
|
8282
|
+
const target = join57(repo, "shared", name);
|
|
8121
8283
|
let stat;
|
|
8122
8284
|
try {
|
|
8123
8285
|
stat = lstatSync20(localPath, { throwIfNoEntry: false });
|
|
8124
8286
|
} catch (err) {
|
|
8125
|
-
|
|
8126
|
-
if (opts.dryRun === true) {
|
|
8127
|
-
warn(
|
|
8128
|
-
`${name} could not be read (${err.message}), so nothing was captured for it and nothing was written. A pull that captures shared edits would skip it too. Check its permissions, or whether another program has it open`
|
|
8129
|
-
);
|
|
8130
|
-
return;
|
|
8131
|
-
}
|
|
8132
|
-
warn(
|
|
8133
|
-
`${name} could not be read (${err.message}), so it was left out of shared/ this run. Check its permissions, or whether another program has it open`
|
|
8134
|
-
);
|
|
8287
|
+
warnUnreadableLocalName(name, target, err, opts.dryRun === true);
|
|
8135
8288
|
return;
|
|
8136
8289
|
}
|
|
8137
8290
|
if (stat === void 0) return;
|
|
8138
8291
|
if (stat.isSymbolicLink()) return;
|
|
8139
|
-
|
|
8292
|
+
const targetState = classifyPresence(target);
|
|
8293
|
+
if (isUnusableTarget(targetState)) {
|
|
8294
|
+
warnUnusableSharedTarget(name, opts.dryRun === true, targetState);
|
|
8295
|
+
return;
|
|
8296
|
+
}
|
|
8297
|
+
if (notShared(target)) return;
|
|
8140
8298
|
if (opts.dryRun === true) {
|
|
8141
8299
|
emitMirror(opts.onPreview, name, localPath, target);
|
|
8142
8300
|
return;
|
|
@@ -8149,21 +8307,58 @@ function mirrorOneSharedName(name, claude, repo, policy, opts) {
|
|
|
8149
8307
|
}
|
|
8150
8308
|
emitMirrorWet(opts.onPreview, name, localPath, target);
|
|
8151
8309
|
}
|
|
8310
|
+
function warnUnusableSharedDir(dryRun, state) {
|
|
8311
|
+
const reason = state === "dangling" ? "shared/ does not resolve to anything usable in the sync repo. Remove it, or restore what it points at" : "shared/ could not be read in the sync repo, so whether it is usable could not be determined. Check its permissions there";
|
|
8312
|
+
if (dryRun) {
|
|
8313
|
+
warn(`no shared name would be captured into shared/: ${reason}`);
|
|
8314
|
+
return;
|
|
8315
|
+
}
|
|
8316
|
+
warn(
|
|
8317
|
+
`no shared name was captured into shared/ this run: ${reason}, then run \`nomad push\` again`
|
|
8318
|
+
);
|
|
8319
|
+
}
|
|
8320
|
+
function resolvesToDirectory(p) {
|
|
8321
|
+
try {
|
|
8322
|
+
return statSync12(p).isDirectory();
|
|
8323
|
+
} catch {
|
|
8324
|
+
return false;
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
function warnSharedNotADirectory(dryRun) {
|
|
8328
|
+
const reason = "shared/ in the sync repo is not a directory, so there is nowhere to capture names into. Replace it with the shared/ directory the repo expects";
|
|
8329
|
+
if (dryRun) {
|
|
8330
|
+
warn(`no shared name would be captured into shared/: ${reason}`);
|
|
8331
|
+
return;
|
|
8332
|
+
}
|
|
8333
|
+
warn(
|
|
8334
|
+
`no shared name was captured into shared/ this run: ${reason}, then run \`nomad push\` again`
|
|
8335
|
+
);
|
|
8336
|
+
}
|
|
8152
8337
|
function mirrorSharedNames(map, policy, opts = {}) {
|
|
8153
8338
|
if (process.platform !== "win32") return;
|
|
8154
8339
|
if (map === null) return;
|
|
8155
8340
|
const claude = claudeHome();
|
|
8156
8341
|
const repo = repoHome();
|
|
8342
|
+
const sharedPath = join57(repo, "shared");
|
|
8343
|
+
const sharedState = classifyPresence(sharedPath);
|
|
8344
|
+
if (isUnusableTarget(sharedState)) {
|
|
8345
|
+
warnUnusableSharedDir(opts.dryRun === true, sharedState);
|
|
8346
|
+
return;
|
|
8347
|
+
}
|
|
8348
|
+
if (sharedState === "resolves" && !resolvesToDirectory(sharedPath)) {
|
|
8349
|
+
warnSharedNotADirectory(opts.dryRun === true);
|
|
8350
|
+
return;
|
|
8351
|
+
}
|
|
8157
8352
|
const linkNames = opts.linkNames ?? allSharedLinks(map);
|
|
8158
8353
|
for (const name of linkNames) {
|
|
8159
8354
|
mirrorOneSharedName(name, claude, repo, policy, opts);
|
|
8160
8355
|
}
|
|
8161
8356
|
}
|
|
8162
8357
|
function syncSharedLinksPush(map, opts = {}) {
|
|
8163
|
-
mirrorSharedNames(map, {
|
|
8358
|
+
mirrorSharedNames(map, { overlay: false }, opts);
|
|
8164
8359
|
}
|
|
8165
8360
|
function stageLocalSharedEdits(map, ts, opts = {}) {
|
|
8166
|
-
mirrorSharedNames(map, {
|
|
8361
|
+
mirrorSharedNames(map, { overlay: true, backupTs: ts }, opts);
|
|
8167
8362
|
}
|
|
8168
8363
|
function presentAt(abs) {
|
|
8169
8364
|
try {
|
|
@@ -8173,7 +8368,7 @@ function presentAt(abs) {
|
|
|
8173
8368
|
}
|
|
8174
8369
|
}
|
|
8175
8370
|
function removeUntrackedDenied(repo, path, segment, ts) {
|
|
8176
|
-
const abs =
|
|
8371
|
+
const abs = join57(repo, path);
|
|
8177
8372
|
const denied = `the path segment "${segment}" is on the never-sync list`;
|
|
8178
8373
|
if (!presentAt(abs)) {
|
|
8179
8374
|
warn(
|
|
@@ -8181,12 +8376,12 @@ function removeUntrackedDenied(repo, path, segment, ts) {
|
|
|
8181
8376
|
);
|
|
8182
8377
|
return;
|
|
8183
8378
|
}
|
|
8184
|
-
const snapshotted =
|
|
8379
|
+
const snapshotted = existsSync41(abs);
|
|
8185
8380
|
try {
|
|
8186
8381
|
backupRepoWrite(abs, ts, repo);
|
|
8187
8382
|
} catch (err) {
|
|
8188
8383
|
warn(
|
|
8189
|
-
`could not snapshot ${abs} before removing it (${err
|
|
8384
|
+
`could not snapshot ${abs} before removing it (${errorText(err)}), so it was left in place: ${denied}, so remove it by hand`
|
|
8190
8385
|
);
|
|
8191
8386
|
return;
|
|
8192
8387
|
}
|
|
@@ -8199,7 +8394,7 @@ function removeUntrackedDenied(repo, path, segment, ts) {
|
|
|
8199
8394
|
const where = snapshotted ? `. A copy was snapshotted under backup/${ts}/repo/ first` : "";
|
|
8200
8395
|
warn(`removed ${path} from the sync repo working tree: ${denied}${where}`);
|
|
8201
8396
|
} catch (err) {
|
|
8202
|
-
warn(`could not remove ${abs}: ${err
|
|
8397
|
+
warn(`could not remove ${abs}: ${errorText(err)}`);
|
|
8203
8398
|
}
|
|
8204
8399
|
}
|
|
8205
8400
|
function reportTrackedDenied(repo, path, segment) {
|
|
@@ -8217,7 +8412,7 @@ function reportTrackedDenied(repo, path, segment) {
|
|
|
8217
8412
|
);
|
|
8218
8413
|
return;
|
|
8219
8414
|
}
|
|
8220
|
-
if (!
|
|
8415
|
+
if (!existsSync41(join57(repo, path))) return;
|
|
8221
8416
|
warn(
|
|
8222
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`
|
|
8223
8418
|
);
|
|
@@ -8235,8 +8430,8 @@ function revertDeniedMirrorPaths(repo, status, ts) {
|
|
|
8235
8430
|
|
|
8236
8431
|
// src/preview.ts
|
|
8237
8432
|
init_config();
|
|
8238
|
-
import { existsSync as
|
|
8239
|
-
import { join as
|
|
8433
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
8434
|
+
import { join as join59 } from "node:path";
|
|
8240
8435
|
|
|
8241
8436
|
// node_modules/diff/libesm/diff/base.js
|
|
8242
8437
|
var Diff = class {
|
|
@@ -8511,16 +8706,16 @@ function diffLinesToUnified(oldStr, newStr) {
|
|
|
8511
8706
|
|
|
8512
8707
|
// src/preview.skills.ts
|
|
8513
8708
|
init_config();
|
|
8514
|
-
import { existsSync as
|
|
8515
|
-
import { join as
|
|
8709
|
+
import { existsSync as existsSync42, readdirSync as readdirSync19 } from "node:fs";
|
|
8710
|
+
import { join as join58 } from "node:path";
|
|
8516
8711
|
function buildSkillsPreviewSection() {
|
|
8517
8712
|
const s = section("Skills");
|
|
8518
|
-
const sharedSkills =
|
|
8519
|
-
if (!
|
|
8713
|
+
const sharedSkills = join58(repoHome(), "shared", "skills");
|
|
8714
|
+
if (!existsSync42(sharedSkills)) return s;
|
|
8520
8715
|
const sharedNames = readdirSync19(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
|
|
8521
8716
|
for (const name of sharedNames) addItem(s, name);
|
|
8522
|
-
const localSkills =
|
|
8523
|
-
const localOnly =
|
|
8717
|
+
const localSkills = join58(claudeHome(), "skills");
|
|
8718
|
+
const localOnly = existsSync42(localSkills) ? readdirSync19(localSkills, { encoding: "utf8" }).filter(
|
|
8524
8719
|
(name) => !isSkillExcluded(name) && !sharedNames.includes(name)
|
|
8525
8720
|
).length : 0;
|
|
8526
8721
|
if (localOnly > 0) {
|
|
@@ -8542,7 +8737,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
8542
8737
|
return lines.join("\n");
|
|
8543
8738
|
}
|
|
8544
8739
|
function readJsonOrNull(path) {
|
|
8545
|
-
if (!
|
|
8740
|
+
if (!existsSync43(path)) return null;
|
|
8546
8741
|
try {
|
|
8547
8742
|
return readJson(path);
|
|
8548
8743
|
} catch {
|
|
@@ -8556,12 +8751,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
8556
8751
|
}
|
|
8557
8752
|
const notes = [];
|
|
8558
8753
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
8559
|
-
if (hostOverrides === null &&
|
|
8754
|
+
if (hostOverrides === null && existsSync43(hostPath)) {
|
|
8560
8755
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
8561
8756
|
}
|
|
8562
8757
|
const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
|
|
8563
8758
|
const current = readJsonOrNull(settingsPath);
|
|
8564
|
-
if (current === null &&
|
|
8759
|
+
if (current === null && existsSync43(settingsPath)) {
|
|
8565
8760
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
8566
8761
|
}
|
|
8567
8762
|
const strippedCurrent = stripGsdHookEntries(current ?? {});
|
|
@@ -8628,9 +8823,9 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8628
8823
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
8629
8824
|
});
|
|
8630
8825
|
const settingsResult = previewSettings(
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8826
|
+
join59(repo, "shared", "settings.base.json"),
|
|
8827
|
+
join59(repo, "hosts", `${HOST}.json`),
|
|
8828
|
+
join59(claude, "settings.json")
|
|
8634
8829
|
);
|
|
8635
8830
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
8636
8831
|
const sessions = section("Sessions");
|
|
@@ -8646,7 +8841,7 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8646
8841
|
const extras = section("Extras");
|
|
8647
8842
|
let extrasSkipped = 0;
|
|
8648
8843
|
let extrasUnmapped = 0;
|
|
8649
|
-
if (
|
|
8844
|
+
if (existsSync43(join59(repo, "path-map.json")) && existsSync43(join59(repo, "shared", "extras"))) {
|
|
8650
8845
|
const extrasResult = remapExtrasPull(ts, { dryRun: true });
|
|
8651
8846
|
for (const entry of extrasResult.wouldPull) {
|
|
8652
8847
|
addItem(extras, entry);
|
|
@@ -8667,7 +8862,7 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8667
8862
|
init_utils();
|
|
8668
8863
|
init_utils_json();
|
|
8669
8864
|
function readMapForMirror(mapPath) {
|
|
8670
|
-
if (!
|
|
8865
|
+
if (!existsSync44(mapPath)) return { projects: {} };
|
|
8671
8866
|
try {
|
|
8672
8867
|
return readPathMap(mapPath);
|
|
8673
8868
|
} catch {
|
|
@@ -8714,7 +8909,7 @@ function reconcileSharedLinksBeforePull(repo, ts) {
|
|
|
8714
8909
|
let namesDerived = false;
|
|
8715
8910
|
let derivedSharedDirs;
|
|
8716
8911
|
try {
|
|
8717
|
-
const map = readMapForMirror(
|
|
8912
|
+
const map = readMapForMirror(join60(repo, "path-map.json"));
|
|
8718
8913
|
if (map !== null) {
|
|
8719
8914
|
linkNames = allSharedLinks(map);
|
|
8720
8915
|
namesDerived = true;
|
|
@@ -8753,7 +8948,7 @@ function buildMirrorSection(events, discard) {
|
|
|
8753
8948
|
function describeSkippedMirrorDiscard(repo, ts) {
|
|
8754
8949
|
if (process.platform !== "win32") return null;
|
|
8755
8950
|
try {
|
|
8756
|
-
const map = readMapForMirror(
|
|
8951
|
+
const map = readMapForMirror(join60(repo, "path-map.json"));
|
|
8757
8952
|
const linkNames = map !== null ? allSharedLinks(map, { quiet: true }) : void 0;
|
|
8758
8953
|
const captures = [];
|
|
8759
8954
|
stageLocalSharedEdits(map, ts, {
|
|
@@ -8762,13 +8957,13 @@ function describeSkippedMirrorDiscard(repo, ts) {
|
|
|
8762
8957
|
linkNames
|
|
8763
8958
|
});
|
|
8764
8959
|
if (captures.length === 0) return null;
|
|
8765
|
-
return { count: captures.length, backupPath:
|
|
8960
|
+
return { count: captures.length, backupPath: join60(backupBase(), ts) };
|
|
8766
8961
|
} catch {
|
|
8767
8962
|
return null;
|
|
8768
8963
|
}
|
|
8769
8964
|
}
|
|
8770
8965
|
function planSharedReconcileBeforePull(repo, ts) {
|
|
8771
|
-
const map = readMapForMirror(
|
|
8966
|
+
const map = readMapForMirror(join60(repo, "path-map.json"));
|
|
8772
8967
|
const captures = [];
|
|
8773
8968
|
const linkNames = process.platform === "win32" && map !== null ? allSharedLinks(map) : void 0;
|
|
8774
8969
|
stageLocalSharedEdits(map, ts, { dryRun: true, onPreview: (e) => captures.push(e), linkNames });
|
|
@@ -8783,8 +8978,8 @@ function planSharedReconcileBeforePull(repo, ts) {
|
|
|
8783
8978
|
// src/commands.pull.collision.ts
|
|
8784
8979
|
init_config();
|
|
8785
8980
|
init_exit_codes();
|
|
8786
|
-
import { existsSync as
|
|
8787
|
-
import { join as
|
|
8981
|
+
import { existsSync as existsSync45, lstatSync as lstatSync21, readFileSync as readFileSync20, rmSync as rmSync19 } from "node:fs";
|
|
8982
|
+
import { join as join61 } from "node:path";
|
|
8788
8983
|
init_utils();
|
|
8789
8984
|
var SHARED_PREFIX = /^shared\//;
|
|
8790
8985
|
var EXTENSION = /(\.[^.]+)?$/;
|
|
@@ -8840,15 +9035,15 @@ function isContainedMirrorPath(rel) {
|
|
|
8840
9035
|
}
|
|
8841
9036
|
function matchesLocalOriginal(repo, rel) {
|
|
8842
9037
|
try {
|
|
8843
|
-
const local =
|
|
8844
|
-
return readFileSync20(
|
|
9038
|
+
const local = join61(claudeHome(), rel.replace(SHARED_PREFIX, ""));
|
|
9039
|
+
return readFileSync20(join61(repo, rel)).equals(readFileSync20(local));
|
|
8845
9040
|
} catch {
|
|
8846
9041
|
return false;
|
|
8847
9042
|
}
|
|
8848
9043
|
}
|
|
8849
9044
|
function removeMirroredCopies(repo, repoRelPaths) {
|
|
8850
9045
|
for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
|
|
8851
|
-
const abs =
|
|
9046
|
+
const abs = join61(repo, rel);
|
|
8852
9047
|
try {
|
|
8853
9048
|
if (lstatSync21(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
|
|
8854
9049
|
if (!matchesLocalOriginal(repo, rel)) continue;
|
|
@@ -8857,7 +9052,7 @@ function removeMirroredCopies(repo, repoRelPaths) {
|
|
|
8857
9052
|
warn(`could not remove nomad's copy at ${abs}: ${err.message}`);
|
|
8858
9053
|
}
|
|
8859
9054
|
}
|
|
8860
|
-
return repoRelPaths.every((rel) => !
|
|
9055
|
+
return repoRelPaths.every((rel) => !existsSync45(join61(repo, rel)));
|
|
8861
9056
|
}
|
|
8862
9057
|
function pullWithCollisionRunbook(repo, mirrored) {
|
|
8863
9058
|
try {
|
|
@@ -9064,7 +9259,7 @@ function runPullCore(opts = {}) {
|
|
|
9064
9259
|
try {
|
|
9065
9260
|
return runPullWithBackupTs(ts, opts);
|
|
9066
9261
|
} finally {
|
|
9067
|
-
discardEmptyBackupDir(
|
|
9262
|
+
discardEmptyBackupDir(join62(backup, ts));
|
|
9068
9263
|
}
|
|
9069
9264
|
}
|
|
9070
9265
|
function runPullWithBackupTs(ts, opts) {
|
|
@@ -9075,7 +9270,7 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9075
9270
|
const backup = backupBase();
|
|
9076
9271
|
const recovered = handleWedge(repo, forceRemote);
|
|
9077
9272
|
if (!dryRun) {
|
|
9078
|
-
const backupRoot =
|
|
9273
|
+
const backupRoot = join62(backup, ts);
|
|
9079
9274
|
try {
|
|
9080
9275
|
mkdirSync14(backupRoot, { recursive: true });
|
|
9081
9276
|
} catch (err) {
|
|
@@ -9098,8 +9293,8 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9098
9293
|
pullWithCollisionRunbook(repo, mirrored);
|
|
9099
9294
|
});
|
|
9100
9295
|
assertNoAutostashConflict(repo, "nomad pull");
|
|
9101
|
-
const mapPath =
|
|
9102
|
-
const map =
|
|
9296
|
+
const mapPath = join62(repo, "path-map.json");
|
|
9297
|
+
const map = existsSync46(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
9103
9298
|
const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
|
|
9104
9299
|
if (dryRun) {
|
|
9105
9300
|
computePreview(ts, map, "pull", plansAgainst(sharedPlans, map));
|
|
@@ -9126,8 +9321,8 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9126
9321
|
}
|
|
9127
9322
|
function cmdPull(opts = {}) {
|
|
9128
9323
|
const repo = repoHome();
|
|
9129
|
-
if (!
|
|
9130
|
-
if (!
|
|
9324
|
+
if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
|
|
9325
|
+
if (!existsSync46(join62(repo, "shared", "settings.base.json"))) {
|
|
9131
9326
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
9132
9327
|
}
|
|
9133
9328
|
const handle = acquireLock("pull");
|
|
@@ -9150,13 +9345,13 @@ function cmdPull(opts = {}) {
|
|
|
9150
9345
|
|
|
9151
9346
|
// src/commands.push.ts
|
|
9152
9347
|
init_config();
|
|
9153
|
-
import { existsSync as
|
|
9154
|
-
import { join as
|
|
9348
|
+
import { existsSync as existsSync50 } from "node:fs";
|
|
9349
|
+
import { join as join67 } from "node:path";
|
|
9155
9350
|
|
|
9156
9351
|
// src/commands.push.selection.ts
|
|
9157
9352
|
init_config();
|
|
9158
|
-
import { existsSync as
|
|
9159
|
-
import { join as
|
|
9353
|
+
import { existsSync as existsSync47, statSync as statSync13 } from "node:fs";
|
|
9354
|
+
import { join as join63 } from "node:path";
|
|
9160
9355
|
init_utils_json();
|
|
9161
9356
|
function buildCurrentMap(map) {
|
|
9162
9357
|
const current = {};
|
|
@@ -9165,10 +9360,10 @@ function buildCurrentMap(map) {
|
|
|
9165
9360
|
for (const [, hostMap] of Object.entries(map.projects)) {
|
|
9166
9361
|
const localPath = hostMap[HOST];
|
|
9167
9362
|
if (!localPath) continue;
|
|
9168
|
-
const localDir =
|
|
9169
|
-
if (!
|
|
9363
|
+
const localDir = join63(claude, "projects", encodePath(localPath));
|
|
9364
|
+
if (!existsSync47(localDir)) continue;
|
|
9170
9365
|
for (const f of enumerateSourceFiles(localDir)) {
|
|
9171
|
-
const st =
|
|
9366
|
+
const st = statSync13(f);
|
|
9172
9367
|
current[f] = { size: st.size, mtime: st.mtimeMs };
|
|
9173
9368
|
}
|
|
9174
9369
|
}
|
|
@@ -9197,7 +9392,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
|
|
|
9197
9392
|
};
|
|
9198
9393
|
}
|
|
9199
9394
|
function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
|
|
9200
|
-
const map =
|
|
9395
|
+
const map = existsSync47(mapPath) ? readPathMap(mapPath) : null;
|
|
9201
9396
|
const { selection, newManifest } = computePushSelection(
|
|
9202
9397
|
map,
|
|
9203
9398
|
old,
|
|
@@ -9290,14 +9485,14 @@ function enforceAllowList(statusPorcelain, map) {
|
|
|
9290
9485
|
|
|
9291
9486
|
// src/commands.push.settings.ts
|
|
9292
9487
|
init_config();
|
|
9293
|
-
import { existsSync as
|
|
9294
|
-
import { join as
|
|
9488
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
9489
|
+
import { join as join64 } from "node:path";
|
|
9295
9490
|
init_utils();
|
|
9296
9491
|
init_utils_fs();
|
|
9297
9492
|
init_utils_json();
|
|
9298
9493
|
function stripGsdHooksFromBase(repo, backup) {
|
|
9299
|
-
const basePath =
|
|
9300
|
-
if (!
|
|
9494
|
+
const basePath = join64(repo, "shared", "settings.base.json");
|
|
9495
|
+
if (!existsSync48(basePath)) return;
|
|
9301
9496
|
let base;
|
|
9302
9497
|
try {
|
|
9303
9498
|
base = readJson(basePath);
|
|
@@ -9311,14 +9506,14 @@ function stripGsdHooksFromBase(repo, backup) {
|
|
|
9311
9506
|
writeJsonAtomic(basePath, stripped);
|
|
9312
9507
|
}
|
|
9313
9508
|
function reportSettingsAheadDrift(repo) {
|
|
9314
|
-
const basePath =
|
|
9315
|
-
if (!
|
|
9316
|
-
const settingsPath =
|
|
9317
|
-
if (!
|
|
9509
|
+
const basePath = join64(repo, "shared", "settings.base.json");
|
|
9510
|
+
if (!existsSync48(basePath)) return;
|
|
9511
|
+
const settingsPath = join64(claudeHome(), "settings.json");
|
|
9512
|
+
if (!existsSync48(settingsPath)) return;
|
|
9318
9513
|
try {
|
|
9319
9514
|
const base = readJson(basePath);
|
|
9320
|
-
const hostPath =
|
|
9321
|
-
const overrides =
|
|
9515
|
+
const hostPath = join64(repo, "hosts", `${HOST}.json`);
|
|
9516
|
+
const overrides = existsSync48(hostPath) ? readJson(hostPath) : {};
|
|
9322
9517
|
const merged = deepMerge(base, overrides);
|
|
9323
9518
|
const settings = readJson(settingsPath);
|
|
9324
9519
|
const { ahead } = classifySettingsDrift(merged, settings);
|
|
@@ -9335,9 +9530,9 @@ function reportSettingsAheadDrift(repo) {
|
|
|
9335
9530
|
// src/commands.push.guards.ts
|
|
9336
9531
|
init_push_checks();
|
|
9337
9532
|
init_utils();
|
|
9338
|
-
import { join as
|
|
9533
|
+
import { join as join65, relative as relative10 } from "node:path";
|
|
9339
9534
|
function guardGitlinks(repo) {
|
|
9340
|
-
const gitlinks = findGitlinks(
|
|
9535
|
+
const gitlinks = findGitlinks(join65(repo, "shared"));
|
|
9341
9536
|
if (gitlinks.length === 0) return;
|
|
9342
9537
|
for (const p of gitlinks) {
|
|
9343
9538
|
const rel = relative10(repo, p).replaceAll("\\", "/");
|
|
@@ -9447,9 +9642,9 @@ init_color();
|
|
|
9447
9642
|
init_config();
|
|
9448
9643
|
init_config_sharedDirs_guard();
|
|
9449
9644
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
9450
|
-
import { copyFileSync, existsSync as
|
|
9645
|
+
import { copyFileSync, existsSync as existsSync49, lstatSync as lstatSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync20, rmSync as rmSync20 } from "node:fs";
|
|
9451
9646
|
import { homedir as homedir7 } from "node:os";
|
|
9452
|
-
import { join as
|
|
9647
|
+
import { join as join66, relative as relative11, sep as sep14 } from "node:path";
|
|
9453
9648
|
init_push_leak_verdict();
|
|
9454
9649
|
init_push_gitleaks();
|
|
9455
9650
|
init_utils_fs();
|
|
@@ -9461,7 +9656,7 @@ function stageSessionDir(localDir, dstDir, changed) {
|
|
|
9461
9656
|
const matching = [...changed].filter((p) => p.startsWith(prefix));
|
|
9462
9657
|
if (matching.length === 0) return false;
|
|
9463
9658
|
for (const src of matching) {
|
|
9464
|
-
copyFileAtomic(src,
|
|
9659
|
+
copyFileAtomic(src, join66(dstDir, relative11(localDir, src)));
|
|
9465
9660
|
}
|
|
9466
9661
|
return true;
|
|
9467
9662
|
}
|
|
@@ -9477,14 +9672,14 @@ function stageSessions(tmpRoot, map, changed) {
|
|
|
9477
9672
|
if (!p || p === "TBD") continue;
|
|
9478
9673
|
reverse.set(encodePath(p), logical);
|
|
9479
9674
|
}
|
|
9480
|
-
const localProjects =
|
|
9481
|
-
if (!
|
|
9675
|
+
const localProjects = join66(claudeHome(), "projects");
|
|
9676
|
+
if (!existsSync49(localProjects)) return 0;
|
|
9482
9677
|
let staged = 0;
|
|
9483
9678
|
for (const dir of readdirSync20(localProjects)) {
|
|
9484
9679
|
const logical = reverse.get(dir);
|
|
9485
9680
|
if (!logical) continue;
|
|
9486
|
-
const localDir =
|
|
9487
|
-
const dstDir =
|
|
9681
|
+
const localDir = join66(localProjects, dir);
|
|
9682
|
+
const dstDir = join66(tmpRoot, "shared", "projects", logical);
|
|
9488
9683
|
if (stageSessionDir(localDir, dstDir, changed)) staged++;
|
|
9489
9684
|
}
|
|
9490
9685
|
return staged;
|
|
@@ -9500,9 +9695,9 @@ function stageExtras(tmpRoot, map) {
|
|
|
9500
9695
|
if (!localRoot || localRoot === "TBD") continue;
|
|
9501
9696
|
for (const dirname15 of dirnames) {
|
|
9502
9697
|
if (!whitelist.includes(dirname15)) continue;
|
|
9503
|
-
const src =
|
|
9504
|
-
if (!
|
|
9505
|
-
const dst =
|
|
9698
|
+
const src = join66(localRoot, dirname15);
|
|
9699
|
+
if (!existsSync49(src)) continue;
|
|
9700
|
+
const dst = join66(tmpRoot, "shared", "extras", logical, dirname15);
|
|
9506
9701
|
copyExtras(src, dst);
|
|
9507
9702
|
staged++;
|
|
9508
9703
|
}
|
|
@@ -9510,19 +9705,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
9510
9705
|
return staged;
|
|
9511
9706
|
}
|
|
9512
9707
|
function stageSkills(tmpRoot) {
|
|
9513
|
-
const localSkills =
|
|
9708
|
+
const localSkills = join66(claudeHome(), "skills");
|
|
9514
9709
|
const stat = lstatSync22(localSkills, { throwIfNoEntry: false });
|
|
9515
9710
|
if (stat === void 0 || stat.isSymbolicLink()) return 0;
|
|
9516
9711
|
const names = readdirSync20(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
|
|
9517
9712
|
if (names.length === 0) return 0;
|
|
9518
|
-
copySkillsPush(localSkills,
|
|
9713
|
+
copySkillsPush(localSkills, join66(tmpRoot, "shared", "skills"));
|
|
9519
9714
|
return names.length;
|
|
9520
9715
|
}
|
|
9521
9716
|
function previewPushLeaks(map, opts = {}) {
|
|
9522
|
-
const cacheDir =
|
|
9717
|
+
const cacheDir = join66(homedir7(), ".cache", "claude-nomad");
|
|
9523
9718
|
mkdirSync15(cacheDir, { recursive: true });
|
|
9524
9719
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
9525
|
-
const tmpRoot =
|
|
9720
|
+
const tmpRoot = join66(cacheDir, `push-preview-tree-${stamp}`);
|
|
9526
9721
|
try {
|
|
9527
9722
|
const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
|
|
9528
9723
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
@@ -9530,9 +9725,9 @@ function previewPushLeaks(map, opts = {}) {
|
|
|
9530
9725
|
if (sessionCount + extrasCount + skillCount === 0) {
|
|
9531
9726
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
9532
9727
|
}
|
|
9533
|
-
const ignoreFile =
|
|
9534
|
-
if (
|
|
9535
|
-
copyFileSync(ignoreFile,
|
|
9728
|
+
const ignoreFile = join66(repoHome(), ".gitleaksignore");
|
|
9729
|
+
if (existsSync49(ignoreFile)) {
|
|
9730
|
+
copyFileSync(ignoreFile, join66(tmpRoot, ".gitleaksignore"));
|
|
9536
9731
|
}
|
|
9537
9732
|
let findings;
|
|
9538
9733
|
try {
|
|
@@ -9660,7 +9855,7 @@ async function runPushCore(opts = {}) {
|
|
|
9660
9855
|
const scannerVersion = probeGitleaks();
|
|
9661
9856
|
const configHash = computeConfigHash();
|
|
9662
9857
|
const old = readManifest(manifestPath());
|
|
9663
|
-
const mapPath =
|
|
9858
|
+
const mapPath = join67(repo, "path-map.json");
|
|
9664
9859
|
const { map, selection, newManifest } = loadSelectionForPush(
|
|
9665
9860
|
mapPath,
|
|
9666
9861
|
old,
|
|
@@ -9713,7 +9908,7 @@ async function cmdPush(opts = {}) {
|
|
|
9713
9908
|
opts.allowRule
|
|
9714
9909
|
);
|
|
9715
9910
|
const repo = repoHome();
|
|
9716
|
-
if (!
|
|
9911
|
+
if (!existsSync50(repo)) die(`repo not cloned at ${repo}`);
|
|
9717
9912
|
const handle = acquireLock("push");
|
|
9718
9913
|
if (handle === null) process.exit(0);
|
|
9719
9914
|
try {
|
|
@@ -9731,8 +9926,8 @@ async function cmdPush(opts = {}) {
|
|
|
9731
9926
|
}
|
|
9732
9927
|
|
|
9733
9928
|
// src/commands.sync.ts
|
|
9734
|
-
import { existsSync as
|
|
9735
|
-
import { join as
|
|
9929
|
+
import { existsSync as existsSync51 } from "node:fs";
|
|
9930
|
+
import { join as join68 } from "node:path";
|
|
9736
9931
|
init_config();
|
|
9737
9932
|
init_color();
|
|
9738
9933
|
init_utils();
|
|
@@ -9873,8 +10068,8 @@ async function cmdSync(opts = {}) {
|
|
|
9873
10068
|
const dryRun = opts.dryRun === true;
|
|
9874
10069
|
const verbose = opts.verbose === true;
|
|
9875
10070
|
const repo = repoHome();
|
|
9876
|
-
if (!
|
|
9877
|
-
if (!
|
|
10071
|
+
if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
|
|
10072
|
+
if (!existsSync51(join68(repo, "shared", "settings.base.json"))) {
|
|
9878
10073
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
9879
10074
|
}
|
|
9880
10075
|
const handle = acquireLock("sync");
|
|
@@ -9950,8 +10145,8 @@ init_config();
|
|
|
9950
10145
|
// src/crash-report.write.ts
|
|
9951
10146
|
init_config();
|
|
9952
10147
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
9953
|
-
import { mkdirSync as mkdirSync16, readdirSync as readdirSync21, statSync as
|
|
9954
|
-
import { join as
|
|
10148
|
+
import { mkdirSync as mkdirSync16, readdirSync as readdirSync21, statSync as statSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
|
|
10149
|
+
import { join as join70 } from "node:path";
|
|
9955
10150
|
|
|
9956
10151
|
// src/crash-report.ts
|
|
9957
10152
|
var CRASH_MAX_STACK_LINES = 50;
|
|
@@ -10036,15 +10231,15 @@ function buildCrashReport(input) {
|
|
|
10036
10231
|
// src/crash-report.redact.ts
|
|
10037
10232
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync21, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10038
10233
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10039
|
-
import { join as
|
|
10234
|
+
import { join as join69 } from "node:path";
|
|
10040
10235
|
init_push_gitleaks_scan();
|
|
10041
10236
|
var CRASH_SCAN_TIMEOUT_MS = 3e3;
|
|
10042
10237
|
var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
|
|
10043
10238
|
function redactWithGitleaks(text, scan = scanFile) {
|
|
10044
10239
|
let dir;
|
|
10045
10240
|
try {
|
|
10046
|
-
dir = mkdtempSync3(
|
|
10047
|
-
const tmp =
|
|
10241
|
+
dir = mkdtempSync3(join69(tmpdir2(), "nomad-crash-scan-"));
|
|
10242
|
+
const tmp = join69(dir, "crash.txt");
|
|
10048
10243
|
writeFileSync10(tmp, text, { mode: 384 });
|
|
10049
10244
|
const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
|
|
10050
10245
|
if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
|
|
@@ -10069,7 +10264,7 @@ function listCrashFiles(dir = crashDir()) {
|
|
|
10069
10264
|
try {
|
|
10070
10265
|
return readdirSync21(dir).flatMap((name) => {
|
|
10071
10266
|
try {
|
|
10072
|
-
return [{ name, mtimeMs:
|
|
10267
|
+
return [{ name, mtimeMs: statSync14(join70(dir, name)).mtimeMs }];
|
|
10073
10268
|
} catch {
|
|
10074
10269
|
return [];
|
|
10075
10270
|
}
|
|
@@ -10083,7 +10278,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
|
|
|
10083
10278
|
const targets = prunableByCount(files, keep);
|
|
10084
10279
|
for (const name of targets) {
|
|
10085
10280
|
try {
|
|
10086
|
-
unlinkSync2(
|
|
10281
|
+
unlinkSync2(join70(dir, name));
|
|
10087
10282
|
} catch {
|
|
10088
10283
|
}
|
|
10089
10284
|
}
|
|
@@ -10091,7 +10286,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
|
|
|
10091
10286
|
function writeCrashReport(text, dir = crashDir()) {
|
|
10092
10287
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
10093
10288
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes5(4).toString("hex")}`;
|
|
10094
|
-
const path =
|
|
10289
|
+
const path = join70(dir, `crash-${stamp}.txt`);
|
|
10095
10290
|
writeFileSync11(path, text, { mode: 384 });
|
|
10096
10291
|
pruneCrashDir(dir);
|
|
10097
10292
|
return path;
|
|
@@ -10123,18 +10318,18 @@ function handleCrash(err, argv, opts) {
|
|
|
10123
10318
|
|
|
10124
10319
|
// src/diff.ts
|
|
10125
10320
|
init_config();
|
|
10126
|
-
import { existsSync as
|
|
10127
|
-
import { join as
|
|
10321
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
10322
|
+
import { join as join71 } from "node:path";
|
|
10128
10323
|
init_utils();
|
|
10129
10324
|
init_utils_fs();
|
|
10130
10325
|
init_utils_json();
|
|
10131
10326
|
function cmdDiff() {
|
|
10132
10327
|
try {
|
|
10133
10328
|
const repo = repoHome();
|
|
10134
|
-
if (!
|
|
10329
|
+
if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
|
|
10135
10330
|
const ts = freshBackupTs(backupBase());
|
|
10136
|
-
const mapPath =
|
|
10137
|
-
const map =
|
|
10331
|
+
const mapPath = join71(repo, "path-map.json");
|
|
10332
|
+
const map = existsSync52(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
10138
10333
|
divergenceCheckExtras(ts);
|
|
10139
10334
|
computePreview(ts, map, "diff");
|
|
10140
10335
|
} catch (err) {
|
|
@@ -10149,8 +10344,8 @@ function cmdDiff() {
|
|
|
10149
10344
|
|
|
10150
10345
|
// src/init.ts
|
|
10151
10346
|
init_config();
|
|
10152
|
-
import { existsSync as
|
|
10153
|
-
import { join as
|
|
10347
|
+
import { existsSync as existsSync54, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
|
|
10348
|
+
import { join as join73 } from "node:path";
|
|
10154
10349
|
|
|
10155
10350
|
// src/init.gh-onboard.ts
|
|
10156
10351
|
init_config();
|
|
@@ -10232,33 +10427,33 @@ init_config();
|
|
|
10232
10427
|
init_utils();
|
|
10233
10428
|
init_utils_fs();
|
|
10234
10429
|
init_utils_json();
|
|
10235
|
-
import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as
|
|
10236
|
-
import { join as
|
|
10430
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync53, rmSync as rmSync22, statSync as statSync15 } from "node:fs";
|
|
10431
|
+
import { join as join72 } from "node:path";
|
|
10237
10432
|
function snapshotIntoShared(map) {
|
|
10238
10433
|
const repo = repoHome();
|
|
10239
10434
|
const claude = claudeHome();
|
|
10240
10435
|
for (const name of allSharedLinks(map)) {
|
|
10241
|
-
const src =
|
|
10242
|
-
if (!
|
|
10243
|
-
const dst =
|
|
10244
|
-
if (
|
|
10245
|
-
const gk =
|
|
10246
|
-
if (
|
|
10436
|
+
const src = join72(claude, name);
|
|
10437
|
+
if (!existsSync53(src)) continue;
|
|
10438
|
+
const dst = join72(repo, "shared", name);
|
|
10439
|
+
if (statSync15(src).isDirectory()) {
|
|
10440
|
+
const gk = join72(dst, ".gitkeep");
|
|
10441
|
+
if (existsSync53(gk)) rmSync22(gk);
|
|
10247
10442
|
cpSync9(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
10248
10443
|
} else {
|
|
10249
10444
|
copyFileSync2(src, dst);
|
|
10250
10445
|
}
|
|
10251
10446
|
log(`snapshotted shared/${name} from ${src}`);
|
|
10252
10447
|
}
|
|
10253
|
-
const userSettings =
|
|
10254
|
-
if (
|
|
10448
|
+
const userSettings = join72(claude, "settings.json");
|
|
10449
|
+
if (existsSync53(userSettings)) {
|
|
10255
10450
|
let parsed;
|
|
10256
10451
|
try {
|
|
10257
10452
|
parsed = readJson(userSettings);
|
|
10258
10453
|
} catch (err) {
|
|
10259
10454
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
10260
10455
|
}
|
|
10261
|
-
const hostFile =
|
|
10456
|
+
const hostFile = join72(repo, "hosts", `${HOST}.json`);
|
|
10262
10457
|
writeJsonAtomic(hostFile, parsed);
|
|
10263
10458
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
10264
10459
|
}
|
|
@@ -10272,14 +10467,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
|
|
|
10272
10467
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
10273
10468
|
function preflightConflict(repoHome2) {
|
|
10274
10469
|
const candidates = [
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10470
|
+
join73(repoHome2, "shared", "settings.base.json"),
|
|
10471
|
+
join73(repoHome2, "shared", "CLAUDE.md"),
|
|
10472
|
+
join73(repoHome2, "path-map.json"),
|
|
10473
|
+
join73(repoHome2, "hosts"),
|
|
10474
|
+
join73(repoHome2, "shared")
|
|
10280
10475
|
];
|
|
10281
10476
|
for (const c of candidates) {
|
|
10282
|
-
if (
|
|
10477
|
+
if (existsSync54(c)) return c;
|
|
10283
10478
|
}
|
|
10284
10479
|
return null;
|
|
10285
10480
|
}
|
|
@@ -10297,27 +10492,27 @@ function cmdInit(opts = {}) {
|
|
|
10297
10492
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
10298
10493
|
}
|
|
10299
10494
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
10300
|
-
mkdirSync17(
|
|
10301
|
-
mkdirSync17(
|
|
10495
|
+
mkdirSync17(join73(repo, "shared"), { recursive: true });
|
|
10496
|
+
mkdirSync17(join73(repo, "hosts"), { recursive: true });
|
|
10302
10497
|
for (const name of SHARED_KEEP_DIRS) {
|
|
10303
|
-
mkdirSync17(
|
|
10498
|
+
mkdirSync17(join73(repo, "shared", name), { recursive: true });
|
|
10304
10499
|
}
|
|
10305
|
-
const userClaudeMd =
|
|
10306
|
-
if (!snapshot || !
|
|
10307
|
-
writeFileSync12(
|
|
10500
|
+
const userClaudeMd = join73(claude, "CLAUDE.md");
|
|
10501
|
+
if (!snapshot || !existsSync54(userClaudeMd)) {
|
|
10502
|
+
writeFileSync12(join73(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
10308
10503
|
item("created shared/CLAUDE.md");
|
|
10309
10504
|
}
|
|
10310
10505
|
for (const name of SHARED_KEEP_DIRS) {
|
|
10311
|
-
writeFileSync12(
|
|
10506
|
+
writeFileSync12(join73(repo, "shared", name, ".gitkeep"), "");
|
|
10312
10507
|
item(`created shared/${name}/.gitkeep`);
|
|
10313
10508
|
}
|
|
10314
|
-
writeFileSync12(
|
|
10509
|
+
writeFileSync12(join73(repo, "hosts", ".gitkeep"), "");
|
|
10315
10510
|
item("created hosts/.gitkeep");
|
|
10316
|
-
writeJsonAtomic(
|
|
10511
|
+
writeJsonAtomic(join73(repo, "shared", "settings.base.json"), {});
|
|
10317
10512
|
item("created shared/settings.base.json");
|
|
10318
|
-
writeJsonAtomic(
|
|
10513
|
+
writeJsonAtomic(join73(repo, "path-map.json"), { projects: {} });
|
|
10319
10514
|
item("created path-map.json");
|
|
10320
|
-
writeFileSync12(
|
|
10515
|
+
writeFileSync12(join73(repo, ".gitattributes"), GITATTRIBUTES);
|
|
10321
10516
|
item("created .gitattributes");
|
|
10322
10517
|
if (snapshot) {
|
|
10323
10518
|
snapshotIntoShared({ projects: {} });
|
|
@@ -10383,21 +10578,21 @@ function maybeDisableRepoActions(repoHome2, run) {
|
|
|
10383
10578
|
// src/init.prompt.ts
|
|
10384
10579
|
init_config();
|
|
10385
10580
|
init_utils();
|
|
10386
|
-
import { existsSync as
|
|
10387
|
-
import { join as
|
|
10581
|
+
import { existsSync as existsSync55, readdirSync as readdirSync22, statSync as statSync16 } from "node:fs";
|
|
10582
|
+
import { join as join74 } from "node:path";
|
|
10388
10583
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
10389
10584
|
function nonEmptyExists(path) {
|
|
10390
|
-
if (!
|
|
10585
|
+
if (!existsSync55(path)) return false;
|
|
10391
10586
|
try {
|
|
10392
|
-
if (
|
|
10587
|
+
if (statSync16(path).isDirectory()) return readdirSync22(path).length > 0;
|
|
10393
10588
|
return true;
|
|
10394
10589
|
} catch {
|
|
10395
10590
|
return false;
|
|
10396
10591
|
}
|
|
10397
10592
|
}
|
|
10398
10593
|
function hasExistingClaudeConfig(claudeHome2) {
|
|
10399
|
-
if (
|
|
10400
|
-
return SHARED_LINKS.some((name) => nonEmptyExists(
|
|
10594
|
+
if (existsSync55(join74(claudeHome2, "settings.json"))) return true;
|
|
10595
|
+
return SHARED_LINKS.some((name) => nonEmptyExists(join74(claudeHome2, name)));
|
|
10401
10596
|
}
|
|
10402
10597
|
async function confirmSnapshotDefault(claudeHome2) {
|
|
10403
10598
|
if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
|
|
@@ -10704,7 +10899,7 @@ function parseSyncArgs(argv) {
|
|
|
10704
10899
|
// package.json
|
|
10705
10900
|
var package_default = {
|
|
10706
10901
|
name: "claude-nomad",
|
|
10707
|
-
version: "0.67.
|
|
10902
|
+
version: "0.67.2",
|
|
10708
10903
|
type: "module",
|
|
10709
10904
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
10710
10905
|
keywords: [
|
|
@@ -10777,10 +10972,11 @@ var package_default = {
|
|
|
10777
10972
|
"@commitlint/cli": "^21.0.1",
|
|
10778
10973
|
"@commitlint/config-conventional": "^21.0.1",
|
|
10779
10974
|
"@eslint/js": "^10.0.1",
|
|
10780
|
-
"@stryker-mutator/core": "
|
|
10781
|
-
"@stryker-mutator/vitest-runner": "
|
|
10975
|
+
"@stryker-mutator/core": "10.0.0",
|
|
10976
|
+
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
10782
10977
|
"@types/node": "^22.0.0",
|
|
10783
10978
|
"@vitest/coverage-v8": "^4.1.6",
|
|
10979
|
+
"conventional-changelog-conventionalcommits": "^10.2.1",
|
|
10784
10980
|
diff: "^9.0.0",
|
|
10785
10981
|
esbuild: "^0.28.0",
|
|
10786
10982
|
eslint: "^10.4.0",
|
|
@@ -10951,15 +11147,15 @@ var DEFAULT_HELP = [
|
|
|
10951
11147
|
init_config();
|
|
10952
11148
|
init_utils();
|
|
10953
11149
|
init_utils_json();
|
|
10954
|
-
import { existsSync as
|
|
10955
|
-
import { join as
|
|
11150
|
+
import { existsSync as existsSync56, readFileSync as readFileSync21, readdirSync as readdirSync23 } from "node:fs";
|
|
11151
|
+
import { join as join75 } from "node:path";
|
|
10956
11152
|
function resumeCmd(sessionId) {
|
|
10957
11153
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
10958
11154
|
fail(`invalid session id: ${sessionId}`);
|
|
10959
11155
|
process.exit(1);
|
|
10960
11156
|
}
|
|
10961
|
-
const projectsRoot =
|
|
10962
|
-
if (!
|
|
11157
|
+
const projectsRoot = join75(claudeHome(), "projects");
|
|
11158
|
+
if (!existsSync56(projectsRoot)) {
|
|
10963
11159
|
fail(`${projectsRoot} does not exist`);
|
|
10964
11160
|
process.exit(1);
|
|
10965
11161
|
}
|
|
@@ -10973,8 +11169,8 @@ function resumeCmd(sessionId) {
|
|
|
10973
11169
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
10974
11170
|
process.exit(1);
|
|
10975
11171
|
}
|
|
10976
|
-
const mapPath =
|
|
10977
|
-
if (!
|
|
11172
|
+
const mapPath = join75(repoHome(), "path-map.json");
|
|
11173
|
+
if (!existsSync56(mapPath)) {
|
|
10978
11174
|
fail("path-map.json missing");
|
|
10979
11175
|
process.exit(1);
|
|
10980
11176
|
}
|
|
@@ -10997,8 +11193,8 @@ function resumeCmd(sessionId) {
|
|
|
10997
11193
|
}
|
|
10998
11194
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
10999
11195
|
for (const dir of readdirSync23(projectsRoot)) {
|
|
11000
|
-
const candidate =
|
|
11001
|
-
if (
|
|
11196
|
+
const candidate = join75(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
11197
|
+
if (existsSync56(candidate)) return candidate;
|
|
11002
11198
|
}
|
|
11003
11199
|
return null;
|
|
11004
11200
|
}
|