@codacy/verity-cli 0.35.0-experimental.3211f79 → 0.35.0-experimental.7c5e75b

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.
Files changed (2) hide show
  1. package/bin/verity.js +896 -496
  2. package/package.json +2 -9
package/bin/verity.js CHANGED
@@ -10192,14 +10192,14 @@ var require_public_api = __commonJS2({
10192
10192
  const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
10193
10193
  const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
10194
10194
  const composer$1 = new composer.Composer(options);
10195
- const docs2 = Array.from(composer$1.compose(parser$1.parse(source2)));
10195
+ const docs = Array.from(composer$1.compose(parser$1.parse(source2)));
10196
10196
  if (prettyErrors && lineCounter2)
10197
- for (const doc of docs2) {
10197
+ for (const doc of docs) {
10198
10198
  doc.errors.forEach(errors.prettifyError(source2, lineCounter2));
10199
10199
  doc.warnings.forEach(errors.prettifyError(source2, lineCounter2));
10200
10200
  }
10201
- if (docs2.length > 0)
10202
- return docs2;
10201
+ if (docs.length > 0)
10202
+ return docs;
10203
10203
  return Object.assign([], { empty: true }, composer$1.streamInfo());
10204
10204
  }
10205
10205
  function parseDocument(source2, options = {}) {
@@ -24520,36 +24520,8 @@ function collectRiskSignals(root, opts = {}) {
24520
24520
  return out2.slice(0, opts.limit ?? 10);
24521
24521
  }
24522
24522
 
24523
- // src/lib/repo-profile.ts
24524
- var PROFILE_SCHEMA = "repo-profile/v1";
24525
- var PROFILE_FILE = ".verity/repo-profile.json";
24526
- var LIMITS = {
24527
- languages: 20,
24528
- frameworks: 50,
24529
- dependencies: 300,
24530
- folders: 300,
24531
- commits: 500,
24532
- // The server allows 20; 10 keeps a full profile well under its body cap.
24533
- pathsPerCommit: 10,
24534
- docs: 8,
24535
- // A long doc is cut by section (fitDoc), so these bound how much of the
24536
- // team's conventions reach the writer, not where a doc is cut.
24537
- docBytes: 12288,
24538
- nestedDocBytes: 6144,
24539
- contributorDocBytes: 4096,
24540
- docsTotalBytes: 32768,
24541
- subject: 120,
24542
- path: 200,
24543
- name: 100
24544
- };
24545
- var WINDOW_DAYS = 180;
24546
- var MAX_PROFILE_BYTES = 120 * 1024;
24547
- var ROOT_DOCS = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".github/copilot-instructions.md"];
24548
- var CONTRIBUTOR_DOCS = ["CONTRIBUTING.md", "ARCHITECTURE.md"];
24549
- var README = "README.md";
24550
- var README_BYTES = 3072;
24551
- var NESTED_DOC = /^(?:[^./][^/]*\/){1,4}(?:AGENTS|CLAUDE)\.md$/;
24552
- var NOT_THE_TEAMS = /(?:^|\/)(?:examples?|samples?|templates?|[\w-]*-template|fixtures?|tests?|__tests__|vendor|third_party|node_modules)\//i;
24523
+ // src/lib/profile-text.ts
24524
+ var PATH_MAX = 200;
24553
24525
  var SECRET_PATTERNS = [
24554
24526
  /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g,
24555
24527
  /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
@@ -24576,63 +24548,77 @@ function redact(text2) {
24576
24548
  function oneLine(text2, max) {
24577
24549
  return redact(text2.replace(/[\r\n\t]+/g, " ")).trim().slice(0, max);
24578
24550
  }
24579
- var DEP = /^[A-Za-z0-9@][A-Za-z0-9@._/:+-]*$/;
24580
- function dependencies(root, files) {
24581
- const seen = /* @__PURE__ */ new Set();
24582
- for (const name2 of declaredDependencies(root, files)) {
24583
- if (name2.length <= LIMITS.name && DEP.test(name2)) seen.add(name2);
24584
- if (seen.size >= LIMITS.dependencies) break;
24585
- }
24586
- return [...seen];
24587
- }
24588
24551
  function safePath(p) {
24589
- if (!p || p.length > LIMITS.path || p.startsWith("/") || p.includes("\\")) return null;
24552
+ if (!p || p.length > PATH_MAX || p.startsWith("/") || p.includes("\\")) return null;
24590
24553
  if (p.split("/").some((seg) => seg === "" || seg === "..")) return null;
24591
24554
  if (/[\u0000-\u001f\u007f]/.test(p)) return null;
24592
24555
  return p;
24593
24556
  }
24594
- function tree(files) {
24595
- const counts = /* @__PURE__ */ new Map();
24596
- for (const file of files) {
24597
- const parts2 = file.split("/");
24598
- for (let depth = 1; depth <= Math.min(3, parts2.length - 1); depth++) {
24599
- const folder = parts2.slice(0, depth).join("/");
24600
- counts.set(folder, (counts.get(folder) ?? 0) + 1);
24601
- }
24557
+
24558
+ // src/lib/profile-docs.ts
24559
+ var DOC_LIMITS = {
24560
+ docs: 8,
24561
+ // A long doc is cut by section (fitDoc), so these bound how much of the
24562
+ // team's conventions reach the writer, not where a doc is cut.
24563
+ docBytes: 12288,
24564
+ nestedDocBytes: 6144,
24565
+ contributorDocBytes: 4096,
24566
+ docsTotalBytes: 32768
24567
+ };
24568
+ var ROOT_DOCS = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".github/copilot-instructions.md"];
24569
+ var CONTRIBUTOR_DOCS = ["CONTRIBUTING.md", "ARCHITECTURE.md"];
24570
+ var README = "README.md";
24571
+ var README_BYTES = 3072;
24572
+ var NESTED_DOC = /^(?:[^./][^/]*\/){1,4}(?:AGENTS|CLAUDE)\.md$/;
24573
+ var NOT_THE_TEAMS = /(?:^|\/)(?:examples?|samples?|templates?|[\w-]*-template|fixtures?|tests?|__tests__|vendor|third_party|node_modules)\//i;
24574
+ var MANAGED_START = "<!-- verity-memory:start -->";
24575
+ var MANAGED_END = "<!-- verity-memory:end -->";
24576
+ var PRESERVE_START2 = "<!-- verity-memory:preserve -->";
24577
+ var PRESERVE_END2 = "<!-- /verity-memory:preserve -->";
24578
+ var FENCE_OPEN = /^[ \t]*(?:(?:[-*+]|\d+[.)])[ \t]+)*(`{3,}|~{3,})/;
24579
+ var FENCE_CLOSE = /^[ \t]*(`{3,}|~{3,})[ \t]*$/;
24580
+ var QUOTE = /^(?:[ \t]*>)+ ?/;
24581
+ var LIST_ITEM = /^[ \t]*(?:[-*+]|\d+[.)])[ \t]/;
24582
+ function indentWidth(line2) {
24583
+ let w = 0;
24584
+ for (const ch of line2) {
24585
+ if (ch === " ") w++;
24586
+ else if (ch === " ") w += 4;
24587
+ else break;
24602
24588
  }
24603
- return [...counts.entries()].filter(([path]) => safePath(path) !== null).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, LIMITS.folders).map(([path, n2]) => ({ path, files: n2 })).sort((a, b) => a.path.localeCompare(b.path));
24589
+ return w;
24604
24590
  }
24605
- function history(root) {
24606
- const res = runGitSync(root, [
24607
- "log",
24608
- `--since=${WINDOW_DAYS} days ago`,
24609
- "-n",
24610
- String(LIMITS.commits),
24611
- "--no-merges",
24612
- "--format=@@%x1f%s",
24613
- "--name-only",
24614
- "--"
24615
- ], { timeoutMs: 3e4 });
24616
- const commits = [];
24617
- if (!res.ok) return { window_days: WINDOW_DAYS, commits };
24618
- let current = null;
24619
- for (const line2 of res.stdout.split("\n")) {
24620
- if (line2.startsWith("@@")) {
24621
- if (current && current.subject) commits.push(current);
24622
- const raw = line2.slice(3);
24623
- current = { subject: oneLine(raw, LIMITS.subject), paths: [], fix: FIX_SUBJECT.test(raw) };
24591
+ function stripCode(text2) {
24592
+ const out2 = [];
24593
+ let fence = null;
24594
+ let underItem = false;
24595
+ for (const line2 of text2.split("\n")) {
24596
+ const body2 = line2.replace(QUOTE, "");
24597
+ if (fence) {
24598
+ const close = FENCE_CLOSE.exec(body2);
24599
+ if (close && close[1][0] === fence[0] && close[1].length >= fence.length) fence = null;
24624
24600
  continue;
24625
24601
  }
24626
- const path = safePath(line2.trim());
24627
- if (current && path && current.paths.length < LIMITS.pathsPerCommit) current.paths.push(path);
24602
+ const open = FENCE_OPEN.exec(body2);
24603
+ if (open) {
24604
+ fence = open[1];
24605
+ underItem = false;
24606
+ continue;
24607
+ }
24608
+ if (body2.trim() === "") {
24609
+ out2.push(line2);
24610
+ underItem = false;
24611
+ continue;
24612
+ }
24613
+ if (indentWidth(body2) >= 4) {
24614
+ if (underItem && LIST_ITEM.test(body2)) out2.push(line2);
24615
+ continue;
24616
+ }
24617
+ out2.push(line2);
24618
+ underItem = LIST_ITEM.test(body2);
24628
24619
  }
24629
- if (current && current.subject) commits.push(current);
24630
- return { window_days: WINDOW_DAYS, commits: commits.slice(0, LIMITS.commits) };
24620
+ return out2.join("\n");
24631
24621
  }
24632
- var MANAGED_START = "<!-- verity-memory:start -->";
24633
- var MANAGED_END = "<!-- verity-memory:end -->";
24634
- var PRESERVE_START2 = "<!-- verity-memory:preserve -->";
24635
- var PRESERVE_END2 = "<!-- /verity-memory:preserve -->";
24636
24622
  function docText(raw) {
24637
24623
  let text2 = raw;
24638
24624
  const start2 = text2.indexOf(MANAGED_START);
@@ -24644,7 +24630,7 @@ function docText(raw) {
24644
24630
  const kept = ps >= 0 && pe > ps ? block.slice(ps + PRESERVE_START2.length, pe) : "";
24645
24631
  text2 = text2.slice(0, start2) + kept + text2.slice(end + MANAGED_END.length);
24646
24632
  }
24647
- text2 = text2.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, "").replace(/<!--[\s\S]*?-->/g, "").replace(/\n{3,}/g, "\n\n");
24633
+ text2 = stripCode(text2.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*$/, "")).replace(/\n{3,}/g, "\n\n");
24648
24634
  return redact(text2).trim();
24649
24635
  }
24650
24636
  var RULE_TITLE = /convention|pattern|rule|gotcha|footgun|pitfall|best practice|guideline|principle|standard|style|architecture|design|security|database|migration|schema|\bapi\b|error|state|naming|structure|i18n|internationali[sz]|translation|important|critical|warning|do not|don't|never|must|avoid/i;
@@ -24677,18 +24663,23 @@ function sectionRank(s) {
24677
24663
  return title * 1e3 + Math.min(999, s.lines.filter((l) => NORMATIVE.test(l)).length);
24678
24664
  }
24679
24665
  function fitDoc(text2, budget) {
24680
- if (text2.length <= budget) return text2;
24666
+ if (utf8(text2) <= budget) return text2;
24681
24667
  const all = docSections(text2);
24682
- const size = (heading, lines) => (heading ? heading.length + 1 : 0) + lines.join("\n").length + 1;
24668
+ const lineBytes = (line2) => utf8(line2) + 1;
24669
+ const GAP = lineBytes("\u2026");
24683
24670
  const chosen = /* @__PURE__ */ new Map();
24671
+ const headingsCounted = /* @__PURE__ */ new Set();
24684
24672
  let used2 = 0;
24685
24673
  for (const s of [...all].sort((a, b) => sectionRank(b) - sectionRank(a) || a.order - b.order)) {
24686
24674
  if (!s.lines.some((l) => l.trim())) continue;
24675
+ const parents = s.parents.filter((p) => !headingsCounted.has(p) && !chosen.has(p));
24676
+ const parentBytes = parents.reduce((n2, p) => n2 + lineBytes(all[p].heading ?? ""), 0);
24687
24677
  const lean = s.lines.filter((l) => NORMATIVE.test(l));
24688
24678
  for (const lines of [s.lines, lean]) {
24689
- const n2 = size(s.heading, lines);
24679
+ const n2 = (s.heading ? lineBytes(s.heading) : 0) + lines.reduce((m, l) => m + lineBytes(l), 0) + parentBytes + GAP;
24690
24680
  if (lines.length && used2 + n2 <= budget) {
24691
24681
  chosen.set(s.order, lines);
24682
+ for (const p of parents) headingsCounted.add(p);
24692
24683
  used2 += n2;
24693
24684
  break;
24694
24685
  }
@@ -24712,23 +24703,38 @@ function fitDoc(text2, budget) {
24712
24703
  if (s.heading) out2.push(s.heading);
24713
24704
  out2.push(...lines);
24714
24705
  }
24715
- return out2.join("\n").replace(/\n{3,}/g, "\n\n").trim().slice(0, budget);
24706
+ return cutToBytes(out2.join("\n").replace(/\n{3,}/g, "\n\n").trim(), budget);
24707
+ }
24708
+ function utf8(text2) {
24709
+ return Buffer.byteLength(text2, "utf8");
24710
+ }
24711
+ function cutToBytes(text2, max) {
24712
+ if (utf8(text2) <= max) return text2;
24713
+ let out2 = "";
24714
+ let n2 = 0;
24715
+ for (const ch of text2) {
24716
+ const b = utf8(ch);
24717
+ if (n2 + b > max) break;
24718
+ out2 += ch;
24719
+ n2 += b;
24720
+ }
24721
+ return out2;
24716
24722
  }
24717
24723
  var normal = (line2) => line2.trim().toLowerCase().replace(/\s+/g, " ");
24718
- function docs(root, files) {
24724
+ function readDocs(root, files) {
24719
24725
  const nested = files.filter((f) => NESTED_DOC.test(f) && !NOT_THE_TEAMS.test(f) && safePath(f) !== null).sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
24720
24726
  const plan = [
24721
- ...ROOT_DOCS.map((n2) => [n2, LIMITS.docBytes]),
24722
- ...nested.map((n2) => [n2, LIMITS.nestedDocBytes]),
24723
- ...CONTRIBUTOR_DOCS.map((n2) => [n2, LIMITS.contributorDocBytes]),
24727
+ ...ROOT_DOCS.map((n2) => [n2, DOC_LIMITS.docBytes]),
24728
+ ...nested.map((n2) => [n2, DOC_LIMITS.nestedDocBytes]),
24729
+ ...CONTRIBUTOR_DOCS.map((n2) => [n2, DOC_LIMITS.contributorDocBytes]),
24724
24730
  [README, README_BYTES]
24725
24731
  ];
24726
24732
  const out2 = [];
24727
24733
  const sent = /* @__PURE__ */ new Set();
24728
24734
  let total = 0;
24729
24735
  for (const [name2, cap2] of plan) {
24730
- const room = Math.min(cap2, LIMITS.docsTotalBytes - total);
24731
- if (out2.length >= LIMITS.docs || room < 200) break;
24736
+ const room = Math.min(cap2, DOC_LIMITS.docsTotalBytes - total);
24737
+ if (out2.length >= DOC_LIMITS.docs || room < 200) break;
24732
24738
  if (name2 === README && out2.some((d) => ROOT_DOCS.includes(d.name))) continue;
24733
24739
  const read2 = readRegularFileInRoot(root, name2, 256 * 1024);
24734
24740
  if (!read2.ok) continue;
@@ -24736,13 +24742,78 @@ function docs(root, files) {
24736
24742
  const text2 = fitDoc(fresh, room);
24737
24743
  if (!text2) continue;
24738
24744
  for (const l of text2.split("\n")) if (normal(l).length >= 25) sent.add(normal(l));
24739
- total += text2.length;
24745
+ total += utf8(text2);
24740
24746
  out2.push({ name: name2, text: text2 });
24741
24747
  }
24742
24748
  return out2;
24743
24749
  }
24750
+
24751
+ // src/lib/repo-profile.ts
24752
+ var PROFILE_SCHEMA = "repo-profile/v1";
24753
+ var PROFILE_FILE = ".verity/repo-profile.json";
24754
+ var LIMITS = {
24755
+ languages: 20,
24756
+ frameworks: 50,
24757
+ dependencies: 300,
24758
+ folders: 300,
24759
+ commits: 500,
24760
+ // The server allows 20; 10 keeps a full profile well under its body cap.
24761
+ pathsPerCommit: 10,
24762
+ subject: 120,
24763
+ path: 200,
24764
+ name: 100
24765
+ };
24766
+ var WINDOW_DAYS = 180;
24767
+ var MAX_PROFILE_BYTES = 120 * 1024;
24768
+ var DEP = /^[A-Za-z0-9@][A-Za-z0-9@._/:+-]*$/;
24769
+ function dependencies(root, files) {
24770
+ const seen = /* @__PURE__ */ new Set();
24771
+ for (const name2 of declaredDependencies(root, files)) {
24772
+ if (name2.length <= LIMITS.name && DEP.test(name2)) seen.add(name2);
24773
+ if (seen.size >= LIMITS.dependencies) break;
24774
+ }
24775
+ return [...seen];
24776
+ }
24777
+ function tree(files) {
24778
+ const counts = /* @__PURE__ */ new Map();
24779
+ for (const file of files) {
24780
+ const parts2 = file.split("/");
24781
+ for (let depth = 1; depth <= Math.min(3, parts2.length - 1); depth++) {
24782
+ const folder = parts2.slice(0, depth).join("/");
24783
+ counts.set(folder, (counts.get(folder) ?? 0) + 1);
24784
+ }
24785
+ }
24786
+ return [...counts.entries()].filter(([path]) => safePath(path) !== null).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, LIMITS.folders).map(([path, n2]) => ({ path, files: n2 })).sort((a, b) => a.path.localeCompare(b.path));
24787
+ }
24788
+ function history(root) {
24789
+ const res = runGitSync(root, [
24790
+ "log",
24791
+ `--since=${WINDOW_DAYS} days ago`,
24792
+ "-n",
24793
+ String(LIMITS.commits),
24794
+ "--no-merges",
24795
+ "--format=@@%x1f%s",
24796
+ "--name-only",
24797
+ "--"
24798
+ ], { timeoutMs: 3e4 });
24799
+ const commits = [];
24800
+ if (!res.ok) return { window_days: WINDOW_DAYS, commits };
24801
+ let current = null;
24802
+ for (const line2 of res.stdout.split("\n")) {
24803
+ if (line2.startsWith("@@")) {
24804
+ if (current && current.subject) commits.push(current);
24805
+ const raw = line2.slice(3);
24806
+ current = { subject: oneLine(raw, LIMITS.subject), paths: [], fix: FIX_SUBJECT.test(raw) };
24807
+ continue;
24808
+ }
24809
+ const path = safePath(line2.trim());
24810
+ if (current && path && current.paths.length < LIMITS.pathsPerCommit) current.paths.push(path);
24811
+ }
24812
+ if (current && current.subject) commits.push(current);
24813
+ return { window_days: WINDOW_DAYS, commits: commits.slice(0, LIMITS.commits) };
24814
+ }
24744
24815
  function fitProfile(p) {
24745
- const size = () => JSON.stringify(p).length;
24816
+ const size = () => Buffer.byteLength(JSON.stringify(p), "utf8");
24746
24817
  if (size() <= MAX_PROFILE_BYTES) return p;
24747
24818
  const commits = p.history.commits;
24748
24819
  for (const wantFix of [false, true]) {
@@ -24750,6 +24821,9 @@ function fitProfile(p) {
24750
24821
  if (commits[i2].fix === wantFix) commits.splice(i2, 1);
24751
24822
  }
24752
24823
  }
24824
+ while (size() > MAX_PROFILE_BYTES && p.docs.length > 0) p.docs.pop();
24825
+ while (size() > MAX_PROFILE_BYTES && p.tree.length > 0) p.tree.pop();
24826
+ while (size() > MAX_PROFILE_BYTES && p.dependencies.length > 0) p.dependencies.pop();
24753
24827
  return p;
24754
24828
  }
24755
24829
  function buildRepoProfile(root = repoRoot()) {
@@ -24771,7 +24845,7 @@ function buildRepoProfile(root = repoRoot()) {
24771
24845
  dependencies: dependencies(root, files),
24772
24846
  tree: tree(files),
24773
24847
  history: history(root),
24774
- docs: docs(root, files)
24848
+ docs: readDocs(root, files)
24775
24849
  });
24776
24850
  }
24777
24851
  function describeProfile(p) {
@@ -25163,11 +25237,11 @@ async function currentReviewMoments(pluginMode) {
25163
25237
  const cfg = readProjectConfig();
25164
25238
  const hooks = await checkAllVerityHooks();
25165
25239
  const turn = cfg.turn_review !== "off" && (pluginMode || hooks.stop);
25166
- const git2 = pluginMode ? cfg.git_moments : hooks.guard ? hooks.guardOn.length > 0 ? hooks.guardOn : cfg.git_moments : [];
25240
+ const git3 = pluginMode ? cfg.git_moments : hooks.guard ? hooks.guardOn.length > 0 ? hooks.guardOn : cfg.git_moments : [];
25167
25241
  return [
25168
25242
  ...turn ? ["stop"] : [],
25169
- ...git2.includes("commit") ? ["pre-commit"] : [],
25170
- ...git2.includes("push") ? ["pre-push"] : []
25243
+ ...git3.includes("commit") ? ["pre-commit"] : [],
25244
+ ...git3.includes("push") ? ["pre-push"] : []
25171
25245
  ];
25172
25246
  }
25173
25247
  async function setReviewMoments(moments, pluginMode) {
@@ -26120,7 +26194,7 @@ function createRun(opts, globals) {
26120
26194
 
26121
26195
  // src/commands/analyze/index.ts
26122
26196
  var import_node_fs55 = require("node:fs");
26123
- var import_node_path42 = require("node:path");
26197
+ var import_node_path43 = require("node:path");
26124
26198
 
26125
26199
  // src/lib/repo-context.ts
26126
26200
  var import_node_child_process11 = require("node:child_process");
@@ -28244,7 +28318,7 @@ function channelSilence(input) {
28244
28318
  // src/lib/cli-version.ts
28245
28319
  function cliVersion() {
28246
28320
  try {
28247
- return true ? "0.35.0-experimental.3211f79" : "dev";
28321
+ return true ? "0.35.0-experimental.7c5e75b" : "dev";
28248
28322
  } catch {
28249
28323
  return "dev";
28250
28324
  }
@@ -28285,6 +28359,8 @@ async function sendSkipBeacon(ctx, reason) {
28285
28359
 
28286
28360
  // src/lib/static-analysis.ts
28287
28361
  var import_node_child_process12 = require("node:child_process");
28362
+ var import_node_module = require("node:module");
28363
+ var import_node_path34 = require("node:path");
28288
28364
  var import_node_fs44 = require("node:fs");
28289
28365
 
28290
28366
  // src/lib/static-config.ts
@@ -28331,19 +28407,44 @@ var SEVERITY_ORDER = {
28331
28407
  Low: 3
28332
28408
  };
28333
28409
  function isCodacyAvailable() {
28334
- return codacyAnalysisPath() !== null;
28410
+ return resolveAnalyzer() !== null;
28411
+ }
28412
+ var NO_BUNDLED_ANALYZER_ENV = "VERITY_NO_BUNDLED_ANALYZER";
28413
+ function bundledAnalyzer(from = __filename) {
28414
+ try {
28415
+ const pkgPath = (0, import_node_module.createRequire)(from).resolve("@codacy/analysis-cli/package.json");
28416
+ const pkg = JSON.parse((0, import_node_fs44.readFileSync)(pkgPath, "utf-8"));
28417
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["codacy-analysis"];
28418
+ if (typeof bin !== "string" || bin === "") return null;
28419
+ const entry = (0, import_node_path34.join)((0, import_node_path34.dirname)(pkgPath), bin);
28420
+ if (!(0, import_node_fs44.statSync)(entry).isFile()) return null;
28421
+ return { path: entry, version: typeof pkg.version === "string" ? pkg.version : "unknown" };
28422
+ } catch {
28423
+ return null;
28424
+ }
28335
28425
  }
28336
- function codacyAnalysisPath() {
28337
- return whichSync("codacy-analysis");
28426
+ function resolveAnalyzer(opts = {}) {
28427
+ const env = opts.env ?? process.env;
28428
+ if (env[NO_BUNDLED_ANALYZER_ENV] !== "1") {
28429
+ const bundled = (opts.bundled ?? bundledAnalyzer)();
28430
+ if (bundled) return { source: "bundled", path: bundled.path, version: bundled.version };
28431
+ }
28432
+ const onPath = (opts.onPath ?? (() => whichSync("codacy-analysis")))();
28433
+ return onPath ? { source: "path", path: onPath } : null;
28434
+ }
28435
+ function analyzerCommand(analyzer, argv, execPath = process.execPath) {
28436
+ if (analyzer.source === "bundled") return { command: execPath, args: [analyzer.path, ...argv] };
28437
+ return { command: analyzer.path, args: argv };
28338
28438
  }
28339
28439
  function startTrivyPrewarm(opts = {}) {
28340
28440
  if ((opts.platform ?? process.platform) === "win32") return false;
28341
28441
  if ((opts.env ?? process.env).CI) return false;
28342
- const analyzer = codacyAnalysisPath();
28442
+ const analyzer = (opts.resolve ?? resolveAnalyzer)();
28343
28443
  const config = ensureStaticConfig();
28344
28444
  if (!analyzer || !config) return false;
28345
28445
  try {
28346
- const child = (opts.run ?? import_node_child_process12.spawn)(analyzer, buildAnalyzerArgv([STATIC_ANALYSIS_CONFIG_FILE], config), {
28446
+ const { command, args: args2 } = analyzerCommand(analyzer, buildAnalyzerArgv([STATIC_ANALYSIS_CONFIG_FILE], config));
28447
+ const child = (opts.run ?? import_node_child_process12.spawn)(command, args2, {
28347
28448
  cwd: repoRoot(),
28348
28449
  detached: true,
28349
28450
  stdio: "ignore"
@@ -28390,7 +28491,7 @@ function runCodacyAnalysisIfAvailable(files) {
28390
28491
  if (!isCodacyAvailable()) {
28391
28492
  return withFailure(
28392
28493
  "analyzer_unavailable",
28393
- "@codacy/analysis-cli was not found on PATH \u2014 no static analysis ran"
28494
+ "@codacy/analysis-cli was not bundled with this Verity install and was not found on PATH \u2014 no static analysis ran. Reinstall Verity: npm install -g @codacy/verity-cli"
28394
28495
  );
28395
28496
  }
28396
28497
  return runCodacyAnalysis(files);
@@ -28413,8 +28514,10 @@ function runCodacyAnalysis(files) {
28413
28514
  "Could not write .verity/static-analysis.json \u2014 no static analysis ran"
28414
28515
  );
28415
28516
  }
28416
- const analyzer = codacyAnalysisPath() ?? "codacy-analysis";
28417
- const proc = (0, import_node_child_process12.spawnSync)(analyzer, buildAnalyzerArgv(existingFiles, configPath), {
28517
+ const argv = buildAnalyzerArgv(existingFiles, configPath);
28518
+ const resolved = resolveAnalyzer();
28519
+ const { command, args: args2 } = resolved ? analyzerCommand(resolved, argv) : { command: "codacy-analysis", args: argv };
28520
+ const proc = (0, import_node_child_process12.spawnSync)(command, args2, {
28418
28521
  encoding: "utf-8",
28419
28522
  maxBuffer: 10 * 1024 * 1024
28420
28523
  });
@@ -28678,7 +28781,7 @@ async function scope(run4) {
28678
28781
 
28679
28782
  // src/lib/specs.ts
28680
28783
  var import_node_fs45 = require("node:fs");
28681
- var import_node_path34 = require("node:path");
28784
+ var import_node_path35 = require("node:path");
28682
28785
  var SPEC_CANDIDATES = [
28683
28786
  "CLAUDE.md",
28684
28787
  "AGENTS.md",
@@ -28700,7 +28803,7 @@ var UNCONSULTED_TOTAL_BYTES = 30720;
28700
28803
  function readSpecFiles(specsOpt, root) {
28701
28804
  const out2 = [];
28702
28805
  for (const raw of specsOpt.split(",").map((f) => f.trim()).filter(Boolean)) {
28703
- const candidate = (0, import_node_path34.isAbsolute)(raw) ? (0, import_node_path34.relative)(root, raw) : raw;
28806
+ const candidate = (0, import_node_path35.isAbsolute)(raw) ? (0, import_node_path35.relative)(root, raw) : raw;
28704
28807
  const content = readFileInside(root, candidate, MAX_EXPLICIT_SPEC_FILE_BYTES);
28705
28808
  if (content === null) continue;
28706
28809
  out2.push({ path: raw, content });
@@ -28759,7 +28862,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
28759
28862
  try {
28760
28863
  const entries = (0, import_node_fs45.readdirSync)(dir, { withFileTypes: true });
28761
28864
  for (const entry of entries) {
28762
- const fullPath = (0, import_node_path34.join)(dir, entry.name);
28865
+ const fullPath = (0, import_node_path35.join)(dir, entry.name);
28763
28866
  if (entry.isFile() && entry.name.endsWith(".md")) {
28764
28867
  result.push(fullPath);
28765
28868
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -28772,10 +28875,10 @@ function findMdFiles(dir, maxDepth, depth = 0) {
28772
28875
  }
28773
28876
  function discoverPlans() {
28774
28877
  const home = process.env.HOME ?? "";
28775
- const homePlansDir = (0, import_node_path34.join)(home, ".claude", "plans");
28878
+ const homePlansDir = (0, import_node_path35.join)(home, ".claude", "plans");
28776
28879
  const sources = [];
28777
28880
  if (isRealDirectoryChain(process.cwd(), [".claude", "plans"])) {
28778
- sources.push({ root: process.cwd(), prefix: (0, import_node_path34.join)(".claude", "plans") });
28881
+ sources.push({ root: process.cwd(), prefix: (0, import_node_path35.join)(".claude", "plans") });
28779
28882
  }
28780
28883
  if (home && (0, import_node_fs45.existsSync)(homePlansDir)) sources.push({ root: homePlansDir, prefix: "" });
28781
28884
  const candidates2 = [];
@@ -28783,14 +28886,14 @@ function discoverPlans() {
28783
28886
  for (const source2 of sources) {
28784
28887
  let names2;
28785
28888
  try {
28786
- names2 = (0, import_node_fs45.readdirSync)(source2.prefix ? (0, import_node_path34.join)(source2.root, source2.prefix) : source2.root);
28889
+ names2 = (0, import_node_fs45.readdirSync)(source2.prefix ? (0, import_node_path35.join)(source2.root, source2.prefix) : source2.root);
28787
28890
  } catch {
28788
28891
  continue;
28789
28892
  }
28790
28893
  for (const f of names2) {
28791
28894
  if (!f.endsWith(".md") || seen.has(f)) continue;
28792
28895
  seen.add(f);
28793
- const relPath = source2.prefix ? (0, import_node_path34.join)(source2.prefix, f) : f;
28896
+ const relPath = source2.prefix ? (0, import_node_path35.join)(source2.prefix, f) : f;
28794
28897
  const stat3 = statRegularInRoot(source2.root, relPath);
28795
28898
  if (!stat3.ok) continue;
28796
28899
  candidates2.push({ name: f, root: source2.root, relPath, mtime: stat3.mtimeMs, size: stat3.size });
@@ -28808,7 +28911,7 @@ function discoverPlans() {
28808
28911
  function isRealDirectoryChain(root, parts2) {
28809
28912
  let current = root;
28810
28913
  for (const part of parts2) {
28811
- current = (0, import_node_path34.join)(current, part);
28914
+ current = (0, import_node_path35.join)(current, part);
28812
28915
  try {
28813
28916
  if (!(0, import_node_fs45.lstatSync)(current).isDirectory()) return false;
28814
28917
  } catch {
@@ -29012,7 +29115,7 @@ async function mode(run4) {
29012
29115
 
29013
29116
  // src/lib/fold.ts
29014
29117
  var import_node_fs46 = require("node:fs");
29015
- var import_node_path35 = require("node:path");
29118
+ var import_node_path36 = require("node:path");
29016
29119
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
29017
29120
  "user",
29018
29121
  "assistant",
@@ -29244,9 +29347,9 @@ function fold(transcriptPath, opts = {}) {
29244
29347
  return result;
29245
29348
  }
29246
29349
  try {
29247
- const sidecarDir = (0, import_node_path35.join)(
29248
- (0, import_node_path35.dirname)(transcriptPath),
29249
- (0, import_node_path35.basename)(transcriptPath).replace(/\.jsonl$/, ""),
29350
+ const sidecarDir = (0, import_node_path36.join)(
29351
+ (0, import_node_path36.dirname)(transcriptPath),
29352
+ (0, import_node_path36.basename)(transcriptPath).replace(/\.jsonl$/, ""),
29250
29353
  "subagents"
29251
29354
  );
29252
29355
  if ((0, import_node_fs46.existsSync)(sidecarDir)) {
@@ -29256,7 +29359,7 @@ function fold(transcriptPath, opts = {}) {
29256
29359
  const walk2 = (d, depth) => {
29257
29360
  if (depth > 4) return;
29258
29361
  for (const e of (0, import_node_fs46.readdirSync)(d, { withFileTypes: true })) {
29259
- const p = (0, import_node_path35.join)(d, e.name);
29362
+ const p = (0, import_node_path36.join)(d, e.name);
29260
29363
  if (e.isDirectory()) {
29261
29364
  walk2(p, depth + 1);
29262
29365
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
@@ -29629,7 +29732,7 @@ async function evidence(run4) {
29629
29732
 
29630
29733
  // src/lib/cache-cleanup.ts
29631
29734
  var import_node_fs47 = require("node:fs");
29632
- var import_node_path36 = require("node:path");
29735
+ var import_node_path37 = require("node:path");
29633
29736
  var CACHE_TTL_DAYS = 7;
29634
29737
  function pruneStaleCache() {
29635
29738
  try {
@@ -29637,7 +29740,7 @@ function pruneStaleCache() {
29637
29740
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
29638
29741
  for (const entry of (0, import_node_fs47.readdirSync)(dir)) {
29639
29742
  if (!entry.startsWith("pending-")) continue;
29640
- const path = (0, import_node_path36.join)(dir, entry);
29743
+ const path = (0, import_node_path37.join)(dir, entry);
29641
29744
  try {
29642
29745
  const stat3 = (0, import_node_fs47.statSync)(path);
29643
29746
  if (stat3.mtimeMs < cutoff) {
@@ -29872,7 +29975,7 @@ function languageOf2(path) {
29872
29975
 
29873
29976
  // src/lib/code/runtime.ts
29874
29977
  var import_node_fs49 = require("node:fs");
29875
- var import_node_path37 = require("node:path");
29978
+ var import_node_path38 = require("node:path");
29876
29979
  var import_web_tree_sitter2 = __toESM2(require_web_tree_sitter2());
29877
29980
 
29878
29981
  // src/lib/code/grammars.json
@@ -29932,12 +30035,12 @@ var grammars_default = {
29932
30035
  var MAX_PARSE_BYTES = 512 * 1024;
29933
30036
  function grammarCandidates(file, pkg, env = process.env) {
29934
30037
  return [
29935
- ...env.VERITY_GRAMMAR_DIR ? [(0, import_node_path37.join)(env.VERITY_GRAMMAR_DIR, file)] : [],
29936
- (0, import_node_path37.join)(__dirname, "..", "data", "grammars", file),
30038
+ ...env.VERITY_GRAMMAR_DIR ? [(0, import_node_path38.join)(env.VERITY_GRAMMAR_DIR, file)] : [],
30039
+ (0, import_node_path38.join)(__dirname, "..", "data", "grammars", file),
29937
30040
  // bundled: cli/bin/ → cli/data/grammars/
29938
- (0, import_node_path37.join)(__dirname, "..", "..", "..", "data", "grammars", file),
30041
+ (0, import_node_path38.join)(__dirname, "..", "..", "..", "data", "grammars", file),
29939
30042
  // tsx: cli/src/lib/code/ → cli/data/grammars/
29940
- (0, import_node_path37.join)(__dirname, "..", "..", "..", "node_modules", pkg, file)
30043
+ (0, import_node_path38.join)(__dirname, "..", "..", "..", "node_modules", pkg, file)
29941
30044
  // tsx, before a build: the pinned devDependency
29942
30045
  ];
29943
30046
  }
@@ -30673,14 +30776,14 @@ function accessMap(input) {
30673
30776
  const targetWrites = (r) => effects.some((e) => e.op === "write" && sameResource(r, e.resource));
30674
30777
  const excluded = /* @__PURE__ */ new Set([target, ...helpers, ...fileFns.filter((f) => inside(f, target))]);
30675
30778
  const accessors = [];
30676
- const consider = (fns, p, sameFile, fileState) => {
30779
+ const consider = (fns, p, sameFile2, fileState) => {
30677
30780
  for (const f of fns) {
30678
- if (sameFile && excluded.has(f)) continue;
30679
- if (sameFile && inside(target, f)) continue;
30680
- const theirs = effectsWithHelpers(f, fns, lang, 2, fileState).filter((e) => !(sameFile && e.via.includes(target.name)) && searchable.some((r) => sameResource(r, e.resource) && (e.op === "write" || targetWrites(r))));
30781
+ if (sameFile2 && excluded.has(f)) continue;
30782
+ if (sameFile2 && inside(target, f)) continue;
30783
+ const theirs = effectsWithHelpers(f, fns, lang, 2, fileState).filter((e) => !(sameFile2 && e.via.includes(target.name)) && searchable.some((r) => sameResource(r, e.resource) && (e.op === "write" || targetWrites(r))));
30681
30784
  if (theirs.length === 0) continue;
30682
- if (sameFile && [...helperNames].includes(f.name)) continue;
30683
- accessors.push({ fn: f, effects: theirs, writes: theirs.some((e) => e.op === "write"), sameFile });
30785
+ if (sameFile2 && [...helperNames].includes(f.name)) continue;
30786
+ accessors.push({ fn: f, effects: theirs, writes: theirs.some((e) => e.op === "write"), sameFile: sameFile2 });
30684
30787
  }
30685
30788
  };
30686
30789
  consider(fileFns, path, true, state);
@@ -31070,7 +31173,7 @@ var MAX_CALLERS = 30;
31070
31173
  var MAX_READ_BYTES4 = 512 * 1024;
31071
31174
  var IDENT = /^[A-Za-z_$][\w$]*$/;
31072
31175
  var TEST_OR_TOOLING = /(^|\/)(tests?|__tests__|spec|specs|fixtures?|e2e|testdata|experiments|scripts)\/|\.(test|spec)\.[cm]?[jt]sx?$|_test\.(py|go)$|(^|\/)test_[^/]+\.py$/;
31073
- var dirname18 = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "";
31176
+ var dirname19 = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "";
31074
31177
  function joinPath(dir, rel) {
31075
31178
  const out2 = dir ? dir.split("/") : [];
31076
31179
  for (const part of rel.split("/")) {
@@ -31195,7 +31298,7 @@ function parseJsonc(text2) {
31195
31298
  return JSON.parse(out2.replace(/,(\s*[}\]])/g, "$1"));
31196
31299
  }
31197
31300
  function tsconfigFor(source2, fromPath, cache) {
31198
- for (let dir = dirname18(fromPath); ; dir = dirname18(dir)) {
31301
+ for (let dir = dirname19(fromPath); ; dir = dirname19(dir)) {
31199
31302
  const file = dir ? `${dir}/tsconfig.json` : "tsconfig.json";
31200
31303
  if (!cache.has(file)) {
31201
31304
  const read2 = source2.read(file, 256 * 1024);
@@ -31219,7 +31322,7 @@ function tsconfigFor(source2, fromPath, cache) {
31219
31322
  function tsCandidates(spec, fromPath, cfg) {
31220
31323
  const bases = [];
31221
31324
  if (spec.startsWith(".")) {
31222
- const b = joinPath(dirname18(fromPath), spec);
31325
+ const b = joinPath(dirname19(fromPath), spec);
31223
31326
  if (b !== null) bases.push(b);
31224
31327
  } else if (cfg) {
31225
31328
  const root = cfg.baseUrl !== null ? joinPath(cfg.dir, cfg.baseUrl) : cfg.dir;
@@ -31251,8 +31354,8 @@ function pyCandidates(spec, fromPath) {
31251
31354
  const mod = spec.slice(dots).replace(/\./g, "/");
31252
31355
  const out2 = [];
31253
31356
  if (dots > 0) {
31254
- let dir = dirname18(fromPath);
31255
- for (let i2 = 1; i2 < dots; i2++) dir = dirname18(dir);
31357
+ let dir = dirname19(fromPath);
31358
+ for (let i2 = 1; i2 < dots; i2++) dir = dirname19(dir);
31256
31359
  const b = mod ? joinPath(dir, mod) : dir;
31257
31360
  if (b !== null) out2.push(`${b}.py`, `${b}/__init__.py`);
31258
31361
  } else {
@@ -31279,7 +31382,7 @@ function moduleFiles(source2, spec, fromPath, lang, cache = /* @__PURE__ */ new
31279
31382
  const module2 = mod.ok ? /^\s*module\s+(\S+)/m.exec(mod.content)?.[1] : void 0;
31280
31383
  if (!module2 || !(spec === module2 || spec.startsWith(`${module2}/`))) return [];
31281
31384
  const dir = spec.slice(module2.length + 1);
31282
- return source2.list(dir || void 0).filter((p) => dirname18(p) === dir && p.endsWith(".go") && !p.endsWith("_test.go"));
31385
+ return source2.list(dir || void 0).filter((p) => dirname19(p) === dir && p.endsWith(".go") && !p.endsWith("_test.go"));
31283
31386
  }
31284
31387
  if (lang === "java") {
31285
31388
  const suffix = `${spec.replace(/\./g, "/")}.java`;
@@ -31330,7 +31433,7 @@ function definitionOf(input) {
31330
31433
  if (type?.startsWith(RESULT_OF)) {
31331
31434
  const [fnName, idx] = type.slice(RESULT_OF.length).split("#");
31332
31435
  type = null;
31333
- const others = lang === "go" ? source2.list(dirname18(path) || void 0).filter((p) => dirname18(p) === dirname18(path) && p.endsWith(".go") && p !== path) : (() => {
31436
+ const others = lang === "go" ? source2.list(dirname19(path) || void 0).filter((p) => dirname19(p) === dirname19(path) && p.endsWith(".go") && p !== path) : (() => {
31334
31437
  const imp = importOf(content, lang, fnName, null);
31335
31438
  return imp ? moduleFiles(source2, imp.spec, path, lang, tsCache) : [];
31336
31439
  })();
@@ -31377,7 +31480,7 @@ function definitionOf(input) {
31377
31480
  }
31378
31481
  }
31379
31482
  if (out2.length === 0 && lang === "go" && !receiver) {
31380
- for (const f of source2.list(dirname18(path) || void 0).filter((p) => dirname18(p) === dirname18(path) && p.endsWith(".go") && p !== path)) {
31483
+ for (const f of source2.list(dirname19(path) || void 0).filter((p) => dirname19(p) === dirname19(path) && p.endsWith(".go") && p !== path)) {
31381
31484
  add(functionsNamed(outlineOf2(f), name2), "package");
31382
31485
  if (Date.now() > input.deadlineAt) break;
31383
31486
  }
@@ -32273,7 +32376,7 @@ async function repoContext(run4) {
32273
32376
  // src/lib/seed-runner.ts
32274
32377
  var import_promises13 = require("node:fs/promises");
32275
32378
  var import_node_fs50 = require("node:fs");
32276
- var import_node_path38 = require("node:path");
32379
+ var import_node_path39 = require("node:path");
32277
32380
  var import_yaml6 = __toESM2(require_dist());
32278
32381
 
32279
32382
  // src/lib/seed.ts
@@ -32496,7 +32599,7 @@ function normalizeTitle(title) {
32496
32599
  async function localSeedTitles(memoryDir3 = MEMORY_DIR) {
32497
32600
  const titles = /* @__PURE__ */ new Set();
32498
32601
  for (const dir of NODE_DIRS) {
32499
- const full = (0, import_node_path38.join)(memoryDir3, dir);
32602
+ const full = (0, import_node_path39.join)(memoryDir3, dir);
32500
32603
  if (!(0, import_node_fs50.existsSync)(full)) continue;
32501
32604
  let files;
32502
32605
  try {
@@ -32508,7 +32611,7 @@ async function localSeedTitles(memoryDir3 = MEMORY_DIR) {
32508
32611
  if (!file.endsWith(".md")) continue;
32509
32612
  let content;
32510
32613
  try {
32511
- content = await (0, import_promises13.readFile)((0, import_node_path38.join)(full, file), "utf-8");
32614
+ content = await (0, import_promises13.readFile)((0, import_node_path39.join)(full, file), "utf-8");
32512
32615
  } catch {
32513
32616
  continue;
32514
32617
  }
@@ -32685,7 +32788,7 @@ async function runSeed(opts) {
32685
32788
  }
32686
32789
  const content = typeof res.data.content === "string" && res.data.content.length > 0 ? res.data.content : renderNodeMarkdown(c, nodeId, createdAt);
32687
32790
  try {
32688
- await (0, import_promises13.mkdir)((0, import_node_path38.dirname)(targetPath), { recursive: true });
32791
+ await (0, import_promises13.mkdir)((0, import_node_path39.dirname)(targetPath), { recursive: true });
32689
32792
  await (0, import_promises13.writeFile)(targetPath, content);
32690
32793
  if (deduplicated) existing++;
32691
32794
  else {
@@ -32702,7 +32805,7 @@ async function runSeed(opts) {
32702
32805
 
32703
32806
  // src/commands/analyze/phases/08-memory-manifest.ts
32704
32807
  var import_node_fs51 = require("node:fs");
32705
- var import_node_path39 = require("node:path");
32808
+ var import_node_path40 = require("node:path");
32706
32809
  async function memoryManifest(run4) {
32707
32810
  const { globals } = run4;
32708
32811
  const { serviceUrl, token } = run4;
@@ -32712,7 +32815,7 @@ async function memoryManifest(run4) {
32712
32815
  let autoSeedNotice = null;
32713
32816
  try {
32714
32817
  await ensureMemoryDir();
32715
- const seedMarker = (0, import_node_path39.join)(VERITY_DIR, ".seeded");
32818
+ const seedMarker = (0, import_node_path40.join)(VERITY_DIR, ".seeded");
32716
32819
  const hasStandard = (0, import_node_fs51.existsSync)(STANDARD_FILE);
32717
32820
  const alreadyTried = (0, import_node_fs51.existsSync)(seedMarker);
32718
32821
  if (hasStandard && !alreadyTried) {
@@ -32833,7 +32936,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
32833
32936
  }
32834
32937
 
32835
32938
  // src/commands/analyze/phases/10-working-memory.ts
32836
- var import_node_path40 = require("node:path");
32939
+ var import_node_path41 = require("node:path");
32837
32940
  async function workingMemory(run4) {
32838
32941
  const { opts } = run4;
32839
32942
  const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run4;
@@ -32845,7 +32948,7 @@ async function workingMemory(run4) {
32845
32948
  const priorState = foldForMarks(memorySession.d);
32846
32949
  incrementReport = computeIncrement(
32847
32950
  allForReview,
32848
- (p) => fileHash((0, import_node_path40.join)(repoRoot(), p)),
32951
+ (p) => fileHash((0, import_node_path41.join)(repoRoot(), p)),
32849
32952
  priorState.authored_all.map((a) => ({
32850
32953
  path: a.path,
32851
32954
  hash_at_last_verdict: a.hash_at_last_verdict
@@ -33474,7 +33577,7 @@ async function transmit(run4) {
33474
33577
 
33475
33578
  // src/commands/analyze/phases/13-reconcile.ts
33476
33579
  var import_node_fs54 = require("node:fs");
33477
- var import_node_path41 = require("node:path");
33580
+ var import_node_path42 = require("node:path");
33478
33581
  async function reconcile(run4) {
33479
33582
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory: memory2, memorySession, response, reviewable, securityFiles, turnId } = run4;
33480
33583
  const sentPaths = codeDelta.files.map((f) => f.path);
@@ -33503,7 +33606,7 @@ async function reconcile(run4) {
33503
33606
  const st = foldDossier(memorySession.d);
33504
33607
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line2) => {
33505
33608
  try {
33506
- const src = (0, import_node_fs54.readFileSync)((0, import_node_path41.join)(repoRoot(), file), "utf8").split("\n");
33609
+ const src = (0, import_node_fs54.readFileSync)((0, import_node_path42.join)(repoRoot(), file), "utf8").split("\n");
33507
33610
  const at = src[line2 - 1];
33508
33611
  return at === void 0 ? null : lineSha(at);
33509
33612
  } catch {
@@ -34325,7 +34428,7 @@ function registerAnalyzeCommand(program2) {
34325
34428
  var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
34326
34429
  async function runAnalyze(opts, globals) {
34327
34430
  if (!verityConfigured()) {
34328
- (0, import_node_fs55.writeSync)(2, isMachineStore((0, import_node_path42.join)(repoRoot(), VERITY_DIR)) ? "[verity] nothing to review in your home directory \u2014 run Verity inside a project.\n" : '[verity] not set up in this project \u2014 run "verity init" first.\n');
34431
+ (0, import_node_fs55.writeSync)(2, isMachineStore((0, import_node_path43.join)(repoRoot(), VERITY_DIR)) ? "[verity] nothing to review in your home directory \u2014 run Verity inside a project.\n" : '[verity] not set up in this project \u2014 run "verity init" first.\n');
34329
34432
  process.exit(0);
34330
34433
  }
34331
34434
  const run4 = createRun(opts, globals);
@@ -34349,23 +34452,23 @@ async function runAnalyze(opts, globals) {
34349
34452
  var import_node_fs60 = require("node:fs");
34350
34453
  var import_node_child_process17 = require("node:child_process");
34351
34454
  var import_node_crypto18 = require("node:crypto");
34352
- var import_node_path47 = require("node:path");
34455
+ var import_node_path48 = require("node:path");
34353
34456
 
34354
34457
  // src/lib/prove/runner.ts
34355
34458
  var import_node_child_process15 = require("node:child_process");
34356
34459
  var import_node_os9 = require("node:os");
34357
34460
  var import_node_fs58 = require("node:fs");
34358
34461
  var import_node_os10 = require("node:os");
34359
- var import_node_path45 = require("node:path");
34462
+ var import_node_path46 = require("node:path");
34360
34463
 
34361
34464
  // src/lib/prove/scaffold.ts
34362
34465
  var import_node_fs57 = require("node:fs");
34363
- var import_node_path44 = require("node:path");
34466
+ var import_node_path45 = require("node:path");
34364
34467
 
34365
34468
  // src/lib/prove/safe-write.ts
34366
34469
  var import_node_fs56 = require("node:fs");
34367
34470
  var import_node_crypto15 = require("node:crypto");
34368
- var import_node_path43 = require("node:path");
34471
+ var import_node_path44 = require("node:path");
34369
34472
  var SafeWriteError = class extends Error {
34370
34473
  constructor(reason, path) {
34371
34474
  super(`refusing to write ${path}: ${reason}`);
@@ -34377,7 +34480,7 @@ var SafeWriteError = class extends Error {
34377
34480
  var FLAGS3 = import_node_fs56.constants;
34378
34481
  var NOFOLLOW2 = FLAGS3.O_NOFOLLOW ?? 0;
34379
34482
  function assertPlainRelative(rel) {
34380
- if (!rel || (0, import_node_path43.isAbsolute)(rel)) throw new SafeWriteError("bad-path", rel);
34483
+ if (!rel || (0, import_node_path44.isAbsolute)(rel)) throw new SafeWriteError("bad-path", rel);
34381
34484
  for (const part of rel.split(/[\\/]/)) {
34382
34485
  if (part === ".." || part === "" || part === ".") throw new SafeWriteError("bad-path", rel);
34383
34486
  }
@@ -34386,7 +34489,7 @@ function ensureDirsNoFollow(root, rel) {
34386
34489
  const parts2 = rel.split(/[\\/]/);
34387
34490
  let cur = root;
34388
34491
  for (const part of parts2.slice(0, -1)) {
34389
- cur = (0, import_node_path43.join)(cur, part);
34492
+ cur = (0, import_node_path44.join)(cur, part);
34390
34493
  let st;
34391
34494
  try {
34392
34495
  st = (0, import_node_fs56.lstatSync)(cur);
@@ -34397,10 +34500,10 @@ function ensureDirsNoFollow(root, rel) {
34397
34500
  if (st.isSymbolicLink()) throw new SafeWriteError("symlink", cur);
34398
34501
  if (!st.isDirectory()) throw new SafeWriteError("not-regular", cur);
34399
34502
  }
34400
- return (0, import_node_path43.join)(root, rel);
34503
+ return (0, import_node_path44.join)(root, rel);
34401
34504
  }
34402
34505
  function insideRoot(realRoot, realPath) {
34403
- return realPath === realRoot || realPath.startsWith(realRoot.endsWith(import_node_path43.sep) ? realRoot : realRoot + import_node_path43.sep);
34506
+ return realPath === realRoot || realPath.startsWith(realRoot.endsWith(import_node_path44.sep) ? realRoot : realRoot + import_node_path44.sep);
34404
34507
  }
34405
34508
  function removeQuietly(path) {
34406
34509
  try {
@@ -34411,7 +34514,7 @@ function removeQuietly(path) {
34411
34514
  function writeFileInRootNoFollow(root, rel, content) {
34412
34515
  assertPlainRelative(rel);
34413
34516
  const dest = ensureDirsNoFollow(root, rel);
34414
- const dir = (0, import_node_path43.dirname)(dest);
34517
+ const dir = (0, import_node_path44.dirname)(dest);
34415
34518
  try {
34416
34519
  const st = (0, import_node_fs56.lstatSync)(dest);
34417
34520
  if (st.isSymbolicLink()) throw new SafeWriteError("symlink", dest);
@@ -34425,7 +34528,7 @@ function writeFileInRootNoFollow(root, rel, content) {
34425
34528
  } catch {
34426
34529
  throw new SafeWriteError("unwritable", root);
34427
34530
  }
34428
- const tmp = (0, import_node_path43.join)(dir, `.${(0, import_node_path43.basename)(dest)}.${(0, import_node_crypto15.randomBytes)(6).toString("hex")}.tmp`);
34531
+ const tmp = (0, import_node_path44.join)(dir, `.${(0, import_node_path44.basename)(dest)}.${(0, import_node_crypto15.randomBytes)(6).toString("hex")}.tmp`);
34429
34532
  let fd;
34430
34533
  try {
34431
34534
  fd = (0, import_node_fs56.openSync)(tmp, import_node_fs56.constants.O_WRONLY | import_node_fs56.constants.O_CREAT | import_node_fs56.constants.O_EXCL | NOFOLLOW2, 420);
@@ -34469,7 +34572,7 @@ function pathHasNoLink(root, rel) {
34469
34572
  }
34470
34573
  let cur = root;
34471
34574
  for (const part of rel.split(/[\\/]/)) {
34472
- cur = cur + import_node_path43.sep + part;
34575
+ cur = cur + import_node_path44.sep + part;
34473
34576
  try {
34474
34577
  if ((0, import_node_fs56.lstatSync)(cur).isSymbolicLink()) return false;
34475
34578
  } catch {
@@ -34781,20 +34884,20 @@ This directory is machine-local and gitignored (see ../.gitignore). Models are
34781
34884
  never committed; what reaches the team is the bug report and the fix.
34782
34885
  `;
34783
34886
  function scaffoldProofs(root) {
34784
- const proofsDir = (0, import_node_path44.join)(root, PROOFS_DIR);
34887
+ const proofsDir = (0, import_node_path45.join)(root, PROOFS_DIR);
34785
34888
  const written = [];
34786
34889
  const kept = [];
34787
34890
  for (const stray of ["lakefile.lean"]) {
34788
34891
  const rel = `${PROOFS_DIR}/${stray}`;
34789
- if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path44.join)(root, rel));
34892
+ if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
34790
34893
  try {
34791
- (0, import_node_fs57.rmSync)((0, import_node_path44.join)(root, rel), { force: true });
34894
+ (0, import_node_fs57.rmSync)((0, import_node_path45.join)(root, rel), { force: true });
34792
34895
  } catch {
34793
34896
  }
34794
34897
  }
34795
34898
  for (const f of PROOFS_TEMPLATE_FILES) {
34796
34899
  const rel = `${PROOFS_DIR}/${f.path}`;
34797
- if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path44.join)(root, rel));
34900
+ if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
34798
34901
  const current = readRegularFileInRoot(root, rel, 4 * 1024 * 1024);
34799
34902
  if (current.ok && current.content === f.content) {
34800
34903
  kept.push(rel);
@@ -34807,8 +34910,8 @@ function scaffoldProofs(root) {
34807
34910
  [`${PROOFS_DIR}/.gitignore`, LOCAL_GITIGNORE],
34808
34911
  [`${MODELS_DIR}/README.md`, MODELS_README]
34809
34912
  ]) {
34810
- if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path44.join)(root, rel));
34811
- if (existsNoFollow((0, import_node_path44.join)(root, rel))) {
34913
+ if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
34914
+ if (existsNoFollow((0, import_node_path45.join)(root, rel))) {
34812
34915
  kept.push(rel);
34813
34916
  continue;
34814
34917
  }
@@ -35077,17 +35180,17 @@ function renderReport(r) {
35077
35180
 
35078
35181
  // src/lib/prove/runner.ts
35079
35182
  function verityLeanHome(env = process.env) {
35080
- return env.VERITY_LEAN_HOME ?? (0, import_node_path45.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".verity", "lean");
35183
+ return env.VERITY_LEAN_HOME ?? (0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".verity", "lean");
35081
35184
  }
35082
35185
  function findToolchain(env = process.env) {
35083
- const managedBin = (0, import_node_path45.join)(verityLeanHome(env), LEAN_TOOLCHAIN.replace(/^leanprover\/lean4:/, ""), "bin");
35084
- const elanBin = (0, import_node_path45.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "bin");
35085
- const elanHas = (0, import_node_fs58.existsSync)((0, import_node_path45.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "toolchains", LEAN_TOOLCHAIN.replace("/", "--").replace(":", "---")));
35086
- const userPath = (env.PATH ?? "").split(import_node_path45.delimiter).filter((d) => d && (elanHas || (0, import_node_path45.resolve)(d) !== (0, import_node_path45.resolve)(elanBin)));
35087
- const path = [managedBin, ...elanHas ? [elanBin] : [], ...userPath].join(import_node_path45.delimiter);
35186
+ const managedBin = (0, import_node_path46.join)(verityLeanHome(env), LEAN_TOOLCHAIN.replace(/^leanprover\/lean4:/, ""), "bin");
35187
+ const elanBin = (0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "bin");
35188
+ const elanHas = (0, import_node_fs58.existsSync)((0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "toolchains", LEAN_TOOLCHAIN.replace("/", "--").replace(":", "---")));
35189
+ const userPath = (env.PATH ?? "").split(import_node_path46.delimiter).filter((d) => d && (elanHas || (0, import_node_path46.resolve)(d) !== (0, import_node_path46.resolve)(elanBin)));
35190
+ const path = [managedBin, ...elanHas ? [elanBin] : [], ...userPath].join(import_node_path46.delimiter);
35088
35191
  const merged = { ...env, PATH: path, ELAN_TOOLCHAIN: env.ELAN_TOOLCHAIN ?? LEAN_TOOLCHAIN };
35089
35192
  const exe = process.platform === "win32" ? "lake.exe" : "lake";
35090
- const candidates2 = [(0, import_node_path45.join)(managedBin, exe), ...elanHas ? [(0, import_node_path45.join)(elanBin, exe)] : [], exe];
35193
+ const candidates2 = [(0, import_node_path46.join)(managedBin, exe), ...elanHas ? [(0, import_node_path46.join)(elanBin, exe)] : [], exe];
35091
35194
  for (const candidate of candidates2) {
35092
35195
  try {
35093
35196
  (0, import_node_child_process15.execFileSync)(candidate, ["--version"], { env: merged, stdio: ["ignore", "pipe", "pipe"], timeout: 1e4 });
@@ -35166,7 +35269,7 @@ function proofsWorkspaceProblem(root) {
35166
35269
  for (const rel of [".verity", PROOFS_DIR, `${PROOFS_DIR}/.lake`]) {
35167
35270
  let st;
35168
35271
  try {
35169
- st = (0, import_node_fs58.lstatSync)((0, import_node_path45.join)(root, rel));
35272
+ st = (0, import_node_fs58.lstatSync)((0, import_node_path46.join)(root, rel));
35170
35273
  } catch {
35171
35274
  break;
35172
35275
  }
@@ -35186,44 +35289,44 @@ function refused(problem) {
35186
35289
  function buildLibrary(tc, root, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS) {
35187
35290
  const problem = proofsWorkspaceProblem(root);
35188
35291
  if (problem) return refused(problem);
35189
- return run2(tc, (0, import_node_path45.join)(root, PROOFS_DIR), ["build"], timeoutMs);
35292
+ return run2(tc, (0, import_node_path46.join)(root, PROOFS_DIR), ["build"], timeoutMs);
35190
35293
  }
35191
35294
  async function buildLibraryAsync(tc, root, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS) {
35192
35295
  const problem = proofsWorkspaceProblem(root);
35193
35296
  if (problem) return refused(problem);
35194
- return runAsync(tc, (0, import_node_path45.join)(root, PROOFS_DIR), ["build"], timeoutMs);
35297
+ return runAsync(tc, (0, import_node_path46.join)(root, PROOFS_DIR), ["build"], timeoutMs);
35195
35298
  }
35196
35299
  function isModelInRoot(modelsAbs, rel) {
35197
- if (!rel.endsWith(".lean") || (0, import_node_path45.isAbsolute)(rel) || rel.startsWith("..")) return false;
35300
+ if (!rel.endsWith(".lean") || (0, import_node_path46.isAbsolute)(rel) || rel.startsWith("..")) return false;
35198
35301
  return statRegularInRoot(modelsAbs, rel).ok;
35199
35302
  }
35200
35303
  function resolveModelFile(root, arg) {
35201
- const modelsAbs = (0, import_node_path45.join)(root, MODELS_DIR);
35202
- const asPath = (0, import_node_path45.relative)(modelsAbs, (0, import_node_path45.isAbsolute)(arg) ? arg : (0, import_node_path45.resolve)(root, arg));
35304
+ const modelsAbs = (0, import_node_path46.join)(root, MODELS_DIR);
35305
+ const asPath = (0, import_node_path46.relative)(modelsAbs, (0, import_node_path46.isAbsolute)(arg) ? arg : (0, import_node_path46.resolve)(root, arg));
35203
35306
  const candidates2 = arg.includes("/") || arg.includes("\\") ? [asPath] : [arg, `${arg}.lean`];
35204
35307
  for (const rel of candidates2) {
35205
- if (isModelInRoot(modelsAbs, rel)) return (0, import_node_path45.join)(modelsAbs, rel);
35308
+ if (isModelInRoot(modelsAbs, rel)) return (0, import_node_path46.join)(modelsAbs, rel);
35206
35309
  }
35207
35310
  return null;
35208
35311
  }
35209
35312
  function listModelFiles(root) {
35210
- const modelsAbs = (0, import_node_path45.join)(root, MODELS_DIR);
35313
+ const modelsAbs = (0, import_node_path46.join)(root, MODELS_DIR);
35211
35314
  if (!(0, import_node_fs58.existsSync)(modelsAbs)) return [];
35212
- return (0, import_node_fs58.readdirSync)(modelsAbs).filter((f) => isModelInRoot(modelsAbs, f)).sort().map((f) => (0, import_node_path45.join)(modelsAbs, f));
35315
+ return (0, import_node_fs58.readdirSync)(modelsAbs).filter((f) => isModelInRoot(modelsAbs, f)).sort().map((f) => (0, import_node_path46.join)(modelsAbs, f));
35213
35316
  }
35214
35317
  function trimDiagnostics(text2) {
35215
35318
  return text2.split("\n").filter((l) => !l.startsWith("trace:")).join("\n").trim().slice(0, 4e3);
35216
35319
  }
35217
35320
  function checkModel(tc, root, modelFile, opts = {}) {
35218
- const proofsAbs = (0, import_node_path45.join)(root, PROOFS_DIR);
35219
- const rel = (0, import_node_path45.relative)(proofsAbs, modelFile);
35321
+ const proofsAbs = (0, import_node_path46.join)(root, PROOFS_DIR);
35322
+ const rel = (0, import_node_path46.relative)(proofsAbs, modelFile);
35220
35323
  const problem = proofsWorkspaceProblem(root);
35221
35324
  const res = problem ? refused(problem) : run2(tc, proofsAbs, ["env", "lean", rel], opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
35222
35325
  return toReport(res, root, modelFile, opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
35223
35326
  }
35224
35327
  async function checkModelAsync(tc, root, modelFile, opts = {}) {
35225
- const proofsAbs = (0, import_node_path45.join)(root, PROOFS_DIR);
35226
- const rel = (0, import_node_path45.relative)(proofsAbs, modelFile);
35328
+ const proofsAbs = (0, import_node_path46.join)(root, PROOFS_DIR);
35329
+ const rel = (0, import_node_path46.relative)(proofsAbs, modelFile);
35227
35330
  const problem = proofsWorkspaceProblem(root);
35228
35331
  const res = problem ? refused(problem) : await runAsync(tc, proofsAbs, ["env", "lean", rel], opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
35229
35332
  return toReport(res, root, modelFile, opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
@@ -35232,10 +35335,10 @@ function toReport(res, root, modelFile, timeoutMs) {
35232
35335
  const parsed = parseVerityLines(res.stdout);
35233
35336
  const combined = `${res.stdout}
35234
35337
  ${res.stderr}`;
35235
- const name2 = parsed.header?.model ?? (0, import_node_path45.basename)(modelFile, ".lean");
35338
+ const name2 = parsed.header?.model ?? (0, import_node_path46.basename)(modelFile, ".lean");
35236
35339
  const report = {
35237
35340
  model: name2,
35238
- file: (0, import_node_path45.relative)(root, modelFile),
35341
+ file: (0, import_node_path46.relative)(root, modelFile),
35239
35342
  ok: res.ok,
35240
35343
  header: parsed.header,
35241
35344
  properties: parsed.properties,
@@ -35281,7 +35384,7 @@ var import_node_fs59 = require("node:fs");
35281
35384
  var import_node_crypto17 = require("node:crypto");
35282
35385
  var import_node_child_process16 = require("node:child_process");
35283
35386
  var import_node_os11 = require("node:os");
35284
- var import_node_path46 = require("node:path");
35387
+ var import_node_path47 = require("node:path");
35285
35388
  var import_promises15 = require("node:stream/promises");
35286
35389
  var import_node_stream = require("node:stream");
35287
35390
  var RELEASES = "https://releases.lean-lang.org/lean4";
@@ -35295,7 +35398,7 @@ function freeBytes(dir) {
35295
35398
  const st = (0, import_node_fs59.statfsSync)(d);
35296
35399
  return Number(st.bavail) * Number(st.bsize);
35297
35400
  } catch {
35298
- const up = (0, import_node_path46.join)(d, "..");
35401
+ const up = (0, import_node_path47.join)(d, "..");
35299
35402
  if (up === d) return null;
35300
35403
  d = up;
35301
35404
  }
@@ -35337,13 +35440,13 @@ function leanAssetName(version = pinnedVersion()) {
35337
35440
  return suffix ? `lean-${v}-${suffix}.zip` : null;
35338
35441
  }
35339
35442
  function pinnedVersionDir(env = process.env) {
35340
- return (0, import_node_path46.join)(verityLeanHome(env), pinnedVersion());
35443
+ return (0, import_node_path47.join)(verityLeanHome(env), pinnedVersion());
35341
35444
  }
35342
35445
  function lakeBinary() {
35343
35446
  return (0, import_node_os11.platform)() === "win32" ? "lake.exe" : "lake";
35344
35447
  }
35345
35448
  function managedInstalled(env = process.env) {
35346
- return (0, import_node_fs59.existsSync)((0, import_node_path46.join)(pinnedVersionDir(env), "bin", lakeBinary()));
35449
+ return (0, import_node_fs59.existsSync)((0, import_node_path47.join)(pinnedVersionDir(env), "bin", lakeBinary()));
35347
35450
  }
35348
35451
  function unzip(zip, dest) {
35349
35452
  const attempts = (0, import_node_os11.platform)() === "linux" ? [["unzip", ["-q", zip, "-d", dest]], ["python3", ["-m", "zipfile", "-e", zip, dest]], ["tar", ["-xf", zip, "-C", dest]]] : [["tar", ["-xf", zip, "-C", dest]], ["unzip", ["-q", zip, "-d", dest]], ["python3", ["-m", "zipfile", "-e", zip, dest]]];
@@ -35356,7 +35459,7 @@ function unzip(zip, dest) {
35356
35459
  } catch (err2) {
35357
35460
  errors.push(`${cmd}: ${String(err2.message).split("\n")[0].slice(0, 120)}`);
35358
35461
  try {
35359
- for (const e of (0, import_node_fs59.readdirSync)(dest)) (0, import_node_fs59.rmSync)((0, import_node_path46.join)(dest, e), { recursive: true, force: true });
35462
+ for (const e of (0, import_node_fs59.readdirSync)(dest)) (0, import_node_fs59.rmSync)((0, import_node_path47.join)(dest, e), { recursive: true, force: true });
35360
35463
  } catch {
35361
35464
  }
35362
35465
  }
@@ -35432,10 +35535,10 @@ async function installLean(opts = {}) {
35432
35535
  const gb = (n2) => (n2 / 1024 ** 3).toFixed(1);
35433
35536
  return { ok: false, error: `not enough disk space for Lean: ${gb(free)} GB free where ${home} lives, needs about ${gb(needed)} GB` };
35434
35537
  }
35435
- const tmp = (0, import_node_path46.join)(home, ".tmp");
35538
+ const tmp = (0, import_node_path47.join)(home, ".tmp");
35436
35539
  (0, import_node_fs59.rmSync)(tmp, { recursive: true, force: true });
35437
35540
  (0, import_node_fs59.mkdirSync)(tmp, { recursive: true });
35438
- const zip = (0, import_node_path46.join)(tmp, asset);
35541
+ const zip = (0, import_node_path47.join)(tmp, asset);
35439
35542
  say(`downloading ${url}`);
35440
35543
  let lastPct = -1;
35441
35544
  try {
@@ -35458,7 +35561,7 @@ async function installLean(opts = {}) {
35458
35561
  return { ok: false, error: verified.error };
35459
35562
  }
35460
35563
  say("unpacking (this takes a minute)");
35461
- const unpack = (0, import_node_path46.join)(tmp, "unpack");
35564
+ const unpack = (0, import_node_path47.join)(tmp, "unpack");
35462
35565
  (0, import_node_fs59.mkdirSync)(unpack);
35463
35566
  const unpacked = unzip(zip, unpack);
35464
35567
  if (!unpacked.ok) {
@@ -35466,8 +35569,8 @@ async function installLean(opts = {}) {
35466
35569
  return { ok: false, error: `unpack failed: ${unpacked.error}` };
35467
35570
  }
35468
35571
  const entries = (0, import_node_fs59.readdirSync)(unpack);
35469
- const top = entries.length === 1 && (0, import_node_fs59.statSync)((0, import_node_path46.join)(unpack, entries[0])).isDirectory() ? (0, import_node_path46.join)(unpack, entries[0]) : unpack;
35470
- if (!(0, import_node_fs59.existsSync)((0, import_node_path46.join)(top, "bin", lakeBinary()))) {
35572
+ const top = entries.length === 1 && (0, import_node_fs59.statSync)((0, import_node_path47.join)(unpack, entries[0])).isDirectory() ? (0, import_node_path47.join)(unpack, entries[0]) : unpack;
35573
+ if (!(0, import_node_fs59.existsSync)((0, import_node_path47.join)(top, "bin", lakeBinary()))) {
35471
35574
  (0, import_node_fs59.rmSync)(tmp, { recursive: true, force: true });
35472
35575
  return { ok: false, error: "the archive did not contain bin/lake" };
35473
35576
  }
@@ -35551,7 +35654,7 @@ function startBackgroundInstall(opts) {
35551
35654
  const home = verityLeanHome(env);
35552
35655
  try {
35553
35656
  (0, import_node_fs60.mkdirSync)(home, { recursive: true });
35554
- const log = (0, import_node_fs60.openSync)((0, import_node_path47.join)(home, LOG_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_APPEND | import_node_fs60.constants.O_CREAT | (import_node_fs60.constants.O_NOFOLLOW ?? 0), 384);
35657
+ const log = (0, import_node_fs60.openSync)((0, import_node_path48.join)(home, LOG_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_APPEND | import_node_fs60.constants.O_CREAT | (import_node_fs60.constants.O_NOFOLLOW ?? 0), 384);
35555
35658
  try {
35556
35659
  const child = (opts.run ?? import_node_child_process17.spawn)(process.execPath, [opts.cli, "lean", "install", "--yes", "--background"], {
35557
35660
  detached: true,
@@ -35583,7 +35686,7 @@ function lockToken(env) {
35583
35686
  function acquireInstallLock(env = process.env, now = Date.now()) {
35584
35687
  const home = verityLeanHome(env);
35585
35688
  (0, import_node_fs60.mkdirSync)(home, { recursive: true });
35586
- const lock = (0, import_node_path47.join)(home, LOCK_FILE);
35689
+ const lock = (0, import_node_path48.join)(home, LOCK_FILE);
35587
35690
  if ((0, import_node_fs60.existsSync)(lock) && !installRunning(env, now)) (0, import_node_fs60.rmSync)(lock, { force: true });
35588
35691
  const token = (0, import_node_crypto18.randomBytes)(16).toString("hex");
35589
35692
  try {
@@ -35599,7 +35702,7 @@ function acquireInstallLock(env = process.env, now = Date.now()) {
35599
35702
  function refreshInstallLock(env = process.env, now = Date.now()) {
35600
35703
  if (!heldToken || lockToken(env) !== heldToken) return;
35601
35704
  try {
35602
- const fd = (0, import_node_fs60.openSync)((0, import_node_path47.join)(verityLeanHome(env), LOCK_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_TRUNC | (import_node_fs60.constants.O_NOFOLLOW ?? 0));
35705
+ const fd = (0, import_node_fs60.openSync)((0, import_node_path48.join)(verityLeanHome(env), LOCK_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_TRUNC | (import_node_fs60.constants.O_NOFOLLOW ?? 0));
35603
35706
  try {
35604
35707
  (0, import_node_fs60.writeFileSync)(fd, JSON.stringify({ pid: process.pid, at: now, token: heldToken }));
35605
35708
  } finally {
@@ -35609,7 +35712,7 @@ function refreshInstallLock(env = process.env, now = Date.now()) {
35609
35712
  }
35610
35713
  }
35611
35714
  function releaseInstallLock(env = process.env) {
35612
- if (heldToken && lockToken(env) === heldToken) (0, import_node_fs60.rmSync)((0, import_node_path47.join)(verityLeanHome(env), LOCK_FILE), { force: true });
35715
+ if (heldToken && lockToken(env) === heldToken) (0, import_node_fs60.rmSync)((0, import_node_path48.join)(verityLeanHome(env), LOCK_FILE), { force: true });
35613
35716
  heldToken = null;
35614
35717
  }
35615
35718
  function writeInstallState(state, env = process.env) {
@@ -35662,14 +35765,14 @@ function resumeInterruptedInstall(opts) {
35662
35765
 
35663
35766
  // src/commands/baseline.ts
35664
35767
  var import_node_fs63 = require("node:fs");
35665
- var import_node_path50 = require("node:path");
35768
+ var import_node_path51 = require("node:path");
35666
35769
 
35667
35770
  // src/lib/skew-notice.ts
35668
35771
  var import_node_fs61 = require("node:fs");
35669
- var import_node_path48 = require("node:path");
35772
+ var import_node_path49 = require("node:path");
35670
35773
  var MARKER = ".skew-notice.json";
35671
35774
  function markerPath2() {
35672
- return (0, import_node_path48.join)(verityHome(), MARKER);
35775
+ return (0, import_node_path49.join)(verityHome(), MARKER);
35673
35776
  }
35674
35777
  function today(now) {
35675
35778
  const y = now.getFullYear();
@@ -35695,7 +35798,7 @@ function recordSkewNotice(skew, now = /* @__PURE__ */ new Date()) {
35695
35798
 
35696
35799
  // src/lib/project-skills.ts
35697
35800
  var import_node_fs62 = require("node:fs");
35698
- var import_node_path49 = require("node:path");
35801
+ var import_node_path50 = require("node:path");
35699
35802
  var PROJECT_SKILL_NAMES = [
35700
35803
  "verity-setup",
35701
35804
  "verity-analyze",
@@ -35722,13 +35825,13 @@ var ALL = [...PROJECT_SKILL_NAMES, ...LEGACY_SKILL_NAMES];
35722
35825
  function staleProjectSkills() {
35723
35826
  const root = projectPath(".claude/skills");
35724
35827
  if (!(0, import_node_fs62.existsSync)(root)) return [];
35725
- return ALL.filter((name2) => (0, import_node_fs62.existsSync)((0, import_node_path49.join)(root, name2)));
35828
+ return ALL.filter((name2) => (0, import_node_fs62.existsSync)((0, import_node_path50.join)(root, name2)));
35726
35829
  }
35727
35830
  function removeProjectSkills() {
35728
35831
  const root = projectPath(".claude/skills");
35729
35832
  const removed = [];
35730
35833
  for (const name2 of staleProjectSkills()) {
35731
- (0, import_node_fs62.rmSync)((0, import_node_path49.join)(root, name2), { recursive: true, force: true });
35834
+ (0, import_node_fs62.rmSync)((0, import_node_path50.join)(root, name2), { recursive: true, force: true });
35732
35835
  removed.push(name2);
35733
35836
  }
35734
35837
  return removed;
@@ -35763,7 +35866,7 @@ function registerBaselineCommands(program2) {
35763
35866
  const loginWarning = "Until they have, do not send them to run `verity login` in a terminal: that runs the global one, which can sign this machine in to a different server.";
35764
35867
  const skewNote = !skew ? null : skewLoud ? `Before anything else, including /verity:init's questions, tell the user their global \`verity\` is ${skew.global}, the hooks run ${skew.hooks}, and \`${skew.remedy}\` fixes it. ${loginWarning}` : `The user's global \`verity\` is ${skew.global} and the hooks run ${skew.hooks}; \`${skew.remedy}\` fixes it. They were already told today, so do not repeat it unprompted \u2014 mention it only where it matters, such as before suggesting a \`verity\` command for their terminal. ${loginWarning}`;
35765
35868
  if (!verityConfigured()) {
35766
- if (isMachineStore((0, import_node_path50.join)(repoRoot(), VERITY_DIR))) process.exit(0);
35869
+ if (isMachineStore((0, import_node_path51.join)(repoRoot(), VERITY_DIR))) process.exit(0);
35767
35870
  const how = isPluginInvocation() ? "Tell the user to run /verity:init \u2014 they type it; you cannot invoke it." : "Tell the user to run `verity init` in their terminal.";
35768
35871
  const lines = [
35769
35872
  `Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
@@ -35907,30 +36010,30 @@ function hookSource(value) {
35907
36010
 
35908
36011
  // src/commands/review.ts
35909
36012
  var import_node_fs64 = require("node:fs");
35910
- var import_node_path51 = require("node:path");
36013
+ var import_node_path52 = require("node:path");
35911
36014
  function containReviewPaths(paths, base) {
35912
36015
  const kept = [];
35913
36016
  const refused2 = [];
35914
36017
  for (const raw of paths) {
35915
- const candidate = (0, import_node_path51.isAbsolute)(raw) ? relativeForm(base, raw) : raw;
36018
+ const candidate = (0, import_node_path52.isAbsolute)(raw) ? relativeForm(base, raw) : raw;
35916
36019
  if (candidate === "" || resolveInside(base, candidate) === null) refused2.push(raw);
35917
36020
  else kept.push(candidate);
35918
36021
  }
35919
36022
  return { kept, refused: refused2 };
35920
36023
  }
35921
36024
  function relativeForm(base, abs) {
35922
- const lexical = (0, import_node_path51.relative)(base, abs);
35923
- if (!(lexical === ".." || lexical.startsWith(`..${import_node_path51.sep}`) || (0, import_node_path51.isAbsolute)(lexical))) return lexical;
36025
+ const lexical = (0, import_node_path52.relative)(base, abs);
36026
+ if (!(lexical === ".." || lexical.startsWith(`..${import_node_path52.sep}`) || (0, import_node_path52.isAbsolute)(lexical))) return lexical;
35924
36027
  try {
35925
- let dir = (0, import_node_path51.dirname)(abs);
35926
- let rest = (0, import_node_path51.basename)(abs);
36028
+ let dir = (0, import_node_path52.dirname)(abs);
36029
+ let rest = (0, import_node_path52.basename)(abs);
35927
36030
  while (!(0, import_node_fs64.existsSync)(dir)) {
35928
- const up = (0, import_node_path51.dirname)(dir);
36031
+ const up = (0, import_node_path52.dirname)(dir);
35929
36032
  if (up === dir) return lexical;
35930
- rest = (0, import_node_path51.join)((0, import_node_path51.basename)(dir), rest);
36033
+ rest = (0, import_node_path52.join)((0, import_node_path52.basename)(dir), rest);
35931
36034
  dir = up;
35932
36035
  }
35933
- return (0, import_node_path51.relative)((0, import_node_fs64.realpathSync)(base), (0, import_node_path51.join)((0, import_node_fs64.realpathSync)(dir), rest));
36036
+ return (0, import_node_path52.relative)((0, import_node_fs64.realpathSync)(base), (0, import_node_path52.join)((0, import_node_fs64.realpathSync)(dir), rest));
35934
36037
  } catch {
35935
36038
  return lexical;
35936
36039
  }
@@ -36036,11 +36139,11 @@ async function runReview(opts, globals) {
36036
36139
 
36037
36140
  // src/commands/guard.ts
36038
36141
  var import_node_fs67 = require("node:fs");
36039
- var import_node_path55 = require("node:path");
36142
+ var import_node_path56 = require("node:path");
36040
36143
 
36041
36144
  // src/lib/vendored.ts
36042
36145
  var import_node_fs65 = require("node:fs");
36043
- var import_node_path52 = require("node:path");
36146
+ var import_node_path53 = require("node:path");
36044
36147
  var INSTALLED_SKILL = /^\.claude\/skills\/(verity-[a-z0-9-]+)\/(.+)$/;
36045
36148
  function dataDirOrNull() {
36046
36149
  try {
@@ -36055,7 +36158,7 @@ function shippedSkillFile(path, dataDir = dataDirOrNull()) {
36055
36158
  const [, skill, rel] = m;
36056
36159
  if (rel.split("/").some((seg) => seg === ".." || seg === ".")) return null;
36057
36160
  try {
36058
- return (0, import_node_fs65.readFileSync)((0, import_node_path52.join)(dataDir, "skills", skill, rel), "utf8");
36161
+ return (0, import_node_fs65.readFileSync)((0, import_node_path53.join)(dataDir, "skills", skill, rel), "utf8");
36059
36162
  } catch {
36060
36163
  return null;
36061
36164
  }
@@ -36166,7 +36269,7 @@ async function runAgentReview(options) {
36166
36269
 
36167
36270
  // src/lib/effort.ts
36168
36271
  var import_node_fs66 = require("node:fs");
36169
- var import_node_path53 = require("node:path");
36272
+ var import_node_path54 = require("node:path");
36170
36273
  var import_yaml7 = __toESM2(require_dist());
36171
36274
  var EFFORT_PROFILES = {
36172
36275
  default: { pushDeadlineMs: 18e4, formal: { tier: "fast", maxRounds: 2, minScore: 0.7, costCapUsd: 1, maxTargets: 1, relaxedGates: false, dailyModels: 5, dailyCostUsd: 5 } },
@@ -36179,7 +36282,7 @@ function asEffort(value) {
36179
36282
  function resolveEffort(root, env = process.env) {
36180
36283
  if (env.VERITY_EFFORT) return asEffort(env.VERITY_EFFORT);
36181
36284
  try {
36182
- const standard = (0, import_yaml7.parse)((0, import_node_fs66.readFileSync)((0, import_node_path53.join)(root, STANDARD_FILE), "utf8"));
36285
+ const standard = (0, import_yaml7.parse)((0, import_node_fs66.readFileSync)((0, import_node_path54.join)(root, STANDARD_FILE), "utf8"));
36183
36286
  return asEffort(standard?.process_constraints?.git_moment_effort);
36184
36287
  } catch {
36185
36288
  return "default";
@@ -36190,7 +36293,7 @@ function effortProfile(root, env = process.env) {
36190
36293
  }
36191
36294
 
36192
36295
  // src/lib/prove/auto.ts
36193
- var import_node_path54 = require("node:path");
36296
+ var import_node_path55 = require("node:path");
36194
36297
  var import_node_crypto21 = require("node:crypto");
36195
36298
 
36196
36299
  // src/lib/prove/grounding.ts
@@ -38344,7 +38447,7 @@ async function runFormalPhase(input) {
38344
38447
  modelName = gen.name;
38345
38448
  const rel = `${AUTO_MODELS_DIR}/${gen.name}.lean`;
38346
38449
  writeFileInRootNoFollow(root, rel, gen.source);
38347
- const abs = resolveModelFile(root, rel) ?? (0, import_node_path54.join)(root, rel);
38450
+ const abs = resolveModelFile(root, rel) ?? (0, import_node_path55.join)(root, rel);
38348
38451
  progress.model = { name: gen.name, file: rel, rounds: round };
38349
38452
  progress.step = "check";
38350
38453
  report = await deps.check(root, abs);
@@ -38493,7 +38596,7 @@ async function runFormalPhase(input) {
38493
38596
  return done({
38494
38597
  status: "checked",
38495
38598
  target,
38496
- model: { name: modelName, file: (0, import_node_path54.relative)(root, (0, import_node_path54.join)(root, modelFile)), rounds },
38599
+ model: { name: modelName, file: (0, import_node_path55.relative)(root, (0, import_node_path55.join)(root, modelFile)), rounds },
38497
38600
  properties: report.properties,
38498
38601
  findings
38499
38602
  });
@@ -38570,7 +38673,7 @@ async function generateAndCheck(root, req, opts) {
38570
38673
  }
38571
38674
  const rel = `${dir}/${gen.name}.lean`;
38572
38675
  writeFileInRootNoFollow(root, rel, gen.source);
38573
- const abs = resolveModelFile(root, rel) ?? (0, import_node_path54.join)(root, rel);
38676
+ const abs = resolveModelFile(root, rel) ?? (0, import_node_path55.join)(root, rel);
38574
38677
  const report = await opts.deps.check(root, abs);
38575
38678
  const abstracts = report.header?.abstracts ?? [];
38576
38679
  const stray = abstracts.find((a) => !headerFiles.has(a.file)) ?? (abstracts[0] && abstracts[0].file !== req.file ? abstracts[0] : void 0);
@@ -38960,7 +39063,7 @@ var WRITERS_BUDGET_MS = 1500;
38960
39063
  var EXCERPT_MAX_LINES2 = 30;
38961
39064
  var VENDORED_READ_MAX_BYTES = 256 * 1024;
38962
39065
  var GUARD_BLOCK_CAP = 2;
38963
- var GUARD_ITER_FILE = (0, import_node_path55.join)(VERITY_DIR, ".guard-iteration");
39066
+ var GUARD_ITER_FILE = (0, import_node_path56.join)(VERITY_DIR, ".guard-iteration");
38964
39067
  function readPreToolUseStdin() {
38965
39068
  const empty = { command: "", cwd: null, sessionId: null };
38966
39069
  return new Promise((resolve9) => {
@@ -39130,11 +39233,11 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
39130
39233
  }
39131
39234
  };
39132
39235
  if (coverageTelemetry) requestBody2.coverage_telemetry = coverageTelemetry;
39133
- const docs2 = discoverGuardDocs(files);
39134
- if (docs2.length > 0 || statedIntent) {
39236
+ const docs = discoverGuardDocs(files);
39237
+ if (docs.length > 0 || statedIntent) {
39135
39238
  const intentContext = {};
39136
39239
  if (statedIntent) intentContext.user_prompt = statedIntent;
39137
- if (docs2.length > 0) intentContext.plans = docs2;
39240
+ if (docs.length > 0) intentContext.plans = docs;
39138
39241
  requestBody2.intent_context = intentContext;
39139
39242
  }
39140
39243
  return requestBody2;
@@ -39819,14 +39922,14 @@ function registerWaiveCommand(program2) {
39819
39922
  }
39820
39923
 
39821
39924
  // src/commands/init/index.ts
39822
- var import_node_fs74 = require("node:fs");
39925
+ var import_node_fs76 = require("node:fs");
39823
39926
  var import_promises19 = require("node:fs/promises");
39824
- var import_node_path60 = require("node:path");
39927
+ var import_node_path64 = require("node:path");
39825
39928
 
39826
39929
  // src/lib/init-trace.ts
39827
39930
  var import_node_crypto23 = require("node:crypto");
39828
39931
  var import_node_fs69 = require("node:fs");
39829
- var import_node_path56 = require("node:path");
39932
+ var import_node_path57 = require("node:path");
39830
39933
  var INIT_STEPS = [
39831
39934
  "started",
39832
39935
  "prerequisites",
@@ -39872,9 +39975,9 @@ function traceDisabled(env = process.env) {
39872
39975
  }
39873
39976
  var PENDING_NAME = "init-trace.json";
39874
39977
  var MAX_PENDING_BYTES = 64 * 1024;
39875
- var verityHome2 = () => (0, import_node_path56.join)(process.env.HOME ?? "", ".verity");
39978
+ var verityHome2 = () => (0, import_node_path57.join)(process.env.HOME ?? "", ".verity");
39876
39979
  function pendingTracePath() {
39877
- return (0, import_node_path56.join)(verityHome2(), PENDING_NAME);
39980
+ return (0, import_node_path57.join)(verityHome2(), PENDING_NAME);
39878
39981
  }
39879
39982
  var RUN_ID = /^[0-9a-f]{16}$/;
39880
39983
  var CLI_VERSION = /^[0-9A-Za-z.+-]{1,40}$/;
@@ -40629,20 +40732,201 @@ var Rail = class {
40629
40732
  };
40630
40733
 
40631
40734
  // src/commands/migrate.ts
40632
- var import_node_fs70 = require("node:fs");
40633
- var import_node_path57 = require("node:path");
40634
- var import_node_child_process18 = require("node:child_process");
40735
+ var import_node_fs71 = require("node:fs");
40736
+ var import_node_path61 = require("node:path");
40737
+ var import_node_child_process19 = require("node:child_process");
40635
40738
 
40636
- // src/lib/telemetry.ts
40739
+ // src/lib/telemetry-local.ts
40740
+ var import_node_path59 = require("node:path");
40741
+
40742
+ // src/lib/telemetry-settings.ts
40637
40743
  var import_promises16 = require("node:fs/promises");
40744
+ var import_node_os12 = require("node:os");
40745
+ var import_node_path58 = require("node:path");
40638
40746
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
40639
- var GITIGNORE_FILE = ".gitignore";
40640
- var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
40641
40747
  var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
40642
- var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
40748
+ var LEGACY_HEADER_KEY = "OTEL_EXPORTER_OTLP_HEADERS";
40749
+ var OTLP_ENDPOINT_KEYS = [
40750
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
40751
+ "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
40752
+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
40753
+ "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
40754
+ ];
40755
+ function claudeConfigDir2() {
40756
+ return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path58.join)((0, import_node_os12.homedir)(), ".claude");
40757
+ }
40758
+ function userSettingsPath() {
40759
+ return (0, import_node_path58.join)(claudeConfigDir2(), "settings.json");
40760
+ }
40643
40761
  function deriveOtlpEndpoint(serviceUrl) {
40644
40762
  return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
40645
40763
  }
40764
+ function isVerityOtlpEndpoint(value, also) {
40765
+ if (typeof value !== "string" || value.trim() === "") return false;
40766
+ if (also && value.replace(/\/+$/, "") === also.replace(/\/+$/, "")) return true;
40767
+ try {
40768
+ return /\/functions\/v1\/otlp\/?$/.test(new URL(value).pathname);
40769
+ } catch {
40770
+ return false;
40771
+ }
40772
+ }
40773
+ function sameUrl(a, b) {
40774
+ const norm3 = (raw) => {
40775
+ const trimmed = raw.trim().replace(/\/+$/, "");
40776
+ try {
40777
+ const u = new URL(trimmed);
40778
+ return `${u.protocol}//${u.host.toLowerCase()}${u.pathname.replace(/\/+$/, "")}`;
40779
+ } catch {
40780
+ return trimmed.toLowerCase();
40781
+ }
40782
+ };
40783
+ return norm3(a) === norm3(b);
40784
+ }
40785
+ function envOf(settings) {
40786
+ const env = settings.env;
40787
+ return env && typeof env === "object" && !Array.isArray(env) ? env : {};
40788
+ }
40789
+ function displayPath(path) {
40790
+ const home = (0, import_node_os12.homedir)();
40791
+ return home && path.startsWith(home + "/") ? "~" + path.slice(home.length) : path;
40792
+ }
40793
+ async function readSettingsFile(path) {
40794
+ let raw;
40795
+ try {
40796
+ raw = await (0, import_promises16.readFile)(path, "utf-8");
40797
+ } catch (err2) {
40798
+ if (err2.code === "ENOENT") return { ok: true, settings: {}, exists: false };
40799
+ return { ok: false, error: `Could not read ${displayPath(path)}: ${err2.message}` };
40800
+ }
40801
+ try {
40802
+ const parsed = raw.trim() === "" ? {} : JSON.parse(raw);
40803
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
40804
+ return { ok: false, error: `${displayPath(path)} is not a JSON object. Fix it, then run the command again. Nothing was changed.` };
40805
+ }
40806
+ return { ok: true, settings: parsed, exists: true };
40807
+ } catch {
40808
+ return { ok: false, error: `${displayPath(path)} is not valid JSON. Fix it, then run the command again. Nothing was changed.` };
40809
+ }
40810
+ }
40811
+
40812
+ // src/lib/telemetry-local.ts
40813
+ var MAX_LOCAL_SETTINGS_BYTES = 1024 * 1024;
40814
+ function nextLocalBytes(raw, value) {
40815
+ try {
40816
+ if (jsonSemanticEqual(JSON.parse(raw), value)) return null;
40817
+ } catch {
40818
+ }
40819
+ const next = JSON.stringify(value, null, detectJsonIndent(raw)) + "\n";
40820
+ return next === raw ? null : next;
40821
+ }
40822
+ var OLD_LOCAL_WRITER_VALUES = {
40823
+ CLAUDE_CODE_ENABLE_TELEMETRY: "1",
40824
+ OTEL_METRICS_EXPORTER: "otlp",
40825
+ OTEL_TRACES_EXPORTER: "otlp",
40826
+ CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
40827
+ OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
40828
+ OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
40829
+ OTEL_LOG_USER_PROMPTS: "0",
40830
+ OTEL_LOG_TOOL_DETAILS: "0",
40831
+ OTEL_LOG_TOOL_CONTENT: "0",
40832
+ OTEL_METRICS_INCLUDE_SESSION_ID: "true",
40833
+ OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
40834
+ OTEL_METRICS_INCLUDE_VERSION: "false",
40835
+ OTEL_METRIC_EXPORT_INTERVAL: "60000"
40836
+ };
40837
+ var LEGACY_VERITY_HEADER = /^Authorization=Bearer (?:verity|gate)_/;
40838
+ function planLocalHeal(settings) {
40839
+ const env = envOf(settings);
40840
+ const envKeys = [];
40841
+ if (typeof env[LEGACY_HEADER_KEY] === "string" && LEGACY_VERITY_HEADER.test(env[LEGACY_HEADER_KEY])) {
40842
+ envKeys.push(LEGACY_HEADER_KEY);
40843
+ }
40844
+ if (isVerityOtlpEndpoint(env.OTEL_EXPORTER_OTLP_ENDPOINT)) {
40845
+ envKeys.push("OTEL_EXPORTER_OTLP_ENDPOINT");
40846
+ for (const [key, value] of Object.entries(OLD_LOCAL_WRITER_VALUES)) {
40847
+ if (env[key] === value) envKeys.push(key);
40848
+ }
40849
+ }
40850
+ return { envKeys, helper: settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD };
40851
+ }
40852
+ function readLocalSettings(root) {
40853
+ const read2 = readRegularFileInRoot(root, SETTINGS_LOCAL_FILE2, MAX_LOCAL_SETTINGS_BYTES);
40854
+ if (!read2.ok) return null;
40855
+ try {
40856
+ const parsed = JSON.parse(read2.content);
40857
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
40858
+ return { raw: read2.content, settings: parsed };
40859
+ } catch {
40860
+ return null;
40861
+ }
40862
+ }
40863
+ function localTelemetryLeftovers(root = repoRoot()) {
40864
+ const local = readLocalSettings(root);
40865
+ if (!local) return [];
40866
+ const plan = planLocalHeal(local.settings);
40867
+ return [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []];
40868
+ }
40869
+ async function healLocalTelemetry(root = repoRoot()) {
40870
+ const local = readLocalSettings(root);
40871
+ if (!local) return { removed: [] };
40872
+ const settings = local.settings;
40873
+ const plan = planLocalHeal(settings);
40874
+ if (plan.envKeys.length === 0 && !plan.helper) return { removed: [] };
40875
+ const env = envOf(settings);
40876
+ for (const key of plan.envKeys) delete env[key];
40877
+ if (settings.env && Object.keys(env).length === 0) delete settings.env;
40878
+ if (plan.helper) delete settings.otelHeadersHelper;
40879
+ const next = nextLocalBytes(local.raw, settings);
40880
+ if (next !== null && !replaceInRoot((0, import_node_path59.join)(root, ".claude"), "settings.local.json", next)) {
40881
+ return { removed: [] };
40882
+ }
40883
+ return { removed: [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []] };
40884
+ }
40885
+
40886
+ // src/lib/telemetry-gate.ts
40887
+ var import_node_child_process18 = require("node:child_process");
40888
+ var import_node_fs70 = require("node:fs");
40889
+ var import_node_path60 = require("node:path");
40890
+ var PROJECT_MARKERS = ["setup.json", "config.json"];
40891
+ function writtenOnThisMachine(root, rel) {
40892
+ const r = (0, import_node_child_process18.spawnSync)("git", hardenedGitArgv(["ls-files", "--error-unmatch", "--", rel]), {
40893
+ cwd: root,
40894
+ env: hardenedGitEnv(),
40895
+ encoding: "utf-8",
40896
+ stdio: ["ignore", "ignore", "pipe"],
40897
+ timeout: 5e3
40898
+ });
40899
+ if (r.status === 1) return true;
40900
+ if (r.status === 128 && /not a git repository/i.test(r.stderr ?? "")) return true;
40901
+ return false;
40902
+ }
40903
+ function isTelemetryProject() {
40904
+ for (const root of [repoRoot(), mainWorktreeRoot()]) {
40905
+ if (!root) continue;
40906
+ const dir = (0, import_node_path60.join)(root, VERITY_DIR);
40907
+ if (isMachineStore(dir)) continue;
40908
+ for (const marker of PROJECT_MARKERS) {
40909
+ if ((0, import_node_fs70.existsSync)((0, import_node_path60.join)(dir, marker)) && writtenOnThisMachine(root, `${VERITY_DIR}/${marker}`)) return true;
40910
+ }
40911
+ }
40912
+ return false;
40913
+ }
40914
+ async function telemetryPinRefusal(token, endpoint) {
40915
+ const serviceUrl = endpoint.trim().replace(/\/+$/, "").replace(/\/otlp$/, "");
40916
+ const pin = await checkTokenPin(token, serviceUrl);
40917
+ if (pin.attach) return null;
40918
+ if (pin.reason === "mismatch" && sameUrl(deriveOtlpEndpoint(pin.mintedFor), endpoint)) return null;
40919
+ return pinRefusalMessage(pin, serviceUrl);
40920
+ }
40921
+ async function sessionExportEndpoints() {
40922
+ const pick = (env) => OTLP_ENDPOINT_KEYS.map((key) => env[key]).filter((v) => typeof v === "string" && v.trim() !== "");
40923
+ const fromEnv = pick(process.env);
40924
+ if (fromEnv.length > 0) return fromEnv;
40925
+ const read2 = await readSettingsFile(userSettingsPath());
40926
+ return read2.ok ? pick(envOf(read2.settings)) : [];
40927
+ }
40928
+
40929
+ // src/lib/telemetry.ts
40646
40930
  function buildTelemetryEnv(serviceUrl) {
40647
40931
  return {
40648
40932
  CLAUDE_CODE_ENABLE_TELEMETRY: "1",
@@ -40658,79 +40942,110 @@ function buildTelemetryEnv(serviceUrl) {
40658
40942
  OTEL_METRICS_INCLUDE_SESSION_ID: "true",
40659
40943
  OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
40660
40944
  OTEL_METRICS_INCLUDE_VERSION: "false",
40945
+ OTEL_METRICS_INCLUDE_REPOSITORY: "true",
40661
40946
  OTEL_METRIC_EXPORT_INTERVAL: "60000"
40662
40947
  };
40663
40948
  }
40664
40949
  var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
40665
- async function readSettingsLocal() {
40666
- try {
40667
- return JSON.parse(await (0, import_promises16.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
40668
- } catch {
40669
- return {};
40950
+ var EXPORTER_KEYS = ["OTEL_METRICS_EXPORTER", "OTEL_TRACES_EXPORTER"];
40951
+ function foreignExportKey(settings, endpoint) {
40952
+ const helper = settings.otelHeadersHelper;
40953
+ if (typeof helper === "string" && helper.trim() !== "" && helper !== OTEL_HEADERS_HELPER_CMD) {
40954
+ return "otelHeadersHelper";
40955
+ }
40956
+ const env = envOf(settings);
40957
+ for (const key of OTLP_ENDPOINT_KEYS) {
40958
+ const value = env[key];
40959
+ if (value !== void 0 && value !== "" && !isVerityOtlpEndpoint(value, endpoint)) return key;
40960
+ }
40961
+ if (env[LEGACY_HEADER_KEY] !== void 0 && env[LEGACY_HEADER_KEY] !== "") return LEGACY_HEADER_KEY;
40962
+ for (const key of EXPORTER_KEYS) {
40963
+ const value = env[key];
40964
+ if (value !== void 0 && value !== "" && value !== "otlp") return key;
40670
40965
  }
40966
+ return null;
40671
40967
  }
40672
- async function writeSettingsLocal(settings) {
40673
- await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
40674
- }
40675
- async function ensureGitignore() {
40676
- const file = projectPath(GITIGNORE_FILE);
40677
- let content = "";
40678
- try {
40679
- content = await (0, import_promises16.readFile)(file, "utf-8");
40680
- } catch {
40968
+ async function installTelemetry(serviceUrl, opts = {}) {
40969
+ const path = userSettingsPath();
40970
+ const read2 = await readSettingsFile(path);
40971
+ if (!read2.ok) return { ok: false, error: read2.error };
40972
+ const settings = read2.settings;
40973
+ const env = buildTelemetryEnv(serviceUrl);
40974
+ const foreign = foreignExportKey(settings, env.OTEL_EXPORTER_OTLP_ENDPOINT);
40975
+ if (foreign) {
40976
+ return {
40977
+ ok: false,
40978
+ error: `Telemetry not installed: ${displayPath(path)} already sets ${foreign}, so you export somewhere else already. Nothing was changed.`
40979
+ };
40681
40980
  }
40682
- const lines = content.split("\n").map((l) => l.trim());
40683
- if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
40684
- return;
40981
+ let token = opts.token;
40982
+ if (!token) {
40983
+ const resolved = await resolveToken();
40984
+ if (!resolved.ok) {
40985
+ return { ok: false, error: 'Telemetry not installed: no Verity token on this machine. Run "verity login". Nothing was changed.' };
40986
+ }
40987
+ token = resolved.data.token;
40685
40988
  }
40686
- const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
40687
- const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
40688
- await (0, import_promises16.writeFile)(file, next);
40689
- }
40690
- async function installTelemetry(serviceUrl) {
40691
- const env = buildTelemetryEnv(serviceUrl);
40692
- const settings = await readSettingsLocal();
40693
- settings.env = { ...settings.env ?? {}, ...env };
40694
- for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
40989
+ const refusal = await telemetryPinRefusal(token, env.OTEL_EXPORTER_OTLP_ENDPOINT);
40990
+ if (refusal) {
40991
+ return { ok: false, error: `Telemetry not installed: ${refusal} Nothing was changed.` };
40992
+ }
40993
+ settings.env = { ...envOf(settings), ...env };
40695
40994
  settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
40696
- await writeSettingsLocal(settings);
40697
- await ensureGitignore();
40698
- return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
40995
+ await writeJsonFilePreservingStyle(path, settings);
40996
+ const healed = await healLocalTelemetry();
40997
+ return {
40998
+ ok: true,
40999
+ data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT, settingsPath: path, healedLocalKeys: healed.removed }
41000
+ };
40699
41001
  }
40700
41002
  async function checkTelemetry() {
40701
- const env = (await readSettingsLocal()).env ?? {};
40702
- const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
40703
- return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
41003
+ const path = userSettingsPath();
41004
+ const read2 = await readSettingsFile(path);
41005
+ const settings = read2.ok ? read2.settings : {};
41006
+ const env = envOf(settings);
41007
+ const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT || null;
41008
+ const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!endpoint && settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD;
41009
+ return { enabled, endpoint, settingsPath: path, staleLocalKeys: localTelemetryLeftovers() };
40704
41010
  }
40705
41011
  async function uninstallTelemetry() {
40706
- const settings = await readSettingsLocal();
40707
- const hadHelper = settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD;
40708
- if (hadHelper) delete settings.otelHeadersHelper;
40709
- if (!settings.env) {
40710
- if (hadHelper) await writeSettingsLocal(settings);
40711
- return { ok: true, data: { removed: 0 } };
40712
- }
41012
+ const path = userSettingsPath();
41013
+ const read2 = await readSettingsFile(path);
41014
+ if (!read2.ok) return { ok: false, error: read2.error };
41015
+ const settings = read2.settings;
41016
+ const env = envOf(settings);
40713
41017
  let removed = 0;
40714
- for (const key of VERITY_TELEMETRY_KEYS) {
40715
- if (key in settings.env) {
40716
- delete settings.env[key];
40717
- removed++;
41018
+ const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT;
41019
+ const foreignEndpoint = endpoint !== void 0 && endpoint !== "" && !isVerityOtlpEndpoint(endpoint);
41020
+ if (isVerityOtlpEndpoint(endpoint)) {
41021
+ const verityValues = buildTelemetryEnv("");
41022
+ for (const key of VERITY_TELEMETRY_KEYS) {
41023
+ if (key === "OTEL_EXPORTER_OTLP_ENDPOINT" || env[key] === verityValues[key]) {
41024
+ if (key in env) {
41025
+ delete env[key];
41026
+ removed++;
41027
+ }
41028
+ }
40718
41029
  }
41030
+ if (settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD) delete settings.otelHeadersHelper;
41031
+ if (settings.env && Object.keys(env).length === 0) delete settings.env;
41032
+ await writeJsonFilePreservingStyle(path, settings);
40719
41033
  }
40720
- for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
40721
- if (Object.keys(settings.env).length === 0) delete settings.env;
40722
- await writeSettingsLocal(settings);
40723
- return { ok: true, data: { removed } };
41034
+ const healed = await healLocalTelemetry();
41035
+ return { ok: true, data: { removed, settingsPath: path, foreignEndpoint, healedLocalKeys: healed.removed } };
40724
41036
  }
40725
41037
 
40726
41038
  // src/commands/migrate.ts
40727
41039
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
40728
41040
  function defaultNpmRemover(pkg) {
40729
- (0, import_node_child_process18.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
41041
+ (0, import_node_child_process19.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
41042
+ }
41043
+ function git2(cwd, args2) {
41044
+ (0, import_node_child_process19.execFileSync)("git", hardenedGitArgv(args2), { cwd, stdio: "pipe", env: hardenedGitEnv() });
40730
41045
  }
40731
41046
  function isGitTracked(cwd, relPath) {
40732
41047
  try {
40733
- (0, import_node_child_process18.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
41048
+ git2(cwd, ["ls-files", "--error-unmatch", "--", relPath]);
40734
41049
  return true;
40735
41050
  } catch {
40736
41051
  return false;
@@ -40738,7 +41053,7 @@ function isGitTracked(cwd, relPath) {
40738
41053
  }
40739
41054
  function isGitRepo(cwd) {
40740
41055
  try {
40741
- (0, import_node_child_process18.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
41056
+ git2(cwd, ["rev-parse", "--is-inside-work-tree"]);
40742
41057
  return true;
40743
41058
  } catch {
40744
41059
  return false;
@@ -40754,17 +41069,17 @@ async function runMigration(opts = {}) {
40754
41069
  await migrateLegacyHooks(root, actions);
40755
41070
  await migrateClaudeMd(root, actions);
40756
41071
  migrateStandardFile(root, actions);
40757
- await migrateTelemetryHeaders(root, actions);
41072
+ await migrateLocalTelemetry(root, actions);
40758
41073
  removeLegacyPackage(movedProjectDir, npmRemover, actions);
40759
41074
  return { actions, migrated: actions.length > 0 };
40760
41075
  }
40761
41076
  function migrateProjectDir(root, actions) {
40762
- const gateDir = (0, import_node_path57.join)(root, ".gate");
40763
- const verityDir = (0, import_node_path57.join)(root, ".verity");
40764
- if ((0, import_node_fs70.existsSync)(gateDir) && !(0, import_node_fs70.existsSync)(verityDir)) {
41077
+ const gateDir = (0, import_node_path61.join)(root, ".gate");
41078
+ const verityDir = (0, import_node_path61.join)(root, ".verity");
41079
+ if ((0, import_node_fs71.existsSync)(gateDir) && !(0, import_node_fs71.existsSync)(verityDir)) {
40765
41080
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
40766
41081
  }
40767
- if ((0, import_node_fs70.existsSync)(gateDir) && (0, import_node_fs70.existsSync)(verityDir)) {
41082
+ if ((0, import_node_fs71.existsSync)(gateDir) && (0, import_node_fs71.existsSync)(verityDir)) {
40768
41083
  return migrateProjectDirCarry(gateDir, verityDir, actions);
40769
41084
  }
40770
41085
  return false;
@@ -40778,20 +41093,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
40778
41093
  );
40779
41094
  }
40780
41095
  try {
40781
- (0, import_node_child_process18.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
41096
+ git2(root, ["mv", "--", ".gate", ".verity"]);
40782
41097
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
40783
41098
  moved = true;
40784
41099
  } catch {
40785
41100
  }
40786
41101
  }
40787
41102
  if (moved) {
40788
- if ((0, import_node_fs70.existsSync)(gateDir)) {
41103
+ if ((0, import_node_fs71.existsSync)(gateDir)) {
40789
41104
  const carried = carryLegacyContents(gateDir, verityDir);
40790
41105
  if (carried > 0) {
40791
41106
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
40792
41107
  }
40793
41108
  try {
40794
- (0, import_node_fs70.rmSync)(gateDir, { recursive: true, force: true });
41109
+ (0, import_node_fs71.rmSync)(gateDir, { recursive: true, force: true });
40795
41110
  } catch {
40796
41111
  }
40797
41112
  }
@@ -40807,18 +41122,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
40807
41122
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
40808
41123
  }
40809
41124
  try {
40810
- (0, import_node_fs70.rmSync)(gateDir, { recursive: true, force: true });
41125
+ (0, import_node_fs71.rmSync)(gateDir, { recursive: true, force: true });
40811
41126
  } catch {
40812
41127
  }
40813
41128
  return carried > 0;
40814
41129
  }
40815
41130
  function migrateGlobalCredentials(home, actions) {
40816
41131
  if (!home) return;
40817
- const gateCreds = (0, import_node_path57.join)(home, ".gate", "credentials");
40818
- const verityCreds = (0, import_node_path57.join)(home, ".verity", "credentials");
40819
- if (!(0, import_node_fs70.existsSync)(gateCreds)) return;
40820
- if (!(0, import_node_fs70.existsSync)(verityCreds)) {
40821
- (0, import_node_fs70.mkdirSync)((0, import_node_path57.join)(home, ".verity"), { recursive: true });
41132
+ const gateCreds = (0, import_node_path61.join)(home, ".gate", "credentials");
41133
+ const verityCreds = (0, import_node_path61.join)(home, ".verity", "credentials");
41134
+ if (!(0, import_node_fs71.existsSync)(gateCreds)) return;
41135
+ if (!(0, import_node_fs71.existsSync)(verityCreds)) {
41136
+ (0, import_node_fs71.mkdirSync)((0, import_node_path61.join)(home, ".verity"), { recursive: true });
40822
41137
  moveFile(gateCreds, verityCreds);
40823
41138
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
40824
41139
  return;
@@ -40840,8 +41155,8 @@ async function migrateLegacyHooks(root, actions) {
40840
41155
  }
40841
41156
  }
40842
41157
  async function migrateClaudeMd(root, actions) {
40843
- const claudeMd = (0, import_node_path57.join)(root, "CLAUDE.md");
40844
- const hadLegacyBlock = (0, import_node_fs70.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
41158
+ const claudeMd = (0, import_node_path61.join)(root, "CLAUDE.md");
41159
+ const hadLegacyBlock = (0, import_node_fs71.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
40845
41160
  if (!hadLegacyBlock) return;
40846
41161
  try {
40847
41162
  await ensureClaudeMdPointer(root);
@@ -40851,13 +41166,13 @@ async function migrateClaudeMd(root, actions) {
40851
41166
  }
40852
41167
  }
40853
41168
  function migrateStandardFile(root, actions) {
40854
- const gateMd = (0, import_node_path57.join)(root, "GATE.md");
40855
- const verityMd = (0, import_node_path57.join)(root, "VERITY.md");
40856
- if (!(0, import_node_fs70.existsSync)(gateMd) || (0, import_node_fs70.existsSync)(verityMd)) return;
41169
+ const gateMd = (0, import_node_path61.join)(root, "GATE.md");
41170
+ const verityMd = (0, import_node_path61.join)(root, "VERITY.md");
41171
+ if (!(0, import_node_fs71.existsSync)(gateMd) || (0, import_node_fs71.existsSync)(verityMd)) return;
40857
41172
  let moved = false;
40858
41173
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
40859
41174
  try {
40860
- (0, import_node_child_process18.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
41175
+ git2(root, ["mv", "--", "GATE.md", "VERITY.md"]);
40861
41176
  moved = true;
40862
41177
  } catch {
40863
41178
  }
@@ -40865,27 +41180,15 @@ function migrateStandardFile(root, actions) {
40865
41180
  if (!moved) moveFile(gateMd, verityMd);
40866
41181
  const content = readFileSyncSafe(verityMd);
40867
41182
  const refreshed = content.split("GATE.md").join("VERITY.md");
40868
- if (refreshed !== content) (0, import_node_fs70.writeFileSync)(verityMd, refreshed);
41183
+ if (refreshed !== content) (0, import_node_fs71.writeFileSync)(verityMd, refreshed);
40869
41184
  actions.push("Renamed GATE.md \u2192 VERITY.md");
40870
41185
  }
40871
- async function migrateTelemetryHeaders(root, actions) {
40872
- const file = (0, import_node_path57.join)(root, ".claude", "settings.local.json");
40873
- if (!(0, import_node_fs70.existsSync)(file)) return;
40874
- let settings;
40875
- try {
40876
- settings = JSON.parse(readFileSyncSafe(file) || "{}");
40877
- } catch {
40878
- return;
40879
- }
40880
- const env = settings.env;
40881
- const hasInlineHeader = !!env && "OTEL_EXPORTER_OTLP_HEADERS" in env;
40882
- const telemetryEnabled = env?.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
40883
- const needsHelper = telemetryEnabled && settings.otelHeadersHelper !== OTEL_HEADERS_HELPER_CMD;
40884
- if (!hasInlineHeader && !needsHelper) return;
40885
- if (hasInlineHeader && env) delete env["OTEL_EXPORTER_OTLP_HEADERS"];
40886
- if (telemetryEnabled) settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
40887
- await writeJsonFilePreservingStyle(file, settings);
40888
- actions.push("Moved the telemetry token out of .claude/settings.local.json (now resolved at runtime)");
41186
+ async function migrateLocalTelemetry(root, actions) {
41187
+ const { removed } = await healLocalTelemetry(root);
41188
+ if (removed.length === 0) return;
41189
+ actions.push(
41190
+ `Removed ${removed.length} old Verity telemetry key(s) from ${SETTINGS_LOCAL_FILE2} (Claude Code ignores them there). Run "verity telemetry install" to export from this machine.`
41191
+ );
40889
41192
  }
40890
41193
  function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
40891
41194
  if (!movedProjectDir) return;
@@ -40918,21 +41221,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
40918
41221
  }
40919
41222
  if (toAppend.length > 0) {
40920
41223
  const sep7 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
40921
- (0, import_node_fs70.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
41224
+ (0, import_node_fs71.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
40922
41225
  }
40923
- (0, import_node_fs70.rmSync)(gateCreds, { force: true });
41226
+ (0, import_node_fs71.rmSync)(gateCreds, { force: true });
40924
41227
  return toAppend.length;
40925
41228
  }
40926
41229
  function readFileSyncSafe(path) {
40927
41230
  try {
40928
- return (0, import_node_fs70.readFileSync)(path, "utf-8");
41231
+ return (0, import_node_fs71.readFileSync)(path, "utf-8");
40929
41232
  } catch {
40930
41233
  return "";
40931
41234
  }
40932
41235
  }
40933
41236
  function hasStagedChanges(root) {
40934
41237
  try {
40935
- (0, import_node_child_process18.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
41238
+ git2(root, ["diff", "--cached", "--quiet", ...DIFF_READ_FLAGS]);
40936
41239
  return false;
40937
41240
  } catch {
40938
41241
  return true;
@@ -40940,59 +41243,90 @@ function hasStagedChanges(root) {
40940
41243
  }
40941
41244
  function moveDir(from, to) {
40942
41245
  try {
40943
- (0, import_node_fs70.renameSync)(from, to);
41246
+ (0, import_node_fs71.renameSync)(from, to);
40944
41247
  } catch (err2) {
40945
41248
  if (err2.code !== "EXDEV") throw err2;
40946
- (0, import_node_fs70.cpSync)(from, to, { recursive: true });
40947
- (0, import_node_fs70.rmSync)(from, { recursive: true, force: true });
41249
+ (0, import_node_fs71.cpSync)(from, to, { recursive: true });
41250
+ (0, import_node_fs71.rmSync)(from, { recursive: true, force: true });
40948
41251
  }
40949
41252
  }
40950
41253
  function moveFile(from, to) {
40951
41254
  try {
40952
- (0, import_node_fs70.renameSync)(from, to);
41255
+ (0, import_node_fs71.renameSync)(from, to);
40953
41256
  } catch (err2) {
40954
41257
  if (err2.code !== "EXDEV") throw err2;
40955
- (0, import_node_fs70.cpSync)(from, to);
40956
- (0, import_node_fs70.rmSync)(from, { force: true });
41258
+ (0, import_node_fs71.cpSync)(from, to);
41259
+ (0, import_node_fs71.rmSync)(from, { force: true });
40957
41260
  }
40958
41261
  }
40959
41262
  function carryLegacyContents(gateDir, verityDir) {
41263
+ if (!isRealDir(gateDir) || !isRealDir(verityDir)) return 0;
40960
41264
  let copied = 0;
40961
41265
  const walk2 = (relDir) => {
40962
- const srcDir = (0, import_node_path57.join)(gateDir, relDir);
40963
- for (const entry of (0, import_node_fs70.readdirSync)(srcDir)) {
40964
- const rel = relDir ? (0, import_node_path57.join)(relDir, entry) : entry;
40965
- const src = (0, import_node_path57.join)(gateDir, rel);
40966
- const dest = (0, import_node_path57.join)(verityDir, rel);
40967
- if ((0, import_node_fs70.statSync)(src).isDirectory()) {
41266
+ let entries;
41267
+ try {
41268
+ entries = (0, import_node_fs71.readdirSync)((0, import_node_path61.join)(gateDir, relDir), { withFileTypes: true });
41269
+ } catch {
41270
+ return;
41271
+ }
41272
+ for (const entry of entries) {
41273
+ const rel = relDir ? (0, import_node_path61.join)(relDir, entry.name) : entry.name;
41274
+ if (entry.isSymbolicLink()) continue;
41275
+ if (entry.isDirectory()) {
40968
41276
  walk2(rel);
40969
- } else if (!(0, import_node_fs70.existsSync)(dest)) {
40970
- (0, import_node_fs70.mkdirSync)((0, import_node_path57.dirname)(dest), { recursive: true });
40971
- (0, import_node_fs70.cpSync)(src, dest);
41277
+ continue;
41278
+ }
41279
+ if (!entry.isFile() || !ensureRealDirs(verityDir, relDir)) continue;
41280
+ const opened = openRegularInRoot(gateDir, rel);
41281
+ if (!opened.ok) continue;
41282
+ try {
41283
+ (0, import_node_fs71.writeFileSync)((0, import_node_path61.join)(verityDir, rel), readOpened(opened.fd, opened.size), { flag: "wx", mode: 384 });
40972
41284
  copied++;
41285
+ } catch {
40973
41286
  }
40974
41287
  }
40975
41288
  };
40976
41289
  walk2("");
40977
41290
  return copied;
40978
41291
  }
41292
+ function isRealDir(path) {
41293
+ try {
41294
+ const st = (0, import_node_fs71.lstatSync)(path);
41295
+ return st.isDirectory() && !st.isSymbolicLink();
41296
+ } catch {
41297
+ return false;
41298
+ }
41299
+ }
41300
+ function ensureRealDirs(root, relDir) {
41301
+ let current = root;
41302
+ for (const segment of relDir.split(/[\\/]/).filter(Boolean)) {
41303
+ current = (0, import_node_path61.join)(current, segment);
41304
+ try {
41305
+ (0, import_node_fs71.mkdirSync)(current);
41306
+ } catch (e) {
41307
+ if (e.code !== "EEXIST") return false;
41308
+ }
41309
+ if (!isRealDir(current)) return false;
41310
+ }
41311
+ return true;
41312
+ }
40979
41313
  async function needsMigration(root = repoRoot()) {
40980
- const gateDir = (0, import_node_path57.join)(root, ".gate");
40981
- const verityDir = (0, import_node_path57.join)(root, ".verity");
40982
- if ((0, import_node_fs70.existsSync)(gateDir) && !(0, import_node_fs70.existsSync)(verityDir)) return true;
40983
- if ((0, import_node_fs70.existsSync)(gateDir) && (0, import_node_fs70.existsSync)(verityDir)) {
40984
- if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(gateDir, "credentials")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(verityDir, "credentials"))) {
41314
+ const gateDir = (0, import_node_path61.join)(root, ".gate");
41315
+ const verityDir = (0, import_node_path61.join)(root, ".verity");
41316
+ if ((0, import_node_fs71.existsSync)(gateDir) && !(0, import_node_fs71.existsSync)(verityDir)) return true;
41317
+ if ((0, import_node_fs71.existsSync)(gateDir) && (0, import_node_fs71.existsSync)(verityDir)) {
41318
+ if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(gateDir, "credentials")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(verityDir, "credentials"))) {
40985
41319
  return true;
40986
41320
  }
40987
- if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(gateDir, "memory")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(verityDir, "memory"))) {
41321
+ if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(gateDir, "memory")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(verityDir, "memory"))) {
40988
41322
  return true;
40989
41323
  }
40990
41324
  }
40991
- const claudeMd = (0, import_node_path57.join)(root, "CLAUDE.md");
40992
- if ((0, import_node_fs70.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
41325
+ const claudeMd = (0, import_node_path61.join)(root, "CLAUDE.md");
41326
+ if ((0, import_node_fs71.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
40993
41327
  return true;
40994
41328
  }
40995
- if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(root, "GATE.md")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(root, "VERITY.md"))) {
41329
+ if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(root, "GATE.md")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(root, "VERITY.md"))) {
40996
41330
  return true;
40997
41331
  }
40998
41332
  if (await hasLegacyHooksAt(root)) return true;
@@ -41018,10 +41352,10 @@ function registerMigrateCommand(program2) {
41018
41352
  }
41019
41353
 
41020
41354
  // src/commands/init/ui.ts
41021
- var import_node_fs71 = require("node:fs");
41022
- var import_node_path58 = require("node:path");
41023
- var import_node_os12 = require("node:os");
41024
- var import_node_child_process19 = require("node:child_process");
41355
+ var import_node_fs72 = require("node:fs");
41356
+ var import_node_path62 = require("node:path");
41357
+ var import_node_os13 = require("node:os");
41358
+ var import_node_child_process20 = require("node:child_process");
41025
41359
  function createInitUi(opts) {
41026
41360
  const term = opts.term ?? detectTerm();
41027
41361
  const screen = new Screen(term);
@@ -41038,25 +41372,26 @@ function installInterrupt(ui) {
41038
41372
  process.once("SIGINT", stop2);
41039
41373
  }
41040
41374
  var SEEN_FILE = ".init-seen";
41041
- function firstInitOnMachine(home = process.env.HOME || (0, import_node_os12.homedir)()) {
41042
- return !(0, import_node_fs71.existsSync)((0, import_node_path58.join)(home, ".verity", SEEN_FILE));
41375
+ function firstInitOnMachine(home = process.env.HOME || (0, import_node_os13.homedir)()) {
41376
+ return !(0, import_node_fs72.existsSync)((0, import_node_path62.join)(home, ".verity", SEEN_FILE));
41043
41377
  }
41044
- function markInitSeen(home = process.env.HOME || (0, import_node_os12.homedir)()) {
41045
- replaceInRoot((0, import_node_path58.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
41378
+ function markInitSeen(home = process.env.HOME || (0, import_node_os13.homedir)()) {
41379
+ replaceInRoot((0, import_node_path62.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
41046
41380
  `);
41047
41381
  }
41048
41382
  function repoLabel() {
41049
41383
  try {
41050
- const url = (0, import_node_child_process19.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41384
+ const url = (0, import_node_child_process20.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41051
41385
  const parsed = parseRemote(url);
41052
41386
  if (parsed) return terminalText(`${parsed.owner}/${parsed.repo}`, 120);
41053
41387
  } catch {
41054
41388
  }
41055
- return terminalText((0, import_node_path58.basename)(repoRoot()), 120);
41389
+ return terminalText((0, import_node_path62.basename)(repoRoot()), 120);
41056
41390
  }
41057
41391
 
41058
41392
  // src/lib/prereqs.ts
41059
- var import_node_child_process20 = require("node:child_process");
41393
+ var import_node_child_process21 = require("node:child_process");
41394
+ var import_node_fs73 = require("node:fs");
41060
41395
  var MIN_NODE_MAJOR = 20;
41061
41396
  function which(bin) {
41062
41397
  return whichSync(bin, { real: false });
@@ -41077,7 +41412,7 @@ function checkNode() {
41077
41412
  function checkGit() {
41078
41413
  let detail = "";
41079
41414
  try {
41080
- detail = (0, import_node_child_process20.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41415
+ detail = (0, import_node_child_process21.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41081
41416
  } catch {
41082
41417
  return {
41083
41418
  id: "git",
@@ -41101,21 +41436,38 @@ function checkClaude() {
41101
41436
  required: false
41102
41437
  };
41103
41438
  }
41104
- function checkAnalysisCli() {
41105
- const path = which("codacy-analysis");
41439
+ var ANALYSIS_CLI_REMEDY = "Reinstall Verity, which includes it: npm install -g @codacy/verity-cli";
41440
+ function checkAnalysisCli(opts = {}) {
41441
+ const base = { id: "analysis-cli", label: "@codacy/analysis-cli", required: false };
41442
+ const analyzer = resolveAnalyzer(opts);
41443
+ const shown = (opts.onPathDisplay ?? (() => which("codacy-analysis")))();
41444
+ if (!analyzer) {
41445
+ return { ...base, status: "warn", detail: "not found", remedy: `${ANALYSIS_CLI_REMEDY} (static findings are unavailable until then)` };
41446
+ }
41447
+ if (analyzer.source === "path") {
41448
+ return { ...base, status: "ok", detail: `${shown ?? analyzer.path} (on PATH)`, source: "path" };
41449
+ }
41450
+ const unused = shown && !sameFile(shown, analyzer.path) ? shown : void 0;
41106
41451
  return {
41107
- id: "analysis-cli",
41108
- label: "@codacy/analysis-cli",
41109
- status: path ? "ok" : "warn",
41110
- detail: path ?? "not found",
41111
- remedy: path ? void 0 : "npm install -g @codacy/analysis-cli (static findings are unavailable until then)",
41112
- required: false
41452
+ ...base,
41453
+ status: "ok",
41454
+ detail: `${analyzer.version} (bundled with Verity)`,
41455
+ source: "bundled",
41456
+ version: analyzer.version,
41457
+ ...unused && { unused }
41113
41458
  };
41114
41459
  }
41460
+ function sameFile(a, b) {
41461
+ try {
41462
+ return (0, import_node_fs73.realpathSync)(a) === (0, import_node_fs73.realpathSync)(b);
41463
+ } catch {
41464
+ return false;
41465
+ }
41466
+ }
41115
41467
  var INSTALL_TIMEOUT_MS = 12e4;
41116
41468
  function run3(command, args2, opts = {}) {
41117
41469
  return new Promise((resolve9) => {
41118
- const child = (0, import_node_child_process20.spawn)(command, args2, {
41470
+ const child = (0, import_node_child_process21.spawn)(command, args2, {
41119
41471
  stdio: opts.inherit ? "inherit" : "pipe",
41120
41472
  timeout: INSTALL_TIMEOUT_MS
41121
41473
  });
@@ -41145,7 +41497,7 @@ async function installAnalysisCli(opts = {}) {
41145
41497
  label: "@codacy/analysis-cli",
41146
41498
  status: "warn",
41147
41499
  detail: "install failed",
41148
- remedy: "Install manually: npm install -g @codacy/analysis-cli",
41500
+ remedy: ANALYSIS_CLI_REMEDY,
41149
41501
  required: false
41150
41502
  };
41151
41503
  if (opts.sudo === false || !process.stdin.isTTY || !process.stdout.isTTY) return failed;
@@ -41234,7 +41586,7 @@ async function prerequisitesStep(ui) {
41234
41586
  const analysis = report.checks.find((c) => c.id === "analysis-cli");
41235
41587
  if (analysis && analysis.status !== "ok") {
41236
41588
  const s = ui.screen.s;
41237
- const installed2 = await ui.screen.spin(`${s.gray}\u2502${s.reset} `, () => "Installing the Codacy analysis CLI (it runs the Trivy security scan)", installAnalysisCli({ quiet: true, sudo: false }));
41589
+ const installed2 = await ui.screen.spin(`${s.gray}\u2502${s.reset} `, () => "Verity's copy of the Codacy analysis CLI is missing. Installing it (it runs the Trivy security scan)", installAnalysisCli({ quiet: true, sudo: false }));
41238
41590
  report.checks[report.checks.indexOf(analysis)] = installed2;
41239
41591
  if (installed2.status === "ok") ui.rail.ticked("Installed the Codacy analysis CLI");
41240
41592
  }
@@ -41371,9 +41723,9 @@ async function scaffoldStep(ui, given = {}) {
41371
41723
  }
41372
41724
 
41373
41725
  // src/commands/init/wiring.ts
41374
- var import_node_fs72 = require("node:fs");
41726
+ var import_node_fs74 = require("node:fs");
41375
41727
  var import_promises18 = require("node:fs/promises");
41376
- var import_node_path59 = require("node:path");
41728
+ var import_node_path63 = require("node:path");
41377
41729
  var DEFAULT_MOMENTS = ["stop", "pre-commit", "pre-push"];
41378
41730
  async function momentsFromInstalledHooks() {
41379
41731
  const status = await checkAllVerityHooks();
@@ -41446,12 +41798,12 @@ var SKILLS = [
41446
41798
  ];
41447
41799
  function resolveDataDir2() {
41448
41800
  const candidates2 = [
41449
- (0, import_node_path59.join)(__dirname, "..", "data"),
41450
- (0, import_node_path59.join)(__dirname, "..", "..", "data"),
41451
- (0, import_node_path59.join)(process.cwd(), "cli", "data")
41801
+ (0, import_node_path63.join)(__dirname, "..", "data"),
41802
+ (0, import_node_path63.join)(__dirname, "..", "..", "data"),
41803
+ (0, import_node_path63.join)(process.cwd(), "cli", "data")
41452
41804
  ];
41453
41805
  for (const candidate of candidates2) {
41454
- if ((0, import_node_fs72.existsSync)((0, import_node_path59.join)(candidate, "skills"))) return candidate;
41806
+ if ((0, import_node_fs74.existsSync)((0, import_node_path63.join)(candidate, "skills"))) return candidate;
41455
41807
  }
41456
41808
  throw new Error("Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli");
41457
41809
  }
@@ -41459,9 +41811,9 @@ async function skillIsCurrent(src, dest) {
41459
41811
  const list2 = (dir) => {
41460
41812
  const out2 = [];
41461
41813
  const walk2 = (d, prefix) => {
41462
- for (const e of (0, import_node_fs72.readdirSync)(d, { withFileTypes: true })) {
41814
+ for (const e of (0, import_node_fs74.readdirSync)(d, { withFileTypes: true })) {
41463
41815
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
41464
- if (e.isDirectory()) walk2((0, import_node_path59.join)(d, e.name), rel);
41816
+ if (e.isDirectory()) walk2((0, import_node_path63.join)(d, e.name), rel);
41465
41817
  else if (e.isFile()) out2.push(rel);
41466
41818
  }
41467
41819
  };
@@ -41472,7 +41824,7 @@ async function skillIsCurrent(src, dest) {
41472
41824
  const shipped = list2(src);
41473
41825
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
41474
41826
  for (const rel of shipped) {
41475
- if (await (0, import_promises18.readFile)((0, import_node_path59.join)(src, rel), "utf-8") !== await (0, import_promises18.readFile)((0, import_node_path59.join)(dest, rel), "utf-8")) return false;
41827
+ if (await (0, import_promises18.readFile)((0, import_node_path63.join)(src, rel), "utf-8") !== await (0, import_promises18.readFile)((0, import_node_path63.join)(dest, rel), "utf-8")) return false;
41476
41828
  }
41477
41829
  return true;
41478
41830
  } catch {
@@ -41480,16 +41832,16 @@ async function skillIsCurrent(src, dest) {
41480
41832
  }
41481
41833
  }
41482
41834
  async function installSkills(force) {
41483
- const source2 = (0, import_node_path59.join)(resolveDataDir2(), "skills");
41835
+ const source2 = (0, import_node_path63.join)(resolveDataDir2(), "skills");
41484
41836
  const missing = [];
41485
41837
  for (const skill of SKILLS) {
41486
- const src = (0, import_node_path59.join)(source2, skill);
41487
- const dest = (0, import_node_path59.join)(".claude/skills", skill);
41488
- if (!(0, import_node_fs72.existsSync)(src)) {
41838
+ const src = (0, import_node_path63.join)(source2, skill);
41839
+ const dest = (0, import_node_path63.join)(".claude/skills", skill);
41840
+ if (!(0, import_node_fs74.existsSync)(src)) {
41489
41841
  missing.push(skill);
41490
41842
  continue;
41491
41843
  }
41492
- if ((0, import_node_fs72.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
41844
+ if ((0, import_node_fs74.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
41493
41845
  await (0, import_promises18.mkdir)(dest, { recursive: true });
41494
41846
  await (0, import_promises18.cp)(src, dest, { recursive: true, force: true });
41495
41847
  }
@@ -41529,7 +41881,7 @@ async function hooksStep(ui, ctx) {
41529
41881
  }
41530
41882
 
41531
41883
  // src/commands/init/sign-in.ts
41532
- var import_node_child_process21 = require("node:child_process");
41884
+ var import_node_child_process22 = require("node:child_process");
41533
41885
  async function classifyExistingLogin(serviceUrl, remote, opts) {
41534
41886
  const existing = await resolveToken(opts.token);
41535
41887
  if (!existing.ok) return { kind: "none" };
@@ -41554,7 +41906,7 @@ async function classifyExistingLogin(serviceUrl, remote, opts) {
41554
41906
  }
41555
41907
  function gitRemote() {
41556
41908
  try {
41557
- return (0, import_node_child_process21.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41909
+ return (0, import_node_child_process22.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
41558
41910
  } catch {
41559
41911
  return "";
41560
41912
  }
@@ -41686,7 +42038,7 @@ async function signInStep(ui, resolution, opts) {
41686
42038
  }
41687
42039
 
41688
42040
  // src/commands/init/standard.ts
41689
- var import_node_fs73 = require("node:fs");
42041
+ var import_node_fs75 = require("node:fs");
41690
42042
 
41691
42043
  // src/commands/init/rules.ts
41692
42044
  async function projectLabel(token, serviceUrl, verbose) {
@@ -41766,13 +42118,13 @@ async function generate(ui, globals, profile, replaces) {
41766
42118
  return { status: "fallback" };
41767
42119
  }
41768
42120
  const secs2 = Math.max(1, Math.round((Date.now() - started) / 1e3));
41769
- const docs2 = profile.docs.length;
42121
+ const docs = profile.docs.length;
41770
42122
  if (outcome.rules.length === 0) {
41771
42123
  rail.warn("No project rules this time");
41772
42124
  rail.line("The proposed rules were too generic, or not safe to use. The built-in rules apply.");
41773
42125
  } else {
41774
42126
  const n2 = outcome.rules.length;
41775
- const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs2 ? [`${docs2} ${docs2 === 1 ? "doc" : "docs"}`] : []].join(" and ");
42127
+ const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs ? [`${docs} ${docs === 1 ? "doc" : "docs"}`] : []].join(" and ");
41776
42128
  rail.done(`${n2} ${n2 === 1 ? "rule" : "rules"} for this repository ${s.gray}\xB7 from ${from} \xB7 ${secs2}s${s.reset}`);
41777
42129
  await rail.rules(outcome.rules);
41778
42130
  if (outcome.rejected > 0) rail.line(`${outcome.rejected} proposed ${outcome.rejected === 1 ? "rule was" : "rules were"} left out: too generic, or not safe to use.`);
@@ -41885,7 +42237,7 @@ async function standardStep(ui, ctx) {
41885
42237
  const d = await deriveFromCatalogue(ui, ctx.globals);
41886
42238
  return end({ source: d.ok ? "derived" : "none", version: d.version, profile: profile2 });
41887
42239
  };
41888
- if ((0, import_node_fs73.existsSync)(projectPath(STANDARD_FILE))) {
42240
+ if ((0, import_node_fs75.existsSync)(projectPath(STANDARD_FILE))) {
41889
42241
  const sync = await pushLocalStandardIfMissing(ctx.globals);
41890
42242
  rail.done("Kept your Standard");
41891
42243
  rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is.`);
@@ -42010,7 +42362,7 @@ async function runInit(program2, opts) {
42010
42362
  standardAnswer = v;
42011
42363
  }
42012
42364
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
42013
- if (!projectMarkers.some((m) => (0, import_node_fs74.existsSync)(m))) {
42365
+ if (!projectMarkers.some((m) => (0, import_node_fs76.existsSync)(m))) {
42014
42366
  printError('No project detected in the current directory. Run "verity init" from your project root.');
42015
42367
  process.exit(1);
42016
42368
  }
@@ -42046,10 +42398,10 @@ async function runInit(program2, opts) {
42046
42398
  trace.mark("prerequisites", "ok");
42047
42399
  await scaffoldStep(ui, { untrackMemory: opts.untrackMemory === true, untrackState: opts.untrackState === true });
42048
42400
  trace.mark("scaffold", "ok");
42049
- await (0, import_promises19.mkdir)((0, import_node_path60.join)(process.env.HOME ?? "", ".verity"), { recursive: true });
42401
+ await (0, import_promises19.mkdir)((0, import_node_path64.join)(process.env.HOME ?? "", ".verity"), { recursive: true });
42050
42402
  await repoStep(ui, detectProject());
42051
42403
  const previous = await readSetupState();
42052
- const setUpBefore = (0, import_node_fs74.existsSync)(projectPath(STANDARD_FILE));
42404
+ const setUpBefore = (0, import_node_fs76.existsSync)(projectPath(STANDARD_FILE));
42053
42405
  const answers = await askSetupQuestions(defaultsOnly, previous, { moments: givenMoments, telemetry: givenTelemetry });
42054
42406
  trace.mark("questions", "ok", givenMoments || givenTelemetry !== void 0 || profileAnswer || standardAnswer ? "asked" : "defaults");
42055
42407
  const plan = planMoments(answers.moments, { given: givenMoments !== void 0, defaultsOnly });
@@ -42093,7 +42445,7 @@ async function runInit(program2, opts) {
42093
42445
  // The effective answer: the next run starts from this record.
42094
42446
  moments: plan.momentIds,
42095
42447
  ...telemetry ? { telemetry } : {},
42096
- init: { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.0-experimental.3211f79" : "dev" }
42448
+ init: { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.0-experimental.7c5e75b" : "dev" }
42097
42449
  });
42098
42450
  } catch (err2) {
42099
42451
  rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
@@ -42124,7 +42476,7 @@ function registerInitCommand(program2) {
42124
42476
  }
42125
42477
 
42126
42478
  // src/commands/doctor.ts
42127
- var import_node_fs75 = require("node:fs");
42479
+ var import_node_fs77 = require("node:fs");
42128
42480
  async function buildReport() {
42129
42481
  const prereqs = await checkPrereqs({ install: false });
42130
42482
  const state = await readSetupState();
@@ -42133,8 +42485,8 @@ async function buildReport() {
42133
42485
  const provenance = cliProvenance();
42134
42486
  const telemetry = await checkTelemetry();
42135
42487
  const artifacts = {
42136
- standard: (0, import_node_fs75.existsSync)(projectPath(STANDARD_FILE)),
42137
- verityMd: (0, import_node_fs75.existsSync)(projectPath(VERITY_MD_FILE))
42488
+ standard: (0, import_node_fs77.existsSync)(projectPath(STANDARD_FILE)),
42489
+ verityMd: (0, import_node_fs77.existsSync)(projectPath(VERITY_MD_FILE))
42138
42490
  };
42139
42491
  const next = [];
42140
42492
  for (const c of prereqs.checks) {
@@ -42173,6 +42525,11 @@ async function buildReport() {
42173
42525
  if (state?.telemetry === "deferred") {
42174
42526
  next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
42175
42527
  }
42528
+ if (telemetry.staleLocalKeys.length > 0) {
42529
+ next.push(
42530
+ `.claude/settings.local.json still holds ${telemetry.staleLocalKeys.length} old Verity telemetry key(s). Claude Code 2.1.282 and later ignores them there, so nothing is exported from them. Run "verity telemetry install" to export from this machine instead (it removes them), or "verity telemetry uninstall" to turn telemetry off and remove them.`
42531
+ );
42532
+ }
42176
42533
  const memory2 = {
42177
42534
  posture: memoryPosture(),
42178
42535
  trackedFiles: committedMemoryFiles().length,
@@ -42220,7 +42577,7 @@ async function buildReport() {
42220
42577
  noAnalysisMoment,
42221
42578
  versions
42222
42579
  },
42223
- telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
42580
+ telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint, staleLocalKeys: telemetry.staleLocalKeys },
42224
42581
  artifacts,
42225
42582
  memory: memory2,
42226
42583
  trackedState,
@@ -42243,6 +42600,7 @@ function registerDoctorCommand(program2) {
42243
42600
  for (const c of report.prerequisites) {
42244
42601
  if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
42245
42602
  else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
42603
+ if (c.unused) printInfo(` ${c.unused}: not used (Verity runs its bundled copy)`);
42246
42604
  }
42247
42605
  printInfo("Setup:");
42248
42606
  printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
@@ -42264,7 +42622,9 @@ function registerDoctorCommand(program2) {
42264
42622
  if (cfg.formal_refused) printWarn(` ${formalRefusalMessage(cfg.formal_refused)}`);
42265
42623
  }
42266
42624
  printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
42267
- printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
42625
+ printInfo(
42626
+ `Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint} (every Claude Code session on this machine)` : "disabled"}`
42627
+ );
42268
42628
  if (process.platform === "win32") printWarn(WINDOWS_STATIC_NOTE);
42269
42629
  else printInfo("Static analysis: Trivy (dependencies, malicious packages, secrets)");
42270
42630
  printInfo("Artifacts:");
@@ -42285,8 +42645,8 @@ function registerDoctorCommand(program2) {
42285
42645
  }
42286
42646
 
42287
42647
  // src/commands/uninstall.ts
42288
- var import_node_fs76 = require("node:fs");
42289
- var import_node_path61 = require("node:path");
42648
+ var import_node_fs78 = require("node:fs");
42649
+ var import_node_path65 = require("node:path");
42290
42650
  function registerUninstallCommand(program2) {
42291
42651
  program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
42292
42652
  const dryRun = opts.dryRun ?? false;
@@ -42295,11 +42655,11 @@ function registerUninstallCommand(program2) {
42295
42655
  const actions = [];
42296
42656
  const skillsRoot = projectPath(".claude/skills");
42297
42657
  for (const name2 of PROJECT_SKILL_NAMES) {
42298
- const dir = (0, import_node_path61.join)(skillsRoot, name2);
42299
- if ((0, import_node_fs76.existsSync)(dir)) {
42658
+ const dir = (0, import_node_path65.join)(skillsRoot, name2);
42659
+ if ((0, import_node_fs78.existsSync)(dir)) {
42300
42660
  actions.push({
42301
42661
  label: `Remove .claude/skills/${name2}/`,
42302
- apply: () => (0, import_node_fs76.rmSync)(dir, { recursive: true, force: true })
42662
+ apply: () => (0, import_node_fs78.rmSync)(dir, { recursive: true, force: true })
42303
42663
  });
42304
42664
  }
42305
42665
  }
@@ -42313,24 +42673,24 @@ function registerUninstallCommand(program2) {
42313
42673
  });
42314
42674
  }
42315
42675
  const verityDir = projectPath(VERITY_DIR);
42316
- if ((0, import_node_fs76.existsSync)(verityDir)) {
42676
+ if ((0, import_node_fs78.existsSync)(verityDir)) {
42317
42677
  actions.push({
42318
42678
  label: `Remove ${VERITY_DIR}/`,
42319
- apply: () => (0, import_node_fs76.rmSync)(verityDir, { recursive: true, force: true })
42679
+ apply: () => (0, import_node_fs78.rmSync)(verityDir, { recursive: true, force: true })
42320
42680
  });
42321
42681
  }
42322
42682
  if (!keepVerityMd) {
42323
42683
  const verityMd = projectPath(VERITY_MD_FILE);
42324
- if ((0, import_node_fs76.existsSync)(verityMd)) {
42684
+ if ((0, import_node_fs78.existsSync)(verityMd)) {
42325
42685
  actions.push({
42326
42686
  label: `Remove ${VERITY_MD_FILE}`,
42327
- apply: () => (0, import_node_fs76.rmSync)(verityMd, { force: true })
42687
+ apply: () => (0, import_node_fs78.rmSync)(verityMd, { force: true })
42328
42688
  });
42329
42689
  }
42330
42690
  }
42331
42691
  const cleanupEmptyDir = (path) => {
42332
- if ((0, import_node_fs76.existsSync)(path) && (0, import_node_fs76.statSync)(path).isDirectory() && (0, import_node_fs76.readdirSync)(path).length === 0) {
42333
- (0, import_node_fs76.rmdirSync)(path);
42692
+ if ((0, import_node_fs78.existsSync)(path) && (0, import_node_fs78.statSync)(path).isDirectory() && (0, import_node_fs78.readdirSync)(path).length === 0) {
42693
+ (0, import_node_fs78.rmdirSync)(path);
42334
42694
  }
42335
42695
  };
42336
42696
  actions.push({
@@ -42341,11 +42701,11 @@ function registerUninstallCommand(program2) {
42341
42701
  }
42342
42702
  });
42343
42703
  const home = process.env.HOME ?? "";
42344
- const globalVerityDir = (0, import_node_path61.join)(home, ".verity");
42345
- if (purgeGlobal && (0, import_node_fs76.existsSync)(globalVerityDir)) {
42704
+ const globalVerityDir = (0, import_node_path65.join)(home, ".verity");
42705
+ if (purgeGlobal && (0, import_node_fs78.existsSync)(globalVerityDir)) {
42346
42706
  actions.push({
42347
42707
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
42348
- apply: () => (0, import_node_fs76.rmSync)(globalVerityDir, { recursive: true, force: true })
42708
+ apply: () => (0, import_node_fs78.rmSync)(globalVerityDir, { recursive: true, force: true })
42349
42709
  });
42350
42710
  }
42351
42711
  if (actions.length === 0) {
@@ -42564,8 +42924,8 @@ function registerTaskCommands(program2) {
42564
42924
  }
42565
42925
 
42566
42926
  // src/commands/reset.ts
42567
- var import_node_fs77 = require("node:fs");
42568
- var import_node_path62 = require("node:path");
42927
+ var import_node_fs79 = require("node:fs");
42928
+ var import_node_path66 = require("node:path");
42569
42929
  function registerResetCommand(program2) {
42570
42930
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
42571
42931
  const globals = program2.opts();
@@ -42605,18 +42965,18 @@ function registerResetCommand(program2) {
42605
42965
  } catch {
42606
42966
  }
42607
42967
  }
42608
- const purged = purgeVerityDir((0, import_node_path62.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
42968
+ const purged = purgeVerityDir((0, import_node_path66.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
42609
42969
  if (purged === null) printError(`Did not purge ${CACHE_DIR}: it is not a directory inside ${VERITY_DIR}.`);
42610
42970
  const stateDir2 = projectPath(VERITY_DIR);
42611
42971
  const filesToClear = [
42612
- (0, import_node_path62.basename)(INTENT_FILE),
42613
- (0, import_node_path62.basename)(HASH_FILE),
42614
- (0, import_node_path62.basename)(BASELINE_SHA_FILE),
42972
+ (0, import_node_path66.basename)(INTENT_FILE),
42973
+ (0, import_node_path66.basename)(HASH_FILE),
42974
+ (0, import_node_path66.basename)(BASELINE_SHA_FILE),
42615
42975
  ".iteration-count",
42616
42976
  ".last-analysis"
42617
42977
  ];
42618
42978
  for (const name2 of filesToClear) {
42619
- if (lstatOrNull((0, import_node_path62.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
42979
+ if (lstatOrNull((0, import_node_path66.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
42620
42980
  printError(`Did not clear ${VERITY_DIR}/${name2}: it is not a regular file inside ${VERITY_DIR}.`);
42621
42981
  }
42622
42982
  }
@@ -42631,29 +42991,29 @@ function registerResetCommand(program2) {
42631
42991
  }
42632
42992
  function lstatOrNull(path) {
42633
42993
  try {
42634
- return (0, import_node_fs77.lstatSync)(path);
42994
+ return (0, import_node_fs79.lstatSync)(path);
42635
42995
  } catch {
42636
42996
  return null;
42637
42997
  }
42638
42998
  }
42639
42999
  function purgeVerityDir(child, select) {
42640
43000
  const verityDir = projectPath(VERITY_DIR);
42641
- const dir = (0, import_node_path62.join)(verityDir, child);
43001
+ const dir = (0, import_node_path66.join)(verityDir, child);
42642
43002
  const parent = lstatOrNull(verityDir);
42643
43003
  const st = lstatOrNull(dir);
42644
43004
  if (!st) return 0;
42645
43005
  if (!parent || parent.isSymbolicLink() || !parent.isDirectory()) return null;
42646
43006
  if (st.isSymbolicLink() || !st.isDirectory()) return null;
42647
43007
  try {
42648
- if ((0, import_node_fs77.realpathSync)(dir) !== (0, import_node_path62.join)((0, import_node_fs77.realpathSync)(verityDir), child)) return null;
43008
+ if ((0, import_node_fs79.realpathSync)(dir) !== (0, import_node_path66.join)((0, import_node_fs79.realpathSync)(verityDir), child)) return null;
42649
43009
  } catch {
42650
43010
  return null;
42651
43011
  }
42652
43012
  let removed = 0;
42653
- for (const entry of (0, import_node_fs77.readdirSync)(dir, { withFileTypes: true })) {
43013
+ for (const entry of (0, import_node_fs79.readdirSync)(dir, { withFileTypes: true })) {
42654
43014
  if (entry.isDirectory() || !select(entry.name)) continue;
42655
43015
  try {
42656
- (0, import_node_fs77.unlinkSync)((0, import_node_path62.join)(dir, entry.name));
43016
+ (0, import_node_fs79.unlinkSync)((0, import_node_path66.join)(dir, entry.name));
42657
43017
  removed++;
42658
43018
  } catch {
42659
43019
  }
@@ -42662,7 +43022,7 @@ function purgeVerityDir(child, select) {
42662
43022
  }
42663
43023
 
42664
43024
  // src/commands/reflect.ts
42665
- var import_node_fs78 = require("node:fs");
43025
+ var import_node_fs80 = require("node:fs");
42666
43026
 
42667
43027
  // src/lib/reflection-globs.ts
42668
43028
  var MAX_GLOBS = 6;
@@ -42700,7 +43060,7 @@ async function writeNodeToDisk(args2) {
42700
43060
  { treePaths: listTrackedFiles(), recordBaseline: false }
42701
43061
  );
42702
43062
  if (written === 0) return false;
42703
- return (0, import_node_fs78.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
43063
+ return (0, import_node_fs80.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
42704
43064
  } catch {
42705
43065
  return false;
42706
43066
  }
@@ -43137,9 +43497,15 @@ function registerRunCommand(program2) {
43137
43497
  }
43138
43498
 
43139
43499
  // src/commands/telemetry.ts
43140
- var import_node_fs79 = require("node:fs");
43500
+ var import_node_fs81 = require("node:fs");
43501
+ var SCOPE = "every Claude Code session on this machine";
43502
+ function declineHeaders(why) {
43503
+ process.stderr.write(`Verity telemetry is off here: ${why} Nothing is exported from this session.
43504
+ `);
43505
+ process.exitCode = 1;
43506
+ }
43141
43507
  async function recordTelemetryChoice(choice) {
43142
- if (!(0, import_node_fs79.existsSync)(projectPath(SETUP_STATE_FILE))) return;
43508
+ if (!(0, import_node_fs81.existsSync)(projectPath(SETUP_STATE_FILE))) return;
43143
43509
  try {
43144
43510
  if ((await readSetupState())?.telemetry === choice) return;
43145
43511
  await writeSetupState({ telemetry: choice });
@@ -43147,8 +43513,8 @@ async function recordTelemetryChoice(choice) {
43147
43513
  }
43148
43514
  }
43149
43515
  function registerTelemetryCommands(program2) {
43150
- const telemetry = program2.command("telemetry").description("Manage Claude Code OpenTelemetry export to Verity (cost & usage)");
43151
- telemetry.command("install").description("Enable Claude Code telemetry export to Verity (writes .claude/settings.local.json)").action(async () => {
43516
+ const telemetry = program2.command("telemetry").description(`Manage Claude Code OpenTelemetry export to Verity (cost & usage) for ${SCOPE}`);
43517
+ telemetry.command("install").description(`Export cost & usage to Verity from ${SCOPE} (writes your Claude Code user settings)`).action(async () => {
43152
43518
  const globals = program2.opts();
43153
43519
  const tokenResult = await resolveToken(globals.token);
43154
43520
  if (!tokenResult.ok) {
@@ -43160,35 +43526,51 @@ function registerTelemetryCommands(program2) {
43160
43526
  printError(urlResult.error);
43161
43527
  process.exit(1);
43162
43528
  }
43163
- const pin = await checkTokenPin(tokenResult.data.token, urlResult.data);
43164
- if (!pin.attach && pin.reason === "unpinnable") {
43165
- printWarn("Your Verity credential does not record which service issued it, so this endpoint");
43166
- printWarn(" cannot be verified \u2014 a repository could point your telemetry elsewhere.");
43167
- printWarn(' Run "verity login" to re-issue the credential; Verity can check it after that.');
43168
- }
43169
- const result = await installTelemetry(urlResult.data);
43529
+ const result = await installTelemetry(urlResult.data, { token: tokenResult.data.token });
43170
43530
  if (!result.ok) {
43171
43531
  printError(result.error);
43172
43532
  process.exit(1);
43173
43533
  }
43174
43534
  await recordTelemetryChoice("enabled");
43175
43535
  printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
43176
- printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (no token stored \u2014 resolved at runtime via otelHeadersHelper)`);
43536
+ printInfo(` scope: ${SCOPE}`);
43537
+ printInfo(` wrote ${displayPath(result.data.settingsPath)} (no token stored \u2014 resolved at runtime via otelHeadersHelper)`);
43538
+ if (result.data.healedLocalKeys.length > 0) {
43539
+ printInfo(` removed ${result.data.healedLocalKeys.length} old telemetry key(s) from ${SETTINGS_LOCAL_FILE2}`);
43540
+ }
43541
+ printInfo(" only Verity projects send data. In other repositories Claude Code shows a startup");
43542
+ printInfo(' warning that "verity telemetry headers" exited 1, and it exports nothing there.');
43177
43543
  printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
43178
43544
  printInfo(" view cost & usage at /usage");
43179
43545
  });
43180
- telemetry.command("headers").description("Print the OTLP Authorization header as JSON (used by Claude Code otelHeadersHelper)").action(async () => {
43546
+ telemetry.command("headers").description("Print the OTLP headers as JSON for Claude Code otelHeadersHelper (exits 1 outside a Verity project)").action(async () => {
43547
+ if (!isTelemetryProject()) {
43548
+ declineHeaders("this directory is not in a Verity project.");
43549
+ return;
43550
+ }
43181
43551
  const globals = program2.opts();
43182
43552
  const tokenResult = await resolveToken(globals.token);
43183
- if (tokenResult.ok) {
43184
- const remote = requestRemote();
43185
- printJsonCompact({
43186
- Authorization: `Bearer ${tokenResult.data.token}`,
43187
- ...remote ? { "X-Verity-Remote": remote } : {}
43188
- });
43189
- } else {
43190
- printJsonCompact({});
43553
+ if (!tokenResult.ok) {
43554
+ declineHeaders('this Verity project has no token on this machine. Run "verity login".');
43555
+ return;
43556
+ }
43557
+ const endpoints = await sessionExportEndpoints();
43558
+ if (endpoints.length === 0) {
43559
+ declineHeaders("no OTLP endpoint is configured for this session.");
43560
+ return;
43561
+ }
43562
+ for (const endpoint of endpoints) {
43563
+ const refusal = await telemetryPinRefusal(tokenResult.data.token, endpoint);
43564
+ if (refusal) {
43565
+ declineHeaders(refusal);
43566
+ return;
43567
+ }
43191
43568
  }
43569
+ const remote = requestRemote();
43570
+ printJsonCompact({
43571
+ Authorization: `Bearer ${tokenResult.data.token}`,
43572
+ ...remote ? { "X-Verity-Remote": remote } : {}
43573
+ });
43192
43574
  });
43193
43575
  telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
43194
43576
  const status = await checkTelemetry();
@@ -43197,21 +43579,36 @@ function registerTelemetryCommands(program2) {
43197
43579
  return;
43198
43580
  }
43199
43581
  if (status.enabled) {
43200
- printInfo(`Telemetry: enabled \u2192 ${status.endpoint}`);
43582
+ printInfo(`Telemetry: enabled \u2192 ${status.endpoint} (${SCOPE})`);
43201
43583
  } else {
43202
43584
  printWarn('Telemetry: disabled. Run "verity telemetry install" to enable cost & usage tracking.');
43203
43585
  }
43586
+ if (status.staleLocalKeys.length > 0) {
43587
+ printWarn(
43588
+ ` ${SETTINGS_LOCAL_FILE2} still holds ${status.staleLocalKeys.length} old Verity telemetry key(s), which Claude Code ignores there. "verity telemetry install" removes them.`
43589
+ );
43590
+ }
43204
43591
  });
43205
- telemetry.command("uninstall").description("Disable Claude Code telemetry export to Verity").action(async () => {
43592
+ telemetry.command("uninstall").description(`Stop the telemetry export to Verity from ${SCOPE}`).action(async () => {
43206
43593
  const result = await uninstallTelemetry();
43207
43594
  if (!result.ok) {
43208
43595
  printError(result.error);
43209
43596
  process.exit(1);
43210
43597
  }
43211
43598
  await recordTelemetryChoice("declined");
43212
- printInfo(
43213
- result.data.removed > 0 ? `Telemetry disabled (${result.data.removed} keys removed from ${SETTINGS_LOCAL_FILE2})` : "Telemetry was not enabled \u2014 nothing to remove."
43214
- );
43599
+ const { removed, settingsPath, foreignEndpoint, healedLocalKeys } = result.data;
43600
+ if (removed > 0) {
43601
+ printInfo(`Telemetry disabled (${removed} keys removed from ${displayPath(settingsPath)})`);
43602
+ } else if (foreignEndpoint) {
43603
+ printInfo(
43604
+ `${displayPath(settingsPath)} exports to another destination, so Verity left it unchanged.`
43605
+ );
43606
+ } else {
43607
+ printInfo("Telemetry was not enabled \u2014 nothing to remove.");
43608
+ }
43609
+ if (healedLocalKeys.length > 0) {
43610
+ printInfo(` removed ${healedLocalKeys.length} old telemetry key(s) from ${SETTINGS_LOCAL_FILE2}`);
43611
+ }
43215
43612
  });
43216
43613
  }
43217
43614
 
@@ -43253,8 +43650,8 @@ function renderSeatUsage(d, now = /* @__PURE__ */ new Date()) {
43253
43650
  lines.push(
43254
43651
  `Session ${utcHm(d.session.start)}\u2013${utcHm(d.session.resets_at)} (resets in ${inText(now, new Date(d.session.resets_at))}) \xB7 week from Mon ${utcDay2(d.week.start)} (resets Mon ${utcDay2(d.week.resets_at)}, 00:00 UTC)`
43255
43652
  );
43256
- if (!d.plan) lines.push("No plan is set for this org: counts only. Plan limits apply from Nov 2.");
43257
- else if (!d.enforced) lines.push("Limits are shown for information. They are not enforced before Nov 2.");
43653
+ if (!d.plan) lines.push("No plan is set for this org: counts only.");
43654
+ else if (!d.enforced) lines.push("Limits are shown for information. They are not enforced yet.");
43258
43655
  const me = d.members.find((m) => m.me);
43259
43656
  if (me) {
43260
43657
  const w = me.weeks[0];
@@ -43358,8 +43755,8 @@ function registerUsageCommand(program2) {
43358
43755
  }
43359
43756
 
43360
43757
  // src/commands/prove.ts
43361
- var import_node_fs80 = require("node:fs");
43362
- var import_node_path63 = require("node:path");
43758
+ var import_node_fs82 = require("node:fs");
43759
+ var import_node_path67 = require("node:path");
43363
43760
 
43364
43761
  // src/lib/prove/hint-pool.ts
43365
43762
  var STOP2 = /* @__PURE__ */ new Set([
@@ -43501,7 +43898,7 @@ function registerProveCommand(program2) {
43501
43898
  process.exit(2);
43502
43899
  }
43503
43900
  const limit = Math.max(1, Math.min(50, Number(opts.limit) || 10));
43504
- let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path63.relative)(root, (0, import_node_path63.resolve)(p)) || ".") });
43901
+ let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path67.relative)(root, (0, import_node_path67.resolve)(p)) || ".") });
43505
43902
  if (opts.hint && paths.length === 0) {
43506
43903
  const hit = hintMatches(root, opts.hint);
43507
43904
  if (hit.files.length > 0) {
@@ -43555,7 +43952,7 @@ function registerProveCommand(program2) {
43555
43952
  });
43556
43953
  prove.command("check [model]").description("Elaborate a model (or every model) with the local Lean toolchain and report each property").option("--timeout <ms>", "Per-model timeout in ms", "120000").option("--json", "Machine-readable output").action((model, opts) => {
43557
43954
  const root = repoRoot();
43558
- if (!(0, import_node_fs80.existsSync)((0, import_node_path63.join)(root, PROOFS_DIR, "lakefile.toml"))) {
43955
+ if (!(0, import_node_fs82.existsSync)((0, import_node_path67.join)(root, PROOFS_DIR, "lakefile.toml"))) {
43559
43956
  printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
43560
43957
  process.exit(2);
43561
43958
  }
@@ -43579,7 +43976,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
43579
43976
  const reports = [];
43580
43977
  for (const f of files) {
43581
43978
  const report = checkModel(tc, root, f, { timeoutMs: Math.max(1e3, Number(opts.timeout) || 12e4) });
43582
- report.file = (0, import_node_path63.relative)(root, f);
43979
+ report.file = (0, import_node_path67.relative)(root, f);
43583
43980
  reports.push(report);
43584
43981
  try {
43585
43982
  writeResult(root, report);
@@ -43617,7 +44014,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
43617
44014
  printError("Not logged in \u2014 replays run on the Verity service. Run `verity login` first.");
43618
44015
  process.exit(2);
43619
44016
  }
43620
- if (!(0, import_node_fs80.existsSync)((0, import_node_path63.join)(root, PROOFS_DIR, "lakefile.toml"))) {
44017
+ if (!(0, import_node_fs82.existsSync)((0, import_node_path67.join)(root, PROOFS_DIR, "lakefile.toml"))) {
43621
44018
  printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
43622
44019
  process.exit(2);
43623
44020
  }
@@ -43648,7 +44045,7 @@ ${report.error ?? ""}`);
43648
44045
  printInfo(opts.property ? `${model} ${opts.property} has no counterexample to replay` : `${model} has no counterexample to replay`);
43649
44046
  return;
43650
44047
  }
43651
- const modelSource = readRegularFileInRoot(root, (0, import_node_path63.relative)(root, file), 512 * 1024);
44048
+ const modelSource = readRegularFileInRoot(root, (0, import_node_path67.relative)(root, file), 512 * 1024);
43652
44049
  if (!opts.json) printInfo(`replaying ${targets.length} counterexample(s) of ${model} in network-less containers (${Math.min(targets.length, REPLAY_CONCURRENCY)} at a time)\u2026`);
43653
44050
  const outcomes = await mapPool(targets, REPLAY_CONCURRENCY, async (p) => {
43654
44051
  const brief = buildReplayBrief(root, report, p, modelSource.ok ? modelSource.content : "");
@@ -43668,7 +44065,7 @@ ${report.error ?? ""}`);
43668
44065
  });
43669
44066
  prove.command("auto [files...]").description("The formal phase the pre-push guard runs: select one risky symbol among changed files, generate a model, check it, replay counterexamples (default files: the commits ahead of upstream)").option("--deadline <ms>", "Budget for the whole phase", "170000").option("--json", "Machine-readable output").action(async (paths, opts) => {
43670
44067
  const root = repoRoot();
43671
- let files = paths.map((p) => (0, import_node_path63.relative)(root, (0, import_node_path63.resolve)(p)));
44068
+ let files = paths.map((p) => (0, import_node_path67.relative)(root, (0, import_node_path67.resolve)(p)));
43672
44069
  if (files.length === 0) {
43673
44070
  const ahead = runGitSync(root, ["diff", "--name-only", "@{upstream}...HEAD"]);
43674
44071
  const fallback = ahead.ok ? ahead : runGitSync(root, ["diff", "--name-only", "HEAD~1..HEAD"]);
@@ -43742,7 +44139,7 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
43742
44139
  let revise;
43743
44140
  if (opts.revise) {
43744
44141
  const existing = resolveModelFile(root, opts.revise);
43745
- const src = existing ? readRegularFileInRoot(root, (0, import_node_path63.relative)(root, existing), 64 * 1024) : null;
44142
+ const src = existing ? readRegularFileInRoot(root, (0, import_node_path67.relative)(root, existing), 64 * 1024) : null;
43746
44143
  if (!src || !src.ok || !opts.note) {
43747
44144
  printError("--revise needs an existing model under Models/ and --note - with what the replay showed on stdin");
43748
44145
  process.exit(2);
@@ -43782,8 +44179,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
43782
44179
  });
43783
44180
  prove.command("waive <file>").description("Dismiss the remembered formal finding(s) on a file for its current bytes \u2014 the accepted-risk path for a formal block").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action((file, opts) => {
43784
44181
  const root = repoRoot();
43785
- const rel0 = (0, import_node_path63.relative)(root, (0, import_node_path63.isAbsolute)(file) ? file : (0, import_node_path63.join)(root, file)).replace(/\\/g, "/");
43786
- const rel = rel0.startsWith("..") || (0, import_node_path63.isAbsolute)(rel0) ? null : rel0;
44182
+ const rel0 = (0, import_node_path67.relative)(root, (0, import_node_path67.isAbsolute)(file) ? file : (0, import_node_path67.join)(root, file)).replace(/\\/g, "/");
44183
+ const rel = rel0.startsWith("..") || (0, import_node_path67.isAbsolute)(rel0) ? null : rel0;
43787
44184
  if (!rel) {
43788
44185
  printError(`${file} is not inside this repository`);
43789
44186
  process.exit(2);
@@ -43799,8 +44196,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
43799
44196
  });
43800
44197
  prove.command("hash <file>").description("Print the sha256 of a file, for a model header (Verity.model \u2026 sha256). Path is REPO-relative, as in the header").action((file) => {
43801
44198
  const root = repoRoot();
43802
- const rel = (0, import_node_path63.relative)(root, (0, import_node_path63.isAbsolute)(file) ? file : (0, import_node_path63.join)(root, file));
43803
- const digest = rel.startsWith("..") || (0, import_node_path63.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
44199
+ const rel = (0, import_node_path67.relative)(root, (0, import_node_path67.isAbsolute)(file) ? file : (0, import_node_path67.join)(root, file));
44200
+ const digest = rel.startsWith("..") || (0, import_node_path67.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
43804
44201
  if (!digest) {
43805
44202
  printError(`Cannot hash ${file}: it must be a regular file inside this repository (not a symlink)`);
43806
44203
  process.exit(2);
@@ -43834,10 +44231,10 @@ function refreshScaffold(root) {
43834
44231
  }
43835
44232
 
43836
44233
  // src/commands/lean.ts
43837
- var import_node_fs81 = require("node:fs");
43838
- var import_node_child_process22 = require("node:child_process");
43839
- var import_node_os13 = require("node:os");
43840
- var import_node_path64 = require("node:path");
44234
+ var import_node_fs83 = require("node:fs");
44235
+ var import_node_child_process23 = require("node:child_process");
44236
+ var import_node_os14 = require("node:os");
44237
+ var import_node_path68 = require("node:path");
43841
44238
  function registerLeanCommand(program2) {
43842
44239
  const lean = program2.command("lean").description("The Lean 4 toolchain formal verification needs (managed download, pinned by this CLI)");
43843
44240
  lean.command("status").option("--json", "Machine-readable output").description("Which toolchain would run, and whether it matches the pinned version").action((opts) => {
@@ -43845,7 +44242,7 @@ function registerLeanCommand(program2) {
43845
44242
  let version = null;
43846
44243
  if (tc) {
43847
44244
  try {
43848
- version = (0, import_node_child_process22.execFileSync)(tc.lake.replace(/lake(\.exe)?$/, "lean$1"), ["--version"], { env: tc.env, encoding: "utf-8", timeout: 1e4 }).trim();
44245
+ version = (0, import_node_child_process23.execFileSync)(tc.lake.replace(/lake(\.exe)?$/, "lean$1"), ["--version"], { env: tc.env, encoding: "utf-8", timeout: 1e4 }).trim();
43849
44246
  } catch {
43850
44247
  version = null;
43851
44248
  }
@@ -43856,7 +44253,7 @@ function registerLeanCommand(program2) {
43856
44253
  const bg = readInstallState();
43857
44254
  const background = bg ? { ...bg, running: bg.state === "running" && installRunning() } : null;
43858
44255
  if (opts.json) {
43859
- printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs81.existsSync)((0, import_node_path64.join)(managed, "bin")), asset: leanAssetName(), background });
44256
+ printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs83.existsSync)((0, import_node_path68.join)(managed, "bin")), asset: leanAssetName(), background });
43860
44257
  return;
43861
44258
  }
43862
44259
  printInfo(`pinned: ${LEAN_TOOLCHAIN}`);
@@ -43873,11 +44270,11 @@ function registerLeanCommand(program2) {
43873
44270
  if (opts.background) process.exit(await runBackgroundInstall());
43874
44271
  const asset = leanAssetName();
43875
44272
  if (!asset) {
43876
- printError(`no Lean release for ${(0, import_node_os13.platform)()}/${(0, import_node_os13.arch)()}`);
44273
+ printError(`no Lean release for ${(0, import_node_os14.platform)()}/${(0, import_node_os14.arch)()}`);
43877
44274
  process.exit(2);
43878
44275
  }
43879
44276
  const dir = pinnedVersionDir();
43880
- if ((0, import_node_fs81.existsSync)((0, import_node_path64.join)(dir, "bin", "lake")) && !opts.force) {
44277
+ if ((0, import_node_fs83.existsSync)((0, import_node_path68.join)(dir, "bin", "lake")) && !opts.force) {
43881
44278
  printInfo(`already installed at ${dir}`);
43882
44279
  return;
43883
44280
  }
@@ -43913,18 +44310,21 @@ function registerLeanCommand(program2) {
43913
44310
  });
43914
44311
  lean.command("remove").description("Delete the Verity-managed toolchain for the pinned version").action(() => {
43915
44312
  const dir = pinnedVersionDir();
43916
- if (!(0, import_node_fs81.existsSync)(dir)) {
44313
+ if (!(0, import_node_fs83.existsSync)(dir)) {
43917
44314
  printInfo("nothing to remove");
43918
44315
  return;
43919
44316
  }
43920
- (0, import_node_fs81.rmSync)(dir, { recursive: true, force: true });
44317
+ (0, import_node_fs83.rmSync)(dir, { recursive: true, force: true });
43921
44318
  printInfo(`removed ${dir}`);
43922
44319
  });
43923
44320
  }
43924
44321
 
43925
44322
  // src/cli.ts
43926
- program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.3211f79").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
43927
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.0-experimental.3211f79");
44323
+ program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.7c5e75b").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
44324
+ const isTelemetryHelper = actionCommand.name() === "headers" && actionCommand.parent?.name() === "telemetry";
44325
+ if (!isTelemetryHelper) {
44326
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.0-experimental.7c5e75b");
44327
+ }
43928
44328
  setUserNamedServiceUrl(program.opts().serviceUrl);
43929
44329
  try {
43930
44330
  await foldLegacyLocalCredential();