@codacy/verity-cli 0.31.1-experimental.be74f71 → 0.31.1-experimental.f2f59c0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/verity.js CHANGED
@@ -675,8 +675,8 @@ var require_option = __commonJS({
675
675
  * @param {(string | string[])} names
676
676
  * @return {Option}
677
677
  */
678
- conflicts(names) {
679
- this.conflictsWith = this.conflictsWith.concat(names);
678
+ conflicts(names2) {
679
+ this.conflictsWith = this.conflictsWith.concat(names2);
680
680
  return this;
681
681
  }
682
682
  /**
@@ -1249,8 +1249,8 @@ var require_command = __commonJS({
1249
1249
  * @param {string} names
1250
1250
  * @return {Command} `this` command for chaining
1251
1251
  */
1252
- arguments(names) {
1253
- names.trim().split(/ +/).forEach((detail) => {
1252
+ arguments(names2) {
1253
+ names2.trim().split(/ +/).forEach((detail) => {
1254
1254
  this.argument(detail);
1255
1255
  });
1256
1256
  return this;
@@ -10395,6 +10395,7 @@ var MAX_DELTA_BYTES = 194560;
10395
10395
  var MAX_FILES = 40;
10396
10396
  var MAX_FILE_BYTES = 51200;
10397
10397
  var DEBOUNCE_SECONDS = 30;
10398
+ var MAX_ITERATIONS = 2;
10398
10399
  var MAX_SPEC_FILES = 6;
10399
10400
  var MAX_SPEC_FILE_BYTES = 512e3;
10400
10401
  var MAX_TOTAL_SPEC_BYTES = 512e3;
@@ -17567,7 +17568,6 @@ function createRun(opts, globals) {
17567
17568
  token: "",
17568
17569
  modeDecision: null,
17569
17570
  sessionIdForMemory: "",
17570
- contextFilePaths: [],
17571
17571
  analysisMode: "standard",
17572
17572
  sessionAuthoredCode: false,
17573
17573
  staticResults: {
@@ -17577,6 +17577,7 @@ function createRun(opts, globals) {
17577
17577
  },
17578
17578
  codeDelta: { files: [], total_lines: 0, total_files: 0, excluded: [] },
17579
17579
  snapshotResult: { has_snapshots: false, diffs: [] },
17580
+ repoContext: null,
17580
17581
  contentHash: null,
17581
17582
  iteration: 1,
17582
17583
  currentCommit: "",
@@ -17658,6 +17659,704 @@ function logToFileOnly(text) {
17658
17659
  `);
17659
17660
  }
17660
17661
 
17662
+ // src/lib/repo-context.ts
17663
+ var import_node_child_process7 = require("node:child_process");
17664
+ var import_node_os3 = require("node:os");
17665
+ function rgInvocations(env = process.env) {
17666
+ const out = [{ cmd: "rg" }];
17667
+ if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
17668
+ out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
17669
+ return out;
17670
+ }
17671
+ var MAX_SYMBOLS = 12;
17672
+ var MAX_SITES = 24;
17673
+ var MAX_SITES_PER_FILE = 3;
17674
+ var MAX_HITS_PER_SYMBOL = 50;
17675
+ var MAX_TEST_SLOTS = 8;
17676
+ var SITE_TEXT_MAX = 160;
17677
+ var ENCLOSING_SCAN_LINES = 200;
17678
+ var RG_TIMEOUT_MS = 1500;
17679
+ var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
17680
+ var STOPLIST = /* @__PURE__ */ new Set([
17681
+ // keyword-shaped captures
17682
+ "if",
17683
+ "for",
17684
+ "while",
17685
+ "switch",
17686
+ "catch",
17687
+ "return",
17688
+ "function",
17689
+ "class",
17690
+ "const",
17691
+ "let",
17692
+ "var",
17693
+ "new",
17694
+ "else",
17695
+ "try",
17696
+ "finally",
17697
+ "throw",
17698
+ "await",
17699
+ "async",
17700
+ "yield",
17701
+ "delete",
17702
+ "typeof",
17703
+ "instanceof",
17704
+ "void",
17705
+ "this",
17706
+ "super",
17707
+ "import",
17708
+ "export",
17709
+ "default",
17710
+ "extends",
17711
+ "implements",
17712
+ "interface",
17713
+ "enum",
17714
+ "type",
17715
+ "public",
17716
+ "private",
17717
+ "protected",
17718
+ "static",
17719
+ "get",
17720
+ "set",
17721
+ "constructor",
17722
+ "def",
17723
+ "elif",
17724
+ "lambda",
17725
+ "with",
17726
+ "pass",
17727
+ "self",
17728
+ "cls",
17729
+ "not",
17730
+ "and",
17731
+ "or",
17732
+ "raise",
17733
+ "except",
17734
+ "func",
17735
+ "defer",
17736
+ "chan",
17737
+ "select",
17738
+ "range",
17739
+ "module",
17740
+ "struct",
17741
+ "trait",
17742
+ "impl",
17743
+ "using",
17744
+ "namespace",
17745
+ // universal noise
17746
+ "main",
17747
+ "init",
17748
+ "index",
17749
+ "data",
17750
+ "value",
17751
+ "result",
17752
+ "item",
17753
+ "name",
17754
+ "key",
17755
+ "run",
17756
+ "test",
17757
+ "setup",
17758
+ "update",
17759
+ "create",
17760
+ "handle",
17761
+ "check",
17762
+ "load",
17763
+ "save",
17764
+ "list",
17765
+ "map",
17766
+ "args",
17767
+ "params",
17768
+ "props",
17769
+ "state",
17770
+ "error",
17771
+ "err",
17772
+ "res",
17773
+ "req",
17774
+ "ctx",
17775
+ "config",
17776
+ "options",
17777
+ "util",
17778
+ "utils",
17779
+ "helper",
17780
+ "render",
17781
+ "build",
17782
+ "parse",
17783
+ "format",
17784
+ "apply",
17785
+ "process",
17786
+ "start",
17787
+ "stop"
17788
+ ]);
17789
+ var TS_RULES = [
17790
+ /\b(?:function|class|interface|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/,
17791
+ /\btype\s+([A-Za-z_][A-Za-z0-9_]*)\s*=/,
17792
+ // const foo = (…) => · const foo = x => · const foo = function.
17793
+ // ⚠ REQUIRES the arrow or `function` ON THE LINE. The first version accepted
17794
+ // any `= (` — and `const started = (rows?.[0] as Row)?.at` is a PARENTHESIZED
17795
+ // CAST, not a function. Replayed over 12 real commits, that one shape put
17796
+ // three local variables into the symbol set per commit. A multi-line arrow
17797
+ // is the accepted false negative; a cast is not an accepted false positive.
17798
+ /\b(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::[^=\n]+)?=\s*(?:async\s+)?(?:function\b|\([^)]*\)(?:\s*:[^=\n]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,
17799
+ // method shape: name(…) { — keyword captures die at the stoplist
17800
+ /^\s*(?:(?:public|private|protected|static|readonly|async|override)\s+)*(?:\*\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*(?::[^{;\n]+)?\s*\{/
17801
+ ];
17802
+ var PY_RULES = [
17803
+ /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/,
17804
+ /^\s*class\s+([A-Za-z_]\w*)/
17805
+ ];
17806
+ var GO_RULES = [
17807
+ /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/,
17808
+ /^type\s+([A-Za-z_]\w*)/
17809
+ ];
17810
+ var CLIKE_RULES = [
17811
+ /\b(?:class|interface|enum|record|struct)\s+([A-Za-z_]\w*)/,
17812
+ // access-modifier method shape: `public async Task<Foo> BarBaz(…`
17813
+ /(?:public|private|protected|internal|static|final|virtual|override|sealed|abstract)[\w<>[\],?\s]*?\s([A-Za-z_]\w*)\s*\(/
17814
+ ];
17815
+ var RB_RULES = [
17816
+ /^\s*def\s+(?:self\.)?([A-Za-z_]\w*)/,
17817
+ /^\s*(?:class|module)\s+([A-Z]\w*)/
17818
+ ];
17819
+ var RS_RULES = [
17820
+ /\bfn\s+([A-Za-z_]\w*)/,
17821
+ /\b(?:struct|enum|trait)\s+([A-Za-z_]\w*)/
17822
+ ];
17823
+ var PHP_RULES = [
17824
+ /\bfunction\s+([A-Za-z_]\w*)/,
17825
+ /\bclass\s+([A-Za-z_]\w*)/
17826
+ ];
17827
+ var C_RULES = [
17828
+ /^(?:static\s+|inline\s+|extern\s+|constexpr\s+)*(?:struct\s+|enum\s+|union\s+|unsigned\s+|const\s+)*[A-Za-z_]\w*(?:\s*[*&]+\s*|\s+)([A-Za-z_]\w*)\s*\([^;]*$/,
17829
+ /\b(?:struct|enum|union|class)\s+([A-Za-z_]\w*)/,
17830
+ /::\s*~?([A-Za-z_]\w*)\s*\([^;]*$/
17831
+ // out-of-line C++ method definition
17832
+ ];
17833
+ var SH_RULES = [
17834
+ /^\s*(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/,
17835
+ /^function\s+([A-Za-z_]\w*)/
17836
+ ];
17837
+ var SQL_RULES = [
17838
+ /\bcreate\s+(?:or\s+replace\s+)?(?:table|view|materialized\s+view|function|procedure|index|trigger|type|policy)\s+(?:if\s+not\s+exists\s+)?(?:[\w".]*\.)?"?([A-Za-z_]\w*)"?/i
17839
+ ];
17840
+ var TF_RULES = [
17841
+ /^\s*(?:resource|data)\s+"[^"]+"\s+"([A-Za-z_]\w*)"/,
17842
+ /^\s*(?:module|variable|output)\s+"([A-Za-z_]\w*)"/
17843
+ ];
17844
+ var SWIFT_RULES = [
17845
+ /\bfunc\s+([A-Za-z_]\w*)/,
17846
+ /\b(?:class|struct|enum|protocol|extension|actor)\s+([A-Za-z_]\w*)/
17847
+ ];
17848
+ var DART_RULES = [
17849
+ /\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/,
17850
+ /^\s*(?:static\s+)?(?:Future<[^>]*>|Stream<[^>]*>|void|int|double|bool|String|num|dynamic|[A-Z]\w*(?:<[^>]*>)?)\s+([a-z_]\w*)\s*\(/
17851
+ ];
17852
+ var LUA_RULES = [
17853
+ /^\s*(?:local\s+)?function\s+(?:[\w.]+[.:])?([A-Za-z_]\w*)/
17854
+ ];
17855
+ var EX_RULES = [
17856
+ /^\s*def(?:p|macro)?\s+([a-z_]\w*)/,
17857
+ /^\s*defmodule\s+(?:[\w.]*\.)?([A-Z]\w*)/
17858
+ ];
17859
+ var PROTO_RULES = [
17860
+ /^\s*(?:message|service|enum)\s+([A-Za-z_]\w*)/,
17861
+ /^\s*rpc\s+([A-Za-z_]\w*)/
17862
+ ];
17863
+ var GRAPHQL_RULES = [
17864
+ /^\s*(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/
17865
+ ];
17866
+ var RULES_BY_EXT = {
17867
+ ts: TS_RULES,
17868
+ tsx: TS_RULES,
17869
+ js: TS_RULES,
17870
+ jsx: TS_RULES,
17871
+ mjs: TS_RULES,
17872
+ cjs: TS_RULES,
17873
+ svelte: TS_RULES,
17874
+ vue: TS_RULES,
17875
+ // script blocks
17876
+ py: PY_RULES,
17877
+ go: GO_RULES,
17878
+ java: CLIKE_RULES,
17879
+ cs: CLIKE_RULES,
17880
+ kt: CLIKE_RULES,
17881
+ scala: CLIKE_RULES,
17882
+ rb: RB_RULES,
17883
+ rs: RS_RULES,
17884
+ php: PHP_RULES,
17885
+ c: C_RULES,
17886
+ cpp: C_RULES,
17887
+ cc: C_RULES,
17888
+ h: C_RULES,
17889
+ hpp: C_RULES,
17890
+ sh: SH_RULES,
17891
+ bash: SH_RULES,
17892
+ zsh: SH_RULES,
17893
+ sql: SQL_RULES,
17894
+ tf: TF_RULES,
17895
+ hcl: TF_RULES,
17896
+ swift: SWIFT_RULES,
17897
+ dart: DART_RULES,
17898
+ lua: LUA_RULES,
17899
+ ex: EX_RULES,
17900
+ exs: EX_RULES,
17901
+ proto: PROTO_RULES,
17902
+ graphql: GRAPHQL_RULES,
17903
+ gql: GRAPHQL_RULES
17904
+ };
17905
+ var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
17906
+ function parseDiffSignals(diff) {
17907
+ const addedRanges = [];
17908
+ const touchPoints = [];
17909
+ const deletedLines = [];
17910
+ let newLine = 0;
17911
+ let oldRemaining = 0;
17912
+ let newRemaining = 0;
17913
+ let runStart = -1;
17914
+ let deletionRun = false;
17915
+ const closeAddedRun = () => {
17916
+ if (runStart >= 0) addedRanges.push([runStart, newLine - 1]);
17917
+ runStart = -1;
17918
+ };
17919
+ const closeDeletionRun = () => {
17920
+ if (deletionRun) touchPoints.push(Math.max(1, newLine));
17921
+ deletionRun = false;
17922
+ };
17923
+ for (const line of diff.split("\n")) {
17924
+ const inHunk = oldRemaining > 0 || newRemaining > 0;
17925
+ if (!inHunk) {
17926
+ closeAddedRun();
17927
+ closeDeletionRun();
17928
+ const header = HUNK_HEADER.exec(line);
17929
+ if (header) {
17930
+ newLine = parseInt(header[3], 10);
17931
+ oldRemaining = header[2] === void 0 ? 1 : parseInt(header[2], 10);
17932
+ newRemaining = header[4] === void 0 ? 1 : parseInt(header[4], 10);
17933
+ if (newRemaining === 0) newLine = Math.max(1, newLine);
17934
+ }
17935
+ continue;
17936
+ }
17937
+ if (line.startsWith("\\")) continue;
17938
+ if (line.startsWith("+") && newRemaining > 0) {
17939
+ deletionRun = false;
17940
+ if (runStart < 0) runStart = newLine;
17941
+ newLine++;
17942
+ newRemaining--;
17943
+ continue;
17944
+ }
17945
+ if (line.startsWith("-") && oldRemaining > 0) {
17946
+ closeAddedRun();
17947
+ deletionRun = true;
17948
+ deletedLines.push(line.slice(1));
17949
+ oldRemaining--;
17950
+ continue;
17951
+ }
17952
+ closeAddedRun();
17953
+ closeDeletionRun();
17954
+ newLine++;
17955
+ if (oldRemaining > 0) oldRemaining--;
17956
+ if (newRemaining > 0) newRemaining--;
17957
+ }
17958
+ closeAddedRun();
17959
+ closeDeletionRun();
17960
+ return { addedRanges, touchPoints, deletedLines };
17961
+ }
17962
+ function declNameOn(line, rules) {
17963
+ for (const r of rules) {
17964
+ const m = r.exec(line);
17965
+ if (m?.[1]) return m[1];
17966
+ }
17967
+ return null;
17968
+ }
17969
+ function acceptable(name) {
17970
+ if (!name || !IDENTIFIER.test(name) || STOPLIST.has(name.toLowerCase())) return false;
17971
+ if (name.length < 3) return false;
17972
+ if (name.length === 3 && name === name.toLowerCase() && !name.includes("_")) return false;
17973
+ return true;
17974
+ }
17975
+ function isMultiSegment(name) {
17976
+ return /[a-z][A-Z]/.test(name) || name.includes("_");
17977
+ }
17978
+ function extractFileSymbols(path, content, signals) {
17979
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
17980
+ const rules = RULES_BY_EXT[ext];
17981
+ if (!rules) return [];
17982
+ if (signals.addedRanges.length === 0 && signals.touchPoints.length === 0 && signals.deletedLines.length === 0) return [];
17983
+ const lines = content.split("\n");
17984
+ const found = [];
17985
+ const seen = /* @__PURE__ */ new Set();
17986
+ const add = (name) => {
17987
+ if (acceptable(name) && !seen.has(name)) {
17988
+ seen.add(name);
17989
+ found.push(name);
17990
+ }
17991
+ };
17992
+ for (const deleted of signals.deletedLines) {
17993
+ add(declNameOn(deleted, rules));
17994
+ }
17995
+ for (const [start, end] of signals.addedRanges) {
17996
+ for (let n = start; n <= Math.min(end, lines.length); n++) {
17997
+ add(declNameOn(lines[n - 1] ?? "", rules));
17998
+ }
17999
+ }
18000
+ const scanStarts = [
18001
+ ...signals.addedRanges.map(([start]) => start),
18002
+ ...signals.touchPoints
18003
+ ];
18004
+ for (const start of scanStarts) {
18005
+ const floor = Math.max(1, start - ENCLOSING_SCAN_LINES);
18006
+ for (let n = Math.min(start, lines.length); n >= floor; n--) {
18007
+ const name = declNameOn(lines[n - 1] ?? "", rules);
18008
+ if (acceptable(name)) {
18009
+ add(name);
18010
+ break;
18011
+ }
18012
+ }
18013
+ }
18014
+ return found;
18015
+ }
18016
+ function rankSymbols(symbols) {
18017
+ return symbols.map((s, i) => ({ s, i })).sort((a, b) => {
18018
+ const seg = Number(isMultiSegment(b.s)) - Number(isMultiSegment(a.s));
18019
+ if (seg !== 0) return seg;
18020
+ if (b.s.length !== a.s.length) return b.s.length - a.s.length;
18021
+ return a.i - b.i;
18022
+ }).slice(0, MAX_SYMBOLS).map((x) => x.s);
18023
+ }
18024
+ var TEST_PATH = /(^|\/)(tests?|specs?|__tests__|e2e)(\/|$)/i;
18025
+ var TEST_FILE = /(\.(test|spec|e2e)\.[^./]+|_test\.[^./]+|_spec\.rb)$/i;
18026
+ var TEST_PY_PREFIX = /(^|\/)test_[^/]+\.py$/i;
18027
+ var IMPORT_LINE = /^\s*(import\s|from\s+\S+\s+import\s|const\s+.*=\s*require\s*\(|require\s*\(|using\s+[\w.]+;|#include|export\s+\{[^}]*\}\s+from|export\s+\*\s+from)/;
18028
+ var BARE_MEMBER_LINE = /^(type\s+)?[A-Za-z_$][\w$]*\s*,?$/;
18029
+ var GO_IMPORT_PATH_LINE = /^"[^"]+",?$/;
18030
+ var SITE_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
18031
+ "ts",
18032
+ "tsx",
18033
+ "js",
18034
+ "jsx",
18035
+ "mjs",
18036
+ "cjs",
18037
+ "py",
18038
+ "go",
18039
+ "java",
18040
+ "kt",
18041
+ "rb",
18042
+ "rs",
18043
+ "scala",
18044
+ "c",
18045
+ "cpp",
18046
+ "cc",
18047
+ "h",
18048
+ "hpp",
18049
+ "cs",
18050
+ "php",
18051
+ "swift",
18052
+ "dart",
18053
+ "lua",
18054
+ "sh",
18055
+ "bash",
18056
+ "zsh",
18057
+ "svelte",
18058
+ "vue",
18059
+ "ex",
18060
+ "exs",
18061
+ // sql/tf carry REAL call sites (SELECT my_function(...), module.name) —
18062
+ // excluded in an earlier round because of migration-comment noise, which the
18063
+ // COMMENT_LINE filter now handles on its own.
18064
+ "sql",
18065
+ "tf",
18066
+ "hcl"
18067
+ ]);
18068
+ var COMMENT_LINE = /^(\/\/|#(?!\[)|\*|\/\*|--\s|<!--)/;
18069
+ function isCodeSiteFile(path) {
18070
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18071
+ return SITE_CODE_EXTENSIONS.has(ext);
18072
+ }
18073
+ function isTestPath(path) {
18074
+ return TEST_PATH.test(path) || TEST_FILE.test(path) || TEST_PY_PREFIX.test(path);
18075
+ }
18076
+ function parseRgLine(line) {
18077
+ const first = line.indexOf(":");
18078
+ if (first <= 0) return null;
18079
+ const second = line.indexOf(":", first + 1);
18080
+ if (second < 0) return null;
18081
+ const n = parseInt(line.slice(first + 1, second), 10);
18082
+ if (!Number.isFinite(n) || n < 1) return null;
18083
+ return { file: line.slice(0, first), line: n, text: line.slice(second + 1) };
18084
+ }
18085
+ var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
18086
+ function wordHit(text, symbol) {
18087
+ let from = 0;
18088
+ for (; ; ) {
18089
+ const at = text.indexOf(symbol, from);
18090
+ if (at < 0) return false;
18091
+ const before = at === 0 ? void 0 : text[at - 1];
18092
+ const after = text[at + symbol.length];
18093
+ if (!isWordChar(before) && !isWordChar(after)) return true;
18094
+ from = at + 1;
18095
+ }
18096
+ }
18097
+ function isContractLine(text, symbol) {
18098
+ for (const kw of ["implements", "extends"]) {
18099
+ let from = 0;
18100
+ for (; ; ) {
18101
+ const at = text.indexOf(kw, from);
18102
+ if (at < 0) break;
18103
+ from = at + 1;
18104
+ const before = at === 0 ? void 0 : text[at - 1];
18105
+ const after = text[at + kw.length];
18106
+ if (isWordChar(before) || isWordChar(after)) continue;
18107
+ let clause = text.slice(at + kw.length);
18108
+ const stop = Math.min(
18109
+ ...[clause.indexOf(";"), clause.indexOf("{")].filter((i) => i >= 0)
18110
+ );
18111
+ if (Number.isFinite(stop)) clause = clause.slice(0, stop);
18112
+ if (wordHit(clause, symbol)) return true;
18113
+ }
18114
+ }
18115
+ return false;
18116
+ }
18117
+ function partitionSites(rgLines, symbols, opts) {
18118
+ const hits = [];
18119
+ const hitCount = /* @__PURE__ */ new Map();
18120
+ for (const line of rgLines) {
18121
+ const hit = parseRgLine(line);
18122
+ if (!hit) continue;
18123
+ const matched = symbols.filter((s) => wordHit(hit.text, s));
18124
+ for (const s of matched) hitCount.set(s, (hitCount.get(s) ?? 0) + 1);
18125
+ if (matched.length === 0) continue;
18126
+ hits.push({ ...hit, symbol: matched[0] });
18127
+ }
18128
+ hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
18129
+ const dropped = symbols.filter((s) => (hitCount.get(s) ?? 0) > MAX_HITS_PER_SYMBOL);
18130
+ const droppedSet = new Set(dropped);
18131
+ const perFile = /* @__PURE__ */ new Map();
18132
+ const callers = [];
18133
+ const tests = [];
18134
+ for (const h of hits) {
18135
+ if (droppedSet.has(h.symbol)) continue;
18136
+ if (opts.sentPaths.has(h.file)) continue;
18137
+ if (opts.isExcluded(h.file)) continue;
18138
+ if (!isCodeSiteFile(h.file)) continue;
18139
+ const text = h.text.trim();
18140
+ if (text.length === 0 || IMPORT_LINE.test(h.text)) continue;
18141
+ if (COMMENT_LINE.test(text)) continue;
18142
+ if (BARE_MEMBER_LINE.test(text) || GO_IMPORT_PATH_LINE.test(text)) continue;
18143
+ const n = perFile.get(h.file) ?? 0;
18144
+ if (n >= MAX_SITES_PER_FILE) continue;
18145
+ const site = {
18146
+ file: h.file,
18147
+ line: h.line,
18148
+ text: text.slice(0, SITE_TEXT_MAX),
18149
+ symbol: h.symbol,
18150
+ // R2 falls out of R1 for free: a word search for `Sym` already matches
18151
+ // `implements Sym` / `extends Sym` lines — classification is all R2 is.
18152
+ ...isContractLine(text, h.symbol) ? { kind: "contract" } : {}
18153
+ };
18154
+ if (isTestPath(h.file)) {
18155
+ if (tests.length < MAX_TEST_SLOTS) {
18156
+ tests.push(site);
18157
+ perFile.set(h.file, n + 1);
18158
+ }
18159
+ } else if (callers.length + tests.length < MAX_SITES) {
18160
+ callers.push(site);
18161
+ perFile.set(h.file, n + 1);
18162
+ }
18163
+ }
18164
+ while (callers.length + tests.length > MAX_SITES) callers.pop();
18165
+ return { callers, tests, dropped };
18166
+ }
18167
+ function buildRepoContext(input) {
18168
+ const started = Date.now();
18169
+ let signalsByPath;
18170
+ if (input.signalsByPath) {
18171
+ if (input.signalsByPath.size === 0) return { state: "absent", reason: "no-diffs" };
18172
+ signalsByPath = input.signalsByPath;
18173
+ } else {
18174
+ if (input.diffs.length === 0) return { state: "absent", reason: "no-diffs" };
18175
+ signalsByPath = /* @__PURE__ */ new Map();
18176
+ for (const d of input.diffs) signalsByPath.set(d.path, parseDiffSignals(d.diff));
18177
+ }
18178
+ const collected = [];
18179
+ const unsupported = /* @__PURE__ */ new Set();
18180
+ const noteUnsupported = (path) => {
18181
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18182
+ if (ext && !RULES_BY_EXT[ext]) unsupported.add(ext);
18183
+ };
18184
+ const deltaPathSet = new Set(input.deltaFiles.map((f) => f.path));
18185
+ for (const f of input.deltaFiles) {
18186
+ const signals = signalsByPath.get(f.path);
18187
+ if (!signals) continue;
18188
+ noteUnsupported(f.path);
18189
+ collected.push(...extractFileSymbols(f.path, f.content, signals));
18190
+ }
18191
+ for (const [path, signals] of signalsByPath) {
18192
+ if (deltaPathSet.has(path)) continue;
18193
+ if (signals.deletedLines.length > 0) {
18194
+ noteUnsupported(path);
18195
+ collected.push(...extractFileSymbols(path, "", signals));
18196
+ }
18197
+ }
18198
+ const unsupportedExts = [...unsupported].sort().slice(0, 8);
18199
+ const audit = unsupportedExts.length > 0 ? { unsupported_exts: unsupportedExts } : {};
18200
+ const symbols = rankSymbols([...new Set(collected)]);
18201
+ if (symbols.length === 0) {
18202
+ const everySupportedFileFoundNothing = unsupportedExts.length > 0;
18203
+ return {
18204
+ state: "absent",
18205
+ reason: everySupportedFileFoundNothing ? "unsupported-language" : "no-symbols",
18206
+ ...audit
18207
+ };
18208
+ }
18209
+ const args = [
18210
+ "-n",
18211
+ "-w",
18212
+ "-F",
18213
+ "--no-heading",
18214
+ "--color",
18215
+ "never",
18216
+ // ⚠ NO `--sort path` — it single-threads rg, and on a large worktree that
18217
+ // is the difference between 17ms and a timeout. Determinism is restored by
18218
+ // sorting the hits in partitionSites instead.
18219
+ //
18220
+ // `-m 8` bounds output PER FILE (shared across all patterns), so a noisy
18221
+ // repo cannot blow the 4MB read buffer and turn the whole feature into
18222
+ // `absent/error`. Cost, accepted: the frequency gate sees per-file-capped
18223
+ // counts, so a symbol concentrated in a handful of files can slip a gate
18224
+ // a full count would have tripped — but the ≤3-sites-per-file cap already
18225
+ // bounds exactly that shape's damage; the gate exists for the many-file
18226
+ // 'init' shape, which 8-per-file still trips (>6 files ⇒ >50).
18227
+ "-m",
18228
+ "8",
18229
+ "--max-columns",
18230
+ "300",
18231
+ "--max-columns-preview",
18232
+ ...symbols.flatMap((s) => ["-e", s]),
18233
+ "-g",
18234
+ "!**/{dist,build,out,vendor,node_modules,.git,coverage,target,__pycache__}/**",
18235
+ "./"
18236
+ ];
18237
+ let res = null;
18238
+ for (const inv of rgInvocations()) {
18239
+ res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
18240
+ ...inv.argv0 ? { argv0: inv.argv0 } : {},
18241
+ cwd: input.cwd ?? process.cwd(),
18242
+ timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
18243
+ maxBuffer: 4 * 1024 * 1024,
18244
+ encoding: "utf8"
18245
+ });
18246
+ if (res.error?.code !== "ENOENT") break;
18247
+ }
18248
+ if (!res || res.error?.code === "ENOENT") {
18249
+ return { state: "absent", reason: "no-tool", symbols, ...audit };
18250
+ }
18251
+ if (res.error) {
18252
+ const code = res.error.code;
18253
+ if (code === "ETIMEDOUT") return { state: "absent", reason: "timeout", symbols, ...audit };
18254
+ return { state: "absent", reason: "error", symbols, ...audit };
18255
+ }
18256
+ if (res.signal) return { state: "absent", reason: "timeout", symbols, ...audit };
18257
+ if (res.status !== 0 && res.status !== 1) return { state: "absent", reason: "error", symbols, ...audit };
18258
+ const lines = (res.stdout ?? "").split("\n").map((l) => l.replace(/^\.\//, "")).filter(Boolean);
18259
+ const { callers, tests, dropped } = partitionSites(lines, symbols, {
18260
+ sentPaths: input.sentPaths,
18261
+ isExcluded: input.isExcluded
18262
+ });
18263
+ if (callers.length === 0 && tests.length === 0) {
18264
+ return {
18265
+ state: "absent",
18266
+ reason: "no-sites",
18267
+ symbols,
18268
+ ...dropped.length > 0 ? { dropped_symbols: dropped } : {},
18269
+ ...audit,
18270
+ elapsed_ms: Date.now() - started
18271
+ };
18272
+ }
18273
+ return {
18274
+ state: "ok",
18275
+ symbols,
18276
+ ...dropped.length > 0 ? { dropped_symbols: dropped } : {},
18277
+ ...audit,
18278
+ callers,
18279
+ tests,
18280
+ elapsed_ms: Date.now() - started
18281
+ };
18282
+ }
18283
+ var MAX_EXCERPTS = 12;
18284
+ var MAX_EXCERPTS_PER_FILE = 2;
18285
+ var EXCERPT_MAX_LINES = 30;
18286
+ var EXCERPT_MAX_CHARS = 2400;
18287
+ var EXCERPT_TOTAL_BYTES = 24576;
18288
+ var EXCERPT_DECL_SCAN = 40;
18289
+ function extractEnclosingExcerpt(content, siteLine, path) {
18290
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18291
+ const rules = RULES_BY_EXT[ext] ?? [];
18292
+ const lines = content.split("\n");
18293
+ if (siteLine < 1 || siteLine > lines.length) return null;
18294
+ let declStart = null;
18295
+ const floor = Math.max(1, siteLine - EXCERPT_DECL_SCAN);
18296
+ for (let n = siteLine; n >= floor; n--) {
18297
+ if (declNameOn(lines[n - 1] ?? "", rules) !== null) {
18298
+ declStart = n;
18299
+ break;
18300
+ }
18301
+ }
18302
+ let start;
18303
+ if (declStart !== null && siteLine - declStart < EXCERPT_MAX_LINES) {
18304
+ start = declStart;
18305
+ } else {
18306
+ start = Math.max(1, siteLine - (EXCERPT_MAX_LINES - 6));
18307
+ }
18308
+ const end = Math.min(lines.length, start + EXCERPT_MAX_LINES - 1);
18309
+ const text = lines.slice(start - 1, end).join("\n").slice(0, EXCERPT_MAX_CHARS);
18310
+ return { start_line: start, text };
18311
+ }
18312
+ function upgradeToExcerpts(rc, opts) {
18313
+ if (rc.state !== "ok") return;
18314
+ const ranked = [
18315
+ ...(rc.callers ?? []).filter((s) => s.kind === "contract"),
18316
+ ...rc.tests ?? [],
18317
+ ...(rc.callers ?? []).filter((s) => s.kind !== "contract")
18318
+ ];
18319
+ const perFile = /* @__PURE__ */ new Map();
18320
+ const contentCache = /* @__PURE__ */ new Map();
18321
+ const excerpts = [];
18322
+ let totalBytes = 0;
18323
+ for (const site of ranked) {
18324
+ if (excerpts.length >= MAX_EXCERPTS) break;
18325
+ const used = perFile.get(site.file) ?? 0;
18326
+ if (used >= MAX_EXCERPTS_PER_FILE) continue;
18327
+ if (!contentCache.has(site.file)) contentCache.set(site.file, opts.readFile(site.file));
18328
+ const content = contentCache.get(site.file);
18329
+ if (content === null || content === void 0) continue;
18330
+ const ex = extractEnclosingExcerpt(content, site.line, site.file);
18331
+ if (!ex) continue;
18332
+ if (totalBytes + ex.text.length > EXCERPT_TOTAL_BYTES) break;
18333
+ excerpts.push({
18334
+ file: site.file,
18335
+ start_line: ex.start_line,
18336
+ symbol: site.symbol,
18337
+ kind: site.kind === "contract" ? "contract" : isTestPath(site.file) ? "test" : "caller",
18338
+ text: ex.text
18339
+ });
18340
+ totalBytes += ex.text.length;
18341
+ perFile.set(site.file, used + 1);
18342
+ }
18343
+ if (excerpts.length > 0) rc.excerpts = excerpts;
18344
+ }
18345
+ function describeRepoContext(rc) {
18346
+ if (rc.state !== "ok") {
18347
+ const exts = rc.unsupported_exts?.length ? ` \xB7 no rules for: ${rc.unsupported_exts.join(", ")}` : "";
18348
+ return `absent (${rc.reason ?? "unknown"})${exts}`;
18349
+ }
18350
+ const parts = [
18351
+ `${rc.symbols?.length ?? 0} symbol(s) \u2192 ${rc.callers?.length ?? 0} caller(s) \xB7 ${rc.tests?.length ?? 0} test(s)`
18352
+ ];
18353
+ if (rc.excerpts?.length) parts.push(`${rc.excerpts.length} excerpt(s)`);
18354
+ if (rc.dropped_symbols?.length) parts.push(`dropped too-common: ${rc.dropped_symbols.join(", ")}`);
18355
+ if (rc.unsupported_exts?.length) parts.push(`no rules for: ${rc.unsupported_exts.join(", ")}`);
18356
+ if (typeof rc.elapsed_ms === "number") parts.push(`${rc.elapsed_ms}ms`);
18357
+ return parts.join(" \xB7 ");
18358
+ }
18359
+
17661
18360
  // src/commands/analyze/evidence-log.ts
17662
18361
  function list(paths, cap = 12) {
17663
18362
  if (paths.length === 0) return "(none)";
@@ -17696,6 +18395,7 @@ function formatRunEvidence(run2, startedAt) {
17696
18395
  }
17697
18396
  out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
17698
18397
  if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
18398
+ if (run2.repoContext) out += row("repo", describeRepoContext(run2.repoContext));
17699
18399
  const withheld = run2.reviewCoverage.notReviewed;
17700
18400
  if (withheld.length > 0) {
17701
18401
  const byReason = /* @__PURE__ */ new Map();
@@ -17777,9 +18477,9 @@ function installRunEvidence(run2) {
17777
18477
  }
17778
18478
 
17779
18479
  // src/lib/git-frame.ts
17780
- var import_node_child_process7 = require("node:child_process");
18480
+ var import_node_child_process8 = require("node:child_process");
17781
18481
  var import_node_fs24 = require("node:fs");
17782
- var import_node_os3 = require("node:os");
18482
+ var import_node_os4 = require("node:os");
17783
18483
  var import_node_path18 = require("node:path");
17784
18484
  var import_node_path19 = require("node:path");
17785
18485
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
@@ -17827,14 +18527,14 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
17827
18527
  if (!m) continue;
17828
18528
  named = true;
17829
18529
  if (m[1] === void 0) {
17830
- dir = (0, import_node_os3.homedir)();
18530
+ dir = (0, import_node_os4.homedir)();
17831
18531
  continue;
17832
18532
  }
17833
18533
  const raw = unquote(m[1]);
17834
18534
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
17835
18535
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
17836
18536
  }
17837
- const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
18537
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
17838
18538
  dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
17839
18539
  }
17840
18540
  const seg = segments[segmentIndex];
@@ -17853,7 +18553,7 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
17853
18553
  if (SHELL_DYNAMIC.test(raw)) {
17854
18554
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
17855
18555
  }
17856
- const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
18556
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
17857
18557
  dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
17858
18558
  }
17859
18559
  }
@@ -17910,7 +18610,7 @@ function parsePushTarget(segment) {
17910
18610
  }
17911
18611
  function gitAt(dir, args) {
17912
18612
  try {
17913
- return (0, import_node_child_process7.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
18613
+ return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
17914
18614
  } catch {
17915
18615
  return "";
17916
18616
  }
@@ -18049,6 +18749,40 @@ function rangeFiles(frame, range) {
18049
18749
  }
18050
18750
  return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
18051
18751
  }
18752
+ function rangeChangeSignals(frame, range, paths) {
18753
+ const out = /* @__PURE__ */ new Map();
18754
+ if (range.kind === "nothing" || paths.length === 0) return out;
18755
+ const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
18756
+ const diff = frameGit(frame, [...args, "--", ...paths]);
18757
+ let current = null;
18758
+ let oldSide = null;
18759
+ let buf = [];
18760
+ const flush = () => {
18761
+ if (current !== null && buf.length > 0) out.set(current, parseDiffSignals(buf.join("\n")));
18762
+ buf = [];
18763
+ };
18764
+ for (const line of diff.split("\n")) {
18765
+ if (line.startsWith("diff --git ")) {
18766
+ flush();
18767
+ current = null;
18768
+ oldSide = null;
18769
+ continue;
18770
+ }
18771
+ const minusM = /^--- (?:a\/)?(.+)$/.exec(line);
18772
+ if (minusM) {
18773
+ oldSide = minusM[1] === "/dev/null" ? null : minusM[1];
18774
+ continue;
18775
+ }
18776
+ const plusM = /^\+\+\+ (?:b\/)?(.+)$/.exec(line);
18777
+ if (plusM) {
18778
+ current = plusM[1] === "/dev/null" ? oldSide : plusM[1];
18779
+ continue;
18780
+ }
18781
+ if (current !== null) buf.push(line);
18782
+ }
18783
+ flush();
18784
+ return out;
18785
+ }
18052
18786
  function rangeMessages(frame, range) {
18053
18787
  if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
18054
18788
  return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
@@ -18108,6 +18842,7 @@ var MAX_COMMANDS = 10;
18108
18842
  var MAX_COMMAND_CHARS = 80;
18109
18843
  var MAX_TOOL_BLOCKS = 200;
18110
18844
  var MAX_SUMMARY_BYTES = 4096;
18845
+ var MAX_SEARCHES_DETAIL = 10;
18111
18846
  var HOME = process.env.HOME ?? "";
18112
18847
  var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
18113
18848
  var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
@@ -18193,6 +18928,7 @@ function buildSummary(lines) {
18193
18928
  let userCommandsTruncated = false;
18194
18929
  let commandsTruncated = false;
18195
18930
  let searches = 0;
18931
+ const searchesDetail = [];
18196
18932
  let subagents = 0;
18197
18933
  let webFetches = 0;
18198
18934
  let totalToolCalls = 0;
@@ -18264,9 +19000,19 @@ function buildSummary(lines) {
18264
19000
  break;
18265
19001
  }
18266
19002
  case "Grep":
18267
- case "Glob":
19003
+ case "Glob": {
18268
19004
  searches++;
19005
+ const pattern = typeof input.pattern === "string" ? input.pattern.slice(0, 120) : "";
19006
+ if (pattern && searchesDetail.length < MAX_SEARCHES_DETAIL) {
19007
+ const scopePath = typeof input.path === "string" ? input.path.slice(0, 200) : void 0;
19008
+ searchesDetail.push({
19009
+ tool: toolName,
19010
+ pattern,
19011
+ ...scopePath ? { path: scopePath } : {}
19012
+ });
19013
+ }
18269
19014
  break;
19015
+ }
18270
19016
  case "Agent":
18271
19017
  case "Task":
18272
19018
  case "Workflow":
@@ -18301,6 +19047,7 @@ function buildSummary(lines) {
18301
19047
  ...cappedOut(filesCreated, MAX_CREATED_LIST)
18302
19048
  ],
18303
19049
  searches,
19050
+ ...searchesDetail.length > 0 ? { searches_detail: searchesDetail } : {},
18304
19051
  commands,
18305
19052
  ...commandsTruncated ? { commands_truncated: true } : {},
18306
19053
  user_commands: userCommands,
@@ -18311,6 +19058,9 @@ function buildSummary(lines) {
18311
19058
  turn_messages: turnMessages,
18312
19059
  turn_duration_ms: turnDurationMs
18313
19060
  };
19061
+ if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
19062
+ delete summary.searches_detail;
19063
+ }
18314
19064
  if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
18315
19065
  summary.commands = [];
18316
19066
  summary.commands_truncated = true;
@@ -18614,7 +19364,7 @@ function channelSilence(input) {
18614
19364
  // src/lib/cli-version.ts
18615
19365
  function cliVersion() {
18616
19366
  try {
18617
- return true ? "0.31.1-experimental.be74f71" : "dev";
19367
+ return true ? "0.31.1-experimental.f2f59c0" : "dev";
18618
19368
  } catch {
18619
19369
  return "dev";
18620
19370
  }
@@ -18654,7 +19404,7 @@ async function sendSkipBeacon(ctx, reason) {
18654
19404
  }
18655
19405
 
18656
19406
  // src/lib/static-analysis.ts
18657
- var import_node_child_process8 = require("node:child_process");
19407
+ var import_node_child_process9 = require("node:child_process");
18658
19408
  var import_node_fs26 = require("node:fs");
18659
19409
  var SEVERITY_ORDER = {
18660
19410
  Error: 0,
@@ -18667,7 +19417,7 @@ var SEVERITY_ORDER = {
18667
19417
  };
18668
19418
  function isCodacyAvailable() {
18669
19419
  try {
18670
- (0, import_node_child_process8.execSync)("which codacy-analysis", { stdio: "pipe" });
19420
+ (0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
18671
19421
  return true;
18672
19422
  } catch {
18673
19423
  return false;
@@ -18709,7 +19459,7 @@ function runCodacyAnalysis(files) {
18709
19459
  }
18710
19460
  });
18711
19461
  if (existingFiles.length === 0) return empty;
18712
- const proc = (0, import_node_child_process8.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
19462
+ const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
18713
19463
  encoding: "utf-8",
18714
19464
  maxBuffer: 10 * 1024 * 1024
18715
19465
  });
@@ -19214,7 +19964,6 @@ async function mode(run2) {
19214
19964
  const { opts, globals } = run2;
19215
19965
  const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
19216
19966
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
19217
- let contextFilePaths = [];
19218
19967
  let predictedMode;
19219
19968
  try {
19220
19969
  const memoryPath2 = sessionIdForMemory ? `/memory?session_id=${encodeURIComponent(sessionIdForMemory)}` : "/memory";
@@ -19228,9 +19977,6 @@ async function mode(run2) {
19228
19977
  cmd: "analyze_context"
19229
19978
  });
19230
19979
  if (memoryResult.ok) {
19231
- if (Array.isArray(memoryResult.data.context_files)) {
19232
- contextFilePaths = memoryResult.data.context_files;
19233
- }
19234
19980
  const rawMode = memoryResult.data.predicted_mode;
19235
19981
  if (rawMode && ["standard", "plan", "debug", "skip"].includes(rawMode)) {
19236
19982
  predictedMode = rawMode;
@@ -19279,7 +20025,7 @@ async function mode(run2) {
19279
20025
  turnAuthoredCode ? "capacity" : void 0
19280
20026
  );
19281
20027
  }
19282
- Object.assign(run2, { analysisMode, contextFilePaths, sessionAuthoredCode, sessionIdForMemory });
20028
+ Object.assign(run2, { analysisMode, sessionAuthoredCode, sessionIdForMemory });
19283
20029
  }
19284
20030
 
19285
20031
  // src/lib/fold.ts
@@ -19917,15 +20663,52 @@ function pruneStaleCache() {
19917
20663
 
19918
20664
  // src/lib/context-files.ts
19919
20665
  var import_node_fs30 = require("node:fs");
20666
+ var import_node_os5 = require("node:os");
19920
20667
  var MAX_CONTEXT_FILES = 10;
19921
20668
  var MAX_CONTEXT_FILE_BYTES = 10240;
19922
- var MAX_CONTEXT_TOTAL_BYTES = 51200;
19923
- function gatherContextFiles(contextPaths, deltaFiles) {
20669
+ var MAX_CONTEXT_TOTAL_BYTES = 24576;
20670
+ function readSetContextPaths(summary, deltaFiles) {
20671
+ const reads = summary?.files_read ?? [];
20672
+ if (reads.length === 0) return [];
20673
+ const root = process.cwd().replace(/\/+$/, "");
20674
+ const home = (0, import_node_os5.homedir)();
20675
+ const toRepoRelative2 = (p) => {
20676
+ if (!p) return null;
20677
+ let abs;
20678
+ if (p.startsWith("/")) {
20679
+ abs = p;
20680
+ } else {
20681
+ const rebuilt = `${home}/${p}`;
20682
+ abs = rebuilt.startsWith(`${root}/`) ? rebuilt : `${root}/${p}`;
20683
+ }
20684
+ if (!abs.startsWith(`${root}/`)) return null;
20685
+ return abs.slice(root.length + 1);
20686
+ };
20687
+ const authored = /* @__PURE__ */ new Set();
20688
+ for (const p of [...summary?.files_edited ?? [], ...summary?.files_created ?? []]) {
20689
+ const rel = toRepoRelative2(p);
20690
+ if (rel) authored.add(rel);
20691
+ }
20692
+ for (const f of deltaFiles) authored.add(f.path);
20693
+ const out = [];
20694
+ const seen = /* @__PURE__ */ new Set();
20695
+ for (const p of reads) {
20696
+ const rel = toRepoRelative2(p);
20697
+ if (!rel || seen.has(rel) || authored.has(rel)) continue;
20698
+ seen.add(rel);
20699
+ const ext = rel.split(".").pop()?.toLowerCase() ?? "";
20700
+ if (!ANALYZABLE_EXTENSIONS.has(ext) && !REVIEWABLE_EXTENSIONS.has(ext)) continue;
20701
+ out.push(rel);
20702
+ }
20703
+ return out;
20704
+ }
20705
+ function gatherContextFiles(contextPaths, deltaFiles, opts) {
20706
+ const fileCap = Math.max(0, Math.min(MAX_CONTEXT_FILES, opts?.maxFiles ?? MAX_CONTEXT_FILES));
19924
20707
  const deltaPaths = new Set(deltaFiles.map((f) => f.path));
19925
20708
  const result = [];
19926
20709
  let totalBytes = 0;
19927
20710
  for (const filePath of contextPaths) {
19928
- if (result.length >= MAX_CONTEXT_FILES) break;
20711
+ if (result.length >= fileCap) break;
19929
20712
  if (deltaPaths.has(filePath)) continue;
19930
20713
  if (isVerityOwnedPath(filePath)) {
19931
20714
  logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
@@ -19983,9 +20766,14 @@ function gatherContextFiles(contextPaths, deltaFiles) {
19983
20766
 
19984
20767
  // src/commands/analyze/phases/07-context-files.ts
19985
20768
  async function contextFiles(run2) {
19986
- const { codeDelta, contextFilePaths } = run2;
19987
- const { kept: externalContext } = partitionVerityOwned(contextFilePaths ?? []);
19988
- const contextFiles2 = gatherContextFiles(externalContext, codeDelta.files);
20769
+ const { codeDelta } = run2;
20770
+ const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
20771
+ const { kept: externalContext } = partitionVerityOwned(readSet);
20772
+ const ig = loadVerityIgnore();
20773
+ const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
20774
+ const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
20775
+ maxFiles: MAX_FILES - codeDelta.files.length
20776
+ });
19989
20777
  for (const f of codeDelta.files) {
19990
20778
  f.role = "delta";
19991
20779
  }
@@ -19997,6 +20785,30 @@ async function contextFiles(run2) {
19997
20785
  });
19998
20786
  }
19999
20787
 
20788
+ // src/commands/analyze/phases/07b-repo-context.ts
20789
+ async function repoContext(run2) {
20790
+ const { codeDelta, snapshotResult } = run2;
20791
+ const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
20792
+ const sentPaths = new Set(codeDelta.files.map((f) => f.path));
20793
+ const ig = loadVerityIgnore();
20794
+ const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
20795
+ run2.repoContext = buildRepoContext({
20796
+ deltaFiles,
20797
+ diffs: snapshotResult.diffs,
20798
+ sentPaths,
20799
+ isExcluded
20800
+ });
20801
+ logEvent("repo_context", {
20802
+ state: run2.repoContext.state,
20803
+ reason: run2.repoContext.reason ?? null,
20804
+ symbols: run2.repoContext.symbols?.length ?? 0,
20805
+ dropped: run2.repoContext.dropped_symbols?.length ?? 0,
20806
+ callers: run2.repoContext.callers?.length ?? 0,
20807
+ tests: run2.repoContext.tests?.length ?? 0,
20808
+ elapsed_ms: run2.repoContext.elapsed_ms ?? null
20809
+ });
20810
+ }
20811
+
20000
20812
  // src/lib/seed-runner.ts
20001
20813
  var import_promises11 = require("node:fs/promises");
20002
20814
  var import_node_fs31 = require("node:fs");
@@ -20630,7 +21442,7 @@ function isExplicitlyAutonomous(env = process.env) {
20630
21442
  }
20631
21443
 
20632
21444
  // src/lib/task-context.ts
20633
- var import_node_child_process9 = require("node:child_process");
21445
+ var import_node_child_process10 = require("node:child_process");
20634
21446
  var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
20635
21447
  var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
20636
21448
  function parseLinkedIssue(sources) {
@@ -20646,7 +21458,7 @@ function parseLinkedIssue(sources) {
20646
21458
  }
20647
21459
  function safeExec(cmd, timeout) {
20648
21460
  try {
20649
- return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
21461
+ return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
20650
21462
  } catch {
20651
21463
  return "";
20652
21464
  }
@@ -20853,6 +21665,9 @@ async function buildRequest(run2) {
20853
21665
  if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
20854
21666
  requestBody.snapshot_diffs = snapshotResult.diffs;
20855
21667
  }
21668
+ if (run2.repoContext) {
21669
+ requestBody.repo_context = run2.repoContext;
21670
+ }
20856
21671
  const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
20857
21672
  const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
20858
21673
  const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
@@ -21701,6 +22516,8 @@ var PIPELINE = [
21701
22516
  // ← THE NARROWING. what gets sent, and why not the rest
21702
22517
  ["contextFiles", contextFiles],
21703
22518
  // supporting files, merged INTO the delta array
22519
+ ["repoContext", repoContext],
22520
+ // R1/R3 — call sites of changed symbols, one line each
21704
22521
  ["memoryManifest", memoryManifest],
21705
22522
  // knowledge-graph manifest + one-time auto-seed
21706
22523
  ["foldTranscript", foldTranscript],
@@ -21717,7 +22534,7 @@ var PIPELINE = [
21717
22534
  // say it — stderr, stdout, disk
21718
22535
  ];
21719
22536
  function registerAnalyzeCommand(program2) {
21720
- program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
22537
+ program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", String(DEBOUNCE_SECONDS)).option("--max-iterations <n>", "Force PASS after N FAIL cycles", String(MAX_ITERATIONS)).option("--max-files <n>", "Max files to send for review", String(MAX_FILES)).option("--max-file-size <bytes>", "Skip files larger than N bytes", String(MAX_FILE_BYTES)).option("--max-total-size <bytes>", "Stop collecting files at N total bytes", String(MAX_DELTA_BYTES)).option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
21721
22538
  const globals = program2.opts();
21722
22539
  try {
21723
22540
  await runAnalyze(opts, globals);
@@ -22116,6 +22933,7 @@ function coverageBlock(c) {
22116
22933
  const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
22117
22934
  lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
22118
22935
  lines.push(` Tree: ${tree}`);
22936
+ if (c.repoContext) lines.push(` Repo context: ${describeRepoContext(c.repoContext)}`);
22119
22937
  for (const f of c.sent) lines.push(` - ${f}`);
22120
22938
  if (c.excluded.length > 0) {
22121
22939
  lines.push(` Excluded (${c.excluded.length}):`);
@@ -22182,6 +23000,39 @@ async function runGuard(opts, globals) {
22182
23000
  statedIntent,
22183
23001
  buildGuardCoverage(files, codeDelta, frame, range)
22184
23002
  );
23003
+ try {
23004
+ const ig = loadVerityIgnore();
23005
+ const repoContext2 = buildRepoContext({
23006
+ deltaFiles: codeDelta.files,
23007
+ diffs: [],
23008
+ signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
23009
+ sentPaths: new Set(codeDelta.files.map((f) => f.path)),
23010
+ isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
23011
+ cwd: frame.worktreeRoot ?? process.cwd()
23012
+ });
23013
+ upgradeToExcerpts(repoContext2, {
23014
+ readFile: (rel) => {
23015
+ try {
23016
+ return (0, import_node_fs38.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
23017
+ } catch {
23018
+ return null;
23019
+ }
23020
+ }
23021
+ });
23022
+ requestBody.repo_context = repoContext2;
23023
+ logEvent("repo_context", {
23024
+ moment,
23025
+ state: repoContext2.state,
23026
+ reason: repoContext2.reason ?? null,
23027
+ symbols: repoContext2.symbols?.length ?? 0,
23028
+ callers: repoContext2.callers?.length ?? 0,
23029
+ tests: repoContext2.tests?.length ?? 0,
23030
+ excerpts: repoContext2.excerpts?.length ?? 0,
23031
+ elapsed_ms: repoContext2.elapsed_ms ?? null
23032
+ });
23033
+ } catch (e) {
23034
+ logEvent("repo_context", { moment, state: "absent", reason: "exception", message: e.message });
23035
+ }
22185
23036
  const coverage = {
22186
23037
  moment,
22187
23038
  root: frame.worktreeRoot,
@@ -22189,7 +23040,8 @@ async function runGuard(opts, globals) {
22189
23040
  linked: frame.isLinkedWorktree,
22190
23041
  range: describeRange(range),
22191
23042
  sent: codeDelta.files.map((f) => f.path),
22192
- excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
23043
+ excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason })),
23044
+ ...requestBody.repo_context ? { repoContext: requestBody.repo_context } : {}
22193
23045
  };
22194
23046
  logToFileOnly(coverageBlock(coverage));
22195
23047
  const reviewStart = Date.now();
@@ -22436,10 +23288,10 @@ function registerWaiveCommand(program2) {
22436
23288
  }
22437
23289
 
22438
23290
  // src/commands/init.ts
22439
- var import_node_fs44 = require("node:fs");
22440
- var import_promises14 = require("node:fs/promises");
22441
- var import_node_path29 = require("node:path");
22442
- var import_node_child_process13 = require("node:child_process");
23291
+ var import_node_fs45 = require("node:fs");
23292
+ var import_promises15 = require("node:fs/promises");
23293
+ var import_node_path30 = require("node:path");
23294
+ var import_node_child_process14 = require("node:child_process");
22443
23295
 
22444
23296
  // src/lib/banner.ts
22445
23297
  var WORDMARK = [
@@ -22520,11 +23372,11 @@ function printPhase(n, of, title, subtitle) {
22520
23372
  var import_node_fs42 = require("node:fs");
22521
23373
 
22522
23374
  // src/lib/prereqs.ts
22523
- var import_node_child_process10 = require("node:child_process");
23375
+ var import_node_child_process11 = require("node:child_process");
22524
23376
  var MIN_NODE_MAJOR = 20;
22525
23377
  function which(bin) {
22526
23378
  try {
22527
- const out = (0, import_node_child_process10.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23379
+ const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22528
23380
  return out || null;
22529
23381
  } catch {
22530
23382
  return null;
@@ -22546,7 +23398,7 @@ function checkNode() {
22546
23398
  function checkGit() {
22547
23399
  let detail = "";
22548
23400
  try {
22549
- detail = (0, import_node_child_process10.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23401
+ detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22550
23402
  } catch {
22551
23403
  return {
22552
23404
  id: "git",
@@ -22584,7 +23436,7 @@ function checkAnalysisCli() {
22584
23436
  var INSTALL_TIMEOUT_MS = 12e4;
22585
23437
  function run(command, args, opts = {}) {
22586
23438
  return new Promise((resolve4) => {
22587
- const child = (0, import_node_child_process10.spawn)(command, args, {
23439
+ const child = (0, import_node_child_process11.spawn)(command, args, {
22588
23440
  stdio: opts.inherit ? "inherit" : "pipe",
22589
23441
  timeout: INSTALL_TIMEOUT_MS
22590
23442
  });
@@ -22636,7 +23488,7 @@ async function checkPrereqs(opts = {}) {
22636
23488
  var import_promises12 = require("node:fs/promises");
22637
23489
 
22638
23490
  // src/lib/gitignore.ts
22639
- var import_node_child_process11 = require("node:child_process");
23491
+ var import_node_child_process12 = require("node:child_process");
22640
23492
  var import_node_fs40 = require("node:fs");
22641
23493
  var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
22642
23494
  var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
@@ -22653,7 +23505,7 @@ var VERITY_GITIGNORE_BLOCK = [
22653
23505
  var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
22654
23506
  function isIgnored2(path) {
22655
23507
  try {
22656
- (0, import_node_child_process11.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
23508
+ (0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
22657
23509
  return true;
22658
23510
  } catch (err) {
22659
23511
  return err.status === 1 ? false : null;
@@ -22700,7 +23552,7 @@ function ensureVerityGitignore() {
22700
23552
  function committedVerityState() {
22701
23553
  let out = "";
22702
23554
  try {
22703
- out = (0, import_node_child_process11.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
23555
+ out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
22704
23556
  } catch {
22705
23557
  return [];
22706
23558
  }
@@ -22710,10 +23562,10 @@ function untrackVerityState() {
22710
23562
  const tracked = committedVerityState();
22711
23563
  if (tracked.length === 0) return "none";
22712
23564
  try {
22713
- (0, import_node_child_process11.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
23565
+ (0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
22714
23566
  for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
22715
23567
  try {
22716
- (0, import_node_child_process11.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
23568
+ (0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
22717
23569
  } catch {
22718
23570
  }
22719
23571
  }
@@ -22849,8 +23701,19 @@ async function buildReport() {
22849
23701
  if (c.status !== "ok" && c.remedy) next.push(c.remedy);
22850
23702
  }
22851
23703
  if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
22852
- if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
22853
- next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
23704
+ const missing = [
23705
+ !artifacts.standard && ".verity/standard.yaml",
23706
+ !artifacts.analysisConfig && ".codacy/codacy.config.json",
23707
+ !artifacts.verityMd && "VERITY.md"
23708
+ ].filter((x) => !!x);
23709
+ if (missing.length === 3) {
23710
+ next.push("Run /verity-setup in Claude Code \u2014 it synthesizes the Standard, the analysis config, and VERITY.md.");
23711
+ } else if (missing.length === 1 && !artifacts.analysisConfig) {
23712
+ next.push(
23713
+ 'No analysis config, so static analysis runs nothing. Run /verity-setup in Claude Code to write one (it keeps the Standard you already have), or have a teammate run "verity config push".'
23714
+ );
23715
+ } else if (missing.length > 0) {
23716
+ next.push(`Missing ${missing.join(" and ")}. Run "/verity-setup --force" in Claude Code to regenerate.`);
22854
23717
  }
22855
23718
  const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
22856
23719
  if (noAnalysisMoment) {
@@ -22917,14 +23780,14 @@ function registerDoctorCommand(program2) {
22917
23780
  // src/commands/migrate.ts
22918
23781
  var import_node_fs43 = require("node:fs");
22919
23782
  var import_node_path28 = require("node:path");
22920
- var import_node_child_process12 = require("node:child_process");
23783
+ var import_node_child_process13 = require("node:child_process");
22921
23784
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
22922
23785
  function defaultNpmRemover(pkg) {
22923
- (0, import_node_child_process12.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
23786
+ (0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
22924
23787
  }
22925
23788
  function isGitTracked(cwd, relPath) {
22926
23789
  try {
22927
- (0, import_node_child_process12.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
23790
+ (0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
22928
23791
  return true;
22929
23792
  } catch {
22930
23793
  return false;
@@ -22932,7 +23795,7 @@ function isGitTracked(cwd, relPath) {
22932
23795
  }
22933
23796
  function isGitRepo(cwd) {
22934
23797
  try {
22935
- (0, import_node_child_process12.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
23798
+ (0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
22936
23799
  return true;
22937
23800
  } catch {
22938
23801
  return false;
@@ -22972,7 +23835,7 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
22972
23835
  );
22973
23836
  }
22974
23837
  try {
22975
- (0, import_node_child_process12.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
23838
+ (0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
22976
23839
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
22977
23840
  moved = true;
22978
23841
  } catch {
@@ -23051,7 +23914,7 @@ function migrateStandardFile(root, actions) {
23051
23914
  let moved = false;
23052
23915
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
23053
23916
  try {
23054
- (0, import_node_child_process12.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
23917
+ (0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
23055
23918
  moved = true;
23056
23919
  } catch {
23057
23920
  }
@@ -23126,7 +23989,7 @@ function readFileSyncSafe(path) {
23126
23989
  }
23127
23990
  function hasStagedChanges(root) {
23128
23991
  try {
23129
- (0, import_node_child_process12.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
23992
+ (0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
23130
23993
  return false;
23131
23994
  } catch {
23132
23995
  return true;
@@ -23212,12 +24075,168 @@ function registerMigrateCommand(program2) {
23212
24075
  }
23213
24076
 
23214
24077
  // src/lib/prompt.ts
23215
- var readline2 = __toESM(require("node:readline/promises"));
24078
+ var readline3 = __toESM(require("node:readline/promises"));
24079
+
24080
+ // src/lib/select.ts
24081
+ var readline2 = __toESM(require("node:readline"));
24082
+ function keyToAction(str, key) {
24083
+ const name = key?.name;
24084
+ if (key?.ctrl && (name === "c" || str === "")) return { type: "interrupt" };
24085
+ if (key?.ctrl && (name === "d" || str === "")) return { type: "eof" };
24086
+ switch (name) {
24087
+ case "up":
24088
+ return { type: "move", delta: -1 };
24089
+ case "down":
24090
+ return { type: "move", delta: 1 };
24091
+ case "k":
24092
+ return key?.ctrl ? null : { type: "move", delta: -1 };
24093
+ case "j":
24094
+ return key?.ctrl ? null : { type: "move", delta: 1 };
24095
+ case "space":
24096
+ return { type: "toggle" };
24097
+ case "return":
24098
+ case "enter":
24099
+ return { type: "confirm" };
24100
+ default:
24101
+ break;
24102
+ }
24103
+ if (str === " ") return { type: "toggle" };
24104
+ if (str === "\r" || str === "\n") return { type: "confirm" };
24105
+ if (str && /^[1-9]$/.test(str)) return { type: "jump", index: Number(str) - 1 };
24106
+ return null;
24107
+ }
24108
+ function applyKey(state, action, count, mode2) {
24109
+ switch (action.type) {
24110
+ case "move": {
24111
+ const cursor = (state.cursor + action.delta + count) % count;
24112
+ return { status: "open", state: { cursor, chosen: state.chosen } };
24113
+ }
24114
+ case "jump": {
24115
+ if (action.index >= count) return { status: "open", state };
24116
+ return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
24117
+ }
24118
+ case "toggle": {
24119
+ if (mode2 === "single") {
24120
+ return { status: "open", state: { cursor: state.cursor, chosen: /* @__PURE__ */ new Set([state.cursor]) } };
24121
+ }
24122
+ const chosen = new Set(state.chosen);
24123
+ if (chosen.has(state.cursor)) chosen.delete(state.cursor);
24124
+ else chosen.add(state.cursor);
24125
+ return { status: "open", state: { cursor: state.cursor, chosen } };
24126
+ }
24127
+ case "confirm": {
24128
+ if (mode2 === "single") return { status: "confirmed", indices: [state.cursor] };
24129
+ if (state.chosen.size === 0) {
24130
+ return {
24131
+ status: "open",
24132
+ state: { ...state, hint: "Select at least one \u2014 space toggles the option under the cursor." }
24133
+ };
24134
+ }
24135
+ return { status: "confirmed", indices: [...state.chosen].sort((a, b) => a - b) };
24136
+ }
24137
+ case "interrupt":
24138
+ return { status: "interrupt" };
24139
+ case "eof":
24140
+ return { status: "eof" };
24141
+ }
24142
+ }
24143
+ var GREEN3 = "\x1B[0;32m";
24144
+ var DIM4 = "\x1B[2m";
24145
+ var BOLD2 = "\x1B[1m";
24146
+ var RESET3 = "\x1B[0m";
24147
+ function renderSelect(question, choices, state, mode2, color = colorEnabled()) {
24148
+ const paint = (text, code) => color ? `${code}${text}${RESET3}` : text;
24149
+ const lines = ["", ` ${question}`];
24150
+ const labelOf = (c) => `${c.label}${c.recommended ? " (recommended)" : ""}`;
24151
+ const gutter = Math.max(...choices.map((c) => labelOf(c).length));
24152
+ choices.forEach((choice, i) => {
24153
+ const here = i === state.cursor;
24154
+ const marker = state.chosen.has(i) ? "\u25C9" : "\u25CB";
24155
+ const cursor = here ? "\u276F" : " ";
24156
+ const label2 = labelOf(choice);
24157
+ const pad = choice.hint ? " ".repeat(gutter - label2.length) : "";
24158
+ const hint = choice.hint ? `${pad} ${paint(choice.hint, DIM4)}` : "";
24159
+ lines.push(` ${cursor} ${marker} ${here ? paint(label2, BOLD2) : label2}${hint}`);
24160
+ });
24161
+ const keys = mode2 === "multi" ? "\u2191\u2193 move \xB7 space toggle \xB7 enter confirm" : "\u2191\u2193 move \xB7 enter confirm";
24162
+ lines.push(` ${paint(state.hint ?? keys, state.hint ? GREEN3 : DIM4)}`);
24163
+ return lines;
24164
+ }
24165
+ function runSelect(opts) {
24166
+ const input = opts.input ?? process.stdin;
24167
+ const output = opts.output ?? process.stdout;
24168
+ const { choices, mode: mode2 } = opts;
24169
+ if (typeof input.setRawMode !== "function" || !input.isTTY) return Promise.resolve(null);
24170
+ const initialIdx = choices.map((c, i) => opts.initial.includes(c.id) ? i : -1).filter((i) => i >= 0);
24171
+ let state = {
24172
+ cursor: initialIdx[0] ?? 0,
24173
+ chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
24174
+ };
24175
+ return new Promise((resolve4) => {
24176
+ let painted = 0;
24177
+ let settled = false;
24178
+ const draw = () => {
24179
+ if (painted > 0) {
24180
+ readline2.moveCursor(output, 0, -painted);
24181
+ readline2.cursorTo(output, 0);
24182
+ readline2.clearScreenDown(output);
24183
+ }
24184
+ const lines = renderSelect(opts.question, choices, state, mode2);
24185
+ output.write(lines.join("\n") + "\n");
24186
+ painted = lines.length;
24187
+ };
24188
+ const onKey = (str, key) => {
24189
+ const action = keyToAction(str, key);
24190
+ if (!action) return;
24191
+ const outcome = applyKey(state, action, choices.length, mode2);
24192
+ if (outcome.status === "open") {
24193
+ state = outcome.state;
24194
+ draw();
24195
+ return;
24196
+ }
24197
+ if (outcome.status === "confirmed") {
24198
+ state = { ...state, hint: void 0 };
24199
+ draw();
24200
+ finish(outcome.indices.map((i) => choices[i].id));
24201
+ return;
24202
+ }
24203
+ if (outcome.status === "eof") {
24204
+ finish(null);
24205
+ return;
24206
+ }
24207
+ restore();
24208
+ output.write("\n");
24209
+ process.exit(130);
24210
+ };
24211
+ const restore = () => {
24212
+ input.removeListener("keypress", onKey);
24213
+ try {
24214
+ input.setRawMode(false);
24215
+ } catch {
24216
+ }
24217
+ input.pause();
24218
+ };
24219
+ const finish = (value) => {
24220
+ if (settled) return;
24221
+ settled = true;
24222
+ restore();
24223
+ resolve4(value);
24224
+ };
24225
+ readline2.emitKeypressEvents(input);
24226
+ input.setRawMode(true);
24227
+ input.resume();
24228
+ input.on("keypress", onKey);
24229
+ input.once("end", () => finish(null));
24230
+ draw();
24231
+ });
24232
+ }
24233
+
24234
+ // src/lib/prompt.ts
23216
24235
  function interactive() {
23217
24236
  return !!process.stdin.isTTY && !!process.stdout.isTTY;
23218
24237
  }
23219
24238
  async function askLine(question, io = {}) {
23220
- const rl = readline2.createInterface({
24239
+ const rl = readline3.createInterface({
23221
24240
  input: io.input ?? process.stdin,
23222
24241
  output: io.output ?? process.stdout
23223
24242
  });
@@ -23275,6 +24294,8 @@ function printOptions(question, choices) {
23275
24294
  }
23276
24295
  async function promptChoice(question, choices, fallback) {
23277
24296
  if (!interactive()) return fallback;
24297
+ const picked = await runSelect({ question, choices, initial: [fallback], mode: "single" });
24298
+ if (picked !== null) return picked[0] ?? fallback;
23278
24299
  printOptions(question, choices);
23279
24300
  const defaultIdx = choices.findIndex((c) => c.id === fallback);
23280
24301
  const answer = await ask(` Choose [${defaultIdx + 1}]: `);
@@ -23288,6 +24309,8 @@ async function promptChoice(question, choices, fallback) {
23288
24309
  }
23289
24310
  async function promptMultiSelect(question, choices, fallback) {
23290
24311
  if (!interactive()) return [...fallback];
24312
+ const picked = await runSelect({ question, choices, initial: fallback, mode: "multi" });
24313
+ if (picked !== null) return picked.length > 0 ? picked : [...fallback];
23291
24314
  printOptions(question, choices);
23292
24315
  const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
23293
24316
  const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
@@ -23300,6 +24323,146 @@ async function promptMultiSelect(question, choices, fallback) {
23300
24323
  return parsed.ids;
23301
24324
  }
23302
24325
 
24326
+ // src/lib/remote-config.ts
24327
+ var import_node_fs44 = require("node:fs");
24328
+ var import_promises14 = require("node:fs/promises");
24329
+ var import_node_path29 = require("node:path");
24330
+ var import_yaml3 = __toESM(require_dist());
24331
+ var IGNORE_RIDER = "verityignore";
24332
+ async function fetchRemoteSetup(opts) {
24333
+ const standard = await apiRequest({
24334
+ method: "GET",
24335
+ path: "/standards/latest",
24336
+ serviceUrl: opts.serviceUrl,
24337
+ token: opts.token,
24338
+ verbose: opts.verbose
24339
+ });
24340
+ if (!standard.ok) {
24341
+ const missing = /STANDARD_NOT_FOUND|not found|404/i.test(standard.error);
24342
+ return missing ? { status: "none" } : { status: "unavailable", reason: standard.error };
24343
+ }
24344
+ const config = await apiRequest({
24345
+ method: "GET",
24346
+ path: "/analysis-configs",
24347
+ serviceUrl: opts.serviceUrl,
24348
+ token: opts.token,
24349
+ verbose: opts.verbose
24350
+ });
24351
+ return {
24352
+ status: "found",
24353
+ standard: {
24354
+ version: standard.data.version,
24355
+ content: standard.data.content ?? {},
24356
+ createdBy: standard.data.created_by,
24357
+ createdAt: standard.data.created_at
24358
+ },
24359
+ // Absent is normal and not an error: the two are stored independently, and a
24360
+ // project can have a Standard with no analysis config.
24361
+ analysisConfig: config.ok ? config.data.content ?? null : null
24362
+ };
24363
+ }
24364
+ async function adoptRemoteSetup(found, opts) {
24365
+ const written = [];
24366
+ const notes = [];
24367
+ const content = { ...found.standard.content };
24368
+ const rider = typeof content[IGNORE_RIDER] === "string" ? content[IGNORE_RIDER] : null;
24369
+ delete content[IGNORE_RIDER];
24370
+ await writeOut(STANDARD_FILE, (0, import_yaml3.stringify)(content));
24371
+ written.push(STANDARD_FILE);
24372
+ if (rider !== null) {
24373
+ const localIgnore = projectPath(VERITYIGNORE_FILE);
24374
+ if (!(0, import_node_fs44.existsSync)(localIgnore)) {
24375
+ await writeOut(VERITYIGNORE_FILE, rider);
24376
+ written.push(VERITYIGNORE_FILE);
24377
+ } else {
24378
+ const local = await (0, import_promises14.readFile)(localIgnore, "utf-8").catch(() => null);
24379
+ if (local !== null && local !== rider) {
24380
+ notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
24381
+ }
24382
+ }
24383
+ }
24384
+ if (found.analysisConfig) {
24385
+ await writeOut(CODACY_CONFIG_FILE, JSON.stringify(found.analysisConfig, null, 2) + "\n");
24386
+ written.push(CODACY_CONFIG_FILE);
24387
+ } else {
24388
+ notes.push(
24389
+ "No analysis config is stored for this repository, so static analysis has nothing to run. Run /verity-setup in Claude Code to generate one."
24390
+ );
24391
+ }
24392
+ await writeOut(VERITY_MD_FILE, renderVerityMd(found.standard, opts));
24393
+ written.push(VERITY_MD_FILE);
24394
+ return { written, notes };
24395
+ }
24396
+ async function writeOut(relative, body) {
24397
+ const target = projectPath(relative);
24398
+ await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(target), { recursive: true });
24399
+ await (0, import_promises14.writeFile)(target, body);
24400
+ }
24401
+ function names(content, key) {
24402
+ const raw = content[key];
24403
+ if (!Array.isArray(raw)) return [];
24404
+ return raw.map((entry) => {
24405
+ if (typeof entry === "string") return entry;
24406
+ if (entry && typeof entry === "object") {
24407
+ const rec = entry;
24408
+ const label2 = rec.name ?? rec.id ?? rec.dimension ?? rec.pattern;
24409
+ return typeof label2 === "string" ? label2 : null;
24410
+ }
24411
+ return null;
24412
+ }).filter((x) => !!x);
24413
+ }
24414
+ function renderVerityMd(standard, opts) {
24415
+ const dimensions = names(standard.content, "quality_dimensions");
24416
+ const patterns = names(standard.content, "security_patterns");
24417
+ const custom = names(standard.content, "custom_patterns");
24418
+ const list2 = (items, fallback) => (items.length > 0 ? items : fallback).map((i) => `- ${i}`).join("\n");
24419
+ return `# VERITY.md \u2014 Quality Gate
24420
+
24421
+ > This project uses [Verity](https://verity.md) to enforce quality and security standards on AI-generated code.
24422
+
24423
+ **URL:** ${opts.serviceUrl}
24424
+ **Project:** ${opts.projectId ?? "this repository"}
24425
+ **Standard:** v${standard.version} (adopted from the service${standard.createdBy ? `, created by ${standard.createdBy}` : ""})
24426
+
24427
+ ## Quality Dimensions
24428
+ ${list2(dimensions, ["comprehensibility", "modularity", "type_safety", "test_adequacy"])}
24429
+
24430
+ ## Security Patterns
24431
+ ${list2(patterns, [
24432
+ "No hardcoded secrets (CWE-798)",
24433
+ "Input sanitization (CWE-20)",
24434
+ "Parameterized queries (CWE-89)",
24435
+ "Dependency verification (CWE-1395)",
24436
+ "No unsafe deserialization (CWE-502)",
24437
+ "Access control checks (CWE-639)",
24438
+ "Config file integrity (CWE-15)"
24439
+ ])}
24440
+ ${custom.length > 0 ? `
24441
+ ## Project Patterns
24442
+ ${list2(custom, [])}
24443
+ ` : ""}
24444
+ ## How It Works
24445
+ Every time the coding agent stops, the Verity hook:
24446
+ 1. Runs static analysis via @codacy/analysis-cli
24447
+ 2. Sends results + code to the Verity service
24448
+ 3. An independent model reviews the code
24449
+ 4. Returns PASS / WARN / FAIL with actionable findings
24450
+
24451
+ _This Standard came from the service \u2014 the team's shared version. To change it,
24452
+ edit \`.verity/standard.yaml\` and run \`verity standard push\`, or re-synthesize
24453
+ from the codebase with \`/verity-setup --force\`._
24454
+ `;
24455
+ }
24456
+ function describeRemote(found) {
24457
+ const when = found.standard.createdAt.slice(0, 10);
24458
+ const who = found.standard.createdBy ? ` by ${found.standard.createdBy}` : "";
24459
+ const extras = [];
24460
+ if (found.analysisConfig) extras.push("analysis config");
24461
+ if (typeof found.standard.content[IGNORE_RIDER] === "string") extras.push(".verityignore");
24462
+ const also = extras.length > 0 ? ` \xB7 with ${extras.join(" and ")}` : "";
24463
+ return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
24464
+ }
24465
+
23303
24466
  // src/commands/init.ts
23304
24467
  async function confirmExistingLogin(serviceUrl, remote, opts) {
23305
24468
  const existing = await resolveToken(opts.token);
@@ -23358,7 +24521,7 @@ async function runOptionalAuth(resolution, opts = {}) {
23358
24521
  }
23359
24522
  let remote = "";
23360
24523
  try {
23361
- remote = (0, import_node_child_process13.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
24524
+ remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23362
24525
  } catch {
23363
24526
  }
23364
24527
  if (!healed) {
@@ -23404,15 +24567,15 @@ async function runOptionalAuth(resolution, opts = {}) {
23404
24567
  }
23405
24568
  function resolveDataDir() {
23406
24569
  const candidates = [
23407
- (0, import_node_path29.join)(__dirname, "..", "data"),
24570
+ (0, import_node_path30.join)(__dirname, "..", "data"),
23408
24571
  // installed: node_modules/@codacy/verity-cli/data
23409
- (0, import_node_path29.join)(__dirname, "..", "..", "data"),
24572
+ (0, import_node_path30.join)(__dirname, "..", "..", "data"),
23410
24573
  // edge case: nested resolution
23411
- (0, import_node_path29.join)(process.cwd(), "cli", "data")
24574
+ (0, import_node_path30.join)(process.cwd(), "cli", "data")
23412
24575
  // local dev: running from repo root
23413
24576
  ];
23414
24577
  for (const candidate of candidates) {
23415
- if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
24578
+ if ((0, import_node_fs45.existsSync)((0, import_node_path30.join)(candidate, "skills"))) {
23416
24579
  return candidate;
23417
24580
  }
23418
24581
  }
@@ -23421,16 +24584,16 @@ function resolveDataDir() {
23421
24584
  );
23422
24585
  }
23423
24586
  async function copyDir(src, dest) {
23424
- await (0, import_promises14.mkdir)(dest, { recursive: true });
23425
- await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
24587
+ await (0, import_promises15.mkdir)(dest, { recursive: true });
24588
+ await (0, import_promises15.cp)(src, dest, { recursive: true, force: true });
23426
24589
  }
23427
24590
  async function skillIsCurrent(src, dest) {
23428
24591
  const list2 = (dir) => {
23429
24592
  const out = [];
23430
24593
  const walk = (d, prefix) => {
23431
- for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
24594
+ for (const e of (0, import_node_fs45.readdirSync)(d, { withFileTypes: true })) {
23432
24595
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23433
- if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
24596
+ if (e.isDirectory()) walk((0, import_node_path30.join)(d, e.name), rel);
23434
24597
  else if (e.isFile()) out.push(rel);
23435
24598
  }
23436
24599
  };
@@ -23441,8 +24604,8 @@ async function skillIsCurrent(src, dest) {
23441
24604
  const shipped = list2(src);
23442
24605
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
23443
24606
  for (const rel of shipped) {
23444
- const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
23445
- const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
24607
+ const a = await (0, import_promises15.readFile)((0, import_node_path30.join)(src, rel), "utf-8");
24608
+ const b = await (0, import_promises15.readFile)((0, import_node_path30.join)(dest, rel), "utf-8");
23446
24609
  if (a !== b) return false;
23447
24610
  }
23448
24611
  return true;
@@ -23471,9 +24634,17 @@ var MOMENT_CHOICES = [
23471
24634
  { id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
23472
24635
  ];
23473
24636
  var DEFAULT_MOMENTS = ["stop"];
24637
+ async function momentsFromInstalledHooks() {
24638
+ const status = await checkAllVerityHooks();
24639
+ const wired = [];
24640
+ if (status.stop) wired.push("stop");
24641
+ if (status.guardOn.includes("commit")) wired.push("pre-commit");
24642
+ if (status.guardOn.includes("push")) wired.push("pre-push");
24643
+ return wired.length > 0 ? wired : DEFAULT_MOMENTS;
24644
+ }
23474
24645
  async function askSetupQuestions(defaultsOnly, previous) {
23475
24646
  const intensityDefault = previous?.intensity ?? "balanced";
23476
- const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
24647
+ const momentsDefault = previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks();
23477
24648
  if (defaultsOnly) {
23478
24649
  return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
23479
24650
  }
@@ -23510,6 +24681,73 @@ function insideClaudeCode() {
23510
24681
  function resumeDeferredPending(previous) {
23511
24682
  return previous?.telemetry === "deferred";
23512
24683
  }
24684
+ async function maybeAdoptRemoteSetup(opts) {
24685
+ if (!opts.adopt) {
24686
+ printInfo(" Skipped (--no-adopt) \u2014 the setup below runs from this codebase.");
24687
+ return false;
24688
+ }
24689
+ const token = await resolveToken(opts.globals.token);
24690
+ const url = await resolveServiceUrl(opts.globals.serviceUrl);
24691
+ if (!token.ok || !url.ok) {
24692
+ printInfo(" Not signed in, so there is nothing to fetch \u2014 the setup below runs from your codebase.");
24693
+ return false;
24694
+ }
24695
+ const found = await fetchRemoteSetup({
24696
+ token: token.data.token,
24697
+ serviceUrl: url.data,
24698
+ verbose: opts.globals.verbose
24699
+ });
24700
+ if (found.status === "unavailable") {
24701
+ printWarn(` Could not ask the service (${found.reason}) \u2014 continuing with the regular setup.`);
24702
+ return false;
24703
+ }
24704
+ if (found.status === "none") {
24705
+ printInfo(" No Standard stored for this repository yet \u2014 this setup will create the first one.");
24706
+ return false;
24707
+ }
24708
+ if (opts.previous?.declinedStandardVersion === found.standard.version) {
24709
+ printInfo(` Standard v${found.standard.version} is available; you declined this version before \u2014 not asking again.`);
24710
+ printInfo(" A newer version will be offered. To take it now: verity standard get, or /verity-setup.");
24711
+ return false;
24712
+ }
24713
+ console.log("");
24714
+ console.log(` This repository already has a Standard on Verity: ${describeRemote(found)}`);
24715
+ console.log(" Using it keeps you consistent with everyone else on the project.");
24716
+ console.log(" Synthesizing a new one from this codebase would push a competing version.");
24717
+ console.log("");
24718
+ const use = opts.defaultsOnly ? true : await promptYes(" Use the existing Standard? [Y/n] ", { nonInteractive: true });
24719
+ if (!use) {
24720
+ printInfo(" Keeping the existing version untouched; /verity-setup will synthesize a new one.");
24721
+ await writeSetupState({ declinedStandardVersion: found.standard.version }).catch(() => {
24722
+ });
24723
+ return false;
24724
+ }
24725
+ try {
24726
+ const outcome = await adoptRemoteSetup(found, {
24727
+ serviceUrl: url.data,
24728
+ projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
24729
+ });
24730
+ for (const path of outcome.written) printInfo(` ${path} \u2713`);
24731
+ for (const note of outcome.notes) printWarn(` ${note}`);
24732
+ if (opts.previous?.declinedStandardVersion != null) {
24733
+ await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
24734
+ });
24735
+ }
24736
+ return true;
24737
+ } catch (err) {
24738
+ printWarn(` Could not write the fetched configuration: ${err.message}`);
24739
+ printInfo(" Falling back to the regular setup.");
24740
+ return false;
24741
+ }
24742
+ }
24743
+ async function projectLabel(token, serviceUrl, verbose) {
24744
+ try {
24745
+ const who = await whoami(token, serviceUrl, verbose);
24746
+ return who.ok ? who.data.project_id : null;
24747
+ } catch {
24748
+ return null;
24749
+ }
24750
+ }
23513
24751
  var PHASE_TWO_ARTIFACTS = [
23514
24752
  {
23515
24753
  path: ".verity/standard.yaml",
@@ -23578,7 +24816,7 @@ async function handoffToSetup(enabled, claudeInstalled) {
23578
24816
  console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
23579
24817
  console.log("");
23580
24818
  const startedAt = Date.now();
23581
- const run2 = (0, import_node_child_process13.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
24819
+ const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
23582
24820
  if (run2.error) {
23583
24821
  printWarn(`Could not start Claude Code: ${run2.error.message}`);
23584
24822
  return instruct("start it yourself and run the skill there");
@@ -23586,12 +24824,13 @@ async function handoffToSetup(enabled, claudeInstalled) {
23586
24824
  await reportPhaseTwo(startedAt);
23587
24825
  }
23588
24826
  function registerInitCommand(program2) {
23589
- program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").action(async (opts) => {
24827
+ program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
23590
24828
  const force = opts.force ?? false;
23591
24829
  const wantsHandoff = opts.setup !== false;
24830
+ const wantsAdopt = opts.adopt !== false;
23592
24831
  const defaultsOnly = (opts.yes ?? false) || !interactive();
23593
24832
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
23594
- const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
24833
+ const isProject = projectMarkers.some((m) => (0, import_node_fs45.existsSync)(m));
23595
24834
  if (!isProject) {
23596
24835
  printError("No project detected in the current directory.");
23597
24836
  printInfo('Run "verity init" from your project root.');
@@ -23606,7 +24845,7 @@ function registerInitCommand(program2) {
23606
24845
  } else {
23607
24846
  printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
23608
24847
  }
23609
- const TOTAL_STEPS = 8;
24848
+ const TOTAL_STEPS = 9;
23610
24849
  let stepNo = 0;
23611
24850
  const step = (label2) => {
23612
24851
  stepNo++;
@@ -23641,17 +24880,17 @@ function registerInitCommand(program2) {
23641
24880
  console.log("");
23642
24881
  step("Installing skills");
23643
24882
  const dataDir = resolveDataDir();
23644
- const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
24883
+ const skillsSource = (0, import_node_path30.join)(dataDir, "skills");
23645
24884
  const skillsDest = ".claude/skills";
23646
24885
  let skillsInstalled = 0;
23647
24886
  for (const skill of SKILLS) {
23648
- const src = (0, import_node_path29.join)(skillsSource, skill);
23649
- const dest = (0, import_node_path29.join)(skillsDest, skill);
23650
- if (!(0, import_node_fs44.existsSync)(src)) {
24887
+ const src = (0, import_node_path30.join)(skillsSource, skill);
24888
+ const dest = (0, import_node_path30.join)(skillsDest, skill);
24889
+ if (!(0, import_node_fs45.existsSync)(src)) {
23651
24890
  printWarn(` Skill data not found: ${skill}`);
23652
24891
  continue;
23653
24892
  }
23654
- if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
24893
+ if ((0, import_node_fs45.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
23655
24894
  skillsInstalled++;
23656
24895
  continue;
23657
24896
  }
@@ -23667,7 +24906,7 @@ function registerInitCommand(program2) {
23667
24906
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
23668
24907
  }
23669
24908
  step("Knowledge base, .gitignore and CLAUDE.md");
23670
- await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
24909
+ await (0, import_promises15.mkdir)(VERITY_DIR, { recursive: true });
23671
24910
  await ensureMemoryDir();
23672
24911
  const ignoreResult = ensureVerityGitignore();
23673
24912
  if (ignoreResult === "failed") {
@@ -23701,8 +24940,8 @@ function registerInitCommand(program2) {
23701
24940
  } catch (err) {
23702
24941
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
23703
24942
  }
23704
- const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
23705
- await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
24943
+ const globalVerityDir = (0, import_node_path30.join)(process.env.HOME ?? "", ".verity");
24944
+ await (0, import_promises15.mkdir)(globalVerityDir, { recursive: true });
23706
24945
  console.log("");
23707
24946
  step("Wiring Claude Code hooks");
23708
24947
  await applyMomentSelection(moments);
@@ -23754,6 +24993,18 @@ function registerInitCommand(program2) {
23754
24993
  printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
23755
24994
  }
23756
24995
  }
24996
+ step("Checking Verity for an existing setup");
24997
+ let adopted = false;
24998
+ if ((0, import_node_fs45.existsSync)(projectPath(STANDARD_FILE))) {
24999
+ printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
25000
+ } else {
25001
+ adopted = await maybeAdoptRemoteSetup({
25002
+ globals: program2.opts(),
25003
+ defaultsOnly,
25004
+ previous,
25005
+ adopt: wantsAdopt
25006
+ });
25007
+ }
23757
25008
  step("Recording your answers");
23758
25009
  try {
23759
25010
  await writeSetupState({
@@ -23762,7 +25013,7 @@ function registerInitCommand(program2) {
23762
25013
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
23763
25014
  init: {
23764
25015
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
23765
- cli_version: true ? "0.31.1-experimental.be74f71" : "dev"
25016
+ cli_version: true ? "0.31.1-experimental.f2f59c0" : "dev"
23766
25017
  }
23767
25018
  });
23768
25019
  } catch (err) {
@@ -23780,14 +25031,20 @@ function registerInitCommand(program2) {
23780
25031
  console.log(" CLAUDE.md memory pointer, waive policy, reflection");
23781
25032
  console.log("");
23782
25033
  console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
23783
- await handoffToSetup(wantsHandoff, claudeInstalled);
25034
+ if (adopted) {
25035
+ console.log("");
25036
+ printInfo("Setup complete \u2014 this project uses your team's Standard.");
25037
+ printInfo(" Re-synthesize from this codebase instead: /verity-setup --force");
25038
+ } else {
25039
+ await handoffToSetup(wantsHandoff, claudeInstalled);
25040
+ }
23784
25041
  console.log("");
23785
25042
  });
23786
25043
  }
23787
25044
 
23788
25045
  // src/commands/uninstall.ts
23789
- var import_node_fs45 = require("node:fs");
23790
- var import_node_path30 = require("node:path");
25046
+ var import_node_fs46 = require("node:fs");
25047
+ var import_node_path31 = require("node:path");
23791
25048
  var SKILL_NAMES = [
23792
25049
  "verity-setup",
23793
25050
  "verity-analyze",
@@ -23806,11 +25063,11 @@ function registerUninstallCommand(program2) {
23806
25063
  const actions = [];
23807
25064
  const skillsRoot = projectPath(".claude/skills");
23808
25065
  for (const name of SKILL_NAMES) {
23809
- const dir = (0, import_node_path30.join)(skillsRoot, name);
23810
- if ((0, import_node_fs45.existsSync)(dir)) {
25066
+ const dir = (0, import_node_path31.join)(skillsRoot, name);
25067
+ if ((0, import_node_fs46.existsSync)(dir)) {
23811
25068
  actions.push({
23812
25069
  label: `Remove .claude/skills/${name}/`,
23813
- apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
25070
+ apply: () => (0, import_node_fs46.rmSync)(dir, { recursive: true, force: true })
23814
25071
  });
23815
25072
  }
23816
25073
  }
@@ -23824,24 +25081,24 @@ function registerUninstallCommand(program2) {
23824
25081
  });
23825
25082
  }
23826
25083
  const verityDir = projectPath(VERITY_DIR);
23827
- if ((0, import_node_fs45.existsSync)(verityDir)) {
25084
+ if ((0, import_node_fs46.existsSync)(verityDir)) {
23828
25085
  actions.push({
23829
25086
  label: `Remove ${VERITY_DIR}/`,
23830
- apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
25087
+ apply: () => (0, import_node_fs46.rmSync)(verityDir, { recursive: true, force: true })
23831
25088
  });
23832
25089
  }
23833
25090
  if (!keepVerityMd) {
23834
25091
  const verityMd = projectPath(VERITY_MD_FILE);
23835
- if ((0, import_node_fs45.existsSync)(verityMd)) {
25092
+ if ((0, import_node_fs46.existsSync)(verityMd)) {
23836
25093
  actions.push({
23837
25094
  label: `Remove ${VERITY_MD_FILE}`,
23838
- apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
25095
+ apply: () => (0, import_node_fs46.rmSync)(verityMd, { force: true })
23839
25096
  });
23840
25097
  }
23841
25098
  }
23842
25099
  const cleanupEmptyDir = (path) => {
23843
- if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
23844
- (0, import_node_fs45.rmdirSync)(path);
25100
+ if ((0, import_node_fs46.existsSync)(path) && (0, import_node_fs46.statSync)(path).isDirectory() && (0, import_node_fs46.readdirSync)(path).length === 0) {
25101
+ (0, import_node_fs46.rmdirSync)(path);
23845
25102
  }
23846
25103
  };
23847
25104
  actions.push({
@@ -23852,11 +25109,11 @@ function registerUninstallCommand(program2) {
23852
25109
  }
23853
25110
  });
23854
25111
  const home = process.env.HOME ?? "";
23855
- const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
23856
- if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
25112
+ const globalVerityDir = (0, import_node_path31.join)(home, ".verity");
25113
+ if (purgeGlobal && (0, import_node_fs46.existsSync)(globalVerityDir)) {
23857
25114
  actions.push({
23858
25115
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
23859
- apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
25116
+ apply: () => (0, import_node_fs46.rmSync)(globalVerityDir, { recursive: true, force: true })
23860
25117
  });
23861
25118
  }
23862
25119
  if (actions.length === 0) {
@@ -24050,8 +25307,8 @@ function registerTaskCommands(program2) {
24050
25307
  }
24051
25308
 
24052
25309
  // src/commands/reset.ts
24053
- var import_node_fs46 = require("node:fs");
24054
- var import_node_path31 = require("node:path");
25310
+ var import_node_fs47 = require("node:fs");
25311
+ var import_node_path32 = require("node:path");
24055
25312
  function registerResetCommand(program2) {
24056
25313
  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) => {
24057
25314
  const globals = program2.opts();
@@ -24088,11 +25345,11 @@ function registerResetCommand(program2) {
24088
25345
  }
24089
25346
  const cacheDir = projectPath(CACHE_DIR);
24090
25347
  let purged = 0;
24091
- if ((0, import_node_fs46.existsSync)(cacheDir)) {
24092
- for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
25348
+ if ((0, import_node_fs47.existsSync)(cacheDir)) {
25349
+ for (const entry of (0, import_node_fs47.readdirSync)(cacheDir)) {
24093
25350
  if (entry.startsWith("pending-")) {
24094
25351
  try {
24095
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
25352
+ (0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(cacheDir, entry));
24096
25353
  purged++;
24097
25354
  } catch {
24098
25355
  }
@@ -24107,19 +25364,19 @@ function registerResetCommand(program2) {
24107
25364
  projectPath(`${VERITY_DIR}/.last-analysis`)
24108
25365
  ];
24109
25366
  for (const file of filesToClear) {
24110
- if ((0, import_node_fs46.existsSync)(file)) {
25367
+ if ((0, import_node_fs47.existsSync)(file)) {
24111
25368
  try {
24112
- (0, import_node_fs46.writeFileSync)(file, "");
25369
+ (0, import_node_fs47.writeFileSync)(file, "");
24113
25370
  } catch {
24114
25371
  }
24115
25372
  }
24116
25373
  }
24117
25374
  if (opts.all) {
24118
25375
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
24119
- if ((0, import_node_fs46.existsSync)(logsDir)) {
24120
- for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
25376
+ if ((0, import_node_fs47.existsSync)(logsDir)) {
25377
+ for (const entry of (0, import_node_fs47.readdirSync)(logsDir)) {
24121
25378
  try {
24122
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
25379
+ (0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(logsDir, entry));
24123
25380
  } catch {
24124
25381
  }
24125
25382
  }
@@ -24427,8 +25684,8 @@ function registerTelemetryCommands(program2) {
24427
25684
  }
24428
25685
 
24429
25686
  // src/cli.ts
24430
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.be74f71").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) => {
24431
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.be74f71");
25687
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.f2f59c0").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) => {
25688
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.f2f59c0");
24432
25689
  setUserNamedServiceUrl(program.opts().serviceUrl);
24433
25690
  try {
24434
25691
  await foldLegacyLocalCredential();