claude-nomad 0.60.0 → 0.62.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 +58 -16
- package/CHANGELOG.md +38 -0
- package/README.md +48 -0
- package/dist/nomad.mjs +1141 -441
- package/package.json +2 -2
package/dist/nomad.mjs
CHANGED
|
@@ -189,8 +189,36 @@ var init_color = __esm({
|
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
// src/exit-codes.ts
|
|
193
|
+
var EXIT;
|
|
194
|
+
var init_exit_codes = __esm({
|
|
195
|
+
"src/exit-codes.ts"() {
|
|
196
|
+
"use strict";
|
|
197
|
+
EXIT = {
|
|
198
|
+
/** Completed successfully. Also returned when a held lock causes a no-op skip. */
|
|
199
|
+
SUCCESS: 0,
|
|
200
|
+
/** Unclassified failure; the default for any error not mapped to a more specific code. */
|
|
201
|
+
GENERIC_FAILURE: 1,
|
|
202
|
+
/** Bad argv: unknown subcommand, unknown flag, or malformed flag value. */
|
|
203
|
+
USAGE: 2,
|
|
204
|
+
/** A wedged repo state (e.g. an unresolved rebase) needing manual git resolution. */
|
|
205
|
+
CONFLICT: 4,
|
|
206
|
+
/** gitleaks confirmed a secret in the staged tree and the push was aborted. */
|
|
207
|
+
LEAK_BLOCKED: 5
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
192
212
|
// src/utils.ts
|
|
193
213
|
import { execFileSync } from "node:child_process";
|
|
214
|
+
function isProcessExit(err) {
|
|
215
|
+
if (typeof err !== "object" || err === null) return false;
|
|
216
|
+
try {
|
|
217
|
+
return err[PROCESS_EXIT_BRAND] === true;
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
194
222
|
function gitCaptureRaw(args, cwd) {
|
|
195
223
|
return execFileSync("git", args, {
|
|
196
224
|
cwd,
|
|
@@ -214,11 +242,12 @@ function gitOrFatal(args, context, cwd) {
|
|
|
214
242
|
throw new NomadFatal(`${context} failed`);
|
|
215
243
|
}
|
|
216
244
|
}
|
|
217
|
-
var log, ok, warn, fail, item, NomadFatal, die, gitStatusPorcelainZ;
|
|
245
|
+
var log, ok, warn, fail, item, NomadFatal, PROCESS_EXIT_BRAND, ProcessExit, die, gitStatusPorcelainZ;
|
|
218
246
|
var init_utils = __esm({
|
|
219
247
|
"src/utils.ts"() {
|
|
220
248
|
"use strict";
|
|
221
249
|
init_color();
|
|
250
|
+
init_exit_codes();
|
|
222
251
|
log = (msg) => console.log(`${dim(infoGlyph)} ${msg}`);
|
|
223
252
|
ok = (msg) => console.log(`${green(okGlyph)} ${msg}`);
|
|
224
253
|
warn = (msg) => {
|
|
@@ -229,13 +258,25 @@ var init_utils = __esm({
|
|
|
229
258
|
};
|
|
230
259
|
item = (msg) => console.log(dim(` ${msg}`));
|
|
231
260
|
NomadFatal = class extends Error {
|
|
232
|
-
|
|
261
|
+
code;
|
|
262
|
+
constructor(message, opts = {}) {
|
|
233
263
|
super(message);
|
|
234
264
|
this.name = "NomadFatal";
|
|
265
|
+
this.code = opts.code ?? EXIT.GENERIC_FAILURE;
|
|
235
266
|
}
|
|
236
267
|
};
|
|
237
|
-
|
|
238
|
-
|
|
268
|
+
PROCESS_EXIT_BRAND = /* @__PURE__ */ Symbol.for("claude-nomad.ProcessExit");
|
|
269
|
+
ProcessExit = class extends Error {
|
|
270
|
+
code;
|
|
271
|
+
[PROCESS_EXIT_BRAND] = true;
|
|
272
|
+
constructor(code) {
|
|
273
|
+
super(`exit:${String(code)}`);
|
|
274
|
+
this.name = "ProcessExit";
|
|
275
|
+
this.code = code;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
die = (msg, opts = {}) => {
|
|
279
|
+
throw new NomadFatal(msg, opts);
|
|
239
280
|
};
|
|
240
281
|
gitStatusPorcelainZ = (cwd, opts = {}) => {
|
|
241
282
|
const args = ["status", "--porcelain=v1", "-z"];
|
|
@@ -459,6 +500,9 @@ function repoHome() {
|
|
|
459
500
|
function backupBase() {
|
|
460
501
|
return join(home(), ".cache", "claude-nomad", "backup");
|
|
461
502
|
}
|
|
503
|
+
function crashDir() {
|
|
504
|
+
return join(home(), ".cache", "claude-nomad", "crash");
|
|
505
|
+
}
|
|
462
506
|
function manifestPath() {
|
|
463
507
|
return join(home(), ".cache", "claude-nomad", `push-manifest-${encodeURIComponent(HOST)}.json`);
|
|
464
508
|
}
|
|
@@ -957,7 +1001,7 @@ function wedgePreflight(wedge) {
|
|
|
957
1001
|
}
|
|
958
1002
|
function rebaseBeforePush(repo) {
|
|
959
1003
|
const wedge = classifyWedge(repo);
|
|
960
|
-
if (wedge !== null) throw new NomadFatal(wedgePreflight(wedge));
|
|
1004
|
+
if (wedge !== null) throw new NomadFatal(wedgePreflight(wedge), { code: EXIT.CONFLICT });
|
|
961
1005
|
try {
|
|
962
1006
|
execFileSync4("git", ["pull", "--rebase", "--autostash"], {
|
|
963
1007
|
cwd: repo,
|
|
@@ -976,6 +1020,7 @@ var init_push_checks = __esm({
|
|
|
976
1020
|
"use strict";
|
|
977
1021
|
init_push_gitleaks_config();
|
|
978
1022
|
init_commands_pull_wedge();
|
|
1023
|
+
init_exit_codes();
|
|
979
1024
|
init_utils();
|
|
980
1025
|
}
|
|
981
1026
|
});
|
|
@@ -1030,7 +1075,7 @@ function scanStagedTree(repoDir, forwardStreams = false) {
|
|
|
1030
1075
|
rmSync8(reportPath, { force: true });
|
|
1031
1076
|
}
|
|
1032
1077
|
}
|
|
1033
|
-
function scanFile(filePath, forwardStreams = false) {
|
|
1078
|
+
function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TIMEOUT_MS) {
|
|
1034
1079
|
const cacheDir = join23(homedir3(), ".cache", "claude-nomad");
|
|
1035
1080
|
mkdirSync3(cacheDir, { recursive: true });
|
|
1036
1081
|
const reportPath = join23(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}.json`);
|
|
@@ -1046,7 +1091,7 @@ function scanFile(filePath, forwardStreams = false) {
|
|
|
1046
1091
|
if (toml !== null) args.push("--config", toml);
|
|
1047
1092
|
const opts = {
|
|
1048
1093
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1049
|
-
timeout:
|
|
1094
|
+
timeout: timeoutMs
|
|
1050
1095
|
};
|
|
1051
1096
|
try {
|
|
1052
1097
|
execFileSync8("gitleaks", args, opts);
|
|
@@ -1166,6 +1211,7 @@ var init_push_gitleaks = __esm({
|
|
|
1166
1211
|
"src/push-gitleaks.ts"() {
|
|
1167
1212
|
"use strict";
|
|
1168
1213
|
init_config();
|
|
1214
|
+
init_exit_codes();
|
|
1169
1215
|
init_push_checks();
|
|
1170
1216
|
init_push_gitleaks_scan();
|
|
1171
1217
|
init_utils();
|
|
@@ -1686,12 +1732,12 @@ function* eachExtrasTarget(v, counts) {
|
|
|
1686
1732
|
counts.unmapped++;
|
|
1687
1733
|
continue;
|
|
1688
1734
|
}
|
|
1689
|
-
for (const
|
|
1690
|
-
if (!whitelist.includes(
|
|
1735
|
+
for (const dirname13 of dirnames) {
|
|
1736
|
+
if (!whitelist.includes(dirname13)) {
|
|
1691
1737
|
counts.skipped++;
|
|
1692
1738
|
continue;
|
|
1693
1739
|
}
|
|
1694
|
-
yield { logical, localRoot, dirname:
|
|
1740
|
+
yield { logical, localRoot, dirname: dirname13 };
|
|
1695
1741
|
}
|
|
1696
1742
|
}
|
|
1697
1743
|
}
|
|
@@ -1729,8 +1775,8 @@ function copyExtrasFileSkipDiverged(src, dst) {
|
|
|
1729
1775
|
}
|
|
1730
1776
|
copyExtras(src, dst);
|
|
1731
1777
|
}
|
|
1732
|
-
function extrasDenySet(
|
|
1733
|
-
return
|
|
1778
|
+
function extrasDenySet(dirname13) {
|
|
1779
|
+
return dirname13 === ".claude" ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
|
|
1734
1780
|
}
|
|
1735
1781
|
function copyExtrasFiltered(src, dst, blockSet) {
|
|
1736
1782
|
rmSync2(dst, { recursive: true, force: true });
|
|
@@ -2024,7 +2070,7 @@ function cmdAdopt(name, opts = {}) {
|
|
|
2024
2070
|
} catch (err) {
|
|
2025
2071
|
if (!(err instanceof NomadFatal)) throw err;
|
|
2026
2072
|
fail(err.message);
|
|
2027
|
-
process.exitCode =
|
|
2073
|
+
process.exitCode = err.code;
|
|
2028
2074
|
}
|
|
2029
2075
|
}
|
|
2030
2076
|
|
|
@@ -2107,6 +2153,7 @@ function appendGitleaksIgnore(fingerprint, repo) {
|
|
|
2107
2153
|
}
|
|
2108
2154
|
|
|
2109
2155
|
// src/commands.allow.ts
|
|
2156
|
+
init_exit_codes();
|
|
2110
2157
|
init_utils();
|
|
2111
2158
|
function cmdAllow(fingerprints) {
|
|
2112
2159
|
const repo = repoHome();
|
|
@@ -2115,7 +2162,7 @@ function cmdAllow(fingerprints) {
|
|
|
2115
2162
|
if (!isValidFingerprint(fp)) {
|
|
2116
2163
|
const shown = fp.replaceAll("\r", String.raw`\r`).replaceAll("\n", String.raw`\n`);
|
|
2117
2164
|
fail(`invalid fingerprint: ${shown}`);
|
|
2118
|
-
process.exit(
|
|
2165
|
+
process.exit(EXIT.USAGE);
|
|
2119
2166
|
}
|
|
2120
2167
|
}
|
|
2121
2168
|
for (const fp of fingerprints) {
|
|
@@ -2324,6 +2371,7 @@ async function cmdCaptureSettings(opts) {
|
|
|
2324
2371
|
|
|
2325
2372
|
// src/commands.clean.ts
|
|
2326
2373
|
init_config();
|
|
2374
|
+
init_exit_codes();
|
|
2327
2375
|
init_utils();
|
|
2328
2376
|
import { existsSync as existsSync8, lstatSync as lstatSync6, readdirSync as readdirSync3, rmSync as rmSync5, statSync as statSync2 } from "node:fs";
|
|
2329
2377
|
import { join as join10 } from "node:path";
|
|
@@ -2364,14 +2412,14 @@ function cmdClean(opts, backupBase2 = backupBase()) {
|
|
|
2364
2412
|
const { dryRun, olderThan, keep } = opts;
|
|
2365
2413
|
if (olderThan !== void 0 && keep !== void 0) {
|
|
2366
2414
|
fail("--older-than and --keep are mutually exclusive");
|
|
2367
|
-
process.exit(
|
|
2415
|
+
process.exit(EXIT.USAGE);
|
|
2368
2416
|
}
|
|
2369
2417
|
let olderThanMs = CLEAN_DEFAULT_OLDER_THAN_MS;
|
|
2370
2418
|
if (olderThan !== void 0) {
|
|
2371
2419
|
const parsed = parseDuration(olderThan);
|
|
2372
2420
|
if (parsed === null) {
|
|
2373
2421
|
fail(`invalid --older-than duration: ${olderThan} (expected e.g. 14d, 24h, 30m)`);
|
|
2374
|
-
process.exit(
|
|
2422
|
+
process.exit(EXIT.USAGE);
|
|
2375
2423
|
}
|
|
2376
2424
|
olderThanMs = parsed;
|
|
2377
2425
|
}
|
|
@@ -2558,8 +2606,8 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
|
|
|
2558
2606
|
}
|
|
2559
2607
|
|
|
2560
2608
|
// src/commands.doctor.ts
|
|
2561
|
-
import { existsSync as
|
|
2562
|
-
import { join as
|
|
2609
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
2610
|
+
import { join as join45 } from "node:path";
|
|
2563
2611
|
|
|
2564
2612
|
// src/commands.doctor.checks.repo.ts
|
|
2565
2613
|
init_color();
|
|
@@ -3406,20 +3454,117 @@ function reportCheckSchema(section2) {
|
|
|
3406
3454
|
|
|
3407
3455
|
// src/commands.doctor.check-shared.ts
|
|
3408
3456
|
init_color();
|
|
3409
|
-
import { randomBytes } from "node:crypto";
|
|
3457
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
3458
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
3459
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync8, readdirSync as readdirSync11, rmSync as rmSync12 } from "node:fs";
|
|
3460
|
+
import { homedir as homedir6 } from "node:os";
|
|
3461
|
+
import { join as join29 } from "node:path";
|
|
3462
|
+
|
|
3463
|
+
// src/commands.doctor.check-shared.memory.ts
|
|
3464
|
+
init_color();
|
|
3410
3465
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
3411
|
-
import {
|
|
3466
|
+
import { randomBytes } from "node:crypto";
|
|
3467
|
+
import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3412
3468
|
import { homedir as homedir4 } from "node:os";
|
|
3413
|
-
import { join as
|
|
3469
|
+
import { dirname as dirname3, join as join24 } from "node:path";
|
|
3470
|
+
init_config();
|
|
3471
|
+
init_push_gitleaks();
|
|
3472
|
+
init_utils_fs();
|
|
3473
|
+
var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
|
|
3474
|
+
function buildMemoryScanTree(tmpRoot) {
|
|
3475
|
+
let paths;
|
|
3476
|
+
try {
|
|
3477
|
+
const out = execFileSync9(
|
|
3478
|
+
"git",
|
|
3479
|
+
["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
|
|
3480
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
|
|
3481
|
+
);
|
|
3482
|
+
paths = out.split("\0").filter((p) => p.length > 0);
|
|
3483
|
+
} catch {
|
|
3484
|
+
return 0;
|
|
3485
|
+
}
|
|
3486
|
+
const logicals = /* @__PURE__ */ new Set();
|
|
3487
|
+
for (const rel of paths) {
|
|
3488
|
+
const m = MEMORY_MD_PATH.exec(rel);
|
|
3489
|
+
if (m?.[1] === void 0) continue;
|
|
3490
|
+
let blob;
|
|
3491
|
+
try {
|
|
3492
|
+
blob = execFileSync9("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
|
|
3493
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3494
|
+
maxBuffer: 67108864,
|
|
3495
|
+
timeout: 1e4
|
|
3496
|
+
});
|
|
3497
|
+
} catch {
|
|
3498
|
+
continue;
|
|
3499
|
+
}
|
|
3500
|
+
const dest = join24(tmpRoot, rel);
|
|
3501
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
3502
|
+
writeFileSync4(dest, blob);
|
|
3503
|
+
logicals.add(m[1]);
|
|
3504
|
+
}
|
|
3505
|
+
return logicals.size;
|
|
3506
|
+
}
|
|
3507
|
+
function reportMemoryFindings(section2, findings) {
|
|
3508
|
+
for (const f of findings) {
|
|
3509
|
+
addItem(section2, `${yellow(warnGlyph)} ${f.RuleID} in ${f.File}`);
|
|
3510
|
+
}
|
|
3511
|
+
addItem(
|
|
3512
|
+
section2,
|
|
3513
|
+
` ${dim("run `nomad push` and choose Redact in the recovery menu to scrub these")}`
|
|
3514
|
+
);
|
|
3515
|
+
}
|
|
3516
|
+
function reportCommittedMemory(section2) {
|
|
3517
|
+
let tmpRoot;
|
|
3518
|
+
try {
|
|
3519
|
+
const cacheDir = join24(homedir4(), ".cache", "claude-nomad");
|
|
3520
|
+
mkdirSync4(cacheDir, { recursive: true });
|
|
3521
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
3522
|
+
tmpRoot = join24(cacheDir, `check-shared-memory-tree-${stamp}`);
|
|
3523
|
+
mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
|
|
3524
|
+
const staged = buildMemoryScanTree(tmpRoot);
|
|
3525
|
+
if (staged === 0) return;
|
|
3526
|
+
let findings;
|
|
3527
|
+
try {
|
|
3528
|
+
findings = scanStagedTree(tmpRoot);
|
|
3529
|
+
} catch (err) {
|
|
3530
|
+
addItem(
|
|
3531
|
+
section2,
|
|
3532
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
|
|
3533
|
+
);
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
if (findings === null) {
|
|
3537
|
+
addItem(
|
|
3538
|
+
section2,
|
|
3539
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: no parseable gitleaks report`
|
|
3540
|
+
);
|
|
3541
|
+
return;
|
|
3542
|
+
}
|
|
3543
|
+
if (findings.length === 0) return;
|
|
3544
|
+
reportMemoryFindings(section2, findings);
|
|
3545
|
+
} catch (err) {
|
|
3546
|
+
addItem(
|
|
3547
|
+
section2,
|
|
3548
|
+
`${yellow(warnGlyph)} committed-memory scan skipped: ${err.message}`
|
|
3549
|
+
);
|
|
3550
|
+
} finally {
|
|
3551
|
+
if (tmpRoot !== void 0) {
|
|
3552
|
+
try {
|
|
3553
|
+
rmSync9(tmpRoot, { recursive: true, force: true });
|
|
3554
|
+
} catch {
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3414
3559
|
|
|
3415
3560
|
// src/commands.doctor.check-shared.scan.ts
|
|
3416
3561
|
init_color();
|
|
3417
|
-
import { join as
|
|
3562
|
+
import { join as join25 } from "node:path";
|
|
3418
3563
|
init_config();
|
|
3419
3564
|
init_push_gitleaks();
|
|
3420
3565
|
function scrubPath(logical, sid, logicalToEncoded) {
|
|
3421
3566
|
const encoded = logicalToEncoded.get(logical) ?? logical;
|
|
3422
|
-
return
|
|
3567
|
+
return join25(claudeHome(), "projects", encoded, `${sid}.jsonl`);
|
|
3423
3568
|
}
|
|
3424
3569
|
function reportSessionFindings(section2, bySession) {
|
|
3425
3570
|
for (const [sid, counts] of bySession) {
|
|
@@ -3502,13 +3647,123 @@ function scanAndReport(section2, tmpRoot, staged, logicalToEncoded) {
|
|
|
3502
3647
|
emitDescriptionLegend(section2, findings);
|
|
3503
3648
|
}
|
|
3504
3649
|
|
|
3650
|
+
// src/commands.doctor.check-shared.skills.ts
|
|
3651
|
+
init_color();
|
|
3652
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
3653
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3654
|
+
import { mkdirSync as mkdirSync5, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3655
|
+
import { homedir as homedir5 } from "node:os";
|
|
3656
|
+
import { dirname as dirname4, join as join26 } from "node:path";
|
|
3657
|
+
init_config();
|
|
3658
|
+
init_push_gitleaks();
|
|
3659
|
+
init_utils_fs();
|
|
3660
|
+
var SKILL_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
|
|
3661
|
+
function buildSkillScanTree(tmpRoot) {
|
|
3662
|
+
let paths;
|
|
3663
|
+
try {
|
|
3664
|
+
const out = execFileSync10(
|
|
3665
|
+
"git",
|
|
3666
|
+
["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/skills"],
|
|
3667
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
|
|
3668
|
+
);
|
|
3669
|
+
paths = out.split("\0").filter((p) => p.length > 0);
|
|
3670
|
+
} catch {
|
|
3671
|
+
return { staged: 0, incomplete: false };
|
|
3672
|
+
}
|
|
3673
|
+
const names = /* @__PURE__ */ new Set();
|
|
3674
|
+
let incomplete = false;
|
|
3675
|
+
for (const rel of paths) {
|
|
3676
|
+
const m = SKILL_PATH.exec(rel);
|
|
3677
|
+
if (m?.[1] === void 0) continue;
|
|
3678
|
+
let blob;
|
|
3679
|
+
try {
|
|
3680
|
+
blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
|
|
3681
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3682
|
+
maxBuffer: 67108864,
|
|
3683
|
+
timeout: 1e4
|
|
3684
|
+
});
|
|
3685
|
+
} catch {
|
|
3686
|
+
incomplete = true;
|
|
3687
|
+
continue;
|
|
3688
|
+
}
|
|
3689
|
+
const dest = join26(tmpRoot, rel);
|
|
3690
|
+
mkdirSync5(dirname4(dest), { recursive: true });
|
|
3691
|
+
writeFileSync5(dest, blob);
|
|
3692
|
+
names.add(m[1]);
|
|
3693
|
+
}
|
|
3694
|
+
return { staged: names.size, incomplete };
|
|
3695
|
+
}
|
|
3696
|
+
function reportSkillFindings(section2, findings) {
|
|
3697
|
+
for (const f of findings) {
|
|
3698
|
+
addItem(section2, `${yellow(warnGlyph)} ${f.RuleID} in ${f.File}`);
|
|
3699
|
+
}
|
|
3700
|
+
addItem(
|
|
3701
|
+
section2,
|
|
3702
|
+
` ${dim("run `nomad push` and choose Redact in the recovery menu to scrub these")}`
|
|
3703
|
+
);
|
|
3704
|
+
addItem(
|
|
3705
|
+
section2,
|
|
3706
|
+
` ${dim("a gsd-* skill is not auto-redactable; remove the secret from it by hand")}`
|
|
3707
|
+
);
|
|
3708
|
+
}
|
|
3709
|
+
function reportCommittedSkills(section2) {
|
|
3710
|
+
let tmpRoot;
|
|
3711
|
+
try {
|
|
3712
|
+
const cacheDir = join26(homedir5(), ".cache", "claude-nomad");
|
|
3713
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3714
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
3715
|
+
tmpRoot = join26(cacheDir, `check-shared-skills-tree-${stamp}`);
|
|
3716
|
+
mkdirSync5(tmpRoot, { recursive: true, mode: 448 });
|
|
3717
|
+
const { staged, incomplete } = buildSkillScanTree(tmpRoot);
|
|
3718
|
+
if (incomplete) {
|
|
3719
|
+
addItem(
|
|
3720
|
+
section2,
|
|
3721
|
+
`${yellow(warnGlyph)} committed-skills scan skipped: could not read every committed skill blob`
|
|
3722
|
+
);
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
if (staged === 0) return;
|
|
3726
|
+
let findings;
|
|
3727
|
+
try {
|
|
3728
|
+
findings = scanStagedTree(tmpRoot);
|
|
3729
|
+
} catch (err) {
|
|
3730
|
+
addItem(
|
|
3731
|
+
section2,
|
|
3732
|
+
`${yellow(warnGlyph)} committed-skills scan skipped: ${err.message}`
|
|
3733
|
+
);
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
if (findings === null) {
|
|
3737
|
+
addItem(
|
|
3738
|
+
section2,
|
|
3739
|
+
`${yellow(warnGlyph)} committed-skills scan skipped: no parseable gitleaks report`
|
|
3740
|
+
);
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
if (findings.length === 0) return;
|
|
3744
|
+
reportSkillFindings(section2, findings);
|
|
3745
|
+
} catch (err) {
|
|
3746
|
+
addItem(
|
|
3747
|
+
section2,
|
|
3748
|
+
`${yellow(warnGlyph)} committed-skills scan skipped: ${err.message}`
|
|
3749
|
+
);
|
|
3750
|
+
} finally {
|
|
3751
|
+
if (tmpRoot !== void 0) {
|
|
3752
|
+
try {
|
|
3753
|
+
rmSync10(tmpRoot, { recursive: true, force: true });
|
|
3754
|
+
} catch {
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3505
3760
|
// src/commands.doctor.check-shared.ts
|
|
3506
3761
|
init_config();
|
|
3507
3762
|
|
|
3508
3763
|
// src/remap.ts
|
|
3509
3764
|
init_config_sharedDirs_guard();
|
|
3510
|
-
import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as
|
|
3511
|
-
import { dirname as
|
|
3765
|
+
import { cpSync as cpSync6, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync7, readdirSync as readdirSync10, rmSync as rmSync11, statSync as statSync5 } from "node:fs";
|
|
3766
|
+
import { dirname as dirname6, join as join28, relative as relative5, sep as sep4 } from "node:path";
|
|
3512
3767
|
init_config();
|
|
3513
3768
|
|
|
3514
3769
|
// src/push-manifest.ts
|
|
@@ -3516,8 +3771,8 @@ init_config();
|
|
|
3516
3771
|
init_push_gitleaks_config();
|
|
3517
3772
|
init_utils_fs();
|
|
3518
3773
|
import { createHash } from "node:crypto";
|
|
3519
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
3520
|
-
import { dirname as
|
|
3774
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
|
|
3775
|
+
import { dirname as dirname5, join as join27 } from "node:path";
|
|
3521
3776
|
function isChanged(prev, cur, hash) {
|
|
3522
3777
|
if (prev === void 0) return true;
|
|
3523
3778
|
if (prev.size !== cur.size) return true;
|
|
@@ -3558,7 +3813,7 @@ function hashFile(absPath) {
|
|
|
3558
3813
|
}
|
|
3559
3814
|
function enumerateDir(dir, results) {
|
|
3560
3815
|
for (const entry of readdirSync9(dir)) {
|
|
3561
|
-
const fullPath =
|
|
3816
|
+
const fullPath = join27(dir, entry);
|
|
3562
3817
|
const st = statSync4(fullPath);
|
|
3563
3818
|
if (st.isDirectory()) {
|
|
3564
3819
|
enumerateDir(fullPath, results);
|
|
@@ -3570,7 +3825,7 @@ function enumerateDir(dir, results) {
|
|
|
3570
3825
|
function enumerateSourceFiles(projectDir) {
|
|
3571
3826
|
const results = [];
|
|
3572
3827
|
for (const entry of readdirSync9(projectDir)) {
|
|
3573
|
-
const fullPath =
|
|
3828
|
+
const fullPath = join27(projectDir, entry);
|
|
3574
3829
|
const st = statSync4(fullPath);
|
|
3575
3830
|
if (st.isDirectory()) {
|
|
3576
3831
|
enumerateDir(fullPath, results);
|
|
@@ -3589,8 +3844,8 @@ function computeConfigHash() {
|
|
|
3589
3844
|
const repo = repoHome();
|
|
3590
3845
|
const parts = [
|
|
3591
3846
|
fileIdentity(resolveTomlPath(repo)),
|
|
3592
|
-
fileIdentity(
|
|
3593
|
-
fileIdentity(
|
|
3847
|
+
fileIdentity(join27(repo, ".gitleaks.overlay.toml")),
|
|
3848
|
+
fileIdentity(join27(repo, ".gitleaksignore"))
|
|
3594
3849
|
];
|
|
3595
3850
|
return createHash("sha256").update(parts.join("\n")).digest("hex");
|
|
3596
3851
|
}
|
|
@@ -3610,7 +3865,7 @@ function readManifest(path) {
|
|
|
3610
3865
|
}
|
|
3611
3866
|
}
|
|
3612
3867
|
function writeManifest(path, manifest) {
|
|
3613
|
-
|
|
3868
|
+
mkdirSync6(dirname5(path), { recursive: true });
|
|
3614
3869
|
writeJsonAtomic(path, manifest);
|
|
3615
3870
|
}
|
|
3616
3871
|
function buildManifest(files, scannerVersion, configHash) {
|
|
@@ -3624,9 +3879,9 @@ init_utils_json();
|
|
|
3624
3879
|
var TMP_SUFFIX = ".nomad-tmp";
|
|
3625
3880
|
function atomicMirror(src, dst, options) {
|
|
3626
3881
|
const tmp = `${dst}${TMP_SUFFIX}`;
|
|
3627
|
-
|
|
3882
|
+
rmSync11(tmp, { recursive: true, force: true });
|
|
3628
3883
|
cpSync6(src, tmp, options);
|
|
3629
|
-
|
|
3884
|
+
rmSync11(dst, { recursive: true, force: true });
|
|
3630
3885
|
renameAtomicRetry(tmp, dst);
|
|
3631
3886
|
}
|
|
3632
3887
|
function overlaySessionDir(src, dst) {
|
|
@@ -3634,7 +3889,7 @@ function overlaySessionDir(src, dst) {
|
|
|
3634
3889
|
cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
|
|
3635
3890
|
}
|
|
3636
3891
|
function copyFileAtomic(src, dst) {
|
|
3637
|
-
|
|
3892
|
+
mkdirSync7(dirname6(dst), { recursive: true });
|
|
3638
3893
|
const tmp = `${dst}.tmp.${process.pid}`;
|
|
3639
3894
|
cpSync6(src, tmp, { force: true, preserveTimestamps: true });
|
|
3640
3895
|
renameAtomicRetry(tmp, dst);
|
|
@@ -3652,12 +3907,12 @@ function applySelective(sel, localDir, repoDst) {
|
|
|
3652
3907
|
const prefix = `${localDir}${sep4}`;
|
|
3653
3908
|
for (const src of sel.changed) {
|
|
3654
3909
|
if (!src.startsWith(prefix)) continue;
|
|
3655
|
-
copyFileAtomic(src,
|
|
3910
|
+
copyFileAtomic(src, join28(repoDst, relative5(localDir, src)));
|
|
3656
3911
|
}
|
|
3657
3912
|
for (const src of sel.deleted) {
|
|
3658
3913
|
if (!src.startsWith(prefix)) continue;
|
|
3659
|
-
const dst =
|
|
3660
|
-
if (existsSync21(dst))
|
|
3914
|
+
const dst = join28(repoDst, relative5(localDir, src));
|
|
3915
|
+
if (existsSync21(dst)) rmSync11(dst);
|
|
3661
3916
|
}
|
|
3662
3917
|
}
|
|
3663
3918
|
function copyDirJsonlOnly(src, dst) {
|
|
@@ -3686,16 +3941,16 @@ function remapPull(ts, opts = {}) {
|
|
|
3686
3941
|
const wouldPull = [];
|
|
3687
3942
|
const repo = repoHome();
|
|
3688
3943
|
const claude = claudeHome();
|
|
3689
|
-
const mapPath =
|
|
3690
|
-
const repoProjects =
|
|
3944
|
+
const mapPath = join28(repo, "path-map.json");
|
|
3945
|
+
const repoProjects = join28(repo, "shared", "projects");
|
|
3691
3946
|
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) {
|
|
3692
3947
|
const text = "no path-map or repo projects dir; skipping session remap";
|
|
3693
3948
|
emitPreview(opts.onPreview, { kind: "note", text }, text);
|
|
3694
3949
|
return { unmapped: 0, pulled, wouldPull };
|
|
3695
3950
|
}
|
|
3696
3951
|
const map = readPathMap(mapPath);
|
|
3697
|
-
const localProjects =
|
|
3698
|
-
if (!dryRun)
|
|
3952
|
+
const localProjects = join28(claude, "projects");
|
|
3953
|
+
if (!dryRun) mkdirSync7(localProjects, { recursive: true });
|
|
3699
3954
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
3700
3955
|
assertSafeLogical(logical);
|
|
3701
3956
|
const localPath = hosts[HOST];
|
|
@@ -3704,9 +3959,9 @@ function remapPull(ts, opts = {}) {
|
|
|
3704
3959
|
continue;
|
|
3705
3960
|
}
|
|
3706
3961
|
assertSafeLocalRoot(localPath, logical);
|
|
3707
|
-
const src =
|
|
3962
|
+
const src = join28(repoProjects, logical);
|
|
3708
3963
|
if (!existsSync21(src)) continue;
|
|
3709
|
-
const dst =
|
|
3964
|
+
const dst = join28(localProjects, encodePath(localPath));
|
|
3710
3965
|
if (dryRun) {
|
|
3711
3966
|
wouldPull.push(logical);
|
|
3712
3967
|
emitPreview(
|
|
@@ -3725,8 +3980,8 @@ function remapPull(ts, opts = {}) {
|
|
|
3725
3980
|
function countLocalOnly(src, dst) {
|
|
3726
3981
|
let count = 0;
|
|
3727
3982
|
for (const name of readdirSync10(dst)) {
|
|
3728
|
-
const dstPath =
|
|
3729
|
-
const srcPath =
|
|
3983
|
+
const dstPath = join28(dst, name);
|
|
3984
|
+
const srcPath = join28(src, name);
|
|
3730
3985
|
if (lstatSync10(dstPath).isDirectory()) {
|
|
3731
3986
|
count += countLocalOnly(srcPath, dstPath);
|
|
3732
3987
|
} else if (lstatSync10(srcPath, { throwIfNoEntry: false }) === void 0) {
|
|
@@ -3738,20 +3993,20 @@ function countLocalOnly(src, dst) {
|
|
|
3738
3993
|
function scanLocalOnly() {
|
|
3739
3994
|
const repo = repoHome();
|
|
3740
3995
|
const claude = claudeHome();
|
|
3741
|
-
const mapPath =
|
|
3742
|
-
const repoProjects =
|
|
3996
|
+
const mapPath = join28(repo, "path-map.json");
|
|
3997
|
+
const repoProjects = join28(repo, "shared", "projects");
|
|
3743
3998
|
if (!existsSync21(mapPath) || !existsSync21(repoProjects)) return 0;
|
|
3744
3999
|
const map = readPathMap(mapPath);
|
|
3745
|
-
const localProjects =
|
|
4000
|
+
const localProjects = join28(claude, "projects");
|
|
3746
4001
|
let count = 0;
|
|
3747
4002
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
3748
4003
|
assertSafeLogical(logical);
|
|
3749
4004
|
const localPath = hosts[HOST];
|
|
3750
4005
|
if (!localPath || localPath === "TBD") continue;
|
|
3751
4006
|
assertSafeLocalRoot(localPath, logical);
|
|
3752
|
-
const dst =
|
|
4007
|
+
const dst = join28(localProjects, encodePath(localPath));
|
|
3753
4008
|
if (!existsSync21(dst)) continue;
|
|
3754
|
-
count += countLocalOnly(
|
|
4009
|
+
count += countLocalOnly(join28(repoProjects, logical), dst);
|
|
3755
4010
|
}
|
|
3756
4011
|
return count;
|
|
3757
4012
|
}
|
|
@@ -3786,17 +4041,17 @@ function remapPush(ts, opts = {}) {
|
|
|
3786
4041
|
const wouldPush = [];
|
|
3787
4042
|
const repo = repoHome();
|
|
3788
4043
|
const claude = claudeHome();
|
|
3789
|
-
const mapPath =
|
|
4044
|
+
const mapPath = join28(repo, "path-map.json");
|
|
3790
4045
|
if (!existsSync21(mapPath)) {
|
|
3791
4046
|
log("no path-map.json; skipping session export");
|
|
3792
4047
|
return { unmapped: 0, collisions: 0, pushed, wouldPush };
|
|
3793
4048
|
}
|
|
3794
4049
|
const map = readPathMap(mapPath);
|
|
3795
|
-
const localProjects =
|
|
3796
|
-
const repoProjects =
|
|
4050
|
+
const localProjects = join28(claude, "projects");
|
|
4051
|
+
const repoProjects = join28(repo, "shared", "projects");
|
|
3797
4052
|
const reverse = buildReverseMap(map);
|
|
3798
4053
|
if (!existsSync21(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
|
|
3799
|
-
if (!dryRun)
|
|
4054
|
+
if (!dryRun) mkdirSync7(repoProjects, { recursive: true });
|
|
3800
4055
|
for (const dir of readdirSync10(localProjects)) {
|
|
3801
4056
|
if (dir.endsWith(TMP_SUFFIX)) continue;
|
|
3802
4057
|
const logical = reverse.get(dir);
|
|
@@ -3804,8 +4059,8 @@ function remapPush(ts, opts = {}) {
|
|
|
3804
4059
|
unmapped++;
|
|
3805
4060
|
continue;
|
|
3806
4061
|
}
|
|
3807
|
-
const localDir =
|
|
3808
|
-
const repoDst =
|
|
4062
|
+
const localDir = join28(localProjects, dir);
|
|
4063
|
+
const repoDst = join28(repoProjects, logical);
|
|
3809
4064
|
if (skipForSelection(opts.selection, localDir)) continue;
|
|
3810
4065
|
if (dryRun) {
|
|
3811
4066
|
wouldPush.push(logical);
|
|
@@ -3829,7 +4084,7 @@ function buildScanTree(tmpRoot) {
|
|
|
3829
4084
|
const logicalToEncoded = /* @__PURE__ */ new Map();
|
|
3830
4085
|
let staged = 0;
|
|
3831
4086
|
const repo = repoHome();
|
|
3832
|
-
const mapPath =
|
|
4087
|
+
const mapPath = join29(repo, "path-map.json");
|
|
3833
4088
|
if (!existsSync22(mapPath)) return { logicalToEncoded, staged, malformed: false };
|
|
3834
4089
|
let map;
|
|
3835
4090
|
try {
|
|
@@ -3847,12 +4102,12 @@ function buildScanTree(tmpRoot) {
|
|
|
3847
4102
|
if (!p || p === "TBD") continue;
|
|
3848
4103
|
reverse.set(encodePath(p), logical);
|
|
3849
4104
|
}
|
|
3850
|
-
const localProjects =
|
|
4105
|
+
const localProjects = join29(claudeHome(), "projects");
|
|
3851
4106
|
if (!existsSync22(localProjects)) return { logicalToEncoded, staged, malformed: false };
|
|
3852
4107
|
for (const dir of readdirSync11(localProjects)) {
|
|
3853
4108
|
const logical = reverse.get(dir);
|
|
3854
4109
|
if (!logical) continue;
|
|
3855
|
-
copyDirJsonlOnly(
|
|
4110
|
+
copyDirJsonlOnly(join29(localProjects, dir), join29(tmpRoot, "shared", "projects", logical));
|
|
3856
4111
|
logicalToEncoded.set(logical, dir);
|
|
3857
4112
|
staged++;
|
|
3858
4113
|
}
|
|
@@ -3860,7 +4115,7 @@ function buildScanTree(tmpRoot) {
|
|
|
3860
4115
|
}
|
|
3861
4116
|
function probeGitleaksForScan() {
|
|
3862
4117
|
try {
|
|
3863
|
-
|
|
4118
|
+
execFileSync11("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
3864
4119
|
return "ok";
|
|
3865
4120
|
} catch (err) {
|
|
3866
4121
|
if (err.code === "ENOENT") return "missing";
|
|
@@ -3881,13 +4136,12 @@ function ensureGitleaksReady(section2, gitleaksReady) {
|
|
|
3881
4136
|
}
|
|
3882
4137
|
return true;
|
|
3883
4138
|
}
|
|
3884
|
-
function
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
const
|
|
3889
|
-
const
|
|
3890
|
-
const tmpRoot = join27(cacheDir, `check-shared-tree-${stamp}`);
|
|
4139
|
+
function runLocalPreviewScan(section2) {
|
|
4140
|
+
const cacheDir = join29(homedir6(), ".cache", "claude-nomad");
|
|
4141
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
4142
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
4143
|
+
const reportPath = join29(cacheDir, `check-shared-${stamp}.json`);
|
|
4144
|
+
const tmpRoot = join29(cacheDir, `check-shared-tree-${stamp}`);
|
|
3891
4145
|
try {
|
|
3892
4146
|
const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
|
|
3893
4147
|
if (malformed) {
|
|
@@ -3901,15 +4155,27 @@ function reportCheckShared(section2, gitleaksReady) {
|
|
|
3901
4155
|
}
|
|
3902
4156
|
scanAndReport(section2, tmpRoot, staged, logicalToEncoded);
|
|
3903
4157
|
} finally {
|
|
3904
|
-
|
|
3905
|
-
|
|
4158
|
+
rmSync12(reportPath, { force: true });
|
|
4159
|
+
rmSync12(tmpRoot, { recursive: true, force: true });
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
function reportCheckShared(section2, gitleaksReady) {
|
|
4163
|
+
if (!ensureGitleaksReady(section2, gitleaksReady)) return;
|
|
4164
|
+
try {
|
|
4165
|
+
runLocalPreviewScan(section2);
|
|
4166
|
+
} catch (err) {
|
|
4167
|
+
addItem(section2, `${red(failGlyph)} shared scan failed: ${err.message}`);
|
|
4168
|
+
process.exitCode = 1;
|
|
4169
|
+
} finally {
|
|
4170
|
+
reportCommittedMemory(section2);
|
|
4171
|
+
reportCommittedSkills(section2);
|
|
3906
4172
|
}
|
|
3907
4173
|
}
|
|
3908
4174
|
|
|
3909
4175
|
// src/commands.doctor.checks.hooks.scope.ts
|
|
3910
4176
|
init_color();
|
|
3911
4177
|
import { existsSync as existsSync23, readFileSync as readFileSync8, readdirSync as readdirSync12, realpathSync as realpathSync2 } from "node:fs";
|
|
3912
|
-
import { dirname as
|
|
4178
|
+
import { dirname as dirname7, extname, join as join30 } from "node:path";
|
|
3913
4179
|
init_config();
|
|
3914
4180
|
function typeFromPackageJson(pkgPath) {
|
|
3915
4181
|
try {
|
|
@@ -3929,11 +4195,11 @@ function effectiveType(hookPath) {
|
|
|
3929
4195
|
} catch {
|
|
3930
4196
|
return null;
|
|
3931
4197
|
}
|
|
3932
|
-
let dir =
|
|
4198
|
+
let dir = dirname7(real);
|
|
3933
4199
|
for (; ; ) {
|
|
3934
|
-
const pkg =
|
|
4200
|
+
const pkg = join30(dir, "package.json");
|
|
3935
4201
|
if (existsSync23(pkg)) return typeFromPackageJson(pkg);
|
|
3936
|
-
const parent =
|
|
4202
|
+
const parent = dirname7(dir);
|
|
3937
4203
|
if (parent === dir) return "cjs";
|
|
3938
4204
|
dir = parent;
|
|
3939
4205
|
}
|
|
@@ -3974,7 +4240,7 @@ function safeRead(path) {
|
|
|
3974
4240
|
}
|
|
3975
4241
|
}
|
|
3976
4242
|
function reportHookScopeCheck(section2) {
|
|
3977
|
-
const hooksDir =
|
|
4243
|
+
const hooksDir = join30(claudeHome(), "hooks");
|
|
3978
4244
|
if (!existsSync23(hooksDir)) {
|
|
3979
4245
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
|
|
3980
4246
|
return;
|
|
@@ -3982,7 +4248,7 @@ function reportHookScopeCheck(section2) {
|
|
|
3982
4248
|
let anyWarn = false;
|
|
3983
4249
|
for (const name of safeReaddir2(hooksDir)) {
|
|
3984
4250
|
if (extname(name) !== ".js") continue;
|
|
3985
|
-
const abs =
|
|
4251
|
+
const abs = join30(hooksDir, name);
|
|
3986
4252
|
const eff = effectiveType(abs);
|
|
3987
4253
|
if (eff === null) continue;
|
|
3988
4254
|
const src = safeRead(abs);
|
|
@@ -4003,7 +4269,7 @@ function reportHookScopeCheck(section2) {
|
|
|
4003
4269
|
// src/commands.doctor.checks.hooks.ts
|
|
4004
4270
|
init_color();
|
|
4005
4271
|
import { existsSync as existsSync24 } from "node:fs";
|
|
4006
|
-
import { join as
|
|
4272
|
+
import { join as join31 } from "node:path";
|
|
4007
4273
|
init_config();
|
|
4008
4274
|
function expandHome(token) {
|
|
4009
4275
|
const h2 = home();
|
|
@@ -4059,7 +4325,7 @@ function checkEventGroups(section2, event, groups) {
|
|
|
4059
4325
|
return anyFail;
|
|
4060
4326
|
}
|
|
4061
4327
|
function reportHooksTargetCheck(section2) {
|
|
4062
|
-
const settingsPath =
|
|
4328
|
+
const settingsPath = join31(claudeHome(), "settings.json");
|
|
4063
4329
|
if (!existsSync24(settingsPath)) {
|
|
4064
4330
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
|
|
4065
4331
|
return;
|
|
@@ -4084,11 +4350,11 @@ function reportHooksTargetCheck(section2) {
|
|
|
4084
4350
|
// src/commands.doctor.checks.hooks.preserve-symlinks.ts
|
|
4085
4351
|
init_color();
|
|
4086
4352
|
import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
|
|
4087
|
-
import { join as
|
|
4353
|
+
import { join as join33 } from "node:path";
|
|
4088
4354
|
|
|
4089
4355
|
// src/commands.doctor.checks.hooks.preserve-symlinks.probe.ts
|
|
4090
4356
|
import { closeSync as closeSync3, existsSync as existsSync25, openSync as openSync3, readSync, realpathSync as realpathSync3 } from "node:fs";
|
|
4091
|
-
import { dirname as
|
|
4357
|
+
import { dirname as dirname8, join as join32, resolve as resolve2 } from "node:path";
|
|
4092
4358
|
function suppressedRanges(src) {
|
|
4093
4359
|
const ranges = [];
|
|
4094
4360
|
const re = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|'[^']*'|"[^"]*"|`[^`]*`/g;
|
|
@@ -4125,7 +4391,7 @@ function specifierIsMissing(specifier, baseDir) {
|
|
|
4125
4391
|
if (existsSync25(base + ext)) return false;
|
|
4126
4392
|
}
|
|
4127
4393
|
for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
|
|
4128
|
-
if (existsSync25(
|
|
4394
|
+
if (existsSync25(join32(base, idx))) return false;
|
|
4129
4395
|
}
|
|
4130
4396
|
return true;
|
|
4131
4397
|
}
|
|
@@ -4151,7 +4417,7 @@ function relativeRequireTargetsBroken(scriptPath) {
|
|
|
4151
4417
|
}
|
|
4152
4418
|
const specifiers = topRelativeSpecifiers(raw);
|
|
4153
4419
|
if (specifiers.length === 0) return false;
|
|
4154
|
-
const baseDir =
|
|
4420
|
+
const baseDir = dirname8(realPath);
|
|
4155
4421
|
for (const spec of specifiers) {
|
|
4156
4422
|
if (specifierIsMissing(spec, baseDir)) return true;
|
|
4157
4423
|
}
|
|
@@ -4180,7 +4446,7 @@ function commandTokens(command) {
|
|
|
4180
4446
|
return tokens;
|
|
4181
4447
|
}
|
|
4182
4448
|
function readPathMapSafe() {
|
|
4183
|
-
const mapPath =
|
|
4449
|
+
const mapPath = join33(repoHome(), "path-map.json");
|
|
4184
4450
|
if (!existsSync26(mapPath)) return { projects: {} };
|
|
4185
4451
|
try {
|
|
4186
4452
|
return JSON.parse(readFileSync9(mapPath, "utf8"));
|
|
@@ -4255,7 +4521,7 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
|
|
|
4255
4521
|
return anyWarn;
|
|
4256
4522
|
}
|
|
4257
4523
|
function reportPreserveSymlinksCheck(section2) {
|
|
4258
|
-
const settingsPath =
|
|
4524
|
+
const settingsPath = join33(claudeHome(), "settings.json");
|
|
4259
4525
|
if (!existsSync26(settingsPath)) {
|
|
4260
4526
|
addItem(
|
|
4261
4527
|
section2,
|
|
@@ -4290,7 +4556,7 @@ function reportPreserveSymlinksCheck(section2) {
|
|
|
4290
4556
|
// src/commands.doctor.checks.settings-drift.ts
|
|
4291
4557
|
init_color();
|
|
4292
4558
|
import { existsSync as existsSync27, readFileSync as readFileSync10 } from "node:fs";
|
|
4293
|
-
import { join as
|
|
4559
|
+
import { join as join34 } from "node:path";
|
|
4294
4560
|
init_config();
|
|
4295
4561
|
init_utils_json();
|
|
4296
4562
|
function diffMergedSettings(merged, settings) {
|
|
@@ -4308,7 +4574,7 @@ function tryReadJson(filePath) {
|
|
|
4308
4574
|
}
|
|
4309
4575
|
}
|
|
4310
4576
|
function reportHooksBaseSelfCleanNote(section2) {
|
|
4311
|
-
const basePath =
|
|
4577
|
+
const basePath = join34(repoHome(), "shared", "settings.base.json");
|
|
4312
4578
|
const base = tryReadJson(basePath);
|
|
4313
4579
|
if (base === null) return;
|
|
4314
4580
|
if (!baseHasGsdHookEntries(base)) return;
|
|
@@ -4321,9 +4587,9 @@ function reportSettingsDriftCheck(section2) {
|
|
|
4321
4587
|
const claude = claudeHome();
|
|
4322
4588
|
const repo = repoHome();
|
|
4323
4589
|
const host = HOST;
|
|
4324
|
-
const settingsPath =
|
|
4325
|
-
const basePath =
|
|
4326
|
-
const hostPath =
|
|
4590
|
+
const settingsPath = join34(claude, "settings.json");
|
|
4591
|
+
const basePath = join34(repo, "shared", "settings.base.json");
|
|
4592
|
+
const hostPath = join34(repo, "hosts", `${host}.json`);
|
|
4327
4593
|
if (!existsSync27(settingsPath)) {
|
|
4328
4594
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
|
|
4329
4595
|
return;
|
|
@@ -4505,7 +4771,7 @@ function reportNodeEngineCheck(section2) {
|
|
|
4505
4771
|
|
|
4506
4772
|
// src/commands.doctor.checks.longpaths.ts
|
|
4507
4773
|
init_color();
|
|
4508
|
-
import { execFileSync as
|
|
4774
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
4509
4775
|
init_config();
|
|
4510
4776
|
var PROBE_TIMEOUT_MS = 3e3;
|
|
4511
4777
|
var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
|
|
@@ -4539,7 +4805,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
|
|
|
4539
4805
|
}
|
|
4540
4806
|
addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
|
|
4541
4807
|
}
|
|
4542
|
-
function reportLongPathsCheck(section2, run =
|
|
4808
|
+
function reportLongPathsCheck(section2, run = execFileSync12) {
|
|
4543
4809
|
if (process.platform !== "win32") return;
|
|
4544
4810
|
addLongpathsRow(
|
|
4545
4811
|
section2,
|
|
@@ -4561,15 +4827,15 @@ function reportSyncModality(section2) {
|
|
|
4561
4827
|
|
|
4562
4828
|
// src/commands.doctor.checks.crlf.ts
|
|
4563
4829
|
init_color();
|
|
4564
|
-
import { execFileSync as
|
|
4830
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
4565
4831
|
import { existsSync as existsSync28, readFileSync as readFileSync13 } from "node:fs";
|
|
4566
|
-
import { join as
|
|
4832
|
+
import { join as join35 } from "node:path";
|
|
4567
4833
|
init_config();
|
|
4568
4834
|
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
4569
4835
|
var GUARD_LINE = /^\*\s+-text\b/;
|
|
4570
4836
|
function hasGitattributesGuard(repo) {
|
|
4571
4837
|
try {
|
|
4572
|
-
const path =
|
|
4838
|
+
const path = join35(repo, ".gitattributes");
|
|
4573
4839
|
if (!existsSync28(path)) return false;
|
|
4574
4840
|
const content = readFileSync13(path, "utf8");
|
|
4575
4841
|
return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
|
|
@@ -4600,7 +4866,7 @@ function addExposedRow(section2, repo, verdict) {
|
|
|
4600
4866
|
const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
|
|
4601
4867
|
addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
|
|
4602
4868
|
}
|
|
4603
|
-
function reportCrlfGuardCheck(section2, run =
|
|
4869
|
+
function reportCrlfGuardCheck(section2, run = execFileSync13) {
|
|
4604
4870
|
const repo = repoHome();
|
|
4605
4871
|
if (hasGitattributesGuard(repo)) {
|
|
4606
4872
|
addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
|
|
@@ -4611,35 +4877,35 @@ function reportCrlfGuardCheck(section2, run = execFileSync11) {
|
|
|
4611
4877
|
|
|
4612
4878
|
// src/spinner.ts
|
|
4613
4879
|
init_color();
|
|
4614
|
-
import { existsSync as
|
|
4880
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
4615
4881
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
4616
4882
|
import { Worker } from "node:worker_threads";
|
|
4617
4883
|
|
|
4618
4884
|
// src/commands.push.recovery.ts
|
|
4619
4885
|
init_config();
|
|
4620
|
-
import { readFileSync as
|
|
4621
|
-
import { join as
|
|
4886
|
+
import { readFileSync as readFileSync18, rmSync as rmSync15, writeFileSync as writeFileSync9 } from "node:fs";
|
|
4887
|
+
import { join as join43 } from "node:path";
|
|
4622
4888
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
4623
4889
|
|
|
4624
4890
|
// src/commands.push.recovery.actions.ts
|
|
4625
4891
|
init_config();
|
|
4626
|
-
import { readFileSync as
|
|
4627
|
-
import { isAbsolute as isAbsolute2, resolve as resolve3, sep as
|
|
4892
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
4893
|
+
import { isAbsolute as isAbsolute2, resolve as resolve3, sep as sep7 } from "node:path";
|
|
4628
4894
|
|
|
4629
4895
|
// src/commands.push.recovery.redact.ts
|
|
4630
4896
|
init_config();
|
|
4631
4897
|
init_config_sharedDirs_guard();
|
|
4632
|
-
import { cpSync as
|
|
4633
|
-
import { dirname as
|
|
4898
|
+
import { cpSync as cpSync8, existsSync as existsSync32, mkdirSync as mkdirSync10, statSync as statSync8 } from "node:fs";
|
|
4899
|
+
import { dirname as dirname10, join as join39, sep as sep5 } from "node:path";
|
|
4634
4900
|
|
|
4635
4901
|
// src/commands.redact.ts
|
|
4636
4902
|
init_config();
|
|
4637
4903
|
import { existsSync as existsSync30, statSync as statSync7 } from "node:fs";
|
|
4638
|
-
import { dirname as
|
|
4904
|
+
import { dirname as dirname9, join as join37 } from "node:path";
|
|
4639
4905
|
|
|
4640
4906
|
// 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
|
|
4907
|
+
import { existsSync as existsSync29, lstatSync as lstatSync11, readFileSync as readFileSync14, readdirSync as readdirSync13, statSync as statSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
4908
|
+
import { join as join36 } from "node:path";
|
|
4643
4909
|
init_utils_fs();
|
|
4644
4910
|
init_utils();
|
|
4645
4911
|
function collectFiles(dir, out) {
|
|
@@ -4647,7 +4913,7 @@ function collectFiles(dir, out) {
|
|
|
4647
4913
|
const st = lstatSync11(dir);
|
|
4648
4914
|
if (!st.isDirectory()) return;
|
|
4649
4915
|
for (const entry of readdirSync13(dir)) {
|
|
4650
|
-
const abs =
|
|
4916
|
+
const abs = join36(dir, entry);
|
|
4651
4917
|
const lst = lstatSync11(abs);
|
|
4652
4918
|
if (lst.isSymbolicLink()) continue;
|
|
4653
4919
|
if (lst.isDirectory()) {
|
|
@@ -4691,7 +4957,7 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
4691
4957
|
`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
4958
|
);
|
|
4693
4959
|
}
|
|
4694
|
-
|
|
4960
|
+
writeFileSync6(filePath, after, "utf8");
|
|
4695
4961
|
}
|
|
4696
4962
|
}
|
|
4697
4963
|
return { total, dirty };
|
|
@@ -4699,10 +4965,10 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
4699
4965
|
|
|
4700
4966
|
// src/commands.pushed-history.ts
|
|
4701
4967
|
init_utils();
|
|
4702
|
-
import { execFileSync as
|
|
4968
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
4703
4969
|
function pushedRef(repo) {
|
|
4704
4970
|
try {
|
|
4705
|
-
const ref =
|
|
4971
|
+
const ref = execFileSync14("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
|
|
4706
4972
|
cwd: repo,
|
|
4707
4973
|
stdio: ["ignore", "pipe", "ignore"]
|
|
4708
4974
|
}).toString().trim();
|
|
@@ -4715,7 +4981,7 @@ function sessionInPushedHistory(id, repo) {
|
|
|
4715
4981
|
const ref = pushedRef(repo);
|
|
4716
4982
|
if (ref === null) return false;
|
|
4717
4983
|
try {
|
|
4718
|
-
const out =
|
|
4984
|
+
const out = execFileSync14(
|
|
4719
4985
|
"git",
|
|
4720
4986
|
[
|
|
4721
4987
|
"log",
|
|
@@ -4747,19 +5013,20 @@ function warnIfSessionPushed(id, repo) {
|
|
|
4747
5013
|
|
|
4748
5014
|
// src/commands.redact.ts
|
|
4749
5015
|
init_push_gitleaks_scan();
|
|
5016
|
+
init_exit_codes();
|
|
4750
5017
|
init_utils_fs();
|
|
4751
5018
|
init_utils_json();
|
|
4752
5019
|
init_utils();
|
|
4753
5020
|
function resolveLiveTranscript(id) {
|
|
4754
5021
|
try {
|
|
4755
|
-
const mapPath =
|
|
5022
|
+
const mapPath = join37(repoHome(), "path-map.json");
|
|
4756
5023
|
if (!existsSync30(mapPath)) return null;
|
|
4757
5024
|
const projects = readJson(mapPath).projects;
|
|
4758
5025
|
const claude = claudeHome();
|
|
4759
5026
|
for (const hostMap of Object.values(projects)) {
|
|
4760
5027
|
const abs = hostMap[HOST];
|
|
4761
5028
|
if (abs === void 0) continue;
|
|
4762
|
-
const live =
|
|
5029
|
+
const live = join37(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
4763
5030
|
if (existsSync30(live)) return live;
|
|
4764
5031
|
}
|
|
4765
5032
|
return null;
|
|
@@ -4776,7 +5043,7 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
4776
5043
|
const { id, rule, dryRun = false, findings: rawFindings } = opts;
|
|
4777
5044
|
if (id.length === 0 || id.length > 128 || !/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
4778
5045
|
fail(`invalid session id: ${id}`);
|
|
4779
|
-
process.exit(
|
|
5046
|
+
process.exit(EXIT.USAGE);
|
|
4780
5047
|
}
|
|
4781
5048
|
const repo = repoHome();
|
|
4782
5049
|
const backup = backupBase();
|
|
@@ -4790,7 +5057,7 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
4790
5057
|
process.exitCode = 1;
|
|
4791
5058
|
return;
|
|
4792
5059
|
}
|
|
4793
|
-
const sessionDir =
|
|
5060
|
+
const sessionDir = join37(dirname9(localPath), id);
|
|
4794
5061
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4795
5062
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
|
|
4796
5063
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4850,6 +5117,78 @@ init_utils();
|
|
|
4850
5117
|
|
|
4851
5118
|
// src/commands.push.recovery.seams.ts
|
|
4852
5119
|
init_push_gitleaks();
|
|
5120
|
+
|
|
5121
|
+
// src/commands.push.recovery.memory.ts
|
|
5122
|
+
init_config();
|
|
5123
|
+
init_config_sharedDirs_guard();
|
|
5124
|
+
import { cpSync as cpSync7, existsSync as existsSync31, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
|
|
5125
|
+
import { join as join38 } from "node:path";
|
|
5126
|
+
init_push_gitleaks_scan();
|
|
5127
|
+
init_utils_fs();
|
|
5128
|
+
init_utils_json();
|
|
5129
|
+
init_utils();
|
|
5130
|
+
var MEMORY_FINDING_PATH = /^shared\/projects\/([^/]+)\/memory\/([^/]+\.md)$/;
|
|
5131
|
+
var SAFE_MEMORY_FILENAME = /^[^/\\]+\.md$/;
|
|
5132
|
+
var MEMORY_DIR_PATH = /^shared\/projects\/[^/]+\/memory\//;
|
|
5133
|
+
function memoryFileFromFinding(f) {
|
|
5134
|
+
const m = MEMORY_FINDING_PATH.exec(f.File);
|
|
5135
|
+
if (m?.[1] === void 0 || m[2] === void 0) return null;
|
|
5136
|
+
return { logical: m[1], filename: m[2] };
|
|
5137
|
+
}
|
|
5138
|
+
function isMemoryFindingPath(f) {
|
|
5139
|
+
return MEMORY_DIR_PATH.test(f.File);
|
|
5140
|
+
}
|
|
5141
|
+
function resolveMemoryLocalPath(logical, filename, map) {
|
|
5142
|
+
assertSafeLogical(logical);
|
|
5143
|
+
if (!SAFE_MEMORY_FILENAME.test(filename) || filename.includes("..")) return null;
|
|
5144
|
+
const abs = map.projects[logical]?.[HOST];
|
|
5145
|
+
if (abs === void 0) return null;
|
|
5146
|
+
const localPath = join38(claudeHome(), "projects", encodePath(abs), "memory", filename);
|
|
5147
|
+
return existsSync31(localPath) ? localPath : null;
|
|
5148
|
+
}
|
|
5149
|
+
function preflightMemoryRedactable(f, map) {
|
|
5150
|
+
const parsed = memoryFileFromFinding(f);
|
|
5151
|
+
if (parsed === null) return "a finding is not a project-level memory file";
|
|
5152
|
+
const localPath = resolveMemoryLocalPath(parsed.logical, parsed.filename, map);
|
|
5153
|
+
if (localPath === null) {
|
|
5154
|
+
return `memory file ${parsed.logical}/memory/${parsed.filename}: local file not found or unmapped`;
|
|
5155
|
+
}
|
|
5156
|
+
return null;
|
|
5157
|
+
}
|
|
5158
|
+
function applyMemoryRedact(f, ts, map, scan = scanFile) {
|
|
5159
|
+
const refuse = (msg) => {
|
|
5160
|
+
log(msg);
|
|
5161
|
+
return false;
|
|
5162
|
+
};
|
|
5163
|
+
const parsed = memoryFileFromFinding(f);
|
|
5164
|
+
if (parsed === null) {
|
|
5165
|
+
return refuse("could not parse this finding as a project-level memory file; choose Skip.");
|
|
5166
|
+
}
|
|
5167
|
+
const { logical, filename } = parsed;
|
|
5168
|
+
const localPath = resolveMemoryLocalPath(logical, filename, map);
|
|
5169
|
+
if (localPath === null) {
|
|
5170
|
+
return refuse(
|
|
5171
|
+
`could not locate the local memory file for ${logical}/memory/${filename}; choose Skip.`
|
|
5172
|
+
);
|
|
5173
|
+
}
|
|
5174
|
+
const findings = scan(localPath);
|
|
5175
|
+
if (findings === null) {
|
|
5176
|
+
return refuse(`re-scan of ${logical}/memory/${filename} failed; choose Skip.`);
|
|
5177
|
+
}
|
|
5178
|
+
if (findings.length === 0) {
|
|
5179
|
+
return refuse(`nothing to redact in ${logical}/memory/${filename}; choose Skip.`);
|
|
5180
|
+
}
|
|
5181
|
+
backupBeforeWrite(localPath, ts);
|
|
5182
|
+
const before = readFileSync15(localPath, "utf8");
|
|
5183
|
+
const after = applyRedactions(before, findings);
|
|
5184
|
+
writeFileSync7(localPath, after, "utf8");
|
|
5185
|
+
const stagedMemoryDir = join38(repoHome(), "shared", "projects", logical, "memory");
|
|
5186
|
+
mkdirSync9(stagedMemoryDir, { recursive: true });
|
|
5187
|
+
cpSync7(localPath, join38(stagedMemoryDir, filename), { force: true });
|
|
5188
|
+
return true;
|
|
5189
|
+
}
|
|
5190
|
+
|
|
5191
|
+
// src/commands.push.recovery.seams.ts
|
|
4853
5192
|
var MASK_LEAD = 4;
|
|
4854
5193
|
var MASK_BODY = "************";
|
|
4855
5194
|
var CONTEXT_WINDOW = 40;
|
|
@@ -4858,8 +5197,10 @@ function findingKey(f) {
|
|
|
4858
5197
|
return `${f.File}:${f.StartLine}:${f.StartColumn}:${f.RuleID}`;
|
|
4859
5198
|
}
|
|
4860
5199
|
var VALID_SID = /^[A-Za-z0-9_-]+$/;
|
|
5200
|
+
var SUBTREE_PATH = /^shared\/projects\/[^/]+\/([^/]+)\/.+$/;
|
|
4861
5201
|
function sessionIdFromFinding(f) {
|
|
4862
|
-
|
|
5202
|
+
if (isMemoryFindingPath(f)) return null;
|
|
5203
|
+
const m = SESSION_PATH.exec(f.File) ?? SUBTREE_PATH.exec(f.File);
|
|
4863
5204
|
if (m === null) return null;
|
|
4864
5205
|
const sid = m[1];
|
|
4865
5206
|
return VALID_SID.test(sid) ? sid : null;
|
|
@@ -4906,8 +5247,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
|
|
|
4906
5247
|
assertSafeLogical(logical);
|
|
4907
5248
|
const abs = hostMap[HOST];
|
|
4908
5249
|
if (abs === void 0) continue;
|
|
4909
|
-
if (localPath.startsWith(
|
|
4910
|
-
return
|
|
5250
|
+
if (localPath.startsWith(join39(claude, "projects", encodePath(abs)) + sep5)) {
|
|
5251
|
+
return join39(repo, "shared", "projects", logical);
|
|
4911
5252
|
}
|
|
4912
5253
|
}
|
|
4913
5254
|
return null;
|
|
@@ -4917,7 +5258,7 @@ function preflightRedactable(f, map, nowMs) {
|
|
|
4917
5258
|
if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
|
|
4918
5259
|
const localPath = resolveLiveTranscript(sid);
|
|
4919
5260
|
if (localPath === null) return `session ${sid}: local transcript not found`;
|
|
4920
|
-
const sessionDir =
|
|
5261
|
+
const sessionDir = join39(dirname10(localPath), sid);
|
|
4921
5262
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4922
5263
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4923
5264
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4947,7 +5288,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4947
5288
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4948
5289
|
);
|
|
4949
5290
|
}
|
|
4950
|
-
const sessionDir =
|
|
5291
|
+
const sessionDir = join39(dirname10(localPath), sid);
|
|
4951
5292
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4952
5293
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4953
5294
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4980,28 +5321,163 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4980
5321
|
`nothing to redact in the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4981
5322
|
);
|
|
4982
5323
|
}
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
if (
|
|
4986
|
-
|
|
5324
|
+
mkdirSync10(stagedProjectDir, { recursive: true });
|
|
5325
|
+
cpSync8(localPath, join39(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
5326
|
+
if (existsSync32(sessionDir)) {
|
|
5327
|
+
cpSync8(sessionDir, join39(stagedProjectDir, sid), { force: true, recursive: true });
|
|
4987
5328
|
}
|
|
4988
5329
|
return true;
|
|
4989
5330
|
}
|
|
4990
5331
|
|
|
4991
5332
|
// src/commands.push.recovery.drop.ts
|
|
4992
5333
|
init_config();
|
|
4993
|
-
import { rmSync as
|
|
4994
|
-
import { join as
|
|
5334
|
+
import { rmSync as rmSync13 } from "node:fs";
|
|
5335
|
+
import { join as join40 } from "node:path";
|
|
4995
5336
|
function dropSessionFromStaged(sid, map) {
|
|
4996
5337
|
const logicals = Object.keys(map.projects);
|
|
4997
5338
|
if (logicals.length === 0) return false;
|
|
4998
5339
|
const repo = repoHome();
|
|
4999
5340
|
for (const logical of logicals) {
|
|
5000
|
-
const jsonl =
|
|
5001
|
-
const dir =
|
|
5002
|
-
|
|
5003
|
-
|
|
5341
|
+
const jsonl = join40(repo, "shared", "projects", logical, `${sid}.jsonl`);
|
|
5342
|
+
const dir = join40(repo, "shared", "projects", logical, sid);
|
|
5343
|
+
rmSync13(jsonl, { force: true });
|
|
5344
|
+
rmSync13(dir, { recursive: true, force: true });
|
|
5345
|
+
}
|
|
5346
|
+
return true;
|
|
5347
|
+
}
|
|
5348
|
+
|
|
5349
|
+
// src/commands.push.recovery.skills.ts
|
|
5350
|
+
init_config();
|
|
5351
|
+
import {
|
|
5352
|
+
cpSync as cpSync9,
|
|
5353
|
+
lstatSync as lstatSync13,
|
|
5354
|
+
mkdirSync as mkdirSync12,
|
|
5355
|
+
readFileSync as readFileSync16,
|
|
5356
|
+
realpathSync as realpathSync4,
|
|
5357
|
+
statSync as statSync9,
|
|
5358
|
+
writeFileSync as writeFileSync8
|
|
5359
|
+
} from "node:fs";
|
|
5360
|
+
import { dirname as dirname11, join as join42, sep as sep6 } from "node:path";
|
|
5361
|
+
|
|
5362
|
+
// src/skills-sync.ts
|
|
5363
|
+
init_config();
|
|
5364
|
+
import { existsSync as existsSync33, lstatSync as lstatSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync14, rmSync as rmSync14 } from "node:fs";
|
|
5365
|
+
import { join as join41 } from "node:path";
|
|
5366
|
+
init_utils_fs();
|
|
5367
|
+
function isGsdOwned(name) {
|
|
5368
|
+
return name.startsWith(GSD_PREFIX);
|
|
5369
|
+
}
|
|
5370
|
+
function isSkillExcluded(name) {
|
|
5371
|
+
return isGsdOwned(name) || isDeniedName(ALWAYS_NEVER_SYNC, name);
|
|
5372
|
+
}
|
|
5373
|
+
function copySkillsPush(src, dst) {
|
|
5374
|
+
const srcNames = readdirSync14(src, { encoding: "utf8" });
|
|
5375
|
+
const blockSet = /* @__PURE__ */ new Set([
|
|
5376
|
+
...srcNames.filter((n) => isGsdOwned(n)),
|
|
5377
|
+
...ALWAYS_NEVER_SYNC
|
|
5378
|
+
]);
|
|
5379
|
+
copyExtrasFiltered(src, dst, blockSet);
|
|
5380
|
+
}
|
|
5381
|
+
function copySkillsPull(src, dst) {
|
|
5382
|
+
copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
|
|
5383
|
+
}
|
|
5384
|
+
function syncSkillsPull(ts) {
|
|
5385
|
+
const sharedSkills = join41(repoHome(), "shared", "skills");
|
|
5386
|
+
if (!existsSync33(sharedSkills)) return;
|
|
5387
|
+
const localSkills = join41(claudeHome(), "skills");
|
|
5388
|
+
const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
5389
|
+
if (dstStat?.isSymbolicLink() === true) {
|
|
5390
|
+
backupBeforeWrite(localSkills, ts);
|
|
5391
|
+
rmSync14(localSkills, { recursive: true, force: true });
|
|
5392
|
+
mkdirSync11(localSkills, { recursive: true });
|
|
5393
|
+
}
|
|
5394
|
+
copySkillsPull(sharedSkills, localSkills);
|
|
5395
|
+
}
|
|
5396
|
+
function syncSkillsPush() {
|
|
5397
|
+
const localSkills = join41(claudeHome(), "skills");
|
|
5398
|
+
const stat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
5399
|
+
if (stat === void 0) return;
|
|
5400
|
+
if (stat.isSymbolicLink()) return;
|
|
5401
|
+
const sharedSkills = join41(repoHome(), "shared", "skills");
|
|
5402
|
+
copySkillsPush(localSkills, sharedSkills);
|
|
5403
|
+
}
|
|
5404
|
+
|
|
5405
|
+
// src/commands.push.recovery.skills.ts
|
|
5406
|
+
init_push_gitleaks_scan();
|
|
5407
|
+
init_utils_fs();
|
|
5408
|
+
init_utils();
|
|
5409
|
+
var SKILL_FINDING_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
|
|
5410
|
+
var SAFE_SKILL_NAME = /^[A-Za-z0-9._-]+$/;
|
|
5411
|
+
var SKILL_DIR_PATH = /^shared\/skills\/[^/]+\//;
|
|
5412
|
+
function skillFileFromFinding(f) {
|
|
5413
|
+
const m = SKILL_FINDING_PATH.exec(f.File);
|
|
5414
|
+
if (m?.[1] === void 0 || m[2] === void 0) return null;
|
|
5415
|
+
return { name: m[1], relPath: m[2] };
|
|
5416
|
+
}
|
|
5417
|
+
function isSkillFindingPath(f) {
|
|
5418
|
+
return SKILL_DIR_PATH.test(f.File);
|
|
5419
|
+
}
|
|
5420
|
+
function isSafeRelPath(relPath) {
|
|
5421
|
+
if (relPath.length === 0 || relPath.startsWith("/") || relPath.includes("\\")) return false;
|
|
5422
|
+
const segments = relPath.split("/");
|
|
5423
|
+
return segments.every((s) => s.length > 0 && s !== "." && s !== "..");
|
|
5424
|
+
}
|
|
5425
|
+
function resolveSkillLocalPath(name, relPath) {
|
|
5426
|
+
if (!SAFE_SKILL_NAME.test(name) || name === "." || name === "..") return null;
|
|
5427
|
+
if (isGsdOwned(name)) return null;
|
|
5428
|
+
if (!isSafeRelPath(relPath)) return null;
|
|
5429
|
+
const skillsRoot = join42(claudeHome(), "skills");
|
|
5430
|
+
const skillRoot = join42(skillsRoot, name);
|
|
5431
|
+
const localPath = join42(skillRoot, ...relPath.split("/"));
|
|
5432
|
+
if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep6)) return null;
|
|
5433
|
+
try {
|
|
5434
|
+
if (lstatSync13(localPath).isSymbolicLink()) return null;
|
|
5435
|
+
const realLocal = realpathSync4(localPath);
|
|
5436
|
+
const realRoot = realpathSync4(skillsRoot);
|
|
5437
|
+
if (!realLocal.startsWith(realRoot + sep6)) return null;
|
|
5438
|
+
if (!statSync9(realLocal).isFile()) return null;
|
|
5439
|
+
} catch {
|
|
5440
|
+
return null;
|
|
5441
|
+
}
|
|
5442
|
+
return localPath;
|
|
5443
|
+
}
|
|
5444
|
+
function preflightSkillRedactable(f) {
|
|
5445
|
+
const parsed = skillFileFromFinding(f);
|
|
5446
|
+
if (parsed === null) return "a finding is not a skill file";
|
|
5447
|
+
const localPath = resolveSkillLocalPath(parsed.name, parsed.relPath);
|
|
5448
|
+
if (localPath === null) {
|
|
5449
|
+
return `skill file ${parsed.name}/${parsed.relPath}: local file not found or unresolvable`;
|
|
5004
5450
|
}
|
|
5451
|
+
return null;
|
|
5452
|
+
}
|
|
5453
|
+
function applySkillRedact(f, ts, scan = scanFile) {
|
|
5454
|
+
const refuse = (msg) => {
|
|
5455
|
+
log(msg);
|
|
5456
|
+
return false;
|
|
5457
|
+
};
|
|
5458
|
+
const parsed = skillFileFromFinding(f);
|
|
5459
|
+
if (parsed === null) {
|
|
5460
|
+
return refuse("could not parse this finding as a skill file; choose Skip.");
|
|
5461
|
+
}
|
|
5462
|
+
const { name, relPath } = parsed;
|
|
5463
|
+
const localPath = resolveSkillLocalPath(name, relPath);
|
|
5464
|
+
if (localPath === null) {
|
|
5465
|
+
return refuse(`could not locate the local skill file for ${name}/${relPath}; choose Skip.`);
|
|
5466
|
+
}
|
|
5467
|
+
const findings = scan(localPath);
|
|
5468
|
+
if (findings === null) {
|
|
5469
|
+
return refuse(`re-scan of ${name}/${relPath} failed; choose Skip.`);
|
|
5470
|
+
}
|
|
5471
|
+
if (findings.length === 0) {
|
|
5472
|
+
return refuse(`nothing to redact in ${name}/${relPath}; choose Skip.`);
|
|
5473
|
+
}
|
|
5474
|
+
backupBeforeWrite(localPath, ts);
|
|
5475
|
+
const before = readFileSync16(localPath, "utf8");
|
|
5476
|
+
const after = applyRedactions(before, findings);
|
|
5477
|
+
writeFileSync8(localPath, after, "utf8");
|
|
5478
|
+
const dest = join42(repoHome(), "shared", "skills", name, ...relPath.split("/"));
|
|
5479
|
+
mkdirSync12(dirname11(dest), { recursive: true });
|
|
5480
|
+
cpSync9(localPath, dest, { force: true });
|
|
5005
5481
|
return true;
|
|
5006
5482
|
}
|
|
5007
5483
|
|
|
@@ -5031,10 +5507,10 @@ function makeDefaultReadLine(repo) {
|
|
|
5031
5507
|
try {
|
|
5032
5508
|
const repoRoot = resolve3(repo);
|
|
5033
5509
|
const target = resolve3(repoRoot, file);
|
|
5034
|
-
if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot +
|
|
5510
|
+
if (isAbsolute2(file) || target !== repoRoot && !target.startsWith(repoRoot + sep7)) {
|
|
5035
5511
|
return null;
|
|
5036
5512
|
}
|
|
5037
|
-
const content =
|
|
5513
|
+
const content = readFileSync17(target, "utf8");
|
|
5038
5514
|
const lines = content.split(/\r?\n/);
|
|
5039
5515
|
const idx = line - 1;
|
|
5040
5516
|
if (idx < 0 || idx >= lines.length) return null;
|
|
@@ -5057,6 +5533,40 @@ Finding: ${f.RuleID} in ${f.File} line ${f.StartLine}` + (sid === null ? "" : `
|
|
|
5057
5533
|
}
|
|
5058
5534
|
return actions;
|
|
5059
5535
|
}
|
|
5536
|
+
function dispatchMemory(f, action, ctx) {
|
|
5537
|
+
const parsed = memoryFileFromFinding(f);
|
|
5538
|
+
if (parsed === null) {
|
|
5539
|
+
log(`memory path not auto-redactable: ${f.File}; scrub it by hand or choose Skip`);
|
|
5540
|
+
return;
|
|
5541
|
+
}
|
|
5542
|
+
if (action === "drop") {
|
|
5543
|
+
log("memory files cannot be dropped; use Redact or Skip");
|
|
5544
|
+
return;
|
|
5545
|
+
}
|
|
5546
|
+
const memKey = `${parsed.logical}/${parsed.filename}`;
|
|
5547
|
+
if (ctx.redactedMemory.has(memKey)) return;
|
|
5548
|
+
if (applyMemoryRedact(f, ctx.ts, ctx.map, ctx.scan)) ctx.redactedMemory.add(memKey);
|
|
5549
|
+
}
|
|
5550
|
+
function dispatchSkill(f, action, ctx) {
|
|
5551
|
+
const parsed = skillFileFromFinding(f);
|
|
5552
|
+
if (parsed === null) return;
|
|
5553
|
+
if (action === "drop") {
|
|
5554
|
+
log("skill files cannot be dropped; use Redact or Skip");
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5557
|
+
const skillKey = `${parsed.name}/${parsed.relPath}`;
|
|
5558
|
+
if (ctx.redactedSkills.has(skillKey)) return;
|
|
5559
|
+
if (applySkillRedact(f, ctx.ts, ctx.scan)) ctx.redactedSkills.add(skillKey);
|
|
5560
|
+
}
|
|
5561
|
+
function dispatchNonSession(f, action, ctx) {
|
|
5562
|
+
if (isMemoryFindingPath(f)) {
|
|
5563
|
+
dispatchMemory(f, action, ctx);
|
|
5564
|
+
return;
|
|
5565
|
+
}
|
|
5566
|
+
if (isSkillFindingPath(f)) {
|
|
5567
|
+
dispatchSkill(f, action, ctx);
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5060
5570
|
function dispatchOne(f, ctx) {
|
|
5061
5571
|
const action = ctx.actions.get(findingKey(f)) ?? "skip";
|
|
5062
5572
|
if (action === "skip") return;
|
|
@@ -5066,7 +5576,10 @@ function dispatchOne(f, ctx) {
|
|
|
5066
5576
|
applyAllow(f, ctx.repo);
|
|
5067
5577
|
return;
|
|
5068
5578
|
}
|
|
5069
|
-
if (sid === null)
|
|
5579
|
+
if (sid === null) {
|
|
5580
|
+
dispatchNonSession(f, action, ctx);
|
|
5581
|
+
return;
|
|
5582
|
+
}
|
|
5070
5583
|
if (action === "drop") {
|
|
5071
5584
|
ctx.droppedSids.add(sid);
|
|
5072
5585
|
if (ctx.drop(sid, ctx.map)) {
|
|
@@ -5091,20 +5604,58 @@ function dispatchActions(findings, actions, opts) {
|
|
|
5091
5604
|
scan,
|
|
5092
5605
|
drop,
|
|
5093
5606
|
redactedSids: /* @__PURE__ */ new Set(),
|
|
5094
|
-
droppedSids: /* @__PURE__ */ new Set()
|
|
5607
|
+
droppedSids: /* @__PURE__ */ new Set(),
|
|
5608
|
+
redactedMemory: /* @__PURE__ */ new Set(),
|
|
5609
|
+
redactedSkills: /* @__PURE__ */ new Set()
|
|
5095
5610
|
};
|
|
5096
5611
|
for (const f of findings) {
|
|
5097
5612
|
dispatchOne(f, ctx);
|
|
5098
5613
|
}
|
|
5099
5614
|
}
|
|
5615
|
+
function redactAllDedupeKey(f) {
|
|
5616
|
+
const sid = sessionIdFromFinding(f);
|
|
5617
|
+
if (sid !== null) return sid;
|
|
5618
|
+
const memParsed = memoryFileFromFinding(f);
|
|
5619
|
+
if (memParsed !== null) return `${memParsed.logical}/${memParsed.filename}`;
|
|
5620
|
+
const skillParsed = skillFileFromFinding(f);
|
|
5621
|
+
if (skillParsed !== null) return `skill:${skillParsed.name}/${skillParsed.relPath}`;
|
|
5622
|
+
return findingKey(f);
|
|
5623
|
+
}
|
|
5624
|
+
function redactAllPreflightOne(f, map, nowMs) {
|
|
5625
|
+
if (sessionIdFromFinding(f) === null) {
|
|
5626
|
+
if (memoryFileFromFinding(f) !== null) return preflightMemoryRedactable(f, map);
|
|
5627
|
+
if (skillFileFromFinding(f) !== null) return preflightSkillRedactable(f);
|
|
5628
|
+
}
|
|
5629
|
+
return preflightRedactable(f, map, nowMs);
|
|
5630
|
+
}
|
|
5631
|
+
function redactAllOne(f, ts, map, nowMs, scan, dedupe) {
|
|
5632
|
+
const sid = sessionIdFromFinding(f);
|
|
5633
|
+
if (sid !== null) {
|
|
5634
|
+
if (dedupe.redactedSids.has(sid)) return;
|
|
5635
|
+
if (applyRedact(f, ts, map, nowMs, scan)) dedupe.redactedSids.add(sid);
|
|
5636
|
+
return;
|
|
5637
|
+
}
|
|
5638
|
+
const memParsed = memoryFileFromFinding(f);
|
|
5639
|
+
if (memParsed !== null) {
|
|
5640
|
+
const memKey = `${memParsed.logical}/${memParsed.filename}`;
|
|
5641
|
+
if (dedupe.redactedMemory.has(memKey)) return;
|
|
5642
|
+
if (applyMemoryRedact(f, ts, map, scan)) dedupe.redactedMemory.add(memKey);
|
|
5643
|
+
return;
|
|
5644
|
+
}
|
|
5645
|
+
const skillParsed = skillFileFromFinding(f);
|
|
5646
|
+
if (skillParsed === null) return;
|
|
5647
|
+
const skillKey = `${skillParsed.name}/${skillParsed.relPath}`;
|
|
5648
|
+
if (dedupe.redactedSkills.has(skillKey)) return;
|
|
5649
|
+
if (applySkillRedact(f, ts, scan)) dedupe.redactedSkills.add(skillKey);
|
|
5650
|
+
}
|
|
5100
5651
|
function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
|
|
5101
5652
|
const refusals = [];
|
|
5102
5653
|
const preflighted = /* @__PURE__ */ new Set();
|
|
5103
5654
|
for (const f of findings) {
|
|
5104
|
-
const dedupeKey =
|
|
5655
|
+
const dedupeKey = redactAllDedupeKey(f);
|
|
5105
5656
|
if (preflighted.has(dedupeKey)) continue;
|
|
5106
5657
|
preflighted.add(dedupeKey);
|
|
5107
|
-
const reason =
|
|
5658
|
+
const reason = redactAllPreflightOne(f, map, nowMs);
|
|
5108
5659
|
if (reason !== null) refusals.push(reason);
|
|
5109
5660
|
}
|
|
5110
5661
|
if (refusals.length > 0) {
|
|
@@ -5114,15 +5665,18 @@ function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
|
|
|
5114
5665
|
Re-run without --redact-all to triage these interactively (Drop session / Skip), or end any active session and retry.`
|
|
5115
5666
|
);
|
|
5116
5667
|
}
|
|
5117
|
-
const
|
|
5668
|
+
const dedupe = {
|
|
5669
|
+
redactedSids: /* @__PURE__ */ new Set(),
|
|
5670
|
+
redactedMemory: /* @__PURE__ */ new Set(),
|
|
5671
|
+
redactedSkills: /* @__PURE__ */ new Set()
|
|
5672
|
+
};
|
|
5118
5673
|
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);
|
|
5674
|
+
redactAllOne(f, ts, map, nowMs, scan, dedupe);
|
|
5122
5675
|
}
|
|
5123
5676
|
}
|
|
5124
5677
|
|
|
5125
5678
|
// src/commands.push.recovery.ts
|
|
5679
|
+
init_exit_codes();
|
|
5126
5680
|
init_push_gitleaks_scan();
|
|
5127
5681
|
init_push_gitleaks();
|
|
5128
5682
|
init_utils();
|
|
@@ -5150,15 +5704,15 @@ function applyThenRescan(scanVerdict, repoHome2) {
|
|
|
5150
5704
|
const next = scanVerdict(repoHome2);
|
|
5151
5705
|
if (next.leak) {
|
|
5152
5706
|
const { bySession, other } = partitionFindings(next.findings);
|
|
5153
|
-
throw new NomadFatal(buildSessionAwareFatal(bySession, other));
|
|
5707
|
+
throw new NomadFatal(buildSessionAwareFatal(bySession, other), { code: EXIT.LEAK_BLOCKED });
|
|
5154
5708
|
}
|
|
5155
5709
|
return next;
|
|
5156
5710
|
}
|
|
5157
5711
|
function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
5158
|
-
const ignPath =
|
|
5712
|
+
const ignPath = join43(repoHome2, ".gitleaksignore");
|
|
5159
5713
|
let before;
|
|
5160
5714
|
try {
|
|
5161
|
-
before =
|
|
5715
|
+
before = readFileSync18(ignPath, "utf8");
|
|
5162
5716
|
} catch {
|
|
5163
5717
|
before = null;
|
|
5164
5718
|
}
|
|
@@ -5166,8 +5720,8 @@ function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
|
5166
5720
|
try {
|
|
5167
5721
|
return applyThenRescan(scanVerdict, repoHome2);
|
|
5168
5722
|
} catch (err) {
|
|
5169
|
-
if (before === null)
|
|
5170
|
-
else
|
|
5723
|
+
if (before === null) rmSync15(ignPath, { force: true });
|
|
5724
|
+
else writeFileSync9(ignPath, before, "utf8");
|
|
5171
5725
|
throw err;
|
|
5172
5726
|
}
|
|
5173
5727
|
}
|
|
@@ -5217,7 +5771,9 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
|
|
|
5217
5771
|
);
|
|
5218
5772
|
}
|
|
5219
5773
|
if (!isTTYCheck()) {
|
|
5220
|
-
throw new NomadFatal(current.recovery ?? "gitleaks detected secrets"
|
|
5774
|
+
throw new NomadFatal(current.recovery ?? "gitleaks detected secrets", {
|
|
5775
|
+
code: EXIT.LEAK_BLOCKED
|
|
5776
|
+
});
|
|
5221
5777
|
}
|
|
5222
5778
|
const prompt = makePromptFn();
|
|
5223
5779
|
printLegend();
|
|
@@ -5226,7 +5782,7 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
|
|
|
5226
5782
|
if (hasUnresolved(actions)) {
|
|
5227
5783
|
const unresolved = current.findings.filter((f) => actions.get(findingKey(f)) === "skip");
|
|
5228
5784
|
const { bySession, other } = partitionFindings(unresolved);
|
|
5229
|
-
throw new NomadFatal(buildSessionAwareFatal(bySession, other));
|
|
5785
|
+
throw new NomadFatal(buildSessionAwareFatal(bySession, other), { code: EXIT.LEAK_BLOCKED });
|
|
5230
5786
|
}
|
|
5231
5787
|
dispatchActions(current.findings, actions, { ts, map, nowMs, repo, scan });
|
|
5232
5788
|
gitOrFatal(["add", "-A"], "git add", repo);
|
|
@@ -5254,7 +5810,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
5254
5810
|
`);
|
|
5255
5811
|
}
|
|
5256
5812
|
function resolveWorkerPath(deps = {}) {
|
|
5257
|
-
const check = deps.existsSyncFn ??
|
|
5813
|
+
const check = deps.existsSyncFn ?? existsSync34;
|
|
5258
5814
|
const base = deps.baseUrl ?? import.meta.url;
|
|
5259
5815
|
const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
|
|
5260
5816
|
if (check(mjs)) return mjs;
|
|
@@ -5320,9 +5876,9 @@ function withSpinner(label, fn, deps) {
|
|
|
5320
5876
|
|
|
5321
5877
|
// src/commands.doctor.gitleaks-version.ts
|
|
5322
5878
|
init_color();
|
|
5323
|
-
import { execFileSync as
|
|
5324
|
-
import { existsSync as
|
|
5325
|
-
import { join as
|
|
5879
|
+
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
5880
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
5881
|
+
import { join as join44 } from "node:path";
|
|
5326
5882
|
init_config();
|
|
5327
5883
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
5328
5884
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -5331,7 +5887,7 @@ function majorMinorOf(value) {
|
|
|
5331
5887
|
return m === null ? null : [m[1], m[2]];
|
|
5332
5888
|
}
|
|
5333
5889
|
function readGitleaksVersion(run, tomlExists) {
|
|
5334
|
-
const tomlPath =
|
|
5890
|
+
const tomlPath = join44(repoHome(), ".gitleaks.toml");
|
|
5335
5891
|
const args = ["version"];
|
|
5336
5892
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
5337
5893
|
try {
|
|
@@ -5343,7 +5899,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
5343
5899
|
return null;
|
|
5344
5900
|
}
|
|
5345
5901
|
}
|
|
5346
|
-
function reportGitleaksVersionCheck(section2, run =
|
|
5902
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists = existsSync35) {
|
|
5347
5903
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
5348
5904
|
if (raw === null) return;
|
|
5349
5905
|
const local = majorMinorOf(raw);
|
|
@@ -5363,7 +5919,7 @@ function reportGitleaksVersionCheck(section2, run = execFileSync13, tomlExists =
|
|
|
5363
5919
|
|
|
5364
5920
|
// src/commands.doctor.checks.deps.ts
|
|
5365
5921
|
init_color();
|
|
5366
|
-
import { execFileSync as
|
|
5922
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
5367
5923
|
var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
|
|
5368
5924
|
var PROBE_TIMEOUT_MS3 = 3e3;
|
|
5369
5925
|
var FETCHER_BASE = "HTTP fetcher";
|
|
@@ -5400,7 +5956,7 @@ function reportFetcherRow(section2, run) {
|
|
|
5400
5956
|
);
|
|
5401
5957
|
}
|
|
5402
5958
|
}
|
|
5403
|
-
function reportOptionalDeps(section2, run =
|
|
5959
|
+
function reportOptionalDeps(section2, run = execFileSync16) {
|
|
5404
5960
|
const gh = probeOptionalDep("gh", run);
|
|
5405
5961
|
if (gh.status === "present") {
|
|
5406
5962
|
addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
|
|
@@ -5415,11 +5971,11 @@ function reportOptionalDeps(section2, run = execFileSync14) {
|
|
|
5415
5971
|
|
|
5416
5972
|
// src/commands.doctor.actions-drift.ts
|
|
5417
5973
|
init_color();
|
|
5418
|
-
import { execFileSync as
|
|
5974
|
+
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
5419
5975
|
init_config();
|
|
5420
5976
|
|
|
5421
5977
|
// src/gh-actions.ts
|
|
5422
|
-
import { execFileSync as
|
|
5978
|
+
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
5423
5979
|
var GH_TIMEOUT_MS = 5e3;
|
|
5424
5980
|
function parseGitHubRemote(remoteUrl) {
|
|
5425
5981
|
const normalized = remoteUrl.trim().replace(/\/$/, "");
|
|
@@ -5427,7 +5983,7 @@ function parseGitHubRemote(remoteUrl) {
|
|
|
5427
5983
|
if (m === null) return null;
|
|
5428
5984
|
return { owner: m[1], repo: m[2] };
|
|
5429
5985
|
}
|
|
5430
|
-
function ghAuthStatus(run =
|
|
5986
|
+
function ghAuthStatus(run = execFileSync17) {
|
|
5431
5987
|
try {
|
|
5432
5988
|
run("gh", ["auth", "status"], {
|
|
5433
5989
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -5441,7 +5997,7 @@ function ghAuthStatus(run = execFileSync15) {
|
|
|
5441
5997
|
return "gh-probe-error";
|
|
5442
5998
|
}
|
|
5443
5999
|
}
|
|
5444
|
-
function isRepoPrivate(ref, run =
|
|
6000
|
+
function isRepoPrivate(ref, run = execFileSync17) {
|
|
5445
6001
|
const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
|
|
5446
6002
|
stdio: ["ignore", "pipe", "ignore"],
|
|
5447
6003
|
timeout: GH_TIMEOUT_MS
|
|
@@ -5449,7 +6005,7 @@ function isRepoPrivate(ref, run = execFileSync15) {
|
|
|
5449
6005
|
const parsed = JSON.parse(out);
|
|
5450
6006
|
return parsed.isPrivate === true;
|
|
5451
6007
|
}
|
|
5452
|
-
function isActionsEnabled(ref, run =
|
|
6008
|
+
function isActionsEnabled(ref, run = execFileSync17) {
|
|
5453
6009
|
const out = run(
|
|
5454
6010
|
"gh",
|
|
5455
6011
|
["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
|
|
@@ -5457,7 +6013,7 @@ function isActionsEnabled(ref, run = execFileSync15) {
|
|
|
5457
6013
|
).toString().trim();
|
|
5458
6014
|
return out === "true";
|
|
5459
6015
|
}
|
|
5460
|
-
function disableActions(ref, run =
|
|
6016
|
+
function disableActions(ref, run = execFileSync17) {
|
|
5461
6017
|
run(
|
|
5462
6018
|
"gh",
|
|
5463
6019
|
[
|
|
@@ -5471,7 +6027,7 @@ function disableActions(ref, run = execFileSync15) {
|
|
|
5471
6027
|
{ stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
|
|
5472
6028
|
);
|
|
5473
6029
|
}
|
|
5474
|
-
function readOriginRemote(cwd, run =
|
|
6030
|
+
function readOriginRemote(cwd, run = execFileSync17) {
|
|
5475
6031
|
return run("git", ["remote", "get-url", "origin"], {
|
|
5476
6032
|
cwd,
|
|
5477
6033
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -5479,7 +6035,7 @@ function readOriginRemote(cwd, run = execFileSync15) {
|
|
|
5479
6035
|
}
|
|
5480
6036
|
|
|
5481
6037
|
// src/commands.doctor.actions-drift.ts
|
|
5482
|
-
function reportActionsDrift(section2, run =
|
|
6038
|
+
function reportActionsDrift(section2, run = execFileSync18) {
|
|
5483
6039
|
let remote;
|
|
5484
6040
|
try {
|
|
5485
6041
|
remote = readOriginRemote(repoHome(), run);
|
|
@@ -5569,8 +6125,8 @@ function gatherDoctorSections(opts) {
|
|
|
5569
6125
|
reportLongPathsCheck(host);
|
|
5570
6126
|
reportCrlfGuardCheck(host);
|
|
5571
6127
|
const links = section("Shared links");
|
|
5572
|
-
const mapPath =
|
|
5573
|
-
const rawMap =
|
|
6128
|
+
const mapPath = join45(repoHome(), "path-map.json");
|
|
6129
|
+
const rawMap = existsSync36(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
5574
6130
|
const map = rawMap ?? { projects: {} };
|
|
5575
6131
|
reportSharedLinks(links, map);
|
|
5576
6132
|
reportDroppedNamesMigration(links);
|
|
@@ -5666,15 +6222,15 @@ function parseDoctorArgs(args) {
|
|
|
5666
6222
|
|
|
5667
6223
|
// src/commands.drop-session.ts
|
|
5668
6224
|
init_config();
|
|
5669
|
-
import { execFileSync as
|
|
5670
|
-
import { existsSync as
|
|
5671
|
-
import { join as
|
|
6225
|
+
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
6226
|
+
import { existsSync as existsSync38, readdirSync as readdirSync15, statSync as statSync10 } from "node:fs";
|
|
6227
|
+
import { join as join47, relative as relative6 } from "node:path";
|
|
5672
6228
|
|
|
5673
6229
|
// src/commands.drop-session.git.ts
|
|
5674
|
-
import { execFileSync as
|
|
6230
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
5675
6231
|
function expandStagedDir(dirRel, repo) {
|
|
5676
6232
|
try {
|
|
5677
|
-
const out =
|
|
6233
|
+
const out = execFileSync19("git", ["ls-files", "-z", "--", dirRel], {
|
|
5678
6234
|
cwd: repo,
|
|
5679
6235
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5680
6236
|
});
|
|
@@ -5686,7 +6242,7 @@ function expandStagedDir(dirRel, repo) {
|
|
|
5686
6242
|
function isTrackedInHead(rel, repo) {
|
|
5687
6243
|
try {
|
|
5688
6244
|
const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
|
|
5689
|
-
|
|
6245
|
+
execFileSync19("git", ["cat-file", "-e", `HEAD:${treePath}`], {
|
|
5690
6246
|
cwd: repo,
|
|
5691
6247
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5692
6248
|
});
|
|
@@ -5697,7 +6253,7 @@ function isTrackedInHead(rel, repo) {
|
|
|
5697
6253
|
}
|
|
5698
6254
|
function isInIndex(rel, repo) {
|
|
5699
6255
|
try {
|
|
5700
|
-
const out =
|
|
6256
|
+
const out = execFileSync19("git", ["ls-files", "--", rel], {
|
|
5701
6257
|
cwd: repo,
|
|
5702
6258
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5703
6259
|
});
|
|
@@ -5711,8 +6267,8 @@ function isInIndex(rel, repo) {
|
|
|
5711
6267
|
init_config();
|
|
5712
6268
|
init_utils();
|
|
5713
6269
|
init_utils_json();
|
|
5714
|
-
import { existsSync as
|
|
5715
|
-
import { join as
|
|
6270
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
6271
|
+
import { join as join46 } from "node:path";
|
|
5716
6272
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
5717
6273
|
function reportScrubHint(id, matches) {
|
|
5718
6274
|
const live = resolveLiveTranscript2(id, matches);
|
|
@@ -5728,8 +6284,8 @@ function reportScrubHint(id, matches) {
|
|
|
5728
6284
|
}
|
|
5729
6285
|
function resolveLiveTranscript2(id, matches) {
|
|
5730
6286
|
try {
|
|
5731
|
-
const mapPath =
|
|
5732
|
-
if (!
|
|
6287
|
+
const mapPath = join46(repoHome(), "path-map.json");
|
|
6288
|
+
if (!existsSync37(mapPath)) return null;
|
|
5733
6289
|
const projects = readJson(mapPath).projects;
|
|
5734
6290
|
const claude = claudeHome();
|
|
5735
6291
|
for (const rel of matches) {
|
|
@@ -5737,8 +6293,8 @@ function resolveLiveTranscript2(id, matches) {
|
|
|
5737
6293
|
if (logical === void 0) continue;
|
|
5738
6294
|
const abs = projects[logical]?.[HOST];
|
|
5739
6295
|
if (abs === void 0) continue;
|
|
5740
|
-
const live =
|
|
5741
|
-
if (
|
|
6296
|
+
const live = join46(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
6297
|
+
if (existsSync37(live)) return live;
|
|
5742
6298
|
}
|
|
5743
6299
|
return null;
|
|
5744
6300
|
} catch {
|
|
@@ -5754,12 +6310,12 @@ function cmdDropSession(id) {
|
|
|
5754
6310
|
process.exit(1);
|
|
5755
6311
|
}
|
|
5756
6312
|
const repo = repoHome();
|
|
5757
|
-
if (!
|
|
6313
|
+
if (!existsSync38(repo)) die(`repo not cloned at ${repo}`);
|
|
5758
6314
|
const handle = acquireLock("drop-session");
|
|
5759
6315
|
if (handle === null) process.exit(0);
|
|
5760
6316
|
try {
|
|
5761
|
-
const repoProjects =
|
|
5762
|
-
if (!
|
|
6317
|
+
const repoProjects = join47(repo, "shared", "projects");
|
|
6318
|
+
if (!existsSync38(repoProjects)) {
|
|
5763
6319
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
5764
6320
|
}
|
|
5765
6321
|
const matches = collectMatches(repoProjects, id, repo);
|
|
@@ -5774,20 +6330,20 @@ function cmdDropSession(id) {
|
|
|
5774
6330
|
throw err;
|
|
5775
6331
|
}
|
|
5776
6332
|
fail(err.message);
|
|
5777
|
-
process.exitCode =
|
|
6333
|
+
process.exitCode = err.code;
|
|
5778
6334
|
} finally {
|
|
5779
6335
|
releaseLock(handle);
|
|
5780
6336
|
}
|
|
5781
6337
|
}
|
|
5782
6338
|
function collectMatches(repoProjects, id, repo) {
|
|
5783
6339
|
const matches = [];
|
|
5784
|
-
for (const logical of
|
|
5785
|
-
const candidate =
|
|
5786
|
-
if (
|
|
6340
|
+
for (const logical of readdirSync15(repoProjects)) {
|
|
6341
|
+
const candidate = join47(repoProjects, logical, `${id}.jsonl`);
|
|
6342
|
+
if (existsSync38(candidate)) {
|
|
5787
6343
|
matches.push(relative6(repo, candidate));
|
|
5788
6344
|
}
|
|
5789
|
-
const dir =
|
|
5790
|
-
if (
|
|
6345
|
+
const dir = join47(repoProjects, logical, id);
|
|
6346
|
+
if (existsSync38(dir) && statSync10(dir).isDirectory()) {
|
|
5791
6347
|
const dirRel = relative6(repo, dir);
|
|
5792
6348
|
const staged = expandStagedDir(dirRel, repo);
|
|
5793
6349
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -5803,12 +6359,12 @@ function unstageOne(rel, repo) {
|
|
|
5803
6359
|
}
|
|
5804
6360
|
try {
|
|
5805
6361
|
if (isTrackedInHead(rel, repo)) {
|
|
5806
|
-
|
|
6362
|
+
execFileSync20("git", ["restore", "--staged", "--worktree", "--", rel], {
|
|
5807
6363
|
cwd: repo,
|
|
5808
6364
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5809
6365
|
});
|
|
5810
6366
|
} else {
|
|
5811
|
-
|
|
6367
|
+
execFileSync20("git", ["rm", "--cached", "-f", "--", rel], {
|
|
5812
6368
|
cwd: repo,
|
|
5813
6369
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5814
6370
|
});
|
|
@@ -5822,8 +6378,8 @@ function unstageOne(rel, repo) {
|
|
|
5822
6378
|
}
|
|
5823
6379
|
|
|
5824
6380
|
// src/commands.pull.ts
|
|
5825
|
-
import { existsSync as
|
|
5826
|
-
import { join as
|
|
6381
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync14 } from "node:fs";
|
|
6382
|
+
import { join as join52 } from "node:path";
|
|
5827
6383
|
|
|
5828
6384
|
// src/commands.push.sections.ts
|
|
5829
6385
|
init_color();
|
|
@@ -5931,18 +6487,18 @@ init_config();
|
|
|
5931
6487
|
|
|
5932
6488
|
// src/extras-sync.ts
|
|
5933
6489
|
init_config();
|
|
5934
|
-
import { existsSync as
|
|
5935
|
-
import { join as
|
|
6490
|
+
import { existsSync as existsSync40, statSync as statSync11 } from "node:fs";
|
|
6491
|
+
import { join as join50 } from "node:path";
|
|
5936
6492
|
init_utils();
|
|
5937
6493
|
init_utils_json();
|
|
5938
6494
|
|
|
5939
6495
|
// src/extras-sync.remap.ts
|
|
5940
6496
|
init_config();
|
|
5941
|
-
import { existsSync as
|
|
5942
|
-
import { dirname as
|
|
6497
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync13, readdirSync as readdirSync16, readFileSync as readFileSync19, realpathSync as realpathSync5, rmSync as rmSync16 } from "node:fs";
|
|
6498
|
+
import { dirname as dirname12, join as join49, sep as sep9 } from "node:path";
|
|
5943
6499
|
|
|
5944
6500
|
// src/extras-sync.planning-diff.ts
|
|
5945
|
-
import { join as
|
|
6501
|
+
import { join as join48, normalize as normalize2, sep as sep8 } from "node:path";
|
|
5946
6502
|
init_utils();
|
|
5947
6503
|
function processRecord(fields, i, changed, deleted) {
|
|
5948
6504
|
const status = fields[i];
|
|
@@ -5994,15 +6550,15 @@ function planningDeleteTargets(opts) {
|
|
|
5994
6550
|
const { deleted } = parsePlanningDiff(raw);
|
|
5995
6551
|
const logicalPrefix = "shared/extras/" + logical + "/";
|
|
5996
6552
|
const prefix = logicalPrefix + ".planning/";
|
|
5997
|
-
const planningRoot =
|
|
5998
|
-
const planningRootBoundary = planningRoot +
|
|
6553
|
+
const planningRoot = join48(localRoot, ".planning");
|
|
6554
|
+
const planningRootBoundary = planningRoot + sep8;
|
|
5999
6555
|
const targets = [];
|
|
6000
6556
|
for (const repoPath of deleted) {
|
|
6001
6557
|
if (!repoPath.startsWith(prefix)) {
|
|
6002
6558
|
continue;
|
|
6003
6559
|
}
|
|
6004
6560
|
const remainder = repoPath.slice(logicalPrefix.length);
|
|
6005
|
-
const candidate =
|
|
6561
|
+
const candidate = join48(localRoot, remainder);
|
|
6006
6562
|
const resolved = normalize2(candidate);
|
|
6007
6563
|
if (!resolved.startsWith(planningRootBoundary)) {
|
|
6008
6564
|
throw new NomadFatal(
|
|
@@ -6023,7 +6579,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
6023
6579
|
const would = [];
|
|
6024
6580
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
6025
6581
|
const { src, dst } = paths(t);
|
|
6026
|
-
if (!
|
|
6582
|
+
if (!existsSync39(src)) continue;
|
|
6027
6583
|
const item2 = `${t.logical}/${t.dirname}`;
|
|
6028
6584
|
if (dryRun) {
|
|
6029
6585
|
would.push(item2);
|
|
@@ -6036,42 +6592,42 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
6036
6592
|
return { ...counts, done, would };
|
|
6037
6593
|
}
|
|
6038
6594
|
function pruneEmptyAncestors(target, planningRoot) {
|
|
6039
|
-
let dir =
|
|
6040
|
-
while (dir !== planningRoot && dir.startsWith(planningRoot +
|
|
6595
|
+
let dir = dirname12(target);
|
|
6596
|
+
while (dir !== planningRoot && dir.startsWith(planningRoot + sep9)) {
|
|
6041
6597
|
try {
|
|
6042
|
-
if (
|
|
6043
|
-
|
|
6598
|
+
if (readdirSync16(dir).length > 0) break;
|
|
6599
|
+
rmSync16(dir, { recursive: true, force: true });
|
|
6044
6600
|
} catch {
|
|
6045
6601
|
break;
|
|
6046
6602
|
}
|
|
6047
|
-
dir =
|
|
6603
|
+
dir = dirname12(dir);
|
|
6048
6604
|
}
|
|
6049
6605
|
}
|
|
6050
6606
|
function tryRealpath(dir) {
|
|
6051
6607
|
try {
|
|
6052
|
-
return
|
|
6608
|
+
return realpathSync5(dir);
|
|
6053
6609
|
} catch {
|
|
6054
6610
|
return void 0;
|
|
6055
6611
|
}
|
|
6056
6612
|
}
|
|
6057
6613
|
function isInsidePlanningRoot(parentReal, rootReal) {
|
|
6058
|
-
return parentReal === rootReal || parentReal.startsWith(rootReal +
|
|
6614
|
+
return parentReal === rootReal || parentReal.startsWith(rootReal + sep9);
|
|
6059
6615
|
}
|
|
6060
6616
|
function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
6061
|
-
if (
|
|
6062
|
-
const parentReal = tryRealpath(
|
|
6617
|
+
if (existsSync39(repoCounterpart)) return;
|
|
6618
|
+
const parentReal = tryRealpath(dirname12(target));
|
|
6063
6619
|
if (parentReal === void 0) return;
|
|
6064
6620
|
const rootReal = tryRealpath(planningRoot);
|
|
6065
6621
|
if (rootReal === void 0) return;
|
|
6066
6622
|
if (!isInsidePlanningRoot(parentReal, rootReal)) return;
|
|
6067
|
-
|
|
6623
|
+
rmSync16(target, { recursive: true, force: true });
|
|
6068
6624
|
pruneEmptyAncestors(target, planningRoot);
|
|
6069
6625
|
}
|
|
6070
6626
|
function localDivergesFromPreDelete(target, pre, repoRel, repo) {
|
|
6071
|
-
if (!
|
|
6627
|
+
if (!existsSync39(target)) return false;
|
|
6072
6628
|
try {
|
|
6073
6629
|
const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
|
|
6074
|
-
return !
|
|
6630
|
+
return !readFileSync19(target).equals(preBlob);
|
|
6075
6631
|
} catch {
|
|
6076
6632
|
return true;
|
|
6077
6633
|
}
|
|
@@ -6082,11 +6638,11 @@ function planningDiffArgs(pre, post, logical) {
|
|
|
6082
6638
|
function deletePairsFor(t, raw) {
|
|
6083
6639
|
return planningDeleteTargets({ raw, logical: t.logical, localRoot: t.localRoot }).map(
|
|
6084
6640
|
(target) => {
|
|
6085
|
-
const relToLocal = target.slice(t.localRoot.length +
|
|
6641
|
+
const relToLocal = target.slice(t.localRoot.length + sep9.length);
|
|
6086
6642
|
return {
|
|
6087
6643
|
target,
|
|
6088
6644
|
relToLocal,
|
|
6089
|
-
repoRel: `shared/extras/${t.logical}/${relToLocal.split(
|
|
6645
|
+
repoRel: `shared/extras/${t.logical}/${relToLocal.split(sep9).join("/")}`
|
|
6090
6646
|
};
|
|
6091
6647
|
}
|
|
6092
6648
|
);
|
|
@@ -6113,7 +6669,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
|
|
|
6113
6669
|
return kept;
|
|
6114
6670
|
}
|
|
6115
6671
|
function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
6116
|
-
const repoExtras =
|
|
6672
|
+
const repoExtras = join49(repo, "shared", "extras");
|
|
6117
6673
|
for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
|
|
6118
6674
|
if (t.dirname !== ".planning") continue;
|
|
6119
6675
|
let raw;
|
|
@@ -6128,14 +6684,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
|
6128
6684
|
}
|
|
6129
6685
|
const pairs = deletePairsFor(t, raw);
|
|
6130
6686
|
if (pairs.length === 0) continue;
|
|
6131
|
-
backupExtrasWrite(
|
|
6132
|
-
const planningRoot =
|
|
6687
|
+
backupExtrasWrite(join49(t.localRoot, t.dirname), ts, t.localRoot);
|
|
6688
|
+
const planningRoot = join49(t.localRoot, ".planning");
|
|
6133
6689
|
for (const { target, relToLocal, repoRel } of pairs) {
|
|
6134
6690
|
if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
|
|
6135
6691
|
warn(keptDeleteWarnLine(t.logical, relToLocal));
|
|
6136
6692
|
continue;
|
|
6137
6693
|
}
|
|
6138
|
-
deletePlanningTarget(target, planningRoot,
|
|
6694
|
+
deletePlanningTarget(target, planningRoot, join49(repoExtras, t.logical, relToLocal));
|
|
6139
6695
|
}
|
|
6140
6696
|
}
|
|
6141
6697
|
}
|
|
@@ -6144,14 +6700,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
6144
6700
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
6145
6701
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
6146
6702
|
const repo = repoHome();
|
|
6147
|
-
const repoExtras =
|
|
6148
|
-
if (!dryRun)
|
|
6703
|
+
const repoExtras = join49(repo, "shared", "extras");
|
|
6704
|
+
if (!dryRun) mkdirSync13(repoExtras, { recursive: true });
|
|
6149
6705
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
6150
6706
|
v,
|
|
6151
6707
|
dryRun,
|
|
6152
|
-
({ localRoot, logical, dirname:
|
|
6153
|
-
src:
|
|
6154
|
-
dst:
|
|
6708
|
+
({ localRoot, logical, dirname: dirname13 }) => ({
|
|
6709
|
+
src: join49(localRoot, dirname13),
|
|
6710
|
+
dst: join49(repoExtras, logical, dirname13)
|
|
6155
6711
|
}),
|
|
6156
6712
|
(dst) => backupRepoWrite(dst, ts, repo),
|
|
6157
6713
|
// Push copy routing per extra type:
|
|
@@ -6163,7 +6719,7 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
6163
6719
|
// (enforceAllowList / blockSetFor in commands.push.allowlist.ts)
|
|
6164
6720
|
// remains the hard security boundary.
|
|
6165
6721
|
// All others: copyExtrasFiltered with per-extra denylist.
|
|
6166
|
-
(src, dst,
|
|
6722
|
+
(src, dst, dirname13) => dirname13 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname13)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname13))
|
|
6167
6723
|
);
|
|
6168
6724
|
return { unmapped, skipped, pushed: done, wouldPush: would };
|
|
6169
6725
|
}
|
|
@@ -6179,9 +6735,9 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
6179
6735
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
6180
6736
|
v,
|
|
6181
6737
|
dryRun,
|
|
6182
|
-
({ localRoot, logical, dirname:
|
|
6183
|
-
src:
|
|
6184
|
-
dst:
|
|
6738
|
+
({ localRoot, logical, dirname: dirname13 }) => ({
|
|
6739
|
+
src: join49(repo, "shared", "extras", logical, dirname13),
|
|
6740
|
+
dst: join49(localRoot, dirname13)
|
|
6185
6741
|
}),
|
|
6186
6742
|
// Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
|
|
6187
6743
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -6199,12 +6755,12 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
6199
6755
|
// copyExtrasFileSkipDiverged keeps a locally-edited file rather than
|
|
6200
6756
|
// clobbering it, so the divergence WARN's keep-local promise holds for
|
|
6201
6757
|
// every extra, not just `.planning`.
|
|
6202
|
-
(src, dst,
|
|
6203
|
-
if (
|
|
6204
|
-
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(
|
|
6205
|
-
if (
|
|
6758
|
+
(src, dst, dirname13) => {
|
|
6759
|
+
if (dirname13 === ".claude")
|
|
6760
|
+
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname13));
|
|
6761
|
+
if (dirname13 === ".planning") {
|
|
6206
6762
|
const divergedSet = new Set(listDivergingModified(dst, src));
|
|
6207
|
-
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(
|
|
6763
|
+
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname13), divergedSet);
|
|
6208
6764
|
}
|
|
6209
6765
|
return copyExtrasFileSkipDiverged(src, dst);
|
|
6210
6766
|
}
|
|
@@ -6228,22 +6784,22 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
6228
6784
|
const v = loadValidatedExtras({});
|
|
6229
6785
|
if (v === null) return 0;
|
|
6230
6786
|
const counts = { unmapped: 0, skipped: 0 };
|
|
6231
|
-
const backupRoot =
|
|
6787
|
+
const backupRoot = join50(backupBase(), ts, "extras");
|
|
6232
6788
|
const repo = repoHome();
|
|
6233
6789
|
let divergedCount = 0;
|
|
6234
|
-
for (const { logical, localRoot, dirname:
|
|
6235
|
-
const local =
|
|
6236
|
-
const repoEntry =
|
|
6237
|
-
if (!
|
|
6790
|
+
for (const { logical, localRoot, dirname: dirname13 } of eachExtrasTarget(v, counts)) {
|
|
6791
|
+
const local = join50(localRoot, dirname13);
|
|
6792
|
+
const repoEntry = join50(repo, "shared", "extras", logical, dirname13);
|
|
6793
|
+
if (!existsSync40(local) || !existsSync40(repoEntry)) continue;
|
|
6238
6794
|
const modified = listDivergingModified(local, repoEntry);
|
|
6239
6795
|
if (modified.length === 0) continue;
|
|
6240
6796
|
divergedCount += modified.length;
|
|
6241
|
-
const projectBackupRoot =
|
|
6797
|
+
const projectBackupRoot = join50(backupRoot, encodePath(localRoot));
|
|
6242
6798
|
warn(
|
|
6243
6799
|
divergenceWarnLine({
|
|
6244
|
-
dirname:
|
|
6800
|
+
dirname: dirname13,
|
|
6245
6801
|
logical,
|
|
6246
|
-
isDir:
|
|
6802
|
+
isDir: statSync11(local).isDirectory(),
|
|
6247
6803
|
count: modified.length,
|
|
6248
6804
|
projectBackupRoot
|
|
6249
6805
|
})
|
|
@@ -6258,53 +6814,10 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
6258
6814
|
return divergedCount;
|
|
6259
6815
|
}
|
|
6260
6816
|
|
|
6261
|
-
// src/skills-sync.ts
|
|
6262
|
-
init_config();
|
|
6263
|
-
import { existsSync as existsSync39, lstatSync as lstatSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync16, rmSync as rmSync14 } from "node:fs";
|
|
6264
|
-
import { join as join46 } from "node:path";
|
|
6265
|
-
init_utils_fs();
|
|
6266
|
-
function isGsdOwned(name) {
|
|
6267
|
-
return name.startsWith(GSD_PREFIX);
|
|
6268
|
-
}
|
|
6269
|
-
function isSkillExcluded(name) {
|
|
6270
|
-
return isGsdOwned(name) || isDeniedName(ALWAYS_NEVER_SYNC, name);
|
|
6271
|
-
}
|
|
6272
|
-
function copySkillsPush(src, dst) {
|
|
6273
|
-
const srcNames = readdirSync16(src, { encoding: "utf8" });
|
|
6274
|
-
const blockSet = /* @__PURE__ */ new Set([
|
|
6275
|
-
...srcNames.filter((n) => isGsdOwned(n)),
|
|
6276
|
-
...ALWAYS_NEVER_SYNC
|
|
6277
|
-
]);
|
|
6278
|
-
copyExtrasFiltered(src, dst, blockSet);
|
|
6279
|
-
}
|
|
6280
|
-
function copySkillsPull(src, dst) {
|
|
6281
|
-
copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded);
|
|
6282
|
-
}
|
|
6283
|
-
function syncSkillsPull(ts) {
|
|
6284
|
-
const sharedSkills = join46(repoHome(), "shared", "skills");
|
|
6285
|
-
if (!existsSync39(sharedSkills)) return;
|
|
6286
|
-
const localSkills = join46(claudeHome(), "skills");
|
|
6287
|
-
const dstStat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
6288
|
-
if (dstStat?.isSymbolicLink() === true) {
|
|
6289
|
-
backupBeforeWrite(localSkills, ts);
|
|
6290
|
-
rmSync14(localSkills, { recursive: true, force: true });
|
|
6291
|
-
mkdirSync9(localSkills, { recursive: true });
|
|
6292
|
-
}
|
|
6293
|
-
copySkillsPull(sharedSkills, localSkills);
|
|
6294
|
-
}
|
|
6295
|
-
function syncSkillsPush() {
|
|
6296
|
-
const localSkills = join46(claudeHome(), "skills");
|
|
6297
|
-
const stat = lstatSync12(localSkills, { throwIfNoEntry: false });
|
|
6298
|
-
if (stat === void 0) return;
|
|
6299
|
-
if (stat.isSymbolicLink()) return;
|
|
6300
|
-
const sharedSkills = join46(repoHome(), "shared", "skills");
|
|
6301
|
-
copySkillsPush(localSkills, sharedSkills);
|
|
6302
|
-
}
|
|
6303
|
-
|
|
6304
6817
|
// src/preview.ts
|
|
6305
6818
|
init_config();
|
|
6306
|
-
import { existsSync as
|
|
6307
|
-
import { join as
|
|
6819
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
6820
|
+
import { join as join51 } from "node:path";
|
|
6308
6821
|
|
|
6309
6822
|
// node_modules/diff/libesm/diff/base.js
|
|
6310
6823
|
var Diff = class {
|
|
@@ -6590,7 +7103,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
6590
7103
|
return lines.join("\n");
|
|
6591
7104
|
}
|
|
6592
7105
|
function readJsonOrNull(path) {
|
|
6593
|
-
if (!
|
|
7106
|
+
if (!existsSync41(path)) return null;
|
|
6594
7107
|
try {
|
|
6595
7108
|
return readJson(path);
|
|
6596
7109
|
} catch {
|
|
@@ -6604,12 +7117,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
6604
7117
|
}
|
|
6605
7118
|
const notes = [];
|
|
6606
7119
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
6607
|
-
if (hostOverrides === null &&
|
|
7120
|
+
if (hostOverrides === null && existsSync41(hostPath)) {
|
|
6608
7121
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
6609
7122
|
}
|
|
6610
7123
|
const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
|
|
6611
7124
|
const current = readJsonOrNull(settingsPath);
|
|
6612
|
-
if (current === null &&
|
|
7125
|
+
if (current === null && existsSync41(settingsPath)) {
|
|
6613
7126
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
6614
7127
|
}
|
|
6615
7128
|
const strippedCurrent = stripGsdHookEntries(current ?? {});
|
|
@@ -6651,9 +7164,9 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
6651
7164
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
6652
7165
|
});
|
|
6653
7166
|
const settingsResult = previewSettings(
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
7167
|
+
join51(repo, "shared", "settings.base.json"),
|
|
7168
|
+
join51(repo, "hosts", `${HOST}.json`),
|
|
7169
|
+
join51(claude, "settings.json")
|
|
6657
7170
|
);
|
|
6658
7171
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
6659
7172
|
const sessions = section("Sessions");
|
|
@@ -6668,7 +7181,7 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
6668
7181
|
const extras = section("Extras");
|
|
6669
7182
|
let extrasSkipped = 0;
|
|
6670
7183
|
let extrasUnmapped = 0;
|
|
6671
|
-
if (
|
|
7184
|
+
if (existsSync41(join51(repo, "path-map.json")) && existsSync41(join51(repo, "shared", "extras"))) {
|
|
6672
7185
|
const extrasResult = remapExtrasPull(ts, { dryRun: true });
|
|
6673
7186
|
for (const entry of extrasResult.wouldPull) {
|
|
6674
7187
|
addItem(extras, entry);
|
|
@@ -6693,9 +7206,9 @@ init_config();
|
|
|
6693
7206
|
init_commands_pull_wedge();
|
|
6694
7207
|
init_utils();
|
|
6695
7208
|
init_utils_fs();
|
|
6696
|
-
import { execFileSync as
|
|
7209
|
+
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
6697
7210
|
function gitCapture(args, cwd) {
|
|
6698
|
-
return
|
|
7211
|
+
return execFileSync21("git", args, {
|
|
6699
7212
|
cwd,
|
|
6700
7213
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6701
7214
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -6814,6 +7327,7 @@ function recoverForceRemote(mode, repo) {
|
|
|
6814
7327
|
}
|
|
6815
7328
|
|
|
6816
7329
|
// src/commands.pull.ts
|
|
7330
|
+
init_exit_codes();
|
|
6817
7331
|
init_utils();
|
|
6818
7332
|
init_utils_fs();
|
|
6819
7333
|
init_utils_json();
|
|
@@ -6862,7 +7376,7 @@ function handleWedge(repo, forceRemote) {
|
|
|
6862
7376
|
if (forceRemote) {
|
|
6863
7377
|
recoverUnmergedIndex(repo);
|
|
6864
7378
|
} else {
|
|
6865
|
-
die(unmergedIndexRunbookText("nomad pull"));
|
|
7379
|
+
die(unmergedIndexRunbookText("nomad pull"), { code: EXIT.CONFLICT });
|
|
6866
7380
|
}
|
|
6867
7381
|
return;
|
|
6868
7382
|
}
|
|
@@ -6871,7 +7385,7 @@ function handleWedge(repo, forceRemote) {
|
|
|
6871
7385
|
return;
|
|
6872
7386
|
}
|
|
6873
7387
|
const state = wedge === "rebase" ? "mid-rebase" : "mid-merge";
|
|
6874
|
-
die(wedgeMarkerRunbookText(state));
|
|
7388
|
+
die(wedgeMarkerRunbookText(state), { code: EXIT.CONFLICT });
|
|
6875
7389
|
}
|
|
6876
7390
|
function runPullCore(opts = {}) {
|
|
6877
7391
|
const dryRun = opts.dryRun === true;
|
|
@@ -6882,9 +7396,9 @@ function runPullCore(opts = {}) {
|
|
|
6882
7396
|
const ts = freshBackupTs(backup);
|
|
6883
7397
|
handleWedge(repo, forceRemote);
|
|
6884
7398
|
if (!dryRun) {
|
|
6885
|
-
const backupRoot =
|
|
7399
|
+
const backupRoot = join52(backup, ts);
|
|
6886
7400
|
try {
|
|
6887
|
-
|
|
7401
|
+
mkdirSync14(backupRoot, { recursive: true });
|
|
6888
7402
|
} catch (err) {
|
|
6889
7403
|
die(`could not create backup dir: ${err.message}`);
|
|
6890
7404
|
}
|
|
@@ -6897,8 +7411,8 @@ function runPullCore(opts = {}) {
|
|
|
6897
7411
|
const prePostHeads = capturePrePostHeads(repo, () => {
|
|
6898
7412
|
gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
|
|
6899
7413
|
});
|
|
6900
|
-
const mapPath =
|
|
6901
|
-
const map =
|
|
7414
|
+
const mapPath = join52(repo, "path-map.json");
|
|
7415
|
+
const map = existsSync42(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
6902
7416
|
const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
|
|
6903
7417
|
if (dryRun) {
|
|
6904
7418
|
computePreview(ts, map, "pull");
|
|
@@ -6924,8 +7438,8 @@ function runPullCore(opts = {}) {
|
|
|
6924
7438
|
}
|
|
6925
7439
|
function cmdPull(opts = {}) {
|
|
6926
7440
|
const repo = repoHome();
|
|
6927
|
-
if (!
|
|
6928
|
-
if (!
|
|
7441
|
+
if (!existsSync42(repo)) die(`repo not cloned at ${repo}`);
|
|
7442
|
+
if (!existsSync42(join52(repo, "shared", "settings.base.json"))) {
|
|
6929
7443
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
6930
7444
|
}
|
|
6931
7445
|
const handle = acquireLock("pull");
|
|
@@ -6936,7 +7450,7 @@ function cmdPull(opts = {}) {
|
|
|
6936
7450
|
} catch (err) {
|
|
6937
7451
|
if (err instanceof NomadFatal) {
|
|
6938
7452
|
fail(err.message);
|
|
6939
|
-
process.exitCode =
|
|
7453
|
+
process.exitCode = err.code;
|
|
6940
7454
|
} else {
|
|
6941
7455
|
throw err;
|
|
6942
7456
|
}
|
|
@@ -6947,13 +7461,13 @@ function cmdPull(opts = {}) {
|
|
|
6947
7461
|
|
|
6948
7462
|
// src/commands.push.ts
|
|
6949
7463
|
init_config();
|
|
6950
|
-
import { existsSync as
|
|
6951
|
-
import { join as
|
|
7464
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
7465
|
+
import { join as join57 } from "node:path";
|
|
6952
7466
|
|
|
6953
7467
|
// src/commands.push.selection.ts
|
|
6954
7468
|
init_config();
|
|
6955
|
-
import { existsSync as
|
|
6956
|
-
import { join as
|
|
7469
|
+
import { existsSync as existsSync43, statSync as statSync12 } from "node:fs";
|
|
7470
|
+
import { join as join53 } from "node:path";
|
|
6957
7471
|
init_utils_json();
|
|
6958
7472
|
function buildCurrentMap(map) {
|
|
6959
7473
|
const current = {};
|
|
@@ -6962,10 +7476,10 @@ function buildCurrentMap(map) {
|
|
|
6962
7476
|
for (const [, hostMap] of Object.entries(map.projects)) {
|
|
6963
7477
|
const localPath = hostMap[HOST];
|
|
6964
7478
|
if (!localPath) continue;
|
|
6965
|
-
const localDir =
|
|
6966
|
-
if (!
|
|
7479
|
+
const localDir = join53(claude, "projects", encodePath(localPath));
|
|
7480
|
+
if (!existsSync43(localDir)) continue;
|
|
6967
7481
|
for (const f of enumerateSourceFiles(localDir)) {
|
|
6968
|
-
const st =
|
|
7482
|
+
const st = statSync12(f);
|
|
6969
7483
|
current[f] = { size: st.size, mtime: st.mtimeMs };
|
|
6970
7484
|
}
|
|
6971
7485
|
}
|
|
@@ -6994,7 +7508,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
|
|
|
6994
7508
|
};
|
|
6995
7509
|
}
|
|
6996
7510
|
function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
|
|
6997
|
-
const map =
|
|
7511
|
+
const map = existsSync43(mapPath) ? readPathMap(mapPath) : null;
|
|
6998
7512
|
const { selection, newManifest } = computePushSelection(
|
|
6999
7513
|
map,
|
|
7000
7514
|
old,
|
|
@@ -7096,14 +7610,14 @@ function enforceAllowList(statusPorcelain, map) {
|
|
|
7096
7610
|
|
|
7097
7611
|
// src/commands.push.settings.ts
|
|
7098
7612
|
init_config();
|
|
7099
|
-
import { existsSync as
|
|
7100
|
-
import { join as
|
|
7613
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
7614
|
+
import { join as join54 } from "node:path";
|
|
7101
7615
|
init_utils();
|
|
7102
7616
|
init_utils_fs();
|
|
7103
7617
|
init_utils_json();
|
|
7104
7618
|
function stripGsdHooksFromBase(repo, backup) {
|
|
7105
|
-
const basePath =
|
|
7106
|
-
if (!
|
|
7619
|
+
const basePath = join54(repo, "shared", "settings.base.json");
|
|
7620
|
+
if (!existsSync44(basePath)) return;
|
|
7107
7621
|
let base;
|
|
7108
7622
|
try {
|
|
7109
7623
|
base = readJson(basePath);
|
|
@@ -7117,14 +7631,14 @@ function stripGsdHooksFromBase(repo, backup) {
|
|
|
7117
7631
|
writeJsonAtomic(basePath, stripped);
|
|
7118
7632
|
}
|
|
7119
7633
|
function reportSettingsAheadDrift(repo) {
|
|
7120
|
-
const basePath =
|
|
7121
|
-
if (!
|
|
7122
|
-
const settingsPath =
|
|
7123
|
-
if (!
|
|
7634
|
+
const basePath = join54(repo, "shared", "settings.base.json");
|
|
7635
|
+
if (!existsSync44(basePath)) return;
|
|
7636
|
+
const settingsPath = join54(claudeHome(), "settings.json");
|
|
7637
|
+
if (!existsSync44(settingsPath)) return;
|
|
7124
7638
|
try {
|
|
7125
7639
|
const base = readJson(basePath);
|
|
7126
|
-
const hostPath =
|
|
7127
|
-
const overrides =
|
|
7640
|
+
const hostPath = join54(repo, "hosts", `${HOST}.json`);
|
|
7641
|
+
const overrides = existsSync44(hostPath) ? readJson(hostPath) : {};
|
|
7128
7642
|
const merged = deepMerge(base, overrides);
|
|
7129
7643
|
const settings = readJson(settingsPath);
|
|
7130
7644
|
const { ahead } = classifySettingsDrift(merged, settings);
|
|
@@ -7141,9 +7655,9 @@ function reportSettingsAheadDrift(repo) {
|
|
|
7141
7655
|
// src/commands.push.guards.ts
|
|
7142
7656
|
init_push_checks();
|
|
7143
7657
|
init_utils();
|
|
7144
|
-
import { join as
|
|
7658
|
+
import { join as join55, relative as relative7 } from "node:path";
|
|
7145
7659
|
function guardGitlinks(repo) {
|
|
7146
|
-
const gitlinks = findGitlinks(
|
|
7660
|
+
const gitlinks = findGitlinks(join55(repo, "shared"));
|
|
7147
7661
|
if (gitlinks.length === 0) return;
|
|
7148
7662
|
for (const p of gitlinks) {
|
|
7149
7663
|
const rel = relative7(repo, p).replaceAll("\\", "/");
|
|
@@ -7175,7 +7689,7 @@ init_config();
|
|
|
7175
7689
|
|
|
7176
7690
|
// src/push-global-config.ts
|
|
7177
7691
|
init_config();
|
|
7178
|
-
import { execFileSync as
|
|
7692
|
+
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
7179
7693
|
var STATUS_LABELS = {
|
|
7180
7694
|
A: "add",
|
|
7181
7695
|
M: "modify",
|
|
@@ -7215,7 +7729,7 @@ function isInScope(filePath, exactPrefixes, dirPrefixes) {
|
|
|
7215
7729
|
}
|
|
7216
7730
|
function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
|
|
7217
7731
|
const args = opts.staged ? ["diff", "--cached", "--name-status", "-z"] : ["diff", "HEAD", "--name-status", "-z"];
|
|
7218
|
-
const raw =
|
|
7732
|
+
const raw = execFileSync22("git", args, {
|
|
7219
7733
|
cwd: repoHome2,
|
|
7220
7734
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7221
7735
|
}).toString();
|
|
@@ -7252,10 +7766,10 @@ init_push_leak_verdict();
|
|
|
7252
7766
|
init_color();
|
|
7253
7767
|
init_config();
|
|
7254
7768
|
init_config_sharedDirs_guard();
|
|
7255
|
-
import { randomBytes as
|
|
7256
|
-
import { copyFileSync, existsSync as
|
|
7257
|
-
import { homedir as
|
|
7258
|
-
import { join as
|
|
7769
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
7770
|
+
import { copyFileSync, existsSync as existsSync45, mkdirSync as mkdirSync15, readdirSync as readdirSync17, rmSync as rmSync17 } from "node:fs";
|
|
7771
|
+
import { homedir as homedir7 } from "node:os";
|
|
7772
|
+
import { join as join56, relative as relative8, sep as sep10 } from "node:path";
|
|
7259
7773
|
init_push_leak_verdict();
|
|
7260
7774
|
init_push_gitleaks();
|
|
7261
7775
|
init_utils_fs();
|
|
@@ -7263,11 +7777,11 @@ init_utils_json();
|
|
|
7263
7777
|
var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
|
|
7264
7778
|
function stageSessionDir(localDir, dstDir, changed) {
|
|
7265
7779
|
if (changed !== void 0) {
|
|
7266
|
-
const prefix = `${localDir}${
|
|
7780
|
+
const prefix = `${localDir}${sep10}`;
|
|
7267
7781
|
const matching = [...changed].filter((p) => p.startsWith(prefix));
|
|
7268
7782
|
if (matching.length === 0) return false;
|
|
7269
7783
|
for (const src of matching) {
|
|
7270
|
-
copyFileAtomic(src,
|
|
7784
|
+
copyFileAtomic(src, join56(dstDir, relative8(localDir, src)));
|
|
7271
7785
|
}
|
|
7272
7786
|
return true;
|
|
7273
7787
|
}
|
|
@@ -7283,14 +7797,14 @@ function stageSessions(tmpRoot, map, changed) {
|
|
|
7283
7797
|
if (!p || p === "TBD") continue;
|
|
7284
7798
|
reverse.set(encodePath(p), logical);
|
|
7285
7799
|
}
|
|
7286
|
-
const localProjects =
|
|
7287
|
-
if (!
|
|
7800
|
+
const localProjects = join56(claudeHome(), "projects");
|
|
7801
|
+
if (!existsSync45(localProjects)) return 0;
|
|
7288
7802
|
let staged = 0;
|
|
7289
7803
|
for (const dir of readdirSync17(localProjects)) {
|
|
7290
7804
|
const logical = reverse.get(dir);
|
|
7291
7805
|
if (!logical) continue;
|
|
7292
|
-
const localDir =
|
|
7293
|
-
const dstDir =
|
|
7806
|
+
const localDir = join56(localProjects, dir);
|
|
7807
|
+
const dstDir = join56(tmpRoot, "shared", "projects", logical);
|
|
7294
7808
|
if (stageSessionDir(localDir, dstDir, changed)) staged++;
|
|
7295
7809
|
}
|
|
7296
7810
|
return staged;
|
|
@@ -7304,11 +7818,11 @@ function stageExtras(tmpRoot, map) {
|
|
|
7304
7818
|
assertSafeLogical(logical);
|
|
7305
7819
|
const localRoot = map.projects[logical]?.[HOST];
|
|
7306
7820
|
if (!localRoot || localRoot === "TBD") continue;
|
|
7307
|
-
for (const
|
|
7308
|
-
if (!whitelist.includes(
|
|
7309
|
-
const src =
|
|
7310
|
-
if (!
|
|
7311
|
-
const dst =
|
|
7821
|
+
for (const dirname13 of dirnames) {
|
|
7822
|
+
if (!whitelist.includes(dirname13)) continue;
|
|
7823
|
+
const src = join56(localRoot, dirname13);
|
|
7824
|
+
if (!existsSync45(src)) continue;
|
|
7825
|
+
const dst = join56(tmpRoot, "shared", "extras", logical, dirname13);
|
|
7312
7826
|
copyExtras(src, dst);
|
|
7313
7827
|
staged++;
|
|
7314
7828
|
}
|
|
@@ -7316,19 +7830,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
7316
7830
|
return staged;
|
|
7317
7831
|
}
|
|
7318
7832
|
function previewPushLeaks(map, opts = {}) {
|
|
7319
|
-
const cacheDir =
|
|
7320
|
-
|
|
7321
|
-
const stamp = `${nowTimestamp()}-${process.pid}-${
|
|
7322
|
-
const tmpRoot =
|
|
7833
|
+
const cacheDir = join56(homedir7(), ".cache", "claude-nomad");
|
|
7834
|
+
mkdirSync15(cacheDir, { recursive: true });
|
|
7835
|
+
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
7836
|
+
const tmpRoot = join56(cacheDir, `push-preview-tree-${stamp}`);
|
|
7323
7837
|
try {
|
|
7324
7838
|
const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
|
|
7325
7839
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
7326
7840
|
if (sessionCount + extrasCount === 0) {
|
|
7327
7841
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
7328
7842
|
}
|
|
7329
|
-
const ignoreFile =
|
|
7330
|
-
if (
|
|
7331
|
-
copyFileSync(ignoreFile,
|
|
7843
|
+
const ignoreFile = join56(repoHome(), ".gitleaksignore");
|
|
7844
|
+
if (existsSync45(ignoreFile)) {
|
|
7845
|
+
copyFileSync(ignoreFile, join56(tmpRoot, ".gitleaksignore"));
|
|
7332
7846
|
}
|
|
7333
7847
|
let findings;
|
|
7334
7848
|
try {
|
|
@@ -7341,7 +7855,7 @@ function previewPushLeaks(map, opts = {}) {
|
|
|
7341
7855
|
}
|
|
7342
7856
|
return verdictFromFindings(findings);
|
|
7343
7857
|
} finally {
|
|
7344
|
-
|
|
7858
|
+
rmSync17(tmpRoot, { recursive: true, force: true });
|
|
7345
7859
|
}
|
|
7346
7860
|
}
|
|
7347
7861
|
|
|
@@ -7454,7 +7968,7 @@ async function runPushCore(opts = {}) {
|
|
|
7454
7968
|
const scannerVersion = probeGitleaks();
|
|
7455
7969
|
const configHash = computeConfigHash();
|
|
7456
7970
|
const old = readManifest(manifestPath());
|
|
7457
|
-
const mapPath =
|
|
7971
|
+
const mapPath = join57(repo, "path-map.json");
|
|
7458
7972
|
const { map, selection, newManifest } = loadSelectionForPush(
|
|
7459
7973
|
mapPath,
|
|
7460
7974
|
old,
|
|
@@ -7505,7 +8019,7 @@ async function cmdPush(opts = {}) {
|
|
|
7505
8019
|
const allowRule = opts.allowRule;
|
|
7506
8020
|
guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
|
|
7507
8021
|
const repo = repoHome();
|
|
7508
|
-
if (!
|
|
8022
|
+
if (!existsSync46(repo)) die(`repo not cloned at ${repo}`);
|
|
7509
8023
|
const handle = acquireLock("push");
|
|
7510
8024
|
if (handle === null) process.exit(0);
|
|
7511
8025
|
try {
|
|
@@ -7513,7 +8027,7 @@ async function cmdPush(opts = {}) {
|
|
|
7513
8027
|
} catch (err) {
|
|
7514
8028
|
if (err instanceof NomadFatal) {
|
|
7515
8029
|
fail(err.message);
|
|
7516
|
-
process.exitCode =
|
|
8030
|
+
process.exitCode = err.code;
|
|
7517
8031
|
} else {
|
|
7518
8032
|
throw err;
|
|
7519
8033
|
}
|
|
@@ -7523,8 +8037,8 @@ async function cmdPush(opts = {}) {
|
|
|
7523
8037
|
}
|
|
7524
8038
|
|
|
7525
8039
|
// src/commands.sync.ts
|
|
7526
|
-
import { existsSync as
|
|
7527
|
-
import { join as
|
|
8040
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
8041
|
+
import { join as join58 } from "node:path";
|
|
7528
8042
|
init_config();
|
|
7529
8043
|
init_color();
|
|
7530
8044
|
init_utils();
|
|
@@ -7639,7 +8153,7 @@ async function runSyncPushHalf() {
|
|
|
7639
8153
|
return { ok: true, result };
|
|
7640
8154
|
} catch (err) {
|
|
7641
8155
|
if (err instanceof NomadFatal) {
|
|
7642
|
-
process.exitCode =
|
|
8156
|
+
process.exitCode = err.code;
|
|
7643
8157
|
return { ok: false, message: err.message };
|
|
7644
8158
|
}
|
|
7645
8159
|
throw err;
|
|
@@ -7652,8 +8166,8 @@ async function runSyncWet(verbose) {
|
|
|
7652
8166
|
}
|
|
7653
8167
|
async function runSyncDryRun(repo, backup) {
|
|
7654
8168
|
const ts = freshBackupTs(backup);
|
|
7655
|
-
const mapPath =
|
|
7656
|
-
const map =
|
|
8169
|
+
const mapPath = join58(repo, "path-map.json");
|
|
8170
|
+
const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
7657
8171
|
computePreview(ts, map, "pull");
|
|
7658
8172
|
log("push preview below is computed against pre-pull state (a real sync pushes after pull)");
|
|
7659
8173
|
await runPushCore({ dryRun: true });
|
|
@@ -7663,8 +8177,8 @@ async function cmdSync(opts = {}) {
|
|
|
7663
8177
|
const verbose = opts.verbose === true;
|
|
7664
8178
|
const repo = repoHome();
|
|
7665
8179
|
const backup = backupBase();
|
|
7666
|
-
if (!
|
|
7667
|
-
if (!
|
|
8180
|
+
if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
|
|
8181
|
+
if (!existsSync47(join58(repo, "shared", "settings.base.json"))) {
|
|
7668
8182
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
7669
8183
|
}
|
|
7670
8184
|
const handle = acquireLock("sync");
|
|
@@ -7678,7 +8192,7 @@ async function cmdSync(opts = {}) {
|
|
|
7678
8192
|
} catch (err) {
|
|
7679
8193
|
if (err instanceof NomadFatal) {
|
|
7680
8194
|
fail(err.message);
|
|
7681
|
-
process.exitCode =
|
|
8195
|
+
process.exitCode = err.code;
|
|
7682
8196
|
} else {
|
|
7683
8197
|
throw err;
|
|
7684
8198
|
}
|
|
@@ -7688,9 +8202,9 @@ async function cmdSync(opts = {}) {
|
|
|
7688
8202
|
}
|
|
7689
8203
|
|
|
7690
8204
|
// src/commands.update.ts
|
|
7691
|
-
import { execFileSync as
|
|
8205
|
+
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
7692
8206
|
init_utils();
|
|
7693
|
-
function readInstalledVersion(run =
|
|
8207
|
+
function readInstalledVersion(run = execFileSync23) {
|
|
7694
8208
|
const isWin = process.platform === "win32";
|
|
7695
8209
|
try {
|
|
7696
8210
|
return run(isWin ? "nomad.cmd" : "nomad", ["--version"], {
|
|
@@ -7701,7 +8215,7 @@ function readInstalledVersion(run = execFileSync21) {
|
|
|
7701
8215
|
return null;
|
|
7702
8216
|
}
|
|
7703
8217
|
}
|
|
7704
|
-
function cmdUpdate(currentVersion, run =
|
|
8218
|
+
function cmdUpdate(currentVersion, run = execFileSync23) {
|
|
7705
8219
|
console.log(`Updating claude-nomad v${currentVersion}...`);
|
|
7706
8220
|
const isWin = process.platform === "win32";
|
|
7707
8221
|
try {
|
|
@@ -7735,26 +8249,191 @@ ${detail}` : "";
|
|
|
7735
8249
|
// src/nomad.ts
|
|
7736
8250
|
init_config();
|
|
7737
8251
|
|
|
8252
|
+
// src/crash-report.write.ts
|
|
8253
|
+
init_config();
|
|
8254
|
+
import { mkdirSync as mkdirSync16, readdirSync as readdirSync18, statSync as statSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
|
|
8255
|
+
import { join as join60 } from "node:path";
|
|
8256
|
+
|
|
8257
|
+
// src/crash-report.ts
|
|
8258
|
+
var CRASH_MAX_STACK_LINES = 50;
|
|
8259
|
+
var CRASH_MAX_ARGV = 32;
|
|
8260
|
+
var CRASH_MAX_ARGV_TOKEN_LENGTH = 200;
|
|
8261
|
+
var CRASH_MAX_REPORT_BYTES = 16384;
|
|
8262
|
+
var TRUNCATION_MARKER = "\n[... truncated ...]\n";
|
|
8263
|
+
function safeStringify(value) {
|
|
8264
|
+
try {
|
|
8265
|
+
const json = JSON.stringify(value);
|
|
8266
|
+
if (json !== void 0) return json;
|
|
8267
|
+
} catch {
|
|
8268
|
+
}
|
|
8269
|
+
try {
|
|
8270
|
+
return String(value);
|
|
8271
|
+
} catch {
|
|
8272
|
+
return "(unstringifiable thrown value)";
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
function normalizeError(err) {
|
|
8276
|
+
try {
|
|
8277
|
+
if (err instanceof Error) {
|
|
8278
|
+
return { name: err.name, message: err.message, stack: err.stack };
|
|
8279
|
+
}
|
|
8280
|
+
} catch {
|
|
8281
|
+
return { name: "Error", message: "(unreadable Error)", stack: void 0 };
|
|
8282
|
+
}
|
|
8283
|
+
if (typeof err === "string") {
|
|
8284
|
+
return { name: "NonErrorThrow", message: err, stack: void 0 };
|
|
8285
|
+
}
|
|
8286
|
+
return { name: "NonErrorThrow", message: safeStringify(err), stack: void 0 };
|
|
8287
|
+
}
|
|
8288
|
+
function boundedStack(stack) {
|
|
8289
|
+
if (stack === void 0) return "(no stack available)";
|
|
8290
|
+
return stack.split("\n").slice(0, CRASH_MAX_STACK_LINES).join("\n");
|
|
8291
|
+
}
|
|
8292
|
+
function clampArgv(argv) {
|
|
8293
|
+
return argv.slice(0, CRASH_MAX_ARGV).map((tok) => {
|
|
8294
|
+
if (tok.length <= CRASH_MAX_ARGV_TOKEN_LENGTH) return tok;
|
|
8295
|
+
return tok.slice(0, CRASH_MAX_ARGV_TOKEN_LENGTH) + "...";
|
|
8296
|
+
});
|
|
8297
|
+
}
|
|
8298
|
+
function truncateToBytes(text, maxBytes) {
|
|
8299
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
8300
|
+
const budget = Math.max(0, maxBytes - Buffer.byteLength(TRUNCATION_MARKER, "utf8"));
|
|
8301
|
+
let sliced = text.slice(0, budget);
|
|
8302
|
+
while (Buffer.byteLength(sliced, "utf8") > budget) {
|
|
8303
|
+
sliced = sliced.slice(0, -1);
|
|
8304
|
+
}
|
|
8305
|
+
return sliced + TRUNCATION_MARKER;
|
|
8306
|
+
}
|
|
8307
|
+
function scrubStructural(text, homeDir, hostLabel) {
|
|
8308
|
+
let out = homeDir.length > 0 ? text.split(homeDir).join("~") : text;
|
|
8309
|
+
if (hostLabel.length > 0) out = out.split(hostLabel).join("<host>");
|
|
8310
|
+
return out;
|
|
8311
|
+
}
|
|
8312
|
+
function buildCrashReport(input) {
|
|
8313
|
+
const { err, argv, version, platform: platform2, timestamp, homeDir, hostLabel } = input;
|
|
8314
|
+
const normalized = normalizeError(err);
|
|
8315
|
+
const command = clampArgv(argv).join(" ");
|
|
8316
|
+
const stack = boundedStack(normalized.stack);
|
|
8317
|
+
const composed = [
|
|
8318
|
+
"nomad crash report",
|
|
8319
|
+
`version: ${version}`,
|
|
8320
|
+
`command: ${command}`,
|
|
8321
|
+
`error: ${normalized.name}: ${normalized.message}`,
|
|
8322
|
+
"stack:",
|
|
8323
|
+
stack,
|
|
8324
|
+
`platform: ${platform2} (node ${process.version})`,
|
|
8325
|
+
`timestamp: ${timestamp}`
|
|
8326
|
+
].join("\n");
|
|
8327
|
+
const scrubbed = scrubStructural(composed, homeDir, hostLabel);
|
|
8328
|
+
return truncateToBytes(scrubbed, CRASH_MAX_REPORT_BYTES);
|
|
8329
|
+
}
|
|
8330
|
+
|
|
8331
|
+
// src/crash-report.redact.ts
|
|
8332
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync18, writeFileSync as writeFileSync10 } from "node:fs";
|
|
8333
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
8334
|
+
import { join as join59 } from "node:path";
|
|
8335
|
+
init_push_gitleaks_scan();
|
|
8336
|
+
var CRASH_SCAN_TIMEOUT_MS = 3e3;
|
|
8337
|
+
var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
|
|
8338
|
+
function redactWithGitleaks(text, scan = scanFile) {
|
|
8339
|
+
let dir;
|
|
8340
|
+
try {
|
|
8341
|
+
dir = mkdtempSync2(join59(tmpdir2(), "nomad-crash-scan-"));
|
|
8342
|
+
const tmp = join59(dir, "crash.txt");
|
|
8343
|
+
writeFileSync10(tmp, text, { mode: 384 });
|
|
8344
|
+
const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
|
|
8345
|
+
if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
|
|
8346
|
+
return applyRedactions(text, findings);
|
|
8347
|
+
} catch {
|
|
8348
|
+
return text + SCAN_UNAVAILABLE_ADVISORY;
|
|
8349
|
+
} finally {
|
|
8350
|
+
if (dir !== void 0) {
|
|
8351
|
+
try {
|
|
8352
|
+
rmSync18(dir, { recursive: true, force: true });
|
|
8353
|
+
} catch {
|
|
8354
|
+
}
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
}
|
|
8358
|
+
|
|
8359
|
+
// src/crash-report.write.ts
|
|
8360
|
+
init_utils_fs();
|
|
8361
|
+
init_utils();
|
|
8362
|
+
var CRASH_RETENTION_KEEP = 20;
|
|
8363
|
+
function listCrashFiles(dir = crashDir()) {
|
|
8364
|
+
try {
|
|
8365
|
+
return readdirSync18(dir).flatMap((name) => {
|
|
8366
|
+
try {
|
|
8367
|
+
return [{ name, mtimeMs: statSync13(join60(dir, name)).mtimeMs }];
|
|
8368
|
+
} catch {
|
|
8369
|
+
return [];
|
|
8370
|
+
}
|
|
8371
|
+
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
8372
|
+
} catch {
|
|
8373
|
+
return [];
|
|
8374
|
+
}
|
|
8375
|
+
}
|
|
8376
|
+
function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
|
|
8377
|
+
const files = listCrashFiles(dir);
|
|
8378
|
+
const targets = prunableByCount(files, keep);
|
|
8379
|
+
for (const name of targets) {
|
|
8380
|
+
try {
|
|
8381
|
+
unlinkSync2(join60(dir, name));
|
|
8382
|
+
} catch {
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
}
|
|
8386
|
+
function writeCrashReport(text, dir = crashDir()) {
|
|
8387
|
+
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
8388
|
+
const path = join60(dir, `crash-${nowTimestamp()}-${process.pid}.txt`);
|
|
8389
|
+
writeFileSync11(path, text, { mode: 384 });
|
|
8390
|
+
pruneCrashDir(dir);
|
|
8391
|
+
return path;
|
|
8392
|
+
}
|
|
8393
|
+
function handleCrash(err, argv, opts) {
|
|
8394
|
+
const redact = opts.redact ?? redactWithGitleaks;
|
|
8395
|
+
const write = opts.write ?? writeCrashReport;
|
|
8396
|
+
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
8397
|
+
try {
|
|
8398
|
+
const report = buildCrashReport({
|
|
8399
|
+
err,
|
|
8400
|
+
argv,
|
|
8401
|
+
version: opts.version,
|
|
8402
|
+
platform: opts.platform,
|
|
8403
|
+
timestamp: now(),
|
|
8404
|
+
homeDir: home(),
|
|
8405
|
+
hostLabel: HOST
|
|
8406
|
+
});
|
|
8407
|
+
const redacted = redact(report);
|
|
8408
|
+
const path = write(redacted);
|
|
8409
|
+
fail("nomad hit an unexpected error. This looks like a bug, not something you did wrong.");
|
|
8410
|
+
item(`Crash report written to: ${path}`);
|
|
8411
|
+
item(`Please consider reporting it at: ${opts.issuesUrl}`);
|
|
8412
|
+
} catch {
|
|
8413
|
+
fail("nomad hit an unexpected error and could not write a crash report.");
|
|
8414
|
+
}
|
|
8415
|
+
}
|
|
8416
|
+
|
|
7738
8417
|
// src/diff.ts
|
|
7739
8418
|
init_config();
|
|
7740
|
-
import { existsSync as
|
|
7741
|
-
import { join as
|
|
8419
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
8420
|
+
import { join as join61 } from "node:path";
|
|
7742
8421
|
init_utils();
|
|
7743
8422
|
init_utils_fs();
|
|
7744
8423
|
init_utils_json();
|
|
7745
8424
|
function cmdDiff() {
|
|
7746
8425
|
try {
|
|
7747
8426
|
const repo = repoHome();
|
|
7748
|
-
if (!
|
|
8427
|
+
if (!existsSync48(repo)) die(`repo not cloned at ${repo}`);
|
|
7749
8428
|
const ts = freshBackupTs(backupBase());
|
|
7750
|
-
const mapPath =
|
|
7751
|
-
const map =
|
|
8429
|
+
const mapPath = join61(repo, "path-map.json");
|
|
8430
|
+
const map = existsSync48(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
7752
8431
|
divergenceCheckExtras(ts);
|
|
7753
8432
|
computePreview(ts, map, "diff");
|
|
7754
8433
|
} catch (err) {
|
|
7755
8434
|
if (err instanceof NomadFatal) {
|
|
7756
8435
|
fail(err.message);
|
|
7757
|
-
process.exitCode =
|
|
8436
|
+
process.exitCode = err.code;
|
|
7758
8437
|
return;
|
|
7759
8438
|
}
|
|
7760
8439
|
throw err;
|
|
@@ -7763,19 +8442,19 @@ function cmdDiff() {
|
|
|
7763
8442
|
|
|
7764
8443
|
// src/init.ts
|
|
7765
8444
|
init_config();
|
|
7766
|
-
import { existsSync as
|
|
7767
|
-
import { join as
|
|
8445
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
|
|
8446
|
+
import { join as join63 } from "node:path";
|
|
7768
8447
|
|
|
7769
8448
|
// src/init.gh-onboard.ts
|
|
7770
8449
|
init_config();
|
|
7771
|
-
import { execFileSync as
|
|
8450
|
+
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
7772
8451
|
init_utils();
|
|
7773
8452
|
var DEFAULT_REPO_NAME = "claude-nomad-config";
|
|
7774
8453
|
function isValidRepoName(name) {
|
|
7775
8454
|
return /^[A-Za-z0-9._-]{1,100}$/.test(name);
|
|
7776
8455
|
}
|
|
7777
8456
|
var GH_NETWORK_TIMEOUT_MS = 3e4;
|
|
7778
|
-
function ensureOriginRepo(repoName, run =
|
|
8457
|
+
function ensureOriginRepo(repoName, run = execFileSync24) {
|
|
7779
8458
|
if (!isValidRepoName(repoName)) {
|
|
7780
8459
|
die(
|
|
7781
8460
|
`invalid repo name: ${JSON.stringify(repoName)}. Use only letters, digits, hyphens, underscores, and dots (1-100 chars).`
|
|
@@ -7846,33 +8525,33 @@ init_config();
|
|
|
7846
8525
|
init_utils();
|
|
7847
8526
|
init_utils_fs();
|
|
7848
8527
|
init_utils_json();
|
|
7849
|
-
import { copyFileSync as copyFileSync2, cpSync as
|
|
7850
|
-
import { join as
|
|
8528
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync10, existsSync as existsSync49, rmSync as rmSync19, statSync as statSync14 } from "node:fs";
|
|
8529
|
+
import { join as join62 } from "node:path";
|
|
7851
8530
|
function snapshotIntoShared(map) {
|
|
7852
8531
|
const repo = repoHome();
|
|
7853
8532
|
const claude = claudeHome();
|
|
7854
8533
|
for (const name of allSharedLinks(map)) {
|
|
7855
|
-
const src =
|
|
7856
|
-
if (!
|
|
7857
|
-
const dst =
|
|
7858
|
-
if (
|
|
7859
|
-
const gk =
|
|
7860
|
-
if (
|
|
7861
|
-
|
|
8534
|
+
const src = join62(claude, name);
|
|
8535
|
+
if (!existsSync49(src)) continue;
|
|
8536
|
+
const dst = join62(repo, "shared", name);
|
|
8537
|
+
if (statSync14(src).isDirectory()) {
|
|
8538
|
+
const gk = join62(dst, ".gitkeep");
|
|
8539
|
+
if (existsSync49(gk)) rmSync19(gk);
|
|
8540
|
+
cpSync10(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
7862
8541
|
} else {
|
|
7863
8542
|
copyFileSync2(src, dst);
|
|
7864
8543
|
}
|
|
7865
8544
|
log(`snapshotted shared/${name} from ${src}`);
|
|
7866
8545
|
}
|
|
7867
|
-
const userSettings =
|
|
7868
|
-
if (
|
|
8546
|
+
const userSettings = join62(claude, "settings.json");
|
|
8547
|
+
if (existsSync49(userSettings)) {
|
|
7869
8548
|
let parsed;
|
|
7870
8549
|
try {
|
|
7871
8550
|
parsed = readJson(userSettings);
|
|
7872
8551
|
} catch (err) {
|
|
7873
8552
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
7874
8553
|
}
|
|
7875
|
-
const hostFile =
|
|
8554
|
+
const hostFile = join62(repo, "hosts", `${HOST}.json`);
|
|
7876
8555
|
writeJsonAtomic(hostFile, parsed);
|
|
7877
8556
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
7878
8557
|
}
|
|
@@ -7886,14 +8565,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
|
|
|
7886
8565
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
7887
8566
|
function preflightConflict(repoHome2) {
|
|
7888
8567
|
const candidates = [
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
8568
|
+
join63(repoHome2, "shared", "settings.base.json"),
|
|
8569
|
+
join63(repoHome2, "shared", "CLAUDE.md"),
|
|
8570
|
+
join63(repoHome2, "path-map.json"),
|
|
8571
|
+
join63(repoHome2, "hosts"),
|
|
8572
|
+
join63(repoHome2, "shared")
|
|
7894
8573
|
];
|
|
7895
8574
|
for (const c of candidates) {
|
|
7896
|
-
if (
|
|
8575
|
+
if (existsSync50(c)) return c;
|
|
7897
8576
|
}
|
|
7898
8577
|
return null;
|
|
7899
8578
|
}
|
|
@@ -7905,33 +8584,33 @@ function cmdInit(opts = {}) {
|
|
|
7905
8584
|
const keepActions = opts.keepActions === true;
|
|
7906
8585
|
const repo = repoHome();
|
|
7907
8586
|
const claude = claudeHome();
|
|
7908
|
-
|
|
8587
|
+
mkdirSync17(repo, { recursive: true });
|
|
7909
8588
|
const conflict = preflightConflict(repo);
|
|
7910
8589
|
if (conflict !== null) {
|
|
7911
8590
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
7912
8591
|
}
|
|
7913
8592
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
7914
|
-
|
|
7915
|
-
|
|
8593
|
+
mkdirSync17(join63(repo, "shared"), { recursive: true });
|
|
8594
|
+
mkdirSync17(join63(repo, "hosts"), { recursive: true });
|
|
7916
8595
|
for (const name of SHARED_KEEP_DIRS) {
|
|
7917
|
-
|
|
8596
|
+
mkdirSync17(join63(repo, "shared", name), { recursive: true });
|
|
7918
8597
|
}
|
|
7919
|
-
const userClaudeMd =
|
|
7920
|
-
if (!snapshot || !
|
|
7921
|
-
|
|
8598
|
+
const userClaudeMd = join63(claude, "CLAUDE.md");
|
|
8599
|
+
if (!snapshot || !existsSync50(userClaudeMd)) {
|
|
8600
|
+
writeFileSync12(join63(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
7922
8601
|
item("created shared/CLAUDE.md");
|
|
7923
8602
|
}
|
|
7924
8603
|
for (const name of SHARED_KEEP_DIRS) {
|
|
7925
|
-
|
|
8604
|
+
writeFileSync12(join63(repo, "shared", name, ".gitkeep"), "");
|
|
7926
8605
|
item(`created shared/${name}/.gitkeep`);
|
|
7927
8606
|
}
|
|
7928
|
-
|
|
8607
|
+
writeFileSync12(join63(repo, "hosts", ".gitkeep"), "");
|
|
7929
8608
|
item("created hosts/.gitkeep");
|
|
7930
|
-
writeJsonAtomic(
|
|
8609
|
+
writeJsonAtomic(join63(repo, "shared", "settings.base.json"), {});
|
|
7931
8610
|
item("created shared/settings.base.json");
|
|
7932
|
-
writeJsonAtomic(
|
|
8611
|
+
writeJsonAtomic(join63(repo, "path-map.json"), { projects: {} });
|
|
7933
8612
|
item("created path-map.json");
|
|
7934
|
-
|
|
8613
|
+
writeFileSync12(join63(repo, ".gitattributes"), GITATTRIBUTES);
|
|
7935
8614
|
item("created .gitattributes");
|
|
7936
8615
|
if (snapshot) {
|
|
7937
8616
|
snapshotIntoShared({ projects: {} });
|
|
@@ -7997,21 +8676,21 @@ function maybeDisableRepoActions(repoHome2, run) {
|
|
|
7997
8676
|
// src/init.prompt.ts
|
|
7998
8677
|
init_config();
|
|
7999
8678
|
init_utils();
|
|
8000
|
-
import { existsSync as
|
|
8001
|
-
import { join as
|
|
8679
|
+
import { existsSync as existsSync51, readdirSync as readdirSync19, statSync as statSync15 } from "node:fs";
|
|
8680
|
+
import { join as join64 } from "node:path";
|
|
8002
8681
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
8003
8682
|
function nonEmptyExists(path) {
|
|
8004
|
-
if (!
|
|
8683
|
+
if (!existsSync51(path)) return false;
|
|
8005
8684
|
try {
|
|
8006
|
-
if (
|
|
8685
|
+
if (statSync15(path).isDirectory()) return readdirSync19(path).length > 0;
|
|
8007
8686
|
return true;
|
|
8008
8687
|
} catch {
|
|
8009
8688
|
return false;
|
|
8010
8689
|
}
|
|
8011
8690
|
}
|
|
8012
8691
|
function hasExistingClaudeConfig(claudeHome2) {
|
|
8013
|
-
if (
|
|
8014
|
-
return SHARED_LINKS.some((name) => nonEmptyExists(
|
|
8692
|
+
if (existsSync51(join64(claudeHome2, "settings.json"))) return true;
|
|
8693
|
+
return SHARED_LINKS.some((name) => nonEmptyExists(join64(claudeHome2, name)));
|
|
8015
8694
|
}
|
|
8016
8695
|
async function confirmSnapshotDefault(claudeHome2) {
|
|
8017
8696
|
if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
|
|
@@ -8318,7 +8997,7 @@ function parseSyncArgs(argv) {
|
|
|
8318
8997
|
// package.json
|
|
8319
8998
|
var package_default = {
|
|
8320
8999
|
name: "claude-nomad",
|
|
8321
|
-
version: "0.
|
|
9000
|
+
version: "0.62.0",
|
|
8322
9001
|
type: "module",
|
|
8323
9002
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
8324
9003
|
keywords: [
|
|
@@ -8401,7 +9080,7 @@ var package_default = {
|
|
|
8401
9080
|
globals: "^17.6.0",
|
|
8402
9081
|
husky: "^9.1.7",
|
|
8403
9082
|
"lint-staged": "^17.0.5",
|
|
8404
|
-
"markdownlint-cli2": "^0.23.
|
|
9083
|
+
"markdownlint-cli2": "^0.23.1",
|
|
8405
9084
|
picocolors: "^1.1.1",
|
|
8406
9085
|
prettier: "^3.8.3",
|
|
8407
9086
|
typescript: "^6.0.3",
|
|
@@ -8559,15 +9238,15 @@ var DEFAULT_HELP = [
|
|
|
8559
9238
|
init_config();
|
|
8560
9239
|
init_utils();
|
|
8561
9240
|
init_utils_json();
|
|
8562
|
-
import { existsSync as
|
|
8563
|
-
import { join as
|
|
9241
|
+
import { existsSync as existsSync52, readFileSync as readFileSync20, readdirSync as readdirSync20 } from "node:fs";
|
|
9242
|
+
import { join as join65 } from "node:path";
|
|
8564
9243
|
function resumeCmd(sessionId) {
|
|
8565
9244
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
8566
9245
|
fail(`invalid session id: ${sessionId}`);
|
|
8567
9246
|
process.exit(1);
|
|
8568
9247
|
}
|
|
8569
|
-
const projectsRoot =
|
|
8570
|
-
if (!
|
|
9248
|
+
const projectsRoot = join65(claudeHome(), "projects");
|
|
9249
|
+
if (!existsSync52(projectsRoot)) {
|
|
8571
9250
|
fail(`${projectsRoot} does not exist`);
|
|
8572
9251
|
process.exit(1);
|
|
8573
9252
|
}
|
|
@@ -8581,8 +9260,8 @@ function resumeCmd(sessionId) {
|
|
|
8581
9260
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
8582
9261
|
process.exit(1);
|
|
8583
9262
|
}
|
|
8584
|
-
const mapPath =
|
|
8585
|
-
if (!
|
|
9263
|
+
const mapPath = join65(repoHome(), "path-map.json");
|
|
9264
|
+
if (!existsSync52(mapPath)) {
|
|
8586
9265
|
fail("path-map.json missing");
|
|
8587
9266
|
process.exit(1);
|
|
8588
9267
|
}
|
|
@@ -8604,14 +9283,14 @@ function resumeCmd(sessionId) {
|
|
|
8604
9283
|
console.log(`cd ${shQuote(hit.localPath)} && claude --resume ${shQuote(sessionId)}`);
|
|
8605
9284
|
}
|
|
8606
9285
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
8607
|
-
for (const dir of
|
|
8608
|
-
const candidate =
|
|
8609
|
-
if (
|
|
9286
|
+
for (const dir of readdirSync20(projectsRoot)) {
|
|
9287
|
+
const candidate = join65(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
9288
|
+
if (existsSync52(candidate)) return candidate;
|
|
8610
9289
|
}
|
|
8611
9290
|
return null;
|
|
8612
9291
|
}
|
|
8613
9292
|
function extractRecordedCwd(jsonlPath) {
|
|
8614
|
-
for (const line of
|
|
9293
|
+
for (const line of readFileSync20(jsonlPath, "utf8").split("\n")) {
|
|
8615
9294
|
if (!line.trim()) continue;
|
|
8616
9295
|
try {
|
|
8617
9296
|
const obj = JSON.parse(line);
|
|
@@ -8641,20 +9320,45 @@ function shQuote(s) {
|
|
|
8641
9320
|
|
|
8642
9321
|
// src/nomad.ts
|
|
8643
9322
|
init_utils();
|
|
9323
|
+
init_exit_codes();
|
|
9324
|
+
function handleTopLevelError(err) {
|
|
9325
|
+
if (isProcessExit(err)) throw err;
|
|
9326
|
+
if (err instanceof NomadFatal) {
|
|
9327
|
+
fail(err.message);
|
|
9328
|
+
process.exit(err.code);
|
|
9329
|
+
}
|
|
9330
|
+
const issuesUrl = package_default.bugs?.url ?? "https://github.com/funkadelic/claude-nomad/issues";
|
|
9331
|
+
handleCrash(err, process.argv, {
|
|
9332
|
+
version: package_default.version,
|
|
9333
|
+
platform: process.platform,
|
|
9334
|
+
issuesUrl
|
|
9335
|
+
});
|
|
9336
|
+
process.exit(EXIT.GENERIC_FAILURE);
|
|
9337
|
+
}
|
|
9338
|
+
process.on("uncaughtException", handleTopLevelError);
|
|
9339
|
+
process.on("unhandledRejection", handleTopLevelError);
|
|
8644
9340
|
var h = home();
|
|
8645
9341
|
if (!h) {
|
|
8646
9342
|
fail(
|
|
8647
9343
|
"could not determine home directory (HOME env unset and no uid mapping). Set HOME and retry."
|
|
8648
9344
|
);
|
|
8649
|
-
process.exit(
|
|
9345
|
+
process.exit(EXIT.GENERIC_FAILURE);
|
|
8650
9346
|
}
|
|
8651
9347
|
try {
|
|
9348
|
+
if (process.env.NOMAD_TEST_FORCE_CRASH) {
|
|
9349
|
+
throw new Error("forced test crash (NOMAD_TEST_FORCE_CRASH)");
|
|
9350
|
+
}
|
|
9351
|
+
if (process.env.NOMAD_TEST_FORCE_ASYNC_CRASH) {
|
|
9352
|
+
setImmediate(() => {
|
|
9353
|
+
void Promise.reject(new Error("forced test async crash (NOMAD_TEST_FORCE_ASYNC_CRASH)"));
|
|
9354
|
+
});
|
|
9355
|
+
}
|
|
8652
9356
|
const cmd = process.argv[2];
|
|
8653
9357
|
switch (cmd) {
|
|
8654
9358
|
case "--version":
|
|
8655
9359
|
if (process.argv.length !== 3) {
|
|
8656
9360
|
console.error("usage: nomad --version (no extra arguments)");
|
|
8657
|
-
process.exit(
|
|
9361
|
+
process.exit(EXIT.USAGE);
|
|
8658
9362
|
}
|
|
8659
9363
|
console.log(package_default.version);
|
|
8660
9364
|
break;
|
|
@@ -8662,7 +9366,7 @@ try {
|
|
|
8662
9366
|
const pullArgs = parsePullArgs(process.argv);
|
|
8663
9367
|
if (pullArgs === null) {
|
|
8664
9368
|
console.error("usage: nomad pull [--dry-run] [--force-remote]");
|
|
8665
|
-
process.exit(
|
|
9369
|
+
process.exit(EXIT.USAGE);
|
|
8666
9370
|
}
|
|
8667
9371
|
cmdPull({ dryRun: pullArgs.dryRun, forceRemote: pullArgs.forceRemote });
|
|
8668
9372
|
break;
|
|
@@ -8673,7 +9377,7 @@ try {
|
|
|
8673
9377
|
console.error(
|
|
8674
9378
|
"usage: nomad push [--dry-run] [--full-scan] [--redact-all] [--allow <rule>] [--allow-all]"
|
|
8675
9379
|
);
|
|
8676
|
-
process.exit(
|
|
9380
|
+
process.exit(EXIT.USAGE);
|
|
8677
9381
|
}
|
|
8678
9382
|
await cmdPush({
|
|
8679
9383
|
dryRun: pushArgs.dryRun,
|
|
@@ -8688,7 +9392,7 @@ try {
|
|
|
8688
9392
|
const syncArgs = parseSyncArgs(process.argv);
|
|
8689
9393
|
if (syncArgs === null) {
|
|
8690
9394
|
console.error("usage: nomad sync [--dry-run] [--verbose|--all|-v]");
|
|
8691
|
-
process.exit(
|
|
9395
|
+
process.exit(EXIT.USAGE);
|
|
8692
9396
|
}
|
|
8693
9397
|
await cmdSync({ dryRun: syncArgs.dryRun, verbose: syncArgs.verbose });
|
|
8694
9398
|
break;
|
|
@@ -8697,7 +9401,7 @@ try {
|
|
|
8697
9401
|
const initArgs = parseInitArgs(process.argv);
|
|
8698
9402
|
if (initArgs === null) {
|
|
8699
9403
|
console.error("usage: nomad init [--snapshot] [--keep-actions] [--repo <name>]");
|
|
8700
|
-
process.exit(
|
|
9404
|
+
process.exit(EXIT.USAGE);
|
|
8701
9405
|
}
|
|
8702
9406
|
const snapshot = isAlreadyInitialized(repoHome()) ? initArgs.snapshot : await resolveSnapshotChoice(initArgs.snapshot, claudeHome());
|
|
8703
9407
|
cmdInit({
|
|
@@ -8710,14 +9414,14 @@ try {
|
|
|
8710
9414
|
case "diff":
|
|
8711
9415
|
if (process.argv.length > 3) {
|
|
8712
9416
|
console.error("usage: nomad diff");
|
|
8713
|
-
process.exit(
|
|
9417
|
+
process.exit(EXIT.USAGE);
|
|
8714
9418
|
}
|
|
8715
9419
|
cmdDiff();
|
|
8716
9420
|
break;
|
|
8717
9421
|
case "update": {
|
|
8718
9422
|
if (process.argv.length !== 3) {
|
|
8719
9423
|
console.error("usage: nomad update");
|
|
8720
|
-
process.exit(
|
|
9424
|
+
process.exit(EXIT.USAGE);
|
|
8721
9425
|
}
|
|
8722
9426
|
cmdUpdate(package_default.version);
|
|
8723
9427
|
break;
|
|
@@ -8727,7 +9431,7 @@ try {
|
|
|
8727
9431
|
const sub = process.argv[4];
|
|
8728
9432
|
if (typeof name !== "string" || name.length === 0 || name.startsWith("-") || sub !== void 0 && (sub !== "--dry-run" || process.argv.length !== 5)) {
|
|
8729
9433
|
console.error("usage: nomad adopt <name> [--dry-run]");
|
|
8730
|
-
process.exit(
|
|
9434
|
+
process.exit(EXIT.USAGE);
|
|
8731
9435
|
}
|
|
8732
9436
|
cmdAdopt(name, { dryRun: sub === "--dry-run" });
|
|
8733
9437
|
break;
|
|
@@ -8736,7 +9440,7 @@ try {
|
|
|
8736
9440
|
const ejectArgs = parseEjectArgs(process.argv);
|
|
8737
9441
|
if (ejectArgs === null) {
|
|
8738
9442
|
console.error("usage: nomad eject [--dry-run]");
|
|
8739
|
-
process.exit(
|
|
9443
|
+
process.exit(EXIT.USAGE);
|
|
8740
9444
|
}
|
|
8741
9445
|
cmdEject({ dryRun: ejectArgs.dryRun });
|
|
8742
9446
|
break;
|
|
@@ -8745,7 +9449,7 @@ try {
|
|
|
8745
9449
|
const captureArgs = parseCaptureSettingsArgs(process.argv);
|
|
8746
9450
|
if (captureArgs === null) {
|
|
8747
9451
|
console.error("usage: nomad capture-settings [--host] [--dry-run] [--yes]");
|
|
8748
|
-
process.exit(
|
|
9452
|
+
process.exit(EXIT.USAGE);
|
|
8749
9453
|
}
|
|
8750
9454
|
await cmdCaptureSettings({
|
|
8751
9455
|
host: captureArgs.host,
|
|
@@ -8760,7 +9464,7 @@ try {
|
|
|
8760
9464
|
console.error(
|
|
8761
9465
|
"usage: nomad doctor [--check-shared] [--check-schema] [--check-remote] [--verbose|--all|-v] | --resume-cmd <session-id>"
|
|
8762
9466
|
);
|
|
8763
|
-
process.exit(
|
|
9467
|
+
process.exit(EXIT.USAGE);
|
|
8764
9468
|
} else if (parsed.kind === "resume") {
|
|
8765
9469
|
resumeCmd(parsed.id);
|
|
8766
9470
|
} else {
|
|
@@ -8777,7 +9481,7 @@ try {
|
|
|
8777
9481
|
const id = process.argv[3];
|
|
8778
9482
|
if (process.argv.length !== 4 || typeof id !== "string" || !/^\w[\w-]{0,127}$/.test(id)) {
|
|
8779
9483
|
console.error("usage: nomad drop-session <id>");
|
|
8780
|
-
process.exit(
|
|
9484
|
+
process.exit(EXIT.USAGE);
|
|
8781
9485
|
}
|
|
8782
9486
|
cmdDropSession(id);
|
|
8783
9487
|
break;
|
|
@@ -8786,7 +9490,7 @@ try {
|
|
|
8786
9490
|
const redactArgs = parseRedactArgs(process.argv);
|
|
8787
9491
|
if (redactArgs === null) {
|
|
8788
9492
|
console.error("usage: nomad redact <session-id> [--rule <rule-id>] [--dry-run]");
|
|
8789
|
-
process.exit(
|
|
9493
|
+
process.exit(EXIT.USAGE);
|
|
8790
9494
|
}
|
|
8791
9495
|
cmdRedact(redactArgs);
|
|
8792
9496
|
break;
|
|
@@ -8795,7 +9499,7 @@ try {
|
|
|
8795
9499
|
const allowArgs = parseAllowArgs(process.argv);
|
|
8796
9500
|
if (allowArgs === null) {
|
|
8797
9501
|
console.error("usage: nomad allow <fingerprint> [<fingerprint>...]");
|
|
8798
|
-
process.exit(
|
|
9502
|
+
process.exit(EXIT.USAGE);
|
|
8799
9503
|
}
|
|
8800
9504
|
cmdAllow(allowArgs);
|
|
8801
9505
|
break;
|
|
@@ -8804,19 +9508,15 @@ try {
|
|
|
8804
9508
|
const cleanArgs = parseCleanArgs(process.argv);
|
|
8805
9509
|
if (cleanArgs === null) {
|
|
8806
9510
|
console.error("usage: nomad clean --backups [--dry-run] [--older-than <dur> | --keep <N>]");
|
|
8807
|
-
process.exit(
|
|
9511
|
+
process.exit(EXIT.USAGE);
|
|
8808
9512
|
}
|
|
8809
9513
|
cmdClean(cleanArgs);
|
|
8810
9514
|
break;
|
|
8811
9515
|
}
|
|
8812
9516
|
default:
|
|
8813
9517
|
console.error(DEFAULT_HELP);
|
|
8814
|
-
process.exit(
|
|
9518
|
+
process.exit(EXIT.USAGE);
|
|
8815
9519
|
}
|
|
8816
9520
|
} catch (err) {
|
|
8817
|
-
|
|
8818
|
-
fail(err.message);
|
|
8819
|
-
process.exit(1);
|
|
8820
|
-
}
|
|
8821
|
-
throw err;
|
|
9521
|
+
handleTopLevelError(err);
|
|
8822
9522
|
}
|