@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/CHANGELOG.md +69 -0
- package/README.md +26 -3
- package/bin/verity.js +1364 -107
- package/data/skills/verity-setup/SKILL.md +7 -0
- package/package.json +1 -1
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(
|
|
679
|
-
this.conflictsWith = this.conflictsWith.concat(
|
|
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(
|
|
1253
|
-
|
|
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
|
|
18480
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17781
18481
|
var import_node_fs24 = require("node:fs");
|
|
17782
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
19923
|
-
function
|
|
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 >=
|
|
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
|
|
19987
|
-
const
|
|
19988
|
-
const
|
|
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
|
|
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,
|
|
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",
|
|
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
|
|
22440
|
-
var
|
|
22441
|
-
var
|
|
22442
|
-
var
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
22853
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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,
|
|
24570
|
+
(0, import_node_path30.join)(__dirname, "..", "data"),
|
|
23408
24571
|
// installed: node_modules/@codacy/verity-cli/data
|
|
23409
|
-
(0,
|
|
24572
|
+
(0, import_node_path30.join)(__dirname, "..", "..", "data"),
|
|
23410
24573
|
// edge case: nested resolution
|
|
23411
|
-
(0,
|
|
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,
|
|
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,
|
|
23425
|
-
await (0,
|
|
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,
|
|
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,
|
|
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,
|
|
23445
|
-
const b = await (0,
|
|
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 :
|
|
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,
|
|
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,
|
|
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 =
|
|
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,
|
|
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,
|
|
23649
|
-
const dest = (0,
|
|
23650
|
-
if (!(0,
|
|
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,
|
|
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,
|
|
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,
|
|
23705
|
-
await (0,
|
|
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.
|
|
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
|
-
|
|
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
|
|
23790
|
-
var
|
|
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,
|
|
23810
|
-
if ((0,
|
|
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,
|
|
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,
|
|
25084
|
+
if ((0, import_node_fs46.existsSync)(verityDir)) {
|
|
23828
25085
|
actions.push({
|
|
23829
25086
|
label: `Remove ${VERITY_DIR}/`,
|
|
23830
|
-
apply: () => (0,
|
|
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,
|
|
25092
|
+
if ((0, import_node_fs46.existsSync)(verityMd)) {
|
|
23836
25093
|
actions.push({
|
|
23837
25094
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
23838
|
-
apply: () => (0,
|
|
25095
|
+
apply: () => (0, import_node_fs46.rmSync)(verityMd, { force: true })
|
|
23839
25096
|
});
|
|
23840
25097
|
}
|
|
23841
25098
|
}
|
|
23842
25099
|
const cleanupEmptyDir = (path) => {
|
|
23843
|
-
if ((0,
|
|
23844
|
-
(0,
|
|
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,
|
|
23856
|
-
if (purgeGlobal && (0,
|
|
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,
|
|
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
|
|
24054
|
-
var
|
|
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,
|
|
24092
|
-
for (const entry of (0,
|
|
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,
|
|
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,
|
|
25367
|
+
if ((0, import_node_fs47.existsSync)(file)) {
|
|
24111
25368
|
try {
|
|
24112
|
-
(0,
|
|
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,
|
|
24120
|
-
for (const entry of (0,
|
|
25376
|
+
if ((0, import_node_fs47.existsSync)(logsDir)) {
|
|
25377
|
+
for (const entry of (0, import_node_fs47.readdirSync)(logsDir)) {
|
|
24121
25378
|
try {
|
|
24122
|
-
(0,
|
|
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.
|
|
24431
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.
|
|
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();
|