claude-nomad 0.60.0 → 0.61.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/.gitleaks.toml +11 -2
- package/CHANGELOG.md +24 -0
- package/README.md +3 -0
- package/dist/nomad.mjs +569 -344
- package/package.json +1 -1
package/dist/nomad.mjs
CHANGED
|
@@ -1686,12 +1686,12 @@ function* eachExtrasTarget(v, counts) {
|
|
|
1686
1686
|
counts.unmapped++;
|
|
1687
1687
|
continue;
|
|
1688
1688
|
}
|
|
1689
|
-
for (const
|
|
1690
|
-
if (!whitelist.includes(
|
|
1689
|
+
for (const dirname11 of dirnames) {
|
|
1690
|
+
if (!whitelist.includes(dirname11)) {
|
|
1691
1691
|
counts.skipped++;
|
|
1692
1692
|
continue;
|
|
1693
1693
|
}
|
|
1694
|
-
yield { logical, localRoot, dirname:
|
|
1694
|
+
yield { logical, localRoot, dirname: dirname11 };
|
|
1695
1695
|
}
|
|
1696
1696
|
}
|
|
1697
1697
|
}
|
|
@@ -1729,8 +1729,8 @@ function copyExtrasFileSkipDiverged(src, dst) {
|
|
|
1729
1729
|
}
|
|
1730
1730
|
copyExtras(src, dst);
|
|
1731
1731
|
}
|
|
1732
|
-
function extrasDenySet(
|
|
1733
|
-
return
|
|
1732
|
+
function extrasDenySet(dirname11) {
|
|
1733
|
+
return dirname11 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
|
|
1734
1734
|
}
|
|
1735
1735
|
function copyExtrasFiltered(src, dst, blockSet) {
|
|
1736
1736
|
rmSync2(dst, { recursive: true, force: true });
|
|
@@ -2558,8 +2558,8 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
|
|
|
2558
2558
|
}
|
|
2559
2559
|
|
|
2560
2560
|
// src/commands.doctor.ts
|
|
2561
|
-
import { existsSync as
|
|
2562
|
-
import { join as
|
|
2561
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
2562
|
+
import { join as join42 } from "node:path";
|
|
2563
2563
|
|
|
2564
2564
|
// src/commands.doctor.checks.repo.ts
|
|
2565
2565
|
init_color();
|
|
@@ -3406,20 +3406,117 @@ function reportCheckSchema(section2) {
|
|
|
3406
3406
|
|
|
3407
3407
|
// src/commands.doctor.check-shared.ts
|
|
3408
3408
|
init_color();
|
|
3409
|
-
import { randomBytes } from "node:crypto";
|
|
3409
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3410
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
3411
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11 } from "node:fs";
|
|
3412
|
+
import { homedir as homedir5 } from "node:os";
|
|
3413
|
+
import { join as join28 } from "node:path";
|
|
3414
|
+
|
|
3415
|
+
// src/commands.doctor.check-shared.memory.ts
|
|
3416
|
+
init_color();
|
|
3410
3417
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
3411
|
-
import {
|
|
3418
|
+
import { randomBytes } from "node:crypto";
|
|
3419
|
+
import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3412
3420
|
import { homedir as homedir4 } from "node:os";
|
|
3413
|
-
import { join as
|
|
3421
|
+
import { dirname as dirname3, join as join24 } from "node:path";
|
|
3422
|
+
init_config();
|
|
3423
|
+
init_push_gitleaks();
|
|
3424
|
+
init_utils_fs();
|
|
3425
|
+
var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
|
|
3426
|
+
function buildMemoryScanTree(tmpRoot) {
|
|
3427
|
+
let paths;
|
|
3428
|
+
try {
|
|
3429
|
+
const out = execFileSync9(
|
|
3430
|
+
"git",
|
|
3431
|
+
["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
|
|
3432
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
|
|
3433
|
+
);
|
|
3434
|
+
paths = out.split("\0").filter((p) => p.length > 0);
|
|
3435
|
+
} catch {
|
|
3436
|
+
return 0;
|
|
3437
|
+
}
|
|
3438
|
+
const logicals = /* @__PURE__ */ new Set();
|
|
3439
|
+
for (const rel of paths) {
|
|
3440
|
+
const m = MEMORY_MD_PATH.exec(rel);
|
|
3441
|
+
if (m?.[1] === void 0) continue;
|
|
3442
|
+
let blob;
|
|
3443
|
+
try {
|
|
3444
|
+
blob = execFileSync9("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
|
|
3445
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3446
|
+
maxBuffer: 67108864,
|
|
3447
|
+
timeout: 1e4
|
|
3448
|
+
});
|
|
3449
|
+
} catch {
|
|
3450
|
+
continue;
|
|
3451
|
+
}
|
|
3452
|
+
const dest = join24(tmpRoot, rel);
|
|
3453
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
3454
|
+
writeFileSync4(dest, blob);
|
|
3455
|
+
logicals.add(m[1]);
|
|
3456
|
+
}
|
|
3457
|
+
return logicals.size;
|
|
3458
|
+
}
|
|
3459
|
+
function reportMemoryFindings(section2, findings) {
|
|
3460
|
+
for (const f of findings) {
|
|
3461
|
+
addItem(section2, `${yellow(warnGlyph)} ${f.RuleID} in ${f.File}`);
|
|
3462
|
+
}
|
|
3463
|
+
addItem(
|
|
3464
|
+
section2,
|
|
3465
|
+
` ${dim("run `nomad push` and choose Redact in the recovery menu to scrub these")}`
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
function reportCommittedMemory(section2) {
|
|
3469
|
+
let tmpRoot;
|
|
3470
|
+
try {
|
|
3471
|
+
const cacheDir = join24(homedir4(), ".cache", "claude-nomad");
|
|
3472
|
+
mkdirSync4(cacheDir, { recursive: true });
|
|
3473
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
3474
|
+
tmpRoot = join24(cacheDir, `check-shared-memory-tree-${stamp}`);
|
|
3475
|
+
mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
|
|
3476
|
+
const staged = buildMemoryScanTree(tmpRoot);
|
|
3477
|
+
if (staged === 0) return;
|
|
3478
|
+
let findings;
|
|
3479
|
+
try {
|
|
3480
|
+
findings = scanStagedTree(tmpRoot);
|
|
3481
|
+
} catch (err) {
|
|
3482
|
+
addItem(
|
|
3483
|
+
section2,
|
|
3484
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
|
|
3485
|
+
);
|
|
3486
|
+
return;
|
|
3487
|
+
}
|
|
3488
|
+
if (findings === null) {
|
|
3489
|
+
addItem(
|
|
3490
|
+
section2,
|
|
3491
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: no parseable gitleaks report`
|
|
3492
|
+
);
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
if (findings.length === 0) return;
|
|
3496
|
+
reportMemoryFindings(section2, findings);
|
|
3497
|
+
} catch (err) {
|
|
3498
|
+
addItem(
|
|
3499
|
+
section2,
|
|
3500
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
|
|
3501
|
+
);
|
|
3502
|
+
} finally {
|
|
3503
|
+
if (tmpRoot !== void 0) {
|
|
3504
|
+
try {
|
|
3505
|
+
rmSync9(tmpRoot, { recursive: true, force: true });
|
|
3506
|
+
} catch {
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3414
3511
|
|
|
3415
3512
|
// src/commands.doctor.check-shared.scan.ts
|
|
3416
3513
|
init_color();
|
|
3417
|
-
import { join as
|
|
3514
|
+
import { join as join25 } from "node:path";
|
|
3418
3515
|
init_config();
|
|
3419
3516
|
init_push_gitleaks();
|
|
3420
3517
|
function scrubPath(logical, sid, logicalToEncoded) {
|
|
3421
3518
|
const encoded = logicalToEncoded.get(logical) ?? logical;
|
|
3422
|
-
return
|
|
3519
|
+
return join25(claudeHome(), "projects", encoded, `${sid}.jsonl`);
|
|
3423
3520
|
}
|
|
3424
3521
|
function reportSessionFindings(section2, bySession) {
|
|
3425
3522
|
for (const [sid, counts] of bySession) {
|
|
@@ -3507,8 +3604,8 @@ init_config();
|
|
|
3507
3604
|
|
|
3508
3605
|
// src/remap.ts
|
|
3509
3606
|
init_config_sharedDirs_guard();
|
|
3510
|
-
import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as
|
|
3511
|
-
import { dirname as
|
|
3607
|
+
import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync10, statSync as statSync5 } from "node:fs";
|
|
3608
|
+
import { dirname as dirname5, join as join27, relative as relative5, sep as sep4 } from "node:path";
|
|
3512
3609
|
init_config();
|
|
3513
3610
|
|
|
3514
3611
|
// src/push-manifest.ts
|
|
@@ -3516,8 +3613,8 @@ init_config();
|
|
|
3516
3613
|
init_push_gitleaks_config();
|
|
3517
3614
|
init_utils_fs();
|
|
3518
3615
|
import { createHash } from "node:crypto";
|
|
3519
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
3520
|
-
import { dirname as
|
|
3616
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
|
|
3617
|
+
import { dirname as dirname4, join as join26 } from "node:path";
|
|
3521
3618
|
function isChanged(prev, cur, hash) {
|
|
3522
3619
|
if (prev === void 0) return true;
|
|
3523
3620
|
if (prev.size !== cur.size) return true;
|
|
@@ -3558,7 +3655,7 @@ function hashFile(absPath) {
|
|
|
3558
3655
|
}
|
|
3559
3656
|
function enumerateDir(dir, results) {
|
|
3560
3657
|
for (const entry of readdirSync9(dir)) {
|
|
3561
|
-
const fullPath =
|
|
3658
|
+
const fullPath = join26(dir, entry);
|
|
3562
3659
|
const st = statSync4(fullPath);
|
|
3563
3660
|
if (st.isDirectory()) {
|
|
3564
3661
|
enumerateDir(fullPath, results);
|
|
@@ -3570,7 +3667,7 @@ function enumerateDir(dir, results) {
|
|
|
3570
3667
|
function enumerateSourceFiles(projectDir) {
|
|
3571
3668
|
const results = [];
|
|
3572
3669
|
for (const entry of readdirSync9(projectDir)) {
|
|
3573
|
-
const fullPath =
|
|
3670
|
+
const fullPath = join26(projectDir, entry);
|
|
3574
3671
|
const st = statSync4(fullPath);
|
|
3575
3672
|
if (st.isDirectory()) {
|
|
3576
3673
|
enumerateDir(fullPath, results);
|
|
@@ -3589,8 +3686,8 @@ function computeConfigHash() {
|
|
|
3589
3686
|
const repo = repoHome();
|
|
3590
3687
|
const parts = [
|
|
3591
3688
|
fileIdentity(resolveTomlPath(repo)),
|
|
3592
|
-
fileIdentity(
|
|
3593
|
-
fileIdentity(
|
|
3689
|
+
fileIdentity(join26(repo, ".gitleaks.overlay.toml")),
|
|
3690
|
+
fileIdentity(join26(repo, ".gitleaksignore"))
|
|
3594
3691
|
];
|
|
3595
3692
|
return createHash("sha256").update(parts.join("\n")).digest("hex");
|
|
3596
3693
|
}
|
|
@@ -3610,7 +3707,7 @@ function readManifest(path) {
|
|
|
3610
3707
|
}
|
|
3611
3708
|
}
|
|
3612
3709
|
function writeManifest(path, manifest) {
|
|
3613
|
-
|
|
3710
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
3614
3711
|
writeJsonAtomic(path, manifest);
|
|
3615
3712
|
}
|
|
3616
3713
|
function buildManifest(files, scannerVersion, configHash) {
|
|
@@ -3624,9 +3721,9 @@ init_utils_json();
|
|
|
3624
3721
|
var TMP_SUFFIX = ".nomad-tmp";
|
|
3625
3722
|
function atomicMirror(src, dst, options) {
|
|
3626
3723
|
const tmp = `${dst}${TMP_SUFFIX}`;
|
|
3627
|
-
|
|
3724
|
+
rmSync10(tmp, { recursive: true, force: true });
|
|
3628
3725
|
cpSync6(src, tmp, options);
|
|
3629
|
-
|
|
3726
|
+
rmSync10(dst, { recursive: true, force: true });
|
|
3630
3727
|
renameAtomicRetry(tmp, dst);
|
|
3631
3728
|
}
|
|
3632
3729
|
function overlaySessionDir(src, dst) {
|
|
@@ -3634,7 +3731,7 @@ function overlaySessionDir(src, dst) {
|
|
|
3634
3731
|
cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
|
|
3635
3732
|
}
|
|
3636
3733
|
function copyFileAtomic(src, dst) {
|
|
3637
|
-
|
|
3734
|
+
mkdirSync6(dirname5(dst), { recursive: true });
|
|
3638
3735
|
const tmp = `${dst}.tmp.${process.pid}`;
|
|
3639
3736
|
cpSync6(src, tmp, { force: true, preserveTimestamps: true });
|
|
3640
3737
|
renameAtomicRetry(tmp, dst);
|
|
@@ -3652,12 +3749,12 @@ function applySelective(sel, localDir, repoDst) {
|
|
|
3652
3749
|
const prefix = `${localDir}${sep4}`;
|
|
3653
3750
|
for (const src of sel.changed) {
|
|
3654
3751
|
if (!src.startsWith(prefix)) continue;
|
|
3655
|
-
copyFileAtomic(src,
|
|
3752
|
+
copyFileAtomic(src, join27(repoDst, relative5(localDir, src)));
|
|
3656
3753
|
}
|
|
3657
3754
|
for (const src of sel.deleted) {
|
|
3658
3755
|
if (!src.startsWith(prefix)) continue;
|
|
3659
|
-
const dst =
|
|
3660
|
-
if (existsSync21(dst))
|
|
3756
|
+
const dst = join27(repoDst, relative5(localDir, src));
|
|
3757
|
+
if (existsSync21(dst)) rmSync10(dst);
|
|
3661
3758
|
}
|
|
3662
3759
|
}
|
|
3663
3760
|
function copyDirJsonlOnly(src, dst) {
|
|
@@ -3686,16 +3783,16 @@ function remapPull(ts, opts = {}) {
|
|
|
3686
3783
|
const wouldPull = [];
|
|
3687
3784
|
const repo = repoHome();
|
|
3688
3785
|
const claude = claudeHome();
|
|
3689
|
-
const mapPath =
|
|
3690
|
-
const repoProjects =
|
|
3786
|
+
const mapPath = join27(repo, "path-map.json");
|
|
3787
|
+
const repoProjects = join27(repo, "shared", "projects");
|
|
3691
3788
|
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) {
|
|
3692
3789
|
const text = "no path-map or repo projects dir; skipping session remap";
|
|
3693
3790
|
emitPreview(opts.onPreview, { kind: "note", text }, text);
|
|
3694
3791
|
return { unmapped: 0, pulled, wouldPull };
|
|
3695
3792
|
}
|
|
3696
3793
|
const map = readPathMap(mapPath);
|
|
3697
|
-
const localProjects =
|
|
3698
|
-
if (!dryRun)
|
|
3794
|
+
const localProjects = join27(claude, "projects");
|
|
3795
|
+
if (!dryRun) mkdirSync6(localProjects, { recursive: true });
|
|
3699
3796
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
3700
3797
|
assertSafeLogical(logical);
|
|
3701
3798
|
const localPath = hosts[HOST];
|
|
@@ -3704,9 +3801,9 @@ function remapPull(ts, opts = {}) {
|
|
|
3704
3801
|
continue;
|
|
3705
3802
|
}
|
|
3706
3803
|
assertSafeLocalRoot(localPath, logical);
|
|
3707
|
-
const src =
|
|
3804
|
+
const src = join27(repoProjects, logical);
|
|
3708
3805
|
if (!existsSync21(src)) continue;
|
|
3709
|
-
const dst =
|
|
3806
|
+
const dst = join27(localProjects, encodePath(localPath));
|
|
3710
3807
|
if (dryRun) {
|
|
3711
3808
|
wouldPull.push(logical);
|
|
3712
3809
|
emitPreview(
|
|
@@ -3725,8 +3822,8 @@ function remapPull(ts, opts = {}) {
|
|
|
3725
3822
|
function countLocalOnly(src, dst) {
|
|
3726
3823
|
let count = 0;
|
|
3727
3824
|
for (const name of readdirSync10(dst)) {
|
|
3728
|
-
const dstPath =
|
|
3729
|
-
const srcPath =
|
|
3825
|
+
const dstPath = join27(dst, name);
|
|
3826
|
+
const srcPath = join27(src, name);
|
|
3730
3827
|
if (lstatSync10(dstPath).isDirectory()) {
|
|
3731
3828
|
count += countLocalOnly(srcPath, dstPath);
|
|
3732
3829
|
} else if (lstatSync10(srcPath, { throwIfNoEntry: false }) === void 0) {
|
|
@@ -3738,20 +3835,20 @@ function countLocalOnly(src, dst) {
|
|
|
3738
3835
|
function scanLocalOnly() {
|
|
3739
3836
|
const repo = repoHome();
|
|
3740
3837
|
const claude = claudeHome();
|
|
3741
|
-
const mapPath =
|
|
3742
|
-
const repoProjects =
|
|
3838
|
+
const mapPath = join27(repo, "path-map.json");
|
|
3839
|
+
const repoProjects = join27(repo, "shared", "projects");
|
|
3743
3840
|
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) return 0;
|
|
3744
3841
|
const map = readPathMap(mapPath);
|
|
3745
|
-
const localProjects =
|
|
3842
|
+
const localProjects = join27(claude, "projects");
|
|
3746
3843
|
let count = 0;
|
|
3747
3844
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
3748
3845
|
assertSafeLogical(logical);
|
|
3749
3846
|
const localPath = hosts[HOST];
|
|
3750
3847
|
if (!localPath || localPath === "TBD") continue;
|
|
3751
3848
|
assertSafeLocalRoot(localPath, logical);
|
|
3752
|
-
const dst =
|
|
3849
|
+
const dst = join27(localProjects, encodePath(localPath));
|
|
3753
3850
|
if (!existsSync21(dst)) continue;
|
|
3754
|
-
count += countLocalOnly(
|
|
3851
|
+
count += countLocalOnly(join27(repoProjects, logical), dst);
|
|
3755
3852
|
}
|
|
3756
3853
|
return count;
|
|
3757
3854
|
}
|
|
@@ -3786,17 +3883,17 @@ function remapPush(ts, opts = {}) {
|
|
|
3786
3883
|
const wouldPush = [];
|
|
3787
3884
|
const repo = repoHome();
|
|
3788
3885
|
const claude = claudeHome();
|
|
3789
|
-
const mapPath =
|
|
3886
|
+
const mapPath = join27(repo, "path-map.json");
|
|
3790
3887
|
if (!existsSync21(mapPath)) {
|
|
3791
3888
|
log("no path-map.json; skipping session export");
|
|
3792
3889
|
return { unmapped: 0, collisions: 0, pushed, wouldPush };
|
|
3793
3890
|
}
|
|
3794
3891
|
const map = readPathMap(mapPath);
|
|
3795
|
-
const localProjects =
|
|
3796
|
-
const repoProjects =
|
|
3892
|
+
const localProjects = join27(claude, "projects");
|
|
3893
|
+
const repoProjects = join27(repo, "shared", "projects");
|
|
3797
3894
|
const reverse = buildReverseMap(map);
|
|
3798
3895
|
if (!existsSync21(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
|
|
3799
|
-
if (!dryRun)
|
|
3896
|
+
if (!dryRun) mkdirSync6(repoProjects, { recursive: true });
|
|
3800
3897
|
for (const dir of readdirSync10(localProjects)) {
|
|
3801
3898
|
if (dir.endsWith(TMP_SUFFIX)) continue;
|
|
3802
3899
|
const logical = reverse.get(dir);
|
|
@@ -3804,8 +3901,8 @@ function remapPush(ts, opts = {}) {
|
|
|
3804
3901
|
unmapped++;
|
|
3805
3902
|
continue;
|
|
3806
3903
|
}
|
|
3807
|
-
const localDir =
|
|
3808
|
-
const repoDst =
|
|
3904
|
+
const localDir = join27(localProjects, dir);
|
|
3905
|
+
const repoDst = join27(repoProjects, logical);
|
|
3809
3906
|
if (skipForSelection(opts.selection, localDir)) continue;
|
|
3810
3907
|
if (dryRun) {
|
|
3811
3908
|
wouldPush.push(logical);
|
|
@@ -3829,7 +3926,7 @@ function buildScanTree(tmpRoot) {
|
|
|
3829
3926
|
const logicalToEncoded = /* @__PURE__ */ new Map();
|
|
3830
3927
|
let staged = 0;
|
|
3831
3928
|
const repo = repoHome();
|
|
3832
|
-
const mapPath =
|
|
3929
|
+
const mapPath = join28(repo, "path-map.json");
|
|
3833
3930
|
if (!existsSync22(mapPath)) return { logicalToEncoded, staged, malformed: false };
|
|
3834
3931
|
let map;
|
|
3835
3932
|
try {
|
|
@@ -3847,12 +3944,12 @@ function buildScanTree(tmpRoot) {
|
|
|
3847
3944
|
if (!p || p === "TBD") continue;
|
|
3848
3945
|
reverse.set(encodePath(p), logical);
|
|
3849
3946
|
}
|
|
3850
|
-
const localProjects =
|
|
3947
|
+
const localProjects = join28(claudeHome(), "projects");
|
|
3851
3948
|
if (!existsSync22(localProjects)) return { logicalToEncoded, staged, malformed: false };
|
|
3852
3949
|
for (const dir of readdirSync11(localProjects)) {
|
|
3853
3950
|
const logical = reverse.get(dir);
|
|
3854
3951
|
if (!logical) continue;
|
|
3855
|
-
copyDirJsonlOnly(
|
|
3952
|
+
copyDirJsonlOnly(join28(localProjects, dir), join28(tmpRoot, "shared", "projects", logical));
|
|
3856
3953
|
logicalToEncoded.set(logical, dir);
|
|
3857
3954
|
staged++;
|
|
3858
3955
|
}
|
|
@@ -3860,7 +3957,7 @@ function buildScanTree(tmpRoot) {
|
|
|
3860
3957
|
}
|
|
3861
3958
|
function probeGitleaksForScan() {
|
|
3862
3959
|
try {
|
|
3863
|
-
|
|
3960
|
+
execFileSync10("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
3864
3961
|
return "ok";
|
|
3865
3962
|
} catch (err) {
|
|
3866
3963
|
if (err.code === "ENOENT") return "missing";
|
|
@@ -3881,13 +3978,12 @@ function ensureGitleaksReady(section2, gitleaksReady) {
|
|
|
3881
3978
|
}
|
|
3882
3979
|
return true;
|
|
3883
3980
|
}
|
|
3884
|
-
function
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
const
|
|
3889
|
-
const
|
|
3890
|
-
const tmpRoot = join27(cacheDir, `check-shared-tree-${stamp}`);
|
|
3981
|
+
function runLocalPreviewScan(section2) {
|
|
3982
|
+
const cacheDir = join28(homedir5(), ".cache", "claude-nomad");
|
|
3983
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
3984
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
3985
|
+
const reportPath = join28(cacheDir, `check-shared-${stamp}.json`);
|
|
3986
|
+
const tmpRoot = join28(cacheDir, `check-shared-tree-${stamp}`);
|
|
3891
3987
|
try {
|
|
3892
3988
|
const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
|
|
3893
3989
|
if (malformed) {
|
|
@@ -3901,15 +3997,26 @@ function reportCheckShared(section2, gitleaksReady) {
|
|
|
3901
3997
|
}
|
|
3902
3998
|
scanAndReport(section2, tmpRoot, staged, logicalToEncoded);
|
|
3903
3999
|
} finally {
|
|
3904
|
-
|
|
3905
|
-
|
|
4000
|
+
rmSync11(reportPath, { force: true });
|
|
4001
|
+
rmSync11(tmpRoot, { recursive: true, force: true });
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
function reportCheckShared(section2, gitleaksReady) {
|
|
4005
|
+
if (!ensureGitleaksReady(section2, gitleaksReady)) return;
|
|
4006
|
+
try {
|
|
4007
|
+
runLocalPreviewScan(section2);
|
|
4008
|
+
} catch (err) {
|
|
4009
|
+
addItem(section2, `${red(failGlyph)} shared scan failed: ${err.message}`);
|
|
4010
|
+
process.exitCode = 1;
|
|
4011
|
+
} finally {
|
|
4012
|
+
reportCommittedMemory(section2);
|
|
3906
4013
|
}
|
|
3907
4014
|
}
|
|
3908
4015
|
|
|
3909
4016
|
// src/commands.doctor.checks.hooks.scope.ts
|
|
3910
4017
|
init_color();
|
|
3911
4018
|
import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync12, realpathSync as realpathSync2 } from "node:fs";
|
|
3912
|
-
import { dirname as
|
|
4019
|
+
import { dirname as dirname6, extname, join as join29 } from "node:path";
|
|
3913
4020
|
init_config();
|
|
3914
4021
|
function typeFromPackageJson(pkgPath) {
|
|
3915
4022
|
try {
|
|
@@ -3929,11 +4036,11 @@ function effectiveType(hookPath) {
|
|
|
3929
4036
|
} catch {
|
|
3930
4037
|
return null;
|
|
3931
4038
|
}
|
|
3932
|
-
let dir =
|
|
4039
|
+
let dir = dirname6(real);
|
|
3933
4040
|
for (; ; ) {
|
|
3934
|
-
const pkg =
|
|
4041
|
+
const pkg = join29(dir, "package.json");
|
|
3935
4042
|
if (existsSync23(pkg)) return typeFromPackageJson(pkg);
|
|
3936
|
-
const parent =
|
|
4043
|
+
const parent = dirname6(dir);
|
|
3937
4044
|
if (parent === dir) return "cjs";
|
|
3938
4045
|
dir = parent;
|
|
3939
4046
|
}
|
|
@@ -3974,7 +4081,7 @@ function safeRead(path) {
|
|
|
3974
4081
|
}
|
|
3975
4082
|
}
|
|
3976
4083
|
function reportHookScopeCheck(section2) {
|
|
3977
|
-
const hooksDir =
|
|
4084
|
+
const hooksDir = join29(claudeHome(), "hooks");
|
|
3978
4085
|
if (!existsSync23(hooksDir)) {
|
|
3979
4086
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
|
|
3980
4087
|
return;
|
|
@@ -3982,7 +4089,7 @@ function reportHookScopeCheck(section2) {
|
|
|
3982
4089
|
let anyWarn = false;
|
|
3983
4090
|
for (const name of safeReaddir2(hooksDir)) {
|
|
3984
4091
|
if (extname(name) !== ".js") continue;
|
|
3985
|
-
const abs =
|
|
4092
|
+
const abs = join29(hooksDir, name);
|
|
3986
4093
|
const eff = effectiveType(abs);
|
|
3987
4094
|
if (eff === null) continue;
|
|
3988
4095
|
const src = safeRead(abs);
|
|
@@ -4003,7 +4110,7 @@ function reportHookScopeCheck(section2) {
|
|
|
4003
4110
|
// src/commands.doctor.checks.hooks.ts
|
|
4004
4111
|
init_color();
|
|
4005
4112
|
import { existsSync as existsSync24 } from "node:fs";
|
|
4006
|
-
import { join as
|
|
4113
|
+
import { join as join30 } from "node:path";
|
|
4007
4114
|
init_config();
|
|
4008
4115
|
function expandHome(token) {
|
|
4009
4116
|
const h2 = home();
|
|
@@ -4059,7 +4166,7 @@ function checkEventGroups(section2, event, groups) {
|
|
|
4059
4166
|
return anyFail;
|
|
4060
4167
|
}
|
|
4061
4168
|
function reportHooksTargetCheck(section2) {
|
|
4062
|
-
const settingsPath =
|
|
4169
|
+
const settingsPath = join30(claudeHome(), "settings.json");
|
|
4063
4170
|
if (!existsSync24(settingsPath)) {
|
|
4064
4171
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
|
|
4065
4172
|
return;
|
|
@@ -4084,11 +4191,11 @@ function reportHooksTargetCheck(section2) {
|
|
|
4084
4191
|
// src/commands.doctor.checks.hooks.preserve-symlinks.ts
|
|
4085
4192
|
init_color();
|
|
4086
4193
|
import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
|
|
4087
|
-
import { join as
|
|
4194
|
+
import { join as join32 } from "node:path";
|
|
4088
4195
|
|
|
4089
4196
|
// src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
|
|
4090
4197
|
import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
|
|
4091
|
-
import { dirname as
|
|
4198
|
+
import { dirname as dirname7, join as join31, resolve as resolve2 } from "node:path";
|
|
4092
4199
|
function suppressedRanges(src) {
|
|
4093
4200
|
const ranges = [];
|
|
4094
4201
|
const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
|
|
@@ -4125,7 +4232,7 @@ function specifierIsMissing(specifier, baseDir) {
|
|
|
4125
4232
|
if (existsSync25(base + ext)) return false;
|
|
4126
4233
|
}
|
|
4127
4234
|
for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
|
|
4128
|
-
if (existsSync25(
|
|
4235
|
+
if (existsSync25(join31(base, idx))) return false;
|
|
4129
4236
|
}
|
|
4130
4237
|
return true;
|
|
4131
4238
|
}
|
|
@@ -4151,7 +4258,7 @@ function relativeRequireTargetsBroken(scriptPath) {
|
|
|
4151
4258
|
}
|
|
4152
4259
|
const specifiers = topRelativeSpecifiers(raw);
|
|
4153
4260
|
if (specifiers.length === 0) return false;
|
|
4154
|
-
const baseDir =
|
|
4261
|
+
const baseDir = dirname7(realPath);
|
|
4155
4262
|
for (const spec of specifiers) {
|
|
4156
4263
|
if (specifierIsMissing(spec, baseDir)) return true;
|
|
4157
4264
|
}
|
|
@@ -4180,7 +4287,7 @@ function commandTokens(command) {
|
|
|
4180
4287
|
return tokens;
|
|
4181
4288
|
}
|
|
4182
4289
|
function readPathMapSafe() {
|
|
4183
|
-
const mapPath =
|
|
4290
|
+
const mapPath = join32(repoHome(), "path-map.json");
|
|
4184
4291
|
if (!existsSync26(mapPath)) return { projects: {} };
|
|
4185
4292
|
try {
|
|
4186
4293
|
return JSON.parse(readFileSync9(mapPath, "utf8"));
|
|
@@ -4255,7 +4362,7 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
|
|
|
4255
4362
|
return anyWarn;
|
|
4256
4363
|
}
|
|
4257
4364
|
function reportPreserveSymlinksCheck(section2) {
|
|
4258
|
-
const settingsPath =
|
|
4365
|
+
const settingsPath = join32(claudeHome(), "settings.json");
|
|
4259
4366
|
if (!existsSync26(settingsPath)) {
|
|
4260
4367
|
addItem(
|
|
4261
4368
|
section2,
|
|
@@ -4290,7 +4397,7 @@ function reportPreserveSymlinksCheck(section2) {
|
|
|
4290
4397
|
// src/commands.doctor.checks.settings-drift.ts
|
|
4291
4398
|
init_color();
|
|
4292
4399
|
import { existsSync as existsSync27, readFileSync as readFileSync10 } from "node:fs";
|
|
4293
|
-
import { join as
|
|
4400
|
+
import { join as join33 } from "node:path";
|
|
4294
4401
|
init_config();
|
|
4295
4402
|
init_utils_json();
|
|
4296
4403
|
function diffMergedSettings(merged, settings) {
|
|
@@ -4308,7 +4415,7 @@ function tryReadJson(filePath) {
|
|
|
4308
4415
|
}
|
|
4309
4416
|
}
|
|
4310
4417
|
function reportHooksBaseSelfCleanNote(section2) {
|
|
4311
|
-
const basePath =
|
|
4418
|
+
const basePath = join33(repoHome(), "shared", "settings.base.json");
|
|
4312
4419
|
const base = tryReadJson(basePath);
|
|
4313
4420
|
if (base === null) return;
|
|
4314
4421
|
if (!baseHasGsdHookEntries(base)) return;
|
|
@@ -4321,9 +4428,9 @@ function reportSettingsDriftCheck(section2) {
|
|
|
4321
4428
|
const claude = claudeHome();
|
|
4322
4429
|
const repo = repoHome();
|
|
4323
4430
|
const host = HOST;
|
|
4324
|
-
const settingsPath =
|
|
4325
|
-
const basePath =
|
|
4326
|
-
const hostPath =
|
|
4431
|
+
const settingsPath = join33(claude, "settings.json");
|
|
4432
|
+
const basePath = join33(repo, "shared", "settings.base.json");
|
|
4433
|
+
const hostPath = join33(repo, "hosts", `${host}.json`);
|
|
4327
4434
|
if (!existsSync27(settingsPath)) {
|
|
4328
4435
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
|
|
4329
4436
|
return;
|
|
@@ -4505,7 +4612,7 @@ function reportNodeEngineCheck(section2) {
|
|
|
4505
4612
|
|
|
4506
4613
|
// src/commands.doctor.checks.longpaths.ts
|
|
4507
4614
|
init_color();
|
|
4508
|
-
import { execFileSync as
|
|
4615
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
4509
4616
|
init_config();
|
|
4510
4617
|
var PROBE_TIMEOUT_MS = 3e3;
|
|
4511
4618
|
var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
|
|
@@ -4539,7 +4646,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
|
|
|
4539
4646
|
}
|
|
4540
4647
|
addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
|
|
4541
4648
|
}
|
|
4542
|
-
function reportLongPathsCheck(section2, run =
|
|
4649
|
+
function reportLongPathsCheck(section2, run = execFileSync11) {
|
|
4543
4650
|
if (process.platform !== "win32") return;
|
|
4544
4651
|
addLongpathsRow(
|
|
4545
4652
|
section2,
|
|
@@ -4561,15 +4668,15 @@ function reportSyncModality(section2) {
|
|
|
4561
4668
|
|
|
4562
4669
|
// src/commands.doctor.checks.crlf.ts
|
|
4563
4670
|
init_color();
|
|
4564
|
-
import { execFileSync as
|
|
4671
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
4565
4672
|
import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
|
|
4566
|
-
import { join as
|
|
4673
|
+
import { join as join34 } from "node:path";
|
|
4567
4674
|
init_config();
|
|
4568
4675
|
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
4569
4676
|
var GUARD_LINE = /^\*\s+-text\b/;
|
|
4570
4677
|
function hasGitattributesGuard(repo) {
|
|
4571
4678
|
try {
|
|
4572
|
-
const path =
|
|
4679
|
+
const path = join34(repo, ".gitattributes");
|
|
4573
4680
|
if (!existsSync28(path)) return false;
|
|
4574
4681
|
const content = readFileSync13(path, "utf8");
|
|
4575
4682
|
return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
|
|
@@ -4600,7 +4707,7 @@ function addExposedRow(section2, repo, verdict) {
|
|
|
4600
4707
|
const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
|
|
4601
4708
|
addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
|
|
4602
4709
|
}
|
|
4603
|
-
function reportCrlfGuardCheck(section2, run =
|
|
4710
|
+
function reportCrlfGuardCheck(section2, run = execFileSync12) {
|
|
4604
4711
|
const repo = repoHome();
|
|
4605
4712
|
if (hasGitattributesGuard(repo)) {
|
|
4606
4713
|
addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
|
|
@@ -4611,35 +4718,35 @@ function reportCrlfGuardCheck(section2, run = execFileSync11) {
|
|
|
4611
4718
|
|
|
4612
4719
|
// src/spinner.ts
|
|
4613
4720
|
init_color();
|
|
4614
|
-
import { existsSync as
|
|
4721
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
4615
4722
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
4616
4723
|
import { Worker } from "node:worker_threads";
|
|
4617
4724
|
|
|
4618
4725
|
// src/commands.push.recovery.ts
|
|
4619
4726
|
init_config();
|
|
4620
|
-
import { readFileSync as
|
|
4621
|
-
import { join as
|
|
4727
|
+
import { readFileSync as readFileSync17, rmSync as rmSync13, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4728
|
+
import { join as join40 } from "node:path";
|
|
4622
4729
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
4623
4730
|
|
|
4624
4731
|
// src/commands.push.recovery.actions.ts
|
|
4625
4732
|
init_config();
|
|
4626
|
-
import { readFileSync as
|
|
4733
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
4627
4734
|
import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep6 } from "node:path";
|
|
4628
4735
|
|
|
4629
4736
|
// src/commands.push.recovery.redact.ts
|
|
4630
4737
|
init_config();
|
|
4631
4738
|
init_config_sharedDirs_guard();
|
|
4632
|
-
import { cpSync as
|
|
4633
|
-
import { dirname as
|
|
4739
|
+
import { cpSync as cpSync8, existsSync as existsSync32, mkdirSync as mkdirSync9, statSync as statSync8 } from "node:fs";
|
|
4740
|
+
import { dirname as dirname9, join as join38, sep as sep5 } from "node:path";
|
|
4634
4741
|
|
|
4635
4742
|
// src/commands.redact.ts
|
|
4636
4743
|
init_config();
|
|
4637
4744
|
import { existsSync as existsSync30, statSync as statSync7 } from "node:fs";
|
|
4638
|
-
import { dirname as
|
|
4745
|
+
import { dirname as dirname8, join as join36 } from "node:path";
|
|
4639
4746
|
|
|
4640
4747
|
// src/commands.redact.subtree.ts
|
|
4641
|
-
import { existsSync as existsSync29, lstatSync as lstatSync11, readFileSync as readFileSync14, readdirSync as readdirSync13, statSync as statSync6, writeFileSync as
|
|
4642
|
-
import { join as
|
|
4748
|
+
import { existsSync as existsSync29, lstatSync as lstatSync11, readFileSync as readFileSync14, readdirSync as readdirSync13, statSync as statSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4749
|
+
import { join as join35 } from "node:path";
|
|
4643
4750
|
init_utils_fs();
|
|
4644
4751
|
init_utils();
|
|
4645
4752
|
function collectFiles(dir, out) {
|
|
@@ -4647,7 +4754,7 @@ function collectFiles(dir, out) {
|
|
|
4647
4754
|
const st = lstatSync11(dir);
|
|
4648
4755
|
if (!st.isDirectory()) return;
|
|
4649
4756
|
for (const entry of readdirSync13(dir)) {
|
|
4650
|
-
const abs =
|
|
4757
|
+
const abs = join35(dir, entry);
|
|
4651
4758
|
const lst = lstatSync11(abs);
|
|
4652
4759
|
if (lst.isSymbolicLink()) continue;
|
|
4653
4760
|
if (lst.isDirectory()) {
|
|
@@ -4691,7 +4798,7 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
4691
4798
|
`warning: no redaction applied to ${filePath}: finding match values were not located in the file. Inspect it manually; the push re-scan still blocks a real leak.`
|
|
4692
4799
|
);
|
|
4693
4800
|
}
|
|
4694
|
-
|
|
4801
|
+
writeFileSync5(filePath, after, "utf8");
|
|
4695
4802
|
}
|
|
4696
4803
|
}
|
|
4697
4804
|
return { total, dirty };
|
|
@@ -4699,10 +4806,10 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
4699
4806
|
|
|
4700
4807
|
// src/commands.pushed-history.ts
|
|
4701
4808
|
init_utils();
|
|
4702
|
-
import { execFileSync as
|
|
4809
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
4703
4810
|
function pushedRef(repo) {
|
|
4704
4811
|
try {
|
|
4705
|
-
const ref =
|
|
4812
|
+
const ref = execFileSync13("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
|
|
4706
4813
|
cwd: repo,
|
|
4707
4814
|
stdio: ["ignore", "pipe", "ignore"]
|
|
4708
4815
|
}).toString().trim();
|
|
@@ -4715,7 +4822,7 @@ function sessionInPushedHistory(id, repo) {
|
|
|
4715
4822
|
const ref = pushedRef(repo);
|
|
4716
4823
|
if (ref === null) return false;
|
|
4717
4824
|
try {
|
|
4718
|
-
const out =
|
|
4825
|
+
const out = execFileSync13(
|
|
4719
4826
|
"git",
|
|
4720
4827
|
[
|
|
4721
4828
|
"log",
|
|
@@ -4752,14 +4859,14 @@ init_utils_json();
|
|
|
4752
4859
|
init_utils();
|
|
4753
4860
|
function resolveLiveTranscript(id) {
|
|
4754
4861
|
try {
|
|
4755
|
-
const mapPath =
|
|
4862
|
+
const mapPath = join36(repoHome(), "path-map.json");
|
|
4756
4863
|
if (!existsSync30(mapPath)) return null;
|
|
4757
4864
|
const projects = readJson(mapPath).projects;
|
|
4758
4865
|
const claude = claudeHome();
|
|
4759
4866
|
for (const hostMap of Object.values(projects)) {
|
|
4760
4867
|
const abs = hostMap[HOST];
|
|
4761
4868
|
if (abs === void 0) continue;
|
|
4762
|
-
const live =
|
|
4869
|
+
const live = join36(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
4763
4870
|
if (existsSync30(live)) return live;
|
|
4764
4871
|
}
|
|
4765
4872
|
return null;
|
|
@@ -4790,7 +4897,7 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
4790
4897
|
process.exitCode = 1;
|
|
4791
4898
|
return;
|
|
4792
4899
|
}
|
|
4793
|
-
const sessionDir =
|
|
4900
|
+
const sessionDir = join36(dirname8(localPath), id);
|
|
4794
4901
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4795
4902
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
|
|
4796
4903
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4850,6 +4957,78 @@ init_utils();
|
|
|
4850
4957
|
|
|
4851
4958
|
// src/commands.push.recovery.seams.ts
|
|
4852
4959
|
init_push_gitleaks();
|
|
4960
|
+
|
|
4961
|
+
// src/commands.push.recovery.memory.ts
|
|
4962
|
+
init_config();
|
|
4963
|
+
init_config_sharedDirs_guard();
|
|
4964
|
+
import { cpSync as cpSync7, existsSync as existsSync31, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "node:fs";
|
|
4965
|
+
import { join as join37 } from "node:path";
|
|
4966
|
+
init_push_gitleaks_scan();
|
|
4967
|
+
init_utils_fs();
|
|
4968
|
+
init_utils_json();
|
|
4969
|
+
init_utils();
|
|
4970
|
+
var MEMORY_FINDING_PATH = /^shared\/projects\/([^/]+)\/memory\/([^/]+\.md)$/;
|
|
4971
|
+
var SAFE_MEMORY_FILENAME = /^[^/\\]+\.md$/;
|
|
4972
|
+
var MEMORY_DIR_PATH = /^shared\/projects\/[^/]+\/memory\//;
|
|
4973
|
+
function memoryFileFromFinding(f) {
|
|
4974
|
+
const m = MEMORY_FINDING_PATH.exec(f.File);
|
|
4975
|
+
if (m?.[1] === void 0 || m[2] === void 0) return null;
|
|
4976
|
+
return { logical: m[1], filename: m[2] };
|
|
4977
|
+
}
|
|
4978
|
+
function isMemoryFindingPath(f) {
|
|
4979
|
+
return MEMORY_DIR_PATH.test(f.File);
|
|
4980
|
+
}
|
|
4981
|
+
function resolveMemoryLocalPath(logical, filename, map) {
|
|
4982
|
+
assertSafeLogical(logical);
|
|
4983
|
+
if (!SAFE_MEMORY_FILENAME.test(filename) || filename.includes("..")) return null;
|
|
4984
|
+
const abs = map.projects[logical]?.[HOST];
|
|
4985
|
+
if (abs === void 0) return null;
|
|
4986
|
+
const localPath = join37(claudeHome(), "projects", encodePath(abs), "memory", filename);
|
|
4987
|
+
return existsSync31(localPath) ? localPath : null;
|
|
4988
|
+
}
|
|
4989
|
+
function preflightMemoryRedactable(f, map) {
|
|
4990
|
+
const parsed = memoryFileFromFinding(f);
|
|
4991
|
+
if (parsed === null) return "a finding is not a project-level memory file";
|
|
4992
|
+
const localPath = resolveMemoryLocalPath(parsed.logical, parsed.filename, map);
|
|
4993
|
+
if (localPath === null) {
|
|
4994
|
+
return `memory file ${parsed.logical}/memory/${parsed.filename}: local file not found or unmapped`;
|
|
4995
|
+
}
|
|
4996
|
+
return null;
|
|
4997
|
+
}
|
|
4998
|
+
function applyMemoryRedact(f, ts, map, scan = scanFile) {
|
|
4999
|
+
const refuse = (msg) => {
|
|
5000
|
+
log(msg);
|
|
5001
|
+
return false;
|
|
5002
|
+
};
|
|
5003
|
+
const parsed = memoryFileFromFinding(f);
|
|
5004
|
+
if (parsed === null) {
|
|
5005
|
+
return refuse("could not parse this finding as a project-level memory file; choose Skip.");
|
|
5006
|
+
}
|
|
5007
|
+
const { logical, filename } = parsed;
|
|
5008
|
+
const localPath = resolveMemoryLocalPath(logical, filename, map);
|
|
5009
|
+
if (localPath === null) {
|
|
5010
|
+
return refuse(
|
|
5011
|
+
`could not locate the local memory file for ${logical}/memory/${filename}; choose Skip.`
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
const findings = scan(localPath);
|
|
5015
|
+
if (findings === null) {
|
|
5016
|
+
return refuse(`re-scan of ${logical}/memory/${filename} failed; choose Skip.`);
|
|
5017
|
+
}
|
|
5018
|
+
if (findings.length === 0) {
|
|
5019
|
+
return refuse(`nothing to redact in ${logical}/memory/${filename}; choose Skip.`);
|
|
5020
|
+
}
|
|
5021
|
+
backupBeforeWrite(localPath, ts);
|
|
5022
|
+
const before = readFileSync15(localPath, "utf8");
|
|
5023
|
+
const after = applyRedactions(before, findings);
|
|
5024
|
+
writeFileSync6(localPath, after, "utf8");
|
|
5025
|
+
const stagedMemoryDir = join37(repoHome(), "shared", "projects", logical, "memory");
|
|
5026
|
+
mkdirSync8(stagedMemoryDir, { recursive: true });
|
|
5027
|
+
cpSync7(localPath, join37(stagedMemoryDir, filename), { force: true });
|
|
5028
|
+
return true;
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// src/commands.push.recovery.seams.ts
|
|
4853
5032
|
var MASK_LEAD = 4;
|
|
4854
5033
|
var MASK_BODY = "************";
|
|
4855
5034
|
var CONTEXT_WINDOW = 40;
|
|
@@ -4858,8 +5037,10 @@ function findingKey(f) {
|
|
|
4858
5037
|
return `${f.File}:${f.StartLine}:${f.StartColumn}:${f.RuleID}`;
|
|
4859
5038
|
}
|
|
4860
5039
|
var VALID_SID = /^[A-Za-z0-9_-]+$/;
|
|
5040
|
+
var SUBTREE_PATH = /^shared\/projects\/[^/]+\/([^/]+)\/.+$/;
|
|
4861
5041
|
function sessionIdFromFinding(f) {
|
|
4862
|
-
|
|
5042
|
+
if (isMemoryFindingPath(f)) return null;
|
|
5043
|
+
const m = SESSION_PATH.exec(f.File) ?? SUBTREE_PATH.exec(f.File);
|
|
4863
5044
|
if (m === null) return null;
|
|
4864
5045
|
const sid = m[1];
|
|
4865
5046
|
return VALID_SID.test(sid) ? sid : null;
|
|
@@ -4906,8 +5087,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
|
|
|
4906
5087
|
assertSafeLogical(logical);
|
|
4907
5088
|
const abs = hostMap[HOST];
|
|
4908
5089
|
if (abs === void 0) continue;
|
|
4909
|
-
if (localPath.startsWith(
|
|
4910
|
-
return
|
|
5090
|
+
if (localPath.startsWith(join38(claude, "projects", encodePath(abs)) + sep5)) {
|
|
5091
|
+
return join38(repo, "shared", "projects", logical);
|
|
4911
5092
|
}
|
|
4912
5093
|
}
|
|
4913
5094
|
return null;
|
|
@@ -4917,7 +5098,7 @@ function preflightRedactable(f, map, nowMs) {
|
|
|
4917
5098
|
if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
|
|
4918
5099
|
const localPath = resolveLiveTranscript(sid);
|
|
4919
5100
|
if (localPath === null) return `session ${sid}: local transcript not found`;
|
|
4920
|
-
const sessionDir =
|
|
5101
|
+
const sessionDir = join38(dirname9(localPath), sid);
|
|
4921
5102
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4922
5103
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4923
5104
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4947,7 +5128,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4947
5128
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4948
5129
|
);
|
|
4949
5130
|
}
|
|
4950
|
-
const sessionDir =
|
|
5131
|
+
const sessionDir = join38(dirname9(localPath), sid);
|
|
4951
5132
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4952
5133
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4953
5134
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4980,27 +5161,27 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4980
5161
|
`nothing to redact in the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4981
5162
|
);
|
|
4982
5163
|
}
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
if (
|
|
4986
|
-
|
|
5164
|
+
mkdirSync9(stagedProjectDir, { recursive: true });
|
|
5165
|
+
cpSync8(localPath, join38(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
5166
|
+
if (existsSync32(sessionDir)) {
|
|
5167
|
+
cpSync8(sessionDir, join38(stagedProjectDir, sid), { force: true, recursive: true });
|
|
4987
5168
|
}
|
|
4988
5169
|
return true;
|
|
4989
5170
|
}
|
|
4990
5171
|
|
|
4991
5172
|
// src/commands.push.recovery.drop.ts
|
|
4992
5173
|
init_config();
|
|
4993
|
-
import { rmSync as
|
|
4994
|
-
import { join as
|
|
5174
|
+
import { rmSync as rmSync12 } from "node:fs";
|
|
5175
|
+
import { join as join39 } from "node:path";
|
|
4995
5176
|
function dropSessionFromStaged(sid, map) {
|
|
4996
5177
|
const logicals = Object.keys(map.projects);
|
|
4997
5178
|
if (logicals.length === 0) return false;
|
|
4998
5179
|
const repo = repoHome();
|
|
4999
5180
|
for (const logical of logicals) {
|
|
5000
|
-
const jsonl =
|
|
5001
|
-
const dir =
|
|
5002
|
-
|
|
5003
|
-
|
|
5181
|
+
const jsonl = join39(repo, "shared", "projects", logical, `${sid}.jsonl`);
|
|
5182
|
+
const dir = join39(repo, "shared", "projects", logical, sid);
|
|
5183
|
+
rmSync12(jsonl, { force: true });
|
|
5184
|
+
rmSync12(dir, { recursive: true, force: true });
|
|
5004
5185
|
}
|
|
5005
5186
|
return true;
|
|
5006
5187
|
}
|
|
@@ -5034,7 +5215,7 @@ function makeDefaultReadLine(repo) {
|
|
|
5034
5215
|
if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep6)) {
|
|
5035
5216
|
return null;
|
|
5036
5217
|
}
|
|
5037
|
-
const content =
|
|
5218
|
+
const content = readFileSync16(target, "utf8");
|
|
5038
5219
|
const lines = content.split(/\r?\n/);
|
|
5039
5220
|
const idx = line - 1;
|
|
5040
5221
|
if (idx < 0 || idx >= lines.length) return null;
|
|
@@ -5057,6 +5238,22 @@ Finding: ${f.RuleID} in ${f.File} line ${f.StartLine}` + (sid === null ? "" : `
|
|
|
5057
5238
|
}
|
|
5058
5239
|
return actions;
|
|
5059
5240
|
}
|
|
5241
|
+
function dispatchMemory(f, action, ctx) {
|
|
5242
|
+
const parsed = memoryFileFromFinding(f);
|
|
5243
|
+
if (parsed === null) {
|
|
5244
|
+
if (isMemoryFindingPath(f)) {
|
|
5245
|
+
log(`memory path not auto-redactable: ${f.File}; scrub it by hand or choose Skip`);
|
|
5246
|
+
}
|
|
5247
|
+
return;
|
|
5248
|
+
}
|
|
5249
|
+
if (action === "drop") {
|
|
5250
|
+
log("memory files cannot be dropped; use Redact or Skip");
|
|
5251
|
+
return;
|
|
5252
|
+
}
|
|
5253
|
+
const memKey = `${parsed.logical}/${parsed.filename}`;
|
|
5254
|
+
if (ctx.redactedMemory.has(memKey)) return;
|
|
5255
|
+
if (applyMemoryRedact(f, ctx.ts, ctx.map, ctx.scan)) ctx.redactedMemory.add(memKey);
|
|
5256
|
+
}
|
|
5060
5257
|
function dispatchOne(f, ctx) {
|
|
5061
5258
|
const action = ctx.actions.get(findingKey(f)) ?? "skip";
|
|
5062
5259
|
if (action === "skip") return;
|
|
@@ -5066,7 +5263,10 @@ function dispatchOne(f, ctx) {
|
|
|
5066
5263
|
applyAllow(f, ctx.repo);
|
|
5067
5264
|
return;
|
|
5068
5265
|
}
|
|
5069
|
-
if (sid === null)
|
|
5266
|
+
if (sid === null) {
|
|
5267
|
+
dispatchMemory(f, action, ctx);
|
|
5268
|
+
return;
|
|
5269
|
+
}
|
|
5070
5270
|
if (action === "drop") {
|
|
5071
5271
|
ctx.droppedSids.add(sid);
|
|
5072
5272
|
if (ctx.drop(sid, ctx.map)) {
|
|
@@ -5091,20 +5291,46 @@ function dispatchActions(findings, actions, opts) {
|
|
|
5091
5291
|
scan,
|
|
5092
5292
|
drop,
|
|
5093
5293
|
redactedSids: /* @__PURE__ */ new Set(),
|
|
5094
|
-
droppedSids: /* @__PURE__ */ new Set()
|
|
5294
|
+
droppedSids: /* @__PURE__ */ new Set(),
|
|
5295
|
+
redactedMemory: /* @__PURE__ */ new Set()
|
|
5095
5296
|
};
|
|
5096
5297
|
for (const f of findings) {
|
|
5097
5298
|
dispatchOne(f, ctx);
|
|
5098
5299
|
}
|
|
5099
5300
|
}
|
|
5301
|
+
function redactAllDedupeKey(f) {
|
|
5302
|
+
const sid = sessionIdFromFinding(f);
|
|
5303
|
+
if (sid !== null) return sid;
|
|
5304
|
+
const parsed = memoryFileFromFinding(f);
|
|
5305
|
+
return parsed !== null ? `${parsed.logical}/${parsed.filename}` : findingKey(f);
|
|
5306
|
+
}
|
|
5307
|
+
function redactAllPreflightOne(f, map, nowMs) {
|
|
5308
|
+
if (sessionIdFromFinding(f) === null && memoryFileFromFinding(f) !== null) {
|
|
5309
|
+
return preflightMemoryRedactable(f, map);
|
|
5310
|
+
}
|
|
5311
|
+
return preflightRedactable(f, map, nowMs);
|
|
5312
|
+
}
|
|
5313
|
+
function redactAllOne(f, ts, map, nowMs, scan, redactedSids, redactedMemory) {
|
|
5314
|
+
const sid = sessionIdFromFinding(f);
|
|
5315
|
+
if (sid !== null) {
|
|
5316
|
+
if (redactedSids.has(sid)) return;
|
|
5317
|
+
if (applyRedact(f, ts, map, nowMs, scan)) redactedSids.add(sid);
|
|
5318
|
+
return;
|
|
5319
|
+
}
|
|
5320
|
+
const parsed = memoryFileFromFinding(f);
|
|
5321
|
+
if (parsed === null) return;
|
|
5322
|
+
const memKey = `${parsed.logical}/${parsed.filename}`;
|
|
5323
|
+
if (redactedMemory.has(memKey)) return;
|
|
5324
|
+
if (applyMemoryRedact(f, ts, map, scan)) redactedMemory.add(memKey);
|
|
5325
|
+
}
|
|
5100
5326
|
function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
|
|
5101
5327
|
const refusals = [];
|
|
5102
5328
|
const preflighted = /* @__PURE__ */ new Set();
|
|
5103
5329
|
for (const f of findings) {
|
|
5104
|
-
const dedupeKey =
|
|
5330
|
+
const dedupeKey = redactAllDedupeKey(f);
|
|
5105
5331
|
if (preflighted.has(dedupeKey)) continue;
|
|
5106
5332
|
preflighted.add(dedupeKey);
|
|
5107
|
-
const reason =
|
|
5333
|
+
const reason = redactAllPreflightOne(f, map, nowMs);
|
|
5108
5334
|
if (reason !== null) refusals.push(reason);
|
|
5109
5335
|
}
|
|
5110
5336
|
if (refusals.length > 0) {
|
|
@@ -5115,10 +5341,9 @@ function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
|
|
|
5115
5341
|
);
|
|
5116
5342
|
}
|
|
5117
5343
|
const redactedSids = /* @__PURE__ */ new Set();
|
|
5344
|
+
const redactedMemory = /* @__PURE__ */ new Set();
|
|
5118
5345
|
for (const f of findings) {
|
|
5119
|
-
|
|
5120
|
-
if (sid === null || redactedSids.has(sid)) continue;
|
|
5121
|
-
if (applyRedact(f, ts, map, nowMs, scan)) redactedSids.add(sid);
|
|
5346
|
+
redactAllOne(f, ts, map, nowMs, scan, redactedSids, redactedMemory);
|
|
5122
5347
|
}
|
|
5123
5348
|
}
|
|
5124
5349
|
|
|
@@ -5155,10 +5380,10 @@ function applyThenRescan(scanVerdict, repoHome2) {
|
|
|
5155
5380
|
return next;
|
|
5156
5381
|
}
|
|
5157
5382
|
function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
5158
|
-
const ignPath =
|
|
5383
|
+
const ignPath = join40(repoHome2, ".gitleaksignore");
|
|
5159
5384
|
let before;
|
|
5160
5385
|
try {
|
|
5161
|
-
before =
|
|
5386
|
+
before = readFileSync17(ignPath, "utf8");
|
|
5162
5387
|
} catch {
|
|
5163
5388
|
before = null;
|
|
5164
5389
|
}
|
|
@@ -5166,8 +5391,8 @@ function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
|
5166
5391
|
try {
|
|
5167
5392
|
return applyThenRescan(scanVerdict, repoHome2);
|
|
5168
5393
|
} catch (err) {
|
|
5169
|
-
if (before === null)
|
|
5170
|
-
else
|
|
5394
|
+
if (before === null) rmSync13(ignPath, { force: true });
|
|
5395
|
+
else writeFileSync7(ignPath, before, "utf8");
|
|
5171
5396
|
throw err;
|
|
5172
5397
|
}
|
|
5173
5398
|
}
|
|
@@ -5254,7 +5479,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
5254
5479
|
`);
|
|
5255
5480
|
}
|
|
5256
5481
|
function resolveWorkerPath(deps = {}) {
|
|
5257
|
-
const check = deps.existsSyncFn ??
|
|
5482
|
+
const check = deps.existsSyncFn ?? existsSync33;
|
|
5258
5483
|
const base = deps.baseUrl ?? import.meta.url;
|
|
5259
5484
|
const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
|
|
5260
5485
|
if (check(mjs)) return mjs;
|
|
@@ -5320,9 +5545,9 @@ function withSpinner(label, fn, deps) {
|
|
|
5320
5545
|
|
|
5321
5546
|
// src/commands.doctor.gitleaks-version.ts
|
|
5322
5547
|
init_color();
|
|
5323
|
-
import { execFileSync as
|
|
5324
|
-
import { existsSync as
|
|
5325
|
-
import { join as
|
|
5548
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
5549
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
5550
|
+
import { join as join41 } from "node:path";
|
|
5326
5551
|
init_config();
|
|
5327
5552
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
5328
5553
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -5331,7 +5556,7 @@ function majorMinorOf(value) {
|
|
|
5331
5556
|
return m === null ? null : [m[1], m[2]];
|
|
5332
5557
|
}
|
|
5333
5558
|
function readGitleaksVersion(run, tomlExists) {
|
|
5334
|
-
const tomlPath =
|
|
5559
|
+
const tomlPath = join41(repoHome(), ".gitleaks.toml");
|
|
5335
5560
|
const args = ["version"];
|
|
5336
5561
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
5337
5562
|
try {
|
|
@@ -5343,7 +5568,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
5343
5568
|
return null;
|
|
5344
5569
|
}
|
|
5345
5570
|
}
|
|
5346
|
-
function reportGitleaksVersionCheck(section2, run =
|
|
5571
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync14, tomlExists = existsSync34) {
|
|
5347
5572
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
5348
5573
|
if (raw === null) return;
|
|
5349
5574
|
const local = majorMinorOf(raw);
|
|
@@ -5363,7 +5588,7 @@ function reportGitleaksVersionCheck(section2, run = execFileSync13, tomlExists =
|
|
|
5363
5588
|
|
|
5364
5589
|
// src/commands.doctor.checks.deps.ts
|
|
5365
5590
|
init_color();
|
|
5366
|
-
import { execFileSync as
|
|
5591
|
+
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
5367
5592
|
var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
|
|
5368
5593
|
var PROBE_TIMEOUT_MS3 = 3e3;
|
|
5369
5594
|
var FETCHER_BASE = "HTTP fetcher";
|
|
@@ -5400,7 +5625,7 @@ function reportFetcherRow(section2, run) {
|
|
|
5400
5625
|
);
|
|
5401
5626
|
}
|
|
5402
5627
|
}
|
|
5403
|
-
function reportOptionalDeps(section2, run =
|
|
5628
|
+
function reportOptionalDeps(section2, run = execFileSync15) {
|
|
5404
5629
|
const gh = probeOptionalDep("gh", run);
|
|
5405
5630
|
if (gh.status === "present") {
|
|
5406
5631
|
addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
|
|
@@ -5415,11 +5640,11 @@ function reportOptionalDeps(section2, run = execFileSync14) {
|
|
|
5415
5640
|
|
|
5416
5641
|
// src/commands.doctor.actions-drift.ts
|
|
5417
5642
|
init_color();
|
|
5418
|
-
import { execFileSync as
|
|
5643
|
+
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
5419
5644
|
init_config();
|
|
5420
5645
|
|
|
5421
5646
|
// src/gh-actions.ts
|
|
5422
|
-
import { execFileSync as
|
|
5647
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
5423
5648
|
var GH_TIMEOUT_MS = 5e3;
|
|
5424
5649
|
function parseGitHubRemote(remoteUrl) {
|
|
5425
5650
|
const normalized = remoteUrl.trim().replace(/\/$/, "");
|
|
@@ -5427,7 +5652,7 @@ function parseGitHubRemote(remoteUrl) {
|
|
|
5427
5652
|
if (m === null) return null;
|
|
5428
5653
|
return { owner: m[1], repo: m[2] };
|
|
5429
5654
|
}
|
|
5430
|
-
function ghAuthStatus(run =
|
|
5655
|
+
function ghAuthStatus(run = execFileSync16) {
|
|
5431
5656
|
try {
|
|
5432
5657
|
run("gh", ["auth", "status"], {
|
|
5433
5658
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -5441,7 +5666,7 @@ function ghAuthStatus(run = execFileSync15) {
|
|
|
5441
5666
|
return "gh-probe-error";
|
|
5442
5667
|
}
|
|
5443
5668
|
}
|
|
5444
|
-
function isRepoPrivate(ref, run =
|
|
5669
|
+
function isRepoPrivate(ref, run = execFileSync16) {
|
|
5445
5670
|
const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
|
|
5446
5671
|
stdio: ["ignore", "pipe", "ignore"],
|
|
5447
5672
|
timeout: GH_TIMEOUT_MS
|
|
@@ -5449,7 +5674,7 @@ function isRepoPrivate(ref, run = execFileSync15) {
|
|
|
5449
5674
|
const parsed = JSON.parse(out);
|
|
5450
5675
|
return parsed.isPrivate === true;
|
|
5451
5676
|
}
|
|
5452
|
-
function isActionsEnabled(ref, run =
|
|
5677
|
+
function isActionsEnabled(ref, run = execFileSync16) {
|
|
5453
5678
|
const out = run(
|
|
5454
5679
|
"gh",
|
|
5455
5680
|
["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
|
|
@@ -5457,7 +5682,7 @@ function isActionsEnabled(ref, run = execFileSync15) {
|
|
|
5457
5682
|
).toString().trim();
|
|
5458
5683
|
return out === "true";
|
|
5459
5684
|
}
|
|
5460
|
-
function disableActions(ref, run =
|
|
5685
|
+
function disableActions(ref, run = execFileSync16) {
|
|
5461
5686
|
run(
|
|
5462
5687
|
"gh",
|
|
5463
5688
|
[
|
|
@@ -5471,7 +5696,7 @@ function disableActions(ref, run = execFileSync15) {
|
|
|
5471
5696
|
{ stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
|
|
5472
5697
|
);
|
|
5473
5698
|
}
|
|
5474
|
-
function readOriginRemote(cwd, run =
|
|
5699
|
+
function readOriginRemote(cwd, run = execFileSync16) {
|
|
5475
5700
|
return run("git", ["remote", "get-url", "origin"], {
|
|
5476
5701
|
cwd,
|
|
5477
5702
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -5479,7 +5704,7 @@ function readOriginRemote(cwd, run = execFileSync15) {
|
|
|
5479
5704
|
}
|
|
5480
5705
|
|
|
5481
5706
|
// src/commands.doctor.actions-drift.ts
|
|
5482
|
-
function reportActionsDrift(section2, run =
|
|
5707
|
+
function reportActionsDrift(section2, run = execFileSync17) {
|
|
5483
5708
|
let remote;
|
|
5484
5709
|
try {
|
|
5485
5710
|
remote = readOriginRemote(repoHome(), run);
|
|
@@ -5569,8 +5794,8 @@ function gatherDoctorSections(opts) {
|
|
|
5569
5794
|
reportLongPathsCheck(host);
|
|
5570
5795
|
reportCrlfGuardCheck(host);
|
|
5571
5796
|
const links = section("Shared links");
|
|
5572
|
-
const mapPath =
|
|
5573
|
-
const rawMap =
|
|
5797
|
+
const mapPath = join42(repoHome(), "path-map.json");
|
|
5798
|
+
const rawMap = existsSync35(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
5574
5799
|
const map = rawMap ?? { projects: {} };
|
|
5575
5800
|
reportSharedLinks(links, map);
|
|
5576
5801
|
reportDroppedNamesMigration(links);
|
|
@@ -5666,15 +5891,15 @@ function parseDoctorArgs(args) {
|
|
|
5666
5891
|
|
|
5667
5892
|
// src/commands.drop-session.ts
|
|
5668
5893
|
init_config();
|
|
5669
|
-
import { execFileSync as
|
|
5670
|
-
import { existsSync as
|
|
5671
|
-
import { join as
|
|
5894
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
5895
|
+
import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync9 } from "node:fs";
|
|
5896
|
+
import { join as join44, relative as relative6 } from "node:path";
|
|
5672
5897
|
|
|
5673
5898
|
// src/commands.drop-session.git.ts
|
|
5674
|
-
import { execFileSync as
|
|
5899
|
+
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
5675
5900
|
function expandStagedDir(dirRel, repo) {
|
|
5676
5901
|
try {
|
|
5677
|
-
const out =
|
|
5902
|
+
const out = execFileSync18("git", ["ls-files", "-z", "--", dirRel], {
|
|
5678
5903
|
cwd: repo,
|
|
5679
5904
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5680
5905
|
});
|
|
@@ -5686,7 +5911,7 @@ function expandStagedDir(dirRel, repo) {
|
|
|
5686
5911
|
function isTrackedInHead(rel, repo) {
|
|
5687
5912
|
try {
|
|
5688
5913
|
const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
|
|
5689
|
-
|
|
5914
|
+
execFileSync18("git", ["cat-file", "-e", `HEAD:${treePath}`], {
|
|
5690
5915
|
cwd: repo,
|
|
5691
5916
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5692
5917
|
});
|
|
@@ -5697,7 +5922,7 @@ function isTrackedInHead(rel, repo) {
|
|
|
5697
5922
|
}
|
|
5698
5923
|
function isInIndex(rel, repo) {
|
|
5699
5924
|
try {
|
|
5700
|
-
const out =
|
|
5925
|
+
const out = execFileSync18("git", ["ls-files", "--", rel], {
|
|
5701
5926
|
cwd: repo,
|
|
5702
5927
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5703
5928
|
});
|
|
@@ -5711,8 +5936,8 @@ function isInIndex(rel, repo) {
|
|
|
5711
5936
|
init_config();
|
|
5712
5937
|
init_utils();
|
|
5713
5938
|
init_utils_json();
|
|
5714
|
-
import { existsSync as
|
|
5715
|
-
import { join as
|
|
5939
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
5940
|
+
import { join as join43 } from "node:path";
|
|
5716
5941
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
5717
5942
|
function reportScrubHint(id, matches) {
|
|
5718
5943
|
const live = resolveLiveTranscript2(id, matches);
|
|
@@ -5728,8 +5953,8 @@ function reportScrubHint(id, matches) {
|
|
|
5728
5953
|
}
|
|
5729
5954
|
function resolveLiveTranscript2(id, matches) {
|
|
5730
5955
|
try {
|
|
5731
|
-
const mapPath =
|
|
5732
|
-
if (!
|
|
5956
|
+
const mapPath = join43(repoHome(), "path-map.json");
|
|
5957
|
+
if (!existsSync36(mapPath)) return null;
|
|
5733
5958
|
const projects = readJson(mapPath).projects;
|
|
5734
5959
|
const claude = claudeHome();
|
|
5735
5960
|
for (const rel of matches) {
|
|
@@ -5737,8 +5962,8 @@ function resolveLiveTranscript2(id, matches) {
|
|
|
5737
5962
|
if (logical === void 0) continue;
|
|
5738
5963
|
const abs = projects[logical]?.[HOST];
|
|
5739
5964
|
if (abs === void 0) continue;
|
|
5740
|
-
const live =
|
|
5741
|
-
if (
|
|
5965
|
+
const live = join43(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
5966
|
+
if (existsSync36(live)) return live;
|
|
5742
5967
|
}
|
|
5743
5968
|
return null;
|
|
5744
5969
|
} catch {
|
|
@@ -5754,12 +5979,12 @@ function cmdDropSession(id) {
|
|
|
5754
5979
|
process.exit(1);
|
|
5755
5980
|
}
|
|
5756
5981
|
const repo = repoHome();
|
|
5757
|
-
if (!
|
|
5982
|
+
if (!existsSync37(repo)) die(`repo not cloned at ${repo}`);
|
|
5758
5983
|
const handle = acquireLock("drop-session");
|
|
5759
5984
|
if (handle === null) process.exit(0);
|
|
5760
5985
|
try {
|
|
5761
|
-
const repoProjects =
|
|
5762
|
-
if (!
|
|
5986
|
+
const repoProjects = join44(repo, "shared", "projects");
|
|
5987
|
+
if (!existsSync37(repoProjects)) {
|
|
5763
5988
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
5764
5989
|
}
|
|
5765
5990
|
const matches = collectMatches(repoProjects, id, repo);
|
|
@@ -5782,12 +6007,12 @@ function cmdDropSession(id) {
|
|
|
5782
6007
|
function collectMatches(repoProjects, id, repo) {
|
|
5783
6008
|
const matches = [];
|
|
5784
6009
|
for (const logical of readdirSync14(repoProjects)) {
|
|
5785
|
-
const candidate =
|
|
5786
|
-
if (
|
|
6010
|
+
const candidate = join44(repoProjects, logical, `${id}.jsonl`);
|
|
6011
|
+
if (existsSync37(candidate)) {
|
|
5787
6012
|
matches.push(relative6(repo, candidate));
|
|
5788
6013
|
}
|
|
5789
|
-
const dir =
|
|
5790
|
-
if (
|
|
6014
|
+
const dir = join44(repoProjects, logical, id);
|
|
6015
|
+
if (existsSync37(dir) && statSync9(dir).isDirectory()) {
|
|
5791
6016
|
const dirRel = relative6(repo, dir);
|
|
5792
6017
|
const staged = expandStagedDir(dirRel, repo);
|
|
5793
6018
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -5803,12 +6028,12 @@ function unstageOne(rel, repo) {
|
|
|
5803
6028
|
}
|
|
5804
6029
|
try {
|
|
5805
6030
|
if (isTrackedInHead(rel, repo)) {
|
|
5806
|
-
|
|
6031
|
+
execFileSync19("git", ["restore", "--staged", "--worktree", "--", rel], {
|
|
5807
6032
|
cwd: repo,
|
|
5808
6033
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5809
6034
|
});
|
|
5810
6035
|
} else {
|
|
5811
|
-
|
|
6036
|
+
execFileSync19("git", ["rm", "--cached", "-f", "--", rel], {
|
|
5812
6037
|
cwd: repo,
|
|
5813
6038
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5814
6039
|
});
|
|
@@ -5822,8 +6047,8 @@ function unstageOne(rel, repo) {
|
|
|
5822
6047
|
}
|
|
5823
6048
|
|
|
5824
6049
|
// src/commands.pull.ts
|
|
5825
|
-
import { existsSync as
|
|
5826
|
-
import { join as
|
|
6050
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync12 } from "node:fs";
|
|
6051
|
+
import { join as join50 } from "node:path";
|
|
5827
6052
|
|
|
5828
6053
|
// src/commands.push.sections.ts
|
|
5829
6054
|
init_color();
|
|
@@ -5931,18 +6156,18 @@ init_config();
|
|
|
5931
6156
|
|
|
5932
6157
|
// src/extras-sync.ts
|
|
5933
6158
|
init_config();
|
|
5934
|
-
import { existsSync as
|
|
5935
|
-
import { join as
|
|
6159
|
+
import { existsSync as existsSync39, statSync as statSync10 } from "node:fs";
|
|
6160
|
+
import { join as join47 } from "node:path";
|
|
5936
6161
|
init_utils();
|
|
5937
6162
|
init_utils_json();
|
|
5938
6163
|
|
|
5939
6164
|
// src/extras-sync.remap.ts
|
|
5940
6165
|
init_config();
|
|
5941
|
-
import { existsSync as
|
|
5942
|
-
import { dirname as
|
|
6166
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync10, readdirSync as readdirSync15, readFileSync as readFileSync18, realpathSync as realpathSync4, rmSync as rmSync14 } from "node:fs";
|
|
6167
|
+
import { dirname as dirname10, join as join46, sep as sep8 } from "node:path";
|
|
5943
6168
|
|
|
5944
6169
|
// src/extras-sync.planning-diff.ts
|
|
5945
|
-
import { join as
|
|
6170
|
+
import { join as join45, normalize as normalize2, sep as sep7 } from "node:path";
|
|
5946
6171
|
init_utils();
|
|
5947
6172
|
function processRecord(fields, i, changed, deleted) {
|
|
5948
6173
|
const status = fields[i];
|
|
@@ -5994,7 +6219,7 @@ function planningDeleteTargets(opts) {
|
|
|
5994
6219
|
const { deleted } = parsePlanningDiff(raw);
|
|
5995
6220
|
const logicalPrefix = "shared/extras/" + logical + "/";
|
|
5996
6221
|
const prefix = logicalPrefix + ".planning/";
|
|
5997
|
-
const planningRoot =
|
|
6222
|
+
const planningRoot = join45(localRoot, ".planning");
|
|
5998
6223
|
const planningRootBoundary = planningRoot + sep7;
|
|
5999
6224
|
const targets = [];
|
|
6000
6225
|
for (const repoPath of deleted) {
|
|
@@ -6002,7 +6227,7 @@ function planningDeleteTargets(opts) {
|
|
|
6002
6227
|
continue;
|
|
6003
6228
|
}
|
|
6004
6229
|
const remainder = repoPath.slice(logicalPrefix.length);
|
|
6005
|
-
const candidate =
|
|
6230
|
+
const candidate = join45(localRoot, remainder);
|
|
6006
6231
|
const resolved = normalize2(candidate);
|
|
6007
6232
|
if (!resolved.startsWith(planningRootBoundary)) {
|
|
6008
6233
|
throw new NomadFatal(
|
|
@@ -6023,7 +6248,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
6023
6248
|
const would = [];
|
|
6024
6249
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
6025
6250
|
const { src, dst } = paths(t);
|
|
6026
|
-
if (!
|
|
6251
|
+
if (!existsSync38(src)) continue;
|
|
6027
6252
|
const item2 = `${t.logical}/${t.dirname}`;
|
|
6028
6253
|
if (dryRun) {
|
|
6029
6254
|
would.push(item2);
|
|
@@ -6036,15 +6261,15 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
6036
6261
|
return { ...counts, done, would };
|
|
6037
6262
|
}
|
|
6038
6263
|
function pruneEmptyAncestors(target, planningRoot) {
|
|
6039
|
-
let dir =
|
|
6264
|
+
let dir = dirname10(target);
|
|
6040
6265
|
while (dir !== planningRoot && dir.startsWith(planningRoot + sep8)) {
|
|
6041
6266
|
try {
|
|
6042
6267
|
if (readdirSync15(dir).length > 0) break;
|
|
6043
|
-
|
|
6268
|
+
rmSync14(dir, { recursive: true, force: true });
|
|
6044
6269
|
} catch {
|
|
6045
6270
|
break;
|
|
6046
6271
|
}
|
|
6047
|
-
dir =
|
|
6272
|
+
dir = dirname10(dir);
|
|
6048
6273
|
}
|
|
6049
6274
|
}
|
|
6050
6275
|
function tryRealpath(dir) {
|
|
@@ -6058,20 +6283,20 @@ function isInsidePlanningRoot(parentReal, rootReal) {
|
|
|
6058
6283
|
return parentReal === rootReal || parentReal.startsWith(rootReal + sep8);
|
|
6059
6284
|
}
|
|
6060
6285
|
function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
6061
|
-
if (
|
|
6062
|
-
const parentReal = tryRealpath(
|
|
6286
|
+
if (existsSync38(repoCounterpart)) return;
|
|
6287
|
+
const parentReal = tryRealpath(dirname10(target));
|
|
6063
6288
|
if (parentReal === void 0) return;
|
|
6064
6289
|
const rootReal = tryRealpath(planningRoot);
|
|
6065
6290
|
if (rootReal === void 0) return;
|
|
6066
6291
|
if (!isInsidePlanningRoot(parentReal, rootReal)) return;
|
|
6067
|
-
|
|
6292
|
+
rmSync14(target, { recursive: true, force: true });
|
|
6068
6293
|
pruneEmptyAncestors(target, planningRoot);
|
|
6069
6294
|
}
|
|
6070
6295
|
function localDivergesFromPreDelete(target, pre, repoRel, repo) {
|
|
6071
|
-
if (!
|
|
6296
|
+
if (!existsSync38(target)) return false;
|
|
6072
6297
|
try {
|
|
6073
6298
|
const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
|
|
6074
|
-
return !
|
|
6299
|
+
return !readFileSync18(target).equals(preBlob);
|
|
6075
6300
|
} catch {
|
|
6076
6301
|
return true;
|
|
6077
6302
|
}
|
|
@@ -6113,7 +6338,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
|
|
|
6113
6338
|
return kept;
|
|
6114
6339
|
}
|
|
6115
6340
|
function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
6116
|
-
const repoExtras =
|
|
6341
|
+
const repoExtras = join46(repo, "shared", "extras");
|
|
6117
6342
|
for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
|
|
6118
6343
|
if (t.dirname !== ".planning") continue;
|
|
6119
6344
|
let raw;
|
|
@@ -6128,14 +6353,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
|
6128
6353
|
}
|
|
6129
6354
|
const pairs = deletePairsFor(t, raw);
|
|
6130
6355
|
if (pairs.length === 0) continue;
|
|
6131
|
-
backupExtrasWrite(
|
|
6132
|
-
const planningRoot =
|
|
6356
|
+
backupExtrasWrite(join46(t.localRoot, t.dirname), ts, t.localRoot);
|
|
6357
|
+
const planningRoot = join46(t.localRoot, ".planning");
|
|
6133
6358
|
for (const { target, relToLocal, repoRel } of pairs) {
|
|
6134
6359
|
if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
|
|
6135
6360
|
warn(keptDeleteWarnLine(t.logical, relToLocal));
|
|
6136
6361
|
continue;
|
|
6137
6362
|
}
|
|
6138
|
-
deletePlanningTarget(target, planningRoot,
|
|
6363
|
+
deletePlanningTarget(target, planningRoot, join46(repoExtras, t.logical, relToLocal));
|
|
6139
6364
|
}
|
|
6140
6365
|
}
|
|
6141
6366
|
}
|
|
@@ -6144,14 +6369,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
6144
6369
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
6145
6370
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
6146
6371
|
const repo = repoHome();
|
|
6147
|
-
const repoExtras =
|
|
6148
|
-
if (!dryRun)
|
|
6372
|
+
const repoExtras = join46(repo, "shared", "extras");
|
|
6373
|
+
if (!dryRun) mkdirSync10(repoExtras, { recursive: true });
|
|
6149
6374
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
6150
6375
|
v,
|
|
6151
6376
|
dryRun,
|
|
6152
|
-
({ localRoot, logical, dirname:
|
|
6153
|
-
src:
|
|
6154
|
-
dst:
|
|
6377
|
+
({ localRoot, logical, dirname: dirname11 }) => ({
|
|
6378
|
+
src: join46(localRoot, dirname11),
|
|
6379
|
+
dst: join46(repoExtras, logical, dirname11)
|
|
6155
6380
|
}),
|
|
6156
6381
|
(dst) => backupRepoWrite(dst, ts, repo),
|
|
6157
6382
|
// Push copy routing per extra type:
|
|
@@ -6163,7 +6388,7 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
6163
6388
|
// (enforceAllowList / blockSetFor in commands.push.allowlist.ts)
|
|
6164
6389
|
// remains the hard security boundary.
|
|
6165
6390
|
// All others: copyExtrasFiltered with per-extra denylist.
|
|
6166
|
-
(src, dst,
|
|
6391
|
+
(src, dst, dirname11) => dirname11 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname11)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname11))
|
|
6167
6392
|
);
|
|
6168
6393
|
return { unmapped, skipped, pushed: done, wouldPush: would };
|
|
6169
6394
|
}
|
|
@@ -6179,9 +6404,9 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
6179
6404
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
6180
6405
|
v,
|
|
6181
6406
|
dryRun,
|
|
6182
|
-
({ localRoot, logical, dirname:
|
|
6183
|
-
src:
|
|
6184
|
-
dst:
|
|
6407
|
+
({ localRoot, logical, dirname: dirname11 }) => ({
|
|
6408
|
+
src: join46(repo, "shared", "extras", logical, dirname11),
|
|
6409
|
+
dst: join46(localRoot, dirname11)
|
|
6185
6410
|
}),
|
|
6186
6411
|
// Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
|
|
6187
6412
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -6199,12 +6424,12 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
6199
6424
|
// copyExtrasFileSkipDiverged keeps a locally-edited file rather than
|
|
6200
6425
|
// clobbering it, so the divergence WARN's keep-local promise holds for
|
|
6201
6426
|
// every extra, not just `.planning`.
|
|
6202
|
-
(src, dst,
|
|
6203
|
-
if (
|
|
6204
|
-
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(
|
|
6205
|
-
if (
|
|
6427
|
+
(src, dst, dirname11) => {
|
|
6428
|
+
if (dirname11 === ".claude")
|
|
6429
|
+
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname11));
|
|
6430
|
+
if (dirname11 === ".planning") {
|
|
6206
6431
|
const divergedSet = new Set(listDivergingModified(dst, src));
|
|
6207
|
-
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(
|
|
6432
|
+
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname11), divergedSet);
|
|
6208
6433
|
}
|
|
6209
6434
|
return copyExtrasFileSkipDiverged(src, dst);
|
|
6210
6435
|
}
|
|
@@ -6228,20 +6453,20 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
6228
6453
|
const v = loadValidatedExtras({});
|
|
6229
6454
|
if (v === null) return 0;
|
|
6230
6455
|
const counts = { unmapped: 0, skipped: 0 };
|
|
6231
|
-
const backupRoot =
|
|
6456
|
+
const backupRoot = join47(backupBase(), ts, "extras");
|
|
6232
6457
|
const repo = repoHome();
|
|
6233
6458
|
let divergedCount = 0;
|
|
6234
|
-
for (const { logical, localRoot, dirname:
|
|
6235
|
-
const local =
|
|
6236
|
-
const repoEntry =
|
|
6237
|
-
if (!
|
|
6459
|
+
for (const { logical, localRoot, dirname: dirname11 } of eachExtrasTarget(v, counts)) {
|
|
6460
|
+
const local = join47(localRoot, dirname11);
|
|
6461
|
+
const repoEntry = join47(repo, "shared", "extras", logical, dirname11);
|
|
6462
|
+
if (!existsSync39(local) || !existsSync39(repoEntry)) continue;
|
|
6238
6463
|
const modified = listDivergingModified(local, repoEntry);
|
|
6239
6464
|
if (modified.length === 0) continue;
|
|
6240
6465
|
divergedCount += modified.length;
|
|
6241
|
-
const projectBackupRoot =
|
|
6466
|
+
const projectBackupRoot = join47(backupRoot, encodePath(localRoot));
|
|
6242
6467
|
warn(
|
|
6243
6468
|
divergenceWarnLine({
|
|
6244
|
-
dirname:
|
|
6469
|
+
dirname: dirname11,
|
|
6245
6470
|
logical,
|
|
6246
6471
|
isDir: statSync10(local).isDirectory(),
|
|
6247
6472
|
count: modified.length,
|
|
@@ -6260,8 +6485,8 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
6260
6485
|
|
|
6261
6486
|
// src/skills-sync.ts
|
|
6262
6487
|
init_config();
|
|
6263
|
-
import { existsSync as
|
|
6264
|
-
import { join as
|
|
6488
|
+
import { existsSync as existsSync40, lstatSync as lstatSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync16, rmSync as rmSync15 } from "node:fs";
|
|
6489
|
+
import { join as join48 } from "node:path";
|
|
6265
6490
|
init_utils_fs();
|
|
6266
6491
|
function isGsdOwned(name) {
|
|
6267
6492
|
return name.startsWith(GSD_PREFIX);
|
|
@@ -6281,30 +6506,30 @@ function copySkillsPull(src, dst) {
|
|
|
6281
6506
|
copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
|
|
6282
6507
|
}
|
|
6283
6508
|
function syncSkillsPull(ts) {
|
|
6284
|
-
const sharedSkills =
|
|
6285
|
-
if (!
|
|
6286
|
-
const localSkills =
|
|
6509
|
+
const sharedSkills = join48(repoHome(), "shared", "skills");
|
|
6510
|
+
if (!existsSync40(sharedSkills)) return;
|
|
6511
|
+
const localSkills = join48(claudeHome(), "skills");
|
|
6287
6512
|
const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
6288
6513
|
if (dstStat?.isSymbolicLink() === true) {
|
|
6289
6514
|
backupBeforeWrite(localSkills, ts);
|
|
6290
|
-
|
|
6291
|
-
|
|
6515
|
+
rmSync15(localSkills, { recursive: true, force: true });
|
|
6516
|
+
mkdirSync11(localSkills, { recursive: true });
|
|
6292
6517
|
}
|
|
6293
6518
|
copySkillsPull(sharedSkills, localSkills);
|
|
6294
6519
|
}
|
|
6295
6520
|
function syncSkillsPush() {
|
|
6296
|
-
const localSkills =
|
|
6521
|
+
const localSkills = join48(claudeHome(), "skills");
|
|
6297
6522
|
const stat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
6298
6523
|
if (stat === void 0) return;
|
|
6299
6524
|
if (stat.isSymbolicLink()) return;
|
|
6300
|
-
const sharedSkills =
|
|
6525
|
+
const sharedSkills = join48(repoHome(), "shared", "skills");
|
|
6301
6526
|
copySkillsPush(localSkills, sharedSkills);
|
|
6302
6527
|
}
|
|
6303
6528
|
|
|
6304
6529
|
// src/preview.ts
|
|
6305
6530
|
init_config();
|
|
6306
|
-
import { existsSync as
|
|
6307
|
-
import { join as
|
|
6531
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
6532
|
+
import { join as join49 } from "node:path";
|
|
6308
6533
|
|
|
6309
6534
|
// node_modules/diff/libesm/diff/base.js
|
|
6310
6535
|
var Diff = class {
|
|
@@ -6590,7 +6815,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
6590
6815
|
return lines.join("\n");
|
|
6591
6816
|
}
|
|
6592
6817
|
function readJsonOrNull(path) {
|
|
6593
|
-
if (!
|
|
6818
|
+
if (!existsSync41(path)) return null;
|
|
6594
6819
|
try {
|
|
6595
6820
|
return readJson(path);
|
|
6596
6821
|
} catch {
|
|
@@ -6604,12 +6829,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
6604
6829
|
}
|
|
6605
6830
|
const notes = [];
|
|
6606
6831
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
6607
|
-
if (hostOverrides === null &&
|
|
6832
|
+
if (hostOverrides === null && existsSync41(hostPath)) {
|
|
6608
6833
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
6609
6834
|
}
|
|
6610
6835
|
const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
|
|
6611
6836
|
const current = readJsonOrNull(settingsPath);
|
|
6612
|
-
if (current === null &&
|
|
6837
|
+
if (current === null && existsSync41(settingsPath)) {
|
|
6613
6838
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
6614
6839
|
}
|
|
6615
6840
|
const strippedCurrent = stripGsdHookEntries(current ?? {});
|
|
@@ -6651,9 +6876,9 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
6651
6876
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
6652
6877
|
});
|
|
6653
6878
|
const settingsResult = previewSettings(
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6879
|
+
join49(repo, "shared", "settings.base.json"),
|
|
6880
|
+
join49(repo, "hosts", `${HOST}.json`),
|
|
6881
|
+
join49(claude, "settings.json")
|
|
6657
6882
|
);
|
|
6658
6883
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
6659
6884
|
const sessions = section("Sessions");
|
|
@@ -6668,7 +6893,7 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
6668
6893
|
const extras = section("Extras");
|
|
6669
6894
|
let extrasSkipped = 0;
|
|
6670
6895
|
let extrasUnmapped = 0;
|
|
6671
|
-
if (
|
|
6896
|
+
if (existsSync41(join49(repo, "path-map.json")) && existsSync41(join49(repo, "shared", "extras"))) {
|
|
6672
6897
|
const extrasResult = remapExtrasPull(ts, { dryRun: true });
|
|
6673
6898
|
for (const entry of extrasResult.wouldPull) {
|
|
6674
6899
|
addItem(extras, entry);
|
|
@@ -6693,9 +6918,9 @@ init_config();
|
|
|
6693
6918
|
init_commands_pull_wedge();
|
|
6694
6919
|
init_utils();
|
|
6695
6920
|
init_utils_fs();
|
|
6696
|
-
import { execFileSync as
|
|
6921
|
+
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
6697
6922
|
function gitCapture(args, cwd) {
|
|
6698
|
-
return
|
|
6923
|
+
return execFileSync20("git", args, {
|
|
6699
6924
|
cwd,
|
|
6700
6925
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6701
6926
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -6882,9 +7107,9 @@ function runPullCore(opts = {}) {
|
|
|
6882
7107
|
const ts = freshBackupTs(backup);
|
|
6883
7108
|
handleWedge(repo, forceRemote);
|
|
6884
7109
|
if (!dryRun) {
|
|
6885
|
-
const backupRoot =
|
|
7110
|
+
const backupRoot = join50(backup, ts);
|
|
6886
7111
|
try {
|
|
6887
|
-
|
|
7112
|
+
mkdirSync12(backupRoot, { recursive: true });
|
|
6888
7113
|
} catch (err) {
|
|
6889
7114
|
die(`could not create backup dir: ${err.message}`);
|
|
6890
7115
|
}
|
|
@@ -6897,8 +7122,8 @@ function runPullCore(opts = {}) {
|
|
|
6897
7122
|
const prePostHeads = capturePrePostHeads(repo, () => {
|
|
6898
7123
|
gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
|
|
6899
7124
|
});
|
|
6900
|
-
const mapPath =
|
|
6901
|
-
const map =
|
|
7125
|
+
const mapPath = join50(repo, "path-map.json");
|
|
7126
|
+
const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
6902
7127
|
const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
|
|
6903
7128
|
if (dryRun) {
|
|
6904
7129
|
computePreview(ts, map, "pull");
|
|
@@ -6924,8 +7149,8 @@ function runPullCore(opts = {}) {
|
|
|
6924
7149
|
}
|
|
6925
7150
|
function cmdPull(opts = {}) {
|
|
6926
7151
|
const repo = repoHome();
|
|
6927
|
-
if (!
|
|
6928
|
-
if (!
|
|
7152
|
+
if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
|
|
7153
|
+
if (!existsSync42(join50(repo, "shared", "settings.base.json"))) {
|
|
6929
7154
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
6930
7155
|
}
|
|
6931
7156
|
const handle = acquireLock("pull");
|
|
@@ -6947,13 +7172,13 @@ function cmdPull(opts = {}) {
|
|
|
6947
7172
|
|
|
6948
7173
|
// src/commands.push.ts
|
|
6949
7174
|
init_config();
|
|
6950
|
-
import { existsSync as
|
|
6951
|
-
import { join as
|
|
7175
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
7176
|
+
import { join as join55 } from "node:path";
|
|
6952
7177
|
|
|
6953
7178
|
// src/commands.push.selection.ts
|
|
6954
7179
|
init_config();
|
|
6955
|
-
import { existsSync as
|
|
6956
|
-
import { join as
|
|
7180
|
+
import { existsSync as existsSync43, statSync as statSync11 } from "node:fs";
|
|
7181
|
+
import { join as join51 } from "node:path";
|
|
6957
7182
|
init_utils_json();
|
|
6958
7183
|
function buildCurrentMap(map) {
|
|
6959
7184
|
const current = {};
|
|
@@ -6962,8 +7187,8 @@ function buildCurrentMap(map) {
|
|
|
6962
7187
|
for (const [, hostMap] of Object.entries(map.projects)) {
|
|
6963
7188
|
const localPath = hostMap[HOST];
|
|
6964
7189
|
if (!localPath) continue;
|
|
6965
|
-
const localDir =
|
|
6966
|
-
if (!
|
|
7190
|
+
const localDir = join51(claude, "projects", encodePath(localPath));
|
|
7191
|
+
if (!existsSync43(localDir)) continue;
|
|
6967
7192
|
for (const f of enumerateSourceFiles(localDir)) {
|
|
6968
7193
|
const st = statSync11(f);
|
|
6969
7194
|
current[f] = { size: st.size, mtime: st.mtimeMs };
|
|
@@ -6994,7 +7219,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
|
|
|
6994
7219
|
};
|
|
6995
7220
|
}
|
|
6996
7221
|
function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
|
|
6997
|
-
const map =
|
|
7222
|
+
const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
|
|
6998
7223
|
const { selection, newManifest } = computePushSelection(
|
|
6999
7224
|
map,
|
|
7000
7225
|
old,
|
|
@@ -7096,14 +7321,14 @@ function enforceAllowList(statusPorcelain, map) {
|
|
|
7096
7321
|
|
|
7097
7322
|
// src/commands.push.settings.ts
|
|
7098
7323
|
init_config();
|
|
7099
|
-
import { existsSync as
|
|
7100
|
-
import { join as
|
|
7324
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
7325
|
+
import { join as join52 } from "node:path";
|
|
7101
7326
|
init_utils();
|
|
7102
7327
|
init_utils_fs();
|
|
7103
7328
|
init_utils_json();
|
|
7104
7329
|
function stripGsdHooksFromBase(repo, backup) {
|
|
7105
|
-
const basePath =
|
|
7106
|
-
if (!
|
|
7330
|
+
const basePath = join52(repo, "shared", "settings.base.json");
|
|
7331
|
+
if (!existsSync44(basePath)) return;
|
|
7107
7332
|
let base;
|
|
7108
7333
|
try {
|
|
7109
7334
|
base = readJson(basePath);
|
|
@@ -7117,14 +7342,14 @@ function stripGsdHooksFromBase(repo, backup) {
|
|
|
7117
7342
|
writeJsonAtomic(basePath, stripped);
|
|
7118
7343
|
}
|
|
7119
7344
|
function reportSettingsAheadDrift(repo) {
|
|
7120
|
-
const basePath =
|
|
7121
|
-
if (!
|
|
7122
|
-
const settingsPath =
|
|
7123
|
-
if (!
|
|
7345
|
+
const basePath = join52(repo, "shared", "settings.base.json");
|
|
7346
|
+
if (!existsSync44(basePath)) return;
|
|
7347
|
+
const settingsPath = join52(claudeHome(), "settings.json");
|
|
7348
|
+
if (!existsSync44(settingsPath)) return;
|
|
7124
7349
|
try {
|
|
7125
7350
|
const base = readJson(basePath);
|
|
7126
|
-
const hostPath =
|
|
7127
|
-
const overrides =
|
|
7351
|
+
const hostPath = join52(repo, "hosts", `${HOST}.json`);
|
|
7352
|
+
const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
|
|
7128
7353
|
const merged = deepMerge(base, overrides);
|
|
7129
7354
|
const settings = readJson(settingsPath);
|
|
7130
7355
|
const { ahead } = classifySettingsDrift(merged, settings);
|
|
@@ -7141,9 +7366,9 @@ function reportSettingsAheadDrift(repo) {
|
|
|
7141
7366
|
// src/commands.push.guards.ts
|
|
7142
7367
|
init_push_checks();
|
|
7143
7368
|
init_utils();
|
|
7144
|
-
import { join as
|
|
7369
|
+
import { join as join53, relative as relative7 } from "node:path";
|
|
7145
7370
|
function guardGitlinks(repo) {
|
|
7146
|
-
const gitlinks = findGitlinks(
|
|
7371
|
+
const gitlinks = findGitlinks(join53(repo, "shared"));
|
|
7147
7372
|
if (gitlinks.length === 0) return;
|
|
7148
7373
|
for (const p of gitlinks) {
|
|
7149
7374
|
const rel = relative7(repo, p).replaceAll("\\", "/");
|
|
@@ -7175,7 +7400,7 @@ init_config();
|
|
|
7175
7400
|
|
|
7176
7401
|
// src/push-global-config.ts
|
|
7177
7402
|
init_config();
|
|
7178
|
-
import { execFileSync as
|
|
7403
|
+
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
7179
7404
|
var STATUS_LABELS = {
|
|
7180
7405
|
A: "add",
|
|
7181
7406
|
M: "modify",
|
|
@@ -7215,7 +7440,7 @@ function isInScope(filePath, exactPrefixes, dirPrefixes) {
|
|
|
7215
7440
|
}
|
|
7216
7441
|
function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
|
|
7217
7442
|
const args = opts.staged ? ["diff", "--cached", "--name-status", "-z"] : ["diff", "HEAD", "--name-status", "-z"];
|
|
7218
|
-
const raw =
|
|
7443
|
+
const raw = execFileSync21("git", args, {
|
|
7219
7444
|
cwd: repoHome2,
|
|
7220
7445
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7221
7446
|
}).toString();
|
|
@@ -7252,10 +7477,10 @@ init_push_leak_verdict();
|
|
|
7252
7477
|
init_color();
|
|
7253
7478
|
init_config();
|
|
7254
7479
|
init_config_sharedDirs_guard();
|
|
7255
|
-
import { randomBytes as
|
|
7256
|
-
import { copyFileSync, existsSync as
|
|
7257
|
-
import { homedir as
|
|
7258
|
-
import { join as
|
|
7480
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
7481
|
+
import { copyFileSync, existsSync as existsSync45, mkdirSync as mkdirSync13, readdirSync as readdirSync17, rmSync as rmSync16 } from "node:fs";
|
|
7482
|
+
import { homedir as homedir6 } from "node:os";
|
|
7483
|
+
import { join as join54, relative as relative8, sep as sep9 } from "node:path";
|
|
7259
7484
|
init_push_leak_verdict();
|
|
7260
7485
|
init_push_gitleaks();
|
|
7261
7486
|
init_utils_fs();
|
|
@@ -7267,7 +7492,7 @@ function stageSessionDir(localDir, dstDir, changed) {
|
|
|
7267
7492
|
const matching = [...changed].filter((p) => p.startsWith(prefix));
|
|
7268
7493
|
if (matching.length === 0) return false;
|
|
7269
7494
|
for (const src of matching) {
|
|
7270
|
-
copyFileAtomic(src,
|
|
7495
|
+
copyFileAtomic(src, join54(dstDir, relative8(localDir, src)));
|
|
7271
7496
|
}
|
|
7272
7497
|
return true;
|
|
7273
7498
|
}
|
|
@@ -7283,14 +7508,14 @@ function stageSessions(tmpRoot, map, changed) {
|
|
|
7283
7508
|
if (!p || p === "TBD") continue;
|
|
7284
7509
|
reverse.set(encodePath(p), logical);
|
|
7285
7510
|
}
|
|
7286
|
-
const localProjects =
|
|
7287
|
-
if (!
|
|
7511
|
+
const localProjects = join54(claudeHome(), "projects");
|
|
7512
|
+
if (!existsSync45(localProjects)) return 0;
|
|
7288
7513
|
let staged = 0;
|
|
7289
7514
|
for (const dir of readdirSync17(localProjects)) {
|
|
7290
7515
|
const logical = reverse.get(dir);
|
|
7291
7516
|
if (!logical) continue;
|
|
7292
|
-
const localDir =
|
|
7293
|
-
const dstDir =
|
|
7517
|
+
const localDir = join54(localProjects, dir);
|
|
7518
|
+
const dstDir = join54(tmpRoot, "shared", "projects", logical);
|
|
7294
7519
|
if (stageSessionDir(localDir, dstDir, changed)) staged++;
|
|
7295
7520
|
}
|
|
7296
7521
|
return staged;
|
|
@@ -7304,11 +7529,11 @@ function stageExtras(tmpRoot, map) {
|
|
|
7304
7529
|
assertSafeLogical(logical);
|
|
7305
7530
|
const localRoot = map.projects[logical]?.[HOST];
|
|
7306
7531
|
if (!localRoot || localRoot === "TBD") continue;
|
|
7307
|
-
for (const
|
|
7308
|
-
if (!whitelist.includes(
|
|
7309
|
-
const src =
|
|
7310
|
-
if (!
|
|
7311
|
-
const dst =
|
|
7532
|
+
for (const dirname11 of dirnames) {
|
|
7533
|
+
if (!whitelist.includes(dirname11)) continue;
|
|
7534
|
+
const src = join54(localRoot, dirname11);
|
|
7535
|
+
if (!existsSync45(src)) continue;
|
|
7536
|
+
const dst = join54(tmpRoot, "shared", "extras", logical, dirname11);
|
|
7312
7537
|
copyExtras(src, dst);
|
|
7313
7538
|
staged++;
|
|
7314
7539
|
}
|
|
@@ -7316,19 +7541,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
7316
7541
|
return staged;
|
|
7317
7542
|
}
|
|
7318
7543
|
function previewPushLeaks(map, opts = {}) {
|
|
7319
|
-
const cacheDir =
|
|
7320
|
-
|
|
7321
|
-
const stamp = `${nowTimestamp()}-${process.pid}-${
|
|
7322
|
-
const tmpRoot =
|
|
7544
|
+
const cacheDir = join54(homedir6(), ".cache", "claude-nomad");
|
|
7545
|
+
mkdirSync13(cacheDir, { recursive: true });
|
|
7546
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
7547
|
+
const tmpRoot = join54(cacheDir, `push-preview-tree-${stamp}`);
|
|
7323
7548
|
try {
|
|
7324
7549
|
const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
|
|
7325
7550
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
7326
7551
|
if (sessionCount + extrasCount === 0) {
|
|
7327
7552
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
7328
7553
|
}
|
|
7329
|
-
const ignoreFile =
|
|
7330
|
-
if (
|
|
7331
|
-
copyFileSync(ignoreFile,
|
|
7554
|
+
const ignoreFile = join54(repoHome(), ".gitleaksignore");
|
|
7555
|
+
if (existsSync45(ignoreFile)) {
|
|
7556
|
+
copyFileSync(ignoreFile, join54(tmpRoot, ".gitleaksignore"));
|
|
7332
7557
|
}
|
|
7333
7558
|
let findings;
|
|
7334
7559
|
try {
|
|
@@ -7341,7 +7566,7 @@ function previewPushLeaks(map, opts = {}) {
|
|
|
7341
7566
|
}
|
|
7342
7567
|
return verdictFromFindings(findings);
|
|
7343
7568
|
} finally {
|
|
7344
|
-
|
|
7569
|
+
rmSync16(tmpRoot, { recursive: true, force: true });
|
|
7345
7570
|
}
|
|
7346
7571
|
}
|
|
7347
7572
|
|
|
@@ -7454,7 +7679,7 @@ async function runPushCore(opts = {}) {
|
|
|
7454
7679
|
const scannerVersion = probeGitleaks();
|
|
7455
7680
|
const configHash = computeConfigHash();
|
|
7456
7681
|
const old = readManifest(manifestPath());
|
|
7457
|
-
const mapPath =
|
|
7682
|
+
const mapPath = join55(repo, "path-map.json");
|
|
7458
7683
|
const { map, selection, newManifest } = loadSelectionForPush(
|
|
7459
7684
|
mapPath,
|
|
7460
7685
|
old,
|
|
@@ -7505,7 +7730,7 @@ async function cmdPush(opts = {}) {
|
|
|
7505
7730
|
const allowRule = opts.allowRule;
|
|
7506
7731
|
guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
|
|
7507
7732
|
const repo = repoHome();
|
|
7508
|
-
if (!
|
|
7733
|
+
if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
|
|
7509
7734
|
const handle = acquireLock("push");
|
|
7510
7735
|
if (handle === null) process.exit(0);
|
|
7511
7736
|
try {
|
|
@@ -7523,8 +7748,8 @@ async function cmdPush(opts = {}) {
|
|
|
7523
7748
|
}
|
|
7524
7749
|
|
|
7525
7750
|
// src/commands.sync.ts
|
|
7526
|
-
import { existsSync as
|
|
7527
|
-
import { join as
|
|
7751
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
7752
|
+
import { join as join56 } from "node:path";
|
|
7528
7753
|
init_config();
|
|
7529
7754
|
init_color();
|
|
7530
7755
|
init_utils();
|
|
@@ -7652,8 +7877,8 @@ async function runSyncWet(verbose) {
|
|
|
7652
7877
|
}
|
|
7653
7878
|
async function runSyncDryRun(repo, backup) {
|
|
7654
7879
|
const ts = freshBackupTs(backup);
|
|
7655
|
-
const mapPath =
|
|
7656
|
-
const map =
|
|
7880
|
+
const mapPath = join56(repo, "path-map.json");
|
|
7881
|
+
const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
7657
7882
|
computePreview(ts, map, "pull");
|
|
7658
7883
|
log("push preview below is computed against pre-pull state (a real sync pushes after pull)");
|
|
7659
7884
|
await runPushCore({ dryRun: true });
|
|
@@ -7663,8 +7888,8 @@ async function cmdSync(opts = {}) {
|
|
|
7663
7888
|
const verbose = opts.verbose === true;
|
|
7664
7889
|
const repo = repoHome();
|
|
7665
7890
|
const backup = backupBase();
|
|
7666
|
-
if (!
|
|
7667
|
-
if (!
|
|
7891
|
+
if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
|
|
7892
|
+
if (!existsSync47(join56(repo, "shared", "settings.base.json"))) {
|
|
7668
7893
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
7669
7894
|
}
|
|
7670
7895
|
const handle = acquireLock("sync");
|
|
@@ -7688,9 +7913,9 @@ async function cmdSync(opts = {}) {
|
|
|
7688
7913
|
}
|
|
7689
7914
|
|
|
7690
7915
|
// src/commands.update.ts
|
|
7691
|
-
import { execFileSync as
|
|
7916
|
+
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
7692
7917
|
init_utils();
|
|
7693
|
-
function readInstalledVersion(run =
|
|
7918
|
+
function readInstalledVersion(run = execFileSync22) {
|
|
7694
7919
|
const isWin = process.platform === "win32";
|
|
7695
7920
|
try {
|
|
7696
7921
|
return run(isWin ? "nomad.cmd" : "nomad", ["--version"], {
|
|
@@ -7701,7 +7926,7 @@ function readInstalledVersion(run = execFileSync21) {
|
|
|
7701
7926
|
return null;
|
|
7702
7927
|
}
|
|
7703
7928
|
}
|
|
7704
|
-
function cmdUpdate(currentVersion, run =
|
|
7929
|
+
function cmdUpdate(currentVersion, run = execFileSync22) {
|
|
7705
7930
|
console.log(`Updating claude-nomad v${currentVersion}...`);
|
|
7706
7931
|
const isWin = process.platform === "win32";
|
|
7707
7932
|
try {
|
|
@@ -7737,18 +7962,18 @@ init_config();
|
|
|
7737
7962
|
|
|
7738
7963
|
// src/diff.ts
|
|
7739
7964
|
init_config();
|
|
7740
|
-
import { existsSync as
|
|
7741
|
-
import { join as
|
|
7965
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
7966
|
+
import { join as join57 } from "node:path";
|
|
7742
7967
|
init_utils();
|
|
7743
7968
|
init_utils_fs();
|
|
7744
7969
|
init_utils_json();
|
|
7745
7970
|
function cmdDiff() {
|
|
7746
7971
|
try {
|
|
7747
7972
|
const repo = repoHome();
|
|
7748
|
-
if (!
|
|
7973
|
+
if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
|
|
7749
7974
|
const ts = freshBackupTs(backupBase());
|
|
7750
|
-
const mapPath =
|
|
7751
|
-
const map =
|
|
7975
|
+
const mapPath = join57(repo, "path-map.json");
|
|
7976
|
+
const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
7752
7977
|
divergenceCheckExtras(ts);
|
|
7753
7978
|
computePreview(ts, map, "diff");
|
|
7754
7979
|
} catch (err) {
|
|
@@ -7763,19 +7988,19 @@ function cmdDiff() {
|
|
|
7763
7988
|
|
|
7764
7989
|
// src/init.ts
|
|
7765
7990
|
init_config();
|
|
7766
|
-
import { existsSync as
|
|
7767
|
-
import { join as
|
|
7991
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync14, writeFileSync as writeFileSync8 } from "node:fs";
|
|
7992
|
+
import { join as join59 } from "node:path";
|
|
7768
7993
|
|
|
7769
7994
|
// src/init.gh-onboard.ts
|
|
7770
7995
|
init_config();
|
|
7771
|
-
import { execFileSync as
|
|
7996
|
+
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
7772
7997
|
init_utils();
|
|
7773
7998
|
var DEFAULT_REPO_NAME = "claude-nomad-config";
|
|
7774
7999
|
function isValidRepoName(name) {
|
|
7775
8000
|
return /^[A-Za-z0-9._-]{1,100}$/.test(name);
|
|
7776
8001
|
}
|
|
7777
8002
|
var GH_NETWORK_TIMEOUT_MS = 3e4;
|
|
7778
|
-
function ensureOriginRepo(repoName, run =
|
|
8003
|
+
function ensureOriginRepo(repoName, run = execFileSync23) {
|
|
7779
8004
|
if (!isValidRepoName(repoName)) {
|
|
7780
8005
|
die(
|
|
7781
8006
|
`invalid repo name: ${JSON.stringify(repoName)}. Use only letters, digits, hyphens, underscores, and dots (1-100 chars).`
|
|
@@ -7846,33 +8071,33 @@ init_config();
|
|
|
7846
8071
|
init_utils();
|
|
7847
8072
|
init_utils_fs();
|
|
7848
8073
|
init_utils_json();
|
|
7849
|
-
import { copyFileSync as copyFileSync2, cpSync as
|
|
7850
|
-
import { join as
|
|
8074
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync49, rmSync as rmSync17, statSync as statSync12 } from "node:fs";
|
|
8075
|
+
import { join as join58 } from "node:path";
|
|
7851
8076
|
function snapshotIntoShared(map) {
|
|
7852
8077
|
const repo = repoHome();
|
|
7853
8078
|
const claude = claudeHome();
|
|
7854
8079
|
for (const name of allSharedLinks(map)) {
|
|
7855
|
-
const src =
|
|
7856
|
-
if (!
|
|
7857
|
-
const dst =
|
|
8080
|
+
const src = join58(claude, name);
|
|
8081
|
+
if (!existsSync49(src)) continue;
|
|
8082
|
+
const dst = join58(repo, "shared", name);
|
|
7858
8083
|
if (statSync12(src).isDirectory()) {
|
|
7859
|
-
const gk =
|
|
7860
|
-
if (
|
|
7861
|
-
|
|
8084
|
+
const gk = join58(dst, ".gitkeep");
|
|
8085
|
+
if (existsSync49(gk)) rmSync17(gk);
|
|
8086
|
+
cpSync9(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
7862
8087
|
} else {
|
|
7863
8088
|
copyFileSync2(src, dst);
|
|
7864
8089
|
}
|
|
7865
8090
|
log(`snapshotted shared/${name} from ${src}`);
|
|
7866
8091
|
}
|
|
7867
|
-
const userSettings =
|
|
7868
|
-
if (
|
|
8092
|
+
const userSettings = join58(claude, "settings.json");
|
|
8093
|
+
if (existsSync49(userSettings)) {
|
|
7869
8094
|
let parsed;
|
|
7870
8095
|
try {
|
|
7871
8096
|
parsed = readJson(userSettings);
|
|
7872
8097
|
} catch (err) {
|
|
7873
8098
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
7874
8099
|
}
|
|
7875
|
-
const hostFile =
|
|
8100
|
+
const hostFile = join58(repo, "hosts", `${HOST}.json`);
|
|
7876
8101
|
writeJsonAtomic(hostFile, parsed);
|
|
7877
8102
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
7878
8103
|
}
|
|
@@ -7886,14 +8111,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
|
|
|
7886
8111
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
7887
8112
|
function preflightConflict(repoHome2) {
|
|
7888
8113
|
const candidates = [
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
8114
|
+
join59(repoHome2, "shared", "settings.base.json"),
|
|
8115
|
+
join59(repoHome2, "shared", "CLAUDE.md"),
|
|
8116
|
+
join59(repoHome2, "path-map.json"),
|
|
8117
|
+
join59(repoHome2, "hosts"),
|
|
8118
|
+
join59(repoHome2, "shared")
|
|
7894
8119
|
];
|
|
7895
8120
|
for (const c of candidates) {
|
|
7896
|
-
if (
|
|
8121
|
+
if (existsSync50(c)) return c;
|
|
7897
8122
|
}
|
|
7898
8123
|
return null;
|
|
7899
8124
|
}
|
|
@@ -7905,33 +8130,33 @@ function cmdInit(opts = {}) {
|
|
|
7905
8130
|
const keepActions = opts.keepActions === true;
|
|
7906
8131
|
const repo = repoHome();
|
|
7907
8132
|
const claude = claudeHome();
|
|
7908
|
-
|
|
8133
|
+
mkdirSync14(repo, { recursive: true });
|
|
7909
8134
|
const conflict = preflightConflict(repo);
|
|
7910
8135
|
if (conflict !== null) {
|
|
7911
8136
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
7912
8137
|
}
|
|
7913
8138
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
7914
|
-
|
|
7915
|
-
|
|
8139
|
+
mkdirSync14(join59(repo, "shared"), { recursive: true });
|
|
8140
|
+
mkdirSync14(join59(repo, "hosts"), { recursive: true });
|
|
7916
8141
|
for (const name of SHARED_KEEP_DIRS) {
|
|
7917
|
-
|
|
8142
|
+
mkdirSync14(join59(repo, "shared", name), { recursive: true });
|
|
7918
8143
|
}
|
|
7919
|
-
const userClaudeMd =
|
|
7920
|
-
if (!snapshot || !
|
|
7921
|
-
|
|
8144
|
+
const userClaudeMd = join59(claude, "CLAUDE.md");
|
|
8145
|
+
if (!snapshot || !existsSync50(userClaudeMd)) {
|
|
8146
|
+
writeFileSync8(join59(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
7922
8147
|
item("created shared/CLAUDE.md");
|
|
7923
8148
|
}
|
|
7924
8149
|
for (const name of SHARED_KEEP_DIRS) {
|
|
7925
|
-
|
|
8150
|
+
writeFileSync8(join59(repo, "shared", name, ".gitkeep"), "");
|
|
7926
8151
|
item(`created shared/${name}/.gitkeep`);
|
|
7927
8152
|
}
|
|
7928
|
-
|
|
8153
|
+
writeFileSync8(join59(repo, "hosts", ".gitkeep"), "");
|
|
7929
8154
|
item("created hosts/.gitkeep");
|
|
7930
|
-
writeJsonAtomic(
|
|
8155
|
+
writeJsonAtomic(join59(repo, "shared", "settings.base.json"), {});
|
|
7931
8156
|
item("created shared/settings.base.json");
|
|
7932
|
-
writeJsonAtomic(
|
|
8157
|
+
writeJsonAtomic(join59(repo, "path-map.json"), { projects: {} });
|
|
7933
8158
|
item("created path-map.json");
|
|
7934
|
-
|
|
8159
|
+
writeFileSync8(join59(repo, ".gitattributes"), GITATTRIBUTES);
|
|
7935
8160
|
item("created .gitattributes");
|
|
7936
8161
|
if (snapshot) {
|
|
7937
8162
|
snapshotIntoShared({ projects: {} });
|
|
@@ -7997,11 +8222,11 @@ function maybeDisableRepoActions(repoHome2, run) {
|
|
|
7997
8222
|
// src/init.prompt.ts
|
|
7998
8223
|
init_config();
|
|
7999
8224
|
init_utils();
|
|
8000
|
-
import { existsSync as
|
|
8001
|
-
import { join as
|
|
8225
|
+
import { existsSync as existsSync51, readdirSync as readdirSync18, statSync as statSync13 } from "node:fs";
|
|
8226
|
+
import { join as join60 } from "node:path";
|
|
8002
8227
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
8003
8228
|
function nonEmptyExists(path) {
|
|
8004
|
-
if (!
|
|
8229
|
+
if (!existsSync51(path)) return false;
|
|
8005
8230
|
try {
|
|
8006
8231
|
if (statSync13(path).isDirectory()) return readdirSync18(path).length > 0;
|
|
8007
8232
|
return true;
|
|
@@ -8010,8 +8235,8 @@ function nonEmptyExists(path) {
|
|
|
8010
8235
|
}
|
|
8011
8236
|
}
|
|
8012
8237
|
function hasExistingClaudeConfig(claudeHome2) {
|
|
8013
|
-
if (
|
|
8014
|
-
return SHARED_LINKS.some((name) => nonEmptyExists(
|
|
8238
|
+
if (existsSync51(join60(claudeHome2, "settings.json"))) return true;
|
|
8239
|
+
return SHARED_LINKS.some((name) => nonEmptyExists(join60(claudeHome2, name)));
|
|
8015
8240
|
}
|
|
8016
8241
|
async function confirmSnapshotDefault(claudeHome2) {
|
|
8017
8242
|
if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
|
|
@@ -8318,7 +8543,7 @@ function parseSyncArgs(argv) {
|
|
|
8318
8543
|
// package.json
|
|
8319
8544
|
var package_default = {
|
|
8320
8545
|
name: "claude-nomad",
|
|
8321
|
-
version: "0.
|
|
8546
|
+
version: "0.61.0",
|
|
8322
8547
|
type: "module",
|
|
8323
8548
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
8324
8549
|
keywords: [
|
|
@@ -8559,15 +8784,15 @@ var DEFAULT_HELP = [
|
|
|
8559
8784
|
init_config();
|
|
8560
8785
|
init_utils();
|
|
8561
8786
|
init_utils_json();
|
|
8562
|
-
import { existsSync as
|
|
8563
|
-
import { join as
|
|
8787
|
+
import { existsSync as existsSync52, readFileSync as readFileSync19, readdirSync as readdirSync19 } from "node:fs";
|
|
8788
|
+
import { join as join61 } from "node:path";
|
|
8564
8789
|
function resumeCmd(sessionId) {
|
|
8565
8790
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
8566
8791
|
fail(`invalid session id: ${sessionId}`);
|
|
8567
8792
|
process.exit(1);
|
|
8568
8793
|
}
|
|
8569
|
-
const projectsRoot =
|
|
8570
|
-
if (!
|
|
8794
|
+
const projectsRoot = join61(claudeHome(), "projects");
|
|
8795
|
+
if (!existsSync52(projectsRoot)) {
|
|
8571
8796
|
fail(`${projectsRoot} does not exist`);
|
|
8572
8797
|
process.exit(1);
|
|
8573
8798
|
}
|
|
@@ -8581,8 +8806,8 @@ function resumeCmd(sessionId) {
|
|
|
8581
8806
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
8582
8807
|
process.exit(1);
|
|
8583
8808
|
}
|
|
8584
|
-
const mapPath =
|
|
8585
|
-
if (!
|
|
8809
|
+
const mapPath = join61(repoHome(), "path-map.json");
|
|
8810
|
+
if (!existsSync52(mapPath)) {
|
|
8586
8811
|
fail("path-map.json missing");
|
|
8587
8812
|
process.exit(1);
|
|
8588
8813
|
}
|
|
@@ -8605,13 +8830,13 @@ function resumeCmd(sessionId) {
|
|
|
8605
8830
|
}
|
|
8606
8831
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
8607
8832
|
for (const dir of readdirSync19(projectsRoot)) {
|
|
8608
|
-
const candidate =
|
|
8609
|
-
if (
|
|
8833
|
+
const candidate = join61(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
8834
|
+
if (existsSync52(candidate)) return candidate;
|
|
8610
8835
|
}
|
|
8611
8836
|
return null;
|
|
8612
8837
|
}
|
|
8613
8838
|
function extractRecordedCwd(jsonlPath) {
|
|
8614
|
-
for (const line of
|
|
8839
|
+
for (const line of readFileSync19(jsonlPath, "utf8").split("\n")) {
|
|
8615
8840
|
if (!line.trim()) continue;
|
|
8616
8841
|
try {
|
|
8617
8842
|
const obj = JSON.parse(line);
|