@codacy/verity-cli 0.35.0-experimental.3211f79 → 0.35.0-experimental.3a13a5e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/verity.js +1875 -1189
- package/data/skills/verity-init/SKILL.md +39 -5
- package/package.json +2 -9
package/bin/verity.js
CHANGED
|
@@ -10192,14 +10192,14 @@ var require_public_api = __commonJS2({
|
|
|
10192
10192
|
const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
|
|
10193
10193
|
const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
|
|
10194
10194
|
const composer$1 = new composer.Composer(options);
|
|
10195
|
-
const
|
|
10195
|
+
const docs = Array.from(composer$1.compose(parser$1.parse(source2)));
|
|
10196
10196
|
if (prettyErrors && lineCounter2)
|
|
10197
|
-
for (const doc of
|
|
10197
|
+
for (const doc of docs) {
|
|
10198
10198
|
doc.errors.forEach(errors.prettifyError(source2, lineCounter2));
|
|
10199
10199
|
doc.warnings.forEach(errors.prettifyError(source2, lineCounter2));
|
|
10200
10200
|
}
|
|
10201
|
-
if (
|
|
10202
|
-
return
|
|
10201
|
+
if (docs.length > 0)
|
|
10202
|
+
return docs;
|
|
10203
10203
|
return Object.assign([], { empty: true }, composer$1.streamInfo());
|
|
10204
10204
|
}
|
|
10205
10205
|
function parseDocument(source2, options = {}) {
|
|
@@ -23881,7 +23881,7 @@ async function readHookStdin() {
|
|
|
23881
23881
|
|
|
23882
23882
|
// src/commands/standard.ts
|
|
23883
23883
|
var import_promises10 = require("node:fs/promises");
|
|
23884
|
-
var
|
|
23884
|
+
var import_yaml6 = __toESM2(require_dist());
|
|
23885
23885
|
|
|
23886
23886
|
// src/lib/synthesize.ts
|
|
23887
23887
|
var import_node_fs35 = require("node:fs");
|
|
@@ -24520,36 +24520,8 @@ function collectRiskSignals(root, opts = {}) {
|
|
|
24520
24520
|
return out2.slice(0, opts.limit ?? 10);
|
|
24521
24521
|
}
|
|
24522
24522
|
|
|
24523
|
-
// src/lib/
|
|
24524
|
-
var
|
|
24525
|
-
var PROFILE_FILE = ".verity/repo-profile.json";
|
|
24526
|
-
var LIMITS = {
|
|
24527
|
-
languages: 20,
|
|
24528
|
-
frameworks: 50,
|
|
24529
|
-
dependencies: 300,
|
|
24530
|
-
folders: 300,
|
|
24531
|
-
commits: 500,
|
|
24532
|
-
// The server allows 20; 10 keeps a full profile well under its body cap.
|
|
24533
|
-
pathsPerCommit: 10,
|
|
24534
|
-
docs: 8,
|
|
24535
|
-
// A long doc is cut by section (fitDoc), so these bound how much of the
|
|
24536
|
-
// team's conventions reach the writer, not where a doc is cut.
|
|
24537
|
-
docBytes: 12288,
|
|
24538
|
-
nestedDocBytes: 6144,
|
|
24539
|
-
contributorDocBytes: 4096,
|
|
24540
|
-
docsTotalBytes: 32768,
|
|
24541
|
-
subject: 120,
|
|
24542
|
-
path: 200,
|
|
24543
|
-
name: 100
|
|
24544
|
-
};
|
|
24545
|
-
var WINDOW_DAYS = 180;
|
|
24546
|
-
var MAX_PROFILE_BYTES = 120 * 1024;
|
|
24547
|
-
var ROOT_DOCS = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".github/copilot-instructions.md"];
|
|
24548
|
-
var CONTRIBUTOR_DOCS = ["CONTRIBUTING.md", "ARCHITECTURE.md"];
|
|
24549
|
-
var README = "README.md";
|
|
24550
|
-
var README_BYTES = 3072;
|
|
24551
|
-
var NESTED_DOC = /^(?:[^./][^/]*\/){1,4}(?:AGENTS|CLAUDE)\.md$/;
|
|
24552
|
-
var NOT_THE_TEAMS = /(?:^|\/)(?:examples?|samples?|templates?|[\w-]*-template|fixtures?|tests?|__tests__|vendor|third_party|node_modules)\//i;
|
|
24523
|
+
// src/lib/profile-text.ts
|
|
24524
|
+
var PATH_MAX = 200;
|
|
24553
24525
|
var SECRET_PATTERNS = [
|
|
24554
24526
|
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g,
|
|
24555
24527
|
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
@@ -24576,63 +24548,77 @@ function redact(text2) {
|
|
|
24576
24548
|
function oneLine(text2, max) {
|
|
24577
24549
|
return redact(text2.replace(/[\r\n\t]+/g, " ")).trim().slice(0, max);
|
|
24578
24550
|
}
|
|
24579
|
-
var DEP = /^[A-Za-z0-9@][A-Za-z0-9@._/:+-]*$/;
|
|
24580
|
-
function dependencies(root, files) {
|
|
24581
|
-
const seen = /* @__PURE__ */ new Set();
|
|
24582
|
-
for (const name2 of declaredDependencies(root, files)) {
|
|
24583
|
-
if (name2.length <= LIMITS.name && DEP.test(name2)) seen.add(name2);
|
|
24584
|
-
if (seen.size >= LIMITS.dependencies) break;
|
|
24585
|
-
}
|
|
24586
|
-
return [...seen];
|
|
24587
|
-
}
|
|
24588
24551
|
function safePath(p) {
|
|
24589
|
-
if (!p || p.length >
|
|
24552
|
+
if (!p || p.length > PATH_MAX || p.startsWith("/") || p.includes("\\")) return null;
|
|
24590
24553
|
if (p.split("/").some((seg) => seg === "" || seg === "..")) return null;
|
|
24591
24554
|
if (/[\u0000-\u001f\u007f]/.test(p)) return null;
|
|
24592
24555
|
return p;
|
|
24593
24556
|
}
|
|
24594
|
-
|
|
24595
|
-
|
|
24596
|
-
|
|
24597
|
-
|
|
24598
|
-
|
|
24599
|
-
|
|
24600
|
-
|
|
24601
|
-
|
|
24557
|
+
|
|
24558
|
+
// src/lib/profile-docs.ts
|
|
24559
|
+
var DOC_LIMITS = {
|
|
24560
|
+
docs: 8,
|
|
24561
|
+
// A long doc is cut by section (fitDoc), so these bound how much of the
|
|
24562
|
+
// team's conventions reach the writer, not where a doc is cut.
|
|
24563
|
+
docBytes: 12288,
|
|
24564
|
+
nestedDocBytes: 6144,
|
|
24565
|
+
contributorDocBytes: 4096,
|
|
24566
|
+
docsTotalBytes: 32768
|
|
24567
|
+
};
|
|
24568
|
+
var ROOT_DOCS = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".github/copilot-instructions.md"];
|
|
24569
|
+
var CONTRIBUTOR_DOCS = ["CONTRIBUTING.md", "ARCHITECTURE.md"];
|
|
24570
|
+
var README = "README.md";
|
|
24571
|
+
var README_BYTES = 3072;
|
|
24572
|
+
var NESTED_DOC = /^(?:[^./][^/]*\/){1,4}(?:AGENTS|CLAUDE)\.md$/;
|
|
24573
|
+
var NOT_THE_TEAMS = /(?:^|\/)(?:examples?|samples?|templates?|[\w-]*-template|fixtures?|tests?|__tests__|vendor|third_party|node_modules)\//i;
|
|
24574
|
+
var MANAGED_START = "<!-- verity-memory:start -->";
|
|
24575
|
+
var MANAGED_END = "<!-- verity-memory:end -->";
|
|
24576
|
+
var PRESERVE_START2 = "<!-- verity-memory:preserve -->";
|
|
24577
|
+
var PRESERVE_END2 = "<!-- /verity-memory:preserve -->";
|
|
24578
|
+
var FENCE_OPEN = /^[ \t]*(?:(?:[-*+]|\d+[.)])[ \t]+)*(`{3,}|~{3,})/;
|
|
24579
|
+
var FENCE_CLOSE = /^[ \t]*(`{3,}|~{3,})[ \t]*$/;
|
|
24580
|
+
var QUOTE = /^(?:[ \t]*>)+ ?/;
|
|
24581
|
+
var LIST_ITEM = /^[ \t]*(?:[-*+]|\d+[.)])[ \t]/;
|
|
24582
|
+
function indentWidth(line2) {
|
|
24583
|
+
let w = 0;
|
|
24584
|
+
for (const ch of line2) {
|
|
24585
|
+
if (ch === " ") w++;
|
|
24586
|
+
else if (ch === " ") w += 4;
|
|
24587
|
+
else break;
|
|
24602
24588
|
}
|
|
24603
|
-
return
|
|
24589
|
+
return w;
|
|
24604
24590
|
}
|
|
24605
|
-
function
|
|
24606
|
-
const
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24611
|
-
|
|
24612
|
-
|
|
24613
|
-
|
|
24614
|
-
"--"
|
|
24615
|
-
], { timeoutMs: 3e4 });
|
|
24616
|
-
const commits = [];
|
|
24617
|
-
if (!res.ok) return { window_days: WINDOW_DAYS, commits };
|
|
24618
|
-
let current = null;
|
|
24619
|
-
for (const line2 of res.stdout.split("\n")) {
|
|
24620
|
-
if (line2.startsWith("@@")) {
|
|
24621
|
-
if (current && current.subject) commits.push(current);
|
|
24622
|
-
const raw = line2.slice(3);
|
|
24623
|
-
current = { subject: oneLine(raw, LIMITS.subject), paths: [], fix: FIX_SUBJECT.test(raw) };
|
|
24591
|
+
function stripCode(text2) {
|
|
24592
|
+
const out2 = [];
|
|
24593
|
+
let fence = null;
|
|
24594
|
+
let underItem = false;
|
|
24595
|
+
for (const line2 of text2.split("\n")) {
|
|
24596
|
+
const body2 = line2.replace(QUOTE, "");
|
|
24597
|
+
if (fence) {
|
|
24598
|
+
const close = FENCE_CLOSE.exec(body2);
|
|
24599
|
+
if (close && close[1][0] === fence[0] && close[1].length >= fence.length) fence = null;
|
|
24624
24600
|
continue;
|
|
24625
24601
|
}
|
|
24626
|
-
const
|
|
24627
|
-
if (
|
|
24602
|
+
const open = FENCE_OPEN.exec(body2);
|
|
24603
|
+
if (open) {
|
|
24604
|
+
fence = open[1];
|
|
24605
|
+
underItem = false;
|
|
24606
|
+
continue;
|
|
24607
|
+
}
|
|
24608
|
+
if (body2.trim() === "") {
|
|
24609
|
+
out2.push(line2);
|
|
24610
|
+
underItem = false;
|
|
24611
|
+
continue;
|
|
24612
|
+
}
|
|
24613
|
+
if (indentWidth(body2) >= 4) {
|
|
24614
|
+
if (underItem && LIST_ITEM.test(body2)) out2.push(line2);
|
|
24615
|
+
continue;
|
|
24616
|
+
}
|
|
24617
|
+
out2.push(line2);
|
|
24618
|
+
underItem = LIST_ITEM.test(body2);
|
|
24628
24619
|
}
|
|
24629
|
-
|
|
24630
|
-
return { window_days: WINDOW_DAYS, commits: commits.slice(0, LIMITS.commits) };
|
|
24620
|
+
return out2.join("\n");
|
|
24631
24621
|
}
|
|
24632
|
-
var MANAGED_START = "<!-- verity-memory:start -->";
|
|
24633
|
-
var MANAGED_END = "<!-- verity-memory:end -->";
|
|
24634
|
-
var PRESERVE_START2 = "<!-- verity-memory:preserve -->";
|
|
24635
|
-
var PRESERVE_END2 = "<!-- /verity-memory:preserve -->";
|
|
24636
24622
|
function docText(raw) {
|
|
24637
24623
|
let text2 = raw;
|
|
24638
24624
|
const start2 = text2.indexOf(MANAGED_START);
|
|
@@ -24644,7 +24630,7 @@ function docText(raw) {
|
|
|
24644
24630
|
const kept = ps >= 0 && pe > ps ? block.slice(ps + PRESERVE_START2.length, pe) : "";
|
|
24645
24631
|
text2 = text2.slice(0, start2) + kept + text2.slice(end + MANAGED_END.length);
|
|
24646
24632
|
}
|
|
24647
|
-
text2 = text2.replace(
|
|
24633
|
+
text2 = stripCode(text2.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*$/, "")).replace(/\n{3,}/g, "\n\n");
|
|
24648
24634
|
return redact(text2).trim();
|
|
24649
24635
|
}
|
|
24650
24636
|
var RULE_TITLE = /convention|pattern|rule|gotcha|footgun|pitfall|best practice|guideline|principle|standard|style|architecture|design|security|database|migration|schema|\bapi\b|error|state|naming|structure|i18n|internationali[sz]|translation|important|critical|warning|do not|don't|never|must|avoid/i;
|
|
@@ -24677,18 +24663,23 @@ function sectionRank(s) {
|
|
|
24677
24663
|
return title * 1e3 + Math.min(999, s.lines.filter((l) => NORMATIVE.test(l)).length);
|
|
24678
24664
|
}
|
|
24679
24665
|
function fitDoc(text2, budget) {
|
|
24680
|
-
if (text2
|
|
24666
|
+
if (utf8(text2) <= budget) return text2;
|
|
24681
24667
|
const all = docSections(text2);
|
|
24682
|
-
const
|
|
24668
|
+
const lineBytes = (line2) => utf8(line2) + 1;
|
|
24669
|
+
const GAP = lineBytes("\u2026");
|
|
24683
24670
|
const chosen = /* @__PURE__ */ new Map();
|
|
24671
|
+
const headingsCounted = /* @__PURE__ */ new Set();
|
|
24684
24672
|
let used2 = 0;
|
|
24685
24673
|
for (const s of [...all].sort((a, b) => sectionRank(b) - sectionRank(a) || a.order - b.order)) {
|
|
24686
24674
|
if (!s.lines.some((l) => l.trim())) continue;
|
|
24675
|
+
const parents = s.parents.filter((p) => !headingsCounted.has(p) && !chosen.has(p));
|
|
24676
|
+
const parentBytes = parents.reduce((n2, p) => n2 + lineBytes(all[p].heading ?? ""), 0);
|
|
24687
24677
|
const lean = s.lines.filter((l) => NORMATIVE.test(l));
|
|
24688
24678
|
for (const lines of [s.lines, lean]) {
|
|
24689
|
-
const n2 =
|
|
24679
|
+
const n2 = (s.heading ? lineBytes(s.heading) : 0) + lines.reduce((m, l) => m + lineBytes(l), 0) + parentBytes + GAP;
|
|
24690
24680
|
if (lines.length && used2 + n2 <= budget) {
|
|
24691
24681
|
chosen.set(s.order, lines);
|
|
24682
|
+
for (const p of parents) headingsCounted.add(p);
|
|
24692
24683
|
used2 += n2;
|
|
24693
24684
|
break;
|
|
24694
24685
|
}
|
|
@@ -24712,23 +24703,38 @@ function fitDoc(text2, budget) {
|
|
|
24712
24703
|
if (s.heading) out2.push(s.heading);
|
|
24713
24704
|
out2.push(...lines);
|
|
24714
24705
|
}
|
|
24715
|
-
return out2.join("\n").replace(/\n{3,}/g, "\n\n").trim()
|
|
24706
|
+
return cutToBytes(out2.join("\n").replace(/\n{3,}/g, "\n\n").trim(), budget);
|
|
24707
|
+
}
|
|
24708
|
+
function utf8(text2) {
|
|
24709
|
+
return Buffer.byteLength(text2, "utf8");
|
|
24710
|
+
}
|
|
24711
|
+
function cutToBytes(text2, max) {
|
|
24712
|
+
if (utf8(text2) <= max) return text2;
|
|
24713
|
+
let out2 = "";
|
|
24714
|
+
let n2 = 0;
|
|
24715
|
+
for (const ch of text2) {
|
|
24716
|
+
const b = utf8(ch);
|
|
24717
|
+
if (n2 + b > max) break;
|
|
24718
|
+
out2 += ch;
|
|
24719
|
+
n2 += b;
|
|
24720
|
+
}
|
|
24721
|
+
return out2;
|
|
24716
24722
|
}
|
|
24717
24723
|
var normal = (line2) => line2.trim().toLowerCase().replace(/\s+/g, " ");
|
|
24718
|
-
function
|
|
24724
|
+
function readDocs(root, files) {
|
|
24719
24725
|
const nested = files.filter((f) => NESTED_DOC.test(f) && !NOT_THE_TEAMS.test(f) && safePath(f) !== null).sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
24720
24726
|
const plan = [
|
|
24721
|
-
...ROOT_DOCS.map((n2) => [n2,
|
|
24722
|
-
...nested.map((n2) => [n2,
|
|
24723
|
-
...CONTRIBUTOR_DOCS.map((n2) => [n2,
|
|
24727
|
+
...ROOT_DOCS.map((n2) => [n2, DOC_LIMITS.docBytes]),
|
|
24728
|
+
...nested.map((n2) => [n2, DOC_LIMITS.nestedDocBytes]),
|
|
24729
|
+
...CONTRIBUTOR_DOCS.map((n2) => [n2, DOC_LIMITS.contributorDocBytes]),
|
|
24724
24730
|
[README, README_BYTES]
|
|
24725
24731
|
];
|
|
24726
24732
|
const out2 = [];
|
|
24727
24733
|
const sent = /* @__PURE__ */ new Set();
|
|
24728
24734
|
let total = 0;
|
|
24729
24735
|
for (const [name2, cap2] of plan) {
|
|
24730
|
-
const room = Math.min(cap2,
|
|
24731
|
-
if (out2.length >=
|
|
24736
|
+
const room = Math.min(cap2, DOC_LIMITS.docsTotalBytes - total);
|
|
24737
|
+
if (out2.length >= DOC_LIMITS.docs || room < 200) break;
|
|
24732
24738
|
if (name2 === README && out2.some((d) => ROOT_DOCS.includes(d.name))) continue;
|
|
24733
24739
|
const read2 = readRegularFileInRoot(root, name2, 256 * 1024);
|
|
24734
24740
|
if (!read2.ok) continue;
|
|
@@ -24736,13 +24742,78 @@ function docs(root, files) {
|
|
|
24736
24742
|
const text2 = fitDoc(fresh, room);
|
|
24737
24743
|
if (!text2) continue;
|
|
24738
24744
|
for (const l of text2.split("\n")) if (normal(l).length >= 25) sent.add(normal(l));
|
|
24739
|
-
total += text2
|
|
24745
|
+
total += utf8(text2);
|
|
24740
24746
|
out2.push({ name: name2, text: text2 });
|
|
24741
24747
|
}
|
|
24742
24748
|
return out2;
|
|
24743
24749
|
}
|
|
24750
|
+
|
|
24751
|
+
// src/lib/repo-profile.ts
|
|
24752
|
+
var PROFILE_SCHEMA = "repo-profile/v1";
|
|
24753
|
+
var PROFILE_FILE = ".verity/repo-profile.json";
|
|
24754
|
+
var LIMITS = {
|
|
24755
|
+
languages: 20,
|
|
24756
|
+
frameworks: 50,
|
|
24757
|
+
dependencies: 300,
|
|
24758
|
+
folders: 300,
|
|
24759
|
+
commits: 500,
|
|
24760
|
+
// The server allows 20; 10 keeps a full profile well under its body cap.
|
|
24761
|
+
pathsPerCommit: 10,
|
|
24762
|
+
subject: 120,
|
|
24763
|
+
path: 200,
|
|
24764
|
+
name: 100
|
|
24765
|
+
};
|
|
24766
|
+
var WINDOW_DAYS = 180;
|
|
24767
|
+
var MAX_PROFILE_BYTES = 120 * 1024;
|
|
24768
|
+
var DEP = /^[A-Za-z0-9@][A-Za-z0-9@._/:+-]*$/;
|
|
24769
|
+
function dependencies(root, files) {
|
|
24770
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24771
|
+
for (const name2 of declaredDependencies(root, files)) {
|
|
24772
|
+
if (name2.length <= LIMITS.name && DEP.test(name2)) seen.add(name2);
|
|
24773
|
+
if (seen.size >= LIMITS.dependencies) break;
|
|
24774
|
+
}
|
|
24775
|
+
return [...seen];
|
|
24776
|
+
}
|
|
24777
|
+
function tree(files) {
|
|
24778
|
+
const counts = /* @__PURE__ */ new Map();
|
|
24779
|
+
for (const file of files) {
|
|
24780
|
+
const parts2 = file.split("/");
|
|
24781
|
+
for (let depth = 1; depth <= Math.min(3, parts2.length - 1); depth++) {
|
|
24782
|
+
const folder = parts2.slice(0, depth).join("/");
|
|
24783
|
+
counts.set(folder, (counts.get(folder) ?? 0) + 1);
|
|
24784
|
+
}
|
|
24785
|
+
}
|
|
24786
|
+
return [...counts.entries()].filter(([path]) => safePath(path) !== null).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, LIMITS.folders).map(([path, n2]) => ({ path, files: n2 })).sort((a, b) => a.path.localeCompare(b.path));
|
|
24787
|
+
}
|
|
24788
|
+
function history(root) {
|
|
24789
|
+
const res = runGitSync(root, [
|
|
24790
|
+
"log",
|
|
24791
|
+
`--since=${WINDOW_DAYS} days ago`,
|
|
24792
|
+
"-n",
|
|
24793
|
+
String(LIMITS.commits),
|
|
24794
|
+
"--no-merges",
|
|
24795
|
+
"--format=@@%x1f%s",
|
|
24796
|
+
"--name-only",
|
|
24797
|
+
"--"
|
|
24798
|
+
], { timeoutMs: 3e4 });
|
|
24799
|
+
const commits = [];
|
|
24800
|
+
if (!res.ok) return { window_days: WINDOW_DAYS, commits };
|
|
24801
|
+
let current = null;
|
|
24802
|
+
for (const line2 of res.stdout.split("\n")) {
|
|
24803
|
+
if (line2.startsWith("@@")) {
|
|
24804
|
+
if (current && current.subject) commits.push(current);
|
|
24805
|
+
const raw = line2.slice(3);
|
|
24806
|
+
current = { subject: oneLine(raw, LIMITS.subject), paths: [], fix: FIX_SUBJECT.test(raw) };
|
|
24807
|
+
continue;
|
|
24808
|
+
}
|
|
24809
|
+
const path = safePath(line2.trim());
|
|
24810
|
+
if (current && path && current.paths.length < LIMITS.pathsPerCommit) current.paths.push(path);
|
|
24811
|
+
}
|
|
24812
|
+
if (current && current.subject) commits.push(current);
|
|
24813
|
+
return { window_days: WINDOW_DAYS, commits: commits.slice(0, LIMITS.commits) };
|
|
24814
|
+
}
|
|
24744
24815
|
function fitProfile(p) {
|
|
24745
|
-
const size = () => JSON.stringify(p)
|
|
24816
|
+
const size = () => Buffer.byteLength(JSON.stringify(p), "utf8");
|
|
24746
24817
|
if (size() <= MAX_PROFILE_BYTES) return p;
|
|
24747
24818
|
const commits = p.history.commits;
|
|
24748
24819
|
for (const wantFix of [false, true]) {
|
|
@@ -24750,6 +24821,9 @@ function fitProfile(p) {
|
|
|
24750
24821
|
if (commits[i2].fix === wantFix) commits.splice(i2, 1);
|
|
24751
24822
|
}
|
|
24752
24823
|
}
|
|
24824
|
+
while (size() > MAX_PROFILE_BYTES && p.docs.length > 0) p.docs.pop();
|
|
24825
|
+
while (size() > MAX_PROFILE_BYTES && p.tree.length > 0) p.tree.pop();
|
|
24826
|
+
while (size() > MAX_PROFILE_BYTES && p.dependencies.length > 0) p.dependencies.pop();
|
|
24753
24827
|
return p;
|
|
24754
24828
|
}
|
|
24755
24829
|
function buildRepoProfile(root = repoRoot()) {
|
|
@@ -24771,7 +24845,7 @@ function buildRepoProfile(root = repoRoot()) {
|
|
|
24771
24845
|
dependencies: dependencies(root, files),
|
|
24772
24846
|
tree: tree(files),
|
|
24773
24847
|
history: history(root),
|
|
24774
|
-
docs:
|
|
24848
|
+
docs: readDocs(root, files)
|
|
24775
24849
|
});
|
|
24776
24850
|
}
|
|
24777
24851
|
function describeProfile(p) {
|
|
@@ -24795,8 +24869,29 @@ function consentLines(p) {
|
|
|
24795
24869
|
];
|
|
24796
24870
|
}
|
|
24797
24871
|
|
|
24872
|
+
// src/commands/standard-generate.ts
|
|
24873
|
+
var import_yaml5 = __toESM2(require_dist());
|
|
24874
|
+
|
|
24798
24875
|
// src/lib/generate-standard.ts
|
|
24799
24876
|
var import_yaml4 = __toESM2(require_dist());
|
|
24877
|
+
async function generationStatus(opts) {
|
|
24878
|
+
const res = await apiRequest({
|
|
24879
|
+
method: "GET",
|
|
24880
|
+
path: "/standards/generate",
|
|
24881
|
+
serviceUrl: opts.serviceUrl,
|
|
24882
|
+
token: opts.token,
|
|
24883
|
+
verbose: opts.verbose,
|
|
24884
|
+
cmd: "standard_generate_status"
|
|
24885
|
+
});
|
|
24886
|
+
if (!res.ok) {
|
|
24887
|
+
if (res.http_status === 404 && /Unknown route/.test(res.error)) return "unsupported";
|
|
24888
|
+
if (res.http_status === 400 && /Version must be a positive integer/.test(res.error)) return "unsupported";
|
|
24889
|
+
return "unknown";
|
|
24890
|
+
}
|
|
24891
|
+
if (res.data.enabled === true) return "on";
|
|
24892
|
+
if (res.data.enabled === false) return "off";
|
|
24893
|
+
return "unknown";
|
|
24894
|
+
}
|
|
24800
24895
|
var GENERATE_TIMEOUT_MS = 6e4;
|
|
24801
24896
|
function rulesFrom(content, before) {
|
|
24802
24897
|
const list2 = Array.isArray(content.custom_patterns) ? content.custom_patterns : [];
|
|
@@ -24895,6 +24990,94 @@ async function promptYes(question, opts) {
|
|
|
24895
24990
|
return a === "" || a === "y" || a === "yes";
|
|
24896
24991
|
}
|
|
24897
24992
|
|
|
24993
|
+
// src/commands/standard-generate.ts
|
|
24994
|
+
function generationBase() {
|
|
24995
|
+
const local = readRepoFile(STANDARD_FILE);
|
|
24996
|
+
try {
|
|
24997
|
+
const parsed = local !== null ? (0, import_yaml5.parse)(local) : null;
|
|
24998
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : buildStandard({ detected: detectProject() }, loadCatalog());
|
|
24999
|
+
} catch (err2) {
|
|
25000
|
+
printError(`${STANDARD_FILE} is not valid YAML: ${err2.message}. Fix it, or delete it, and run this again.`);
|
|
25001
|
+
process.exit(1);
|
|
25002
|
+
}
|
|
25003
|
+
}
|
|
25004
|
+
async function consentToSend(profile, yes) {
|
|
25005
|
+
try {
|
|
25006
|
+
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
25007
|
+
} catch (err2) {
|
|
25008
|
+
printError(`Could not write ${PROFILE_FILE} for you to read: ${err2.message}`);
|
|
25009
|
+
process.exit(1);
|
|
25010
|
+
}
|
|
25011
|
+
for (const line2 of consentLines(profile)) printInfo(line2);
|
|
25012
|
+
if (yes) return true;
|
|
25013
|
+
if (!interactive()) {
|
|
25014
|
+
printError(`Nothing was sent. Read ${PROFILE_FILE}, then run this again with --yes to send it.`);
|
|
25015
|
+
process.exit(1);
|
|
25016
|
+
}
|
|
25017
|
+
const send = await promptYes("Send the profile? [Y/n] ", { nonInteractive: false });
|
|
25018
|
+
if (!send) printInfo("Not sent. Nothing changed.");
|
|
25019
|
+
return send;
|
|
25020
|
+
}
|
|
25021
|
+
async function runGenerate(globals, opts) {
|
|
25022
|
+
const tokenResult = await resolveToken(globals.token);
|
|
25023
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
25024
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
25025
|
+
printError("Sign in first: verity login. Rule generation needs your Verity account.");
|
|
25026
|
+
process.exit(1);
|
|
25027
|
+
}
|
|
25028
|
+
const status = await generationStatus({ serviceUrl: urlResult.data, token: tokenResult.data.token, verbose: globals.verbose });
|
|
25029
|
+
if (opts.status) {
|
|
25030
|
+
process.stdout.write(`${status}
|
|
25031
|
+
`);
|
|
25032
|
+
return;
|
|
25033
|
+
}
|
|
25034
|
+
if (status === "off") {
|
|
25035
|
+
printWarn("Rule generation is not switched on for this project yet. The built-in rules apply. Nothing was sent.");
|
|
25036
|
+
return;
|
|
25037
|
+
}
|
|
25038
|
+
if (status === "unsupported") {
|
|
25039
|
+
printWarn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply. Nothing was sent.");
|
|
25040
|
+
return;
|
|
25041
|
+
}
|
|
25042
|
+
const base = generationBase();
|
|
25043
|
+
const profile = buildRepoProfile();
|
|
25044
|
+
if (!await consentToSend(profile, opts.yes === true)) return;
|
|
25045
|
+
const spinner = startSpinner("Writing rules for this repository");
|
|
25046
|
+
const outcome = await generateStandard({
|
|
25047
|
+
serviceUrl: urlResult.data,
|
|
25048
|
+
token: tokenResult.data.token,
|
|
25049
|
+
profile,
|
|
25050
|
+
base,
|
|
25051
|
+
verbose: globals.verbose
|
|
25052
|
+
});
|
|
25053
|
+
if (outcome.status === "created") {
|
|
25054
|
+
spinner.succeed(`${outcome.rules.length} ${outcome.rules.length === 1 ? "rule" : "rules"} for this repository \xB7 Standard v${outcome.version}`);
|
|
25055
|
+
for (const r of outcome.rules) {
|
|
25056
|
+
printInfo(` ${r.id}`);
|
|
25057
|
+
printInfo(` ${r.description}`);
|
|
25058
|
+
printInfo(` Applies: ${r.scope} \xB7 ${r.basis}`);
|
|
25059
|
+
}
|
|
25060
|
+
if (outcome.rejected > 0) printInfo(` ${outcome.rejected} proposed rule(s) were left out: too generic, or not safe to use.`);
|
|
25061
|
+
printInfo(`Edit them in ${STANDARD_FILE}, then: verity standard push`);
|
|
25062
|
+
if (outcome.warning) printWarn(outcome.warning);
|
|
25063
|
+
return;
|
|
25064
|
+
}
|
|
25065
|
+
if (outcome.status === "exists") {
|
|
25066
|
+
spinner.warn('This project already has a Standard on the Verity service. Run "verity init" to use it.');
|
|
25067
|
+
return;
|
|
25068
|
+
}
|
|
25069
|
+
if (outcome.status === "disabled") {
|
|
25070
|
+
spinner.warn("Rule generation is not switched on for this project yet. The built-in rules apply.");
|
|
25071
|
+
return;
|
|
25072
|
+
}
|
|
25073
|
+
if (outcome.status === "unsupported") {
|
|
25074
|
+
spinner.warn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply.");
|
|
25075
|
+
return;
|
|
25076
|
+
}
|
|
25077
|
+
spinner.warn(`The rules could not be written: ${outcome.reason}`);
|
|
25078
|
+
process.exit(1);
|
|
25079
|
+
}
|
|
25080
|
+
|
|
24898
25081
|
// src/commands/standard.ts
|
|
24899
25082
|
function registerStandardCommands(program2) {
|
|
24900
25083
|
const standard = program2.command("standard").description("Manage the project Standard");
|
|
@@ -24911,15 +25094,15 @@ function registerStandardCommands(program2) {
|
|
|
24911
25094
|
serviceUrl: urlResult.ok ? urlResult.data : void 0
|
|
24912
25095
|
});
|
|
24913
25096
|
if ("refused" in result) {
|
|
24914
|
-
printWarn(result.refused);
|
|
25097
|
+
printWarn(terminalText(result.refused, 300));
|
|
24915
25098
|
process.exit(1);
|
|
24916
25099
|
}
|
|
24917
|
-
printInfo(` ${describeDetected(result.detected)}`);
|
|
24918
|
-
for (const path of result.written) printInfo(` ${path} \u2713`);
|
|
24919
|
-
for (const note of result.notes) printWarn(` ${note}`);
|
|
25100
|
+
printInfo(` ${terminalText(describeDetected(result.detected), 400)}`);
|
|
25101
|
+
for (const path of result.written) printInfo(` ${terminalText(path, 200)} \u2713`);
|
|
25102
|
+
for (const note of result.notes) printWarn(` ${terminalText(note, 300)}`);
|
|
24920
25103
|
if (opts.push) {
|
|
24921
25104
|
const pushed = await pushStandard(globals);
|
|
24922
|
-
for (const line2 of pushed.lines) printInfo(` ${line2}`);
|
|
25105
|
+
for (const line2 of pushed.lines) printInfo(` ${terminalText(line2, 600)}`);
|
|
24923
25106
|
if (pushed.standardVersion !== null) {
|
|
24924
25107
|
await correctVerityMdVersion({
|
|
24925
25108
|
standard: result.standard,
|
|
@@ -24930,7 +25113,7 @@ function registerStandardCommands(program2) {
|
|
|
24930
25113
|
}
|
|
24931
25114
|
}
|
|
24932
25115
|
});
|
|
24933
|
-
standard.command("generate").description("Have Verity write project-specific rules from a profile of this repository (asks before sending it)").option("--yes", `Send the profile without asking (you have read ${PROFILE_FILE})`).action((opts) => runGenerate(program2.opts(), opts));
|
|
25116
|
+
standard.command("generate").description("Have Verity write project-specific rules from a profile of this repository (asks before sending it)").option("--yes", `Send the profile without asking (you have read ${PROFILE_FILE})`).option("--status", "Print whether the service writes project rules for this project: on, off, unsupported or unknown. Sends nothing.").action((opts) => runGenerate(program2.opts(), opts));
|
|
24934
25117
|
standard.command("push").description("Upload the project Standard to the service").option("--file <path>", "Path to standard YAML file", STANDARD_FILE).option("--created-by <name>", "Who created this version", "claude-code").action(async (opts) => {
|
|
24935
25118
|
const globals = program2.opts();
|
|
24936
25119
|
const tokenResult = await resolveToken(globals.token);
|
|
@@ -24952,7 +25135,7 @@ function registerStandardCommands(program2) {
|
|
|
24952
25135
|
}
|
|
24953
25136
|
let content;
|
|
24954
25137
|
try {
|
|
24955
|
-
content = (0,
|
|
25138
|
+
content = (0, import_yaml6.parse)(yamlContent);
|
|
24956
25139
|
} catch (err2) {
|
|
24957
25140
|
printError(`Invalid YAML in ${opts.file}: ${err2.message}`);
|
|
24958
25141
|
process.exit(1);
|
|
@@ -24968,10 +25151,10 @@ function registerStandardCommands(program2) {
|
|
|
24968
25151
|
verbose: globals.verbose
|
|
24969
25152
|
});
|
|
24970
25153
|
if (!result.ok) {
|
|
24971
|
-
printError(result.error);
|
|
25154
|
+
printError(terminalText(result.error, 300));
|
|
24972
25155
|
process.exit(1);
|
|
24973
25156
|
}
|
|
24974
|
-
printInfo(`Standard uploaded (version ${result.data.version})`);
|
|
25157
|
+
printInfo(`Standard uploaded (version ${versionText(result.data.version)})`);
|
|
24975
25158
|
printJson(result.data);
|
|
24976
25159
|
});
|
|
24977
25160
|
standard.command("get").description("Get the current Standard from the service").option("--version <n>", "Specific version number").action(async (opts) => {
|
|
@@ -24986,7 +25169,15 @@ function registerStandardCommands(program2) {
|
|
|
24986
25169
|
printError(urlResult.error);
|
|
24987
25170
|
process.exit(1);
|
|
24988
25171
|
}
|
|
24989
|
-
|
|
25172
|
+
let path = "/standards/latest";
|
|
25173
|
+
if (opts.version !== void 0) {
|
|
25174
|
+
const version = Number(opts.version);
|
|
25175
|
+
if (!/^[1-9][0-9]*$/.test(String(opts.version)) || !Number.isSafeInteger(version)) {
|
|
25176
|
+
printError(`--version: "${terminalText(opts.version, 40)}" is not a Standard version. Use a positive whole number, such as --version 3.`);
|
|
25177
|
+
process.exit(1);
|
|
25178
|
+
}
|
|
25179
|
+
path = `/standards/${version}`;
|
|
25180
|
+
}
|
|
24990
25181
|
const result = await apiRequest({
|
|
24991
25182
|
method: "GET",
|
|
24992
25183
|
path,
|
|
@@ -24995,7 +25186,7 @@ function registerStandardCommands(program2) {
|
|
|
24995
25186
|
verbose: globals.verbose
|
|
24996
25187
|
});
|
|
24997
25188
|
if (!result.ok) {
|
|
24998
|
-
printError(result.error);
|
|
25189
|
+
printError(terminalText(result.error, 300));
|
|
24999
25190
|
process.exit(1);
|
|
25000
25191
|
}
|
|
25001
25192
|
printJson(result.data);
|
|
@@ -25020,93 +25211,24 @@ function registerStandardCommands(program2) {
|
|
|
25020
25211
|
verbose: globals.verbose
|
|
25021
25212
|
});
|
|
25022
25213
|
if (!result.ok) {
|
|
25023
|
-
printError(result.error);
|
|
25214
|
+
printError(terminalText(result.error, 300));
|
|
25024
25215
|
process.exit(1);
|
|
25025
25216
|
}
|
|
25026
|
-
const versions = result.data.versions;
|
|
25217
|
+
const versions = Array.isArray(result.data.versions) ? result.data.versions : [];
|
|
25027
25218
|
if (versions.length === 0) {
|
|
25028
25219
|
printInfo("No Standard versions found.");
|
|
25029
25220
|
return;
|
|
25030
25221
|
}
|
|
25031
25222
|
printInfo("Standard versions:");
|
|
25032
25223
|
for (const v of versions) {
|
|
25033
|
-
const date = v.created_at.slice(0, 19).replace("T", " ");
|
|
25224
|
+
const date = typeof v.created_at === "string" ? v.created_at.slice(0, 19).replace("T", " ") : "unknown date";
|
|
25034
25225
|
const by = v.created_by ?? "unknown";
|
|
25035
|
-
printInfo(` v${v.version} ${date} (by ${by})`);
|
|
25226
|
+
printInfo(` v${versionText(v.version)} ${terminalText(date, 40)} (by ${terminalText(by, 80)})`);
|
|
25036
25227
|
}
|
|
25037
25228
|
});
|
|
25038
25229
|
}
|
|
25039
|
-
function
|
|
25040
|
-
|
|
25041
|
-
try {
|
|
25042
|
-
const parsed = local !== null ? (0, import_yaml5.parse)(local) : null;
|
|
25043
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : buildStandard({ detected: detectProject() }, loadCatalog());
|
|
25044
|
-
} catch (err2) {
|
|
25045
|
-
printError(`${STANDARD_FILE} is not valid YAML: ${err2.message}. Fix it, or delete it, and run this again.`);
|
|
25046
|
-
process.exit(1);
|
|
25047
|
-
}
|
|
25048
|
-
}
|
|
25049
|
-
async function consentToSend(profile, yes) {
|
|
25050
|
-
try {
|
|
25051
|
-
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
25052
|
-
} catch (err2) {
|
|
25053
|
-
printError(`Could not write ${PROFILE_FILE} for you to read: ${err2.message}`);
|
|
25054
|
-
process.exit(1);
|
|
25055
|
-
}
|
|
25056
|
-
for (const line2 of consentLines(profile)) printInfo(line2);
|
|
25057
|
-
if (yes) return true;
|
|
25058
|
-
if (!interactive()) {
|
|
25059
|
-
printError(`Nothing was sent. Read ${PROFILE_FILE}, then run this again with --yes to send it.`);
|
|
25060
|
-
process.exit(1);
|
|
25061
|
-
}
|
|
25062
|
-
const send = await promptYes("Send the profile? [Y/n] ", { nonInteractive: false });
|
|
25063
|
-
if (!send) printInfo("Not sent. Nothing changed.");
|
|
25064
|
-
return send;
|
|
25065
|
-
}
|
|
25066
|
-
async function runGenerate(globals, opts) {
|
|
25067
|
-
const tokenResult = await resolveToken(globals.token);
|
|
25068
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
25069
|
-
if (!tokenResult.ok || !urlResult.ok) {
|
|
25070
|
-
printError("Sign in first: verity login. Rule generation needs your Verity account.");
|
|
25071
|
-
process.exit(1);
|
|
25072
|
-
}
|
|
25073
|
-
const base = generationBase();
|
|
25074
|
-
const profile = buildRepoProfile();
|
|
25075
|
-
if (!await consentToSend(profile, opts.yes === true)) return;
|
|
25076
|
-
const spinner = startSpinner("Writing rules for this repository");
|
|
25077
|
-
const outcome = await generateStandard({
|
|
25078
|
-
serviceUrl: urlResult.data,
|
|
25079
|
-
token: tokenResult.data.token,
|
|
25080
|
-
profile,
|
|
25081
|
-
base,
|
|
25082
|
-
verbose: globals.verbose
|
|
25083
|
-
});
|
|
25084
|
-
if (outcome.status === "created") {
|
|
25085
|
-
spinner.succeed(`${outcome.rules.length} ${outcome.rules.length === 1 ? "rule" : "rules"} for this repository \xB7 Standard v${outcome.version}`);
|
|
25086
|
-
for (const r of outcome.rules) {
|
|
25087
|
-
printInfo(` ${r.id}`);
|
|
25088
|
-
printInfo(` ${r.description}`);
|
|
25089
|
-
printInfo(` Applies: ${r.scope} \xB7 ${r.basis}`);
|
|
25090
|
-
}
|
|
25091
|
-
if (outcome.rejected > 0) printInfo(` ${outcome.rejected} proposed rule(s) were left out: too generic, or not safe to use.`);
|
|
25092
|
-
printInfo(`Edit them in ${STANDARD_FILE}, then: verity standard push`);
|
|
25093
|
-
if (outcome.warning) printWarn(outcome.warning);
|
|
25094
|
-
return;
|
|
25095
|
-
}
|
|
25096
|
-
if (outcome.status === "exists") {
|
|
25097
|
-
spinner.warn('This project already has a Standard on the Verity service. Run "verity init" to use it.');
|
|
25098
|
-
return;
|
|
25099
|
-
}
|
|
25100
|
-
if (outcome.status === "disabled") {
|
|
25101
|
-
spinner.warn("Rule generation is not switched on for this project yet. The built-in rules apply.");
|
|
25102
|
-
return;
|
|
25103
|
-
}
|
|
25104
|
-
if (outcome.status === "unsupported") {
|
|
25105
|
-
spinner.warn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply.");
|
|
25106
|
-
return;
|
|
25107
|
-
}
|
|
25108
|
-
spinner.warn(`The rules could not be written: ${outcome.reason}`);
|
|
25109
|
-
process.exit(1);
|
|
25230
|
+
function versionText(value) {
|
|
25231
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? String(value) : "?";
|
|
25110
25232
|
}
|
|
25111
25233
|
|
|
25112
25234
|
// src/commands/config.ts
|
|
@@ -25163,11 +25285,11 @@ async function currentReviewMoments(pluginMode) {
|
|
|
25163
25285
|
const cfg = readProjectConfig();
|
|
25164
25286
|
const hooks = await checkAllVerityHooks();
|
|
25165
25287
|
const turn = cfg.turn_review !== "off" && (pluginMode || hooks.stop);
|
|
25166
|
-
const
|
|
25288
|
+
const git3 = pluginMode ? cfg.git_moments : hooks.guard ? hooks.guardOn.length > 0 ? hooks.guardOn : cfg.git_moments : [];
|
|
25167
25289
|
return [
|
|
25168
25290
|
...turn ? ["stop"] : [],
|
|
25169
|
-
...
|
|
25170
|
-
...
|
|
25291
|
+
...git3.includes("commit") ? ["pre-commit"] : [],
|
|
25292
|
+
...git3.includes("push") ? ["pre-push"] : []
|
|
25171
25293
|
];
|
|
25172
25294
|
}
|
|
25173
25295
|
async function setReviewMoments(moments, pluginMode) {
|
|
@@ -26120,7 +26242,7 @@ function createRun(opts, globals) {
|
|
|
26120
26242
|
|
|
26121
26243
|
// src/commands/analyze/index.ts
|
|
26122
26244
|
var import_node_fs55 = require("node:fs");
|
|
26123
|
-
var
|
|
26245
|
+
var import_node_path43 = require("node:path");
|
|
26124
26246
|
|
|
26125
26247
|
// src/lib/repo-context.ts
|
|
26126
26248
|
var import_node_child_process11 = require("node:child_process");
|
|
@@ -28244,7 +28366,7 @@ function channelSilence(input) {
|
|
|
28244
28366
|
// src/lib/cli-version.ts
|
|
28245
28367
|
function cliVersion() {
|
|
28246
28368
|
try {
|
|
28247
|
-
return true ? "0.35.0-experimental.
|
|
28369
|
+
return true ? "0.35.0-experimental.3a13a5e" : "dev";
|
|
28248
28370
|
} catch {
|
|
28249
28371
|
return "dev";
|
|
28250
28372
|
}
|
|
@@ -28285,6 +28407,8 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
28285
28407
|
|
|
28286
28408
|
// src/lib/static-analysis.ts
|
|
28287
28409
|
var import_node_child_process12 = require("node:child_process");
|
|
28410
|
+
var import_node_module = require("node:module");
|
|
28411
|
+
var import_node_path34 = require("node:path");
|
|
28288
28412
|
var import_node_fs44 = require("node:fs");
|
|
28289
28413
|
|
|
28290
28414
|
// src/lib/static-config.ts
|
|
@@ -28331,19 +28455,44 @@ var SEVERITY_ORDER = {
|
|
|
28331
28455
|
Low: 3
|
|
28332
28456
|
};
|
|
28333
28457
|
function isCodacyAvailable() {
|
|
28334
|
-
return
|
|
28458
|
+
return resolveAnalyzer() !== null;
|
|
28459
|
+
}
|
|
28460
|
+
var NO_BUNDLED_ANALYZER_ENV = "VERITY_NO_BUNDLED_ANALYZER";
|
|
28461
|
+
function bundledAnalyzer(from = __filename) {
|
|
28462
|
+
try {
|
|
28463
|
+
const pkgPath = (0, import_node_module.createRequire)(from).resolve("@codacy/analysis-cli/package.json");
|
|
28464
|
+
const pkg = JSON.parse((0, import_node_fs44.readFileSync)(pkgPath, "utf-8"));
|
|
28465
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["codacy-analysis"];
|
|
28466
|
+
if (typeof bin !== "string" || bin === "") return null;
|
|
28467
|
+
const entry = (0, import_node_path34.join)((0, import_node_path34.dirname)(pkgPath), bin);
|
|
28468
|
+
if (!(0, import_node_fs44.statSync)(entry).isFile()) return null;
|
|
28469
|
+
return { path: entry, version: typeof pkg.version === "string" ? pkg.version : "unknown" };
|
|
28470
|
+
} catch {
|
|
28471
|
+
return null;
|
|
28472
|
+
}
|
|
28335
28473
|
}
|
|
28336
|
-
function
|
|
28337
|
-
|
|
28474
|
+
function resolveAnalyzer(opts = {}) {
|
|
28475
|
+
const env = opts.env ?? process.env;
|
|
28476
|
+
if (env[NO_BUNDLED_ANALYZER_ENV] !== "1") {
|
|
28477
|
+
const bundled = (opts.bundled ?? bundledAnalyzer)();
|
|
28478
|
+
if (bundled) return { source: "bundled", path: bundled.path, version: bundled.version };
|
|
28479
|
+
}
|
|
28480
|
+
const onPath = (opts.onPath ?? (() => whichSync("codacy-analysis")))();
|
|
28481
|
+
return onPath ? { source: "path", path: onPath } : null;
|
|
28482
|
+
}
|
|
28483
|
+
function analyzerCommand(analyzer, argv, execPath = process.execPath) {
|
|
28484
|
+
if (analyzer.source === "bundled") return { command: execPath, args: [analyzer.path, ...argv] };
|
|
28485
|
+
return { command: analyzer.path, args: argv };
|
|
28338
28486
|
}
|
|
28339
28487
|
function startTrivyPrewarm(opts = {}) {
|
|
28340
28488
|
if ((opts.platform ?? process.platform) === "win32") return false;
|
|
28341
28489
|
if ((opts.env ?? process.env).CI) return false;
|
|
28342
|
-
const analyzer =
|
|
28490
|
+
const analyzer = (opts.resolve ?? resolveAnalyzer)();
|
|
28343
28491
|
const config = ensureStaticConfig();
|
|
28344
28492
|
if (!analyzer || !config) return false;
|
|
28345
28493
|
try {
|
|
28346
|
-
const
|
|
28494
|
+
const { command, args: args2 } = analyzerCommand(analyzer, buildAnalyzerArgv([STATIC_ANALYSIS_CONFIG_FILE], config));
|
|
28495
|
+
const child = (opts.run ?? import_node_child_process12.spawn)(command, args2, {
|
|
28347
28496
|
cwd: repoRoot(),
|
|
28348
28497
|
detached: true,
|
|
28349
28498
|
stdio: "ignore"
|
|
@@ -28390,7 +28539,7 @@ function runCodacyAnalysisIfAvailable(files) {
|
|
|
28390
28539
|
if (!isCodacyAvailable()) {
|
|
28391
28540
|
return withFailure(
|
|
28392
28541
|
"analyzer_unavailable",
|
|
28393
|
-
"@codacy/analysis-cli was not found on PATH \u2014 no static analysis ran"
|
|
28542
|
+
"@codacy/analysis-cli was not bundled with this Verity install and was not found on PATH \u2014 no static analysis ran. Reinstall Verity: npm install -g @codacy/verity-cli"
|
|
28394
28543
|
);
|
|
28395
28544
|
}
|
|
28396
28545
|
return runCodacyAnalysis(files);
|
|
@@ -28413,8 +28562,10 @@ function runCodacyAnalysis(files) {
|
|
|
28413
28562
|
"Could not write .verity/static-analysis.json \u2014 no static analysis ran"
|
|
28414
28563
|
);
|
|
28415
28564
|
}
|
|
28416
|
-
const
|
|
28417
|
-
const
|
|
28565
|
+
const argv = buildAnalyzerArgv(existingFiles, configPath);
|
|
28566
|
+
const resolved = resolveAnalyzer();
|
|
28567
|
+
const { command, args: args2 } = resolved ? analyzerCommand(resolved, argv) : { command: "codacy-analysis", args: argv };
|
|
28568
|
+
const proc = (0, import_node_child_process12.spawnSync)(command, args2, {
|
|
28418
28569
|
encoding: "utf-8",
|
|
28419
28570
|
maxBuffer: 10 * 1024 * 1024
|
|
28420
28571
|
});
|
|
@@ -28678,7 +28829,7 @@ async function scope(run4) {
|
|
|
28678
28829
|
|
|
28679
28830
|
// src/lib/specs.ts
|
|
28680
28831
|
var import_node_fs45 = require("node:fs");
|
|
28681
|
-
var
|
|
28832
|
+
var import_node_path35 = require("node:path");
|
|
28682
28833
|
var SPEC_CANDIDATES = [
|
|
28683
28834
|
"CLAUDE.md",
|
|
28684
28835
|
"AGENTS.md",
|
|
@@ -28700,7 +28851,7 @@ var UNCONSULTED_TOTAL_BYTES = 30720;
|
|
|
28700
28851
|
function readSpecFiles(specsOpt, root) {
|
|
28701
28852
|
const out2 = [];
|
|
28702
28853
|
for (const raw of specsOpt.split(",").map((f) => f.trim()).filter(Boolean)) {
|
|
28703
|
-
const candidate = (0,
|
|
28854
|
+
const candidate = (0, import_node_path35.isAbsolute)(raw) ? (0, import_node_path35.relative)(root, raw) : raw;
|
|
28704
28855
|
const content = readFileInside(root, candidate, MAX_EXPLICIT_SPEC_FILE_BYTES);
|
|
28705
28856
|
if (content === null) continue;
|
|
28706
28857
|
out2.push({ path: raw, content });
|
|
@@ -28759,7 +28910,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
28759
28910
|
try {
|
|
28760
28911
|
const entries = (0, import_node_fs45.readdirSync)(dir, { withFileTypes: true });
|
|
28761
28912
|
for (const entry of entries) {
|
|
28762
|
-
const fullPath = (0,
|
|
28913
|
+
const fullPath = (0, import_node_path35.join)(dir, entry.name);
|
|
28763
28914
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
28764
28915
|
result.push(fullPath);
|
|
28765
28916
|
} else if (entry.isDirectory() && depth < maxDepth - 1) {
|
|
@@ -28772,10 +28923,10 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
28772
28923
|
}
|
|
28773
28924
|
function discoverPlans() {
|
|
28774
28925
|
const home = process.env.HOME ?? "";
|
|
28775
|
-
const homePlansDir = (0,
|
|
28926
|
+
const homePlansDir = (0, import_node_path35.join)(home, ".claude", "plans");
|
|
28776
28927
|
const sources = [];
|
|
28777
28928
|
if (isRealDirectoryChain(process.cwd(), [".claude", "plans"])) {
|
|
28778
|
-
sources.push({ root: process.cwd(), prefix: (0,
|
|
28929
|
+
sources.push({ root: process.cwd(), prefix: (0, import_node_path35.join)(".claude", "plans") });
|
|
28779
28930
|
}
|
|
28780
28931
|
if (home && (0, import_node_fs45.existsSync)(homePlansDir)) sources.push({ root: homePlansDir, prefix: "" });
|
|
28781
28932
|
const candidates2 = [];
|
|
@@ -28783,14 +28934,14 @@ function discoverPlans() {
|
|
|
28783
28934
|
for (const source2 of sources) {
|
|
28784
28935
|
let names2;
|
|
28785
28936
|
try {
|
|
28786
|
-
names2 = (0, import_node_fs45.readdirSync)(source2.prefix ? (0,
|
|
28937
|
+
names2 = (0, import_node_fs45.readdirSync)(source2.prefix ? (0, import_node_path35.join)(source2.root, source2.prefix) : source2.root);
|
|
28787
28938
|
} catch {
|
|
28788
28939
|
continue;
|
|
28789
28940
|
}
|
|
28790
28941
|
for (const f of names2) {
|
|
28791
28942
|
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
28792
28943
|
seen.add(f);
|
|
28793
|
-
const relPath = source2.prefix ? (0,
|
|
28944
|
+
const relPath = source2.prefix ? (0, import_node_path35.join)(source2.prefix, f) : f;
|
|
28794
28945
|
const stat3 = statRegularInRoot(source2.root, relPath);
|
|
28795
28946
|
if (!stat3.ok) continue;
|
|
28796
28947
|
candidates2.push({ name: f, root: source2.root, relPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
@@ -28808,7 +28959,7 @@ function discoverPlans() {
|
|
|
28808
28959
|
function isRealDirectoryChain(root, parts2) {
|
|
28809
28960
|
let current = root;
|
|
28810
28961
|
for (const part of parts2) {
|
|
28811
|
-
current = (0,
|
|
28962
|
+
current = (0, import_node_path35.join)(current, part);
|
|
28812
28963
|
try {
|
|
28813
28964
|
if (!(0, import_node_fs45.lstatSync)(current).isDirectory()) return false;
|
|
28814
28965
|
} catch {
|
|
@@ -29012,7 +29163,7 @@ async function mode(run4) {
|
|
|
29012
29163
|
|
|
29013
29164
|
// src/lib/fold.ts
|
|
29014
29165
|
var import_node_fs46 = require("node:fs");
|
|
29015
|
-
var
|
|
29166
|
+
var import_node_path36 = require("node:path");
|
|
29016
29167
|
var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
29017
29168
|
"user",
|
|
29018
29169
|
"assistant",
|
|
@@ -29244,9 +29395,9 @@ function fold(transcriptPath, opts = {}) {
|
|
|
29244
29395
|
return result;
|
|
29245
29396
|
}
|
|
29246
29397
|
try {
|
|
29247
|
-
const sidecarDir = (0,
|
|
29248
|
-
(0,
|
|
29249
|
-
(0,
|
|
29398
|
+
const sidecarDir = (0, import_node_path36.join)(
|
|
29399
|
+
(0, import_node_path36.dirname)(transcriptPath),
|
|
29400
|
+
(0, import_node_path36.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
29250
29401
|
"subagents"
|
|
29251
29402
|
);
|
|
29252
29403
|
if ((0, import_node_fs46.existsSync)(sidecarDir)) {
|
|
@@ -29256,7 +29407,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
29256
29407
|
const walk2 = (d, depth) => {
|
|
29257
29408
|
if (depth > 4) return;
|
|
29258
29409
|
for (const e of (0, import_node_fs46.readdirSync)(d, { withFileTypes: true })) {
|
|
29259
|
-
const p = (0,
|
|
29410
|
+
const p = (0, import_node_path36.join)(d, e.name);
|
|
29260
29411
|
if (e.isDirectory()) {
|
|
29261
29412
|
walk2(p, depth + 1);
|
|
29262
29413
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
@@ -29629,7 +29780,7 @@ async function evidence(run4) {
|
|
|
29629
29780
|
|
|
29630
29781
|
// src/lib/cache-cleanup.ts
|
|
29631
29782
|
var import_node_fs47 = require("node:fs");
|
|
29632
|
-
var
|
|
29783
|
+
var import_node_path37 = require("node:path");
|
|
29633
29784
|
var CACHE_TTL_DAYS = 7;
|
|
29634
29785
|
function pruneStaleCache() {
|
|
29635
29786
|
try {
|
|
@@ -29637,7 +29788,7 @@ function pruneStaleCache() {
|
|
|
29637
29788
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
29638
29789
|
for (const entry of (0, import_node_fs47.readdirSync)(dir)) {
|
|
29639
29790
|
if (!entry.startsWith("pending-")) continue;
|
|
29640
|
-
const path = (0,
|
|
29791
|
+
const path = (0, import_node_path37.join)(dir, entry);
|
|
29641
29792
|
try {
|
|
29642
29793
|
const stat3 = (0, import_node_fs47.statSync)(path);
|
|
29643
29794
|
if (stat3.mtimeMs < cutoff) {
|
|
@@ -29872,7 +30023,7 @@ function languageOf2(path) {
|
|
|
29872
30023
|
|
|
29873
30024
|
// src/lib/code/runtime.ts
|
|
29874
30025
|
var import_node_fs49 = require("node:fs");
|
|
29875
|
-
var
|
|
30026
|
+
var import_node_path38 = require("node:path");
|
|
29876
30027
|
var import_web_tree_sitter2 = __toESM2(require_web_tree_sitter2());
|
|
29877
30028
|
|
|
29878
30029
|
// src/lib/code/grammars.json
|
|
@@ -29932,12 +30083,12 @@ var grammars_default = {
|
|
|
29932
30083
|
var MAX_PARSE_BYTES = 512 * 1024;
|
|
29933
30084
|
function grammarCandidates(file, pkg, env = process.env) {
|
|
29934
30085
|
return [
|
|
29935
|
-
...env.VERITY_GRAMMAR_DIR ? [(0,
|
|
29936
|
-
(0,
|
|
30086
|
+
...env.VERITY_GRAMMAR_DIR ? [(0, import_node_path38.join)(env.VERITY_GRAMMAR_DIR, file)] : [],
|
|
30087
|
+
(0, import_node_path38.join)(__dirname, "..", "data", "grammars", file),
|
|
29937
30088
|
// bundled: cli/bin/ → cli/data/grammars/
|
|
29938
|
-
(0,
|
|
30089
|
+
(0, import_node_path38.join)(__dirname, "..", "..", "..", "data", "grammars", file),
|
|
29939
30090
|
// tsx: cli/src/lib/code/ → cli/data/grammars/
|
|
29940
|
-
(0,
|
|
30091
|
+
(0, import_node_path38.join)(__dirname, "..", "..", "..", "node_modules", pkg, file)
|
|
29941
30092
|
// tsx, before a build: the pinned devDependency
|
|
29942
30093
|
];
|
|
29943
30094
|
}
|
|
@@ -30673,14 +30824,14 @@ function accessMap(input) {
|
|
|
30673
30824
|
const targetWrites = (r) => effects.some((e) => e.op === "write" && sameResource(r, e.resource));
|
|
30674
30825
|
const excluded = /* @__PURE__ */ new Set([target, ...helpers, ...fileFns.filter((f) => inside(f, target))]);
|
|
30675
30826
|
const accessors = [];
|
|
30676
|
-
const consider = (fns, p,
|
|
30827
|
+
const consider = (fns, p, sameFile2, fileState) => {
|
|
30677
30828
|
for (const f of fns) {
|
|
30678
|
-
if (
|
|
30679
|
-
if (
|
|
30680
|
-
const theirs = effectsWithHelpers(f, fns, lang, 2, fileState).filter((e) => !(
|
|
30829
|
+
if (sameFile2 && excluded.has(f)) continue;
|
|
30830
|
+
if (sameFile2 && inside(target, f)) continue;
|
|
30831
|
+
const theirs = effectsWithHelpers(f, fns, lang, 2, fileState).filter((e) => !(sameFile2 && e.via.includes(target.name)) && searchable.some((r) => sameResource(r, e.resource) && (e.op === "write" || targetWrites(r))));
|
|
30681
30832
|
if (theirs.length === 0) continue;
|
|
30682
|
-
if (
|
|
30683
|
-
accessors.push({ fn: f, effects: theirs, writes: theirs.some((e) => e.op === "write"), sameFile });
|
|
30833
|
+
if (sameFile2 && [...helperNames].includes(f.name)) continue;
|
|
30834
|
+
accessors.push({ fn: f, effects: theirs, writes: theirs.some((e) => e.op === "write"), sameFile: sameFile2 });
|
|
30684
30835
|
}
|
|
30685
30836
|
};
|
|
30686
30837
|
consider(fileFns, path, true, state);
|
|
@@ -31070,7 +31221,7 @@ var MAX_CALLERS = 30;
|
|
|
31070
31221
|
var MAX_READ_BYTES4 = 512 * 1024;
|
|
31071
31222
|
var IDENT = /^[A-Za-z_$][\w$]*$/;
|
|
31072
31223
|
var TEST_OR_TOOLING = /(^|\/)(tests?|__tests__|spec|specs|fixtures?|e2e|testdata|experiments|scripts)\/|\.(test|spec)\.[cm]?[jt]sx?$|_test\.(py|go)$|(^|\/)test_[^/]+\.py$/;
|
|
31073
|
-
var
|
|
31224
|
+
var dirname19 = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "";
|
|
31074
31225
|
function joinPath(dir, rel) {
|
|
31075
31226
|
const out2 = dir ? dir.split("/") : [];
|
|
31076
31227
|
for (const part of rel.split("/")) {
|
|
@@ -31195,7 +31346,7 @@ function parseJsonc(text2) {
|
|
|
31195
31346
|
return JSON.parse(out2.replace(/,(\s*[}\]])/g, "$1"));
|
|
31196
31347
|
}
|
|
31197
31348
|
function tsconfigFor(source2, fromPath, cache) {
|
|
31198
|
-
for (let dir =
|
|
31349
|
+
for (let dir = dirname19(fromPath); ; dir = dirname19(dir)) {
|
|
31199
31350
|
const file = dir ? `${dir}/tsconfig.json` : "tsconfig.json";
|
|
31200
31351
|
if (!cache.has(file)) {
|
|
31201
31352
|
const read2 = source2.read(file, 256 * 1024);
|
|
@@ -31219,7 +31370,7 @@ function tsconfigFor(source2, fromPath, cache) {
|
|
|
31219
31370
|
function tsCandidates(spec, fromPath, cfg) {
|
|
31220
31371
|
const bases = [];
|
|
31221
31372
|
if (spec.startsWith(".")) {
|
|
31222
|
-
const b = joinPath(
|
|
31373
|
+
const b = joinPath(dirname19(fromPath), spec);
|
|
31223
31374
|
if (b !== null) bases.push(b);
|
|
31224
31375
|
} else if (cfg) {
|
|
31225
31376
|
const root = cfg.baseUrl !== null ? joinPath(cfg.dir, cfg.baseUrl) : cfg.dir;
|
|
@@ -31251,8 +31402,8 @@ function pyCandidates(spec, fromPath) {
|
|
|
31251
31402
|
const mod = spec.slice(dots).replace(/\./g, "/");
|
|
31252
31403
|
const out2 = [];
|
|
31253
31404
|
if (dots > 0) {
|
|
31254
|
-
let dir =
|
|
31255
|
-
for (let i2 = 1; i2 < dots; i2++) dir =
|
|
31405
|
+
let dir = dirname19(fromPath);
|
|
31406
|
+
for (let i2 = 1; i2 < dots; i2++) dir = dirname19(dir);
|
|
31256
31407
|
const b = mod ? joinPath(dir, mod) : dir;
|
|
31257
31408
|
if (b !== null) out2.push(`${b}.py`, `${b}/__init__.py`);
|
|
31258
31409
|
} else {
|
|
@@ -31279,7 +31430,7 @@ function moduleFiles(source2, spec, fromPath, lang, cache = /* @__PURE__ */ new
|
|
|
31279
31430
|
const module2 = mod.ok ? /^\s*module\s+(\S+)/m.exec(mod.content)?.[1] : void 0;
|
|
31280
31431
|
if (!module2 || !(spec === module2 || spec.startsWith(`${module2}/`))) return [];
|
|
31281
31432
|
const dir = spec.slice(module2.length + 1);
|
|
31282
|
-
return source2.list(dir || void 0).filter((p) =>
|
|
31433
|
+
return source2.list(dir || void 0).filter((p) => dirname19(p) === dir && p.endsWith(".go") && !p.endsWith("_test.go"));
|
|
31283
31434
|
}
|
|
31284
31435
|
if (lang === "java") {
|
|
31285
31436
|
const suffix = `${spec.replace(/\./g, "/")}.java`;
|
|
@@ -31330,7 +31481,7 @@ function definitionOf(input) {
|
|
|
31330
31481
|
if (type?.startsWith(RESULT_OF)) {
|
|
31331
31482
|
const [fnName, idx] = type.slice(RESULT_OF.length).split("#");
|
|
31332
31483
|
type = null;
|
|
31333
|
-
const others = lang === "go" ? source2.list(
|
|
31484
|
+
const others = lang === "go" ? source2.list(dirname19(path) || void 0).filter((p) => dirname19(p) === dirname19(path) && p.endsWith(".go") && p !== path) : (() => {
|
|
31334
31485
|
const imp = importOf(content, lang, fnName, null);
|
|
31335
31486
|
return imp ? moduleFiles(source2, imp.spec, path, lang, tsCache) : [];
|
|
31336
31487
|
})();
|
|
@@ -31377,7 +31528,7 @@ function definitionOf(input) {
|
|
|
31377
31528
|
}
|
|
31378
31529
|
}
|
|
31379
31530
|
if (out2.length === 0 && lang === "go" && !receiver) {
|
|
31380
|
-
for (const f of source2.list(
|
|
31531
|
+
for (const f of source2.list(dirname19(path) || void 0).filter((p) => dirname19(p) === dirname19(path) && p.endsWith(".go") && p !== path)) {
|
|
31381
31532
|
add(functionsNamed(outlineOf2(f), name2), "package");
|
|
31382
31533
|
if (Date.now() > input.deadlineAt) break;
|
|
31383
31534
|
}
|
|
@@ -32273,8 +32424,8 @@ async function repoContext(run4) {
|
|
|
32273
32424
|
// src/lib/seed-runner.ts
|
|
32274
32425
|
var import_promises13 = require("node:fs/promises");
|
|
32275
32426
|
var import_node_fs50 = require("node:fs");
|
|
32276
|
-
var
|
|
32277
|
-
var
|
|
32427
|
+
var import_node_path39 = require("node:path");
|
|
32428
|
+
var import_yaml7 = __toESM2(require_dist());
|
|
32278
32429
|
|
|
32279
32430
|
// src/lib/seed.ts
|
|
32280
32431
|
function extractReadmeIntro(content) {
|
|
@@ -32496,7 +32647,7 @@ function normalizeTitle(title) {
|
|
|
32496
32647
|
async function localSeedTitles(memoryDir3 = MEMORY_DIR) {
|
|
32497
32648
|
const titles = /* @__PURE__ */ new Set();
|
|
32498
32649
|
for (const dir of NODE_DIRS) {
|
|
32499
|
-
const full = (0,
|
|
32650
|
+
const full = (0, import_node_path39.join)(memoryDir3, dir);
|
|
32500
32651
|
if (!(0, import_node_fs50.existsSync)(full)) continue;
|
|
32501
32652
|
let files;
|
|
32502
32653
|
try {
|
|
@@ -32508,7 +32659,7 @@ async function localSeedTitles(memoryDir3 = MEMORY_DIR) {
|
|
|
32508
32659
|
if (!file.endsWith(".md")) continue;
|
|
32509
32660
|
let content;
|
|
32510
32661
|
try {
|
|
32511
|
-
content = await (0, import_promises13.readFile)((0,
|
|
32662
|
+
content = await (0, import_promises13.readFile)((0, import_node_path39.join)(full, file), "utf-8");
|
|
32512
32663
|
} catch {
|
|
32513
32664
|
continue;
|
|
32514
32665
|
}
|
|
@@ -32585,7 +32736,7 @@ async function runSeed(opts) {
|
|
|
32585
32736
|
let standardDoc;
|
|
32586
32737
|
try {
|
|
32587
32738
|
const raw = await (0, import_promises13.readFile)(STANDARD_FILE, "utf-8");
|
|
32588
|
-
standardDoc = (0,
|
|
32739
|
+
standardDoc = (0, import_yaml7.parse)(raw);
|
|
32589
32740
|
} catch {
|
|
32590
32741
|
return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
32591
32742
|
}
|
|
@@ -32685,7 +32836,7 @@ async function runSeed(opts) {
|
|
|
32685
32836
|
}
|
|
32686
32837
|
const content = typeof res.data.content === "string" && res.data.content.length > 0 ? res.data.content : renderNodeMarkdown(c, nodeId, createdAt);
|
|
32687
32838
|
try {
|
|
32688
|
-
await (0, import_promises13.mkdir)((0,
|
|
32839
|
+
await (0, import_promises13.mkdir)((0, import_node_path39.dirname)(targetPath), { recursive: true });
|
|
32689
32840
|
await (0, import_promises13.writeFile)(targetPath, content);
|
|
32690
32841
|
if (deduplicated) existing++;
|
|
32691
32842
|
else {
|
|
@@ -32702,7 +32853,7 @@ async function runSeed(opts) {
|
|
|
32702
32853
|
|
|
32703
32854
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
32704
32855
|
var import_node_fs51 = require("node:fs");
|
|
32705
|
-
var
|
|
32856
|
+
var import_node_path40 = require("node:path");
|
|
32706
32857
|
async function memoryManifest(run4) {
|
|
32707
32858
|
const { globals } = run4;
|
|
32708
32859
|
const { serviceUrl, token } = run4;
|
|
@@ -32712,7 +32863,7 @@ async function memoryManifest(run4) {
|
|
|
32712
32863
|
let autoSeedNotice = null;
|
|
32713
32864
|
try {
|
|
32714
32865
|
await ensureMemoryDir();
|
|
32715
|
-
const seedMarker = (0,
|
|
32866
|
+
const seedMarker = (0, import_node_path40.join)(VERITY_DIR, ".seeded");
|
|
32716
32867
|
const hasStandard = (0, import_node_fs51.existsSync)(STANDARD_FILE);
|
|
32717
32868
|
const alreadyTried = (0, import_node_fs51.existsSync)(seedMarker);
|
|
32718
32869
|
if (hasStandard && !alreadyTried) {
|
|
@@ -32833,7 +32984,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
32833
32984
|
}
|
|
32834
32985
|
|
|
32835
32986
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
32836
|
-
var
|
|
32987
|
+
var import_node_path41 = require("node:path");
|
|
32837
32988
|
async function workingMemory(run4) {
|
|
32838
32989
|
const { opts } = run4;
|
|
32839
32990
|
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run4;
|
|
@@ -32845,7 +32996,7 @@ async function workingMemory(run4) {
|
|
|
32845
32996
|
const priorState = foldForMarks(memorySession.d);
|
|
32846
32997
|
incrementReport = computeIncrement(
|
|
32847
32998
|
allForReview,
|
|
32848
|
-
(p) => fileHash((0,
|
|
32999
|
+
(p) => fileHash((0, import_node_path41.join)(repoRoot(), p)),
|
|
32849
33000
|
priorState.authored_all.map((a) => ({
|
|
32850
33001
|
path: a.path,
|
|
32851
33002
|
hash_at_last_verdict: a.hash_at_last_verdict
|
|
@@ -33474,7 +33625,7 @@ async function transmit(run4) {
|
|
|
33474
33625
|
|
|
33475
33626
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
33476
33627
|
var import_node_fs54 = require("node:fs");
|
|
33477
|
-
var
|
|
33628
|
+
var import_node_path42 = require("node:path");
|
|
33478
33629
|
async function reconcile(run4) {
|
|
33479
33630
|
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory: memory2, memorySession, response, reviewable, securityFiles, turnId } = run4;
|
|
33480
33631
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
@@ -33503,7 +33654,7 @@ async function reconcile(run4) {
|
|
|
33503
33654
|
const st = foldDossier(memorySession.d);
|
|
33504
33655
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line2) => {
|
|
33505
33656
|
try {
|
|
33506
|
-
const src = (0, import_node_fs54.readFileSync)((0,
|
|
33657
|
+
const src = (0, import_node_fs54.readFileSync)((0, import_node_path42.join)(repoRoot(), file), "utf8").split("\n");
|
|
33507
33658
|
const at = src[line2 - 1];
|
|
33508
33659
|
return at === void 0 ? null : lineSha(at);
|
|
33509
33660
|
} catch {
|
|
@@ -34067,11 +34218,11 @@ async function render(run4) {
|
|
|
34067
34218
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
34068
34219
|
return sev === "critical" || sev === "high";
|
|
34069
34220
|
});
|
|
34070
|
-
const
|
|
34221
|
+
const fingerprint2 = findingsFingerprint(blocking);
|
|
34071
34222
|
const prior = readBlockState(currentCommit, {
|
|
34072
34223
|
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
34073
34224
|
});
|
|
34074
|
-
const sameProblem = isSameProblem(prior.fingerprint,
|
|
34225
|
+
const sameProblem = isSameProblem(prior.fingerprint, fingerprint2);
|
|
34075
34226
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
34076
34227
|
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
34077
34228
|
const blocks = prior.blocks + 1;
|
|
@@ -34084,7 +34235,7 @@ async function render(run4) {
|
|
|
34084
34235
|
maxIterations
|
|
34085
34236
|
});
|
|
34086
34237
|
if (decisionNow.block) {
|
|
34087
|
-
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
34238
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint: fingerprint2 });
|
|
34088
34239
|
iteration = attempts;
|
|
34089
34240
|
} else {
|
|
34090
34241
|
release = decisionNow.release;
|
|
@@ -34095,7 +34246,7 @@ async function render(run4) {
|
|
|
34095
34246
|
blocks,
|
|
34096
34247
|
reviewed_files: codeDelta.files.length,
|
|
34097
34248
|
cycle_cut: silenced,
|
|
34098
|
-
fingerprint
|
|
34249
|
+
fingerprint: fingerprint2
|
|
34099
34250
|
});
|
|
34100
34251
|
}
|
|
34101
34252
|
}
|
|
@@ -34325,7 +34476,7 @@ function registerAnalyzeCommand(program2) {
|
|
|
34325
34476
|
var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
|
|
34326
34477
|
async function runAnalyze(opts, globals) {
|
|
34327
34478
|
if (!verityConfigured()) {
|
|
34328
|
-
(0, import_node_fs55.writeSync)(2, isMachineStore((0,
|
|
34479
|
+
(0, import_node_fs55.writeSync)(2, isMachineStore((0, import_node_path43.join)(repoRoot(), VERITY_DIR)) ? "[verity] nothing to review in your home directory \u2014 run Verity inside a project.\n" : '[verity] not set up in this project \u2014 run "verity init" first.\n');
|
|
34329
34480
|
process.exit(0);
|
|
34330
34481
|
}
|
|
34331
34482
|
const run4 = createRun(opts, globals);
|
|
@@ -34349,23 +34500,23 @@ async function runAnalyze(opts, globals) {
|
|
|
34349
34500
|
var import_node_fs60 = require("node:fs");
|
|
34350
34501
|
var import_node_child_process17 = require("node:child_process");
|
|
34351
34502
|
var import_node_crypto18 = require("node:crypto");
|
|
34352
|
-
var
|
|
34503
|
+
var import_node_path48 = require("node:path");
|
|
34353
34504
|
|
|
34354
34505
|
// src/lib/prove/runner.ts
|
|
34355
34506
|
var import_node_child_process15 = require("node:child_process");
|
|
34356
34507
|
var import_node_os9 = require("node:os");
|
|
34357
34508
|
var import_node_fs58 = require("node:fs");
|
|
34358
34509
|
var import_node_os10 = require("node:os");
|
|
34359
|
-
var
|
|
34510
|
+
var import_node_path46 = require("node:path");
|
|
34360
34511
|
|
|
34361
34512
|
// src/lib/prove/scaffold.ts
|
|
34362
34513
|
var import_node_fs57 = require("node:fs");
|
|
34363
|
-
var
|
|
34514
|
+
var import_node_path45 = require("node:path");
|
|
34364
34515
|
|
|
34365
34516
|
// src/lib/prove/safe-write.ts
|
|
34366
34517
|
var import_node_fs56 = require("node:fs");
|
|
34367
34518
|
var import_node_crypto15 = require("node:crypto");
|
|
34368
|
-
var
|
|
34519
|
+
var import_node_path44 = require("node:path");
|
|
34369
34520
|
var SafeWriteError = class extends Error {
|
|
34370
34521
|
constructor(reason, path) {
|
|
34371
34522
|
super(`refusing to write ${path}: ${reason}`);
|
|
@@ -34377,7 +34528,7 @@ var SafeWriteError = class extends Error {
|
|
|
34377
34528
|
var FLAGS3 = import_node_fs56.constants;
|
|
34378
34529
|
var NOFOLLOW2 = FLAGS3.O_NOFOLLOW ?? 0;
|
|
34379
34530
|
function assertPlainRelative(rel) {
|
|
34380
|
-
if (!rel || (0,
|
|
34531
|
+
if (!rel || (0, import_node_path44.isAbsolute)(rel)) throw new SafeWriteError("bad-path", rel);
|
|
34381
34532
|
for (const part of rel.split(/[\\/]/)) {
|
|
34382
34533
|
if (part === ".." || part === "" || part === ".") throw new SafeWriteError("bad-path", rel);
|
|
34383
34534
|
}
|
|
@@ -34386,7 +34537,7 @@ function ensureDirsNoFollow(root, rel) {
|
|
|
34386
34537
|
const parts2 = rel.split(/[\\/]/);
|
|
34387
34538
|
let cur = root;
|
|
34388
34539
|
for (const part of parts2.slice(0, -1)) {
|
|
34389
|
-
cur = (0,
|
|
34540
|
+
cur = (0, import_node_path44.join)(cur, part);
|
|
34390
34541
|
let st;
|
|
34391
34542
|
try {
|
|
34392
34543
|
st = (0, import_node_fs56.lstatSync)(cur);
|
|
@@ -34397,10 +34548,10 @@ function ensureDirsNoFollow(root, rel) {
|
|
|
34397
34548
|
if (st.isSymbolicLink()) throw new SafeWriteError("symlink", cur);
|
|
34398
34549
|
if (!st.isDirectory()) throw new SafeWriteError("not-regular", cur);
|
|
34399
34550
|
}
|
|
34400
|
-
return (0,
|
|
34551
|
+
return (0, import_node_path44.join)(root, rel);
|
|
34401
34552
|
}
|
|
34402
34553
|
function insideRoot(realRoot, realPath) {
|
|
34403
|
-
return realPath === realRoot || realPath.startsWith(realRoot.endsWith(
|
|
34554
|
+
return realPath === realRoot || realPath.startsWith(realRoot.endsWith(import_node_path44.sep) ? realRoot : realRoot + import_node_path44.sep);
|
|
34404
34555
|
}
|
|
34405
34556
|
function removeQuietly(path) {
|
|
34406
34557
|
try {
|
|
@@ -34411,7 +34562,7 @@ function removeQuietly(path) {
|
|
|
34411
34562
|
function writeFileInRootNoFollow(root, rel, content) {
|
|
34412
34563
|
assertPlainRelative(rel);
|
|
34413
34564
|
const dest = ensureDirsNoFollow(root, rel);
|
|
34414
|
-
const dir = (0,
|
|
34565
|
+
const dir = (0, import_node_path44.dirname)(dest);
|
|
34415
34566
|
try {
|
|
34416
34567
|
const st = (0, import_node_fs56.lstatSync)(dest);
|
|
34417
34568
|
if (st.isSymbolicLink()) throw new SafeWriteError("symlink", dest);
|
|
@@ -34425,7 +34576,7 @@ function writeFileInRootNoFollow(root, rel, content) {
|
|
|
34425
34576
|
} catch {
|
|
34426
34577
|
throw new SafeWriteError("unwritable", root);
|
|
34427
34578
|
}
|
|
34428
|
-
const tmp = (0,
|
|
34579
|
+
const tmp = (0, import_node_path44.join)(dir, `.${(0, import_node_path44.basename)(dest)}.${(0, import_node_crypto15.randomBytes)(6).toString("hex")}.tmp`);
|
|
34429
34580
|
let fd;
|
|
34430
34581
|
try {
|
|
34431
34582
|
fd = (0, import_node_fs56.openSync)(tmp, import_node_fs56.constants.O_WRONLY | import_node_fs56.constants.O_CREAT | import_node_fs56.constants.O_EXCL | NOFOLLOW2, 420);
|
|
@@ -34469,7 +34620,7 @@ function pathHasNoLink(root, rel) {
|
|
|
34469
34620
|
}
|
|
34470
34621
|
let cur = root;
|
|
34471
34622
|
for (const part of rel.split(/[\\/]/)) {
|
|
34472
|
-
cur = cur +
|
|
34623
|
+
cur = cur + import_node_path44.sep + part;
|
|
34473
34624
|
try {
|
|
34474
34625
|
if ((0, import_node_fs56.lstatSync)(cur).isSymbolicLink()) return false;
|
|
34475
34626
|
} catch {
|
|
@@ -34781,20 +34932,20 @@ This directory is machine-local and gitignored (see ../.gitignore). Models are
|
|
|
34781
34932
|
never committed; what reaches the team is the bug report and the fix.
|
|
34782
34933
|
`;
|
|
34783
34934
|
function scaffoldProofs(root) {
|
|
34784
|
-
const proofsDir = (0,
|
|
34935
|
+
const proofsDir = (0, import_node_path45.join)(root, PROOFS_DIR);
|
|
34785
34936
|
const written = [];
|
|
34786
34937
|
const kept = [];
|
|
34787
34938
|
for (const stray of ["lakefile.lean"]) {
|
|
34788
34939
|
const rel = `${PROOFS_DIR}/${stray}`;
|
|
34789
|
-
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0,
|
|
34940
|
+
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
|
|
34790
34941
|
try {
|
|
34791
|
-
(0, import_node_fs57.rmSync)((0,
|
|
34942
|
+
(0, import_node_fs57.rmSync)((0, import_node_path45.join)(root, rel), { force: true });
|
|
34792
34943
|
} catch {
|
|
34793
34944
|
}
|
|
34794
34945
|
}
|
|
34795
34946
|
for (const f of PROOFS_TEMPLATE_FILES) {
|
|
34796
34947
|
const rel = `${PROOFS_DIR}/${f.path}`;
|
|
34797
|
-
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0,
|
|
34948
|
+
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
|
|
34798
34949
|
const current = readRegularFileInRoot(root, rel, 4 * 1024 * 1024);
|
|
34799
34950
|
if (current.ok && current.content === f.content) {
|
|
34800
34951
|
kept.push(rel);
|
|
@@ -34807,8 +34958,8 @@ function scaffoldProofs(root) {
|
|
|
34807
34958
|
[`${PROOFS_DIR}/.gitignore`, LOCAL_GITIGNORE],
|
|
34808
34959
|
[`${MODELS_DIR}/README.md`, MODELS_README]
|
|
34809
34960
|
]) {
|
|
34810
|
-
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0,
|
|
34811
|
-
if (existsNoFollow((0,
|
|
34961
|
+
if (!pathHasNoLink(root, rel)) throw new SafeWriteError("symlink", (0, import_node_path45.join)(root, rel));
|
|
34962
|
+
if (existsNoFollow((0, import_node_path45.join)(root, rel))) {
|
|
34812
34963
|
kept.push(rel);
|
|
34813
34964
|
continue;
|
|
34814
34965
|
}
|
|
@@ -35077,17 +35228,17 @@ function renderReport(r) {
|
|
|
35077
35228
|
|
|
35078
35229
|
// src/lib/prove/runner.ts
|
|
35079
35230
|
function verityLeanHome(env = process.env) {
|
|
35080
|
-
return env.VERITY_LEAN_HOME ?? (0,
|
|
35231
|
+
return env.VERITY_LEAN_HOME ?? (0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".verity", "lean");
|
|
35081
35232
|
}
|
|
35082
35233
|
function findToolchain(env = process.env) {
|
|
35083
|
-
const managedBin = (0,
|
|
35084
|
-
const elanBin = (0,
|
|
35085
|
-
const elanHas = (0, import_node_fs58.existsSync)((0,
|
|
35086
|
-
const userPath = (env.PATH ?? "").split(
|
|
35087
|
-
const path = [managedBin, ...elanHas ? [elanBin] : [], ...userPath].join(
|
|
35234
|
+
const managedBin = (0, import_node_path46.join)(verityLeanHome(env), LEAN_TOOLCHAIN.replace(/^leanprover\/lean4:/, ""), "bin");
|
|
35235
|
+
const elanBin = (0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "bin");
|
|
35236
|
+
const elanHas = (0, import_node_fs58.existsSync)((0, import_node_path46.join)(env.HOME ?? (0, import_node_os10.homedir)(), ".elan", "toolchains", LEAN_TOOLCHAIN.replace("/", "--").replace(":", "---")));
|
|
35237
|
+
const userPath = (env.PATH ?? "").split(import_node_path46.delimiter).filter((d) => d && (elanHas || (0, import_node_path46.resolve)(d) !== (0, import_node_path46.resolve)(elanBin)));
|
|
35238
|
+
const path = [managedBin, ...elanHas ? [elanBin] : [], ...userPath].join(import_node_path46.delimiter);
|
|
35088
35239
|
const merged = { ...env, PATH: path, ELAN_TOOLCHAIN: env.ELAN_TOOLCHAIN ?? LEAN_TOOLCHAIN };
|
|
35089
35240
|
const exe = process.platform === "win32" ? "lake.exe" : "lake";
|
|
35090
|
-
const candidates2 = [(0,
|
|
35241
|
+
const candidates2 = [(0, import_node_path46.join)(managedBin, exe), ...elanHas ? [(0, import_node_path46.join)(elanBin, exe)] : [], exe];
|
|
35091
35242
|
for (const candidate of candidates2) {
|
|
35092
35243
|
try {
|
|
35093
35244
|
(0, import_node_child_process15.execFileSync)(candidate, ["--version"], { env: merged, stdio: ["ignore", "pipe", "pipe"], timeout: 1e4 });
|
|
@@ -35166,7 +35317,7 @@ function proofsWorkspaceProblem(root) {
|
|
|
35166
35317
|
for (const rel of [".verity", PROOFS_DIR, `${PROOFS_DIR}/.lake`]) {
|
|
35167
35318
|
let st;
|
|
35168
35319
|
try {
|
|
35169
|
-
st = (0, import_node_fs58.lstatSync)((0,
|
|
35320
|
+
st = (0, import_node_fs58.lstatSync)((0, import_node_path46.join)(root, rel));
|
|
35170
35321
|
} catch {
|
|
35171
35322
|
break;
|
|
35172
35323
|
}
|
|
@@ -35186,44 +35337,44 @@ function refused(problem) {
|
|
|
35186
35337
|
function buildLibrary(tc, root, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS) {
|
|
35187
35338
|
const problem = proofsWorkspaceProblem(root);
|
|
35188
35339
|
if (problem) return refused(problem);
|
|
35189
|
-
return run2(tc, (0,
|
|
35340
|
+
return run2(tc, (0, import_node_path46.join)(root, PROOFS_DIR), ["build"], timeoutMs);
|
|
35190
35341
|
}
|
|
35191
35342
|
async function buildLibraryAsync(tc, root, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS) {
|
|
35192
35343
|
const problem = proofsWorkspaceProblem(root);
|
|
35193
35344
|
if (problem) return refused(problem);
|
|
35194
|
-
return runAsync(tc, (0,
|
|
35345
|
+
return runAsync(tc, (0, import_node_path46.join)(root, PROOFS_DIR), ["build"], timeoutMs);
|
|
35195
35346
|
}
|
|
35196
35347
|
function isModelInRoot(modelsAbs, rel) {
|
|
35197
|
-
if (!rel.endsWith(".lean") || (0,
|
|
35348
|
+
if (!rel.endsWith(".lean") || (0, import_node_path46.isAbsolute)(rel) || rel.startsWith("..")) return false;
|
|
35198
35349
|
return statRegularInRoot(modelsAbs, rel).ok;
|
|
35199
35350
|
}
|
|
35200
35351
|
function resolveModelFile(root, arg) {
|
|
35201
|
-
const modelsAbs = (0,
|
|
35202
|
-
const asPath = (0,
|
|
35352
|
+
const modelsAbs = (0, import_node_path46.join)(root, MODELS_DIR);
|
|
35353
|
+
const asPath = (0, import_node_path46.relative)(modelsAbs, (0, import_node_path46.isAbsolute)(arg) ? arg : (0, import_node_path46.resolve)(root, arg));
|
|
35203
35354
|
const candidates2 = arg.includes("/") || arg.includes("\\") ? [asPath] : [arg, `${arg}.lean`];
|
|
35204
35355
|
for (const rel of candidates2) {
|
|
35205
|
-
if (isModelInRoot(modelsAbs, rel)) return (0,
|
|
35356
|
+
if (isModelInRoot(modelsAbs, rel)) return (0, import_node_path46.join)(modelsAbs, rel);
|
|
35206
35357
|
}
|
|
35207
35358
|
return null;
|
|
35208
35359
|
}
|
|
35209
35360
|
function listModelFiles(root) {
|
|
35210
|
-
const modelsAbs = (0,
|
|
35361
|
+
const modelsAbs = (0, import_node_path46.join)(root, MODELS_DIR);
|
|
35211
35362
|
if (!(0, import_node_fs58.existsSync)(modelsAbs)) return [];
|
|
35212
|
-
return (0, import_node_fs58.readdirSync)(modelsAbs).filter((f) => isModelInRoot(modelsAbs, f)).sort().map((f) => (0,
|
|
35363
|
+
return (0, import_node_fs58.readdirSync)(modelsAbs).filter((f) => isModelInRoot(modelsAbs, f)).sort().map((f) => (0, import_node_path46.join)(modelsAbs, f));
|
|
35213
35364
|
}
|
|
35214
35365
|
function trimDiagnostics(text2) {
|
|
35215
35366
|
return text2.split("\n").filter((l) => !l.startsWith("trace:")).join("\n").trim().slice(0, 4e3);
|
|
35216
35367
|
}
|
|
35217
35368
|
function checkModel(tc, root, modelFile, opts = {}) {
|
|
35218
|
-
const proofsAbs = (0,
|
|
35219
|
-
const rel = (0,
|
|
35369
|
+
const proofsAbs = (0, import_node_path46.join)(root, PROOFS_DIR);
|
|
35370
|
+
const rel = (0, import_node_path46.relative)(proofsAbs, modelFile);
|
|
35220
35371
|
const problem = proofsWorkspaceProblem(root);
|
|
35221
35372
|
const res = problem ? refused(problem) : run2(tc, proofsAbs, ["env", "lean", rel], opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
|
|
35222
35373
|
return toReport(res, root, modelFile, opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
|
|
35223
35374
|
}
|
|
35224
35375
|
async function checkModelAsync(tc, root, modelFile, opts = {}) {
|
|
35225
|
-
const proofsAbs = (0,
|
|
35226
|
-
const rel = (0,
|
|
35376
|
+
const proofsAbs = (0, import_node_path46.join)(root, PROOFS_DIR);
|
|
35377
|
+
const rel = (0, import_node_path46.relative)(proofsAbs, modelFile);
|
|
35227
35378
|
const problem = proofsWorkspaceProblem(root);
|
|
35228
35379
|
const res = problem ? refused(problem) : await runAsync(tc, proofsAbs, ["env", "lean", rel], opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
|
|
35229
35380
|
return toReport(res, root, modelFile, opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS);
|
|
@@ -35232,10 +35383,10 @@ function toReport(res, root, modelFile, timeoutMs) {
|
|
|
35232
35383
|
const parsed = parseVerityLines(res.stdout);
|
|
35233
35384
|
const combined = `${res.stdout}
|
|
35234
35385
|
${res.stderr}`;
|
|
35235
|
-
const name2 = parsed.header?.model ?? (0,
|
|
35386
|
+
const name2 = parsed.header?.model ?? (0, import_node_path46.basename)(modelFile, ".lean");
|
|
35236
35387
|
const report = {
|
|
35237
35388
|
model: name2,
|
|
35238
|
-
file: (0,
|
|
35389
|
+
file: (0, import_node_path46.relative)(root, modelFile),
|
|
35239
35390
|
ok: res.ok,
|
|
35240
35391
|
header: parsed.header,
|
|
35241
35392
|
properties: parsed.properties,
|
|
@@ -35281,7 +35432,7 @@ var import_node_fs59 = require("node:fs");
|
|
|
35281
35432
|
var import_node_crypto17 = require("node:crypto");
|
|
35282
35433
|
var import_node_child_process16 = require("node:child_process");
|
|
35283
35434
|
var import_node_os11 = require("node:os");
|
|
35284
|
-
var
|
|
35435
|
+
var import_node_path47 = require("node:path");
|
|
35285
35436
|
var import_promises15 = require("node:stream/promises");
|
|
35286
35437
|
var import_node_stream = require("node:stream");
|
|
35287
35438
|
var RELEASES = "https://releases.lean-lang.org/lean4";
|
|
@@ -35295,7 +35446,7 @@ function freeBytes(dir) {
|
|
|
35295
35446
|
const st = (0, import_node_fs59.statfsSync)(d);
|
|
35296
35447
|
return Number(st.bavail) * Number(st.bsize);
|
|
35297
35448
|
} catch {
|
|
35298
|
-
const up = (0,
|
|
35449
|
+
const up = (0, import_node_path47.join)(d, "..");
|
|
35299
35450
|
if (up === d) return null;
|
|
35300
35451
|
d = up;
|
|
35301
35452
|
}
|
|
@@ -35337,13 +35488,13 @@ function leanAssetName(version = pinnedVersion()) {
|
|
|
35337
35488
|
return suffix ? `lean-${v}-${suffix}.zip` : null;
|
|
35338
35489
|
}
|
|
35339
35490
|
function pinnedVersionDir(env = process.env) {
|
|
35340
|
-
return (0,
|
|
35491
|
+
return (0, import_node_path47.join)(verityLeanHome(env), pinnedVersion());
|
|
35341
35492
|
}
|
|
35342
35493
|
function lakeBinary() {
|
|
35343
35494
|
return (0, import_node_os11.platform)() === "win32" ? "lake.exe" : "lake";
|
|
35344
35495
|
}
|
|
35345
35496
|
function managedInstalled(env = process.env) {
|
|
35346
|
-
return (0, import_node_fs59.existsSync)((0,
|
|
35497
|
+
return (0, import_node_fs59.existsSync)((0, import_node_path47.join)(pinnedVersionDir(env), "bin", lakeBinary()));
|
|
35347
35498
|
}
|
|
35348
35499
|
function unzip(zip, dest) {
|
|
35349
35500
|
const attempts = (0, import_node_os11.platform)() === "linux" ? [["unzip", ["-q", zip, "-d", dest]], ["python3", ["-m", "zipfile", "-e", zip, dest]], ["tar", ["-xf", zip, "-C", dest]]] : [["tar", ["-xf", zip, "-C", dest]], ["unzip", ["-q", zip, "-d", dest]], ["python3", ["-m", "zipfile", "-e", zip, dest]]];
|
|
@@ -35356,7 +35507,7 @@ function unzip(zip, dest) {
|
|
|
35356
35507
|
} catch (err2) {
|
|
35357
35508
|
errors.push(`${cmd}: ${String(err2.message).split("\n")[0].slice(0, 120)}`);
|
|
35358
35509
|
try {
|
|
35359
|
-
for (const e of (0, import_node_fs59.readdirSync)(dest)) (0, import_node_fs59.rmSync)((0,
|
|
35510
|
+
for (const e of (0, import_node_fs59.readdirSync)(dest)) (0, import_node_fs59.rmSync)((0, import_node_path47.join)(dest, e), { recursive: true, force: true });
|
|
35360
35511
|
} catch {
|
|
35361
35512
|
}
|
|
35362
35513
|
}
|
|
@@ -35432,10 +35583,10 @@ async function installLean(opts = {}) {
|
|
|
35432
35583
|
const gb = (n2) => (n2 / 1024 ** 3).toFixed(1);
|
|
35433
35584
|
return { ok: false, error: `not enough disk space for Lean: ${gb(free)} GB free where ${home} lives, needs about ${gb(needed)} GB` };
|
|
35434
35585
|
}
|
|
35435
|
-
const tmp = (0,
|
|
35586
|
+
const tmp = (0, import_node_path47.join)(home, ".tmp");
|
|
35436
35587
|
(0, import_node_fs59.rmSync)(tmp, { recursive: true, force: true });
|
|
35437
35588
|
(0, import_node_fs59.mkdirSync)(tmp, { recursive: true });
|
|
35438
|
-
const zip = (0,
|
|
35589
|
+
const zip = (0, import_node_path47.join)(tmp, asset);
|
|
35439
35590
|
say(`downloading ${url}`);
|
|
35440
35591
|
let lastPct = -1;
|
|
35441
35592
|
try {
|
|
@@ -35458,7 +35609,7 @@ async function installLean(opts = {}) {
|
|
|
35458
35609
|
return { ok: false, error: verified.error };
|
|
35459
35610
|
}
|
|
35460
35611
|
say("unpacking (this takes a minute)");
|
|
35461
|
-
const unpack = (0,
|
|
35612
|
+
const unpack = (0, import_node_path47.join)(tmp, "unpack");
|
|
35462
35613
|
(0, import_node_fs59.mkdirSync)(unpack);
|
|
35463
35614
|
const unpacked = unzip(zip, unpack);
|
|
35464
35615
|
if (!unpacked.ok) {
|
|
@@ -35466,8 +35617,8 @@ async function installLean(opts = {}) {
|
|
|
35466
35617
|
return { ok: false, error: `unpack failed: ${unpacked.error}` };
|
|
35467
35618
|
}
|
|
35468
35619
|
const entries = (0, import_node_fs59.readdirSync)(unpack);
|
|
35469
|
-
const top = entries.length === 1 && (0, import_node_fs59.statSync)((0,
|
|
35470
|
-
if (!(0, import_node_fs59.existsSync)((0,
|
|
35620
|
+
const top = entries.length === 1 && (0, import_node_fs59.statSync)((0, import_node_path47.join)(unpack, entries[0])).isDirectory() ? (0, import_node_path47.join)(unpack, entries[0]) : unpack;
|
|
35621
|
+
if (!(0, import_node_fs59.existsSync)((0, import_node_path47.join)(top, "bin", lakeBinary()))) {
|
|
35471
35622
|
(0, import_node_fs59.rmSync)(tmp, { recursive: true, force: true });
|
|
35472
35623
|
return { ok: false, error: "the archive did not contain bin/lake" };
|
|
35473
35624
|
}
|
|
@@ -35551,7 +35702,7 @@ function startBackgroundInstall(opts) {
|
|
|
35551
35702
|
const home = verityLeanHome(env);
|
|
35552
35703
|
try {
|
|
35553
35704
|
(0, import_node_fs60.mkdirSync)(home, { recursive: true });
|
|
35554
|
-
const log = (0, import_node_fs60.openSync)((0,
|
|
35705
|
+
const log = (0, import_node_fs60.openSync)((0, import_node_path48.join)(home, LOG_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_APPEND | import_node_fs60.constants.O_CREAT | (import_node_fs60.constants.O_NOFOLLOW ?? 0), 384);
|
|
35555
35706
|
try {
|
|
35556
35707
|
const child = (opts.run ?? import_node_child_process17.spawn)(process.execPath, [opts.cli, "lean", "install", "--yes", "--background"], {
|
|
35557
35708
|
detached: true,
|
|
@@ -35583,7 +35734,7 @@ function lockToken(env) {
|
|
|
35583
35734
|
function acquireInstallLock(env = process.env, now = Date.now()) {
|
|
35584
35735
|
const home = verityLeanHome(env);
|
|
35585
35736
|
(0, import_node_fs60.mkdirSync)(home, { recursive: true });
|
|
35586
|
-
const lock = (0,
|
|
35737
|
+
const lock = (0, import_node_path48.join)(home, LOCK_FILE);
|
|
35587
35738
|
if ((0, import_node_fs60.existsSync)(lock) && !installRunning(env, now)) (0, import_node_fs60.rmSync)(lock, { force: true });
|
|
35588
35739
|
const token = (0, import_node_crypto18.randomBytes)(16).toString("hex");
|
|
35589
35740
|
try {
|
|
@@ -35599,7 +35750,7 @@ function acquireInstallLock(env = process.env, now = Date.now()) {
|
|
|
35599
35750
|
function refreshInstallLock(env = process.env, now = Date.now()) {
|
|
35600
35751
|
if (!heldToken || lockToken(env) !== heldToken) return;
|
|
35601
35752
|
try {
|
|
35602
|
-
const fd = (0, import_node_fs60.openSync)((0,
|
|
35753
|
+
const fd = (0, import_node_fs60.openSync)((0, import_node_path48.join)(verityLeanHome(env), LOCK_FILE), import_node_fs60.constants.O_WRONLY | import_node_fs60.constants.O_TRUNC | (import_node_fs60.constants.O_NOFOLLOW ?? 0));
|
|
35603
35754
|
try {
|
|
35604
35755
|
(0, import_node_fs60.writeFileSync)(fd, JSON.stringify({ pid: process.pid, at: now, token: heldToken }));
|
|
35605
35756
|
} finally {
|
|
@@ -35609,7 +35760,7 @@ function refreshInstallLock(env = process.env, now = Date.now()) {
|
|
|
35609
35760
|
}
|
|
35610
35761
|
}
|
|
35611
35762
|
function releaseInstallLock(env = process.env) {
|
|
35612
|
-
if (heldToken && lockToken(env) === heldToken) (0, import_node_fs60.rmSync)((0,
|
|
35763
|
+
if (heldToken && lockToken(env) === heldToken) (0, import_node_fs60.rmSync)((0, import_node_path48.join)(verityLeanHome(env), LOCK_FILE), { force: true });
|
|
35613
35764
|
heldToken = null;
|
|
35614
35765
|
}
|
|
35615
35766
|
function writeInstallState(state, env = process.env) {
|
|
@@ -35662,14 +35813,14 @@ function resumeInterruptedInstall(opts) {
|
|
|
35662
35813
|
|
|
35663
35814
|
// src/commands/baseline.ts
|
|
35664
35815
|
var import_node_fs63 = require("node:fs");
|
|
35665
|
-
var
|
|
35816
|
+
var import_node_path51 = require("node:path");
|
|
35666
35817
|
|
|
35667
35818
|
// src/lib/skew-notice.ts
|
|
35668
35819
|
var import_node_fs61 = require("node:fs");
|
|
35669
|
-
var
|
|
35820
|
+
var import_node_path49 = require("node:path");
|
|
35670
35821
|
var MARKER = ".skew-notice.json";
|
|
35671
35822
|
function markerPath2() {
|
|
35672
|
-
return (0,
|
|
35823
|
+
return (0, import_node_path49.join)(verityHome(), MARKER);
|
|
35673
35824
|
}
|
|
35674
35825
|
function today(now) {
|
|
35675
35826
|
const y = now.getFullYear();
|
|
@@ -35695,7 +35846,7 @@ function recordSkewNotice(skew, now = /* @__PURE__ */ new Date()) {
|
|
|
35695
35846
|
|
|
35696
35847
|
// src/lib/project-skills.ts
|
|
35697
35848
|
var import_node_fs62 = require("node:fs");
|
|
35698
|
-
var
|
|
35849
|
+
var import_node_path50 = require("node:path");
|
|
35699
35850
|
var PROJECT_SKILL_NAMES = [
|
|
35700
35851
|
"verity-setup",
|
|
35701
35852
|
"verity-analyze",
|
|
@@ -35722,13 +35873,13 @@ var ALL = [...PROJECT_SKILL_NAMES, ...LEGACY_SKILL_NAMES];
|
|
|
35722
35873
|
function staleProjectSkills() {
|
|
35723
35874
|
const root = projectPath(".claude/skills");
|
|
35724
35875
|
if (!(0, import_node_fs62.existsSync)(root)) return [];
|
|
35725
|
-
return ALL.filter((name2) => (0, import_node_fs62.existsSync)((0,
|
|
35876
|
+
return ALL.filter((name2) => (0, import_node_fs62.existsSync)((0, import_node_path50.join)(root, name2)));
|
|
35726
35877
|
}
|
|
35727
35878
|
function removeProjectSkills() {
|
|
35728
35879
|
const root = projectPath(".claude/skills");
|
|
35729
35880
|
const removed = [];
|
|
35730
35881
|
for (const name2 of staleProjectSkills()) {
|
|
35731
|
-
(0, import_node_fs62.rmSync)((0,
|
|
35882
|
+
(0, import_node_fs62.rmSync)((0, import_node_path50.join)(root, name2), { recursive: true, force: true });
|
|
35732
35883
|
removed.push(name2);
|
|
35733
35884
|
}
|
|
35734
35885
|
return removed;
|
|
@@ -35763,7 +35914,7 @@ function registerBaselineCommands(program2) {
|
|
|
35763
35914
|
const loginWarning = "Until they have, do not send them to run `verity login` in a terminal: that runs the global one, which can sign this machine in to a different server.";
|
|
35764
35915
|
const skewNote = !skew ? null : skewLoud ? `Before anything else, including /verity:init's questions, tell the user their global \`verity\` is ${skew.global}, the hooks run ${skew.hooks}, and \`${skew.remedy}\` fixes it. ${loginWarning}` : `The user's global \`verity\` is ${skew.global} and the hooks run ${skew.hooks}; \`${skew.remedy}\` fixes it. They were already told today, so do not repeat it unprompted \u2014 mention it only where it matters, such as before suggesting a \`verity\` command for their terminal. ${loginWarning}`;
|
|
35765
35916
|
if (!verityConfigured()) {
|
|
35766
|
-
if (isMachineStore((0,
|
|
35917
|
+
if (isMachineStore((0, import_node_path51.join)(repoRoot(), VERITY_DIR))) process.exit(0);
|
|
35767
35918
|
const how = isPluginInvocation() ? "Tell the user to run /verity:init \u2014 they type it; you cannot invoke it." : "Tell the user to run `verity init` in their terminal.";
|
|
35768
35919
|
const lines = [
|
|
35769
35920
|
`Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
|
|
@@ -35907,30 +36058,30 @@ function hookSource(value) {
|
|
|
35907
36058
|
|
|
35908
36059
|
// src/commands/review.ts
|
|
35909
36060
|
var import_node_fs64 = require("node:fs");
|
|
35910
|
-
var
|
|
36061
|
+
var import_node_path52 = require("node:path");
|
|
35911
36062
|
function containReviewPaths(paths, base) {
|
|
35912
36063
|
const kept = [];
|
|
35913
36064
|
const refused2 = [];
|
|
35914
36065
|
for (const raw of paths) {
|
|
35915
|
-
const candidate = (0,
|
|
36066
|
+
const candidate = (0, import_node_path52.isAbsolute)(raw) ? relativeForm(base, raw) : raw;
|
|
35916
36067
|
if (candidate === "" || resolveInside(base, candidate) === null) refused2.push(raw);
|
|
35917
36068
|
else kept.push(candidate);
|
|
35918
36069
|
}
|
|
35919
36070
|
return { kept, refused: refused2 };
|
|
35920
36071
|
}
|
|
35921
36072
|
function relativeForm(base, abs) {
|
|
35922
|
-
const lexical = (0,
|
|
35923
|
-
if (!(lexical === ".." || lexical.startsWith(`..${
|
|
36073
|
+
const lexical = (0, import_node_path52.relative)(base, abs);
|
|
36074
|
+
if (!(lexical === ".." || lexical.startsWith(`..${import_node_path52.sep}`) || (0, import_node_path52.isAbsolute)(lexical))) return lexical;
|
|
35924
36075
|
try {
|
|
35925
|
-
let dir = (0,
|
|
35926
|
-
let rest = (0,
|
|
36076
|
+
let dir = (0, import_node_path52.dirname)(abs);
|
|
36077
|
+
let rest = (0, import_node_path52.basename)(abs);
|
|
35927
36078
|
while (!(0, import_node_fs64.existsSync)(dir)) {
|
|
35928
|
-
const up = (0,
|
|
36079
|
+
const up = (0, import_node_path52.dirname)(dir);
|
|
35929
36080
|
if (up === dir) return lexical;
|
|
35930
|
-
rest = (0,
|
|
36081
|
+
rest = (0, import_node_path52.join)((0, import_node_path52.basename)(dir), rest);
|
|
35931
36082
|
dir = up;
|
|
35932
36083
|
}
|
|
35933
|
-
return (0,
|
|
36084
|
+
return (0, import_node_path52.relative)((0, import_node_fs64.realpathSync)(base), (0, import_node_path52.join)((0, import_node_fs64.realpathSync)(dir), rest));
|
|
35934
36085
|
} catch {
|
|
35935
36086
|
return lexical;
|
|
35936
36087
|
}
|
|
@@ -36036,11 +36187,11 @@ async function runReview(opts, globals) {
|
|
|
36036
36187
|
|
|
36037
36188
|
// src/commands/guard.ts
|
|
36038
36189
|
var import_node_fs67 = require("node:fs");
|
|
36039
|
-
var
|
|
36190
|
+
var import_node_path56 = require("node:path");
|
|
36040
36191
|
|
|
36041
36192
|
// src/lib/vendored.ts
|
|
36042
36193
|
var import_node_fs65 = require("node:fs");
|
|
36043
|
-
var
|
|
36194
|
+
var import_node_path53 = require("node:path");
|
|
36044
36195
|
var INSTALLED_SKILL = /^\.claude\/skills\/(verity-[a-z0-9-]+)\/(.+)$/;
|
|
36045
36196
|
function dataDirOrNull() {
|
|
36046
36197
|
try {
|
|
@@ -36055,7 +36206,7 @@ function shippedSkillFile(path, dataDir = dataDirOrNull()) {
|
|
|
36055
36206
|
const [, skill, rel] = m;
|
|
36056
36207
|
if (rel.split("/").some((seg) => seg === ".." || seg === ".")) return null;
|
|
36057
36208
|
try {
|
|
36058
|
-
return (0, import_node_fs65.readFileSync)((0,
|
|
36209
|
+
return (0, import_node_fs65.readFileSync)((0, import_node_path53.join)(dataDir, "skills", skill, rel), "utf8");
|
|
36059
36210
|
} catch {
|
|
36060
36211
|
return null;
|
|
36061
36212
|
}
|
|
@@ -36166,8 +36317,8 @@ async function runAgentReview(options) {
|
|
|
36166
36317
|
|
|
36167
36318
|
// src/lib/effort.ts
|
|
36168
36319
|
var import_node_fs66 = require("node:fs");
|
|
36169
|
-
var
|
|
36170
|
-
var
|
|
36320
|
+
var import_node_path54 = require("node:path");
|
|
36321
|
+
var import_yaml8 = __toESM2(require_dist());
|
|
36171
36322
|
var EFFORT_PROFILES = {
|
|
36172
36323
|
default: { pushDeadlineMs: 18e4, formal: { tier: "fast", maxRounds: 2, minScore: 0.7, costCapUsd: 1, maxTargets: 1, relaxedGates: false, dailyModels: 5, dailyCostUsd: 5 } },
|
|
36173
36324
|
high: { pushDeadlineMs: 22e4, formal: { tier: "deep", maxRounds: 2, minScore: 0.6, costCapUsd: 2, maxTargets: 2, relaxedGates: false, dailyModels: 15, dailyCostUsd: 15 } },
|
|
@@ -36179,7 +36330,7 @@ function asEffort(value) {
|
|
|
36179
36330
|
function resolveEffort(root, env = process.env) {
|
|
36180
36331
|
if (env.VERITY_EFFORT) return asEffort(env.VERITY_EFFORT);
|
|
36181
36332
|
try {
|
|
36182
|
-
const standard = (0,
|
|
36333
|
+
const standard = (0, import_yaml8.parse)((0, import_node_fs66.readFileSync)((0, import_node_path54.join)(root, STANDARD_FILE), "utf8"));
|
|
36183
36334
|
return asEffort(standard?.process_constraints?.git_moment_effort);
|
|
36184
36335
|
} catch {
|
|
36185
36336
|
return "default";
|
|
@@ -36190,7 +36341,7 @@ function effortProfile(root, env = process.env) {
|
|
|
36190
36341
|
}
|
|
36191
36342
|
|
|
36192
36343
|
// src/lib/prove/auto.ts
|
|
36193
|
-
var
|
|
36344
|
+
var import_node_path55 = require("node:path");
|
|
36194
36345
|
var import_node_crypto21 = require("node:crypto");
|
|
36195
36346
|
|
|
36196
36347
|
// src/lib/prove/grounding.ts
|
|
@@ -37885,8 +38036,8 @@ function readVerified(root, now = Date.now()) {
|
|
|
37885
38036
|
return [];
|
|
37886
38037
|
}
|
|
37887
38038
|
}
|
|
37888
|
-
function findVerified(root, file, symbol,
|
|
37889
|
-
return readVerified(root, now).find((r) => r.file === file && r.symbol === symbol && r.fingerprint ===
|
|
38039
|
+
function findVerified(root, file, symbol, fingerprint2, now = Date.now()) {
|
|
38040
|
+
return readVerified(root, now).find((r) => r.file === file && r.symbol === symbol && r.fingerprint === fingerprint2) ?? null;
|
|
37890
38041
|
}
|
|
37891
38042
|
function recordVerified(root, rec, now = Date.now()) {
|
|
37892
38043
|
const others = readVerified(root, now).filter((r) => !(r.file === rec.file && r.symbol === rec.symbol));
|
|
@@ -38344,7 +38495,7 @@ async function runFormalPhase(input) {
|
|
|
38344
38495
|
modelName = gen.name;
|
|
38345
38496
|
const rel = `${AUTO_MODELS_DIR}/${gen.name}.lean`;
|
|
38346
38497
|
writeFileInRootNoFollow(root, rel, gen.source);
|
|
38347
|
-
const abs = resolveModelFile(root, rel) ?? (0,
|
|
38498
|
+
const abs = resolveModelFile(root, rel) ?? (0, import_node_path55.join)(root, rel);
|
|
38348
38499
|
progress.model = { name: gen.name, file: rel, rounds: round };
|
|
38349
38500
|
progress.step = "check";
|
|
38350
38501
|
report = await deps.check(root, abs);
|
|
@@ -38493,7 +38644,7 @@ async function runFormalPhase(input) {
|
|
|
38493
38644
|
return done({
|
|
38494
38645
|
status: "checked",
|
|
38495
38646
|
target,
|
|
38496
|
-
model: { name: modelName, file: (0,
|
|
38647
|
+
model: { name: modelName, file: (0, import_node_path55.relative)(root, (0, import_node_path55.join)(root, modelFile)), rounds },
|
|
38497
38648
|
properties: report.properties,
|
|
38498
38649
|
findings
|
|
38499
38650
|
});
|
|
@@ -38570,7 +38721,7 @@ async function generateAndCheck(root, req, opts) {
|
|
|
38570
38721
|
}
|
|
38571
38722
|
const rel = `${dir}/${gen.name}.lean`;
|
|
38572
38723
|
writeFileInRootNoFollow(root, rel, gen.source);
|
|
38573
|
-
const abs = resolveModelFile(root, rel) ?? (0,
|
|
38724
|
+
const abs = resolveModelFile(root, rel) ?? (0, import_node_path55.join)(root, rel);
|
|
38574
38725
|
const report = await opts.deps.check(root, abs);
|
|
38575
38726
|
const abstracts = report.header?.abstracts ?? [];
|
|
38576
38727
|
const stray = abstracts.find((a) => !headerFiles.has(a.file)) ?? (abstracts[0] && abstracts[0].file !== req.file ? abstracts[0] : void 0);
|
|
@@ -38960,7 +39111,7 @@ var WRITERS_BUDGET_MS = 1500;
|
|
|
38960
39111
|
var EXCERPT_MAX_LINES2 = 30;
|
|
38961
39112
|
var VENDORED_READ_MAX_BYTES = 256 * 1024;
|
|
38962
39113
|
var GUARD_BLOCK_CAP = 2;
|
|
38963
|
-
var GUARD_ITER_FILE = (0,
|
|
39114
|
+
var GUARD_ITER_FILE = (0, import_node_path56.join)(VERITY_DIR, ".guard-iteration");
|
|
38964
39115
|
function readPreToolUseStdin() {
|
|
38965
39116
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
38966
39117
|
return new Promise((resolve9) => {
|
|
@@ -39130,11 +39281,11 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
|
|
|
39130
39281
|
}
|
|
39131
39282
|
};
|
|
39132
39283
|
if (coverageTelemetry) requestBody2.coverage_telemetry = coverageTelemetry;
|
|
39133
|
-
const
|
|
39134
|
-
if (
|
|
39284
|
+
const docs = discoverGuardDocs(files);
|
|
39285
|
+
if (docs.length > 0 || statedIntent) {
|
|
39135
39286
|
const intentContext = {};
|
|
39136
39287
|
if (statedIntent) intentContext.user_prompt = statedIntent;
|
|
39137
|
-
if (
|
|
39288
|
+
if (docs.length > 0) intentContext.plans = docs;
|
|
39138
39289
|
requestBody2.intent_context = intentContext;
|
|
39139
39290
|
}
|
|
39140
39291
|
return requestBody2;
|
|
@@ -39819,14 +39970,99 @@ function registerWaiveCommand(program2) {
|
|
|
39819
39970
|
}
|
|
39820
39971
|
|
|
39821
39972
|
// src/commands/init/index.ts
|
|
39822
|
-
var
|
|
39973
|
+
var import_node_fs77 = require("node:fs");
|
|
39823
39974
|
var import_promises19 = require("node:fs/promises");
|
|
39824
|
-
var
|
|
39975
|
+
var import_node_path65 = require("node:path");
|
|
39825
39976
|
|
|
39826
|
-
// src/lib/init-
|
|
39977
|
+
// src/lib/init-changes.ts
|
|
39827
39978
|
var import_node_crypto23 = require("node:crypto");
|
|
39828
39979
|
var import_node_fs69 = require("node:fs");
|
|
39829
|
-
var
|
|
39980
|
+
var import_node_os12 = require("node:os");
|
|
39981
|
+
var import_node_path57 = require("node:path");
|
|
39982
|
+
var REPO_FILES = [
|
|
39983
|
+
".gitignore",
|
|
39984
|
+
"CLAUDE.md",
|
|
39985
|
+
".claude/settings.json",
|
|
39986
|
+
".claude/settings.local.json",
|
|
39987
|
+
STANDARD_FILE,
|
|
39988
|
+
VERITY_MD_FILE,
|
|
39989
|
+
CODACY_CONFIG_FILE,
|
|
39990
|
+
".codacy/tools-configs/eslint.config.mjs"
|
|
39991
|
+
];
|
|
39992
|
+
var SKILLS_DIR = ".claude/skills";
|
|
39993
|
+
var MAX_HASH_BYTES = 2 * 1024 * 1024;
|
|
39994
|
+
var MAX_TREE_ENTRIES = 2e3;
|
|
39995
|
+
function userSettingsDir(env) {
|
|
39996
|
+
return env.CLAUDE_CONFIG_DIR || (0, import_node_path57.join)(env.HOME || (0, import_node_os12.homedir)(), ".claude");
|
|
39997
|
+
}
|
|
39998
|
+
function fingerprint(root, rel) {
|
|
39999
|
+
const opened = openRegularInRoot(root, rel);
|
|
40000
|
+
if (!opened.ok) {
|
|
40001
|
+
if (opened.reason === "missing") return null;
|
|
40002
|
+
if (opened.reason === "symlink") {
|
|
40003
|
+
try {
|
|
40004
|
+
return `link:${(0, import_node_fs69.readlinkSync)((0, import_node_path57.join)(root, rel))}`;
|
|
40005
|
+
} catch {
|
|
40006
|
+
return "link";
|
|
40007
|
+
}
|
|
40008
|
+
}
|
|
40009
|
+
return `refused:${opened.reason}`;
|
|
40010
|
+
}
|
|
40011
|
+
if (opened.size > MAX_HASH_BYTES) {
|
|
40012
|
+
closeOpened(opened.fd);
|
|
40013
|
+
return `size:${opened.size}`;
|
|
40014
|
+
}
|
|
40015
|
+
try {
|
|
40016
|
+
return (0, import_node_crypto23.createHash)("sha256").update(readOpened(opened.fd, opened.size)).digest("hex");
|
|
40017
|
+
} catch {
|
|
40018
|
+
return "unreadable";
|
|
40019
|
+
}
|
|
40020
|
+
}
|
|
40021
|
+
function treeFingerprint(root, dirRel) {
|
|
40022
|
+
try {
|
|
40023
|
+
if (!(0, import_node_fs69.lstatSync)((0, import_node_path57.join)(root, dirRel)).isDirectory()) return fingerprint(root, dirRel);
|
|
40024
|
+
} catch {
|
|
40025
|
+
return null;
|
|
40026
|
+
}
|
|
40027
|
+
const parts2 = [];
|
|
40028
|
+
const walk2 = (rel) => {
|
|
40029
|
+
let names2;
|
|
40030
|
+
try {
|
|
40031
|
+
names2 = (0, import_node_fs69.readdirSync)((0, import_node_path57.join)(root, rel)).sort();
|
|
40032
|
+
} catch {
|
|
40033
|
+
return;
|
|
40034
|
+
}
|
|
40035
|
+
for (const name2 of names2) {
|
|
40036
|
+
if (parts2.length >= MAX_TREE_ENTRIES) return;
|
|
40037
|
+
const relPath = `${rel}/${name2}`;
|
|
40038
|
+
let st;
|
|
40039
|
+
try {
|
|
40040
|
+
st = (0, import_node_fs69.lstatSync)((0, import_node_path57.join)(root, relPath));
|
|
40041
|
+
} catch {
|
|
40042
|
+
continue;
|
|
40043
|
+
}
|
|
40044
|
+
if (st.isDirectory()) walk2(relPath);
|
|
40045
|
+
else parts2.push(`${relPath}\0${fingerprint(root, relPath)}`);
|
|
40046
|
+
}
|
|
40047
|
+
};
|
|
40048
|
+
walk2(dirRel);
|
|
40049
|
+
return (0, import_node_crypto23.createHash)("sha256").update(parts2.join("\n")).digest("hex");
|
|
40050
|
+
}
|
|
40051
|
+
function snapshotInitFiles(root = process.cwd(), env = process.env) {
|
|
40052
|
+
const snap = /* @__PURE__ */ new Map();
|
|
40053
|
+
for (const f of REPO_FILES) snap.set(f, fingerprint(root, f));
|
|
40054
|
+
snap.set(`${SKILLS_DIR}/`, treeFingerprint(root, SKILLS_DIR));
|
|
40055
|
+
snap.set("~/.claude/settings.json", fingerprint(userSettingsDir(env), "settings.json"));
|
|
40056
|
+
return snap;
|
|
40057
|
+
}
|
|
40058
|
+
function changedSince(before, after) {
|
|
40059
|
+
return [...after.keys()].filter((k) => before.get(k) !== after.get(k));
|
|
40060
|
+
}
|
|
40061
|
+
|
|
40062
|
+
// src/lib/init-trace.ts
|
|
40063
|
+
var import_node_crypto24 = require("node:crypto");
|
|
40064
|
+
var import_node_fs70 = require("node:fs");
|
|
40065
|
+
var import_node_path58 = require("node:path");
|
|
39830
40066
|
var INIT_STEPS = [
|
|
39831
40067
|
"started",
|
|
39832
40068
|
"prerequisites",
|
|
@@ -39872,9 +40108,9 @@ function traceDisabled(env = process.env) {
|
|
|
39872
40108
|
}
|
|
39873
40109
|
var PENDING_NAME = "init-trace.json";
|
|
39874
40110
|
var MAX_PENDING_BYTES = 64 * 1024;
|
|
39875
|
-
var verityHome2 = () => (0,
|
|
40111
|
+
var verityHome2 = () => (0, import_node_path58.join)(process.env.HOME ?? "", ".verity");
|
|
39876
40112
|
function pendingTracePath() {
|
|
39877
|
-
return (0,
|
|
40113
|
+
return (0, import_node_path58.join)(verityHome2(), PENDING_NAME);
|
|
39878
40114
|
}
|
|
39879
40115
|
var RUN_ID = /^[0-9a-f]{16}$/;
|
|
39880
40116
|
var CLI_VERSION = /^[0-9A-Za-z.+-]{1,40}$/;
|
|
@@ -39932,10 +40168,10 @@ function writePending(runs) {
|
|
|
39932
40168
|
try {
|
|
39933
40169
|
const kept = runs.filter((r) => r.events.length > 0).slice(-MAX_PENDING_RUNS);
|
|
39934
40170
|
if (kept.length === 0) {
|
|
39935
|
-
(0,
|
|
40171
|
+
(0, import_node_fs70.rmSync)(pendingTracePath(), { force: true });
|
|
39936
40172
|
return;
|
|
39937
40173
|
}
|
|
39938
|
-
(0,
|
|
40174
|
+
(0, import_node_fs70.mkdirSync)(verityHome2(), { recursive: true });
|
|
39939
40175
|
if (!replaceInRoot(verityHome2(), PENDING_NAME, JSON.stringify(kept))) {
|
|
39940
40176
|
logEvent("init_trace_save_refused", {});
|
|
39941
40177
|
}
|
|
@@ -39978,7 +40214,7 @@ function startInitTrace(opts) {
|
|
|
39978
40214
|
if (traceDisabled()) return NOOP;
|
|
39979
40215
|
const startedAt = Date.now();
|
|
39980
40216
|
const current = {
|
|
39981
|
-
run_id: (0,
|
|
40217
|
+
run_id: (0, import_node_crypto24.randomBytes)(8).toString("hex"),
|
|
39982
40218
|
mode: opts.mode,
|
|
39983
40219
|
interactive: opts.interactive,
|
|
39984
40220
|
cli_version: cliVersion(),
|
|
@@ -40628,400 +40864,11 @@ var Rail = class {
|
|
|
40628
40864
|
}
|
|
40629
40865
|
};
|
|
40630
40866
|
|
|
40631
|
-
// src/commands/migrate.ts
|
|
40632
|
-
var import_node_fs70 = require("node:fs");
|
|
40633
|
-
var import_node_path57 = require("node:path");
|
|
40634
|
-
var import_node_child_process18 = require("node:child_process");
|
|
40635
|
-
|
|
40636
|
-
// src/lib/telemetry.ts
|
|
40637
|
-
var import_promises16 = require("node:fs/promises");
|
|
40638
|
-
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
40639
|
-
var GITIGNORE_FILE = ".gitignore";
|
|
40640
|
-
var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
|
|
40641
|
-
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
40642
|
-
var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
40643
|
-
function deriveOtlpEndpoint(serviceUrl) {
|
|
40644
|
-
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
40645
|
-
}
|
|
40646
|
-
function buildTelemetryEnv(serviceUrl) {
|
|
40647
|
-
return {
|
|
40648
|
-
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
40649
|
-
OTEL_METRICS_EXPORTER: "otlp",
|
|
40650
|
-
OTEL_TRACES_EXPORTER: "otlp",
|
|
40651
|
-
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
40652
|
-
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
40653
|
-
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
40654
|
-
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
40655
|
-
OTEL_LOG_USER_PROMPTS: "0",
|
|
40656
|
-
OTEL_LOG_TOOL_DETAILS: "0",
|
|
40657
|
-
OTEL_LOG_TOOL_CONTENT: "0",
|
|
40658
|
-
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
40659
|
-
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
40660
|
-
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
40661
|
-
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
40662
|
-
};
|
|
40663
|
-
}
|
|
40664
|
-
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
|
|
40665
|
-
async function readSettingsLocal() {
|
|
40666
|
-
try {
|
|
40667
|
-
return JSON.parse(await (0, import_promises16.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
40668
|
-
} catch {
|
|
40669
|
-
return {};
|
|
40670
|
-
}
|
|
40671
|
-
}
|
|
40672
|
-
async function writeSettingsLocal(settings) {
|
|
40673
|
-
await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
|
|
40674
|
-
}
|
|
40675
|
-
async function ensureGitignore() {
|
|
40676
|
-
const file = projectPath(GITIGNORE_FILE);
|
|
40677
|
-
let content = "";
|
|
40678
|
-
try {
|
|
40679
|
-
content = await (0, import_promises16.readFile)(file, "utf-8");
|
|
40680
|
-
} catch {
|
|
40681
|
-
}
|
|
40682
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
40683
|
-
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
40684
|
-
return;
|
|
40685
|
-
}
|
|
40686
|
-
const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
|
|
40687
|
-
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
40688
|
-
await (0, import_promises16.writeFile)(file, next);
|
|
40689
|
-
}
|
|
40690
|
-
async function installTelemetry(serviceUrl) {
|
|
40691
|
-
const env = buildTelemetryEnv(serviceUrl);
|
|
40692
|
-
const settings = await readSettingsLocal();
|
|
40693
|
-
settings.env = { ...settings.env ?? {}, ...env };
|
|
40694
|
-
for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
|
|
40695
|
-
settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
|
|
40696
|
-
await writeSettingsLocal(settings);
|
|
40697
|
-
await ensureGitignore();
|
|
40698
|
-
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
40699
|
-
}
|
|
40700
|
-
async function checkTelemetry() {
|
|
40701
|
-
const env = (await readSettingsLocal()).env ?? {};
|
|
40702
|
-
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
40703
|
-
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
40704
|
-
}
|
|
40705
|
-
async function uninstallTelemetry() {
|
|
40706
|
-
const settings = await readSettingsLocal();
|
|
40707
|
-
const hadHelper = settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD;
|
|
40708
|
-
if (hadHelper) delete settings.otelHeadersHelper;
|
|
40709
|
-
if (!settings.env) {
|
|
40710
|
-
if (hadHelper) await writeSettingsLocal(settings);
|
|
40711
|
-
return { ok: true, data: { removed: 0 } };
|
|
40712
|
-
}
|
|
40713
|
-
let removed = 0;
|
|
40714
|
-
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
40715
|
-
if (key in settings.env) {
|
|
40716
|
-
delete settings.env[key];
|
|
40717
|
-
removed++;
|
|
40718
|
-
}
|
|
40719
|
-
}
|
|
40720
|
-
for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
|
|
40721
|
-
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
40722
|
-
await writeSettingsLocal(settings);
|
|
40723
|
-
return { ok: true, data: { removed } };
|
|
40724
|
-
}
|
|
40725
|
-
|
|
40726
|
-
// src/commands/migrate.ts
|
|
40727
|
-
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
40728
|
-
function defaultNpmRemover(pkg) {
|
|
40729
|
-
(0, import_node_child_process18.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
40730
|
-
}
|
|
40731
|
-
function isGitTracked(cwd, relPath) {
|
|
40732
|
-
try {
|
|
40733
|
-
(0, import_node_child_process18.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
40734
|
-
return true;
|
|
40735
|
-
} catch {
|
|
40736
|
-
return false;
|
|
40737
|
-
}
|
|
40738
|
-
}
|
|
40739
|
-
function isGitRepo(cwd) {
|
|
40740
|
-
try {
|
|
40741
|
-
(0, import_node_child_process18.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
40742
|
-
return true;
|
|
40743
|
-
} catch {
|
|
40744
|
-
return false;
|
|
40745
|
-
}
|
|
40746
|
-
}
|
|
40747
|
-
async function runMigration(opts = {}) {
|
|
40748
|
-
const root = opts.cwd ?? repoRoot();
|
|
40749
|
-
const home = opts.home ?? process.env.HOME ?? "";
|
|
40750
|
-
const npmRemover = opts.npmRemover ?? defaultNpmRemover;
|
|
40751
|
-
const actions = [];
|
|
40752
|
-
const movedProjectDir = migrateProjectDir(root, actions);
|
|
40753
|
-
migrateGlobalCredentials(home, actions);
|
|
40754
|
-
await migrateLegacyHooks(root, actions);
|
|
40755
|
-
await migrateClaudeMd(root, actions);
|
|
40756
|
-
migrateStandardFile(root, actions);
|
|
40757
|
-
await migrateTelemetryHeaders(root, actions);
|
|
40758
|
-
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
40759
|
-
return { actions, migrated: actions.length > 0 };
|
|
40760
|
-
}
|
|
40761
|
-
function migrateProjectDir(root, actions) {
|
|
40762
|
-
const gateDir = (0, import_node_path57.join)(root, ".gate");
|
|
40763
|
-
const verityDir = (0, import_node_path57.join)(root, ".verity");
|
|
40764
|
-
if ((0, import_node_fs70.existsSync)(gateDir) && !(0, import_node_fs70.existsSync)(verityDir)) {
|
|
40765
|
-
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
40766
|
-
}
|
|
40767
|
-
if ((0, import_node_fs70.existsSync)(gateDir) && (0, import_node_fs70.existsSync)(verityDir)) {
|
|
40768
|
-
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
40769
|
-
}
|
|
40770
|
-
return false;
|
|
40771
|
-
}
|
|
40772
|
-
function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
40773
|
-
let moved = false;
|
|
40774
|
-
if (isGitRepo(root) && isGitTracked(root, ".gate")) {
|
|
40775
|
-
if (hasStagedChanges(root)) {
|
|
40776
|
-
printWarn(
|
|
40777
|
-
"Migrating .gate \u2192 .verity with `git mv` while the index has staged changes \u2014 the rename will be staged alongside them. Review `git status` before committing."
|
|
40778
|
-
);
|
|
40779
|
-
}
|
|
40780
|
-
try {
|
|
40781
|
-
(0, import_node_child_process18.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
40782
|
-
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
40783
|
-
moved = true;
|
|
40784
|
-
} catch {
|
|
40785
|
-
}
|
|
40786
|
-
}
|
|
40787
|
-
if (moved) {
|
|
40788
|
-
if ((0, import_node_fs70.existsSync)(gateDir)) {
|
|
40789
|
-
const carried = carryLegacyContents(gateDir, verityDir);
|
|
40790
|
-
if (carried > 0) {
|
|
40791
|
-
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
40792
|
-
}
|
|
40793
|
-
try {
|
|
40794
|
-
(0, import_node_fs70.rmSync)(gateDir, { recursive: true, force: true });
|
|
40795
|
-
} catch {
|
|
40796
|
-
}
|
|
40797
|
-
}
|
|
40798
|
-
return true;
|
|
40799
|
-
}
|
|
40800
|
-
moveDir(gateDir, verityDir);
|
|
40801
|
-
actions.push("Moved .gate/ \u2192 .verity/");
|
|
40802
|
-
return true;
|
|
40803
|
-
}
|
|
40804
|
-
function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
40805
|
-
const carried = carryLegacyContents(gateDir, verityDir);
|
|
40806
|
-
if (carried > 0) {
|
|
40807
|
-
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
40808
|
-
}
|
|
40809
|
-
try {
|
|
40810
|
-
(0, import_node_fs70.rmSync)(gateDir, { recursive: true, force: true });
|
|
40811
|
-
} catch {
|
|
40812
|
-
}
|
|
40813
|
-
return carried > 0;
|
|
40814
|
-
}
|
|
40815
|
-
function migrateGlobalCredentials(home, actions) {
|
|
40816
|
-
if (!home) return;
|
|
40817
|
-
const gateCreds = (0, import_node_path57.join)(home, ".gate", "credentials");
|
|
40818
|
-
const verityCreds = (0, import_node_path57.join)(home, ".verity", "credentials");
|
|
40819
|
-
if (!(0, import_node_fs70.existsSync)(gateCreds)) return;
|
|
40820
|
-
if (!(0, import_node_fs70.existsSync)(verityCreds)) {
|
|
40821
|
-
(0, import_node_fs70.mkdirSync)((0, import_node_path57.join)(home, ".verity"), { recursive: true });
|
|
40822
|
-
moveFile(gateCreds, verityCreds);
|
|
40823
|
-
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
40824
|
-
return;
|
|
40825
|
-
}
|
|
40826
|
-
const added = mergeGlobalCredentials(gateCreds, verityCreds);
|
|
40827
|
-
if (added > 0) {
|
|
40828
|
-
actions.push(
|
|
40829
|
-
`Merged ${added} legacy credential line(s) from ~/.gate/credentials into ~/.verity/credentials`
|
|
40830
|
-
);
|
|
40831
|
-
}
|
|
40832
|
-
}
|
|
40833
|
-
async function migrateLegacyHooks(root, actions) {
|
|
40834
|
-
const settings = await readSettingsAt(root);
|
|
40835
|
-
const settingsBefore = JSON.stringify(settings);
|
|
40836
|
-
const updatedSettings = removeVerityHooks(settings);
|
|
40837
|
-
if (settingsBefore !== JSON.stringify(updatedSettings)) {
|
|
40838
|
-
await writeSettingsAt(root, updatedSettings);
|
|
40839
|
-
actions.push("Stripped legacy gate hooks from .claude/settings.json");
|
|
40840
|
-
}
|
|
40841
|
-
}
|
|
40842
|
-
async function migrateClaudeMd(root, actions) {
|
|
40843
|
-
const claudeMd = (0, import_node_path57.join)(root, "CLAUDE.md");
|
|
40844
|
-
const hadLegacyBlock = (0, import_node_fs70.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
40845
|
-
if (!hadLegacyBlock) return;
|
|
40846
|
-
try {
|
|
40847
|
-
await ensureClaudeMdPointer(root);
|
|
40848
|
-
actions.push("Rewrote legacy gate-memory block in CLAUDE.md");
|
|
40849
|
-
} catch (err2) {
|
|
40850
|
-
printWarn(`Could not rewrite CLAUDE.md block: ${err2.message}`);
|
|
40851
|
-
}
|
|
40852
|
-
}
|
|
40853
|
-
function migrateStandardFile(root, actions) {
|
|
40854
|
-
const gateMd = (0, import_node_path57.join)(root, "GATE.md");
|
|
40855
|
-
const verityMd = (0, import_node_path57.join)(root, "VERITY.md");
|
|
40856
|
-
if (!(0, import_node_fs70.existsSync)(gateMd) || (0, import_node_fs70.existsSync)(verityMd)) return;
|
|
40857
|
-
let moved = false;
|
|
40858
|
-
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
40859
|
-
try {
|
|
40860
|
-
(0, import_node_child_process18.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
40861
|
-
moved = true;
|
|
40862
|
-
} catch {
|
|
40863
|
-
}
|
|
40864
|
-
}
|
|
40865
|
-
if (!moved) moveFile(gateMd, verityMd);
|
|
40866
|
-
const content = readFileSyncSafe(verityMd);
|
|
40867
|
-
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
40868
|
-
if (refreshed !== content) (0, import_node_fs70.writeFileSync)(verityMd, refreshed);
|
|
40869
|
-
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
40870
|
-
}
|
|
40871
|
-
async function migrateTelemetryHeaders(root, actions) {
|
|
40872
|
-
const file = (0, import_node_path57.join)(root, ".claude", "settings.local.json");
|
|
40873
|
-
if (!(0, import_node_fs70.existsSync)(file)) return;
|
|
40874
|
-
let settings;
|
|
40875
|
-
try {
|
|
40876
|
-
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
40877
|
-
} catch {
|
|
40878
|
-
return;
|
|
40879
|
-
}
|
|
40880
|
-
const env = settings.env;
|
|
40881
|
-
const hasInlineHeader = !!env && "OTEL_EXPORTER_OTLP_HEADERS" in env;
|
|
40882
|
-
const telemetryEnabled = env?.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
|
|
40883
|
-
const needsHelper = telemetryEnabled && settings.otelHeadersHelper !== OTEL_HEADERS_HELPER_CMD;
|
|
40884
|
-
if (!hasInlineHeader && !needsHelper) return;
|
|
40885
|
-
if (hasInlineHeader && env) delete env["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
40886
|
-
if (telemetryEnabled) settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
|
|
40887
|
-
await writeJsonFilePreservingStyle(file, settings);
|
|
40888
|
-
actions.push("Moved the telemetry token out of .claude/settings.local.json (now resolved at runtime)");
|
|
40889
|
-
}
|
|
40890
|
-
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
40891
|
-
if (!movedProjectDir) return;
|
|
40892
|
-
try {
|
|
40893
|
-
npmRemover(LEGACY_NPM_PACKAGE);
|
|
40894
|
-
actions.push(`Removed legacy global package ${LEGACY_NPM_PACKAGE}`);
|
|
40895
|
-
} catch (err2) {
|
|
40896
|
-
printWarn(
|
|
40897
|
-
`Could not remove ${LEGACY_NPM_PACKAGE} (${err2.message}). Remove it manually: npm rm -g ${LEGACY_NPM_PACKAGE}`
|
|
40898
|
-
);
|
|
40899
|
-
}
|
|
40900
|
-
}
|
|
40901
|
-
function credentialRemoteKey(line2) {
|
|
40902
|
-
const trimmed = line2.trim();
|
|
40903
|
-
if (trimmed === "") return null;
|
|
40904
|
-
const idx = trimmed.indexOf(" token:");
|
|
40905
|
-
return idx === -1 ? trimmed : trimmed.slice(0, idx).trim();
|
|
40906
|
-
}
|
|
40907
|
-
function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
40908
|
-
const verityContent = readFileSyncSafe(verityCreds);
|
|
40909
|
-
const existingKeys = new Set(
|
|
40910
|
-
verityContent.split("\n").map(credentialRemoteKey).filter((k) => k !== null)
|
|
40911
|
-
);
|
|
40912
|
-
const toAppend = [];
|
|
40913
|
-
for (const line2 of readFileSyncSafe(gateCreds).split("\n")) {
|
|
40914
|
-
const key = credentialRemoteKey(line2);
|
|
40915
|
-
if (key === null || existingKeys.has(key)) continue;
|
|
40916
|
-
existingKeys.add(key);
|
|
40917
|
-
toAppend.push(line2.replace(/\r$/, ""));
|
|
40918
|
-
}
|
|
40919
|
-
if (toAppend.length > 0) {
|
|
40920
|
-
const sep7 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
40921
|
-
(0, import_node_fs70.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
|
|
40922
|
-
}
|
|
40923
|
-
(0, import_node_fs70.rmSync)(gateCreds, { force: true });
|
|
40924
|
-
return toAppend.length;
|
|
40925
|
-
}
|
|
40926
|
-
function readFileSyncSafe(path) {
|
|
40927
|
-
try {
|
|
40928
|
-
return (0, import_node_fs70.readFileSync)(path, "utf-8");
|
|
40929
|
-
} catch {
|
|
40930
|
-
return "";
|
|
40931
|
-
}
|
|
40932
|
-
}
|
|
40933
|
-
function hasStagedChanges(root) {
|
|
40934
|
-
try {
|
|
40935
|
-
(0, import_node_child_process18.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
40936
|
-
return false;
|
|
40937
|
-
} catch {
|
|
40938
|
-
return true;
|
|
40939
|
-
}
|
|
40940
|
-
}
|
|
40941
|
-
function moveDir(from, to) {
|
|
40942
|
-
try {
|
|
40943
|
-
(0, import_node_fs70.renameSync)(from, to);
|
|
40944
|
-
} catch (err2) {
|
|
40945
|
-
if (err2.code !== "EXDEV") throw err2;
|
|
40946
|
-
(0, import_node_fs70.cpSync)(from, to, { recursive: true });
|
|
40947
|
-
(0, import_node_fs70.rmSync)(from, { recursive: true, force: true });
|
|
40948
|
-
}
|
|
40949
|
-
}
|
|
40950
|
-
function moveFile(from, to) {
|
|
40951
|
-
try {
|
|
40952
|
-
(0, import_node_fs70.renameSync)(from, to);
|
|
40953
|
-
} catch (err2) {
|
|
40954
|
-
if (err2.code !== "EXDEV") throw err2;
|
|
40955
|
-
(0, import_node_fs70.cpSync)(from, to);
|
|
40956
|
-
(0, import_node_fs70.rmSync)(from, { force: true });
|
|
40957
|
-
}
|
|
40958
|
-
}
|
|
40959
|
-
function carryLegacyContents(gateDir, verityDir) {
|
|
40960
|
-
let copied = 0;
|
|
40961
|
-
const walk2 = (relDir) => {
|
|
40962
|
-
const srcDir = (0, import_node_path57.join)(gateDir, relDir);
|
|
40963
|
-
for (const entry of (0, import_node_fs70.readdirSync)(srcDir)) {
|
|
40964
|
-
const rel = relDir ? (0, import_node_path57.join)(relDir, entry) : entry;
|
|
40965
|
-
const src = (0, import_node_path57.join)(gateDir, rel);
|
|
40966
|
-
const dest = (0, import_node_path57.join)(verityDir, rel);
|
|
40967
|
-
if ((0, import_node_fs70.statSync)(src).isDirectory()) {
|
|
40968
|
-
walk2(rel);
|
|
40969
|
-
} else if (!(0, import_node_fs70.existsSync)(dest)) {
|
|
40970
|
-
(0, import_node_fs70.mkdirSync)((0, import_node_path57.dirname)(dest), { recursive: true });
|
|
40971
|
-
(0, import_node_fs70.cpSync)(src, dest);
|
|
40972
|
-
copied++;
|
|
40973
|
-
}
|
|
40974
|
-
}
|
|
40975
|
-
};
|
|
40976
|
-
walk2("");
|
|
40977
|
-
return copied;
|
|
40978
|
-
}
|
|
40979
|
-
async function needsMigration(root = repoRoot()) {
|
|
40980
|
-
const gateDir = (0, import_node_path57.join)(root, ".gate");
|
|
40981
|
-
const verityDir = (0, import_node_path57.join)(root, ".verity");
|
|
40982
|
-
if ((0, import_node_fs70.existsSync)(gateDir) && !(0, import_node_fs70.existsSync)(verityDir)) return true;
|
|
40983
|
-
if ((0, import_node_fs70.existsSync)(gateDir) && (0, import_node_fs70.existsSync)(verityDir)) {
|
|
40984
|
-
if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(gateDir, "credentials")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(verityDir, "credentials"))) {
|
|
40985
|
-
return true;
|
|
40986
|
-
}
|
|
40987
|
-
if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(gateDir, "memory")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(verityDir, "memory"))) {
|
|
40988
|
-
return true;
|
|
40989
|
-
}
|
|
40990
|
-
}
|
|
40991
|
-
const claudeMd = (0, import_node_path57.join)(root, "CLAUDE.md");
|
|
40992
|
-
if ((0, import_node_fs70.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
40993
|
-
return true;
|
|
40994
|
-
}
|
|
40995
|
-
if ((0, import_node_fs70.existsSync)((0, import_node_path57.join)(root, "GATE.md")) && !(0, import_node_fs70.existsSync)((0, import_node_path57.join)(root, "VERITY.md"))) {
|
|
40996
|
-
return true;
|
|
40997
|
-
}
|
|
40998
|
-
if (await hasLegacyHooksAt(root)) return true;
|
|
40999
|
-
return false;
|
|
41000
|
-
}
|
|
41001
|
-
function registerMigrateCommand(program2) {
|
|
41002
|
-
program2.command("migrate").description("Migrate a legacy GATE.md install to Verity (.gate \u2192 .verity, hooks, CLAUDE.md)").action(async () => {
|
|
41003
|
-
let result;
|
|
41004
|
-
try {
|
|
41005
|
-
result = await runMigration();
|
|
41006
|
-
} catch (err2) {
|
|
41007
|
-
printError(`Migration failed: ${err2.message}`);
|
|
41008
|
-
process.exitCode = 1;
|
|
41009
|
-
return;
|
|
41010
|
-
}
|
|
41011
|
-
if (!result.migrated) {
|
|
41012
|
-
printInfo("Nothing to migrate \u2014 this project is already on Verity.");
|
|
41013
|
-
return;
|
|
41014
|
-
}
|
|
41015
|
-
printInfo("Migration complete:");
|
|
41016
|
-
for (const a of result.actions) printInfo(` ${a} \u2713`);
|
|
41017
|
-
});
|
|
41018
|
-
}
|
|
41019
|
-
|
|
41020
40867
|
// src/commands/init/ui.ts
|
|
41021
40868
|
var import_node_fs71 = require("node:fs");
|
|
41022
|
-
var
|
|
41023
|
-
var
|
|
41024
|
-
var
|
|
40869
|
+
var import_node_path59 = require("node:path");
|
|
40870
|
+
var import_node_os13 = require("node:os");
|
|
40871
|
+
var import_node_child_process18 = require("node:child_process");
|
|
41025
40872
|
function createInitUi(opts) {
|
|
41026
40873
|
const term = opts.term ?? detectTerm();
|
|
41027
40874
|
const screen = new Screen(term);
|
|
@@ -41038,25 +40885,26 @@ function installInterrupt(ui) {
|
|
|
41038
40885
|
process.once("SIGINT", stop2);
|
|
41039
40886
|
}
|
|
41040
40887
|
var SEEN_FILE = ".init-seen";
|
|
41041
|
-
function firstInitOnMachine(home = process.env.HOME || (0,
|
|
41042
|
-
return !(0, import_node_fs71.existsSync)((0,
|
|
40888
|
+
function firstInitOnMachine(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
40889
|
+
return !(0, import_node_fs71.existsSync)((0, import_node_path59.join)(home, ".verity", SEEN_FILE));
|
|
41043
40890
|
}
|
|
41044
|
-
function markInitSeen(home = process.env.HOME || (0,
|
|
41045
|
-
replaceInRoot((0,
|
|
40891
|
+
function markInitSeen(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
40892
|
+
replaceInRoot((0, import_node_path59.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41046
40893
|
`);
|
|
41047
40894
|
}
|
|
41048
40895
|
function repoLabel() {
|
|
41049
40896
|
try {
|
|
41050
|
-
const url = (0,
|
|
40897
|
+
const url = (0, import_node_child_process18.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
41051
40898
|
const parsed = parseRemote(url);
|
|
41052
40899
|
if (parsed) return terminalText(`${parsed.owner}/${parsed.repo}`, 120);
|
|
41053
40900
|
} catch {
|
|
41054
40901
|
}
|
|
41055
|
-
return terminalText((0,
|
|
40902
|
+
return terminalText((0, import_node_path59.basename)(repoRoot()), 120);
|
|
41056
40903
|
}
|
|
41057
40904
|
|
|
41058
40905
|
// src/lib/prereqs.ts
|
|
41059
|
-
var
|
|
40906
|
+
var import_node_child_process19 = require("node:child_process");
|
|
40907
|
+
var import_node_fs72 = require("node:fs");
|
|
41060
40908
|
var MIN_NODE_MAJOR = 20;
|
|
41061
40909
|
function which(bin) {
|
|
41062
40910
|
return whichSync(bin, { real: false });
|
|
@@ -41077,7 +40925,7 @@ function checkNode() {
|
|
|
41077
40925
|
function checkGit() {
|
|
41078
40926
|
let detail = "";
|
|
41079
40927
|
try {
|
|
41080
|
-
detail = (0,
|
|
40928
|
+
detail = (0, import_node_child_process19.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
41081
40929
|
} catch {
|
|
41082
40930
|
return {
|
|
41083
40931
|
id: "git",
|
|
@@ -41101,21 +40949,38 @@ function checkClaude() {
|
|
|
41101
40949
|
required: false
|
|
41102
40950
|
};
|
|
41103
40951
|
}
|
|
41104
|
-
|
|
41105
|
-
|
|
40952
|
+
var ANALYSIS_CLI_REMEDY = "Reinstall Verity, which includes it: npm install -g @codacy/verity-cli";
|
|
40953
|
+
function checkAnalysisCli(opts = {}) {
|
|
40954
|
+
const base = { id: "analysis-cli", label: "@codacy/analysis-cli", required: false };
|
|
40955
|
+
const analyzer = resolveAnalyzer(opts);
|
|
40956
|
+
const shown = (opts.onPathDisplay ?? (() => which("codacy-analysis")))();
|
|
40957
|
+
if (!analyzer) {
|
|
40958
|
+
return { ...base, status: "warn", detail: "not found", remedy: `${ANALYSIS_CLI_REMEDY} (static findings are unavailable until then)` };
|
|
40959
|
+
}
|
|
40960
|
+
if (analyzer.source === "path") {
|
|
40961
|
+
return { ...base, status: "ok", detail: `${shown ?? analyzer.path} (on PATH)`, source: "path" };
|
|
40962
|
+
}
|
|
40963
|
+
const unused = shown && !sameFile(shown, analyzer.path) ? shown : void 0;
|
|
41106
40964
|
return {
|
|
41107
|
-
|
|
41108
|
-
|
|
41109
|
-
|
|
41110
|
-
|
|
41111
|
-
|
|
41112
|
-
|
|
40965
|
+
...base,
|
|
40966
|
+
status: "ok",
|
|
40967
|
+
detail: `${analyzer.version} (bundled with Verity)`,
|
|
40968
|
+
source: "bundled",
|
|
40969
|
+
version: analyzer.version,
|
|
40970
|
+
...unused && { unused }
|
|
41113
40971
|
};
|
|
41114
40972
|
}
|
|
40973
|
+
function sameFile(a, b) {
|
|
40974
|
+
try {
|
|
40975
|
+
return (0, import_node_fs72.realpathSync)(a) === (0, import_node_fs72.realpathSync)(b);
|
|
40976
|
+
} catch {
|
|
40977
|
+
return false;
|
|
40978
|
+
}
|
|
40979
|
+
}
|
|
41115
40980
|
var INSTALL_TIMEOUT_MS = 12e4;
|
|
41116
40981
|
function run3(command, args2, opts = {}) {
|
|
41117
40982
|
return new Promise((resolve9) => {
|
|
41118
|
-
const child = (0,
|
|
40983
|
+
const child = (0, import_node_child_process19.spawn)(command, args2, {
|
|
41119
40984
|
stdio: opts.inherit ? "inherit" : "pipe",
|
|
41120
40985
|
timeout: INSTALL_TIMEOUT_MS
|
|
41121
40986
|
});
|
|
@@ -41145,7 +41010,7 @@ async function installAnalysisCli(opts = {}) {
|
|
|
41145
41010
|
label: "@codacy/analysis-cli",
|
|
41146
41011
|
status: "warn",
|
|
41147
41012
|
detail: "install failed",
|
|
41148
|
-
remedy:
|
|
41013
|
+
remedy: ANALYSIS_CLI_REMEDY,
|
|
41149
41014
|
required: false
|
|
41150
41015
|
};
|
|
41151
41016
|
if (opts.sudo === false || !process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
@@ -41166,6 +41031,305 @@ async function checkPrereqs(opts = {}) {
|
|
|
41166
41031
|
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
41167
41032
|
}
|
|
41168
41033
|
|
|
41034
|
+
// src/lib/telemetry-local.ts
|
|
41035
|
+
var import_node_path61 = require("node:path");
|
|
41036
|
+
|
|
41037
|
+
// src/lib/telemetry-settings.ts
|
|
41038
|
+
var import_promises16 = require("node:fs/promises");
|
|
41039
|
+
var import_node_os14 = require("node:os");
|
|
41040
|
+
var import_node_path60 = require("node:path");
|
|
41041
|
+
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
41042
|
+
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
41043
|
+
var LEGACY_HEADER_KEY = "OTEL_EXPORTER_OTLP_HEADERS";
|
|
41044
|
+
var OTLP_ENDPOINT_KEYS = [
|
|
41045
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
41046
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
41047
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
41048
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
|
|
41049
|
+
];
|
|
41050
|
+
function claudeConfigDir2() {
|
|
41051
|
+
return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path60.join)((0, import_node_os14.homedir)(), ".claude");
|
|
41052
|
+
}
|
|
41053
|
+
function userSettingsPath() {
|
|
41054
|
+
return (0, import_node_path60.join)(claudeConfigDir2(), "settings.json");
|
|
41055
|
+
}
|
|
41056
|
+
function deriveOtlpEndpoint(serviceUrl) {
|
|
41057
|
+
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
41058
|
+
}
|
|
41059
|
+
function isVerityOtlpEndpoint(value, also) {
|
|
41060
|
+
if (typeof value !== "string" || value.trim() === "") return false;
|
|
41061
|
+
if (also && value.replace(/\/+$/, "") === also.replace(/\/+$/, "")) return true;
|
|
41062
|
+
try {
|
|
41063
|
+
return /\/functions\/v1\/otlp\/?$/.test(new URL(value).pathname);
|
|
41064
|
+
} catch {
|
|
41065
|
+
return false;
|
|
41066
|
+
}
|
|
41067
|
+
}
|
|
41068
|
+
function sameUrl(a, b) {
|
|
41069
|
+
const norm3 = (raw) => {
|
|
41070
|
+
const trimmed = raw.trim().replace(/\/+$/, "");
|
|
41071
|
+
try {
|
|
41072
|
+
const u = new URL(trimmed);
|
|
41073
|
+
return `${u.protocol}//${u.host.toLowerCase()}${u.pathname.replace(/\/+$/, "")}`;
|
|
41074
|
+
} catch {
|
|
41075
|
+
return trimmed.toLowerCase();
|
|
41076
|
+
}
|
|
41077
|
+
};
|
|
41078
|
+
return norm3(a) === norm3(b);
|
|
41079
|
+
}
|
|
41080
|
+
function envOf(settings) {
|
|
41081
|
+
const env = settings.env;
|
|
41082
|
+
return env && typeof env === "object" && !Array.isArray(env) ? env : {};
|
|
41083
|
+
}
|
|
41084
|
+
function displayPath(path) {
|
|
41085
|
+
const home = (0, import_node_os14.homedir)();
|
|
41086
|
+
return home && path.startsWith(home + "/") ? "~" + path.slice(home.length) : path;
|
|
41087
|
+
}
|
|
41088
|
+
async function readSettingsFile(path) {
|
|
41089
|
+
let raw;
|
|
41090
|
+
try {
|
|
41091
|
+
raw = await (0, import_promises16.readFile)(path, "utf-8");
|
|
41092
|
+
} catch (err2) {
|
|
41093
|
+
if (err2.code === "ENOENT") return { ok: true, settings: {}, exists: false };
|
|
41094
|
+
return { ok: false, error: `Could not read ${displayPath(path)}: ${err2.message}` };
|
|
41095
|
+
}
|
|
41096
|
+
try {
|
|
41097
|
+
const parsed = raw.trim() === "" ? {} : JSON.parse(raw);
|
|
41098
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
41099
|
+
return { ok: false, error: `${displayPath(path)} is not a JSON object. Fix it, then run the command again. Nothing was changed.` };
|
|
41100
|
+
}
|
|
41101
|
+
return { ok: true, settings: parsed, exists: true };
|
|
41102
|
+
} catch {
|
|
41103
|
+
return { ok: false, error: `${displayPath(path)} is not valid JSON. Fix it, then run the command again. Nothing was changed.` };
|
|
41104
|
+
}
|
|
41105
|
+
}
|
|
41106
|
+
|
|
41107
|
+
// src/lib/telemetry-local.ts
|
|
41108
|
+
var MAX_LOCAL_SETTINGS_BYTES = 1024 * 1024;
|
|
41109
|
+
function nextLocalBytes(raw, value) {
|
|
41110
|
+
try {
|
|
41111
|
+
if (jsonSemanticEqual(JSON.parse(raw), value)) return null;
|
|
41112
|
+
} catch {
|
|
41113
|
+
}
|
|
41114
|
+
const next = JSON.stringify(value, null, detectJsonIndent(raw)) + "\n";
|
|
41115
|
+
return next === raw ? null : next;
|
|
41116
|
+
}
|
|
41117
|
+
var OLD_LOCAL_WRITER_VALUES = {
|
|
41118
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
41119
|
+
OTEL_METRICS_EXPORTER: "otlp",
|
|
41120
|
+
OTEL_TRACES_EXPORTER: "otlp",
|
|
41121
|
+
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
41122
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
41123
|
+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
41124
|
+
OTEL_LOG_USER_PROMPTS: "0",
|
|
41125
|
+
OTEL_LOG_TOOL_DETAILS: "0",
|
|
41126
|
+
OTEL_LOG_TOOL_CONTENT: "0",
|
|
41127
|
+
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
41128
|
+
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
41129
|
+
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
41130
|
+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
41131
|
+
};
|
|
41132
|
+
var LEGACY_VERITY_HEADER = /^Authorization=Bearer (?:verity|gate)_/;
|
|
41133
|
+
function planLocalHeal(settings) {
|
|
41134
|
+
const env = envOf(settings);
|
|
41135
|
+
const envKeys = [];
|
|
41136
|
+
if (typeof env[LEGACY_HEADER_KEY] === "string" && LEGACY_VERITY_HEADER.test(env[LEGACY_HEADER_KEY])) {
|
|
41137
|
+
envKeys.push(LEGACY_HEADER_KEY);
|
|
41138
|
+
}
|
|
41139
|
+
if (isVerityOtlpEndpoint(env.OTEL_EXPORTER_OTLP_ENDPOINT)) {
|
|
41140
|
+
envKeys.push("OTEL_EXPORTER_OTLP_ENDPOINT");
|
|
41141
|
+
for (const [key, value] of Object.entries(OLD_LOCAL_WRITER_VALUES)) {
|
|
41142
|
+
if (env[key] === value) envKeys.push(key);
|
|
41143
|
+
}
|
|
41144
|
+
}
|
|
41145
|
+
return { envKeys, helper: settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD };
|
|
41146
|
+
}
|
|
41147
|
+
function readLocalSettings(root) {
|
|
41148
|
+
const read2 = readRegularFileInRoot(root, SETTINGS_LOCAL_FILE2, MAX_LOCAL_SETTINGS_BYTES);
|
|
41149
|
+
if (!read2.ok) return null;
|
|
41150
|
+
try {
|
|
41151
|
+
const parsed = JSON.parse(read2.content);
|
|
41152
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
41153
|
+
return { raw: read2.content, settings: parsed };
|
|
41154
|
+
} catch {
|
|
41155
|
+
return null;
|
|
41156
|
+
}
|
|
41157
|
+
}
|
|
41158
|
+
function localTelemetryLeftovers(root = repoRoot()) {
|
|
41159
|
+
const local = readLocalSettings(root);
|
|
41160
|
+
if (!local) return [];
|
|
41161
|
+
const plan = planLocalHeal(local.settings);
|
|
41162
|
+
return [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []];
|
|
41163
|
+
}
|
|
41164
|
+
async function healLocalTelemetry(root = repoRoot()) {
|
|
41165
|
+
const local = readLocalSettings(root);
|
|
41166
|
+
if (!local) return { removed: [] };
|
|
41167
|
+
const settings = local.settings;
|
|
41168
|
+
const plan = planLocalHeal(settings);
|
|
41169
|
+
if (plan.envKeys.length === 0 && !plan.helper) return { removed: [] };
|
|
41170
|
+
const env = envOf(settings);
|
|
41171
|
+
for (const key of plan.envKeys) delete env[key];
|
|
41172
|
+
if (settings.env && Object.keys(env).length === 0) delete settings.env;
|
|
41173
|
+
if (plan.helper) delete settings.otelHeadersHelper;
|
|
41174
|
+
const next = nextLocalBytes(local.raw, settings);
|
|
41175
|
+
if (next !== null && !replaceInRoot((0, import_node_path61.join)(root, ".claude"), "settings.local.json", next)) {
|
|
41176
|
+
return { removed: [] };
|
|
41177
|
+
}
|
|
41178
|
+
return { removed: [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []] };
|
|
41179
|
+
}
|
|
41180
|
+
|
|
41181
|
+
// src/lib/telemetry-gate.ts
|
|
41182
|
+
var import_node_child_process20 = require("node:child_process");
|
|
41183
|
+
var import_node_fs73 = require("node:fs");
|
|
41184
|
+
var import_node_path62 = require("node:path");
|
|
41185
|
+
var PROJECT_MARKERS = ["setup.json", "config.json"];
|
|
41186
|
+
function writtenOnThisMachine(root, rel) {
|
|
41187
|
+
const r = (0, import_node_child_process20.spawnSync)("git", hardenedGitArgv(["ls-files", "--error-unmatch", "--", rel]), {
|
|
41188
|
+
cwd: root,
|
|
41189
|
+
env: hardenedGitEnv(),
|
|
41190
|
+
encoding: "utf-8",
|
|
41191
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
41192
|
+
timeout: 5e3
|
|
41193
|
+
});
|
|
41194
|
+
if (r.status === 1) return true;
|
|
41195
|
+
if (r.status === 128 && /not a git repository/i.test(r.stderr ?? "")) return true;
|
|
41196
|
+
return false;
|
|
41197
|
+
}
|
|
41198
|
+
function isTelemetryProject() {
|
|
41199
|
+
for (const root of [repoRoot(), mainWorktreeRoot()]) {
|
|
41200
|
+
if (!root) continue;
|
|
41201
|
+
const dir = (0, import_node_path62.join)(root, VERITY_DIR);
|
|
41202
|
+
if (isMachineStore(dir)) continue;
|
|
41203
|
+
for (const marker of PROJECT_MARKERS) {
|
|
41204
|
+
if ((0, import_node_fs73.existsSync)((0, import_node_path62.join)(dir, marker)) && writtenOnThisMachine(root, `${VERITY_DIR}/${marker}`)) return true;
|
|
41205
|
+
}
|
|
41206
|
+
}
|
|
41207
|
+
return false;
|
|
41208
|
+
}
|
|
41209
|
+
async function telemetryPinRefusal(token, endpoint) {
|
|
41210
|
+
const serviceUrl = endpoint.trim().replace(/\/+$/, "").replace(/\/otlp$/, "");
|
|
41211
|
+
const pin = await checkTokenPin(token, serviceUrl);
|
|
41212
|
+
if (pin.attach) return null;
|
|
41213
|
+
if (pin.reason === "mismatch" && sameUrl(deriveOtlpEndpoint(pin.mintedFor), endpoint)) return null;
|
|
41214
|
+
return pinRefusalMessage(pin, serviceUrl);
|
|
41215
|
+
}
|
|
41216
|
+
async function sessionExportEndpoints() {
|
|
41217
|
+
const pick = (env) => OTLP_ENDPOINT_KEYS.map((key) => env[key]).filter((v) => typeof v === "string" && v.trim() !== "");
|
|
41218
|
+
const fromEnv = pick(process.env);
|
|
41219
|
+
if (fromEnv.length > 0) return fromEnv;
|
|
41220
|
+
const read2 = await readSettingsFile(userSettingsPath());
|
|
41221
|
+
return read2.ok ? pick(envOf(read2.settings)) : [];
|
|
41222
|
+
}
|
|
41223
|
+
|
|
41224
|
+
// src/lib/telemetry.ts
|
|
41225
|
+
function buildTelemetryEnv(serviceUrl) {
|
|
41226
|
+
return {
|
|
41227
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
41228
|
+
OTEL_METRICS_EXPORTER: "otlp",
|
|
41229
|
+
OTEL_TRACES_EXPORTER: "otlp",
|
|
41230
|
+
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
41231
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
41232
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
41233
|
+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
41234
|
+
OTEL_LOG_USER_PROMPTS: "0",
|
|
41235
|
+
OTEL_LOG_TOOL_DETAILS: "0",
|
|
41236
|
+
OTEL_LOG_TOOL_CONTENT: "0",
|
|
41237
|
+
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
41238
|
+
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
41239
|
+
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
41240
|
+
OTEL_METRICS_INCLUDE_REPOSITORY: "true",
|
|
41241
|
+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
41242
|
+
};
|
|
41243
|
+
}
|
|
41244
|
+
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
|
|
41245
|
+
var EXPORTER_KEYS = ["OTEL_METRICS_EXPORTER", "OTEL_TRACES_EXPORTER"];
|
|
41246
|
+
function foreignExportKey(settings, endpoint) {
|
|
41247
|
+
const helper = settings.otelHeadersHelper;
|
|
41248
|
+
if (typeof helper === "string" && helper.trim() !== "" && helper !== OTEL_HEADERS_HELPER_CMD) {
|
|
41249
|
+
return "otelHeadersHelper";
|
|
41250
|
+
}
|
|
41251
|
+
const env = envOf(settings);
|
|
41252
|
+
for (const key of OTLP_ENDPOINT_KEYS) {
|
|
41253
|
+
const value = env[key];
|
|
41254
|
+
if (value !== void 0 && value !== "" && !isVerityOtlpEndpoint(value, endpoint)) return key;
|
|
41255
|
+
}
|
|
41256
|
+
if (env[LEGACY_HEADER_KEY] !== void 0 && env[LEGACY_HEADER_KEY] !== "") return LEGACY_HEADER_KEY;
|
|
41257
|
+
for (const key of EXPORTER_KEYS) {
|
|
41258
|
+
const value = env[key];
|
|
41259
|
+
if (value !== void 0 && value !== "" && value !== "otlp") return key;
|
|
41260
|
+
}
|
|
41261
|
+
return null;
|
|
41262
|
+
}
|
|
41263
|
+
async function installTelemetry(serviceUrl, opts = {}) {
|
|
41264
|
+
const path = userSettingsPath();
|
|
41265
|
+
const read2 = await readSettingsFile(path);
|
|
41266
|
+
if (!read2.ok) return { ok: false, error: read2.error };
|
|
41267
|
+
const settings = read2.settings;
|
|
41268
|
+
const env = buildTelemetryEnv(serviceUrl);
|
|
41269
|
+
const foreign = foreignExportKey(settings, env.OTEL_EXPORTER_OTLP_ENDPOINT);
|
|
41270
|
+
if (foreign) {
|
|
41271
|
+
return {
|
|
41272
|
+
ok: false,
|
|
41273
|
+
error: `Telemetry not installed: ${displayPath(path)} already sets ${foreign}, so you export somewhere else already. Nothing was changed.`
|
|
41274
|
+
};
|
|
41275
|
+
}
|
|
41276
|
+
let token = opts.token;
|
|
41277
|
+
if (!token) {
|
|
41278
|
+
const resolved = await resolveToken();
|
|
41279
|
+
if (!resolved.ok) {
|
|
41280
|
+
return { ok: false, error: 'Telemetry not installed: no Verity token on this machine. Run "verity login". Nothing was changed.' };
|
|
41281
|
+
}
|
|
41282
|
+
token = resolved.data.token;
|
|
41283
|
+
}
|
|
41284
|
+
const refusal = await telemetryPinRefusal(token, env.OTEL_EXPORTER_OTLP_ENDPOINT);
|
|
41285
|
+
if (refusal) {
|
|
41286
|
+
return { ok: false, error: `Telemetry not installed: ${refusal} Nothing was changed.` };
|
|
41287
|
+
}
|
|
41288
|
+
settings.env = { ...envOf(settings), ...env };
|
|
41289
|
+
settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
|
|
41290
|
+
await writeJsonFilePreservingStyle(path, settings);
|
|
41291
|
+
const healed = await healLocalTelemetry();
|
|
41292
|
+
return {
|
|
41293
|
+
ok: true,
|
|
41294
|
+
data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT, settingsPath: path, healedLocalKeys: healed.removed }
|
|
41295
|
+
};
|
|
41296
|
+
}
|
|
41297
|
+
async function checkTelemetry() {
|
|
41298
|
+
const path = userSettingsPath();
|
|
41299
|
+
const read2 = await readSettingsFile(path);
|
|
41300
|
+
const settings = read2.ok ? read2.settings : {};
|
|
41301
|
+
const env = envOf(settings);
|
|
41302
|
+
const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT || null;
|
|
41303
|
+
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!endpoint && settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD;
|
|
41304
|
+
return { enabled, endpoint, settingsPath: path, staleLocalKeys: localTelemetryLeftovers() };
|
|
41305
|
+
}
|
|
41306
|
+
async function uninstallTelemetry() {
|
|
41307
|
+
const path = userSettingsPath();
|
|
41308
|
+
const read2 = await readSettingsFile(path);
|
|
41309
|
+
if (!read2.ok) return { ok: false, error: read2.error };
|
|
41310
|
+
const settings = read2.settings;
|
|
41311
|
+
const env = envOf(settings);
|
|
41312
|
+
let removed = 0;
|
|
41313
|
+
const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
41314
|
+
const foreignEndpoint = endpoint !== void 0 && endpoint !== "" && !isVerityOtlpEndpoint(endpoint);
|
|
41315
|
+
if (isVerityOtlpEndpoint(endpoint)) {
|
|
41316
|
+
const verityValues = buildTelemetryEnv("");
|
|
41317
|
+
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
41318
|
+
if (key === "OTEL_EXPORTER_OTLP_ENDPOINT" || env[key] === verityValues[key]) {
|
|
41319
|
+
if (key in env) {
|
|
41320
|
+
delete env[key];
|
|
41321
|
+
removed++;
|
|
41322
|
+
}
|
|
41323
|
+
}
|
|
41324
|
+
}
|
|
41325
|
+
if (settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD) delete settings.otelHeadersHelper;
|
|
41326
|
+
if (settings.env && Object.keys(env).length === 0) delete settings.env;
|
|
41327
|
+
await writeJsonFilePreservingStyle(path, settings);
|
|
41328
|
+
}
|
|
41329
|
+
const healed = await healLocalTelemetry();
|
|
41330
|
+
return { ok: true, data: { removed, settingsPath: path, foreignEndpoint, healedLocalKeys: healed.removed } };
|
|
41331
|
+
}
|
|
41332
|
+
|
|
41169
41333
|
// src/commands/init/steps.ts
|
|
41170
41334
|
function languageShares(d) {
|
|
41171
41335
|
const total = d.languages.reduce((n2, l) => n2 + (d.languageCounts[l] ?? 0), 0);
|
|
@@ -41234,19 +41398,19 @@ async function prerequisitesStep(ui) {
|
|
|
41234
41398
|
const analysis = report.checks.find((c) => c.id === "analysis-cli");
|
|
41235
41399
|
if (analysis && analysis.status !== "ok") {
|
|
41236
41400
|
const s = ui.screen.s;
|
|
41237
|
-
const installed2 = await ui.screen.spin(`${s.gray}\u2502${s.reset} `, () => "
|
|
41401
|
+
const installed2 = await ui.screen.spin(`${s.gray}\u2502${s.reset} `, () => "Verity's copy of the Codacy analysis CLI is missing. Installing it (it runs the Trivy security scan)", installAnalysisCli({ quiet: true, sudo: false }));
|
|
41238
41402
|
report.checks[report.checks.indexOf(analysis)] = installed2;
|
|
41239
41403
|
if (installed2.status === "ok") ui.rail.ticked("Installed the Codacy analysis CLI");
|
|
41240
41404
|
}
|
|
41241
41405
|
for (const c of report.checks) {
|
|
41242
41406
|
if (c.status === "ok") continue;
|
|
41243
|
-
ui.rail.warn(`${c.label}: ${c.detail}
|
|
41244
|
-
if (c.remedy) ui.rail.line(c.remedy);
|
|
41407
|
+
ui.rail.warn(terminalText(`${c.label}: ${c.detail}`, 300));
|
|
41408
|
+
if (c.remedy) ui.rail.line(terminalText(c.remedy, 300));
|
|
41245
41409
|
}
|
|
41246
41410
|
ui.rail.settle();
|
|
41247
41411
|
return !report.checks.some((c) => c.required && c.status !== "ok");
|
|
41248
41412
|
}
|
|
41249
|
-
async function telemetryStep(ui, answer, resumeDeferred, globals) {
|
|
41413
|
+
async function telemetryStep(ui, answer, resumeDeferred, globals, previous) {
|
|
41250
41414
|
if (answer === "already-on") return "enabled";
|
|
41251
41415
|
if (answer === "no") return "declined";
|
|
41252
41416
|
if (answer === "turn-off") {
|
|
@@ -41256,7 +41420,16 @@ async function telemetryStep(ui, answer, resumeDeferred, globals) {
|
|
|
41256
41420
|
ui.rail.settle();
|
|
41257
41421
|
return "declined";
|
|
41258
41422
|
}
|
|
41259
|
-
if (answer
|
|
41423
|
+
if (answer === "not-asked" && !resumeDeferred) {
|
|
41424
|
+
if (!ui.asking || previous === "declined") return void 0;
|
|
41425
|
+
if (!(await resolveToken(globals.token)).ok) return void 0;
|
|
41426
|
+
if ((await checkTelemetry()).enabled) return "enabled";
|
|
41427
|
+
if (!await askTelemetry(ui)) {
|
|
41428
|
+
ui.rail.line("Telemetry stays off. Turn it on later: verity telemetry install", "muted");
|
|
41429
|
+
ui.rail.settle();
|
|
41430
|
+
return "declined";
|
|
41431
|
+
}
|
|
41432
|
+
}
|
|
41260
41433
|
const token = await resolveToken(globals.token);
|
|
41261
41434
|
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41262
41435
|
if (!token.ok || !url.ok) {
|
|
@@ -41273,7 +41446,18 @@ async function telemetryStep(ui, answer, resumeDeferred, globals) {
|
|
|
41273
41446
|
}
|
|
41274
41447
|
ui.rail.gap();
|
|
41275
41448
|
ui.rail.settle();
|
|
41276
|
-
return installed2.ok ? "enabled" :
|
|
41449
|
+
return installed2.ok ? "enabled" : void 0;
|
|
41450
|
+
}
|
|
41451
|
+
async function askTelemetry(ui) {
|
|
41452
|
+
const { rail, screen } = ui;
|
|
41453
|
+
rail.active("Send Claude Code cost and usage from this machine to Verity?");
|
|
41454
|
+
rail.line("Token counts, cost, models, tool names and the repository name, from every Claude Code session on this machine. No prompts, no code, no file contents.");
|
|
41455
|
+
rail.line("See it on the /usage page. Turn it off any time: verity telemetry uninstall");
|
|
41456
|
+
rail.gap();
|
|
41457
|
+
rail.keys([["Enter", "yes"], ["n", "no"]]);
|
|
41458
|
+
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41459
|
+
rail.collapse();
|
|
41460
|
+
return k !== "n";
|
|
41277
41461
|
}
|
|
41278
41462
|
function signInDetail(outcome) {
|
|
41279
41463
|
if (outcome === "already" || outcome === "new") return ["ok", outcome];
|
|
@@ -41324,6 +41508,55 @@ function backgroundStep(ui, opts) {
|
|
|
41324
41508
|
|
|
41325
41509
|
// src/commands/init/scaffold.ts
|
|
41326
41510
|
var import_promises17 = require("node:fs/promises");
|
|
41511
|
+
|
|
41512
|
+
// src/lib/codacy-legacy.ts
|
|
41513
|
+
var import_node_child_process21 = require("node:child_process");
|
|
41514
|
+
var VERITY_ESLINT_CONFIG_FILE = ".codacy/tools-configs/eslint.config.mjs";
|
|
41515
|
+
var VERITY_ESLINT_LOCAL_PATH = "./.codacy/tools-configs/eslint.config.mjs";
|
|
41516
|
+
var VERITY_ESLINT_FIRST_LINE = "// Verity-owned ESLint config for the quality gate.";
|
|
41517
|
+
var MAX_BYTES = 256 * 1024;
|
|
41518
|
+
function classifyCodacyConfig(root = repoRoot()) {
|
|
41519
|
+
const cfg = readRegularFileInRoot(root, CODACY_CONFIG_FILE, MAX_BYTES);
|
|
41520
|
+
if (!cfg.ok) return cfg.reason === "missing" ? "none" : "foreign";
|
|
41521
|
+
let parsed;
|
|
41522
|
+
try {
|
|
41523
|
+
parsed = JSON.parse(cfg.content);
|
|
41524
|
+
} catch {
|
|
41525
|
+
return "foreign";
|
|
41526
|
+
}
|
|
41527
|
+
const tools = typeof parsed === "object" && parsed !== null ? parsed.tools : void 0;
|
|
41528
|
+
const pointsAtVerity2 = Array.isArray(tools) && tools.some((t) => typeof t === "object" && t !== null && t.localConfigurationFile === VERITY_ESLINT_LOCAL_PATH);
|
|
41529
|
+
if (!pointsAtVerity2) return "foreign";
|
|
41530
|
+
const eslint = readRegularFileInRoot(root, VERITY_ESLINT_CONFIG_FILE, MAX_BYTES);
|
|
41531
|
+
return eslint.ok && eslint.content.startsWith(VERITY_ESLINT_FIRST_LINE) ? "verity" : "foreign";
|
|
41532
|
+
}
|
|
41533
|
+
function isTracked(root, path) {
|
|
41534
|
+
try {
|
|
41535
|
+
(0, import_node_child_process21.execFileSync)("git", ["ls-files", "--error-unmatch", "--", path], { cwd: root, stdio: "pipe" });
|
|
41536
|
+
return true;
|
|
41537
|
+
} catch {
|
|
41538
|
+
return false;
|
|
41539
|
+
}
|
|
41540
|
+
}
|
|
41541
|
+
function removeVerityCodacyConfig(root = repoRoot()) {
|
|
41542
|
+
const removed = [];
|
|
41543
|
+
const untracked = [];
|
|
41544
|
+
try {
|
|
41545
|
+
for (const f of [CODACY_CONFIG_FILE, VERITY_ESLINT_CONFIG_FILE]) {
|
|
41546
|
+
if (!isTracked(root, f)) {
|
|
41547
|
+
untracked.push(f);
|
|
41548
|
+
continue;
|
|
41549
|
+
}
|
|
41550
|
+
(0, import_node_child_process21.execFileSync)("git", ["rm", "--quiet", "--", f], { cwd: root, stdio: "pipe" });
|
|
41551
|
+
removed.push(f);
|
|
41552
|
+
}
|
|
41553
|
+
} catch (err2) {
|
|
41554
|
+
return { ok: false, removed, error: err2.message };
|
|
41555
|
+
}
|
|
41556
|
+
return { ok: true, removed, untracked };
|
|
41557
|
+
}
|
|
41558
|
+
|
|
41559
|
+
// src/commands/init/scaffold.ts
|
|
41327
41560
|
async function scaffoldStep(ui, given = {}) {
|
|
41328
41561
|
const { rail } = ui;
|
|
41329
41562
|
await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
|
|
@@ -41351,15 +41584,16 @@ async function scaffoldStep(ui, given = {}) {
|
|
|
41351
41584
|
}
|
|
41352
41585
|
const tracked = committedVerityState();
|
|
41353
41586
|
if (tracked.length > 0) {
|
|
41354
|
-
if (given.untrackState) {
|
|
41587
|
+
if (given.untrackState || ui.asking && await askUntrackState(ui, tracked)) {
|
|
41355
41588
|
const result = untrackVerityState();
|
|
41356
41589
|
if (result === "untracked") rail.line("Untracked the Verity state files (staged). Commit to finish.");
|
|
41357
41590
|
else if (result === "failed") rail.line("Could not untrack them. Run: git rm -r --cached .verity && git add .verity/standard.yaml", "warn");
|
|
41358
41591
|
} else {
|
|
41359
41592
|
rail.warn(`${tracked.length} Verity state file(s) are tracked in git`);
|
|
41360
|
-
rail.line(`For example ${tracked[0]}. Fix: git rm -r --cached .verity && git add .verity/standard.yaml`);
|
|
41593
|
+
rail.line(`For example ${terminalText(tracked[0], 200)}. Fix: git rm -r --cached .verity && git add .verity/standard.yaml`);
|
|
41361
41594
|
}
|
|
41362
41595
|
}
|
|
41596
|
+
retireCodacyConfig(ui);
|
|
41363
41597
|
try {
|
|
41364
41598
|
await ensureClaudeMdPointer();
|
|
41365
41599
|
} catch (err2) {
|
|
@@ -41369,11 +41603,44 @@ async function scaffoldStep(ui, given = {}) {
|
|
|
41369
41603
|
if (cfg.formal_refused) rail.line(`Formal verification: off \u2014 ${formalRefusalMessage(cfg.formal_refused)}`, "warn");
|
|
41370
41604
|
rail.settle();
|
|
41371
41605
|
}
|
|
41606
|
+
async function askUntrackState(ui, tracked) {
|
|
41607
|
+
const { rail, screen } = ui;
|
|
41608
|
+
rail.active(`${tracked.length} Verity state file(s) are tracked in git`);
|
|
41609
|
+
rail.line(`For example ${terminalText(tracked[0], 200)}. They can hold copies of secrets the gate flagged, and each push shares them.`);
|
|
41610
|
+
rail.line("Stop tracking them? The files stay on disk, and the removal is staged for you to commit.");
|
|
41611
|
+
rail.line("Copies already pushed stay in the git history: rotate any secret the gate flagged.");
|
|
41612
|
+
rail.gap();
|
|
41613
|
+
rail.keys([["Enter", "stop tracking them"], ["n", "keep them tracked"]]);
|
|
41614
|
+
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41615
|
+
rail.collapse();
|
|
41616
|
+
return k !== "n";
|
|
41617
|
+
}
|
|
41618
|
+
function retireCodacyConfig(ui) {
|
|
41619
|
+
const kind2 = classifyCodacyConfig();
|
|
41620
|
+
if (kind2 === "none") return;
|
|
41621
|
+
if (kind2 === "foreign") {
|
|
41622
|
+
ui.rail.line("Verity no longer reads .codacy/. Remove it if nothing else uses it.", "muted");
|
|
41623
|
+
return;
|
|
41624
|
+
}
|
|
41625
|
+
const result = removeVerityCodacyConfig();
|
|
41626
|
+
if (result.ok) {
|
|
41627
|
+
if (result.removed.length > 0) {
|
|
41628
|
+
ui.rail.done("Removed the .codacy/ config that an older Verity wrote (staged)");
|
|
41629
|
+
ui.rail.line("Verity runs Trivy from .verity/static-analysis.json now. To keep the files: git restore --staged --worktree .codacy");
|
|
41630
|
+
}
|
|
41631
|
+
if (result.untracked.length > 0) {
|
|
41632
|
+
ui.rail.line(`An older Verity wrote ${result.untracked.join(" and ")}, and Verity no longer uses them. Remove them by hand: rm ${result.untracked.join(" ")}`, "warn");
|
|
41633
|
+
}
|
|
41634
|
+
} else {
|
|
41635
|
+
ui.rail.warn("Could not remove the .codacy/ config that an older Verity wrote");
|
|
41636
|
+
ui.rail.line(terminalText(result.error, 200));
|
|
41637
|
+
}
|
|
41638
|
+
}
|
|
41372
41639
|
|
|
41373
41640
|
// src/commands/init/wiring.ts
|
|
41374
|
-
var
|
|
41641
|
+
var import_node_fs74 = require("node:fs");
|
|
41375
41642
|
var import_promises18 = require("node:fs/promises");
|
|
41376
|
-
var
|
|
41643
|
+
var import_node_path63 = require("node:path");
|
|
41377
41644
|
var DEFAULT_MOMENTS = ["stop", "pre-commit", "pre-push"];
|
|
41378
41645
|
async function momentsFromInstalledHooks() {
|
|
41379
41646
|
const status = await checkAllVerityHooks();
|
|
@@ -41446,12 +41713,12 @@ var SKILLS = [
|
|
|
41446
41713
|
];
|
|
41447
41714
|
function resolveDataDir2() {
|
|
41448
41715
|
const candidates2 = [
|
|
41449
|
-
(0,
|
|
41450
|
-
(0,
|
|
41451
|
-
(0,
|
|
41716
|
+
(0, import_node_path63.join)(__dirname, "..", "data"),
|
|
41717
|
+
(0, import_node_path63.join)(__dirname, "..", "..", "data"),
|
|
41718
|
+
(0, import_node_path63.join)(process.cwd(), "cli", "data")
|
|
41452
41719
|
];
|
|
41453
41720
|
for (const candidate of candidates2) {
|
|
41454
|
-
if ((0,
|
|
41721
|
+
if ((0, import_node_fs74.existsSync)((0, import_node_path63.join)(candidate, "skills"))) return candidate;
|
|
41455
41722
|
}
|
|
41456
41723
|
throw new Error("Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli");
|
|
41457
41724
|
}
|
|
@@ -41459,9 +41726,9 @@ async function skillIsCurrent(src, dest) {
|
|
|
41459
41726
|
const list2 = (dir) => {
|
|
41460
41727
|
const out2 = [];
|
|
41461
41728
|
const walk2 = (d, prefix) => {
|
|
41462
|
-
for (const e of (0,
|
|
41729
|
+
for (const e of (0, import_node_fs74.readdirSync)(d, { withFileTypes: true })) {
|
|
41463
41730
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
41464
|
-
if (e.isDirectory()) walk2((0,
|
|
41731
|
+
if (e.isDirectory()) walk2((0, import_node_path63.join)(d, e.name), rel);
|
|
41465
41732
|
else if (e.isFile()) out2.push(rel);
|
|
41466
41733
|
}
|
|
41467
41734
|
};
|
|
@@ -41472,7 +41739,7 @@ async function skillIsCurrent(src, dest) {
|
|
|
41472
41739
|
const shipped = list2(src);
|
|
41473
41740
|
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
41474
41741
|
for (const rel of shipped) {
|
|
41475
|
-
if (await (0, import_promises18.readFile)((0,
|
|
41742
|
+
if (await (0, import_promises18.readFile)((0, import_node_path63.join)(src, rel), "utf-8") !== await (0, import_promises18.readFile)((0, import_node_path63.join)(dest, rel), "utf-8")) return false;
|
|
41476
41743
|
}
|
|
41477
41744
|
return true;
|
|
41478
41745
|
} catch {
|
|
@@ -41480,16 +41747,16 @@ async function skillIsCurrent(src, dest) {
|
|
|
41480
41747
|
}
|
|
41481
41748
|
}
|
|
41482
41749
|
async function installSkills(force) {
|
|
41483
|
-
const source2 = (0,
|
|
41750
|
+
const source2 = (0, import_node_path63.join)(resolveDataDir2(), "skills");
|
|
41484
41751
|
const missing = [];
|
|
41485
41752
|
for (const skill of SKILLS) {
|
|
41486
|
-
const src = (0,
|
|
41487
|
-
const dest = (0,
|
|
41488
|
-
if (!(0,
|
|
41753
|
+
const src = (0, import_node_path63.join)(source2, skill);
|
|
41754
|
+
const dest = (0, import_node_path63.join)(".claude/skills", skill);
|
|
41755
|
+
if (!(0, import_node_fs74.existsSync)(src)) {
|
|
41489
41756
|
missing.push(skill);
|
|
41490
41757
|
continue;
|
|
41491
41758
|
}
|
|
41492
|
-
if ((0,
|
|
41759
|
+
if ((0, import_node_fs74.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
|
|
41493
41760
|
await (0, import_promises18.mkdir)(dest, { recursive: true });
|
|
41494
41761
|
await (0, import_promises18.cp)(src, dest, { recursive: true, force: true });
|
|
41495
41762
|
}
|
|
@@ -41528,8 +41795,651 @@ async function hooksStep(ui, ctx) {
|
|
|
41528
41795
|
rail.settle();
|
|
41529
41796
|
}
|
|
41530
41797
|
|
|
41798
|
+
// src/commands/init/standard.ts
|
|
41799
|
+
var import_node_fs75 = require("node:fs");
|
|
41800
|
+
|
|
41801
|
+
// src/commands/init/rules.ts
|
|
41802
|
+
async function projectLabel(token, serviceUrl, verbose) {
|
|
41803
|
+
try {
|
|
41804
|
+
const who = await whoami(token, serviceUrl, verbose);
|
|
41805
|
+
return who.ok ? who.data.project_id : null;
|
|
41806
|
+
} catch {
|
|
41807
|
+
return null;
|
|
41808
|
+
}
|
|
41809
|
+
}
|
|
41810
|
+
function profilePreview(profile) {
|
|
41811
|
+
return JSON.stringify(profile, null, 2).split("\n").slice(0, 12).map((l) => terminalText(l, 400)).concat(["\u2026", `The whole profile: ${PROFILE_FILE}`]);
|
|
41812
|
+
}
|
|
41813
|
+
async function rulesOffered(ui, globals) {
|
|
41814
|
+
const token = await resolveToken(globals.token);
|
|
41815
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41816
|
+
if (!token.ok || !url.ok) return false;
|
|
41817
|
+
const status = await generationStatus({ serviceUrl: url.data, token: token.data.token, verbose: globals.verbose });
|
|
41818
|
+
if (status === "off") ui.rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
41819
|
+
if (status === "unsupported") {
|
|
41820
|
+
ui.rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
41821
|
+
}
|
|
41822
|
+
return status === "on" || status === "unknown";
|
|
41823
|
+
}
|
|
41824
|
+
async function askToSend(ui, profile) {
|
|
41825
|
+
const { rail, screen } = ui;
|
|
41826
|
+
const lines = consentLines(profile);
|
|
41827
|
+
rail.active("Write review rules for this repository?");
|
|
41828
|
+
rail.box(lines.slice(1, -1));
|
|
41829
|
+
rail.line(lines[lines.length - 1]);
|
|
41830
|
+
rail.gap();
|
|
41831
|
+
rail.keys([["Enter", "send"], ["v", "view the profile"], ["n", "skip"]]);
|
|
41832
|
+
let allowed = ["enter", "v", "n"];
|
|
41833
|
+
for (; ; ) {
|
|
41834
|
+
const k = await screen.key(allowed, "n", { freshMs: 300 });
|
|
41835
|
+
if (k !== "v") {
|
|
41836
|
+
rail.collapse();
|
|
41837
|
+
return k === "enter";
|
|
41838
|
+
}
|
|
41839
|
+
rail.gap();
|
|
41840
|
+
for (const l of profilePreview(profile)) rail.line(l);
|
|
41841
|
+
rail.gap();
|
|
41842
|
+
rail.keys([["Enter", "send"], ["n", "skip"]]);
|
|
41843
|
+
allowed = ["enter", "n"];
|
|
41844
|
+
}
|
|
41845
|
+
}
|
|
41846
|
+
async function generate(ui, globals, profile, replaces) {
|
|
41847
|
+
const { rail, screen } = ui;
|
|
41848
|
+
const s = screen.s;
|
|
41849
|
+
const token = await resolveToken(globals.token);
|
|
41850
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41851
|
+
if (!token.ok || !url.ok) return { status: "fallback" };
|
|
41852
|
+
const commits = profile.history.commits.length;
|
|
41853
|
+
const fixes = profile.history.commits.filter((c) => c.fix).length;
|
|
41854
|
+
const reverts = profile.history.commits.filter((c) => /^revert\b/i.test(c.subject)).length;
|
|
41855
|
+
rail.active("Writing review rules");
|
|
41856
|
+
await rail.counted(([c, f, r]) => `${s.green}\u2713${s.reset} Read ${c} commits ${s.gray}\xB7${s.reset} ${f} fixes ${s.gray}\xB7${s.reset} ${r} reverts`, [commits, fixes, reverts]);
|
|
41857
|
+
if (profile.docs.length) rail.ticked(`Read ${profile.docs.map((d) => terminalText(d.name, 120)).join(" and ")}`);
|
|
41858
|
+
if (ui.term.screen && !ui.term.motion) rail.line("Writing rules for this repository\u2026");
|
|
41859
|
+
const started = Date.now();
|
|
41860
|
+
const base = buildStandard({ detected: detectProject() }, loadCatalog());
|
|
41861
|
+
const outcome = await screen.spin(`${s.gray}\u2502${s.reset} `, () => "Writing rules for this repository", generateStandard({
|
|
41862
|
+
serviceUrl: url.data,
|
|
41863
|
+
token: token.data.token,
|
|
41864
|
+
profile,
|
|
41865
|
+
base,
|
|
41866
|
+
projectId: await projectLabel(token.data.token, url.data, globals.verbose),
|
|
41867
|
+
verbose: globals.verbose,
|
|
41868
|
+
replaces
|
|
41869
|
+
}));
|
|
41870
|
+
rail.collapse();
|
|
41871
|
+
if (outcome.status === "exists") return { status: "exists" };
|
|
41872
|
+
if (outcome.status === "disabled") {
|
|
41873
|
+
rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
41874
|
+
rail.gap();
|
|
41875
|
+
return { status: "fallback" };
|
|
41876
|
+
}
|
|
41877
|
+
if (outcome.status === "unsupported") {
|
|
41878
|
+
rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
41879
|
+
rail.gap();
|
|
41880
|
+
return { status: "fallback" };
|
|
41881
|
+
}
|
|
41882
|
+
if (outcome.status === "failed") {
|
|
41883
|
+
rail.warn("The rules could not be written this time");
|
|
41884
|
+
rail.line(terminalText(outcome.reason, 300));
|
|
41885
|
+
rail.line("The built-in rules apply. Try again later: verity standard generate");
|
|
41886
|
+
rail.gap();
|
|
41887
|
+
return { status: "fallback" };
|
|
41888
|
+
}
|
|
41889
|
+
const secs2 = Math.max(1, Math.round((Date.now() - started) / 1e3));
|
|
41890
|
+
const docs = profile.docs.length;
|
|
41891
|
+
if (outcome.rules.length === 0) {
|
|
41892
|
+
rail.warn("No project rules this time");
|
|
41893
|
+
rail.line("The proposed rules were too generic, or not safe to use. The built-in rules apply.");
|
|
41894
|
+
} else {
|
|
41895
|
+
const n2 = outcome.rules.length;
|
|
41896
|
+
const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs ? [`${docs} ${docs === 1 ? "doc" : "docs"}`] : []].join(" and ");
|
|
41897
|
+
rail.done(`${n2} ${n2 === 1 ? "rule" : "rules"} for this repository ${s.gray}\xB7 from ${from} \xB7 ${secs2}s${s.reset}`);
|
|
41898
|
+
await rail.rules(outcome.rules);
|
|
41899
|
+
if (outcome.rejected > 0) rail.line(`${outcome.rejected} proposed ${outcome.rejected === 1 ? "rule was" : "rules were"} left out: too generic, or not safe to use.`);
|
|
41900
|
+
}
|
|
41901
|
+
rail.gap();
|
|
41902
|
+
rail.done(`Standard v${outcome.version} saved`);
|
|
41903
|
+
rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
41904
|
+
if (outcome.warning) rail.line(terminalText(outcome.warning, 600), "warn");
|
|
41905
|
+
return { status: "created", version: outcome.version };
|
|
41906
|
+
}
|
|
41907
|
+
|
|
41908
|
+
// src/commands/init/team-standard.ts
|
|
41909
|
+
async function lookUpTeamStandard(opts) {
|
|
41910
|
+
if (!opts.adopt) return { status: "skipped", why: "no-adopt" };
|
|
41911
|
+
const token = await resolveToken(opts.globals.token);
|
|
41912
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
41913
|
+
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
41914
|
+
const found = await fetchRemoteSetup({ token: token.data.token, serviceUrl: url.data, verbose: opts.globals.verbose });
|
|
41915
|
+
if (found.status === "unavailable") return { status: "unavailable", reason: terminalText(found.reason, 300) };
|
|
41916
|
+
if (found.status === "none") return { status: "none" };
|
|
41917
|
+
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
41918
|
+
return { status: "declined-before", version: found.standard.version };
|
|
41919
|
+
}
|
|
41920
|
+
return { status: "found", found, version: found.standard.version };
|
|
41921
|
+
}
|
|
41922
|
+
async function writeTeamStandard(team, opts) {
|
|
41923
|
+
const token = await resolveToken(opts.globals.token);
|
|
41924
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
41925
|
+
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
41926
|
+
const found = team.found;
|
|
41927
|
+
try {
|
|
41928
|
+
const outcome = await adoptRemoteSetup(found, {
|
|
41929
|
+
serviceUrl: url.data,
|
|
41930
|
+
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
41931
|
+
});
|
|
41932
|
+
if (opts.previous?.declinedStandardVersion != null) {
|
|
41933
|
+
await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
|
|
41934
|
+
});
|
|
41935
|
+
}
|
|
41936
|
+
return { status: "adopted", version: found.standard.version, summary: describeRemote(found), notes: outcome.notes };
|
|
41937
|
+
} catch (err2) {
|
|
41938
|
+
return { status: "write-failed", reason: terminalText(err2.message, 300) };
|
|
41939
|
+
}
|
|
41940
|
+
}
|
|
41941
|
+
async function adoptTeamStandard(opts) {
|
|
41942
|
+
const team = await lookUpTeamStandard(opts);
|
|
41943
|
+
return team.status === "found" ? writeTeamStandard(team, opts) : team;
|
|
41944
|
+
}
|
|
41945
|
+
async function askTeamChoice(ui, team) {
|
|
41946
|
+
const { rail, screen } = ui;
|
|
41947
|
+
rail.active("Your team already has a Standard for this repository");
|
|
41948
|
+
rail.line(describeRemote(team.found));
|
|
41949
|
+
rail.line(`Use it to review with the same rules as everyone else. A new one becomes v${team.version + 1} for the whole team.`);
|
|
41950
|
+
rail.gap();
|
|
41951
|
+
rail.keys([["Enter", "use it"], ["n", "create a new one"]]);
|
|
41952
|
+
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41953
|
+
rail.collapse();
|
|
41954
|
+
return k === "n" ? "new" : "use";
|
|
41955
|
+
}
|
|
41956
|
+
function sayAdopted(ui, team) {
|
|
41957
|
+
ui.rail.done(`Using your team's Standard v${team.version}`);
|
|
41958
|
+
ui.rail.line(terminalText(team.summary, 300));
|
|
41959
|
+
ui.rail.line(`Edit it in ${STANDARD_FILE}, then share the change: verity standard push`);
|
|
41960
|
+
for (const note of team.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
41961
|
+
}
|
|
41962
|
+
|
|
41963
|
+
// src/commands/init/standard.ts
|
|
41964
|
+
async function deriveFromCatalogue(ui, globals) {
|
|
41965
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41966
|
+
try {
|
|
41967
|
+
const result = await runSynthesis({ serviceUrl: url.ok ? url.data : void 0 });
|
|
41968
|
+
if ("refused" in result) {
|
|
41969
|
+
ui.rail.warn("No Standard was written");
|
|
41970
|
+
ui.rail.line(terminalText(result.refused, 300));
|
|
41971
|
+
return { ok: false, version: null };
|
|
41972
|
+
}
|
|
41973
|
+
const pushed = await pushStandard(globals);
|
|
41974
|
+
if (pushed.standardVersion !== null) {
|
|
41975
|
+
await correctVerityMdVersion({
|
|
41976
|
+
standard: result.standard,
|
|
41977
|
+
version: pushed.standardVersion,
|
|
41978
|
+
tools: result.tools,
|
|
41979
|
+
serviceUrl: url.ok ? url.data : void 0
|
|
41980
|
+
});
|
|
41981
|
+
ui.rail.done(`Standard v${pushed.standardVersion} saved`);
|
|
41982
|
+
ui.rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
41983
|
+
} else {
|
|
41984
|
+
ui.rail.done("Standard saved on this machine");
|
|
41985
|
+
}
|
|
41986
|
+
for (const line2 of pushed.lines) {
|
|
41987
|
+
if (line2.startsWith("Standard pushed")) continue;
|
|
41988
|
+
if (line2.startsWith("Not signed in")) ui.rail.line(`Sign in to share it with your team: verity login, then verity standard push. Edit it in ${STANDARD_FILE}`);
|
|
41989
|
+
else ui.rail.line(terminalText(line2, 600), "warn");
|
|
41990
|
+
}
|
|
41991
|
+
for (const note of result.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
41992
|
+
return { ok: true, version: pushed.standardVersion };
|
|
41993
|
+
} catch (err2) {
|
|
41994
|
+
ui.rail.warn("No Standard was written");
|
|
41995
|
+
ui.rail.line(terminalText(err2.message, 300));
|
|
41996
|
+
return { ok: false, version: null };
|
|
41997
|
+
}
|
|
41998
|
+
}
|
|
41999
|
+
async function standardStep(ui, ctx) {
|
|
42000
|
+
const { rail } = ui;
|
|
42001
|
+
const s = ui.screen.s;
|
|
42002
|
+
const notAsked = { outcome: "skipped", detail: "none" };
|
|
42003
|
+
const end = (r) => {
|
|
42004
|
+
rail.gap();
|
|
42005
|
+
rail.settle();
|
|
42006
|
+
return r;
|
|
42007
|
+
};
|
|
42008
|
+
const derived = async (profile2) => {
|
|
42009
|
+
const d = await deriveFromCatalogue(ui, ctx.globals);
|
|
42010
|
+
return end({ source: d.ok ? "derived" : "none", version: d.version, profile: profile2 });
|
|
42011
|
+
};
|
|
42012
|
+
if ((0, import_node_fs75.existsSync)(projectPath(STANDARD_FILE))) {
|
|
42013
|
+
const sync = await pushLocalStandardIfMissing(ctx.globals);
|
|
42014
|
+
rail.done("Kept your Standard");
|
|
42015
|
+
rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is. Edit it there, then share the change: verity standard push`);
|
|
42016
|
+
if (sync.status === "pushed") {
|
|
42017
|
+
rail.line(sync.version !== null ? `It was only on this machine. It is shared now as v${sync.version}.` : "It is only on this machine:", sync.version !== null ? "muted" : "warn");
|
|
42018
|
+
for (const line2 of sync.lines) if (!line2.startsWith("Standard pushed")) rail.line(terminalText(line2, 600), "warn");
|
|
42019
|
+
}
|
|
42020
|
+
return end({ source: "kept", version: sync.status === "pushed" ? sync.version : null, profile: notAsked });
|
|
42021
|
+
}
|
|
42022
|
+
const team = await lookUpTeamStandard(ctx);
|
|
42023
|
+
let replaces;
|
|
42024
|
+
if (team.status === "found") {
|
|
42025
|
+
const choice = ctx.standard ?? (ui.asking ? await askTeamChoice(ui, team) : "use");
|
|
42026
|
+
if (choice === "use") {
|
|
42027
|
+
const adopted = await writeTeamStandard(team, ctx);
|
|
42028
|
+
if (adopted.status === "adopted") {
|
|
42029
|
+
sayAdopted(ui, adopted);
|
|
42030
|
+
return end({ source: "adopted", version: adopted.version, profile: notAsked });
|
|
42031
|
+
}
|
|
42032
|
+
rail.warn("Could not write the team's Standard here");
|
|
42033
|
+
if (adopted.status === "write-failed") rail.line(terminalText(adopted.reason, 300));
|
|
42034
|
+
return derived(notAsked);
|
|
42035
|
+
}
|
|
42036
|
+
await writeSetupState({ declinedStandardVersion: team.version }).catch(() => {
|
|
42037
|
+
});
|
|
42038
|
+
rail.done(`Creating a new Standard ${s.gray}\xB7 it becomes v${team.version + 1} for the team${ctx.standard ? " (--standard new)" : ""}${s.reset}`);
|
|
42039
|
+
replaces = team.version;
|
|
42040
|
+
} else if (team.status === "declined-before") {
|
|
42041
|
+
rail.line(`This project turned down the team's Standard v${team.version} before, so this run builds its own.`);
|
|
42042
|
+
return derived(notAsked);
|
|
42043
|
+
}
|
|
42044
|
+
if (team.status === "unavailable") {
|
|
42045
|
+
rail.warn("Could not ask the Verity service for this repository's Standard");
|
|
42046
|
+
rail.line(terminalText(team.reason, 300));
|
|
42047
|
+
return derived(notAsked);
|
|
42048
|
+
}
|
|
42049
|
+
if (team.status === "skipped") return derived(notAsked);
|
|
42050
|
+
if (!await rulesOffered(ui, ctx.globals)) return derived(notAsked);
|
|
42051
|
+
const profile = buildRepoProfile();
|
|
42052
|
+
try {
|
|
42053
|
+
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
42054
|
+
} catch (err2) {
|
|
42055
|
+
rail.warn(`Could not write ${PROFILE_FILE} for you to read, so nothing was sent`);
|
|
42056
|
+
rail.line(terminalText(err2.message, 200));
|
|
42057
|
+
return derived({ outcome: "failed", detail: "error" });
|
|
42058
|
+
}
|
|
42059
|
+
let detail;
|
|
42060
|
+
let send;
|
|
42061
|
+
if (ctx.profile) {
|
|
42062
|
+
send = ctx.profile === "send";
|
|
42063
|
+
detail = "given";
|
|
42064
|
+
} else if (ui.asking) {
|
|
42065
|
+
send = await askToSend(ui, profile);
|
|
42066
|
+
detail = send ? "asked" : "declined";
|
|
42067
|
+
} else {
|
|
42068
|
+
send = false;
|
|
42069
|
+
detail = "unattended";
|
|
42070
|
+
}
|
|
42071
|
+
if (!send) {
|
|
42072
|
+
rail.warn("Profile not sent");
|
|
42073
|
+
rail.line(detail === "unattended" ? "Nobody was asked, so nothing was sent. Your Standard has the built-in rules only." : "Your Standard has the built-in rules only.");
|
|
42074
|
+
rail.line(`Add project rules later: verity standard generate (read ${PROFILE_FILE} first)`);
|
|
42075
|
+
rail.gap();
|
|
42076
|
+
return derived({ outcome: "skipped", detail });
|
|
42077
|
+
}
|
|
42078
|
+
if (detail === "given") rail.done(`Profile sent ${s.gray}\xB7 no source code (--profile send)${s.reset}`);
|
|
42079
|
+
else rail.done(`Profile sent ${s.gray}\xB7 no source code${s.reset}`);
|
|
42080
|
+
rail.gap();
|
|
42081
|
+
rail.settle();
|
|
42082
|
+
const generated = await generate(ui, ctx.globals, profile, replaces);
|
|
42083
|
+
if (generated.status === "created") {
|
|
42084
|
+
return end({ source: "generated", version: generated.version, profile: { outcome: "ok", detail } });
|
|
42085
|
+
}
|
|
42086
|
+
if (generated.status === "exists") {
|
|
42087
|
+
const again = await adoptTeamStandard(ctx);
|
|
42088
|
+
if (again.status === "adopted") {
|
|
42089
|
+
sayAdopted(ui, again);
|
|
42090
|
+
return end({ source: "adopted", version: again.version, profile: { outcome: "ok", detail } });
|
|
42091
|
+
}
|
|
42092
|
+
}
|
|
42093
|
+
return derived({ outcome: "ok", detail });
|
|
42094
|
+
}
|
|
42095
|
+
|
|
42096
|
+
// src/commands/init/finish.ts
|
|
42097
|
+
async function finishRun(ui, ctx) {
|
|
42098
|
+
const { rail } = ui;
|
|
42099
|
+
const s = ui.screen.s;
|
|
42100
|
+
const secs2 = Math.max(1, Math.round((Date.now() - ctx.started) / 1e3));
|
|
42101
|
+
if (!ctx.haveStandard) {
|
|
42102
|
+
sayChanged(ui, ctx.before);
|
|
42103
|
+
rail.end(`${s.yellow}Not finished:${s.reset} no Standard was written, so the gate cannot review.`, "Fix the error above, then run verity init again.");
|
|
42104
|
+
return;
|
|
42105
|
+
}
|
|
42106
|
+
if (ctx.pluginMode) {
|
|
42107
|
+
const version = activePluginVersion();
|
|
42108
|
+
if (version) await writeSetupState({ pluginSetup: { version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, wiring: void 0 });
|
|
42109
|
+
} else if (ctx.noPlugin) {
|
|
42110
|
+
await writeSetupState({ wiring: "project" });
|
|
42111
|
+
}
|
|
42112
|
+
sayChanged(ui, ctx.before);
|
|
42113
|
+
const next = ctx.momentIds.length === 0 ? "Verity reviews nothing until you turn a moment on: verity config moments turn,commit,push" : ctx.pluginMode ? ctx.signedIn ? "Verity reviews from your next turn." : "The security scan runs from your next turn. Sign in for AI review: verity login" : ctx.signedIn ? "Start a new Claude Code session. Verity reviews from its first turn." : "Start a new Claude Code session. The security scan runs from the first turn.";
|
|
42114
|
+
rail.end(`${s.green}${s.bold}Ready${s.reset} ${s.gray}in ${secs2}s.${s.reset}`, next);
|
|
42115
|
+
}
|
|
42116
|
+
function sayChanged(ui, before) {
|
|
42117
|
+
const changed = changedSince(before, snapshotInitFiles());
|
|
42118
|
+
if (changed.length === 0) return;
|
|
42119
|
+
ui.rail.line(`Changed: ${changed.join(", ")}. Details: verity doctor`, "muted");
|
|
42120
|
+
}
|
|
42121
|
+
|
|
42122
|
+
// src/commands/migrate.ts
|
|
42123
|
+
var import_node_fs76 = require("node:fs");
|
|
42124
|
+
var import_node_path64 = require("node:path");
|
|
42125
|
+
var import_node_child_process22 = require("node:child_process");
|
|
42126
|
+
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
42127
|
+
function defaultNpmRemover(pkg) {
|
|
42128
|
+
(0, import_node_child_process22.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
42129
|
+
}
|
|
42130
|
+
function git2(cwd, args2) {
|
|
42131
|
+
(0, import_node_child_process22.execFileSync)("git", hardenedGitArgv(args2), { cwd, stdio: "pipe", env: hardenedGitEnv() });
|
|
42132
|
+
}
|
|
42133
|
+
function isGitTracked(cwd, relPath) {
|
|
42134
|
+
try {
|
|
42135
|
+
git2(cwd, ["ls-files", "--error-unmatch", "--", relPath]);
|
|
42136
|
+
return true;
|
|
42137
|
+
} catch {
|
|
42138
|
+
return false;
|
|
42139
|
+
}
|
|
42140
|
+
}
|
|
42141
|
+
function isGitRepo(cwd) {
|
|
42142
|
+
try {
|
|
42143
|
+
git2(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
42144
|
+
return true;
|
|
42145
|
+
} catch {
|
|
42146
|
+
return false;
|
|
42147
|
+
}
|
|
42148
|
+
}
|
|
42149
|
+
async function runMigration(opts = {}) {
|
|
42150
|
+
const root = opts.cwd ?? repoRoot();
|
|
42151
|
+
const home = opts.home ?? process.env.HOME ?? "";
|
|
42152
|
+
const npmRemover = opts.npmRemover ?? defaultNpmRemover;
|
|
42153
|
+
const actions = [];
|
|
42154
|
+
const movedProjectDir = migrateProjectDir(root, actions);
|
|
42155
|
+
migrateGlobalCredentials(home, actions);
|
|
42156
|
+
await migrateLegacyHooks(root, actions);
|
|
42157
|
+
await migrateClaudeMd(root, actions);
|
|
42158
|
+
migrateStandardFile(root, actions);
|
|
42159
|
+
await migrateLocalTelemetry(root, actions);
|
|
42160
|
+
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
42161
|
+
return { actions, migrated: actions.length > 0 };
|
|
42162
|
+
}
|
|
42163
|
+
function migrateProjectDir(root, actions) {
|
|
42164
|
+
const gateDir = (0, import_node_path64.join)(root, ".gate");
|
|
42165
|
+
const verityDir = (0, import_node_path64.join)(root, ".verity");
|
|
42166
|
+
if ((0, import_node_fs76.existsSync)(gateDir) && !(0, import_node_fs76.existsSync)(verityDir)) {
|
|
42167
|
+
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
42168
|
+
}
|
|
42169
|
+
if ((0, import_node_fs76.existsSync)(gateDir) && (0, import_node_fs76.existsSync)(verityDir)) {
|
|
42170
|
+
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
42171
|
+
}
|
|
42172
|
+
return false;
|
|
42173
|
+
}
|
|
42174
|
+
function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
42175
|
+
let moved = false;
|
|
42176
|
+
if (isGitRepo(root) && isGitTracked(root, ".gate")) {
|
|
42177
|
+
if (hasStagedChanges(root)) {
|
|
42178
|
+
printWarn(
|
|
42179
|
+
"Migrating .gate \u2192 .verity with `git mv` while the index has staged changes \u2014 the rename will be staged alongside them. Review `git status` before committing."
|
|
42180
|
+
);
|
|
42181
|
+
}
|
|
42182
|
+
try {
|
|
42183
|
+
git2(root, ["mv", "--", ".gate", ".verity"]);
|
|
42184
|
+
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
42185
|
+
moved = true;
|
|
42186
|
+
} catch {
|
|
42187
|
+
}
|
|
42188
|
+
}
|
|
42189
|
+
if (moved) {
|
|
42190
|
+
if ((0, import_node_fs76.existsSync)(gateDir)) {
|
|
42191
|
+
const carried = carryLegacyContents(gateDir, verityDir);
|
|
42192
|
+
if (carried > 0) {
|
|
42193
|
+
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
42194
|
+
}
|
|
42195
|
+
try {
|
|
42196
|
+
(0, import_node_fs76.rmSync)(gateDir, { recursive: true, force: true });
|
|
42197
|
+
} catch {
|
|
42198
|
+
}
|
|
42199
|
+
}
|
|
42200
|
+
return true;
|
|
42201
|
+
}
|
|
42202
|
+
moveDir(gateDir, verityDir);
|
|
42203
|
+
actions.push("Moved .gate/ \u2192 .verity/");
|
|
42204
|
+
return true;
|
|
42205
|
+
}
|
|
42206
|
+
function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
42207
|
+
const carried = carryLegacyContents(gateDir, verityDir);
|
|
42208
|
+
if (carried > 0) {
|
|
42209
|
+
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
42210
|
+
}
|
|
42211
|
+
try {
|
|
42212
|
+
(0, import_node_fs76.rmSync)(gateDir, { recursive: true, force: true });
|
|
42213
|
+
} catch {
|
|
42214
|
+
}
|
|
42215
|
+
return carried > 0;
|
|
42216
|
+
}
|
|
42217
|
+
function migrateGlobalCredentials(home, actions) {
|
|
42218
|
+
if (!home) return;
|
|
42219
|
+
const gateCreds = (0, import_node_path64.join)(home, ".gate", "credentials");
|
|
42220
|
+
const verityCreds = (0, import_node_path64.join)(home, ".verity", "credentials");
|
|
42221
|
+
if (!(0, import_node_fs76.existsSync)(gateCreds)) return;
|
|
42222
|
+
if (!(0, import_node_fs76.existsSync)(verityCreds)) {
|
|
42223
|
+
(0, import_node_fs76.mkdirSync)((0, import_node_path64.join)(home, ".verity"), { recursive: true });
|
|
42224
|
+
moveFile(gateCreds, verityCreds);
|
|
42225
|
+
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
42226
|
+
return;
|
|
42227
|
+
}
|
|
42228
|
+
const added = mergeGlobalCredentials(gateCreds, verityCreds);
|
|
42229
|
+
if (added > 0) {
|
|
42230
|
+
actions.push(
|
|
42231
|
+
`Merged ${added} legacy credential line(s) from ~/.gate/credentials into ~/.verity/credentials`
|
|
42232
|
+
);
|
|
42233
|
+
}
|
|
42234
|
+
}
|
|
42235
|
+
async function migrateLegacyHooks(root, actions) {
|
|
42236
|
+
const settings = await readSettingsAt(root);
|
|
42237
|
+
const settingsBefore = JSON.stringify(settings);
|
|
42238
|
+
const updatedSettings = removeVerityHooks(settings);
|
|
42239
|
+
if (settingsBefore !== JSON.stringify(updatedSettings)) {
|
|
42240
|
+
await writeSettingsAt(root, updatedSettings);
|
|
42241
|
+
actions.push("Stripped legacy gate hooks from .claude/settings.json");
|
|
42242
|
+
}
|
|
42243
|
+
}
|
|
42244
|
+
async function migrateClaudeMd(root, actions) {
|
|
42245
|
+
const claudeMd = (0, import_node_path64.join)(root, "CLAUDE.md");
|
|
42246
|
+
const hadLegacyBlock = (0, import_node_fs76.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
42247
|
+
if (!hadLegacyBlock) return;
|
|
42248
|
+
try {
|
|
42249
|
+
await ensureClaudeMdPointer(root);
|
|
42250
|
+
actions.push("Rewrote legacy gate-memory block in CLAUDE.md");
|
|
42251
|
+
} catch (err2) {
|
|
42252
|
+
printWarn(`Could not rewrite CLAUDE.md block: ${err2.message}`);
|
|
42253
|
+
}
|
|
42254
|
+
}
|
|
42255
|
+
function migrateStandardFile(root, actions) {
|
|
42256
|
+
const gateMd = (0, import_node_path64.join)(root, "GATE.md");
|
|
42257
|
+
const verityMd = (0, import_node_path64.join)(root, "VERITY.md");
|
|
42258
|
+
if (!(0, import_node_fs76.existsSync)(gateMd) || (0, import_node_fs76.existsSync)(verityMd)) return;
|
|
42259
|
+
let moved = false;
|
|
42260
|
+
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
42261
|
+
try {
|
|
42262
|
+
git2(root, ["mv", "--", "GATE.md", "VERITY.md"]);
|
|
42263
|
+
moved = true;
|
|
42264
|
+
} catch {
|
|
42265
|
+
}
|
|
42266
|
+
}
|
|
42267
|
+
if (!moved) moveFile(gateMd, verityMd);
|
|
42268
|
+
const content = readFileSyncSafe(verityMd);
|
|
42269
|
+
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
42270
|
+
if (refreshed !== content) (0, import_node_fs76.writeFileSync)(verityMd, refreshed);
|
|
42271
|
+
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
42272
|
+
}
|
|
42273
|
+
async function migrateLocalTelemetry(root, actions) {
|
|
42274
|
+
const { removed } = await healLocalTelemetry(root);
|
|
42275
|
+
if (removed.length === 0) return;
|
|
42276
|
+
actions.push(
|
|
42277
|
+
`Removed ${removed.length} old Verity telemetry key(s) from ${SETTINGS_LOCAL_FILE2} (Claude Code ignores them there). Run "verity telemetry install" to export from this machine.`
|
|
42278
|
+
);
|
|
42279
|
+
}
|
|
42280
|
+
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
42281
|
+
if (!movedProjectDir) return;
|
|
42282
|
+
try {
|
|
42283
|
+
npmRemover(LEGACY_NPM_PACKAGE);
|
|
42284
|
+
actions.push(`Removed legacy global package ${LEGACY_NPM_PACKAGE}`);
|
|
42285
|
+
} catch (err2) {
|
|
42286
|
+
printWarn(
|
|
42287
|
+
`Could not remove ${LEGACY_NPM_PACKAGE} (${err2.message}). Remove it manually: npm rm -g ${LEGACY_NPM_PACKAGE}`
|
|
42288
|
+
);
|
|
42289
|
+
}
|
|
42290
|
+
}
|
|
42291
|
+
function credentialRemoteKey(line2) {
|
|
42292
|
+
const trimmed = line2.trim();
|
|
42293
|
+
if (trimmed === "") return null;
|
|
42294
|
+
const idx = trimmed.indexOf(" token:");
|
|
42295
|
+
return idx === -1 ? trimmed : trimmed.slice(0, idx).trim();
|
|
42296
|
+
}
|
|
42297
|
+
function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
42298
|
+
const verityContent = readFileSyncSafe(verityCreds);
|
|
42299
|
+
const existingKeys = new Set(
|
|
42300
|
+
verityContent.split("\n").map(credentialRemoteKey).filter((k) => k !== null)
|
|
42301
|
+
);
|
|
42302
|
+
const toAppend = [];
|
|
42303
|
+
for (const line2 of readFileSyncSafe(gateCreds).split("\n")) {
|
|
42304
|
+
const key = credentialRemoteKey(line2);
|
|
42305
|
+
if (key === null || existingKeys.has(key)) continue;
|
|
42306
|
+
existingKeys.add(key);
|
|
42307
|
+
toAppend.push(line2.replace(/\r$/, ""));
|
|
42308
|
+
}
|
|
42309
|
+
if (toAppend.length > 0) {
|
|
42310
|
+
const sep7 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
42311
|
+
(0, import_node_fs76.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
|
|
42312
|
+
}
|
|
42313
|
+
(0, import_node_fs76.rmSync)(gateCreds, { force: true });
|
|
42314
|
+
return toAppend.length;
|
|
42315
|
+
}
|
|
42316
|
+
function readFileSyncSafe(path) {
|
|
42317
|
+
try {
|
|
42318
|
+
return (0, import_node_fs76.readFileSync)(path, "utf-8");
|
|
42319
|
+
} catch {
|
|
42320
|
+
return "";
|
|
42321
|
+
}
|
|
42322
|
+
}
|
|
42323
|
+
function hasStagedChanges(root) {
|
|
42324
|
+
try {
|
|
42325
|
+
git2(root, ["diff", "--cached", "--quiet", ...DIFF_READ_FLAGS]);
|
|
42326
|
+
return false;
|
|
42327
|
+
} catch {
|
|
42328
|
+
return true;
|
|
42329
|
+
}
|
|
42330
|
+
}
|
|
42331
|
+
function moveDir(from, to) {
|
|
42332
|
+
try {
|
|
42333
|
+
(0, import_node_fs76.renameSync)(from, to);
|
|
42334
|
+
} catch (err2) {
|
|
42335
|
+
if (err2.code !== "EXDEV") throw err2;
|
|
42336
|
+
(0, import_node_fs76.cpSync)(from, to, { recursive: true });
|
|
42337
|
+
(0, import_node_fs76.rmSync)(from, { recursive: true, force: true });
|
|
42338
|
+
}
|
|
42339
|
+
}
|
|
42340
|
+
function moveFile(from, to) {
|
|
42341
|
+
try {
|
|
42342
|
+
(0, import_node_fs76.renameSync)(from, to);
|
|
42343
|
+
} catch (err2) {
|
|
42344
|
+
if (err2.code !== "EXDEV") throw err2;
|
|
42345
|
+
(0, import_node_fs76.cpSync)(from, to);
|
|
42346
|
+
(0, import_node_fs76.rmSync)(from, { force: true });
|
|
42347
|
+
}
|
|
42348
|
+
}
|
|
42349
|
+
function carryLegacyContents(gateDir, verityDir) {
|
|
42350
|
+
if (!isRealDir(gateDir) || !isRealDir(verityDir)) return 0;
|
|
42351
|
+
let copied = 0;
|
|
42352
|
+
const walk2 = (relDir) => {
|
|
42353
|
+
let entries;
|
|
42354
|
+
try {
|
|
42355
|
+
entries = (0, import_node_fs76.readdirSync)((0, import_node_path64.join)(gateDir, relDir), { withFileTypes: true });
|
|
42356
|
+
} catch {
|
|
42357
|
+
return;
|
|
42358
|
+
}
|
|
42359
|
+
for (const entry of entries) {
|
|
42360
|
+
const rel = relDir ? (0, import_node_path64.join)(relDir, entry.name) : entry.name;
|
|
42361
|
+
if (entry.isSymbolicLink()) continue;
|
|
42362
|
+
if (entry.isDirectory()) {
|
|
42363
|
+
walk2(rel);
|
|
42364
|
+
continue;
|
|
42365
|
+
}
|
|
42366
|
+
if (!entry.isFile() || !ensureRealDirs(verityDir, relDir)) continue;
|
|
42367
|
+
const opened = openRegularInRoot(gateDir, rel);
|
|
42368
|
+
if (!opened.ok) continue;
|
|
42369
|
+
try {
|
|
42370
|
+
(0, import_node_fs76.writeFileSync)((0, import_node_path64.join)(verityDir, rel), readOpened(opened.fd, opened.size), { flag: "wx", mode: 384 });
|
|
42371
|
+
copied++;
|
|
42372
|
+
} catch {
|
|
42373
|
+
}
|
|
42374
|
+
}
|
|
42375
|
+
};
|
|
42376
|
+
walk2("");
|
|
42377
|
+
return copied;
|
|
42378
|
+
}
|
|
42379
|
+
function isRealDir(path) {
|
|
42380
|
+
try {
|
|
42381
|
+
const st = (0, import_node_fs76.lstatSync)(path);
|
|
42382
|
+
return st.isDirectory() && !st.isSymbolicLink();
|
|
42383
|
+
} catch {
|
|
42384
|
+
return false;
|
|
42385
|
+
}
|
|
42386
|
+
}
|
|
42387
|
+
function ensureRealDirs(root, relDir) {
|
|
42388
|
+
let current = root;
|
|
42389
|
+
for (const segment of relDir.split(/[\\/]/).filter(Boolean)) {
|
|
42390
|
+
current = (0, import_node_path64.join)(current, segment);
|
|
42391
|
+
try {
|
|
42392
|
+
(0, import_node_fs76.mkdirSync)(current);
|
|
42393
|
+
} catch (e) {
|
|
42394
|
+
if (e.code !== "EEXIST") return false;
|
|
42395
|
+
}
|
|
42396
|
+
if (!isRealDir(current)) return false;
|
|
42397
|
+
}
|
|
42398
|
+
return true;
|
|
42399
|
+
}
|
|
42400
|
+
async function needsMigration(root = repoRoot()) {
|
|
42401
|
+
const gateDir = (0, import_node_path64.join)(root, ".gate");
|
|
42402
|
+
const verityDir = (0, import_node_path64.join)(root, ".verity");
|
|
42403
|
+
if ((0, import_node_fs76.existsSync)(gateDir) && !(0, import_node_fs76.existsSync)(verityDir)) return true;
|
|
42404
|
+
if ((0, import_node_fs76.existsSync)(gateDir) && (0, import_node_fs76.existsSync)(verityDir)) {
|
|
42405
|
+
if ((0, import_node_fs76.existsSync)((0, import_node_path64.join)(gateDir, "credentials")) && !(0, import_node_fs76.existsSync)((0, import_node_path64.join)(verityDir, "credentials"))) {
|
|
42406
|
+
return true;
|
|
42407
|
+
}
|
|
42408
|
+
if ((0, import_node_fs76.existsSync)((0, import_node_path64.join)(gateDir, "memory")) && !(0, import_node_fs76.existsSync)((0, import_node_path64.join)(verityDir, "memory"))) {
|
|
42409
|
+
return true;
|
|
42410
|
+
}
|
|
42411
|
+
}
|
|
42412
|
+
const claudeMd = (0, import_node_path64.join)(root, "CLAUDE.md");
|
|
42413
|
+
if ((0, import_node_fs76.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
42414
|
+
return true;
|
|
42415
|
+
}
|
|
42416
|
+
if ((0, import_node_fs76.existsSync)((0, import_node_path64.join)(root, "GATE.md")) && !(0, import_node_fs76.existsSync)((0, import_node_path64.join)(root, "VERITY.md"))) {
|
|
42417
|
+
return true;
|
|
42418
|
+
}
|
|
42419
|
+
if (await hasLegacyHooksAt(root)) return true;
|
|
42420
|
+
return false;
|
|
42421
|
+
}
|
|
42422
|
+
function registerMigrateCommand(program2) {
|
|
42423
|
+
program2.command("migrate").description("Migrate a legacy GATE.md install to Verity (.gate \u2192 .verity, hooks, CLAUDE.md)").action(async () => {
|
|
42424
|
+
let result;
|
|
42425
|
+
try {
|
|
42426
|
+
result = await runMigration();
|
|
42427
|
+
} catch (err2) {
|
|
42428
|
+
printError(`Migration failed: ${err2.message}`);
|
|
42429
|
+
process.exitCode = 1;
|
|
42430
|
+
return;
|
|
42431
|
+
}
|
|
42432
|
+
if (!result.migrated) {
|
|
42433
|
+
printInfo("Nothing to migrate \u2014 this project is already on Verity.");
|
|
42434
|
+
return;
|
|
42435
|
+
}
|
|
42436
|
+
printInfo("Migration complete:");
|
|
42437
|
+
for (const a of result.actions) printInfo(` ${a} \u2713`);
|
|
42438
|
+
});
|
|
42439
|
+
}
|
|
42440
|
+
|
|
41531
42441
|
// src/commands/init/sign-in.ts
|
|
41532
|
-
var
|
|
42442
|
+
var import_node_child_process23 = require("node:child_process");
|
|
41533
42443
|
async function classifyExistingLogin(serviceUrl, remote, opts) {
|
|
41534
42444
|
const existing = await resolveToken(opts.token);
|
|
41535
42445
|
if (!existing.ok) return { kind: "none" };
|
|
@@ -41554,7 +42464,7 @@ async function classifyExistingLogin(serviceUrl, remote, opts) {
|
|
|
41554
42464
|
}
|
|
41555
42465
|
function gitRemote() {
|
|
41556
42466
|
try {
|
|
41557
|
-
return (0,
|
|
42467
|
+
return (0, import_node_child_process23.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
41558
42468
|
} catch {
|
|
41559
42469
|
return "";
|
|
41560
42470
|
}
|
|
@@ -41685,299 +42595,54 @@ async function signInStep(ui, resolution, opts) {
|
|
|
41685
42595
|
return end("new");
|
|
41686
42596
|
}
|
|
41687
42597
|
|
|
41688
|
-
// src/commands/init/
|
|
41689
|
-
|
|
41690
|
-
|
|
41691
|
-
// src/commands/init/rules.ts
|
|
41692
|
-
async function projectLabel(token, serviceUrl, verbose) {
|
|
42598
|
+
// src/commands/init/phases.ts
|
|
42599
|
+
async function moveGateInstall(ui) {
|
|
42600
|
+
if (!await needsMigration()) return;
|
|
41693
42601
|
try {
|
|
41694
|
-
const
|
|
41695
|
-
|
|
41696
|
-
|
|
41697
|
-
|
|
41698
|
-
|
|
41699
|
-
}
|
|
41700
|
-
function profilePreview(profile) {
|
|
41701
|
-
return JSON.stringify(profile, null, 2).split("\n").slice(0, 12).map((l) => terminalText(l, 400)).concat(["\u2026", `The whole profile: ${PROFILE_FILE}`]);
|
|
41702
|
-
}
|
|
41703
|
-
async function askToSend(ui, profile) {
|
|
41704
|
-
const { rail, screen } = ui;
|
|
41705
|
-
const lines = consentLines(profile);
|
|
41706
|
-
rail.active("Write review rules for this repository?");
|
|
41707
|
-
rail.box(lines.slice(1, -1));
|
|
41708
|
-
rail.line(lines[lines.length - 1]);
|
|
41709
|
-
rail.gap();
|
|
41710
|
-
rail.keys([["Enter", "send"], ["v", "view the profile"], ["n", "skip"]]);
|
|
41711
|
-
let allowed = ["enter", "v", "n"];
|
|
41712
|
-
for (; ; ) {
|
|
41713
|
-
const k = await screen.key(allowed, "n", { freshMs: 300 });
|
|
41714
|
-
if (k !== "v") {
|
|
41715
|
-
rail.collapse();
|
|
41716
|
-
return k === "enter";
|
|
41717
|
-
}
|
|
41718
|
-
rail.gap();
|
|
41719
|
-
for (const l of profilePreview(profile)) rail.line(l);
|
|
41720
|
-
rail.gap();
|
|
41721
|
-
rail.keys([["Enter", "send"], ["n", "skip"]]);
|
|
41722
|
-
allowed = ["enter", "n"];
|
|
41723
|
-
}
|
|
41724
|
-
}
|
|
41725
|
-
async function generate(ui, globals, profile, replaces) {
|
|
41726
|
-
const { rail, screen } = ui;
|
|
41727
|
-
const s = screen.s;
|
|
41728
|
-
const token = await resolveToken(globals.token);
|
|
41729
|
-
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41730
|
-
if (!token.ok || !url.ok) return { status: "fallback" };
|
|
41731
|
-
const commits = profile.history.commits.length;
|
|
41732
|
-
const fixes = profile.history.commits.filter((c) => c.fix).length;
|
|
41733
|
-
const reverts = profile.history.commits.filter((c) => /^revert\b/i.test(c.subject)).length;
|
|
41734
|
-
rail.active("Writing review rules");
|
|
41735
|
-
await rail.counted(([c, f, r]) => `${s.green}\u2713${s.reset} Read ${c} commits ${s.gray}\xB7${s.reset} ${f} fixes ${s.gray}\xB7${s.reset} ${r} reverts`, [commits, fixes, reverts]);
|
|
41736
|
-
if (profile.docs.length) rail.ticked(`Read ${profile.docs.map((d) => terminalText(d.name, 120)).join(" and ")}`);
|
|
41737
|
-
if (ui.term.screen && !ui.term.motion) rail.line("Writing rules for this repository\u2026");
|
|
41738
|
-
const started = Date.now();
|
|
41739
|
-
const base = buildStandard({ detected: detectProject() }, loadCatalog());
|
|
41740
|
-
const outcome = await screen.spin(`${s.gray}\u2502${s.reset} `, () => "Writing rules for this repository", generateStandard({
|
|
41741
|
-
serviceUrl: url.data,
|
|
41742
|
-
token: token.data.token,
|
|
41743
|
-
profile,
|
|
41744
|
-
base,
|
|
41745
|
-
projectId: await projectLabel(token.data.token, url.data, globals.verbose),
|
|
41746
|
-
verbose: globals.verbose,
|
|
41747
|
-
replaces
|
|
41748
|
-
}));
|
|
41749
|
-
rail.collapse();
|
|
41750
|
-
if (outcome.status === "exists") return { status: "exists" };
|
|
41751
|
-
if (outcome.status === "disabled") {
|
|
41752
|
-
rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
41753
|
-
rail.gap();
|
|
41754
|
-
return { status: "fallback" };
|
|
41755
|
-
}
|
|
41756
|
-
if (outcome.status === "unsupported") {
|
|
41757
|
-
rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
41758
|
-
rail.gap();
|
|
41759
|
-
return { status: "fallback" };
|
|
41760
|
-
}
|
|
41761
|
-
if (outcome.status === "failed") {
|
|
41762
|
-
rail.warn("The rules could not be written this time");
|
|
41763
|
-
rail.line(terminalText(outcome.reason, 300));
|
|
41764
|
-
rail.line("The built-in rules apply. Try again later: verity standard generate");
|
|
41765
|
-
rail.gap();
|
|
41766
|
-
return { status: "fallback" };
|
|
41767
|
-
}
|
|
41768
|
-
const secs2 = Math.max(1, Math.round((Date.now() - started) / 1e3));
|
|
41769
|
-
const docs2 = profile.docs.length;
|
|
41770
|
-
if (outcome.rules.length === 0) {
|
|
41771
|
-
rail.warn("No project rules this time");
|
|
41772
|
-
rail.line("The proposed rules were too generic, or not safe to use. The built-in rules apply.");
|
|
41773
|
-
} else {
|
|
41774
|
-
const n2 = outcome.rules.length;
|
|
41775
|
-
const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs2 ? [`${docs2} ${docs2 === 1 ? "doc" : "docs"}`] : []].join(" and ");
|
|
41776
|
-
rail.done(`${n2} ${n2 === 1 ? "rule" : "rules"} for this repository ${s.gray}\xB7 from ${from} \xB7 ${secs2}s${s.reset}`);
|
|
41777
|
-
await rail.rules(outcome.rules);
|
|
41778
|
-
if (outcome.rejected > 0) rail.line(`${outcome.rejected} proposed ${outcome.rejected === 1 ? "rule was" : "rules were"} left out: too generic, or not safe to use.`);
|
|
41779
|
-
}
|
|
41780
|
-
rail.gap();
|
|
41781
|
-
rail.done(`Standard v${outcome.version} saved`);
|
|
41782
|
-
rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
41783
|
-
if (outcome.warning) rail.line(terminalText(outcome.warning, 600), "warn");
|
|
41784
|
-
return { status: "created", version: outcome.version };
|
|
41785
|
-
}
|
|
41786
|
-
|
|
41787
|
-
// src/commands/init/standard.ts
|
|
41788
|
-
async function lookUpTeamStandard(opts) {
|
|
41789
|
-
if (!opts.adopt) return { status: "skipped", why: "no-adopt" };
|
|
41790
|
-
const token = await resolveToken(opts.globals.token);
|
|
41791
|
-
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
41792
|
-
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
41793
|
-
const found = await fetchRemoteSetup({ token: token.data.token, serviceUrl: url.data, verbose: opts.globals.verbose });
|
|
41794
|
-
if (found.status === "unavailable") return { status: "unavailable", reason: terminalText(found.reason, 300) };
|
|
41795
|
-
if (found.status === "none") return { status: "none" };
|
|
41796
|
-
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
41797
|
-
return { status: "declined-before", version: found.standard.version };
|
|
42602
|
+
const result = await runMigration();
|
|
42603
|
+
ui.rail.done("Moved a GATE.md install to Verity");
|
|
42604
|
+
for (const a of result.actions) ui.rail.line(a);
|
|
42605
|
+
} catch (err2) {
|
|
42606
|
+
ui.rail.warn(`The GATE.md migration failed: ${terminalText(err2.message, 200)}`);
|
|
41798
42607
|
}
|
|
41799
|
-
|
|
42608
|
+
ui.rail.settle();
|
|
41800
42609
|
}
|
|
41801
|
-
async function
|
|
41802
|
-
const token = await resolveToken(opts.globals.token);
|
|
41803
|
-
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
41804
|
-
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
41805
|
-
const found = team.found;
|
|
42610
|
+
async function signInSafely(ui, globals) {
|
|
41806
42611
|
try {
|
|
41807
|
-
|
|
41808
|
-
serviceUrl: url.data,
|
|
41809
|
-
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
41810
|
-
});
|
|
41811
|
-
if (opts.previous?.declinedStandardVersion != null) {
|
|
41812
|
-
await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
|
|
41813
|
-
});
|
|
41814
|
-
}
|
|
41815
|
-
return { status: "adopted", version: found.standard.version, summary: describeRemote(found), notes: outcome.notes };
|
|
42612
|
+
return await signInStep(ui, await resolveServiceUrlForAuth(globals.serviceUrl), { token: globals.token, verbose: globals.verbose, repo: repoLabel() });
|
|
41816
42613
|
} catch (err2) {
|
|
41817
|
-
|
|
42614
|
+
ui.rail.collapse();
|
|
42615
|
+
ui.rail.warn(`Sign-in did not complete: ${terminalText(err2.message, 200)}`);
|
|
42616
|
+
ui.rail.gap();
|
|
42617
|
+
ui.rail.settle();
|
|
42618
|
+
return "failed";
|
|
41818
42619
|
}
|
|
41819
42620
|
}
|
|
41820
|
-
|
|
41821
|
-
|
|
41822
|
-
|
|
42621
|
+
function traceTelemetry(trace, telemetry, answer) {
|
|
42622
|
+
if (telemetry === "enabled") trace.mark("telemetry", "ok", answer === "already-on" ? "already" : "new");
|
|
42623
|
+
else if (telemetry === "declined") trace.mark("telemetry", "skipped", "declined");
|
|
42624
|
+
else if (telemetry === "deferred") trace.mark("telemetry", "failed", "none");
|
|
42625
|
+
else trace.mark("telemetry", "skipped", "unattended");
|
|
41823
42626
|
}
|
|
41824
|
-
async function
|
|
41825
|
-
const { rail, screen } = ui;
|
|
41826
|
-
rail.active("Your team already has a Standard for this repository");
|
|
41827
|
-
rail.line(describeRemote(team.found));
|
|
41828
|
-
rail.line(`Use it to review with the same rules as everyone else. A new one becomes v${team.version + 1} for the whole team.`);
|
|
41829
|
-
rail.gap();
|
|
41830
|
-
rail.keys([["Enter", "use it"], ["n", "create a new one"]]);
|
|
41831
|
-
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41832
|
-
rail.collapse();
|
|
41833
|
-
return k === "n" ? "new" : "use";
|
|
41834
|
-
}
|
|
41835
|
-
function sayAdopted(ui, team) {
|
|
41836
|
-
ui.rail.done(`Using your team's Standard v${team.version}`);
|
|
41837
|
-
ui.rail.line(terminalText(team.summary, 300));
|
|
41838
|
-
for (const note of team.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
41839
|
-
}
|
|
41840
|
-
async function deriveFromCatalogue(ui, globals) {
|
|
41841
|
-
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
42627
|
+
async function recordAnswers(ui, momentIds, telemetry) {
|
|
41842
42628
|
try {
|
|
41843
|
-
|
|
41844
|
-
|
|
41845
|
-
|
|
41846
|
-
|
|
41847
|
-
return { ok: false, version: null };
|
|
41848
|
-
}
|
|
41849
|
-
const pushed = await pushStandard(globals);
|
|
41850
|
-
if (pushed.standardVersion !== null) {
|
|
41851
|
-
await correctVerityMdVersion({
|
|
41852
|
-
standard: result.standard,
|
|
41853
|
-
version: pushed.standardVersion,
|
|
41854
|
-
tools: result.tools,
|
|
41855
|
-
serviceUrl: url.ok ? url.data : void 0
|
|
41856
|
-
});
|
|
41857
|
-
ui.rail.done(`Standard v${pushed.standardVersion} saved`);
|
|
41858
|
-
ui.rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
41859
|
-
} else {
|
|
41860
|
-
ui.rail.done("Standard saved on this machine");
|
|
41861
|
-
}
|
|
41862
|
-
for (const line2 of pushed.lines) {
|
|
41863
|
-
if (line2.startsWith("Standard pushed")) continue;
|
|
41864
|
-
if (line2.startsWith("Not signed in")) ui.rail.line(`Sign in to share it with your team: verity login, then verity standard push. Edit it in ${STANDARD_FILE}`);
|
|
41865
|
-
else ui.rail.line(line2, "warn");
|
|
41866
|
-
}
|
|
41867
|
-
for (const note of result.notes) ui.rail.line(note, "warn");
|
|
41868
|
-
return { ok: true, version: pushed.standardVersion };
|
|
41869
|
-
} catch (err2) {
|
|
41870
|
-
ui.rail.warn("No Standard was written");
|
|
41871
|
-
ui.rail.line(terminalText(err2.message, 300));
|
|
41872
|
-
return { ok: false, version: null };
|
|
41873
|
-
}
|
|
41874
|
-
}
|
|
41875
|
-
async function standardStep(ui, ctx) {
|
|
41876
|
-
const { rail } = ui;
|
|
41877
|
-
const s = ui.screen.s;
|
|
41878
|
-
const notAsked = { outcome: "skipped", detail: "none" };
|
|
41879
|
-
const end = (r) => {
|
|
41880
|
-
rail.gap();
|
|
41881
|
-
rail.settle();
|
|
41882
|
-
return r;
|
|
41883
|
-
};
|
|
41884
|
-
const derived = async (profile2) => {
|
|
41885
|
-
const d = await deriveFromCatalogue(ui, ctx.globals);
|
|
41886
|
-
return end({ source: d.ok ? "derived" : "none", version: d.version, profile: profile2 });
|
|
41887
|
-
};
|
|
41888
|
-
if ((0, import_node_fs73.existsSync)(projectPath(STANDARD_FILE))) {
|
|
41889
|
-
const sync = await pushLocalStandardIfMissing(ctx.globals);
|
|
41890
|
-
rail.done("Kept your Standard");
|
|
41891
|
-
rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is.`);
|
|
41892
|
-
if (sync.status === "pushed") {
|
|
41893
|
-
rail.line(sync.version !== null ? `It was only on this machine. It is shared now as v${sync.version}.` : "It is only on this machine:", sync.version !== null ? "muted" : "warn");
|
|
41894
|
-
for (const line2 of sync.lines) if (!line2.startsWith("Standard pushed")) rail.line(line2, "warn");
|
|
41895
|
-
}
|
|
41896
|
-
return end({ source: "kept", version: sync.status === "pushed" ? sync.version : null, profile: notAsked });
|
|
41897
|
-
}
|
|
41898
|
-
const team = await lookUpTeamStandard(ctx);
|
|
41899
|
-
let replaces;
|
|
41900
|
-
if (team.status === "found") {
|
|
41901
|
-
const choice = ctx.standard ?? (ui.asking ? await askTeamChoice(ui, team) : "use");
|
|
41902
|
-
if (choice === "use") {
|
|
41903
|
-
const adopted = await writeTeamStandard(team, ctx);
|
|
41904
|
-
if (adopted.status === "adopted") {
|
|
41905
|
-
sayAdopted(ui, adopted);
|
|
41906
|
-
return end({ source: "adopted", version: adopted.version, profile: notAsked });
|
|
41907
|
-
}
|
|
41908
|
-
rail.warn("Could not write the team's Standard here");
|
|
41909
|
-
if (adopted.status === "write-failed") rail.line(adopted.reason);
|
|
41910
|
-
return derived(notAsked);
|
|
41911
|
-
}
|
|
41912
|
-
await writeSetupState({ declinedStandardVersion: team.version }).catch(() => {
|
|
42629
|
+
await writeSetupState({
|
|
42630
|
+
moments: momentIds,
|
|
42631
|
+
...telemetry ? { telemetry } : {},
|
|
42632
|
+
init: { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.0-experimental.3a13a5e" : "dev" }
|
|
41913
42633
|
});
|
|
41914
|
-
rail.done(`Creating a new Standard ${s.gray}\xB7 it becomes v${team.version + 1} for the team${ctx.standard ? " (--standard new)" : ""}${s.reset}`);
|
|
41915
|
-
replaces = team.version;
|
|
41916
|
-
} else if (team.status === "declined-before") {
|
|
41917
|
-
rail.line(`This project turned down the team's Standard v${team.version} before, so this run builds its own.`);
|
|
41918
|
-
return derived(notAsked);
|
|
41919
|
-
}
|
|
41920
|
-
if (team.status === "unavailable") {
|
|
41921
|
-
rail.warn("Could not ask the Verity service for this repository's Standard");
|
|
41922
|
-
rail.line(team.reason);
|
|
41923
|
-
return derived(notAsked);
|
|
41924
|
-
}
|
|
41925
|
-
if (team.status === "skipped") return derived(notAsked);
|
|
41926
|
-
const profile = buildRepoProfile();
|
|
41927
|
-
try {
|
|
41928
|
-
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
41929
42634
|
} catch (err2) {
|
|
41930
|
-
rail.
|
|
41931
|
-
rail.line(terminalText(err2.message, 200));
|
|
41932
|
-
return derived({ outcome: "failed", detail: "error" });
|
|
42635
|
+
ui.rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
|
|
41933
42636
|
}
|
|
41934
|
-
let detail;
|
|
41935
|
-
let send;
|
|
41936
|
-
if (ctx.profile) {
|
|
41937
|
-
send = ctx.profile === "send";
|
|
41938
|
-
detail = "given";
|
|
41939
|
-
} else if (ui.asking) {
|
|
41940
|
-
send = await askToSend(ui, profile);
|
|
41941
|
-
detail = send ? "asked" : "declined";
|
|
41942
|
-
} else {
|
|
41943
|
-
send = false;
|
|
41944
|
-
detail = "unattended";
|
|
41945
|
-
}
|
|
41946
|
-
if (!send) {
|
|
41947
|
-
rail.warn("Profile not sent");
|
|
41948
|
-
rail.line(detail === "unattended" ? "Nobody was asked, so nothing was sent. Your Standard has the built-in rules only." : "Your Standard has the built-in rules only.");
|
|
41949
|
-
rail.line(`Add project rules later: verity standard generate (read ${PROFILE_FILE} first)`);
|
|
41950
|
-
rail.gap();
|
|
41951
|
-
return derived({ outcome: "skipped", detail });
|
|
41952
|
-
}
|
|
41953
|
-
if (detail === "given") rail.done(`Profile sent ${s.gray}\xB7 no source code (--profile send)${s.reset}`);
|
|
41954
|
-
else rail.done(`Profile sent ${s.gray}\xB7 no source code${s.reset}`);
|
|
41955
|
-
rail.gap();
|
|
41956
|
-
rail.settle();
|
|
41957
|
-
const generated = await generate(ui, ctx.globals, profile, replaces);
|
|
41958
|
-
if (generated.status === "created") {
|
|
41959
|
-
return end({ source: "generated", version: generated.version, profile: { outcome: "ok", detail } });
|
|
41960
|
-
}
|
|
41961
|
-
if (generated.status === "exists") {
|
|
41962
|
-
const again = await adoptTeamStandard(ctx);
|
|
41963
|
-
if (again.status === "adopted") {
|
|
41964
|
-
sayAdopted(ui, again);
|
|
41965
|
-
return end({ source: "adopted", version: again.version, profile: { outcome: "ok", detail } });
|
|
41966
|
-
}
|
|
41967
|
-
}
|
|
41968
|
-
return derived({ outcome: "ok", detail });
|
|
41969
42637
|
}
|
|
41970
42638
|
|
|
41971
42639
|
// src/commands/init/index.ts
|
|
41972
|
-
|
|
41973
|
-
const started = Date.now();
|
|
41974
|
-
const globals = program2.opts();
|
|
41975
|
-
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
42640
|
+
function parseInitFlags(opts) {
|
|
41976
42641
|
let givenMoments;
|
|
41977
42642
|
if (opts.moments != null) {
|
|
41978
42643
|
const parsed = parseMomentSelection(String(opts.moments));
|
|
41979
42644
|
if ("error" in parsed) {
|
|
41980
|
-
printError(parsed.error);
|
|
42645
|
+
printError(terminalText(parsed.error, 300));
|
|
41981
42646
|
process.exit(1);
|
|
41982
42647
|
}
|
|
41983
42648
|
givenMoments = parsed.moments;
|
|
@@ -41986,7 +42651,7 @@ async function runInit(program2, opts) {
|
|
|
41986
42651
|
if (opts.telemetry != null) {
|
|
41987
42652
|
const parsed = parseTelemetryAnswer(String(opts.telemetry));
|
|
41988
42653
|
if (parsed === null) {
|
|
41989
|
-
printError(`--telemetry: unrecognised "${opts.telemetry}". Valid: on, off.`);
|
|
42654
|
+
printError(`--telemetry: unrecognised "${terminalText(String(opts.telemetry), 60)}". Valid: on, off.`);
|
|
41990
42655
|
process.exit(1);
|
|
41991
42656
|
}
|
|
41992
42657
|
givenTelemetry = parsed;
|
|
@@ -41995,7 +42660,7 @@ async function runInit(program2, opts) {
|
|
|
41995
42660
|
if (opts.profile != null) {
|
|
41996
42661
|
const v = String(opts.profile).trim().toLowerCase();
|
|
41997
42662
|
if (v !== "send" && v !== "skip") {
|
|
41998
|
-
printError(`--profile: unrecognised "${opts.profile}". Valid: send, skip.`);
|
|
42663
|
+
printError(`--profile: unrecognised "${terminalText(String(opts.profile), 60)}". Valid: send, skip.`);
|
|
41999
42664
|
process.exit(1);
|
|
42000
42665
|
}
|
|
42001
42666
|
profileAnswer = v;
|
|
@@ -42004,16 +42669,24 @@ async function runInit(program2, opts) {
|
|
|
42004
42669
|
if (opts.standard != null) {
|
|
42005
42670
|
const v = String(opts.standard).trim().toLowerCase();
|
|
42006
42671
|
if (v !== "use" && v !== "new") {
|
|
42007
|
-
printError(`--standard: unrecognised "${opts.standard}". Valid: use, new.`);
|
|
42672
|
+
printError(`--standard: unrecognised "${terminalText(String(opts.standard), 60)}". Valid: use, new.`);
|
|
42008
42673
|
process.exit(1);
|
|
42009
42674
|
}
|
|
42010
42675
|
standardAnswer = v;
|
|
42011
42676
|
}
|
|
42677
|
+
return { givenMoments, givenTelemetry, profileAnswer, standardAnswer };
|
|
42678
|
+
}
|
|
42679
|
+
async function runInit(program2, opts) {
|
|
42680
|
+
const started = Date.now();
|
|
42681
|
+
const globals = program2.opts();
|
|
42682
|
+
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
42683
|
+
const { givenMoments, givenTelemetry, profileAnswer, standardAnswer } = parseInitFlags(opts);
|
|
42012
42684
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
42013
|
-
if (!projectMarkers.some((m) => (0,
|
|
42685
|
+
if (!projectMarkers.some((m) => (0, import_node_fs77.existsSync)(m))) {
|
|
42014
42686
|
printError('No project detected in the current directory. Run "verity init" from your project root.');
|
|
42015
42687
|
process.exit(1);
|
|
42016
42688
|
}
|
|
42689
|
+
const before = snapshotInitFiles();
|
|
42017
42690
|
const stale = clearStalePluginMarker();
|
|
42018
42691
|
const pluginMode = opts.plugin === false ? false : opts.pluginMode ?? pluginActiveHere();
|
|
42019
42692
|
const install = pluginMode ? activePluginInstall() : null;
|
|
@@ -42026,16 +42699,7 @@ async function runInit(program2, opts) {
|
|
|
42026
42699
|
await wordmark(ui.screen, { reveal: firstInitOnMachine() });
|
|
42027
42700
|
rail.open(repoLabel());
|
|
42028
42701
|
if (opts.intensity != null) rail.line("--intensity is ignored: static analysis is Trivy only.", "warn");
|
|
42029
|
-
|
|
42030
|
-
try {
|
|
42031
|
-
const result = await runMigration();
|
|
42032
|
-
rail.done("Moved a GATE.md install to Verity");
|
|
42033
|
-
for (const a of result.actions) rail.line(a);
|
|
42034
|
-
} catch (err2) {
|
|
42035
|
-
rail.warn(`The GATE.md migration failed: ${terminalText(err2.message, 200)}`);
|
|
42036
|
-
}
|
|
42037
|
-
rail.settle();
|
|
42038
|
-
}
|
|
42702
|
+
await moveGateInstall(ui);
|
|
42039
42703
|
if (!await prerequisitesStep(ui)) {
|
|
42040
42704
|
rail.end("Stopped: a required tool is missing.", "Install it, then run verity init again.");
|
|
42041
42705
|
trace.mark("prerequisites", "failed", "blocked");
|
|
@@ -42046,10 +42710,10 @@ async function runInit(program2, opts) {
|
|
|
42046
42710
|
trace.mark("prerequisites", "ok");
|
|
42047
42711
|
await scaffoldStep(ui, { untrackMemory: opts.untrackMemory === true, untrackState: opts.untrackState === true });
|
|
42048
42712
|
trace.mark("scaffold", "ok");
|
|
42049
|
-
await (0, import_promises19.mkdir)((0,
|
|
42713
|
+
await (0, import_promises19.mkdir)((0, import_node_path65.join)(process.env.HOME ?? "", ".verity"), { recursive: true });
|
|
42050
42714
|
await repoStep(ui, detectProject());
|
|
42051
42715
|
const previous = await readSetupState();
|
|
42052
|
-
const setUpBefore = (0,
|
|
42716
|
+
const setUpBefore = (0, import_node_fs77.existsSync)(projectPath(STANDARD_FILE));
|
|
42053
42717
|
const answers = await askSetupQuestions(defaultsOnly, previous, { moments: givenMoments, telemetry: givenTelemetry });
|
|
42054
42718
|
trace.mark("questions", "ok", givenMoments || givenTelemetry !== void 0 || profileAnswer || standardAnswer ? "asked" : "defaults");
|
|
42055
42719
|
const plan = planMoments(answers.moments, { given: givenMoments !== void 0, defaultsOnly });
|
|
@@ -42065,66 +42729,40 @@ async function runInit(program2, opts) {
|
|
|
42065
42729
|
force: opts.force ?? false
|
|
42066
42730
|
});
|
|
42067
42731
|
trace.mark("hooks", "ok", pluginMode ? "plugin" : "project");
|
|
42068
|
-
|
|
42069
|
-
try {
|
|
42070
|
-
signIn = await signInStep(ui, await resolveServiceUrlForAuth(globals.serviceUrl), { token: globals.token, verbose: globals.verbose, repo: repoLabel() });
|
|
42071
|
-
} catch (err2) {
|
|
42072
|
-
rail.collapse();
|
|
42073
|
-
rail.warn(`Sign-in did not complete: ${terminalText(err2.message, 200)}`);
|
|
42074
|
-
rail.gap();
|
|
42075
|
-
rail.settle();
|
|
42076
|
-
signIn = "failed";
|
|
42077
|
-
}
|
|
42732
|
+
const signIn = await signInSafely(ui, globals);
|
|
42078
42733
|
trace.mark("sign_in", ...signInDetail(signIn));
|
|
42079
42734
|
const signedIn = signIn === "already" || signIn === "new" || signIn === "unreachable";
|
|
42080
|
-
const telemetry = await telemetryStep(ui, answers.telemetry, answers.telemetry === "not-asked" && previous?.telemetry === "deferred", globals);
|
|
42081
|
-
|
|
42082
|
-
else if (telemetry === "declined") trace.mark("telemetry", "skipped", "declined");
|
|
42083
|
-
else if (telemetry === "deferred") trace.mark("telemetry", "failed", "none");
|
|
42084
|
-
else trace.mark("telemetry", "skipped", "unattended");
|
|
42735
|
+
const telemetry = await telemetryStep(ui, answers.telemetry, answers.telemetry === "not-asked" && previous?.telemetry === "deferred", globals, previous?.telemetry);
|
|
42736
|
+
traceTelemetry(trace, telemetry, answers.telemetry);
|
|
42085
42737
|
const standard = await standardStep(ui, { globals, previous, adopt: opts.adopt !== false, profile: profileAnswer, standard: standardAnswer });
|
|
42086
42738
|
trace.mark("profile", standard.profile.outcome, standard.profile.detail);
|
|
42087
42739
|
const haveStandard = standard.source !== "none";
|
|
42088
42740
|
trace.mark("standard", haveStandard ? "ok" : "failed", standard.source);
|
|
42089
42741
|
const formalNew = formalDefault(previous, { setUpBefore });
|
|
42090
42742
|
backgroundStep(ui, { cli: process.argv[1], formalNew });
|
|
42091
|
-
|
|
42092
|
-
await writeSetupState({
|
|
42093
|
-
// The effective answer: the next run starts from this record.
|
|
42094
|
-
moments: plan.momentIds,
|
|
42095
|
-
...telemetry ? { telemetry } : {},
|
|
42096
|
-
init: { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.0-experimental.3211f79" : "dev" }
|
|
42097
|
-
});
|
|
42098
|
-
} catch (err2) {
|
|
42099
|
-
rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
|
|
42100
|
-
}
|
|
42743
|
+
await recordAnswers(ui, plan.momentIds, telemetry);
|
|
42101
42744
|
trace.mark("completed", haveStandard ? "ok" : "failed", haveStandard ? void 0 : "none");
|
|
42102
42745
|
await trace.settle();
|
|
42103
|
-
|
|
42104
|
-
|
|
42105
|
-
|
|
42106
|
-
|
|
42107
|
-
|
|
42108
|
-
|
|
42109
|
-
|
|
42110
|
-
|
|
42111
|
-
|
|
42112
|
-
await writeSetupState({ wiring: "project" });
|
|
42113
|
-
}
|
|
42114
|
-
const next = plan.momentIds.length === 0 ? "Verity reviews nothing until you turn a moment on: verity config moments turn,commit,push" : pluginMode ? signedIn ? "Verity reviews from your next turn." : "The security scan runs from your next turn. Sign in for AI review: verity login" : signedIn ? "Start a new Claude Code session. Verity reviews from its first turn." : "Start a new Claude Code session. The security scan runs from the first turn.";
|
|
42115
|
-
rail.end(`${s.green}${s.bold}Ready${s.reset} ${s.gray}in ${secs2}s.${s.reset}`, next);
|
|
42116
|
-
}
|
|
42746
|
+
await finishRun(ui, {
|
|
42747
|
+
started,
|
|
42748
|
+
before,
|
|
42749
|
+
haveStandard,
|
|
42750
|
+
pluginMode,
|
|
42751
|
+
noPlugin: opts.plugin === false,
|
|
42752
|
+
signedIn,
|
|
42753
|
+
momentIds: plan.momentIds
|
|
42754
|
+
});
|
|
42117
42755
|
markInitSeen();
|
|
42118
42756
|
} finally {
|
|
42119
42757
|
ui.screen.release();
|
|
42120
42758
|
}
|
|
42121
42759
|
}
|
|
42122
42760
|
function registerInitCommand(program2) {
|
|
42123
|
-
program2.command("init").alias("setup").description("Set up Verity in this project:
|
|
42761
|
+
program2.command("init").alias("setup").description("Set up Verity in this project: sign in, telemetry, and the Standard; every other choice is a stated default").option("--force", "Reinstall the skills even when they are up to date (hooks are always reconciled)").option("-y, --yes", "Ask nothing: skip sign-in unless already signed in, and send the profile only with --profile send").option("--profile <send|skip>", "Answer the repo-profile question without asking (the plugin asks in the session)").option("--standard <use|new>", "When the team already has a Standard: use it, or create a new one (the plugin asks in the session; unattended runs use it)").option("--no-setup", "Ignored: init no longer hands off to /verity-setup (kept for old scripts)").option("--plugin-mode", "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has").option("--no-plugin", "Ignore any Claude Code plugin here and wire this project's own skills and hooks").option("--no-adopt", "Don't use this repository's Standard from the service; build a new one").option("--moments <list>", 'Set when Verity reviews without the default: stop,pre-commit,pre-push \u2014 or "none"').option("--intensity <level>", "Ignored: static analysis is Trivy only (kept so older plugin skills keep working)").option("--telemetry <on|off>", "Answer the telemetry question without asking: send cost & usage from this machine, or not").option("--untrack-memory", "Stop tracking the knowledge graph in git (init does not ask)").option("--untrack-state", "Untrack committed Verity state without asking (a terminal run asks when it finds some)").action((opts) => runInit(program2, opts));
|
|
42124
42762
|
}
|
|
42125
42763
|
|
|
42126
42764
|
// src/commands/doctor.ts
|
|
42127
|
-
var
|
|
42765
|
+
var import_node_fs78 = require("node:fs");
|
|
42128
42766
|
async function buildReport() {
|
|
42129
42767
|
const prereqs = await checkPrereqs({ install: false });
|
|
42130
42768
|
const state = await readSetupState();
|
|
@@ -42133,8 +42771,8 @@ async function buildReport() {
|
|
|
42133
42771
|
const provenance = cliProvenance();
|
|
42134
42772
|
const telemetry = await checkTelemetry();
|
|
42135
42773
|
const artifacts = {
|
|
42136
|
-
standard: (0,
|
|
42137
|
-
verityMd: (0,
|
|
42774
|
+
standard: (0, import_node_fs78.existsSync)(projectPath(STANDARD_FILE)),
|
|
42775
|
+
verityMd: (0, import_node_fs78.existsSync)(projectPath(VERITY_MD_FILE))
|
|
42138
42776
|
};
|
|
42139
42777
|
const next = [];
|
|
42140
42778
|
for (const c of prereqs.checks) {
|
|
@@ -42173,6 +42811,11 @@ async function buildReport() {
|
|
|
42173
42811
|
if (state?.telemetry === "deferred") {
|
|
42174
42812
|
next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
|
|
42175
42813
|
}
|
|
42814
|
+
if (telemetry.staleLocalKeys.length > 0) {
|
|
42815
|
+
next.push(
|
|
42816
|
+
`.claude/settings.local.json still holds ${telemetry.staleLocalKeys.length} old Verity telemetry key(s). Claude Code 2.1.282 and later ignores them there, so nothing is exported from them. Run "verity telemetry install" to export from this machine instead (it removes them), or "verity telemetry uninstall" to turn telemetry off and remove them.`
|
|
42817
|
+
);
|
|
42818
|
+
}
|
|
42176
42819
|
const memory2 = {
|
|
42177
42820
|
posture: memoryPosture(),
|
|
42178
42821
|
trackedFiles: committedMemoryFiles().length,
|
|
@@ -42220,7 +42863,7 @@ async function buildReport() {
|
|
|
42220
42863
|
noAnalysisMoment,
|
|
42221
42864
|
versions
|
|
42222
42865
|
},
|
|
42223
|
-
telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
|
|
42866
|
+
telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint, staleLocalKeys: telemetry.staleLocalKeys },
|
|
42224
42867
|
artifacts,
|
|
42225
42868
|
memory: memory2,
|
|
42226
42869
|
trackedState,
|
|
@@ -42243,6 +42886,7 @@ function registerDoctorCommand(program2) {
|
|
|
42243
42886
|
for (const c of report.prerequisites) {
|
|
42244
42887
|
if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
42245
42888
|
else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
|
|
42889
|
+
if (c.unused) printInfo(` ${c.unused}: not used (Verity runs its bundled copy)`);
|
|
42246
42890
|
}
|
|
42247
42891
|
printInfo("Setup:");
|
|
42248
42892
|
printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
|
|
@@ -42264,7 +42908,9 @@ function registerDoctorCommand(program2) {
|
|
|
42264
42908
|
if (cfg.formal_refused) printWarn(` ${formalRefusalMessage(cfg.formal_refused)}`);
|
|
42265
42909
|
}
|
|
42266
42910
|
printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
|
|
42267
|
-
printInfo(
|
|
42911
|
+
printInfo(
|
|
42912
|
+
`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint} (every Claude Code session on this machine)` : "disabled"}`
|
|
42913
|
+
);
|
|
42268
42914
|
if (process.platform === "win32") printWarn(WINDOWS_STATIC_NOTE);
|
|
42269
42915
|
else printInfo("Static analysis: Trivy (dependencies, malicious packages, secrets)");
|
|
42270
42916
|
printInfo("Artifacts:");
|
|
@@ -42285,8 +42931,8 @@ function registerDoctorCommand(program2) {
|
|
|
42285
42931
|
}
|
|
42286
42932
|
|
|
42287
42933
|
// src/commands/uninstall.ts
|
|
42288
|
-
var
|
|
42289
|
-
var
|
|
42934
|
+
var import_node_fs79 = require("node:fs");
|
|
42935
|
+
var import_node_path66 = require("node:path");
|
|
42290
42936
|
function registerUninstallCommand(program2) {
|
|
42291
42937
|
program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
|
|
42292
42938
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -42295,11 +42941,11 @@ function registerUninstallCommand(program2) {
|
|
|
42295
42941
|
const actions = [];
|
|
42296
42942
|
const skillsRoot = projectPath(".claude/skills");
|
|
42297
42943
|
for (const name2 of PROJECT_SKILL_NAMES) {
|
|
42298
|
-
const dir = (0,
|
|
42299
|
-
if ((0,
|
|
42944
|
+
const dir = (0, import_node_path66.join)(skillsRoot, name2);
|
|
42945
|
+
if ((0, import_node_fs79.existsSync)(dir)) {
|
|
42300
42946
|
actions.push({
|
|
42301
42947
|
label: `Remove .claude/skills/${name2}/`,
|
|
42302
|
-
apply: () => (0,
|
|
42948
|
+
apply: () => (0, import_node_fs79.rmSync)(dir, { recursive: true, force: true })
|
|
42303
42949
|
});
|
|
42304
42950
|
}
|
|
42305
42951
|
}
|
|
@@ -42313,24 +42959,24 @@ function registerUninstallCommand(program2) {
|
|
|
42313
42959
|
});
|
|
42314
42960
|
}
|
|
42315
42961
|
const verityDir = projectPath(VERITY_DIR);
|
|
42316
|
-
if ((0,
|
|
42962
|
+
if ((0, import_node_fs79.existsSync)(verityDir)) {
|
|
42317
42963
|
actions.push({
|
|
42318
42964
|
label: `Remove ${VERITY_DIR}/`,
|
|
42319
|
-
apply: () => (0,
|
|
42965
|
+
apply: () => (0, import_node_fs79.rmSync)(verityDir, { recursive: true, force: true })
|
|
42320
42966
|
});
|
|
42321
42967
|
}
|
|
42322
42968
|
if (!keepVerityMd) {
|
|
42323
42969
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
42324
|
-
if ((0,
|
|
42970
|
+
if ((0, import_node_fs79.existsSync)(verityMd)) {
|
|
42325
42971
|
actions.push({
|
|
42326
42972
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
42327
|
-
apply: () => (0,
|
|
42973
|
+
apply: () => (0, import_node_fs79.rmSync)(verityMd, { force: true })
|
|
42328
42974
|
});
|
|
42329
42975
|
}
|
|
42330
42976
|
}
|
|
42331
42977
|
const cleanupEmptyDir = (path) => {
|
|
42332
|
-
if ((0,
|
|
42333
|
-
(0,
|
|
42978
|
+
if ((0, import_node_fs79.existsSync)(path) && (0, import_node_fs79.statSync)(path).isDirectory() && (0, import_node_fs79.readdirSync)(path).length === 0) {
|
|
42979
|
+
(0, import_node_fs79.rmdirSync)(path);
|
|
42334
42980
|
}
|
|
42335
42981
|
};
|
|
42336
42982
|
actions.push({
|
|
@@ -42341,11 +42987,11 @@ function registerUninstallCommand(program2) {
|
|
|
42341
42987
|
}
|
|
42342
42988
|
});
|
|
42343
42989
|
const home = process.env.HOME ?? "";
|
|
42344
|
-
const globalVerityDir = (0,
|
|
42345
|
-
if (purgeGlobal && (0,
|
|
42990
|
+
const globalVerityDir = (0, import_node_path66.join)(home, ".verity");
|
|
42991
|
+
if (purgeGlobal && (0, import_node_fs79.existsSync)(globalVerityDir)) {
|
|
42346
42992
|
actions.push({
|
|
42347
42993
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
42348
|
-
apply: () => (0,
|
|
42994
|
+
apply: () => (0, import_node_fs79.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
42349
42995
|
});
|
|
42350
42996
|
}
|
|
42351
42997
|
if (actions.length === 0) {
|
|
@@ -42564,8 +43210,8 @@ function registerTaskCommands(program2) {
|
|
|
42564
43210
|
}
|
|
42565
43211
|
|
|
42566
43212
|
// src/commands/reset.ts
|
|
42567
|
-
var
|
|
42568
|
-
var
|
|
43213
|
+
var import_node_fs80 = require("node:fs");
|
|
43214
|
+
var import_node_path67 = require("node:path");
|
|
42569
43215
|
function registerResetCommand(program2) {
|
|
42570
43216
|
program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
|
|
42571
43217
|
const globals = program2.opts();
|
|
@@ -42605,18 +43251,18 @@ function registerResetCommand(program2) {
|
|
|
42605
43251
|
} catch {
|
|
42606
43252
|
}
|
|
42607
43253
|
}
|
|
42608
|
-
const purged = purgeVerityDir((0,
|
|
43254
|
+
const purged = purgeVerityDir((0, import_node_path67.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
|
|
42609
43255
|
if (purged === null) printError(`Did not purge ${CACHE_DIR}: it is not a directory inside ${VERITY_DIR}.`);
|
|
42610
43256
|
const stateDir2 = projectPath(VERITY_DIR);
|
|
42611
43257
|
const filesToClear = [
|
|
42612
|
-
(0,
|
|
42613
|
-
(0,
|
|
42614
|
-
(0,
|
|
43258
|
+
(0, import_node_path67.basename)(INTENT_FILE),
|
|
43259
|
+
(0, import_node_path67.basename)(HASH_FILE),
|
|
43260
|
+
(0, import_node_path67.basename)(BASELINE_SHA_FILE),
|
|
42615
43261
|
".iteration-count",
|
|
42616
43262
|
".last-analysis"
|
|
42617
43263
|
];
|
|
42618
43264
|
for (const name2 of filesToClear) {
|
|
42619
|
-
if (lstatOrNull((0,
|
|
43265
|
+
if (lstatOrNull((0, import_node_path67.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
|
|
42620
43266
|
printError(`Did not clear ${VERITY_DIR}/${name2}: it is not a regular file inside ${VERITY_DIR}.`);
|
|
42621
43267
|
}
|
|
42622
43268
|
}
|
|
@@ -42631,29 +43277,29 @@ function registerResetCommand(program2) {
|
|
|
42631
43277
|
}
|
|
42632
43278
|
function lstatOrNull(path) {
|
|
42633
43279
|
try {
|
|
42634
|
-
return (0,
|
|
43280
|
+
return (0, import_node_fs80.lstatSync)(path);
|
|
42635
43281
|
} catch {
|
|
42636
43282
|
return null;
|
|
42637
43283
|
}
|
|
42638
43284
|
}
|
|
42639
43285
|
function purgeVerityDir(child, select) {
|
|
42640
43286
|
const verityDir = projectPath(VERITY_DIR);
|
|
42641
|
-
const dir = (0,
|
|
43287
|
+
const dir = (0, import_node_path67.join)(verityDir, child);
|
|
42642
43288
|
const parent = lstatOrNull(verityDir);
|
|
42643
43289
|
const st = lstatOrNull(dir);
|
|
42644
43290
|
if (!st) return 0;
|
|
42645
43291
|
if (!parent || parent.isSymbolicLink() || !parent.isDirectory()) return null;
|
|
42646
43292
|
if (st.isSymbolicLink() || !st.isDirectory()) return null;
|
|
42647
43293
|
try {
|
|
42648
|
-
if ((0,
|
|
43294
|
+
if ((0, import_node_fs80.realpathSync)(dir) !== (0, import_node_path67.join)((0, import_node_fs80.realpathSync)(verityDir), child)) return null;
|
|
42649
43295
|
} catch {
|
|
42650
43296
|
return null;
|
|
42651
43297
|
}
|
|
42652
43298
|
let removed = 0;
|
|
42653
|
-
for (const entry of (0,
|
|
43299
|
+
for (const entry of (0, import_node_fs80.readdirSync)(dir, { withFileTypes: true })) {
|
|
42654
43300
|
if (entry.isDirectory() || !select(entry.name)) continue;
|
|
42655
43301
|
try {
|
|
42656
|
-
(0,
|
|
43302
|
+
(0, import_node_fs80.unlinkSync)((0, import_node_path67.join)(dir, entry.name));
|
|
42657
43303
|
removed++;
|
|
42658
43304
|
} catch {
|
|
42659
43305
|
}
|
|
@@ -42662,7 +43308,7 @@ function purgeVerityDir(child, select) {
|
|
|
42662
43308
|
}
|
|
42663
43309
|
|
|
42664
43310
|
// src/commands/reflect.ts
|
|
42665
|
-
var
|
|
43311
|
+
var import_node_fs81 = require("node:fs");
|
|
42666
43312
|
|
|
42667
43313
|
// src/lib/reflection-globs.ts
|
|
42668
43314
|
var MAX_GLOBS = 6;
|
|
@@ -42700,7 +43346,7 @@ async function writeNodeToDisk(args2) {
|
|
|
42700
43346
|
{ treePaths: listTrackedFiles(), recordBaseline: false }
|
|
42701
43347
|
);
|
|
42702
43348
|
if (written === 0) return false;
|
|
42703
|
-
return (0,
|
|
43349
|
+
return (0, import_node_fs81.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
|
|
42704
43350
|
} catch {
|
|
42705
43351
|
return false;
|
|
42706
43352
|
}
|
|
@@ -43137,9 +43783,15 @@ function registerRunCommand(program2) {
|
|
|
43137
43783
|
}
|
|
43138
43784
|
|
|
43139
43785
|
// src/commands/telemetry.ts
|
|
43140
|
-
var
|
|
43786
|
+
var import_node_fs82 = require("node:fs");
|
|
43787
|
+
var SCOPE = "every Claude Code session on this machine";
|
|
43788
|
+
function declineHeaders(why) {
|
|
43789
|
+
process.stderr.write(`Verity telemetry is off here: ${why} Nothing is exported from this session.
|
|
43790
|
+
`);
|
|
43791
|
+
process.exitCode = 1;
|
|
43792
|
+
}
|
|
43141
43793
|
async function recordTelemetryChoice(choice) {
|
|
43142
|
-
if (!(0,
|
|
43794
|
+
if (!(0, import_node_fs82.existsSync)(projectPath(SETUP_STATE_FILE))) return;
|
|
43143
43795
|
try {
|
|
43144
43796
|
if ((await readSetupState())?.telemetry === choice) return;
|
|
43145
43797
|
await writeSetupState({ telemetry: choice });
|
|
@@ -43147,8 +43799,8 @@ async function recordTelemetryChoice(choice) {
|
|
|
43147
43799
|
}
|
|
43148
43800
|
}
|
|
43149
43801
|
function registerTelemetryCommands(program2) {
|
|
43150
|
-
const telemetry = program2.command("telemetry").description(
|
|
43151
|
-
telemetry.command("install").description(
|
|
43802
|
+
const telemetry = program2.command("telemetry").description(`Manage Claude Code OpenTelemetry export to Verity (cost & usage) for ${SCOPE}`);
|
|
43803
|
+
telemetry.command("install").description(`Export cost & usage to Verity from ${SCOPE} (writes your Claude Code user settings)`).action(async () => {
|
|
43152
43804
|
const globals = program2.opts();
|
|
43153
43805
|
const tokenResult = await resolveToken(globals.token);
|
|
43154
43806
|
if (!tokenResult.ok) {
|
|
@@ -43160,35 +43812,51 @@ function registerTelemetryCommands(program2) {
|
|
|
43160
43812
|
printError(urlResult.error);
|
|
43161
43813
|
process.exit(1);
|
|
43162
43814
|
}
|
|
43163
|
-
const
|
|
43164
|
-
if (!pin.attach && pin.reason === "unpinnable") {
|
|
43165
|
-
printWarn("Your Verity credential does not record which service issued it, so this endpoint");
|
|
43166
|
-
printWarn(" cannot be verified \u2014 a repository could point your telemetry elsewhere.");
|
|
43167
|
-
printWarn(' Run "verity login" to re-issue the credential; Verity can check it after that.');
|
|
43168
|
-
}
|
|
43169
|
-
const result = await installTelemetry(urlResult.data);
|
|
43815
|
+
const result = await installTelemetry(urlResult.data, { token: tokenResult.data.token });
|
|
43170
43816
|
if (!result.ok) {
|
|
43171
43817
|
printError(result.error);
|
|
43172
43818
|
process.exit(1);
|
|
43173
43819
|
}
|
|
43174
43820
|
await recordTelemetryChoice("enabled");
|
|
43175
43821
|
printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
|
|
43176
|
-
printInfo(`
|
|
43822
|
+
printInfo(` scope: ${SCOPE}`);
|
|
43823
|
+
printInfo(` wrote ${displayPath(result.data.settingsPath)} (no token stored \u2014 resolved at runtime via otelHeadersHelper)`);
|
|
43824
|
+
if (result.data.healedLocalKeys.length > 0) {
|
|
43825
|
+
printInfo(` removed ${result.data.healedLocalKeys.length} old telemetry key(s) from ${SETTINGS_LOCAL_FILE2}`);
|
|
43826
|
+
}
|
|
43827
|
+
printInfo(" only Verity projects send data. In other repositories Claude Code shows a startup");
|
|
43828
|
+
printInfo(' warning that "verity telemetry headers" exited 1, and it exports nothing there.');
|
|
43177
43829
|
printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
|
|
43178
43830
|
printInfo(" view cost & usage at /usage");
|
|
43179
43831
|
});
|
|
43180
|
-
telemetry.command("headers").description("Print the OTLP
|
|
43832
|
+
telemetry.command("headers").description("Print the OTLP headers as JSON for Claude Code otelHeadersHelper (exits 1 outside a Verity project)").action(async () => {
|
|
43833
|
+
if (!isTelemetryProject()) {
|
|
43834
|
+
declineHeaders("this directory is not in a Verity project.");
|
|
43835
|
+
return;
|
|
43836
|
+
}
|
|
43181
43837
|
const globals = program2.opts();
|
|
43182
43838
|
const tokenResult = await resolveToken(globals.token);
|
|
43183
|
-
if (tokenResult.ok) {
|
|
43184
|
-
|
|
43185
|
-
|
|
43186
|
-
|
|
43187
|
-
|
|
43188
|
-
|
|
43189
|
-
|
|
43190
|
-
|
|
43839
|
+
if (!tokenResult.ok) {
|
|
43840
|
+
declineHeaders('this Verity project has no token on this machine. Run "verity login".');
|
|
43841
|
+
return;
|
|
43842
|
+
}
|
|
43843
|
+
const endpoints = await sessionExportEndpoints();
|
|
43844
|
+
if (endpoints.length === 0) {
|
|
43845
|
+
declineHeaders("no OTLP endpoint is configured for this session.");
|
|
43846
|
+
return;
|
|
43847
|
+
}
|
|
43848
|
+
for (const endpoint of endpoints) {
|
|
43849
|
+
const refusal = await telemetryPinRefusal(tokenResult.data.token, endpoint);
|
|
43850
|
+
if (refusal) {
|
|
43851
|
+
declineHeaders(refusal);
|
|
43852
|
+
return;
|
|
43853
|
+
}
|
|
43191
43854
|
}
|
|
43855
|
+
const remote = requestRemote();
|
|
43856
|
+
printJsonCompact({
|
|
43857
|
+
Authorization: `Bearer ${tokenResult.data.token}`,
|
|
43858
|
+
...remote ? { "X-Verity-Remote": remote } : {}
|
|
43859
|
+
});
|
|
43192
43860
|
});
|
|
43193
43861
|
telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
|
|
43194
43862
|
const status = await checkTelemetry();
|
|
@@ -43197,21 +43865,36 @@ function registerTelemetryCommands(program2) {
|
|
|
43197
43865
|
return;
|
|
43198
43866
|
}
|
|
43199
43867
|
if (status.enabled) {
|
|
43200
|
-
printInfo(`Telemetry: enabled \u2192 ${status.endpoint}`);
|
|
43868
|
+
printInfo(`Telemetry: enabled \u2192 ${status.endpoint} (${SCOPE})`);
|
|
43201
43869
|
} else {
|
|
43202
43870
|
printWarn('Telemetry: disabled. Run "verity telemetry install" to enable cost & usage tracking.');
|
|
43203
43871
|
}
|
|
43872
|
+
if (status.staleLocalKeys.length > 0) {
|
|
43873
|
+
printWarn(
|
|
43874
|
+
` ${SETTINGS_LOCAL_FILE2} still holds ${status.staleLocalKeys.length} old Verity telemetry key(s), which Claude Code ignores there. "verity telemetry install" removes them.`
|
|
43875
|
+
);
|
|
43876
|
+
}
|
|
43204
43877
|
});
|
|
43205
|
-
telemetry.command("uninstall").description(
|
|
43878
|
+
telemetry.command("uninstall").description(`Stop the telemetry export to Verity from ${SCOPE}`).action(async () => {
|
|
43206
43879
|
const result = await uninstallTelemetry();
|
|
43207
43880
|
if (!result.ok) {
|
|
43208
43881
|
printError(result.error);
|
|
43209
43882
|
process.exit(1);
|
|
43210
43883
|
}
|
|
43211
43884
|
await recordTelemetryChoice("declined");
|
|
43212
|
-
|
|
43213
|
-
|
|
43214
|
-
|
|
43885
|
+
const { removed, settingsPath, foreignEndpoint, healedLocalKeys } = result.data;
|
|
43886
|
+
if (removed > 0) {
|
|
43887
|
+
printInfo(`Telemetry disabled (${removed} keys removed from ${displayPath(settingsPath)})`);
|
|
43888
|
+
} else if (foreignEndpoint) {
|
|
43889
|
+
printInfo(
|
|
43890
|
+
`${displayPath(settingsPath)} exports to another destination, so Verity left it unchanged.`
|
|
43891
|
+
);
|
|
43892
|
+
} else {
|
|
43893
|
+
printInfo("Telemetry was not enabled \u2014 nothing to remove.");
|
|
43894
|
+
}
|
|
43895
|
+
if (healedLocalKeys.length > 0) {
|
|
43896
|
+
printInfo(` removed ${healedLocalKeys.length} old telemetry key(s) from ${SETTINGS_LOCAL_FILE2}`);
|
|
43897
|
+
}
|
|
43215
43898
|
});
|
|
43216
43899
|
}
|
|
43217
43900
|
|
|
@@ -43253,8 +43936,8 @@ function renderSeatUsage(d, now = /* @__PURE__ */ new Date()) {
|
|
|
43253
43936
|
lines.push(
|
|
43254
43937
|
`Session ${utcHm(d.session.start)}\u2013${utcHm(d.session.resets_at)} (resets in ${inText(now, new Date(d.session.resets_at))}) \xB7 week from Mon ${utcDay2(d.week.start)} (resets Mon ${utcDay2(d.week.resets_at)}, 00:00 UTC)`
|
|
43255
43938
|
);
|
|
43256
|
-
if (!d.plan) lines.push("No plan is set for this org: counts only.
|
|
43257
|
-
else if (!d.enforced) lines.push("Limits are shown for information. They are not enforced
|
|
43939
|
+
if (!d.plan) lines.push("No plan is set for this org: counts only.");
|
|
43940
|
+
else if (!d.enforced) lines.push("Limits are shown for information. They are not enforced yet.");
|
|
43258
43941
|
const me = d.members.find((m) => m.me);
|
|
43259
43942
|
if (me) {
|
|
43260
43943
|
const w = me.weeks[0];
|
|
@@ -43358,8 +44041,8 @@ function registerUsageCommand(program2) {
|
|
|
43358
44041
|
}
|
|
43359
44042
|
|
|
43360
44043
|
// src/commands/prove.ts
|
|
43361
|
-
var
|
|
43362
|
-
var
|
|
44044
|
+
var import_node_fs83 = require("node:fs");
|
|
44045
|
+
var import_node_path68 = require("node:path");
|
|
43363
44046
|
|
|
43364
44047
|
// src/lib/prove/hint-pool.ts
|
|
43365
44048
|
var STOP2 = /* @__PURE__ */ new Set([
|
|
@@ -43501,7 +44184,7 @@ function registerProveCommand(program2) {
|
|
|
43501
44184
|
process.exit(2);
|
|
43502
44185
|
}
|
|
43503
44186
|
const limit = Math.max(1, Math.min(50, Number(opts.limit) || 10));
|
|
43504
|
-
let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0,
|
|
44187
|
+
let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path68.relative)(root, (0, import_node_path68.resolve)(p)) || ".") });
|
|
43505
44188
|
if (opts.hint && paths.length === 0) {
|
|
43506
44189
|
const hit = hintMatches(root, opts.hint);
|
|
43507
44190
|
if (hit.files.length > 0) {
|
|
@@ -43555,7 +44238,7 @@ function registerProveCommand(program2) {
|
|
|
43555
44238
|
});
|
|
43556
44239
|
prove.command("check [model]").description("Elaborate a model (or every model) with the local Lean toolchain and report each property").option("--timeout <ms>", "Per-model timeout in ms", "120000").option("--json", "Machine-readable output").action((model, opts) => {
|
|
43557
44240
|
const root = repoRoot();
|
|
43558
|
-
if (!(0,
|
|
44241
|
+
if (!(0, import_node_fs83.existsSync)((0, import_node_path68.join)(root, PROOFS_DIR, "lakefile.toml"))) {
|
|
43559
44242
|
printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
|
|
43560
44243
|
process.exit(2);
|
|
43561
44244
|
}
|
|
@@ -43579,7 +44262,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
|
|
|
43579
44262
|
const reports = [];
|
|
43580
44263
|
for (const f of files) {
|
|
43581
44264
|
const report = checkModel(tc, root, f, { timeoutMs: Math.max(1e3, Number(opts.timeout) || 12e4) });
|
|
43582
|
-
report.file = (0,
|
|
44265
|
+
report.file = (0, import_node_path68.relative)(root, f);
|
|
43583
44266
|
reports.push(report);
|
|
43584
44267
|
try {
|
|
43585
44268
|
writeResult(root, report);
|
|
@@ -43617,7 +44300,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
|
|
|
43617
44300
|
printError("Not logged in \u2014 replays run on the Verity service. Run `verity login` first.");
|
|
43618
44301
|
process.exit(2);
|
|
43619
44302
|
}
|
|
43620
|
-
if (!(0,
|
|
44303
|
+
if (!(0, import_node_fs83.existsSync)((0, import_node_path68.join)(root, PROOFS_DIR, "lakefile.toml"))) {
|
|
43621
44304
|
printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
|
|
43622
44305
|
process.exit(2);
|
|
43623
44306
|
}
|
|
@@ -43648,7 +44331,7 @@ ${report.error ?? ""}`);
|
|
|
43648
44331
|
printInfo(opts.property ? `${model} ${opts.property} has no counterexample to replay` : `${model} has no counterexample to replay`);
|
|
43649
44332
|
return;
|
|
43650
44333
|
}
|
|
43651
|
-
const modelSource = readRegularFileInRoot(root, (0,
|
|
44334
|
+
const modelSource = readRegularFileInRoot(root, (0, import_node_path68.relative)(root, file), 512 * 1024);
|
|
43652
44335
|
if (!opts.json) printInfo(`replaying ${targets.length} counterexample(s) of ${model} in network-less containers (${Math.min(targets.length, REPLAY_CONCURRENCY)} at a time)\u2026`);
|
|
43653
44336
|
const outcomes = await mapPool(targets, REPLAY_CONCURRENCY, async (p) => {
|
|
43654
44337
|
const brief = buildReplayBrief(root, report, p, modelSource.ok ? modelSource.content : "");
|
|
@@ -43668,7 +44351,7 @@ ${report.error ?? ""}`);
|
|
|
43668
44351
|
});
|
|
43669
44352
|
prove.command("auto [files...]").description("The formal phase the pre-push guard runs: select one risky symbol among changed files, generate a model, check it, replay counterexamples (default files: the commits ahead of upstream)").option("--deadline <ms>", "Budget for the whole phase", "170000").option("--json", "Machine-readable output").action(async (paths, opts) => {
|
|
43670
44353
|
const root = repoRoot();
|
|
43671
|
-
let files = paths.map((p) => (0,
|
|
44354
|
+
let files = paths.map((p) => (0, import_node_path68.relative)(root, (0, import_node_path68.resolve)(p)));
|
|
43672
44355
|
if (files.length === 0) {
|
|
43673
44356
|
const ahead = runGitSync(root, ["diff", "--name-only", "@{upstream}...HEAD"]);
|
|
43674
44357
|
const fallback = ahead.ok ? ahead : runGitSync(root, ["diff", "--name-only", "HEAD~1..HEAD"]);
|
|
@@ -43742,7 +44425,7 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
43742
44425
|
let revise;
|
|
43743
44426
|
if (opts.revise) {
|
|
43744
44427
|
const existing = resolveModelFile(root, opts.revise);
|
|
43745
|
-
const src = existing ? readRegularFileInRoot(root, (0,
|
|
44428
|
+
const src = existing ? readRegularFileInRoot(root, (0, import_node_path68.relative)(root, existing), 64 * 1024) : null;
|
|
43746
44429
|
if (!src || !src.ok || !opts.note) {
|
|
43747
44430
|
printError("--revise needs an existing model under Models/ and --note - with what the replay showed on stdin");
|
|
43748
44431
|
process.exit(2);
|
|
@@ -43782,8 +44465,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
43782
44465
|
});
|
|
43783
44466
|
prove.command("waive <file>").description("Dismiss the remembered formal finding(s) on a file for its current bytes \u2014 the accepted-risk path for a formal block").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action((file, opts) => {
|
|
43784
44467
|
const root = repoRoot();
|
|
43785
|
-
const rel0 = (0,
|
|
43786
|
-
const rel = rel0.startsWith("..") || (0,
|
|
44468
|
+
const rel0 = (0, import_node_path68.relative)(root, (0, import_node_path68.isAbsolute)(file) ? file : (0, import_node_path68.join)(root, file)).replace(/\\/g, "/");
|
|
44469
|
+
const rel = rel0.startsWith("..") || (0, import_node_path68.isAbsolute)(rel0) ? null : rel0;
|
|
43787
44470
|
if (!rel) {
|
|
43788
44471
|
printError(`${file} is not inside this repository`);
|
|
43789
44472
|
process.exit(2);
|
|
@@ -43799,8 +44482,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
43799
44482
|
});
|
|
43800
44483
|
prove.command("hash <file>").description("Print the sha256 of a file, for a model header (Verity.model \u2026 sha256). Path is REPO-relative, as in the header").action((file) => {
|
|
43801
44484
|
const root = repoRoot();
|
|
43802
|
-
const rel = (0,
|
|
43803
|
-
const digest = rel.startsWith("..") || (0,
|
|
44485
|
+
const rel = (0, import_node_path68.relative)(root, (0, import_node_path68.isAbsolute)(file) ? file : (0, import_node_path68.join)(root, file));
|
|
44486
|
+
const digest = rel.startsWith("..") || (0, import_node_path68.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
|
|
43804
44487
|
if (!digest) {
|
|
43805
44488
|
printError(`Cannot hash ${file}: it must be a regular file inside this repository (not a symlink)`);
|
|
43806
44489
|
process.exit(2);
|
|
@@ -43834,10 +44517,10 @@ function refreshScaffold(root) {
|
|
|
43834
44517
|
}
|
|
43835
44518
|
|
|
43836
44519
|
// src/commands/lean.ts
|
|
43837
|
-
var
|
|
43838
|
-
var
|
|
43839
|
-
var
|
|
43840
|
-
var
|
|
44520
|
+
var import_node_fs84 = require("node:fs");
|
|
44521
|
+
var import_node_child_process24 = require("node:child_process");
|
|
44522
|
+
var import_node_os15 = require("node:os");
|
|
44523
|
+
var import_node_path69 = require("node:path");
|
|
43841
44524
|
function registerLeanCommand(program2) {
|
|
43842
44525
|
const lean = program2.command("lean").description("The Lean 4 toolchain formal verification needs (managed download, pinned by this CLI)");
|
|
43843
44526
|
lean.command("status").option("--json", "Machine-readable output").description("Which toolchain would run, and whether it matches the pinned version").action((opts) => {
|
|
@@ -43845,7 +44528,7 @@ function registerLeanCommand(program2) {
|
|
|
43845
44528
|
let version = null;
|
|
43846
44529
|
if (tc) {
|
|
43847
44530
|
try {
|
|
43848
|
-
version = (0,
|
|
44531
|
+
version = (0, import_node_child_process24.execFileSync)(tc.lake.replace(/lake(\.exe)?$/, "lean$1"), ["--version"], { env: tc.env, encoding: "utf-8", timeout: 1e4 }).trim();
|
|
43849
44532
|
} catch {
|
|
43850
44533
|
version = null;
|
|
43851
44534
|
}
|
|
@@ -43856,7 +44539,7 @@ function registerLeanCommand(program2) {
|
|
|
43856
44539
|
const bg = readInstallState();
|
|
43857
44540
|
const background = bg ? { ...bg, running: bg.state === "running" && installRunning() } : null;
|
|
43858
44541
|
if (opts.json) {
|
|
43859
|
-
printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0,
|
|
44542
|
+
printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs84.existsSync)((0, import_node_path69.join)(managed, "bin")), asset: leanAssetName(), background });
|
|
43860
44543
|
return;
|
|
43861
44544
|
}
|
|
43862
44545
|
printInfo(`pinned: ${LEAN_TOOLCHAIN}`);
|
|
@@ -43873,11 +44556,11 @@ function registerLeanCommand(program2) {
|
|
|
43873
44556
|
if (opts.background) process.exit(await runBackgroundInstall());
|
|
43874
44557
|
const asset = leanAssetName();
|
|
43875
44558
|
if (!asset) {
|
|
43876
|
-
printError(`no Lean release for ${(0,
|
|
44559
|
+
printError(`no Lean release for ${(0, import_node_os15.platform)()}/${(0, import_node_os15.arch)()}`);
|
|
43877
44560
|
process.exit(2);
|
|
43878
44561
|
}
|
|
43879
44562
|
const dir = pinnedVersionDir();
|
|
43880
|
-
if ((0,
|
|
44563
|
+
if ((0, import_node_fs84.existsSync)((0, import_node_path69.join)(dir, "bin", "lake")) && !opts.force) {
|
|
43881
44564
|
printInfo(`already installed at ${dir}`);
|
|
43882
44565
|
return;
|
|
43883
44566
|
}
|
|
@@ -43913,18 +44596,21 @@ function registerLeanCommand(program2) {
|
|
|
43913
44596
|
});
|
|
43914
44597
|
lean.command("remove").description("Delete the Verity-managed toolchain for the pinned version").action(() => {
|
|
43915
44598
|
const dir = pinnedVersionDir();
|
|
43916
|
-
if (!(0,
|
|
44599
|
+
if (!(0, import_node_fs84.existsSync)(dir)) {
|
|
43917
44600
|
printInfo("nothing to remove");
|
|
43918
44601
|
return;
|
|
43919
44602
|
}
|
|
43920
|
-
(0,
|
|
44603
|
+
(0, import_node_fs84.rmSync)(dir, { recursive: true, force: true });
|
|
43921
44604
|
printInfo(`removed ${dir}`);
|
|
43922
44605
|
});
|
|
43923
44606
|
}
|
|
43924
44607
|
|
|
43925
44608
|
// src/cli.ts
|
|
43926
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.
|
|
43927
|
-
|
|
44609
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.3a13a5e").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) => {
|
|
44610
|
+
const isTelemetryHelper = actionCommand.name() === "headers" && actionCommand.parent?.name() === "telemetry";
|
|
44611
|
+
if (!isTelemetryHelper) {
|
|
44612
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.0-experimental.3a13a5e");
|
|
44613
|
+
}
|
|
43928
44614
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
43929
44615
|
try {
|
|
43930
44616
|
await foldLegacyLocalCredential();
|