claude-nomad 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dist/nomad.mjs +513 -319
- package/package.json +1 -1
package/dist/nomad.mjs
CHANGED
|
@@ -561,31 +561,31 @@ var init_utils_fs = __esm({
|
|
|
561
561
|
});
|
|
562
562
|
|
|
563
563
|
// src/push-gitleaks.config.ts
|
|
564
|
-
import { existsSync as
|
|
564
|
+
import { existsSync as existsSync11, mkdtempSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
565
565
|
import { tmpdir } from "node:os";
|
|
566
|
-
import { join as
|
|
566
|
+
import { join as join12 } from "node:path";
|
|
567
567
|
import { fileURLToPath } from "node:url";
|
|
568
568
|
function resolveTomlPath() {
|
|
569
|
-
const repoToml =
|
|
570
|
-
if (
|
|
569
|
+
const repoToml = join12(REPO_HOME, ".gitleaks.toml");
|
|
570
|
+
if (existsSync11(repoToml)) return repoToml;
|
|
571
571
|
const bundled = fileURLToPath(new URL("../.gitleaks.toml", import.meta.url));
|
|
572
|
-
return
|
|
572
|
+
return existsSync11(bundled) ? bundled : null;
|
|
573
573
|
}
|
|
574
574
|
function buildOverlayTempConfig(overlayBody, bundled) {
|
|
575
575
|
const tempBody = `[extend]
|
|
576
576
|
path = ${JSON.stringify(bundled)}
|
|
577
577
|
|
|
578
578
|
${overlayBody}`;
|
|
579
|
-
const tempPath = mkdtempSync(
|
|
580
|
-
const configPath =
|
|
579
|
+
const tempPath = mkdtempSync(join12(tmpdir(), "nomad-gitleaks-cfg-"));
|
|
580
|
+
const configPath = join12(tempPath, "config.toml");
|
|
581
581
|
writeFileSync2(configPath, tempBody, { mode: 384, flag: "wx" });
|
|
582
582
|
return { configPath, tempPath };
|
|
583
583
|
}
|
|
584
584
|
function resolveTomlConfig() {
|
|
585
|
-
const overlayPath =
|
|
586
|
-
const repoToml =
|
|
585
|
+
const overlayPath = join12(REPO_HOME, ".gitleaks.overlay.toml");
|
|
586
|
+
const repoToml = join12(REPO_HOME, ".gitleaks.toml");
|
|
587
587
|
const bundled = resolveTomlPath();
|
|
588
|
-
if (!
|
|
588
|
+
if (!existsSync11(overlayPath)) {
|
|
589
589
|
return { path: bundled, tempPath: null };
|
|
590
590
|
}
|
|
591
591
|
if (bundled === repoToml) {
|
|
@@ -626,9 +626,9 @@ var init_push_gitleaks_config = __esm({
|
|
|
626
626
|
|
|
627
627
|
// src/push-checks.ts
|
|
628
628
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
629
|
-
import { readdirSync as readdirSync4, rmSync as
|
|
629
|
+
import { readdirSync as readdirSync4, rmSync as rmSync4 } from "node:fs";
|
|
630
630
|
import { homedir as homedir2, platform } from "node:os";
|
|
631
|
-
import { join as
|
|
631
|
+
import { join as join13 } from "node:path";
|
|
632
632
|
function gitleaksInstallHint() {
|
|
633
633
|
const head = "gitleaks not on PATH (required for nomad push). Install:";
|
|
634
634
|
const plat = platform();
|
|
@@ -671,7 +671,7 @@ function findGitlinks(dir) {
|
|
|
671
671
|
return;
|
|
672
672
|
}
|
|
673
673
|
for (const e of entries) {
|
|
674
|
-
const p =
|
|
674
|
+
const p = join13(current, e.name);
|
|
675
675
|
if (e.name === ".git") {
|
|
676
676
|
hits.push(p);
|
|
677
677
|
continue;
|
|
@@ -693,7 +693,7 @@ function probeGitleaks() {
|
|
|
693
693
|
if (e.code === "ENOENT") throw new NomadFatal(gitleaksInstallHint());
|
|
694
694
|
throw new NomadFatal(`gitleaks --version failed: ${e.message}`);
|
|
695
695
|
} finally {
|
|
696
|
-
if (tempPath !== null)
|
|
696
|
+
if (tempPath !== null) rmSync4(tempPath, { recursive: true, force: true });
|
|
697
697
|
}
|
|
698
698
|
}
|
|
699
699
|
function rebaseBeforePush() {
|
|
@@ -721,9 +721,9 @@ var init_push_checks = __esm({
|
|
|
721
721
|
|
|
722
722
|
// src/push-gitleaks.scan.ts
|
|
723
723
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
724
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as
|
|
724
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync5 } from "node:fs";
|
|
725
725
|
import { homedir as homedir3 } from "node:os";
|
|
726
|
-
import { join as
|
|
726
|
+
import { join as join17 } from "node:path";
|
|
727
727
|
function readGitleaksReport(reportPath) {
|
|
728
728
|
try {
|
|
729
729
|
const raw = readFileSync4(reportPath, "utf8");
|
|
@@ -735,9 +735,9 @@ function readGitleaksReport(reportPath) {
|
|
|
735
735
|
}
|
|
736
736
|
}
|
|
737
737
|
function scanStagedTree(repoDir, forwardStreams = false) {
|
|
738
|
-
const cacheDir =
|
|
738
|
+
const cacheDir = join17(homedir3(), ".cache", "claude-nomad");
|
|
739
739
|
mkdirSync2(cacheDir, { recursive: true });
|
|
740
|
-
const reportPath =
|
|
740
|
+
const reportPath = join17(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
|
|
741
741
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
742
742
|
const args = [
|
|
743
743
|
"protect",
|
|
@@ -764,14 +764,14 @@ function scanStagedTree(repoDir, forwardStreams = false) {
|
|
|
764
764
|
}
|
|
765
765
|
return report;
|
|
766
766
|
} finally {
|
|
767
|
-
if (tempPath !== null)
|
|
768
|
-
|
|
767
|
+
if (tempPath !== null) rmSync5(tempPath, { recursive: true, force: true });
|
|
768
|
+
rmSync5(reportPath, { force: true });
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
771
|
function scanFile(filePath, forwardStreams = false) {
|
|
772
|
-
const cacheDir =
|
|
772
|
+
const cacheDir = join17(homedir3(), ".cache", "claude-nomad");
|
|
773
773
|
mkdirSync2(cacheDir, { recursive: true });
|
|
774
|
-
const reportPath =
|
|
774
|
+
const reportPath = join17(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}.json`);
|
|
775
775
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
776
776
|
const args = [
|
|
777
777
|
"detect",
|
|
@@ -796,8 +796,8 @@ function scanFile(filePath, forwardStreams = false) {
|
|
|
796
796
|
}
|
|
797
797
|
return report;
|
|
798
798
|
} finally {
|
|
799
|
-
if (tempPath !== null)
|
|
800
|
-
|
|
799
|
+
if (tempPath !== null) rmSync5(tempPath, { recursive: true, force: true });
|
|
800
|
+
rmSync5(reportPath, { force: true });
|
|
801
801
|
}
|
|
802
802
|
}
|
|
803
803
|
var init_push_gitleaks_scan = __esm({
|
|
@@ -1219,15 +1219,178 @@ function cmdClean(opts, backupBase = BACKUP_BASE) {
|
|
|
1219
1219
|
log(`removed ${targets.length} backup(s)`);
|
|
1220
1220
|
}
|
|
1221
1221
|
|
|
1222
|
+
// src/commands.eject.ts
|
|
1223
|
+
init_config();
|
|
1224
|
+
init_utils();
|
|
1225
|
+
init_utils_json();
|
|
1226
|
+
import { cpSync as cpSync3, existsSync as existsSync5, lstatSync as lstatSync4, realpathSync, renameSync as renameSync2, rmSync as rmSync3 } from "node:fs";
|
|
1227
|
+
import { join as join6, sep } from "node:path";
|
|
1228
|
+
var EJECT_CHECKLIST = [
|
|
1229
|
+
"Manual steps remaining to finish leaving claude-nomad on this host:",
|
|
1230
|
+
` 1. Uninstall the CLI: npm uninstall -g claude-nomad`,
|
|
1231
|
+
` 2. Remove NOMAD_HOST and NOMAD_REPO from your shell rc (~/.zshrc or ~/.bashrc)`,
|
|
1232
|
+
` 3. Optionally delete the local sync checkout: rm -rf ${REPO_HOME}`,
|
|
1233
|
+
` 4. Optionally delete the private sync repo on GitHub`,
|
|
1234
|
+
` 5. Optionally delete the backup cache: rm -rf ${BACKUP_BASE}`
|
|
1235
|
+
].join("\n");
|
|
1236
|
+
var DEFAULT_ROOTS = { claudeHome: CLAUDE_HOME, repoHome: REPO_HOME };
|
|
1237
|
+
function errMessage(err) {
|
|
1238
|
+
return err instanceof Error ? err.message : String(err);
|
|
1239
|
+
}
|
|
1240
|
+
function lexists2(p) {
|
|
1241
|
+
try {
|
|
1242
|
+
lstatSync4(p);
|
|
1243
|
+
return true;
|
|
1244
|
+
} catch {
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
function readMapIfPresent2(repoHome) {
|
|
1249
|
+
const mapPath = join6(repoHome, "path-map.json");
|
|
1250
|
+
return existsSync5(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
1251
|
+
}
|
|
1252
|
+
function classifyName(linkPath) {
|
|
1253
|
+
if (!lexists2(linkPath)) return "absent";
|
|
1254
|
+
if (!lstatSync4(linkPath).isSymbolicLink()) return "skip-real";
|
|
1255
|
+
if (!existsSync5(linkPath)) return "dangling";
|
|
1256
|
+
return "materialize";
|
|
1257
|
+
}
|
|
1258
|
+
function resolveSharedRoot(repoHome) {
|
|
1259
|
+
try {
|
|
1260
|
+
return realpathSync(join6(repoHome, "shared"));
|
|
1261
|
+
} catch {
|
|
1262
|
+
return die(
|
|
1263
|
+
`cannot resolve ${join6(repoHome, "shared")} (repo checkout incomplete). run \`nomad pull\` first, then re-run \`nomad eject\``
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
function isManagedTarget(target, sharedRoot) {
|
|
1268
|
+
return target.startsWith(sharedRoot + sep);
|
|
1269
|
+
}
|
|
1270
|
+
function materializeOne(name, linkPath, sharedRoot) {
|
|
1271
|
+
const target = realpathSync(linkPath);
|
|
1272
|
+
if (!isManagedTarget(target, sharedRoot)) {
|
|
1273
|
+
log(`skipped (not a nomad-managed target): ${name} -> ${target}`);
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
1276
|
+
const tmp = `${linkPath}.eject.tmp.${process.pid}.${Date.now()}`;
|
|
1277
|
+
try {
|
|
1278
|
+
rmSync3(tmp, { recursive: true, force: true });
|
|
1279
|
+
cpSync3(target, tmp, {
|
|
1280
|
+
recursive: true,
|
|
1281
|
+
force: true,
|
|
1282
|
+
dereference: true,
|
|
1283
|
+
preserveTimestamps: true
|
|
1284
|
+
});
|
|
1285
|
+
rmSync3(linkPath, { force: true });
|
|
1286
|
+
renameSync2(tmp, linkPath);
|
|
1287
|
+
log(`ejected: ${name}`);
|
|
1288
|
+
return true;
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
try {
|
|
1291
|
+
rmSync3(tmp, { recursive: true, force: true });
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
throw err;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
function previewDryRun(names, classifications, claudeHome, sharedRoot) {
|
|
1298
|
+
for (const name of names) {
|
|
1299
|
+
const cls = classifications.get(name);
|
|
1300
|
+
const linkPath = join6(claudeHome, name);
|
|
1301
|
+
if (cls === "absent") {
|
|
1302
|
+
log(`skipped (absent): ${name}`);
|
|
1303
|
+
} else if (cls === "skip-real") {
|
|
1304
|
+
log(`skipped (not a symlink): ${name}`);
|
|
1305
|
+
} else {
|
|
1306
|
+
previewMaterialize(name, linkPath, sharedRoot);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
log(EJECT_CHECKLIST);
|
|
1310
|
+
}
|
|
1311
|
+
function previewMaterialize(name, linkPath, sharedRoot) {
|
|
1312
|
+
let target;
|
|
1313
|
+
try {
|
|
1314
|
+
target = realpathSync(linkPath);
|
|
1315
|
+
} catch {
|
|
1316
|
+
log(`would materialize: ${name} (target now unresolvable; re-run to re-classify)`);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (!isManagedTarget(target, sharedRoot)) {
|
|
1320
|
+
log(`skipped (not a nomad-managed target): ${name} -> ${target}`);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
log(`would materialize: ${name} (copy ${target} -> ${linkPath})`);
|
|
1324
|
+
}
|
|
1325
|
+
function runLiveEject(names, classifications, claudeHome, sharedRoot) {
|
|
1326
|
+
const done = [];
|
|
1327
|
+
let skipped = 0;
|
|
1328
|
+
for (const name of names) {
|
|
1329
|
+
const cls = classifications.get(name);
|
|
1330
|
+
const linkPath = join6(claudeHome, name);
|
|
1331
|
+
if (cls === "absent") {
|
|
1332
|
+
log(`skipped (absent): ${name}`);
|
|
1333
|
+
skipped++;
|
|
1334
|
+
} else if (cls === "skip-real") {
|
|
1335
|
+
log(`skipped (not a symlink): ${name}`);
|
|
1336
|
+
skipped++;
|
|
1337
|
+
} else if (materializeOneOrDie(name, linkPath, sharedRoot, done)) {
|
|
1338
|
+
done.push(name);
|
|
1339
|
+
} else {
|
|
1340
|
+
skipped++;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
log(`materialized ${done.length}, skipped ${skipped}`);
|
|
1344
|
+
log(EJECT_CHECKLIST);
|
|
1345
|
+
}
|
|
1346
|
+
function materializeOneOrDie(name, linkPath, sharedRoot, done) {
|
|
1347
|
+
try {
|
|
1348
|
+
return materializeOne(name, linkPath, sharedRoot);
|
|
1349
|
+
} catch (err) {
|
|
1350
|
+
const msg = errMessage(err);
|
|
1351
|
+
return die(
|
|
1352
|
+
`failed to materialize ${name}: ${msg}. already materialized: ${done.join(", ") || "(none)"}. the remaining names are still symlinks; do NOT delete ${REPO_HOME} yet, fix the cause and re-run \`nomad eject\` (it is idempotent on already-real names)`
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
function cmdEject(opts = {}, roots = DEFAULT_ROOTS) {
|
|
1357
|
+
const dryRun = opts.dryRun === true;
|
|
1358
|
+
const { claudeHome, repoHome } = roots;
|
|
1359
|
+
const map = readMapIfPresent2(repoHome);
|
|
1360
|
+
const names = allSharedLinks(map);
|
|
1361
|
+
const classifications = /* @__PURE__ */ new Map();
|
|
1362
|
+
for (const name of names) {
|
|
1363
|
+
classifications.set(name, classifyName(join6(claudeHome, name)));
|
|
1364
|
+
}
|
|
1365
|
+
const dangling = names.filter((n) => classifications.get(n) === "dangling");
|
|
1366
|
+
if (dangling.length > 0) {
|
|
1367
|
+
fail(
|
|
1368
|
+
`dangling symlink(s): ${dangling.join(", ")}. run \`nomad pull\` first to restore the missing target, then re-run \`nomad eject\``
|
|
1369
|
+
);
|
|
1370
|
+
process.exit(1);
|
|
1371
|
+
}
|
|
1372
|
+
const sharedRoot = resolveSharedRoot(repoHome);
|
|
1373
|
+
if (dryRun) {
|
|
1374
|
+
previewDryRun(names, classifications, claudeHome, sharedRoot);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
try {
|
|
1378
|
+
runLiveEject(names, classifications, claudeHome, sharedRoot);
|
|
1379
|
+
} catch (err) {
|
|
1380
|
+
fail(errMessage(err));
|
|
1381
|
+
process.exit(1);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1222
1385
|
// src/commands.doctor.ts
|
|
1223
|
-
import { existsSync as
|
|
1224
|
-
import { join as
|
|
1386
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
1387
|
+
import { join as join26 } from "node:path";
|
|
1225
1388
|
|
|
1226
1389
|
// src/commands.doctor.checks.repo.ts
|
|
1227
1390
|
init_color();
|
|
1228
1391
|
init_config();
|
|
1229
|
-
import { existsSync as
|
|
1230
|
-
import { join as
|
|
1392
|
+
import { existsSync as existsSync7, lstatSync as lstatSync5, statSync as statSync3 } from "node:fs";
|
|
1393
|
+
import { join as join8 } from "node:path";
|
|
1231
1394
|
|
|
1232
1395
|
// src/commands.doctor.format.ts
|
|
1233
1396
|
init_color();
|
|
@@ -1305,15 +1468,15 @@ function readJsonSafe(path, label, section2) {
|
|
|
1305
1468
|
// src/init.classify.ts
|
|
1306
1469
|
init_config();
|
|
1307
1470
|
init_utils_json();
|
|
1308
|
-
import { existsSync as
|
|
1309
|
-
import { join as
|
|
1471
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1472
|
+
import { join as join7 } from "node:path";
|
|
1310
1473
|
function classifyRepoState(repoHome, host) {
|
|
1311
|
-
const basePath =
|
|
1312
|
-
const mapPath =
|
|
1313
|
-
const hostPath =
|
|
1314
|
-
const hasBase =
|
|
1315
|
-
const hasMap =
|
|
1316
|
-
const hasHost =
|
|
1474
|
+
const basePath = join7(repoHome, "shared", "settings.base.json");
|
|
1475
|
+
const mapPath = join7(repoHome, "path-map.json");
|
|
1476
|
+
const hostPath = join7(repoHome, "hosts", `${host}.json`);
|
|
1477
|
+
const hasBase = existsSync6(basePath);
|
|
1478
|
+
const hasMap = existsSync6(mapPath);
|
|
1479
|
+
const hasHost = existsSync6(hostPath);
|
|
1317
1480
|
let mapEntryCount = 0;
|
|
1318
1481
|
if (hasMap) {
|
|
1319
1482
|
try {
|
|
@@ -1328,11 +1491,11 @@ function classifyRepoState(repoHome, host) {
|
|
|
1328
1491
|
return "partial";
|
|
1329
1492
|
}
|
|
1330
1493
|
function reasonForPartial(repoHome, host) {
|
|
1331
|
-
const basePath =
|
|
1332
|
-
const mapPath =
|
|
1333
|
-
const hostPath =
|
|
1334
|
-
if (!
|
|
1335
|
-
if (!
|
|
1494
|
+
const basePath = join7(repoHome, "shared", "settings.base.json");
|
|
1495
|
+
const mapPath = join7(repoHome, "path-map.json");
|
|
1496
|
+
const hostPath = join7(repoHome, "hosts", `${host}.json`);
|
|
1497
|
+
if (!existsSync6(basePath)) return "- shared/settings.base.json missing";
|
|
1498
|
+
if (!existsSync6(mapPath)) return "- path-map.json missing";
|
|
1336
1499
|
let mapEntryCount;
|
|
1337
1500
|
try {
|
|
1338
1501
|
const map = readJson(mapPath);
|
|
@@ -1341,7 +1504,7 @@ function reasonForPartial(repoHome, host) {
|
|
|
1341
1504
|
mapEntryCount = 0;
|
|
1342
1505
|
}
|
|
1343
1506
|
if (mapEntryCount === 0) return "- path-map.json.projects has no entries";
|
|
1344
|
-
if (!
|
|
1507
|
+
if (!existsSync6(hostPath)) return `- hosts/${host}.json missing`;
|
|
1345
1508
|
return "- partial state (unknown gap)";
|
|
1346
1509
|
}
|
|
1347
1510
|
|
|
@@ -1357,11 +1520,11 @@ function reportHostAndPaths(section2) {
|
|
|
1357
1520
|
}
|
|
1358
1521
|
addItem(
|
|
1359
1522
|
section2,
|
|
1360
|
-
`${
|
|
1523
|
+
`${existsSync7(REPO_HOME) ? green(okGlyph) : yellow(warnGlyph)} repo: ${blue(REPO_HOME)}`
|
|
1361
1524
|
);
|
|
1362
1525
|
addItem(
|
|
1363
1526
|
section2,
|
|
1364
|
-
`${
|
|
1527
|
+
`${existsSync7(CLAUDE_HOME) ? green(okGlyph) : yellow(warnGlyph)} claude home: ${blue(CLAUDE_HOME)}`
|
|
1365
1528
|
);
|
|
1366
1529
|
}
|
|
1367
1530
|
function reportRepoState(section2) {
|
|
@@ -1383,12 +1546,12 @@ function reportRepoState(section2) {
|
|
|
1383
1546
|
}
|
|
1384
1547
|
}
|
|
1385
1548
|
function repoHasSharedSource(name) {
|
|
1386
|
-
return
|
|
1549
|
+
return existsSync7(join8(REPO_HOME, "shared", name));
|
|
1387
1550
|
}
|
|
1388
1551
|
function classifySharedLink(name, p) {
|
|
1389
1552
|
let stat;
|
|
1390
1553
|
try {
|
|
1391
|
-
stat =
|
|
1554
|
+
stat = lstatSync5(p);
|
|
1392
1555
|
} catch (err) {
|
|
1393
1556
|
const code = err.code;
|
|
1394
1557
|
if (code === "ENOENT") {
|
|
@@ -1430,7 +1593,7 @@ function classifySymlinkTarget(name, p) {
|
|
|
1430
1593
|
}
|
|
1431
1594
|
function reportSharedLinks(section2, map) {
|
|
1432
1595
|
for (const name of allSharedLinks(map)) {
|
|
1433
|
-
const p =
|
|
1596
|
+
const p = join8(CLAUDE_HOME, name);
|
|
1434
1597
|
const { line, fail: fail2 } = classifySharedLink(name, p);
|
|
1435
1598
|
addItem(section2, line);
|
|
1436
1599
|
if (fail2) process.exitCode = 1;
|
|
@@ -1440,11 +1603,11 @@ function reportSharedLinks(section2, map) {
|
|
|
1440
1603
|
// src/commands.doctor.checks.settings.ts
|
|
1441
1604
|
init_color();
|
|
1442
1605
|
init_config();
|
|
1443
|
-
import { existsSync as
|
|
1444
|
-
import { join as
|
|
1606
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2 } from "node:fs";
|
|
1607
|
+
import { join as join9 } from "node:path";
|
|
1445
1608
|
function loadBaseSettings(section2) {
|
|
1446
|
-
const basePath =
|
|
1447
|
-
if (!
|
|
1609
|
+
const basePath = join9(REPO_HOME, "shared", "settings.base.json");
|
|
1610
|
+
if (!existsSync8(basePath)) {
|
|
1448
1611
|
addItem(section2, `${red(failGlyph)} shared/settings.base.json missing at ${blue(basePath)}`);
|
|
1449
1612
|
process.exitCode = 1;
|
|
1450
1613
|
return null;
|
|
@@ -1452,8 +1615,8 @@ function loadBaseSettings(section2) {
|
|
|
1452
1615
|
return readJsonSafe(basePath, basePath, section2);
|
|
1453
1616
|
}
|
|
1454
1617
|
function loadAndReportSettings(section2) {
|
|
1455
|
-
const settingsPath =
|
|
1456
|
-
if (!
|
|
1618
|
+
const settingsPath = join9(CLAUDE_HOME, "settings.json");
|
|
1619
|
+
if (!existsSync8(settingsPath)) return null;
|
|
1457
1620
|
const settings = readJsonSafe(settingsPath, settingsPath, section2);
|
|
1458
1621
|
if (settings === null) return null;
|
|
1459
1622
|
const unknownKeys = Object.keys(settings).filter((k) => !KNOWN_SETTINGS_KEYS.has(k));
|
|
@@ -1468,13 +1631,13 @@ function loadAndReportSettings(section2) {
|
|
|
1468
1631
|
return settings;
|
|
1469
1632
|
}
|
|
1470
1633
|
function reportHostOverrides(section2, base, settings) {
|
|
1471
|
-
const hostFile =
|
|
1634
|
+
const hostFile = join9(REPO_HOME, "hosts", `${HOST}.json`);
|
|
1472
1635
|
let drift = [];
|
|
1473
1636
|
if (base !== null && settings !== null) {
|
|
1474
1637
|
const baseKeys = new Set(Object.keys(base));
|
|
1475
1638
|
drift = Object.keys(settings).filter((k) => !baseKeys.has(k));
|
|
1476
1639
|
}
|
|
1477
|
-
if (
|
|
1640
|
+
if (existsSync8(hostFile)) {
|
|
1478
1641
|
if (readJsonSafe(hostFile, hostFile, section2) !== null) {
|
|
1479
1642
|
addItem(section2, `${green(okGlyph)} host overrides: ${blue(hostFile)}`);
|
|
1480
1643
|
}
|
|
@@ -1483,8 +1646,8 @@ function reportHostOverrides(section2, base, settings) {
|
|
|
1483
1646
|
section2,
|
|
1484
1647
|
`${red(failGlyph)} no hosts/${HOST}.json AND settings.json has unbased keys ${JSON.stringify(drift)}`
|
|
1485
1648
|
);
|
|
1486
|
-
const hostsDir =
|
|
1487
|
-
if (
|
|
1649
|
+
const hostsDir = join9(REPO_HOME, "hosts");
|
|
1650
|
+
if (existsSync8(hostsDir)) {
|
|
1488
1651
|
const cands = readdirSync2(hostsDir).filter((f) => f.endsWith(".json"));
|
|
1489
1652
|
if (cands.length > 0) addItem(section2, `${dim(infoGlyph)} candidates: ${cands.join(", ")}`);
|
|
1490
1653
|
}
|
|
@@ -1500,8 +1663,8 @@ function reportHostOverrides(section2, base, settings) {
|
|
|
1500
1663
|
// src/commands.doctor.checks.pathmap.ts
|
|
1501
1664
|
init_color();
|
|
1502
1665
|
init_config();
|
|
1503
|
-
import { existsSync as
|
|
1504
|
-
import { join as
|
|
1666
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3 } from "node:fs";
|
|
1667
|
+
import { join as join10 } from "node:path";
|
|
1505
1668
|
init_utils_json();
|
|
1506
1669
|
function reportMappedProjects(section2, map) {
|
|
1507
1670
|
const mapped = Object.entries(map.projects).filter(([, hosts]) => hosts[HOST]);
|
|
@@ -1511,8 +1674,8 @@ function reportMappedProjects(section2, map) {
|
|
|
1511
1674
|
}
|
|
1512
1675
|
}
|
|
1513
1676
|
function reportUnmappedProjects(section2, map) {
|
|
1514
|
-
const localProjects =
|
|
1515
|
-
if (!
|
|
1677
|
+
const localProjects = join10(CLAUDE_HOME, "projects");
|
|
1678
|
+
if (!existsSync9(localProjects)) return;
|
|
1516
1679
|
let localDirs;
|
|
1517
1680
|
try {
|
|
1518
1681
|
localDirs = readdirSync3(localProjects);
|
|
@@ -1552,8 +1715,8 @@ function reportPathCollisions(section2, map) {
|
|
|
1552
1715
|
else addItem(section2, `${green(okGlyph)} path-encoding: no collisions`);
|
|
1553
1716
|
}
|
|
1554
1717
|
function reportPathMap(section2) {
|
|
1555
|
-
const mapPath =
|
|
1556
|
-
if (!
|
|
1718
|
+
const mapPath = join10(REPO_HOME, "path-map.json");
|
|
1719
|
+
if (!existsSync9(mapPath)) {
|
|
1557
1720
|
addItem(section2, `${red(failGlyph)} path-map.json missing at ${blue(mapPath)}`);
|
|
1558
1721
|
process.exitCode = 1;
|
|
1559
1722
|
return;
|
|
@@ -1606,16 +1769,16 @@ function reportNeverSync(section2) {
|
|
|
1606
1769
|
init_color();
|
|
1607
1770
|
init_config();
|
|
1608
1771
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1609
|
-
import { existsSync as
|
|
1610
|
-
import { join as
|
|
1772
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
1773
|
+
import { join as join14, relative as relative2 } from "node:path";
|
|
1611
1774
|
|
|
1612
1775
|
// src/commands.pull.wedge.ts
|
|
1613
|
-
import { existsSync as
|
|
1614
|
-
import { join as
|
|
1776
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
1777
|
+
import { join as join11 } from "node:path";
|
|
1615
1778
|
function detectWedge(repo) {
|
|
1616
|
-
const g =
|
|
1617
|
-
if (
|
|
1618
|
-
if (
|
|
1779
|
+
const g = join11(repo, ".git");
|
|
1780
|
+
if (existsSync10(join11(g, "rebase-merge")) || existsSync10(join11(g, "rebase-apply"))) return "rebase";
|
|
1781
|
+
if (existsSync10(join11(g, "MERGE_HEAD"))) return "merge";
|
|
1619
1782
|
return null;
|
|
1620
1783
|
}
|
|
1621
1784
|
|
|
@@ -1640,8 +1803,8 @@ function reportGitleaksProbe(section2) {
|
|
|
1640
1803
|
}
|
|
1641
1804
|
}
|
|
1642
1805
|
function reportGitlinks(section2) {
|
|
1643
|
-
const sharedDir =
|
|
1644
|
-
if (
|
|
1806
|
+
const sharedDir = join14(REPO_HOME, "shared");
|
|
1807
|
+
if (existsSync12(sharedDir)) {
|
|
1645
1808
|
const gitlinks = findGitlinks(sharedDir);
|
|
1646
1809
|
for (const p of gitlinks) {
|
|
1647
1810
|
const rel = relative2(REPO_HOME, p);
|
|
@@ -1697,8 +1860,8 @@ function reportRebaseState(section2) {
|
|
|
1697
1860
|
|
|
1698
1861
|
// src/commands.doctor.checks.backups.ts
|
|
1699
1862
|
init_color();
|
|
1700
|
-
import { existsSync as
|
|
1701
|
-
import { join as
|
|
1863
|
+
import { existsSync as existsSync13, lstatSync as lstatSync6, readdirSync as readdirSync5 } from "node:fs";
|
|
1864
|
+
import { join as join15 } from "node:path";
|
|
1702
1865
|
init_config();
|
|
1703
1866
|
var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
|
|
1704
1867
|
function safeReaddir(dir) {
|
|
@@ -1714,8 +1877,8 @@ var BYTES_PER_MB = 1024 * 1024;
|
|
|
1714
1877
|
function dirSizeBytes(dir) {
|
|
1715
1878
|
let bytes = 0;
|
|
1716
1879
|
for (const entry of safeReaddir(dir)) {
|
|
1717
|
-
const full =
|
|
1718
|
-
const st =
|
|
1880
|
+
const full = join15(dir, entry);
|
|
1881
|
+
const st = lstatSync6(full, { throwIfNoEntry: false });
|
|
1719
1882
|
if (!st) continue;
|
|
1720
1883
|
if (st.isSymbolicLink()) continue;
|
|
1721
1884
|
if (st.isDirectory()) bytes += dirSizeBytes(full);
|
|
@@ -1725,11 +1888,11 @@ function dirSizeBytes(dir) {
|
|
|
1725
1888
|
}
|
|
1726
1889
|
function totalSizeMb(backupBase, dirs) {
|
|
1727
1890
|
let bytes = 0;
|
|
1728
|
-
for (const name of dirs) bytes += dirSizeBytes(
|
|
1891
|
+
for (const name of dirs) bytes += dirSizeBytes(join15(backupBase, name));
|
|
1729
1892
|
return bytes / BYTES_PER_MB;
|
|
1730
1893
|
}
|
|
1731
1894
|
function reportBackupsCheck(section2, backupBase = BACKUP_BASE) {
|
|
1732
|
-
if (!
|
|
1895
|
+
if (!existsSync13(backupBase)) return;
|
|
1733
1896
|
const dirs = safeReaddir(backupBase).filter((n) => TS_SHAPE2.test(n));
|
|
1734
1897
|
const count = dirs.length;
|
|
1735
1898
|
const sizeMb = totalSizeMb(backupBase, dirs);
|
|
@@ -1743,8 +1906,8 @@ function reportBackupsCheck(section2, backupBase = BACKUP_BASE) {
|
|
|
1743
1906
|
|
|
1744
1907
|
// src/commands.doctor.check-schema.ts
|
|
1745
1908
|
init_color();
|
|
1746
|
-
import { existsSync as
|
|
1747
|
-
import { join as
|
|
1909
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
1910
|
+
import { join as join16 } from "node:path";
|
|
1748
1911
|
init_config();
|
|
1749
1912
|
|
|
1750
1913
|
// src/http-fetch.ts
|
|
@@ -1781,8 +1944,8 @@ function fetchSchemaKeys() {
|
|
|
1781
1944
|
}
|
|
1782
1945
|
}
|
|
1783
1946
|
function reportCheckSchema(section2) {
|
|
1784
|
-
const settingsPath =
|
|
1785
|
-
if (!
|
|
1947
|
+
const settingsPath = join16(CLAUDE_HOME, "settings.json");
|
|
1948
|
+
if (!existsSync14(settingsPath)) {
|
|
1786
1949
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json to check`);
|
|
1787
1950
|
return;
|
|
1788
1951
|
}
|
|
@@ -1812,18 +1975,18 @@ function reportCheckSchema(section2) {
|
|
|
1812
1975
|
init_color();
|
|
1813
1976
|
import { randomBytes } from "node:crypto";
|
|
1814
1977
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
1815
|
-
import { existsSync as
|
|
1978
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync4, readdirSync as readdirSync7, rmSync as rmSync7 } from "node:fs";
|
|
1816
1979
|
import { homedir as homedir4 } from "node:os";
|
|
1817
|
-
import { join as
|
|
1980
|
+
import { join as join20 } from "node:path";
|
|
1818
1981
|
|
|
1819
1982
|
// src/commands.doctor.check-shared.scan.ts
|
|
1820
1983
|
init_color();
|
|
1821
|
-
import { join as
|
|
1984
|
+
import { join as join18 } from "node:path";
|
|
1822
1985
|
init_config();
|
|
1823
1986
|
init_push_gitleaks();
|
|
1824
1987
|
function scrubPath(logical, sid, logicalToEncoded) {
|
|
1825
1988
|
const encoded = logicalToEncoded.get(logical) ?? logical;
|
|
1826
|
-
return
|
|
1989
|
+
return join18(CLAUDE_HOME, "projects", encoded, `${sid}.jsonl`);
|
|
1827
1990
|
}
|
|
1828
1991
|
function reportSessionFindings(section2, bySession) {
|
|
1829
1992
|
for (const [sid, counts] of bySession) {
|
|
@@ -1915,21 +2078,21 @@ init_config();
|
|
|
1915
2078
|
init_utils();
|
|
1916
2079
|
init_utils_fs();
|
|
1917
2080
|
init_utils_json();
|
|
1918
|
-
import { cpSync as
|
|
1919
|
-
import { join as
|
|
2081
|
+
import { cpSync as cpSync4, existsSync as existsSync15, mkdirSync as mkdirSync3, readdirSync as readdirSync6, rmSync as rmSync6, statSync as statSync4 } from "node:fs";
|
|
2082
|
+
import { join as join19, relative as relative3, sep as sep2 } from "node:path";
|
|
1920
2083
|
function copyDir(src, dst) {
|
|
1921
|
-
|
|
1922
|
-
|
|
2084
|
+
rmSync6(dst, { recursive: true, force: true });
|
|
2085
|
+
cpSync4(src, dst, { recursive: true, force: true });
|
|
1923
2086
|
}
|
|
1924
2087
|
function copyDirJsonlOnly(src, dst) {
|
|
1925
|
-
|
|
1926
|
-
|
|
2088
|
+
rmSync6(dst, { recursive: true, force: true });
|
|
2089
|
+
cpSync4(src, dst, {
|
|
1927
2090
|
recursive: true,
|
|
1928
2091
|
force: true,
|
|
1929
2092
|
filter: (srcPath) => {
|
|
1930
2093
|
const rel = relative3(src, srcPath);
|
|
1931
2094
|
if (rel === "") return true;
|
|
1932
|
-
if (rel.split(
|
|
2095
|
+
if (rel.split(sep2).length > 1) return true;
|
|
1933
2096
|
if (statSync4(srcPath).isDirectory()) return true;
|
|
1934
2097
|
if (srcPath.endsWith(".jsonl")) return true;
|
|
1935
2098
|
log(`skip ${rel}: extension not in allowlist`);
|
|
@@ -1946,15 +2109,15 @@ function remapPull(ts, opts = {}) {
|
|
|
1946
2109
|
let unmapped = 0;
|
|
1947
2110
|
const pulled = [];
|
|
1948
2111
|
const wouldPull = [];
|
|
1949
|
-
const mapPath =
|
|
1950
|
-
const repoProjects =
|
|
1951
|
-
if (!
|
|
2112
|
+
const mapPath = join19(REPO_HOME, "path-map.json");
|
|
2113
|
+
const repoProjects = join19(REPO_HOME, "shared", "projects");
|
|
2114
|
+
if (!existsSync15(mapPath) || !existsSync15(repoProjects)) {
|
|
1952
2115
|
const text = "no path-map or repo projects dir; skipping session remap";
|
|
1953
2116
|
emitPreview(opts.onPreview, { kind: "note", text }, text);
|
|
1954
2117
|
return { unmapped: 0, pulled, wouldPull };
|
|
1955
2118
|
}
|
|
1956
2119
|
const map = readJson(mapPath);
|
|
1957
|
-
const localProjects =
|
|
2120
|
+
const localProjects = join19(CLAUDE_HOME, "projects");
|
|
1958
2121
|
if (!dryRun) mkdirSync3(localProjects, { recursive: true });
|
|
1959
2122
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
1960
2123
|
assertSafeLogical(logical);
|
|
@@ -1963,9 +2126,9 @@ function remapPull(ts, opts = {}) {
|
|
|
1963
2126
|
unmapped++;
|
|
1964
2127
|
continue;
|
|
1965
2128
|
}
|
|
1966
|
-
const src =
|
|
1967
|
-
if (!
|
|
1968
|
-
const dst =
|
|
2129
|
+
const src = join19(repoProjects, logical);
|
|
2130
|
+
if (!existsSync15(src)) continue;
|
|
2131
|
+
const dst = join19(localProjects, encodePath(localPath));
|
|
1969
2132
|
if (dryRun) {
|
|
1970
2133
|
wouldPull.push(logical);
|
|
1971
2134
|
emitPreview(
|
|
@@ -2010,16 +2173,16 @@ function remapPush(ts, opts = {}) {
|
|
|
2010
2173
|
let unmapped = 0;
|
|
2011
2174
|
const pushed = [];
|
|
2012
2175
|
const wouldPush = [];
|
|
2013
|
-
const mapPath =
|
|
2014
|
-
if (!
|
|
2176
|
+
const mapPath = join19(REPO_HOME, "path-map.json");
|
|
2177
|
+
if (!existsSync15(mapPath)) {
|
|
2015
2178
|
log("no path-map.json; skipping session export");
|
|
2016
2179
|
return { unmapped: 0, collisions: 0, pushed, wouldPush };
|
|
2017
2180
|
}
|
|
2018
2181
|
const map = readJson(mapPath);
|
|
2019
|
-
const localProjects =
|
|
2020
|
-
const repoProjects =
|
|
2182
|
+
const localProjects = join19(CLAUDE_HOME, "projects");
|
|
2183
|
+
const repoProjects = join19(REPO_HOME, "shared", "projects");
|
|
2021
2184
|
const reverse = buildReverseMap(map);
|
|
2022
|
-
if (!
|
|
2185
|
+
if (!existsSync15(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
|
|
2023
2186
|
if (!dryRun) mkdirSync3(repoProjects, { recursive: true });
|
|
2024
2187
|
for (const dir of readdirSync6(localProjects)) {
|
|
2025
2188
|
const logical = reverse.get(dir);
|
|
@@ -2027,13 +2190,13 @@ function remapPush(ts, opts = {}) {
|
|
|
2027
2190
|
unmapped++;
|
|
2028
2191
|
continue;
|
|
2029
2192
|
}
|
|
2030
|
-
const repoDst =
|
|
2193
|
+
const repoDst = join19(repoProjects, logical);
|
|
2031
2194
|
if (dryRun) {
|
|
2032
2195
|
wouldPush.push(logical);
|
|
2033
2196
|
continue;
|
|
2034
2197
|
}
|
|
2035
2198
|
backupRepoWrite(repoDst, ts, REPO_HOME);
|
|
2036
|
-
copyDirJsonlOnly(
|
|
2199
|
+
copyDirJsonlOnly(join19(localProjects, dir), repoDst);
|
|
2037
2200
|
pushed.push(logical);
|
|
2038
2201
|
}
|
|
2039
2202
|
return { unmapped, collisions: 0, pushed, wouldPush };
|
|
@@ -2045,8 +2208,8 @@ init_utils_json();
|
|
|
2045
2208
|
function buildScanTree(tmpRoot) {
|
|
2046
2209
|
const logicalToEncoded = /* @__PURE__ */ new Map();
|
|
2047
2210
|
let staged = 0;
|
|
2048
|
-
const mapPath =
|
|
2049
|
-
if (!
|
|
2211
|
+
const mapPath = join20(REPO_HOME, "path-map.json");
|
|
2212
|
+
if (!existsSync16(mapPath)) return { logicalToEncoded, staged, malformed: false };
|
|
2050
2213
|
let map;
|
|
2051
2214
|
try {
|
|
2052
2215
|
map = readJson(mapPath);
|
|
@@ -2063,12 +2226,12 @@ function buildScanTree(tmpRoot) {
|
|
|
2063
2226
|
if (!p || p === "TBD") continue;
|
|
2064
2227
|
reverse.set(encodePath(p), logical);
|
|
2065
2228
|
}
|
|
2066
|
-
const localProjects =
|
|
2067
|
-
if (!
|
|
2229
|
+
const localProjects = join20(CLAUDE_HOME, "projects");
|
|
2230
|
+
if (!existsSync16(localProjects)) return { logicalToEncoded, staged, malformed: false };
|
|
2068
2231
|
for (const dir of readdirSync7(localProjects)) {
|
|
2069
2232
|
const logical = reverse.get(dir);
|
|
2070
2233
|
if (!logical) continue;
|
|
2071
|
-
copyDirJsonlOnly(
|
|
2234
|
+
copyDirJsonlOnly(join20(localProjects, dir), join20(tmpRoot, "shared", "projects", logical));
|
|
2072
2235
|
logicalToEncoded.set(logical, dir);
|
|
2073
2236
|
staged++;
|
|
2074
2237
|
}
|
|
@@ -2099,11 +2262,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
|
|
|
2099
2262
|
}
|
|
2100
2263
|
function reportCheckShared(section2, gitleaksReady) {
|
|
2101
2264
|
if (!ensureGitleaksReady(section2, gitleaksReady)) return;
|
|
2102
|
-
const cacheDir =
|
|
2265
|
+
const cacheDir = join20(homedir4(), ".cache", "claude-nomad");
|
|
2103
2266
|
mkdirSync4(cacheDir, { recursive: true });
|
|
2104
2267
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
2105
|
-
const reportPath =
|
|
2106
|
-
const tmpRoot =
|
|
2268
|
+
const reportPath = join20(cacheDir, `check-shared-${stamp}.json`);
|
|
2269
|
+
const tmpRoot = join20(cacheDir, `check-shared-tree-${stamp}`);
|
|
2107
2270
|
try {
|
|
2108
2271
|
const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
|
|
2109
2272
|
if (malformed) {
|
|
@@ -2117,15 +2280,15 @@ function reportCheckShared(section2, gitleaksReady) {
|
|
|
2117
2280
|
}
|
|
2118
2281
|
scanAndReport(section2, tmpRoot, staged, logicalToEncoded);
|
|
2119
2282
|
} finally {
|
|
2120
|
-
|
|
2121
|
-
|
|
2283
|
+
rmSync7(reportPath, { force: true });
|
|
2284
|
+
rmSync7(tmpRoot, { recursive: true, force: true });
|
|
2122
2285
|
}
|
|
2123
2286
|
}
|
|
2124
2287
|
|
|
2125
2288
|
// src/commands.doctor.checks.hooks.scope.ts
|
|
2126
2289
|
init_color();
|
|
2127
|
-
import { existsSync as
|
|
2128
|
-
import { dirname as dirname2, extname, join as
|
|
2290
|
+
import { existsSync as existsSync17, readFileSync as readFileSync5, readdirSync as readdirSync8, realpathSync as realpathSync2 } from "node:fs";
|
|
2291
|
+
import { dirname as dirname2, extname, join as join21 } from "node:path";
|
|
2129
2292
|
init_config();
|
|
2130
2293
|
function typeFromPackageJson(pkgPath) {
|
|
2131
2294
|
try {
|
|
@@ -2141,14 +2304,14 @@ function effectiveType(hookPath) {
|
|
|
2141
2304
|
if (ext === ".cjs") return "cjs";
|
|
2142
2305
|
let real;
|
|
2143
2306
|
try {
|
|
2144
|
-
real =
|
|
2307
|
+
real = realpathSync2(hookPath);
|
|
2145
2308
|
} catch {
|
|
2146
2309
|
return null;
|
|
2147
2310
|
}
|
|
2148
2311
|
let dir = dirname2(real);
|
|
2149
2312
|
for (; ; ) {
|
|
2150
|
-
const pkg =
|
|
2151
|
-
if (
|
|
2313
|
+
const pkg = join21(dir, "package.json");
|
|
2314
|
+
if (existsSync17(pkg)) return typeFromPackageJson(pkg);
|
|
2152
2315
|
const parent = dirname2(dir);
|
|
2153
2316
|
if (parent === dir) return "cjs";
|
|
2154
2317
|
dir = parent;
|
|
@@ -2190,15 +2353,15 @@ function safeRead(path) {
|
|
|
2190
2353
|
}
|
|
2191
2354
|
}
|
|
2192
2355
|
function reportHookScopeCheck(section2) {
|
|
2193
|
-
const hooksDir =
|
|
2194
|
-
if (!
|
|
2356
|
+
const hooksDir = join21(CLAUDE_HOME, "hooks");
|
|
2357
|
+
if (!existsSync17(hooksDir)) {
|
|
2195
2358
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
|
|
2196
2359
|
return;
|
|
2197
2360
|
}
|
|
2198
2361
|
let anyWarn = false;
|
|
2199
2362
|
for (const name of safeReaddir2(hooksDir)) {
|
|
2200
2363
|
if (extname(name) !== ".js") continue;
|
|
2201
|
-
const abs =
|
|
2364
|
+
const abs = join21(hooksDir, name);
|
|
2202
2365
|
const eff = effectiveType(abs);
|
|
2203
2366
|
if (eff === null) continue;
|
|
2204
2367
|
const src = safeRead(abs);
|
|
@@ -2218,8 +2381,8 @@ function reportHookScopeCheck(section2) {
|
|
|
2218
2381
|
|
|
2219
2382
|
// src/commands.doctor.checks.hooks.ts
|
|
2220
2383
|
init_color();
|
|
2221
|
-
import { existsSync as
|
|
2222
|
-
import { join as
|
|
2384
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
2385
|
+
import { join as join22 } from "node:path";
|
|
2223
2386
|
init_config();
|
|
2224
2387
|
function expandHome(token) {
|
|
2225
2388
|
return token.replace(/^\$\{HOME\}/, HOME).replace(/^\$HOME/, HOME).replace(/^~/, HOME);
|
|
@@ -2257,7 +2420,7 @@ function checkEventGroups(section2, event, groups) {
|
|
|
2257
2420
|
for (const group of groups) {
|
|
2258
2421
|
for (const cmd of commandsFromGroup(group)) {
|
|
2259
2422
|
for (const resolved of claudePathsIn(cmd)) {
|
|
2260
|
-
if (
|
|
2423
|
+
if (existsSync18(resolved)) continue;
|
|
2261
2424
|
addItem(
|
|
2262
2425
|
section2,
|
|
2263
2426
|
`${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
|
|
@@ -2270,8 +2433,8 @@ function checkEventGroups(section2, event, groups) {
|
|
|
2270
2433
|
return anyFail;
|
|
2271
2434
|
}
|
|
2272
2435
|
function reportHooksTargetCheck(section2) {
|
|
2273
|
-
const settingsPath =
|
|
2274
|
-
if (!
|
|
2436
|
+
const settingsPath = join22(CLAUDE_HOME, "settings.json");
|
|
2437
|
+
if (!existsSync18(settingsPath)) {
|
|
2275
2438
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
|
|
2276
2439
|
return;
|
|
2277
2440
|
}
|
|
@@ -2294,12 +2457,12 @@ function reportHooksTargetCheck(section2) {
|
|
|
2294
2457
|
|
|
2295
2458
|
// src/commands.doctor.checks.hooks.preserve-symlinks.ts
|
|
2296
2459
|
init_color();
|
|
2297
|
-
import { existsSync as
|
|
2298
|
-
import { join as
|
|
2460
|
+
import { existsSync as existsSync20, readFileSync as readFileSync6 } from "node:fs";
|
|
2461
|
+
import { join as join24 } from "node:path";
|
|
2299
2462
|
|
|
2300
2463
|
// src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
|
|
2301
|
-
import { closeSync as closeSync2, existsSync as
|
|
2302
|
-
import { dirname as dirname3, join as
|
|
2464
|
+
import { closeSync as closeSync2, existsSync as existsSync19, openSync as openSync2, readSync, realpathSync as realpathSync3 } from "node:fs";
|
|
2465
|
+
import { dirname as dirname3, join as join23, resolve as resolve2 } from "node:path";
|
|
2303
2466
|
function suppressedRanges(src) {
|
|
2304
2467
|
const ranges = [];
|
|
2305
2468
|
const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
|
|
@@ -2331,19 +2494,19 @@ function topRelativeSpecifiers(src) {
|
|
|
2331
2494
|
}
|
|
2332
2495
|
function specifierIsMissing(specifier, baseDir) {
|
|
2333
2496
|
const base = resolve2(baseDir, specifier);
|
|
2334
|
-
if (
|
|
2497
|
+
if (existsSync19(base)) return false;
|
|
2335
2498
|
for (const ext of [".js", ".cjs", ".mjs"]) {
|
|
2336
|
-
if (
|
|
2499
|
+
if (existsSync19(base + ext)) return false;
|
|
2337
2500
|
}
|
|
2338
2501
|
for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
|
|
2339
|
-
if (
|
|
2502
|
+
if (existsSync19(join23(base, idx))) return false;
|
|
2340
2503
|
}
|
|
2341
2504
|
return true;
|
|
2342
2505
|
}
|
|
2343
2506
|
function relativeRequireTargetsBroken(scriptPath) {
|
|
2344
2507
|
let realPath;
|
|
2345
2508
|
try {
|
|
2346
|
-
realPath =
|
|
2509
|
+
realPath = realpathSync3(scriptPath);
|
|
2347
2510
|
} catch {
|
|
2348
2511
|
return false;
|
|
2349
2512
|
}
|
|
@@ -2390,8 +2553,8 @@ function commandTokens(command) {
|
|
|
2390
2553
|
return tokens;
|
|
2391
2554
|
}
|
|
2392
2555
|
function readPathMapSafe() {
|
|
2393
|
-
const mapPath =
|
|
2394
|
-
if (!
|
|
2556
|
+
const mapPath = join24(REPO_HOME, "path-map.json");
|
|
2557
|
+
if (!existsSync20(mapPath)) return { projects: {} };
|
|
2395
2558
|
try {
|
|
2396
2559
|
return JSON.parse(readFileSync6(mapPath, "utf8"));
|
|
2397
2560
|
} catch {
|
|
@@ -2464,8 +2627,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
|
|
|
2464
2627
|
return anyWarn;
|
|
2465
2628
|
}
|
|
2466
2629
|
function reportPreserveSymlinksCheck(section2) {
|
|
2467
|
-
const settingsPath =
|
|
2468
|
-
if (!
|
|
2630
|
+
const settingsPath = join24(CLAUDE_HOME, "settings.json");
|
|
2631
|
+
if (!existsSync20(settingsPath)) {
|
|
2469
2632
|
addItem(
|
|
2470
2633
|
section2,
|
|
2471
2634
|
`${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
|
|
@@ -2611,8 +2774,8 @@ function reportNodeEngineCheck(section2) {
|
|
|
2611
2774
|
// src/commands.doctor.gitleaks-version.ts
|
|
2612
2775
|
init_color();
|
|
2613
2776
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
2614
|
-
import { existsSync as
|
|
2615
|
-
import { join as
|
|
2777
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
2778
|
+
import { join as join25 } from "node:path";
|
|
2616
2779
|
init_config();
|
|
2617
2780
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
2618
2781
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -2621,7 +2784,7 @@ function majorMinorOf(value) {
|
|
|
2621
2784
|
return m === null ? null : [m[1], m[2]];
|
|
2622
2785
|
}
|
|
2623
2786
|
function readGitleaksVersion(run, tomlExists) {
|
|
2624
|
-
const tomlPath =
|
|
2787
|
+
const tomlPath = join25(REPO_HOME, ".gitleaks.toml");
|
|
2625
2788
|
const args = ["version"];
|
|
2626
2789
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
2627
2790
|
try {
|
|
@@ -2633,7 +2796,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
2633
2796
|
return null;
|
|
2634
2797
|
}
|
|
2635
2798
|
}
|
|
2636
|
-
function reportGitleaksVersionCheck(section2, run = execFileSync7, tomlExists =
|
|
2799
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync7, tomlExists = existsSync21) {
|
|
2637
2800
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
2638
2801
|
if (raw === null) return;
|
|
2639
2802
|
const local = majorMinorOf(raw);
|
|
@@ -2833,8 +2996,8 @@ function cmdDoctor(opts = {}) {
|
|
|
2833
2996
|
reportHostAndPaths(host);
|
|
2834
2997
|
reportRepoState(host);
|
|
2835
2998
|
const links = section("Shared links");
|
|
2836
|
-
const mapPath =
|
|
2837
|
-
const rawMap =
|
|
2999
|
+
const mapPath = join26(REPO_HOME, "path-map.json");
|
|
3000
|
+
const rawMap = existsSync22(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
2838
3001
|
const map = rawMap ?? { projects: {} };
|
|
2839
3002
|
reportSharedLinks(links, map);
|
|
2840
3003
|
const hooksScan = section("Hook targets");
|
|
@@ -2888,8 +3051,8 @@ function cmdDoctor(opts = {}) {
|
|
|
2888
3051
|
// src/commands.drop-session.ts
|
|
2889
3052
|
init_config();
|
|
2890
3053
|
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
2891
|
-
import { existsSync as
|
|
2892
|
-
import { join as
|
|
3054
|
+
import { existsSync as existsSync24, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
|
|
3055
|
+
import { join as join29, relative as relative4 } from "node:path";
|
|
2893
3056
|
|
|
2894
3057
|
// src/commands.drop-session.git.ts
|
|
2895
3058
|
init_config();
|
|
@@ -2932,8 +3095,8 @@ function isInIndex(rel) {
|
|
|
2932
3095
|
init_config();
|
|
2933
3096
|
init_utils();
|
|
2934
3097
|
init_utils_json();
|
|
2935
|
-
import { existsSync as
|
|
2936
|
-
import { join as
|
|
3098
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
3099
|
+
import { join as join27 } from "node:path";
|
|
2937
3100
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
2938
3101
|
function reportScrubHint(id, matches) {
|
|
2939
3102
|
const live = resolveLiveTranscript(id, matches);
|
|
@@ -2949,16 +3112,16 @@ function reportScrubHint(id, matches) {
|
|
|
2949
3112
|
}
|
|
2950
3113
|
function resolveLiveTranscript(id, matches) {
|
|
2951
3114
|
try {
|
|
2952
|
-
const mapPath =
|
|
2953
|
-
if (!
|
|
3115
|
+
const mapPath = join27(REPO_HOME, "path-map.json");
|
|
3116
|
+
if (!existsSync23(mapPath)) return null;
|
|
2954
3117
|
const projects = readJson(mapPath).projects;
|
|
2955
3118
|
for (const rel of matches) {
|
|
2956
3119
|
const logical = SHARED_PROJECT_LOGICAL.exec(rel)?.[1];
|
|
2957
3120
|
if (logical === void 0) continue;
|
|
2958
3121
|
const abs = projects[logical]?.[HOST];
|
|
2959
3122
|
if (abs === void 0) continue;
|
|
2960
|
-
const live =
|
|
2961
|
-
if (
|
|
3123
|
+
const live = join27(CLAUDE_HOME, "projects", encodePath(abs), `${id}.jsonl`);
|
|
3124
|
+
if (existsSync23(live)) return live;
|
|
2962
3125
|
}
|
|
2963
3126
|
return null;
|
|
2964
3127
|
} catch {
|
|
@@ -2973,8 +3136,8 @@ init_utils();
|
|
|
2973
3136
|
init_config();
|
|
2974
3137
|
init_utils();
|
|
2975
3138
|
import { closeSync as closeSync3, mkdirSync as mkdirSync5, openSync as openSync3, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2976
|
-
import { dirname as dirname4, join as
|
|
2977
|
-
var LOCK_PATH =
|
|
3139
|
+
import { dirname as dirname4, join as join28 } from "node:path";
|
|
3140
|
+
var LOCK_PATH = join28(HOME, ".cache", "claude-nomad", "nomad.lock");
|
|
2978
3141
|
function acquireLock(verb) {
|
|
2979
3142
|
mkdirSync5(dirname4(LOCK_PATH), { recursive: true });
|
|
2980
3143
|
try {
|
|
@@ -3084,12 +3247,12 @@ function cmdDropSession(id) {
|
|
|
3084
3247
|
fail(`invalid session id: ${id}`);
|
|
3085
3248
|
process.exit(1);
|
|
3086
3249
|
}
|
|
3087
|
-
if (!
|
|
3250
|
+
if (!existsSync24(REPO_HOME)) die(`repo not cloned at ${REPO_HOME}`);
|
|
3088
3251
|
const handle = acquireLock("drop-session");
|
|
3089
3252
|
if (handle === null) process.exit(0);
|
|
3090
3253
|
try {
|
|
3091
|
-
const repoProjects =
|
|
3092
|
-
if (!
|
|
3254
|
+
const repoProjects = join29(REPO_HOME, "shared", "projects");
|
|
3255
|
+
if (!existsSync24(repoProjects)) {
|
|
3093
3256
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
3094
3257
|
}
|
|
3095
3258
|
const matches = collectMatches(repoProjects, id);
|
|
@@ -3111,12 +3274,12 @@ function cmdDropSession(id) {
|
|
|
3111
3274
|
function collectMatches(repoProjects, id) {
|
|
3112
3275
|
const matches = [];
|
|
3113
3276
|
for (const logical of readdirSync9(repoProjects)) {
|
|
3114
|
-
const candidate =
|
|
3115
|
-
if (
|
|
3277
|
+
const candidate = join29(repoProjects, logical, `${id}.jsonl`);
|
|
3278
|
+
if (existsSync24(candidate)) {
|
|
3116
3279
|
matches.push(relative4(REPO_HOME, candidate));
|
|
3117
3280
|
}
|
|
3118
|
-
const dir =
|
|
3119
|
-
if (
|
|
3281
|
+
const dir = join29(repoProjects, logical, id);
|
|
3282
|
+
if (existsSync24(dir) && statSync5(dir).isDirectory()) {
|
|
3120
3283
|
const dirRel = relative4(REPO_HOME, dir);
|
|
3121
3284
|
const staged = expandStagedDir(dirRel);
|
|
3122
3285
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -3152,20 +3315,20 @@ function unstageOne(rel) {
|
|
|
3152
3315
|
|
|
3153
3316
|
// src/commands.redact.ts
|
|
3154
3317
|
init_config();
|
|
3155
|
-
import { existsSync as
|
|
3156
|
-
import { dirname as dirname5, join as
|
|
3318
|
+
import { existsSync as existsSync26, statSync as statSync7 } from "node:fs";
|
|
3319
|
+
import { dirname as dirname5, join as join31 } from "node:path";
|
|
3157
3320
|
|
|
3158
3321
|
// src/commands.redact.subtree.ts
|
|
3159
|
-
import { existsSync as
|
|
3160
|
-
import { join as
|
|
3322
|
+
import { existsSync as existsSync25, lstatSync as lstatSync7, readFileSync as readFileSync10, readdirSync as readdirSync10, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3323
|
+
import { join as join30 } from "node:path";
|
|
3161
3324
|
init_utils_fs();
|
|
3162
3325
|
function collectFiles(dir, out) {
|
|
3163
|
-
if (!
|
|
3164
|
-
const st =
|
|
3326
|
+
if (!existsSync25(dir)) return;
|
|
3327
|
+
const st = lstatSync7(dir);
|
|
3165
3328
|
if (!st.isDirectory()) return;
|
|
3166
3329
|
for (const entry of readdirSync10(dir)) {
|
|
3167
|
-
const abs =
|
|
3168
|
-
const lst =
|
|
3330
|
+
const abs = join30(dir, entry);
|
|
3331
|
+
const lst = lstatSync7(abs);
|
|
3169
3332
|
if (lst.isSymbolicLink()) continue;
|
|
3170
3333
|
if (lst.isDirectory()) {
|
|
3171
3334
|
collectFiles(abs, out);
|
|
@@ -3214,14 +3377,14 @@ init_utils_json();
|
|
|
3214
3377
|
init_utils();
|
|
3215
3378
|
function resolveLiveTranscript2(id) {
|
|
3216
3379
|
try {
|
|
3217
|
-
const mapPath =
|
|
3218
|
-
if (!
|
|
3380
|
+
const mapPath = join31(REPO_HOME, "path-map.json");
|
|
3381
|
+
if (!existsSync26(mapPath)) return null;
|
|
3219
3382
|
const projects = readJson(mapPath).projects;
|
|
3220
3383
|
for (const hostMap of Object.values(projects)) {
|
|
3221
3384
|
const abs = hostMap[HOST];
|
|
3222
3385
|
if (abs === void 0) continue;
|
|
3223
|
-
const live =
|
|
3224
|
-
if (
|
|
3386
|
+
const live = join31(CLAUDE_HOME, "projects", encodePath(abs), `${id}.jsonl`);
|
|
3387
|
+
if (existsSync26(live)) return live;
|
|
3225
3388
|
}
|
|
3226
3389
|
return null;
|
|
3227
3390
|
} catch {
|
|
@@ -3239,17 +3402,17 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
3239
3402
|
fail(`invalid session id: ${id}`);
|
|
3240
3403
|
process.exit(1);
|
|
3241
3404
|
}
|
|
3242
|
-
if (!
|
|
3405
|
+
if (!existsSync26(REPO_HOME)) die(`repo not cloned at ${REPO_HOME}`);
|
|
3243
3406
|
const handle = acquireLock("redact");
|
|
3244
3407
|
if (handle === null) process.exit(0);
|
|
3245
3408
|
try {
|
|
3246
3409
|
const localPath = resolveLiveTranscript2(id);
|
|
3247
|
-
if (localPath === null || !
|
|
3410
|
+
if (localPath === null || !existsSync26(localPath)) {
|
|
3248
3411
|
fail(`could not resolve local transcript for session ${id} on this host`);
|
|
3249
3412
|
process.exitCode = 1;
|
|
3250
3413
|
return;
|
|
3251
3414
|
}
|
|
3252
|
-
const sessionDir =
|
|
3415
|
+
const sessionDir = join31(dirname5(localPath), id);
|
|
3253
3416
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
3254
3417
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
|
|
3255
3418
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -3302,8 +3465,8 @@ ${lines}`);
|
|
|
3302
3465
|
}
|
|
3303
3466
|
|
|
3304
3467
|
// src/commands.pull.ts
|
|
3305
|
-
import { existsSync as
|
|
3306
|
-
import { join as
|
|
3468
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync8 } from "node:fs";
|
|
3469
|
+
import { join as join40 } from "node:path";
|
|
3307
3470
|
|
|
3308
3471
|
// src/commands.push.sections.ts
|
|
3309
3472
|
init_color();
|
|
@@ -3391,8 +3554,8 @@ init_config();
|
|
|
3391
3554
|
|
|
3392
3555
|
// src/extras-sync.ts
|
|
3393
3556
|
init_config();
|
|
3394
|
-
import { existsSync as
|
|
3395
|
-
import { join as
|
|
3557
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
3558
|
+
import { join as join34 } from "node:path";
|
|
3396
3559
|
|
|
3397
3560
|
// src/extras-sync.diff.ts
|
|
3398
3561
|
init_utils();
|
|
@@ -3431,8 +3594,8 @@ function listDivergingFiles(a, b) {
|
|
|
3431
3594
|
|
|
3432
3595
|
// src/extras-sync.core.ts
|
|
3433
3596
|
init_config();
|
|
3434
|
-
import { cpSync as
|
|
3435
|
-
import { join as
|
|
3597
|
+
import { cpSync as cpSync5, existsSync as existsSync27, rmSync as rmSync8 } from "node:fs";
|
|
3598
|
+
import { join as join32 } from "node:path";
|
|
3436
3599
|
|
|
3437
3600
|
// src/extras-sync.guards.ts
|
|
3438
3601
|
init_utils();
|
|
@@ -3455,9 +3618,9 @@ function assertSafeLocalRoot(localRoot, logical) {
|
|
|
3455
3618
|
init_utils();
|
|
3456
3619
|
init_utils_json();
|
|
3457
3620
|
function loadValidatedExtras(opts) {
|
|
3458
|
-
const mapPath =
|
|
3459
|
-
const repoExtras =
|
|
3460
|
-
if (!
|
|
3621
|
+
const mapPath = join32(REPO_HOME, "path-map.json");
|
|
3622
|
+
const repoExtras = join32(REPO_HOME, "shared", "extras");
|
|
3623
|
+
if (!existsSync27(mapPath) || opts.requireRepoExtras === true && !existsSync27(repoExtras)) {
|
|
3461
3624
|
if (opts.missingMsg !== void 0) log(opts.missingMsg);
|
|
3462
3625
|
return null;
|
|
3463
3626
|
}
|
|
@@ -3489,8 +3652,8 @@ function* eachExtrasTarget(v, counts) {
|
|
|
3489
3652
|
}
|
|
3490
3653
|
}
|
|
3491
3654
|
function copyExtras(src, dst) {
|
|
3492
|
-
|
|
3493
|
-
|
|
3655
|
+
rmSync8(dst, { recursive: true, force: true });
|
|
3656
|
+
cpSync5(src, dst, { recursive: true, force: true, verbatimSymlinks: true });
|
|
3494
3657
|
}
|
|
3495
3658
|
|
|
3496
3659
|
// src/extras-sync.ts
|
|
@@ -3499,8 +3662,8 @@ init_utils_json();
|
|
|
3499
3662
|
|
|
3500
3663
|
// src/extras-sync.remap.ts
|
|
3501
3664
|
init_config();
|
|
3502
|
-
import { existsSync as
|
|
3503
|
-
import { join as
|
|
3665
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3666
|
+
import { join as join33 } from "node:path";
|
|
3504
3667
|
init_utils_fs();
|
|
3505
3668
|
function runExtrasOp(v, dryRun, paths, backup) {
|
|
3506
3669
|
const counts = { unmapped: 0, skipped: 0 };
|
|
@@ -3508,7 +3671,7 @@ function runExtrasOp(v, dryRun, paths, backup) {
|
|
|
3508
3671
|
const would = [];
|
|
3509
3672
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
3510
3673
|
const { src, dst } = paths(t);
|
|
3511
|
-
if (!
|
|
3674
|
+
if (!existsSync28(src)) continue;
|
|
3512
3675
|
const item = `${t.logical}/${t.dirname}`;
|
|
3513
3676
|
if (dryRun) {
|
|
3514
3677
|
would.push(item);
|
|
@@ -3524,14 +3687,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
3524
3687
|
const dryRun = opts.dryRun === true;
|
|
3525
3688
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
3526
3689
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
3527
|
-
const repoExtras =
|
|
3690
|
+
const repoExtras = join33(REPO_HOME, "shared", "extras");
|
|
3528
3691
|
if (!dryRun) mkdirSync6(repoExtras, { recursive: true });
|
|
3529
3692
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
3530
3693
|
v,
|
|
3531
3694
|
dryRun,
|
|
3532
3695
|
({ localRoot, logical, dirname: dirname7 }) => ({
|
|
3533
|
-
src:
|
|
3534
|
-
dst:
|
|
3696
|
+
src: join33(localRoot, dirname7),
|
|
3697
|
+
dst: join33(repoExtras, logical, dirname7)
|
|
3535
3698
|
}),
|
|
3536
3699
|
(dst) => backupRepoWrite(dst, ts, REPO_HOME)
|
|
3537
3700
|
);
|
|
@@ -3544,13 +3707,13 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
3544
3707
|
missingMsg: "no path-map or repo extras dir; skipping extras remap"
|
|
3545
3708
|
});
|
|
3546
3709
|
if (v === null) return { unmapped: 0, skipped: 0, pulled: [], wouldPull: [] };
|
|
3547
|
-
const repoExtras =
|
|
3710
|
+
const repoExtras = join33(REPO_HOME, "shared", "extras");
|
|
3548
3711
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
3549
3712
|
v,
|
|
3550
3713
|
dryRun,
|
|
3551
3714
|
({ localRoot, logical, dirname: dirname7 }) => ({
|
|
3552
|
-
src:
|
|
3553
|
-
dst:
|
|
3715
|
+
src: join33(repoExtras, logical, dirname7),
|
|
3716
|
+
dst: join33(localRoot, dirname7)
|
|
3554
3717
|
}),
|
|
3555
3718
|
// Snapshot the host-side dst BEFORE copyExtras clobbers it. Anchor on
|
|
3556
3719
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -3564,14 +3727,14 @@ function divergenceCheckExtras(ts) {
|
|
|
3564
3727
|
const v = loadValidatedExtras({});
|
|
3565
3728
|
if (v === null) return;
|
|
3566
3729
|
const counts = { unmapped: 0, skipped: 0 };
|
|
3567
|
-
const backupRoot =
|
|
3730
|
+
const backupRoot = join34(BACKUP_BASE, ts, "extras");
|
|
3568
3731
|
for (const { logical, localRoot, dirname: dirname7 } of eachExtrasTarget(v, counts)) {
|
|
3569
|
-
const local =
|
|
3570
|
-
const repo =
|
|
3571
|
-
if (!
|
|
3732
|
+
const local = join34(localRoot, dirname7);
|
|
3733
|
+
const repo = join34(REPO_HOME, "shared", "extras", logical, dirname7);
|
|
3734
|
+
if (!existsSync29(local) || !existsSync29(repo)) continue;
|
|
3572
3735
|
const diff = listDivergingFiles(local, repo);
|
|
3573
3736
|
if (diff.length === 0) continue;
|
|
3574
|
-
const projectBackupRoot =
|
|
3737
|
+
const projectBackupRoot = join34(backupRoot, encodePath(localRoot));
|
|
3575
3738
|
warn(
|
|
3576
3739
|
`local ${dirname7} for ${logical} diverges from origin in ${diff.length} file(s); next remapExtrasPull will overwrite them (backups at ${projectBackupRoot}/)`
|
|
3577
3740
|
);
|
|
@@ -3584,8 +3747,8 @@ init_config();
|
|
|
3584
3747
|
init_utils();
|
|
3585
3748
|
init_utils_fs();
|
|
3586
3749
|
init_utils_json();
|
|
3587
|
-
import { existsSync as
|
|
3588
|
-
import { join as
|
|
3750
|
+
import { existsSync as existsSync30, lstatSync as lstatSync8, rmSync as rmSync9 } from "node:fs";
|
|
3751
|
+
import { join as join35 } from "node:path";
|
|
3589
3752
|
function emitAutoMove(onPreview, linkPath, ts, name) {
|
|
3590
3753
|
if (onPreview) {
|
|
3591
3754
|
onPreview({ kind: "auto-move", from: linkPath, to: `backup/${ts}/${name}` });
|
|
@@ -3604,41 +3767,41 @@ function applySharedLinks(ts, map, opts = {}) {
|
|
|
3604
3767
|
const dryRun = opts.dryRun === true;
|
|
3605
3768
|
const linkNames = allSharedLinks(map);
|
|
3606
3769
|
for (const name of linkNames) {
|
|
3607
|
-
const linkPath =
|
|
3608
|
-
const target =
|
|
3609
|
-
if (!
|
|
3610
|
-
if (
|
|
3611
|
-
if (!
|
|
3770
|
+
const linkPath = join35(CLAUDE_HOME, name);
|
|
3771
|
+
const target = join35(REPO_HOME, "shared", name);
|
|
3772
|
+
if (!existsSync30(linkPath)) continue;
|
|
3773
|
+
if (lstatSync8(linkPath).isSymbolicLink()) continue;
|
|
3774
|
+
if (!existsSync30(target)) continue;
|
|
3612
3775
|
if (dryRun) {
|
|
3613
3776
|
emitAutoMove(opts.onPreview, linkPath, ts, name);
|
|
3614
3777
|
continue;
|
|
3615
3778
|
}
|
|
3616
3779
|
backupBeforeWrite(linkPath, ts);
|
|
3617
|
-
|
|
3780
|
+
rmSync9(linkPath, { recursive: true, force: true });
|
|
3618
3781
|
}
|
|
3619
3782
|
for (const name of linkNames) {
|
|
3620
|
-
const target =
|
|
3621
|
-
if (!
|
|
3783
|
+
const target = join35(REPO_HOME, "shared", name);
|
|
3784
|
+
if (!existsSync30(target)) continue;
|
|
3622
3785
|
if (dryRun) {
|
|
3623
|
-
emitCreate(opts.onPreview,
|
|
3786
|
+
emitCreate(opts.onPreview, join35(CLAUDE_HOME, name), target);
|
|
3624
3787
|
continue;
|
|
3625
3788
|
}
|
|
3626
|
-
ensureSymlink(
|
|
3789
|
+
ensureSymlink(join35(CLAUDE_HOME, name), target);
|
|
3627
3790
|
}
|
|
3628
3791
|
}
|
|
3629
3792
|
function regenerateSettings(ts, opts = {}) {
|
|
3630
3793
|
const dryRun = opts.dryRun === true;
|
|
3631
|
-
const basePath =
|
|
3632
|
-
const hostPath =
|
|
3633
|
-
if (!
|
|
3794
|
+
const basePath = join35(REPO_HOME, "shared", "settings.base.json");
|
|
3795
|
+
const hostPath = join35(REPO_HOME, "hosts", `${HOST}.json`);
|
|
3796
|
+
if (!existsSync30(basePath)) {
|
|
3634
3797
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
3635
3798
|
}
|
|
3636
3799
|
const base = readJson(basePath);
|
|
3637
|
-
const hasOverrides =
|
|
3800
|
+
const hasOverrides = existsSync30(hostPath);
|
|
3638
3801
|
const overrides = hasOverrides ? readJson(hostPath) : {};
|
|
3639
3802
|
const merged = deepMerge(base, overrides);
|
|
3640
|
-
const settingsPath =
|
|
3641
|
-
if (!hasOverrides &&
|
|
3803
|
+
const settingsPath = join35(CLAUDE_HOME, "settings.json");
|
|
3804
|
+
if (!hasOverrides && existsSync30(settingsPath)) {
|
|
3642
3805
|
try {
|
|
3643
3806
|
const existing = readJson(settingsPath);
|
|
3644
3807
|
const baseKeys = new Set(Object.keys(base));
|
|
@@ -3664,8 +3827,8 @@ function regenerateSettings(ts, opts = {}) {
|
|
|
3664
3827
|
|
|
3665
3828
|
// src/preview.ts
|
|
3666
3829
|
init_config();
|
|
3667
|
-
import { existsSync as
|
|
3668
|
-
import { join as
|
|
3830
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
3831
|
+
import { join as join36 } from "node:path";
|
|
3669
3832
|
|
|
3670
3833
|
// node_modules/diff/libesm/diff/base.js
|
|
3671
3834
|
var Diff = class {
|
|
@@ -3951,7 +4114,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
3951
4114
|
return lines.join("\n");
|
|
3952
4115
|
}
|
|
3953
4116
|
function readJsonOrNull(path) {
|
|
3954
|
-
if (!
|
|
4117
|
+
if (!existsSync31(path)) return null;
|
|
3955
4118
|
try {
|
|
3956
4119
|
return readJson(path);
|
|
3957
4120
|
} catch {
|
|
@@ -3965,12 +4128,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
3965
4128
|
}
|
|
3966
4129
|
const notes = [];
|
|
3967
4130
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
3968
|
-
if (hostOverrides === null &&
|
|
4131
|
+
if (hostOverrides === null && existsSync31(hostPath)) {
|
|
3969
4132
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
3970
4133
|
}
|
|
3971
4134
|
const merged = deepMerge(base, hostOverrides ?? {});
|
|
3972
4135
|
const current = readJsonOrNull(settingsPath);
|
|
3973
|
-
if (current === null &&
|
|
4136
|
+
if (current === null && existsSync31(settingsPath)) {
|
|
3974
4137
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
3975
4138
|
}
|
|
3976
4139
|
const rawEqual = JSON.stringify(current ?? {}, null, 2) === JSON.stringify(merged, null, 2);
|
|
@@ -4008,9 +4171,9 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
4008
4171
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
4009
4172
|
});
|
|
4010
4173
|
const settingsResult = previewSettings(
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4174
|
+
join36(REPO_HOME, "shared", "settings.base.json"),
|
|
4175
|
+
join36(REPO_HOME, "hosts", `${HOST}.json`),
|
|
4176
|
+
join36(CLAUDE_HOME, "settings.json")
|
|
4014
4177
|
);
|
|
4015
4178
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
4016
4179
|
const sessions = section("Sessions");
|
|
@@ -4026,21 +4189,21 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
4026
4189
|
|
|
4027
4190
|
// src/spinner.ts
|
|
4028
4191
|
init_color();
|
|
4029
|
-
import { existsSync as
|
|
4192
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
4030
4193
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
4031
4194
|
import { Worker } from "node:worker_threads";
|
|
4032
4195
|
|
|
4033
4196
|
// src/commands.push.recovery.ts
|
|
4034
4197
|
init_config();
|
|
4035
|
-
import { readFileSync as readFileSync11, rmSync as
|
|
4036
|
-
import { join as
|
|
4198
|
+
import { readFileSync as readFileSync11, rmSync as rmSync11, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4199
|
+
import { join as join39 } from "node:path";
|
|
4037
4200
|
import { createInterface } from "node:readline/promises";
|
|
4038
4201
|
|
|
4039
4202
|
// src/commands.push.recovery.redact.ts
|
|
4040
4203
|
init_config();
|
|
4041
4204
|
init_config_sharedDirs_guard();
|
|
4042
|
-
import { cpSync as
|
|
4043
|
-
import { dirname as dirname6, join as
|
|
4205
|
+
import { cpSync as cpSync6, existsSync as existsSync32, mkdirSync as mkdirSync7, statSync as statSync8 } from "node:fs";
|
|
4206
|
+
import { dirname as dirname6, join as join37, sep as sep3 } from "node:path";
|
|
4044
4207
|
init_push_gitleaks_scan();
|
|
4045
4208
|
init_utils_json();
|
|
4046
4209
|
init_utils();
|
|
@@ -4071,8 +4234,8 @@ function resolveStagedDir(localPath, map) {
|
|
|
4071
4234
|
assertSafeLogical(logical);
|
|
4072
4235
|
const abs = hostMap[HOST];
|
|
4073
4236
|
if (abs === void 0) continue;
|
|
4074
|
-
if (localPath.startsWith(
|
|
4075
|
-
return
|
|
4237
|
+
if (localPath.startsWith(join37(CLAUDE_HOME, "projects", encodePath(abs)) + sep3)) {
|
|
4238
|
+
return join37(REPO_HOME, "shared", "projects", logical);
|
|
4076
4239
|
}
|
|
4077
4240
|
}
|
|
4078
4241
|
return null;
|
|
@@ -4094,7 +4257,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4094
4257
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4095
4258
|
);
|
|
4096
4259
|
}
|
|
4097
|
-
const sessionDir =
|
|
4260
|
+
const sessionDir = join37(dirname6(localPath), sid);
|
|
4098
4261
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4099
4262
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4100
4263
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4128,25 +4291,25 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4128
4291
|
);
|
|
4129
4292
|
}
|
|
4130
4293
|
mkdirSync7(stagedProjectDir, { recursive: true });
|
|
4131
|
-
|
|
4132
|
-
if (
|
|
4133
|
-
|
|
4294
|
+
cpSync6(localPath, join37(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
4295
|
+
if (existsSync32(sessionDir)) {
|
|
4296
|
+
cpSync6(sessionDir, join37(stagedProjectDir, sid), { force: true, recursive: true });
|
|
4134
4297
|
}
|
|
4135
4298
|
return true;
|
|
4136
4299
|
}
|
|
4137
4300
|
|
|
4138
4301
|
// src/commands.push.recovery.drop.ts
|
|
4139
4302
|
init_config();
|
|
4140
|
-
import { rmSync as
|
|
4141
|
-
import { join as
|
|
4303
|
+
import { rmSync as rmSync10 } from "node:fs";
|
|
4304
|
+
import { join as join38 } from "node:path";
|
|
4142
4305
|
function dropSessionFromStaged(sid, map) {
|
|
4143
4306
|
const logicals = Object.keys(map.projects);
|
|
4144
4307
|
if (logicals.length === 0) return false;
|
|
4145
4308
|
for (const logical of logicals) {
|
|
4146
|
-
const jsonl =
|
|
4147
|
-
const dir =
|
|
4148
|
-
|
|
4149
|
-
|
|
4309
|
+
const jsonl = join38(REPO_HOME, "shared", "projects", logical, `${sid}.jsonl`);
|
|
4310
|
+
const dir = join38(REPO_HOME, "shared", "projects", logical, sid);
|
|
4311
|
+
rmSync10(jsonl, { force: true });
|
|
4312
|
+
rmSync10(dir, { recursive: true, force: true });
|
|
4150
4313
|
}
|
|
4151
4314
|
return true;
|
|
4152
4315
|
}
|
|
@@ -4262,7 +4425,7 @@ function applyThenRescan(scanVerdict, repoHome) {
|
|
|
4262
4425
|
return next;
|
|
4263
4426
|
}
|
|
4264
4427
|
function allowThenRescan(append, scanVerdict, repoHome) {
|
|
4265
|
-
const ignPath =
|
|
4428
|
+
const ignPath = join39(repoHome, ".gitleaksignore");
|
|
4266
4429
|
let before;
|
|
4267
4430
|
try {
|
|
4268
4431
|
before = readFileSync11(ignPath, "utf8");
|
|
@@ -4273,7 +4436,7 @@ function allowThenRescan(append, scanVerdict, repoHome) {
|
|
|
4273
4436
|
try {
|
|
4274
4437
|
return applyThenRescan(scanVerdict, repoHome);
|
|
4275
4438
|
} catch (err) {
|
|
4276
|
-
if (before === null)
|
|
4439
|
+
if (before === null) rmSync11(ignPath, { force: true });
|
|
4277
4440
|
else writeFileSync5(ignPath, before, "utf8");
|
|
4278
4441
|
throw err;
|
|
4279
4442
|
}
|
|
@@ -4360,7 +4523,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
4360
4523
|
`);
|
|
4361
4524
|
}
|
|
4362
4525
|
function resolveWorkerPath(deps = {}) {
|
|
4363
|
-
const check = deps.existsSyncFn ??
|
|
4526
|
+
const check = deps.existsSyncFn ?? existsSync33;
|
|
4364
4527
|
const base = deps.baseUrl ?? import.meta.url;
|
|
4365
4528
|
const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
|
|
4366
4529
|
if (check(mjs)) return mjs;
|
|
@@ -4566,8 +4729,8 @@ function handleWedge(repo, forceRemote) {
|
|
|
4566
4729
|
function cmdPull(opts = {}) {
|
|
4567
4730
|
const dryRun = opts.dryRun === true;
|
|
4568
4731
|
const forceRemote = opts.forceRemote === true;
|
|
4569
|
-
if (!
|
|
4570
|
-
if (!
|
|
4732
|
+
if (!existsSync34(REPO_HOME)) die(`repo not cloned at ${REPO_HOME}`);
|
|
4733
|
+
if (!existsSync34(join40(REPO_HOME, "shared", "settings.base.json"))) {
|
|
4571
4734
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
4572
4735
|
}
|
|
4573
4736
|
const handle = acquireLock("pull");
|
|
@@ -4576,7 +4739,7 @@ function cmdPull(opts = {}) {
|
|
|
4576
4739
|
const ts = freshBackupTs(BACKUP_BASE);
|
|
4577
4740
|
handleWedge(REPO_HOME, forceRemote);
|
|
4578
4741
|
if (!dryRun) {
|
|
4579
|
-
const backupRoot =
|
|
4742
|
+
const backupRoot = join40(BACKUP_BASE, ts);
|
|
4580
4743
|
try {
|
|
4581
4744
|
mkdirSync8(backupRoot, { recursive: true });
|
|
4582
4745
|
} catch (err) {
|
|
@@ -4587,8 +4750,8 @@ function cmdPull(opts = {}) {
|
|
|
4587
4750
|
dryRun ? `pulling on host=${HOST} (backup=${ts}; dry-run)` : `pull on host=${HOST} (backup=${ts})`
|
|
4588
4751
|
);
|
|
4589
4752
|
gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", REPO_HOME);
|
|
4590
|
-
const mapPath =
|
|
4591
|
-
const map =
|
|
4753
|
+
const mapPath = join40(REPO_HOME, "path-map.json");
|
|
4754
|
+
const map = existsSync34(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
4592
4755
|
divergenceCheckExtras(ts);
|
|
4593
4756
|
if (dryRun) {
|
|
4594
4757
|
computePreview(ts, map, "pull");
|
|
@@ -4610,8 +4773,8 @@ function cmdPull(opts = {}) {
|
|
|
4610
4773
|
|
|
4611
4774
|
// src/commands.push.ts
|
|
4612
4775
|
init_config();
|
|
4613
|
-
import { existsSync as
|
|
4614
|
-
import { join as
|
|
4776
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
4777
|
+
import { join as join42, relative as relative5 } from "node:path";
|
|
4615
4778
|
|
|
4616
4779
|
// src/commands.push.allowlist.ts
|
|
4617
4780
|
init_config();
|
|
@@ -4691,9 +4854,9 @@ init_color();
|
|
|
4691
4854
|
init_config();
|
|
4692
4855
|
init_config_sharedDirs_guard();
|
|
4693
4856
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4694
|
-
import { copyFileSync, existsSync as
|
|
4857
|
+
import { copyFileSync, existsSync as existsSync35, mkdirSync as mkdirSync9, readdirSync as readdirSync11, rmSync as rmSync12 } from "node:fs";
|
|
4695
4858
|
import { homedir as homedir5 } from "node:os";
|
|
4696
|
-
import { join as
|
|
4859
|
+
import { join as join41 } from "node:path";
|
|
4697
4860
|
init_push_leak_verdict();
|
|
4698
4861
|
init_push_gitleaks();
|
|
4699
4862
|
init_utils_fs();
|
|
@@ -4708,13 +4871,13 @@ function stageSessions(tmpRoot, map) {
|
|
|
4708
4871
|
if (!p || p === "TBD") continue;
|
|
4709
4872
|
reverse.set(encodePath(p), logical);
|
|
4710
4873
|
}
|
|
4711
|
-
const localProjects =
|
|
4712
|
-
if (!
|
|
4874
|
+
const localProjects = join41(CLAUDE_HOME, "projects");
|
|
4875
|
+
if (!existsSync35(localProjects)) return 0;
|
|
4713
4876
|
let staged = 0;
|
|
4714
4877
|
for (const dir of readdirSync11(localProjects)) {
|
|
4715
4878
|
const logical = reverse.get(dir);
|
|
4716
4879
|
if (!logical) continue;
|
|
4717
|
-
copyDirJsonlOnly(
|
|
4880
|
+
copyDirJsonlOnly(join41(localProjects, dir), join41(tmpRoot, "shared", "projects", logical));
|
|
4718
4881
|
staged++;
|
|
4719
4882
|
}
|
|
4720
4883
|
return staged;
|
|
@@ -4730,9 +4893,9 @@ function stageExtras(tmpRoot, map) {
|
|
|
4730
4893
|
if (!localRoot || localRoot === "TBD") continue;
|
|
4731
4894
|
for (const dirname7 of dirnames) {
|
|
4732
4895
|
if (!whitelist.includes(dirname7)) continue;
|
|
4733
|
-
const src =
|
|
4734
|
-
if (!
|
|
4735
|
-
const dst =
|
|
4896
|
+
const src = join41(localRoot, dirname7);
|
|
4897
|
+
if (!existsSync35(src)) continue;
|
|
4898
|
+
const dst = join41(tmpRoot, "shared", "extras", logical, dirname7);
|
|
4736
4899
|
copyExtras(src, dst);
|
|
4737
4900
|
staged++;
|
|
4738
4901
|
}
|
|
@@ -4740,19 +4903,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
4740
4903
|
return staged;
|
|
4741
4904
|
}
|
|
4742
4905
|
function previewPushLeaks(map) {
|
|
4743
|
-
const cacheDir =
|
|
4906
|
+
const cacheDir = join41(homedir5(), ".cache", "claude-nomad");
|
|
4744
4907
|
mkdirSync9(cacheDir, { recursive: true });
|
|
4745
4908
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
4746
|
-
const tmpRoot =
|
|
4909
|
+
const tmpRoot = join41(cacheDir, `push-preview-tree-${stamp}`);
|
|
4747
4910
|
try {
|
|
4748
4911
|
const sessionCount = stageSessions(tmpRoot, map);
|
|
4749
4912
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
4750
4913
|
if (sessionCount + extrasCount === 0) {
|
|
4751
4914
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
4752
4915
|
}
|
|
4753
|
-
const ignoreFile =
|
|
4754
|
-
if (
|
|
4755
|
-
copyFileSync(ignoreFile,
|
|
4916
|
+
const ignoreFile = join41(REPO_HOME, ".gitleaksignore");
|
|
4917
|
+
if (existsSync35(ignoreFile)) {
|
|
4918
|
+
copyFileSync(ignoreFile, join41(tmpRoot, ".gitleaksignore"));
|
|
4756
4919
|
}
|
|
4757
4920
|
let findings;
|
|
4758
4921
|
try {
|
|
@@ -4765,7 +4928,7 @@ function previewPushLeaks(map) {
|
|
|
4765
4928
|
}
|
|
4766
4929
|
return verdictFromFindings(findings);
|
|
4767
4930
|
} finally {
|
|
4768
|
-
|
|
4931
|
+
rmSync12(tmpRoot, { recursive: true, force: true });
|
|
4769
4932
|
}
|
|
4770
4933
|
}
|
|
4771
4934
|
|
|
@@ -4774,7 +4937,7 @@ init_utils();
|
|
|
4774
4937
|
init_utils_fs();
|
|
4775
4938
|
init_utils_json();
|
|
4776
4939
|
function guardGitlinks() {
|
|
4777
|
-
const gitlinks = findGitlinks(
|
|
4940
|
+
const gitlinks = findGitlinks(join42(REPO_HOME, "shared"));
|
|
4778
4941
|
if (gitlinks.length === 0) return;
|
|
4779
4942
|
for (const p of gitlinks) {
|
|
4780
4943
|
const rel = relative5(REPO_HOME, p);
|
|
@@ -4826,7 +4989,7 @@ async function cmdPush(opts = {}) {
|
|
|
4826
4989
|
const allowAll = opts.allowAll === true;
|
|
4827
4990
|
const allowRule = opts.allowRule;
|
|
4828
4991
|
guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
|
|
4829
|
-
if (!
|
|
4992
|
+
if (!existsSync36(REPO_HOME)) die(`repo not cloned at ${REPO_HOME}`);
|
|
4830
4993
|
const handle = acquireLock("push");
|
|
4831
4994
|
if (handle === null) process.exit(0);
|
|
4832
4995
|
try {
|
|
@@ -4844,8 +5007,8 @@ async function cmdPush(opts = {}) {
|
|
|
4844
5007
|
renderNoScanTree(st);
|
|
4845
5008
|
return;
|
|
4846
5009
|
}
|
|
4847
|
-
const mapPath =
|
|
4848
|
-
if (!
|
|
5010
|
+
const mapPath = join42(REPO_HOME, "path-map.json");
|
|
5011
|
+
if (!existsSync36(mapPath)) {
|
|
4849
5012
|
if (dryRun) return runDryRunPreview(st, null);
|
|
4850
5013
|
die("path-map.json missing, cannot enforce push allow-list");
|
|
4851
5014
|
}
|
|
@@ -4885,17 +5048,17 @@ init_config();
|
|
|
4885
5048
|
|
|
4886
5049
|
// src/diff.ts
|
|
4887
5050
|
init_config();
|
|
4888
|
-
import { existsSync as
|
|
4889
|
-
import { join as
|
|
5051
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
5052
|
+
import { join as join43 } from "node:path";
|
|
4890
5053
|
init_utils();
|
|
4891
5054
|
init_utils_fs();
|
|
4892
5055
|
init_utils_json();
|
|
4893
5056
|
function cmdDiff() {
|
|
4894
5057
|
try {
|
|
4895
|
-
if (!
|
|
5058
|
+
if (!existsSync37(REPO_HOME)) die(`repo not cloned at ${REPO_HOME}`);
|
|
4896
5059
|
const ts = freshBackupTs(BACKUP_BASE);
|
|
4897
|
-
const mapPath =
|
|
4898
|
-
const map =
|
|
5060
|
+
const mapPath = join43(REPO_HOME, "path-map.json");
|
|
5061
|
+
const map = existsSync37(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
4899
5062
|
computePreview(ts, map, "diff");
|
|
4900
5063
|
} catch (err) {
|
|
4901
5064
|
if (err instanceof NomadFatal) {
|
|
@@ -4909,8 +5072,8 @@ function cmdDiff() {
|
|
|
4909
5072
|
|
|
4910
5073
|
// src/init.ts
|
|
4911
5074
|
init_config();
|
|
4912
|
-
import { existsSync as
|
|
4913
|
-
import { join as
|
|
5075
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
5076
|
+
import { join as join45 } from "node:path";
|
|
4914
5077
|
|
|
4915
5078
|
// src/init.gh-onboard.ts
|
|
4916
5079
|
init_config();
|
|
@@ -4991,31 +5154,31 @@ init_config();
|
|
|
4991
5154
|
init_utils();
|
|
4992
5155
|
init_utils_fs();
|
|
4993
5156
|
init_utils_json();
|
|
4994
|
-
import { copyFileSync as copyFileSync2, cpSync as
|
|
4995
|
-
import { join as
|
|
5157
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync38, rmSync as rmSync13, statSync as statSync9 } from "node:fs";
|
|
5158
|
+
import { join as join44 } from "node:path";
|
|
4996
5159
|
function snapshotIntoShared(map) {
|
|
4997
5160
|
for (const name of allSharedLinks(map)) {
|
|
4998
|
-
const src =
|
|
4999
|
-
if (!
|
|
5000
|
-
const dst =
|
|
5161
|
+
const src = join44(CLAUDE_HOME, name);
|
|
5162
|
+
if (!existsSync38(src)) continue;
|
|
5163
|
+
const dst = join44(REPO_HOME, "shared", name);
|
|
5001
5164
|
if (statSync9(src).isDirectory()) {
|
|
5002
|
-
const gk =
|
|
5003
|
-
if (
|
|
5004
|
-
|
|
5165
|
+
const gk = join44(dst, ".gitkeep");
|
|
5166
|
+
if (existsSync38(gk)) rmSync13(gk);
|
|
5167
|
+
cpSync7(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
5005
5168
|
} else {
|
|
5006
5169
|
copyFileSync2(src, dst);
|
|
5007
5170
|
}
|
|
5008
5171
|
log(`snapshotted shared/${name} from ${src}`);
|
|
5009
5172
|
}
|
|
5010
|
-
const userSettings =
|
|
5011
|
-
if (
|
|
5173
|
+
const userSettings = join44(CLAUDE_HOME, "settings.json");
|
|
5174
|
+
if (existsSync38(userSettings)) {
|
|
5012
5175
|
let parsed;
|
|
5013
5176
|
try {
|
|
5014
5177
|
parsed = readJson(userSettings);
|
|
5015
5178
|
} catch (err) {
|
|
5016
5179
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
5017
5180
|
}
|
|
5018
|
-
const hostFile =
|
|
5181
|
+
const hostFile = join44(REPO_HOME, "hosts", `${HOST}.json`);
|
|
5019
5182
|
writeJsonAtomic(hostFile, parsed);
|
|
5020
5183
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
5021
5184
|
}
|
|
@@ -5028,14 +5191,14 @@ var SHARED_CLAUDE_MD = "<!-- claude-nomad shared CLAUDE.md; symlinked into ~/.cl
|
|
|
5028
5191
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
5029
5192
|
function preflightConflict(repoHome) {
|
|
5030
5193
|
const candidates = [
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5194
|
+
join45(repoHome, "shared", "settings.base.json"),
|
|
5195
|
+
join45(repoHome, "shared", "CLAUDE.md"),
|
|
5196
|
+
join45(repoHome, "path-map.json"),
|
|
5197
|
+
join45(repoHome, "hosts"),
|
|
5198
|
+
join45(repoHome, "shared")
|
|
5036
5199
|
];
|
|
5037
5200
|
for (const c of candidates) {
|
|
5038
|
-
if (
|
|
5201
|
+
if (existsSync39(c)) return c;
|
|
5039
5202
|
}
|
|
5040
5203
|
return null;
|
|
5041
5204
|
}
|
|
@@ -5048,25 +5211,25 @@ function cmdInit(opts = {}) {
|
|
|
5048
5211
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
5049
5212
|
}
|
|
5050
5213
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
5051
|
-
mkdirSync10(
|
|
5052
|
-
mkdirSync10(
|
|
5214
|
+
mkdirSync10(join45(REPO_HOME, "shared"), { recursive: true });
|
|
5215
|
+
mkdirSync10(join45(REPO_HOME, "hosts"), { recursive: true });
|
|
5053
5216
|
for (const name of SHARED_KEEP_DIRS) {
|
|
5054
|
-
mkdirSync10(
|
|
5217
|
+
mkdirSync10(join45(REPO_HOME, "shared", name), { recursive: true });
|
|
5055
5218
|
}
|
|
5056
|
-
const userClaudeMd =
|
|
5057
|
-
if (!snapshot || !
|
|
5058
|
-
writeFileSync6(
|
|
5219
|
+
const userClaudeMd = join45(CLAUDE_HOME, "CLAUDE.md");
|
|
5220
|
+
if (!snapshot || !existsSync39(userClaudeMd)) {
|
|
5221
|
+
writeFileSync6(join45(REPO_HOME, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
5059
5222
|
log("created shared/CLAUDE.md");
|
|
5060
5223
|
}
|
|
5061
5224
|
for (const name of SHARED_KEEP_DIRS) {
|
|
5062
|
-
writeFileSync6(
|
|
5225
|
+
writeFileSync6(join45(REPO_HOME, "shared", name, ".gitkeep"), "");
|
|
5063
5226
|
log(`created shared/${name}/.gitkeep`);
|
|
5064
5227
|
}
|
|
5065
|
-
writeFileSync6(
|
|
5228
|
+
writeFileSync6(join45(REPO_HOME, "hosts", ".gitkeep"), "");
|
|
5066
5229
|
log("created hosts/.gitkeep");
|
|
5067
|
-
writeJsonAtomic(
|
|
5230
|
+
writeJsonAtomic(join45(REPO_HOME, "shared", "settings.base.json"), {});
|
|
5068
5231
|
log("created shared/settings.base.json");
|
|
5069
|
-
writeJsonAtomic(
|
|
5232
|
+
writeJsonAtomic(join45(REPO_HOME, "path-map.json"), { projects: {} });
|
|
5070
5233
|
log("created path-map.json");
|
|
5071
5234
|
if (snapshot) {
|
|
5072
5235
|
snapshotIntoShared({ projects: {} });
|
|
@@ -5260,6 +5423,23 @@ function parseCleanArgs(argv) {
|
|
|
5260
5423
|
return { dryRun: st.dryRun, olderThan: st.olderThan, keep: st.keep };
|
|
5261
5424
|
}
|
|
5262
5425
|
|
|
5426
|
+
// src/nomad.dispatch.eject.ts
|
|
5427
|
+
function parseEjectArgs(argv) {
|
|
5428
|
+
let dryRun = false;
|
|
5429
|
+
let i = 3;
|
|
5430
|
+
while (i < argv.length) {
|
|
5431
|
+
const token = argv[i];
|
|
5432
|
+
if (token === "--dry-run") {
|
|
5433
|
+
if (dryRun) return null;
|
|
5434
|
+
dryRun = true;
|
|
5435
|
+
} else {
|
|
5436
|
+
return null;
|
|
5437
|
+
}
|
|
5438
|
+
i++;
|
|
5439
|
+
}
|
|
5440
|
+
return { dryRun };
|
|
5441
|
+
}
|
|
5442
|
+
|
|
5263
5443
|
// src/nomad.dispatch.allow.ts
|
|
5264
5444
|
function parseAllowArgs(argv) {
|
|
5265
5445
|
const positionals = argv.slice(3);
|
|
@@ -5350,7 +5530,7 @@ function parsePushArgs(argv) {
|
|
|
5350
5530
|
// package.json
|
|
5351
5531
|
var package_default = {
|
|
5352
5532
|
name: "claude-nomad",
|
|
5353
|
-
version: "0.
|
|
5533
|
+
version: "0.44.0",
|
|
5354
5534
|
type: "module",
|
|
5355
5535
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
5356
5536
|
keywords: [
|
|
@@ -5509,6 +5689,11 @@ var DEFAULT_HELP = [
|
|
|
5509
5689
|
cont("symlink, and stage for push. <name> must be in SHARED_LINKS or sharedDirs."),
|
|
5510
5690
|
row(" --dry-run", "Preview backup, move, and git-add without writing."),
|
|
5511
5691
|
"",
|
|
5692
|
+
row(" eject", "Materialize every managed ~/.claude/ symlink into a real copy so the"),
|
|
5693
|
+
cont("setup keeps working after deleting the sync repo. Prints a"),
|
|
5694
|
+
cont("manual-remainder checklist (uninstall CLI, drop env vars, optional deletes)."),
|
|
5695
|
+
row(" --dry-run", "List what would be materialized without writing anything."),
|
|
5696
|
+
"",
|
|
5512
5697
|
row(
|
|
5513
5698
|
" redact <session-id>",
|
|
5514
5699
|
"Rewrite the secret span in the local source transcript for a session,"
|
|
@@ -5545,15 +5730,15 @@ var DEFAULT_HELP = [
|
|
|
5545
5730
|
init_config();
|
|
5546
5731
|
init_utils();
|
|
5547
5732
|
init_utils_json();
|
|
5548
|
-
import { existsSync as
|
|
5549
|
-
import { join as
|
|
5733
|
+
import { existsSync as existsSync40, readFileSync as readFileSync12, readdirSync as readdirSync12 } from "node:fs";
|
|
5734
|
+
import { join as join46 } from "node:path";
|
|
5550
5735
|
function resumeCmd(sessionId) {
|
|
5551
5736
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
5552
5737
|
fail(`invalid session id: ${sessionId}`);
|
|
5553
5738
|
process.exit(1);
|
|
5554
5739
|
}
|
|
5555
|
-
const projectsRoot =
|
|
5556
|
-
if (!
|
|
5740
|
+
const projectsRoot = join46(CLAUDE_HOME, "projects");
|
|
5741
|
+
if (!existsSync40(projectsRoot)) {
|
|
5557
5742
|
fail(`${projectsRoot} does not exist`);
|
|
5558
5743
|
process.exit(1);
|
|
5559
5744
|
}
|
|
@@ -5567,8 +5752,8 @@ function resumeCmd(sessionId) {
|
|
|
5567
5752
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
5568
5753
|
process.exit(1);
|
|
5569
5754
|
}
|
|
5570
|
-
const mapPath =
|
|
5571
|
-
if (!
|
|
5755
|
+
const mapPath = join46(REPO_HOME, "path-map.json");
|
|
5756
|
+
if (!existsSync40(mapPath)) {
|
|
5572
5757
|
fail("path-map.json missing");
|
|
5573
5758
|
process.exit(1);
|
|
5574
5759
|
}
|
|
@@ -5591,8 +5776,8 @@ function resumeCmd(sessionId) {
|
|
|
5591
5776
|
}
|
|
5592
5777
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
5593
5778
|
for (const dir of readdirSync12(projectsRoot)) {
|
|
5594
|
-
const candidate =
|
|
5595
|
-
if (
|
|
5779
|
+
const candidate = join46(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
5780
|
+
if (existsSync40(candidate)) return candidate;
|
|
5596
5781
|
}
|
|
5597
5782
|
return null;
|
|
5598
5783
|
}
|
|
@@ -5726,6 +5911,15 @@ try {
|
|
|
5726
5911
|
cmdAdopt(name, { dryRun: sub === "--dry-run" });
|
|
5727
5912
|
break;
|
|
5728
5913
|
}
|
|
5914
|
+
case "eject": {
|
|
5915
|
+
const ejectArgs = parseEjectArgs(process.argv);
|
|
5916
|
+
if (ejectArgs === null) {
|
|
5917
|
+
console.error("usage: nomad eject [--dry-run]");
|
|
5918
|
+
process.exit(1);
|
|
5919
|
+
}
|
|
5920
|
+
cmdEject({ dryRun: ejectArgs.dryRun });
|
|
5921
|
+
break;
|
|
5922
|
+
}
|
|
5729
5923
|
case "doctor":
|
|
5730
5924
|
if (process.argv[3] === void 0) {
|
|
5731
5925
|
cmdDoctor();
|