@codacy/verity-cli 0.25.0 → 0.26.0-experimental.d7dfc00
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 +885 -580
- package/data/skills/verity-analyze/SKILL.md +7 -1
- package/data/skills/verity-setup/SKILL.md +49 -10
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10325,9 +10325,7 @@ var {
|
|
|
10325
10325
|
} = import_index.default;
|
|
10326
10326
|
|
|
10327
10327
|
// src/commands/auth.ts
|
|
10328
|
-
var
|
|
10329
|
-
var import_node_child_process3 = require("node:child_process");
|
|
10330
|
-
var import_node_path2 = require("node:path");
|
|
10328
|
+
var import_node_child_process4 = require("node:child_process");
|
|
10331
10329
|
|
|
10332
10330
|
// src/lib/auth.ts
|
|
10333
10331
|
var import_promises = require("node:fs/promises");
|
|
@@ -10475,7 +10473,11 @@ var SECURITY_PATTERNS = [
|
|
|
10475
10473
|
/Dockerfile/
|
|
10476
10474
|
];
|
|
10477
10475
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10478
|
-
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10476
|
+
var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10477
|
+
var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
|
|
10478
|
+
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10479
|
+
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
10480
|
+
var GITHUB_OAUTH_SCOPES = "repo user:email";
|
|
10479
10481
|
|
|
10480
10482
|
// src/lib/auth.ts
|
|
10481
10483
|
async function resolveToken(flagToken) {
|
|
@@ -10641,7 +10643,8 @@ async function apiRequest(options) {
|
|
|
10641
10643
|
timeout = 9e4,
|
|
10642
10644
|
cmd = "unknown",
|
|
10643
10645
|
retry = false,
|
|
10644
|
-
encodeBody = false
|
|
10646
|
+
encodeBody = false,
|
|
10647
|
+
extraHeaders
|
|
10645
10648
|
} = options;
|
|
10646
10649
|
const url = `${serviceUrl}${path}`;
|
|
10647
10650
|
const headers = {
|
|
@@ -10650,6 +10653,9 @@ async function apiRequest(options) {
|
|
|
10650
10653
|
if (token) {
|
|
10651
10654
|
headers["Authorization"] = `Bearer ${token}`;
|
|
10652
10655
|
}
|
|
10656
|
+
if (extraHeaders) {
|
|
10657
|
+
Object.assign(headers, extraHeaders);
|
|
10658
|
+
}
|
|
10653
10659
|
const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
|
|
10654
10660
|
if (testMockScenario) {
|
|
10655
10661
|
headers["X-Verity-Mock-Scenario"] = testMockScenario;
|
|
@@ -10740,6 +10746,388 @@ function analyzeRequest(options) {
|
|
|
10740
10746
|
});
|
|
10741
10747
|
}
|
|
10742
10748
|
|
|
10749
|
+
// src/lib/register.ts
|
|
10750
|
+
var import_promises3 = require("node:fs/promises");
|
|
10751
|
+
var import_node_path3 = require("node:path");
|
|
10752
|
+
|
|
10753
|
+
// src/lib/provider-auth.ts
|
|
10754
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10755
|
+
var form = (fields) => new URLSearchParams(fields).toString();
|
|
10756
|
+
async function githubDeviceFlow() {
|
|
10757
|
+
const override = process.env.VERITY_PROVIDER_TOKEN;
|
|
10758
|
+
if (override) return { ok: true, data: override };
|
|
10759
|
+
let dc;
|
|
10760
|
+
try {
|
|
10761
|
+
const res = await fetch(GITHUB_DEVICE_CODE_URL, {
|
|
10762
|
+
method: "POST",
|
|
10763
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10764
|
+
body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_OAUTH_SCOPES })
|
|
10765
|
+
});
|
|
10766
|
+
if (!res.ok) {
|
|
10767
|
+
return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
|
|
10768
|
+
}
|
|
10769
|
+
dc = await res.json();
|
|
10770
|
+
} catch (err) {
|
|
10771
|
+
return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
|
|
10772
|
+
}
|
|
10773
|
+
if (!dc.device_code || !dc.user_code) {
|
|
10774
|
+
return {
|
|
10775
|
+
ok: false,
|
|
10776
|
+
error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
|
|
10777
|
+
};
|
|
10778
|
+
}
|
|
10779
|
+
printInfo("");
|
|
10780
|
+
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
10781
|
+
printInfo(`And enter the code: ${dc.user_code}`);
|
|
10782
|
+
printInfo("Waiting for authorization\u2026");
|
|
10783
|
+
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
10784
|
+
let interval = dc.interval || 5;
|
|
10785
|
+
while (Date.now() < deadline) {
|
|
10786
|
+
await sleep(interval * 1e3);
|
|
10787
|
+
let data;
|
|
10788
|
+
try {
|
|
10789
|
+
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
10790
|
+
method: "POST",
|
|
10791
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10792
|
+
body: form({
|
|
10793
|
+
client_id: GITHUB_CLIENT_ID,
|
|
10794
|
+
device_code: dc.device_code,
|
|
10795
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
10796
|
+
})
|
|
10797
|
+
});
|
|
10798
|
+
data = await res.json().catch(() => ({}));
|
|
10799
|
+
} catch {
|
|
10800
|
+
continue;
|
|
10801
|
+
}
|
|
10802
|
+
if (data.access_token) return { ok: true, data: data.access_token };
|
|
10803
|
+
switch (data.error) {
|
|
10804
|
+
case "authorization_pending":
|
|
10805
|
+
break;
|
|
10806
|
+
case "slow_down":
|
|
10807
|
+
interval += 5;
|
|
10808
|
+
break;
|
|
10809
|
+
case "access_denied":
|
|
10810
|
+
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
10811
|
+
case "expired_token":
|
|
10812
|
+
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
10813
|
+
default:
|
|
10814
|
+
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
10818
|
+
}
|
|
10819
|
+
|
|
10820
|
+
// src/lib/git.ts
|
|
10821
|
+
var import_node_child_process3 = require("node:child_process");
|
|
10822
|
+
var import_node_fs2 = require("node:fs");
|
|
10823
|
+
var import_node_path2 = require("node:path");
|
|
10824
|
+
function resolveFile(relpath) {
|
|
10825
|
+
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10826
|
+
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
10827
|
+
try {
|
|
10828
|
+
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10829
|
+
for (const entry of entries) {
|
|
10830
|
+
if (!entry.isDirectory()) continue;
|
|
10831
|
+
const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
|
|
10832
|
+
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10833
|
+
}
|
|
10834
|
+
} catch {
|
|
10835
|
+
}
|
|
10836
|
+
}
|
|
10837
|
+
return null;
|
|
10838
|
+
}
|
|
10839
|
+
function execGit(cmd) {
|
|
10840
|
+
try {
|
|
10841
|
+
return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10842
|
+
} catch {
|
|
10843
|
+
return "";
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10846
|
+
function splitLines(s) {
|
|
10847
|
+
return s.split("\n").filter((l) => l.length > 0);
|
|
10848
|
+
}
|
|
10849
|
+
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
10850
|
+
function readBaselineSha() {
|
|
10851
|
+
if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
10852
|
+
let sha;
|
|
10853
|
+
try {
|
|
10854
|
+
sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10855
|
+
} catch {
|
|
10856
|
+
return null;
|
|
10857
|
+
}
|
|
10858
|
+
if (!SHA_RE.test(sha)) return null;
|
|
10859
|
+
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10860
|
+
if (!reachable) {
|
|
10861
|
+
try {
|
|
10862
|
+
(0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
|
|
10863
|
+
} catch {
|
|
10864
|
+
}
|
|
10865
|
+
return null;
|
|
10866
|
+
}
|
|
10867
|
+
return sha;
|
|
10868
|
+
}
|
|
10869
|
+
function writeBaselineSha(sha) {
|
|
10870
|
+
if (!SHA_RE.test(sha)) return;
|
|
10871
|
+
try {
|
|
10872
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10873
|
+
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10874
|
+
} catch {
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
function getChangedFiles() {
|
|
10878
|
+
const sets = /* @__PURE__ */ new Set();
|
|
10879
|
+
let hasRecentCommitFiles = false;
|
|
10880
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
10881
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
10882
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
10883
|
+
const baseline = readBaselineSha();
|
|
10884
|
+
if (baseline) {
|
|
10885
|
+
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
10886
|
+
if (committed.length > 0) {
|
|
10887
|
+
hasRecentCommitFiles = true;
|
|
10888
|
+
for (const f of committed) sets.add(f);
|
|
10889
|
+
}
|
|
10890
|
+
} else {
|
|
10891
|
+
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
10892
|
+
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
10893
|
+
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
10894
|
+
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
10895
|
+
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
10896
|
+
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
10897
|
+
if (recentFiles.length > 0) {
|
|
10898
|
+
hasRecentCommitFiles = true;
|
|
10899
|
+
for (const f of recentFiles) sets.add(f);
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
}
|
|
10903
|
+
for (const f of getWorktreeFiles()) sets.add(f);
|
|
10904
|
+
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10905
|
+
return { files: filtered, hasRecentCommitFiles };
|
|
10906
|
+
}
|
|
10907
|
+
function getStagedFiles() {
|
|
10908
|
+
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10909
|
+
}
|
|
10910
|
+
function getDirtyFiles() {
|
|
10911
|
+
const set = /* @__PURE__ */ new Set();
|
|
10912
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
10913
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
10914
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
10915
|
+
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10916
|
+
}
|
|
10917
|
+
function showContentAtRef(ref, repoRelPath) {
|
|
10918
|
+
if (!ref || ref === "no-git") return null;
|
|
10919
|
+
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
10920
|
+
try {
|
|
10921
|
+
return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
10922
|
+
encoding: "utf-8",
|
|
10923
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
10924
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10925
|
+
});
|
|
10926
|
+
} catch {
|
|
10927
|
+
return null;
|
|
10928
|
+
}
|
|
10929
|
+
}
|
|
10930
|
+
function getPushRangeFiles() {
|
|
10931
|
+
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10932
|
+
const resolvers = [
|
|
10933
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
10934
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
10935
|
+
() => {
|
|
10936
|
+
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
10937
|
+
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
10938
|
+
}
|
|
10939
|
+
];
|
|
10940
|
+
for (const resolve of resolvers) {
|
|
10941
|
+
const range = resolve();
|
|
10942
|
+
if (range) return { files: diff(range), range };
|
|
10943
|
+
}
|
|
10944
|
+
const baseline = readBaselineSha();
|
|
10945
|
+
if (baseline) {
|
|
10946
|
+
const files = diff(`${baseline}..HEAD`);
|
|
10947
|
+
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10948
|
+
}
|
|
10949
|
+
const last = diff("HEAD~1..HEAD");
|
|
10950
|
+
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10951
|
+
}
|
|
10952
|
+
function getPushRangeMessages() {
|
|
10953
|
+
const { range } = getPushRangeFiles();
|
|
10954
|
+
if (!range) return "";
|
|
10955
|
+
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10956
|
+
}
|
|
10957
|
+
function getWorktreeFiles() {
|
|
10958
|
+
const result = [];
|
|
10959
|
+
const worktreeDir = ".claude/worktrees";
|
|
10960
|
+
if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
|
|
10961
|
+
try {
|
|
10962
|
+
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
10963
|
+
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
10964
|
+
for (const entry of entries) {
|
|
10965
|
+
if (!entry.isDirectory()) continue;
|
|
10966
|
+
const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
|
|
10967
|
+
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
10968
|
+
}
|
|
10969
|
+
} catch {
|
|
10970
|
+
}
|
|
10971
|
+
return result;
|
|
10972
|
+
}
|
|
10973
|
+
function scanDir(baseDir, dir, minMtime, result) {
|
|
10974
|
+
try {
|
|
10975
|
+
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
10976
|
+
for (const entry of entries) {
|
|
10977
|
+
const fullPath = (0, import_node_path2.join)(dir, entry.name);
|
|
10978
|
+
if (entry.isDirectory()) {
|
|
10979
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
10980
|
+
scanDir(baseDir, fullPath, minMtime, result);
|
|
10981
|
+
} else if (entry.isFile()) {
|
|
10982
|
+
const ext = (0, import_node_path2.extname)(entry.name).slice(1);
|
|
10983
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
10984
|
+
try {
|
|
10985
|
+
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
10986
|
+
if (stat3.mtimeMs >= minMtime) {
|
|
10987
|
+
const relPath = fullPath.slice(baseDir.length + 1);
|
|
10988
|
+
result.push(relPath);
|
|
10989
|
+
}
|
|
10990
|
+
} catch {
|
|
10991
|
+
}
|
|
10992
|
+
}
|
|
10993
|
+
}
|
|
10994
|
+
} catch {
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
function filterAnalyzable(files) {
|
|
10998
|
+
return files.filter((f) => {
|
|
10999
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11000
|
+
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
11001
|
+
});
|
|
11002
|
+
}
|
|
11003
|
+
function filterReviewable(files) {
|
|
11004
|
+
return files.filter((f) => {
|
|
11005
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11006
|
+
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11007
|
+
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11008
|
+
const basename3 = f.split("/").pop() ?? "";
|
|
11009
|
+
if (REVIEWABLE_FILENAMES.has(basename3)) return true;
|
|
11010
|
+
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
11011
|
+
return false;
|
|
11012
|
+
});
|
|
11013
|
+
}
|
|
11014
|
+
function filterSecurity(files) {
|
|
11015
|
+
return files.filter(
|
|
11016
|
+
(f) => SECURITY_PATTERNS.some((p) => p.test(f))
|
|
11017
|
+
);
|
|
11018
|
+
}
|
|
11019
|
+
function getCurrentCommit() {
|
|
11020
|
+
return execGit("git rev-parse HEAD") || "no-git";
|
|
11021
|
+
}
|
|
11022
|
+
function detectProvider(host) {
|
|
11023
|
+
const h = host.toLowerCase();
|
|
11024
|
+
if (h.includes("github")) return "github";
|
|
11025
|
+
if (h.includes("gitlab")) return "gitlab";
|
|
11026
|
+
if (h.includes("bitbucket")) return "bitbucket";
|
|
11027
|
+
return "unknown";
|
|
11028
|
+
}
|
|
11029
|
+
function parseRemote(raw) {
|
|
11030
|
+
if (!raw || typeof raw !== "string") return null;
|
|
11031
|
+
let s = raw.trim();
|
|
11032
|
+
if (!s) return null;
|
|
11033
|
+
let host = "";
|
|
11034
|
+
let path = "";
|
|
11035
|
+
const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
|
|
11036
|
+
if (scp) {
|
|
11037
|
+
host = scp[1];
|
|
11038
|
+
path = scp[2];
|
|
11039
|
+
} else {
|
|
11040
|
+
s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
|
|
11041
|
+
s = s.replace(/^[^/@]+@/, "");
|
|
11042
|
+
const slash = s.indexOf("/");
|
|
11043
|
+
if (slash === -1) return null;
|
|
11044
|
+
host = s.slice(0, slash);
|
|
11045
|
+
path = s.slice(slash + 1);
|
|
11046
|
+
}
|
|
11047
|
+
host = host.toLowerCase().trim();
|
|
11048
|
+
path = path.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
11049
|
+
if (!host || !path) return null;
|
|
11050
|
+
const segments = path.split("/").filter(Boolean);
|
|
11051
|
+
if (segments.length < 2) return null;
|
|
11052
|
+
const owner = segments[0];
|
|
11053
|
+
const repo = segments[segments.length - 1];
|
|
11054
|
+
if (!owner || !repo) return null;
|
|
11055
|
+
return {
|
|
11056
|
+
host,
|
|
11057
|
+
owner,
|
|
11058
|
+
repo,
|
|
11059
|
+
provider: detectProvider(host),
|
|
11060
|
+
orgUrl: `https://${host}/${owner}`,
|
|
11061
|
+
orgName: owner
|
|
11062
|
+
};
|
|
11063
|
+
}
|
|
11064
|
+
function listTrackedFiles() {
|
|
11065
|
+
const set = /* @__PURE__ */ new Set();
|
|
11066
|
+
for (const f of splitLines(execGit("git ls-files"))) set.add(f);
|
|
11067
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
11068
|
+
return Array.from(set);
|
|
11069
|
+
}
|
|
11070
|
+
|
|
11071
|
+
// src/lib/register.ts
|
|
11072
|
+
async function registerProject(opts) {
|
|
11073
|
+
const parsed = parseRemote(opts.remote);
|
|
11074
|
+
if (!parsed) {
|
|
11075
|
+
return { ok: false, error: `Could not parse git remote: ${opts.remote}` };
|
|
11076
|
+
}
|
|
11077
|
+
if (parsed.provider !== "github") {
|
|
11078
|
+
return { ok: false, error: `Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.` };
|
|
11079
|
+
}
|
|
11080
|
+
const providerAuth = await githubDeviceFlow();
|
|
11081
|
+
if (!providerAuth.ok) {
|
|
11082
|
+
return { ok: false, error: providerAuth.error };
|
|
11083
|
+
}
|
|
11084
|
+
const providerToken = providerAuth.data;
|
|
11085
|
+
const result = await apiRequest({
|
|
11086
|
+
method: "POST",
|
|
11087
|
+
path: "/auth/register",
|
|
11088
|
+
serviceUrl: opts.serviceUrl,
|
|
11089
|
+
body: { project_name: opts.projectName, git_remote_url: opts.remote },
|
|
11090
|
+
extraHeaders: { "X-Provider-Token": providerToken },
|
|
11091
|
+
verbose: opts.verbose
|
|
11092
|
+
});
|
|
11093
|
+
if (!result.ok) {
|
|
11094
|
+
return { ok: false, error: result.error };
|
|
11095
|
+
}
|
|
11096
|
+
const { project_id, token, service_url, user } = result.data;
|
|
11097
|
+
try {
|
|
11098
|
+
await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
|
|
11099
|
+
await (0, import_promises3.writeFile)(
|
|
11100
|
+
CREDENTIALS_FILE,
|
|
11101
|
+
`token: ${token}
|
|
11102
|
+
service_url: ${service_url}
|
|
11103
|
+
provider_token: ${providerToken}
|
|
11104
|
+
`,
|
|
11105
|
+
{ mode: 384 }
|
|
11106
|
+
);
|
|
11107
|
+
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11108
|
+
});
|
|
11109
|
+
} catch (err) {
|
|
11110
|
+
return {
|
|
11111
|
+
ok: false,
|
|
11112
|
+
error: `Registered with the Verity service, but could not save credentials to ${CREDENTIALS_FILE}: ${err.message}. Check filesystem permissions and re-run "verity auth register".`
|
|
11113
|
+
};
|
|
11114
|
+
}
|
|
11115
|
+
try {
|
|
11116
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
11117
|
+
await (0, import_promises3.appendFile)(
|
|
11118
|
+
GLOBAL_CREDENTIALS_FILE,
|
|
11119
|
+
`${opts.remote} token: ${token}
|
|
11120
|
+
${opts.remote} provider_token: ${providerToken}
|
|
11121
|
+
`,
|
|
11122
|
+
{ mode: 384 }
|
|
11123
|
+
);
|
|
11124
|
+
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11125
|
+
});
|
|
11126
|
+
} catch {
|
|
11127
|
+
}
|
|
11128
|
+
return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
|
|
11129
|
+
}
|
|
11130
|
+
|
|
10743
11131
|
// src/commands/auth.ts
|
|
10744
11132
|
function registerAuthCommands(program2) {
|
|
10745
11133
|
const auth = program2.command("auth").description("Manage project authentication");
|
|
@@ -10749,36 +11137,26 @@ function registerAuthCommands(program2) {
|
|
|
10749
11137
|
let remote = opts.remote;
|
|
10750
11138
|
if (!remote) {
|
|
10751
11139
|
try {
|
|
10752
|
-
remote = (0,
|
|
11140
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10753
11141
|
} catch {
|
|
10754
11142
|
printError("No git remote found. Use --remote to specify one.");
|
|
10755
11143
|
process.exit(1);
|
|
10756
11144
|
}
|
|
10757
11145
|
}
|
|
10758
|
-
const result = await
|
|
10759
|
-
|
|
10760
|
-
|
|
11146
|
+
const result = await registerProject({
|
|
11147
|
+
projectName: opts.project,
|
|
11148
|
+
remote,
|
|
10761
11149
|
serviceUrl,
|
|
10762
|
-
body: { project_name: opts.project, git_remote_url: remote },
|
|
10763
11150
|
verbose: globals.verbose
|
|
10764
11151
|
});
|
|
10765
11152
|
if (!result.ok) {
|
|
10766
11153
|
printError(result.error);
|
|
10767
11154
|
process.exit(1);
|
|
10768
11155
|
}
|
|
10769
|
-
const {
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
service_url:
|
|
10773
|
-
`);
|
|
10774
|
-
try {
|
|
10775
|
-
await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
10776
|
-
await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
|
|
10777
|
-
`);
|
|
10778
|
-
} catch {
|
|
10779
|
-
}
|
|
10780
|
-
printInfo(`Project registered: ${project_id}`);
|
|
10781
|
-
printJson({ project_id, service_url });
|
|
11156
|
+
const { projectId, serviceUrl: resolvedUrl, email } = result.data;
|
|
11157
|
+
printInfo(`Project registered: ${projectId}`);
|
|
11158
|
+
if (email) printInfo(`Authenticated as: ${email}`);
|
|
11159
|
+
printJson({ project_id: projectId, service_url: resolvedUrl });
|
|
10782
11160
|
});
|
|
10783
11161
|
auth.command("verify").description("Verify the current token is valid").action(async () => {
|
|
10784
11162
|
const globals = program2.opts();
|
|
@@ -10811,7 +11189,7 @@ service_url: ${service_url}
|
|
|
10811
11189
|
let remote = opts.remote;
|
|
10812
11190
|
if (!remote) {
|
|
10813
11191
|
try {
|
|
10814
|
-
remote = (0,
|
|
11192
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10815
11193
|
} catch {
|
|
10816
11194
|
printError("No git remote found. Use --remote to specify one.");
|
|
10817
11195
|
process.exit(1);
|
|
@@ -10838,8 +11216,63 @@ service_url: ${service_url}
|
|
|
10838
11216
|
}
|
|
10839
11217
|
|
|
10840
11218
|
// src/lib/hooks.ts
|
|
10841
|
-
var
|
|
10842
|
-
var
|
|
11219
|
+
var import_promises5 = require("node:fs/promises");
|
|
11220
|
+
var import_node_path5 = require("node:path");
|
|
11221
|
+
|
|
11222
|
+
// src/lib/json-file.ts
|
|
11223
|
+
var import_promises4 = require("node:fs/promises");
|
|
11224
|
+
var import_node_path4 = require("node:path");
|
|
11225
|
+
function jsonSemanticEqual(a, b) {
|
|
11226
|
+
if (a === b) return true;
|
|
11227
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
|
|
11228
|
+
return a === b;
|
|
11229
|
+
}
|
|
11230
|
+
const aIsArr = Array.isArray(a);
|
|
11231
|
+
const bIsArr = Array.isArray(b);
|
|
11232
|
+
if (aIsArr || bIsArr) {
|
|
11233
|
+
if (!aIsArr || !bIsArr || a.length !== b.length) return false;
|
|
11234
|
+
for (let i = 0; i < a.length; i++) {
|
|
11235
|
+
if (!jsonSemanticEqual(a[i], b[i])) return false;
|
|
11236
|
+
}
|
|
11237
|
+
return true;
|
|
11238
|
+
}
|
|
11239
|
+
const ao = a;
|
|
11240
|
+
const bo = b;
|
|
11241
|
+
const aKeys = Object.keys(ao).filter((k) => ao[k] !== void 0);
|
|
11242
|
+
const bKeys = Object.keys(bo).filter((k) => bo[k] !== void 0);
|
|
11243
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
11244
|
+
for (const k of aKeys) {
|
|
11245
|
+
if (bo[k] === void 0) return false;
|
|
11246
|
+
if (!jsonSemanticEqual(ao[k], bo[k])) return false;
|
|
11247
|
+
}
|
|
11248
|
+
return true;
|
|
11249
|
+
}
|
|
11250
|
+
function detectJsonIndent(raw) {
|
|
11251
|
+
const m = raw.match(/\n([ \t]+)\S/);
|
|
11252
|
+
return m ? m[1] : 2;
|
|
11253
|
+
}
|
|
11254
|
+
async function writeJsonFilePreservingStyle(file, value) {
|
|
11255
|
+
let currentRaw = null;
|
|
11256
|
+
try {
|
|
11257
|
+
currentRaw = await (0, import_promises4.readFile)(file, "utf-8");
|
|
11258
|
+
} catch {
|
|
11259
|
+
currentRaw = null;
|
|
11260
|
+
}
|
|
11261
|
+
if (currentRaw !== null) {
|
|
11262
|
+
try {
|
|
11263
|
+
if (jsonSemanticEqual(JSON.parse(currentRaw), value)) return false;
|
|
11264
|
+
} catch {
|
|
11265
|
+
}
|
|
11266
|
+
}
|
|
11267
|
+
const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
|
|
11268
|
+
const next = JSON.stringify(value, null, indent) + "\n";
|
|
11269
|
+
if (next === currentRaw) return false;
|
|
11270
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
|
|
11271
|
+
await (0, import_promises4.writeFile)(file, next);
|
|
11272
|
+
return true;
|
|
11273
|
+
}
|
|
11274
|
+
|
|
11275
|
+
// src/lib/hooks.ts
|
|
10843
11276
|
var VERITY_STOP_HOOK = {
|
|
10844
11277
|
type: "command",
|
|
10845
11278
|
command: "verity analyze",
|
|
@@ -10917,7 +11350,7 @@ function globalSettingsFile() {
|
|
|
10917
11350
|
}
|
|
10918
11351
|
async function readSettings() {
|
|
10919
11352
|
try {
|
|
10920
|
-
const content = await (0,
|
|
11353
|
+
const content = await (0, import_promises5.readFile)(CLAUDE_SETTINGS_FILE, "utf-8");
|
|
10921
11354
|
return JSON.parse(content);
|
|
10922
11355
|
} catch {
|
|
10923
11356
|
return {};
|
|
@@ -10928,7 +11361,7 @@ async function readAllSettings() {
|
|
|
10928
11361
|
const out = [];
|
|
10929
11362
|
for (const f of files) {
|
|
10930
11363
|
try {
|
|
10931
|
-
out.push(JSON.parse(await (0,
|
|
11364
|
+
out.push(JSON.parse(await (0, import_promises5.readFile)(f, "utf-8")));
|
|
10932
11365
|
} catch {
|
|
10933
11366
|
}
|
|
10934
11367
|
}
|
|
@@ -10957,7 +11390,7 @@ async function checkExternalVerityHooks() {
|
|
|
10957
11390
|
for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
|
|
10958
11391
|
let settings;
|
|
10959
11392
|
try {
|
|
10960
|
-
settings = JSON.parse(await (0,
|
|
11393
|
+
settings = JSON.parse(await (0, import_promises5.readFile)(f, "utf-8"));
|
|
10961
11394
|
} catch {
|
|
10962
11395
|
continue;
|
|
10963
11396
|
}
|
|
@@ -10991,20 +11424,17 @@ async function checkAllVerityHooksDetailed() {
|
|
|
10991
11424
|
return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
10992
11425
|
}
|
|
10993
11426
|
async function writeSettings(settings) {
|
|
10994
|
-
await (
|
|
10995
|
-
await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
11427
|
+
await writeJsonFilePreservingStyle(CLAUDE_SETTINGS_FILE, settings);
|
|
10996
11428
|
}
|
|
10997
11429
|
async function readSettingsAt(root) {
|
|
10998
11430
|
try {
|
|
10999
|
-
return JSON.parse(await (0,
|
|
11431
|
+
return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
|
|
11000
11432
|
} catch {
|
|
11001
11433
|
return {};
|
|
11002
11434
|
}
|
|
11003
11435
|
}
|
|
11004
11436
|
async function writeSettingsAt(root, settings) {
|
|
11005
|
-
|
|
11006
|
-
await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(file), { recursive: true });
|
|
11007
|
-
await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
11437
|
+
await writeJsonFilePreservingStyle((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), settings);
|
|
11008
11438
|
}
|
|
11009
11439
|
async function hasLegacyHooksAt(root) {
|
|
11010
11440
|
const settings = await readSettingsAt(root);
|
|
@@ -11243,9 +11673,9 @@ function registerHooksCommands(program2) {
|
|
|
11243
11673
|
var import_node_crypto3 = require("node:crypto");
|
|
11244
11674
|
|
|
11245
11675
|
// src/lib/conversation-buffer.ts
|
|
11246
|
-
var
|
|
11247
|
-
var
|
|
11248
|
-
var
|
|
11676
|
+
var import_promises6 = require("node:fs/promises");
|
|
11677
|
+
var import_node_fs3 = require("node:fs");
|
|
11678
|
+
var import_node_child_process5 = require("node:child_process");
|
|
11249
11679
|
var import_node_crypto = require("node:crypto");
|
|
11250
11680
|
function stripImageReferences(text) {
|
|
11251
11681
|
return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
|
|
@@ -11255,7 +11685,7 @@ function bufferTmpPath() {
|
|
|
11255
11685
|
}
|
|
11256
11686
|
async function appendToConversationBuffer(prompt, sessionId) {
|
|
11257
11687
|
try {
|
|
11258
|
-
await (0,
|
|
11688
|
+
await (0, import_promises6.mkdir)(VERITY_DIR, { recursive: true });
|
|
11259
11689
|
let sanitized = prompt.length > MAX_INTENT_CHARS ? prompt.slice(0, MAX_INTENT_CHARS) : prompt;
|
|
11260
11690
|
sanitized = stripImageReferences(sanitized);
|
|
11261
11691
|
const entry = {
|
|
@@ -11273,14 +11703,14 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11273
11703
|
const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
|
|
11274
11704
|
const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11275
11705
|
const tmpFile = bufferTmpPath();
|
|
11276
|
-
await (0,
|
|
11277
|
-
await (0,
|
|
11706
|
+
await (0, import_promises6.writeFile)(tmpFile, content);
|
|
11707
|
+
await (0, import_promises6.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11278
11708
|
} catch {
|
|
11279
11709
|
}
|
|
11280
11710
|
}
|
|
11281
11711
|
async function readAndClearConversationBuffer(currentSessionId) {
|
|
11282
11712
|
try {
|
|
11283
|
-
if ((0,
|
|
11713
|
+
if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
11284
11714
|
const entries = await readBufferEntries();
|
|
11285
11715
|
let mine = entries;
|
|
11286
11716
|
let others = [];
|
|
@@ -11291,10 +11721,10 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11291
11721
|
if (others.length > 0) {
|
|
11292
11722
|
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11293
11723
|
const tmpFile = bufferTmpPath();
|
|
11294
|
-
await (0,
|
|
11295
|
-
await (0,
|
|
11724
|
+
await (0, import_promises6.writeFile)(tmpFile, remaining);
|
|
11725
|
+
await (0, import_promises6.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11296
11726
|
} else {
|
|
11297
|
-
await (0,
|
|
11727
|
+
await (0, import_promises6.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11298
11728
|
});
|
|
11299
11729
|
}
|
|
11300
11730
|
if (mine.length > 0) {
|
|
@@ -11304,10 +11734,10 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11304
11734
|
};
|
|
11305
11735
|
}
|
|
11306
11736
|
}
|
|
11307
|
-
if ((0,
|
|
11737
|
+
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11308
11738
|
try {
|
|
11309
|
-
const content = await (0,
|
|
11310
|
-
await (0,
|
|
11739
|
+
const content = await (0, import_promises6.readFile)(INTENT_FILE, "utf-8");
|
|
11740
|
+
await (0, import_promises6.unlink)(INTENT_FILE).catch(() => {
|
|
11311
11741
|
});
|
|
11312
11742
|
const data = JSON.parse(content);
|
|
11313
11743
|
if (data.prompt) {
|
|
@@ -11330,7 +11760,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11330
11760
|
}
|
|
11331
11761
|
async function readBufferEntries() {
|
|
11332
11762
|
try {
|
|
11333
|
-
const content = await (0,
|
|
11763
|
+
const content = await (0, import_promises6.readFile)(CONVERSATION_BUFFER_FILE, "utf-8");
|
|
11334
11764
|
const entries = [];
|
|
11335
11765
|
for (const line of content.split("\n")) {
|
|
11336
11766
|
const trimmed = line.trim();
|
|
@@ -11348,7 +11778,7 @@ async function readBufferEntries() {
|
|
|
11348
11778
|
}
|
|
11349
11779
|
function getRecentCommitMessages() {
|
|
11350
11780
|
try {
|
|
11351
|
-
const output = (0,
|
|
11781
|
+
const output = (0, import_node_child_process5.execSync)(
|
|
11352
11782
|
'git log --since="30 minutes ago" --format="%s" -5',
|
|
11353
11783
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
11354
11784
|
).trim();
|
|
@@ -11360,9 +11790,9 @@ function getRecentCommitMessages() {
|
|
|
11360
11790
|
}
|
|
11361
11791
|
|
|
11362
11792
|
// src/lib/task-context-buffer.ts
|
|
11363
|
-
var
|
|
11364
|
-
var
|
|
11365
|
-
var
|
|
11793
|
+
var import_promises7 = require("node:fs/promises");
|
|
11794
|
+
var import_node_fs4 = require("node:fs");
|
|
11795
|
+
var import_node_path6 = require("node:path");
|
|
11366
11796
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
11367
11797
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
11368
11798
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -11401,9 +11831,9 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
11401
11831
|
}
|
|
11402
11832
|
async function readTaskContextBuffer(taskId) {
|
|
11403
11833
|
const filePath = bufferPath(taskId);
|
|
11404
|
-
if (!(0,
|
|
11834
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
11405
11835
|
try {
|
|
11406
|
-
const content = await (0,
|
|
11836
|
+
const content = await (0, import_promises7.readFile)(filePath, "utf-8");
|
|
11407
11837
|
if (!content.trim()) return null;
|
|
11408
11838
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
11409
11839
|
const formatted = [];
|
|
@@ -11435,16 +11865,16 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11435
11865
|
}
|
|
11436
11866
|
async function cleanupTaskContextBuffers() {
|
|
11437
11867
|
try {
|
|
11438
|
-
if (!(0,
|
|
11439
|
-
const files = await (0,
|
|
11868
|
+
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
11869
|
+
const files = await (0, import_promises7.readdir)(TASK_CONTEXT_DIR);
|
|
11440
11870
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
11441
11871
|
for (const file of files) {
|
|
11442
11872
|
if (!file.endsWith(".jsonl")) continue;
|
|
11443
|
-
const filePath = (0,
|
|
11873
|
+
const filePath = (0, import_node_path6.join)(TASK_CONTEXT_DIR, file);
|
|
11444
11874
|
try {
|
|
11445
|
-
const stats = await (0,
|
|
11875
|
+
const stats = await (0, import_promises7.stat)(filePath);
|
|
11446
11876
|
if (stats.mtimeMs < cutoffMs) {
|
|
11447
|
-
await (0,
|
|
11877
|
+
await (0, import_promises7.unlink)(filePath);
|
|
11448
11878
|
}
|
|
11449
11879
|
} catch {
|
|
11450
11880
|
}
|
|
@@ -11454,33 +11884,33 @@ async function cleanupTaskContextBuffers() {
|
|
|
11454
11884
|
}
|
|
11455
11885
|
function bufferPath(taskId) {
|
|
11456
11886
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
11457
|
-
return (0,
|
|
11887
|
+
return (0, import_node_path6.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
11458
11888
|
}
|
|
11459
11889
|
async function appendEntry(taskId, entry) {
|
|
11460
11890
|
try {
|
|
11461
|
-
await (0,
|
|
11891
|
+
await (0, import_promises7.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
11462
11892
|
const filePath = bufferPath(taskId);
|
|
11463
|
-
if ((0,
|
|
11464
|
-
const stats = await (0,
|
|
11893
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
11894
|
+
const stats = await (0, import_promises7.stat)(filePath);
|
|
11465
11895
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
11466
|
-
const content = await (0,
|
|
11896
|
+
const content = await (0, import_promises7.readFile)(filePath, "utf-8");
|
|
11467
11897
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
11468
11898
|
const keepFrom = Math.floor(lines.length * 0.25);
|
|
11469
11899
|
const pruned = lines.slice(keepFrom).join("\n") + "\n";
|
|
11470
|
-
await (0,
|
|
11900
|
+
await (0, import_promises7.writeFile)(filePath, pruned);
|
|
11471
11901
|
}
|
|
11472
11902
|
}
|
|
11473
11903
|
const line = JSON.stringify(entry) + "\n";
|
|
11474
|
-
const existing = (0,
|
|
11475
|
-
await (0,
|
|
11904
|
+
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises7.readFile)(filePath, "utf-8") : "";
|
|
11905
|
+
await (0, import_promises7.writeFile)(filePath, existing + line);
|
|
11476
11906
|
} catch {
|
|
11477
11907
|
}
|
|
11478
11908
|
}
|
|
11479
11909
|
|
|
11480
11910
|
// src/lib/memory-retrieval.ts
|
|
11481
|
-
var
|
|
11482
|
-
var
|
|
11483
|
-
var
|
|
11911
|
+
var import_promises8 = require("node:fs/promises");
|
|
11912
|
+
var import_node_fs5 = require("node:fs");
|
|
11913
|
+
var import_node_path7 = require("node:path");
|
|
11484
11914
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
11485
11915
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
11486
11916
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
@@ -11573,19 +12003,19 @@ function parseFrontmatter(content) {
|
|
|
11573
12003
|
return { fm, body: match[2].trim() };
|
|
11574
12004
|
}
|
|
11575
12005
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
11576
|
-
if (!(0,
|
|
12006
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
|
|
11577
12007
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
11578
12008
|
const promptTokens = tokenize(promptText);
|
|
11579
12009
|
const nodes = [];
|
|
11580
12010
|
for (const domain of DOMAINS) {
|
|
11581
|
-
const domainDir = (0,
|
|
11582
|
-
if (!(0,
|
|
12011
|
+
const domainDir = (0, import_node_path7.join)(memoryDir(), domain);
|
|
12012
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
11583
12013
|
try {
|
|
11584
|
-
const files = await (0,
|
|
12014
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11585
12015
|
for (const file of files) {
|
|
11586
12016
|
if (!file.endsWith(".md")) continue;
|
|
11587
12017
|
try {
|
|
11588
|
-
const content = await (0,
|
|
12018
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
|
|
11589
12019
|
const { fm, body } = parseFrontmatter(content);
|
|
11590
12020
|
if (fm.status && fm.status !== "active") continue;
|
|
11591
12021
|
nodes.push({
|
|
@@ -11643,9 +12073,9 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
11643
12073
|
}
|
|
11644
12074
|
|
|
11645
12075
|
// src/lib/memory-sync.ts
|
|
11646
|
-
var
|
|
11647
|
-
var
|
|
11648
|
-
var
|
|
12076
|
+
var import_promises9 = require("node:fs/promises");
|
|
12077
|
+
var import_node_fs6 = require("node:fs");
|
|
12078
|
+
var import_node_path8 = require("node:path");
|
|
11649
12079
|
var import_node_crypto2 = require("node:crypto");
|
|
11650
12080
|
|
|
11651
12081
|
// src/lib/glob-match.ts
|
|
@@ -11714,36 +12144,36 @@ var memoryDir2 = () => projectPath(`${VERITY_DIR}/memory`);
|
|
|
11714
12144
|
var DOMAINS2 = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations", "_archive"];
|
|
11715
12145
|
var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
11716
12146
|
async function ensureMemoryDir() {
|
|
11717
|
-
await (0,
|
|
12147
|
+
await (0, import_promises9.mkdir)(memoryDir2(), { recursive: true });
|
|
11718
12148
|
for (const domain of DOMAINS2) {
|
|
11719
|
-
await (0,
|
|
12149
|
+
await (0, import_promises9.mkdir)((0, import_node_path8.join)(memoryDir2(), domain), { recursive: true });
|
|
11720
12150
|
}
|
|
11721
|
-
if (!(0,
|
|
11722
|
-
await (0,
|
|
12151
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12152
|
+
await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
11723
12153
|
}
|
|
11724
|
-
if (!(0,
|
|
11725
|
-
await (0,
|
|
12154
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "index.md"))) {
|
|
12155
|
+
await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
11726
12156
|
}
|
|
11727
|
-
if (!(0,
|
|
11728
|
-
await (0,
|
|
12157
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "log.md"))) {
|
|
12158
|
+
await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
11729
12159
|
}
|
|
11730
12160
|
}
|
|
11731
12161
|
async function buildManifest() {
|
|
11732
|
-
if (!(0,
|
|
12162
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
|
|
11733
12163
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
11734
12164
|
}
|
|
11735
12165
|
const nodes = [];
|
|
11736
12166
|
for (const domain of DOMAINS2) {
|
|
11737
|
-
const domainDir = (0,
|
|
11738
|
-
if (!(0,
|
|
12167
|
+
const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
|
|
12168
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11739
12169
|
try {
|
|
11740
|
-
const files = await (0,
|
|
12170
|
+
const files = await (0, import_promises9.readdir)(domainDir);
|
|
11741
12171
|
for (const file of files) {
|
|
11742
12172
|
if (!file.endsWith(".md")) continue;
|
|
11743
12173
|
const filePath = `${domain}/${file}`;
|
|
11744
|
-
const fullPath = (0,
|
|
12174
|
+
const fullPath = (0, import_node_path8.join)(memoryDir2(), filePath);
|
|
11745
12175
|
try {
|
|
11746
|
-
const content = await (0,
|
|
12176
|
+
const content = await (0, import_promises9.readFile)(fullPath, "utf-8");
|
|
11747
12177
|
const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
11748
12178
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
11749
12179
|
} catch {
|
|
@@ -11754,13 +12184,13 @@ async function buildManifest() {
|
|
|
11754
12184
|
}
|
|
11755
12185
|
let indexHash = null;
|
|
11756
12186
|
try {
|
|
11757
|
-
const indexContent = await (0,
|
|
12187
|
+
const indexContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "utf-8");
|
|
11758
12188
|
indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
11759
12189
|
} catch {
|
|
11760
12190
|
}
|
|
11761
12191
|
let logLength = 0;
|
|
11762
12192
|
try {
|
|
11763
|
-
const logContent = await (0,
|
|
12193
|
+
const logContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8");
|
|
11764
12194
|
logLength = logContent.split("\n").length;
|
|
11765
12195
|
} catch {
|
|
11766
12196
|
}
|
|
@@ -11771,15 +12201,15 @@ function hashContent(content) {
|
|
|
11771
12201
|
}
|
|
11772
12202
|
async function readOnDiskNodes() {
|
|
11773
12203
|
const out = /* @__PURE__ */ new Map();
|
|
11774
|
-
if (!(0,
|
|
12204
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
|
|
11775
12205
|
for (const domain of DOMAINS2) {
|
|
11776
|
-
const domainDir = (0,
|
|
11777
|
-
if (!(0,
|
|
12206
|
+
const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
|
|
12207
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11778
12208
|
try {
|
|
11779
|
-
for (const file of await (0,
|
|
12209
|
+
for (const file of await (0, import_promises9.readdir)(domainDir)) {
|
|
11780
12210
|
if (!file.endsWith(".md")) continue;
|
|
11781
12211
|
try {
|
|
11782
|
-
out.set(`${domain}/${file}`, hashContent(await (0,
|
|
12212
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8")));
|
|
11783
12213
|
} catch {
|
|
11784
12214
|
}
|
|
11785
12215
|
}
|
|
@@ -11791,7 +12221,7 @@ async function readOnDiskNodes() {
|
|
|
11791
12221
|
async function readSyncBaseline() {
|
|
11792
12222
|
const out = /* @__PURE__ */ new Map();
|
|
11793
12223
|
try {
|
|
11794
|
-
const parsed = JSON.parse(await (0,
|
|
12224
|
+
const parsed = JSON.parse(await (0, import_promises9.readFile)(syncStateFile(), "utf-8"));
|
|
11795
12225
|
if (Array.isArray(parsed?.nodes)) {
|
|
11796
12226
|
for (const n of parsed.nodes) if (n?.path) out.set(n.path, n.hash ?? null);
|
|
11797
12227
|
} else if (Array.isArray(parsed?.paths)) {
|
|
@@ -11807,12 +12237,12 @@ async function recordSyncedNodePaths() {
|
|
|
11807
12237
|
const next = JSON.stringify({ schema: 2, nodes }) + "\n";
|
|
11808
12238
|
let existing = "";
|
|
11809
12239
|
try {
|
|
11810
|
-
existing = await (0,
|
|
12240
|
+
existing = await (0, import_promises9.readFile)(syncStateFile(), "utf-8");
|
|
11811
12241
|
} catch {
|
|
11812
12242
|
}
|
|
11813
12243
|
if (existing === next) return;
|
|
11814
|
-
await (0,
|
|
11815
|
-
await (0,
|
|
12244
|
+
await (0, import_promises9.mkdir)(projectPath(VERITY_DIR), { recursive: true });
|
|
12245
|
+
await (0, import_promises9.writeFile)(syncStateFile(), next);
|
|
11816
12246
|
} catch {
|
|
11817
12247
|
}
|
|
11818
12248
|
}
|
|
@@ -11825,11 +12255,11 @@ async function computeEditedNodeUploads() {
|
|
|
11825
12255
|
const uploads = [];
|
|
11826
12256
|
for (const [path, prevHash] of prev) {
|
|
11827
12257
|
if (prevHash == null) continue;
|
|
11828
|
-
const full = (0,
|
|
11829
|
-
if (!(0,
|
|
12258
|
+
const full = (0, import_node_path8.join)(memoryDir2(), path);
|
|
12259
|
+
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
11830
12260
|
let content;
|
|
11831
12261
|
try {
|
|
11832
|
-
content = await (0,
|
|
12262
|
+
content = await (0, import_promises9.readFile)(full, "utf-8");
|
|
11833
12263
|
} catch {
|
|
11834
12264
|
continue;
|
|
11835
12265
|
}
|
|
@@ -11862,15 +12292,15 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
11862
12292
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
11863
12293
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
11864
12294
|
try {
|
|
11865
|
-
const existing = (0,
|
|
11866
|
-
await (0,
|
|
12295
|
+
const existing = (0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "log.md")) ? await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
12296
|
+
await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
11867
12297
|
} catch {
|
|
11868
12298
|
}
|
|
11869
12299
|
await recordSyncedNodePaths();
|
|
11870
12300
|
return count;
|
|
11871
12301
|
}
|
|
11872
12302
|
async function applyOneWrite(write, treePaths) {
|
|
11873
|
-
const fullPath = (0,
|
|
12303
|
+
const fullPath = (0, import_node_path8.join)(memoryDir2(), write.path);
|
|
11874
12304
|
const notes = [];
|
|
11875
12305
|
let content = write.content;
|
|
11876
12306
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -11880,10 +12310,10 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11880
12310
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
11881
12311
|
}
|
|
11882
12312
|
}
|
|
11883
|
-
if ((0,
|
|
12313
|
+
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
11884
12314
|
let existing = "";
|
|
11885
12315
|
try {
|
|
11886
|
-
existing = await (0,
|
|
12316
|
+
existing = await (0, import_promises9.readFile)(fullPath, "utf-8");
|
|
11887
12317
|
} catch {
|
|
11888
12318
|
}
|
|
11889
12319
|
if (existing === content) return { written: false, notes };
|
|
@@ -11892,8 +12322,8 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11892
12322
|
return { written: false, notes };
|
|
11893
12323
|
}
|
|
11894
12324
|
}
|
|
11895
|
-
await (0,
|
|
11896
|
-
await (0,
|
|
12325
|
+
await (0, import_promises9.mkdir)((0, import_node_path8.dirname)(fullPath), { recursive: true });
|
|
12326
|
+
await (0, import_promises9.writeFile)(fullPath, content);
|
|
11897
12327
|
return { written: true, notes };
|
|
11898
12328
|
}
|
|
11899
12329
|
function groundFileGlobs(content, treePaths) {
|
|
@@ -11933,10 +12363,10 @@ async function regenerateIndex() {
|
|
|
11933
12363
|
];
|
|
11934
12364
|
let totalNodes = 0;
|
|
11935
12365
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
11936
|
-
const domainDir = (0,
|
|
11937
|
-
if (!(0,
|
|
12366
|
+
const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
|
|
12367
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11938
12368
|
try {
|
|
11939
|
-
const files = await (0,
|
|
12369
|
+
const files = await (0, import_promises9.readdir)(domainDir);
|
|
11940
12370
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
11941
12371
|
if (mdFiles.length === 0) continue;
|
|
11942
12372
|
lines.push(`## ${domain}/ (${mdFiles.length})`);
|
|
@@ -11944,7 +12374,7 @@ async function regenerateIndex() {
|
|
|
11944
12374
|
for (const file of mdFiles.sort()) {
|
|
11945
12375
|
const slug = file.replace(/\.md$/, "");
|
|
11946
12376
|
try {
|
|
11947
|
-
const content = await (0,
|
|
12377
|
+
const content = await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
|
|
11948
12378
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
11949
12379
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
11950
12380
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -11968,14 +12398,14 @@ async function regenerateIndex() {
|
|
|
11968
12398
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
11969
12399
|
}
|
|
11970
12400
|
const next = lines.join("\n") + "\n";
|
|
11971
|
-
const indexPath = (0,
|
|
12401
|
+
const indexPath = (0, import_node_path8.join)(memoryDir2(), "index.md");
|
|
11972
12402
|
let existing = null;
|
|
11973
12403
|
try {
|
|
11974
|
-
existing = await (0,
|
|
12404
|
+
existing = await (0, import_promises9.readFile)(indexPath, "utf-8");
|
|
11975
12405
|
} catch {
|
|
11976
12406
|
}
|
|
11977
12407
|
if (existing === next) return;
|
|
11978
|
-
await (0,
|
|
12408
|
+
await (0, import_promises9.writeFile)(indexPath, next);
|
|
11979
12409
|
}
|
|
11980
12410
|
function pickFrontmatter(content, key) {
|
|
11981
12411
|
const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
|
|
@@ -12050,10 +12480,10 @@ function hasLegacyMemoryBlock(text) {
|
|
|
12050
12480
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
12051
12481
|
}
|
|
12052
12482
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
12053
|
-
const claudeMdPath = (0,
|
|
12483
|
+
const claudeMdPath = (0, import_node_path8.join)(cwd, "CLAUDE.md");
|
|
12054
12484
|
let existing = "";
|
|
12055
|
-
if ((0,
|
|
12056
|
-
existing = await (0,
|
|
12485
|
+
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12486
|
+
existing = await (0, import_promises9.readFile)(claudeMdPath, "utf-8");
|
|
12057
12487
|
}
|
|
12058
12488
|
let startTag = CLAUDE_MD_START;
|
|
12059
12489
|
let endTag = CLAUDE_MD_END;
|
|
@@ -12109,7 +12539,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
12109
12539
|
next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
|
|
12110
12540
|
}
|
|
12111
12541
|
if (next === existing) return;
|
|
12112
|
-
await (0,
|
|
12542
|
+
await (0, import_promises9.writeFile)(claudeMdPath, next);
|
|
12113
12543
|
}
|
|
12114
12544
|
function extractPreserveContent(interior) {
|
|
12115
12545
|
for (const [start, end] of [
|
|
@@ -12182,7 +12612,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
12182
12612
|
`;
|
|
12183
12613
|
|
|
12184
12614
|
// src/commands/intent.ts
|
|
12185
|
-
var
|
|
12615
|
+
var import_node_fs7 = require("node:fs");
|
|
12186
12616
|
function registerIntentCommands(program2) {
|
|
12187
12617
|
const intent = program2.command("intent").description("Manage intent capture");
|
|
12188
12618
|
intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
|
|
@@ -12191,7 +12621,7 @@ function registerIntentCommands(program2) {
|
|
|
12191
12621
|
process.chdir(repoRoot());
|
|
12192
12622
|
} catch {
|
|
12193
12623
|
}
|
|
12194
|
-
if (!(0,
|
|
12624
|
+
if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
|
|
12195
12625
|
process.exit(0);
|
|
12196
12626
|
}
|
|
12197
12627
|
const chunks = [];
|
|
@@ -12292,7 +12722,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12292
12722
|
}
|
|
12293
12723
|
|
|
12294
12724
|
// src/commands/standard.ts
|
|
12295
|
-
var
|
|
12725
|
+
var import_promises10 = require("node:fs/promises");
|
|
12296
12726
|
var import_yaml = __toESM(require_dist());
|
|
12297
12727
|
function registerStandardCommands(program2) {
|
|
12298
12728
|
const standard = program2.command("standard").description("Manage the project Standard");
|
|
@@ -12310,7 +12740,7 @@ function registerStandardCommands(program2) {
|
|
|
12310
12740
|
}
|
|
12311
12741
|
let yamlContent;
|
|
12312
12742
|
try {
|
|
12313
|
-
yamlContent = await (0,
|
|
12743
|
+
yamlContent = await (0, import_promises10.readFile)(opts.file, "utf-8");
|
|
12314
12744
|
} catch {
|
|
12315
12745
|
printError(`Cannot read ${opts.file}`);
|
|
12316
12746
|
process.exit(1);
|
|
@@ -12401,7 +12831,7 @@ function registerStandardCommands(program2) {
|
|
|
12401
12831
|
}
|
|
12402
12832
|
|
|
12403
12833
|
// src/commands/config.ts
|
|
12404
|
-
var
|
|
12834
|
+
var import_promises11 = require("node:fs/promises");
|
|
12405
12835
|
function registerConfigCommands(program2) {
|
|
12406
12836
|
const config = program2.command("config").description("Manage analysis configuration");
|
|
12407
12837
|
config.command("push").description("Upload the analysis config to the service").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
|
|
@@ -12418,7 +12848,7 @@ function registerConfigCommands(program2) {
|
|
|
12418
12848
|
}
|
|
12419
12849
|
let content;
|
|
12420
12850
|
try {
|
|
12421
|
-
const raw = await (0,
|
|
12851
|
+
const raw = await (0, import_promises11.readFile)(opts.file, "utf-8");
|
|
12422
12852
|
content = JSON.parse(raw);
|
|
12423
12853
|
} catch {
|
|
12424
12854
|
printError(`Cannot read or parse ${opts.file}`);
|
|
@@ -12618,370 +13048,161 @@ function registerStatusCommand(program2) {
|
|
|
12618
13048
|
if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
|
|
12619
13049
|
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : "none (run /verity-setup)"}`);
|
|
12620
13050
|
if (mem.recent_runs) {
|
|
12621
|
-
const r = mem.recent_runs;
|
|
12622
|
-
printInfo("");
|
|
12623
|
-
printInfo("--- Last Run ---");
|
|
12624
|
-
printInfo(`Decision: ${r.last_gate_decision ?? "none"}`);
|
|
12625
|
-
printInfo(`Quality: ${r.last_quality_score ?? "-"}/10`);
|
|
12626
|
-
printInfo(`Security: ${r.last_security_score ?? "-"}/10`);
|
|
12627
|
-
printInfo(`Trend: ${r.trend}`);
|
|
12628
|
-
printInfo(`Runs: ${r.count} recorded`);
|
|
12629
|
-
}
|
|
12630
|
-
if (mem.pending_items && mem.pending_items.length > 0) {
|
|
12631
|
-
printInfo("");
|
|
12632
|
-
printInfo("--- Pending Items ---");
|
|
12633
|
-
for (const item of mem.pending_items) {
|
|
12634
|
-
printInfo(` [${item.priority.toUpperCase()}] ${item.description}`);
|
|
12635
|
-
}
|
|
12636
|
-
}
|
|
12637
|
-
const recentTasks = mem.recent_tasks;
|
|
12638
|
-
const currentTask = mem.current_task;
|
|
12639
|
-
const contextFiles = mem.context_files;
|
|
12640
|
-
if (recentTasks && recentTasks.length > 0) {
|
|
12641
|
-
printInfo("");
|
|
12642
|
-
printInfo("--- Active Tasks ---");
|
|
12643
|
-
for (const task of recentTasks) {
|
|
12644
|
-
const isCurrent = currentTask && task.id === currentTask.id;
|
|
12645
|
-
const marker = isCurrent ? "\u25CF" : "\u25CB";
|
|
12646
|
-
const runCount = task.run_count ?? 0;
|
|
12647
|
-
const lastAt = task.last_run_at ? timeAgo(task.last_run_at) : "no runs";
|
|
12648
|
-
printInfo(` ${marker} ${task.label} ${runCount} run${runCount === 1 ? "" : "s"}, ${lastAt}`);
|
|
12649
|
-
}
|
|
12650
|
-
if (currentTask) {
|
|
12651
|
-
printInfo("");
|
|
12652
|
-
printInfo(`Current task: ${currentTask.label}`);
|
|
12653
|
-
if (contextFiles && contextFiles.length > 0) {
|
|
12654
|
-
printInfo(` Files in task: ${contextFiles.length} (${contextFiles.slice(0, 3).join(", ")}${contextFiles.length > 3 ? "..." : ""})`);
|
|
12655
|
-
}
|
|
12656
|
-
}
|
|
12657
|
-
}
|
|
12658
|
-
if (opts.history) {
|
|
12659
|
-
const runsResult = await apiRequest({
|
|
12660
|
-
method: "GET",
|
|
12661
|
-
path: `/runs?limit=${opts.limit}`,
|
|
12662
|
-
serviceUrl,
|
|
12663
|
-
token,
|
|
12664
|
-
verbose: globals.verbose
|
|
12665
|
-
});
|
|
12666
|
-
if (runsResult.ok && runsResult.data.runs.length > 0) {
|
|
12667
|
-
printInfo("");
|
|
12668
|
-
printInfo("--- Recent Runs ---");
|
|
12669
|
-
printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
|
|
12670
|
-
for (const run of runsResult.data.runs) {
|
|
12671
|
-
const q = run.quality_score != null ? `${run.quality_score}` : "-";
|
|
12672
|
-
const s = run.security_score != null ? `${run.security_score}` : "-";
|
|
12673
|
-
const findings = formatFindingsSummary(run.findings_count);
|
|
12674
|
-
const date = run.created_at.slice(0, 19).replace("T", " ");
|
|
12675
|
-
printInfo(`${run.run_id.padEnd(32)} ${run.gate_decision.padEnd(10)}${q.padEnd(4)}${s.padEnd(4)}${findings.padEnd(32)}${date}`);
|
|
12676
|
-
}
|
|
12677
|
-
}
|
|
12678
|
-
}
|
|
12679
|
-
});
|
|
12680
|
-
}
|
|
12681
|
-
|
|
12682
|
-
// src/commands/feedback.ts
|
|
12683
|
-
function registerFeedbackCommand(program2) {
|
|
12684
|
-
const feedbackCmd = program2.command("feedback").description("Send feedback to the Verity team");
|
|
12685
|
-
feedbackCmd.command("message <text>").description("Send general feedback").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
12686
|
-
const globals = program2.opts();
|
|
12687
|
-
await sendGeneralFeedback(message, opts, globals);
|
|
12688
|
-
});
|
|
12689
|
-
feedbackCmd.command("finding <run-id> <pattern-id> <action> [note]").description("Submit per-finding feedback (false_positive, acknowledged, useful, etc.)").option("--file <path>", "File path the finding applies to").option("--line <n>", "Line number", parseInt).action(async (runId, patternId, action, note, opts) => {
|
|
12690
|
-
const globals = program2.opts();
|
|
12691
|
-
const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
|
|
12692
|
-
if (!validActions.includes(action)) {
|
|
12693
|
-
printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
|
|
12694
|
-
process.exit(1);
|
|
12695
|
-
}
|
|
12696
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12697
|
-
if (!tokenResult.ok) {
|
|
12698
|
-
printError(tokenResult.error);
|
|
12699
|
-
process.exit(1);
|
|
12700
|
-
}
|
|
12701
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12702
|
-
if (!urlResult.ok) {
|
|
12703
|
-
printError(urlResult.error);
|
|
12704
|
-
process.exit(1);
|
|
12705
|
-
}
|
|
12706
|
-
const body = {
|
|
12707
|
-
run_id: runId,
|
|
12708
|
-
pattern_id: patternId,
|
|
12709
|
-
action
|
|
12710
|
-
};
|
|
12711
|
-
if (note) body.note = note;
|
|
12712
|
-
if (opts.file) body.file_path = opts.file;
|
|
12713
|
-
if (opts.line != null) body.line = opts.line;
|
|
12714
|
-
const result = await apiRequest({
|
|
12715
|
-
method: "POST",
|
|
12716
|
-
path: "/feedback/findings",
|
|
12717
|
-
serviceUrl: urlResult.data,
|
|
12718
|
-
token: tokenResult.data.token,
|
|
12719
|
-
body,
|
|
12720
|
-
verbose: globals.verbose
|
|
12721
|
-
});
|
|
12722
|
-
if (!result.ok) {
|
|
12723
|
-
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
12724
|
-
process.exit(1);
|
|
12725
|
-
}
|
|
12726
|
-
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
12727
|
-
printInfo(status);
|
|
12728
|
-
});
|
|
12729
|
-
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
12730
|
-
if (!message) return;
|
|
12731
|
-
const globals = program2.opts();
|
|
12732
|
-
await sendGeneralFeedback(message, opts, globals);
|
|
12733
|
-
});
|
|
12734
|
-
}
|
|
12735
|
-
async function sendGeneralFeedback(message, opts, globals) {
|
|
12736
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12737
|
-
if (!tokenResult.ok) {
|
|
12738
|
-
printError(tokenResult.error);
|
|
12739
|
-
printInfo(`Your message: ${message}`);
|
|
12740
|
-
process.exit(1);
|
|
12741
|
-
}
|
|
12742
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12743
|
-
if (!urlResult.ok) {
|
|
12744
|
-
printError(urlResult.error);
|
|
12745
|
-
printInfo(`Your message: ${message}`);
|
|
12746
|
-
process.exit(1);
|
|
12747
|
-
}
|
|
12748
|
-
const body = { message };
|
|
12749
|
-
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
12750
|
-
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
12751
|
-
if (sessionId) body.session_id = sessionId;
|
|
12752
|
-
if (model) body.agent_model = model;
|
|
12753
|
-
const result = await apiRequest({
|
|
12754
|
-
method: "POST",
|
|
12755
|
-
path: "/feedback",
|
|
12756
|
-
serviceUrl: urlResult.data,
|
|
12757
|
-
token: tokenResult.data.token,
|
|
12758
|
-
body,
|
|
12759
|
-
verbose: globals.verbose
|
|
12760
|
-
});
|
|
12761
|
-
if (!result.ok) {
|
|
12762
|
-
printError(`Couldn't send feedback: ${result.error}`);
|
|
12763
|
-
printInfo(`Your message: ${message}`);
|
|
12764
|
-
process.exit(1);
|
|
12765
|
-
}
|
|
12766
|
-
printInfo("Thanks, feedback sent!");
|
|
12767
|
-
}
|
|
12768
|
-
|
|
12769
|
-
// src/commands/analyze.ts
|
|
12770
|
-
var import_node_fs19 = require("node:fs");
|
|
12771
|
-
var import_node_path14 = require("node:path");
|
|
12772
|
-
|
|
12773
|
-
// src/lib/git.ts
|
|
12774
|
-
var import_node_child_process5 = require("node:child_process");
|
|
12775
|
-
var import_node_fs7 = require("node:fs");
|
|
12776
|
-
var import_node_path7 = require("node:path");
|
|
12777
|
-
function resolveFile(relpath) {
|
|
12778
|
-
if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
|
|
12779
|
-
if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
|
|
12780
|
-
try {
|
|
12781
|
-
const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
12782
|
-
for (const entry of entries) {
|
|
12783
|
-
if (!entry.isDirectory()) continue;
|
|
12784
|
-
const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
|
|
12785
|
-
if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
|
|
12786
|
-
}
|
|
12787
|
-
} catch {
|
|
12788
|
-
}
|
|
12789
|
-
}
|
|
12790
|
-
return null;
|
|
12791
|
-
}
|
|
12792
|
-
function execGit(cmd) {
|
|
12793
|
-
try {
|
|
12794
|
-
return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12795
|
-
} catch {
|
|
12796
|
-
return "";
|
|
12797
|
-
}
|
|
12798
|
-
}
|
|
12799
|
-
function splitLines(s) {
|
|
12800
|
-
return s.split("\n").filter((l) => l.length > 0);
|
|
12801
|
-
}
|
|
12802
|
-
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
12803
|
-
function readBaselineSha() {
|
|
12804
|
-
if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
12805
|
-
let sha;
|
|
12806
|
-
try {
|
|
12807
|
-
sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
12808
|
-
} catch {
|
|
12809
|
-
return null;
|
|
12810
|
-
}
|
|
12811
|
-
if (!SHA_RE.test(sha)) return null;
|
|
12812
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
12813
|
-
if (!reachable) {
|
|
12814
|
-
try {
|
|
12815
|
-
(0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
|
|
12816
|
-
} catch {
|
|
12817
|
-
}
|
|
12818
|
-
return null;
|
|
12819
|
-
}
|
|
12820
|
-
return sha;
|
|
12821
|
-
}
|
|
12822
|
-
function writeBaselineSha(sha) {
|
|
12823
|
-
if (!SHA_RE.test(sha)) return;
|
|
12824
|
-
try {
|
|
12825
|
-
(0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
12826
|
-
(0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
12827
|
-
} catch {
|
|
12828
|
-
}
|
|
12829
|
-
}
|
|
12830
|
-
function getChangedFiles() {
|
|
12831
|
-
const sets = /* @__PURE__ */ new Set();
|
|
12832
|
-
let hasRecentCommitFiles = false;
|
|
12833
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
12834
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
12835
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
12836
|
-
const baseline = readBaselineSha();
|
|
12837
|
-
if (baseline) {
|
|
12838
|
-
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
12839
|
-
if (committed.length > 0) {
|
|
12840
|
-
hasRecentCommitFiles = true;
|
|
12841
|
-
for (const f of committed) sets.add(f);
|
|
12842
|
-
}
|
|
12843
|
-
} else {
|
|
12844
|
-
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
12845
|
-
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
12846
|
-
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
12847
|
-
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
12848
|
-
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
12849
|
-
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
12850
|
-
if (recentFiles.length > 0) {
|
|
12851
|
-
hasRecentCommitFiles = true;
|
|
12852
|
-
for (const f of recentFiles) sets.add(f);
|
|
12853
|
-
}
|
|
12854
|
-
}
|
|
12855
|
-
}
|
|
12856
|
-
for (const f of getWorktreeFiles()) sets.add(f);
|
|
12857
|
-
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12858
|
-
return { files: filtered, hasRecentCommitFiles };
|
|
12859
|
-
}
|
|
12860
|
-
function getStagedFiles() {
|
|
12861
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12862
|
-
}
|
|
12863
|
-
function getDirtyFiles() {
|
|
12864
|
-
const set = /* @__PURE__ */ new Set();
|
|
12865
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
12866
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
12867
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
12868
|
-
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12869
|
-
}
|
|
12870
|
-
function showContentAtRef(ref, repoRelPath) {
|
|
12871
|
-
if (!ref || ref === "no-git") return null;
|
|
12872
|
-
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
12873
|
-
try {
|
|
12874
|
-
return (0, import_node_child_process5.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
12875
|
-
encoding: "utf-8",
|
|
12876
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
12877
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
12878
|
-
});
|
|
12879
|
-
} catch {
|
|
12880
|
-
return null;
|
|
12881
|
-
}
|
|
12882
|
-
}
|
|
12883
|
-
function getPushRangeFiles() {
|
|
12884
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12885
|
-
const resolvers = [
|
|
12886
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
12887
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
12888
|
-
() => {
|
|
12889
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
12890
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
12891
|
-
}
|
|
12892
|
-
];
|
|
12893
|
-
for (const resolve of resolvers) {
|
|
12894
|
-
const range = resolve();
|
|
12895
|
-
if (range) return { files: diff(range), range };
|
|
12896
|
-
}
|
|
12897
|
-
const baseline = readBaselineSha();
|
|
12898
|
-
if (baseline) {
|
|
12899
|
-
const files = diff(`${baseline}..HEAD`);
|
|
12900
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
12901
|
-
}
|
|
12902
|
-
const last = diff("HEAD~1..HEAD");
|
|
12903
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
12904
|
-
}
|
|
12905
|
-
function getPushRangeMessages() {
|
|
12906
|
-
const { range } = getPushRangeFiles();
|
|
12907
|
-
if (!range) return "";
|
|
12908
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
12909
|
-
}
|
|
12910
|
-
function getWorktreeFiles() {
|
|
12911
|
-
const result = [];
|
|
12912
|
-
const worktreeDir = ".claude/worktrees";
|
|
12913
|
-
if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
|
|
12914
|
-
try {
|
|
12915
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
12916
|
-
const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
12917
|
-
for (const entry of entries) {
|
|
12918
|
-
if (!entry.isDirectory()) continue;
|
|
12919
|
-
const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
|
|
12920
|
-
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
13051
|
+
const r = mem.recent_runs;
|
|
13052
|
+
printInfo("");
|
|
13053
|
+
printInfo("--- Last Run ---");
|
|
13054
|
+
printInfo(`Decision: ${r.last_gate_decision ?? "none"}`);
|
|
13055
|
+
printInfo(`Quality: ${r.last_quality_score ?? "-"}/10`);
|
|
13056
|
+
printInfo(`Security: ${r.last_security_score ?? "-"}/10`);
|
|
13057
|
+
printInfo(`Trend: ${r.trend}`);
|
|
13058
|
+
printInfo(`Runs: ${r.count} recorded`);
|
|
12921
13059
|
}
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
13060
|
+
if (mem.pending_items && mem.pending_items.length > 0) {
|
|
13061
|
+
printInfo("");
|
|
13062
|
+
printInfo("--- Pending Items ---");
|
|
13063
|
+
for (const item of mem.pending_items) {
|
|
13064
|
+
printInfo(` [${item.priority.toUpperCase()}] ${item.description}`);
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
const recentTasks = mem.recent_tasks;
|
|
13068
|
+
const currentTask = mem.current_task;
|
|
13069
|
+
const contextFiles = mem.context_files;
|
|
13070
|
+
if (recentTasks && recentTasks.length > 0) {
|
|
13071
|
+
printInfo("");
|
|
13072
|
+
printInfo("--- Active Tasks ---");
|
|
13073
|
+
for (const task of recentTasks) {
|
|
13074
|
+
const isCurrent = currentTask && task.id === currentTask.id;
|
|
13075
|
+
const marker = isCurrent ? "\u25CF" : "\u25CB";
|
|
13076
|
+
const runCount = task.run_count ?? 0;
|
|
13077
|
+
const lastAt = task.last_run_at ? timeAgo(task.last_run_at) : "no runs";
|
|
13078
|
+
printInfo(` ${marker} ${task.label} ${runCount} run${runCount === 1 ? "" : "s"}, ${lastAt}`);
|
|
13079
|
+
}
|
|
13080
|
+
if (currentTask) {
|
|
13081
|
+
printInfo("");
|
|
13082
|
+
printInfo(`Current task: ${currentTask.label}`);
|
|
13083
|
+
if (contextFiles && contextFiles.length > 0) {
|
|
13084
|
+
printInfo(` Files in task: ${contextFiles.length} (${contextFiles.slice(0, 3).join(", ")}${contextFiles.length > 3 ? "..." : ""})`);
|
|
13085
|
+
}
|
|
13086
|
+
}
|
|
13087
|
+
}
|
|
13088
|
+
if (opts.history) {
|
|
13089
|
+
const runsResult = await apiRequest({
|
|
13090
|
+
method: "GET",
|
|
13091
|
+
path: `/runs?limit=${opts.limit}`,
|
|
13092
|
+
serviceUrl,
|
|
13093
|
+
token,
|
|
13094
|
+
verbose: globals.verbose
|
|
13095
|
+
});
|
|
13096
|
+
if (runsResult.ok && runsResult.data.runs.length > 0) {
|
|
13097
|
+
printInfo("");
|
|
13098
|
+
printInfo("--- Recent Runs ---");
|
|
13099
|
+
printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
|
|
13100
|
+
for (const run of runsResult.data.runs) {
|
|
13101
|
+
const q = run.quality_score != null ? `${run.quality_score}` : "-";
|
|
13102
|
+
const s = run.security_score != null ? `${run.security_score}` : "-";
|
|
13103
|
+
const findings = formatFindingsSummary(run.findings_count);
|
|
13104
|
+
const date = run.created_at.slice(0, 19).replace("T", " ");
|
|
13105
|
+
printInfo(`${run.run_id.padEnd(32)} ${run.gate_decision.padEnd(10)}${q.padEnd(4)}${s.padEnd(4)}${findings.padEnd(32)}${date}`);
|
|
12944
13106
|
}
|
|
12945
13107
|
}
|
|
12946
13108
|
}
|
|
12947
|
-
} catch {
|
|
12948
|
-
}
|
|
12949
|
-
}
|
|
12950
|
-
function filterAnalyzable(files) {
|
|
12951
|
-
return files.filter((f) => {
|
|
12952
|
-
const ext = (0, import_node_path7.extname)(f).slice(1);
|
|
12953
|
-
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
12954
13109
|
});
|
|
12955
13110
|
}
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
const
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
13111
|
+
|
|
13112
|
+
// src/commands/feedback.ts
|
|
13113
|
+
function registerFeedbackCommand(program2) {
|
|
13114
|
+
const feedbackCmd = program2.command("feedback").description("Send feedback to the Verity team");
|
|
13115
|
+
feedbackCmd.command("message <text>").description("Send general feedback").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
13116
|
+
const globals = program2.opts();
|
|
13117
|
+
await sendGeneralFeedback(message, opts, globals);
|
|
13118
|
+
});
|
|
13119
|
+
feedbackCmd.command("finding <run-id> <pattern-id> <action> [note]").description("Submit per-finding feedback (false_positive, acknowledged, useful, etc.)").option("--file <path>", "File path the finding applies to").option("--line <n>", "Line number", parseInt).action(async (runId, patternId, action, note, opts) => {
|
|
13120
|
+
const globals = program2.opts();
|
|
13121
|
+
const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
|
|
13122
|
+
if (!validActions.includes(action)) {
|
|
13123
|
+
printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
|
|
13124
|
+
process.exit(1);
|
|
13125
|
+
}
|
|
13126
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13127
|
+
if (!tokenResult.ok) {
|
|
13128
|
+
printError(tokenResult.error);
|
|
13129
|
+
process.exit(1);
|
|
13130
|
+
}
|
|
13131
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13132
|
+
if (!urlResult.ok) {
|
|
13133
|
+
printError(urlResult.error);
|
|
13134
|
+
process.exit(1);
|
|
13135
|
+
}
|
|
13136
|
+
const body = {
|
|
13137
|
+
run_id: runId,
|
|
13138
|
+
pattern_id: patternId,
|
|
13139
|
+
action
|
|
13140
|
+
};
|
|
13141
|
+
if (note) body.note = note;
|
|
13142
|
+
if (opts.file) body.file_path = opts.file;
|
|
13143
|
+
if (opts.line != null) body.line = opts.line;
|
|
13144
|
+
const result = await apiRequest({
|
|
13145
|
+
method: "POST",
|
|
13146
|
+
path: "/feedback/findings",
|
|
13147
|
+
serviceUrl: urlResult.data,
|
|
13148
|
+
token: tokenResult.data.token,
|
|
13149
|
+
body,
|
|
13150
|
+
verbose: globals.verbose
|
|
13151
|
+
});
|
|
13152
|
+
if (!result.ok) {
|
|
13153
|
+
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
13154
|
+
process.exit(1);
|
|
13155
|
+
}
|
|
13156
|
+
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
13157
|
+
printInfo(status);
|
|
13158
|
+
});
|
|
13159
|
+
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
13160
|
+
if (!message) return;
|
|
13161
|
+
const globals = program2.opts();
|
|
13162
|
+
await sendGeneralFeedback(message, opts, globals);
|
|
12965
13163
|
});
|
|
12966
13164
|
}
|
|
12967
|
-
function
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
}
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
13165
|
+
async function sendGeneralFeedback(message, opts, globals) {
|
|
13166
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13167
|
+
if (!tokenResult.ok) {
|
|
13168
|
+
printError(tokenResult.error);
|
|
13169
|
+
printInfo(`Your message: ${message}`);
|
|
13170
|
+
process.exit(1);
|
|
13171
|
+
}
|
|
13172
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13173
|
+
if (!urlResult.ok) {
|
|
13174
|
+
printError(urlResult.error);
|
|
13175
|
+
printInfo(`Your message: ${message}`);
|
|
13176
|
+
process.exit(1);
|
|
13177
|
+
}
|
|
13178
|
+
const body = { message };
|
|
13179
|
+
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
13180
|
+
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
13181
|
+
if (sessionId) body.session_id = sessionId;
|
|
13182
|
+
if (model) body.agent_model = model;
|
|
13183
|
+
const result = await apiRequest({
|
|
13184
|
+
method: "POST",
|
|
13185
|
+
path: "/feedback",
|
|
13186
|
+
serviceUrl: urlResult.data,
|
|
13187
|
+
token: tokenResult.data.token,
|
|
13188
|
+
body,
|
|
13189
|
+
verbose: globals.verbose
|
|
13190
|
+
});
|
|
13191
|
+
if (!result.ok) {
|
|
13192
|
+
printError(`Couldn't send feedback: ${result.error}`);
|
|
13193
|
+
printInfo(`Your message: ${message}`);
|
|
13194
|
+
process.exit(1);
|
|
13195
|
+
}
|
|
13196
|
+
printInfo("Thanks, feedback sent!");
|
|
12980
13197
|
}
|
|
12981
13198
|
|
|
13199
|
+
// src/commands/analyze.ts
|
|
13200
|
+
var import_node_fs19 = require("node:fs");
|
|
13201
|
+
var import_node_path15 = require("node:path");
|
|
13202
|
+
|
|
12982
13203
|
// src/lib/files.ts
|
|
12983
13204
|
var import_node_fs8 = require("node:fs");
|
|
12984
|
-
var
|
|
13205
|
+
var import_node_path9 = require("node:path");
|
|
12985
13206
|
var LANG_MAP = {
|
|
12986
13207
|
// Analyzable (static analysis + Gemini)
|
|
12987
13208
|
ts: "typescript",
|
|
@@ -13049,7 +13270,7 @@ var LANG_MAP = {
|
|
|
13049
13270
|
mk: "make"
|
|
13050
13271
|
};
|
|
13051
13272
|
function detectLanguage(filepath) {
|
|
13052
|
-
const ext = (0,
|
|
13273
|
+
const ext = (0, import_node_path9.extname)(filepath).slice(1);
|
|
13053
13274
|
return LANG_MAP[ext] ?? ext;
|
|
13054
13275
|
}
|
|
13055
13276
|
function sortByMtime(files) {
|
|
@@ -13335,7 +13556,7 @@ function runCodacyAnalysis(files) {
|
|
|
13335
13556
|
|
|
13336
13557
|
// src/lib/specs.ts
|
|
13337
13558
|
var import_node_fs11 = require("node:fs");
|
|
13338
|
-
var
|
|
13559
|
+
var import_node_path10 = require("node:path");
|
|
13339
13560
|
var SPEC_CANDIDATES = [
|
|
13340
13561
|
"CLAUDE.md",
|
|
13341
13562
|
"AGENTS.md",
|
|
@@ -13397,7 +13618,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
13397
13618
|
try {
|
|
13398
13619
|
const entries = (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true });
|
|
13399
13620
|
for (const entry of entries) {
|
|
13400
|
-
const fullPath = (0,
|
|
13621
|
+
const fullPath = (0, import_node_path10.join)(dir, entry.name);
|
|
13401
13622
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
13402
13623
|
result.push(fullPath);
|
|
13403
13624
|
} else if (entry.isDirectory() && depth < maxDepth - 1) {
|
|
@@ -13409,7 +13630,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
13409
13630
|
return result;
|
|
13410
13631
|
}
|
|
13411
13632
|
function discoverPlans() {
|
|
13412
|
-
const homePlansDir = (0,
|
|
13633
|
+
const homePlansDir = (0, import_node_path10.join)(process.env.HOME ?? "", ".claude", "plans");
|
|
13413
13634
|
const localPlansDir = ".claude/plans";
|
|
13414
13635
|
const candidates = [];
|
|
13415
13636
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -13419,7 +13640,7 @@ function discoverPlans() {
|
|
|
13419
13640
|
for (const f of (0, import_node_fs11.readdirSync)(plansDir)) {
|
|
13420
13641
|
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
13421
13642
|
seen.add(f);
|
|
13422
|
-
const fullPath = (0,
|
|
13643
|
+
const fullPath = (0, import_node_path10.join)(plansDir, f);
|
|
13423
13644
|
try {
|
|
13424
13645
|
const stat3 = (0, import_node_fs11.statSync)(fullPath);
|
|
13425
13646
|
candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
@@ -13444,7 +13665,7 @@ function discoverPlans() {
|
|
|
13444
13665
|
|
|
13445
13666
|
// src/lib/snapshot.ts
|
|
13446
13667
|
var import_node_fs12 = require("node:fs");
|
|
13447
|
-
var
|
|
13668
|
+
var import_node_path11 = require("node:path");
|
|
13448
13669
|
var import_node_child_process7 = require("node:child_process");
|
|
13449
13670
|
function generateSnapshotDiffs(files) {
|
|
13450
13671
|
if (!(0, import_node_fs12.existsSync)(SNAPSHOT_DIR)) {
|
|
@@ -13452,7 +13673,7 @@ function generateSnapshotDiffs(files) {
|
|
|
13452
13673
|
}
|
|
13453
13674
|
const diffs = [];
|
|
13454
13675
|
for (const file of files) {
|
|
13455
|
-
const snapshotPath = (0,
|
|
13676
|
+
const snapshotPath = (0, import_node_path11.join)(SNAPSHOT_DIR, file.path);
|
|
13456
13677
|
const language = file.language ?? detectLanguage(file.path);
|
|
13457
13678
|
if ((0, import_node_fs12.existsSync)(snapshotPath)) {
|
|
13458
13679
|
const oldContent = (0, import_node_fs12.readFileSync)(snapshotPath, "utf-8");
|
|
@@ -13479,16 +13700,16 @@ ${addedLines}`,
|
|
|
13479
13700
|
function saveSnapshots(files) {
|
|
13480
13701
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
13481
13702
|
for (const file of files) {
|
|
13482
|
-
const snapshotPath = (0,
|
|
13703
|
+
const snapshotPath = (0, import_node_path11.join)(SNAPSHOT_DIR, file.path);
|
|
13483
13704
|
snapshotPaths.add(snapshotPath);
|
|
13484
|
-
(0, import_node_fs12.mkdirSync)((0,
|
|
13705
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(snapshotPath), { recursive: true });
|
|
13485
13706
|
(0, import_node_fs12.writeFileSync)(snapshotPath, file.content);
|
|
13486
13707
|
}
|
|
13487
13708
|
cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
|
|
13488
13709
|
}
|
|
13489
13710
|
function computeDiff(oldContent, newContent, filePath) {
|
|
13490
|
-
const tmpOld = (0,
|
|
13491
|
-
const tmpNew = (0,
|
|
13711
|
+
const tmpOld = (0, import_node_path11.join)(SNAPSHOT_DIR, ".diff-old.tmp");
|
|
13712
|
+
const tmpNew = (0, import_node_path11.join)(SNAPSHOT_DIR, ".diff-new.tmp");
|
|
13492
13713
|
try {
|
|
13493
13714
|
(0, import_node_fs12.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
|
|
13494
13715
|
(0, import_node_fs12.writeFileSync)(tmpOld, oldContent);
|
|
@@ -13521,7 +13742,7 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13521
13742
|
const entries = (0, import_node_fs12.readdirSync)(dir, { withFileTypes: true });
|
|
13522
13743
|
for (const entry of entries) {
|
|
13523
13744
|
if (entry.name.startsWith(".")) continue;
|
|
13524
|
-
const fullPath = (0,
|
|
13745
|
+
const fullPath = (0, import_node_path11.join)(dir, entry.name);
|
|
13525
13746
|
if (entry.isDirectory()) {
|
|
13526
13747
|
cleanStaleSnapshots(fullPath, keepSet);
|
|
13527
13748
|
try {
|
|
@@ -13542,7 +13763,7 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13542
13763
|
|
|
13543
13764
|
// src/lib/baseline.ts
|
|
13544
13765
|
var import_node_fs13 = require("node:fs");
|
|
13545
|
-
var
|
|
13766
|
+
var import_node_path12 = require("node:path");
|
|
13546
13767
|
var import_node_crypto5 = require("node:crypto");
|
|
13547
13768
|
var BASELINE_VERSION = 1;
|
|
13548
13769
|
var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -13553,13 +13774,13 @@ function sessionKey(sessionId) {
|
|
|
13553
13774
|
return (0, import_node_crypto5.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
13554
13775
|
}
|
|
13555
13776
|
function sessionDir(key) {
|
|
13556
|
-
return (0,
|
|
13777
|
+
return (0, import_node_path12.join)(projectPath(BASELINE_DIR), key);
|
|
13557
13778
|
}
|
|
13558
13779
|
function manifestPath(dir) {
|
|
13559
|
-
return (0,
|
|
13780
|
+
return (0, import_node_path12.join)(dir, "manifest.json");
|
|
13560
13781
|
}
|
|
13561
13782
|
function mirrorPath(dir, repoRelPath) {
|
|
13562
|
-
return (0,
|
|
13783
|
+
return (0, import_node_path12.join)(dir, "files", repoRelPath);
|
|
13563
13784
|
}
|
|
13564
13785
|
function captureBaseline(opts = {}) {
|
|
13565
13786
|
const key = sessionKey(opts.sessionId);
|
|
@@ -13575,7 +13796,7 @@ function captureBaseline(opts = {}) {
|
|
|
13575
13796
|
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13576
13797
|
} catch {
|
|
13577
13798
|
}
|
|
13578
|
-
const filesDir = (0,
|
|
13799
|
+
const filesDir = (0, import_node_path12.join)(dir, "files");
|
|
13579
13800
|
const mirrored = [];
|
|
13580
13801
|
try {
|
|
13581
13802
|
(0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
|
|
@@ -13585,7 +13806,7 @@ function captureBaseline(opts = {}) {
|
|
|
13585
13806
|
if (content === null) continue;
|
|
13586
13807
|
const dest = mirrorPath(dir, p);
|
|
13587
13808
|
try {
|
|
13588
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
13809
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(dest), { recursive: true });
|
|
13589
13810
|
(0, import_node_fs13.writeFileSync)(dest, content);
|
|
13590
13811
|
mirrored.push(p);
|
|
13591
13812
|
} catch {
|
|
@@ -13713,7 +13934,7 @@ function pruneOldBaselines() {
|
|
|
13713
13934
|
}
|
|
13714
13935
|
const now = Date.now();
|
|
13715
13936
|
for (const name of entries) {
|
|
13716
|
-
const dir = (0,
|
|
13937
|
+
const dir = (0, import_node_path12.join)(root, name);
|
|
13717
13938
|
const manifest = readManifest(dir);
|
|
13718
13939
|
if (!manifest) {
|
|
13719
13940
|
try {
|
|
@@ -13812,7 +14033,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13812
14033
|
|
|
13813
14034
|
// src/lib/cache-cleanup.ts
|
|
13814
14035
|
var import_node_fs16 = require("node:fs");
|
|
13815
|
-
var
|
|
14036
|
+
var import_node_path13 = require("node:path");
|
|
13816
14037
|
var CACHE_TTL_DAYS = 7;
|
|
13817
14038
|
function pruneStaleCache() {
|
|
13818
14039
|
try {
|
|
@@ -13820,7 +14041,7 @@ function pruneStaleCache() {
|
|
|
13820
14041
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
13821
14042
|
for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
|
|
13822
14043
|
if (!entry.startsWith("pending-")) continue;
|
|
13823
|
-
const path = (0,
|
|
14044
|
+
const path = (0, import_node_path13.join)(dir, entry);
|
|
13824
14045
|
try {
|
|
13825
14046
|
const stat3 = (0, import_node_fs16.statSync)(path);
|
|
13826
14047
|
if (stat3.mtimeMs < cutoff) {
|
|
@@ -14211,9 +14432,9 @@ function capArray(set, max) {
|
|
|
14211
14432
|
}
|
|
14212
14433
|
|
|
14213
14434
|
// src/lib/seed-runner.ts
|
|
14214
|
-
var
|
|
14435
|
+
var import_promises12 = require("node:fs/promises");
|
|
14215
14436
|
var import_node_fs18 = require("node:fs");
|
|
14216
|
-
var
|
|
14437
|
+
var import_node_path14 = require("node:path");
|
|
14217
14438
|
var import_yaml2 = __toESM(require_dist());
|
|
14218
14439
|
|
|
14219
14440
|
// src/lib/seed.ts
|
|
@@ -14457,7 +14678,7 @@ async function runSeed(opts) {
|
|
|
14457
14678
|
}
|
|
14458
14679
|
let standardDoc;
|
|
14459
14680
|
try {
|
|
14460
|
-
const raw = await (0,
|
|
14681
|
+
const raw = await (0, import_promises12.readFile)(STANDARD_FILE, "utf-8");
|
|
14461
14682
|
standardDoc = (0, import_yaml2.parse)(raw);
|
|
14462
14683
|
} catch {
|
|
14463
14684
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
@@ -14466,7 +14687,7 @@ async function runSeed(opts) {
|
|
|
14466
14687
|
let readmeContent;
|
|
14467
14688
|
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
14468
14689
|
try {
|
|
14469
|
-
readmeContent = await (0,
|
|
14690
|
+
readmeContent = await (0, import_promises12.readFile)("README.md", "utf-8");
|
|
14470
14691
|
} catch {
|
|
14471
14692
|
}
|
|
14472
14693
|
}
|
|
@@ -14474,7 +14695,7 @@ async function runSeed(opts) {
|
|
|
14474
14695
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
14475
14696
|
if ((0, import_node_fs18.existsSync)(p)) {
|
|
14476
14697
|
try {
|
|
14477
|
-
claudeMdContent = await (0,
|
|
14698
|
+
claudeMdContent = await (0, import_promises12.readFile)(p, "utf-8");
|
|
14478
14699
|
break;
|
|
14479
14700
|
} catch {
|
|
14480
14701
|
}
|
|
@@ -14495,7 +14716,7 @@ async function runSeed(opts) {
|
|
|
14495
14716
|
if (candidates.length === 0) {
|
|
14496
14717
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
14497
14718
|
}
|
|
14498
|
-
const overviewPath = (0,
|
|
14719
|
+
const overviewPath = (0, import_node_path14.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
14499
14720
|
if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
|
|
14500
14721
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
14501
14722
|
}
|
|
@@ -14531,10 +14752,10 @@ async function runSeed(opts) {
|
|
|
14531
14752
|
}
|
|
14532
14753
|
const nodeId = res.data.node_id;
|
|
14533
14754
|
const filePathRel = res.data.file_path;
|
|
14534
|
-
const targetPath = (0,
|
|
14755
|
+
const targetPath = (0, import_node_path14.join)(MEMORY_DIR, filePathRel);
|
|
14535
14756
|
try {
|
|
14536
|
-
await (0,
|
|
14537
|
-
await (0,
|
|
14757
|
+
await (0, import_promises12.mkdir)((0, import_node_path14.dirname)(targetPath), { recursive: true });
|
|
14758
|
+
await (0, import_promises12.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
14538
14759
|
created++;
|
|
14539
14760
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
14540
14761
|
} catch (err) {
|
|
@@ -14586,6 +14807,27 @@ function passAndExit(reason) {
|
|
|
14586
14807
|
printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
|
|
14587
14808
|
process.exit(0);
|
|
14588
14809
|
}
|
|
14810
|
+
var EMPTY_STATIC = {
|
|
14811
|
+
tool: "@codacy/analysis-cli",
|
|
14812
|
+
findings: [],
|
|
14813
|
+
summary: { total_findings: 0, by_severity: {}, tools_run: [] }
|
|
14814
|
+
};
|
|
14815
|
+
function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
|
|
14816
|
+
if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
|
|
14817
|
+
let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14818
|
+
if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
14819
|
+
if (scannable.length === 0) return EMPTY_STATIC;
|
|
14820
|
+
return runCodacyAnalysis(scannable);
|
|
14821
|
+
}
|
|
14822
|
+
function localOnlyAndExit(staticResults) {
|
|
14823
|
+
printJsonCompact({
|
|
14824
|
+
gate_decision: "PASS",
|
|
14825
|
+
systemMessage: "Verity: not authenticated \u2014 ran a local static-only check (no deep review, no upload). Run `verity init` to authenticate and enable the full quality gate.",
|
|
14826
|
+
unauthenticated: true,
|
|
14827
|
+
static_results: staticResults
|
|
14828
|
+
});
|
|
14829
|
+
process.exit(0);
|
|
14830
|
+
}
|
|
14589
14831
|
function registerAnalyzeCommand(program2) {
|
|
14590
14832
|
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
14591
14833
|
const globals = program2.opts();
|
|
@@ -14636,12 +14878,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14636
14878
|
passAndExit("Reflection-prompt turn \u2014 skipping analysis");
|
|
14637
14879
|
}
|
|
14638
14880
|
const tokenResult = await resolveToken(globals.token);
|
|
14639
|
-
if (!tokenResult.ok) {
|
|
14640
|
-
passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
|
|
14641
|
-
}
|
|
14642
14881
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
14643
|
-
if (!urlResult.ok) {
|
|
14644
|
-
|
|
14882
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
14883
|
+
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
14645
14884
|
}
|
|
14646
14885
|
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
14647
14886
|
let contextFilePaths = [];
|
|
@@ -14797,7 +15036,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14797
15036
|
let autoSeedNotice = null;
|
|
14798
15037
|
try {
|
|
14799
15038
|
await ensureMemoryDir();
|
|
14800
|
-
const seedMarker = (0,
|
|
15039
|
+
const seedMarker = (0, import_node_path15.join)(VERITY_DIR, ".seeded");
|
|
14801
15040
|
const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
|
|
14802
15041
|
const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
|
|
14803
15042
|
if (hasStandard && !alreadyTried) {
|
|
@@ -15215,13 +15454,14 @@ async function runReview(opts, globals) {
|
|
|
15215
15454
|
}
|
|
15216
15455
|
const codeDelta = collectCodeDelta(allFiles);
|
|
15217
15456
|
const tokenResult = await resolveToken(globals.token);
|
|
15218
|
-
if (!tokenResult.ok) {
|
|
15219
|
-
printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
|
|
15220
|
-
process.exit(0);
|
|
15221
|
-
}
|
|
15222
15457
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
15223
|
-
if (!urlResult.ok) {
|
|
15224
|
-
|
|
15458
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
15459
|
+
printJsonCompact({
|
|
15460
|
+
gate_decision: "PASS",
|
|
15461
|
+
systemMessage: "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
|
|
15462
|
+
unauthenticated: true,
|
|
15463
|
+
static_results: staticResults
|
|
15464
|
+
});
|
|
15225
15465
|
process.exit(0);
|
|
15226
15466
|
}
|
|
15227
15467
|
let specs;
|
|
@@ -15290,9 +15530,9 @@ async function runReview(opts, globals) {
|
|
|
15290
15530
|
|
|
15291
15531
|
// src/commands/guard.ts
|
|
15292
15532
|
var import_node_fs22 = require("node:fs");
|
|
15293
|
-
var
|
|
15533
|
+
var import_node_path16 = require("node:path");
|
|
15294
15534
|
var GUARD_BLOCK_CAP = 2;
|
|
15295
|
-
var GUARD_ITER_FILE = (0,
|
|
15535
|
+
var GUARD_ITER_FILE = (0, import_node_path16.join)(VERITY_DIR, ".guard-iteration");
|
|
15296
15536
|
function readPreToolUseStdin() {
|
|
15297
15537
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
15298
15538
|
return new Promise((resolve) => {
|
|
@@ -15607,13 +15847,14 @@ function writeBlockMessage(moment, response) {
|
|
|
15607
15847
|
|
|
15608
15848
|
// src/commands/init.ts
|
|
15609
15849
|
var import_node_fs24 = require("node:fs");
|
|
15610
|
-
var
|
|
15611
|
-
var
|
|
15850
|
+
var import_promises13 = require("node:fs/promises");
|
|
15851
|
+
var import_node_path18 = require("node:path");
|
|
15612
15852
|
var import_node_child_process9 = require("node:child_process");
|
|
15853
|
+
var readline = __toESM(require("node:readline/promises"));
|
|
15613
15854
|
|
|
15614
15855
|
// src/commands/migrate.ts
|
|
15615
15856
|
var import_node_fs23 = require("node:fs");
|
|
15616
|
-
var
|
|
15857
|
+
var import_node_path17 = require("node:path");
|
|
15617
15858
|
var import_node_child_process8 = require("node:child_process");
|
|
15618
15859
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
15619
15860
|
function defaultNpmRemover(pkg) {
|
|
@@ -15649,8 +15890,8 @@ async function runMigration(opts = {}) {
|
|
|
15649
15890
|
return { actions, migrated: actions.length > 0 };
|
|
15650
15891
|
}
|
|
15651
15892
|
function migrateProjectDir(root, actions) {
|
|
15652
|
-
const gateDir = (0,
|
|
15653
|
-
const verityDir = (0,
|
|
15893
|
+
const gateDir = (0, import_node_path17.join)(root, ".gate");
|
|
15894
|
+
const verityDir = (0, import_node_path17.join)(root, ".verity");
|
|
15654
15895
|
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
|
|
15655
15896
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
15656
15897
|
}
|
|
@@ -15704,11 +15945,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
15704
15945
|
}
|
|
15705
15946
|
function migrateGlobalCredentials(home, actions) {
|
|
15706
15947
|
if (!home) return;
|
|
15707
|
-
const gateCreds = (0,
|
|
15708
|
-
const verityCreds = (0,
|
|
15948
|
+
const gateCreds = (0, import_node_path17.join)(home, ".gate", "credentials");
|
|
15949
|
+
const verityCreds = (0, import_node_path17.join)(home, ".verity", "credentials");
|
|
15709
15950
|
if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
|
|
15710
15951
|
if (!(0, import_node_fs23.existsSync)(verityCreds)) {
|
|
15711
|
-
(0, import_node_fs23.mkdirSync)((0,
|
|
15952
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path17.join)(home, ".verity"), { recursive: true });
|
|
15712
15953
|
moveFile(gateCreds, verityCreds);
|
|
15713
15954
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
15714
15955
|
return;
|
|
@@ -15730,7 +15971,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
15730
15971
|
}
|
|
15731
15972
|
}
|
|
15732
15973
|
async function migrateClaudeMd(root, actions) {
|
|
15733
|
-
const claudeMd = (0,
|
|
15974
|
+
const claudeMd = (0, import_node_path17.join)(root, "CLAUDE.md");
|
|
15734
15975
|
const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
15735
15976
|
if (!hadLegacyBlock) return;
|
|
15736
15977
|
try {
|
|
@@ -15741,8 +15982,8 @@ async function migrateClaudeMd(root, actions) {
|
|
|
15741
15982
|
}
|
|
15742
15983
|
}
|
|
15743
15984
|
function migrateStandardFile(root, actions) {
|
|
15744
|
-
const gateMd = (0,
|
|
15745
|
-
const verityMd = (0,
|
|
15985
|
+
const gateMd = (0, import_node_path17.join)(root, "GATE.md");
|
|
15986
|
+
const verityMd = (0, import_node_path17.join)(root, "VERITY.md");
|
|
15746
15987
|
if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
|
|
15747
15988
|
let moved = false;
|
|
15748
15989
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
@@ -15830,15 +16071,15 @@ function moveFile(from, to) {
|
|
|
15830
16071
|
function carryLegacyContents(gateDir, verityDir) {
|
|
15831
16072
|
let copied = 0;
|
|
15832
16073
|
const walk = (relDir) => {
|
|
15833
|
-
const srcDir = (0,
|
|
16074
|
+
const srcDir = (0, import_node_path17.join)(gateDir, relDir);
|
|
15834
16075
|
for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
|
|
15835
|
-
const rel = relDir ? (0,
|
|
15836
|
-
const src = (0,
|
|
15837
|
-
const dest = (0,
|
|
16076
|
+
const rel = relDir ? (0, import_node_path17.join)(relDir, entry) : entry;
|
|
16077
|
+
const src = (0, import_node_path17.join)(gateDir, rel);
|
|
16078
|
+
const dest = (0, import_node_path17.join)(verityDir, rel);
|
|
15838
16079
|
if ((0, import_node_fs23.statSync)(src).isDirectory()) {
|
|
15839
16080
|
walk(rel);
|
|
15840
16081
|
} else if (!(0, import_node_fs23.existsSync)(dest)) {
|
|
15841
|
-
(0, import_node_fs23.mkdirSync)((0,
|
|
16082
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path17.dirname)(dest), { recursive: true });
|
|
15842
16083
|
(0, import_node_fs23.cpSync)(src, dest);
|
|
15843
16084
|
copied++;
|
|
15844
16085
|
}
|
|
@@ -15848,22 +16089,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
15848
16089
|
return copied;
|
|
15849
16090
|
}
|
|
15850
16091
|
async function needsMigration(root = repoRoot()) {
|
|
15851
|
-
const gateDir = (0,
|
|
15852
|
-
const verityDir = (0,
|
|
16092
|
+
const gateDir = (0, import_node_path17.join)(root, ".gate");
|
|
16093
|
+
const verityDir = (0, import_node_path17.join)(root, ".verity");
|
|
15853
16094
|
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
|
|
15854
16095
|
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
15855
|
-
if ((0, import_node_fs23.existsSync)((0,
|
|
16096
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(verityDir, "credentials"))) {
|
|
15856
16097
|
return true;
|
|
15857
16098
|
}
|
|
15858
|
-
if ((0, import_node_fs23.existsSync)((0,
|
|
16099
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(verityDir, "memory"))) {
|
|
15859
16100
|
return true;
|
|
15860
16101
|
}
|
|
15861
16102
|
}
|
|
15862
|
-
const claudeMd = (0,
|
|
16103
|
+
const claudeMd = (0, import_node_path17.join)(root, "CLAUDE.md");
|
|
15863
16104
|
if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
15864
16105
|
return true;
|
|
15865
16106
|
}
|
|
15866
|
-
if ((0, import_node_fs23.existsSync)((0,
|
|
16107
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(root, "VERITY.md"))) {
|
|
15867
16108
|
return true;
|
|
15868
16109
|
}
|
|
15869
16110
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -15889,17 +16130,77 @@ function registerMigrateCommand(program2) {
|
|
|
15889
16130
|
}
|
|
15890
16131
|
|
|
15891
16132
|
// src/commands/init.ts
|
|
16133
|
+
async function promptYes(question) {
|
|
16134
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
16135
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
16136
|
+
try {
|
|
16137
|
+
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
16138
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
16139
|
+
} finally {
|
|
16140
|
+
rl.close();
|
|
16141
|
+
}
|
|
16142
|
+
}
|
|
16143
|
+
async function runOptionalAuth() {
|
|
16144
|
+
const existing = await resolveToken();
|
|
16145
|
+
if (existing.ok) {
|
|
16146
|
+
printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
|
|
16147
|
+
return;
|
|
16148
|
+
}
|
|
16149
|
+
let remote = "";
|
|
16150
|
+
try {
|
|
16151
|
+
remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
16152
|
+
} catch {
|
|
16153
|
+
}
|
|
16154
|
+
const localOnlyNote = () => {
|
|
16155
|
+
printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
|
|
16156
|
+
printInfo(' Authenticate anytime: run "verity init" again, or "verity auth register".');
|
|
16157
|
+
};
|
|
16158
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
16159
|
+
console.log("");
|
|
16160
|
+
console.log(" Signing in is optional. What it does:");
|
|
16161
|
+
console.log(" - Confirms you have write access to this repository. The GitHub token");
|
|
16162
|
+
console.log(" is used once to verify that, then discarded \u2014 Verity never stores it.");
|
|
16163
|
+
console.log(" - It does NOT give Verity access to your code. Code checked by the gate");
|
|
16164
|
+
console.log(" is analyzed in memory and discarded \u2014 we never store your code.");
|
|
16165
|
+
console.log(" - It is required to store and access run history for this repo");
|
|
16166
|
+
console.log(" (past results, trends, and shareable reports).");
|
|
16167
|
+
console.log(" - Skip and Verity still works fully locally: the gate runs and shows");
|
|
16168
|
+
console.log(" findings, but nothing is uploaded.");
|
|
16169
|
+
console.log("");
|
|
16170
|
+
}
|
|
16171
|
+
const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
|
|
16172
|
+
if (!wantsAuth) {
|
|
16173
|
+
printInfo("Skipped authentication.");
|
|
16174
|
+
localOnlyNote();
|
|
16175
|
+
return;
|
|
16176
|
+
}
|
|
16177
|
+
if (!remote) {
|
|
16178
|
+
printWarn("No git remote found \u2014 cannot authenticate yet.");
|
|
16179
|
+
localOnlyNote();
|
|
16180
|
+
return;
|
|
16181
|
+
}
|
|
16182
|
+
const projectName = parseRemote(remote)?.repo ?? (0, import_node_path18.basename)(process.cwd());
|
|
16183
|
+
printInfo("Authenticating with GitHub\u2026");
|
|
16184
|
+
const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
|
|
16185
|
+
if (result.ok) {
|
|
16186
|
+
printInfo(`Project registered: ${result.data.projectId} \u2713`);
|
|
16187
|
+
if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
|
|
16188
|
+
} else {
|
|
16189
|
+
printWarn(`Authentication did not complete: ${result.error}`);
|
|
16190
|
+
localOnlyNote();
|
|
16191
|
+
}
|
|
16192
|
+
}
|
|
15892
16193
|
function resolveDataDir() {
|
|
15893
16194
|
const candidates = [
|
|
15894
|
-
(0,
|
|
16195
|
+
(0, import_node_path18.join)(__dirname, "..", "data"),
|
|
15895
16196
|
// installed: node_modules/@codacy/verity-cli/data
|
|
15896
|
-
(0,
|
|
16197
|
+
(0, import_node_path18.join)(__dirname, "..", "..", "data"),
|
|
15897
16198
|
// edge case: nested resolution
|
|
15898
|
-
(0,
|
|
16199
|
+
(0, import_node_path18.join)(process.cwd(), "cli", "data")
|
|
15899
16200
|
// local dev: running from repo root
|
|
15900
16201
|
];
|
|
15901
16202
|
for (const candidate of candidates) {
|
|
15902
|
-
if ((0, import_node_fs24.existsSync)((0,
|
|
16203
|
+
if ((0, import_node_fs24.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
|
|
15903
16204
|
return candidate;
|
|
15904
16205
|
}
|
|
15905
16206
|
}
|
|
@@ -15908,8 +16209,8 @@ function resolveDataDir() {
|
|
|
15908
16209
|
);
|
|
15909
16210
|
}
|
|
15910
16211
|
async function copyDir(src, dest) {
|
|
15911
|
-
await (0,
|
|
15912
|
-
await (0,
|
|
16212
|
+
await (0, import_promises13.mkdir)(dest, { recursive: true });
|
|
16213
|
+
await (0, import_promises13.cp)(src, dest, { recursive: true, force: true });
|
|
15913
16214
|
}
|
|
15914
16215
|
function registerInitCommand(program2) {
|
|
15915
16216
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
@@ -15978,24 +16279,24 @@ function registerInitCommand(program2) {
|
|
|
15978
16279
|
console.log("");
|
|
15979
16280
|
printInfo("Installing skills...");
|
|
15980
16281
|
const dataDir = resolveDataDir();
|
|
15981
|
-
const skillsSource = (0,
|
|
16282
|
+
const skillsSource = (0, import_node_path18.join)(dataDir, "skills");
|
|
15982
16283
|
const skillsDest = ".claude/skills";
|
|
15983
16284
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
15984
16285
|
let skillsInstalled = 0;
|
|
15985
16286
|
for (const skill of skills) {
|
|
15986
|
-
const src = (0,
|
|
15987
|
-
const dest = (0,
|
|
16287
|
+
const src = (0, import_node_path18.join)(skillsSource, skill);
|
|
16288
|
+
const dest = (0, import_node_path18.join)(skillsDest, skill);
|
|
15988
16289
|
if (!(0, import_node_fs24.existsSync)(src)) {
|
|
15989
16290
|
printWarn(` Skill data not found: ${skill}`);
|
|
15990
16291
|
continue;
|
|
15991
16292
|
}
|
|
15992
16293
|
if ((0, import_node_fs24.existsSync)(dest) && !force) {
|
|
15993
|
-
const srcSkill = (0,
|
|
15994
|
-
const destSkill = (0,
|
|
16294
|
+
const srcSkill = (0, import_node_path18.join)(src, "SKILL.md");
|
|
16295
|
+
const destSkill = (0, import_node_path18.join)(dest, "SKILL.md");
|
|
15995
16296
|
if ((0, import_node_fs24.existsSync)(destSkill)) {
|
|
15996
16297
|
try {
|
|
15997
|
-
const srcContent = await (0,
|
|
15998
|
-
const destContent = await (0,
|
|
16298
|
+
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
16299
|
+
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
15999
16300
|
if (srcContent === destContent) {
|
|
16000
16301
|
skillsInstalled++;
|
|
16001
16302
|
continue;
|
|
@@ -16021,7 +16322,7 @@ function registerInitCommand(program2) {
|
|
|
16021
16322
|
printWarn(` ${hookResult.error}`);
|
|
16022
16323
|
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
16023
16324
|
}
|
|
16024
|
-
await (0,
|
|
16325
|
+
await (0, import_promises13.mkdir)(VERITY_DIR, { recursive: true });
|
|
16025
16326
|
await ensureMemoryDir();
|
|
16026
16327
|
try {
|
|
16027
16328
|
await ensureClaudeMdPointer();
|
|
@@ -16029,8 +16330,14 @@ function registerInitCommand(program2) {
|
|
|
16029
16330
|
} catch (err) {
|
|
16030
16331
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
16031
16332
|
}
|
|
16032
|
-
const globalVerityDir = (0,
|
|
16033
|
-
await (0,
|
|
16333
|
+
const globalVerityDir = (0, import_node_path18.join)(process.env.HOME ?? "", ".verity");
|
|
16334
|
+
await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
|
|
16335
|
+
console.log("");
|
|
16336
|
+
try {
|
|
16337
|
+
await runOptionalAuth();
|
|
16338
|
+
} catch (err) {
|
|
16339
|
+
printWarn(`Authentication step skipped: ${err.message}`);
|
|
16340
|
+
}
|
|
16034
16341
|
console.log("");
|
|
16035
16342
|
printInfo("Verity initialized!");
|
|
16036
16343
|
console.log("");
|
|
@@ -16047,13 +16354,14 @@ function registerInitCommand(program2) {
|
|
|
16047
16354
|
console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
|
|
16048
16355
|
console.log("");
|
|
16049
16356
|
console.log(" Next step: open this project in Claude Code and run /verity-setup");
|
|
16357
|
+
console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity auth register".)');
|
|
16050
16358
|
console.log("");
|
|
16051
16359
|
});
|
|
16052
16360
|
}
|
|
16053
16361
|
|
|
16054
16362
|
// src/commands/uninstall.ts
|
|
16055
16363
|
var import_node_fs25 = require("node:fs");
|
|
16056
|
-
var
|
|
16364
|
+
var import_node_path19 = require("node:path");
|
|
16057
16365
|
var SKILL_NAMES = [
|
|
16058
16366
|
"verity-setup",
|
|
16059
16367
|
"verity-analyze",
|
|
@@ -16072,7 +16380,7 @@ function registerUninstallCommand(program2) {
|
|
|
16072
16380
|
const actions = [];
|
|
16073
16381
|
const skillsRoot = projectPath(".claude/skills");
|
|
16074
16382
|
for (const name of SKILL_NAMES) {
|
|
16075
|
-
const dir = (0,
|
|
16383
|
+
const dir = (0, import_node_path19.join)(skillsRoot, name);
|
|
16076
16384
|
if ((0, import_node_fs25.existsSync)(dir)) {
|
|
16077
16385
|
actions.push({
|
|
16078
16386
|
label: `Remove .claude/skills/${name}/`,
|
|
@@ -16118,7 +16426,7 @@ function registerUninstallCommand(program2) {
|
|
|
16118
16426
|
}
|
|
16119
16427
|
});
|
|
16120
16428
|
const home = process.env.HOME ?? "";
|
|
16121
|
-
const globalVerityDir = (0,
|
|
16429
|
+
const globalVerityDir = (0, import_node_path19.join)(home, ".verity");
|
|
16122
16430
|
if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
|
|
16123
16431
|
actions.push({
|
|
16124
16432
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
@@ -16317,7 +16625,7 @@ function registerTaskCommands(program2) {
|
|
|
16317
16625
|
|
|
16318
16626
|
// src/commands/reset.ts
|
|
16319
16627
|
var import_node_fs26 = require("node:fs");
|
|
16320
|
-
var
|
|
16628
|
+
var import_node_path20 = require("node:path");
|
|
16321
16629
|
function registerResetCommand(program2) {
|
|
16322
16630
|
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) => {
|
|
16323
16631
|
const globals = program2.opts();
|
|
@@ -16358,7 +16666,7 @@ function registerResetCommand(program2) {
|
|
|
16358
16666
|
for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
|
|
16359
16667
|
if (entry.startsWith("pending-")) {
|
|
16360
16668
|
try {
|
|
16361
|
-
(0, import_node_fs26.unlinkSync)((0,
|
|
16669
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path20.join)(cacheDir, entry));
|
|
16362
16670
|
purged++;
|
|
16363
16671
|
} catch {
|
|
16364
16672
|
}
|
|
@@ -16385,7 +16693,7 @@ function registerResetCommand(program2) {
|
|
|
16385
16693
|
if ((0, import_node_fs26.existsSync)(logsDir)) {
|
|
16386
16694
|
for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
|
|
16387
16695
|
try {
|
|
16388
|
-
(0, import_node_fs26.unlinkSync)((0,
|
|
16696
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path20.join)(logsDir, entry));
|
|
16389
16697
|
} catch {
|
|
16390
16698
|
}
|
|
16391
16699
|
}
|
|
@@ -16643,8 +16951,7 @@ function registerRunCommand(program2) {
|
|
|
16643
16951
|
}
|
|
16644
16952
|
|
|
16645
16953
|
// src/lib/telemetry.ts
|
|
16646
|
-
var
|
|
16647
|
-
var import_node_path20 = require("node:path");
|
|
16954
|
+
var import_promises14 = require("node:fs/promises");
|
|
16648
16955
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
16649
16956
|
var GITIGNORE_FILE = ".gitignore";
|
|
16650
16957
|
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
@@ -16675,21 +16982,19 @@ function buildTelemetryEnv(serviceUrl, token) {
|
|
|
16675
16982
|
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv("", ""));
|
|
16676
16983
|
async function readSettingsLocal() {
|
|
16677
16984
|
try {
|
|
16678
|
-
return JSON.parse(await (0,
|
|
16985
|
+
return JSON.parse(await (0, import_promises14.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
16679
16986
|
} catch {
|
|
16680
16987
|
return {};
|
|
16681
16988
|
}
|
|
16682
16989
|
}
|
|
16683
16990
|
async function writeSettingsLocal(settings) {
|
|
16684
|
-
|
|
16685
|
-
await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
|
|
16686
|
-
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
16991
|
+
await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
|
|
16687
16992
|
}
|
|
16688
16993
|
async function ensureGitignore() {
|
|
16689
16994
|
const file = projectPath(GITIGNORE_FILE);
|
|
16690
16995
|
let content = "";
|
|
16691
16996
|
try {
|
|
16692
|
-
content = await (0,
|
|
16997
|
+
content = await (0, import_promises14.readFile)(file, "utf-8");
|
|
16693
16998
|
} catch {
|
|
16694
16999
|
}
|
|
16695
17000
|
const lines = content.split("\n").map((l) => l.trim());
|
|
@@ -16698,7 +17003,7 @@ async function ensureGitignore() {
|
|
|
16698
17003
|
}
|
|
16699
17004
|
const block = "# Verity telemetry \u2014 holds your project token\n" + GITIGNORE_ENTRY + "\n";
|
|
16700
17005
|
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
16701
|
-
await (0,
|
|
17006
|
+
await (0, import_promises14.writeFile)(file, next);
|
|
16702
17007
|
}
|
|
16703
17008
|
async function installTelemetry(serviceUrl, token) {
|
|
16704
17009
|
const env = buildTelemetryEnv(serviceUrl, token);
|
|
@@ -16778,7 +17083,7 @@ function registerTelemetryCommands(program2) {
|
|
|
16778
17083
|
}
|
|
16779
17084
|
|
|
16780
17085
|
// src/cli.ts
|
|
16781
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
17086
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.26.0-experimental.d7dfc00").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
16782
17087
|
registerAuthCommands(program);
|
|
16783
17088
|
registerHooksCommands(program);
|
|
16784
17089
|
registerIntentCommands(program);
|