tamperward 2.23.5 → 2.23.6

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/dist/cli/index.js CHANGED
@@ -206,7 +206,7 @@ function forwardedEnv(env) {
206
206
  return out3;
207
207
  }
208
208
  function exchange(paths, req2, timeoutMs) {
209
- return new Promise((resolve15) => {
209
+ return new Promise((resolve16) => {
210
210
  let done = false;
211
211
  let handedOff = false;
212
212
  let accepted = false;
@@ -217,7 +217,7 @@ function exchange(paths, req2, timeoutMs) {
217
217
  done = true;
218
218
  clearTimeout(timer);
219
219
  sock.destroy();
220
- resolve15(value);
220
+ resolve16(value);
221
221
  };
222
222
  const sock = createConnection(paths.socket);
223
223
  const timer = setTimeout(() => finish(handedOff ? acceptedFailure("timeout") : null), timeoutMs);
@@ -603,12 +603,12 @@ function irregularName(st) {
603
603
  function meta(st) {
604
604
  return { mode: st.mode, size: st.size, mtimeMs: st.mtimeMs };
605
605
  }
606
- function readRegular(abs2, st) {
606
+ function readRegular(abs, st) {
607
607
  let fd;
608
608
  try {
609
- fd = openSync(abs2, O_READ);
609
+ fd = openSync(abs, O_READ);
610
610
  } catch (e) {
611
- if (errCode(e) === "ELOOP") return inspectOnce(abs2);
611
+ if (errCode(e) === "ELOOP") return inspectOnce(abs);
612
612
  return { kind: "unreadable", content: null, ...meta(st), detail: errText(e) };
613
613
  }
614
614
  try {
@@ -629,10 +629,10 @@ function readRegular(abs2, st) {
629
629
  closeSync(fd);
630
630
  }
631
631
  }
632
- function classify(abs2, st) {
632
+ function classify(abs, st) {
633
633
  if (st.isSymbolicLink()) {
634
634
  try {
635
- const target = readlinkSync(abs2);
635
+ const target = readlinkSync(abs);
636
636
  return { kind: "symlink", content: Buffer.from(target, "utf8"), ...meta(st), detail: target };
637
637
  } catch (e) {
638
638
  return { kind: "unreadable", content: null, ...meta(st), detail: errText(e) };
@@ -641,38 +641,38 @@ function classify(abs2, st) {
641
641
  if (st.isDirectory()) return { kind: "directory", content: null, ...meta(st), detail: "directory" };
642
642
  if (!st.isFile()) return { kind: "irregular", content: null, ...meta(st), detail: irregularName(st) };
643
643
  if (st.size > READ_CAP) return { kind: "oversize", content: null, ...meta(st), detail: `${st.size} bytes` };
644
- return readRegular(abs2, st);
644
+ return readRegular(abs, st);
645
645
  }
646
- function inspectOnce(abs2) {
646
+ function inspectOnce(abs) {
647
647
  let st;
648
648
  try {
649
- st = lstatSync2(abs2);
649
+ st = lstatSync2(abs);
650
650
  } catch (e) {
651
651
  const code2 = errCode(e);
652
652
  if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
653
653
  return { kind: "unreadable", content: null, ...NO_META, detail: errText(e) };
654
654
  }
655
- if (st.isSymbolicLink() || !st.isFile()) return classify(abs2, st);
655
+ if (st.isSymbolicLink() || !st.isFile()) return classify(abs, st);
656
656
  return { kind: "unreadable", content: null, ...meta(st), detail: "the path changed shape while it was being read" };
657
657
  }
658
- function inspectPath(abs2) {
658
+ function inspectPath(abs) {
659
659
  let st;
660
660
  try {
661
- st = lstatSync2(abs2);
661
+ st = lstatSync2(abs);
662
662
  } catch (e) {
663
663
  const code2 = errCode(e);
664
664
  if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
665
665
  return { kind: "unreadable", content: null, ...NO_META, detail: errText(e) };
666
666
  }
667
- return classify(abs2, st);
667
+ return classify(abs, st);
668
668
  }
669
669
  function inspectRel(cwd, rel) {
670
670
  return inspectPath(join3(cwd, rel));
671
671
  }
672
- function inspectResolved(abs2) {
672
+ function inspectResolved(abs) {
673
673
  let real;
674
674
  try {
675
- real = realpathSync2(abs2);
675
+ real = realpathSync2(abs);
676
676
  } catch (e) {
677
677
  const code2 = errCode(e);
678
678
  if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
@@ -1021,11 +1021,55 @@ var init_trusted = __esm({
1021
1021
  }
1022
1022
  });
1023
1023
 
1024
- // src/git/build.ts
1024
+ // src/repo-context.ts
1025
1025
  import { execFileSync as execFileSync2 } from "node:child_process";
1026
+ import { existsSync as existsSync2 } from "node:fs";
1027
+ import { isAbsolute as isAbsolute3, resolve } from "node:path";
1028
+ function rev(cwd, flag) {
1029
+ try {
1030
+ const out3 = execFileSync2("git", ["rev-parse", flag], {
1031
+ cwd,
1032
+ encoding: "utf8",
1033
+ stdio: ["ignore", "pipe", "pipe"]
1034
+ }).trim();
1035
+ return out3 ? isAbsolute3(out3) ? out3 : resolve(cwd, out3) : null;
1036
+ } catch {
1037
+ return null;
1038
+ }
1039
+ }
1040
+ function repoContext(cwd) {
1041
+ const key2 = resolve(cwd);
1042
+ const hit = cache.get(key2);
1043
+ if (hit && existsSync2(hit.gitDir) && existsSync2(hit.root)) return hit;
1044
+ cache.delete(key2);
1045
+ const root = rev(key2, "--show-toplevel");
1046
+ const gitDir3 = root ? rev(key2, "--absolute-git-dir") : null;
1047
+ if (!root || !gitDir3) return null;
1048
+ const ctx = { root, gitDir: gitDir3 };
1049
+ if (cache.size >= CACHE_MAX) {
1050
+ const oldest = cache.keys().next().value;
1051
+ if (oldest !== void 0) cache.delete(oldest);
1052
+ }
1053
+ cache.set(key2, ctx);
1054
+ return ctx;
1055
+ }
1056
+ function repoRoot(cwd) {
1057
+ return repoContext(cwd)?.root ?? cwd;
1058
+ }
1059
+ var cache, CACHE_MAX;
1060
+ var init_repo_context = __esm({
1061
+ "src/repo-context.ts"() {
1062
+ "use strict";
1063
+ cache = /* @__PURE__ */ new Map();
1064
+ CACHE_MAX = 256;
1065
+ }
1066
+ });
1067
+
1068
+ // src/git/build.ts
1069
+ import { execFileSync as execFileSync3 } from "node:child_process";
1026
1070
  function git(args, cwd) {
1027
1071
  try {
1028
- return execFileSync2("git", args, {
1072
+ return execFileSync3("git", args, {
1029
1073
  cwd,
1030
1074
  encoding: "utf8",
1031
1075
  // 256 MiB, matching the hook adapter. Every diff below runs with `--text`, so a
@@ -1047,15 +1091,15 @@ function git(args, cwd) {
1047
1091
  throw new Error(detail ? `git ${args[0]}: ${detail}` : failure.message || `git ${args[0]} failed`);
1048
1092
  }
1049
1093
  }
1050
- function blobAt(rev, path, cwd) {
1094
+ function blobAt(rev2, path, cwd) {
1051
1095
  try {
1052
- return git(["show", `${rev}:${path}`], cwd);
1096
+ return git(["show", `${rev2}:${path}`], cwd);
1053
1097
  } catch {
1054
1098
  return null;
1055
1099
  }
1056
1100
  }
1057
1101
  function fromDisk(path, cwd) {
1058
- return textOf2(inspectRel(cwd ?? process.cwd(), path));
1102
+ return textOf2(inspectRel(repoRoot(cwd ?? process.cwd()), path));
1059
1103
  }
1060
1104
  function enrich(c, beforeReader, afterReader) {
1061
1105
  if (c.kind !== "file") return c;
@@ -1063,9 +1107,9 @@ function enrich(c, beforeReader, afterReader) {
1063
1107
  const after = c.op !== "delete" ? afterReader(c.path) : null;
1064
1108
  return { ...c, before, after };
1065
1109
  }
1066
- function assertRev(rev) {
1067
- if (rev.startsWith("-")) throw new Error(`refusing a revision that git would read as an option: ${rev}`);
1068
- return rev;
1110
+ function assertRev(rev2) {
1111
+ if (rev2.startsWith("-")) throw new Error(`refusing a revision that git would read as an option: ${rev2}`);
1112
+ return rev2;
1069
1113
  }
1070
1114
  function mergeBaseOf(base, head, opts = {}) {
1071
1115
  try {
@@ -1074,8 +1118,8 @@ function mergeBaseOf(base, head, opts = {}) {
1074
1118
  return base;
1075
1119
  }
1076
1120
  }
1077
- function fileAt(rev, path, opts = {}) {
1078
- return blobAt(rev, path, opts.cwd);
1121
+ function fileAt(rev2, path, opts = {}) {
1122
+ return blobAt(rev2, path, opts.cwd);
1079
1123
  }
1080
1124
  function fileOnDisk(path, opts = {}) {
1081
1125
  return fromDisk(path, opts.cwd);
@@ -1102,10 +1146,10 @@ function headSha(cwd) {
1102
1146
  return null;
1103
1147
  }
1104
1148
  }
1105
- function diffSince(rev, opts = {}) {
1106
- const raw = git([...DIFF_ARGS, rev], opts.cwd);
1149
+ function diffSince(rev2, opts = {}) {
1150
+ const raw = git([...DIFF_ARGS, rev2], opts.cwd);
1107
1151
  return parseDiff(raw).map(
1108
- (c) => enrich(c, (p) => blobAt(rev, p, opts.cwd), (p) => fromDisk(p, opts.cwd))
1152
+ (c) => enrich(c, (p) => blobAt(rev2, p, opts.cwd), (p) => fromDisk(p, opts.cwd))
1109
1153
  );
1110
1154
  }
1111
1155
  function diffRange(base, head, opts = {}) {
@@ -1195,6 +1239,7 @@ var init_build = __esm({
1195
1239
  init_synth();
1196
1240
  init_trusted();
1197
1241
  init_narrow();
1242
+ init_repo_context();
1198
1243
  DIFF_ARGS = ["diff", "--no-color", "--text", "-M"];
1199
1244
  SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".hg", ".svn"]);
1200
1245
  IGNORED_LS = ["ls-files", "--others", "--ignored", "--exclude-standard", "-z"];
@@ -1202,22 +1247,19 @@ var init_build = __esm({
1202
1247
  });
1203
1248
 
1204
1249
  // src/adapters/claude/changes.ts
1205
- import { execFileSync as execFileSync3 } from "node:child_process";
1250
+ import { execFileSync as execFileSync4 } from "node:child_process";
1206
1251
  import { writeFileSync, mkdtempSync, rmSync } from "node:fs";
1207
1252
  import { tmpdir as tmpdir2 } from "node:os";
1208
- import { isAbsolute as isAbsolute3, join as join5, relative, resolve } from "node:path";
1253
+ import { isAbsolute as isAbsolute4, join as join5, relative, resolve as resolve2 } from "node:path";
1209
1254
  function asStr(v) {
1210
1255
  return typeof v === "string" ? v : "";
1211
1256
  }
1212
1257
  function readDisk(path) {
1213
1258
  return textOf2(inspectResolved(path));
1214
1259
  }
1215
- function abs(path, cwd) {
1216
- return resolve(cwd, path);
1217
- }
1218
1260
  function relForDisplay(path, cwd) {
1219
1261
  const rel = relative(cwd, path);
1220
- return rel && !rel.startsWith("..") && !isAbsolute3(rel) ? rel : path;
1262
+ return rel && !rel.startsWith("..") && !isAbsolute4(rel) ? rel : path;
1221
1263
  }
1222
1264
  function applyEdit(content, oldStr, newStr) {
1223
1265
  if (content === null) return newStr;
@@ -1238,7 +1280,7 @@ function synthFileChange(displayPath, before, after) {
1238
1280
  writeFileSync(a, before ?? "");
1239
1281
  writeFileSync(b, after ?? "");
1240
1282
  try {
1241
- raw = execFileSync3("git", ["diff", "--no-index", "--no-color", "--text", a, b], {
1283
+ raw = execFileSync4("git", ["diff", "--no-index", "--no-color", "--text", a, b], {
1242
1284
  encoding: "utf8",
1243
1285
  maxBuffer: 256 * 1024 * 1024
1244
1286
  });
@@ -1256,8 +1298,9 @@ function synthFileChange(displayPath, before, after) {
1256
1298
  const hunks = first && first.kind === "file" ? first.hunks : [];
1257
1299
  return [{ kind: "file", path: displayPath, oldPath: null, op, before, after, binary: false, hunks }];
1258
1300
  }
1259
- function changesFromClaudeHook(input, cwd) {
1301
+ function changesFromClaudeHook(input, cwd, base = cwd) {
1260
1302
  const ti = input.tool_input ?? {};
1303
+ const abs = (path) => resolve2(base, path);
1261
1304
  switch (input.tool_name) {
1262
1305
  case "Bash": {
1263
1306
  const raw = asStr(ti.command);
@@ -1266,33 +1309,33 @@ function changesFromClaudeHook(input, cwd) {
1266
1309
  case "Write": {
1267
1310
  const fp = asStr(ti.file_path);
1268
1311
  if (!fp) return [];
1269
- const before = readDisk(abs(fp, cwd));
1270
- return synthFileChange(relForDisplay(abs(fp, cwd), cwd), before, asStr(ti.content));
1312
+ const before = readDisk(abs(fp));
1313
+ return synthFileChange(relForDisplay(abs(fp), cwd), before, asStr(ti.content));
1271
1314
  }
1272
1315
  case "Edit": {
1273
1316
  const fp = asStr(ti.file_path);
1274
1317
  if (!fp) return [];
1275
- const before = readDisk(abs(fp, cwd));
1318
+ const before = readDisk(abs(fp));
1276
1319
  const after = applyEdit(before, asStr(ti.old_string), asStr(ti.new_string));
1277
- return synthFileChange(relForDisplay(abs(fp, cwd), cwd), before, after);
1320
+ return synthFileChange(relForDisplay(abs(fp), cwd), before, after);
1278
1321
  }
1279
1322
  case "MultiEdit": {
1280
1323
  const fp = asStr(ti.file_path);
1281
1324
  if (!fp) return [];
1282
- const before = readDisk(abs(fp, cwd));
1325
+ const before = readDisk(abs(fp));
1283
1326
  let after = before;
1284
1327
  const edits = Array.isArray(ti.edits) ? ti.edits : [];
1285
1328
  for (const raw of edits) {
1286
1329
  const ed = isRecord(raw) ? raw : {};
1287
1330
  after = applyEdit(after, asStr(ed.old_string), asStr(ed.new_string));
1288
1331
  }
1289
- return synthFileChange(relForDisplay(abs(fp, cwd), cwd), before, after);
1332
+ return synthFileChange(relForDisplay(abs(fp), cwd), before, after);
1290
1333
  }
1291
1334
  case "NotebookEdit": {
1292
1335
  const fp = asStr(ti.notebook_path);
1293
1336
  const src = asStr(ti.new_source);
1294
1337
  if (!fp || !src) return [];
1295
- return synthFileChange(relForDisplay(abs(fp, cwd), cwd), "", src);
1338
+ return synthFileChange(relForDisplay(abs(fp), cwd), "", src);
1296
1339
  }
1297
1340
  default:
1298
1341
  return [];
@@ -2506,18 +2549,18 @@ var init_test_skip = __esm({
2506
2549
  });
2507
2550
 
2508
2551
  // src/detectors/repo.ts
2509
- import { execFileSync as execFileSync4 } from "node:child_process";
2552
+ import { execFileSync as execFileSync5 } from "node:child_process";
2510
2553
  import { readFileSync as readFileSync3 } from "node:fs";
2511
2554
  import { join as join6 } from "node:path";
2512
2555
  function trackedFiles(ctx) {
2513
2556
  if (!ctx) return null;
2514
2557
  if (ctx.trackedFiles) return ctx.trackedFiles;
2515
2558
  if (!ctx.cwd) return null;
2516
- const hit = cache.get(ctx.cwd);
2559
+ const hit = cache2.get(ctx.cwd);
2517
2560
  if (hit !== void 0) return hit;
2518
2561
  let files = null;
2519
2562
  try {
2520
- files = execFileSync4("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2563
+ files = execFileSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2521
2564
  cwd: ctx.cwd,
2522
2565
  encoding: "utf8",
2523
2566
  maxBuffer: 64 * 1024 * 1024,
@@ -2526,7 +2569,7 @@ function trackedFiles(ctx) {
2526
2569
  } catch {
2527
2570
  files = null;
2528
2571
  }
2529
- cache.set(ctx.cwd, files);
2572
+ cache2.set(ctx.cwd, files);
2530
2573
  return files;
2531
2574
  }
2532
2575
  function trackedContent(path, ctx) {
@@ -2551,7 +2594,7 @@ function branchRefs(ctx) {
2551
2594
  if (hit !== void 0) return hit;
2552
2595
  let refs = null;
2553
2596
  try {
2554
- const out3 = execFileSync4("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads", "refs/remotes"], {
2597
+ const out3 = execFileSync5("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads", "refs/remotes"], {
2555
2598
  cwd: ctx.cwd,
2556
2599
  encoding: "utf8",
2557
2600
  stdio: ["ignore", "pipe", "pipe"]
@@ -2570,11 +2613,11 @@ function branchExists(name, ctx) {
2570
2613
  for (const r of refs) if (r.endsWith("/" + name)) return true;
2571
2614
  return false;
2572
2615
  }
2573
- function revIsHead(rev, ctx) {
2574
- if (!ctx?.cwd || rev.startsWith("-")) return null;
2616
+ function revIsHead(rev2, ctx) {
2617
+ if (!ctx?.cwd || rev2.startsWith("-")) return null;
2575
2618
  const parse = (r) => {
2576
2619
  try {
2577
- return execFileSync4("git", ["rev-parse", "--verify", "--quiet", "--end-of-options", `${r}^{commit}`], {
2620
+ return execFileSync5("git", ["rev-parse", "--verify", "--quiet", "--end-of-options", `${r}^{commit}`], {
2578
2621
  cwd: ctx.cwd,
2579
2622
  encoding: "utf8",
2580
2623
  stdio: ["ignore", "pipe", "pipe"]
@@ -2584,7 +2627,7 @@ function revIsHead(rev, ctx) {
2584
2627
  }
2585
2628
  };
2586
2629
  const head = parse("HEAD");
2587
- const target = parse(rev);
2630
+ const target = parse(rev2);
2588
2631
  if (!head || !target) return null;
2589
2632
  return head === target;
2590
2633
  }
@@ -2594,7 +2637,7 @@ function defaultBranch(ctx) {
2594
2637
  if (hit !== void 0) return hit;
2595
2638
  let name = null;
2596
2639
  try {
2597
- const ref = execFileSync4("git", ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"], {
2640
+ const ref = execFileSync5("git", ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"], {
2598
2641
  cwd: ctx.cwd,
2599
2642
  encoding: "utf8",
2600
2643
  stdio: ["ignore", "pipe", "pipe"]
@@ -2619,12 +2662,12 @@ function containsProtected(token, policy, category, ctx, except) {
2619
2662
  const base = dir.split("/").pop() ?? "";
2620
2663
  return category === "tests" && /^(?:test|tests|__tests__|spec|specs)$/i.test(base);
2621
2664
  }
2622
- var cache, contentCache, branchCache, refsCache, dirOf;
2665
+ var cache2, contentCache, branchCache, refsCache, dirOf;
2623
2666
  var init_repo = __esm({
2624
2667
  "src/detectors/repo.ts"() {
2625
2668
  "use strict";
2626
2669
  init_policy();
2627
- cache = /* @__PURE__ */ new Map();
2670
+ cache2 = /* @__PURE__ */ new Map();
2628
2671
  contentCache = /* @__PURE__ */ new Map();
2629
2672
  branchCache = /* @__PURE__ */ new Map();
2630
2673
  refsCache = /* @__PURE__ */ new Map();
@@ -2645,8 +2688,8 @@ function numericOf(e) {
2645
2688
  }
2646
2689
  return void 0;
2647
2690
  }
2648
- function metricsOf(expr, resolve15) {
2649
- const obj = resolve15(expr);
2691
+ function metricsOf(expr, resolve16) {
2692
+ const obj = resolve16(expr);
2650
2693
  if (!ts.isObjectLiteralExpression(obj)) return void 0;
2651
2694
  const out3 = { values: {}, opaque: false };
2652
2695
  let any = false;
@@ -2660,7 +2703,7 @@ function metricsOf(expr, resolve15) {
2660
2703
  const k = keyName(p.name);
2661
2704
  if (k === null || !isMetric(k)) continue;
2662
2705
  any = true;
2663
- const n = numericOf(resolve15(p.initializer));
2706
+ const n = numericOf(resolve16(p.initializer));
2664
2707
  if (n === void 0) out3.opaque = true;
2665
2708
  else out3.values[k] = n;
2666
2709
  }
@@ -2706,17 +2749,17 @@ function parseThresholds(src) {
2706
2749
  const res = { paths: /* @__PURE__ */ new Map(), present: false, opaque: false };
2707
2750
  try {
2708
2751
  const sf = sourceOf(src);
2709
- const resolve15 = resolverFor(sf);
2752
+ const resolve16 = resolverFor(sf);
2710
2753
  const visit = (node) => {
2711
2754
  if (ts.isPropertyAssignment(node)) {
2712
2755
  const key2 = keyName(node.name);
2713
2756
  if (key2 === "coverageThreshold" || key2 === "thresholds" && underKey(node, "coverage")) {
2714
2757
  res.present = true;
2715
- const init = resolve15(node.initializer);
2758
+ const init = resolve16(node.initializer);
2716
2759
  if (!ts.isObjectLiteralExpression(init)) {
2717
2760
  res.opaque = true;
2718
2761
  } else {
2719
- const flat = metricsOf(init, resolve15);
2762
+ const flat = metricsOf(init, resolve16);
2720
2763
  if (flat) res.global = merge(res.global, flat);
2721
2764
  for (const p of init.properties) {
2722
2765
  if (ts.isSpreadAssignment(p)) {
@@ -2730,7 +2773,7 @@ function parseThresholds(src) {
2730
2773
  res.global = merge(res.global, { values: { branches: 100, functions: 100, lines: 100, statements: 100 }, opaque: false });
2731
2774
  continue;
2732
2775
  }
2733
- const m = metricsOf(p.initializer, resolve15);
2776
+ const m = metricsOf(p.initializer, resolve16);
2734
2777
  if (!m) continue;
2735
2778
  if (k === "global") res.global = merge(res.global, m);
2736
2779
  else res.paths.set(norm(k), m);
@@ -2763,18 +2806,18 @@ function parseLists(src) {
2763
2806
  const res = { collectFrom: null, collectKey: "collectCoverageFrom", ignore: [], opaqueCollect: false, opaqueIgnore: false };
2764
2807
  try {
2765
2808
  const sf = sourceOf(src);
2766
- const resolve15 = resolverFor(sf);
2809
+ const resolve16 = resolverFor(sf);
2767
2810
  const visit = (node) => {
2768
2811
  if (ts.isPropertyAssignment(node)) {
2769
2812
  const k = keyName(node.name);
2770
2813
  const vitestCoverage = ownerKey(node) === "coverage";
2771
2814
  if (k === "collectCoverageFrom" || k === "include" && vitestCoverage) {
2772
- const { items, opaque } = literals(resolve15(node.initializer));
2815
+ const { items, opaque } = literals(resolve16(node.initializer));
2773
2816
  res.collectFrom = items;
2774
2817
  res.collectKey = k === "include" ? "coverage.include" : k;
2775
2818
  if (opaque) res.opaqueCollect = true;
2776
2819
  } else if (k === "coveragePathIgnorePatterns" || k === "exclude" && vitestCoverage) {
2777
- const { items, opaque } = literals(resolve15(node.initializer));
2820
+ const { items, opaque } = literals(resolve16(node.initializer));
2778
2821
  res.ignore.push(...items);
2779
2822
  if (opaque) res.opaqueIgnore = true;
2780
2823
  }
@@ -4047,8 +4090,8 @@ var init_policy_diff = __esm({
4047
4090
  });
4048
4091
 
4049
4092
  // src/detectors/hook-wiring.ts
4050
- import { existsSync as existsSync2 } from "node:fs";
4051
- import { isAbsolute as isAbsolute4, resolve as resolve2 } from "node:path";
4093
+ import { existsSync as existsSync3 } from "node:fs";
4094
+ import { isAbsolute as isAbsolute5, resolve as resolve3 } from "node:path";
4052
4095
  function stripComment(line) {
4053
4096
  let single = false;
4054
4097
  let double = false;
@@ -5066,7 +5109,7 @@ function expandShellPath(t, env = process.env) {
5066
5109
  }
5067
5110
  function hookTests(policy, ctx) {
5068
5111
  const cwd = ctx?.cwd;
5069
- const diskCwd = cwd !== void 0 && existsSync2(cwd) ? cwd : void 0;
5112
+ const diskCwd = cwd !== void 0 && existsSync3(cwd) ? cwd : void 0;
5070
5113
  const onDisk = diskCwd !== void 0;
5071
5114
  const root = diskCwd !== void 0 ? canonicalPath(diskCwd) : cwd;
5072
5115
  const memo = /* @__PURE__ */ new Map();
@@ -5074,10 +5117,10 @@ function hookTests(policy, ctx) {
5074
5117
  const hit = memo.get(t);
5075
5118
  if (hit !== void 0) return hit;
5076
5119
  let p = expandShellPath(t.replace(/^["']|["']$/g, ""));
5077
- const pathy = isAbsolute4(p) || p.includes("/") && !/^[-:]/.test(p);
5078
- const abs2 = isAbsolute4(p) ? p : diskCwd !== void 0 ? resolve2(diskCwd, p) : null;
5079
- if (pathy && abs2 !== null) {
5080
- p = canonicalPath(abs2);
5120
+ const pathy = isAbsolute5(p) || p.includes("/") && !/^[-:]/.test(p);
5121
+ const abs = isAbsolute5(p) ? p : diskCwd !== void 0 ? resolve3(diskCwd, p) : null;
5122
+ if (pathy && abs !== null) {
5123
+ p = canonicalPath(abs);
5081
5124
  if (root !== void 0) {
5082
5125
  if (p === root) p = ".";
5083
5126
  else if (p.startsWith(root + "/")) p = p.slice(root.length + 1);
@@ -5350,8 +5393,8 @@ var init_hook_wiring = __esm({
5350
5393
  });
5351
5394
 
5352
5395
  // src/detectors/hook-tampering.ts
5353
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
5354
- import { isAbsolute as isAbsolute5, join as join7, relative as relative2 } from "node:path";
5396
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
5397
+ import { isAbsolute as isAbsolute6, join as join7, relative as relative2 } from "node:path";
5355
5398
  function parseSettings(src) {
5356
5399
  try {
5357
5400
  const v = JSON.parse(src);
@@ -5723,7 +5766,7 @@ function lefthookBase(c, changes, ctx) {
5723
5766
  for (const n of names) {
5724
5767
  try {
5725
5768
  const p = join7(ctx.cwd, n);
5726
- if (existsSync3(p)) return parseDoc(readFileSync4(p, "utf8"));
5769
+ if (existsSync4(p)) return parseDoc(readFileSync4(p, "utf8"));
5727
5770
  } catch {
5728
5771
  }
5729
5772
  }
@@ -5793,9 +5836,9 @@ function updateIndexHiding(toks, policy) {
5793
5836
  return null;
5794
5837
  }
5795
5838
  function repoRelative(path, ctx) {
5796
- if (!ctx?.cwd || !isAbsolute5(path)) return path;
5839
+ if (!ctx?.cwd || !isAbsolute6(path)) return path;
5797
5840
  const rel = relative2(ctx.cwd, path);
5798
- return rel && !rel.startsWith("..") && !isAbsolute5(rel) ? rel : path;
5841
+ return rel && !rel.startsWith("..") && !isAbsolute6(rel) ? rel : path;
5799
5842
  }
5800
5843
  function huskyRuntimeWrite(path, content, ctx) {
5801
5844
  const m = path.match(/(?:^|\/)\.husky\/_\/([^/]+)$/);
@@ -6133,7 +6176,7 @@ They WILL FAIL in v10.0.0
6133
6176
  if (why) {
6134
6177
  const target = shellHookTarget(seg, toks, policy, ctx) ?? shellHookTarget("", fed, policy, ctx);
6135
6178
  const file = target ? repoRelative(target, ctx) : void 0;
6136
- const outside = file !== void 0 && (isAbsolute5(file) || /^[A-Za-z]:\//.test(file)) && isClaudeSettings(file) ? ` \u2014 ${file} is a Claude Code settings file the runtime reads hooks from` : "";
6179
+ const outside = file !== void 0 && (isAbsolute6(file) || /^[A-Za-z]:\//.test(file)) && isClaudeSettings(file) ? ` \u2014 ${file} is a Claude Code settings file the runtime reads hooks from` : "";
6137
6180
  out3.push(
6138
6181
  makeFinding(RULE7, policy, {
6139
6182
  ...file !== void 0 ? { file } : {},
@@ -6941,8 +6984,8 @@ var init_test_deletion = __esm({
6941
6984
  const gitRestoresOld = cmd === "git" && /^git\s+(?:checkout|restore)\b/.test(seg) && (() => {
6942
6985
  const src = seg.match(/--source[= ]\s*(\S+)/)?.[1];
6943
6986
  const revIdx = toks.findIndex((t, i) => i >= 2 && !t.startsWith("-") && !named.includes(t) && t !== "--");
6944
- const rev = src ?? (revIdx >= 0 ? toks[revIdx] : void 0);
6945
- if (!rev || rev === "HEAD" || rev === "@" || revIsHead(rev, ctx) === true) return false;
6987
+ const rev2 = src ?? (revIdx >= 0 ? toks[revIdx] : void 0);
6988
+ if (!rev2 || rev2 === "HEAD" || rev2 === "@" || revIsHead(rev2, ctx) === true) return false;
6946
6989
  if (src || seg.includes(" -- ")) return true;
6947
6990
  return revIdx >= 0 && toks.some((t, i) => i > revIdx && named.includes(t));
6948
6991
  })();
@@ -7556,8 +7599,8 @@ var init_engine = __esm({
7556
7599
  });
7557
7600
 
7558
7601
  // src/policy-load.ts
7559
- import { readFileSync as readFileSync5, existsSync as existsSync4 } from "node:fs";
7560
- import { isAbsolute as isAbsolute6, join as join8, posix as posix2 } from "node:path";
7602
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
7603
+ import { isAbsolute as isAbsolute7, join as join8, posix as posix2 } from "node:path";
7561
7604
  function normalizeVersion(v, where = POLICY_FILE) {
7562
7605
  if (v === void 0 || v === null) return 1;
7563
7606
  if (typeof v !== "number" || !Number.isInteger(v) || v < 1) {
@@ -7566,7 +7609,7 @@ function normalizeVersion(v, where = POLICY_FILE) {
7566
7609
  return v;
7567
7610
  }
7568
7611
  function ledgerInsideRepo(ledger) {
7569
- if (!ledger.trim() || isAbsolute6(ledger) || /^[A-Za-z]:[\\/]/.test(ledger)) return false;
7612
+ if (!ledger.trim() || isAbsolute7(ledger) || /^[A-Za-z]:[\\/]/.test(ledger)) return false;
7570
7613
  const norm2 = posix2.normalize(ledger.replace(/\\/g, "/"));
7571
7614
  return norm2 !== ".." && !norm2.startsWith("../") && !norm2.startsWith("/");
7572
7615
  }
@@ -7717,14 +7760,14 @@ function parseOrThrow(src, where) {
7717
7760
  return parsePolicy(raw, where);
7718
7761
  }
7719
7762
  function loadPolicy(cwd = process.cwd()) {
7720
- const path = join8(cwd, POLICY_FILE);
7721
- if (!existsSync4(path)) return defaultPolicy();
7763
+ const path = join8(repoRoot(cwd), POLICY_FILE);
7764
+ if (!existsSync5(path)) return defaultPolicy();
7722
7765
  return parseOrThrow(readFileSync5(path, "utf8"), POLICY_FILE);
7723
7766
  }
7724
- function loadPolicyAt(rev, cwd) {
7725
- const src = fileAt(rev, POLICY_FILE, { cwd });
7767
+ function loadPolicyAt(rev2, cwd) {
7768
+ const src = fileAt(rev2, POLICY_FILE, { cwd: cwd === void 0 ? void 0 : repoRoot(cwd) });
7726
7769
  if (src == null) return null;
7727
- return parseOrThrow(src, `${POLICY_FILE} at ${rev}`);
7770
+ return parseOrThrow(src, `${POLICY_FILE} at ${rev2}`);
7728
7771
  }
7729
7772
  var PolicyError, SEVERITIES, isSeverity, isStringList, isMapping, TOP_LEVEL_KEYS;
7730
7773
  var init_policy_load = __esm({
@@ -7734,6 +7777,7 @@ var init_policy_load = __esm({
7734
7777
  init_policy();
7735
7778
  init_build();
7736
7779
  init_narrow();
7780
+ init_repo_context();
7737
7781
  PolicyError = class extends Error {
7738
7782
  };
7739
7783
  SEVERITIES = ["block", "warn"];
@@ -7746,7 +7790,7 @@ var init_policy_load = __esm({
7746
7790
 
7747
7791
  // src/signoff.ts
7748
7792
  import { createHash as createHash2 } from "node:crypto";
7749
- import { appendFileSync, mkdirSync, readFileSync as readFileSync6, existsSync as existsSync5 } from "node:fs";
7793
+ import { appendFileSync, mkdirSync, readFileSync as readFileSync6, existsSync as existsSync6 } from "node:fs";
7750
7794
  import { dirname as dirname2, join as join9 } from "node:path";
7751
7795
  function ledgerEntryFrom(value) {
7752
7796
  if (!isRecord(value)) return null;
@@ -7768,7 +7812,7 @@ function ledgerPath(cwd, policy) {
7768
7812
  }
7769
7813
  function readLedger(cwd, policy) {
7770
7814
  const p = ledgerPath(cwd, policy);
7771
- if (!existsSync5(p)) return [];
7815
+ if (!existsSync6(p)) return [];
7772
7816
  const out3 = [];
7773
7817
  for (const line of readFileSync6(p, "utf8").split("\n")) {
7774
7818
  if (!line.trim()) continue;
@@ -8131,27 +8175,28 @@ var init_report = __esm({
8131
8175
 
8132
8176
  // src/cli/check.ts
8133
8177
  function check(opts) {
8134
- const cwd = opts.cwd ?? process.cwd();
8135
- if (!isGitRepo(cwd)) {
8136
- process.stderr.write(`tamperward: ${cwd} is not inside a git repository \u2014 nothing to check
8178
+ const requested = opts.cwd ?? process.cwd();
8179
+ if (!isGitRepo(requested)) {
8180
+ process.stderr.write(`tamperward: ${requested} is not inside a git repository \u2014 nothing to check
8137
8181
  `);
8138
8182
  return 2;
8139
8183
  }
8140
- let policy = opts.policyOverride ?? loadPolicy(opts.cwd);
8184
+ const cwd = repoRoot(requested);
8185
+ let policy = opts.policyOverride ?? loadPolicy(cwd);
8141
8186
  let changes;
8142
8187
  let layer;
8143
8188
  let view;
8144
8189
  if (opts.staged) {
8145
- changes = diffStaged({ cwd: opts.cwd });
8190
+ changes = diffStaged({ cwd });
8146
8191
  layer = "local";
8147
8192
  view = "staged";
8148
8193
  } else if (opts.worktree) {
8149
8194
  const p = policy;
8150
- changes = opts.includeUntracked ? diffWorktreeWithUntracked({ cwd: opts.cwd }, (rel) => isProtected(rel, p)) : diffWorktree({ cwd: opts.cwd });
8195
+ changes = opts.includeUntracked ? diffWorktreeWithUntracked({ cwd }, (rel) => isProtected(rel, p)) : diffWorktree({ cwd });
8151
8196
  const seen = new Set(changes.filter((c) => c.kind === "file").map((c) => c.path));
8152
- for (const rel of hiddenTrackedPaths({ cwd: opts.cwd })) {
8197
+ for (const rel of hiddenTrackedPaths({ cwd })) {
8153
8198
  if (seen.has(rel) || !isProtected(rel, p)) continue;
8154
- changes.push(...synthFileChange(rel, fileAt("HEAD", rel, { cwd: opts.cwd }), fileOnDisk(rel, { cwd: opts.cwd })));
8199
+ changes.push(...synthFileChange(rel, fileAt("HEAD", rel, { cwd }), fileOnDisk(rel, { cwd })));
8155
8200
  }
8156
8201
  layer = opts.ciLayer ? "ci" : "local";
8157
8202
  view = "worktree";
@@ -8163,7 +8208,7 @@ function check(opts) {
8163
8208
  `);
8164
8209
  return 2;
8165
8210
  }
8166
- changes = diffRange(base, head, { cwd: opts.cwd });
8211
+ changes = diffRange(base, head, { cwd });
8167
8212
  if (changes.length === 0) {
8168
8213
  process.stderr.write(
8169
8214
  `tamperward: note: the range ${base}...${head} contains no changes \u2014 if this is not an empty pull request, check that the range resolves to the intended commits
@@ -8172,7 +8217,7 @@ function check(opts) {
8172
8217
  }
8173
8218
  layer = "ci";
8174
8219
  view = "range";
8175
- policy = opts.policyOverride ?? loadPolicyAt(mergeBaseOf(base, head, { cwd: opts.cwd }), opts.cwd) ?? defaultPolicy();
8220
+ policy = opts.policyOverride ?? loadPolicyAt(mergeBaseOf(base, head, { cwd }), cwd) ?? defaultPolicy();
8176
8221
  } else {
8177
8222
  process.stderr.write("tamperward: specify --staged, --worktree, or --diff <base>...<head>\n");
8178
8223
  return 2;
@@ -8215,6 +8260,7 @@ var init_check = __esm({
8215
8260
  init_disk();
8216
8261
  init_signoff();
8217
8262
  init_report();
8263
+ init_repo_context();
8218
8264
  }
8219
8265
  });
8220
8266
 
@@ -8244,7 +8290,7 @@ var init_deny = __esm({
8244
8290
  });
8245
8291
 
8246
8292
  // src/session.ts
8247
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
8293
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
8248
8294
  import { dirname as dirname3, join as join10 } from "node:path";
8249
8295
  function baselinePath(cwd, sessionId) {
8250
8296
  const gd = gitDir(cwd);
@@ -8256,7 +8302,7 @@ function turnBaseline(cwd, sessionId) {
8256
8302
  try {
8257
8303
  const p = baselinePath(cwd, sessionId);
8258
8304
  if (!p) return null;
8259
- if (existsSync6(p)) {
8305
+ if (existsSync7(p)) {
8260
8306
  const v = readFileSync7(p, "utf8").trim();
8261
8307
  if (SHA.test(v)) return v;
8262
8308
  }
@@ -8292,9 +8338,8 @@ var init_session = __esm({
8292
8338
 
8293
8339
  // src/effect.ts
8294
8340
  import { createHash as createHash3 } from "node:crypto";
8295
- import { existsSync as existsSync7, lstatSync as lstatSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync8, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
8296
- import { isAbsolute as isAbsolute7, join as join11 } from "node:path";
8297
- import { execFileSync as execFileSync5 } from "node:child_process";
8341
+ import { existsSync as existsSync8, lstatSync as lstatSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync8, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
8342
+ import { join as join11 } from "node:path";
8298
8343
  function ptreeFrom(value) {
8299
8344
  if (!isRecord(value)) return null;
8300
8345
  const out3 = {};
@@ -8307,17 +8352,7 @@ function ptreeFrom(value) {
8307
8352
  return out3;
8308
8353
  }
8309
8354
  function gitDir2(cwd) {
8310
- try {
8311
- const d = execFileSync5("git", ["rev-parse", "--git-dir"], {
8312
- cwd,
8313
- encoding: "utf8",
8314
- stdio: ["ignore", "pipe", "pipe"]
8315
- }).trim();
8316
- if (!d) return null;
8317
- return isAbsolute7(d) ? d : join11(cwd, d);
8318
- } catch {
8319
- return null;
8320
- }
8355
+ return repoContext(cwd)?.gitDir ?? null;
8321
8356
  }
8322
8357
  function statePath(cwd, sessionId, kind) {
8323
8358
  if (!sessionId) return null;
@@ -8339,10 +8374,11 @@ function entryOf(e) {
8339
8374
  const hash = e.content != null ? sha(e.content) : e.kind === "unreadable" ? "unreadable" : `${e.kind}:${e.detail}`;
8340
8375
  return { hash, mode: e.mode, size: e.size, mtimeMs: e.mtimeMs };
8341
8376
  }
8342
- function snapshotProtected(cwd, policy, prev, cache2) {
8377
+ function snapshotProtected(cwd, policy, prev, cache3) {
8343
8378
  void prev;
8344
- cache2?.beginSnapshot();
8345
- const entryAt = (rel) => cache2 ? cache2.entry(cwd, rel, () => entryOf(inspectRel(cwd, rel))) : entryOf(inspectRel(cwd, rel));
8379
+ cwd = repoRoot(cwd);
8380
+ cache3?.beginSnapshot();
8381
+ const entryAt = (rel) => cache3 ? cache3.entry(cwd, rel, () => entryOf(inspectRel(cwd, rel))) : entryOf(inspectRel(cwd, rel));
8346
8382
  const out3 = {};
8347
8383
  const keep = (rel) => isProtected(rel, policy);
8348
8384
  let pruned = /* @__PURE__ */ new Set();
@@ -8354,10 +8390,10 @@ function snapshotProtected(cwd, policy, prev, cache2) {
8354
8390
  } catch {
8355
8391
  }
8356
8392
  const walk2 = (rel) => {
8357
- const abs2 = rel ? join11(cwd, rel) : cwd;
8393
+ const abs = rel ? join11(cwd, rel) : cwd;
8358
8394
  let names;
8359
8395
  try {
8360
- names = readdirSync(abs2);
8396
+ names = readdirSync(abs);
8361
8397
  } catch {
8362
8398
  return;
8363
8399
  }
@@ -8394,7 +8430,7 @@ function driftBetween(expected, current) {
8394
8430
  return { changed, deleted, added };
8395
8431
  }
8396
8432
  function loadTree(p) {
8397
- if (!p || !existsSync7(p)) return null;
8433
+ if (!p || !existsSync8(p)) return null;
8398
8434
  try {
8399
8435
  return ptreeFrom(JSON.parse(readFileSync8(p, "utf8")));
8400
8436
  } catch {
@@ -8437,6 +8473,7 @@ var UNSAFE2, SKIP_DIRS2, sha;
8437
8473
  var init_effect = __esm({
8438
8474
  "src/effect.ts"() {
8439
8475
  "use strict";
8476
+ init_repo_context();
8440
8477
  init_policy();
8441
8478
  init_disk();
8442
8479
  init_build();
@@ -8451,8 +8488,8 @@ var init_effect = __esm({
8451
8488
  import { appendFileSync as appendFileSync3, lstatSync as lstatSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync9, readdirSync as readdirSync2, watch, writeFileSync as writeFileSync4 } from "node:fs";
8452
8489
  import { join as join12 } from "node:path";
8453
8490
  import { createHash as createHash4 } from "node:crypto";
8454
- function snap(abs2) {
8455
- const e = inspectPath(abs2);
8491
+ function snap(abs) {
8492
+ const e = inspectPath(abs);
8456
8493
  if (e.kind === "absent" || e.kind === "unreadable") return { mode: null, size: null, hash: null };
8457
8494
  if (e.kind !== "file" || e.content == null) return { mode: e.mode, size: null, hash: null };
8458
8495
  const hash = createHash4("sha256").update(e.content).digest("hex").slice(0, 16);
@@ -8968,10 +9005,10 @@ var init_fs_events = __esm({
8968
9005
  });
8969
9006
 
8970
9007
  // src/cli/hook.ts
8971
- import { readFileSync as readFileSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "node:fs";
9008
+ import { readFileSync as readFileSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "node:fs";
8972
9009
  import { createHash as createHash5 } from "node:crypto";
8973
- function setSnapshotCache(cache2) {
8974
- snapshotCache = cache2;
9010
+ function setSnapshotCache(cache3) {
9011
+ snapshotCache = cache3;
8975
9012
  }
8976
9013
  function readStdin() {
8977
9014
  try {
@@ -9101,8 +9138,8 @@ function uncoveredDrift(cwd, policy, expected, current, covered, base, turnTree)
9101
9138
  }
9102
9139
  const want = expected[rel]?.hash;
9103
9140
  let before = null;
9104
- for (const rev of candidates) {
9105
- const blob = fileAt(rev, rel, { cwd });
9141
+ for (const rev2 of candidates) {
9142
+ const blob = fileAt(rev2, rel, { cwd });
9106
9143
  if (blob != null && contentHash(blob) === want) {
9107
9144
  before = blob;
9108
9145
  break;
@@ -9213,12 +9250,13 @@ function sanctionPredictedWrites(cwd, sessionId, policy, changes) {
9213
9250
  }
9214
9251
  function preToolUseVerdict(input, defaultCwd) {
9215
9252
  try {
9216
- const cwd = input.cwd ?? defaultCwd ?? process.cwd();
9253
+ const sessionCwd = input.cwd ?? defaultCwd ?? process.cwd();
9254
+ const cwd = repoRoot(sessionCwd);
9217
9255
  turnBaseline(cwd, input.session_id);
9218
9256
  const policy = loadPolicy(cwd);
9219
9257
  const driftBlocks = effectDriftBlocks(cwd, input.session_id, policy);
9220
9258
  if (driftBlocks) return verdict(driftBlocks, "PreToolUse");
9221
- const changes = changesFromClaudeHook(input, cwd);
9259
+ const changes = changesFromClaudeHook(input, cwd, sessionCwd);
9222
9260
  const blocks2 = evaluate(changes, policy, void 0, "tool-call", { cwd }).filter((f) => f.severity === "block");
9223
9261
  if (blocks2.length === 0) sanctionPredictedWrites(cwd, input.session_id, policy, changes);
9224
9262
  return verdict(blocks2, "PreToolUse");
@@ -9250,9 +9288,9 @@ function turnTransientBlocks(cwd, sessionId, policy, changes) {
9250
9288
  recordObserverHealth(telemetry.state, telemetry.reason);
9251
9289
  }
9252
9290
  const cp = cursorPath(cwd, sessionId);
9253
- if (!existsSync8(log)) return none;
9291
+ if (!existsSync9(log)) return none;
9254
9292
  let offset = 0;
9255
- if (cp && existsSync8(cp)) {
9293
+ if (cp && existsSync9(cp)) {
9256
9294
  const n = Number(readFileSync10(cp, "utf8"));
9257
9295
  offset = Number.isFinite(n) ? n : 0;
9258
9296
  }
@@ -9321,8 +9359,9 @@ function recordObserverHealth(state, reason) {
9321
9359
  }
9322
9360
  function stopVerdict(input, defaultCwd) {
9323
9361
  if (input.stop_hook_active) return { exitCode: 0, stdout: "" };
9324
- const cwd = input.cwd ?? defaultCwd ?? process.cwd();
9325
- if (!isGitRepo(cwd)) return { exitCode: 0, stdout: "" };
9362
+ const sessionCwd = input.cwd ?? defaultCwd ?? process.cwd();
9363
+ if (!isGitRepo(sessionCwd)) return { exitCode: 0, stdout: "" };
9364
+ const cwd = repoRoot(sessionCwd);
9326
9365
  let blocks2;
9327
9366
  let commitCursor = () => {
9328
9367
  };
@@ -9410,6 +9449,7 @@ var init_hook = __esm({
9410
9449
  init_policy();
9411
9450
  init_disk();
9412
9451
  init_narrow();
9452
+ init_repo_context();
9413
9453
  snapshotCache = null;
9414
9454
  HookInputError = class extends Error {
9415
9455
  };
@@ -9468,22 +9508,22 @@ var init_ptree_cache = __esm({
9468
9508
  * time, the way the in-process path treats them.
9469
9509
  */
9470
9510
  entry(cwd, rel, compute) {
9471
- const abs2 = join13(cwd, rel);
9511
+ const abs = join13(cwd, rel);
9472
9512
  let st;
9473
9513
  try {
9474
- st = lstatSync5(abs2, { bigint: true });
9514
+ st = lstatSync5(abs, { bigint: true });
9475
9515
  } catch {
9476
- this.map.delete(abs2);
9516
+ this.map.delete(abs);
9477
9517
  this.stats.misses++;
9478
9518
  return compute();
9479
9519
  }
9480
9520
  if (!st.isFile()) {
9481
- this.map.delete(abs2);
9521
+ this.map.delete(abs);
9482
9522
  this.stats.misses++;
9483
9523
  return compute();
9484
9524
  }
9485
9525
  const key2 = keyOf(st);
9486
- const hit = this.map.get(abs2);
9526
+ const hit = this.map.get(abs);
9487
9527
  if (hit && hit.key === key2 && hit.recordedAtNs - hit.ctimeNs >= this.racyMarginNs) {
9488
9528
  this.stats.hits++;
9489
9529
  return hit.entry;
@@ -9493,17 +9533,17 @@ var init_ptree_cache = __esm({
9493
9533
  const entry = compute();
9494
9534
  let after;
9495
9535
  try {
9496
- after = lstatSync5(abs2, { bigint: true });
9536
+ after = lstatSync5(abs, { bigint: true });
9497
9537
  } catch {
9498
- this.map.delete(abs2);
9538
+ this.map.delete(abs);
9499
9539
  return entry;
9500
9540
  }
9501
9541
  if (!after.isFile() || keyOf(after) !== key2) {
9502
- this.map.delete(abs2);
9542
+ this.map.delete(abs);
9503
9543
  return entry;
9504
9544
  }
9505
9545
  if (this.map.size >= this.maxEntries) this.clear();
9506
- this.map.set(abs2, { key: key2, ctimeNs: st.ctimeNs, recordedAtNs, entry });
9546
+ this.map.set(abs, { key: key2, ctimeNs: st.ctimeNs, recordedAtNs, entry });
9507
9547
  return entry;
9508
9548
  }
9509
9549
  };
@@ -9511,7 +9551,7 @@ var init_ptree_cache = __esm({
9511
9551
  });
9512
9552
 
9513
9553
  // src/cli/hook-service.ts
9514
- import { chmodSync, existsSync as existsSync9, lstatSync as lstatSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
9554
+ import { chmodSync, existsSync as existsSync10, lstatSync as lstatSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
9515
9555
  import { createConnection as createConnection2, createServer } from "node:net";
9516
9556
  import { realpathSync as realpathSync3 } from "node:fs";
9517
9557
  import { sep } from "node:path";
@@ -9536,12 +9576,12 @@ function pidAlive2(pid) {
9536
9576
  }
9537
9577
  }
9538
9578
  function listening(socket) {
9539
- return new Promise((resolve15) => {
9579
+ return new Promise((resolve16) => {
9540
9580
  const sock = createConnection2(socket);
9541
9581
  const done = (v) => {
9542
9582
  clearTimeout(timer);
9543
9583
  sock.destroy();
9544
- resolve15(v);
9584
+ resolve16(v);
9545
9585
  };
9546
9586
  const timer = setTimeout(() => done(false), 1e3);
9547
9587
  sock.once("connect", () => done(true));
@@ -9599,7 +9639,7 @@ function removeQuietly(p) {
9599
9639
  }
9600
9640
  }
9601
9641
  function ensurePrivateDir(dir, uid) {
9602
- if (!existsSync9(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
9642
+ if (!existsSync10(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
9603
9643
  const st = lstatSync6(dir);
9604
9644
  if (!st.isDirectory()) throw new Error(`${dir} is not a directory`);
9605
9645
  if (st.uid !== uid) throw new Error(`${dir} is owned by uid ${st.uid}, not ${uid}; refusing to serve from it`);
@@ -9616,7 +9656,7 @@ async function startHookService(opts) {
9616
9656
  if (uid === void 0) throw new Error("no uid on this platform; refusing to start");
9617
9657
  let root;
9618
9658
  try {
9619
- root = realpathSync3(opts.root);
9659
+ root = realpathSync3(repoRoot(realpathSync3(opts.root)));
9620
9660
  } catch {
9621
9661
  throw new Error(`cannot resolve ${opts.root}`);
9622
9662
  }
@@ -9627,7 +9667,7 @@ async function startHookService(opts) {
9627
9667
  }
9628
9668
  removeQuietly(paths.socket);
9629
9669
  removeQuietly(paths.state);
9630
- const cache2 = opts.cache ?? new SnapshotCache();
9670
+ const cache3 = opts.cache ?? new SnapshotCache();
9631
9671
  let served = 0;
9632
9672
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
9633
9673
  const respond = (sock, body) => {
@@ -9653,7 +9693,7 @@ async function startHookService(opts) {
9653
9693
  root,
9654
9694
  started_at: startedAt,
9655
9695
  served,
9656
- cache: { hits: cache2.stats.hits, misses: cache2.stats.misses, size: cache2.size }
9696
+ cache: { hits: cache3.stats.hits, misses: cache3.stats.misses, size: cache3.size }
9657
9697
  });
9658
9698
  }
9659
9699
  if (!within(root, req2.cwd)) return respond(sock, { refused: "cwd outside the bound repository" });
@@ -9694,11 +9734,11 @@ async function startHookService(opts) {
9694
9734
  });
9695
9735
  const umask = process.umask(63);
9696
9736
  try {
9697
- await new Promise((resolve15, reject) => {
9737
+ await new Promise((resolve16, reject) => {
9698
9738
  server.once("error", reject);
9699
9739
  server.listen(paths.socket, () => {
9700
9740
  server.off("error", reject);
9701
- resolve15();
9741
+ resolve16();
9702
9742
  });
9703
9743
  });
9704
9744
  } finally {
@@ -9707,7 +9747,7 @@ async function startHookService(opts) {
9707
9747
  chmodSync(paths.socket, 384);
9708
9748
  const state = { pid: process.pid, version: TW_VERSION, root, started_at: startedAt };
9709
9749
  writeFileSync6(paths.state, JSON.stringify(state) + "\n", { mode: 384 });
9710
- setSnapshotCache(cache2);
9750
+ setSnapshotCache(cache3);
9711
9751
  let closed = false;
9712
9752
  return {
9713
9753
  paths,
@@ -9715,14 +9755,14 @@ async function startHookService(opts) {
9715
9755
  get served() {
9716
9756
  return served;
9717
9757
  },
9718
- close: () => new Promise((resolve15) => {
9719
- if (closed) return resolve15();
9758
+ close: () => new Promise((resolve16) => {
9759
+ if (closed) return resolve16();
9720
9760
  closed = true;
9721
9761
  setSnapshotCache(null);
9722
9762
  server.close(() => {
9723
9763
  removeQuietly(paths.socket);
9724
9764
  removeQuietly(paths.state);
9725
- resolve15();
9765
+ resolve16();
9726
9766
  });
9727
9767
  })
9728
9768
  };
@@ -9751,7 +9791,7 @@ async function stopHookService(paths) {
9751
9791
  process.kill(state.pid, "SIGTERM");
9752
9792
  } catch {
9753
9793
  }
9754
- const gone = () => !pidAlive2(state.pid) || !existsSync9(paths.socket);
9794
+ const gone = () => !pidAlive2(state.pid) || !existsSync10(paths.socket);
9755
9795
  const until = Date.now() + STOP_WAIT_MS;
9756
9796
  while (!gone() && Date.now() < until) sleepSync(50);
9757
9797
  if (!gone()) throw new Error(`hook service pid ${state.pid} did not exit within ${STOP_WAIT_MS / 1e3}s`);
@@ -9819,9 +9859,9 @@ function runHookService(args) {
9819
9859
  `);
9820
9860
  process.exit(0);
9821
9861
  }
9822
- const cache2 = isRecord(res.cache) ? res.cache : {};
9862
+ const cache3 = isRecord(res.cache) ? res.cache : {};
9823
9863
  process.stdout.write(
9824
- `tamperward hook-service: running (pid ${state.pid}, tamperward@${state.version}, root ${state.root}, since ${state.started_at}); served: ${res.served}; snapshot cache hits/misses/entries: ${String(cache2.hits ?? 0)}/${String(cache2.misses ?? 0)}/${String(cache2.size ?? 0)}; ${optIn}
9864
+ `tamperward hook-service: running (pid ${state.pid}, tamperward@${state.version}, root ${state.root}, since ${state.started_at}); served: ${res.served}; snapshot cache hits/misses/entries: ${String(cache3.hits ?? 0)}/${String(cache3.misses ?? 0)}/${String(cache3.size ?? 0)}; ${optIn}
9825
9865
  `
9826
9866
  );
9827
9867
  process.exit(0);
@@ -9835,6 +9875,7 @@ var init_hook_service = __esm({
9835
9875
  init_narrow();
9836
9876
  init_wiring();
9837
9877
  init_ptree_cache();
9878
+ init_repo_context();
9838
9879
  init_hook();
9839
9880
  init_hook_client();
9840
9881
  MAX_REQUEST_BYTES = 64 * 1024 * 1024;
@@ -9852,7 +9893,7 @@ function runAllow(opts) {
9852
9893
  process.stderr.write("tamperward: a --reason is required \u2014 sign-offs must be justified.\n");
9853
9894
  return 2;
9854
9895
  }
9855
- const cwd = opts.cwd ?? process.cwd();
9896
+ const cwd = repoRoot(opts.cwd ?? process.cwd());
9856
9897
  const policy = loadPolicy(cwd);
9857
9898
  let findings;
9858
9899
  try {
@@ -9894,6 +9935,7 @@ var init_allow = __esm({
9894
9935
  init_engine();
9895
9936
  init_policy_load();
9896
9937
  init_signoff();
9938
+ init_repo_context();
9897
9939
  }
9898
9940
  });
9899
9941
 
@@ -9934,8 +9976,8 @@ var init_verifier_limits = __esm({
9934
9976
  // src/cli/init.ts
9935
9977
  import { execFileSync as execFileSync6 } from "node:child_process";
9936
9978
  import { createHash as createHash6 } from "node:crypto";
9937
- import { existsSync as existsSync10, lstatSync as lstatSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync7, chmodSync as chmodSync2, statSync as statSync2 } from "node:fs";
9938
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute8, join as join14, relative as relative3, resolve as resolve3 } from "node:path";
9979
+ import { existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, writeFileSync as writeFileSync7, chmodSync as chmodSync2, statSync as statSync2 } from "node:fs";
9980
+ import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute8, join as join14, relative as relative3, resolve as resolve4 } from "node:path";
9939
9981
  function gitConfig(cwd, key2) {
9940
9982
  try {
9941
9983
  return execFileSync6("git", ["config", "--get", key2], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
@@ -10038,7 +10080,7 @@ function verifierCandidates(cwd) {
10038
10080
  if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
10039
10081
  } catch {
10040
10082
  }
10041
- if (existsSync10(join14(cwd, "pytest.ini"))) add("pytest");
10083
+ if (existsSync11(join14(cwd, "pytest.ini"))) add("pytest");
10042
10084
  try {
10043
10085
  const setupCfg = readFileSync12(join14(cwd, "setup.cfg"), "utf8");
10044
10086
  if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
@@ -10096,7 +10138,7 @@ function verifierSetupMessage(cwd) {
10096
10138
  }
10097
10139
  function planPolicy(cwd) {
10098
10140
  const path = join14(cwd, POLICY_FILE);
10099
- if (existsSync10(path)) {
10141
+ if (existsSync11(path)) {
10100
10142
  try {
10101
10143
  loadPolicy(cwd);
10102
10144
  } catch (e) {
@@ -10115,9 +10157,9 @@ function planPolicy(cwd) {
10115
10157
  function planGitignore(cwd) {
10116
10158
  const rel = ".gitignore";
10117
10159
  const path = join14(cwd, rel);
10118
- if (!existsSync10(join14(cwd, "node_modules"))) return null;
10160
+ if (!existsSync11(join14(cwd, "node_modules"))) return null;
10119
10161
  let existing = "";
10120
- if (existsSync10(path)) {
10162
+ if (existsSync11(path)) {
10121
10163
  let st;
10122
10164
  try {
10123
10165
  st = lstatSync7(path);
@@ -10166,7 +10208,7 @@ function planGitignore(cwd) {
10166
10208
  return {
10167
10209
  item: "gitignore",
10168
10210
  path: rel,
10169
- status: existsSync10(path) ? "update" : "create",
10211
+ status: existsSync11(path) ? "update" : "create",
10170
10212
  detail: "exclude installed Node dependencies from repository diffs",
10171
10213
  apply: () => writeFileSync7(path, next)
10172
10214
  };
@@ -10175,7 +10217,7 @@ function planClaudeHooks(cwd) {
10175
10217
  const rel = ".claude/settings.json";
10176
10218
  const path = join14(cwd, rel);
10177
10219
  let settings = {};
10178
- if (existsSync10(path)) {
10220
+ if (existsSync11(path)) {
10179
10221
  let parsed;
10180
10222
  try {
10181
10223
  parsed = JSON.parse(readFileSync12(path, "utf8"));
@@ -10213,7 +10255,7 @@ function planClaudeHooks(cwd) {
10213
10255
  return {
10214
10256
  item: "agent",
10215
10257
  path: rel,
10216
- status: existsSync10(path) ? "update" : "create",
10258
+ status: existsSync11(path) ? "update" : "create",
10217
10259
  detail: [
10218
10260
  needPre && "wire PreToolUse deny",
10219
10261
  needStop && "wire Stop sweep",
@@ -10247,10 +10289,10 @@ function planClaudeHooks(cwd) {
10247
10289
  function hooksDir(cwd) {
10248
10290
  try {
10249
10291
  const out3 = execFileSync6("git", ["rev-parse", "--git-path", "hooks"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
10250
- if (out3) return resolve3(cwd, out3);
10292
+ if (out3) return resolve4(cwd, out3);
10251
10293
  } catch {
10252
10294
  }
10253
- return existsSync10(join14(cwd, ".git")) ? join14(cwd, ".git", "hooks") : null;
10295
+ return existsSync11(join14(cwd, ".git")) ? join14(cwd, ".git", "hooks") : null;
10254
10296
  }
10255
10297
  function display(cwd, path) {
10256
10298
  const rel = relative3(cwd, path);
@@ -10277,11 +10319,11 @@ function planPreCommit(cwd) {
10277
10319
  const skip = (detail) => ({ item: "pre-commit", path: "(none)", status: "skip", detail });
10278
10320
  let path;
10279
10321
  let note;
10280
- if (existsSync10(join14(cwd, ".husky"))) {
10322
+ if (existsSync11(join14(cwd, ".husky"))) {
10281
10323
  path = join14(cwd, ".husky", "pre-commit");
10282
10324
  note = "husky";
10283
10325
  } else {
10284
- const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) => existsSync10(join14(cwd, c))));
10326
+ const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) => existsSync11(join14(cwd, c))));
10285
10327
  if (managed) {
10286
10328
  return skip(`${managed.name} manages this repository's hooks and regenerates them on install, so a line in the hooks directory would not last \u2014 ${managed.hint}`);
10287
10329
  }
@@ -10297,10 +10339,10 @@ function planPreCommit(cwd) {
10297
10339
  }
10298
10340
  }
10299
10341
  const rel = display(cwd, path);
10300
- if (existsSync10(path) && !statSync2(path).isFile()) {
10342
+ if (existsSync11(path) && !statSync2(path).isFile()) {
10301
10343
  return { item: "pre-commit", path: rel, status: "error", detail: "exists but is not a regular file \u2014 refusing to write" };
10302
10344
  }
10303
- const existing = existsSync10(path) ? readFileSync12(path, "utf8") : null;
10345
+ const existing = existsSync11(path) ? readFileSync12(path, "utf8") : null;
10304
10346
  if (existing === null) {
10305
10347
  return {
10306
10348
  item: "pre-commit",
@@ -10389,7 +10431,7 @@ ${block.join("\n")}
10389
10431
  function planCodeowners(cwd) {
10390
10432
  const rel = ".github/CODEOWNERS";
10391
10433
  const existingRel = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"].find(
10392
- (r) => existsSync10(join14(cwd, r))
10434
+ (r) => existsSync11(join14(cwd, r))
10393
10435
  );
10394
10436
  const path = join14(cwd, existingRel ?? rel);
10395
10437
  const existing = existingRel ? readFileSync12(path, "utf8") : null;
@@ -10430,7 +10472,7 @@ function planWorkflow(cwd, force) {
10430
10472
  mkdirSync6(dirname4(path), { recursive: true });
10431
10473
  writeFileSync7(path, workflowFile(WORKFLOW_CONTENT));
10432
10474
  };
10433
- if (!existsSync10(path)) {
10475
+ if (!existsSync11(path)) {
10434
10476
  return {
10435
10477
  item: "ci",
10436
10478
  path: rel,
@@ -10482,6 +10524,13 @@ function planned(item, path, plan) {
10482
10524
  return { item, path, status: "error", detail: `cannot plan this item \u2014 ${errText3(e)}` };
10483
10525
  }
10484
10526
  }
10527
+ function sameDir(a, b) {
10528
+ try {
10529
+ return realpathSync4(a) === realpathSync4(b);
10530
+ } catch {
10531
+ return false;
10532
+ }
10533
+ }
10485
10534
  function planInit(cwd, opts = {}) {
10486
10535
  requireShippedVersion();
10487
10536
  const gitignore = planGitignore(cwd);
@@ -10495,9 +10544,14 @@ function planInit(cwd, opts = {}) {
10495
10544
  ];
10496
10545
  }
10497
10546
  function runInit(opts) {
10498
- const cwd = opts.cwd ?? process.cwd();
10499
- const plan = planInit(cwd, { forceWorkflow: opts.forceWorkflow });
10547
+ const requested = resolve4(opts.cwd ?? process.cwd());
10548
+ const cwd = repoRoot(requested);
10500
10549
  const w = opts.quiet ? { write: (_text) => true } : process.stdout;
10550
+ if (cwd !== requested && !sameDir(cwd, requested)) {
10551
+ process.stderr.write(`tamperward init: ${requested} is inside the repository rooted at ${cwd}; wiring the root.
10552
+ `);
10553
+ }
10554
+ const plan = planInit(cwd, { forceWorkflow: opts.forceWorkflow });
10501
10555
  let applied = 0;
10502
10556
  for (const a of plan) {
10503
10557
  if (!opts.dryRun && a.apply) {
@@ -10552,6 +10606,7 @@ var init_init = __esm({
10552
10606
  init_verifier_limits();
10553
10607
  init_wiring();
10554
10608
  init_narrow();
10609
+ init_repo_context();
10555
10610
  errText3 = (e) => (e instanceof Error ? e.message : String(e)).replace(/\s+/g, " ").trim();
10556
10611
  isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
10557
10612
  HOOK_RE = /\btamperward(?:@\S+)?\s+hook\s+claude\b/;
@@ -10720,12 +10775,12 @@ var init_fingerprint = __esm({
10720
10775
  // src/dependency-env.ts
10721
10776
  import { createHash as createHash8 } from "node:crypto";
10722
10777
  import {
10723
- existsSync as existsSync11,
10778
+ existsSync as existsSync12,
10724
10779
  lstatSync as lstatSync8,
10725
10780
  readdirSync as readdirSync3,
10726
10781
  readFileSync as readFileSync13,
10727
10782
  readlinkSync as readlinkSync2,
10728
- realpathSync as realpathSync4,
10783
+ realpathSync as realpathSync5,
10729
10784
  statSync as statSync3
10730
10785
  } from "node:fs";
10731
10786
  import {
@@ -10735,14 +10790,14 @@ import {
10735
10790
  isAbsolute as isAbsolute9,
10736
10791
  join as join15,
10737
10792
  relative as relative4,
10738
- resolve as resolve4,
10793
+ resolve as resolve5,
10739
10794
  sep as sep2
10740
10795
  } from "node:path";
10741
10796
  function metricsFor(descriptor) {
10742
10797
  return descriptor.metrics ??= { fullSnapshots: 0, reusedSnapshots: 0, totalMs: 0 };
10743
10798
  }
10744
10799
  function inside(root, candidate) {
10745
- const rel = relative4(resolve4(root), resolve4(candidate));
10800
+ const rel = relative4(resolve5(root), resolve5(candidate));
10746
10801
  return rel === "" || rel !== ".." && !rel.startsWith(".." + sep2) && !isAbsolute9(rel);
10747
10802
  }
10748
10803
  function modeKind(path) {
@@ -10757,7 +10812,7 @@ function fingerprintRoot(root, cwd, h) {
10757
10812
  let currentReal;
10758
10813
  try {
10759
10814
  rootStat = lstatSync8(root.path);
10760
- currentReal = realpathSync4(root.path);
10815
+ currentReal = realpathSync5(root.path);
10761
10816
  } catch (e) {
10762
10817
  throw new Unattestable(
10763
10818
  root.kind + " root " + JSON.stringify(root.path) + " can no longer be resolved (" + (e instanceof Error ? e.message : String(e)) + ")"
@@ -10773,7 +10828,7 @@ function fingerprintRoot(root, cwd, h) {
10773
10828
  }
10774
10829
  h.update("root\0" + root.kind + "\0" + root.path + "\0" + root.realPath + "\0" + String(rootStat.mode) + "\0");
10775
10830
  if (rootStat.isSymbolicLink()) h.update(readlinkSync2(root.path) + "\0");
10776
- const cwdReal = realpathSync4(cwd);
10831
+ const cwdReal = realpathSync5(cwd);
10777
10832
  const visited = /* @__PURE__ */ new Set();
10778
10833
  const hashFile = (path, label) => {
10779
10834
  let st;
@@ -10790,7 +10845,7 @@ function fingerprintRoot(root, cwd, h) {
10790
10845
  }
10791
10846
  };
10792
10847
  const walk2 = (dir, namespace) => {
10793
- const realDir = realpathSync4(dir);
10848
+ const realDir = realpathSync5(dir);
10794
10849
  if (visited.has(realDir)) return;
10795
10850
  visited.add(realDir);
10796
10851
  let entries;
@@ -10840,7 +10895,7 @@ function fingerprintRoot(root, cwd, h) {
10840
10895
  h.update(target + "\0");
10841
10896
  let resolved;
10842
10897
  try {
10843
- resolved = realpathSync4(path);
10898
+ resolved = realpathSync5(path);
10844
10899
  } catch {
10845
10900
  continue;
10846
10901
  }
@@ -10874,7 +10929,7 @@ function snapshot(roots, probes, cwd) {
10874
10929
  const h = createHash8("sha256");
10875
10930
  for (const probe of probes) {
10876
10931
  h.update(
10877
- "probe\0" + probe.kind + "\0" + probe.path + "\0" + (existsSync11(probe.path) ? "present" : "absent") + "\0"
10932
+ "probe\0" + probe.kind + "\0" + probe.path + "\0" + (existsSync12(probe.path) ? "present" : "absent") + "\0"
10878
10933
  );
10879
10934
  }
10880
10935
  for (const root of roots) fingerprintRoot(root, cwd, h);
@@ -10895,7 +10950,7 @@ function freezeRoot(kind, path) {
10895
10950
  if (!statSync3(path).isDirectory()) {
10896
10951
  throw new Error("not a directory");
10897
10952
  }
10898
- return { kind, path: resolve4(path), realPath: realpathSync4(path) };
10953
+ return { kind, path: resolve5(path), realPath: realpathSync5(path) };
10899
10954
  } catch (e) {
10900
10955
  throw new Unattestable(
10901
10956
  kind + " root " + JSON.stringify(path) + " is not resolvable (" + (e instanceof Error ? e.message : String(e)) + ")"
@@ -10931,17 +10986,17 @@ function firstExecutable(command) {
10931
10986
  function resolveExecutable(token, cwd, env) {
10932
10987
  if (!token) return null;
10933
10988
  if (token.includes("/") || token.includes("\\")) {
10934
- const path = isAbsolute9(token) ? token : resolve4(cwd, token);
10989
+ const path = isAbsolute9(token) ? token : resolve5(cwd, token);
10935
10990
  try {
10936
- return existsSync11(path) ? realpathSync4(path) : null;
10991
+ return existsSync12(path) ? realpathSync5(path) : null;
10937
10992
  } catch {
10938
10993
  return null;
10939
10994
  }
10940
10995
  }
10941
10996
  for (const entry of (env.PATH ?? "").split(delimiter).filter(Boolean)) {
10942
- const candidate = resolve4(entry, token);
10997
+ const candidate = resolve5(entry, token);
10943
10998
  try {
10944
- if (existsSync11(candidate) && statSync3(candidate).isFile()) return realpathSync4(candidate);
10999
+ if (existsSync12(candidate) && statSync3(candidate).isFile()) return realpathSync5(candidate);
10945
11000
  } catch {
10946
11001
  }
10947
11002
  }
@@ -10958,13 +11013,13 @@ function looksPython(command, executable, token) {
10958
11013
  }
10959
11014
  function inferredVenvFromToken(token, cwd) {
10960
11015
  if (!token || !token.includes("/") && !token.includes("\\")) return null;
10961
- const path = isAbsolute9(token) ? token : resolve4(cwd, token);
11016
+ const path = isAbsolute9(token) ? token : resolve5(cwd, token);
10962
11017
  if (!pythonName(basename3(path))) return null;
10963
11018
  const parent = dirname5(path);
10964
11019
  const parentName = basename3(parent).toLowerCase();
10965
11020
  if (parentName !== "bin" && parentName !== "scripts") return null;
10966
11021
  const root = dirname5(parent);
10967
- return existsSync11(join15(root, "pyvenv.cfg")) ? root : null;
11022
+ return existsSync12(join15(root, "pyvenv.cfg")) ? root : null;
10968
11023
  }
10969
11024
  function unsupportedEcosystem(command, token) {
10970
11025
  const name = token ? basename3(token).toLowerCase() : "";
@@ -10980,11 +11035,11 @@ function inferredVenv(executable) {
10980
11035
  const parentName = basename3(parent).toLowerCase();
10981
11036
  if (parentName !== "bin" && parentName !== "scripts") return null;
10982
11037
  const root = dirname5(parent);
10983
- return existsSync11(join15(root, "pyvenv.cfg")) ? root : null;
11038
+ return existsSync12(join15(root, "pyvenv.cfg")) ? root : null;
10984
11039
  }
10985
11040
  function venvProblem(root) {
10986
11041
  const cfg = join15(root, "pyvenv.cfg");
10987
- if (!existsSync11(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
11042
+ if (!existsSync12(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
10988
11043
  try {
10989
11044
  const body = readFileSync13(cfg, "utf8");
10990
11045
  if (/^\s*include-system-site-packages\s*=\s*true\s*$/im.test(body)) {
@@ -10996,7 +11051,7 @@ function venvProblem(root) {
10996
11051
  return null;
10997
11052
  }
10998
11053
  function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
10999
- const cwd = resolve4(cwdInput);
11054
+ const cwd = resolve5(cwdInput);
11000
11055
  const roots = [];
11001
11056
  const probes = [];
11002
11057
  const seen = /* @__PURE__ */ new Set();
@@ -11011,7 +11066,7 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
11011
11066
  };
11012
11067
  try {
11013
11068
  const nodeModules = join15(cwd, "node_modules");
11014
- if (existsSync11(nodeModules)) addRoot("node_modules", nodeModules);
11069
+ if (existsSync12(nodeModules)) addRoot("node_modules", nodeModules);
11015
11070
  else probes.push({ kind: "node_modules", path: nodeModules });
11016
11071
  const token = firstExecutable(command);
11017
11072
  const executable = resolveExecutable(token, cwd, env);
@@ -11020,10 +11075,10 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
11020
11075
  if (!isAbsolute9(env.VIRTUAL_ENV)) {
11021
11076
  throw new Unattestable("VIRTUAL_ENV is relative (" + JSON.stringify(env.VIRTUAL_ENV) + ")");
11022
11077
  }
11023
- venv = resolve4(env.VIRTUAL_ENV);
11078
+ venv = resolve5(env.VIRTUAL_ENV);
11024
11079
  } else {
11025
11080
  venv = inferredVenv(executable) ?? inferredVenvFromToken(token, cwd);
11026
- if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) && existsSync11(join15(cwd, ".venv", "pyvenv.cfg"))) {
11081
+ if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) && existsSync12(join15(cwd, ".venv", "pyvenv.cfg"))) {
11027
11082
  venv = join15(cwd, ".venv");
11028
11083
  }
11029
11084
  }
@@ -11060,7 +11115,7 @@ function checkDependencyEnvironment(cwdInput, descriptor) {
11060
11115
  const fingerprint2 = measuredSnapshot(
11061
11116
  descriptor.roots,
11062
11117
  descriptor.probes,
11063
- resolve4(cwdInput),
11118
+ resolve5(cwdInput),
11064
11119
  metricsFor(descriptor)
11065
11120
  );
11066
11121
  if (fingerprint2 !== descriptor.fingerprint) {
@@ -11073,7 +11128,7 @@ function checkDependencyEnvironment(cwdInput, descriptor) {
11073
11128
  }
11074
11129
  function attestDependencyEnvironment(cwdInput, descriptor) {
11075
11130
  const attestation = {
11076
- cwd: resolve4(cwdInput),
11131
+ cwd: resolve5(cwdInput),
11077
11132
  descriptor,
11078
11133
  check: checkDependencyEnvironment(cwdInput, descriptor)
11079
11134
  };
@@ -11081,7 +11136,7 @@ function attestDependencyEnvironment(cwdInput, descriptor) {
11081
11136
  return attestation;
11082
11137
  }
11083
11138
  function reuseDependencyEnvironmentAttestation(cwdInput, descriptor, attestation) {
11084
- if (!attestation || !issuedAttestations.has(attestation) || attestation.descriptor !== descriptor || attestation.cwd !== resolve4(cwdInput)) return null;
11139
+ if (!attestation || !issuedAttestations.has(attestation) || attestation.descriptor !== descriptor || attestation.cwd !== resolve5(cwdInput)) return null;
11085
11140
  metricsFor(descriptor).reusedSnapshots += 1;
11086
11141
  return attestation.check;
11087
11142
  }
@@ -11447,8 +11502,8 @@ if (child) {
11447
11502
  // src/verifier-backend.ts
11448
11503
  import { createHash as createHash9, randomUUID } from "node:crypto";
11449
11504
  import { spawnSync as spawnSync2 } from "node:child_process";
11450
- import { accessSync, constants as constants2, readFileSync as readFileSync15, realpathSync as realpathSync5, statSync as statSync4 } from "node:fs";
11451
- import { delimiter as delimiter2, dirname as dirname6, join as join17, resolve as resolve5 } from "node:path";
11505
+ import { accessSync, constants as constants2, readFileSync as readFileSync15, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
11506
+ import { delimiter as delimiter2, dirname as dirname6, join as join17, resolve as resolve6 } from "node:path";
11452
11507
  function identity() {
11453
11508
  const uid = typeof process.getuid === "function" ? process.getuid() : null;
11454
11509
  const groups = typeof process.getgroups === "function" ? process.getgroups() : [];
@@ -11468,7 +11523,7 @@ function writableByCaller(path) {
11468
11523
  }
11469
11524
  }
11470
11525
  function replaceableByCaller(path) {
11471
- let current = resolve5(path);
11526
+ let current = resolve6(path);
11472
11527
  if (writableByCaller(current)) return true;
11473
11528
  current = dirname6(current);
11474
11529
  while (true) {
@@ -11485,7 +11540,7 @@ function findExecutable(name) {
11485
11540
  const candidate = join17(raw, name);
11486
11541
  try {
11487
11542
  accessSync(candidate, constants2.X_OK);
11488
- return realpathSync5(candidate);
11543
+ return realpathSync6(candidate);
11489
11544
  } catch {
11490
11545
  }
11491
11546
  }
@@ -11560,7 +11615,7 @@ function inspectImageMetadata(enginePath, host, image) {
11560
11615
  function engineStable(backend) {
11561
11616
  if (!backend.enginePath || !backend.engineSha256) return false;
11562
11617
  try {
11563
- if (realpathSync5(backend.enginePath) !== backend.enginePath) return false;
11618
+ if (realpathSync6(backend.enginePath) !== backend.enginePath) return false;
11564
11619
  if (replaceableByCaller(backend.enginePath)) return false;
11565
11620
  return fileSha256(backend.enginePath) === backend.engineSha256;
11566
11621
  } catch {
@@ -11814,7 +11869,7 @@ function runContainerStage(backend, dir, command, budgetSecs) {
11814
11869
  const args = containerRunArgs({
11815
11870
  image: backend.image,
11816
11871
  name,
11817
- workspace: resolve5(dir),
11872
+ workspace: resolve6(dir),
11818
11873
  command,
11819
11874
  uid,
11820
11875
  gid,
@@ -11926,9 +11981,9 @@ var init_verifier_backend = __esm({
11926
11981
  // src/cli/verify.ts
11927
11982
  import { execFileSync as execFileSync8, spawnSync as spawnSync3 } from "node:child_process";
11928
11983
  import { createHash as createHash10 } from "node:crypto";
11929
- import { chmodSync as chmodSync3, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync12 } from "node:fs";
11984
+ import { chmodSync as chmodSync3, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync13 } from "node:fs";
11930
11985
  import { tmpdir as tmpdir4 } from "node:os";
11931
- import { dirname as dirname7, isAbsolute as isAbsolute10, join as join18, relative as relative5, resolve as resolve6, sep as sep3 } from "node:path";
11986
+ import { dirname as dirname7, isAbsolute as isAbsolute10, join as join18, relative as relative5, resolve as resolve7, sep as sep3 } from "node:path";
11932
11987
  function oracleAssuranceReport() {
11933
11988
  return {
11934
11989
  level: "suite-exit-only",
@@ -11942,11 +11997,11 @@ function git2(args, cwd) {
11942
11997
  }
11943
11998
  function resolveBase(base, cwd) {
11944
11999
  assertRev(base);
11945
- const rev = git2(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
12000
+ const rev2 = git2(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
11946
12001
  try {
11947
- return git2(["merge-base", rev, "HEAD"], cwd).trim();
12002
+ return git2(["merge-base", rev2, "HEAD"], cwd).trim();
11948
12003
  } catch {
11949
- return rev;
12004
+ return rev2;
11950
12005
  }
11951
12006
  }
11952
12007
  function baseIsAncestorOfHead(base, cwd) {
@@ -11954,7 +12009,7 @@ function baseIsAncestorOfHead(base, cwd) {
11954
12009
  return r.status === 0;
11955
12010
  }
11956
12011
  function inside2(root, path) {
11957
- const rel = relative5(resolve6(root), resolve6(path));
12012
+ const rel = relative5(resolve7(root), resolve7(path));
11958
12013
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute10(rel);
11959
12014
  }
11960
12015
  function linkParts(path) {
@@ -11971,7 +12026,7 @@ function rootedLinkTarget(target) {
11971
12026
  return isAbsolute10(target) || driveRelativeLinkTarget(target);
11972
12027
  }
11973
12028
  function safeSymlink(target, out3, root, label) {
11974
- if (rootedLinkTarget(target) || !inside2(root, resolve6(dirname7(out3), target))) {
12029
+ if (rootedLinkTarget(target) || !inside2(root, resolve7(dirname7(out3), target))) {
11975
12030
  throw linkEscape(label, target);
11976
12031
  }
11977
12032
  rmSync5(out3, { force: true });
@@ -12022,7 +12077,7 @@ function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
12022
12077
  throw linkEscape(label, target);
12023
12078
  }
12024
12079
  stack = [];
12025
- pending.unshift(...linkParts(relative5(dependencyRoot, resolve6(next))));
12080
+ pending.unshift(...linkParts(relative5(dependencyRoot, resolve7(next))));
12026
12081
  } else {
12027
12082
  pending.unshift(...linkParts(next));
12028
12083
  }
@@ -12083,7 +12138,7 @@ function probeFilesystemCaseSensitivity(dir) {
12083
12138
  const lower = join18(dir, ".tw-case-probe-a");
12084
12139
  try {
12085
12140
  writeFileSync9(upper, "", { flag: "wx" });
12086
- return !existsSync12(lower);
12141
+ return !existsSync13(lower);
12087
12142
  } finally {
12088
12143
  rmSync5(upper, { force: true });
12089
12144
  }
@@ -12801,9 +12856,9 @@ var init_verify = __esm({
12801
12856
  // src/cli/run.ts
12802
12857
  import { execFileSync as execFileSync9, spawn, spawnSync as spawnSync4 } from "node:child_process";
12803
12858
  import { randomBytes } from "node:crypto";
12804
- import { accessSync as accessSync2, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync7, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync10 } from "node:fs";
12859
+ import { accessSync as accessSync2, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync10 } from "node:fs";
12805
12860
  import { tmpdir as tmpdir5 } from "node:os";
12806
- import { dirname as dirname8, join as join19, resolve as resolve7 } from "node:path";
12861
+ import { dirname as dirname8, join as join19, resolve as resolve8 } from "node:path";
12807
12862
  function authoritativeRunLifecyclePlatform(platform = process.platform) {
12808
12863
  return platform === "linux";
12809
12864
  }
@@ -12856,7 +12911,7 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
12856
12911
  }
12857
12912
  for (const candidate of candidates) {
12858
12913
  try {
12859
- const real = realpathSync7(candidate);
12914
+ const real = realpathSync8(candidate);
12860
12915
  const st = statSync5(real);
12861
12916
  if (!st.isFile() || writableByCaller2(real)) continue;
12862
12917
  const probe = spawnSync4(
@@ -13022,7 +13077,7 @@ function startSupervisedObserver(cwd, base, entry) {
13022
13077
  try {
13023
13078
  child = spawn(
13024
13079
  process.execPath,
13025
- [resolve7(cliEntry), "watch", "--dir", cwd, "--log", log, "--base", base],
13080
+ [resolve8(cliEntry), "watch", "--dir", cwd, "--log", log, "--base", base],
13026
13081
  {
13027
13082
  cwd,
13028
13083
  stdio: ["ignore", "ignore", "inherit"],
@@ -13130,7 +13185,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
13130
13185
  const out3 = [];
13131
13186
  let real;
13132
13187
  try {
13133
- real = realpathSync7(cwd);
13188
+ real = realpathSync8(cwd);
13134
13189
  } catch {
13135
13190
  return out3;
13136
13191
  }
@@ -13159,7 +13214,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
13159
13214
  for (const link of procLinks) {
13160
13215
  try {
13161
13216
  const raw = readlinkSync4(link).replace(/ \(deleted\)$/, "");
13162
- const held = raw.startsWith("/") ? resolve7(raw) : raw;
13217
+ const held = raw.startsWith("/") ? resolve8(raw) : raw;
13163
13218
  if (held === real || held.startsWith(real + "/")) {
13164
13219
  holds = true;
13165
13220
  break;
@@ -13172,7 +13227,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
13172
13227
  return out3;
13173
13228
  }
13174
13229
  function runEnvelope(opts) {
13175
- const cwd = resolve7(opts.cwd ?? process.cwd());
13230
+ const cwd = resolve8(opts.cwd ?? process.cwd());
13176
13231
  const say = opts.json ? (_s) => {
13177
13232
  } : out;
13178
13233
  if (!opts.argv.length) {
@@ -13716,8 +13771,8 @@ raise SystemExit(0)
13716
13771
 
13717
13772
  // src/cli/doctor.ts
13718
13773
  import { execFileSync as execFileSync10 } from "node:child_process";
13719
- import { existsSync as existsSync13, readFileSync as readFileSync18, readdirSync as readdirSync5 } from "node:fs";
13720
- import { join as join20, resolve as resolve8 } from "node:path";
13774
+ import { existsSync as existsSync14, readFileSync as readFileSync18, readdirSync as readdirSync5 } from "node:fs";
13775
+ import { join as join20, resolve as resolve9 } from "node:path";
13721
13776
  function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
13722
13777
  if (platform === "linux") {
13723
13778
  if (linuxPython?.path) {
@@ -13772,7 +13827,7 @@ function wiringState(status, authority) {
13772
13827
  return authority ? "BROKEN" : "WARN";
13773
13828
  }
13774
13829
  function pinsInFile(path) {
13775
- if (!existsSync13(path)) return [];
13830
+ if (!existsSync14(path)) return [];
13776
13831
  let src = "";
13777
13832
  try {
13778
13833
  src = readFileSync18(path, "utf8");
@@ -13795,8 +13850,8 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
13795
13850
  const broken = [];
13796
13851
  const warnings = [];
13797
13852
  for (const rel of workflowRels) {
13798
- const path = resolve8(cwd, rel);
13799
- if (!existsSync13(path)) {
13853
+ const path = resolve9(cwd, rel);
13854
+ if (!existsSync14(path)) {
13800
13855
  broken.push(`${rel} is missing`);
13801
13856
  continue;
13802
13857
  }
@@ -14142,7 +14197,7 @@ function verifyJobs(doc) {
14142
14197
  return found;
14143
14198
  }
14144
14199
  function diagnose(opts = {}) {
14145
- const cwd = resolve8(opts.cwd ?? process.cwd());
14200
+ const cwd = repoRoot(resolve9(opts.cwd ?? process.cwd()));
14146
14201
  let policy;
14147
14202
  try {
14148
14203
  policy = policyFor(opts, cwd);
@@ -14166,8 +14221,8 @@ function diagnose(opts = {}) {
14166
14221
  workflowRels.push(opts.workflow);
14167
14222
  } else {
14168
14223
  const workflowDirRel = ".github/workflows";
14169
- const workflowDir = resolve8(cwd, workflowDirRel);
14170
- if (!existsSync13(workflowDir)) {
14224
+ const workflowDir = resolve9(cwd, workflowDirRel);
14225
+ if (!existsSync14(workflowDir)) {
14171
14226
  return fail("ci-verifier", `${workflowDirRel}: workflow directory does not exist`);
14172
14227
  }
14173
14228
  let entries;
@@ -14186,8 +14241,8 @@ function diagnose(opts = {}) {
14186
14241
  let verifyJobCount = 0;
14187
14242
  const authorityWorkflows = [];
14188
14243
  for (const workflowRel of workflowRels) {
14189
- const workflowPath = resolve8(cwd, workflowRel);
14190
- if (!existsSync13(workflowPath)) {
14244
+ const workflowPath = resolve9(cwd, workflowRel);
14245
+ if (!existsSync14(workflowPath)) {
14191
14246
  return fail("ci-verifier", `${workflowRel}: workflow does not exist`);
14192
14247
  }
14193
14248
  let doc;
@@ -14329,6 +14384,7 @@ var init_doctor = __esm({
14329
14384
  init_run();
14330
14385
  init_machine_output();
14331
14386
  init_narrow();
14387
+ init_repo_context();
14332
14388
  VERIFY_COMMAND = /\btamperward(?:@\S+)?\s+verify\b/;
14333
14389
  GITHUB_API_SCRIPT = [
14334
14390
  "const endpoint = process.argv[1];",
@@ -14347,16 +14403,16 @@ var init_doctor = __esm({
14347
14403
  // src/cli/trace-verify.ts
14348
14404
  import { execFileSync as execFileSync11, spawnSync as spawnSync5 } from "node:child_process";
14349
14405
  import {
14350
- existsSync as existsSync14,
14406
+ existsSync as existsSync15,
14351
14407
  mkdtempSync as mkdtempSync5,
14352
14408
  readFileSync as readFileSync19,
14353
14409
  readdirSync as readdirSync6,
14354
- realpathSync as realpathSync8,
14410
+ realpathSync as realpathSync9,
14355
14411
  rmSync as rmSync7,
14356
14412
  symlinkSync as symlinkSync2
14357
14413
  } from "node:fs";
14358
14414
  import { tmpdir as tmpdir6 } from "node:os";
14359
- import { isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
14415
+ import { isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as resolve10, sep as sep4 } from "node:path";
14360
14416
  function unescapeStraceString(s) {
14361
14417
  return s.replace(/\\([0-7]{1,3})/g, (_m, oct) => String.fromCharCode(parseInt(oct, 8))).replace(/\\x([0-9a-fA-F]{2})/g, (_m, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
14362
14418
  }
@@ -14381,13 +14437,13 @@ function parseStraceFileAccess(raw) {
14381
14437
  return out3;
14382
14438
  }
14383
14439
  function inside3(root, path) {
14384
- const rel = relative6(resolve9(root), resolve9(path));
14440
+ const rel = relative6(resolve10(root), resolve10(path));
14385
14441
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep4}`) && !isAbsolute11(rel);
14386
14442
  }
14387
14443
  function normalRepoPath(root, raw) {
14388
- const abs2 = isAbsolute11(raw) ? resolve9(raw) : resolve9(root, raw);
14389
- if (!inside3(root, abs2)) return null;
14390
- const rel = relative6(root, abs2).split(sep4).join("/");
14444
+ const abs = isAbsolute11(raw) ? resolve10(raw) : resolve10(root, raw);
14445
+ if (!inside3(root, abs)) return null;
14446
+ const rel = relative6(root, abs).split(sep4).join("/");
14391
14447
  return rel && rel !== ".." ? rel : null;
14392
14448
  }
14393
14449
  function likelyConfig(path) {
@@ -14405,7 +14461,7 @@ function summarizeTraceRuns(opts) {
14405
14461
  for (const item of run) {
14406
14462
  const rel = normalRepoPath(opts.root, item.path);
14407
14463
  const isTracked = rel !== null && opts.tracked.has(rel);
14408
- const keyPath = rel !== null ? opts.tracked.has(rel) ? rel : null : resolve9(opts.root, item.path);
14464
+ const keyPath = rel !== null ? opts.tracked.has(rel) ? rel : null : resolve10(opts.root, item.path);
14409
14465
  if (keyPath === null) continue;
14410
14466
  const target = isTracked ? repo : external;
14411
14467
  const key2 = `${isTracked ? "repo" : "external"}\0${keyPath}`;
@@ -14484,22 +14540,22 @@ function materializeBase(base, cwd, dest) {
14484
14540
  }
14485
14541
  const hostModules = join21(cwd, "node_modules");
14486
14542
  const tracedModules = join21(dest, "node_modules");
14487
- if (existsSync14(hostModules) && !existsSync14(tracedModules)) {
14488
- symlinkSync2(resolve9(hostModules), tracedModules, "dir");
14543
+ if (existsSync15(hostModules) && !existsSync15(tracedModules)) {
14544
+ symlinkSync2(resolve10(hostModules), tracedModules, "dir");
14489
14545
  }
14490
14546
  }
14491
14547
  function traceFiles(prefix) {
14492
- const dir = resolve9(prefix, "..");
14548
+ const dir = resolve10(prefix, "..");
14493
14549
  const base = prefix.split(sep4).at(-1) ?? prefix;
14494
14550
  return readdirSync6(dir).filter((name) => name === base || name.startsWith(base + ".")).map((name) => join21(dir, name)).sort();
14495
14551
  }
14496
14552
  function rewriteTraceRoot(accesses, actualRoot) {
14497
14553
  return accesses.map((item) => {
14498
- const abs2 = isAbsolute11(item.path) ? resolve9(item.path) : resolve9(actualRoot, item.path);
14499
- if (!inside3(actualRoot, abs2)) return { ...item, path: abs2 };
14500
- const rel = relative6(actualRoot, abs2).split(sep4).join("/");
14554
+ const abs = isAbsolute11(item.path) ? resolve10(item.path) : resolve10(actualRoot, item.path);
14555
+ if (!inside3(actualRoot, abs)) return { ...item, path: abs };
14556
+ const rel = relative6(actualRoot, abs).split(sep4).join("/");
14501
14557
  try {
14502
- const real = resolve9(realpathSync8(abs2));
14558
+ const real = resolve10(realpathSync9(abs));
14503
14559
  if (!inside3(actualRoot, real)) return { ...item, path: real };
14504
14560
  } catch {
14505
14561
  }
@@ -14588,7 +14644,7 @@ function renderText2(report2) {
14588
14644
  for (const note of report2.notes) out3(`note: ${note}`);
14589
14645
  }
14590
14646
  function runTraceVerify(opts = {}) {
14591
- const cwd = resolve9(opts.cwd ?? process.cwd());
14647
+ const cwd = resolve10(opts.cwd ?? process.cwd());
14592
14648
  if (process.platform !== "linux") {
14593
14649
  process.stderr.write("tamperward trace-verify: Linux only in this release; unsupported platform \u2014 no parity is implied.\n");
14594
14650
  return 2;
@@ -14736,9 +14792,9 @@ var init_trace_verify = __esm({
14736
14792
  // src/cli/onboard.ts
14737
14793
  import { execFileSync as execFileSync12 } from "node:child_process";
14738
14794
  import { randomBytes as randomBytes2 } from "node:crypto";
14739
- import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as realpathSync9, renameSync as renameSync2, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
14795
+ import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as realpathSync10, renameSync as renameSync2, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
14740
14796
  import { tmpdir as tmpdir7 } from "node:os";
14741
- import { join as join22, relative as relative7, resolve as resolve10, sep as sep5 } from "node:path";
14797
+ import { join as join22, relative as relative7, resolve as resolve11, sep as sep5 } from "node:path";
14742
14798
  import { createInterface } from "node:readline";
14743
14799
  import { parseDocument } from "yaml";
14744
14800
  function readlineAsker(input, output) {
@@ -14747,9 +14803,9 @@ function readlineAsker(input, output) {
14747
14803
  let pending = null;
14748
14804
  rl.on("close", () => {
14749
14805
  closed = true;
14750
- const resolve15 = pending;
14806
+ const resolve16 = pending;
14751
14807
  pending = null;
14752
- resolve15?.(null);
14808
+ resolve16?.(null);
14753
14809
  });
14754
14810
  rl.on("SIGINT", () => rl.close());
14755
14811
  const ask = (question) => new Promise((resolvePromise) => {
@@ -14775,7 +14831,7 @@ function terminalText(text) {
14775
14831
  return stripControl(text).replace(/\s+/g, " ").trim();
14776
14832
  }
14777
14833
  function committableSetupPath(cwd, path) {
14778
- const rel = relative7(cwd, resolve10(cwd, path));
14834
+ const rel = relative7(cwd, resolve11(cwd, path));
14779
14835
  if (!rel || rel === ".." || rel.startsWith(".." + sep5)) return false;
14780
14836
  return rel !== ".git" && !rel.startsWith(".git" + sep5);
14781
14837
  }
@@ -14844,7 +14900,7 @@ function writeVerifyCommand(cwd, command) {
14844
14900
  }
14845
14901
  }
14846
14902
  async function runOnboard(opts, io = {}) {
14847
- const requestedCwd = resolve10(opts.cwd ?? process.cwd());
14903
+ const requestedCwd = resolve11(opts.cwd ?? process.cwd());
14848
14904
  const out3 = io.out ?? ((line) => void process.stdout.write(line + "\n"));
14849
14905
  const rawErr = (line) => void process.stderr.write(terminalText(line) + "\n");
14850
14906
  const platform = io.platform ?? process.platform;
@@ -14880,10 +14936,10 @@ async function runOnboard(opts, io = {}) {
14880
14936
  fail(requestedCwd + " is not inside a Git repository. Run `git init` first.");
14881
14937
  return 2;
14882
14938
  }
14883
- const cwd = resolve10(rootText);
14939
+ const cwd = resolve11(rootText);
14884
14940
  let sameRoot = cwd === requestedCwd;
14885
14941
  try {
14886
- sameRoot = realpathSync9(cwd) === realpathSync9(requestedCwd);
14942
+ sameRoot = realpathSync10(cwd) === realpathSync10(requestedCwd);
14887
14943
  } catch {
14888
14944
  }
14889
14945
  if (!sameRoot) {
@@ -15216,8 +15272,8 @@ function runDemo(cwd, head, runners, out3) {
15216
15272
  let worktreeRoot;
15217
15273
  let realTarget;
15218
15274
  try {
15219
- worktreeRoot = realpathSync9(wt);
15220
- realTarget = realpathSync9(targetPath);
15275
+ worktreeRoot = realpathSync10(wt);
15276
+ realTarget = realpathSync10(targetPath);
15221
15277
  } catch {
15222
15278
  out3("Demo skipped: the selected test could not be resolved safely inside the disposable worktree.");
15223
15279
  return;
@@ -15291,7 +15347,7 @@ var init_onboard = __esm({
15291
15347
  });
15292
15348
 
15293
15349
  // src/research/adapter.ts
15294
- import { isAbsolute as isAbsolute12, resolve as resolve11 } from "node:path";
15350
+ import { isAbsolute as isAbsolute12, resolve as resolve12 } from "node:path";
15295
15351
  function taskEnv(task) {
15296
15352
  return {
15297
15353
  TAMPERWARD_RESEARCH_TASK: task.id,
@@ -15307,7 +15363,7 @@ function normalizeCommandArgv(argv, cwd = process.cwd()) {
15307
15363
  throw new ResearchError('the command adapter needs an agent command after "--"');
15308
15364
  }
15309
15365
  const head = argv[0];
15310
- return head.includes("/") && !isAbsolute12(head) ? [resolve11(cwd, head), ...argv.slice(1)] : [...argv];
15366
+ return head.includes("/") && !isAbsolute12(head) ? [resolve12(cwd, head), ...argv.slice(1)] : [...argv];
15311
15367
  }
15312
15368
  function commandAdapter(argv, cwd = process.cwd()) {
15313
15369
  const anchored = normalizeCommandArgv(argv, cwd);
@@ -15408,7 +15464,7 @@ var init_capture = __esm({
15408
15464
  // src/research/manifest.ts
15409
15465
  import { createHash as createHash12 } from "node:crypto";
15410
15466
  import { readFileSync as readFileSync21 } from "node:fs";
15411
- import { dirname as dirname9, resolve as resolve12 } from "node:path";
15467
+ import { dirname as dirname9, resolve as resolve13 } from "node:path";
15412
15468
  function taskFrom(raw, index, manifestDir) {
15413
15469
  const where = `task #${index + 1}`;
15414
15470
  if (!isRecord(raw)) throw new ResearchError(`task manifest: ${where} is not an object`);
@@ -15431,40 +15487,40 @@ function taskFrom(raw, index, manifestDir) {
15431
15487
  const isUrl = /^[a-z][a-z0-9+.-]*:\/\//i.test(repo) || /^[^/]+@[^:]+:/.test(repo);
15432
15488
  return {
15433
15489
  id,
15434
- repo: isUrl ? repo : resolve12(manifestDir, repo),
15490
+ repo: isUrl ? repo : resolve13(manifestDir, repo),
15435
15491
  base: base ?? "HEAD",
15436
15492
  prompt,
15437
15493
  verify: { command: verify.command, ...budget !== void 0 ? { budget } : {} }
15438
15494
  };
15439
15495
  }
15440
15496
  function readManifest(path) {
15441
- const abs2 = resolve12(path);
15497
+ const abs = resolve13(path);
15442
15498
  let bytes;
15443
15499
  try {
15444
- bytes = readFileSync21(abs2);
15500
+ bytes = readFileSync21(abs);
15445
15501
  } catch (e) {
15446
- throw new ResearchError(`cannot read task manifest ${abs2}: ${e instanceof Error ? e.message : String(e)}`);
15502
+ throw new ResearchError(`cannot read task manifest ${abs}: ${e instanceof Error ? e.message : String(e)}`);
15447
15503
  }
15448
15504
  let raw;
15449
15505
  try {
15450
15506
  raw = JSON.parse(bytes.toString("utf8"));
15451
15507
  } catch {
15452
- throw new ResearchError(`task manifest ${abs2} is not valid JSON`);
15508
+ throw new ResearchError(`task manifest ${abs} is not valid JSON`);
15453
15509
  }
15454
- if (!isRecord(raw)) throw new ResearchError(`task manifest ${abs2}: top level must be an object`);
15510
+ if (!isRecord(raw)) throw new ResearchError(`task manifest ${abs}: top level must be an object`);
15455
15511
  if (raw.version !== MANIFEST_VERSION) {
15456
- throw new ResearchError(`task manifest ${abs2}: version must be ${MANIFEST_VERSION} (got ${JSON.stringify(raw.version)})`);
15512
+ throw new ResearchError(`task manifest ${abs}: version must be ${MANIFEST_VERSION} (got ${JSON.stringify(raw.version)})`);
15457
15513
  }
15458
15514
  if (!Array.isArray(raw.tasks) || raw.tasks.length === 0) {
15459
- throw new ResearchError(`task manifest ${abs2}: no tasks (tasks must be a non-empty array)`);
15515
+ throw new ResearchError(`task manifest ${abs}: no tasks (tasks must be a non-empty array)`);
15460
15516
  }
15461
- const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname9(abs2)));
15517
+ const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname9(abs)));
15462
15518
  const seen = /* @__PURE__ */ new Set();
15463
15519
  for (const t of tasks) {
15464
- if (seen.has(t.id)) throw new ResearchError(`task manifest ${abs2}: duplicate task id "${t.id}"`);
15520
+ if (seen.has(t.id)) throw new ResearchError(`task manifest ${abs}: duplicate task id "${t.id}"`);
15465
15521
  seen.add(t.id);
15466
15522
  }
15467
- return { path: abs2, sha256: createHash12("sha256").update(bytes).digest("hex"), tasks };
15523
+ return { path: abs, sha256: createHash12("sha256").update(bytes).digest("hex"), tasks };
15468
15524
  }
15469
15525
  var MANIFEST_VERSION;
15470
15526
  var init_manifest = __esm({
@@ -15653,8 +15709,8 @@ var init_record = __esm({
15653
15709
 
15654
15710
  // src/research/run.ts
15655
15711
  import { execFileSync as execFileSync13 } from "node:child_process";
15656
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync3, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
15657
- import { join as join23, resolve as resolve13 } from "node:path";
15712
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync3, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
15713
+ import { join as join23, resolve as resolve14 } from "node:path";
15658
15714
  function git6(args, cwd) {
15659
15715
  return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
15660
15716
  }
@@ -15717,9 +15773,9 @@ function freshWorkspace(ledger, task, pair, arm, sourceBase) {
15717
15773
  git6(["config", "user.email", "research@tamperward.invalid"], ws2);
15718
15774
  const requestedBase = sourceBase ?? task.base;
15719
15775
  const candidates = sourceBase !== void 0 ? [sourceBase] : task.base === "HEAD" ? ["HEAD"] : [task.base, `origin/${task.base}`];
15720
- for (const rev of candidates) {
15776
+ for (const rev2 of candidates) {
15721
15777
  try {
15722
- git6(["checkout", "-q", "--detach", rev], ws2);
15778
+ git6(["checkout", "-q", "--detach", rev2], ws2);
15723
15779
  return ws2;
15724
15780
  } catch {
15725
15781
  }
@@ -15953,7 +16009,7 @@ function runResearch(opts) {
15953
16009
  err2("tamperward research: the gated arm cannot start here (see `tamperward doctor`); run as a non-root user on Linux.");
15954
16010
  return 2;
15955
16011
  }
15956
- const ledger = resolve13(opts.out);
16012
+ const ledger = resolve14(opts.out);
15957
16013
  const pairs = opts.pairs ?? 1;
15958
16014
  mkdirSync8(join23(ledger, "pairs"), { recursive: true });
15959
16015
  for (const task of tasks) {
@@ -15962,7 +16018,7 @@ function runResearch(opts) {
15962
16018
  try {
15963
16019
  for (let pair = 1; pair <= pairs; pair++) {
15964
16020
  const path = pairRecordPath(ledger, task.id, pair);
15965
- if (!existsSync15(path)) continue;
16021
+ if (!existsSync16(path)) continue;
15966
16022
  const existing = resumableRecord(path, {
15967
16023
  task: task.id,
15968
16024
  pair,
@@ -16088,9 +16144,9 @@ var init_run2 = __esm({
16088
16144
 
16089
16145
  // src/research/summarize.ts
16090
16146
  import { readdirSync as readdirSync7, readFileSync as readFileSync23 } from "node:fs";
16091
- import { join as join24, resolve as resolve14 } from "node:path";
16147
+ import { join as join24, resolve as resolve15 } from "node:path";
16092
16148
  function readLedger2(dir) {
16093
- const pairsDir = join24(resolve14(dir), "pairs");
16149
+ const pairsDir = join24(resolve15(dir), "pairs");
16094
16150
  let names;
16095
16151
  try {
16096
16152
  names = readdirSync7(pairsDir).filter((n) => n.endsWith(".json")).sort();