claude-nomad 0.44.1 → 0.45.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 +21 -0
- package/CHANGELOG.md +17 -0
- package/README.md +3 -1
- package/dist/nomad.mjs +311 -177
- package/package.json +1 -1
package/.gitleaks.toml
CHANGED
|
@@ -39,6 +39,27 @@ paths = [
|
|
|
39
39
|
]
|
|
40
40
|
condition = "AND"
|
|
41
41
|
|
|
42
|
+
# Path-scoped: SSH public-key fingerprints in git signature-verification
|
|
43
|
+
# output (`git log --show-signature`, `git verify-commit`) land in session
|
|
44
|
+
# transcripts as `Good "git" signature for <signer> with ED25519 key
|
|
45
|
+
# SHA256:<43-char base64>`. The fingerprint is a hash of a PUBLIC key, not a
|
|
46
|
+
# credential, but its 43-char base64 body trips generic-api-key. Anchoring on
|
|
47
|
+
# the surrounding `with <keytype> key SHA256:` structure (via
|
|
48
|
+
# regexTarget = "line") keeps this from whitelisting a bare token: a real
|
|
49
|
+
# secret is never preceded by an SSH key-type label and the SHA256: scheme
|
|
50
|
+
# prefix. `condition = "AND"` plus the session-jsonl path scope double-locks
|
|
51
|
+
# it to synced transcripts.
|
|
52
|
+
[[allowlists]]
|
|
53
|
+
description = "claude-nomad: SSH public-key fingerprints (with <keytype> key SHA256:<b64>) in git signature output in synced session transcripts"
|
|
54
|
+
regexTarget = "line"
|
|
55
|
+
regexes = [
|
|
56
|
+
'''with (?:ED25519|ED25519-SK|ECDSA|ECDSA-SK|RSA|DSA) key SHA256:[A-Za-z0-9+/]{43}''',
|
|
57
|
+
]
|
|
58
|
+
paths = [
|
|
59
|
+
'''^shared/projects/[^/]+/.*\.jsonl$''',
|
|
60
|
+
]
|
|
61
|
+
condition = "AND"
|
|
62
|
+
|
|
42
63
|
# Path-scoped: SonarCloud issue-listing tool output (`gh`/sonar API dumps of
|
|
43
64
|
# the form `key: <20-char id>` immediately followed by `rule: <lang>:S<n>`)
|
|
44
65
|
# lands in session transcripts during PR reviews. The issue key is an opaque
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.45.0](https://github.com/funkadelic/claude-nomad/compare/v0.44.1...v0.45.0) (2026-06-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
* **doctor:** warn when settings.json drifts from the base+host merge ([#259](https://github.com/funkadelic/claude-nomad/issues/259)) ([6401732](https://github.com/funkadelic/claude-nomad/commit/6401732199aa9f883de78b15b323ec4dfecf8d3f))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
* **gitleaks:** allowlist SSH key fingerprints in signature output ([#257](https://github.com/funkadelic/claude-nomad/issues/257)) ([ca310aa](https://github.com/funkadelic/claude-nomad/commit/ca310aa7f3a23b152ed149224400d60332ada037))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Testing
|
|
17
|
+
|
|
18
|
+
* kill mutation survivors from the full Stryker sweep ([#260](https://github.com/funkadelic/claude-nomad/issues/260)) ([314e315](https://github.com/funkadelic/claude-nomad/commit/314e31587cb97e0fdf5b410587bb0ca64a2e46d5))
|
|
19
|
+
|
|
3
20
|
## [0.44.1](https://github.com/funkadelic/claude-nomad/compare/v0.44.0...v0.44.1) (2026-06-06)
|
|
4
21
|
|
|
5
22
|
|
package/README.md
CHANGED
|
@@ -37,7 +37,9 @@ survives different file paths and your secrets never ride along.
|
|
|
37
37
|
`--dry-run` on pull and push prints the plan without writing anything.
|
|
38
38
|
- **One command tells you what is wrong.** `nomad doctor` is a read-only health check: wedged sync
|
|
39
39
|
repo, broken hook references, hooks that would crash on session start because of a missing
|
|
40
|
-
`--preserve-symlinks-main` flag, version drift, oversized backup cache,
|
|
40
|
+
`--preserve-symlinks-main` flag, version drift, oversized backup cache, and settings drift (warns
|
|
41
|
+
when `~/.claude/settings.json` no longer matches the base+host merge nomad would write, the
|
|
42
|
+
silent-clobber case, with `nomad pull` as the fix), each with a fix hint.
|
|
41
43
|
- **Self-healing sync.** Every overwrite is backed up first, and `nomad pull --force-remote`
|
|
42
44
|
recovers a sync repo stuck mid-rebase while parking your stranded work on a branch, refusing
|
|
43
45
|
entirely if shared config is at risk.
|
package/dist/nomad.mjs
CHANGED
|
@@ -1400,8 +1400,8 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
|
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
1402
1402
|
// src/commands.doctor.ts
|
|
1403
|
-
import { existsSync as
|
|
1404
|
-
import { join as
|
|
1403
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
1404
|
+
import { join as join27 } from "node:path";
|
|
1405
1405
|
|
|
1406
1406
|
// src/commands.doctor.checks.repo.ts
|
|
1407
1407
|
init_color();
|
|
@@ -2686,17 +2686,150 @@ function reportPreserveSymlinksCheck(section2) {
|
|
|
2686
2686
|
}
|
|
2687
2687
|
}
|
|
2688
2688
|
|
|
2689
|
+
// src/commands.doctor.checks.settings-drift.ts
|
|
2690
|
+
init_color();
|
|
2691
|
+
import { existsSync as existsSync21, readFileSync as readFileSync7 } from "node:fs";
|
|
2692
|
+
import { join as join25 } from "node:path";
|
|
2693
|
+
init_config();
|
|
2694
|
+
init_utils_json();
|
|
2695
|
+
function arraysEqual(a, b) {
|
|
2696
|
+
if (a.length !== b.length) return false;
|
|
2697
|
+
for (let i = 0; i < a.length; i++) {
|
|
2698
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
2699
|
+
}
|
|
2700
|
+
return true;
|
|
2701
|
+
}
|
|
2702
|
+
function objectsEqual(a, b) {
|
|
2703
|
+
const aKeys = Object.keys(a);
|
|
2704
|
+
const bKeys = Object.keys(b);
|
|
2705
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
2706
|
+
for (const k of aKeys) {
|
|
2707
|
+
if (!Object.hasOwn(b, k)) return false;
|
|
2708
|
+
if (!deepEqual(a[k], b[k])) return false;
|
|
2709
|
+
}
|
|
2710
|
+
return true;
|
|
2711
|
+
}
|
|
2712
|
+
function deepEqual(a, b) {
|
|
2713
|
+
if (a === b) return true;
|
|
2714
|
+
if (a === null || b === null) return false;
|
|
2715
|
+
if (Array.isArray(a) && Array.isArray(b)) return arraysEqual(a, b);
|
|
2716
|
+
if (Array.isArray(a) || Array.isArray(b)) return false;
|
|
2717
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
2718
|
+
return objectsEqual(a, b);
|
|
2719
|
+
}
|
|
2720
|
+
return false;
|
|
2721
|
+
}
|
|
2722
|
+
function diffMergedSettings(merged, settings) {
|
|
2723
|
+
const missing = [];
|
|
2724
|
+
const changed = [];
|
|
2725
|
+
const extra = [];
|
|
2726
|
+
const settingsKeys = new Set(Object.keys(settings));
|
|
2727
|
+
for (const key of Object.keys(merged)) {
|
|
2728
|
+
if (!settingsKeys.has(key)) {
|
|
2729
|
+
missing.push(key);
|
|
2730
|
+
} else if (!deepEqual(merged[key], settings[key])) {
|
|
2731
|
+
changed.push(key);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
const mergedKeys = new Set(Object.keys(merged));
|
|
2735
|
+
for (const key of Object.keys(settings)) {
|
|
2736
|
+
if (!mergedKeys.has(key)) extra.push(key);
|
|
2737
|
+
}
|
|
2738
|
+
const collator = (a, b) => a.localeCompare(b, "en");
|
|
2739
|
+
return {
|
|
2740
|
+
missing: missing.toSorted(collator),
|
|
2741
|
+
changed: changed.toSorted(collator),
|
|
2742
|
+
extra: extra.toSorted(collator)
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
function tryReadJson(filePath) {
|
|
2746
|
+
try {
|
|
2747
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
2748
|
+
const parsed = JSON.parse(raw);
|
|
2749
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
2750
|
+
return parsed;
|
|
2751
|
+
} catch {
|
|
2752
|
+
return null;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
function reportSettingsDriftCheck(section2) {
|
|
2756
|
+
const claude = claudeHome();
|
|
2757
|
+
const repo = repoHome();
|
|
2758
|
+
const host = HOST;
|
|
2759
|
+
const settingsPath = join25(claude, "settings.json");
|
|
2760
|
+
const basePath = join25(repo, "shared", "settings.base.json");
|
|
2761
|
+
const hostPath = join25(repo, "hosts", `${host}.json`);
|
|
2762
|
+
if (!existsSync21(settingsPath)) {
|
|
2763
|
+
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
if (!existsSync21(basePath)) {
|
|
2767
|
+
addItem(
|
|
2768
|
+
section2,
|
|
2769
|
+
`${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
|
|
2770
|
+
);
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
const base = tryReadJson(basePath);
|
|
2774
|
+
if (base === null) {
|
|
2775
|
+
addItem(
|
|
2776
|
+
section2,
|
|
2777
|
+
`${dim(infoGlyph)} shared/settings.base.json unparseable; skipping merge-drift check`
|
|
2778
|
+
);
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
const settings = tryReadJson(settingsPath);
|
|
2782
|
+
if (settings === null) {
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
const hostExists = existsSync21(hostPath);
|
|
2786
|
+
const hostObj = hostExists ? tryReadJson(hostPath) : null;
|
|
2787
|
+
if (hostExists && hostObj === null) {
|
|
2788
|
+
addItem(
|
|
2789
|
+
section2,
|
|
2790
|
+
`${yellow(warnGlyph)} hosts/${host}.json unparseable; 'nomad pull' will fail (fix the host file)`
|
|
2791
|
+
);
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
const merged = deepMerge(base, hostObj ?? {});
|
|
2795
|
+
const { missing, changed, extra } = diffMergedSettings(merged, settings);
|
|
2796
|
+
emitDriftRows(section2, missing, changed, extra, host, hostExists);
|
|
2797
|
+
}
|
|
2798
|
+
function emitDriftRows(section2, missing, changed, extra, host, hostFileExists) {
|
|
2799
|
+
if (missing.length > 0) {
|
|
2800
|
+
addItem(
|
|
2801
|
+
section2,
|
|
2802
|
+
`${yellow(warnGlyph)} settings.json drift: merged keys missing locally: ${missing.join(", ")} (external writer clobbered settings.json; run 'nomad pull')`
|
|
2803
|
+
);
|
|
2804
|
+
}
|
|
2805
|
+
if (changed.length > 0) {
|
|
2806
|
+
addItem(
|
|
2807
|
+
section2,
|
|
2808
|
+
`${yellow(warnGlyph)} settings.json drift: merged keys with changed values: ${changed.join(", ")} (run 'nomad pull')`
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
if (extra.length > 0 && hostFileExists) {
|
|
2812
|
+
addItem(
|
|
2813
|
+
section2,
|
|
2814
|
+
`${dim(infoGlyph)} settings.json has ${extra.length} local-only key(s) not in base+host merge: ${extra.join(", ")} (promotion candidates for shared/settings.base.json or hosts/${host}.json)`
|
|
2815
|
+
);
|
|
2816
|
+
}
|
|
2817
|
+
if (missing.length === 0 && changed.length === 0 && extra.length === 0) {
|
|
2818
|
+
addItem(section2, `${green(okGlyph)} settings.json matches base+host merge`);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2689
2822
|
// src/commands.doctor.ts
|
|
2690
2823
|
init_config();
|
|
2691
2824
|
|
|
2692
2825
|
// src/commands.doctor.engine.ts
|
|
2693
2826
|
init_color();
|
|
2694
|
-
import { readFileSync as
|
|
2827
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
2695
2828
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2696
2829
|
|
|
2697
2830
|
// src/commands.doctor.version.ts
|
|
2698
2831
|
init_color();
|
|
2699
|
-
import { readFileSync as
|
|
2832
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2700
2833
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2701
2834
|
init_config();
|
|
2702
2835
|
var STRICT_SEMVER = /^\d+\.\d+\.\d+$/;
|
|
@@ -2713,7 +2846,7 @@ function compareSemver(a, b) {
|
|
|
2713
2846
|
function readLocalVersion() {
|
|
2714
2847
|
try {
|
|
2715
2848
|
const pkgPath = fileURLToPath2(new URL("../package.json", import.meta.url));
|
|
2716
|
-
const parsed = JSON.parse(
|
|
2849
|
+
const parsed = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
2717
2850
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
2718
2851
|
return parsed.version;
|
|
2719
2852
|
}
|
|
@@ -2772,7 +2905,7 @@ function parseMinVersion(spec) {
|
|
|
2772
2905
|
function readEnginesNode() {
|
|
2773
2906
|
try {
|
|
2774
2907
|
const pkgPath = fileURLToPath3(new URL("../package.json", import.meta.url));
|
|
2775
|
-
const parsed = JSON.parse(
|
|
2908
|
+
const parsed = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
2776
2909
|
const node = parsed.engines?.node;
|
|
2777
2910
|
if (typeof node === "string" && node.length > 0) return node;
|
|
2778
2911
|
return null;
|
|
@@ -2801,8 +2934,8 @@ function reportNodeEngineCheck(section2) {
|
|
|
2801
2934
|
// src/commands.doctor.gitleaks-version.ts
|
|
2802
2935
|
init_color();
|
|
2803
2936
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
2804
|
-
import { existsSync as
|
|
2805
|
-
import { join as
|
|
2937
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
2938
|
+
import { join as join26 } from "node:path";
|
|
2806
2939
|
init_config();
|
|
2807
2940
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
2808
2941
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -2811,7 +2944,7 @@ function majorMinorOf(value) {
|
|
|
2811
2944
|
return m === null ? null : [m[1], m[2]];
|
|
2812
2945
|
}
|
|
2813
2946
|
function readGitleaksVersion(run, tomlExists) {
|
|
2814
|
-
const tomlPath =
|
|
2947
|
+
const tomlPath = join26(repoHome(), ".gitleaks.toml");
|
|
2815
2948
|
const args = ["version"];
|
|
2816
2949
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
2817
2950
|
try {
|
|
@@ -2823,7 +2956,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
2823
2956
|
return null;
|
|
2824
2957
|
}
|
|
2825
2958
|
}
|
|
2826
|
-
function reportGitleaksVersionCheck(section2, run = execFileSync7, tomlExists =
|
|
2959
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync7, tomlExists = existsSync22) {
|
|
2827
2960
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
2828
2961
|
if (raw === null) return;
|
|
2829
2962
|
const local = majorMinorOf(raw);
|
|
@@ -3023,8 +3156,8 @@ function cmdDoctor(opts = {}) {
|
|
|
3023
3156
|
reportHostAndPaths(host);
|
|
3024
3157
|
reportRepoState(host);
|
|
3025
3158
|
const links = section("Shared links");
|
|
3026
|
-
const mapPath =
|
|
3027
|
-
const rawMap =
|
|
3159
|
+
const mapPath = join27(repoHome(), "path-map.json");
|
|
3160
|
+
const rawMap = existsSync23(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
3028
3161
|
const map = rawMap ?? { projects: {} };
|
|
3029
3162
|
reportSharedLinks(links, map);
|
|
3030
3163
|
const hooksScan = section("Hook targets");
|
|
@@ -3035,6 +3168,7 @@ function cmdDoctor(opts = {}) {
|
|
|
3035
3168
|
const base = loadBaseSettings(settings);
|
|
3036
3169
|
const parsedSettings = loadAndReportSettings(settings);
|
|
3037
3170
|
reportHostOverrides(settings, base, parsedSettings);
|
|
3171
|
+
reportSettingsDriftCheck(settings);
|
|
3038
3172
|
const pathMap = section("Path map");
|
|
3039
3173
|
reportPathMap(pathMap);
|
|
3040
3174
|
const neverSync = section("Never-sync");
|
|
@@ -3078,8 +3212,8 @@ function cmdDoctor(opts = {}) {
|
|
|
3078
3212
|
// src/commands.drop-session.ts
|
|
3079
3213
|
init_config();
|
|
3080
3214
|
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
3081
|
-
import { existsSync as
|
|
3082
|
-
import { join as
|
|
3215
|
+
import { existsSync as existsSync25, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
|
|
3216
|
+
import { join as join30, relative as relative4 } from "node:path";
|
|
3083
3217
|
|
|
3084
3218
|
// src/commands.drop-session.git.ts
|
|
3085
3219
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
@@ -3121,8 +3255,8 @@ function isInIndex(rel, repo) {
|
|
|
3121
3255
|
init_config();
|
|
3122
3256
|
init_utils();
|
|
3123
3257
|
init_utils_json();
|
|
3124
|
-
import { existsSync as
|
|
3125
|
-
import { join as
|
|
3258
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
3259
|
+
import { join as join28 } from "node:path";
|
|
3126
3260
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
3127
3261
|
function reportScrubHint(id, matches) {
|
|
3128
3262
|
const live = resolveLiveTranscript(id, matches);
|
|
@@ -3138,8 +3272,8 @@ function reportScrubHint(id, matches) {
|
|
|
3138
3272
|
}
|
|
3139
3273
|
function resolveLiveTranscript(id, matches) {
|
|
3140
3274
|
try {
|
|
3141
|
-
const mapPath =
|
|
3142
|
-
if (!
|
|
3275
|
+
const mapPath = join28(repoHome(), "path-map.json");
|
|
3276
|
+
if (!existsSync24(mapPath)) return null;
|
|
3143
3277
|
const projects = readJson(mapPath).projects;
|
|
3144
3278
|
const claude = claudeHome();
|
|
3145
3279
|
for (const rel of matches) {
|
|
@@ -3147,8 +3281,8 @@ function resolveLiveTranscript(id, matches) {
|
|
|
3147
3281
|
if (logical === void 0) continue;
|
|
3148
3282
|
const abs = projects[logical]?.[HOST];
|
|
3149
3283
|
if (abs === void 0) continue;
|
|
3150
|
-
const live =
|
|
3151
|
-
if (
|
|
3284
|
+
const live = join28(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
3285
|
+
if (existsSync24(live)) return live;
|
|
3152
3286
|
}
|
|
3153
3287
|
return null;
|
|
3154
3288
|
} catch {
|
|
@@ -3162,10 +3296,10 @@ init_utils();
|
|
|
3162
3296
|
// src/utils.lockfile.ts
|
|
3163
3297
|
init_config();
|
|
3164
3298
|
init_utils();
|
|
3165
|
-
import { closeSync as closeSync3, mkdirSync as mkdirSync5, openSync as openSync3, readFileSync as
|
|
3166
|
-
import { dirname as dirname4, join as
|
|
3299
|
+
import { closeSync as closeSync3, mkdirSync as mkdirSync5, openSync as openSync3, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3300
|
+
import { dirname as dirname4, join as join29 } from "node:path";
|
|
3167
3301
|
function lockFilePath() {
|
|
3168
|
-
return
|
|
3302
|
+
return join29(home(), ".cache", "claude-nomad", "nomad.lock");
|
|
3169
3303
|
}
|
|
3170
3304
|
function acquireLock(verb) {
|
|
3171
3305
|
const lp = lockFilePath();
|
|
@@ -3208,7 +3342,7 @@ function releaseLock(handle) {
|
|
|
3208
3342
|
function unlinkIfSamePid(expectedPidStr, lp) {
|
|
3209
3343
|
let current;
|
|
3210
3344
|
try {
|
|
3211
|
-
current =
|
|
3345
|
+
current = readFileSync10(lp, "utf8").trim();
|
|
3212
3346
|
} catch {
|
|
3213
3347
|
return false;
|
|
3214
3348
|
}
|
|
@@ -3223,7 +3357,7 @@ function unlinkIfSamePid(expectedPidStr, lp) {
|
|
|
3223
3357
|
function checkStaleAndRetry(verb, lp) {
|
|
3224
3358
|
let pidStr;
|
|
3225
3359
|
try {
|
|
3226
|
-
pidStr =
|
|
3360
|
+
pidStr = readFileSync10(lp, "utf8").trim();
|
|
3227
3361
|
} catch {
|
|
3228
3362
|
pidStr = "";
|
|
3229
3363
|
}
|
|
@@ -3279,12 +3413,12 @@ function cmdDropSession(id) {
|
|
|
3279
3413
|
process.exit(1);
|
|
3280
3414
|
}
|
|
3281
3415
|
const repo = repoHome();
|
|
3282
|
-
if (!
|
|
3416
|
+
if (!existsSync25(repo)) die(`repo not cloned at ${repo}`);
|
|
3283
3417
|
const handle = acquireLock("drop-session");
|
|
3284
3418
|
if (handle === null) process.exit(0);
|
|
3285
3419
|
try {
|
|
3286
|
-
const repoProjects =
|
|
3287
|
-
if (!
|
|
3420
|
+
const repoProjects = join30(repo, "shared", "projects");
|
|
3421
|
+
if (!existsSync25(repoProjects)) {
|
|
3288
3422
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
3289
3423
|
}
|
|
3290
3424
|
const matches = collectMatches(repoProjects, id, repo);
|
|
@@ -3306,12 +3440,12 @@ function cmdDropSession(id) {
|
|
|
3306
3440
|
function collectMatches(repoProjects, id, repo) {
|
|
3307
3441
|
const matches = [];
|
|
3308
3442
|
for (const logical of readdirSync9(repoProjects)) {
|
|
3309
|
-
const candidate =
|
|
3310
|
-
if (
|
|
3443
|
+
const candidate = join30(repoProjects, logical, `${id}.jsonl`);
|
|
3444
|
+
if (existsSync25(candidate)) {
|
|
3311
3445
|
matches.push(relative4(repo, candidate));
|
|
3312
3446
|
}
|
|
3313
|
-
const dir =
|
|
3314
|
-
if (
|
|
3447
|
+
const dir = join30(repoProjects, logical, id);
|
|
3448
|
+
if (existsSync25(dir) && statSync5(dir).isDirectory()) {
|
|
3315
3449
|
const dirRel = relative4(repo, dir);
|
|
3316
3450
|
const staged = expandStagedDir(dirRel, repo);
|
|
3317
3451
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -3347,19 +3481,19 @@ function unstageOne(rel, repo) {
|
|
|
3347
3481
|
|
|
3348
3482
|
// src/commands.redact.ts
|
|
3349
3483
|
init_config();
|
|
3350
|
-
import { existsSync as
|
|
3351
|
-
import { dirname as dirname5, join as
|
|
3484
|
+
import { existsSync as existsSync27, statSync as statSync7 } from "node:fs";
|
|
3485
|
+
import { dirname as dirname5, join as join32 } from "node:path";
|
|
3352
3486
|
|
|
3353
3487
|
// src/commands.redact.subtree.ts
|
|
3354
|
-
import { existsSync as
|
|
3355
|
-
import { join as
|
|
3488
|
+
import { existsSync as existsSync26, lstatSync as lstatSync7, readFileSync as readFileSync11, readdirSync as readdirSync10, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3489
|
+
import { join as join31 } from "node:path";
|
|
3356
3490
|
init_utils_fs();
|
|
3357
3491
|
function collectFiles(dir, out) {
|
|
3358
|
-
if (!
|
|
3492
|
+
if (!existsSync26(dir)) return;
|
|
3359
3493
|
const st = lstatSync7(dir);
|
|
3360
3494
|
if (!st.isDirectory()) return;
|
|
3361
3495
|
for (const entry of readdirSync10(dir)) {
|
|
3362
|
-
const abs =
|
|
3496
|
+
const abs = join31(dir, entry);
|
|
3363
3497
|
const lst = lstatSync7(abs);
|
|
3364
3498
|
if (lst.isSymbolicLink()) continue;
|
|
3365
3499
|
if (lst.isDirectory()) {
|
|
@@ -3396,7 +3530,7 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
3396
3530
|
if (!dryRun && total > 0) {
|
|
3397
3531
|
for (const { path: filePath, findings } of dirty) {
|
|
3398
3532
|
backupBeforeWrite(filePath, ts);
|
|
3399
|
-
writeFileSync4(filePath, applyRedactions(
|
|
3533
|
+
writeFileSync4(filePath, applyRedactions(readFileSync11(filePath, "utf8"), findings), "utf8");
|
|
3400
3534
|
}
|
|
3401
3535
|
}
|
|
3402
3536
|
return { total, dirty };
|
|
@@ -3409,15 +3543,15 @@ init_utils_json();
|
|
|
3409
3543
|
init_utils();
|
|
3410
3544
|
function resolveLiveTranscript2(id) {
|
|
3411
3545
|
try {
|
|
3412
|
-
const mapPath =
|
|
3413
|
-
if (!
|
|
3546
|
+
const mapPath = join32(repoHome(), "path-map.json");
|
|
3547
|
+
if (!existsSync27(mapPath)) return null;
|
|
3414
3548
|
const projects = readJson(mapPath).projects;
|
|
3415
3549
|
const claude = claudeHome();
|
|
3416
3550
|
for (const hostMap of Object.values(projects)) {
|
|
3417
3551
|
const abs = hostMap[HOST];
|
|
3418
3552
|
if (abs === void 0) continue;
|
|
3419
|
-
const live =
|
|
3420
|
-
if (
|
|
3553
|
+
const live = join32(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
3554
|
+
if (existsSync27(live)) return live;
|
|
3421
3555
|
}
|
|
3422
3556
|
return null;
|
|
3423
3557
|
} catch {
|
|
@@ -3437,17 +3571,17 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
3437
3571
|
}
|
|
3438
3572
|
const repo = repoHome();
|
|
3439
3573
|
const backup = backupBase();
|
|
3440
|
-
if (!
|
|
3574
|
+
if (!existsSync27(repo)) die(`repo not cloned at ${repo}`);
|
|
3441
3575
|
const handle = acquireLock("redact");
|
|
3442
3576
|
if (handle === null) process.exit(0);
|
|
3443
3577
|
try {
|
|
3444
3578
|
const localPath = resolveLiveTranscript2(id);
|
|
3445
|
-
if (localPath === null || !
|
|
3579
|
+
if (localPath === null || !existsSync27(localPath)) {
|
|
3446
3580
|
fail(`could not resolve local transcript for session ${id} on this host`);
|
|
3447
3581
|
process.exitCode = 1;
|
|
3448
3582
|
return;
|
|
3449
3583
|
}
|
|
3450
|
-
const sessionDir =
|
|
3584
|
+
const sessionDir = join32(dirname5(localPath), id);
|
|
3451
3585
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
3452
3586
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync7(p).mtimeMs);
|
|
3453
3587
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -3500,8 +3634,8 @@ ${lines}`);
|
|
|
3500
3634
|
}
|
|
3501
3635
|
|
|
3502
3636
|
// src/commands.pull.ts
|
|
3503
|
-
import { existsSync as
|
|
3504
|
-
import { join as
|
|
3637
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync8 } from "node:fs";
|
|
3638
|
+
import { join as join41 } from "node:path";
|
|
3505
3639
|
|
|
3506
3640
|
// src/commands.push.sections.ts
|
|
3507
3641
|
init_color();
|
|
@@ -3589,8 +3723,8 @@ init_config();
|
|
|
3589
3723
|
|
|
3590
3724
|
// src/extras-sync.ts
|
|
3591
3725
|
init_config();
|
|
3592
|
-
import { existsSync as
|
|
3593
|
-
import { join as
|
|
3726
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
3727
|
+
import { join as join35 } from "node:path";
|
|
3594
3728
|
|
|
3595
3729
|
// src/extras-sync.diff.ts
|
|
3596
3730
|
init_utils();
|
|
@@ -3629,8 +3763,8 @@ function listDivergingFiles(a, b) {
|
|
|
3629
3763
|
|
|
3630
3764
|
// src/extras-sync.core.ts
|
|
3631
3765
|
init_config();
|
|
3632
|
-
import { cpSync as cpSync5, existsSync as
|
|
3633
|
-
import { join as
|
|
3766
|
+
import { cpSync as cpSync5, existsSync as existsSync28, rmSync as rmSync8 } from "node:fs";
|
|
3767
|
+
import { join as join33 } from "node:path";
|
|
3634
3768
|
|
|
3635
3769
|
// src/extras-sync.guards.ts
|
|
3636
3770
|
init_utils();
|
|
@@ -3654,9 +3788,9 @@ init_utils();
|
|
|
3654
3788
|
init_utils_json();
|
|
3655
3789
|
function loadValidatedExtras(opts) {
|
|
3656
3790
|
const repo = repoHome();
|
|
3657
|
-
const mapPath =
|
|
3658
|
-
const repoExtras =
|
|
3659
|
-
if (!
|
|
3791
|
+
const mapPath = join33(repo, "path-map.json");
|
|
3792
|
+
const repoExtras = join33(repo, "shared", "extras");
|
|
3793
|
+
if (!existsSync28(mapPath) || opts.requireRepoExtras === true && !existsSync28(repoExtras)) {
|
|
3660
3794
|
if (opts.missingMsg !== void 0) log(opts.missingMsg);
|
|
3661
3795
|
return null;
|
|
3662
3796
|
}
|
|
@@ -3698,8 +3832,8 @@ init_utils_json();
|
|
|
3698
3832
|
|
|
3699
3833
|
// src/extras-sync.remap.ts
|
|
3700
3834
|
init_config();
|
|
3701
|
-
import { existsSync as
|
|
3702
|
-
import { join as
|
|
3835
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3836
|
+
import { join as join34 } from "node:path";
|
|
3703
3837
|
init_utils_fs();
|
|
3704
3838
|
function runExtrasOp(v, dryRun, paths, backup) {
|
|
3705
3839
|
const counts = { unmapped: 0, skipped: 0 };
|
|
@@ -3707,7 +3841,7 @@ function runExtrasOp(v, dryRun, paths, backup) {
|
|
|
3707
3841
|
const would = [];
|
|
3708
3842
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
3709
3843
|
const { src, dst } = paths(t);
|
|
3710
|
-
if (!
|
|
3844
|
+
if (!existsSync29(src)) continue;
|
|
3711
3845
|
const item2 = `${t.logical}/${t.dirname}`;
|
|
3712
3846
|
if (dryRun) {
|
|
3713
3847
|
would.push(item2);
|
|
@@ -3724,14 +3858,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
3724
3858
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
3725
3859
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
3726
3860
|
const repo = repoHome();
|
|
3727
|
-
const repoExtras =
|
|
3861
|
+
const repoExtras = join34(repo, "shared", "extras");
|
|
3728
3862
|
if (!dryRun) mkdirSync6(repoExtras, { recursive: true });
|
|
3729
3863
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
3730
3864
|
v,
|
|
3731
3865
|
dryRun,
|
|
3732
3866
|
({ localRoot, logical, dirname: dirname7 }) => ({
|
|
3733
|
-
src:
|
|
3734
|
-
dst:
|
|
3867
|
+
src: join34(localRoot, dirname7),
|
|
3868
|
+
dst: join34(repoExtras, logical, dirname7)
|
|
3735
3869
|
}),
|
|
3736
3870
|
(dst) => backupRepoWrite(dst, ts, repo)
|
|
3737
3871
|
);
|
|
@@ -3744,13 +3878,13 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
3744
3878
|
missingMsg: "no path-map or repo extras dir; skipping extras remap"
|
|
3745
3879
|
});
|
|
3746
3880
|
if (v === null) return { unmapped: 0, skipped: 0, pulled: [], wouldPull: [] };
|
|
3747
|
-
const repoExtras =
|
|
3881
|
+
const repoExtras = join34(repoHome(), "shared", "extras");
|
|
3748
3882
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
3749
3883
|
v,
|
|
3750
3884
|
dryRun,
|
|
3751
3885
|
({ localRoot, logical, dirname: dirname7 }) => ({
|
|
3752
|
-
src:
|
|
3753
|
-
dst:
|
|
3886
|
+
src: join34(repoExtras, logical, dirname7),
|
|
3887
|
+
dst: join34(localRoot, dirname7)
|
|
3754
3888
|
}),
|
|
3755
3889
|
// Snapshot the host-side dst BEFORE copyExtras clobbers it. Anchor on
|
|
3756
3890
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -3764,15 +3898,15 @@ function divergenceCheckExtras(ts) {
|
|
|
3764
3898
|
const v = loadValidatedExtras({});
|
|
3765
3899
|
if (v === null) return;
|
|
3766
3900
|
const counts = { unmapped: 0, skipped: 0 };
|
|
3767
|
-
const backupRoot =
|
|
3901
|
+
const backupRoot = join35(backupBase(), ts, "extras");
|
|
3768
3902
|
const repo = repoHome();
|
|
3769
3903
|
for (const { logical, localRoot, dirname: dirname7 } of eachExtrasTarget(v, counts)) {
|
|
3770
|
-
const local =
|
|
3771
|
-
const repoEntry =
|
|
3772
|
-
if (!
|
|
3904
|
+
const local = join35(localRoot, dirname7);
|
|
3905
|
+
const repoEntry = join35(repo, "shared", "extras", logical, dirname7);
|
|
3906
|
+
if (!existsSync30(local) || !existsSync30(repoEntry)) continue;
|
|
3773
3907
|
const diff = listDivergingFiles(local, repoEntry);
|
|
3774
3908
|
if (diff.length === 0) continue;
|
|
3775
|
-
const projectBackupRoot =
|
|
3909
|
+
const projectBackupRoot = join35(backupRoot, encodePath(localRoot));
|
|
3776
3910
|
warn(
|
|
3777
3911
|
`local ${dirname7} for ${logical} diverges from origin in ${diff.length} file(s); next remapExtrasPull will overwrite them (backups at ${projectBackupRoot}/)`
|
|
3778
3912
|
);
|
|
@@ -3785,8 +3919,8 @@ init_config();
|
|
|
3785
3919
|
init_utils();
|
|
3786
3920
|
init_utils_fs();
|
|
3787
3921
|
init_utils_json();
|
|
3788
|
-
import { existsSync as
|
|
3789
|
-
import { join as
|
|
3922
|
+
import { existsSync as existsSync31, lstatSync as lstatSync8, rmSync as rmSync9 } from "node:fs";
|
|
3923
|
+
import { join as join36 } from "node:path";
|
|
3790
3924
|
function emitAutoMove(onPreview, linkPath, ts, name) {
|
|
3791
3925
|
if (onPreview) {
|
|
3792
3926
|
onPreview({ kind: "auto-move", from: linkPath, to: `backup/${ts}/${name}` });
|
|
@@ -3807,11 +3941,11 @@ function applySharedLinks(ts, map, opts = {}) {
|
|
|
3807
3941
|
const repo = repoHome();
|
|
3808
3942
|
const linkNames = allSharedLinks(map);
|
|
3809
3943
|
for (const name of linkNames) {
|
|
3810
|
-
const linkPath =
|
|
3811
|
-
const target =
|
|
3812
|
-
if (!
|
|
3944
|
+
const linkPath = join36(claude, name);
|
|
3945
|
+
const target = join36(repo, "shared", name);
|
|
3946
|
+
if (!existsSync31(linkPath)) continue;
|
|
3813
3947
|
if (lstatSync8(linkPath).isSymbolicLink()) continue;
|
|
3814
|
-
if (!
|
|
3948
|
+
if (!existsSync31(target)) continue;
|
|
3815
3949
|
if (dryRun) {
|
|
3816
3950
|
emitAutoMove(opts.onPreview, linkPath, ts, name);
|
|
3817
3951
|
continue;
|
|
@@ -3820,30 +3954,30 @@ function applySharedLinks(ts, map, opts = {}) {
|
|
|
3820
3954
|
rmSync9(linkPath, { recursive: true, force: true });
|
|
3821
3955
|
}
|
|
3822
3956
|
for (const name of linkNames) {
|
|
3823
|
-
const target =
|
|
3824
|
-
if (!
|
|
3957
|
+
const target = join36(repo, "shared", name);
|
|
3958
|
+
if (!existsSync31(target)) continue;
|
|
3825
3959
|
if (dryRun) {
|
|
3826
|
-
emitCreate(opts.onPreview,
|
|
3960
|
+
emitCreate(opts.onPreview, join36(claude, name), target);
|
|
3827
3961
|
continue;
|
|
3828
3962
|
}
|
|
3829
|
-
ensureSymlink(
|
|
3963
|
+
ensureSymlink(join36(claude, name), target);
|
|
3830
3964
|
}
|
|
3831
3965
|
}
|
|
3832
3966
|
function regenerateSettings(ts, opts = {}) {
|
|
3833
3967
|
const dryRun = opts.dryRun === true;
|
|
3834
3968
|
const repo = repoHome();
|
|
3835
3969
|
const claude = claudeHome();
|
|
3836
|
-
const basePath =
|
|
3837
|
-
const hostPath =
|
|
3838
|
-
if (!
|
|
3970
|
+
const basePath = join36(repo, "shared", "settings.base.json");
|
|
3971
|
+
const hostPath = join36(repo, "hosts", `${HOST}.json`);
|
|
3972
|
+
if (!existsSync31(basePath)) {
|
|
3839
3973
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
3840
3974
|
}
|
|
3841
3975
|
const base = readJson(basePath);
|
|
3842
|
-
const hasOverrides =
|
|
3976
|
+
const hasOverrides = existsSync31(hostPath);
|
|
3843
3977
|
const overrides = hasOverrides ? readJson(hostPath) : {};
|
|
3844
3978
|
const merged = deepMerge(base, overrides);
|
|
3845
|
-
const settingsPath =
|
|
3846
|
-
if (!hasOverrides &&
|
|
3979
|
+
const settingsPath = join36(claude, "settings.json");
|
|
3980
|
+
if (!hasOverrides && existsSync31(settingsPath)) {
|
|
3847
3981
|
try {
|
|
3848
3982
|
const existing = readJson(settingsPath);
|
|
3849
3983
|
const baseKeys = new Set(Object.keys(base));
|
|
@@ -3869,8 +4003,8 @@ function regenerateSettings(ts, opts = {}) {
|
|
|
3869
4003
|
|
|
3870
4004
|
// src/preview.ts
|
|
3871
4005
|
init_config();
|
|
3872
|
-
import { existsSync as
|
|
3873
|
-
import { join as
|
|
4006
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
4007
|
+
import { join as join37 } from "node:path";
|
|
3874
4008
|
|
|
3875
4009
|
// node_modules/diff/libesm/diff/base.js
|
|
3876
4010
|
var Diff = class {
|
|
@@ -4156,7 +4290,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
4156
4290
|
return lines.join("\n");
|
|
4157
4291
|
}
|
|
4158
4292
|
function readJsonOrNull(path) {
|
|
4159
|
-
if (!
|
|
4293
|
+
if (!existsSync32(path)) return null;
|
|
4160
4294
|
try {
|
|
4161
4295
|
return readJson(path);
|
|
4162
4296
|
} catch {
|
|
@@ -4170,12 +4304,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
4170
4304
|
}
|
|
4171
4305
|
const notes = [];
|
|
4172
4306
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
4173
|
-
if (hostOverrides === null &&
|
|
4307
|
+
if (hostOverrides === null && existsSync32(hostPath)) {
|
|
4174
4308
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
4175
4309
|
}
|
|
4176
4310
|
const merged = deepMerge(base, hostOverrides ?? {});
|
|
4177
4311
|
const current = readJsonOrNull(settingsPath);
|
|
4178
|
-
if (current === null &&
|
|
4312
|
+
if (current === null && existsSync32(settingsPath)) {
|
|
4179
4313
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
4180
4314
|
}
|
|
4181
4315
|
const rawEqual = JSON.stringify(current ?? {}, null, 2) === JSON.stringify(merged, null, 2);
|
|
@@ -4215,9 +4349,9 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
4215
4349
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
4216
4350
|
});
|
|
4217
4351
|
const settingsResult = previewSettings(
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4352
|
+
join37(repo, "shared", "settings.base.json"),
|
|
4353
|
+
join37(repo, "hosts", `${HOST}.json`),
|
|
4354
|
+
join37(claude, "settings.json")
|
|
4221
4355
|
);
|
|
4222
4356
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
4223
4357
|
const sessions = section("Sessions");
|
|
@@ -4233,21 +4367,21 @@ function computePreview(ts, map, verb = "pull") {
|
|
|
4233
4367
|
|
|
4234
4368
|
// src/spinner.ts
|
|
4235
4369
|
init_color();
|
|
4236
|
-
import { existsSync as
|
|
4370
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
4237
4371
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
4238
4372
|
import { Worker } from "node:worker_threads";
|
|
4239
4373
|
|
|
4240
4374
|
// src/commands.push.recovery.ts
|
|
4241
4375
|
init_config();
|
|
4242
|
-
import { readFileSync as
|
|
4243
|
-
import { join as
|
|
4376
|
+
import { readFileSync as readFileSync12, rmSync as rmSync11, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4377
|
+
import { join as join40 } from "node:path";
|
|
4244
4378
|
import { createInterface } from "node:readline/promises";
|
|
4245
4379
|
|
|
4246
4380
|
// src/commands.push.recovery.redact.ts
|
|
4247
4381
|
init_config();
|
|
4248
4382
|
init_config_sharedDirs_guard();
|
|
4249
|
-
import { cpSync as cpSync6, existsSync as
|
|
4250
|
-
import { dirname as dirname6, join as
|
|
4383
|
+
import { cpSync as cpSync6, existsSync as existsSync33, mkdirSync as mkdirSync7, statSync as statSync8 } from "node:fs";
|
|
4384
|
+
import { dirname as dirname6, join as join38, sep as sep3 } from "node:path";
|
|
4251
4385
|
init_push_gitleaks_scan();
|
|
4252
4386
|
init_utils_json();
|
|
4253
4387
|
init_utils();
|
|
@@ -4278,8 +4412,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
|
|
|
4278
4412
|
assertSafeLogical(logical);
|
|
4279
4413
|
const abs = hostMap[HOST];
|
|
4280
4414
|
if (abs === void 0) continue;
|
|
4281
|
-
if (localPath.startsWith(
|
|
4282
|
-
return
|
|
4415
|
+
if (localPath.startsWith(join38(claude, "projects", encodePath(abs)) + sep3)) {
|
|
4416
|
+
return join38(repo, "shared", "projects", logical);
|
|
4283
4417
|
}
|
|
4284
4418
|
}
|
|
4285
4419
|
return null;
|
|
@@ -4303,7 +4437,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4303
4437
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
4304
4438
|
);
|
|
4305
4439
|
}
|
|
4306
|
-
const sessionDir =
|
|
4440
|
+
const sessionDir = join38(dirname6(localPath), sid);
|
|
4307
4441
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
4308
4442
|
const subtreeMtime = newestSubtreeMtimeMs(localPath, subtreeFiles, (p) => statSync8(p).mtimeMs);
|
|
4309
4443
|
if (isRecentlyModified(subtreeMtime, nowMs())) {
|
|
@@ -4337,9 +4471,9 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4337
4471
|
);
|
|
4338
4472
|
}
|
|
4339
4473
|
mkdirSync7(stagedProjectDir, { recursive: true });
|
|
4340
|
-
cpSync6(localPath,
|
|
4341
|
-
if (
|
|
4342
|
-
cpSync6(sessionDir,
|
|
4474
|
+
cpSync6(localPath, join38(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
4475
|
+
if (existsSync33(sessionDir)) {
|
|
4476
|
+
cpSync6(sessionDir, join38(stagedProjectDir, sid), { force: true, recursive: true });
|
|
4343
4477
|
}
|
|
4344
4478
|
return true;
|
|
4345
4479
|
}
|
|
@@ -4347,14 +4481,14 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
4347
4481
|
// src/commands.push.recovery.drop.ts
|
|
4348
4482
|
init_config();
|
|
4349
4483
|
import { rmSync as rmSync10 } from "node:fs";
|
|
4350
|
-
import { join as
|
|
4484
|
+
import { join as join39 } from "node:path";
|
|
4351
4485
|
function dropSessionFromStaged(sid, map) {
|
|
4352
4486
|
const logicals = Object.keys(map.projects);
|
|
4353
4487
|
if (logicals.length === 0) return false;
|
|
4354
4488
|
const repo = repoHome();
|
|
4355
4489
|
for (const logical of logicals) {
|
|
4356
|
-
const jsonl =
|
|
4357
|
-
const dir =
|
|
4490
|
+
const jsonl = join39(repo, "shared", "projects", logical, `${sid}.jsonl`);
|
|
4491
|
+
const dir = join39(repo, "shared", "projects", logical, sid);
|
|
4358
4492
|
rmSync10(jsonl, { force: true });
|
|
4359
4493
|
rmSync10(dir, { recursive: true, force: true });
|
|
4360
4494
|
}
|
|
@@ -4474,10 +4608,10 @@ function applyThenRescan(scanVerdict, repoHome2) {
|
|
|
4474
4608
|
return next;
|
|
4475
4609
|
}
|
|
4476
4610
|
function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
4477
|
-
const ignPath =
|
|
4611
|
+
const ignPath = join40(repoHome2, ".gitleaksignore");
|
|
4478
4612
|
let before;
|
|
4479
4613
|
try {
|
|
4480
|
-
before =
|
|
4614
|
+
before = readFileSync12(ignPath, "utf8");
|
|
4481
4615
|
} catch {
|
|
4482
4616
|
before = null;
|
|
4483
4617
|
}
|
|
@@ -4573,7 +4707,7 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
4573
4707
|
`);
|
|
4574
4708
|
}
|
|
4575
4709
|
function resolveWorkerPath(deps = {}) {
|
|
4576
|
-
const check = deps.existsSyncFn ??
|
|
4710
|
+
const check = deps.existsSyncFn ?? existsSync34;
|
|
4577
4711
|
const base = deps.baseUrl ?? import.meta.url;
|
|
4578
4712
|
const mjs = fileURLToPath4(new URL("./nomad.worker.mjs", base));
|
|
4579
4713
|
if (check(mjs)) return mjs;
|
|
@@ -4781,8 +4915,8 @@ function cmdPull(opts = {}) {
|
|
|
4781
4915
|
const forceRemote = opts.forceRemote === true;
|
|
4782
4916
|
const repo = repoHome();
|
|
4783
4917
|
const backup = backupBase();
|
|
4784
|
-
if (!
|
|
4785
|
-
if (!
|
|
4918
|
+
if (!existsSync35(repo)) die(`repo not cloned at ${repo}`);
|
|
4919
|
+
if (!existsSync35(join41(repo, "shared", "settings.base.json"))) {
|
|
4786
4920
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
4787
4921
|
}
|
|
4788
4922
|
const handle = acquireLock("pull");
|
|
@@ -4791,7 +4925,7 @@ function cmdPull(opts = {}) {
|
|
|
4791
4925
|
const ts = freshBackupTs(backup);
|
|
4792
4926
|
handleWedge(repo, forceRemote);
|
|
4793
4927
|
if (!dryRun) {
|
|
4794
|
-
const backupRoot =
|
|
4928
|
+
const backupRoot = join41(backup, ts);
|
|
4795
4929
|
try {
|
|
4796
4930
|
mkdirSync8(backupRoot, { recursive: true });
|
|
4797
4931
|
} catch (err) {
|
|
@@ -4802,8 +4936,8 @@ function cmdPull(opts = {}) {
|
|
|
4802
4936
|
dryRun ? `pulling on host=${HOST} (backup=${ts}; dry-run)` : `pull on host=${HOST} (backup=${ts})`
|
|
4803
4937
|
);
|
|
4804
4938
|
gitOrFatal(["pull", "--rebase", "--autostash"], "git pull --rebase", repo);
|
|
4805
|
-
const mapPath =
|
|
4806
|
-
const map =
|
|
4939
|
+
const mapPath = join41(repo, "path-map.json");
|
|
4940
|
+
const map = existsSync35(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
4807
4941
|
divergenceCheckExtras(ts);
|
|
4808
4942
|
if (dryRun) {
|
|
4809
4943
|
computePreview(ts, map, "pull");
|
|
@@ -4825,8 +4959,8 @@ function cmdPull(opts = {}) {
|
|
|
4825
4959
|
|
|
4826
4960
|
// src/commands.push.ts
|
|
4827
4961
|
init_config();
|
|
4828
|
-
import { existsSync as
|
|
4829
|
-
import { join as
|
|
4962
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
4963
|
+
import { join as join43, relative as relative5 } from "node:path";
|
|
4830
4964
|
|
|
4831
4965
|
// src/commands.push.allowlist.ts
|
|
4832
4966
|
init_config();
|
|
@@ -4906,9 +5040,9 @@ init_color();
|
|
|
4906
5040
|
init_config();
|
|
4907
5041
|
init_config_sharedDirs_guard();
|
|
4908
5042
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4909
|
-
import { copyFileSync, existsSync as
|
|
5043
|
+
import { copyFileSync, existsSync as existsSync36, mkdirSync as mkdirSync9, readdirSync as readdirSync11, rmSync as rmSync12 } from "node:fs";
|
|
4910
5044
|
import { homedir as homedir5 } from "node:os";
|
|
4911
|
-
import { join as
|
|
5045
|
+
import { join as join42 } from "node:path";
|
|
4912
5046
|
init_push_leak_verdict();
|
|
4913
5047
|
init_push_gitleaks();
|
|
4914
5048
|
init_utils_fs();
|
|
@@ -4923,13 +5057,13 @@ function stageSessions(tmpRoot, map) {
|
|
|
4923
5057
|
if (!p || p === "TBD") continue;
|
|
4924
5058
|
reverse.set(encodePath(p), logical);
|
|
4925
5059
|
}
|
|
4926
|
-
const localProjects =
|
|
4927
|
-
if (!
|
|
5060
|
+
const localProjects = join42(claudeHome(), "projects");
|
|
5061
|
+
if (!existsSync36(localProjects)) return 0;
|
|
4928
5062
|
let staged = 0;
|
|
4929
5063
|
for (const dir of readdirSync11(localProjects)) {
|
|
4930
5064
|
const logical = reverse.get(dir);
|
|
4931
5065
|
if (!logical) continue;
|
|
4932
|
-
copyDirJsonlOnly(
|
|
5066
|
+
copyDirJsonlOnly(join42(localProjects, dir), join42(tmpRoot, "shared", "projects", logical));
|
|
4933
5067
|
staged++;
|
|
4934
5068
|
}
|
|
4935
5069
|
return staged;
|
|
@@ -4945,9 +5079,9 @@ function stageExtras(tmpRoot, map) {
|
|
|
4945
5079
|
if (!localRoot || localRoot === "TBD") continue;
|
|
4946
5080
|
for (const dirname7 of dirnames) {
|
|
4947
5081
|
if (!whitelist.includes(dirname7)) continue;
|
|
4948
|
-
const src =
|
|
4949
|
-
if (!
|
|
4950
|
-
const dst =
|
|
5082
|
+
const src = join42(localRoot, dirname7);
|
|
5083
|
+
if (!existsSync36(src)) continue;
|
|
5084
|
+
const dst = join42(tmpRoot, "shared", "extras", logical, dirname7);
|
|
4951
5085
|
copyExtras(src, dst);
|
|
4952
5086
|
staged++;
|
|
4953
5087
|
}
|
|
@@ -4955,19 +5089,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
4955
5089
|
return staged;
|
|
4956
5090
|
}
|
|
4957
5091
|
function previewPushLeaks(map) {
|
|
4958
|
-
const cacheDir =
|
|
5092
|
+
const cacheDir = join42(homedir5(), ".cache", "claude-nomad");
|
|
4959
5093
|
mkdirSync9(cacheDir, { recursive: true });
|
|
4960
5094
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
4961
|
-
const tmpRoot =
|
|
5095
|
+
const tmpRoot = join42(cacheDir, `push-preview-tree-${stamp}`);
|
|
4962
5096
|
try {
|
|
4963
5097
|
const sessionCount = stageSessions(tmpRoot, map);
|
|
4964
5098
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
4965
5099
|
if (sessionCount + extrasCount === 0) {
|
|
4966
5100
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
4967
5101
|
}
|
|
4968
|
-
const ignoreFile =
|
|
4969
|
-
if (
|
|
4970
|
-
copyFileSync(ignoreFile,
|
|
5102
|
+
const ignoreFile = join42(repoHome(), ".gitleaksignore");
|
|
5103
|
+
if (existsSync36(ignoreFile)) {
|
|
5104
|
+
copyFileSync(ignoreFile, join42(tmpRoot, ".gitleaksignore"));
|
|
4971
5105
|
}
|
|
4972
5106
|
let findings;
|
|
4973
5107
|
try {
|
|
@@ -4989,7 +5123,7 @@ init_utils();
|
|
|
4989
5123
|
init_utils_fs();
|
|
4990
5124
|
init_utils_json();
|
|
4991
5125
|
function guardGitlinks(repo) {
|
|
4992
|
-
const gitlinks = findGitlinks(
|
|
5126
|
+
const gitlinks = findGitlinks(join43(repo, "shared"));
|
|
4993
5127
|
if (gitlinks.length === 0) return;
|
|
4994
5128
|
for (const p of gitlinks) {
|
|
4995
5129
|
const rel = relative5(repo, p);
|
|
@@ -5043,7 +5177,7 @@ async function cmdPush(opts = {}) {
|
|
|
5043
5177
|
guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule);
|
|
5044
5178
|
const repo = repoHome();
|
|
5045
5179
|
const backup = backupBase();
|
|
5046
|
-
if (!
|
|
5180
|
+
if (!existsSync37(repo)) die(`repo not cloned at ${repo}`);
|
|
5047
5181
|
const handle = acquireLock("push");
|
|
5048
5182
|
if (handle === null) process.exit(0);
|
|
5049
5183
|
try {
|
|
@@ -5061,8 +5195,8 @@ async function cmdPush(opts = {}) {
|
|
|
5061
5195
|
renderNoScanTree(st);
|
|
5062
5196
|
return;
|
|
5063
5197
|
}
|
|
5064
|
-
const mapPath =
|
|
5065
|
-
if (!
|
|
5198
|
+
const mapPath = join43(repo, "path-map.json");
|
|
5199
|
+
if (!existsSync37(mapPath)) {
|
|
5066
5200
|
if (dryRun) return runDryRunPreview(st, null);
|
|
5067
5201
|
die("path-map.json missing, cannot enforce push allow-list");
|
|
5068
5202
|
}
|
|
@@ -5102,18 +5236,18 @@ init_config();
|
|
|
5102
5236
|
|
|
5103
5237
|
// src/diff.ts
|
|
5104
5238
|
init_config();
|
|
5105
|
-
import { existsSync as
|
|
5106
|
-
import { join as
|
|
5239
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
5240
|
+
import { join as join44 } from "node:path";
|
|
5107
5241
|
init_utils();
|
|
5108
5242
|
init_utils_fs();
|
|
5109
5243
|
init_utils_json();
|
|
5110
5244
|
function cmdDiff() {
|
|
5111
5245
|
try {
|
|
5112
5246
|
const repo = repoHome();
|
|
5113
|
-
if (!
|
|
5247
|
+
if (!existsSync38(repo)) die(`repo not cloned at ${repo}`);
|
|
5114
5248
|
const ts = freshBackupTs(backupBase());
|
|
5115
|
-
const mapPath =
|
|
5116
|
-
const map =
|
|
5249
|
+
const mapPath = join44(repo, "path-map.json");
|
|
5250
|
+
const map = existsSync38(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
5117
5251
|
computePreview(ts, map, "diff");
|
|
5118
5252
|
} catch (err) {
|
|
5119
5253
|
if (err instanceof NomadFatal) {
|
|
@@ -5127,8 +5261,8 @@ function cmdDiff() {
|
|
|
5127
5261
|
|
|
5128
5262
|
// src/init.ts
|
|
5129
5263
|
init_config();
|
|
5130
|
-
import { existsSync as
|
|
5131
|
-
import { join as
|
|
5264
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
5265
|
+
import { join as join46 } from "node:path";
|
|
5132
5266
|
|
|
5133
5267
|
// src/init.gh-onboard.ts
|
|
5134
5268
|
init_config();
|
|
@@ -5210,33 +5344,33 @@ init_config();
|
|
|
5210
5344
|
init_utils();
|
|
5211
5345
|
init_utils_fs();
|
|
5212
5346
|
init_utils_json();
|
|
5213
|
-
import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as
|
|
5214
|
-
import { join as
|
|
5347
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync39, rmSync as rmSync13, statSync as statSync9 } from "node:fs";
|
|
5348
|
+
import { join as join45 } from "node:path";
|
|
5215
5349
|
function snapshotIntoShared(map) {
|
|
5216
5350
|
const repo = repoHome();
|
|
5217
5351
|
const claude = claudeHome();
|
|
5218
5352
|
for (const name of allSharedLinks(map)) {
|
|
5219
|
-
const src =
|
|
5220
|
-
if (!
|
|
5221
|
-
const dst =
|
|
5353
|
+
const src = join45(claude, name);
|
|
5354
|
+
if (!existsSync39(src)) continue;
|
|
5355
|
+
const dst = join45(repo, "shared", name);
|
|
5222
5356
|
if (statSync9(src).isDirectory()) {
|
|
5223
|
-
const gk =
|
|
5224
|
-
if (
|
|
5357
|
+
const gk = join45(dst, ".gitkeep");
|
|
5358
|
+
if (existsSync39(gk)) rmSync13(gk);
|
|
5225
5359
|
cpSync7(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
5226
5360
|
} else {
|
|
5227
5361
|
copyFileSync2(src, dst);
|
|
5228
5362
|
}
|
|
5229
5363
|
log(`snapshotted shared/${name} from ${src}`);
|
|
5230
5364
|
}
|
|
5231
|
-
const userSettings =
|
|
5232
|
-
if (
|
|
5365
|
+
const userSettings = join45(claude, "settings.json");
|
|
5366
|
+
if (existsSync39(userSettings)) {
|
|
5233
5367
|
let parsed;
|
|
5234
5368
|
try {
|
|
5235
5369
|
parsed = readJson(userSettings);
|
|
5236
5370
|
} catch (err) {
|
|
5237
5371
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
5238
5372
|
}
|
|
5239
|
-
const hostFile =
|
|
5373
|
+
const hostFile = join45(repo, "hosts", `${HOST}.json`);
|
|
5240
5374
|
writeJsonAtomic(hostFile, parsed);
|
|
5241
5375
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
5242
5376
|
}
|
|
@@ -5249,14 +5383,14 @@ var SHARED_CLAUDE_MD = "<!-- claude-nomad shared CLAUDE.md; symlinked into ~/.cl
|
|
|
5249
5383
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
5250
5384
|
function preflightConflict(repoHome2) {
|
|
5251
5385
|
const candidates = [
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5386
|
+
join46(repoHome2, "shared", "settings.base.json"),
|
|
5387
|
+
join46(repoHome2, "shared", "CLAUDE.md"),
|
|
5388
|
+
join46(repoHome2, "path-map.json"),
|
|
5389
|
+
join46(repoHome2, "hosts"),
|
|
5390
|
+
join46(repoHome2, "shared")
|
|
5257
5391
|
];
|
|
5258
5392
|
for (const c of candidates) {
|
|
5259
|
-
if (
|
|
5393
|
+
if (existsSync40(c)) return c;
|
|
5260
5394
|
}
|
|
5261
5395
|
return null;
|
|
5262
5396
|
}
|
|
@@ -5271,25 +5405,25 @@ function cmdInit(opts = {}) {
|
|
|
5271
5405
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
5272
5406
|
}
|
|
5273
5407
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
5274
|
-
mkdirSync10(
|
|
5275
|
-
mkdirSync10(
|
|
5408
|
+
mkdirSync10(join46(repo, "shared"), { recursive: true });
|
|
5409
|
+
mkdirSync10(join46(repo, "hosts"), { recursive: true });
|
|
5276
5410
|
for (const name of SHARED_KEEP_DIRS) {
|
|
5277
|
-
mkdirSync10(
|
|
5411
|
+
mkdirSync10(join46(repo, "shared", name), { recursive: true });
|
|
5278
5412
|
}
|
|
5279
|
-
const userClaudeMd =
|
|
5280
|
-
if (!snapshot || !
|
|
5281
|
-
writeFileSync6(
|
|
5413
|
+
const userClaudeMd = join46(claude, "CLAUDE.md");
|
|
5414
|
+
if (!snapshot || !existsSync40(userClaudeMd)) {
|
|
5415
|
+
writeFileSync6(join46(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
5282
5416
|
item("created shared/CLAUDE.md");
|
|
5283
5417
|
}
|
|
5284
5418
|
for (const name of SHARED_KEEP_DIRS) {
|
|
5285
|
-
writeFileSync6(
|
|
5419
|
+
writeFileSync6(join46(repo, "shared", name, ".gitkeep"), "");
|
|
5286
5420
|
item(`created shared/${name}/.gitkeep`);
|
|
5287
5421
|
}
|
|
5288
|
-
writeFileSync6(
|
|
5422
|
+
writeFileSync6(join46(repo, "hosts", ".gitkeep"), "");
|
|
5289
5423
|
item("created hosts/.gitkeep");
|
|
5290
|
-
writeJsonAtomic(
|
|
5424
|
+
writeJsonAtomic(join46(repo, "shared", "settings.base.json"), {});
|
|
5291
5425
|
item("created shared/settings.base.json");
|
|
5292
|
-
writeJsonAtomic(
|
|
5426
|
+
writeJsonAtomic(join46(repo, "path-map.json"), { projects: {} });
|
|
5293
5427
|
item("created path-map.json");
|
|
5294
5428
|
if (snapshot) {
|
|
5295
5429
|
snapshotIntoShared({ projects: {} });
|
|
@@ -5590,7 +5724,7 @@ function parsePushArgs(argv) {
|
|
|
5590
5724
|
// package.json
|
|
5591
5725
|
var package_default = {
|
|
5592
5726
|
name: "claude-nomad",
|
|
5593
|
-
version: "0.
|
|
5727
|
+
version: "0.45.0",
|
|
5594
5728
|
type: "module",
|
|
5595
5729
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
5596
5730
|
keywords: [
|
|
@@ -5792,15 +5926,15 @@ var DEFAULT_HELP = [
|
|
|
5792
5926
|
init_config();
|
|
5793
5927
|
init_utils();
|
|
5794
5928
|
init_utils_json();
|
|
5795
|
-
import { existsSync as
|
|
5796
|
-
import { join as
|
|
5929
|
+
import { existsSync as existsSync41, readFileSync as readFileSync13, readdirSync as readdirSync12 } from "node:fs";
|
|
5930
|
+
import { join as join47 } from "node:path";
|
|
5797
5931
|
function resumeCmd(sessionId) {
|
|
5798
5932
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
5799
5933
|
fail(`invalid session id: ${sessionId}`);
|
|
5800
5934
|
process.exit(1);
|
|
5801
5935
|
}
|
|
5802
|
-
const projectsRoot =
|
|
5803
|
-
if (!
|
|
5936
|
+
const projectsRoot = join47(claudeHome(), "projects");
|
|
5937
|
+
if (!existsSync41(projectsRoot)) {
|
|
5804
5938
|
fail(`${projectsRoot} does not exist`);
|
|
5805
5939
|
process.exit(1);
|
|
5806
5940
|
}
|
|
@@ -5814,8 +5948,8 @@ function resumeCmd(sessionId) {
|
|
|
5814
5948
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
5815
5949
|
process.exit(1);
|
|
5816
5950
|
}
|
|
5817
|
-
const mapPath =
|
|
5818
|
-
if (!
|
|
5951
|
+
const mapPath = join47(repoHome(), "path-map.json");
|
|
5952
|
+
if (!existsSync41(mapPath)) {
|
|
5819
5953
|
fail("path-map.json missing");
|
|
5820
5954
|
process.exit(1);
|
|
5821
5955
|
}
|
|
@@ -5838,13 +5972,13 @@ function resumeCmd(sessionId) {
|
|
|
5838
5972
|
}
|
|
5839
5973
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
5840
5974
|
for (const dir of readdirSync12(projectsRoot)) {
|
|
5841
|
-
const candidate =
|
|
5842
|
-
if (
|
|
5975
|
+
const candidate = join47(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
5976
|
+
if (existsSync41(candidate)) return candidate;
|
|
5843
5977
|
}
|
|
5844
5978
|
return null;
|
|
5845
5979
|
}
|
|
5846
5980
|
function extractRecordedCwd(jsonlPath) {
|
|
5847
|
-
for (const line of
|
|
5981
|
+
for (const line of readFileSync13(jsonlPath, "utf8").split("\n")) {
|
|
5848
5982
|
if (!line.trim()) continue;
|
|
5849
5983
|
try {
|
|
5850
5984
|
const obj = JSON.parse(line);
|
package/package.json
CHANGED