claude-nomad 0.67.3 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -8
- package/README.md +21 -18
- package/dist/nomad.mjs +1050 -810
- package/dist/nomad.worker.mjs +1 -1
- package/package.json +5 -4
package/dist/nomad.mjs
CHANGED
|
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
41
41
|
mod
|
|
42
42
|
));
|
|
43
43
|
|
|
44
|
-
// src/config.never-sync.ts
|
|
44
|
+
// src/core/config.never-sync.ts
|
|
45
45
|
function stripTrailingDotsAndWhitespace(name) {
|
|
46
46
|
let end = name.length;
|
|
47
47
|
while (end > 0 && (name[end - 1] === "." || /\s/.test(name[end - 1]))) end -= 1;
|
|
@@ -97,7 +97,7 @@ function isNeverSync(path) {
|
|
|
97
97
|
}
|
|
98
98
|
var NEVER_SYNC, CLAUDE_EXTRA_NEVER_SYNC, ALWAYS_NEVER_SYNC, SECRET_FILE_PATTERNS, UNFILTERED_SHARED_REGIONS;
|
|
99
99
|
var init_config_never_sync = __esm({
|
|
100
|
-
"src/config.never-sync.ts"() {
|
|
100
|
+
"src/core/config.never-sync.ts"() {
|
|
101
101
|
"use strict";
|
|
102
102
|
NEVER_SYNC = /* @__PURE__ */ new Set([
|
|
103
103
|
".claude.json",
|
|
@@ -222,10 +222,10 @@ var require_picocolors = __commonJS({
|
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
-
// src/color.ts
|
|
225
|
+
// src/render/color.ts
|
|
226
226
|
var import_picocolors, enabled, red, yellow, green, cyan, blue, dim, bold, wslNarrowPad, okGlyph, failGlyph, warnGlyph, infoGlyph;
|
|
227
227
|
var init_color = __esm({
|
|
228
|
-
"src/color.ts"() {
|
|
228
|
+
"src/render/color.ts"() {
|
|
229
229
|
"use strict";
|
|
230
230
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
231
231
|
enabled = import_picocolors.default.isColorSupported;
|
|
@@ -244,10 +244,10 @@ var init_color = __esm({
|
|
|
244
244
|
}
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
-
// src/exit-codes.ts
|
|
247
|
+
// src/core/exit-codes.ts
|
|
248
248
|
var EXIT;
|
|
249
249
|
var init_exit_codes = __esm({
|
|
250
|
-
"src/exit-codes.ts"() {
|
|
250
|
+
"src/core/exit-codes.ts"() {
|
|
251
251
|
"use strict";
|
|
252
252
|
EXIT = {
|
|
253
253
|
/** Completed successfully. Also returned when a held lock causes a no-op skip. */
|
|
@@ -269,7 +269,7 @@ var init_exit_codes = __esm({
|
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
// src/utils.ts
|
|
272
|
+
// src/core/utils.ts
|
|
273
273
|
import { execFileSync } from "node:child_process";
|
|
274
274
|
function isProcessExit(err) {
|
|
275
275
|
if (typeof err !== "object" || err === null) return false;
|
|
@@ -304,7 +304,7 @@ function gitOrFatal(args, context, cwd) {
|
|
|
304
304
|
}
|
|
305
305
|
var log, ok, warn, fail, item, NomadFatal, PROCESS_EXIT_BRAND, ProcessExit, die, gitStatusPorcelainZ;
|
|
306
306
|
var init_utils = __esm({
|
|
307
|
-
"src/utils.ts"() {
|
|
307
|
+
"src/core/utils.ts"() {
|
|
308
308
|
"use strict";
|
|
309
309
|
init_color();
|
|
310
310
|
init_exit_codes();
|
|
@@ -349,7 +349,7 @@ var init_utils = __esm({
|
|
|
349
349
|
}
|
|
350
350
|
});
|
|
351
351
|
|
|
352
|
-
// src/config.sharedDirs.guard.ts
|
|
352
|
+
// src/core/config.sharedDirs.guard.ts
|
|
353
353
|
function assertSafeLogical(logical) {
|
|
354
354
|
if (!SAFE_LOGICAL.test(logical) || logical === "." || logical === "..") {
|
|
355
355
|
throw new NomadFatal(
|
|
@@ -419,7 +419,7 @@ function isValidSharedDir(entry) {
|
|
|
419
419
|
}
|
|
420
420
|
var SAFE_LOGICAL, SAFE_SEGMENT, RESERVED_SHARED, RESERVED_SHARED_FOLDED, WIN32_DEVICE_NAMES, UNJOINABLE_REASONS;
|
|
421
421
|
var init_config_sharedDirs_guard = __esm({
|
|
422
|
-
"src/config.sharedDirs.guard.ts"() {
|
|
422
|
+
"src/core/config.sharedDirs.guard.ts"() {
|
|
423
423
|
"use strict";
|
|
424
424
|
init_config_never_sync();
|
|
425
425
|
init_utils();
|
|
@@ -455,10 +455,10 @@ var init_config_sharedDirs_guard = __esm({
|
|
|
455
455
|
}
|
|
456
456
|
});
|
|
457
457
|
|
|
458
|
-
// src/settings-keys.ts
|
|
458
|
+
// src/core/settings-keys.ts
|
|
459
459
|
var SCHEMA_KEYS, APP_ONLY_KEYS, KNOWN_SETTINGS_KEYS;
|
|
460
460
|
var init_settings_keys = __esm({
|
|
461
|
-
"src/settings-keys.ts"() {
|
|
461
|
+
"src/core/settings-keys.ts"() {
|
|
462
462
|
"use strict";
|
|
463
463
|
SCHEMA_KEYS = [
|
|
464
464
|
"$schema",
|
|
@@ -635,7 +635,7 @@ var init_settings_keys = __esm({
|
|
|
635
635
|
}
|
|
636
636
|
});
|
|
637
637
|
|
|
638
|
-
// src/config.ts
|
|
638
|
+
// src/core/config.ts
|
|
639
639
|
import { homedir, hostname } from "node:os";
|
|
640
640
|
import { join, resolve } from "node:path";
|
|
641
641
|
function home() {
|
|
@@ -686,7 +686,7 @@ function sharedDirEntries(map) {
|
|
|
686
686
|
}
|
|
687
687
|
var SETTINGS_SCHEMA_URL, NPM_REGISTRY_LATEST_URL, GITLEAKS_PINNED_VERSION, GITLEAKS_SCAN_TIMEOUT_MS, HOST, SHARED_LINKS, GSD_PREFIX, GSD_DROPPED_NAMES, SUPPORTED_EXTRAS, PUSH_ALLOWED_STATIC;
|
|
688
688
|
var init_config = __esm({
|
|
689
|
-
"src/config.ts"() {
|
|
689
|
+
"src/core/config.ts"() {
|
|
690
690
|
"use strict";
|
|
691
691
|
init_config_sharedDirs_guard();
|
|
692
692
|
init_utils();
|
|
@@ -721,7 +721,22 @@ var init_config = __esm({
|
|
|
721
721
|
}
|
|
722
722
|
});
|
|
723
723
|
|
|
724
|
-
// src/
|
|
724
|
+
// src/core/error-text.ts
|
|
725
|
+
function errorText(err) {
|
|
726
|
+
try {
|
|
727
|
+
const message = err?.message;
|
|
728
|
+
return typeof message === "string" ? message : String(err);
|
|
729
|
+
} catch {
|
|
730
|
+
return "unprintable error value";
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
var init_error_text = __esm({
|
|
734
|
+
"src/core/error-text.ts"() {
|
|
735
|
+
"use strict";
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// src/core/utils.json.ts
|
|
725
740
|
import { readFileSync } from "node:fs";
|
|
726
741
|
function readJson(path) {
|
|
727
742
|
const data = JSON.parse(readFileSync(path, "utf8"));
|
|
@@ -759,10 +774,13 @@ function validatePathMapShape(raw) {
|
|
|
759
774
|
}
|
|
760
775
|
return null;
|
|
761
776
|
}
|
|
777
|
+
function isProtoPollutionKey(key) {
|
|
778
|
+
return key === "__proto__" || key === "constructor" || key === "prototype";
|
|
779
|
+
}
|
|
762
780
|
function deepMerge(target, source) {
|
|
763
781
|
const out = { ...target };
|
|
764
782
|
for (const [key, value] of Object.entries(source)) {
|
|
765
|
-
if (key
|
|
783
|
+
if (isProtoPollutionKey(key)) continue;
|
|
766
784
|
const existing = out[key];
|
|
767
785
|
const bothObjects = value !== null && typeof value === "object" && !Array.isArray(value) && existing !== null && typeof existing === "object" && !Array.isArray(existing);
|
|
768
786
|
out[key] = bothObjects ? deepMerge(existing, value) : value;
|
|
@@ -774,6 +792,7 @@ function sortKeysDeep(value) {
|
|
|
774
792
|
if (value !== null && typeof value === "object") {
|
|
775
793
|
const out = {};
|
|
776
794
|
for (const key of Object.keys(value).sort((a, b) => a.localeCompare(b, "en"))) {
|
|
795
|
+
if (isProtoPollutionKey(key)) continue;
|
|
777
796
|
out[key] = sortKeysDeep(value[key]);
|
|
778
797
|
}
|
|
779
798
|
return out;
|
|
@@ -782,7 +801,7 @@ function sortKeysDeep(value) {
|
|
|
782
801
|
}
|
|
783
802
|
var ENCODE_MAX, hashPath, encodePath;
|
|
784
803
|
var init_utils_json = __esm({
|
|
785
|
-
"src/utils.json.ts"() {
|
|
804
|
+
"src/core/utils.json.ts"() {
|
|
786
805
|
"use strict";
|
|
787
806
|
init_config();
|
|
788
807
|
init_utils();
|
|
@@ -800,7 +819,7 @@ var init_utils_json = __esm({
|
|
|
800
819
|
}
|
|
801
820
|
});
|
|
802
821
|
|
|
803
|
-
// src/utils.fs.ts
|
|
822
|
+
// src/core/utils.fs.ts
|
|
804
823
|
import {
|
|
805
824
|
closeSync,
|
|
806
825
|
cpSync as cpSync3,
|
|
@@ -871,6 +890,12 @@ function freshBackupTs(backupRoot) {
|
|
|
871
890
|
while (existsSync3(join5(backupRoot, `${base}-${n}`))) n++;
|
|
872
891
|
return `${base}-${n}`;
|
|
873
892
|
}
|
|
893
|
+
function prunableByAge(dirs, olderThanMs, nowMs) {
|
|
894
|
+
return dirs.filter((d) => nowMs - d.mtimeMs > olderThanMs).map((d) => d.name);
|
|
895
|
+
}
|
|
896
|
+
function prunableByCount(dirs, keep) {
|
|
897
|
+
return dirs.slice(keep).map((d) => d.name);
|
|
898
|
+
}
|
|
874
899
|
function discardEmptyBackupDir(backupRoot) {
|
|
875
900
|
try {
|
|
876
901
|
rmdirSync(backupRoot);
|
|
@@ -886,17 +911,22 @@ function ensureSymlink(linkPath, target) {
|
|
|
886
911
|
symlinkSync(target, linkPath);
|
|
887
912
|
log(`linked ${linkPath} -> ${target}`);
|
|
888
913
|
}
|
|
889
|
-
function backupUnder(absPath, anchor, destRoot) {
|
|
914
|
+
function backupUnder(absPath, anchor, destRoot, hint) {
|
|
890
915
|
if (!existsSync3(absPath)) return false;
|
|
891
916
|
const rel = relative3(anchor, absPath);
|
|
892
917
|
if (rel === "" || rel === ".." || rel.startsWith(`..${sep2}`)) return false;
|
|
893
918
|
const dst = join5(destRoot, rel);
|
|
894
|
-
|
|
895
|
-
|
|
919
|
+
try {
|
|
920
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
921
|
+
cpSync3(absPath, dst, { recursive: true, force: false, preserveTimestamps: true });
|
|
922
|
+
} catch (err) {
|
|
923
|
+
const fragment = `could not back up ${absPath} into ${dst} (${errorText(err)}); a partial copy may be left there`;
|
|
924
|
+
throw new NomadFatal(hint === void 0 ? fragment : `${fragment}. ${hint}`);
|
|
925
|
+
}
|
|
896
926
|
return true;
|
|
897
927
|
}
|
|
898
|
-
function backupBeforeWrite(absPath, ts) {
|
|
899
|
-
return backupUnder(absPath, claudeHome(), join5(backupBase(), ts));
|
|
928
|
+
function backupBeforeWrite(absPath, ts, hint) {
|
|
929
|
+
return backupUnder(absPath, claudeHome(), join5(backupBase(), ts), hint);
|
|
900
930
|
}
|
|
901
931
|
function backupRepoWrite(absPath, ts, repoHome2) {
|
|
902
932
|
backupUnder(absPath, repoHome2, join5(backupBase(), ts, "repo"));
|
|
@@ -906,9 +936,10 @@ function backupExtrasWrite(absPath, ts, projectRoot) {
|
|
|
906
936
|
}
|
|
907
937
|
var RENAME_RETRY_MAX_ATTEMPTS, RENAME_RETRY_BACKOFF_MS;
|
|
908
938
|
var init_utils_fs = __esm({
|
|
909
|
-
"src/utils.fs.ts"() {
|
|
939
|
+
"src/core/utils.fs.ts"() {
|
|
910
940
|
"use strict";
|
|
911
941
|
init_config();
|
|
942
|
+
init_error_text();
|
|
912
943
|
init_utils_json();
|
|
913
944
|
init_utils();
|
|
914
945
|
RENAME_RETRY_MAX_ATTEMPTS = 5;
|
|
@@ -916,7 +947,7 @@ var init_utils_fs = __esm({
|
|
|
916
947
|
}
|
|
917
948
|
});
|
|
918
949
|
|
|
919
|
-
// src/commands
|
|
950
|
+
// src/commands/pull/wedge.ts
|
|
920
951
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
921
952
|
import { existsSync as existsSync15 } from "node:fs";
|
|
922
953
|
import { join as join21 } from "node:path";
|
|
@@ -985,23 +1016,54 @@ function orphanedAutostashPresent(repo) {
|
|
|
985
1016
|
return raw.split("\n").some((line) => /:\s*autostash$/.test(line));
|
|
986
1017
|
}
|
|
987
1018
|
var INDEX_PROBE_TIMEOUT_MS;
|
|
988
|
-
var
|
|
989
|
-
"src/commands
|
|
1019
|
+
var init_wedge = __esm({
|
|
1020
|
+
"src/commands/pull/wedge.ts"() {
|
|
990
1021
|
"use strict";
|
|
991
1022
|
INDEX_PROBE_TIMEOUT_MS = 3e4;
|
|
992
1023
|
}
|
|
993
1024
|
});
|
|
994
1025
|
|
|
995
|
-
// src/
|
|
996
|
-
import { existsSync as existsSync16
|
|
997
|
-
import {
|
|
998
|
-
import { join as join22 } from "node:path";
|
|
1026
|
+
// src/core/package-root.ts
|
|
1027
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
1028
|
+
import { basename as basename3, dirname as dirname4, join as join22 } from "node:path";
|
|
999
1029
|
import { fileURLToPath } from "node:url";
|
|
1030
|
+
function packageRoot(from = HERE) {
|
|
1031
|
+
let dir = from;
|
|
1032
|
+
for (; ; ) {
|
|
1033
|
+
if (existsSync16(join22(dir, "package.json"))) return dir;
|
|
1034
|
+
if (basename3(dir) === "node_modules") {
|
|
1035
|
+
throw new Error(`no package.json above ${from} within its own package`);
|
|
1036
|
+
}
|
|
1037
|
+
const parent = dirname4(dir);
|
|
1038
|
+
if (parent === dir) throw new Error(`no package.json above ${from}`);
|
|
1039
|
+
dir = parent;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
var HERE;
|
|
1043
|
+
var init_package_root = __esm({
|
|
1044
|
+
"src/core/package-root.ts"() {
|
|
1045
|
+
"use strict";
|
|
1046
|
+
HERE = dirname4(fileURLToPath(import.meta.url));
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
// src/commands/push/gitleaks.config.ts
|
|
1051
|
+
import { existsSync as existsSync17, mkdtempSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1052
|
+
import { tmpdir } from "node:os";
|
|
1053
|
+
import { join as join23 } from "node:path";
|
|
1000
1054
|
function resolveTomlPath(repo = repoHome()) {
|
|
1001
|
-
const repoToml =
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1004
|
-
|
|
1055
|
+
const repoToml = join23(repo, ".gitleaks.toml");
|
|
1056
|
+
if (existsSync17(repoToml)) return repoToml;
|
|
1057
|
+
let root;
|
|
1058
|
+
try {
|
|
1059
|
+
root = packageRoot();
|
|
1060
|
+
} catch {
|
|
1061
|
+
throw new NomadFatal(
|
|
1062
|
+
"claude-nomad install looks incomplete (no package.json above the CLI); reinstall with `npm i -g claude-nomad`"
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
const bundled = join23(root, ".gitleaks.toml");
|
|
1066
|
+
return existsSync17(bundled) ? bundled : null;
|
|
1005
1067
|
}
|
|
1006
1068
|
function assertOverlayAllowlistsScoped(overlayBody) {
|
|
1007
1069
|
if (OVERLAY_INLINE_ALLOWLIST_RE.test(overlayBody)) {
|
|
@@ -1039,17 +1101,17 @@ function buildOverlayTempConfig(overlayBody, bundled) {
|
|
|
1039
1101
|
path = ${JSON.stringify(bundled)}
|
|
1040
1102
|
|
|
1041
1103
|
${overlayBody}`;
|
|
1042
|
-
const tempPath = mkdtempSync(
|
|
1043
|
-
const configPath =
|
|
1104
|
+
const tempPath = mkdtempSync(join23(tmpdir(), "nomad-gitleaks-cfg-"));
|
|
1105
|
+
const configPath = join23(tempPath, "config.toml");
|
|
1044
1106
|
writeFileSync3(configPath, tempBody, { mode: 384, flag: "wx" });
|
|
1045
1107
|
return { configPath, tempPath };
|
|
1046
1108
|
}
|
|
1047
1109
|
function resolveTomlConfig() {
|
|
1048
1110
|
const repo = repoHome();
|
|
1049
|
-
const overlayPath =
|
|
1050
|
-
const repoToml =
|
|
1111
|
+
const overlayPath = join23(repo, ".gitleaks.overlay.toml");
|
|
1112
|
+
const repoToml = join23(repo, ".gitleaks.toml");
|
|
1051
1113
|
const bundled = resolveTomlPath(repo);
|
|
1052
|
-
if (!
|
|
1114
|
+
if (!existsSync17(overlayPath)) {
|
|
1053
1115
|
return { path: bundled, tempPath: null };
|
|
1054
1116
|
}
|
|
1055
1117
|
if (bundled === repoToml) {
|
|
@@ -1080,10 +1142,11 @@ function resolveTomlConfig() {
|
|
|
1080
1142
|
}
|
|
1081
1143
|
}
|
|
1082
1144
|
var OVERLAY_EXTEND_RE, TABLE_HEADER_RE, OVERLAY_ALLOWLIST_HEADER_RE, OVERLAY_INLINE_ALLOWLIST_RE, PATHS_KEY_RE, CATCH_ALL_RE;
|
|
1083
|
-
var
|
|
1084
|
-
"src/push
|
|
1145
|
+
var init_gitleaks_config = __esm({
|
|
1146
|
+
"src/commands/push/gitleaks.config.ts"() {
|
|
1085
1147
|
"use strict";
|
|
1086
1148
|
init_config();
|
|
1149
|
+
init_package_root();
|
|
1087
1150
|
init_utils();
|
|
1088
1151
|
OVERLAY_EXTEND_RE = /^[ \t]*(?:\[[ \t]*extend[ \t]*\]|extend[ \t]*[.=])/m;
|
|
1089
1152
|
TABLE_HEADER_RE = /^\s*\[/;
|
|
@@ -1094,7 +1157,7 @@ var init_push_gitleaks_config = __esm({
|
|
|
1094
1157
|
}
|
|
1095
1158
|
});
|
|
1096
1159
|
|
|
1097
|
-
// src/autostash-guard.ts
|
|
1160
|
+
// src/commands/autostash-guard.ts
|
|
1098
1161
|
function autostashConflictRunbookText(resumeCmd2, stashRetained) {
|
|
1099
1162
|
const head = `${resumeCmd2} reported success, but the autostash pop it ran internally conflicted. The working tree now holds conflict markers and the index is unmerged. Nothing has been copied to ~/.claude/ yet, and there is no rebase or merge in progress to abort.`;
|
|
1100
1163
|
if (stashRetained) {
|
|
@@ -1125,19 +1188,19 @@ function assertNoAutostashConflict(repo, resumeCmd2) {
|
|
|
1125
1188
|
});
|
|
1126
1189
|
}
|
|
1127
1190
|
var init_autostash_guard = __esm({
|
|
1128
|
-
"src/autostash-guard.ts"() {
|
|
1191
|
+
"src/commands/autostash-guard.ts"() {
|
|
1129
1192
|
"use strict";
|
|
1130
|
-
|
|
1193
|
+
init_wedge();
|
|
1131
1194
|
init_exit_codes();
|
|
1132
1195
|
init_utils();
|
|
1133
1196
|
}
|
|
1134
1197
|
});
|
|
1135
1198
|
|
|
1136
|
-
// src/push
|
|
1137
|
-
import { execFileSync as
|
|
1199
|
+
// src/commands/push/checks.ts
|
|
1200
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
1138
1201
|
import { readdirSync as readdirSync8, rmSync as rmSync7 } from "node:fs";
|
|
1139
1202
|
import { homedir as homedir2, platform } from "node:os";
|
|
1140
|
-
import { delimiter, join as
|
|
1203
|
+
import { delimiter, join as join24 } from "node:path";
|
|
1141
1204
|
function gitleaksInstallHint() {
|
|
1142
1205
|
const head = "gitleaks not on PATH (required for nomad push). Install:";
|
|
1143
1206
|
const plat = platform();
|
|
@@ -1185,7 +1248,7 @@ function findGitlinks(dir) {
|
|
|
1185
1248
|
return;
|
|
1186
1249
|
}
|
|
1187
1250
|
for (const e of entries) {
|
|
1188
|
-
const p =
|
|
1251
|
+
const p = join24(current, e.name);
|
|
1189
1252
|
if (e.name === ".git") {
|
|
1190
1253
|
hits.push(p);
|
|
1191
1254
|
continue;
|
|
@@ -1201,7 +1264,7 @@ function probeGitleaks() {
|
|
|
1201
1264
|
const args = ["version"];
|
|
1202
1265
|
if (toml !== null) args.push("--config", toml);
|
|
1203
1266
|
try {
|
|
1204
|
-
return
|
|
1267
|
+
return execFileSync5("gitleaks", args, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
1205
1268
|
} catch (err) {
|
|
1206
1269
|
const e = err;
|
|
1207
1270
|
if (e.code === "ENOENT") throw new NomadFatal(gitleaksInstallHint());
|
|
@@ -1221,7 +1284,7 @@ function rebaseBeforePush(repo) {
|
|
|
1221
1284
|
const wedge = classifyWedge(repo);
|
|
1222
1285
|
if (wedge !== null) throw new NomadFatal(wedgePreflight(wedge), { code: EXIT.CONFLICT });
|
|
1223
1286
|
try {
|
|
1224
|
-
|
|
1287
|
+
execFileSync5("git", ["pull", "--rebase", "--autostash"], {
|
|
1225
1288
|
cwd: repo,
|
|
1226
1289
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1227
1290
|
});
|
|
@@ -1234,22 +1297,22 @@ function rebaseBeforePush(repo) {
|
|
|
1234
1297
|
}
|
|
1235
1298
|
assertNoAutostashConflict(repo, "nomad push");
|
|
1236
1299
|
}
|
|
1237
|
-
var
|
|
1238
|
-
"src/push
|
|
1300
|
+
var init_checks = __esm({
|
|
1301
|
+
"src/commands/push/checks.ts"() {
|
|
1239
1302
|
"use strict";
|
|
1240
|
-
|
|
1303
|
+
init_gitleaks_config();
|
|
1241
1304
|
init_autostash_guard();
|
|
1242
|
-
|
|
1305
|
+
init_wedge();
|
|
1243
1306
|
init_exit_codes();
|
|
1244
1307
|
init_utils();
|
|
1245
1308
|
}
|
|
1246
1309
|
});
|
|
1247
1310
|
|
|
1248
|
-
// src/push
|
|
1249
|
-
import { execFileSync as
|
|
1311
|
+
// src/commands/push/gitleaks.scan.ts
|
|
1312
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
1250
1313
|
import { mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync8 } from "node:fs";
|
|
1251
1314
|
import { homedir as homedir3 } from "node:os";
|
|
1252
|
-
import { join as
|
|
1315
|
+
import { join as join28 } from "node:path";
|
|
1253
1316
|
function toFinding(entry) {
|
|
1254
1317
|
if (typeof entry !== "object" || entry === null) return null;
|
|
1255
1318
|
const e = entry;
|
|
@@ -1297,9 +1360,9 @@ function resolveScanFailure(reportPath, e, forwardStreams) {
|
|
|
1297
1360
|
return report;
|
|
1298
1361
|
}
|
|
1299
1362
|
function scanStagedTree(repoDir, forwardStreams = false) {
|
|
1300
|
-
const cacheDir =
|
|
1363
|
+
const cacheDir = join28(homedir3(), ".cache", "claude-nomad");
|
|
1301
1364
|
mkdirSync3(cacheDir, { recursive: true });
|
|
1302
|
-
const reportPath =
|
|
1365
|
+
const reportPath = join28(cacheDir, `gitleaks-${nowTimestamp()}-${process.pid}.json`);
|
|
1303
1366
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
1304
1367
|
const args = [
|
|
1305
1368
|
"protect",
|
|
@@ -1313,9 +1376,9 @@ function scanStagedTree(repoDir, forwardStreams = false) {
|
|
|
1313
1376
|
const opts = { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] };
|
|
1314
1377
|
const gitleaksOpts = { ...opts, timeout: GITLEAKS_SCAN_TIMEOUT_MS };
|
|
1315
1378
|
try {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1379
|
+
execFileSync9("git", ["init", "-q"], opts);
|
|
1380
|
+
execFileSync9("git", ["add", "-A"], opts);
|
|
1381
|
+
execFileSync9("gitleaks", args, gitleaksOpts);
|
|
1319
1382
|
return [];
|
|
1320
1383
|
} catch (err) {
|
|
1321
1384
|
const e = err;
|
|
@@ -1327,14 +1390,14 @@ function scanStagedTree(repoDir, forwardStreams = false) {
|
|
|
1327
1390
|
}
|
|
1328
1391
|
}
|
|
1329
1392
|
function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TIMEOUT_MS) {
|
|
1330
|
-
const cacheDir =
|
|
1393
|
+
const cacheDir = join28(homedir3(), ".cache", "claude-nomad");
|
|
1331
1394
|
mkdirSync3(cacheDir, { recursive: true });
|
|
1332
1395
|
const { path: toml, tempPath } = resolveTomlConfig();
|
|
1333
1396
|
let reportDir;
|
|
1334
1397
|
let reportPath;
|
|
1335
1398
|
try {
|
|
1336
|
-
reportDir = mkdtempSync2(
|
|
1337
|
-
reportPath =
|
|
1399
|
+
reportDir = mkdtempSync2(join28(cacheDir, `gitleaks-file-${nowTimestamp()}-${process.pid}-`));
|
|
1400
|
+
reportPath = join28(reportDir, "report.json");
|
|
1338
1401
|
const args = [
|
|
1339
1402
|
"detect",
|
|
1340
1403
|
"--no-git",
|
|
@@ -1348,7 +1411,7 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
|
|
|
1348
1411
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1349
1412
|
timeout: timeoutMs
|
|
1350
1413
|
};
|
|
1351
|
-
|
|
1414
|
+
execFileSync9("gitleaks", args, opts);
|
|
1352
1415
|
return [];
|
|
1353
1416
|
} catch (err) {
|
|
1354
1417
|
const e = err;
|
|
@@ -1359,16 +1422,16 @@ function scanFile(filePath, forwardStreams = false, timeoutMs = GITLEAKS_SCAN_TI
|
|
|
1359
1422
|
if (reportDir !== void 0) rmSync8(reportDir, { recursive: true, force: true });
|
|
1360
1423
|
}
|
|
1361
1424
|
}
|
|
1362
|
-
var
|
|
1363
|
-
"src/push
|
|
1425
|
+
var init_gitleaks_scan = __esm({
|
|
1426
|
+
"src/commands/push/gitleaks.scan.ts"() {
|
|
1364
1427
|
"use strict";
|
|
1365
1428
|
init_config();
|
|
1366
|
-
|
|
1429
|
+
init_gitleaks_config();
|
|
1367
1430
|
init_utils_fs();
|
|
1368
1431
|
}
|
|
1369
1432
|
});
|
|
1370
1433
|
|
|
1371
|
-
// src/push
|
|
1434
|
+
// src/commands/push/gitleaks.ts
|
|
1372
1435
|
function findingIdentityKey(f) {
|
|
1373
1436
|
const fp = f.Fingerprint;
|
|
1374
1437
|
return typeof fp === "string" && fp.length > 0 ? fp : `${f.File}:${f.RuleID}:${f.StartLine}:${f.StartColumn}`;
|
|
@@ -1456,13 +1519,13 @@ function buildSessionAwareFatal(bySession, other) {
|
|
|
1456
1519
|
return lines.join("\n");
|
|
1457
1520
|
}
|
|
1458
1521
|
var SESSION_PATH, SUBAGENT_SESSION_PATH, LEGACY_FATAL;
|
|
1459
|
-
var
|
|
1460
|
-
"src/push
|
|
1522
|
+
var init_gitleaks = __esm({
|
|
1523
|
+
"src/commands/push/gitleaks.ts"() {
|
|
1461
1524
|
"use strict";
|
|
1462
1525
|
init_config();
|
|
1463
1526
|
init_exit_codes();
|
|
1464
|
-
|
|
1465
|
-
|
|
1527
|
+
init_checks();
|
|
1528
|
+
init_gitleaks_scan();
|
|
1466
1529
|
init_utils();
|
|
1467
1530
|
SESSION_PATH = /^shared\/projects\/[^/]+\/([^/]+)\.jsonl$/;
|
|
1468
1531
|
SUBAGENT_SESSION_PATH = /^shared\/projects\/[^/]+\/([^/]+)\/.*\.jsonl$/;
|
|
@@ -1470,9 +1533,9 @@ var init_push_gitleaks = __esm({
|
|
|
1470
1533
|
}
|
|
1471
1534
|
});
|
|
1472
1535
|
|
|
1473
|
-
// src/push
|
|
1474
|
-
var
|
|
1475
|
-
__export(
|
|
1536
|
+
// src/commands/push/leak-verdict.ts
|
|
1537
|
+
var leak_verdict_exports = {};
|
|
1538
|
+
__export(leak_verdict_exports, {
|
|
1476
1539
|
failRow: () => failRow,
|
|
1477
1540
|
leakVerdictRow: () => leakVerdictRow,
|
|
1478
1541
|
noLeaksRow: () => noLeaksRow,
|
|
@@ -1545,65 +1608,59 @@ function scanPushVerdict(repo) {
|
|
|
1545
1608
|
return leakFound(findings);
|
|
1546
1609
|
}
|
|
1547
1610
|
var noLeaksRow, failRow;
|
|
1548
|
-
var
|
|
1549
|
-
"src/push
|
|
1611
|
+
var init_leak_verdict = __esm({
|
|
1612
|
+
"src/commands/push/leak-verdict.ts"() {
|
|
1550
1613
|
"use strict";
|
|
1551
1614
|
init_color();
|
|
1552
1615
|
init_exit_codes();
|
|
1553
|
-
|
|
1554
|
-
|
|
1616
|
+
init_checks();
|
|
1617
|
+
init_gitleaks();
|
|
1555
1618
|
noLeaksRow = () => `${green(okGlyph)} no leaks`;
|
|
1556
1619
|
failRow = (text) => `${red(failGlyph)} ${text}`;
|
|
1557
1620
|
}
|
|
1558
1621
|
});
|
|
1559
1622
|
|
|
1560
|
-
// src/commands
|
|
1623
|
+
// src/commands/adopt.ts
|
|
1561
1624
|
import { existsSync as existsSync5, lstatSync as lstatSync7 } from "node:fs";
|
|
1562
1625
|
import { join as join9 } from "node:path";
|
|
1563
1626
|
|
|
1564
|
-
// src/commands
|
|
1627
|
+
// src/commands/adopt.recover.ts
|
|
1565
1628
|
import { rmSync as rmSync4 } from "node:fs";
|
|
1566
1629
|
import { dirname as dirname2, join as join7, resolve as resolve2 } from "node:path";
|
|
1567
1630
|
|
|
1568
|
-
// src/commands
|
|
1631
|
+
// src/commands/adopt.scan.ts
|
|
1569
1632
|
init_config();
|
|
1570
|
-
|
|
1571
|
-
import { join as join2, relative } from "node:path";
|
|
1572
|
-
|
|
1573
|
-
// src/error-text.ts
|
|
1574
|
-
function errorText(err) {
|
|
1575
|
-
try {
|
|
1576
|
-
const message = err?.message;
|
|
1577
|
-
return typeof message === "string" ? message : String(err);
|
|
1578
|
-
} catch {
|
|
1579
|
-
return "unprintable error value";
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
// src/commands.adopt.scan.ts
|
|
1633
|
+
init_error_text();
|
|
1584
1634
|
init_exit_codes();
|
|
1585
1635
|
init_utils();
|
|
1636
|
+
import { lstatSync, readdirSync } from "node:fs";
|
|
1637
|
+
import { join as join2, relative } from "node:path";
|
|
1586
1638
|
function toForwardSlash(p) {
|
|
1587
1639
|
return p.replaceAll("\\", "/");
|
|
1588
1640
|
}
|
|
1589
|
-
function walk(root
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1641
|
+
function walk(root) {
|
|
1642
|
+
const out = [];
|
|
1643
|
+
const stack = [root];
|
|
1644
|
+
while (stack.length > 0) {
|
|
1645
|
+
const dir = stack.pop();
|
|
1646
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1647
|
+
const match = matchDeniedName(ALWAYS_NEVER_SYNC, entry.name);
|
|
1648
|
+
if (match !== null) {
|
|
1649
|
+
out.push({
|
|
1650
|
+
path: toForwardSlash(relative(root, join2(dir, entry.name))),
|
|
1651
|
+
matched: match.axis === "name" ? match.entry : null
|
|
1652
|
+
});
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (entry.isDirectory()) stack.push(join2(dir, entry.name));
|
|
1598
1656
|
}
|
|
1599
|
-
if (entry.isDirectory()) walk(root, join2(dir, entry.name), out);
|
|
1600
1657
|
}
|
|
1658
|
+
return out;
|
|
1601
1659
|
}
|
|
1602
1660
|
function scanDeniedEntries(root) {
|
|
1603
1661
|
const stat = lstatSync(root, { throwIfNoEntry: false });
|
|
1604
1662
|
if (!stat?.isDirectory()) return [];
|
|
1605
|
-
const out =
|
|
1606
|
-
walk(root, root, out);
|
|
1663
|
+
const out = walk(root);
|
|
1607
1664
|
out.sort((a, b) => a.path.localeCompare(b.path));
|
|
1608
1665
|
return out;
|
|
1609
1666
|
}
|
|
@@ -1650,16 +1707,17 @@ function refuseDeniedEntries(name, root) {
|
|
|
1650
1707
|
});
|
|
1651
1708
|
}
|
|
1652
1709
|
|
|
1653
|
-
// src/commands
|
|
1710
|
+
// src/commands/adopt.recover.ts
|
|
1654
1711
|
init_config();
|
|
1712
|
+
init_error_text();
|
|
1655
1713
|
init_exit_codes();
|
|
1656
1714
|
|
|
1657
|
-
// src/extras
|
|
1715
|
+
// src/sync/extras/core.ts
|
|
1658
1716
|
init_config();
|
|
1659
1717
|
import { cpSync as cpSync2, existsSync as existsSync2, lstatSync as lstatSync3, readdirSync as readdirSync3, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
|
|
1660
1718
|
import { basename as basename2, join as join4, relative as relative2 } from "node:path";
|
|
1661
1719
|
|
|
1662
|
-
// src/extras
|
|
1720
|
+
// src/sync/extras/collision.ts
|
|
1663
1721
|
init_utils();
|
|
1664
1722
|
import { cpSync, existsSync, lstatSync as lstatSync2, readdirSync as readdirSync2, rmSync } from "node:fs";
|
|
1665
1723
|
import { basename, join as join3 } from "node:path";
|
|
@@ -1734,7 +1792,7 @@ function copyExtrasFilteredPreservingBy(src, dst, isPreserved, isRootPreserved)
|
|
|
1734
1792
|
});
|
|
1735
1793
|
}
|
|
1736
1794
|
|
|
1737
|
-
// src/extras
|
|
1795
|
+
// src/sync/extras/guards.ts
|
|
1738
1796
|
init_utils();
|
|
1739
1797
|
init_config_sharedDirs_guard();
|
|
1740
1798
|
import { isAbsolute, normalize, sep } from "node:path";
|
|
@@ -1752,7 +1810,7 @@ function assertSafeLocalRoot(localRoot, logical) {
|
|
|
1752
1810
|
}
|
|
1753
1811
|
}
|
|
1754
1812
|
|
|
1755
|
-
// src/extras
|
|
1813
|
+
// src/sync/extras/core.ts
|
|
1756
1814
|
init_utils();
|
|
1757
1815
|
init_utils_json();
|
|
1758
1816
|
function loadValidatedExtras(opts) {
|
|
@@ -1781,12 +1839,12 @@ function* eachExtrasTarget(v, counts) {
|
|
|
1781
1839
|
counts.unmapped++;
|
|
1782
1840
|
continue;
|
|
1783
1841
|
}
|
|
1784
|
-
for (const
|
|
1785
|
-
if (!whitelist.includes(
|
|
1842
|
+
for (const dirname16 of dirnames) {
|
|
1843
|
+
if (!whitelist.includes(dirname16)) {
|
|
1786
1844
|
counts.skipped++;
|
|
1787
1845
|
continue;
|
|
1788
1846
|
}
|
|
1789
|
-
yield { logical, localRoot, dirname:
|
|
1847
|
+
yield { logical, localRoot, dirname: dirname16 };
|
|
1790
1848
|
}
|
|
1791
1849
|
}
|
|
1792
1850
|
}
|
|
@@ -1828,8 +1886,8 @@ function copyExtrasFileSkipDiverged(src, dst) {
|
|
|
1828
1886
|
}
|
|
1829
1887
|
copyExtras(src, dst);
|
|
1830
1888
|
}
|
|
1831
|
-
function extrasDenySet(
|
|
1832
|
-
return isClaudeExtraName(
|
|
1889
|
+
function extrasDenySet(dirname16) {
|
|
1890
|
+
return isClaudeExtraName(dirname16) ? CLAUDE_EXTRA_NEVER_SYNC : ALWAYS_NEVER_SYNC;
|
|
1833
1891
|
}
|
|
1834
1892
|
function copyExtrasFiltered(src, dst, blockSet) {
|
|
1835
1893
|
rmSync2(dst, { recursive: true, force: true });
|
|
@@ -1872,7 +1930,7 @@ function copyExtrasFilteredPreserving(src, dst, blockSet) {
|
|
|
1872
1930
|
});
|
|
1873
1931
|
}
|
|
1874
1932
|
|
|
1875
|
-
// src/fs-presence.ts
|
|
1933
|
+
// src/core/fs-presence.ts
|
|
1876
1934
|
import { lstatSync as lstatSync4, statSync } from "node:fs";
|
|
1877
1935
|
var ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR", "ENAMETOOLONG"]);
|
|
1878
1936
|
function classifyPresence(p) {
|
|
@@ -1897,14 +1955,110 @@ function lexists(p) {
|
|
|
1897
1955
|
return classifyPresence(p) !== "absent";
|
|
1898
1956
|
}
|
|
1899
1957
|
|
|
1900
|
-
// src/links.ts
|
|
1958
|
+
// src/sync/links.ts
|
|
1901
1959
|
init_config();
|
|
1902
1960
|
import { existsSync as existsSync4, lstatSync as lstatSync6, rmSync as rmSync3 } from "node:fs";
|
|
1903
1961
|
import { join as join6 } from "node:path";
|
|
1904
1962
|
|
|
1905
|
-
// src/
|
|
1963
|
+
// src/commands/capture-settings/core.ts
|
|
1964
|
+
init_utils_json();
|
|
1965
|
+
|
|
1966
|
+
// src/sync/hooks-filter.ts
|
|
1906
1967
|
init_config();
|
|
1907
|
-
|
|
1968
|
+
init_utils_json();
|
|
1969
|
+
|
|
1970
|
+
// src/sync/hooks-filter.command-sub.ts
|
|
1971
|
+
var SUB_START = /^"?(?:\$\(|`)/;
|
|
1972
|
+
function opensSubstitution(token) {
|
|
1973
|
+
return SUB_START.test(token);
|
|
1974
|
+
}
|
|
1975
|
+
function stepChar(token, i, scan) {
|
|
1976
|
+
const c = token[i];
|
|
1977
|
+
const top = scan.stack.at(-1);
|
|
1978
|
+
if (top === "sq") {
|
|
1979
|
+
if (c === "'") scan.stack.pop();
|
|
1980
|
+
return i + 1;
|
|
1981
|
+
}
|
|
1982
|
+
if (scan.escaped) {
|
|
1983
|
+
scan.escaped = false;
|
|
1984
|
+
return i + 1;
|
|
1985
|
+
}
|
|
1986
|
+
if (c === "\\") {
|
|
1987
|
+
scan.escaped = true;
|
|
1988
|
+
return i + 1;
|
|
1989
|
+
}
|
|
1990
|
+
if (c === "$" && token[i + 1] === "(") {
|
|
1991
|
+
scan.stack.push("cmd");
|
|
1992
|
+
scan.depth++;
|
|
1993
|
+
return i + 2;
|
|
1994
|
+
}
|
|
1995
|
+
if (c === "'" && top === "cmd") scan.stack.push("sq");
|
|
1996
|
+
else if (c === "`") toggleBacktick(scan, top);
|
|
1997
|
+
else if (c === '"') toggleDoubleQuote(scan, top);
|
|
1998
|
+
else if (top === "cmd") stepParen(c, scan);
|
|
1999
|
+
return i + 1;
|
|
2000
|
+
}
|
|
2001
|
+
function toggleBacktick(scan, top) {
|
|
2002
|
+
if (top === "bt") scan.stack.pop();
|
|
2003
|
+
else scan.stack.push("bt");
|
|
2004
|
+
}
|
|
2005
|
+
function toggleDoubleQuote(scan, top) {
|
|
2006
|
+
if (top === "dq") scan.stack.pop();
|
|
2007
|
+
else scan.stack.push("dq");
|
|
2008
|
+
}
|
|
2009
|
+
function stepParen(c, scan) {
|
|
2010
|
+
if (c === "(") {
|
|
2011
|
+
scan.stack.push("cmd");
|
|
2012
|
+
scan.depth++;
|
|
2013
|
+
} else if (c === ")") {
|
|
2014
|
+
scan.stack.pop();
|
|
2015
|
+
scan.depth--;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
function skipDollarParen(tokens, start, from) {
|
|
2019
|
+
const scan = { stack: ["cmd"], depth: 1, escaped: false };
|
|
2020
|
+
let i = from;
|
|
2021
|
+
for (let j = start; j < tokens.length; j++) {
|
|
2022
|
+
const token = tokens[j];
|
|
2023
|
+
while (i < token.length) {
|
|
2024
|
+
i = stepChar(token, i, scan);
|
|
2025
|
+
if (scan.depth === 0) return { next: j + 1, rest: token.slice(i) };
|
|
2026
|
+
}
|
|
2027
|
+
i = 0;
|
|
2028
|
+
scan.escaped = false;
|
|
2029
|
+
}
|
|
2030
|
+
return unterminated(tokens, start);
|
|
2031
|
+
}
|
|
2032
|
+
function skipBacktick(tokens, start, from) {
|
|
2033
|
+
let i = from;
|
|
2034
|
+
for (let j = start; j < tokens.length; j++) {
|
|
2035
|
+
const token = tokens[j];
|
|
2036
|
+
while (i < token.length) {
|
|
2037
|
+
if (token[i] === "\\") i += 2;
|
|
2038
|
+
else if (token[i] === "`") return { next: j + 1, rest: token.slice(i + 1) };
|
|
2039
|
+
else i++;
|
|
2040
|
+
}
|
|
2041
|
+
i = 0;
|
|
2042
|
+
}
|
|
2043
|
+
return unterminated(tokens, start);
|
|
2044
|
+
}
|
|
2045
|
+
function unterminated(tokens, start) {
|
|
2046
|
+
return { next: start + 1, rest: tokens[start] };
|
|
2047
|
+
}
|
|
2048
|
+
function skipSubstitution(tokens, start) {
|
|
2049
|
+
const token = tokens[start] ?? "";
|
|
2050
|
+
const paren = token.indexOf("$(");
|
|
2051
|
+
const tick = token.indexOf("`");
|
|
2052
|
+
if (paren < 0 && tick < 0) return { next: start + 1, rest: token };
|
|
2053
|
+
if (tick >= 0 && (paren < 0 || tick < paren)) return skipBacktick(tokens, start, tick + 1);
|
|
2054
|
+
return skipDollarParen(tokens, start, paren + 2);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/sync/hooks-filter.ts
|
|
2058
|
+
var KNOWN_LAUNCHER_BASENAMES = /* @__PURE__ */ new Set(["env", "node", "bash", "sh"]);
|
|
2059
|
+
var SUBSTITUTION_ARTIFACT = /[`()'"]/;
|
|
2060
|
+
var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/;
|
|
2061
|
+
var SHELL_OPERATORS = /* @__PURE__ */ new Set(["&&", "||", ";", "|", "&"]);
|
|
1908
2062
|
function scriptBasename(token) {
|
|
1909
2063
|
const lastSlash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
1910
2064
|
return lastSlash >= 0 ? token.slice(lastSlash + 1) : token;
|
|
@@ -1919,25 +2073,56 @@ function stripQuotes(token) {
|
|
|
1919
2073
|
}
|
|
1920
2074
|
return token;
|
|
1921
2075
|
}
|
|
2076
|
+
var NO_CANDIDATE = { index: -1, word: "" };
|
|
2077
|
+
function nextScriptWord(tokens, from, inScriptSlot) {
|
|
2078
|
+
let slot = inScriptSlot;
|
|
2079
|
+
let i = from;
|
|
2080
|
+
while (i < tokens.length) {
|
|
2081
|
+
const token = tokens[i];
|
|
2082
|
+
if (SHELL_OPERATORS.has(token)) {
|
|
2083
|
+
slot = false;
|
|
2084
|
+
i++;
|
|
2085
|
+
} else if (token.startsWith("-")) {
|
|
2086
|
+
i++;
|
|
2087
|
+
} else if (!opensSubstitution(token)) {
|
|
2088
|
+
return { index: i, word: token };
|
|
2089
|
+
} else {
|
|
2090
|
+
const end = skipSubstitution(tokens, i);
|
|
2091
|
+
if (end.rest !== "") return { index: end.next - 1, word: end.rest };
|
|
2092
|
+
if (slot && !SHELL_OPERATORS.has(tokens[end.next] ?? "")) return NO_CANDIDATE;
|
|
2093
|
+
i = end.next;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
return NO_CANDIDATE;
|
|
2097
|
+
}
|
|
2098
|
+
function resolveScriptWord(tokens, from, underEnv) {
|
|
2099
|
+
let candidate = nextScriptWord(tokens, from, true);
|
|
2100
|
+
while (underEnv && candidate.index >= 0) {
|
|
2101
|
+
const word = stripQuotes(candidate.word);
|
|
2102
|
+
const isBareInterpreter = !word.includes("/") && !word.includes("\\") && KNOWN_LAUNCHER_BASENAMES.has(word);
|
|
2103
|
+
if (!isBareInterpreter && !ENV_ASSIGNMENT.test(word)) return candidate;
|
|
2104
|
+
candidate = nextScriptWord(tokens, candidate.index + 1, true);
|
|
2105
|
+
}
|
|
2106
|
+
return candidate;
|
|
2107
|
+
}
|
|
1922
2108
|
function isGsdHookEntry(command) {
|
|
1923
2109
|
const tokens = command.trim().split(/\s+/);
|
|
1924
|
-
if (tokens
|
|
1925
|
-
const envAssign = /^[A-Za-z_]\w*=/;
|
|
2110
|
+
if (tokens[0] === "") return false;
|
|
1926
2111
|
let i = 0;
|
|
1927
|
-
while (i < tokens.length &&
|
|
2112
|
+
while (i < tokens.length && ENV_ASSIGNMENT.test(tokens[i])) {
|
|
1928
2113
|
i++;
|
|
1929
2114
|
}
|
|
1930
|
-
const
|
|
2115
|
+
const launcher = nextScriptWord(tokens, i, false);
|
|
2116
|
+
if (launcher.index < 0) return false;
|
|
2117
|
+
const first = stripQuotes(launcher.word);
|
|
1931
2118
|
const firstBase = scriptBasename(first);
|
|
1932
2119
|
const firstHasPath = first.includes("/") || first.includes("\\");
|
|
1933
|
-
if (
|
|
2120
|
+
if (!KNOWN_LAUNCHER_BASENAMES.has(firstBase) && (firstHasPath || !SUBSTITUTION_ARTIFACT.test(first))) {
|
|
1934
2121
|
return firstBase.startsWith(GSD_PREFIX);
|
|
1935
2122
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
1940
|
-
return false;
|
|
2123
|
+
const script = resolveScriptWord(tokens, launcher.index + 1, firstBase === "env");
|
|
2124
|
+
if (script.index < 0) return false;
|
|
2125
|
+
return scriptBasename(stripQuotes(script.word)).startsWith(GSD_PREFIX);
|
|
1941
2126
|
}
|
|
1942
2127
|
function filterMatcherEntry(entry) {
|
|
1943
2128
|
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return entry;
|
|
@@ -1965,24 +2150,26 @@ function filterEventMatchers(matchers) {
|
|
|
1965
2150
|
function stripGsdHookEntries(settings) {
|
|
1966
2151
|
const out = {};
|
|
1967
2152
|
for (const [key, value] of Object.entries(settings)) {
|
|
1968
|
-
if (key
|
|
2153
|
+
if (isProtoPollutionKey(key)) continue;
|
|
2154
|
+
const hooksObj = key === "hooks" ? asPlainObject(value) : null;
|
|
2155
|
+
if (hooksObj === null) {
|
|
1969
2156
|
out[key] = value;
|
|
1970
2157
|
continue;
|
|
1971
2158
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
continue;
|
|
1975
|
-
}
|
|
1976
|
-
const hooksObj = value;
|
|
1977
|
-
const filteredHooks = {};
|
|
1978
|
-
for (const [event, matchers] of Object.entries(hooksObj)) {
|
|
1979
|
-
const filtered = filterEventMatchers(matchers);
|
|
1980
|
-
if (filtered !== null) filteredHooks[event] = filtered;
|
|
1981
|
-
}
|
|
1982
|
-
if (Object.keys(filteredHooks).length > 0) out[key] = filteredHooks;
|
|
2159
|
+
const filteredHooks = filterHooksBlock(hooksObj);
|
|
2160
|
+
if (filteredHooks !== null) out[key] = filteredHooks;
|
|
1983
2161
|
}
|
|
1984
2162
|
return out;
|
|
1985
2163
|
}
|
|
2164
|
+
function filterHooksBlock(hooksObj) {
|
|
2165
|
+
const filteredHooks = {};
|
|
2166
|
+
for (const [event, matchers] of Object.entries(hooksObj)) {
|
|
2167
|
+
if (isProtoPollutionKey(event)) continue;
|
|
2168
|
+
const filtered = filterEventMatchers(matchers);
|
|
2169
|
+
if (filtered !== null) filteredHooks[event] = filtered;
|
|
2170
|
+
}
|
|
2171
|
+
return Object.keys(filteredHooks).length > 0 ? filteredHooks : null;
|
|
2172
|
+
}
|
|
1986
2173
|
function keepMatcherEntry(entry) {
|
|
1987
2174
|
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
1988
2175
|
const entryObj = entry;
|
|
@@ -2012,6 +2199,7 @@ function keepGsdHookEntries(settings) {
|
|
|
2012
2199
|
const hooksObj = hooksVal;
|
|
2013
2200
|
const keptHooks = {};
|
|
2014
2201
|
for (const [event, matchers] of Object.entries(hooksObj)) {
|
|
2202
|
+
if (isProtoPollutionKey(event)) continue;
|
|
2015
2203
|
const kept = keepEventMatchers(matchers);
|
|
2016
2204
|
if (kept !== null) keptHooks[event] = kept;
|
|
2017
2205
|
}
|
|
@@ -2046,7 +2234,7 @@ function graftGsdHookEntries(base, gsdOnly) {
|
|
|
2046
2234
|
const baseHooks = asPlainObject(base.hooks);
|
|
2047
2235
|
const mergedHooks = baseHooks ? { ...baseHooks } : {};
|
|
2048
2236
|
for (const [event, gsdMatchers] of Object.entries(gsdHooks)) {
|
|
2049
|
-
if (!Array.isArray(gsdMatchers)) continue;
|
|
2237
|
+
if (isProtoPollutionKey(event) || !Array.isArray(gsdMatchers)) continue;
|
|
2050
2238
|
const baseMatchers = mergedHooks[event];
|
|
2051
2239
|
mergedHooks[event] = Array.isArray(baseMatchers) ? unionMatcherArrays(baseMatchers, gsdMatchers) : gsdMatchers;
|
|
2052
2240
|
}
|
|
@@ -2077,7 +2265,7 @@ function baseHasGsdHookEntries(settings) {
|
|
|
2077
2265
|
return false;
|
|
2078
2266
|
}
|
|
2079
2267
|
|
|
2080
|
-
// src/commands
|
|
2268
|
+
// src/commands/capture-settings/core.ts
|
|
2081
2269
|
function arraysEqual(a, b) {
|
|
2082
2270
|
if (a.length !== b.length) return false;
|
|
2083
2271
|
for (let i = 0; i < a.length; i++) {
|
|
@@ -2171,6 +2359,7 @@ function normalizeNodePathsDeep(value) {
|
|
|
2171
2359
|
if (value !== null && typeof value === "object") {
|
|
2172
2360
|
const out = {};
|
|
2173
2361
|
for (const [k, v] of Object.entries(value)) {
|
|
2362
|
+
if (isProtoPollutionKey(k)) continue;
|
|
2174
2363
|
out[k] = normalizeNodePathsDeep(v);
|
|
2175
2364
|
}
|
|
2176
2365
|
return out;
|
|
@@ -2189,7 +2378,7 @@ function buildCaptureSubset(merged, settings, opts) {
|
|
|
2189
2378
|
return out;
|
|
2190
2379
|
}
|
|
2191
2380
|
|
|
2192
|
-
// src/links.ts
|
|
2381
|
+
// src/sync/links.ts
|
|
2193
2382
|
init_utils();
|
|
2194
2383
|
init_utils_fs();
|
|
2195
2384
|
init_utils_json();
|
|
@@ -2375,7 +2564,7 @@ function regenerateSettings(ts, opts = {}) {
|
|
|
2375
2564
|
return { label: overrideLabel };
|
|
2376
2565
|
}
|
|
2377
2566
|
|
|
2378
|
-
// src/commands
|
|
2567
|
+
// src/commands/adopt.recover.ts
|
|
2379
2568
|
init_utils();
|
|
2380
2569
|
function isDirectChildOf(root, abs) {
|
|
2381
2570
|
return dirname2(resolve2(abs)) === resolve2(root);
|
|
@@ -2487,14 +2676,14 @@ function reportSourceRemovalFailure(name, linkPath, message, stage) {
|
|
|
2487
2676
|
);
|
|
2488
2677
|
}
|
|
2489
2678
|
|
|
2490
|
-
// src/commands
|
|
2679
|
+
// src/commands/adopt.ts
|
|
2491
2680
|
init_config();
|
|
2492
2681
|
init_config_sharedDirs_guard();
|
|
2493
2682
|
init_exit_codes();
|
|
2494
2683
|
init_utils();
|
|
2495
2684
|
init_utils_fs();
|
|
2496
2685
|
|
|
2497
|
-
// src/utils.lockfile.ts
|
|
2686
|
+
// src/core/utils.lockfile.ts
|
|
2498
2687
|
init_config();
|
|
2499
2688
|
init_utils();
|
|
2500
2689
|
import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -2607,7 +2796,7 @@ function retryOnce(verb, lp) {
|
|
|
2607
2796
|
}
|
|
2608
2797
|
}
|
|
2609
2798
|
|
|
2610
|
-
// src/commands
|
|
2799
|
+
// src/commands/adopt.ts
|
|
2611
2800
|
init_utils_json();
|
|
2612
2801
|
var ADOPT_PUSH_HINT = "run `nomad push` to share with other hosts";
|
|
2613
2802
|
function readMapIfPresent(repoHome2) {
|
|
@@ -2689,7 +2878,11 @@ function adoptStopsEarly(name, linkPath, sharedTarget) {
|
|
|
2689
2878
|
}
|
|
2690
2879
|
function performAdoptMove(name, linkPath, sharedTarget, repo, backup) {
|
|
2691
2880
|
const ts = freshBackupTs(backup);
|
|
2692
|
-
const snapshotted = backupBeforeWrite(
|
|
2881
|
+
const snapshotted = backupBeforeWrite(
|
|
2882
|
+
linkPath,
|
|
2883
|
+
ts,
|
|
2884
|
+
`Nothing was removed from ${linkPath} and nothing was written to the repo. Fix what the error names, then run \`nomad adopt ${name}\` again.`
|
|
2885
|
+
);
|
|
2693
2886
|
const rel = join9("shared", name);
|
|
2694
2887
|
const stage = () => gitOrFatal(["add", "--", rel], `git add shared/${name}`, repo);
|
|
2695
2888
|
copyIntoSharedOrFatal(name, linkPath, sharedTarget, repo);
|
|
@@ -2767,11 +2960,11 @@ function adoptPreflightAndMove(name, linkPath, sharedTarget, repo, backup, dryRu
|
|
|
2767
2960
|
}
|
|
2768
2961
|
}
|
|
2769
2962
|
|
|
2770
|
-
// src/commands
|
|
2963
|
+
// src/commands/allow.ts
|
|
2771
2964
|
init_config();
|
|
2772
2965
|
import { existsSync as existsSync6 } from "node:fs";
|
|
2773
2966
|
|
|
2774
|
-
// src/commands
|
|
2967
|
+
// src/commands/redact/core.ts
|
|
2775
2968
|
import { appendFileSync, readFileSync as readFileSync4 } from "node:fs";
|
|
2776
2969
|
import { join as join10 } from "node:path";
|
|
2777
2970
|
function collectMatchIntervals(content, findings) {
|
|
@@ -2845,7 +3038,7 @@ function appendGitleaksIgnore(fingerprint, repo) {
|
|
|
2845
3038
|
appendFileSync(ignPath, prefix + formatFingerprint(sanitized), "utf8");
|
|
2846
3039
|
}
|
|
2847
3040
|
|
|
2848
|
-
// src/commands
|
|
3041
|
+
// src/commands/allow.ts
|
|
2849
3042
|
init_exit_codes();
|
|
2850
3043
|
init_utils();
|
|
2851
3044
|
function cmdAllow(fingerprints) {
|
|
@@ -2865,7 +3058,7 @@ function cmdAllow(fingerprints) {
|
|
|
2865
3058
|
log(`allowed ${fingerprints.length} fingerprint(s)`);
|
|
2866
3059
|
}
|
|
2867
3060
|
|
|
2868
|
-
// src/commands
|
|
3061
|
+
// src/commands/capture-settings/capture-settings.ts
|
|
2869
3062
|
init_config();
|
|
2870
3063
|
import { existsSync as existsSync7 } from "node:fs";
|
|
2871
3064
|
import { join as join11 } from "node:path";
|
|
@@ -2947,10 +3140,11 @@ async function cmdCaptureSettings(opts) {
|
|
|
2947
3140
|
}
|
|
2948
3141
|
}
|
|
2949
3142
|
|
|
2950
|
-
// src/commands
|
|
3143
|
+
// src/commands/clean.ts
|
|
2951
3144
|
init_config();
|
|
2952
3145
|
init_exit_codes();
|
|
2953
3146
|
init_utils();
|
|
3147
|
+
init_utils_fs();
|
|
2954
3148
|
import { existsSync as existsSync8, lstatSync as lstatSync8, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync3 } from "node:fs";
|
|
2955
3149
|
import { join as join12 } from "node:path";
|
|
2956
3150
|
var TS_SHAPE = /^\d{8}-\d{6}(-\d+)?$/;
|
|
@@ -2985,12 +3179,6 @@ function holdsNoContent(dir) {
|
|
|
2985
3179
|
}
|
|
2986
3180
|
return true;
|
|
2987
3181
|
}
|
|
2988
|
-
function prunableByAge(dirs, olderThanMs, nowMs) {
|
|
2989
|
-
return dirs.filter((d) => nowMs - d.mtimeMs > olderThanMs).map((d) => d.name);
|
|
2990
|
-
}
|
|
2991
|
-
function prunableByCount(dirs, keep) {
|
|
2992
|
-
return dirs.slice(keep).map((d) => d.name);
|
|
2993
|
-
}
|
|
2994
3182
|
function safeDelete(backupBase2, name) {
|
|
2995
3183
|
if (!isTsDir(name)) return;
|
|
2996
3184
|
const full = join12(backupBase2, name);
|
|
@@ -3050,7 +3238,7 @@ function pruneBackups(opts, backupBase2) {
|
|
|
3050
3238
|
log(`removed ${targets.length} backup(s)`);
|
|
3051
3239
|
}
|
|
3052
3240
|
|
|
3053
|
-
// src/commands
|
|
3241
|
+
// src/commands/eject.ts
|
|
3054
3242
|
init_config();
|
|
3055
3243
|
init_config_sharedDirs_guard();
|
|
3056
3244
|
init_utils();
|
|
@@ -3250,20 +3438,20 @@ function cmdEject(opts = {}, roots = defaultEjectRoots()) {
|
|
|
3250
3438
|
}
|
|
3251
3439
|
}
|
|
3252
3440
|
|
|
3253
|
-
// src/commands
|
|
3254
|
-
import { existsSync as
|
|
3255
|
-
import { join as
|
|
3441
|
+
// src/commands/doctor/doctor.ts
|
|
3442
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
3443
|
+
import { join as join52 } from "node:path";
|
|
3256
3444
|
|
|
3257
|
-
// src/commands
|
|
3445
|
+
// src/commands/doctor/checks/repo.ts
|
|
3258
3446
|
init_color();
|
|
3259
3447
|
init_config();
|
|
3260
3448
|
import { existsSync as existsSync11, lstatSync as lstatSync10, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
3261
3449
|
import { join as join17 } from "node:path";
|
|
3262
3450
|
|
|
3263
|
-
// src/commands
|
|
3451
|
+
// src/commands/doctor/format.ts
|
|
3264
3452
|
init_color();
|
|
3265
3453
|
|
|
3266
|
-
// src/output-tree.ts
|
|
3454
|
+
// src/render/output-tree.ts
|
|
3267
3455
|
init_color();
|
|
3268
3456
|
var FAIL_GLYPH_BARE = "\u2717";
|
|
3269
3457
|
function section(header, raw = false) {
|
|
@@ -3321,7 +3509,7 @@ function renderTree(sections) {
|
|
|
3321
3509
|
}
|
|
3322
3510
|
var renderDoctor = renderTree;
|
|
3323
3511
|
|
|
3324
|
-
// src/commands
|
|
3512
|
+
// src/commands/doctor/format.ts
|
|
3325
3513
|
init_utils_json();
|
|
3326
3514
|
function readJsonSafe(path, label, section2) {
|
|
3327
3515
|
try {
|
|
@@ -3333,12 +3521,12 @@ function readJsonSafe(path, label, section2) {
|
|
|
3333
3521
|
}
|
|
3334
3522
|
}
|
|
3335
3523
|
|
|
3336
|
-
// src/commands
|
|
3524
|
+
// src/commands/doctor/checks/repo.win32.ts
|
|
3337
3525
|
init_color();
|
|
3338
3526
|
init_config();
|
|
3339
3527
|
import { join as join14, win32 as win32Path } from "node:path";
|
|
3340
3528
|
|
|
3341
|
-
// src/extras
|
|
3529
|
+
// src/sync/extras/diff.ts
|
|
3342
3530
|
init_utils();
|
|
3343
3531
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
3344
3532
|
import { relative as relative4 } from "node:path";
|
|
@@ -3393,7 +3581,7 @@ function listDivergingModified(a, b) {
|
|
|
3393
3581
|
return runNameStatusDiff(a, b, (stdout) => parseModifiedPaths(stdout, a));
|
|
3394
3582
|
}
|
|
3395
3583
|
|
|
3396
|
-
// src/commands
|
|
3584
|
+
// src/commands/doctor/checks/repo.win32.ts
|
|
3397
3585
|
function win32CopyOkRow(name, exempt = 0) {
|
|
3398
3586
|
const note = exempt > 0 ? dim(` (${exempt} never-synced path(s) not compared)`) : "";
|
|
3399
3587
|
return { line: `${green(okGlyph)} ${name}: real copy (win32 copy-sync)${note}`, fail: false };
|
|
@@ -3445,7 +3633,7 @@ function classifyWin32Copy(name, p) {
|
|
|
3445
3633
|
};
|
|
3446
3634
|
}
|
|
3447
3635
|
|
|
3448
|
-
// src/commands
|
|
3636
|
+
// src/commands/doctor/checks/repo.source.ts
|
|
3449
3637
|
init_color();
|
|
3450
3638
|
init_config();
|
|
3451
3639
|
import { join as join15 } from "node:path";
|
|
@@ -3496,7 +3684,7 @@ function repoSourceRow(name, present, absent) {
|
|
|
3496
3684
|
return state === "resolves" ? present : absent;
|
|
3497
3685
|
}
|
|
3498
3686
|
|
|
3499
|
-
// src/init
|
|
3687
|
+
// src/commands/init/classify.ts
|
|
3500
3688
|
init_config();
|
|
3501
3689
|
init_utils_json();
|
|
3502
3690
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -3539,7 +3727,7 @@ function reasonForPartial(repoHome2, host) {
|
|
|
3539
3727
|
return "- partial state (unknown gap)";
|
|
3540
3728
|
}
|
|
3541
3729
|
|
|
3542
|
-
// src/commands
|
|
3730
|
+
// src/commands/doctor/checks/repo.ts
|
|
3543
3731
|
init_utils_json();
|
|
3544
3732
|
function isOverrideActive() {
|
|
3545
3733
|
return Boolean(process.env.NOMAD_REPO);
|
|
@@ -3698,7 +3886,7 @@ function reportDroppedNamesMigration(section2) {
|
|
|
3698
3886
|
}
|
|
3699
3887
|
}
|
|
3700
3888
|
|
|
3701
|
-
// src/commands
|
|
3889
|
+
// src/commands/doctor/checks/settings.ts
|
|
3702
3890
|
init_color();
|
|
3703
3891
|
init_config();
|
|
3704
3892
|
import { existsSync as existsSync12, readdirSync as readdirSync6 } from "node:fs";
|
|
@@ -3759,7 +3947,7 @@ function reportHostOverrides(section2, base, settings) {
|
|
|
3759
3947
|
}
|
|
3760
3948
|
}
|
|
3761
3949
|
|
|
3762
|
-
// src/commands
|
|
3950
|
+
// src/commands/doctor/checks/pathmap.ts
|
|
3763
3951
|
init_color();
|
|
3764
3952
|
init_config();
|
|
3765
3953
|
init_config_sharedDirs_guard();
|
|
@@ -3945,7 +4133,7 @@ function reportNeverSync(section2) {
|
|
|
3945
4133
|
);
|
|
3946
4134
|
}
|
|
3947
4135
|
|
|
3948
|
-
// src/commands
|
|
4136
|
+
// src/commands/doctor/checks/skills.ts
|
|
3949
4137
|
init_color();
|
|
3950
4138
|
import { existsSync as existsSync14 } from "node:fs";
|
|
3951
4139
|
import { join as join20 } from "node:path";
|
|
@@ -3993,18 +4181,37 @@ function reportSkillsDivergence(section2) {
|
|
|
3993
4181
|
}
|
|
3994
4182
|
}
|
|
3995
4183
|
|
|
3996
|
-
// src/commands
|
|
4184
|
+
// src/commands/doctor/checks/git-state.ts
|
|
3997
4185
|
init_color();
|
|
3998
4186
|
init_config();
|
|
3999
|
-
import { execFileSync as
|
|
4000
|
-
import { existsSync as
|
|
4001
|
-
import { join as
|
|
4002
|
-
|
|
4003
|
-
|
|
4187
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
4188
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
4189
|
+
import { join as join25, relative as relative5 } from "node:path";
|
|
4190
|
+
init_wedge();
|
|
4191
|
+
|
|
4192
|
+
// src/core/git-probe.ts
|
|
4193
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
4194
|
+
var PROBE_TIMEOUT_MS = 1e4;
|
|
4195
|
+
var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
|
|
4196
|
+
function gitProbe(args, repo) {
|
|
4197
|
+
try {
|
|
4198
|
+
return execFileSync4("git", args, {
|
|
4199
|
+
cwd: repo,
|
|
4200
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4201
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
4202
|
+
maxBuffer: PROBE_MAX_BUFFER
|
|
4203
|
+
}).toString();
|
|
4204
|
+
} catch {
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// src/commands/doctor/checks/git-state.ts
|
|
4210
|
+
init_checks();
|
|
4004
4211
|
init_utils();
|
|
4005
4212
|
function reportGitleaksProbe(section2) {
|
|
4006
4213
|
try {
|
|
4007
|
-
|
|
4214
|
+
execFileSync6("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
4008
4215
|
return true;
|
|
4009
4216
|
} catch (err) {
|
|
4010
4217
|
if (err.code === "ENOENT") {
|
|
@@ -4021,8 +4228,8 @@ function reportGitleaksProbe(section2) {
|
|
|
4021
4228
|
}
|
|
4022
4229
|
function reportGitlinks(section2) {
|
|
4023
4230
|
const repo = repoHome();
|
|
4024
|
-
const sharedDir =
|
|
4025
|
-
if (
|
|
4231
|
+
const sharedDir = join25(repo, "shared");
|
|
4232
|
+
if (existsSync18(sharedDir)) {
|
|
4026
4233
|
const gitlinks = findGitlinks(sharedDir);
|
|
4027
4234
|
for (const p of gitlinks) {
|
|
4028
4235
|
const rel = relative5(repo, p).replaceAll("\\", "/");
|
|
@@ -4040,7 +4247,7 @@ function reportGitlinks(section2) {
|
|
|
4040
4247
|
}
|
|
4041
4248
|
function reportRemote(section2) {
|
|
4042
4249
|
try {
|
|
4043
|
-
const url =
|
|
4250
|
+
const url = execFileSync6("git", ["remote", "get-url", "origin"], {
|
|
4044
4251
|
cwd: repoHome(),
|
|
4045
4252
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4046
4253
|
}).toString().trim();
|
|
@@ -4097,7 +4304,7 @@ function reportGitIdentity(section2) {
|
|
|
4097
4304
|
const missing = [];
|
|
4098
4305
|
for (const field of ["user.name", "user.email"]) {
|
|
4099
4306
|
try {
|
|
4100
|
-
const val =
|
|
4307
|
+
const val = execFileSync6("git", ["config", field], {
|
|
4101
4308
|
cwd: repo,
|
|
4102
4309
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4103
4310
|
}).toString().trim();
|
|
@@ -4117,11 +4324,50 @@ function reportGitIdentity(section2) {
|
|
|
4117
4324
|
addItem(section2, `${green(okGlyph)} git identity: user.name and user.email configured`);
|
|
4118
4325
|
}
|
|
4119
4326
|
}
|
|
4327
|
+
var MAX_TRACKED_DENIED_ROWS = 5;
|
|
4328
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
4329
|
+
function renderable(text) {
|
|
4330
|
+
return text.replace(CONTROL_CHARS, " ");
|
|
4331
|
+
}
|
|
4332
|
+
function reportTrackedDeniedShared(section2) {
|
|
4333
|
+
const out = gitProbe(["ls-files", "-z", "--", ":(icase)shared/"], repoHome());
|
|
4334
|
+
if (out === null) {
|
|
4335
|
+
addItem(
|
|
4336
|
+
section2,
|
|
4337
|
+
`${dim(infoGlyph)} never-sync scan: git could not list shared/, so nothing was scanned`
|
|
4338
|
+
);
|
|
4339
|
+
return;
|
|
4340
|
+
}
|
|
4341
|
+
const hits = /* @__PURE__ */ new Map();
|
|
4342
|
+
for (const path of out.split("\0")) {
|
|
4343
|
+
const segment = path === "" ? null : deniedSegmentFor(path);
|
|
4344
|
+
if (segment !== null) hits.set(path, segment);
|
|
4345
|
+
}
|
|
4346
|
+
if (hits.size === 0) {
|
|
4347
|
+
addItem(
|
|
4348
|
+
section2,
|
|
4349
|
+
`${green(okGlyph)} never-sync scan: no tracked path under shared/ is denylisted`
|
|
4350
|
+
);
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4353
|
+
for (const [path, segment] of [...hits].slice(0, MAX_TRACKED_DENIED_ROWS)) {
|
|
4354
|
+
addItem(
|
|
4355
|
+
section2,
|
|
4356
|
+
`${yellow(warnGlyph)} never-sync: git tracks ${blue(renderable(path))} (the path segment "${renderable(segment)}" is blocked by the never-sync boundary)`
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
const overflow = hits.size - MAX_TRACKED_DENIED_ROWS;
|
|
4360
|
+
const more = overflow > 0 ? `${overflow} more not listed, run git ls-files -- ":(icase)shared/" in the sync repo to see them all. ` : "";
|
|
4361
|
+
addItem(
|
|
4362
|
+
section2,
|
|
4363
|
+
`${yellow(warnGlyph)} never-sync: ${more}Run git rm --cached -- "<path>" and commit to stop tracking each one; that leaves the file on disk, so move it outside shared/ if you want to keep it. If one holds a real secret, rotate it and rewrite history too, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent`
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4120
4366
|
|
|
4121
|
-
// src/commands
|
|
4367
|
+
// src/commands/doctor/checks/backups.ts
|
|
4122
4368
|
init_color();
|
|
4123
|
-
import { existsSync as
|
|
4124
|
-
import { join as
|
|
4369
|
+
import { existsSync as existsSync19, lstatSync as lstatSync12, readdirSync as readdirSync9 } from "node:fs";
|
|
4370
|
+
import { join as join26 } from "node:path";
|
|
4125
4371
|
init_config();
|
|
4126
4372
|
var TS_SHAPE2 = /^\d{8}-\d{6}(-\d+)?$/;
|
|
4127
4373
|
function safeReaddir(dir) {
|
|
@@ -4137,7 +4383,7 @@ var BYTES_PER_MB = 1024 * 1024;
|
|
|
4137
4383
|
function dirSizeBytes(dir) {
|
|
4138
4384
|
let bytes = 0;
|
|
4139
4385
|
for (const entry of safeReaddir(dir)) {
|
|
4140
|
-
const full =
|
|
4386
|
+
const full = join26(dir, entry);
|
|
4141
4387
|
const st = lstatSync12(full, { throwIfNoEntry: false });
|
|
4142
4388
|
if (!st) continue;
|
|
4143
4389
|
if (st.isSymbolicLink()) continue;
|
|
@@ -4148,11 +4394,11 @@ function dirSizeBytes(dir) {
|
|
|
4148
4394
|
}
|
|
4149
4395
|
function totalSizeMb(backupBase2, dirs) {
|
|
4150
4396
|
let bytes = 0;
|
|
4151
|
-
for (const name of dirs) bytes += dirSizeBytes(
|
|
4397
|
+
for (const name of dirs) bytes += dirSizeBytes(join26(backupBase2, name));
|
|
4152
4398
|
return bytes / BYTES_PER_MB;
|
|
4153
4399
|
}
|
|
4154
4400
|
function reportBackupsCheck(section2, backupBase2 = backupBase()) {
|
|
4155
|
-
if (!
|
|
4401
|
+
if (!existsSync19(backupBase2)) return;
|
|
4156
4402
|
const dirs = safeReaddir(backupBase2).filter((n) => TS_SHAPE2.test(n));
|
|
4157
4403
|
const count = dirs.length;
|
|
4158
4404
|
const sizeMb = totalSizeMb(backupBase2, dirs);
|
|
@@ -4164,16 +4410,16 @@ function reportBackupsCheck(section2, backupBase2 = backupBase()) {
|
|
|
4164
4410
|
}
|
|
4165
4411
|
}
|
|
4166
4412
|
|
|
4167
|
-
// src/commands
|
|
4413
|
+
// src/commands/doctor/checks/remote.ts
|
|
4168
4414
|
init_color();
|
|
4169
|
-
import { execFileSync as
|
|
4415
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
4170
4416
|
init_config();
|
|
4171
4417
|
init_utils_json();
|
|
4172
4418
|
var GIT_REMOTE_TIMEOUT_MS = 3e3;
|
|
4173
4419
|
function readRemotePathMap(sec, repo) {
|
|
4174
4420
|
let rawJson;
|
|
4175
4421
|
try {
|
|
4176
|
-
rawJson =
|
|
4422
|
+
rawJson = execFileSync7("git", ["show", "origin/main:path-map.json"], {
|
|
4177
4423
|
cwd: repo,
|
|
4178
4424
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4179
4425
|
timeout: GIT_REMOTE_TIMEOUT_MS
|
|
@@ -4206,7 +4452,7 @@ function reportCheckRemote(section2) {
|
|
|
4206
4452
|
const repo = repoHome();
|
|
4207
4453
|
let names;
|
|
4208
4454
|
try {
|
|
4209
|
-
const out =
|
|
4455
|
+
const out = execFileSync7("git", ["ls-tree", "--name-only", "origin/main"], {
|
|
4210
4456
|
cwd: repo,
|
|
4211
4457
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4212
4458
|
timeout: GIT_REMOTE_TIMEOUT_MS
|
|
@@ -4231,16 +4477,16 @@ function reportCheckRemote(section2) {
|
|
|
4231
4477
|
addItem(section2, `${green(okGlyph)} remote: origin/main has shared/ and a valid path-map.json`);
|
|
4232
4478
|
}
|
|
4233
4479
|
|
|
4234
|
-
// src/commands
|
|
4480
|
+
// src/commands/doctor/checks/schema.ts
|
|
4235
4481
|
init_color();
|
|
4236
|
-
import { existsSync as
|
|
4237
|
-
import { join as
|
|
4482
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
4483
|
+
import { join as join27 } from "node:path";
|
|
4238
4484
|
init_config();
|
|
4239
4485
|
|
|
4240
|
-
// src/http-fetch.ts
|
|
4241
|
-
import { execFileSync as
|
|
4486
|
+
// src/core/http-fetch.ts
|
|
4487
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
4242
4488
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
4243
|
-
function fetchUrl(url, run =
|
|
4489
|
+
function fetchUrl(url, run = execFileSync8) {
|
|
4244
4490
|
try {
|
|
4245
4491
|
return run("curl", ["-fsSL", "-m", "3", url], {
|
|
4246
4492
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4258,7 +4504,7 @@ function fetchUrl(url, run = execFileSync7) {
|
|
|
4258
4504
|
}
|
|
4259
4505
|
}
|
|
4260
4506
|
|
|
4261
|
-
// src/commands
|
|
4507
|
+
// src/commands/doctor/checks/schema.ts
|
|
4262
4508
|
function fetchSchemaKeys() {
|
|
4263
4509
|
try {
|
|
4264
4510
|
const raw = fetchUrl(SETTINGS_SCHEMA_URL);
|
|
@@ -4271,8 +4517,8 @@ function fetchSchemaKeys() {
|
|
|
4271
4517
|
}
|
|
4272
4518
|
}
|
|
4273
4519
|
function reportCheckSchema(section2) {
|
|
4274
|
-
const settingsPath =
|
|
4275
|
-
if (!
|
|
4520
|
+
const settingsPath = join27(claudeHome(), "settings.json");
|
|
4521
|
+
if (!existsSync20(settingsPath)) {
|
|
4276
4522
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json to check`);
|
|
4277
4523
|
return;
|
|
4278
4524
|
}
|
|
@@ -4298,29 +4544,29 @@ function reportCheckSchema(section2) {
|
|
|
4298
4544
|
}
|
|
4299
4545
|
}
|
|
4300
4546
|
|
|
4301
|
-
// src/commands
|
|
4547
|
+
// src/commands/doctor/checks/shared.ts
|
|
4302
4548
|
init_color();
|
|
4303
4549
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
4304
|
-
import { execFileSync as
|
|
4305
|
-
import { existsSync as
|
|
4550
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
4551
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync8, readdirSync as readdirSync12, rmSync as rmSync12 } from "node:fs";
|
|
4306
4552
|
import { homedir as homedir6 } from "node:os";
|
|
4307
|
-
import { join as
|
|
4553
|
+
import { join as join34 } from "node:path";
|
|
4308
4554
|
|
|
4309
|
-
// src/commands
|
|
4555
|
+
// src/commands/doctor/checks/shared.memory.ts
|
|
4310
4556
|
init_color();
|
|
4311
|
-
import { execFileSync as
|
|
4557
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
4312
4558
|
import { randomBytes } from "node:crypto";
|
|
4313
4559
|
import { mkdirSync as mkdirSync4, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4314
4560
|
import { homedir as homedir4 } from "node:os";
|
|
4315
|
-
import { dirname as
|
|
4561
|
+
import { dirname as dirname5, join as join29 } from "node:path";
|
|
4316
4562
|
init_config();
|
|
4317
|
-
|
|
4563
|
+
init_gitleaks();
|
|
4318
4564
|
init_utils_fs();
|
|
4319
4565
|
var MEMORY_MD_PATH = /^shared\/projects\/([^/]+)\/memory\/[^/]+\.md$/;
|
|
4320
4566
|
function buildMemoryScanTree(tmpRoot) {
|
|
4321
4567
|
let paths;
|
|
4322
4568
|
try {
|
|
4323
|
-
const out =
|
|
4569
|
+
const out = execFileSync10(
|
|
4324
4570
|
"git",
|
|
4325
4571
|
["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/projects"],
|
|
4326
4572
|
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
|
|
@@ -4336,7 +4582,7 @@ function buildMemoryScanTree(tmpRoot) {
|
|
|
4336
4582
|
if (m?.[1] === void 0) continue;
|
|
4337
4583
|
let blob;
|
|
4338
4584
|
try {
|
|
4339
|
-
blob =
|
|
4585
|
+
blob = execFileSync10("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
|
|
4340
4586
|
stdio: ["ignore", "pipe", "ignore"],
|
|
4341
4587
|
maxBuffer: 67108864,
|
|
4342
4588
|
timeout: 1e4
|
|
@@ -4345,8 +4591,8 @@ function buildMemoryScanTree(tmpRoot) {
|
|
|
4345
4591
|
incomplete = true;
|
|
4346
4592
|
continue;
|
|
4347
4593
|
}
|
|
4348
|
-
const dest =
|
|
4349
|
-
mkdirSync4(
|
|
4594
|
+
const dest = join29(tmpRoot, rel);
|
|
4595
|
+
mkdirSync4(dirname5(dest), { recursive: true });
|
|
4350
4596
|
writeFileSync4(dest, blob);
|
|
4351
4597
|
logicals.add(m[1]);
|
|
4352
4598
|
}
|
|
@@ -4364,10 +4610,10 @@ function reportMemoryFindings(section2, findings) {
|
|
|
4364
4610
|
function reportCommittedMemory(section2) {
|
|
4365
4611
|
let tmpRoot;
|
|
4366
4612
|
try {
|
|
4367
|
-
const cacheDir =
|
|
4613
|
+
const cacheDir = join29(homedir4(), ".cache", "claude-nomad");
|
|
4368
4614
|
mkdirSync4(cacheDir, { recursive: true });
|
|
4369
4615
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
4370
|
-
tmpRoot =
|
|
4616
|
+
tmpRoot = join29(cacheDir, `check-shared-memory-tree-${stamp}`);
|
|
4371
4617
|
mkdirSync4(tmpRoot, { recursive: true, mode: 448 });
|
|
4372
4618
|
const { staged, incomplete } = buildMemoryScanTree(tmpRoot);
|
|
4373
4619
|
if (incomplete) {
|
|
@@ -4412,14 +4658,14 @@ function reportCommittedMemory(section2) {
|
|
|
4412
4658
|
}
|
|
4413
4659
|
}
|
|
4414
4660
|
|
|
4415
|
-
// src/commands
|
|
4661
|
+
// src/commands/doctor/checks/shared.scan.ts
|
|
4416
4662
|
init_color();
|
|
4417
|
-
import { join as
|
|
4663
|
+
import { join as join30 } from "node:path";
|
|
4418
4664
|
init_config();
|
|
4419
|
-
|
|
4665
|
+
init_gitleaks();
|
|
4420
4666
|
function scrubPath(logical, sid, logicalToEncoded) {
|
|
4421
4667
|
const encoded = logicalToEncoded.get(logical) ?? logical;
|
|
4422
|
-
return
|
|
4668
|
+
return join30(claudeHome(), "projects", encoded, `${sid}.jsonl`);
|
|
4423
4669
|
}
|
|
4424
4670
|
function reportSessionFindings(section2, bySession) {
|
|
4425
4671
|
for (const [sid, counts] of bySession) {
|
|
@@ -4502,21 +4748,21 @@ function scanAndReport(section2, tmpRoot, staged, logicalToEncoded) {
|
|
|
4502
4748
|
emitDescriptionLegend(section2, findings);
|
|
4503
4749
|
}
|
|
4504
4750
|
|
|
4505
|
-
// src/commands
|
|
4751
|
+
// src/commands/doctor/checks/shared.skills.ts
|
|
4506
4752
|
init_color();
|
|
4507
|
-
import { execFileSync as
|
|
4753
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
4508
4754
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4509
4755
|
import { mkdirSync as mkdirSync5, rmSync as rmSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4510
4756
|
import { homedir as homedir5 } from "node:os";
|
|
4511
|
-
import { dirname as
|
|
4757
|
+
import { dirname as dirname6, join as join31 } from "node:path";
|
|
4512
4758
|
init_config();
|
|
4513
|
-
|
|
4759
|
+
init_gitleaks();
|
|
4514
4760
|
init_utils_fs();
|
|
4515
4761
|
var SKILL_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
|
|
4516
4762
|
function buildSkillScanTree(tmpRoot) {
|
|
4517
4763
|
let paths;
|
|
4518
4764
|
try {
|
|
4519
|
-
const out =
|
|
4765
|
+
const out = execFileSync11(
|
|
4520
4766
|
"git",
|
|
4521
4767
|
["-C", repoHome(), "ls-tree", "-r", "-z", "--name-only", "HEAD", "--", "shared/skills"],
|
|
4522
4768
|
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1e4 }
|
|
@@ -4532,7 +4778,7 @@ function buildSkillScanTree(tmpRoot) {
|
|
|
4532
4778
|
if (m?.[1] === void 0) continue;
|
|
4533
4779
|
let blob;
|
|
4534
4780
|
try {
|
|
4535
|
-
blob =
|
|
4781
|
+
blob = execFileSync11("git", ["-C", repoHome(), "cat-file", "blob", `HEAD:${rel}`], {
|
|
4536
4782
|
stdio: ["ignore", "pipe", "ignore"],
|
|
4537
4783
|
maxBuffer: 67108864,
|
|
4538
4784
|
timeout: 1e4
|
|
@@ -4541,8 +4787,8 @@ function buildSkillScanTree(tmpRoot) {
|
|
|
4541
4787
|
incomplete = true;
|
|
4542
4788
|
continue;
|
|
4543
4789
|
}
|
|
4544
|
-
const dest =
|
|
4545
|
-
mkdirSync5(
|
|
4790
|
+
const dest = join31(tmpRoot, rel);
|
|
4791
|
+
mkdirSync5(dirname6(dest), { recursive: true });
|
|
4546
4792
|
writeFileSync5(dest, blob);
|
|
4547
4793
|
names.add(m[1]);
|
|
4548
4794
|
}
|
|
@@ -4564,10 +4810,10 @@ function reportSkillFindings(section2, findings) {
|
|
|
4564
4810
|
function reportCommittedSkills(section2) {
|
|
4565
4811
|
let tmpRoot;
|
|
4566
4812
|
try {
|
|
4567
|
-
const cacheDir =
|
|
4813
|
+
const cacheDir = join31(homedir5(), ".cache", "claude-nomad");
|
|
4568
4814
|
mkdirSync5(cacheDir, { recursive: true });
|
|
4569
4815
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes2(4).toString("hex")}`;
|
|
4570
|
-
tmpRoot =
|
|
4816
|
+
tmpRoot = join31(cacheDir, `check-shared-skills-tree-${stamp}`);
|
|
4571
4817
|
mkdirSync5(tmpRoot, { recursive: true, mode: 448 });
|
|
4572
4818
|
const { staged, incomplete } = buildSkillScanTree(tmpRoot);
|
|
4573
4819
|
if (incomplete) {
|
|
@@ -4612,22 +4858,22 @@ function reportCommittedSkills(section2) {
|
|
|
4612
4858
|
}
|
|
4613
4859
|
}
|
|
4614
4860
|
|
|
4615
|
-
// src/commands
|
|
4861
|
+
// src/commands/doctor/checks/shared.ts
|
|
4616
4862
|
init_config();
|
|
4617
4863
|
|
|
4618
|
-
// src/remap.ts
|
|
4864
|
+
// src/sync/remap.ts
|
|
4619
4865
|
init_config_sharedDirs_guard();
|
|
4620
|
-
import { cpSync as cpSync5, existsSync as
|
|
4621
|
-
import { dirname as
|
|
4866
|
+
import { cpSync as cpSync5, existsSync as existsSync22, lstatSync as lstatSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync11, rmSync as rmSync11, statSync as statSync6 } from "node:fs";
|
|
4867
|
+
import { dirname as dirname8, join as join33, relative as relative6, sep as sep5 } from "node:path";
|
|
4622
4868
|
init_config();
|
|
4623
4869
|
|
|
4624
|
-
// src/push
|
|
4870
|
+
// src/commands/push/manifest.ts
|
|
4625
4871
|
init_config();
|
|
4626
|
-
|
|
4872
|
+
init_gitleaks_config();
|
|
4627
4873
|
init_utils_fs();
|
|
4628
4874
|
import { createHash } from "node:crypto";
|
|
4629
|
-
import { existsSync as
|
|
4630
|
-
import { dirname as
|
|
4875
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync6, readdirSync as readdirSync10, readFileSync as readFileSync7, statSync as statSync5 } from "node:fs";
|
|
4876
|
+
import { dirname as dirname7, join as join32 } from "node:path";
|
|
4631
4877
|
function isChanged(prev, cur, hash) {
|
|
4632
4878
|
if (prev === void 0) return true;
|
|
4633
4879
|
if (prev.size !== cur.size) return true;
|
|
@@ -4668,7 +4914,7 @@ function hashFile(absPath) {
|
|
|
4668
4914
|
}
|
|
4669
4915
|
function enumerateDir(dir, results) {
|
|
4670
4916
|
for (const entry of readdirSync10(dir)) {
|
|
4671
|
-
const fullPath =
|
|
4917
|
+
const fullPath = join32(dir, entry);
|
|
4672
4918
|
const st = statSync5(fullPath);
|
|
4673
4919
|
if (st.isDirectory()) {
|
|
4674
4920
|
enumerateDir(fullPath, results);
|
|
@@ -4680,7 +4926,7 @@ function enumerateDir(dir, results) {
|
|
|
4680
4926
|
function enumerateSourceFiles(projectDir) {
|
|
4681
4927
|
const results = [];
|
|
4682
4928
|
for (const entry of readdirSync10(projectDir)) {
|
|
4683
|
-
const fullPath =
|
|
4929
|
+
const fullPath = join32(projectDir, entry);
|
|
4684
4930
|
const st = statSync5(fullPath);
|
|
4685
4931
|
if (st.isDirectory()) {
|
|
4686
4932
|
enumerateDir(fullPath, results);
|
|
@@ -4692,15 +4938,15 @@ function enumerateSourceFiles(projectDir) {
|
|
|
4692
4938
|
}
|
|
4693
4939
|
function fileIdentity(p) {
|
|
4694
4940
|
if (p === null) return "none::absent";
|
|
4695
|
-
if (!
|
|
4941
|
+
if (!existsSync21(p)) return `${p}::absent`;
|
|
4696
4942
|
return `${p}::${createHash("sha256").update(readFileSync7(p)).digest("hex")}`;
|
|
4697
4943
|
}
|
|
4698
4944
|
function computeConfigHash() {
|
|
4699
4945
|
const repo = repoHome();
|
|
4700
4946
|
const parts = [
|
|
4701
4947
|
fileIdentity(resolveTomlPath(repo)),
|
|
4702
|
-
fileIdentity(
|
|
4703
|
-
fileIdentity(
|
|
4948
|
+
fileIdentity(join32(repo, ".gitleaks.overlay.toml")),
|
|
4949
|
+
fileIdentity(join32(repo, ".gitleaksignore"))
|
|
4704
4950
|
];
|
|
4705
4951
|
return createHash("sha256").update(parts.join("\n")).digest("hex");
|
|
4706
4952
|
}
|
|
@@ -4720,14 +4966,14 @@ function readManifest(path) {
|
|
|
4720
4966
|
}
|
|
4721
4967
|
}
|
|
4722
4968
|
function writeManifest(path, manifest) {
|
|
4723
|
-
mkdirSync6(
|
|
4969
|
+
mkdirSync6(dirname7(path), { recursive: true });
|
|
4724
4970
|
writeJsonAtomic(path, manifest);
|
|
4725
4971
|
}
|
|
4726
4972
|
function buildManifest(files, scannerVersion, configHash) {
|
|
4727
4973
|
return { schema: 1, scannerVersion, configHash, files };
|
|
4728
4974
|
}
|
|
4729
4975
|
|
|
4730
|
-
// src/remap.ts
|
|
4976
|
+
// src/sync/remap.ts
|
|
4731
4977
|
init_utils();
|
|
4732
4978
|
init_utils_fs();
|
|
4733
4979
|
init_utils_json();
|
|
@@ -4744,16 +4990,15 @@ function overlaySessionDir(src, dst) {
|
|
|
4744
4990
|
cpSyncGuarded(src, dst, void 0, "overlaySessionDir");
|
|
4745
4991
|
}
|
|
4746
4992
|
function copyFileAtomic(src, dst) {
|
|
4747
|
-
mkdirSync7(
|
|
4993
|
+
mkdirSync7(dirname8(dst), { recursive: true });
|
|
4748
4994
|
const tmp = `${dst}.tmp.${process.pid}`;
|
|
4749
4995
|
cpSync5(src, tmp, { force: true, preserveTimestamps: true });
|
|
4750
4996
|
renameAtomicRetry(tmp, dst);
|
|
4751
4997
|
}
|
|
4752
4998
|
function hasDeltaForDir(sel, localDir) {
|
|
4753
4999
|
const prefix = `${localDir}${sep5}`;
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
return false;
|
|
5000
|
+
const underDir = (p) => p.startsWith(prefix);
|
|
5001
|
+
return [...sel.changed].some(underDir) || sel.deleted.some(underDir);
|
|
4757
5002
|
}
|
|
4758
5003
|
function skipForSelection(sel, localDir) {
|
|
4759
5004
|
return sel !== void 0 && !hasDeltaForDir(sel, localDir);
|
|
@@ -4762,12 +5007,12 @@ function applySelective(sel, localDir, repoDst) {
|
|
|
4762
5007
|
const prefix = `${localDir}${sep5}`;
|
|
4763
5008
|
for (const src of sel.changed) {
|
|
4764
5009
|
if (!src.startsWith(prefix)) continue;
|
|
4765
|
-
copyFileAtomic(src,
|
|
5010
|
+
copyFileAtomic(src, join33(repoDst, relative6(localDir, src)));
|
|
4766
5011
|
}
|
|
4767
5012
|
for (const src of sel.deleted) {
|
|
4768
5013
|
if (!src.startsWith(prefix)) continue;
|
|
4769
|
-
const dst =
|
|
4770
|
-
if (
|
|
5014
|
+
const dst = join33(repoDst, relative6(localDir, src));
|
|
5015
|
+
if (existsSync22(dst)) rmSync11(dst);
|
|
4771
5016
|
}
|
|
4772
5017
|
}
|
|
4773
5018
|
function copyDirJsonlOnly(src, dst) {
|
|
@@ -4796,15 +5041,15 @@ function remapPull(ts, opts = {}) {
|
|
|
4796
5041
|
const wouldPull = [];
|
|
4797
5042
|
const repo = repoHome();
|
|
4798
5043
|
const claude = claudeHome();
|
|
4799
|
-
const mapPath =
|
|
4800
|
-
const repoProjects =
|
|
4801
|
-
if (!
|
|
5044
|
+
const mapPath = join33(repo, "path-map.json");
|
|
5045
|
+
const repoProjects = join33(repo, "shared", "projects");
|
|
5046
|
+
if (!existsSync22(mapPath) || !existsSync22(repoProjects)) {
|
|
4802
5047
|
const text = "no path-map or repo projects dir; skipping session remap";
|
|
4803
5048
|
emitPreview(opts.onPreview, { kind: "note", text }, text);
|
|
4804
5049
|
return { unmapped: 0, pulled, wouldPull };
|
|
4805
5050
|
}
|
|
4806
5051
|
const map = readPathMap(mapPath);
|
|
4807
|
-
const localProjects =
|
|
5052
|
+
const localProjects = join33(claude, "projects");
|
|
4808
5053
|
if (!dryRun) mkdirSync7(localProjects, { recursive: true });
|
|
4809
5054
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
4810
5055
|
assertSafeLogical(logical);
|
|
@@ -4814,9 +5059,9 @@ function remapPull(ts, opts = {}) {
|
|
|
4814
5059
|
continue;
|
|
4815
5060
|
}
|
|
4816
5061
|
assertSafeLocalRoot(localPath, logical);
|
|
4817
|
-
const src =
|
|
4818
|
-
if (!
|
|
4819
|
-
const dst =
|
|
5062
|
+
const src = join33(repoProjects, logical);
|
|
5063
|
+
if (!existsSync22(src)) continue;
|
|
5064
|
+
const dst = join33(localProjects, encodePath(localPath));
|
|
4820
5065
|
if (dryRun) {
|
|
4821
5066
|
wouldPull.push(logical);
|
|
4822
5067
|
emitPreview(
|
|
@@ -4835,8 +5080,8 @@ function remapPull(ts, opts = {}) {
|
|
|
4835
5080
|
function countLocalOnly(src, dst) {
|
|
4836
5081
|
let count = 0;
|
|
4837
5082
|
for (const name of readdirSync11(dst)) {
|
|
4838
|
-
const dstPath =
|
|
4839
|
-
const srcPath =
|
|
5083
|
+
const dstPath = join33(dst, name);
|
|
5084
|
+
const srcPath = join33(src, name);
|
|
4840
5085
|
if (lstatSync13(dstPath).isDirectory()) {
|
|
4841
5086
|
count += countLocalOnly(srcPath, dstPath);
|
|
4842
5087
|
} else if (lstatSync13(srcPath, { throwIfNoEntry: false }) === void 0) {
|
|
@@ -4848,20 +5093,20 @@ function countLocalOnly(src, dst) {
|
|
|
4848
5093
|
function scanLocalOnly() {
|
|
4849
5094
|
const repo = repoHome();
|
|
4850
5095
|
const claude = claudeHome();
|
|
4851
|
-
const mapPath =
|
|
4852
|
-
const repoProjects =
|
|
4853
|
-
if (!
|
|
5096
|
+
const mapPath = join33(repo, "path-map.json");
|
|
5097
|
+
const repoProjects = join33(repo, "shared", "projects");
|
|
5098
|
+
if (!existsSync22(mapPath) || !existsSync22(repoProjects)) return 0;
|
|
4854
5099
|
const map = readPathMap(mapPath);
|
|
4855
|
-
const localProjects =
|
|
5100
|
+
const localProjects = join33(claude, "projects");
|
|
4856
5101
|
let count = 0;
|
|
4857
5102
|
for (const [logical, hosts] of Object.entries(map.projects)) {
|
|
4858
5103
|
assertSafeLogical(logical);
|
|
4859
5104
|
const localPath = hosts[HOST];
|
|
4860
5105
|
if (!localPath || localPath === "TBD") continue;
|
|
4861
5106
|
assertSafeLocalRoot(localPath, logical);
|
|
4862
|
-
const dst =
|
|
4863
|
-
if (!
|
|
4864
|
-
count += countLocalOnly(
|
|
5107
|
+
const dst = join33(localProjects, encodePath(localPath));
|
|
5108
|
+
if (!existsSync22(dst)) continue;
|
|
5109
|
+
count += countLocalOnly(join33(repoProjects, logical), dst);
|
|
4865
5110
|
}
|
|
4866
5111
|
return count;
|
|
4867
5112
|
}
|
|
@@ -4896,16 +5141,16 @@ function remapPush(ts, opts = {}) {
|
|
|
4896
5141
|
const wouldPush = [];
|
|
4897
5142
|
const repo = repoHome();
|
|
4898
5143
|
const claude = claudeHome();
|
|
4899
|
-
const mapPath =
|
|
4900
|
-
if (!
|
|
5144
|
+
const mapPath = join33(repo, "path-map.json");
|
|
5145
|
+
if (!existsSync22(mapPath)) {
|
|
4901
5146
|
log("no path-map.json; skipping session export");
|
|
4902
5147
|
return { unmapped: 0, collisions: 0, pushed, wouldPush };
|
|
4903
5148
|
}
|
|
4904
5149
|
const map = readPathMap(mapPath);
|
|
4905
|
-
const localProjects =
|
|
4906
|
-
const repoProjects =
|
|
5150
|
+
const localProjects = join33(claude, "projects");
|
|
5151
|
+
const repoProjects = join33(repo, "shared", "projects");
|
|
4907
5152
|
const reverse = buildReverseMap(map);
|
|
4908
|
-
if (!
|
|
5153
|
+
if (!existsSync22(localProjects)) return { unmapped, collisions: 0, pushed, wouldPush };
|
|
4909
5154
|
if (!dryRun) mkdirSync7(repoProjects, { recursive: true });
|
|
4910
5155
|
for (const dir of readdirSync11(localProjects)) {
|
|
4911
5156
|
if (dir.endsWith(TMP_SUFFIX)) continue;
|
|
@@ -4914,8 +5159,8 @@ function remapPush(ts, opts = {}) {
|
|
|
4914
5159
|
unmapped++;
|
|
4915
5160
|
continue;
|
|
4916
5161
|
}
|
|
4917
|
-
const localDir =
|
|
4918
|
-
const repoDst =
|
|
5162
|
+
const localDir = join33(localProjects, dir);
|
|
5163
|
+
const repoDst = join33(repoProjects, logical);
|
|
4919
5164
|
if (skipForSelection(opts.selection, localDir)) continue;
|
|
4920
5165
|
if (dryRun) {
|
|
4921
5166
|
wouldPush.push(logical);
|
|
@@ -4932,15 +5177,15 @@ function remapPush(ts, opts = {}) {
|
|
|
4932
5177
|
return { unmapped, collisions: 0, pushed, wouldPush };
|
|
4933
5178
|
}
|
|
4934
5179
|
|
|
4935
|
-
// src/commands
|
|
5180
|
+
// src/commands/doctor/checks/shared.ts
|
|
4936
5181
|
init_utils_fs();
|
|
4937
5182
|
init_utils_json();
|
|
4938
5183
|
function buildScanTree(tmpRoot) {
|
|
4939
5184
|
const logicalToEncoded = /* @__PURE__ */ new Map();
|
|
4940
5185
|
let staged = 0;
|
|
4941
5186
|
const repo = repoHome();
|
|
4942
|
-
const mapPath =
|
|
4943
|
-
if (!
|
|
5187
|
+
const mapPath = join34(repo, "path-map.json");
|
|
5188
|
+
if (!existsSync23(mapPath)) return { logicalToEncoded, staged, malformed: false };
|
|
4944
5189
|
let map;
|
|
4945
5190
|
try {
|
|
4946
5191
|
map = readJson(mapPath);
|
|
@@ -4957,12 +5202,12 @@ function buildScanTree(tmpRoot) {
|
|
|
4957
5202
|
if (!p || p === "TBD") continue;
|
|
4958
5203
|
reverse.set(encodePath(p), logical);
|
|
4959
5204
|
}
|
|
4960
|
-
const localProjects =
|
|
4961
|
-
if (!
|
|
5205
|
+
const localProjects = join34(claudeHome(), "projects");
|
|
5206
|
+
if (!existsSync23(localProjects)) return { logicalToEncoded, staged, malformed: false };
|
|
4962
5207
|
for (const dir of readdirSync12(localProjects)) {
|
|
4963
5208
|
const logical = reverse.get(dir);
|
|
4964
5209
|
if (!logical) continue;
|
|
4965
|
-
copyDirJsonlOnly(
|
|
5210
|
+
copyDirJsonlOnly(join34(localProjects, dir), join34(tmpRoot, "shared", "projects", logical));
|
|
4966
5211
|
logicalToEncoded.set(logical, dir);
|
|
4967
5212
|
staged++;
|
|
4968
5213
|
}
|
|
@@ -4970,7 +5215,7 @@ function buildScanTree(tmpRoot) {
|
|
|
4970
5215
|
}
|
|
4971
5216
|
function probeGitleaksForScan() {
|
|
4972
5217
|
try {
|
|
4973
|
-
|
|
5218
|
+
execFileSync12("gitleaks", ["version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
4974
5219
|
return "ok";
|
|
4975
5220
|
} catch (err) {
|
|
4976
5221
|
if (err.code === "ENOENT") return "missing";
|
|
@@ -4992,11 +5237,11 @@ function ensureGitleaksReady(section2, gitleaksReady) {
|
|
|
4992
5237
|
return true;
|
|
4993
5238
|
}
|
|
4994
5239
|
function runLocalPreviewScan(section2) {
|
|
4995
|
-
const cacheDir =
|
|
5240
|
+
const cacheDir = join34(homedir6(), ".cache", "claude-nomad");
|
|
4996
5241
|
mkdirSync8(cacheDir, { recursive: true });
|
|
4997
5242
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
4998
|
-
const reportPath =
|
|
4999
|
-
const tmpRoot =
|
|
5243
|
+
const reportPath = join34(cacheDir, `check-shared-${stamp}.json`);
|
|
5244
|
+
const tmpRoot = join34(cacheDir, `check-shared-tree-${stamp}`);
|
|
5000
5245
|
try {
|
|
5001
5246
|
const { logicalToEncoded, staged, malformed } = buildScanTree(tmpRoot);
|
|
5002
5247
|
if (malformed) {
|
|
@@ -5027,10 +5272,10 @@ function reportCheckShared(section2, gitleaksReady) {
|
|
|
5027
5272
|
}
|
|
5028
5273
|
}
|
|
5029
5274
|
|
|
5030
|
-
// src/commands
|
|
5275
|
+
// src/commands/doctor/checks/hooks.scope.ts
|
|
5031
5276
|
init_color();
|
|
5032
|
-
import { existsSync as
|
|
5033
|
-
import { dirname as
|
|
5277
|
+
import { existsSync as existsSync24, readFileSync as readFileSync8, readdirSync as readdirSync13, realpathSync as realpathSync3 } from "node:fs";
|
|
5278
|
+
import { dirname as dirname9, extname, join as join35 } from "node:path";
|
|
5034
5279
|
init_config();
|
|
5035
5280
|
function typeFromPackageJson(pkgPath) {
|
|
5036
5281
|
try {
|
|
@@ -5050,11 +5295,11 @@ function effectiveType(hookPath) {
|
|
|
5050
5295
|
} catch {
|
|
5051
5296
|
return null;
|
|
5052
5297
|
}
|
|
5053
|
-
let dir =
|
|
5298
|
+
let dir = dirname9(real);
|
|
5054
5299
|
for (; ; ) {
|
|
5055
|
-
const pkg =
|
|
5056
|
-
if (
|
|
5057
|
-
const parent =
|
|
5300
|
+
const pkg = join35(dir, "package.json");
|
|
5301
|
+
if (existsSync24(pkg)) return typeFromPackageJson(pkg);
|
|
5302
|
+
const parent = dirname9(dir);
|
|
5058
5303
|
if (parent === dir) return "cjs";
|
|
5059
5304
|
dir = parent;
|
|
5060
5305
|
}
|
|
@@ -5095,15 +5340,15 @@ function safeRead(path) {
|
|
|
5095
5340
|
}
|
|
5096
5341
|
}
|
|
5097
5342
|
function reportHookScopeCheck(section2) {
|
|
5098
|
-
const hooksDir =
|
|
5099
|
-
if (!
|
|
5343
|
+
const hooksDir = join35(claudeHome(), "hooks");
|
|
5344
|
+
if (!existsSync24(hooksDir)) {
|
|
5100
5345
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/hooks; skipping module-scope check`);
|
|
5101
5346
|
return;
|
|
5102
5347
|
}
|
|
5103
5348
|
let anyWarn = false;
|
|
5104
5349
|
for (const name of safeReaddir2(hooksDir)) {
|
|
5105
5350
|
if (extname(name) !== ".js") continue;
|
|
5106
|
-
const abs =
|
|
5351
|
+
const abs = join35(hooksDir, name);
|
|
5107
5352
|
const eff = effectiveType(abs);
|
|
5108
5353
|
if (eff === null) continue;
|
|
5109
5354
|
const src = safeRead(abs);
|
|
@@ -5121,10 +5366,10 @@ function reportHookScopeCheck(section2) {
|
|
|
5121
5366
|
}
|
|
5122
5367
|
}
|
|
5123
5368
|
|
|
5124
|
-
// src/commands
|
|
5369
|
+
// src/commands/doctor/checks/hooks.ts
|
|
5125
5370
|
init_color();
|
|
5126
|
-
import { existsSync as
|
|
5127
|
-
import { join as
|
|
5371
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
5372
|
+
import { join as join36 } from "node:path";
|
|
5128
5373
|
init_config();
|
|
5129
5374
|
function expandHome(token) {
|
|
5130
5375
|
const h2 = home();
|
|
@@ -5167,7 +5412,7 @@ function checkEventGroups(section2, event, groups) {
|
|
|
5167
5412
|
for (const group of groups) {
|
|
5168
5413
|
for (const cmd of commandsFromGroup(group)) {
|
|
5169
5414
|
for (const resolved of claudePathsIn(cmd)) {
|
|
5170
|
-
if (
|
|
5415
|
+
if (existsSync25(resolved)) continue;
|
|
5171
5416
|
addItem(
|
|
5172
5417
|
section2,
|
|
5173
5418
|
`${red(failGlyph)} hooks/${event}: command target missing: ${resolved} (run nomad pull)`
|
|
@@ -5180,8 +5425,8 @@ function checkEventGroups(section2, event, groups) {
|
|
|
5180
5425
|
return anyFail;
|
|
5181
5426
|
}
|
|
5182
5427
|
function reportHooksTargetCheck(section2) {
|
|
5183
|
-
const settingsPath =
|
|
5184
|
-
if (!
|
|
5428
|
+
const settingsPath = join36(claudeHome(), "settings.json");
|
|
5429
|
+
if (!existsSync25(settingsPath)) {
|
|
5185
5430
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping hook target check`);
|
|
5186
5431
|
return;
|
|
5187
5432
|
}
|
|
@@ -5202,17 +5447,17 @@ function reportHooksTargetCheck(section2) {
|
|
|
5202
5447
|
}
|
|
5203
5448
|
}
|
|
5204
5449
|
|
|
5205
|
-
// src/commands
|
|
5450
|
+
// src/commands/doctor/checks/hooks.preserve-symlinks.ts
|
|
5206
5451
|
init_color();
|
|
5207
|
-
import { existsSync as
|
|
5208
|
-
import { join as
|
|
5452
|
+
import { existsSync as existsSync27, readFileSync as readFileSync9 } from "node:fs";
|
|
5453
|
+
import { join as join38 } from "node:path";
|
|
5209
5454
|
|
|
5210
|
-
// src/commands
|
|
5211
|
-
import { closeSync as closeSync3, existsSync as
|
|
5212
|
-
import { dirname as
|
|
5455
|
+
// src/commands/doctor/checks/hooks.preserve-symlinks.probe.ts
|
|
5456
|
+
import { closeSync as closeSync3, existsSync as existsSync26, openSync as openSync3, readSync, realpathSync as realpathSync4 } from "node:fs";
|
|
5457
|
+
import { dirname as dirname10, join as join37, resolve as resolve3 } from "node:path";
|
|
5213
5458
|
function suppressedRanges(src) {
|
|
5214
5459
|
const ranges = [];
|
|
5215
|
-
const re = /\/\*[
|
|
5460
|
+
const re = /\/\*[^]*?\*\/|\/\/[^\n]*|(['"`])(?:\\[^]|[^\\])*?\1/g;
|
|
5216
5461
|
let m;
|
|
5217
5462
|
while ((m = re.exec(src)) !== null) {
|
|
5218
5463
|
ranges.push([m.index, m.index + m[0].length]);
|
|
@@ -5225,7 +5470,7 @@ function inSuppressedRange(pos, ranges) {
|
|
|
5225
5470
|
}
|
|
5226
5471
|
return false;
|
|
5227
5472
|
}
|
|
5228
|
-
function
|
|
5473
|
+
function relativeSpecifiers(src) {
|
|
5229
5474
|
const ranges = suppressedRanges(src);
|
|
5230
5475
|
const specifiers = [];
|
|
5231
5476
|
const reqRe = /\brequire\s*\(\s*(['"])(\.\.?\/[^'"]*)\1\s*\)/g;
|
|
@@ -5241,12 +5486,12 @@ function topRelativeSpecifiers(src) {
|
|
|
5241
5486
|
}
|
|
5242
5487
|
function specifierIsMissing(specifier, baseDir) {
|
|
5243
5488
|
const base = resolve3(baseDir, specifier);
|
|
5244
|
-
if (
|
|
5489
|
+
if (existsSync26(base)) return false;
|
|
5245
5490
|
for (const ext of [".js", ".cjs", ".mjs"]) {
|
|
5246
|
-
if (
|
|
5491
|
+
if (existsSync26(base + ext)) return false;
|
|
5247
5492
|
}
|
|
5248
5493
|
for (const idx of ["index.js", "index.cjs", "index.mjs"]) {
|
|
5249
|
-
if (
|
|
5494
|
+
if (existsSync26(join37(base, idx))) return false;
|
|
5250
5495
|
}
|
|
5251
5496
|
return true;
|
|
5252
5497
|
}
|
|
@@ -5270,16 +5515,16 @@ function relativeRequireTargetsBroken(scriptPath) {
|
|
|
5270
5515
|
} catch {
|
|
5271
5516
|
return false;
|
|
5272
5517
|
}
|
|
5273
|
-
const specifiers =
|
|
5518
|
+
const specifiers = relativeSpecifiers(raw);
|
|
5274
5519
|
if (specifiers.length === 0) return false;
|
|
5275
|
-
const baseDir =
|
|
5520
|
+
const baseDir = dirname10(realPath);
|
|
5276
5521
|
for (const spec of specifiers) {
|
|
5277
5522
|
if (specifierIsMissing(spec, baseDir)) return true;
|
|
5278
5523
|
}
|
|
5279
5524
|
return false;
|
|
5280
5525
|
}
|
|
5281
5526
|
|
|
5282
|
-
// src/commands
|
|
5527
|
+
// src/commands/doctor/checks/hooks.preserve-symlinks.ts
|
|
5283
5528
|
init_config();
|
|
5284
5529
|
function expandHome2(token) {
|
|
5285
5530
|
const h2 = home();
|
|
@@ -5301,8 +5546,8 @@ function commandTokens(command) {
|
|
|
5301
5546
|
return tokens;
|
|
5302
5547
|
}
|
|
5303
5548
|
function readPathMapSafe() {
|
|
5304
|
-
const mapPath =
|
|
5305
|
-
if (!
|
|
5549
|
+
const mapPath = join38(repoHome(), "path-map.json");
|
|
5550
|
+
if (!existsSync27(mapPath)) return { projects: {} };
|
|
5306
5551
|
try {
|
|
5307
5552
|
return JSON.parse(readFileSync9(mapPath, "utf8"));
|
|
5308
5553
|
} catch {
|
|
@@ -5376,8 +5621,8 @@ function checkEventForPreserveSymlinks(section2, event, groups, sharedLinkNames)
|
|
|
5376
5621
|
return anyWarn;
|
|
5377
5622
|
}
|
|
5378
5623
|
function reportPreserveSymlinksCheck(section2) {
|
|
5379
|
-
const settingsPath =
|
|
5380
|
-
if (!
|
|
5624
|
+
const settingsPath = join38(claudeHome(), "settings.json");
|
|
5625
|
+
if (!existsSync27(settingsPath)) {
|
|
5381
5626
|
addItem(
|
|
5382
5627
|
section2,
|
|
5383
5628
|
`${dim(infoGlyph)} no ~/.claude/settings.json; skipping preserve-symlinks-main check`
|
|
@@ -5408,10 +5653,10 @@ function reportPreserveSymlinksCheck(section2) {
|
|
|
5408
5653
|
}
|
|
5409
5654
|
}
|
|
5410
5655
|
|
|
5411
|
-
// src/commands
|
|
5656
|
+
// src/commands/doctor/checks/settings-drift.ts
|
|
5412
5657
|
init_color();
|
|
5413
|
-
import { existsSync as
|
|
5414
|
-
import { join as
|
|
5658
|
+
import { existsSync as existsSync28, readFileSync as readFileSync10 } from "node:fs";
|
|
5659
|
+
import { join as join39 } from "node:path";
|
|
5415
5660
|
init_config();
|
|
5416
5661
|
init_utils_json();
|
|
5417
5662
|
function diffMergedSettings(merged, settings) {
|
|
@@ -5429,7 +5674,7 @@ function tryReadJson(filePath) {
|
|
|
5429
5674
|
}
|
|
5430
5675
|
}
|
|
5431
5676
|
function reportHooksBaseSelfCleanNote(section2) {
|
|
5432
|
-
const basePath =
|
|
5677
|
+
const basePath = join39(repoHome(), "shared", "settings.base.json");
|
|
5433
5678
|
const base = tryReadJson(basePath);
|
|
5434
5679
|
if (base === null) return;
|
|
5435
5680
|
if (!baseHasGsdHookEntries(base)) return;
|
|
@@ -5442,14 +5687,14 @@ function reportSettingsDriftCheck(section2) {
|
|
|
5442
5687
|
const claude = claudeHome();
|
|
5443
5688
|
const repo = repoHome();
|
|
5444
5689
|
const host = HOST;
|
|
5445
|
-
const settingsPath =
|
|
5446
|
-
const basePath =
|
|
5447
|
-
const hostPath =
|
|
5448
|
-
if (!
|
|
5690
|
+
const settingsPath = join39(claude, "settings.json");
|
|
5691
|
+
const basePath = join39(repo, "shared", "settings.base.json");
|
|
5692
|
+
const hostPath = join39(repo, "hosts", `${host}.json`);
|
|
5693
|
+
if (!existsSync28(settingsPath)) {
|
|
5449
5694
|
addItem(section2, `${dim(infoGlyph)} no ~/.claude/settings.json; skipping merge-drift check`);
|
|
5450
5695
|
return;
|
|
5451
5696
|
}
|
|
5452
|
-
if (!
|
|
5697
|
+
if (!existsSync28(basePath)) {
|
|
5453
5698
|
addItem(
|
|
5454
5699
|
section2,
|
|
5455
5700
|
`${dim(infoGlyph)} shared/settings.base.json missing; skipping merge-drift check`
|
|
@@ -5468,7 +5713,7 @@ function reportSettingsDriftCheck(section2) {
|
|
|
5468
5713
|
if (settings === null) {
|
|
5469
5714
|
return;
|
|
5470
5715
|
}
|
|
5471
|
-
const hostExists =
|
|
5716
|
+
const hostExists = existsSync28(hostPath);
|
|
5472
5717
|
const hostObj = hostExists ? tryReadJson(hostPath) : null;
|
|
5473
5718
|
if (hostExists && hostObj === null) {
|
|
5474
5719
|
addItem(
|
|
@@ -5512,19 +5757,20 @@ function emitDriftRows(section2, missing, changed, promotable, excluded, hostFil
|
|
|
5512
5757
|
}
|
|
5513
5758
|
}
|
|
5514
5759
|
|
|
5515
|
-
// src/commands
|
|
5760
|
+
// src/commands/doctor/doctor.ts
|
|
5516
5761
|
init_config();
|
|
5517
5762
|
|
|
5518
|
-
// src/commands
|
|
5763
|
+
// src/commands/doctor/engine.ts
|
|
5519
5764
|
init_color();
|
|
5520
5765
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
5521
|
-
import {
|
|
5766
|
+
import { join as join41 } from "node:path";
|
|
5522
5767
|
|
|
5523
|
-
// src/commands
|
|
5768
|
+
// src/commands/doctor/version.ts
|
|
5524
5769
|
init_color();
|
|
5525
5770
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
5526
|
-
import {
|
|
5771
|
+
import { join as join40 } from "node:path";
|
|
5527
5772
|
init_config();
|
|
5773
|
+
init_package_root();
|
|
5528
5774
|
var STRICT_SEMVER = /^\d+\.\d+\.\d+$/;
|
|
5529
5775
|
var STRICT_SEMVER_PREFIX = /^(\d+\.\d+\.\d+)(?:[-+]|$)/;
|
|
5530
5776
|
function compareSemver(a, b) {
|
|
@@ -5538,7 +5784,7 @@ function compareSemver(a, b) {
|
|
|
5538
5784
|
}
|
|
5539
5785
|
function readLocalVersion() {
|
|
5540
5786
|
try {
|
|
5541
|
-
const pkgPath =
|
|
5787
|
+
const pkgPath = join40(packageRoot(), "package.json");
|
|
5542
5788
|
const parsed = JSON.parse(readFileSync11(pkgPath, "utf8"));
|
|
5543
5789
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
5544
5790
|
return parsed.version;
|
|
@@ -5589,7 +5835,8 @@ function reportVersionCheck(section2) {
|
|
|
5589
5835
|
}
|
|
5590
5836
|
}
|
|
5591
5837
|
|
|
5592
|
-
// src/commands
|
|
5838
|
+
// src/commands/doctor/engine.ts
|
|
5839
|
+
init_package_root();
|
|
5593
5840
|
var ENGINES_GTE = /^>=\s*(\d+\.\d+\.\d+)$/;
|
|
5594
5841
|
function parseMinVersion(spec) {
|
|
5595
5842
|
const m = ENGINES_GTE.exec(spec);
|
|
@@ -5597,7 +5844,7 @@ function parseMinVersion(spec) {
|
|
|
5597
5844
|
}
|
|
5598
5845
|
function readEnginesNode() {
|
|
5599
5846
|
try {
|
|
5600
|
-
const pkgPath =
|
|
5847
|
+
const pkgPath = join41(packageRoot(), "package.json");
|
|
5601
5848
|
const parsed = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
5602
5849
|
const node = parsed.engines?.node;
|
|
5603
5850
|
if (typeof node === "string" && node.length > 0) return node;
|
|
@@ -5624,18 +5871,18 @@ function reportNodeEngineCheck(section2) {
|
|
|
5624
5871
|
addItem(section2, `${green(okGlyph)} node: ${process.version}`);
|
|
5625
5872
|
}
|
|
5626
5873
|
|
|
5627
|
-
// src/commands
|
|
5874
|
+
// src/commands/doctor/checks/longpaths.ts
|
|
5628
5875
|
init_color();
|
|
5629
|
-
import { execFileSync as
|
|
5876
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
5630
5877
|
init_config();
|
|
5631
|
-
var
|
|
5878
|
+
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
5632
5879
|
var LONGPATHS_REG_KEY = String.raw`HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`;
|
|
5633
5880
|
var LONGPATHS_REG_VALUE = "LongPathsEnabled";
|
|
5634
5881
|
function probeGitLongpaths(run) {
|
|
5635
5882
|
try {
|
|
5636
5883
|
const out = run("git", ["-C", repoHome(), "config", "--get", "core.longpaths"], {
|
|
5637
5884
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5638
|
-
timeout:
|
|
5885
|
+
timeout: PROBE_TIMEOUT_MS2
|
|
5639
5886
|
}).toString().trim();
|
|
5640
5887
|
return out === "true" || out === "1";
|
|
5641
5888
|
} catch {
|
|
@@ -5646,7 +5893,7 @@ function probeRegistryLongpaths(run) {
|
|
|
5646
5893
|
try {
|
|
5647
5894
|
const out = run("reg", ["query", LONGPATHS_REG_KEY, "/v", LONGPATHS_REG_VALUE], {
|
|
5648
5895
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5649
|
-
timeout:
|
|
5896
|
+
timeout: PROBE_TIMEOUT_MS2
|
|
5650
5897
|
}).toString();
|
|
5651
5898
|
return /0x1\b/.test(out);
|
|
5652
5899
|
} catch {
|
|
@@ -5660,7 +5907,7 @@ function addLongpathsRow(section2, label, enabled2, remediation) {
|
|
|
5660
5907
|
}
|
|
5661
5908
|
addItem(section2, `${yellow(warnGlyph)} ${label}: not enabled (${remediation})`);
|
|
5662
5909
|
}
|
|
5663
|
-
function reportLongPathsCheck(section2, run =
|
|
5910
|
+
function reportLongPathsCheck(section2, run = execFileSync13) {
|
|
5664
5911
|
if (process.platform !== "win32") return;
|
|
5665
5912
|
addLongpathsRow(
|
|
5666
5913
|
section2,
|
|
@@ -5682,18 +5929,18 @@ function reportSyncModality(section2) {
|
|
|
5682
5929
|
addItem(section2, `${dim(infoGlyph)} sync modality: ${modality}`);
|
|
5683
5930
|
}
|
|
5684
5931
|
|
|
5685
|
-
// src/commands
|
|
5932
|
+
// src/commands/doctor/checks/crlf.ts
|
|
5686
5933
|
init_color();
|
|
5687
|
-
import { execFileSync as
|
|
5688
|
-
import { existsSync as
|
|
5689
|
-
import { join as
|
|
5934
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
5935
|
+
import { existsSync as existsSync29, readFileSync as readFileSync13 } from "node:fs";
|
|
5936
|
+
import { join as join42 } from "node:path";
|
|
5690
5937
|
init_config();
|
|
5691
|
-
var
|
|
5938
|
+
var PROBE_TIMEOUT_MS3 = 3e3;
|
|
5692
5939
|
var GUARD_LINE = /^\*\s+-text\b/;
|
|
5693
5940
|
function hasGitattributesGuard(repo) {
|
|
5694
5941
|
try {
|
|
5695
|
-
const path =
|
|
5696
|
-
if (!
|
|
5942
|
+
const path = join42(repo, ".gitattributes");
|
|
5943
|
+
if (!existsSync29(path)) return false;
|
|
5697
5944
|
const content = readFileSync13(path, "utf8");
|
|
5698
5945
|
return content.split("\n").map((line) => line.trim()).some((line) => line !== "" && !line.startsWith("#") && GUARD_LINE.test(line));
|
|
5699
5946
|
} catch {
|
|
@@ -5704,7 +5951,7 @@ function probeAutocrlf(repo, run) {
|
|
|
5704
5951
|
try {
|
|
5705
5952
|
const out = run("git", ["-C", repo, "config", "core.autocrlf"], {
|
|
5706
5953
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5707
|
-
timeout:
|
|
5954
|
+
timeout: PROBE_TIMEOUT_MS3
|
|
5708
5955
|
}).toString().trim();
|
|
5709
5956
|
if (out === "true" || out === "input") return "active";
|
|
5710
5957
|
if (out === "false") return "disabled";
|
|
@@ -5723,7 +5970,7 @@ function addExposedRow(section2, repo, verdict) {
|
|
|
5723
5970
|
const remediation = `add a .gitattributes with a \`* -text\` line, or run \`git config core.autocrlf false\`, in ${repo}`;
|
|
5724
5971
|
addItem(section2, `${yellow(warnGlyph)} CRLF guard: ${risk}; ${remediation}`);
|
|
5725
5972
|
}
|
|
5726
|
-
function reportCrlfGuardCheck(section2, run =
|
|
5973
|
+
function reportCrlfGuardCheck(section2, run = execFileSync14) {
|
|
5727
5974
|
const repo = repoHome();
|
|
5728
5975
|
if (hasGitattributesGuard(repo)) {
|
|
5729
5976
|
addItem(section2, `${green(okGlyph)} CRLF guard: .gitattributes (* -text) present`);
|
|
@@ -5732,24 +5979,24 @@ function reportCrlfGuardCheck(section2, run = execFileSync13) {
|
|
|
5732
5979
|
addExposedRow(section2, repo, probeAutocrlf(repo, run));
|
|
5733
5980
|
}
|
|
5734
5981
|
|
|
5735
|
-
// src/spinner.ts
|
|
5982
|
+
// src/render/spinner.ts
|
|
5736
5983
|
init_color();
|
|
5737
|
-
import { existsSync as
|
|
5738
|
-
import { fileURLToPath as
|
|
5984
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
5985
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5739
5986
|
import { Worker } from "node:worker_threads";
|
|
5740
5987
|
|
|
5741
|
-
// src/commands
|
|
5988
|
+
// src/commands/push/recovery/recovery.ts
|
|
5742
5989
|
init_config();
|
|
5743
5990
|
import { readFileSync as readFileSync18, rmSync as rmSync15, writeFileSync as writeFileSync9 } from "node:fs";
|
|
5744
|
-
import { join as
|
|
5991
|
+
import { join as join50 } from "node:path";
|
|
5745
5992
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5746
5993
|
|
|
5747
|
-
// src/commands
|
|
5994
|
+
// src/commands/push/recovery/actions.ts
|
|
5748
5995
|
init_config();
|
|
5749
5996
|
import { readFileSync as readFileSync17 } from "node:fs";
|
|
5750
5997
|
import { isAbsolute as isAbsolute2, resolve as resolve4, sep as sep9 } from "node:path";
|
|
5751
5998
|
|
|
5752
|
-
// src/commands
|
|
5999
|
+
// src/commands/push/recovery/memory.ts
|
|
5753
6000
|
init_config();
|
|
5754
6001
|
init_config_sharedDirs_guard();
|
|
5755
6002
|
import {
|
|
@@ -5761,17 +6008,17 @@ import {
|
|
|
5761
6008
|
statSync as statSync7,
|
|
5762
6009
|
writeFileSync as writeFileSync6
|
|
5763
6010
|
} from "node:fs";
|
|
5764
|
-
import { dirname as
|
|
5765
|
-
|
|
6011
|
+
import { dirname as dirname11, join as join43, sep as sep6 } from "node:path";
|
|
6012
|
+
init_gitleaks_scan();
|
|
5766
6013
|
|
|
5767
|
-
// src/rel-path-guard.ts
|
|
6014
|
+
// src/core/rel-path-guard.ts
|
|
5768
6015
|
function isSafeRelPath(relPath) {
|
|
5769
6016
|
if (relPath.length === 0 || relPath.startsWith("/") || relPath.includes("\\")) return false;
|
|
5770
6017
|
const segments = relPath.split("/");
|
|
5771
6018
|
return segments.every((s) => s.length > 0 && s !== "." && s !== "..");
|
|
5772
6019
|
}
|
|
5773
6020
|
|
|
5774
|
-
// src/commands
|
|
6021
|
+
// src/commands/push/recovery/memory.ts
|
|
5775
6022
|
init_utils_fs();
|
|
5776
6023
|
init_utils_json();
|
|
5777
6024
|
init_utils();
|
|
@@ -5790,8 +6037,8 @@ function resolveMemoryLocalPath(logical, relPath, map) {
|
|
|
5790
6037
|
if (!isSafeRelPath(relPath)) return null;
|
|
5791
6038
|
const abs = map.projects[logical]?.[HOST];
|
|
5792
6039
|
if (abs === void 0) return null;
|
|
5793
|
-
const memoryRoot =
|
|
5794
|
-
const localPath =
|
|
6040
|
+
const memoryRoot = join43(claudeHome(), "projects", encodePath(abs), "memory");
|
|
6041
|
+
const localPath = join43(memoryRoot, ...relPath.split("/"));
|
|
5795
6042
|
if (localPath !== memoryRoot && !localPath.startsWith(memoryRoot + sep6)) return null;
|
|
5796
6043
|
try {
|
|
5797
6044
|
if (lstatSync14(localPath).isSymbolicLink()) return null;
|
|
@@ -5845,13 +6092,13 @@ function applyMemoryRedact(f, ts, map, scan = scanFile) {
|
|
|
5845
6092
|
);
|
|
5846
6093
|
}
|
|
5847
6094
|
writeFileSync6(localPath, after, "utf8");
|
|
5848
|
-
const dest =
|
|
5849
|
-
mkdirSync9(
|
|
6095
|
+
const dest = join43(repoHome(), "shared", "projects", logical, "memory", ...relPath.split("/"));
|
|
6096
|
+
mkdirSync9(dirname11(dest), { recursive: true });
|
|
5850
6097
|
cpSync6(localPath, dest, { force: true });
|
|
5851
6098
|
return true;
|
|
5852
6099
|
}
|
|
5853
6100
|
|
|
5854
|
-
// src/commands
|
|
6101
|
+
// src/commands/push/recovery/skills.ts
|
|
5855
6102
|
init_config();
|
|
5856
6103
|
import {
|
|
5857
6104
|
cpSync as cpSync7,
|
|
@@ -5862,27 +6109,27 @@ import {
|
|
|
5862
6109
|
statSync as statSync8,
|
|
5863
6110
|
writeFileSync as writeFileSync7
|
|
5864
6111
|
} from "node:fs";
|
|
5865
|
-
import { dirname as
|
|
6112
|
+
import { dirname as dirname12, join as join45, sep as sep7 } from "node:path";
|
|
5866
6113
|
|
|
5867
|
-
// src/skills-sync.ts
|
|
6114
|
+
// src/sync/skills-sync.ts
|
|
5868
6115
|
init_config();
|
|
5869
|
-
import { existsSync as
|
|
5870
|
-
import { join as
|
|
6116
|
+
import { existsSync as existsSync30, lstatSync as lstatSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync14, rmSync as rmSync13 } from "node:fs";
|
|
6117
|
+
import { join as join44 } from "node:path";
|
|
5871
6118
|
|
|
5872
|
-
// src/skills-sync.tracked.ts
|
|
6119
|
+
// src/sync/skills-sync.tracked.ts
|
|
5873
6120
|
init_utils();
|
|
5874
|
-
import { basename as
|
|
6121
|
+
import { basename as basename4 } from "node:path";
|
|
5875
6122
|
function trackedRootSkillsAt(ref, repo) {
|
|
5876
6123
|
try {
|
|
5877
6124
|
const raw = gitCaptureRaw(["ls-tree", "--name-only", "-z", ref, "--", "shared/skills/"], repo);
|
|
5878
6125
|
const entries = raw.split("\0").filter((entry) => entry !== "");
|
|
5879
|
-
return new Set(entries.map((entry) =>
|
|
6126
|
+
return new Set(entries.map((entry) => basename4(entry)));
|
|
5880
6127
|
} catch {
|
|
5881
6128
|
return /* @__PURE__ */ new Set();
|
|
5882
6129
|
}
|
|
5883
6130
|
}
|
|
5884
6131
|
|
|
5885
|
-
// src/skills-sync.ts
|
|
6132
|
+
// src/sync/skills-sync.ts
|
|
5886
6133
|
init_utils_fs();
|
|
5887
6134
|
function isGsdOwned(name) {
|
|
5888
6135
|
return name.startsWith(GSD_PREFIX);
|
|
@@ -5902,9 +6149,9 @@ function copySkillsPull(src, dst, isRootPreserved) {
|
|
|
5902
6149
|
copyExtrasFilteredPreservingBy(src, dst, isSkillExcluded, isRootPreserved);
|
|
5903
6150
|
}
|
|
5904
6151
|
function syncSkillsPull(ts, prePostHeads) {
|
|
5905
|
-
const sharedSkills =
|
|
5906
|
-
if (!
|
|
5907
|
-
const localSkills =
|
|
6152
|
+
const sharedSkills = join44(repoHome(), "shared", "skills");
|
|
6153
|
+
if (!existsSync30(sharedSkills)) return;
|
|
6154
|
+
const localSkills = join44(claudeHome(), "skills");
|
|
5908
6155
|
backupBeforeWrite(localSkills, ts);
|
|
5909
6156
|
const dstStat = lstatSync15(localSkills, { throwIfNoEntry: false });
|
|
5910
6157
|
if (dstStat?.isSymbolicLink() === true) {
|
|
@@ -5915,16 +6162,16 @@ function syncSkillsPull(ts, prePostHeads) {
|
|
|
5915
6162
|
copySkillsPull(sharedSkills, localSkills, (name) => !tracked.has(name));
|
|
5916
6163
|
}
|
|
5917
6164
|
function syncSkillsPush() {
|
|
5918
|
-
const localSkills =
|
|
6165
|
+
const localSkills = join44(claudeHome(), "skills");
|
|
5919
6166
|
const stat = lstatSync15(localSkills, { throwIfNoEntry: false });
|
|
5920
6167
|
if (stat === void 0) return;
|
|
5921
6168
|
if (stat.isSymbolicLink()) return;
|
|
5922
|
-
const sharedSkills =
|
|
6169
|
+
const sharedSkills = join44(repoHome(), "shared", "skills");
|
|
5923
6170
|
copySkillsPush(localSkills, sharedSkills);
|
|
5924
6171
|
}
|
|
5925
6172
|
|
|
5926
|
-
// src/commands
|
|
5927
|
-
|
|
6173
|
+
// src/commands/push/recovery/skills.ts
|
|
6174
|
+
init_gitleaks_scan();
|
|
5928
6175
|
init_utils_fs();
|
|
5929
6176
|
init_utils();
|
|
5930
6177
|
var SKILL_FINDING_PATH = /^shared\/skills\/([^/]+)\/(.+)$/;
|
|
@@ -5942,9 +6189,9 @@ function resolveSkillLocalPath(name, relPath) {
|
|
|
5942
6189
|
if (!SAFE_SKILL_NAME.test(name) || name === "." || name === "..") return null;
|
|
5943
6190
|
if (isGsdOwned(name)) return null;
|
|
5944
6191
|
if (!isSafeRelPath(relPath)) return null;
|
|
5945
|
-
const skillsRoot =
|
|
5946
|
-
const skillRoot =
|
|
5947
|
-
const localPath =
|
|
6192
|
+
const skillsRoot = join45(claudeHome(), "skills");
|
|
6193
|
+
const skillRoot = join45(skillsRoot, name);
|
|
6194
|
+
const localPath = join45(skillRoot, ...relPath.split("/"));
|
|
5948
6195
|
if (localPath !== skillRoot && !localPath.startsWith(skillRoot + sep7)) return null;
|
|
5949
6196
|
try {
|
|
5950
6197
|
if (lstatSync16(localPath).isSymbolicLink()) return null;
|
|
@@ -5996,17 +6243,17 @@ function applySkillRedact(f, ts, scan = scanFile) {
|
|
|
5996
6243
|
);
|
|
5997
6244
|
}
|
|
5998
6245
|
writeFileSync7(localPath, after, "utf8");
|
|
5999
|
-
const dest =
|
|
6000
|
-
mkdirSync11(
|
|
6246
|
+
const dest = join45(repoHome(), "shared", "skills", name, ...relPath.split("/"));
|
|
6247
|
+
mkdirSync11(dirname12(dest), { recursive: true });
|
|
6001
6248
|
cpSync7(localPath, dest, { force: true });
|
|
6002
6249
|
return true;
|
|
6003
6250
|
}
|
|
6004
6251
|
|
|
6005
|
-
// src/commands
|
|
6252
|
+
// src/commands/push/recovery/allow-gate.ts
|
|
6006
6253
|
init_utils();
|
|
6007
6254
|
|
|
6008
|
-
// src/commands
|
|
6009
|
-
|
|
6255
|
+
// src/commands/push/recovery/seams.ts
|
|
6256
|
+
init_gitleaks();
|
|
6010
6257
|
function findingKey(f) {
|
|
6011
6258
|
return `${f.File}:${f.StartLine}:${f.StartColumn}:${f.RuleID}`;
|
|
6012
6259
|
}
|
|
@@ -6027,7 +6274,7 @@ function parseAction(raw) {
|
|
|
6027
6274
|
return "skip";
|
|
6028
6275
|
}
|
|
6029
6276
|
|
|
6030
|
-
// src/commands
|
|
6277
|
+
// src/commands/push/recovery/allow-gate.ts
|
|
6031
6278
|
function isCleared(f, state) {
|
|
6032
6279
|
const action = state.actions.get(findingKey(f)) ?? "skip";
|
|
6033
6280
|
if (action === "allow") return true;
|
|
@@ -6071,28 +6318,28 @@ function applyDeferredAllows(findings, state, repo) {
|
|
|
6071
6318
|
}
|
|
6072
6319
|
}
|
|
6073
6320
|
|
|
6074
|
-
// src/commands
|
|
6321
|
+
// src/commands/push/recovery/redact.ts
|
|
6075
6322
|
init_config();
|
|
6076
6323
|
init_config_sharedDirs_guard();
|
|
6077
|
-
import { cpSync as cpSync8, existsSync as
|
|
6078
|
-
import { dirname as
|
|
6324
|
+
import { cpSync as cpSync8, existsSync as existsSync33, mkdirSync as mkdirSync12 } from "node:fs";
|
|
6325
|
+
import { dirname as dirname14, join as join48, sep as sep8 } from "node:path";
|
|
6079
6326
|
|
|
6080
|
-
// src/commands
|
|
6327
|
+
// src/commands/redact/redact.ts
|
|
6081
6328
|
init_config();
|
|
6082
|
-
import { existsSync as
|
|
6083
|
-
import { dirname as
|
|
6329
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
6330
|
+
import { dirname as dirname13, join as join47 } from "node:path";
|
|
6084
6331
|
|
|
6085
|
-
// src/commands
|
|
6086
|
-
import { existsSync as
|
|
6087
|
-
import { join as
|
|
6332
|
+
// src/commands/redact/subtree.ts
|
|
6333
|
+
import { existsSync as existsSync31, lstatSync as lstatSync17, readFileSync as readFileSync16, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
6334
|
+
import { join as join46 } from "node:path";
|
|
6088
6335
|
init_utils_fs();
|
|
6089
6336
|
init_utils();
|
|
6090
6337
|
function collectFiles(dir, out) {
|
|
6091
|
-
if (!
|
|
6338
|
+
if (!existsSync31(dir)) return;
|
|
6092
6339
|
const st = lstatSync17(dir);
|
|
6093
6340
|
if (!st.isDirectory()) return;
|
|
6094
6341
|
for (const entry of readdirSync15(dir)) {
|
|
6095
|
-
const abs =
|
|
6342
|
+
const abs = join46(dir, entry);
|
|
6096
6343
|
const lst = lstatSync17(abs);
|
|
6097
6344
|
if (lst.isSymbolicLink()) continue;
|
|
6098
6345
|
if (lst.isDirectory()) {
|
|
@@ -6146,12 +6393,12 @@ function applySubtreeRedactions(mainPath, mainFindings, subtreeFiles, rule, ts,
|
|
|
6146
6393
|
return { total, dirty };
|
|
6147
6394
|
}
|
|
6148
6395
|
|
|
6149
|
-
// src/commands
|
|
6396
|
+
// src/commands/pushed-history.ts
|
|
6150
6397
|
init_utils();
|
|
6151
|
-
import { execFileSync as
|
|
6398
|
+
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
6152
6399
|
function pushedRef(repo) {
|
|
6153
6400
|
try {
|
|
6154
|
-
const ref =
|
|
6401
|
+
const ref = execFileSync15("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], {
|
|
6155
6402
|
cwd: repo,
|
|
6156
6403
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6157
6404
|
}).toString().trim();
|
|
@@ -6164,7 +6411,7 @@ function sessionInPushedHistory(id, repo) {
|
|
|
6164
6411
|
const ref = pushedRef(repo);
|
|
6165
6412
|
if (ref === null) return false;
|
|
6166
6413
|
try {
|
|
6167
|
-
const out =
|
|
6414
|
+
const out = execFileSync15(
|
|
6168
6415
|
"git",
|
|
6169
6416
|
[
|
|
6170
6417
|
"log",
|
|
@@ -6194,23 +6441,23 @@ function warnIfSessionPushed(id, repo) {
|
|
|
6194
6441
|
);
|
|
6195
6442
|
}
|
|
6196
6443
|
|
|
6197
|
-
// src/commands
|
|
6198
|
-
|
|
6444
|
+
// src/commands/redact/redact.ts
|
|
6445
|
+
init_gitleaks_scan();
|
|
6199
6446
|
init_exit_codes();
|
|
6200
6447
|
init_utils_fs();
|
|
6201
6448
|
init_utils_json();
|
|
6202
6449
|
init_utils();
|
|
6203
6450
|
function resolveLiveTranscript(id) {
|
|
6204
6451
|
try {
|
|
6205
|
-
const mapPath =
|
|
6206
|
-
if (!
|
|
6452
|
+
const mapPath = join47(repoHome(), "path-map.json");
|
|
6453
|
+
if (!existsSync32(mapPath)) return null;
|
|
6207
6454
|
const projects = readJson(mapPath).projects;
|
|
6208
6455
|
const claude = claudeHome();
|
|
6209
6456
|
for (const hostMap of Object.values(projects)) {
|
|
6210
6457
|
const abs = hostMap[HOST];
|
|
6211
6458
|
if (abs === void 0) continue;
|
|
6212
|
-
const live =
|
|
6213
|
-
if (
|
|
6459
|
+
const live = join47(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
6460
|
+
if (existsSync32(live)) return live;
|
|
6214
6461
|
}
|
|
6215
6462
|
return null;
|
|
6216
6463
|
} catch {
|
|
@@ -6230,17 +6477,17 @@ function cmdRedact(opts, nowMs = Date.now, scan = scanFile) {
|
|
|
6230
6477
|
}
|
|
6231
6478
|
const repo = repoHome();
|
|
6232
6479
|
const backup = backupBase();
|
|
6233
|
-
if (!
|
|
6480
|
+
if (!existsSync32(repo)) die(`repo not cloned at ${repo}`);
|
|
6234
6481
|
const handle = acquireLock("redact");
|
|
6235
6482
|
if (handle === null) process.exit(0);
|
|
6236
6483
|
try {
|
|
6237
6484
|
const localPath = resolveLiveTranscript(id);
|
|
6238
|
-
if (localPath === null || !
|
|
6485
|
+
if (localPath === null || !existsSync32(localPath)) {
|
|
6239
6486
|
fail(`could not resolve local transcript for session ${id} on this host`);
|
|
6240
6487
|
process.exitCode = 1;
|
|
6241
6488
|
return;
|
|
6242
6489
|
}
|
|
6243
|
-
const sessionDir =
|
|
6490
|
+
const sessionDir = join47(dirname13(localPath), id);
|
|
6244
6491
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6245
6492
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6246
6493
|
log(
|
|
@@ -6292,8 +6539,8 @@ ${lines}`);
|
|
|
6292
6539
|
}
|
|
6293
6540
|
}
|
|
6294
6541
|
|
|
6295
|
-
// src/commands
|
|
6296
|
-
|
|
6542
|
+
// src/commands/push/recovery/redact.ts
|
|
6543
|
+
init_gitleaks_scan();
|
|
6297
6544
|
init_utils_json();
|
|
6298
6545
|
init_utils();
|
|
6299
6546
|
function resolveStagedDir(localPath, map, claude, repo) {
|
|
@@ -6301,8 +6548,8 @@ function resolveStagedDir(localPath, map, claude, repo) {
|
|
|
6301
6548
|
assertSafeLogical(logical);
|
|
6302
6549
|
const abs = hostMap[HOST];
|
|
6303
6550
|
if (abs === void 0) continue;
|
|
6304
|
-
if (localPath.startsWith(
|
|
6305
|
-
return
|
|
6551
|
+
if (localPath.startsWith(join48(claude, "projects", encodePath(abs)) + sep8)) {
|
|
6552
|
+
return join48(repo, "shared", "projects", logical);
|
|
6306
6553
|
}
|
|
6307
6554
|
}
|
|
6308
6555
|
return null;
|
|
@@ -6312,7 +6559,7 @@ function preflightRedactable(f, map, nowMs) {
|
|
|
6312
6559
|
if (sid === null) return "a finding has no resolvable session id (not a session transcript)";
|
|
6313
6560
|
const localPath = resolveLiveTranscript(sid);
|
|
6314
6561
|
if (localPath === null) return `session ${sid}: local transcript not found`;
|
|
6315
|
-
const sessionDir =
|
|
6562
|
+
const sessionDir = join48(dirname14(localPath), sid);
|
|
6316
6563
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6317
6564
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6318
6565
|
return `session ${sid}: looks active (modified within the last 5 minutes)`;
|
|
@@ -6341,7 +6588,7 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
6341
6588
|
`could not locate the local transcript for session ${sid}; choose Skip or Drop session.`
|
|
6342
6589
|
);
|
|
6343
6590
|
}
|
|
6344
|
-
const sessionDir =
|
|
6591
|
+
const sessionDir = join48(dirname14(localPath), sid);
|
|
6345
6592
|
const subtreeFiles = listSubtreeFiles(sessionDir);
|
|
6346
6593
|
if (isSubtreeActive(localPath, subtreeFiles, nowMs())) {
|
|
6347
6594
|
return refuse(
|
|
@@ -6374,35 +6621,35 @@ function applyRedact(f, ts, map, nowMs, scan = scanFile) {
|
|
|
6374
6621
|
);
|
|
6375
6622
|
}
|
|
6376
6623
|
mkdirSync12(stagedProjectDir, { recursive: true });
|
|
6377
|
-
cpSync8(localPath,
|
|
6378
|
-
if (
|
|
6379
|
-
cpSync8(sessionDir,
|
|
6624
|
+
cpSync8(localPath, join48(stagedProjectDir, `${sid}.jsonl`), { force: true });
|
|
6625
|
+
if (existsSync33(sessionDir)) {
|
|
6626
|
+
cpSync8(sessionDir, join48(stagedProjectDir, sid), { force: true, recursive: true });
|
|
6380
6627
|
}
|
|
6381
6628
|
return true;
|
|
6382
6629
|
}
|
|
6383
6630
|
|
|
6384
|
-
// src/commands
|
|
6631
|
+
// src/commands/push/recovery/drop.ts
|
|
6385
6632
|
init_config();
|
|
6386
6633
|
import { rmSync as rmSync14 } from "node:fs";
|
|
6387
|
-
import { join as
|
|
6634
|
+
import { join as join49 } from "node:path";
|
|
6388
6635
|
function dropSessionFromStaged(sid, map) {
|
|
6389
6636
|
const logicals = Object.keys(map.projects);
|
|
6390
6637
|
if (logicals.length === 0) return false;
|
|
6391
6638
|
const repo = repoHome();
|
|
6392
6639
|
for (const logical of logicals) {
|
|
6393
|
-
const jsonl =
|
|
6394
|
-
const dir =
|
|
6640
|
+
const jsonl = join49(repo, "shared", "projects", logical, `${sid}.jsonl`);
|
|
6641
|
+
const dir = join49(repo, "shared", "projects", logical, sid);
|
|
6395
6642
|
rmSync14(jsonl, { force: true });
|
|
6396
6643
|
rmSync14(dir, { recursive: true, force: true });
|
|
6397
6644
|
}
|
|
6398
6645
|
return true;
|
|
6399
6646
|
}
|
|
6400
6647
|
|
|
6401
|
-
// src/commands
|
|
6402
|
-
|
|
6648
|
+
// src/commands/push/recovery/actions.ts
|
|
6649
|
+
init_gitleaks_scan();
|
|
6403
6650
|
init_utils();
|
|
6404
6651
|
|
|
6405
|
-
// src/commands
|
|
6652
|
+
// src/commands/push/recovery/span-align.ts
|
|
6406
6653
|
var REDACTED_TOKEN = "REDACTED";
|
|
6407
6654
|
var SHIFT_WINDOW = [0, -1, 1, -2, 2];
|
|
6408
6655
|
var ENTROPY_TOLERANCE = 1e-4;
|
|
@@ -6489,7 +6736,7 @@ function alignFindingSpan(finding, buf) {
|
|
|
6489
6736
|
return breakTie(hits, finding);
|
|
6490
6737
|
}
|
|
6491
6738
|
|
|
6492
|
-
// src/commands
|
|
6739
|
+
// src/commands/push/recovery/secret-shape.ts
|
|
6493
6740
|
var VALUE_HEAD = 4;
|
|
6494
6741
|
var VALUE_TAIL = 4;
|
|
6495
6742
|
var MIN_ELIDE_LEN = 16;
|
|
@@ -6497,7 +6744,7 @@ var FRAGMENT_SEP = "...";
|
|
|
6497
6744
|
var NEAR_WINDOW = 40;
|
|
6498
6745
|
var VALUE_PAD = 16;
|
|
6499
6746
|
var SIBLING_PAD = 2;
|
|
6500
|
-
var
|
|
6747
|
+
var CONTROL_CHARS2 = /[\x00-\x1f\x7f]/g;
|
|
6501
6748
|
function classifyCharset(value) {
|
|
6502
6749
|
if (/^[0-9a-fA-F]+$/.test(value)) return "hex";
|
|
6503
6750
|
if (/^[0-9a-zA-Z]+$/.test(value)) return "alphanumeric";
|
|
@@ -6571,7 +6818,7 @@ function resolveSpanContext(finding, readLine, siblings = []) {
|
|
|
6571
6818
|
return { value: aligned.value, near: emptyToNull(near), exact: aligned.exact };
|
|
6572
6819
|
}
|
|
6573
6820
|
function formatNear(text) {
|
|
6574
|
-
const stripped = elideTokenRuns(text.replace(
|
|
6821
|
+
const stripped = elideTokenRuns(text.replace(CONTROL_CHARS2, ""));
|
|
6575
6822
|
const truncated = stripped.length > NEAR_WINDOW;
|
|
6576
6823
|
const window = stripped.slice(-NEAR_WINDOW).trim();
|
|
6577
6824
|
if (window.length === 0) return null;
|
|
@@ -6585,15 +6832,15 @@ function elideTokenRuns(text) {
|
|
|
6585
6832
|
});
|
|
6586
6833
|
}
|
|
6587
6834
|
|
|
6588
|
-
// src/commands
|
|
6835
|
+
// src/commands/push/recovery/display.ts
|
|
6589
6836
|
var RULE_PAD = 24;
|
|
6590
6837
|
var GROUP_SEP = "\0";
|
|
6591
6838
|
function sessionSuffix(finding) {
|
|
6592
6839
|
const sid = sessionIdFromFinding(finding);
|
|
6593
6840
|
if (sid === null) return "";
|
|
6594
6841
|
const rawBasename = finding.File.replace(/^.*\//, "");
|
|
6595
|
-
const
|
|
6596
|
-
return
|
|
6842
|
+
const basename5 = rawBasename.endsWith(".jsonl") ? rawBasename.slice(0, -".jsonl".length) : rawBasename;
|
|
6843
|
+
return basename5 === sid ? "" : ` (session: ${sid})`;
|
|
6597
6844
|
}
|
|
6598
6845
|
function renderFindingBlock(finding, readLine, siblings = []) {
|
|
6599
6846
|
const lines = [` file: ${finding.File}:${finding.StartLine}${sessionSuffix(finding)}`];
|
|
@@ -6643,7 +6890,7 @@ function buildPromptHeader(group, index, total, readLine, peers = 0, siblings =
|
|
|
6643
6890
|
return ["", headerLine, ...body, menu, ""].join("\n");
|
|
6644
6891
|
}
|
|
6645
6892
|
|
|
6646
|
-
// src/commands
|
|
6893
|
+
// src/commands/push/recovery/actions.ts
|
|
6647
6894
|
function allowAllFindings(findings, repo) {
|
|
6648
6895
|
for (const f of findings) {
|
|
6649
6896
|
appendGitleaksIgnore(f.Fingerprint, repo);
|
|
@@ -6776,8 +7023,8 @@ function dispatchActions(findings, actions, opts) {
|
|
|
6776
7023
|
applyDeferredAllows(findings, ctx, repo);
|
|
6777
7024
|
}
|
|
6778
7025
|
|
|
6779
|
-
// src/commands
|
|
6780
|
-
|
|
7026
|
+
// src/commands/push/recovery/redact-all.ts
|
|
7027
|
+
init_gitleaks_scan();
|
|
6781
7028
|
init_utils();
|
|
6782
7029
|
function redactAllDedupeKey(f) {
|
|
6783
7030
|
const sid = sessionIdFromFinding(f);
|
|
@@ -6842,10 +7089,10 @@ function redactAllFindings(findings, ts, map, nowMs, scan = scanFile) {
|
|
|
6842
7089
|
}
|
|
6843
7090
|
}
|
|
6844
7091
|
|
|
6845
|
-
// src/commands
|
|
7092
|
+
// src/commands/push/recovery/recovery.ts
|
|
6846
7093
|
init_exit_codes();
|
|
6847
|
-
|
|
6848
|
-
|
|
7094
|
+
init_gitleaks_scan();
|
|
7095
|
+
init_gitleaks();
|
|
6849
7096
|
init_utils();
|
|
6850
7097
|
function isTTY(stdin = process.stdin, stdout = process.stdout) {
|
|
6851
7098
|
return stdin.isTTY === true && stdout.isTTY === true;
|
|
@@ -6876,7 +7123,7 @@ function applyThenRescan(scanVerdict, repoHome2) {
|
|
|
6876
7123
|
return next;
|
|
6877
7124
|
}
|
|
6878
7125
|
function allowThenRescan(append, scanVerdict, repoHome2) {
|
|
6879
|
-
const ignPath =
|
|
7126
|
+
const ignPath = join50(repoHome2, ".gitleaksignore");
|
|
6880
7127
|
let before;
|
|
6881
7128
|
try {
|
|
6882
7129
|
before = readFileSync18(ignPath, "utf8");
|
|
@@ -6920,7 +7167,7 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
|
|
|
6920
7167
|
scan = scanFile,
|
|
6921
7168
|
printLegend = printRecoveryLegend
|
|
6922
7169
|
} = deps;
|
|
6923
|
-
const scanVerdict = deps.scanVerdict ?? (await Promise.resolve().then(() => (
|
|
7170
|
+
const scanVerdict = deps.scanVerdict ?? (await Promise.resolve().then(() => (init_leak_verdict(), leak_verdict_exports))).scanPushVerdict;
|
|
6924
7171
|
const repo = repoHome();
|
|
6925
7172
|
let current = verdict;
|
|
6926
7173
|
if (redactAll) {
|
|
@@ -6963,7 +7210,7 @@ async function resolveLeakFindings(verdict, ts, map, deps = {}) {
|
|
|
6963
7210
|
return current;
|
|
6964
7211
|
}
|
|
6965
7212
|
|
|
6966
|
-
// src/spinner.ts
|
|
7213
|
+
// src/render/spinner.ts
|
|
6967
7214
|
function formatElapsed(ms) {
|
|
6968
7215
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
6969
7216
|
}
|
|
@@ -6982,11 +7229,11 @@ function writeAnimatedDone(out, label, ms, useTTY) {
|
|
|
6982
7229
|
`);
|
|
6983
7230
|
}
|
|
6984
7231
|
function resolveWorkerPath(deps = {}) {
|
|
6985
|
-
const check = deps.existsSyncFn ??
|
|
7232
|
+
const check = deps.existsSyncFn ?? existsSync34;
|
|
6986
7233
|
const base = deps.baseUrl ?? import.meta.url;
|
|
6987
|
-
const mjs =
|
|
7234
|
+
const mjs = fileURLToPath2(new URL("./nomad.worker.mjs", base));
|
|
6988
7235
|
if (check(mjs)) return mjs;
|
|
6989
|
-
return
|
|
7236
|
+
return fileURLToPath2(new URL("./spinner.worker.ts", base));
|
|
6990
7237
|
}
|
|
6991
7238
|
function makeRealWorker() {
|
|
6992
7239
|
return new Worker(resolveWorkerPath());
|
|
@@ -7046,11 +7293,11 @@ function withSpinner(label, fn, deps) {
|
|
|
7046
7293
|
}
|
|
7047
7294
|
}
|
|
7048
7295
|
|
|
7049
|
-
// src/commands
|
|
7296
|
+
// src/commands/doctor/gitleaks-version.ts
|
|
7050
7297
|
init_color();
|
|
7051
|
-
import { execFileSync as
|
|
7052
|
-
import { existsSync as
|
|
7053
|
-
import { join as
|
|
7298
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
7299
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
7300
|
+
import { join as join51 } from "node:path";
|
|
7054
7301
|
init_config();
|
|
7055
7302
|
var SEMVER_MAJOR_MINOR = /^(\d+)\.(\d+)\.\d+$/;
|
|
7056
7303
|
var GITLEAKS_TIMEOUT_MS = 5e3;
|
|
@@ -7059,7 +7306,7 @@ function majorMinorOf(value) {
|
|
|
7059
7306
|
return m === null ? null : [m[1], m[2]];
|
|
7060
7307
|
}
|
|
7061
7308
|
function readGitleaksVersion(run, tomlExists) {
|
|
7062
|
-
const tomlPath =
|
|
7309
|
+
const tomlPath = join51(repoHome(), ".gitleaks.toml");
|
|
7063
7310
|
const args = ["version"];
|
|
7064
7311
|
if (tomlExists(tomlPath)) args.push("--config", tomlPath);
|
|
7065
7312
|
try {
|
|
@@ -7071,7 +7318,7 @@ function readGitleaksVersion(run, tomlExists) {
|
|
|
7071
7318
|
return null;
|
|
7072
7319
|
}
|
|
7073
7320
|
}
|
|
7074
|
-
function reportGitleaksVersionCheck(section2, run =
|
|
7321
|
+
function reportGitleaksVersionCheck(section2, run = execFileSync16, tomlExists = existsSync35) {
|
|
7075
7322
|
const raw = readGitleaksVersion(run, tomlExists);
|
|
7076
7323
|
if (raw === null) return;
|
|
7077
7324
|
const local = majorMinorOf(raw);
|
|
@@ -7089,11 +7336,11 @@ function reportGitleaksVersionCheck(section2, run = execFileSync15, tomlExists =
|
|
|
7089
7336
|
);
|
|
7090
7337
|
}
|
|
7091
7338
|
|
|
7092
|
-
// src/commands
|
|
7339
|
+
// src/commands/doctor/checks/deps.ts
|
|
7093
7340
|
init_color();
|
|
7094
|
-
import { execFileSync as
|
|
7341
|
+
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
7095
7342
|
var VERSION_TOKEN = /(\d{1,9}\.\d{1,9}\.\d{1,9})/;
|
|
7096
|
-
var
|
|
7343
|
+
var PROBE_TIMEOUT_MS4 = 3e3;
|
|
7097
7344
|
var FETCHER_BASE = "HTTP fetcher";
|
|
7098
7345
|
function parseFirstVersion(line) {
|
|
7099
7346
|
const m = VERSION_TOKEN.exec(line);
|
|
@@ -7103,7 +7350,7 @@ function probeOptionalDep(bin, run) {
|
|
|
7103
7350
|
try {
|
|
7104
7351
|
const firstLine = run(bin, ["--version"], {
|
|
7105
7352
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7106
|
-
timeout:
|
|
7353
|
+
timeout: PROBE_TIMEOUT_MS4
|
|
7107
7354
|
}).toString().split("\n")[0].trim();
|
|
7108
7355
|
const version = parseFirstVersion(firstLine);
|
|
7109
7356
|
return { status: "present", version };
|
|
@@ -7128,7 +7375,7 @@ function reportFetcherRow(section2, run) {
|
|
|
7128
7375
|
);
|
|
7129
7376
|
}
|
|
7130
7377
|
}
|
|
7131
|
-
function reportOptionalDeps(section2, run =
|
|
7378
|
+
function reportOptionalDeps(section2, run = execFileSync17) {
|
|
7132
7379
|
const gh = probeOptionalDep("gh", run);
|
|
7133
7380
|
if (gh.status === "present") {
|
|
7134
7381
|
addItem(section2, `${green(okGlyph)} gh: ${gh.version ?? "present"}`);
|
|
@@ -7141,13 +7388,13 @@ function reportOptionalDeps(section2, run = execFileSync16) {
|
|
|
7141
7388
|
reportFetcherRow(section2, run);
|
|
7142
7389
|
}
|
|
7143
7390
|
|
|
7144
|
-
// src/commands
|
|
7391
|
+
// src/commands/doctor/actions-drift.ts
|
|
7145
7392
|
init_color();
|
|
7146
|
-
import { execFileSync as
|
|
7393
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
7147
7394
|
init_config();
|
|
7148
7395
|
|
|
7149
|
-
// src/gh-actions.ts
|
|
7150
|
-
import { execFileSync as
|
|
7396
|
+
// src/commands/init/gh-actions.ts
|
|
7397
|
+
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
7151
7398
|
var GH_TIMEOUT_MS = 5e3;
|
|
7152
7399
|
function parseGitHubRemote(remoteUrl) {
|
|
7153
7400
|
const normalized = remoteUrl.trim().replace(/\/$/, "");
|
|
@@ -7155,7 +7402,7 @@ function parseGitHubRemote(remoteUrl) {
|
|
|
7155
7402
|
if (m === null) return null;
|
|
7156
7403
|
return { owner: m[1], repo: m[2] };
|
|
7157
7404
|
}
|
|
7158
|
-
function ghAuthStatus(run =
|
|
7405
|
+
function ghAuthStatus(run = execFileSync18) {
|
|
7159
7406
|
try {
|
|
7160
7407
|
run("gh", ["auth", "status"], {
|
|
7161
7408
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -7169,7 +7416,7 @@ function ghAuthStatus(run = execFileSync17) {
|
|
|
7169
7416
|
return "gh-probe-error";
|
|
7170
7417
|
}
|
|
7171
7418
|
}
|
|
7172
|
-
function isRepoPrivate(ref, run =
|
|
7419
|
+
function isRepoPrivate(ref, run = execFileSync18) {
|
|
7173
7420
|
const out = run("gh", ["repo", "view", `${ref.owner}/${ref.repo}`, "--json", "isPrivate"], {
|
|
7174
7421
|
stdio: ["ignore", "pipe", "ignore"],
|
|
7175
7422
|
timeout: GH_TIMEOUT_MS
|
|
@@ -7177,7 +7424,7 @@ function isRepoPrivate(ref, run = execFileSync17) {
|
|
|
7177
7424
|
const parsed = JSON.parse(out);
|
|
7178
7425
|
return parsed.isPrivate === true;
|
|
7179
7426
|
}
|
|
7180
|
-
function isActionsEnabled(ref, run =
|
|
7427
|
+
function isActionsEnabled(ref, run = execFileSync18) {
|
|
7181
7428
|
const out = run(
|
|
7182
7429
|
"gh",
|
|
7183
7430
|
["api", `repos/${ref.owner}/${ref.repo}/actions/permissions`, "--jq", ".enabled"],
|
|
@@ -7185,7 +7432,7 @@ function isActionsEnabled(ref, run = execFileSync17) {
|
|
|
7185
7432
|
).toString().trim();
|
|
7186
7433
|
return out === "true";
|
|
7187
7434
|
}
|
|
7188
|
-
function disableActions(ref, run =
|
|
7435
|
+
function disableActions(ref, run = execFileSync18) {
|
|
7189
7436
|
run(
|
|
7190
7437
|
"gh",
|
|
7191
7438
|
[
|
|
@@ -7199,15 +7446,15 @@ function disableActions(ref, run = execFileSync17) {
|
|
|
7199
7446
|
{ stdio: ["ignore", "ignore", "pipe"], timeout: GH_TIMEOUT_MS }
|
|
7200
7447
|
);
|
|
7201
7448
|
}
|
|
7202
|
-
function readOriginRemote(cwd, run =
|
|
7449
|
+
function readOriginRemote(cwd, run = execFileSync18) {
|
|
7203
7450
|
return run("git", ["remote", "get-url", "origin"], {
|
|
7204
7451
|
cwd,
|
|
7205
7452
|
stdio: ["ignore", "pipe", "ignore"]
|
|
7206
7453
|
}).toString().trim();
|
|
7207
7454
|
}
|
|
7208
7455
|
|
|
7209
|
-
// src/commands
|
|
7210
|
-
function reportActionsDrift(section2, run =
|
|
7456
|
+
// src/commands/doctor/actions-drift.ts
|
|
7457
|
+
function reportActionsDrift(section2, run = execFileSync19) {
|
|
7211
7458
|
let remote;
|
|
7212
7459
|
try {
|
|
7213
7460
|
remote = readOriginRemote(repoHome(), run);
|
|
@@ -7238,7 +7485,7 @@ function reportActionsDrift(section2, run = execFileSync18) {
|
|
|
7238
7485
|
);
|
|
7239
7486
|
}
|
|
7240
7487
|
|
|
7241
|
-
// src/commands
|
|
7488
|
+
// src/commands/doctor/verdict.ts
|
|
7242
7489
|
init_color();
|
|
7243
7490
|
function isFailLine(item2) {
|
|
7244
7491
|
return item2.includes(failGlyph);
|
|
@@ -7262,7 +7509,7 @@ function buildVerdictSection(sections) {
|
|
|
7262
7509
|
return summary;
|
|
7263
7510
|
}
|
|
7264
7511
|
|
|
7265
|
-
// src/commands
|
|
7512
|
+
// src/commands/doctor/banner.ts
|
|
7266
7513
|
var NOMAD_BANNER = [
|
|
7267
7514
|
" __",
|
|
7268
7515
|
" ____ ____ ____ ___ ____ _ ____/ /",
|
|
@@ -7276,7 +7523,7 @@ function printBanner(log2, stdout) {
|
|
|
7276
7523
|
log2("");
|
|
7277
7524
|
}
|
|
7278
7525
|
|
|
7279
|
-
// src/commands
|
|
7526
|
+
// src/commands/doctor/compact.ts
|
|
7280
7527
|
init_color();
|
|
7281
7528
|
var ALWAYS_FULL = /* @__PURE__ */ new Set([
|
|
7282
7529
|
"Nomad Version",
|
|
@@ -7318,7 +7565,7 @@ function compactSections(sections) {
|
|
|
7318
7565
|
});
|
|
7319
7566
|
}
|
|
7320
7567
|
|
|
7321
|
-
// src/commands
|
|
7568
|
+
// src/commands/doctor/doctor.ts
|
|
7322
7569
|
function gatherDoctorSections(opts) {
|
|
7323
7570
|
const host = section("Environment");
|
|
7324
7571
|
reportHostAndPaths(host);
|
|
@@ -7328,8 +7575,8 @@ function gatherDoctorSections(opts) {
|
|
|
7328
7575
|
reportLongPathsCheck(host);
|
|
7329
7576
|
reportCrlfGuardCheck(host);
|
|
7330
7577
|
const links = section("Shared links");
|
|
7331
|
-
const mapPath =
|
|
7332
|
-
const rawMap =
|
|
7578
|
+
const mapPath = join52(repoHome(), "path-map.json");
|
|
7579
|
+
const rawMap = existsSync36(mapPath) ? readJsonSafe(mapPath, mapPath, links) : null;
|
|
7333
7580
|
const map = rawMap ?? { projects: {} };
|
|
7334
7581
|
reportSharedLinks(links, map);
|
|
7335
7582
|
reportDroppedNamesMigration(links);
|
|
@@ -7347,6 +7594,7 @@ function gatherDoctorSections(opts) {
|
|
|
7347
7594
|
reportPathMap(pathMap);
|
|
7348
7595
|
const neverSync = section("Never-sync");
|
|
7349
7596
|
reportNeverSync(neverSync);
|
|
7597
|
+
reportTrackedDeniedShared(neverSync);
|
|
7350
7598
|
const skills = section("Skills");
|
|
7351
7599
|
reportSkillsDivergence(skills);
|
|
7352
7600
|
const repository = section("Repository");
|
|
@@ -7403,7 +7651,7 @@ function cmdDoctor(opts = {}) {
|
|
|
7403
7651
|
renderDoctor(opts.verbose === true ? report : compactSections(report));
|
|
7404
7652
|
}
|
|
7405
7653
|
|
|
7406
|
-
// src/
|
|
7654
|
+
// src/cli/dispatch.doctor.ts
|
|
7407
7655
|
function parseDoctorArgs(args) {
|
|
7408
7656
|
if (args[0] === "--resume-cmd") {
|
|
7409
7657
|
const id = args[1];
|
|
@@ -7424,17 +7672,17 @@ function parseDoctorArgs(args) {
|
|
|
7424
7672
|
return { kind: "run", checkShared, checkSchema, checkRemote, verbose };
|
|
7425
7673
|
}
|
|
7426
7674
|
|
|
7427
|
-
// src/commands
|
|
7675
|
+
// src/commands/drop-session/drop-session.ts
|
|
7428
7676
|
init_config();
|
|
7429
|
-
import { execFileSync as
|
|
7430
|
-
import { existsSync as
|
|
7431
|
-
import { join as
|
|
7677
|
+
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
7678
|
+
import { existsSync as existsSync38, readdirSync as readdirSync16, statSync as statSync10 } from "node:fs";
|
|
7679
|
+
import { join as join54, relative as relative7 } from "node:path";
|
|
7432
7680
|
|
|
7433
|
-
// src/commands
|
|
7434
|
-
import { execFileSync as
|
|
7681
|
+
// src/commands/drop-session/git.ts
|
|
7682
|
+
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
7435
7683
|
function expandStagedDir(dirRel, repo) {
|
|
7436
7684
|
try {
|
|
7437
|
-
const out =
|
|
7685
|
+
const out = execFileSync20("git", ["ls-files", "-z", "--", dirRel], {
|
|
7438
7686
|
cwd: repo,
|
|
7439
7687
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7440
7688
|
});
|
|
@@ -7446,7 +7694,7 @@ function expandStagedDir(dirRel, repo) {
|
|
|
7446
7694
|
function isTrackedInHead(rel, repo) {
|
|
7447
7695
|
try {
|
|
7448
7696
|
const treePath = process.platform === "win32" ? rel.replaceAll("\\", "/") : rel;
|
|
7449
|
-
|
|
7697
|
+
execFileSync20("git", ["cat-file", "-e", `HEAD:${treePath}`], {
|
|
7450
7698
|
cwd: repo,
|
|
7451
7699
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7452
7700
|
});
|
|
@@ -7457,7 +7705,7 @@ function isTrackedInHead(rel, repo) {
|
|
|
7457
7705
|
}
|
|
7458
7706
|
function isInIndex(rel, repo) {
|
|
7459
7707
|
try {
|
|
7460
|
-
const out =
|
|
7708
|
+
const out = execFileSync20("git", ["ls-files", "--", rel], {
|
|
7461
7709
|
cwd: repo,
|
|
7462
7710
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7463
7711
|
});
|
|
@@ -7467,12 +7715,12 @@ function isInIndex(rel, repo) {
|
|
|
7467
7715
|
}
|
|
7468
7716
|
}
|
|
7469
7717
|
|
|
7470
|
-
// src/commands
|
|
7718
|
+
// src/commands/drop-session/scrub-hint.ts
|
|
7471
7719
|
init_config();
|
|
7472
7720
|
init_utils();
|
|
7473
7721
|
init_utils_json();
|
|
7474
|
-
import { existsSync as
|
|
7475
|
-
import { join as
|
|
7722
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
7723
|
+
import { join as join53 } from "node:path";
|
|
7476
7724
|
var SHARED_PROJECT_LOGICAL = /^shared\/projects\/([^/]+)\//;
|
|
7477
7725
|
function reportScrubHint(id, matches) {
|
|
7478
7726
|
const live = resolveLiveTranscript2(id, matches);
|
|
@@ -7488,8 +7736,8 @@ function reportScrubHint(id, matches) {
|
|
|
7488
7736
|
}
|
|
7489
7737
|
function resolveLiveTranscript2(id, matches) {
|
|
7490
7738
|
try {
|
|
7491
|
-
const mapPath =
|
|
7492
|
-
if (!
|
|
7739
|
+
const mapPath = join53(repoHome(), "path-map.json");
|
|
7740
|
+
if (!existsSync37(mapPath)) return null;
|
|
7493
7741
|
const projects = readJson(mapPath).projects;
|
|
7494
7742
|
const claude = claudeHome();
|
|
7495
7743
|
for (const rel of matches) {
|
|
@@ -7497,8 +7745,8 @@ function resolveLiveTranscript2(id, matches) {
|
|
|
7497
7745
|
if (logical === void 0) continue;
|
|
7498
7746
|
const abs = projects[logical]?.[HOST];
|
|
7499
7747
|
if (abs === void 0) continue;
|
|
7500
|
-
const live =
|
|
7501
|
-
if (
|
|
7748
|
+
const live = join53(claude, "projects", encodePath(abs), `${id}.jsonl`);
|
|
7749
|
+
if (existsSync37(live)) return live;
|
|
7502
7750
|
}
|
|
7503
7751
|
return null;
|
|
7504
7752
|
} catch {
|
|
@@ -7506,7 +7754,7 @@ function resolveLiveTranscript2(id, matches) {
|
|
|
7506
7754
|
}
|
|
7507
7755
|
}
|
|
7508
7756
|
|
|
7509
|
-
// src/commands
|
|
7757
|
+
// src/commands/drop-session/drop-session.ts
|
|
7510
7758
|
init_utils();
|
|
7511
7759
|
function cmdDropSession(id) {
|
|
7512
7760
|
if (id.length === 0 || id.length > 128 || !/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
@@ -7514,12 +7762,12 @@ function cmdDropSession(id) {
|
|
|
7514
7762
|
process.exit(1);
|
|
7515
7763
|
}
|
|
7516
7764
|
const repo = repoHome();
|
|
7517
|
-
if (!
|
|
7765
|
+
if (!existsSync38(repo)) die(`repo not cloned at ${repo}`);
|
|
7518
7766
|
const handle = acquireLock("drop-session");
|
|
7519
7767
|
if (handle === null) process.exit(0);
|
|
7520
7768
|
try {
|
|
7521
|
-
const repoProjects =
|
|
7522
|
-
if (!
|
|
7769
|
+
const repoProjects = join54(repo, "shared", "projects");
|
|
7770
|
+
if (!existsSync38(repoProjects)) {
|
|
7523
7771
|
throw new NomadFatal(`no staged session matches ${id}`);
|
|
7524
7772
|
}
|
|
7525
7773
|
const matches = collectMatches(repoProjects, id, repo);
|
|
@@ -7542,12 +7790,12 @@ function cmdDropSession(id) {
|
|
|
7542
7790
|
function collectMatches(repoProjects, id, repo) {
|
|
7543
7791
|
const matches = [];
|
|
7544
7792
|
for (const logical of readdirSync16(repoProjects)) {
|
|
7545
|
-
const candidate =
|
|
7546
|
-
if (
|
|
7793
|
+
const candidate = join54(repoProjects, logical, `${id}.jsonl`);
|
|
7794
|
+
if (existsSync38(candidate)) {
|
|
7547
7795
|
matches.push(relative7(repo, candidate));
|
|
7548
7796
|
}
|
|
7549
|
-
const dir =
|
|
7550
|
-
if (
|
|
7797
|
+
const dir = join54(repoProjects, logical, id);
|
|
7798
|
+
if (existsSync38(dir) && statSync10(dir).isDirectory()) {
|
|
7551
7799
|
const dirRel = relative7(repo, dir);
|
|
7552
7800
|
const staged = expandStagedDir(dirRel, repo);
|
|
7553
7801
|
if (staged.length > 0) matches.push(...staged);
|
|
@@ -7563,12 +7811,12 @@ function unstageOne(rel, repo) {
|
|
|
7563
7811
|
}
|
|
7564
7812
|
try {
|
|
7565
7813
|
if (isTrackedInHead(rel, repo)) {
|
|
7566
|
-
|
|
7814
|
+
execFileSync21("git", ["restore", "--staged", "--worktree", "--", rel], {
|
|
7567
7815
|
cwd: repo,
|
|
7568
7816
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7569
7817
|
});
|
|
7570
7818
|
} else {
|
|
7571
|
-
|
|
7819
|
+
execFileSync21("git", ["rm", "--cached", "-f", "--", rel], {
|
|
7572
7820
|
cwd: repo,
|
|
7573
7821
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7574
7822
|
});
|
|
@@ -7581,15 +7829,15 @@ function unstageOne(rel, repo) {
|
|
|
7581
7829
|
item(`dropped ${rel}`);
|
|
7582
7830
|
}
|
|
7583
7831
|
|
|
7584
|
-
// src/commands
|
|
7832
|
+
// src/commands/pull/pull.ts
|
|
7585
7833
|
init_autostash_guard();
|
|
7586
|
-
import { existsSync as
|
|
7587
|
-
import { join as
|
|
7834
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync14 } from "node:fs";
|
|
7835
|
+
import { join as join65 } from "node:path";
|
|
7588
7836
|
|
|
7589
|
-
// src/commands
|
|
7837
|
+
// src/commands/push/sections.ts
|
|
7590
7838
|
init_color();
|
|
7591
7839
|
|
|
7592
|
-
// src/summary.ts
|
|
7840
|
+
// src/render/summary.ts
|
|
7593
7841
|
init_utils();
|
|
7594
7842
|
function summaryText(verb, unmapped, collisions = 0, extrasSkipped = 0, localOnly = 0) {
|
|
7595
7843
|
const extras = extrasSkipped > 0 ? `, ${extrasSkipped} extras skipped` : "";
|
|
@@ -7614,7 +7862,7 @@ function summaryRow(verb, unmapped, collisions = 0, extrasSkipped = 0, localOnly
|
|
|
7614
7862
|
return text.replace(/^summary: /, "");
|
|
7615
7863
|
}
|
|
7616
7864
|
|
|
7617
|
-
// src/commands
|
|
7865
|
+
// src/commands/push/sections.ts
|
|
7618
7866
|
function collapsedSkipRow(n, noun) {
|
|
7619
7867
|
if (n <= 0) return null;
|
|
7620
7868
|
return `${dim(infoGlyph)} ${n} ${noun}`;
|
|
@@ -7687,23 +7935,23 @@ function renderNoScanTree(st, opts = {}) {
|
|
|
7687
7935
|
renderTree(buildNoScanSections(st, opts));
|
|
7688
7936
|
}
|
|
7689
7937
|
|
|
7690
|
-
// src/commands
|
|
7938
|
+
// src/commands/pull/pull.ts
|
|
7691
7939
|
init_config();
|
|
7692
7940
|
|
|
7693
|
-
// src/extras
|
|
7941
|
+
// src/sync/extras/extras.ts
|
|
7694
7942
|
init_config();
|
|
7695
|
-
import { existsSync as
|
|
7696
|
-
import { join as
|
|
7943
|
+
import { existsSync as existsSync40, statSync as statSync11 } from "node:fs";
|
|
7944
|
+
import { join as join57 } from "node:path";
|
|
7697
7945
|
init_utils();
|
|
7698
7946
|
init_utils_json();
|
|
7699
7947
|
|
|
7700
|
-
// src/extras
|
|
7948
|
+
// src/sync/extras/remap.ts
|
|
7701
7949
|
init_config();
|
|
7702
|
-
import { existsSync as
|
|
7703
|
-
import { dirname as
|
|
7950
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync13, readdirSync as readdirSync17, readFileSync as readFileSync19, realpathSync as realpathSync7, rmSync as rmSync16 } from "node:fs";
|
|
7951
|
+
import { dirname as dirname15, join as join56, sep as sep11 } from "node:path";
|
|
7704
7952
|
|
|
7705
|
-
// src/extras
|
|
7706
|
-
import { join as
|
|
7953
|
+
// src/sync/extras/planning-diff.ts
|
|
7954
|
+
import { join as join55, normalize as normalize2, sep as sep10 } from "node:path";
|
|
7707
7955
|
init_utils();
|
|
7708
7956
|
function processRecord(fields, i, changed, deleted) {
|
|
7709
7957
|
const status = fields[i];
|
|
@@ -7755,7 +8003,7 @@ function planningDeleteTargets(opts) {
|
|
|
7755
8003
|
const { deleted } = parsePlanningDiff(raw);
|
|
7756
8004
|
const logicalPrefix = "shared/extras/" + logical + "/";
|
|
7757
8005
|
const prefix = logicalPrefix + ".planning/";
|
|
7758
|
-
const planningRoot =
|
|
8006
|
+
const planningRoot = join55(localRoot, ".planning");
|
|
7759
8007
|
const planningRootBoundary = planningRoot + sep10;
|
|
7760
8008
|
const targets = [];
|
|
7761
8009
|
for (const repoPath of deleted) {
|
|
@@ -7763,7 +8011,7 @@ function planningDeleteTargets(opts) {
|
|
|
7763
8011
|
continue;
|
|
7764
8012
|
}
|
|
7765
8013
|
const remainder = repoPath.slice(logicalPrefix.length);
|
|
7766
|
-
const candidate =
|
|
8014
|
+
const candidate = join55(localRoot, remainder);
|
|
7767
8015
|
const resolved = normalize2(candidate);
|
|
7768
8016
|
if (!resolved.startsWith(planningRootBoundary)) {
|
|
7769
8017
|
throw new NomadFatal(
|
|
@@ -7775,7 +8023,7 @@ function planningDeleteTargets(opts) {
|
|
|
7775
8023
|
return targets;
|
|
7776
8024
|
}
|
|
7777
8025
|
|
|
7778
|
-
// src/extras
|
|
8026
|
+
// src/sync/extras/remap.ts
|
|
7779
8027
|
init_utils_fs();
|
|
7780
8028
|
init_utils();
|
|
7781
8029
|
function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
@@ -7784,7 +8032,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
7784
8032
|
const would = [];
|
|
7785
8033
|
for (const t of eachExtrasTarget(v, counts)) {
|
|
7786
8034
|
const { src, dst } = paths(t);
|
|
7787
|
-
if (!
|
|
8035
|
+
if (!existsSync39(src)) continue;
|
|
7788
8036
|
const item2 = `${t.logical}/${t.dirname}`;
|
|
7789
8037
|
if (dryRun) {
|
|
7790
8038
|
would.push(item2);
|
|
@@ -7797,7 +8045,7 @@ function runExtrasOp(v, dryRun, paths, backup, copy) {
|
|
|
7797
8045
|
return { ...counts, done, would };
|
|
7798
8046
|
}
|
|
7799
8047
|
function pruneEmptyAncestors(target, planningRoot) {
|
|
7800
|
-
let dir =
|
|
8048
|
+
let dir = dirname15(target);
|
|
7801
8049
|
while (dir !== planningRoot && dir.startsWith(planningRoot + sep11)) {
|
|
7802
8050
|
try {
|
|
7803
8051
|
if (readdirSync17(dir).length > 0) break;
|
|
@@ -7805,7 +8053,7 @@ function pruneEmptyAncestors(target, planningRoot) {
|
|
|
7805
8053
|
} catch {
|
|
7806
8054
|
break;
|
|
7807
8055
|
}
|
|
7808
|
-
dir =
|
|
8056
|
+
dir = dirname15(dir);
|
|
7809
8057
|
}
|
|
7810
8058
|
}
|
|
7811
8059
|
function tryRealpath(dir) {
|
|
@@ -7819,8 +8067,8 @@ function isInsidePlanningRoot(parentReal, rootReal) {
|
|
|
7819
8067
|
return parentReal === rootReal || parentReal.startsWith(rootReal + sep11);
|
|
7820
8068
|
}
|
|
7821
8069
|
function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
7822
|
-
if (
|
|
7823
|
-
const parentReal = tryRealpath(
|
|
8070
|
+
if (existsSync39(repoCounterpart)) return;
|
|
8071
|
+
const parentReal = tryRealpath(dirname15(target));
|
|
7824
8072
|
if (parentReal === void 0) return;
|
|
7825
8073
|
const rootReal = tryRealpath(planningRoot);
|
|
7826
8074
|
if (rootReal === void 0) return;
|
|
@@ -7829,7 +8077,7 @@ function deletePlanningTarget(target, planningRoot, repoCounterpart) {
|
|
|
7829
8077
|
pruneEmptyAncestors(target, planningRoot);
|
|
7830
8078
|
}
|
|
7831
8079
|
function localDivergesFromPreDelete(target, pre, repoRel, repo) {
|
|
7832
|
-
if (!
|
|
8080
|
+
if (!existsSync39(target)) return false;
|
|
7833
8081
|
try {
|
|
7834
8082
|
const preBlob = gitCaptureBuffer(["show", `${pre}:${repoRel}`], repo);
|
|
7835
8083
|
return !readFileSync19(target).equals(preBlob);
|
|
@@ -7874,7 +8122,7 @@ function keptDeletePreview(v, prePostHeads, repo) {
|
|
|
7874
8122
|
return kept;
|
|
7875
8123
|
}
|
|
7876
8124
|
function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
7877
|
-
const repoExtras =
|
|
8125
|
+
const repoExtras = join56(repo, "shared", "extras");
|
|
7878
8126
|
for (const t of eachExtrasTarget(v, { unmapped: 0, skipped: 0 })) {
|
|
7879
8127
|
if (t.dirname !== ".planning") continue;
|
|
7880
8128
|
let raw;
|
|
@@ -7889,14 +8137,14 @@ function propagatePlanningDeletes(v, ts, prePostHeads, repo) {
|
|
|
7889
8137
|
}
|
|
7890
8138
|
const pairs = deletePairsFor(t, raw);
|
|
7891
8139
|
if (pairs.length === 0) continue;
|
|
7892
|
-
backupExtrasWrite(
|
|
7893
|
-
const planningRoot =
|
|
8140
|
+
backupExtrasWrite(join56(t.localRoot, t.dirname), ts, t.localRoot);
|
|
8141
|
+
const planningRoot = join56(t.localRoot, ".planning");
|
|
7894
8142
|
for (const { target, relToLocal, repoRel } of pairs) {
|
|
7895
8143
|
if (localDivergesFromPreDelete(target, prePostHeads.pre, repoRel, repo)) {
|
|
7896
8144
|
warn(keptDeleteWarnLine(t.logical, relToLocal));
|
|
7897
8145
|
continue;
|
|
7898
8146
|
}
|
|
7899
|
-
deletePlanningTarget(target, planningRoot,
|
|
8147
|
+
deletePlanningTarget(target, planningRoot, join56(repoExtras, t.logical, relToLocal));
|
|
7900
8148
|
}
|
|
7901
8149
|
}
|
|
7902
8150
|
}
|
|
@@ -7905,14 +8153,14 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
7905
8153
|
const v = loadValidatedExtras({ missingMsg: "no path-map.json; skipping extras push" });
|
|
7906
8154
|
if (v === null) return { unmapped: 0, skipped: 0, pushed: [], wouldPush: [] };
|
|
7907
8155
|
const repo = repoHome();
|
|
7908
|
-
const repoExtras =
|
|
8156
|
+
const repoExtras = join56(repo, "shared", "extras");
|
|
7909
8157
|
if (!dryRun) mkdirSync13(repoExtras, { recursive: true });
|
|
7910
8158
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
7911
8159
|
v,
|
|
7912
8160
|
dryRun,
|
|
7913
|
-
({ localRoot, logical, dirname:
|
|
7914
|
-
src:
|
|
7915
|
-
dst:
|
|
8161
|
+
({ localRoot, logical, dirname: dirname16 }) => ({
|
|
8162
|
+
src: join56(localRoot, dirname16),
|
|
8163
|
+
dst: join56(repoExtras, logical, dirname16)
|
|
7916
8164
|
}),
|
|
7917
8165
|
(dst) => backupRepoWrite(dst, ts, repo),
|
|
7918
8166
|
// Push copy routing per extra type:
|
|
@@ -7921,11 +8169,11 @@ function remapExtrasPush(ts, opts = {}) {
|
|
|
7921
8169
|
// The filter prevents ALWAYS_NEVER_SYNC files from landing in the repo
|
|
7922
8170
|
// working tree before the allow-list gate fires, eliminating the
|
|
7923
8171
|
// "residue wedges repeat push" regression. The allow-list gate
|
|
7924
|
-
// (enforceAllowList in commands
|
|
8172
|
+
// (enforceAllowList in commands/push/allowlist.ts, blockSetFor in
|
|
7925
8173
|
// config.never-sync.ts)
|
|
7926
8174
|
// remains the hard security boundary.
|
|
7927
8175
|
// All others: copyExtrasFiltered with per-extra denylist.
|
|
7928
|
-
(src, dst,
|
|
8176
|
+
(src, dst, dirname16) => dirname16 === ".planning" ? copyExtrasOverlayFiltered(src, dst, extrasDenySet(dirname16)) : copyExtrasFiltered(src, dst, extrasDenySet(dirname16))
|
|
7929
8177
|
);
|
|
7930
8178
|
return { unmapped, skipped, pushed: done, wouldPush: would };
|
|
7931
8179
|
}
|
|
@@ -7941,9 +8189,9 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
7941
8189
|
const { unmapped, skipped, done, would } = runExtrasOp(
|
|
7942
8190
|
v,
|
|
7943
8191
|
dryRun,
|
|
7944
|
-
({ localRoot, logical, dirname:
|
|
7945
|
-
src:
|
|
7946
|
-
dst:
|
|
8192
|
+
({ localRoot, logical, dirname: dirname16 }) => ({
|
|
8193
|
+
src: join56(repo, "shared", "extras", logical, dirname16),
|
|
8194
|
+
dst: join56(localRoot, dirname16)
|
|
7947
8195
|
}),
|
|
7948
8196
|
// Snapshot the host-side dst BEFORE the copy step touches it. Anchor on
|
|
7949
8197
|
// localRoot so the backup tree mirrors the project layout.
|
|
@@ -7961,12 +8209,12 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
7961
8209
|
// copyExtrasFileSkipDiverged keeps a locally-edited file rather than
|
|
7962
8210
|
// clobbering it, so the divergence WARN's keep-local promise holds for
|
|
7963
8211
|
// every extra, not just `.planning`.
|
|
7964
|
-
(src, dst,
|
|
7965
|
-
if (
|
|
7966
|
-
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(
|
|
7967
|
-
if (
|
|
8212
|
+
(src, dst, dirname16) => {
|
|
8213
|
+
if (dirname16 === ".claude")
|
|
8214
|
+
return copyExtrasFilteredPreserving(src, dst, extrasDenySet(dirname16));
|
|
8215
|
+
if (dirname16 === ".planning") {
|
|
7968
8216
|
const divergedSet = new Set(listDivergingModified(dst, src));
|
|
7969
|
-
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(
|
|
8217
|
+
return copyExtrasOverlaySkipDiverged(src, dst, extrasDenySet(dirname16), divergedSet);
|
|
7970
8218
|
}
|
|
7971
8219
|
return copyExtrasFileSkipDiverged(src, dst);
|
|
7972
8220
|
}
|
|
@@ -7977,7 +8225,7 @@ function remapExtrasPull(ts, opts = {}) {
|
|
|
7977
8225
|
return { unmapped, skipped, pulled: done, wouldPull: would };
|
|
7978
8226
|
}
|
|
7979
8227
|
|
|
7980
|
-
// src/extras
|
|
8228
|
+
// src/sync/extras/extras.ts
|
|
7981
8229
|
function divergenceWarnLine(o) {
|
|
7982
8230
|
const kind = o.isDir ? "folder" : "file";
|
|
7983
8231
|
const name = o.isDir ? `${o.dirname}/` : o.dirname;
|
|
@@ -7990,20 +8238,20 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
7990
8238
|
const v = loadValidatedExtras({});
|
|
7991
8239
|
if (v === null) return 0;
|
|
7992
8240
|
const counts = { unmapped: 0, skipped: 0 };
|
|
7993
|
-
const backupRoot =
|
|
8241
|
+
const backupRoot = join57(backupBase(), ts, "extras");
|
|
7994
8242
|
const repo = repoHome();
|
|
7995
8243
|
let divergedCount = 0;
|
|
7996
|
-
for (const { logical, localRoot, dirname:
|
|
7997
|
-
const local =
|
|
7998
|
-
const repoEntry =
|
|
7999
|
-
if (!
|
|
8244
|
+
for (const { logical, localRoot, dirname: dirname16 } of eachExtrasTarget(v, counts)) {
|
|
8245
|
+
const local = join57(localRoot, dirname16);
|
|
8246
|
+
const repoEntry = join57(repo, "shared", "extras", logical, dirname16);
|
|
8247
|
+
if (!existsSync40(local) || !existsSync40(repoEntry)) continue;
|
|
8000
8248
|
const modified = listDivergingModified(local, repoEntry);
|
|
8001
8249
|
if (modified.length === 0) continue;
|
|
8002
8250
|
divergedCount += modified.length;
|
|
8003
|
-
const projectBackupRoot =
|
|
8251
|
+
const projectBackupRoot = join57(backupRoot, encodePath(localRoot));
|
|
8004
8252
|
warn(
|
|
8005
8253
|
divergenceWarnLine({
|
|
8006
|
-
dirname:
|
|
8254
|
+
dirname: dirname16,
|
|
8007
8255
|
logical,
|
|
8008
8256
|
isDir: statSync11(local).isDirectory(),
|
|
8009
8257
|
count: modified.length,
|
|
@@ -8020,10 +8268,10 @@ function divergenceCheckExtras(ts, prePostHeads) {
|
|
|
8020
8268
|
return divergedCount;
|
|
8021
8269
|
}
|
|
8022
8270
|
|
|
8023
|
-
// src/links.baseline.ts
|
|
8271
|
+
// src/sync/links.baseline.ts
|
|
8024
8272
|
init_config();
|
|
8025
8273
|
import { lstatSync as lstatSync18, readdirSync as readdirSync18 } from "node:fs";
|
|
8026
|
-
import { join as
|
|
8274
|
+
import { join as join58, relative as relative8, sep as sep12 } from "node:path";
|
|
8027
8275
|
init_utils();
|
|
8028
8276
|
var SHARED_BASELINE_KIND = "shared-links-baseline/1";
|
|
8029
8277
|
var SHARED_BASELINE_CONFIG_HASH = "not-applicable";
|
|
@@ -8065,7 +8313,7 @@ function collectSharedFiles(dir, claude, scan) {
|
|
|
8065
8313
|
return;
|
|
8066
8314
|
}
|
|
8067
8315
|
for (const entry of entries) {
|
|
8068
|
-
const abs =
|
|
8316
|
+
const abs = join58(dir, entry);
|
|
8069
8317
|
if (isDeniedName(ALWAYS_NEVER_SYNC, entry)) {
|
|
8070
8318
|
scan.declined.push(baselineKey(claude, abs));
|
|
8071
8319
|
continue;
|
|
@@ -8081,7 +8329,7 @@ function enumerateLocalSharedScan(map, opts = {}) {
|
|
|
8081
8329
|
scan.declined.push(name);
|
|
8082
8330
|
continue;
|
|
8083
8331
|
}
|
|
8084
|
-
addLocalPath(
|
|
8332
|
+
addLocalPath(join58(claude, name), claude, scan);
|
|
8085
8333
|
}
|
|
8086
8334
|
return scan;
|
|
8087
8335
|
}
|
|
@@ -8093,7 +8341,7 @@ function buildSharedBaseline(map, opts = {}) {
|
|
|
8093
8341
|
const files = {};
|
|
8094
8342
|
for (const [key, st] of Object.entries(enumerateLocalSharedFiles(map, opts))) {
|
|
8095
8343
|
try {
|
|
8096
|
-
files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(
|
|
8344
|
+
files[key] = { size: st.size, mtime: st.mtime, hash: hashFile(join58(claude, key)) };
|
|
8097
8345
|
} catch {
|
|
8098
8346
|
}
|
|
8099
8347
|
}
|
|
@@ -8108,16 +8356,16 @@ function writeSharedBaseline(map, opts = {}) {
|
|
|
8108
8356
|
}
|
|
8109
8357
|
}
|
|
8110
8358
|
|
|
8111
|
-
// src/commands
|
|
8359
|
+
// src/commands/pull/win32.ts
|
|
8112
8360
|
init_color();
|
|
8113
|
-
import { existsSync as
|
|
8114
|
-
import { join as
|
|
8361
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
8362
|
+
import { join as join63 } from "node:path";
|
|
8115
8363
|
|
|
8116
|
-
// src/commands
|
|
8364
|
+
// src/commands/pull/recovery.git.ts
|
|
8117
8365
|
init_utils();
|
|
8118
|
-
import { execFileSync as
|
|
8366
|
+
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
8119
8367
|
function gitCapture(args, cwd) {
|
|
8120
|
-
return
|
|
8368
|
+
return execFileSync22("git", args, {
|
|
8121
8369
|
cwd,
|
|
8122
8370
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8123
8371
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -8152,30 +8400,13 @@ function parseDirtyPaths(repo, opts = {}) {
|
|
|
8152
8400
|
return parsePorcelainZ(gitStatusPorcelainZ(repo, opts));
|
|
8153
8401
|
}
|
|
8154
8402
|
|
|
8155
|
-
// src/commands
|
|
8403
|
+
// src/commands/pull/win32.ts
|
|
8156
8404
|
init_config();
|
|
8157
8405
|
|
|
8158
|
-
// src/
|
|
8159
|
-
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
8160
|
-
var PROBE_TIMEOUT_MS4 = 1e4;
|
|
8161
|
-
var PROBE_MAX_BUFFER = 64 * 1024 * 1024;
|
|
8162
|
-
function gitProbe(args, repo) {
|
|
8163
|
-
try {
|
|
8164
|
-
return execFileSync22("git", args, {
|
|
8165
|
-
cwd: repo,
|
|
8166
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
8167
|
-
timeout: PROBE_TIMEOUT_MS4,
|
|
8168
|
-
maxBuffer: PROBE_MAX_BUFFER
|
|
8169
|
-
}).toString();
|
|
8170
|
-
} catch {
|
|
8171
|
-
return null;
|
|
8172
|
-
}
|
|
8173
|
-
}
|
|
8174
|
-
|
|
8175
|
-
// src/links.deletions.ts
|
|
8406
|
+
// src/sync/links.deletions.ts
|
|
8176
8407
|
init_config();
|
|
8177
|
-
import { existsSync as
|
|
8178
|
-
import { join as
|
|
8408
|
+
import { existsSync as existsSync41, lstatSync as lstatSync19, rmSync as rmSync17 } from "node:fs";
|
|
8409
|
+
import { join as join59, relative as relative9, resolve as resolve5, sep as sep13 } from "node:path";
|
|
8179
8410
|
init_utils();
|
|
8180
8411
|
init_utils_fs();
|
|
8181
8412
|
function isUnknown(declined, key) {
|
|
@@ -8185,15 +8416,15 @@ function deletionFor(key, names, claude, sharedRoot) {
|
|
|
8185
8416
|
const segments = key.split("/");
|
|
8186
8417
|
const name = segments[0];
|
|
8187
8418
|
if (!names.has(name)) return null;
|
|
8188
|
-
if (!
|
|
8419
|
+
if (!existsSync41(join59(claude, name))) return null;
|
|
8189
8420
|
if (segments.some((segment) => isDeniedName(ALWAYS_NEVER_SYNC, segment))) return null;
|
|
8190
8421
|
const repoPath = resolve5(sharedRoot, key);
|
|
8191
|
-
const rel = relative9(
|
|
8422
|
+
const rel = relative9(join59(sharedRoot, name), repoPath);
|
|
8192
8423
|
if (rel === "" || rel === ".." || rel.startsWith(`..${sep13}`)) return null;
|
|
8193
8424
|
if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
8194
8425
|
return null;
|
|
8195
|
-
if (!
|
|
8196
|
-
return { kind: "deletion", name, localPath:
|
|
8426
|
+
if (!existsSync41(repoPath)) return null;
|
|
8427
|
+
return { kind: "deletion", name, localPath: join59(claude, key), repoPath };
|
|
8197
8428
|
}
|
|
8198
8429
|
function planSharedLinkDeletions(map, opts = {}) {
|
|
8199
8430
|
if (process.platform !== "win32") return [];
|
|
@@ -8201,7 +8432,7 @@ function planSharedLinkDeletions(map, opts = {}) {
|
|
|
8201
8432
|
const baseline = readSharedBaseline();
|
|
8202
8433
|
if (baseline === null) return [];
|
|
8203
8434
|
const claude = claudeHome();
|
|
8204
|
-
const sharedRoot =
|
|
8435
|
+
const sharedRoot = join59(repoHome(), "shared");
|
|
8205
8436
|
const linkNames = opts.linkNames ?? allSharedLinks(map);
|
|
8206
8437
|
const scan = enumerateLocalSharedScan(map, { linkNames });
|
|
8207
8438
|
const present = new Set(Object.keys(scan.files).map((key) => key.toLowerCase()));
|
|
@@ -8233,10 +8464,11 @@ function applySharedLinkDeletions(map, ts, opts = {}) {
|
|
|
8233
8464
|
return removed;
|
|
8234
8465
|
}
|
|
8235
8466
|
|
|
8236
|
-
// src/links.mirror.ts
|
|
8467
|
+
// src/sync/links.mirror.ts
|
|
8237
8468
|
init_config();
|
|
8238
|
-
|
|
8239
|
-
import {
|
|
8469
|
+
init_error_text();
|
|
8470
|
+
import { existsSync as existsSync42, lstatSync as lstatSync20, rmSync as rmSync18, statSync as statSync12 } from "node:fs";
|
|
8471
|
+
import { join as join60 } from "node:path";
|
|
8240
8472
|
init_utils();
|
|
8241
8473
|
init_utils_fs();
|
|
8242
8474
|
function emitMirror(onPreview, name, localPath, repoPath) {
|
|
@@ -8250,7 +8482,7 @@ function emitMirrorWet(onPreview, name, localPath, repoPath) {
|
|
|
8250
8482
|
if (onPreview) onPreview({ kind: "mirror", name, localPath, repoPath });
|
|
8251
8483
|
}
|
|
8252
8484
|
function notShared(target) {
|
|
8253
|
-
return !
|
|
8485
|
+
return !existsSync42(target);
|
|
8254
8486
|
}
|
|
8255
8487
|
function warnUnusableSharedTarget(name, dryRun, state) {
|
|
8256
8488
|
const reason = state === "dangling" ? `shared/${name} does not resolve to anything usable in the sync repo. Remove it, or restore what it points at` : `shared/${name} could not be read in the sync repo, so whether it is usable could not be determined. Check its permissions there`;
|
|
@@ -8278,8 +8510,8 @@ function warnUnreadableLocalName(name, target, err, dryRun) {
|
|
|
8278
8510
|
);
|
|
8279
8511
|
}
|
|
8280
8512
|
function mirrorOneSharedName(name, claude, repo, policy, opts) {
|
|
8281
|
-
const localPath =
|
|
8282
|
-
const target =
|
|
8513
|
+
const localPath = join60(claude, name);
|
|
8514
|
+
const target = join60(repo, "shared", name);
|
|
8283
8515
|
let stat;
|
|
8284
8516
|
try {
|
|
8285
8517
|
stat = lstatSync20(localPath, { throwIfNoEntry: false });
|
|
@@ -8339,7 +8571,7 @@ function mirrorSharedNames(map, policy, opts = {}) {
|
|
|
8339
8571
|
if (map === null) return;
|
|
8340
8572
|
const claude = claudeHome();
|
|
8341
8573
|
const repo = repoHome();
|
|
8342
|
-
const sharedPath =
|
|
8574
|
+
const sharedPath = join60(repo, "shared");
|
|
8343
8575
|
const sharedState = classifyPresence(sharedPath);
|
|
8344
8576
|
if (isUnusableTarget(sharedState)) {
|
|
8345
8577
|
warnUnusableSharedDir(opts.dryRun === true, sharedState);
|
|
@@ -8368,7 +8600,7 @@ function presentAt(abs) {
|
|
|
8368
8600
|
}
|
|
8369
8601
|
}
|
|
8370
8602
|
function removeUntrackedDenied(repo, path, segment, ts) {
|
|
8371
|
-
const abs =
|
|
8603
|
+
const abs = join60(repo, path);
|
|
8372
8604
|
const denied = `the path segment "${segment}" is on the never-sync list`;
|
|
8373
8605
|
if (!presentAt(abs)) {
|
|
8374
8606
|
warn(
|
|
@@ -8376,7 +8608,7 @@ function removeUntrackedDenied(repo, path, segment, ts) {
|
|
|
8376
8608
|
);
|
|
8377
8609
|
return;
|
|
8378
8610
|
}
|
|
8379
|
-
const snapshotted =
|
|
8611
|
+
const snapshotted = existsSync42(abs);
|
|
8380
8612
|
try {
|
|
8381
8613
|
backupRepoWrite(abs, ts, repo);
|
|
8382
8614
|
} catch (err) {
|
|
@@ -8402,19 +8634,19 @@ function reportTrackedDenied(repo, path, segment) {
|
|
|
8402
8634
|
const denied = `the path segment "${segment}" is on the never-sync list`;
|
|
8403
8635
|
if (inHead === null) {
|
|
8404
8636
|
warn(
|
|
8405
|
-
`could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it
|
|
8637
|
+
`could not check ${path} against HEAD: ${denied}. Nothing was changed. Inspect it by running git status -- "${path}" and take it out of shared/ before committing`
|
|
8406
8638
|
);
|
|
8407
8639
|
return;
|
|
8408
8640
|
}
|
|
8409
8641
|
if (inHead.trim() === "") {
|
|
8410
8642
|
warn(
|
|
8411
|
-
`${path} is staged and has no committed version: ${denied}. Nothing was changed. Run
|
|
8643
|
+
`${path} is staged and has no committed version: ${denied}. Nothing was changed. Run git rm --cached -- "${path}" to take it out of the index; that leaves the file on disk but makes it untracked, which the next nomad pull removes from the sync repo working tree (snapshotting it into the backup cache first), so move it outside shared/ instead if you want to keep it. If it is the destination half of a staged rename, run git diff --cached --name-status to name the source, whose content IS committed, so restore that half with git checkout HEAD -- "<source>" rather than leaving its deletion staged`
|
|
8412
8644
|
);
|
|
8413
8645
|
return;
|
|
8414
8646
|
}
|
|
8415
|
-
if (!
|
|
8647
|
+
if (!existsSync42(join60(repo, path))) return;
|
|
8416
8648
|
warn(
|
|
8417
|
-
`${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run
|
|
8649
|
+
`${path} is tracked and has changes against HEAD: ${denied}. Nothing was changed. Run git checkout HEAD -- "${path}" to put the committed content back, or move the file outside shared/ if you want to keep it. Neither of those takes the committed copy out of the repo: git rm -- "${path}" and a commit does that going forward, and if it holds a real secret, rotate it and rewrite history, because nomad only changes your local worktree and index and cannot scrub what a previous push already sent to the remote`
|
|
8418
8650
|
);
|
|
8419
8651
|
}
|
|
8420
8652
|
function revertDeniedMirrorPaths(repo, status, ts) {
|
|
@@ -8428,10 +8660,10 @@ function revertDeniedMirrorPaths(repo, status, ts) {
|
|
|
8428
8660
|
}
|
|
8429
8661
|
}
|
|
8430
8662
|
|
|
8431
|
-
// src/preview.ts
|
|
8663
|
+
// src/render/preview.ts
|
|
8432
8664
|
init_config();
|
|
8433
|
-
import { existsSync as
|
|
8434
|
-
import { join as
|
|
8665
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
8666
|
+
import { join as join62 } from "node:path";
|
|
8435
8667
|
|
|
8436
8668
|
// node_modules/diff/libesm/diff/base.js
|
|
8437
8669
|
var Diff = class {
|
|
@@ -8683,7 +8915,7 @@ function tokenize(value, options) {
|
|
|
8683
8915
|
return retLines;
|
|
8684
8916
|
}
|
|
8685
8917
|
|
|
8686
|
-
// src/diff-lines.ts
|
|
8918
|
+
// src/render/diff-lines.ts
|
|
8687
8919
|
init_color();
|
|
8688
8920
|
function diffLinesToUnified(oldStr, newStr) {
|
|
8689
8921
|
const parts = diffLines(oldStr, newStr);
|
|
@@ -8704,18 +8936,18 @@ function diffLinesToUnified(oldStr, newStr) {
|
|
|
8704
8936
|
return lines;
|
|
8705
8937
|
}
|
|
8706
8938
|
|
|
8707
|
-
// src/preview.skills.ts
|
|
8939
|
+
// src/render/preview.skills.ts
|
|
8708
8940
|
init_config();
|
|
8709
|
-
import { existsSync as
|
|
8710
|
-
import { join as
|
|
8941
|
+
import { existsSync as existsSync43, readdirSync as readdirSync19 } from "node:fs";
|
|
8942
|
+
import { join as join61 } from "node:path";
|
|
8711
8943
|
function buildSkillsPreviewSection() {
|
|
8712
8944
|
const s = section("Skills");
|
|
8713
|
-
const sharedSkills =
|
|
8714
|
-
if (!
|
|
8945
|
+
const sharedSkills = join61(repoHome(), "shared", "skills");
|
|
8946
|
+
if (!existsSync43(sharedSkills)) return s;
|
|
8715
8947
|
const sharedNames = readdirSync19(sharedSkills, { encoding: "utf8" }).filter((name) => !isSkillExcluded(name)).sort((a, b) => a.localeCompare(b, "en"));
|
|
8716
8948
|
for (const name of sharedNames) addItem(s, name);
|
|
8717
|
-
const localSkills =
|
|
8718
|
-
const localOnly =
|
|
8949
|
+
const localSkills = join61(claudeHome(), "skills");
|
|
8950
|
+
const localOnly = existsSync43(localSkills) ? readdirSync19(localSkills, { encoding: "utf8" }).filter(
|
|
8719
8951
|
(name) => !isSkillExcluded(name) && !sharedNames.includes(name)
|
|
8720
8952
|
).length : 0;
|
|
8721
8953
|
if (localOnly > 0) {
|
|
@@ -8724,7 +8956,7 @@ function buildSkillsPreviewSection() {
|
|
|
8724
8956
|
return s;
|
|
8725
8957
|
}
|
|
8726
8958
|
|
|
8727
|
-
// src/preview.ts
|
|
8959
|
+
// src/render/preview.ts
|
|
8728
8960
|
init_utils_json();
|
|
8729
8961
|
var CANONICAL_ORDER_NOTE = "settings.json will be rewritten in canonical key order; no value changes";
|
|
8730
8962
|
function diffJsonStrings(currentJsonText, newJsonText) {
|
|
@@ -8737,7 +8969,7 @@ function diffJsonStrings(currentJsonText, newJsonText) {
|
|
|
8737
8969
|
return lines.join("\n");
|
|
8738
8970
|
}
|
|
8739
8971
|
function readJsonOrNull(path) {
|
|
8740
|
-
if (!
|
|
8972
|
+
if (!existsSync44(path)) return null;
|
|
8741
8973
|
try {
|
|
8742
8974
|
return readJson(path);
|
|
8743
8975
|
} catch {
|
|
@@ -8751,12 +8983,12 @@ function previewSettings(basePath, hostPath, settingsPath) {
|
|
|
8751
8983
|
}
|
|
8752
8984
|
const notes = [];
|
|
8753
8985
|
const hostOverrides = readJsonOrNull(hostPath);
|
|
8754
|
-
if (hostOverrides === null &&
|
|
8986
|
+
if (hostOverrides === null && existsSync44(hostPath)) {
|
|
8755
8987
|
notes.push(`malformed hosts/${HOST}.json; ignoring overrides`);
|
|
8756
8988
|
}
|
|
8757
8989
|
const merged = stripGsdHookEntries(deepMerge(base, hostOverrides ?? {}));
|
|
8758
8990
|
const current = readJsonOrNull(settingsPath);
|
|
8759
|
-
if (current === null &&
|
|
8991
|
+
if (current === null && existsSync44(settingsPath)) {
|
|
8760
8992
|
return { diff: "", notes: [...notes, "malformed; skipping diff"] };
|
|
8761
8993
|
}
|
|
8762
8994
|
const strippedCurrent = stripGsdHookEntries(current ?? {});
|
|
@@ -8823,9 +9055,9 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8823
9055
|
onPreview: (e) => addItem(links, formatLinkRow(e))
|
|
8824
9056
|
});
|
|
8825
9057
|
const settingsResult = previewSettings(
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
9058
|
+
join62(repo, "shared", "settings.base.json"),
|
|
9059
|
+
join62(repo, "hosts", `${HOST}.json`),
|
|
9060
|
+
join62(claude, "settings.json")
|
|
8829
9061
|
);
|
|
8830
9062
|
const settingsSection = buildSettingsSectionForPreview(settingsResult);
|
|
8831
9063
|
const sessions = section("Sessions");
|
|
@@ -8841,7 +9073,7 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8841
9073
|
const extras = section("Extras");
|
|
8842
9074
|
let extrasSkipped = 0;
|
|
8843
9075
|
let extrasUnmapped = 0;
|
|
8844
|
-
if (
|
|
9076
|
+
if (existsSync44(join62(repo, "path-map.json")) && existsSync44(join62(repo, "shared", "extras"))) {
|
|
8845
9077
|
const extrasResult = remapExtrasPull(ts, { dryRun: true });
|
|
8846
9078
|
for (const entry of extrasResult.wouldPull) {
|
|
8847
9079
|
addItem(extras, entry);
|
|
@@ -8858,11 +9090,11 @@ function computePreview(ts, map, verb = "pull", plans) {
|
|
|
8858
9090
|
return { unmapped: remapResult.unmapped, collisions: 0, localOnly };
|
|
8859
9091
|
}
|
|
8860
9092
|
|
|
8861
|
-
// src/commands
|
|
9093
|
+
// src/commands/pull/win32.ts
|
|
8862
9094
|
init_utils();
|
|
8863
9095
|
init_utils_json();
|
|
8864
9096
|
function readMapForMirror(mapPath) {
|
|
8865
|
-
if (!
|
|
9097
|
+
if (!existsSync45(mapPath)) return { projects: {} };
|
|
8866
9098
|
try {
|
|
8867
9099
|
return readPathMap(mapPath);
|
|
8868
9100
|
} catch {
|
|
@@ -8926,7 +9158,7 @@ function reconcileSharedLinksBeforePull(repo, ts) {
|
|
|
8926
9158
|
let namesDerived = false;
|
|
8927
9159
|
let derivedSharedDirs;
|
|
8928
9160
|
try {
|
|
8929
|
-
const map = readMapForMirror(
|
|
9161
|
+
const map = readMapForMirror(join63(repo, "path-map.json"));
|
|
8930
9162
|
if (map !== null) {
|
|
8931
9163
|
linkNames = allSharedLinks(map);
|
|
8932
9164
|
namesDerived = true;
|
|
@@ -8965,7 +9197,7 @@ function buildMirrorSection(events, discard) {
|
|
|
8965
9197
|
function describeSkippedMirrorDiscard(repo, ts) {
|
|
8966
9198
|
if (process.platform !== "win32") return null;
|
|
8967
9199
|
try {
|
|
8968
|
-
const map = readMapForMirror(
|
|
9200
|
+
const map = readMapForMirror(join63(repo, "path-map.json"));
|
|
8969
9201
|
const linkNames = map !== null ? allSharedLinks(map, { quiet: true }) : void 0;
|
|
8970
9202
|
const captures = [];
|
|
8971
9203
|
stageLocalSharedEdits(map, ts, {
|
|
@@ -8974,13 +9206,13 @@ function describeSkippedMirrorDiscard(repo, ts) {
|
|
|
8974
9206
|
linkNames
|
|
8975
9207
|
});
|
|
8976
9208
|
if (captures.length === 0) return null;
|
|
8977
|
-
return { count: captures.length, backupPath:
|
|
9209
|
+
return { count: captures.length, backupPath: join63(backupBase(), ts) };
|
|
8978
9210
|
} catch {
|
|
8979
9211
|
return null;
|
|
8980
9212
|
}
|
|
8981
9213
|
}
|
|
8982
9214
|
function planSharedReconcileBeforePull(repo, ts) {
|
|
8983
|
-
const map = readMapForMirror(
|
|
9215
|
+
const map = readMapForMirror(join63(repo, "path-map.json"));
|
|
8984
9216
|
const captures = [];
|
|
8985
9217
|
const linkNames = process.platform === "win32" && map !== null ? allSharedLinks(map) : void 0;
|
|
8986
9218
|
stageLocalSharedEdits(map, ts, { dryRun: true, onPreview: (e) => captures.push(e), linkNames });
|
|
@@ -8992,11 +9224,11 @@ function planSharedReconcileBeforePull(repo, ts) {
|
|
|
8992
9224
|
};
|
|
8993
9225
|
}
|
|
8994
9226
|
|
|
8995
|
-
// src/commands
|
|
9227
|
+
// src/commands/pull/collision.ts
|
|
8996
9228
|
init_config();
|
|
8997
9229
|
init_exit_codes();
|
|
8998
|
-
import { existsSync as
|
|
8999
|
-
import { join as
|
|
9230
|
+
import { existsSync as existsSync46, lstatSync as lstatSync21, readFileSync as readFileSync20, rmSync as rmSync19 } from "node:fs";
|
|
9231
|
+
import { join as join64 } from "node:path";
|
|
9000
9232
|
init_utils();
|
|
9001
9233
|
var SHARED_PREFIX = /^shared\//;
|
|
9002
9234
|
var EXTENSION = /(\.[^.]+)?$/;
|
|
@@ -9052,15 +9284,15 @@ function isContainedMirrorPath(rel) {
|
|
|
9052
9284
|
}
|
|
9053
9285
|
function matchesLocalOriginal(repo, rel) {
|
|
9054
9286
|
try {
|
|
9055
|
-
const local =
|
|
9056
|
-
return readFileSync20(
|
|
9287
|
+
const local = join64(claudeHome(), rel.replace(SHARED_PREFIX, ""));
|
|
9288
|
+
return readFileSync20(join64(repo, rel)).equals(readFileSync20(local));
|
|
9057
9289
|
} catch {
|
|
9058
9290
|
return false;
|
|
9059
9291
|
}
|
|
9060
9292
|
}
|
|
9061
9293
|
function removeMirroredCopies(repo, repoRelPaths) {
|
|
9062
9294
|
for (const rel of repoRelPaths.filter(isContainedMirrorPath)) {
|
|
9063
|
-
const abs =
|
|
9295
|
+
const abs = join64(repo, rel);
|
|
9064
9296
|
try {
|
|
9065
9297
|
if (lstatSync21(abs, { throwIfNoEntry: false })?.isFile() !== true) continue;
|
|
9066
9298
|
if (!matchesLocalOriginal(repo, rel)) continue;
|
|
@@ -9069,7 +9301,7 @@ function removeMirroredCopies(repo, repoRelPaths) {
|
|
|
9069
9301
|
warn(`could not remove nomad's copy at ${abs}: ${err.message}`);
|
|
9070
9302
|
}
|
|
9071
9303
|
}
|
|
9072
|
-
return repoRelPaths.every((rel) => !
|
|
9304
|
+
return repoRelPaths.every((rel) => !existsSync46(join64(repo, rel)));
|
|
9073
9305
|
}
|
|
9074
9306
|
function pullWithCollisionRunbook(repo, mirrored) {
|
|
9075
9307
|
try {
|
|
@@ -9084,10 +9316,10 @@ function pullWithCollisionRunbook(repo, mirrored) {
|
|
|
9084
9316
|
}
|
|
9085
9317
|
}
|
|
9086
9318
|
|
|
9087
|
-
// src/commands
|
|
9088
|
-
|
|
9319
|
+
// src/commands/pull/pull.ts
|
|
9320
|
+
init_wedge();
|
|
9089
9321
|
|
|
9090
|
-
// src/commands
|
|
9322
|
+
// src/commands/pull/recovery.ts
|
|
9091
9323
|
init_config();
|
|
9092
9324
|
init_utils();
|
|
9093
9325
|
init_utils_fs();
|
|
@@ -9181,9 +9413,9 @@ an ordinary pull.
|
|
|
9181
9413
|
log(buildRecoverySummary(branchName, strandedLog, untracked));
|
|
9182
9414
|
}
|
|
9183
9415
|
|
|
9184
|
-
// src/commands
|
|
9416
|
+
// src/commands/pull/recovery.unmerged.ts
|
|
9185
9417
|
init_exit_codes();
|
|
9186
|
-
|
|
9418
|
+
init_wedge();
|
|
9187
9419
|
init_utils();
|
|
9188
9420
|
function recoverUnmergedIndex(repo) {
|
|
9189
9421
|
const conflicted = new Set(
|
|
@@ -9206,7 +9438,7 @@ function recoverUnmergedIndex(repo) {
|
|
|
9206
9438
|
}
|
|
9207
9439
|
}
|
|
9208
9440
|
|
|
9209
|
-
// src/commands
|
|
9441
|
+
// src/commands/pull/pull.ts
|
|
9210
9442
|
init_exit_codes();
|
|
9211
9443
|
init_utils();
|
|
9212
9444
|
init_utils_fs();
|
|
@@ -9276,7 +9508,7 @@ function runPullCore(opts = {}) {
|
|
|
9276
9508
|
try {
|
|
9277
9509
|
return runPullWithBackupTs(ts, opts);
|
|
9278
9510
|
} finally {
|
|
9279
|
-
discardEmptyBackupDir(
|
|
9511
|
+
discardEmptyBackupDir(join65(backup, ts));
|
|
9280
9512
|
}
|
|
9281
9513
|
}
|
|
9282
9514
|
function runPullWithBackupTs(ts, opts) {
|
|
@@ -9287,7 +9519,7 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9287
9519
|
const backup = backupBase();
|
|
9288
9520
|
const recovered = handleWedge(repo, forceRemote);
|
|
9289
9521
|
if (!dryRun) {
|
|
9290
|
-
const backupRoot =
|
|
9522
|
+
const backupRoot = join65(backup, ts);
|
|
9291
9523
|
try {
|
|
9292
9524
|
mkdirSync14(backupRoot, { recursive: true });
|
|
9293
9525
|
} catch (err) {
|
|
@@ -9310,8 +9542,8 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9310
9542
|
pullWithCollisionRunbook(repo, mirrored);
|
|
9311
9543
|
});
|
|
9312
9544
|
assertNoAutostashConflict(repo, "nomad pull");
|
|
9313
|
-
const mapPath =
|
|
9314
|
-
const map =
|
|
9545
|
+
const mapPath = join65(repo, "path-map.json");
|
|
9546
|
+
const map = existsSync47(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
9315
9547
|
const divergedKeptLocal = divergenceCheckExtras(ts, dryRun ? prePostHeads : void 0);
|
|
9316
9548
|
if (dryRun) {
|
|
9317
9549
|
computePreview(ts, map, "pull", plansAgainst(sharedPlans, map));
|
|
@@ -9338,8 +9570,8 @@ function runPullWithBackupTs(ts, opts) {
|
|
|
9338
9570
|
}
|
|
9339
9571
|
function cmdPull(opts = {}) {
|
|
9340
9572
|
const repo = repoHome();
|
|
9341
|
-
if (!
|
|
9342
|
-
if (!
|
|
9573
|
+
if (!existsSync47(repo)) die(`repo not cloned at ${repo}`);
|
|
9574
|
+
if (!existsSync47(join65(repo, "shared", "settings.base.json"))) {
|
|
9343
9575
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
9344
9576
|
}
|
|
9345
9577
|
const handle = acquireLock("pull");
|
|
@@ -9360,15 +9592,15 @@ function cmdPull(opts = {}) {
|
|
|
9360
9592
|
}
|
|
9361
9593
|
}
|
|
9362
9594
|
|
|
9363
|
-
// src/commands
|
|
9595
|
+
// src/commands/push/push.ts
|
|
9364
9596
|
init_config();
|
|
9365
|
-
import { existsSync as
|
|
9366
|
-
import { join as
|
|
9597
|
+
import { existsSync as existsSync51 } from "node:fs";
|
|
9598
|
+
import { join as join70 } from "node:path";
|
|
9367
9599
|
|
|
9368
|
-
// src/commands
|
|
9600
|
+
// src/commands/push/selection.ts
|
|
9369
9601
|
init_config();
|
|
9370
|
-
import { existsSync as
|
|
9371
|
-
import { join as
|
|
9602
|
+
import { existsSync as existsSync48, statSync as statSync13 } from "node:fs";
|
|
9603
|
+
import { join as join66 } from "node:path";
|
|
9372
9604
|
init_utils_json();
|
|
9373
9605
|
function buildCurrentMap(map) {
|
|
9374
9606
|
const current = {};
|
|
@@ -9377,8 +9609,8 @@ function buildCurrentMap(map) {
|
|
|
9377
9609
|
for (const [, hostMap] of Object.entries(map.projects)) {
|
|
9378
9610
|
const localPath = hostMap[HOST];
|
|
9379
9611
|
if (!localPath) continue;
|
|
9380
|
-
const localDir =
|
|
9381
|
-
if (!
|
|
9612
|
+
const localDir = join66(claude, "projects", encodePath(localPath));
|
|
9613
|
+
if (!existsSync48(localDir)) continue;
|
|
9382
9614
|
for (const f of enumerateSourceFiles(localDir)) {
|
|
9383
9615
|
const st = statSync13(f);
|
|
9384
9616
|
current[f] = { size: st.size, mtime: st.mtimeMs };
|
|
@@ -9409,7 +9641,7 @@ function computePushSelection(map, old, scannerVersion, configHash, fullScan) {
|
|
|
9409
9641
|
};
|
|
9410
9642
|
}
|
|
9411
9643
|
function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan) {
|
|
9412
|
-
const map =
|
|
9644
|
+
const map = existsSync48(mapPath) ? readPathMap(mapPath) : null;
|
|
9413
9645
|
const { selection, newManifest } = computePushSelection(
|
|
9414
9646
|
map,
|
|
9415
9647
|
old,
|
|
@@ -9420,7 +9652,7 @@ function loadSelectionForPush(mapPath, old, scannerVersion, configHash, fullScan
|
|
|
9420
9652
|
return { map, selection, newManifest };
|
|
9421
9653
|
}
|
|
9422
9654
|
|
|
9423
|
-
// src/commands
|
|
9655
|
+
// src/commands/push/allowlist.ts
|
|
9424
9656
|
init_config();
|
|
9425
9657
|
init_config_sharedDirs_guard();
|
|
9426
9658
|
init_utils();
|
|
@@ -9495,21 +9727,21 @@ function enforceAllowList(statusPorcelain, map) {
|
|
|
9495
9727
|
fail(`${p} is in NEVER_SYNC and must never be pushed`);
|
|
9496
9728
|
}
|
|
9497
9729
|
for (const p of violations) {
|
|
9498
|
-
fail(`to sync ${p}, add to PUSH_ALLOWED in src/config.ts`);
|
|
9730
|
+
fail(`to sync ${p}, add to PUSH_ALLOWED in src/core/config.ts`);
|
|
9499
9731
|
}
|
|
9500
9732
|
throw new NomadFatal("push allow-list violations");
|
|
9501
9733
|
}
|
|
9502
9734
|
|
|
9503
|
-
// src/commands
|
|
9735
|
+
// src/commands/push/settings.ts
|
|
9504
9736
|
init_config();
|
|
9505
|
-
import { existsSync as
|
|
9506
|
-
import { join as
|
|
9737
|
+
import { existsSync as existsSync49 } from "node:fs";
|
|
9738
|
+
import { join as join67 } from "node:path";
|
|
9507
9739
|
init_utils();
|
|
9508
9740
|
init_utils_fs();
|
|
9509
9741
|
init_utils_json();
|
|
9510
9742
|
function stripGsdHooksFromBase(repo, backup) {
|
|
9511
|
-
const basePath =
|
|
9512
|
-
if (!
|
|
9743
|
+
const basePath = join67(repo, "shared", "settings.base.json");
|
|
9744
|
+
if (!existsSync49(basePath)) return;
|
|
9513
9745
|
let base;
|
|
9514
9746
|
try {
|
|
9515
9747
|
base = readJson(basePath);
|
|
@@ -9523,14 +9755,14 @@ function stripGsdHooksFromBase(repo, backup) {
|
|
|
9523
9755
|
writeJsonAtomic(basePath, stripped);
|
|
9524
9756
|
}
|
|
9525
9757
|
function reportSettingsAheadDrift(repo) {
|
|
9526
|
-
const basePath =
|
|
9527
|
-
if (!
|
|
9528
|
-
const settingsPath =
|
|
9529
|
-
if (!
|
|
9758
|
+
const basePath = join67(repo, "shared", "settings.base.json");
|
|
9759
|
+
if (!existsSync49(basePath)) return;
|
|
9760
|
+
const settingsPath = join67(claudeHome(), "settings.json");
|
|
9761
|
+
if (!existsSync49(settingsPath)) return;
|
|
9530
9762
|
try {
|
|
9531
9763
|
const base = readJson(basePath);
|
|
9532
|
-
const hostPath =
|
|
9533
|
-
const overrides =
|
|
9764
|
+
const hostPath = join67(repo, "hosts", `${HOST}.json`);
|
|
9765
|
+
const overrides = existsSync49(hostPath) ? readJson(hostPath) : {};
|
|
9534
9766
|
const merged = deepMerge(base, overrides);
|
|
9535
9767
|
const settings = readJson(settingsPath);
|
|
9536
9768
|
const { ahead } = classifySettingsDrift(merged, settings);
|
|
@@ -9544,12 +9776,12 @@ function reportSettingsAheadDrift(repo) {
|
|
|
9544
9776
|
}
|
|
9545
9777
|
}
|
|
9546
9778
|
|
|
9547
|
-
// src/commands
|
|
9548
|
-
|
|
9779
|
+
// src/commands/push/guards.ts
|
|
9780
|
+
init_checks();
|
|
9549
9781
|
init_utils();
|
|
9550
|
-
import { join as
|
|
9782
|
+
import { join as join68, relative as relative10 } from "node:path";
|
|
9551
9783
|
function guardGitlinks(repo) {
|
|
9552
|
-
const gitlinks = findGitlinks(
|
|
9784
|
+
const gitlinks = findGitlinks(join68(repo, "shared"));
|
|
9553
9785
|
if (gitlinks.length === 0) return;
|
|
9554
9786
|
for (const p of gitlinks) {
|
|
9555
9787
|
const rel = relative10(repo, p).replaceAll("\\", "/");
|
|
@@ -9576,10 +9808,10 @@ function guardResolutionModeConflicts(dryRun, redactAll, allowAll, allowRule) {
|
|
|
9576
9808
|
}
|
|
9577
9809
|
}
|
|
9578
9810
|
|
|
9579
|
-
// src/commands
|
|
9811
|
+
// src/commands/push/steps.ts
|
|
9580
9812
|
init_config();
|
|
9581
9813
|
|
|
9582
|
-
// src/push
|
|
9814
|
+
// src/commands/push/global-config.ts
|
|
9583
9815
|
init_config();
|
|
9584
9816
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
9585
9817
|
var STATUS_LABELS = {
|
|
@@ -9651,19 +9883,19 @@ function collectGlobalConfigChanges(repoHome2, hostname2, opts) {
|
|
|
9651
9883
|
return changes;
|
|
9652
9884
|
}
|
|
9653
9885
|
|
|
9654
|
-
// src/commands
|
|
9655
|
-
|
|
9886
|
+
// src/commands/push/steps.ts
|
|
9887
|
+
init_leak_verdict();
|
|
9656
9888
|
|
|
9657
|
-
// src/push
|
|
9889
|
+
// src/commands/push/preview.ts
|
|
9658
9890
|
init_color();
|
|
9659
9891
|
init_config();
|
|
9660
9892
|
init_config_sharedDirs_guard();
|
|
9661
9893
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
9662
|
-
import { copyFileSync, existsSync as
|
|
9894
|
+
import { copyFileSync, existsSync as existsSync50, lstatSync as lstatSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync20, rmSync as rmSync20 } from "node:fs";
|
|
9663
9895
|
import { homedir as homedir7 } from "node:os";
|
|
9664
|
-
import { join as
|
|
9665
|
-
|
|
9666
|
-
|
|
9896
|
+
import { join as join69, relative as relative11, sep as sep14 } from "node:path";
|
|
9897
|
+
init_leak_verdict();
|
|
9898
|
+
init_gitleaks();
|
|
9667
9899
|
init_utils_fs();
|
|
9668
9900
|
init_utils_json();
|
|
9669
9901
|
var NOTHING_TO_SCAN_ROW = `${dim(infoGlyph)} nothing to scan, no leaks`;
|
|
@@ -9673,7 +9905,7 @@ function stageSessionDir(localDir, dstDir, changed) {
|
|
|
9673
9905
|
const matching = [...changed].filter((p) => p.startsWith(prefix));
|
|
9674
9906
|
if (matching.length === 0) return false;
|
|
9675
9907
|
for (const src of matching) {
|
|
9676
|
-
copyFileAtomic(src,
|
|
9908
|
+
copyFileAtomic(src, join69(dstDir, relative11(localDir, src)));
|
|
9677
9909
|
}
|
|
9678
9910
|
return true;
|
|
9679
9911
|
}
|
|
@@ -9689,14 +9921,14 @@ function stageSessions(tmpRoot, map, changed) {
|
|
|
9689
9921
|
if (!p || p === "TBD") continue;
|
|
9690
9922
|
reverse.set(encodePath(p), logical);
|
|
9691
9923
|
}
|
|
9692
|
-
const localProjects =
|
|
9693
|
-
if (!
|
|
9924
|
+
const localProjects = join69(claudeHome(), "projects");
|
|
9925
|
+
if (!existsSync50(localProjects)) return 0;
|
|
9694
9926
|
let staged = 0;
|
|
9695
9927
|
for (const dir of readdirSync20(localProjects)) {
|
|
9696
9928
|
const logical = reverse.get(dir);
|
|
9697
9929
|
if (!logical) continue;
|
|
9698
|
-
const localDir =
|
|
9699
|
-
const dstDir =
|
|
9930
|
+
const localDir = join69(localProjects, dir);
|
|
9931
|
+
const dstDir = join69(tmpRoot, "shared", "projects", logical);
|
|
9700
9932
|
if (stageSessionDir(localDir, dstDir, changed)) staged++;
|
|
9701
9933
|
}
|
|
9702
9934
|
return staged;
|
|
@@ -9710,11 +9942,11 @@ function stageExtras(tmpRoot, map) {
|
|
|
9710
9942
|
assertSafeLogical(logical);
|
|
9711
9943
|
const localRoot = map.projects[logical]?.[HOST];
|
|
9712
9944
|
if (!localRoot || localRoot === "TBD") continue;
|
|
9713
|
-
for (const
|
|
9714
|
-
if (!whitelist.includes(
|
|
9715
|
-
const src =
|
|
9716
|
-
if (!
|
|
9717
|
-
const dst =
|
|
9945
|
+
for (const dirname16 of dirnames) {
|
|
9946
|
+
if (!whitelist.includes(dirname16)) continue;
|
|
9947
|
+
const src = join69(localRoot, dirname16);
|
|
9948
|
+
if (!existsSync50(src)) continue;
|
|
9949
|
+
const dst = join69(tmpRoot, "shared", "extras", logical, dirname16);
|
|
9718
9950
|
copyExtras(src, dst);
|
|
9719
9951
|
staged++;
|
|
9720
9952
|
}
|
|
@@ -9722,19 +9954,19 @@ function stageExtras(tmpRoot, map) {
|
|
|
9722
9954
|
return staged;
|
|
9723
9955
|
}
|
|
9724
9956
|
function stageSkills(tmpRoot) {
|
|
9725
|
-
const localSkills =
|
|
9957
|
+
const localSkills = join69(claudeHome(), "skills");
|
|
9726
9958
|
const stat = lstatSync22(localSkills, { throwIfNoEntry: false });
|
|
9727
9959
|
if (stat === void 0 || stat.isSymbolicLink()) return 0;
|
|
9728
9960
|
const names = readdirSync20(localSkills, { encoding: "utf8" }).filter((n) => !isSkillExcluded(n));
|
|
9729
9961
|
if (names.length === 0) return 0;
|
|
9730
|
-
copySkillsPush(localSkills,
|
|
9962
|
+
copySkillsPush(localSkills, join69(tmpRoot, "shared", "skills"));
|
|
9731
9963
|
return names.length;
|
|
9732
9964
|
}
|
|
9733
9965
|
function previewPushLeaks(map, opts = {}) {
|
|
9734
|
-
const cacheDir =
|
|
9966
|
+
const cacheDir = join69(homedir7(), ".cache", "claude-nomad");
|
|
9735
9967
|
mkdirSync15(cacheDir, { recursive: true });
|
|
9736
9968
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
9737
|
-
const tmpRoot =
|
|
9969
|
+
const tmpRoot = join69(cacheDir, `push-preview-tree-${stamp}`);
|
|
9738
9970
|
try {
|
|
9739
9971
|
const sessionCount = stageSessions(tmpRoot, map, opts.selection?.changed);
|
|
9740
9972
|
const extrasCount = stageExtras(tmpRoot, map);
|
|
@@ -9742,9 +9974,9 @@ function previewPushLeaks(map, opts = {}) {
|
|
|
9742
9974
|
if (sessionCount + extrasCount + skillCount === 0) {
|
|
9743
9975
|
return { leak: false, verdictRow: NOTHING_TO_SCAN_ROW, recovery: null, findings: [] };
|
|
9744
9976
|
}
|
|
9745
|
-
const ignoreFile =
|
|
9746
|
-
if (
|
|
9747
|
-
copyFileSync(ignoreFile,
|
|
9977
|
+
const ignoreFile = join69(repoHome(), ".gitleaksignore");
|
|
9978
|
+
if (existsSync50(ignoreFile)) {
|
|
9979
|
+
copyFileSync(ignoreFile, join69(tmpRoot, ".gitleaksignore"));
|
|
9748
9980
|
}
|
|
9749
9981
|
let findings;
|
|
9750
9982
|
try {
|
|
@@ -9761,7 +9993,7 @@ function previewPushLeaks(map, opts = {}) {
|
|
|
9761
9993
|
}
|
|
9762
9994
|
}
|
|
9763
9995
|
|
|
9764
|
-
// src/commands
|
|
9996
|
+
// src/commands/push/steps.ts
|
|
9765
9997
|
init_utils();
|
|
9766
9998
|
async function commitAndPush(st, ts, map, resolution, repo, newManifest, render) {
|
|
9767
9999
|
gitOrFatal(["add", "-A"], "git add", repo);
|
|
@@ -9813,8 +10045,8 @@ function runDryRunPreview(st, map, repo, selection) {
|
|
|
9813
10045
|
if (verdict.recovery !== null) fail(verdict.recovery);
|
|
9814
10046
|
}
|
|
9815
10047
|
|
|
9816
|
-
// src/commands
|
|
9817
|
-
|
|
10048
|
+
// src/commands/push/push.ts
|
|
10049
|
+
init_checks();
|
|
9818
10050
|
init_utils();
|
|
9819
10051
|
init_utils_fs();
|
|
9820
10052
|
function aheadOfUpstream(repo) {
|
|
@@ -9872,7 +10104,7 @@ async function runPushCore(opts = {}) {
|
|
|
9872
10104
|
const scannerVersion = probeGitleaks();
|
|
9873
10105
|
const configHash = computeConfigHash();
|
|
9874
10106
|
const old = readManifest(manifestPath());
|
|
9875
|
-
const mapPath =
|
|
10107
|
+
const mapPath = join70(repo, "path-map.json");
|
|
9876
10108
|
const { map, selection, newManifest } = loadSelectionForPush(
|
|
9877
10109
|
mapPath,
|
|
9878
10110
|
old,
|
|
@@ -9925,7 +10157,7 @@ async function cmdPush(opts = {}) {
|
|
|
9925
10157
|
opts.allowRule
|
|
9926
10158
|
);
|
|
9927
10159
|
const repo = repoHome();
|
|
9928
|
-
if (!
|
|
10160
|
+
if (!existsSync51(repo)) die(`repo not cloned at ${repo}`);
|
|
9929
10161
|
const handle = acquireLock("push");
|
|
9930
10162
|
if (handle === null) process.exit(0);
|
|
9931
10163
|
try {
|
|
@@ -9942,9 +10174,9 @@ async function cmdPush(opts = {}) {
|
|
|
9942
10174
|
}
|
|
9943
10175
|
}
|
|
9944
10176
|
|
|
9945
|
-
// src/commands
|
|
9946
|
-
import { existsSync as
|
|
9947
|
-
import { join as
|
|
10177
|
+
// src/commands/sync.ts
|
|
10178
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
10179
|
+
import { join as join71 } from "node:path";
|
|
9948
10180
|
init_config();
|
|
9949
10181
|
init_color();
|
|
9950
10182
|
init_utils();
|
|
@@ -10085,8 +10317,8 @@ async function cmdSync(opts = {}) {
|
|
|
10085
10317
|
const dryRun = opts.dryRun === true;
|
|
10086
10318
|
const verbose = opts.verbose === true;
|
|
10087
10319
|
const repo = repoHome();
|
|
10088
|
-
if (!
|
|
10089
|
-
if (!
|
|
10320
|
+
if (!existsSync52(repo)) die(`repo not cloned at ${repo}`);
|
|
10321
|
+
if (!existsSync52(join71(repo, "shared", "settings.base.json"))) {
|
|
10090
10322
|
die("repo not initialized; run 'nomad init' to scaffold");
|
|
10091
10323
|
}
|
|
10092
10324
|
const handle = acquireLock("sync");
|
|
@@ -10109,9 +10341,11 @@ async function cmdSync(opts = {}) {
|
|
|
10109
10341
|
}
|
|
10110
10342
|
}
|
|
10111
10343
|
|
|
10112
|
-
// src/commands
|
|
10113
|
-
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
10344
|
+
// src/commands/update.ts
|
|
10114
10345
|
init_utils();
|
|
10346
|
+
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
10347
|
+
var RELEASE_NOTES_TAG_URL = "https://github.com/funkadelic/claude-nomad/releases/tag";
|
|
10348
|
+
var SEMVER = /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/;
|
|
10115
10349
|
function readInstalledVersion(run = execFileSync24) {
|
|
10116
10350
|
const isWin = process.platform === "win32";
|
|
10117
10351
|
try {
|
|
@@ -10123,6 +10357,10 @@ function readInstalledVersion(run = execFileSync24) {
|
|
|
10123
10357
|
return null;
|
|
10124
10358
|
}
|
|
10125
10359
|
}
|
|
10360
|
+
function readOwnVersion(run) {
|
|
10361
|
+
const version = readInstalledVersion(run);
|
|
10362
|
+
return version !== null && SEMVER.test(version) ? version : null;
|
|
10363
|
+
}
|
|
10126
10364
|
function cmdUpdate(currentVersion, run = execFileSync24) {
|
|
10127
10365
|
console.log(`Updating claude-nomad v${currentVersion}...`);
|
|
10128
10366
|
const isWin = process.platform === "win32";
|
|
@@ -10146,26 +10384,27 @@ function cmdUpdate(currentVersion, run = execFileSync24) {
|
|
|
10146
10384
|
${detail}` : "";
|
|
10147
10385
|
throw new NomadFatal(`npm update -g claude-nomad failed: ${e.message}${suffix}`);
|
|
10148
10386
|
}
|
|
10149
|
-
const version =
|
|
10387
|
+
const version = readOwnVersion(run);
|
|
10150
10388
|
if (!version) {
|
|
10151
10389
|
console.log('Update complete. Run "nomad --version" to confirm the new version.');
|
|
10152
|
-
|
|
10153
|
-
console.log(`claude-nomad is already at the latest version (v${version}).`);
|
|
10154
|
-
} else {
|
|
10155
|
-
console.log(`claude-nomad is now at v${version}`);
|
|
10390
|
+
return;
|
|
10156
10391
|
}
|
|
10392
|
+
console.log(
|
|
10393
|
+
version === currentVersion ? `claude-nomad is already at the latest version (v${version}).` : `claude-nomad is now at v${version}`
|
|
10394
|
+
);
|
|
10395
|
+
console.log(`Release notes: ${RELEASE_NOTES_TAG_URL}/v${version}`);
|
|
10157
10396
|
}
|
|
10158
10397
|
|
|
10159
10398
|
// src/nomad.ts
|
|
10160
10399
|
init_config();
|
|
10161
10400
|
|
|
10162
|
-
// src/crash-report.write.ts
|
|
10401
|
+
// src/core/crash-report.write.ts
|
|
10163
10402
|
init_config();
|
|
10164
10403
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
10165
10404
|
import { mkdirSync as mkdirSync16, readdirSync as readdirSync21, statSync as statSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
|
|
10166
|
-
import { join as
|
|
10405
|
+
import { join as join73 } from "node:path";
|
|
10167
10406
|
|
|
10168
|
-
// src/crash-report.ts
|
|
10407
|
+
// src/core/crash-report.ts
|
|
10169
10408
|
var CRASH_MAX_STACK_LINES = 50;
|
|
10170
10409
|
var CRASH_MAX_ARGV = 32;
|
|
10171
10410
|
var CRASH_MAX_ARGV_TOKEN_LENGTH = 200;
|
|
@@ -10245,18 +10484,18 @@ function buildCrashReport(input) {
|
|
|
10245
10484
|
return truncateToBytes(scrubbed, CRASH_MAX_REPORT_BYTES);
|
|
10246
10485
|
}
|
|
10247
10486
|
|
|
10248
|
-
// src/crash-report.redact.ts
|
|
10487
|
+
// src/core/crash-report.redact.ts
|
|
10249
10488
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync21, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10250
10489
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10251
|
-
import { join as
|
|
10252
|
-
|
|
10490
|
+
import { join as join72 } from "node:path";
|
|
10491
|
+
init_gitleaks_scan();
|
|
10253
10492
|
var CRASH_SCAN_TIMEOUT_MS = 3e3;
|
|
10254
10493
|
var SCAN_UNAVAILABLE_ADVISORY = "\n\n[gitleaks value-based scan unavailable; only structural redaction applied. Review before sharing this file publicly.]\n";
|
|
10255
10494
|
function redactWithGitleaks(text, scan = scanFile) {
|
|
10256
10495
|
let dir;
|
|
10257
10496
|
try {
|
|
10258
|
-
dir = mkdtempSync3(
|
|
10259
|
-
const tmp =
|
|
10497
|
+
dir = mkdtempSync3(join72(tmpdir2(), "nomad-crash-scan-"));
|
|
10498
|
+
const tmp = join72(dir, "crash.txt");
|
|
10260
10499
|
writeFileSync10(tmp, text, { mode: 384 });
|
|
10261
10500
|
const findings = scan(tmp, false, CRASH_SCAN_TIMEOUT_MS);
|
|
10262
10501
|
if (findings === null) return text + SCAN_UNAVAILABLE_ADVISORY;
|
|
@@ -10273,7 +10512,7 @@ function redactWithGitleaks(text, scan = scanFile) {
|
|
|
10273
10512
|
}
|
|
10274
10513
|
}
|
|
10275
10514
|
|
|
10276
|
-
// src/crash-report.write.ts
|
|
10515
|
+
// src/core/crash-report.write.ts
|
|
10277
10516
|
init_utils_fs();
|
|
10278
10517
|
init_utils();
|
|
10279
10518
|
var CRASH_RETENTION_KEEP = 20;
|
|
@@ -10281,7 +10520,7 @@ function listCrashFiles(dir = crashDir()) {
|
|
|
10281
10520
|
try {
|
|
10282
10521
|
return readdirSync21(dir).flatMap((name) => {
|
|
10283
10522
|
try {
|
|
10284
|
-
return [{ name, mtimeMs: statSync14(
|
|
10523
|
+
return [{ name, mtimeMs: statSync14(join73(dir, name)).mtimeMs }];
|
|
10285
10524
|
} catch {
|
|
10286
10525
|
return [];
|
|
10287
10526
|
}
|
|
@@ -10295,7 +10534,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
|
|
|
10295
10534
|
const targets = prunableByCount(files, keep);
|
|
10296
10535
|
for (const name of targets) {
|
|
10297
10536
|
try {
|
|
10298
|
-
unlinkSync2(
|
|
10537
|
+
unlinkSync2(join73(dir, name));
|
|
10299
10538
|
} catch {
|
|
10300
10539
|
}
|
|
10301
10540
|
}
|
|
@@ -10303,7 +10542,7 @@ function pruneCrashDir(dir, keep = CRASH_RETENTION_KEEP) {
|
|
|
10303
10542
|
function writeCrashReport(text, dir = crashDir()) {
|
|
10304
10543
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
10305
10544
|
const stamp = `${nowTimestamp()}-${process.pid}-${randomBytes5(4).toString("hex")}`;
|
|
10306
|
-
const path =
|
|
10545
|
+
const path = join73(dir, `crash-${stamp}.txt`);
|
|
10307
10546
|
writeFileSync11(path, text, { mode: 384 });
|
|
10308
10547
|
pruneCrashDir(dir);
|
|
10309
10548
|
return path;
|
|
@@ -10333,20 +10572,20 @@ function handleCrash(err, argv, opts) {
|
|
|
10333
10572
|
}
|
|
10334
10573
|
}
|
|
10335
10574
|
|
|
10336
|
-
// src/diff.ts
|
|
10575
|
+
// src/commands/diff.ts
|
|
10337
10576
|
init_config();
|
|
10338
|
-
import { existsSync as
|
|
10339
|
-
import { join as
|
|
10577
|
+
import { existsSync as existsSync53 } from "node:fs";
|
|
10578
|
+
import { join as join74 } from "node:path";
|
|
10340
10579
|
init_utils();
|
|
10341
10580
|
init_utils_fs();
|
|
10342
10581
|
init_utils_json();
|
|
10343
10582
|
function cmdDiff() {
|
|
10344
10583
|
try {
|
|
10345
10584
|
const repo = repoHome();
|
|
10346
|
-
if (!
|
|
10585
|
+
if (!existsSync53(repo)) die(`repo not cloned at ${repo}`);
|
|
10347
10586
|
const ts = freshBackupTs(backupBase());
|
|
10348
|
-
const mapPath =
|
|
10349
|
-
const map =
|
|
10587
|
+
const mapPath = join74(repo, "path-map.json");
|
|
10588
|
+
const map = existsSync53(mapPath) ? readPathMap(mapPath) : { projects: {} };
|
|
10350
10589
|
divergenceCheckExtras(ts);
|
|
10351
10590
|
computePreview(ts, map, "diff");
|
|
10352
10591
|
} catch (err) {
|
|
@@ -10359,12 +10598,12 @@ function cmdDiff() {
|
|
|
10359
10598
|
}
|
|
10360
10599
|
}
|
|
10361
10600
|
|
|
10362
|
-
// src/init.ts
|
|
10601
|
+
// src/commands/init/init.ts
|
|
10363
10602
|
init_config();
|
|
10364
|
-
import { existsSync as
|
|
10365
|
-
import { join as
|
|
10603
|
+
import { existsSync as existsSync55, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "node:fs";
|
|
10604
|
+
import { join as join76 } from "node:path";
|
|
10366
10605
|
|
|
10367
|
-
// src/init
|
|
10606
|
+
// src/commands/init/gh-onboard.ts
|
|
10368
10607
|
init_config();
|
|
10369
10608
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
10370
10609
|
init_utils();
|
|
@@ -10439,44 +10678,44 @@ function ensureOriginRepo(repoName, run = execFileSync25) {
|
|
|
10439
10678
|
log(`created private repo ${owner}/${repoName} and wired origin`);
|
|
10440
10679
|
}
|
|
10441
10680
|
|
|
10442
|
-
// src/init
|
|
10681
|
+
// src/commands/init/snapshot.ts
|
|
10443
10682
|
init_config();
|
|
10444
10683
|
init_utils();
|
|
10445
10684
|
init_utils_fs();
|
|
10446
10685
|
init_utils_json();
|
|
10447
|
-
import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as
|
|
10448
|
-
import { join as
|
|
10686
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync9, existsSync as existsSync54, rmSync as rmSync22, statSync as statSync15 } from "node:fs";
|
|
10687
|
+
import { join as join75 } from "node:path";
|
|
10449
10688
|
function snapshotIntoShared(map) {
|
|
10450
10689
|
const repo = repoHome();
|
|
10451
10690
|
const claude = claudeHome();
|
|
10452
10691
|
for (const name of allSharedLinks(map)) {
|
|
10453
|
-
const src =
|
|
10454
|
-
if (!
|
|
10455
|
-
const dst =
|
|
10692
|
+
const src = join75(claude, name);
|
|
10693
|
+
if (!existsSync54(src)) continue;
|
|
10694
|
+
const dst = join75(repo, "shared", name);
|
|
10456
10695
|
if (statSync15(src).isDirectory()) {
|
|
10457
|
-
const gk =
|
|
10458
|
-
if (
|
|
10696
|
+
const gk = join75(dst, ".gitkeep");
|
|
10697
|
+
if (existsSync54(gk)) rmSync22(gk);
|
|
10459
10698
|
cpSync9(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
10460
10699
|
} else {
|
|
10461
10700
|
copyFileSync2(src, dst);
|
|
10462
10701
|
}
|
|
10463
10702
|
log(`snapshotted shared/${name} from ${src}`);
|
|
10464
10703
|
}
|
|
10465
|
-
const userSettings =
|
|
10466
|
-
if (
|
|
10704
|
+
const userSettings = join75(claude, "settings.json");
|
|
10705
|
+
if (existsSync54(userSettings)) {
|
|
10467
10706
|
let parsed;
|
|
10468
10707
|
try {
|
|
10469
10708
|
parsed = readJson(userSettings);
|
|
10470
10709
|
} catch (err) {
|
|
10471
10710
|
return die(`malformed ${userSettings}: ${err.message}`);
|
|
10472
10711
|
}
|
|
10473
|
-
const hostFile =
|
|
10712
|
+
const hostFile = join75(repo, "hosts", `${HOST}.json`);
|
|
10474
10713
|
writeJsonAtomic(hostFile, parsed);
|
|
10475
10714
|
log(`snapshotted hosts/${HOST}.json from ${userSettings}`);
|
|
10476
10715
|
}
|
|
10477
10716
|
}
|
|
10478
10717
|
|
|
10479
|
-
// src/init.ts
|
|
10718
|
+
// src/commands/init/init.ts
|
|
10480
10719
|
init_utils();
|
|
10481
10720
|
init_utils_fs();
|
|
10482
10721
|
var SHARED_CLAUDE_MD = "<!-- claude-nomad shared CLAUDE.md; symlinked into ~/.claude/CLAUDE.md by nomad pull -->\n";
|
|
@@ -10484,14 +10723,14 @@ var GITATTRIBUTES = "# nomad: sync content is byte-managed, disable all line-end
|
|
|
10484
10723
|
var SHARED_KEEP_DIRS = ["agents", "skills", "commands", "rules", "hooks"];
|
|
10485
10724
|
function preflightConflict(repoHome2) {
|
|
10486
10725
|
const candidates = [
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10726
|
+
join76(repoHome2, "shared", "settings.base.json"),
|
|
10727
|
+
join76(repoHome2, "shared", "CLAUDE.md"),
|
|
10728
|
+
join76(repoHome2, "path-map.json"),
|
|
10729
|
+
join76(repoHome2, "hosts"),
|
|
10730
|
+
join76(repoHome2, "shared")
|
|
10492
10731
|
];
|
|
10493
10732
|
for (const c of candidates) {
|
|
10494
|
-
if (
|
|
10733
|
+
if (existsSync55(c)) return c;
|
|
10495
10734
|
}
|
|
10496
10735
|
return null;
|
|
10497
10736
|
}
|
|
@@ -10509,27 +10748,27 @@ function cmdInit(opts = {}) {
|
|
|
10509
10748
|
die(`already initialized; refusing to clobber ${conflict}`);
|
|
10510
10749
|
}
|
|
10511
10750
|
ensureOriginRepo(opts.repoName ?? DEFAULT_REPO_NAME, opts.run);
|
|
10512
|
-
mkdirSync17(
|
|
10513
|
-
mkdirSync17(
|
|
10751
|
+
mkdirSync17(join76(repo, "shared"), { recursive: true });
|
|
10752
|
+
mkdirSync17(join76(repo, "hosts"), { recursive: true });
|
|
10514
10753
|
for (const name of SHARED_KEEP_DIRS) {
|
|
10515
|
-
mkdirSync17(
|
|
10754
|
+
mkdirSync17(join76(repo, "shared", name), { recursive: true });
|
|
10516
10755
|
}
|
|
10517
|
-
const userClaudeMd =
|
|
10518
|
-
if (!snapshot || !
|
|
10519
|
-
writeFileSync12(
|
|
10756
|
+
const userClaudeMd = join76(claude, "CLAUDE.md");
|
|
10757
|
+
if (!snapshot || !existsSync55(userClaudeMd)) {
|
|
10758
|
+
writeFileSync12(join76(repo, "shared", "CLAUDE.md"), SHARED_CLAUDE_MD);
|
|
10520
10759
|
item("created shared/CLAUDE.md");
|
|
10521
10760
|
}
|
|
10522
10761
|
for (const name of SHARED_KEEP_DIRS) {
|
|
10523
|
-
writeFileSync12(
|
|
10762
|
+
writeFileSync12(join76(repo, "shared", name, ".gitkeep"), "");
|
|
10524
10763
|
item(`created shared/${name}/.gitkeep`);
|
|
10525
10764
|
}
|
|
10526
|
-
writeFileSync12(
|
|
10765
|
+
writeFileSync12(join76(repo, "hosts", ".gitkeep"), "");
|
|
10527
10766
|
item("created hosts/.gitkeep");
|
|
10528
|
-
writeJsonAtomic(
|
|
10767
|
+
writeJsonAtomic(join76(repo, "shared", "settings.base.json"), {});
|
|
10529
10768
|
item("created shared/settings.base.json");
|
|
10530
|
-
writeJsonAtomic(
|
|
10769
|
+
writeJsonAtomic(join76(repo, "path-map.json"), { projects: {} });
|
|
10531
10770
|
item("created path-map.json");
|
|
10532
|
-
writeFileSync12(
|
|
10771
|
+
writeFileSync12(join76(repo, ".gitattributes"), GITATTRIBUTES);
|
|
10533
10772
|
item("created .gitattributes");
|
|
10534
10773
|
if (snapshot) {
|
|
10535
10774
|
snapshotIntoShared({ projects: {} });
|
|
@@ -10592,14 +10831,14 @@ function maybeDisableRepoActions(repoHome2, run) {
|
|
|
10592
10831
|
}
|
|
10593
10832
|
}
|
|
10594
10833
|
|
|
10595
|
-
// src/init
|
|
10834
|
+
// src/commands/init/prompt.ts
|
|
10596
10835
|
init_config();
|
|
10597
10836
|
init_utils();
|
|
10598
|
-
import { existsSync as
|
|
10599
|
-
import { join as
|
|
10837
|
+
import { existsSync as existsSync56, readdirSync as readdirSync22, statSync as statSync16 } from "node:fs";
|
|
10838
|
+
import { join as join77 } from "node:path";
|
|
10600
10839
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
10601
10840
|
function nonEmptyExists(path) {
|
|
10602
|
-
if (!
|
|
10841
|
+
if (!existsSync56(path)) return false;
|
|
10603
10842
|
try {
|
|
10604
10843
|
if (statSync16(path).isDirectory()) return readdirSync22(path).length > 0;
|
|
10605
10844
|
return true;
|
|
@@ -10608,8 +10847,8 @@ function nonEmptyExists(path) {
|
|
|
10608
10847
|
}
|
|
10609
10848
|
}
|
|
10610
10849
|
function hasExistingClaudeConfig(claudeHome2) {
|
|
10611
|
-
if (
|
|
10612
|
-
return SHARED_LINKS.some((name) => nonEmptyExists(
|
|
10850
|
+
if (existsSync56(join77(claudeHome2, "settings.json"))) return true;
|
|
10851
|
+
return SHARED_LINKS.some((name) => nonEmptyExists(join77(claudeHome2, name)));
|
|
10613
10852
|
}
|
|
10614
10853
|
async function confirmSnapshotDefault(claudeHome2) {
|
|
10615
10854
|
if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
|
|
@@ -10633,7 +10872,7 @@ async function resolveSnapshotChoice(flagSnapshot, claudeHome2, confirm = confir
|
|
|
10633
10872
|
return confirm(claudeHome2);
|
|
10634
10873
|
}
|
|
10635
10874
|
|
|
10636
|
-
// src/
|
|
10875
|
+
// src/cli/dispatch.helpers.ts
|
|
10637
10876
|
var REJECT = { ok: false, advance: 0 };
|
|
10638
10877
|
function applyBool(seen, set) {
|
|
10639
10878
|
if (seen) return REJECT;
|
|
@@ -10646,7 +10885,7 @@ function extractFlagValue(argv, i) {
|
|
|
10646
10885
|
return val;
|
|
10647
10886
|
}
|
|
10648
10887
|
|
|
10649
|
-
// src/
|
|
10888
|
+
// src/cli/dispatch.clean.ts
|
|
10650
10889
|
function applyOlderThan(argv, i, st) {
|
|
10651
10890
|
if (st.olderThan !== void 0) return REJECT;
|
|
10652
10891
|
const val = extractFlagValue(argv, i);
|
|
@@ -10693,7 +10932,7 @@ function parseCleanArgs(argv) {
|
|
|
10693
10932
|
return { dryRun: st.dryRun, olderThan: st.olderThan, keep: st.keep };
|
|
10694
10933
|
}
|
|
10695
10934
|
|
|
10696
|
-
// src/
|
|
10935
|
+
// src/cli/dispatch.capture-settings.ts
|
|
10697
10936
|
function parseCaptureSettingsArgs(argv) {
|
|
10698
10937
|
let host = false;
|
|
10699
10938
|
let dryRun = false;
|
|
@@ -10718,7 +10957,7 @@ function parseCaptureSettingsArgs(argv) {
|
|
|
10718
10957
|
return { host, dryRun, yes };
|
|
10719
10958
|
}
|
|
10720
10959
|
|
|
10721
|
-
// src/
|
|
10960
|
+
// src/cli/dispatch.eject.ts
|
|
10722
10961
|
function parseEjectArgs(argv) {
|
|
10723
10962
|
let dryRun = false;
|
|
10724
10963
|
let i = 3;
|
|
@@ -10735,7 +10974,7 @@ function parseEjectArgs(argv) {
|
|
|
10735
10974
|
return { dryRun };
|
|
10736
10975
|
}
|
|
10737
10976
|
|
|
10738
|
-
// src/
|
|
10977
|
+
// src/cli/dispatch.ts
|
|
10739
10978
|
function applyInitToken(argv, i, st) {
|
|
10740
10979
|
const token = argv[i];
|
|
10741
10980
|
if (token === "--snapshot") {
|
|
@@ -10808,7 +11047,7 @@ function parseRedactArgs(argv) {
|
|
|
10808
11047
|
return { id, rule, dryRun };
|
|
10809
11048
|
}
|
|
10810
11049
|
|
|
10811
|
-
// src/
|
|
11050
|
+
// src/cli/dispatch.allow.ts
|
|
10812
11051
|
function parseAllowArgs(argv) {
|
|
10813
11052
|
const positionals = argv.slice(3);
|
|
10814
11053
|
if (positionals.length === 0) return null;
|
|
@@ -10818,7 +11057,7 @@ function parseAllowArgs(argv) {
|
|
|
10818
11057
|
return positionals;
|
|
10819
11058
|
}
|
|
10820
11059
|
|
|
10821
|
-
// src/
|
|
11060
|
+
// src/cli/dispatch.pull.ts
|
|
10822
11061
|
function parsePullArgs(argv) {
|
|
10823
11062
|
let dryRun = false;
|
|
10824
11063
|
let forceRemote = false;
|
|
@@ -10840,7 +11079,7 @@ function parsePullArgs(argv) {
|
|
|
10840
11079
|
return { dryRun, forceRemote };
|
|
10841
11080
|
}
|
|
10842
11081
|
|
|
10843
|
-
// src/
|
|
11082
|
+
// src/cli/dispatch.push.ts
|
|
10844
11083
|
var RULE_ID_RE = /^\w[\w-]*$/;
|
|
10845
11084
|
function applyAllow(argv, i, st) {
|
|
10846
11085
|
if (st.allowRule !== void 0) return REJECT;
|
|
@@ -10893,7 +11132,7 @@ function parsePushArgs(argv) {
|
|
|
10893
11132
|
};
|
|
10894
11133
|
}
|
|
10895
11134
|
|
|
10896
|
-
// src/
|
|
11135
|
+
// src/cli/dispatch.sync.ts
|
|
10897
11136
|
function parseSyncArgs(argv) {
|
|
10898
11137
|
let dryRun = false;
|
|
10899
11138
|
let verbose = false;
|
|
@@ -10916,7 +11155,7 @@ function parseSyncArgs(argv) {
|
|
|
10916
11155
|
// package.json
|
|
10917
11156
|
var package_default = {
|
|
10918
11157
|
name: "claude-nomad",
|
|
10919
|
-
version: "0.
|
|
11158
|
+
version: "0.69.0",
|
|
10920
11159
|
type: "module",
|
|
10921
11160
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
10922
11161
|
keywords: [
|
|
@@ -10983,7 +11222,8 @@ var package_default = {
|
|
|
10983
11222
|
overrides: {
|
|
10984
11223
|
"@conventional-changelog/git-client": {
|
|
10985
11224
|
"conventional-commits-parser": "^7.0.0"
|
|
10986
|
-
}
|
|
11225
|
+
},
|
|
11226
|
+
"smol-toml": "^1.7.1"
|
|
10987
11227
|
},
|
|
10988
11228
|
devDependencies: {
|
|
10989
11229
|
"@commitlint/cli": "^21.0.1",
|
|
@@ -10992,7 +11232,7 @@ var package_default = {
|
|
|
10992
11232
|
"@stryker-mutator/core": "10.0.0",
|
|
10993
11233
|
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
10994
11234
|
"@types/node": "^22.0.0",
|
|
10995
|
-
"@vitest/coverage-v8": "^
|
|
11235
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
10996
11236
|
"conventional-changelog-conventionalcommits": "^10.2.1",
|
|
10997
11237
|
diff: "^9.0.0",
|
|
10998
11238
|
esbuild: "^0.28.0",
|
|
@@ -11008,11 +11248,11 @@ var package_default = {
|
|
|
11008
11248
|
prettier: "^3.8.3",
|
|
11009
11249
|
typescript: "^6.0.3",
|
|
11010
11250
|
"typescript-eslint": "^8.59.4",
|
|
11011
|
-
vitest: "^
|
|
11251
|
+
vitest: "^5.0.0"
|
|
11012
11252
|
}
|
|
11013
11253
|
};
|
|
11014
11254
|
|
|
11015
|
-
// src/
|
|
11255
|
+
// src/cli/help.ts
|
|
11016
11256
|
var DESC_COL = 26;
|
|
11017
11257
|
var row = (label, desc) => label.padEnd(DESC_COL) + desc;
|
|
11018
11258
|
var cont = (text) => " ".repeat(DESC_COL) + text;
|
|
@@ -11160,19 +11400,19 @@ var DEFAULT_HELP = [
|
|
|
11160
11400
|
"every pull)."
|
|
11161
11401
|
].join("\n");
|
|
11162
11402
|
|
|
11163
|
-
// src/resume.ts
|
|
11403
|
+
// src/sync/resume.ts
|
|
11164
11404
|
init_config();
|
|
11165
11405
|
init_utils();
|
|
11166
11406
|
init_utils_json();
|
|
11167
|
-
import { existsSync as
|
|
11168
|
-
import { join as
|
|
11407
|
+
import { existsSync as existsSync57, readFileSync as readFileSync21, readdirSync as readdirSync23 } from "node:fs";
|
|
11408
|
+
import { join as join78 } from "node:path";
|
|
11169
11409
|
function resumeCmd(sessionId) {
|
|
11170
11410
|
if (!/^[A-Za-z0-9_-]+$/.test(sessionId) || sessionId.length > 128) {
|
|
11171
11411
|
fail(`invalid session id: ${sessionId}`);
|
|
11172
11412
|
process.exit(1);
|
|
11173
11413
|
}
|
|
11174
|
-
const projectsRoot =
|
|
11175
|
-
if (!
|
|
11414
|
+
const projectsRoot = join78(claudeHome(), "projects");
|
|
11415
|
+
if (!existsSync57(projectsRoot)) {
|
|
11176
11416
|
fail(`${projectsRoot} does not exist`);
|
|
11177
11417
|
process.exit(1);
|
|
11178
11418
|
}
|
|
@@ -11186,8 +11426,8 @@ function resumeCmd(sessionId) {
|
|
|
11186
11426
|
fail(`no cwd field found in ${jsonlPath}`);
|
|
11187
11427
|
process.exit(1);
|
|
11188
11428
|
}
|
|
11189
|
-
const mapPath =
|
|
11190
|
-
if (!
|
|
11429
|
+
const mapPath = join78(repoHome(), "path-map.json");
|
|
11430
|
+
if (!existsSync57(mapPath)) {
|
|
11191
11431
|
fail("path-map.json missing");
|
|
11192
11432
|
process.exit(1);
|
|
11193
11433
|
}
|
|
@@ -11210,8 +11450,8 @@ function resumeCmd(sessionId) {
|
|
|
11210
11450
|
}
|
|
11211
11451
|
function findTranscriptPath(projectsRoot, sessionId) {
|
|
11212
11452
|
for (const dir of readdirSync23(projectsRoot)) {
|
|
11213
|
-
const candidate =
|
|
11214
|
-
if (
|
|
11453
|
+
const candidate = join78(projectsRoot, dir, `${sessionId}.jsonl`);
|
|
11454
|
+
if (existsSync57(candidate)) return candidate;
|
|
11215
11455
|
}
|
|
11216
11456
|
return null;
|
|
11217
11457
|
}
|
|
@@ -11244,7 +11484,7 @@ function shQuote(s) {
|
|
|
11244
11484
|
return `'${escaped}'`;
|
|
11245
11485
|
}
|
|
11246
11486
|
|
|
11247
|
-
// src/user-abort.ts
|
|
11487
|
+
// src/core/user-abort.ts
|
|
11248
11488
|
var ABORT_NAMES = /* @__PURE__ */ new Set(["AbortError", "ExitPromptError"]);
|
|
11249
11489
|
var ABORT_CODE = "ABORT_ERR";
|
|
11250
11490
|
function isUserAbort(err) {
|