@codacy/verity-cli 0.24.0 → 0.25.0-experimental.e3da48a
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 +1022 -530
- package/data/skills/verity-setup/SKILL.md +11 -4
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10326,8 +10326,8 @@ var {
|
|
|
10326
10326
|
|
|
10327
10327
|
// src/commands/auth.ts
|
|
10328
10328
|
var import_promises3 = require("node:fs/promises");
|
|
10329
|
-
var
|
|
10330
|
-
var
|
|
10329
|
+
var import_node_child_process4 = require("node:child_process");
|
|
10330
|
+
var import_node_path3 = require("node:path");
|
|
10331
10331
|
|
|
10332
10332
|
// src/lib/auth.ts
|
|
10333
10333
|
var import_promises = require("node:fs/promises");
|
|
@@ -10379,6 +10379,7 @@ var MAX_PLAN_FILES = 3;
|
|
|
10379
10379
|
var MAX_PLAN_FILE_BYTES = 10240;
|
|
10380
10380
|
var MAX_INTENT_CHARS = 2e3;
|
|
10381
10381
|
var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
|
|
10382
|
+
var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
|
|
10382
10383
|
var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
|
|
10383
10384
|
var CONVERSATION_MAX_ENTRIES = 10;
|
|
10384
10385
|
var CONVERSATION_WINDOW_MINUTES = 15;
|
|
@@ -10473,7 +10474,12 @@ var SECURITY_PATTERNS = [
|
|
|
10473
10474
|
/Cargo\.lock$/,
|
|
10474
10475
|
/Dockerfile/
|
|
10475
10476
|
];
|
|
10476
|
-
var
|
|
10477
|
+
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10478
|
+
var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10479
|
+
var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
|
|
10480
|
+
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10481
|
+
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
10482
|
+
var GITHUB_OAUTH_SCOPES = "repo user:email";
|
|
10477
10483
|
|
|
10478
10484
|
// src/lib/auth.ts
|
|
10479
10485
|
async function resolveToken(flagToken) {
|
|
@@ -10639,7 +10645,8 @@ async function apiRequest(options) {
|
|
|
10639
10645
|
timeout = 9e4,
|
|
10640
10646
|
cmd = "unknown",
|
|
10641
10647
|
retry = false,
|
|
10642
|
-
encodeBody = false
|
|
10648
|
+
encodeBody = false,
|
|
10649
|
+
extraHeaders
|
|
10643
10650
|
} = options;
|
|
10644
10651
|
const url = `${serviceUrl}${path}`;
|
|
10645
10652
|
const headers = {
|
|
@@ -10648,6 +10655,9 @@ async function apiRequest(options) {
|
|
|
10648
10655
|
if (token) {
|
|
10649
10656
|
headers["Authorization"] = `Bearer ${token}`;
|
|
10650
10657
|
}
|
|
10658
|
+
if (extraHeaders) {
|
|
10659
|
+
Object.assign(headers, extraHeaders);
|
|
10660
|
+
}
|
|
10651
10661
|
const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
|
|
10652
10662
|
if (testMockScenario) {
|
|
10653
10663
|
headers["X-Verity-Mock-Scenario"] = testMockScenario;
|
|
@@ -10738,6 +10748,324 @@ function analyzeRequest(options) {
|
|
|
10738
10748
|
});
|
|
10739
10749
|
}
|
|
10740
10750
|
|
|
10751
|
+
// src/lib/provider-auth.ts
|
|
10752
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10753
|
+
var form = (fields) => new URLSearchParams(fields).toString();
|
|
10754
|
+
async function githubDeviceFlow() {
|
|
10755
|
+
const override = process.env.VERITY_PROVIDER_TOKEN;
|
|
10756
|
+
if (override) return { ok: true, data: override };
|
|
10757
|
+
let dc;
|
|
10758
|
+
try {
|
|
10759
|
+
const res = await fetch(GITHUB_DEVICE_CODE_URL, {
|
|
10760
|
+
method: "POST",
|
|
10761
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10762
|
+
body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_OAUTH_SCOPES })
|
|
10763
|
+
});
|
|
10764
|
+
if (!res.ok) {
|
|
10765
|
+
return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
|
|
10766
|
+
}
|
|
10767
|
+
dc = await res.json();
|
|
10768
|
+
} catch (err) {
|
|
10769
|
+
return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
|
|
10770
|
+
}
|
|
10771
|
+
if (!dc.device_code || !dc.user_code) {
|
|
10772
|
+
return {
|
|
10773
|
+
ok: false,
|
|
10774
|
+
error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
|
|
10775
|
+
};
|
|
10776
|
+
}
|
|
10777
|
+
printInfo("");
|
|
10778
|
+
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
10779
|
+
printInfo(`And enter the code: ${dc.user_code}`);
|
|
10780
|
+
printInfo("Waiting for authorization\u2026");
|
|
10781
|
+
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
10782
|
+
let interval = dc.interval || 5;
|
|
10783
|
+
while (Date.now() < deadline) {
|
|
10784
|
+
await sleep(interval * 1e3);
|
|
10785
|
+
let data;
|
|
10786
|
+
try {
|
|
10787
|
+
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
10788
|
+
method: "POST",
|
|
10789
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10790
|
+
body: form({
|
|
10791
|
+
client_id: GITHUB_CLIENT_ID,
|
|
10792
|
+
device_code: dc.device_code,
|
|
10793
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
10794
|
+
})
|
|
10795
|
+
});
|
|
10796
|
+
data = await res.json().catch(() => ({}));
|
|
10797
|
+
} catch {
|
|
10798
|
+
continue;
|
|
10799
|
+
}
|
|
10800
|
+
if (data.access_token) return { ok: true, data: data.access_token };
|
|
10801
|
+
switch (data.error) {
|
|
10802
|
+
case "authorization_pending":
|
|
10803
|
+
break;
|
|
10804
|
+
case "slow_down":
|
|
10805
|
+
interval += 5;
|
|
10806
|
+
break;
|
|
10807
|
+
case "access_denied":
|
|
10808
|
+
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
10809
|
+
case "expired_token":
|
|
10810
|
+
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
10811
|
+
default:
|
|
10812
|
+
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10815
|
+
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
10816
|
+
}
|
|
10817
|
+
|
|
10818
|
+
// src/lib/git.ts
|
|
10819
|
+
var import_node_child_process3 = require("node:child_process");
|
|
10820
|
+
var import_node_fs2 = require("node:fs");
|
|
10821
|
+
var import_node_path2 = require("node:path");
|
|
10822
|
+
function resolveFile(relpath) {
|
|
10823
|
+
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10824
|
+
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
10825
|
+
try {
|
|
10826
|
+
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10827
|
+
for (const entry of entries) {
|
|
10828
|
+
if (!entry.isDirectory()) continue;
|
|
10829
|
+
const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
|
|
10830
|
+
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10831
|
+
}
|
|
10832
|
+
} catch {
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
return null;
|
|
10836
|
+
}
|
|
10837
|
+
function execGit(cmd) {
|
|
10838
|
+
try {
|
|
10839
|
+
return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10840
|
+
} catch {
|
|
10841
|
+
return "";
|
|
10842
|
+
}
|
|
10843
|
+
}
|
|
10844
|
+
function splitLines(s) {
|
|
10845
|
+
return s.split("\n").filter((l) => l.length > 0);
|
|
10846
|
+
}
|
|
10847
|
+
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
10848
|
+
function readBaselineSha() {
|
|
10849
|
+
if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
10850
|
+
let sha;
|
|
10851
|
+
try {
|
|
10852
|
+
sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10853
|
+
} catch {
|
|
10854
|
+
return null;
|
|
10855
|
+
}
|
|
10856
|
+
if (!SHA_RE.test(sha)) return null;
|
|
10857
|
+
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10858
|
+
if (!reachable) {
|
|
10859
|
+
try {
|
|
10860
|
+
(0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
|
|
10861
|
+
} catch {
|
|
10862
|
+
}
|
|
10863
|
+
return null;
|
|
10864
|
+
}
|
|
10865
|
+
return sha;
|
|
10866
|
+
}
|
|
10867
|
+
function writeBaselineSha(sha) {
|
|
10868
|
+
if (!SHA_RE.test(sha)) return;
|
|
10869
|
+
try {
|
|
10870
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10871
|
+
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10872
|
+
} catch {
|
|
10873
|
+
}
|
|
10874
|
+
}
|
|
10875
|
+
function getChangedFiles() {
|
|
10876
|
+
const sets = /* @__PURE__ */ new Set();
|
|
10877
|
+
let hasRecentCommitFiles = false;
|
|
10878
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
10879
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
10880
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
10881
|
+
const baseline = readBaselineSha();
|
|
10882
|
+
if (baseline) {
|
|
10883
|
+
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
10884
|
+
if (committed.length > 0) {
|
|
10885
|
+
hasRecentCommitFiles = true;
|
|
10886
|
+
for (const f of committed) sets.add(f);
|
|
10887
|
+
}
|
|
10888
|
+
} else {
|
|
10889
|
+
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
10890
|
+
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
10891
|
+
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
10892
|
+
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
10893
|
+
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
10894
|
+
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
10895
|
+
if (recentFiles.length > 0) {
|
|
10896
|
+
hasRecentCommitFiles = true;
|
|
10897
|
+
for (const f of recentFiles) sets.add(f);
|
|
10898
|
+
}
|
|
10899
|
+
}
|
|
10900
|
+
}
|
|
10901
|
+
for (const f of getWorktreeFiles()) sets.add(f);
|
|
10902
|
+
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10903
|
+
return { files: filtered, hasRecentCommitFiles };
|
|
10904
|
+
}
|
|
10905
|
+
function getStagedFiles() {
|
|
10906
|
+
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10907
|
+
}
|
|
10908
|
+
function getDirtyFiles() {
|
|
10909
|
+
const set = /* @__PURE__ */ new Set();
|
|
10910
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
10911
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
10912
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
10913
|
+
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10914
|
+
}
|
|
10915
|
+
function showContentAtRef(ref, repoRelPath) {
|
|
10916
|
+
if (!ref || ref === "no-git") return null;
|
|
10917
|
+
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
10918
|
+
try {
|
|
10919
|
+
return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
10920
|
+
encoding: "utf-8",
|
|
10921
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
10922
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10923
|
+
});
|
|
10924
|
+
} catch {
|
|
10925
|
+
return null;
|
|
10926
|
+
}
|
|
10927
|
+
}
|
|
10928
|
+
function getPushRangeFiles() {
|
|
10929
|
+
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10930
|
+
const resolvers = [
|
|
10931
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
10932
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
10933
|
+
() => {
|
|
10934
|
+
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
10935
|
+
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
10936
|
+
}
|
|
10937
|
+
];
|
|
10938
|
+
for (const resolve of resolvers) {
|
|
10939
|
+
const range = resolve();
|
|
10940
|
+
if (range) return { files: diff(range), range };
|
|
10941
|
+
}
|
|
10942
|
+
const baseline = readBaselineSha();
|
|
10943
|
+
if (baseline) {
|
|
10944
|
+
const files = diff(`${baseline}..HEAD`);
|
|
10945
|
+
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10946
|
+
}
|
|
10947
|
+
const last = diff("HEAD~1..HEAD");
|
|
10948
|
+
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10949
|
+
}
|
|
10950
|
+
function getPushRangeMessages() {
|
|
10951
|
+
const { range } = getPushRangeFiles();
|
|
10952
|
+
if (!range) return "";
|
|
10953
|
+
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10954
|
+
}
|
|
10955
|
+
function getWorktreeFiles() {
|
|
10956
|
+
const result = [];
|
|
10957
|
+
const worktreeDir = ".claude/worktrees";
|
|
10958
|
+
if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
|
|
10959
|
+
try {
|
|
10960
|
+
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
10961
|
+
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
10962
|
+
for (const entry of entries) {
|
|
10963
|
+
if (!entry.isDirectory()) continue;
|
|
10964
|
+
const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
|
|
10965
|
+
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
10966
|
+
}
|
|
10967
|
+
} catch {
|
|
10968
|
+
}
|
|
10969
|
+
return result;
|
|
10970
|
+
}
|
|
10971
|
+
function scanDir(baseDir, dir, minMtime, result) {
|
|
10972
|
+
try {
|
|
10973
|
+
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
10974
|
+
for (const entry of entries) {
|
|
10975
|
+
const fullPath = (0, import_node_path2.join)(dir, entry.name);
|
|
10976
|
+
if (entry.isDirectory()) {
|
|
10977
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
10978
|
+
scanDir(baseDir, fullPath, minMtime, result);
|
|
10979
|
+
} else if (entry.isFile()) {
|
|
10980
|
+
const ext = (0, import_node_path2.extname)(entry.name).slice(1);
|
|
10981
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
10982
|
+
try {
|
|
10983
|
+
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
10984
|
+
if (stat3.mtimeMs >= minMtime) {
|
|
10985
|
+
const relPath = fullPath.slice(baseDir.length + 1);
|
|
10986
|
+
result.push(relPath);
|
|
10987
|
+
}
|
|
10988
|
+
} catch {
|
|
10989
|
+
}
|
|
10990
|
+
}
|
|
10991
|
+
}
|
|
10992
|
+
} catch {
|
|
10993
|
+
}
|
|
10994
|
+
}
|
|
10995
|
+
function filterAnalyzable(files) {
|
|
10996
|
+
return files.filter((f) => {
|
|
10997
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
10998
|
+
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
10999
|
+
});
|
|
11000
|
+
}
|
|
11001
|
+
function filterReviewable(files) {
|
|
11002
|
+
return files.filter((f) => {
|
|
11003
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11004
|
+
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11005
|
+
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11006
|
+
const basename2 = f.split("/").pop() ?? "";
|
|
11007
|
+
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
11008
|
+
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
11009
|
+
return false;
|
|
11010
|
+
});
|
|
11011
|
+
}
|
|
11012
|
+
function filterSecurity(files) {
|
|
11013
|
+
return files.filter(
|
|
11014
|
+
(f) => SECURITY_PATTERNS.some((p) => p.test(f))
|
|
11015
|
+
);
|
|
11016
|
+
}
|
|
11017
|
+
function getCurrentCommit() {
|
|
11018
|
+
return execGit("git rev-parse HEAD") || "no-git";
|
|
11019
|
+
}
|
|
11020
|
+
function detectProvider(host) {
|
|
11021
|
+
const h = host.toLowerCase();
|
|
11022
|
+
if (h.includes("github")) return "github";
|
|
11023
|
+
if (h.includes("gitlab")) return "gitlab";
|
|
11024
|
+
if (h.includes("bitbucket")) return "bitbucket";
|
|
11025
|
+
return "unknown";
|
|
11026
|
+
}
|
|
11027
|
+
function parseRemote(raw) {
|
|
11028
|
+
if (!raw || typeof raw !== "string") return null;
|
|
11029
|
+
let s = raw.trim();
|
|
11030
|
+
if (!s) return null;
|
|
11031
|
+
let host = "";
|
|
11032
|
+
let path = "";
|
|
11033
|
+
const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
|
|
11034
|
+
if (scp) {
|
|
11035
|
+
host = scp[1];
|
|
11036
|
+
path = scp[2];
|
|
11037
|
+
} else {
|
|
11038
|
+
s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
|
|
11039
|
+
s = s.replace(/^[^/@]+@/, "");
|
|
11040
|
+
const slash = s.indexOf("/");
|
|
11041
|
+
if (slash === -1) return null;
|
|
11042
|
+
host = s.slice(0, slash);
|
|
11043
|
+
path = s.slice(slash + 1);
|
|
11044
|
+
}
|
|
11045
|
+
host = host.toLowerCase().trim();
|
|
11046
|
+
path = path.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
11047
|
+
if (!host || !path) return null;
|
|
11048
|
+
const segments = path.split("/").filter(Boolean);
|
|
11049
|
+
if (segments.length < 2) return null;
|
|
11050
|
+
const owner = segments[0];
|
|
11051
|
+
const repo = segments[segments.length - 1];
|
|
11052
|
+
if (!owner || !repo) return null;
|
|
11053
|
+
return {
|
|
11054
|
+
host,
|
|
11055
|
+
owner,
|
|
11056
|
+
repo,
|
|
11057
|
+
provider: detectProvider(host),
|
|
11058
|
+
orgUrl: `https://${host}/${owner}`,
|
|
11059
|
+
orgName: owner
|
|
11060
|
+
};
|
|
11061
|
+
}
|
|
11062
|
+
function listTrackedFiles() {
|
|
11063
|
+
const set = /* @__PURE__ */ new Set();
|
|
11064
|
+
for (const f of splitLines(execGit("git ls-files"))) set.add(f);
|
|
11065
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
11066
|
+
return Array.from(set);
|
|
11067
|
+
}
|
|
11068
|
+
|
|
10741
11069
|
// src/commands/auth.ts
|
|
10742
11070
|
function registerAuthCommands(program2) {
|
|
10743
11071
|
const auth = program2.command("auth").description("Manage project authentication");
|
|
@@ -10747,35 +11075,65 @@ function registerAuthCommands(program2) {
|
|
|
10747
11075
|
let remote = opts.remote;
|
|
10748
11076
|
if (!remote) {
|
|
10749
11077
|
try {
|
|
10750
|
-
remote = (0,
|
|
11078
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10751
11079
|
} catch {
|
|
10752
11080
|
printError("No git remote found. Use --remote to specify one.");
|
|
10753
11081
|
process.exit(1);
|
|
10754
11082
|
}
|
|
10755
11083
|
}
|
|
11084
|
+
const parsed = parseRemote(remote);
|
|
11085
|
+
if (!parsed) {
|
|
11086
|
+
printError(`Could not parse git remote: ${remote}`);
|
|
11087
|
+
process.exit(1);
|
|
11088
|
+
}
|
|
11089
|
+
if (parsed.provider !== "github") {
|
|
11090
|
+
printError(`Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.`);
|
|
11091
|
+
process.exit(1);
|
|
11092
|
+
}
|
|
11093
|
+
const providerAuth = await githubDeviceFlow();
|
|
11094
|
+
if (!providerAuth.ok) {
|
|
11095
|
+
printError(providerAuth.error);
|
|
11096
|
+
process.exit(1);
|
|
11097
|
+
}
|
|
11098
|
+
const providerToken = providerAuth.data;
|
|
10756
11099
|
const result = await apiRequest({
|
|
10757
11100
|
method: "POST",
|
|
10758
11101
|
path: "/auth/register",
|
|
10759
11102
|
serviceUrl,
|
|
10760
11103
|
body: { project_name: opts.project, git_remote_url: remote },
|
|
11104
|
+
extraHeaders: { "X-Provider-Token": providerToken },
|
|
10761
11105
|
verbose: globals.verbose
|
|
10762
11106
|
});
|
|
10763
11107
|
if (!result.ok) {
|
|
10764
11108
|
printError(result.error);
|
|
10765
11109
|
process.exit(1);
|
|
10766
11110
|
}
|
|
10767
|
-
const { project_id, token, service_url } = result.data;
|
|
11111
|
+
const { project_id, token, service_url, user } = result.data;
|
|
10768
11112
|
await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
|
|
10769
|
-
await (0, import_promises3.writeFile)(
|
|
11113
|
+
await (0, import_promises3.writeFile)(
|
|
11114
|
+
CREDENTIALS_FILE,
|
|
11115
|
+
`token: ${token}
|
|
10770
11116
|
service_url: ${service_url}
|
|
10771
|
-
|
|
11117
|
+
provider_token: ${providerToken}
|
|
11118
|
+
`,
|
|
11119
|
+
{ mode: 384 }
|
|
11120
|
+
);
|
|
11121
|
+
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11122
|
+
});
|
|
10772
11123
|
try {
|
|
10773
|
-
await (0, import_promises3.mkdir)((0,
|
|
10774
|
-
await (0, import_promises3.appendFile)(
|
|
10775
|
-
|
|
11124
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
11125
|
+
await (0, import_promises3.appendFile)(
|
|
11126
|
+
GLOBAL_CREDENTIALS_FILE,
|
|
11127
|
+
`${remote} token: ${token}
|
|
11128
|
+
${remote} provider_token: ${providerToken}
|
|
11129
|
+
`
|
|
11130
|
+
);
|
|
11131
|
+
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11132
|
+
});
|
|
10776
11133
|
} catch {
|
|
10777
11134
|
}
|
|
10778
11135
|
printInfo(`Project registered: ${project_id}`);
|
|
11136
|
+
if (user?.email) printInfo(`Authenticated as: ${user.email}`);
|
|
10779
11137
|
printJson({ project_id, service_url });
|
|
10780
11138
|
});
|
|
10781
11139
|
auth.command("verify").description("Verify the current token is valid").action(async () => {
|
|
@@ -10809,7 +11167,7 @@ service_url: ${service_url}
|
|
|
10809
11167
|
let remote = opts.remote;
|
|
10810
11168
|
if (!remote) {
|
|
10811
11169
|
try {
|
|
10812
|
-
remote = (0,
|
|
11170
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10813
11171
|
} catch {
|
|
10814
11172
|
printError("No git remote found. Use --remote to specify one.");
|
|
10815
11173
|
process.exit(1);
|
|
@@ -10837,7 +11195,7 @@ service_url: ${service_url}
|
|
|
10837
11195
|
|
|
10838
11196
|
// src/lib/hooks.ts
|
|
10839
11197
|
var import_promises4 = require("node:fs/promises");
|
|
10840
|
-
var
|
|
11198
|
+
var import_node_path4 = require("node:path");
|
|
10841
11199
|
var VERITY_STOP_HOOK = {
|
|
10842
11200
|
type: "command",
|
|
10843
11201
|
command: "verity analyze",
|
|
@@ -10848,6 +11206,10 @@ var VERITY_INTENT_HOOK = {
|
|
|
10848
11206
|
type: "command",
|
|
10849
11207
|
command: "verity intent capture"
|
|
10850
11208
|
};
|
|
11209
|
+
var VERITY_BASELINE_HOOK = {
|
|
11210
|
+
type: "command",
|
|
11211
|
+
command: "verity baseline capture"
|
|
11212
|
+
};
|
|
10851
11213
|
var GUARD_TIMEOUT = 300;
|
|
10852
11214
|
function buildGuardHook(on) {
|
|
10853
11215
|
return {
|
|
@@ -10860,9 +11222,13 @@ function buildGuardHook(on) {
|
|
|
10860
11222
|
var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
|
|
10861
11223
|
var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
|
|
10862
11224
|
var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
|
|
11225
|
+
var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
|
|
10863
11226
|
function isVerityGuardHook(entry) {
|
|
10864
11227
|
return VERITY_GUARD_RE.test(entry.command ?? "");
|
|
10865
11228
|
}
|
|
11229
|
+
function isVerityBaselineHook(entry) {
|
|
11230
|
+
return VERITY_BASELINE_RE.test(entry.command ?? "");
|
|
11231
|
+
}
|
|
10866
11232
|
var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
|
|
10867
11233
|
var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
|
|
10868
11234
|
function isVerityStopHook(entry) {
|
|
@@ -10874,7 +11240,7 @@ function isVerityIntentHook(entry) {
|
|
|
10874
11240
|
return VERITY_INTENT_RE.test(c) || LEGACY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh") || c.includes(".gate/hooks/capture-intent.sh");
|
|
10875
11241
|
}
|
|
10876
11242
|
function isVerityHook(entry) {
|
|
10877
|
-
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11243
|
+
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10878
11244
|
}
|
|
10879
11245
|
function isCurrentVerityStopHook(entry) {
|
|
10880
11246
|
const c = entry.command ?? "";
|
|
@@ -10885,7 +11251,7 @@ function isCurrentVerityIntentHook(entry) {
|
|
|
10885
11251
|
return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
|
|
10886
11252
|
}
|
|
10887
11253
|
function isCurrentVerityHook(entry) {
|
|
10888
|
-
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11254
|
+
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10889
11255
|
}
|
|
10890
11256
|
function settingsHasLegacyHook(settings) {
|
|
10891
11257
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
@@ -10927,16 +11293,18 @@ async function readAllSettings() {
|
|
|
10927
11293
|
async function checkAllVerityHooks() {
|
|
10928
11294
|
let stop = false;
|
|
10929
11295
|
let intent = false;
|
|
11296
|
+
let baseline = false;
|
|
10930
11297
|
let guard = false;
|
|
10931
11298
|
let guardOn = [];
|
|
10932
11299
|
for (const settings of await readAllSettings()) {
|
|
10933
11300
|
const r = checkVerityHooks(settings);
|
|
10934
11301
|
stop = stop || r.stop;
|
|
10935
11302
|
intent = intent || r.intent;
|
|
11303
|
+
baseline = baseline || r.baseline;
|
|
10936
11304
|
guard = guard || r.guard;
|
|
10937
11305
|
if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
|
|
10938
11306
|
}
|
|
10939
|
-
return { stop, intent, guard, guardOn };
|
|
11307
|
+
return { stop, intent, baseline, guard, guardOn };
|
|
10940
11308
|
}
|
|
10941
11309
|
async function checkExternalVerityHooks() {
|
|
10942
11310
|
let stop = false;
|
|
@@ -10959,12 +11327,14 @@ async function checkExternalVerityHooks() {
|
|
|
10959
11327
|
async function checkAllVerityHooksDetailed() {
|
|
10960
11328
|
let stop = false;
|
|
10961
11329
|
let intent = false;
|
|
11330
|
+
let baseline = false;
|
|
10962
11331
|
let hasCurrent = false;
|
|
10963
11332
|
let hasLegacy = false;
|
|
10964
11333
|
for (const settings of await readAllSettings()) {
|
|
10965
11334
|
const r = checkVerityHooks(settings);
|
|
10966
11335
|
stop = stop || r.stop;
|
|
10967
11336
|
intent = intent || r.intent;
|
|
11337
|
+
baseline = baseline || r.baseline;
|
|
10968
11338
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
10969
11339
|
for (const g of groups ?? []) {
|
|
10970
11340
|
for (const h of g.hooks ?? []) {
|
|
@@ -10974,22 +11344,22 @@ async function checkAllVerityHooksDetailed() {
|
|
|
10974
11344
|
}
|
|
10975
11345
|
}
|
|
10976
11346
|
}
|
|
10977
|
-
return { stop, intent, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
11347
|
+
return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
10978
11348
|
}
|
|
10979
11349
|
async function writeSettings(settings) {
|
|
10980
|
-
await (0, import_promises4.mkdir)((0,
|
|
11350
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
|
|
10981
11351
|
await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
10982
11352
|
}
|
|
10983
11353
|
async function readSettingsAt(root) {
|
|
10984
11354
|
try {
|
|
10985
|
-
return JSON.parse(await (0, import_promises4.readFile)((0,
|
|
11355
|
+
return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
|
|
10986
11356
|
} catch {
|
|
10987
11357
|
return {};
|
|
10988
11358
|
}
|
|
10989
11359
|
}
|
|
10990
11360
|
async function writeSettingsAt(root, settings) {
|
|
10991
|
-
const file = (0,
|
|
10992
|
-
await (0, import_promises4.mkdir)((0,
|
|
11361
|
+
const file = (0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE);
|
|
11362
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
|
|
10993
11363
|
await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
10994
11364
|
}
|
|
10995
11365
|
async function hasLegacyHooksAt(root) {
|
|
@@ -11017,6 +11387,10 @@ function checkVerityHooks(settings) {
|
|
|
11017
11387
|
const hasIntent = intentGroups.some(
|
|
11018
11388
|
(g) => g.hooks?.some((h) => isCurrentVerityIntentHook(h))
|
|
11019
11389
|
);
|
|
11390
|
+
const sessionStartGroups = hooks["SessionStart"] ?? [];
|
|
11391
|
+
const hasBaseline = sessionStartGroups.some(
|
|
11392
|
+
(g) => g.hooks?.some((h) => isVerityBaselineHook(h))
|
|
11393
|
+
);
|
|
11020
11394
|
let guard = false;
|
|
11021
11395
|
let guardOn = [];
|
|
11022
11396
|
for (const g of hooks["PreToolUse"] ?? []) {
|
|
@@ -11029,16 +11403,17 @@ function checkVerityHooks(settings) {
|
|
|
11029
11403
|
}
|
|
11030
11404
|
}
|
|
11031
11405
|
}
|
|
11032
|
-
return { stop: hasStop, intent: hasIntent, guard, guardOn };
|
|
11406
|
+
return { stop: hasStop, intent: hasIntent, baseline: hasBaseline, guard, guardOn };
|
|
11033
11407
|
}
|
|
11034
|
-
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false }) {
|
|
11408
|
+
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false, baseline: false }) {
|
|
11035
11409
|
const local = checkVerityHooks(settings);
|
|
11036
11410
|
const existing = {
|
|
11037
11411
|
stop: local.stop || externalPresent.stop,
|
|
11038
|
-
intent: local.intent || externalPresent.intent
|
|
11412
|
+
intent: local.intent || externalPresent.intent,
|
|
11413
|
+
baseline: local.baseline || (externalPresent.baseline ?? false)
|
|
11039
11414
|
};
|
|
11040
11415
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11041
|
-
if (existing.stop && existing.intent && !force && !hasLocalLegacy) {
|
|
11416
|
+
if (existing.stop && existing.intent && existing.baseline && !force && !hasLocalLegacy) {
|
|
11042
11417
|
return { ok: false, error: "Verity hooks already installed. Use --force to overwrite." };
|
|
11043
11418
|
}
|
|
11044
11419
|
const newSettings = { ...settings };
|
|
@@ -11046,7 +11421,7 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11046
11421
|
newSettings.hooks = {};
|
|
11047
11422
|
}
|
|
11048
11423
|
const shouldStrip = force ? isVerityHook : isLegacyHook;
|
|
11049
|
-
for (const key of ["Stop", "UserPromptSubmit"]) {
|
|
11424
|
+
for (const key of ["Stop", "UserPromptSubmit", "SessionStart"]) {
|
|
11050
11425
|
const groups = newSettings.hooks[key];
|
|
11051
11426
|
if (groups) {
|
|
11052
11427
|
newSettings.hooks[key] = groups.map((g) => ({
|
|
@@ -11073,6 +11448,15 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11073
11448
|
if (!newSettings.hooks["UserPromptSubmit"]) newSettings.hooks["UserPromptSubmit"] = [];
|
|
11074
11449
|
newSettings.hooks["UserPromptSubmit"].push({ hooks: [VERITY_INTENT_HOOK] });
|
|
11075
11450
|
}
|
|
11451
|
+
if (!existing.baseline) {
|
|
11452
|
+
if (!newSettings.hooks["SessionStart"]) {
|
|
11453
|
+
newSettings.hooks["SessionStart"] = [];
|
|
11454
|
+
}
|
|
11455
|
+
newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
|
|
11456
|
+
} else if (force && local.baseline) {
|
|
11457
|
+
if (!newSettings.hooks["SessionStart"]) newSettings.hooks["SessionStart"] = [];
|
|
11458
|
+
newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
|
|
11459
|
+
}
|
|
11076
11460
|
return { ok: true, data: newSettings };
|
|
11077
11461
|
}
|
|
11078
11462
|
function removeVerityHooks(settings) {
|
|
@@ -11106,6 +11490,7 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
|
|
|
11106
11490
|
(s.hooks[event] ??= []).push(group);
|
|
11107
11491
|
};
|
|
11108
11492
|
if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
|
|
11493
|
+
push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
|
|
11109
11494
|
if (moments.includes("stop") && !externalPresent.stop) {
|
|
11110
11495
|
push("Stop", { hooks: [VERITY_STOP_HOOK] });
|
|
11111
11496
|
}
|
|
@@ -11154,7 +11539,7 @@ function registerHooksCommands(program2) {
|
|
|
11154
11539
|
return;
|
|
11155
11540
|
}
|
|
11156
11541
|
const detail = await checkAllVerityHooksDetailed();
|
|
11157
|
-
const present = { stop: detail.stop, intent: detail.intent };
|
|
11542
|
+
const present = { stop: detail.stop, intent: detail.intent, baseline: detail.baseline };
|
|
11158
11543
|
if (detail.legacyOnly) {
|
|
11159
11544
|
printInfo("Found a legacy GATE.md hook \u2014 upgrading it to Verity...");
|
|
11160
11545
|
const settings2 = removeVerityHooks(await readSettings());
|
|
@@ -11167,11 +11552,12 @@ function registerHooksCommands(program2) {
|
|
|
11167
11552
|
printInfo("Verity hooks installed in .claude/settings.json (legacy hook removed)");
|
|
11168
11553
|
printInfo(" Stop hook: verity analyze");
|
|
11169
11554
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11555
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11170
11556
|
return;
|
|
11171
11557
|
}
|
|
11172
11558
|
const settings = await readSettings();
|
|
11173
11559
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11174
|
-
if (!force && present.stop && present.intent && !hasLocalLegacy) {
|
|
11560
|
+
if (!force && present.stop && present.intent && present.baseline && !hasLocalLegacy) {
|
|
11175
11561
|
printInfo("Verity hooks already installed (found in .claude/settings.json, settings.local.json, or your global settings).");
|
|
11176
11562
|
printInfo("Use --force to rewrite the project settings.json copy.");
|
|
11177
11563
|
return;
|
|
@@ -11185,11 +11571,13 @@ function registerHooksCommands(program2) {
|
|
|
11185
11571
|
printInfo("Verity hooks installed in .claude/settings.json");
|
|
11186
11572
|
printInfo(" Stop hook: verity analyze");
|
|
11187
11573
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11574
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11188
11575
|
});
|
|
11189
11576
|
hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
|
|
11190
11577
|
const status = await checkAllVerityHooks();
|
|
11191
11578
|
printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
|
|
11192
11579
|
printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
|
|
11580
|
+
printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
|
|
11193
11581
|
const gates = status.guard ? status.guardOn.join(", ") : "none";
|
|
11194
11582
|
printInfo(`Git-moment gate (verity guard): ${status.guard ? `installed [${gates}]` : "not installed"}`);
|
|
11195
11583
|
if (!status.stop && !status.guard) {
|
|
@@ -11208,15 +11596,19 @@ function registerHooksCommands(program2) {
|
|
|
11208
11596
|
}
|
|
11209
11597
|
|
|
11210
11598
|
// src/commands/intent.ts
|
|
11211
|
-
var
|
|
11599
|
+
var import_node_crypto3 = require("node:crypto");
|
|
11212
11600
|
|
|
11213
11601
|
// src/lib/conversation-buffer.ts
|
|
11214
11602
|
var import_promises5 = require("node:fs/promises");
|
|
11215
|
-
var
|
|
11216
|
-
var
|
|
11603
|
+
var import_node_fs3 = require("node:fs");
|
|
11604
|
+
var import_node_child_process5 = require("node:child_process");
|
|
11605
|
+
var import_node_crypto = require("node:crypto");
|
|
11217
11606
|
function stripImageReferences(text) {
|
|
11218
11607
|
return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
|
|
11219
11608
|
}
|
|
11609
|
+
function bufferTmpPath() {
|
|
11610
|
+
return `${CONVERSATION_BUFFER_FILE}.${process.pid}.${(0, import_node_crypto.randomBytes)(4).toString("hex")}.tmp`;
|
|
11611
|
+
}
|
|
11220
11612
|
async function appendToConversationBuffer(prompt, sessionId) {
|
|
11221
11613
|
try {
|
|
11222
11614
|
await (0, import_promises5.mkdir)(VERITY_DIR, { recursive: true });
|
|
@@ -11236,26 +11628,39 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11236
11628
|
recent.push(entry);
|
|
11237
11629
|
const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
|
|
11238
11630
|
const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11239
|
-
const tmpFile =
|
|
11631
|
+
const tmpFile = bufferTmpPath();
|
|
11240
11632
|
await (0, import_promises5.writeFile)(tmpFile, content);
|
|
11241
11633
|
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11242
11634
|
} catch {
|
|
11243
11635
|
}
|
|
11244
11636
|
}
|
|
11245
|
-
async function readAndClearConversationBuffer() {
|
|
11637
|
+
async function readAndClearConversationBuffer(currentSessionId) {
|
|
11246
11638
|
try {
|
|
11247
|
-
if ((0,
|
|
11639
|
+
if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
11248
11640
|
const entries = await readBufferEntries();
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
if (
|
|
11641
|
+
let mine = entries;
|
|
11642
|
+
let others = [];
|
|
11643
|
+
if (currentSessionId) {
|
|
11644
|
+
mine = entries.filter((e) => e.session_id === currentSessionId || !e.session_id);
|
|
11645
|
+
others = entries.filter((e) => e.session_id && e.session_id !== currentSessionId);
|
|
11646
|
+
}
|
|
11647
|
+
if (others.length > 0) {
|
|
11648
|
+
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11649
|
+
const tmpFile = bufferTmpPath();
|
|
11650
|
+
await (0, import_promises5.writeFile)(tmpFile, remaining);
|
|
11651
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11652
|
+
} else {
|
|
11653
|
+
await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11654
|
+
});
|
|
11655
|
+
}
|
|
11656
|
+
if (mine.length > 0) {
|
|
11252
11657
|
return {
|
|
11253
|
-
prompts:
|
|
11658
|
+
prompts: mine,
|
|
11254
11659
|
recent_commits: getRecentCommitMessages()
|
|
11255
11660
|
};
|
|
11256
11661
|
}
|
|
11257
11662
|
}
|
|
11258
|
-
if ((0,
|
|
11663
|
+
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11259
11664
|
try {
|
|
11260
11665
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
11261
11666
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -11299,7 +11704,7 @@ async function readBufferEntries() {
|
|
|
11299
11704
|
}
|
|
11300
11705
|
function getRecentCommitMessages() {
|
|
11301
11706
|
try {
|
|
11302
|
-
const output = (0,
|
|
11707
|
+
const output = (0, import_node_child_process5.execSync)(
|
|
11303
11708
|
'git log --since="30 minutes ago" --format="%s" -5',
|
|
11304
11709
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
11305
11710
|
).trim();
|
|
@@ -11312,8 +11717,8 @@ function getRecentCommitMessages() {
|
|
|
11312
11717
|
|
|
11313
11718
|
// src/lib/task-context-buffer.ts
|
|
11314
11719
|
var import_promises6 = require("node:fs/promises");
|
|
11315
|
-
var
|
|
11316
|
-
var
|
|
11720
|
+
var import_node_fs4 = require("node:fs");
|
|
11721
|
+
var import_node_path5 = require("node:path");
|
|
11317
11722
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
11318
11723
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
11319
11724
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -11352,7 +11757,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
11352
11757
|
}
|
|
11353
11758
|
async function readTaskContextBuffer(taskId) {
|
|
11354
11759
|
const filePath = bufferPath(taskId);
|
|
11355
|
-
if (!(0,
|
|
11760
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
11356
11761
|
try {
|
|
11357
11762
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
11358
11763
|
if (!content.trim()) return null;
|
|
@@ -11386,12 +11791,12 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11386
11791
|
}
|
|
11387
11792
|
async function cleanupTaskContextBuffers() {
|
|
11388
11793
|
try {
|
|
11389
|
-
if (!(0,
|
|
11794
|
+
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
11390
11795
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
11391
11796
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
11392
11797
|
for (const file of files) {
|
|
11393
11798
|
if (!file.endsWith(".jsonl")) continue;
|
|
11394
|
-
const filePath = (0,
|
|
11799
|
+
const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
|
|
11395
11800
|
try {
|
|
11396
11801
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11397
11802
|
if (stats.mtimeMs < cutoffMs) {
|
|
@@ -11405,13 +11810,13 @@ async function cleanupTaskContextBuffers() {
|
|
|
11405
11810
|
}
|
|
11406
11811
|
function bufferPath(taskId) {
|
|
11407
11812
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
11408
|
-
return (0,
|
|
11813
|
+
return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
11409
11814
|
}
|
|
11410
11815
|
async function appendEntry(taskId, entry) {
|
|
11411
11816
|
try {
|
|
11412
11817
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
11413
11818
|
const filePath = bufferPath(taskId);
|
|
11414
|
-
if ((0,
|
|
11819
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
11415
11820
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11416
11821
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
11417
11822
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -11422,7 +11827,7 @@ async function appendEntry(taskId, entry) {
|
|
|
11422
11827
|
}
|
|
11423
11828
|
}
|
|
11424
11829
|
const line = JSON.stringify(entry) + "\n";
|
|
11425
|
-
const existing = (0,
|
|
11830
|
+
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
11426
11831
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
11427
11832
|
} catch {
|
|
11428
11833
|
}
|
|
@@ -11430,8 +11835,8 @@ async function appendEntry(taskId, entry) {
|
|
|
11430
11835
|
|
|
11431
11836
|
// src/lib/memory-retrieval.ts
|
|
11432
11837
|
var import_promises7 = require("node:fs/promises");
|
|
11433
|
-
var
|
|
11434
|
-
var
|
|
11838
|
+
var import_node_fs5 = require("node:fs");
|
|
11839
|
+
var import_node_path6 = require("node:path");
|
|
11435
11840
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
11436
11841
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
11437
11842
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
@@ -11524,19 +11929,19 @@ function parseFrontmatter(content) {
|
|
|
11524
11929
|
return { fm, body: match[2].trim() };
|
|
11525
11930
|
}
|
|
11526
11931
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
11527
|
-
if (!(0,
|
|
11932
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
|
|
11528
11933
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
11529
11934
|
const promptTokens = tokenize(promptText);
|
|
11530
11935
|
const nodes = [];
|
|
11531
11936
|
for (const domain of DOMAINS) {
|
|
11532
|
-
const domainDir = (0,
|
|
11533
|
-
if (!(0,
|
|
11937
|
+
const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
|
|
11938
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
11534
11939
|
try {
|
|
11535
11940
|
const files = await (0, import_promises7.readdir)(domainDir);
|
|
11536
11941
|
for (const file of files) {
|
|
11537
11942
|
if (!file.endsWith(".md")) continue;
|
|
11538
11943
|
try {
|
|
11539
|
-
const content = await (0, import_promises7.readFile)((0,
|
|
11944
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
|
|
11540
11945
|
const { fm, body } = parseFrontmatter(content);
|
|
11541
11946
|
if (fm.status && fm.status !== "active") continue;
|
|
11542
11947
|
nodes.push({
|
|
@@ -11595,9 +12000,9 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
11595
12000
|
|
|
11596
12001
|
// src/lib/memory-sync.ts
|
|
11597
12002
|
var import_promises8 = require("node:fs/promises");
|
|
11598
|
-
var
|
|
11599
|
-
var
|
|
11600
|
-
var
|
|
12003
|
+
var import_node_fs6 = require("node:fs");
|
|
12004
|
+
var import_node_path7 = require("node:path");
|
|
12005
|
+
var import_node_crypto2 = require("node:crypto");
|
|
11601
12006
|
|
|
11602
12007
|
// src/lib/glob-match.ts
|
|
11603
12008
|
function globToRegex(glob) {
|
|
@@ -11667,35 +12072,35 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
|
11667
12072
|
async function ensureMemoryDir() {
|
|
11668
12073
|
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
11669
12074
|
for (const domain of DOMAINS2) {
|
|
11670
|
-
await (0, import_promises8.mkdir)((0,
|
|
12075
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
|
|
11671
12076
|
}
|
|
11672
|
-
if (!(0,
|
|
11673
|
-
await (0, import_promises8.writeFile)((0,
|
|
12077
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12078
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
11674
12079
|
}
|
|
11675
|
-
if (!(0,
|
|
11676
|
-
await (0, import_promises8.writeFile)((0,
|
|
12080
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
|
|
12081
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
11677
12082
|
}
|
|
11678
|
-
if (!(0,
|
|
11679
|
-
await (0, import_promises8.writeFile)((0,
|
|
12083
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
|
|
12084
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
11680
12085
|
}
|
|
11681
12086
|
}
|
|
11682
12087
|
async function buildManifest() {
|
|
11683
|
-
if (!(0,
|
|
12088
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
|
|
11684
12089
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
11685
12090
|
}
|
|
11686
12091
|
const nodes = [];
|
|
11687
12092
|
for (const domain of DOMAINS2) {
|
|
11688
|
-
const domainDir = (0,
|
|
11689
|
-
if (!(0,
|
|
12093
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12094
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11690
12095
|
try {
|
|
11691
12096
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11692
12097
|
for (const file of files) {
|
|
11693
12098
|
if (!file.endsWith(".md")) continue;
|
|
11694
12099
|
const filePath = `${domain}/${file}`;
|
|
11695
|
-
const fullPath = (0,
|
|
12100
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
|
|
11696
12101
|
try {
|
|
11697
12102
|
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
11698
|
-
const hash = (0,
|
|
12103
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
11699
12104
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
11700
12105
|
} catch {
|
|
11701
12106
|
}
|
|
@@ -11705,32 +12110,32 @@ async function buildManifest() {
|
|
|
11705
12110
|
}
|
|
11706
12111
|
let indexHash = null;
|
|
11707
12112
|
try {
|
|
11708
|
-
const indexContent = await (0, import_promises8.readFile)((0,
|
|
11709
|
-
indexHash = `sha256:${(0,
|
|
12113
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
|
|
12114
|
+
indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
11710
12115
|
} catch {
|
|
11711
12116
|
}
|
|
11712
12117
|
let logLength = 0;
|
|
11713
12118
|
try {
|
|
11714
|
-
const logContent = await (0, import_promises8.readFile)((0,
|
|
12119
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
|
|
11715
12120
|
logLength = logContent.split("\n").length;
|
|
11716
12121
|
} catch {
|
|
11717
12122
|
}
|
|
11718
12123
|
return { schema_version: 1, nodes, index_hash: indexHash, log_length: logLength };
|
|
11719
12124
|
}
|
|
11720
12125
|
function hashContent(content) {
|
|
11721
|
-
return `sha256:${(0,
|
|
12126
|
+
return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
11722
12127
|
}
|
|
11723
12128
|
async function readOnDiskNodes() {
|
|
11724
12129
|
const out = /* @__PURE__ */ new Map();
|
|
11725
|
-
if (!(0,
|
|
12130
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
|
|
11726
12131
|
for (const domain of DOMAINS2) {
|
|
11727
|
-
const domainDir = (0,
|
|
11728
|
-
if (!(0,
|
|
12132
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12133
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11729
12134
|
try {
|
|
11730
12135
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
11731
12136
|
if (!file.endsWith(".md")) continue;
|
|
11732
12137
|
try {
|
|
11733
|
-
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0,
|
|
12138
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
|
|
11734
12139
|
} catch {
|
|
11735
12140
|
}
|
|
11736
12141
|
}
|
|
@@ -11776,8 +12181,8 @@ async function computeEditedNodeUploads() {
|
|
|
11776
12181
|
const uploads = [];
|
|
11777
12182
|
for (const [path, prevHash] of prev) {
|
|
11778
12183
|
if (prevHash == null) continue;
|
|
11779
|
-
const full = (0,
|
|
11780
|
-
if (!(0,
|
|
12184
|
+
const full = (0, import_node_path7.join)(memoryDir2(), path);
|
|
12185
|
+
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
11781
12186
|
let content;
|
|
11782
12187
|
try {
|
|
11783
12188
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -11813,15 +12218,15 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
11813
12218
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
11814
12219
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
11815
12220
|
try {
|
|
11816
|
-
const existing = (0,
|
|
11817
|
-
await (0, import_promises8.writeFile)((0,
|
|
12221
|
+
const existing = (0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
12222
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
11818
12223
|
} catch {
|
|
11819
12224
|
}
|
|
11820
12225
|
await recordSyncedNodePaths();
|
|
11821
12226
|
return count;
|
|
11822
12227
|
}
|
|
11823
12228
|
async function applyOneWrite(write, treePaths) {
|
|
11824
|
-
const fullPath = (0,
|
|
12229
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
|
|
11825
12230
|
const notes = [];
|
|
11826
12231
|
let content = write.content;
|
|
11827
12232
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -11831,7 +12236,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11831
12236
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
11832
12237
|
}
|
|
11833
12238
|
}
|
|
11834
|
-
if ((0,
|
|
12239
|
+
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
11835
12240
|
let existing = "";
|
|
11836
12241
|
try {
|
|
11837
12242
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -11843,7 +12248,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11843
12248
|
return { written: false, notes };
|
|
11844
12249
|
}
|
|
11845
12250
|
}
|
|
11846
|
-
await (0, import_promises8.mkdir)((0,
|
|
12251
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
|
|
11847
12252
|
await (0, import_promises8.writeFile)(fullPath, content);
|
|
11848
12253
|
return { written: true, notes };
|
|
11849
12254
|
}
|
|
@@ -11884,8 +12289,8 @@ async function regenerateIndex() {
|
|
|
11884
12289
|
];
|
|
11885
12290
|
let totalNodes = 0;
|
|
11886
12291
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
11887
|
-
const domainDir = (0,
|
|
11888
|
-
if (!(0,
|
|
12292
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12293
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11889
12294
|
try {
|
|
11890
12295
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11891
12296
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -11895,7 +12300,7 @@ async function regenerateIndex() {
|
|
|
11895
12300
|
for (const file of mdFiles.sort()) {
|
|
11896
12301
|
const slug = file.replace(/\.md$/, "");
|
|
11897
12302
|
try {
|
|
11898
|
-
const content = await (0, import_promises8.readFile)((0,
|
|
12303
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
|
|
11899
12304
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
11900
12305
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
11901
12306
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -11919,7 +12324,7 @@ async function regenerateIndex() {
|
|
|
11919
12324
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
11920
12325
|
}
|
|
11921
12326
|
const next = lines.join("\n") + "\n";
|
|
11922
|
-
const indexPath = (0,
|
|
12327
|
+
const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
|
|
11923
12328
|
let existing = null;
|
|
11924
12329
|
try {
|
|
11925
12330
|
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
@@ -12001,9 +12406,9 @@ function hasLegacyMemoryBlock(text) {
|
|
|
12001
12406
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
12002
12407
|
}
|
|
12003
12408
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
12004
|
-
const claudeMdPath = (0,
|
|
12409
|
+
const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
|
|
12005
12410
|
let existing = "";
|
|
12006
|
-
if ((0,
|
|
12411
|
+
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12007
12412
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12008
12413
|
}
|
|
12009
12414
|
let startTag = CLAUDE_MD_START;
|
|
@@ -12133,7 +12538,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
12133
12538
|
`;
|
|
12134
12539
|
|
|
12135
12540
|
// src/commands/intent.ts
|
|
12136
|
-
var
|
|
12541
|
+
var import_node_fs7 = require("node:fs");
|
|
12137
12542
|
function registerIntentCommands(program2) {
|
|
12138
12543
|
const intent = program2.command("intent").description("Manage intent capture");
|
|
12139
12544
|
intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
|
|
@@ -12142,7 +12547,7 @@ function registerIntentCommands(program2) {
|
|
|
12142
12547
|
process.chdir(repoRoot());
|
|
12143
12548
|
} catch {
|
|
12144
12549
|
}
|
|
12145
|
-
if (!(0,
|
|
12550
|
+
if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
|
|
12146
12551
|
process.exit(0);
|
|
12147
12552
|
}
|
|
12148
12553
|
const chunks = [];
|
|
@@ -12205,7 +12610,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12205
12610
|
logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
|
|
12206
12611
|
return;
|
|
12207
12612
|
}
|
|
12208
|
-
const promptHash = (0,
|
|
12613
|
+
const promptHash = (0, import_node_crypto3.createHash)("sha256").update(prompt).digest("hex");
|
|
12209
12614
|
const result = await apiRequest({
|
|
12210
12615
|
method: "POST",
|
|
12211
12616
|
path: "/classify-task",
|
|
@@ -12638,278 +13043,89 @@ function registerFeedbackCommand(program2) {
|
|
|
12638
13043
|
await sendGeneralFeedback(message, opts, globals);
|
|
12639
13044
|
});
|
|
12640
13045
|
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) => {
|
|
12641
|
-
const globals = program2.opts();
|
|
12642
|
-
const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
|
|
12643
|
-
if (!validActions.includes(action)) {
|
|
12644
|
-
printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
|
|
12645
|
-
process.exit(1);
|
|
12646
|
-
}
|
|
12647
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12648
|
-
if (!tokenResult.ok) {
|
|
12649
|
-
printError(tokenResult.error);
|
|
12650
|
-
process.exit(1);
|
|
12651
|
-
}
|
|
12652
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12653
|
-
if (!urlResult.ok) {
|
|
12654
|
-
printError(urlResult.error);
|
|
12655
|
-
process.exit(1);
|
|
12656
|
-
}
|
|
12657
|
-
const body = {
|
|
12658
|
-
run_id: runId,
|
|
12659
|
-
pattern_id: patternId,
|
|
12660
|
-
action
|
|
12661
|
-
};
|
|
12662
|
-
if (note) body.note = note;
|
|
12663
|
-
if (opts.file) body.file_path = opts.file;
|
|
12664
|
-
if (opts.line != null) body.line = opts.line;
|
|
12665
|
-
const result = await apiRequest({
|
|
12666
|
-
method: "POST",
|
|
12667
|
-
path: "/feedback/findings",
|
|
12668
|
-
serviceUrl: urlResult.data,
|
|
12669
|
-
token: tokenResult.data.token,
|
|
12670
|
-
body,
|
|
12671
|
-
verbose: globals.verbose
|
|
12672
|
-
});
|
|
12673
|
-
if (!result.ok) {
|
|
12674
|
-
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
12675
|
-
process.exit(1);
|
|
12676
|
-
}
|
|
12677
|
-
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
12678
|
-
printInfo(status);
|
|
12679
|
-
});
|
|
12680
|
-
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
12681
|
-
if (!message) return;
|
|
12682
|
-
const globals = program2.opts();
|
|
12683
|
-
await sendGeneralFeedback(message, opts, globals);
|
|
12684
|
-
});
|
|
12685
|
-
}
|
|
12686
|
-
async function sendGeneralFeedback(message, opts, globals) {
|
|
12687
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12688
|
-
if (!tokenResult.ok) {
|
|
12689
|
-
printError(tokenResult.error);
|
|
12690
|
-
printInfo(`Your message: ${message}`);
|
|
12691
|
-
process.exit(1);
|
|
12692
|
-
}
|
|
12693
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12694
|
-
if (!urlResult.ok) {
|
|
12695
|
-
printError(urlResult.error);
|
|
12696
|
-
printInfo(`Your message: ${message}`);
|
|
12697
|
-
process.exit(1);
|
|
12698
|
-
}
|
|
12699
|
-
const body = { message };
|
|
12700
|
-
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
12701
|
-
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
12702
|
-
if (sessionId) body.session_id = sessionId;
|
|
12703
|
-
if (model) body.agent_model = model;
|
|
12704
|
-
const result = await apiRequest({
|
|
12705
|
-
method: "POST",
|
|
12706
|
-
path: "/feedback",
|
|
12707
|
-
serviceUrl: urlResult.data,
|
|
12708
|
-
token: tokenResult.data.token,
|
|
12709
|
-
body,
|
|
12710
|
-
verbose: globals.verbose
|
|
12711
|
-
});
|
|
12712
|
-
if (!result.ok) {
|
|
12713
|
-
printError(`Couldn't send feedback: ${result.error}`);
|
|
12714
|
-
printInfo(`Your message: ${message}`);
|
|
12715
|
-
process.exit(1);
|
|
12716
|
-
}
|
|
12717
|
-
printInfo("Thanks, feedback sent!");
|
|
12718
|
-
}
|
|
12719
|
-
|
|
12720
|
-
// src/commands/analyze.ts
|
|
12721
|
-
var import_node_fs18 = require("node:fs");
|
|
12722
|
-
var import_node_path13 = require("node:path");
|
|
12723
|
-
|
|
12724
|
-
// src/lib/git.ts
|
|
12725
|
-
var import_node_child_process5 = require("node:child_process");
|
|
12726
|
-
var import_node_fs7 = require("node:fs");
|
|
12727
|
-
var import_node_path7 = require("node:path");
|
|
12728
|
-
function resolveFile(relpath) {
|
|
12729
|
-
if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
|
|
12730
|
-
if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
|
|
12731
|
-
try {
|
|
12732
|
-
const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
12733
|
-
for (const entry of entries) {
|
|
12734
|
-
if (!entry.isDirectory()) continue;
|
|
12735
|
-
const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
|
|
12736
|
-
if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
|
|
12737
|
-
}
|
|
12738
|
-
} catch {
|
|
12739
|
-
}
|
|
12740
|
-
}
|
|
12741
|
-
return null;
|
|
12742
|
-
}
|
|
12743
|
-
function execGit(cmd) {
|
|
12744
|
-
try {
|
|
12745
|
-
return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12746
|
-
} catch {
|
|
12747
|
-
return "";
|
|
12748
|
-
}
|
|
12749
|
-
}
|
|
12750
|
-
function splitLines(s) {
|
|
12751
|
-
return s.split("\n").filter((l) => l.length > 0);
|
|
12752
|
-
}
|
|
12753
|
-
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
12754
|
-
function readBaselineSha() {
|
|
12755
|
-
if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
12756
|
-
let sha;
|
|
12757
|
-
try {
|
|
12758
|
-
sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
12759
|
-
} catch {
|
|
12760
|
-
return null;
|
|
12761
|
-
}
|
|
12762
|
-
if (!SHA_RE.test(sha)) return null;
|
|
12763
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
12764
|
-
if (!reachable) {
|
|
12765
|
-
try {
|
|
12766
|
-
(0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
|
|
12767
|
-
} catch {
|
|
12768
|
-
}
|
|
12769
|
-
return null;
|
|
12770
|
-
}
|
|
12771
|
-
return sha;
|
|
12772
|
-
}
|
|
12773
|
-
function writeBaselineSha(sha) {
|
|
12774
|
-
if (!SHA_RE.test(sha)) return;
|
|
12775
|
-
try {
|
|
12776
|
-
(0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
12777
|
-
(0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
12778
|
-
} catch {
|
|
12779
|
-
}
|
|
12780
|
-
}
|
|
12781
|
-
function getChangedFiles() {
|
|
12782
|
-
const sets = /* @__PURE__ */ new Set();
|
|
12783
|
-
let hasRecentCommitFiles = false;
|
|
12784
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
12785
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
12786
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
12787
|
-
const baseline = readBaselineSha();
|
|
12788
|
-
if (baseline) {
|
|
12789
|
-
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
12790
|
-
if (committed.length > 0) {
|
|
12791
|
-
hasRecentCommitFiles = true;
|
|
12792
|
-
for (const f of committed) sets.add(f);
|
|
12793
|
-
}
|
|
12794
|
-
} else {
|
|
12795
|
-
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
12796
|
-
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
12797
|
-
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
12798
|
-
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
12799
|
-
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
12800
|
-
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
12801
|
-
if (recentFiles.length > 0) {
|
|
12802
|
-
hasRecentCommitFiles = true;
|
|
12803
|
-
for (const f of recentFiles) sets.add(f);
|
|
12804
|
-
}
|
|
12805
|
-
}
|
|
12806
|
-
}
|
|
12807
|
-
for (const f of getWorktreeFiles()) sets.add(f);
|
|
12808
|
-
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12809
|
-
return { files: filtered, hasRecentCommitFiles };
|
|
12810
|
-
}
|
|
12811
|
-
function getStagedFiles() {
|
|
12812
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12813
|
-
}
|
|
12814
|
-
function getPushRangeFiles() {
|
|
12815
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12816
|
-
const resolvers = [
|
|
12817
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
12818
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
12819
|
-
() => {
|
|
12820
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
12821
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
12822
|
-
}
|
|
12823
|
-
];
|
|
12824
|
-
for (const resolve of resolvers) {
|
|
12825
|
-
const range = resolve();
|
|
12826
|
-
if (range) return { files: diff(range), range };
|
|
12827
|
-
}
|
|
12828
|
-
const baseline = readBaselineSha();
|
|
12829
|
-
if (baseline) {
|
|
12830
|
-
const files = diff(`${baseline}..HEAD`);
|
|
12831
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
12832
|
-
}
|
|
12833
|
-
const last = diff("HEAD~1..HEAD");
|
|
12834
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
12835
|
-
}
|
|
12836
|
-
function getPushRangeMessages() {
|
|
12837
|
-
const { range } = getPushRangeFiles();
|
|
12838
|
-
if (!range) return "";
|
|
12839
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
12840
|
-
}
|
|
12841
|
-
function getWorktreeFiles() {
|
|
12842
|
-
const result = [];
|
|
12843
|
-
const worktreeDir = ".claude/worktrees";
|
|
12844
|
-
if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
|
|
12845
|
-
try {
|
|
12846
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
12847
|
-
const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
12848
|
-
for (const entry of entries) {
|
|
12849
|
-
if (!entry.isDirectory()) continue;
|
|
12850
|
-
const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
|
|
12851
|
-
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
12852
|
-
}
|
|
12853
|
-
} catch {
|
|
12854
|
-
}
|
|
12855
|
-
return result;
|
|
12856
|
-
}
|
|
12857
|
-
function scanDir(baseDir, dir, minMtime, result) {
|
|
12858
|
-
try {
|
|
12859
|
-
const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
|
|
12860
|
-
for (const entry of entries) {
|
|
12861
|
-
const fullPath = (0, import_node_path7.join)(dir, entry.name);
|
|
12862
|
-
if (entry.isDirectory()) {
|
|
12863
|
-
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12864
|
-
scanDir(baseDir, fullPath, minMtime, result);
|
|
12865
|
-
} else if (entry.isFile()) {
|
|
12866
|
-
const ext = (0, import_node_path7.extname)(entry.name).slice(1);
|
|
12867
|
-
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
12868
|
-
try {
|
|
12869
|
-
const stat3 = (0, import_node_fs7.statSync)(fullPath);
|
|
12870
|
-
if (stat3.mtimeMs >= minMtime) {
|
|
12871
|
-
const relPath = fullPath.slice(baseDir.length + 1);
|
|
12872
|
-
result.push(relPath);
|
|
12873
|
-
}
|
|
12874
|
-
} catch {
|
|
12875
|
-
}
|
|
12876
|
-
}
|
|
13046
|
+
const globals = program2.opts();
|
|
13047
|
+
const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
|
|
13048
|
+
if (!validActions.includes(action)) {
|
|
13049
|
+
printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
|
|
13050
|
+
process.exit(1);
|
|
12877
13051
|
}
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
const
|
|
12884
|
-
|
|
13052
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13053
|
+
if (!tokenResult.ok) {
|
|
13054
|
+
printError(tokenResult.error);
|
|
13055
|
+
process.exit(1);
|
|
13056
|
+
}
|
|
13057
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13058
|
+
if (!urlResult.ok) {
|
|
13059
|
+
printError(urlResult.error);
|
|
13060
|
+
process.exit(1);
|
|
13061
|
+
}
|
|
13062
|
+
const body = {
|
|
13063
|
+
run_id: runId,
|
|
13064
|
+
pattern_id: patternId,
|
|
13065
|
+
action
|
|
13066
|
+
};
|
|
13067
|
+
if (note) body.note = note;
|
|
13068
|
+
if (opts.file) body.file_path = opts.file;
|
|
13069
|
+
if (opts.line != null) body.line = opts.line;
|
|
13070
|
+
const result = await apiRequest({
|
|
13071
|
+
method: "POST",
|
|
13072
|
+
path: "/feedback/findings",
|
|
13073
|
+
serviceUrl: urlResult.data,
|
|
13074
|
+
token: tokenResult.data.token,
|
|
13075
|
+
body,
|
|
13076
|
+
verbose: globals.verbose
|
|
13077
|
+
});
|
|
13078
|
+
if (!result.ok) {
|
|
13079
|
+
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
13080
|
+
process.exit(1);
|
|
13081
|
+
}
|
|
13082
|
+
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
13083
|
+
printInfo(status);
|
|
12885
13084
|
});
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
12891
|
-
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
12892
|
-
const basename2 = f.split("/").pop() ?? "";
|
|
12893
|
-
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
12894
|
-
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
12895
|
-
return false;
|
|
13085
|
+
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
13086
|
+
if (!message) return;
|
|
13087
|
+
const globals = program2.opts();
|
|
13088
|
+
await sendGeneralFeedback(message, opts, globals);
|
|
12896
13089
|
});
|
|
12897
13090
|
}
|
|
12898
|
-
function
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
}
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
13091
|
+
async function sendGeneralFeedback(message, opts, globals) {
|
|
13092
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13093
|
+
if (!tokenResult.ok) {
|
|
13094
|
+
printError(tokenResult.error);
|
|
13095
|
+
printInfo(`Your message: ${message}`);
|
|
13096
|
+
process.exit(1);
|
|
13097
|
+
}
|
|
13098
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13099
|
+
if (!urlResult.ok) {
|
|
13100
|
+
printError(urlResult.error);
|
|
13101
|
+
printInfo(`Your message: ${message}`);
|
|
13102
|
+
process.exit(1);
|
|
13103
|
+
}
|
|
13104
|
+
const body = { message };
|
|
13105
|
+
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
13106
|
+
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
13107
|
+
if (sessionId) body.session_id = sessionId;
|
|
13108
|
+
if (model) body.agent_model = model;
|
|
13109
|
+
const result = await apiRequest({
|
|
13110
|
+
method: "POST",
|
|
13111
|
+
path: "/feedback",
|
|
13112
|
+
serviceUrl: urlResult.data,
|
|
13113
|
+
token: tokenResult.data.token,
|
|
13114
|
+
body,
|
|
13115
|
+
verbose: globals.verbose
|
|
13116
|
+
});
|
|
13117
|
+
if (!result.ok) {
|
|
13118
|
+
printError(`Couldn't send feedback: ${result.error}`);
|
|
13119
|
+
printInfo(`Your message: ${message}`);
|
|
13120
|
+
process.exit(1);
|
|
13121
|
+
}
|
|
13122
|
+
printInfo("Thanks, feedback sent!");
|
|
12911
13123
|
}
|
|
12912
13124
|
|
|
13125
|
+
// src/commands/analyze.ts
|
|
13126
|
+
var import_node_fs19 = require("node:fs");
|
|
13127
|
+
var import_node_path14 = require("node:path");
|
|
13128
|
+
|
|
12913
13129
|
// src/lib/files.ts
|
|
12914
13130
|
var import_node_fs8 = require("node:fs");
|
|
12915
13131
|
var import_node_path8 = require("node:path");
|
|
@@ -13042,11 +13258,16 @@ function collectCodeDelta(files, opts) {
|
|
|
13042
13258
|
|
|
13043
13259
|
// src/lib/debounce.ts
|
|
13044
13260
|
var import_node_fs9 = require("node:fs");
|
|
13045
|
-
var
|
|
13046
|
-
function
|
|
13047
|
-
if (!
|
|
13261
|
+
var import_node_crypto4 = require("node:crypto");
|
|
13262
|
+
function scopedFile(base, sessionId) {
|
|
13263
|
+
if (!sessionId) return base;
|
|
13264
|
+
return `${base}.${(0, import_node_crypto4.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
|
|
13265
|
+
}
|
|
13266
|
+
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
13267
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13268
|
+
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13048
13269
|
try {
|
|
13049
|
-
const lastTs = parseInt((0, import_node_fs9.readFileSync)(
|
|
13270
|
+
const lastTs = parseInt((0, import_node_fs9.readFileSync)(file, "utf-8").trim(), 10);
|
|
13050
13271
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
13051
13272
|
const elapsed = nowTs - lastTs;
|
|
13052
13273
|
if (elapsed < debounceSeconds) {
|
|
@@ -13056,12 +13277,13 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
|
|
|
13056
13277
|
}
|
|
13057
13278
|
return null;
|
|
13058
13279
|
}
|
|
13059
|
-
function checkMtime(files, bypassForRecentCommits) {
|
|
13280
|
+
function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
13060
13281
|
if (bypassForRecentCommits) return null;
|
|
13061
|
-
|
|
13282
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13283
|
+
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13062
13284
|
let debounceTime;
|
|
13063
13285
|
try {
|
|
13064
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13286
|
+
debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
|
|
13065
13287
|
} catch {
|
|
13066
13288
|
return null;
|
|
13067
13289
|
}
|
|
@@ -13080,7 +13302,7 @@ function checkMtime(files, bypassForRecentCommits) {
|
|
|
13080
13302
|
return "No files modified since last analysis";
|
|
13081
13303
|
}
|
|
13082
13304
|
function computeContentHash(files) {
|
|
13083
|
-
const hash = (0,
|
|
13305
|
+
const hash = (0, import_node_crypto4.createHash)("sha1");
|
|
13084
13306
|
const sorted = [...files].sort();
|
|
13085
13307
|
for (const f of sorted) {
|
|
13086
13308
|
const resolved = resolveFile(f) ?? f;
|
|
@@ -13093,11 +13315,12 @@ function computeContentHash(files) {
|
|
|
13093
13315
|
}
|
|
13094
13316
|
return hash.digest("hex");
|
|
13095
13317
|
}
|
|
13096
|
-
function checkContentHash(files) {
|
|
13318
|
+
function checkContentHash(files, sessionId) {
|
|
13097
13319
|
const hash = computeContentHash(files);
|
|
13098
|
-
|
|
13320
|
+
const file = scopedFile(HASH_FILE, sessionId);
|
|
13321
|
+
if ((0, import_node_fs9.existsSync)(file)) {
|
|
13099
13322
|
try {
|
|
13100
|
-
const storedHash = (0, import_node_fs9.readFileSync)(
|
|
13323
|
+
const storedHash = (0, import_node_fs9.readFileSync)(file, "utf-8").trim();
|
|
13101
13324
|
if (hash === storedHash) {
|
|
13102
13325
|
return { skip: "No source changes since last analysis", hash };
|
|
13103
13326
|
}
|
|
@@ -13106,18 +13329,19 @@ function checkContentHash(files) {
|
|
|
13106
13329
|
}
|
|
13107
13330
|
return { skip: null, hash };
|
|
13108
13331
|
}
|
|
13109
|
-
function recordAnalysisStart() {
|
|
13332
|
+
function recordAnalysisStart(sessionId) {
|
|
13110
13333
|
(0, import_node_fs9.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
13111
|
-
(0, import_node_fs9.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
|
|
13334
|
+
(0, import_node_fs9.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
|
|
13112
13335
|
}
|
|
13113
|
-
function recordPassHash(hash) {
|
|
13114
|
-
(0, import_node_fs9.writeFileSync)(HASH_FILE, hash);
|
|
13336
|
+
function recordPassHash(hash, sessionId) {
|
|
13337
|
+
(0, import_node_fs9.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
|
|
13115
13338
|
}
|
|
13116
|
-
function narrowToRecent(files) {
|
|
13117
|
-
|
|
13339
|
+
function narrowToRecent(files, sessionId) {
|
|
13340
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13341
|
+
if (!(0, import_node_fs9.existsSync)(file)) return files;
|
|
13118
13342
|
let debounceTime;
|
|
13119
13343
|
try {
|
|
13120
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13344
|
+
debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
|
|
13121
13345
|
} catch {
|
|
13122
13346
|
return files;
|
|
13123
13347
|
}
|
|
@@ -13463,15 +13687,207 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13463
13687
|
}
|
|
13464
13688
|
}
|
|
13465
13689
|
|
|
13466
|
-
// src/lib/
|
|
13690
|
+
// src/lib/baseline.ts
|
|
13467
13691
|
var import_node_fs13 = require("node:fs");
|
|
13468
|
-
var
|
|
13692
|
+
var import_node_path11 = require("node:path");
|
|
13693
|
+
var import_node_crypto5 = require("node:crypto");
|
|
13694
|
+
var BASELINE_VERSION = 1;
|
|
13695
|
+
var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13696
|
+
var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
|
|
13697
|
+
var DEFAULT_SESSION_KEY = "_default";
|
|
13698
|
+
function sessionKey(sessionId) {
|
|
13699
|
+
if (!sessionId) return DEFAULT_SESSION_KEY;
|
|
13700
|
+
return (0, import_node_crypto5.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
13701
|
+
}
|
|
13702
|
+
function sessionDir(key) {
|
|
13703
|
+
return (0, import_node_path11.join)(projectPath(BASELINE_DIR), key);
|
|
13704
|
+
}
|
|
13705
|
+
function manifestPath(dir) {
|
|
13706
|
+
return (0, import_node_path11.join)(dir, "manifest.json");
|
|
13707
|
+
}
|
|
13708
|
+
function mirrorPath(dir, repoRelPath) {
|
|
13709
|
+
return (0, import_node_path11.join)(dir, "files", repoRelPath);
|
|
13710
|
+
}
|
|
13711
|
+
function captureBaseline(opts = {}) {
|
|
13712
|
+
const key = sessionKey(opts.sessionId);
|
|
13713
|
+
const dir = sessionDir(key);
|
|
13714
|
+
const existing = readManifest(dir);
|
|
13715
|
+
const freshStart = opts.source === "startup" || opts.source === "clear";
|
|
13716
|
+
if (existing && !freshStart) {
|
|
13717
|
+
return { baseline: existing, created: false };
|
|
13718
|
+
}
|
|
13719
|
+
const head_sha = getCurrentCommit();
|
|
13720
|
+
const dirty = getDirtyFiles();
|
|
13721
|
+
try {
|
|
13722
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13723
|
+
} catch {
|
|
13724
|
+
}
|
|
13725
|
+
const filesDir = (0, import_node_path11.join)(dir, "files");
|
|
13726
|
+
const mirrored = [];
|
|
13727
|
+
try {
|
|
13728
|
+
(0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
|
|
13729
|
+
for (const p of dirty) {
|
|
13730
|
+
if (p.includes("..")) continue;
|
|
13731
|
+
const content = safeReadForMirror(projectPath(p));
|
|
13732
|
+
if (content === null) continue;
|
|
13733
|
+
const dest = mirrorPath(dir, p);
|
|
13734
|
+
try {
|
|
13735
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(dest), { recursive: true });
|
|
13736
|
+
(0, import_node_fs13.writeFileSync)(dest, content);
|
|
13737
|
+
mirrored.push(p);
|
|
13738
|
+
} catch {
|
|
13739
|
+
}
|
|
13740
|
+
}
|
|
13741
|
+
} catch {
|
|
13742
|
+
}
|
|
13743
|
+
const baseline = {
|
|
13744
|
+
session_id: opts.sessionId ?? "",
|
|
13745
|
+
head_sha,
|
|
13746
|
+
captured_at: Date.now(),
|
|
13747
|
+
dirty_paths: mirrored,
|
|
13748
|
+
version: BASELINE_VERSION
|
|
13749
|
+
};
|
|
13750
|
+
try {
|
|
13751
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
13752
|
+
(0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
|
|
13753
|
+
} catch {
|
|
13754
|
+
}
|
|
13755
|
+
pruneOldBaselines();
|
|
13756
|
+
return { baseline, created: true };
|
|
13757
|
+
}
|
|
13758
|
+
function readBaseline(sessionId) {
|
|
13759
|
+
return readManifest(sessionDir(sessionKey(sessionId)));
|
|
13760
|
+
}
|
|
13761
|
+
function readManifest(dir) {
|
|
13762
|
+
const mp = manifestPath(dir);
|
|
13763
|
+
if (!(0, import_node_fs13.existsSync)(mp)) return null;
|
|
13764
|
+
try {
|
|
13765
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(mp, "utf-8"));
|
|
13766
|
+
if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
|
|
13767
|
+
return null;
|
|
13768
|
+
}
|
|
13769
|
+
return {
|
|
13770
|
+
session_id: typeof parsed.session_id === "string" ? parsed.session_id : "",
|
|
13771
|
+
head_sha: parsed.head_sha,
|
|
13772
|
+
captured_at: parsed.captured_at,
|
|
13773
|
+
dirty_paths: parsed.dirty_paths.filter((p) => typeof p === "string"),
|
|
13774
|
+
version: parsed.version
|
|
13775
|
+
};
|
|
13776
|
+
} catch {
|
|
13777
|
+
return null;
|
|
13778
|
+
}
|
|
13779
|
+
}
|
|
13780
|
+
var preImageCache = /* @__PURE__ */ new WeakMap();
|
|
13781
|
+
function preImage(repoRelPath, baseline) {
|
|
13782
|
+
let perBaseline = preImageCache.get(baseline);
|
|
13783
|
+
if (!perBaseline) {
|
|
13784
|
+
perBaseline = /* @__PURE__ */ new Map();
|
|
13785
|
+
preImageCache.set(baseline, perBaseline);
|
|
13786
|
+
}
|
|
13787
|
+
const cached = perBaseline.get(repoRelPath);
|
|
13788
|
+
if (cached) return cached;
|
|
13789
|
+
const resolved = resolvePreImage(repoRelPath, baseline);
|
|
13790
|
+
perBaseline.set(repoRelPath, resolved);
|
|
13791
|
+
return resolved;
|
|
13792
|
+
}
|
|
13793
|
+
function resolvePreImage(repoRelPath, baseline) {
|
|
13794
|
+
if (baseline.dirty_paths.includes(repoRelPath)) {
|
|
13795
|
+
const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
|
|
13796
|
+
if ((0, import_node_fs13.existsSync)(mp)) {
|
|
13797
|
+
try {
|
|
13798
|
+
return { content: (0, import_node_fs13.readFileSync)(mp, "utf-8"), existed: true };
|
|
13799
|
+
} catch {
|
|
13800
|
+
}
|
|
13801
|
+
}
|
|
13802
|
+
}
|
|
13803
|
+
const atHead = showContentAtRef(baseline.head_sha, repoRelPath);
|
|
13804
|
+
if (atHead !== null) return { content: atHead, existed: true };
|
|
13805
|
+
return { content: "", existed: false };
|
|
13806
|
+
}
|
|
13807
|
+
function generateBaselineDiffs(files, baseline) {
|
|
13808
|
+
if (!baseline) return { diffs: [], has_baseline: false };
|
|
13809
|
+
const diffs = [];
|
|
13810
|
+
for (const file of files) {
|
|
13811
|
+
const language = file.language ?? detectLanguage(file.path);
|
|
13812
|
+
const pre = preImage(file.path, baseline);
|
|
13813
|
+
if (pre.existed) {
|
|
13814
|
+
if (pre.content === file.content) continue;
|
|
13815
|
+
const diff = computeDiff(pre.content, file.content, file.path);
|
|
13816
|
+
if (diff) diffs.push({ path: file.path, language, diff, status: "modified" });
|
|
13817
|
+
} else {
|
|
13818
|
+
const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
|
|
13819
|
+
diffs.push({
|
|
13820
|
+
path: file.path,
|
|
13821
|
+
language,
|
|
13822
|
+
diff: `--- /dev/null
|
|
13823
|
+
+++ b/${file.path}
|
|
13824
|
+
@@ -0,0 +1,${file.content.split("\n").length} @@
|
|
13825
|
+
${addedLines}`,
|
|
13826
|
+
status: "added"
|
|
13827
|
+
});
|
|
13828
|
+
}
|
|
13829
|
+
}
|
|
13830
|
+
return { diffs, has_baseline: true };
|
|
13831
|
+
}
|
|
13832
|
+
function changedSinceBaseline(repoRelPath, baseline) {
|
|
13833
|
+
const pre = preImage(repoRelPath, baseline);
|
|
13834
|
+
let current;
|
|
13835
|
+
try {
|
|
13836
|
+
current = (0, import_node_fs13.readFileSync)(projectPath(repoRelPath), "utf-8");
|
|
13837
|
+
} catch {
|
|
13838
|
+
return pre.existed;
|
|
13839
|
+
}
|
|
13840
|
+
if (!pre.existed) return true;
|
|
13841
|
+
return current !== pre.content;
|
|
13842
|
+
}
|
|
13843
|
+
function safeReadForMirror(absPath) {
|
|
13844
|
+
try {
|
|
13845
|
+
if ((0, import_node_fs13.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
|
|
13846
|
+
const buf = (0, import_node_fs13.readFileSync)(absPath);
|
|
13847
|
+
if (buf.includes(0)) return null;
|
|
13848
|
+
return buf.toString("utf-8");
|
|
13849
|
+
} catch {
|
|
13850
|
+
return null;
|
|
13851
|
+
}
|
|
13852
|
+
}
|
|
13853
|
+
function pruneOldBaselines() {
|
|
13854
|
+
const root = projectPath(BASELINE_DIR);
|
|
13855
|
+
let entries;
|
|
13856
|
+
try {
|
|
13857
|
+
entries = (0, import_node_fs13.readdirSync)(root);
|
|
13858
|
+
} catch {
|
|
13859
|
+
return;
|
|
13860
|
+
}
|
|
13861
|
+
const now = Date.now();
|
|
13862
|
+
for (const name of entries) {
|
|
13863
|
+
const dir = (0, import_node_path11.join)(root, name);
|
|
13864
|
+
const manifest = readManifest(dir);
|
|
13865
|
+
if (!manifest) {
|
|
13866
|
+
try {
|
|
13867
|
+
if (now - (0, import_node_fs13.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
|
|
13868
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13869
|
+
}
|
|
13870
|
+
} catch {
|
|
13871
|
+
}
|
|
13872
|
+
continue;
|
|
13873
|
+
}
|
|
13874
|
+
if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
|
|
13875
|
+
try {
|
|
13876
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13877
|
+
} catch {
|
|
13878
|
+
}
|
|
13879
|
+
}
|
|
13880
|
+
}
|
|
13881
|
+
|
|
13882
|
+
// src/lib/offline.ts
|
|
13883
|
+
var import_node_fs14 = require("node:fs");
|
|
13884
|
+
var import_node_crypto6 = require("node:crypto");
|
|
13469
13885
|
function cacheRequest(body) {
|
|
13470
13886
|
try {
|
|
13471
|
-
(0,
|
|
13472
|
-
const suffix = (0,
|
|
13887
|
+
(0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
13888
|
+
const suffix = (0, import_node_crypto6.randomBytes)(4).toString("hex");
|
|
13473
13889
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
13474
|
-
(0,
|
|
13890
|
+
(0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
|
|
13475
13891
|
} catch {
|
|
13476
13892
|
}
|
|
13477
13893
|
}
|
|
@@ -13485,7 +13901,7 @@ function buildOfflineFallback(reason, staticResults) {
|
|
|
13485
13901
|
}
|
|
13486
13902
|
|
|
13487
13903
|
// src/lib/context-files.ts
|
|
13488
|
-
var
|
|
13904
|
+
var import_node_fs15 = require("node:fs");
|
|
13489
13905
|
var MAX_CONTEXT_FILES = 10;
|
|
13490
13906
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
13491
13907
|
var MAX_CONTEXT_TOTAL_BYTES = 51200;
|
|
@@ -13497,7 +13913,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13497
13913
|
if (result.length >= MAX_CONTEXT_FILES) break;
|
|
13498
13914
|
if (deltaPaths.has(filePath)) continue;
|
|
13499
13915
|
try {
|
|
13500
|
-
const content = (0,
|
|
13916
|
+
const content = (0, import_node_fs15.readFileSync)(filePath, "utf8");
|
|
13501
13917
|
const bytes = Buffer.byteLength(content);
|
|
13502
13918
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
13503
13919
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -13542,20 +13958,20 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13542
13958
|
}
|
|
13543
13959
|
|
|
13544
13960
|
// src/lib/cache-cleanup.ts
|
|
13545
|
-
var
|
|
13546
|
-
var
|
|
13961
|
+
var import_node_fs16 = require("node:fs");
|
|
13962
|
+
var import_node_path12 = require("node:path");
|
|
13547
13963
|
var CACHE_TTL_DAYS = 7;
|
|
13548
13964
|
function pruneStaleCache() {
|
|
13549
13965
|
try {
|
|
13550
13966
|
const dir = projectPath(CACHE_DIR);
|
|
13551
13967
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
13552
|
-
for (const entry of (0,
|
|
13968
|
+
for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
|
|
13553
13969
|
if (!entry.startsWith("pending-")) continue;
|
|
13554
|
-
const path = (0,
|
|
13970
|
+
const path = (0, import_node_path12.join)(dir, entry);
|
|
13555
13971
|
try {
|
|
13556
|
-
const stat3 = (0,
|
|
13972
|
+
const stat3 = (0, import_node_fs16.statSync)(path);
|
|
13557
13973
|
if (stat3.mtimeMs < cutoff) {
|
|
13558
|
-
(0,
|
|
13974
|
+
(0, import_node_fs16.unlinkSync)(path);
|
|
13559
13975
|
logEvent("cache_entry_pruned", {
|
|
13560
13976
|
path: entry,
|
|
13561
13977
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -13627,10 +14043,11 @@ function reconcileAnalysisMode(predictedMode, signals) {
|
|
|
13627
14043
|
signals.noFilesChanged,
|
|
13628
14044
|
signals.assistantResponse,
|
|
13629
14045
|
signals.conversationPrompts,
|
|
13630
|
-
signals.actionSummary
|
|
14046
|
+
signals.actionSummary,
|
|
14047
|
+
signals.sessionAuthoredCode
|
|
13631
14048
|
);
|
|
13632
14049
|
}
|
|
13633
|
-
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0));
|
|
14050
|
+
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
13634
14051
|
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
13635
14052
|
switch (predictedMode) {
|
|
13636
14053
|
case "skip":
|
|
@@ -13655,8 +14072,8 @@ function didAgentInvestigate(summary) {
|
|
|
13655
14072
|
function isValidMode(mode) {
|
|
13656
14073
|
return mode === "standard" || mode === "plan" || mode === "debug" || mode === "skip";
|
|
13657
14074
|
}
|
|
13658
|
-
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary) {
|
|
13659
|
-
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
14075
|
+
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
14076
|
+
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
13660
14077
|
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
13661
14078
|
if (!agentAuthoredCode) return "skip";
|
|
13662
14079
|
}
|
|
@@ -13736,7 +14153,7 @@ function isMetaTaskLabel(label2) {
|
|
|
13736
14153
|
}
|
|
13737
14154
|
|
|
13738
14155
|
// src/lib/transcript.ts
|
|
13739
|
-
var
|
|
14156
|
+
var import_node_fs17 = require("node:fs");
|
|
13740
14157
|
var MAX_READ_BYTES = 256 * 1024;
|
|
13741
14158
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
13742
14159
|
var MAX_FILES_LIST = 20;
|
|
@@ -13758,14 +14175,14 @@ async function extractActionSummary(transcriptPath) {
|
|
|
13758
14175
|
function readTurnLines(transcriptPath) {
|
|
13759
14176
|
let size;
|
|
13760
14177
|
try {
|
|
13761
|
-
size = (0,
|
|
14178
|
+
size = (0, import_node_fs17.statSync)(transcriptPath).size;
|
|
13762
14179
|
} catch {
|
|
13763
14180
|
return null;
|
|
13764
14181
|
}
|
|
13765
14182
|
if (size === 0) return null;
|
|
13766
14183
|
let raw;
|
|
13767
14184
|
if (size <= SMALL_FILE_BYTES) {
|
|
13768
|
-
raw = (0,
|
|
14185
|
+
raw = (0, import_node_fs17.readFileSync)(transcriptPath, "utf-8");
|
|
13769
14186
|
} else {
|
|
13770
14187
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
13771
14188
|
const fd = require("node:fs").openSync(transcriptPath, "r");
|
|
@@ -13853,6 +14270,12 @@ function buildSummary(lines) {
|
|
|
13853
14270
|
case "Edit":
|
|
13854
14271
|
addPath(filesEdited, input.file_path);
|
|
13855
14272
|
break;
|
|
14273
|
+
case "MultiEdit":
|
|
14274
|
+
addPath(filesEdited, input.file_path);
|
|
14275
|
+
break;
|
|
14276
|
+
case "NotebookEdit":
|
|
14277
|
+
addPath(filesEdited, input.notebook_path ?? input.file_path);
|
|
14278
|
+
break;
|
|
13856
14279
|
case "Write":
|
|
13857
14280
|
addPath(filesCreated, input.file_path);
|
|
13858
14281
|
addPath(filesEdited, input.file_path);
|
|
@@ -13936,8 +14359,8 @@ function capArray(set, max) {
|
|
|
13936
14359
|
|
|
13937
14360
|
// src/lib/seed-runner.ts
|
|
13938
14361
|
var import_promises11 = require("node:fs/promises");
|
|
13939
|
-
var
|
|
13940
|
-
var
|
|
14362
|
+
var import_node_fs18 = require("node:fs");
|
|
14363
|
+
var import_node_path13 = require("node:path");
|
|
13941
14364
|
var import_yaml2 = __toESM(require_dist());
|
|
13942
14365
|
|
|
13943
14366
|
// src/lib/seed.ts
|
|
@@ -14176,7 +14599,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
14176
14599
|
return fm;
|
|
14177
14600
|
}
|
|
14178
14601
|
async function runSeed(opts) {
|
|
14179
|
-
if (!(0,
|
|
14602
|
+
if (!(0, import_node_fs18.existsSync)(STANDARD_FILE)) {
|
|
14180
14603
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
14181
14604
|
}
|
|
14182
14605
|
let standardDoc;
|
|
@@ -14188,7 +14611,7 @@ async function runSeed(opts) {
|
|
|
14188
14611
|
}
|
|
14189
14612
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
14190
14613
|
let readmeContent;
|
|
14191
|
-
if ((0,
|
|
14614
|
+
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
14192
14615
|
try {
|
|
14193
14616
|
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
14194
14617
|
} catch {
|
|
@@ -14196,7 +14619,7 @@ async function runSeed(opts) {
|
|
|
14196
14619
|
}
|
|
14197
14620
|
let claudeMdContent;
|
|
14198
14621
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
14199
|
-
if ((0,
|
|
14622
|
+
if ((0, import_node_fs18.existsSync)(p)) {
|
|
14200
14623
|
try {
|
|
14201
14624
|
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
14202
14625
|
break;
|
|
@@ -14219,8 +14642,8 @@ async function runSeed(opts) {
|
|
|
14219
14642
|
if (candidates.length === 0) {
|
|
14220
14643
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
14221
14644
|
}
|
|
14222
|
-
const overviewPath = (0,
|
|
14223
|
-
if ((0,
|
|
14645
|
+
const overviewPath = (0, import_node_path13.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
14646
|
+
if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
|
|
14224
14647
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
14225
14648
|
}
|
|
14226
14649
|
if (opts.dryRun) {
|
|
@@ -14255,9 +14678,9 @@ async function runSeed(opts) {
|
|
|
14255
14678
|
}
|
|
14256
14679
|
const nodeId = res.data.node_id;
|
|
14257
14680
|
const filePathRel = res.data.file_path;
|
|
14258
|
-
const targetPath = (0,
|
|
14681
|
+
const targetPath = (0, import_node_path13.join)(MEMORY_DIR, filePathRel);
|
|
14259
14682
|
try {
|
|
14260
|
-
await (0, import_promises11.mkdir)((0,
|
|
14683
|
+
await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(targetPath), { recursive: true });
|
|
14261
14684
|
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
14262
14685
|
created++;
|
|
14263
14686
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
@@ -14327,6 +14750,15 @@ async function runAnalyze(opts, globals) {
|
|
|
14327
14750
|
}
|
|
14328
14751
|
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
14329
14752
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
14753
|
+
const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
14754
|
+
const baseline = readBaseline(baselineSessionId);
|
|
14755
|
+
if (baseline) {
|
|
14756
|
+
logEvent("baseline_loaded", {
|
|
14757
|
+
head: baseline.head_sha.slice(0, 12),
|
|
14758
|
+
dirty_count: baseline.dirty_paths.length,
|
|
14759
|
+
age_ms: Date.now() - baseline.captured_at
|
|
14760
|
+
});
|
|
14761
|
+
}
|
|
14330
14762
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
14331
14763
|
const analyzable = filterAnalyzable(allChanged);
|
|
14332
14764
|
const reviewable = filterReviewable(allChanged);
|
|
@@ -14336,7 +14768,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14336
14768
|
passAndExit("No analyzable files changed");
|
|
14337
14769
|
}
|
|
14338
14770
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
14339
|
-
const conversation = await readAndClearConversationBuffer();
|
|
14771
|
+
const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
|
|
14340
14772
|
const specs = discoverSpecs();
|
|
14341
14773
|
const plans = discoverPlans();
|
|
14342
14774
|
const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
|
|
@@ -14386,13 +14818,14 @@ async function runAnalyze(opts, globals) {
|
|
|
14386
14818
|
}
|
|
14387
14819
|
const conversationPrompts = (conversation?.prompts ?? []).map((p) => p.prompt);
|
|
14388
14820
|
let analysisMode;
|
|
14821
|
+
const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
14389
14822
|
const modeOverride = opts.mode;
|
|
14390
14823
|
if (modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride)) {
|
|
14391
14824
|
analysisMode = modeOverride;
|
|
14392
14825
|
} else {
|
|
14393
14826
|
analysisMode = reconcileAnalysisMode(
|
|
14394
14827
|
predictedMode,
|
|
14395
|
-
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts }
|
|
14828
|
+
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
|
|
14396
14829
|
);
|
|
14397
14830
|
}
|
|
14398
14831
|
if (analysisMode === "skip") {
|
|
@@ -14414,7 +14847,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14414
14847
|
let currentCommit = "";
|
|
14415
14848
|
if (analysisMode !== "plan") {
|
|
14416
14849
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
14417
|
-
const debounceSkip = checkDebounce(debounceSeconds);
|
|
14850
|
+
const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
|
|
14418
14851
|
if (debounceSkip) {
|
|
14419
14852
|
if (assistantResponse) {
|
|
14420
14853
|
analysisMode = "plan";
|
|
@@ -14424,7 +14857,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14424
14857
|
}
|
|
14425
14858
|
if (analysisMode !== "plan") {
|
|
14426
14859
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14427
|
-
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
14860
|
+
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
|
|
14428
14861
|
if (mtimeSkip) {
|
|
14429
14862
|
if (assistantResponse) {
|
|
14430
14863
|
analysisMode = "plan";
|
|
@@ -14434,9 +14867,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14434
14867
|
}
|
|
14435
14868
|
}
|
|
14436
14869
|
if (analysisMode !== "plan") {
|
|
14437
|
-
recordAnalysisStart();
|
|
14870
|
+
recordAnalysisStart(baselineSessionId);
|
|
14438
14871
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14439
|
-
const hashResult = checkContentHash(allCheckable);
|
|
14872
|
+
const hashResult = checkContentHash(allCheckable, baselineSessionId);
|
|
14440
14873
|
if (hashResult.skip) {
|
|
14441
14874
|
if (assistantResponse) {
|
|
14442
14875
|
analysisMode = "plan";
|
|
@@ -14448,12 +14881,18 @@ async function runAnalyze(opts, globals) {
|
|
|
14448
14881
|
if (analysisMode !== "plan") {
|
|
14449
14882
|
const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
|
|
14450
14883
|
const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
|
|
14451
|
-
const recentForReview = narrowToRecent(baseForReview);
|
|
14884
|
+
const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
|
|
14452
14885
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
14453
|
-
|
|
14454
|
-
|
|
14886
|
+
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14887
|
+
if (baseline) {
|
|
14888
|
+
allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
14889
|
+
}
|
|
14890
|
+
if (allScannable.length > 0) {
|
|
14891
|
+
staticResults = runCodacyAnalysis(allScannable);
|
|
14892
|
+
}
|
|
14455
14893
|
}
|
|
14456
|
-
|
|
14894
|
+
const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
|
|
14895
|
+
codeDelta = collectCodeDelta(deltaSet, {
|
|
14457
14896
|
maxFiles: parseInt(opts.maxFiles, 10),
|
|
14458
14897
|
maxFileBytes: parseInt(opts.maxFileSize, 10),
|
|
14459
14898
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
@@ -14468,7 +14907,12 @@ async function runAnalyze(opts, globals) {
|
|
|
14468
14907
|
}
|
|
14469
14908
|
}
|
|
14470
14909
|
if (analysisMode !== "plan") {
|
|
14471
|
-
|
|
14910
|
+
if (baseline) {
|
|
14911
|
+
const baselineDiffs = generateBaselineDiffs(codeDelta.files, baseline);
|
|
14912
|
+
snapshotResult = { has_snapshots: baselineDiffs.diffs.length > 0, diffs: baselineDiffs.diffs };
|
|
14913
|
+
} else {
|
|
14914
|
+
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
14915
|
+
}
|
|
14472
14916
|
currentCommit = getCurrentCommit();
|
|
14473
14917
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
14474
14918
|
const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
@@ -14500,9 +14944,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14500
14944
|
let autoSeedNotice = null;
|
|
14501
14945
|
try {
|
|
14502
14946
|
await ensureMemoryDir();
|
|
14503
|
-
const seedMarker = (0,
|
|
14504
|
-
const hasStandard = (0,
|
|
14505
|
-
const alreadyTried = (0,
|
|
14947
|
+
const seedMarker = (0, import_node_path14.join)(VERITY_DIR, ".seeded");
|
|
14948
|
+
const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
|
|
14949
|
+
const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
|
|
14506
14950
|
if (hasStandard && !alreadyTried) {
|
|
14507
14951
|
const preManifest = await buildManifest();
|
|
14508
14952
|
if (preManifest.nodes.length === 0) {
|
|
@@ -14515,7 +14959,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14515
14959
|
dryRun: false
|
|
14516
14960
|
});
|
|
14517
14961
|
if (seedResult.created > 0) {
|
|
14518
|
-
(0,
|
|
14962
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
14519
14963
|
`);
|
|
14520
14964
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
14521
14965
|
logEvent("auto_seed_ran", {
|
|
@@ -14523,7 +14967,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14523
14967
|
failed: seedResult.failed
|
|
14524
14968
|
});
|
|
14525
14969
|
} else if (seedResult.skipped === "already_seeded") {
|
|
14526
|
-
(0,
|
|
14970
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
14527
14971
|
`);
|
|
14528
14972
|
} else {
|
|
14529
14973
|
logEvent("auto_seed_noop", {
|
|
@@ -14812,7 +15256,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14812
15256
|
}
|
|
14813
15257
|
case "PASS": {
|
|
14814
15258
|
writeIteration(1, currentCommit, contentHash ?? void 0);
|
|
14815
|
-
if (contentHash) recordPassHash(contentHash);
|
|
15259
|
+
if (contentHash) recordPassHash(contentHash, baselineSessionId);
|
|
14816
15260
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
14817
15261
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
14818
15262
|
const viewUrl = response.view_url ?? "";
|
|
@@ -14823,7 +15267,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14823
15267
|
break;
|
|
14824
15268
|
}
|
|
14825
15269
|
case "WARN": {
|
|
14826
|
-
if (contentHash) recordPassHash(contentHash);
|
|
15270
|
+
if (contentHash) recordPassHash(contentHash, baselineSessionId);
|
|
14827
15271
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
14828
15272
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
14829
15273
|
const viewUrl = response.view_url ?? "";
|
|
@@ -14841,8 +15285,54 @@ async function runAnalyze(opts, globals) {
|
|
|
14841
15285
|
}
|
|
14842
15286
|
}
|
|
14843
15287
|
|
|
15288
|
+
// src/commands/baseline.ts
|
|
15289
|
+
var import_node_fs20 = require("node:fs");
|
|
15290
|
+
function registerBaselineCommands(program2) {
|
|
15291
|
+
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
15292
|
+
baseline.command("capture").description("Snapshot the working tree at task start (used by SessionStart hook)").option("--session-id <id>", "Session id (overrides any value from stdin)").option("--source <source>", "Lifecycle hint: startup|resume|clear|compact").action(async (opts) => {
|
|
15293
|
+
try {
|
|
15294
|
+
try {
|
|
15295
|
+
process.chdir(repoRoot());
|
|
15296
|
+
} catch {
|
|
15297
|
+
}
|
|
15298
|
+
if (!(0, import_node_fs20.existsSync)(VERITY_DIR)) {
|
|
15299
|
+
process.exit(0);
|
|
15300
|
+
}
|
|
15301
|
+
let sessionId = opts.sessionId;
|
|
15302
|
+
let source = opts.source;
|
|
15303
|
+
if (!process.stdin.isTTY) {
|
|
15304
|
+
const input = (await readStdin()).trim();
|
|
15305
|
+
if (input) {
|
|
15306
|
+
try {
|
|
15307
|
+
const event = JSON.parse(input);
|
|
15308
|
+
sessionId = sessionId ?? event.session_id;
|
|
15309
|
+
source = source ?? event.source;
|
|
15310
|
+
} catch {
|
|
15311
|
+
}
|
|
15312
|
+
}
|
|
15313
|
+
}
|
|
15314
|
+
const result = captureBaseline({ sessionId, source });
|
|
15315
|
+
logEvent("baseline_capture", {
|
|
15316
|
+
created: result.created,
|
|
15317
|
+
source: source ?? null,
|
|
15318
|
+
dirty_count: result.baseline.dirty_paths.length,
|
|
15319
|
+
head: result.baseline.head_sha.slice(0, 12)
|
|
15320
|
+
});
|
|
15321
|
+
} catch {
|
|
15322
|
+
}
|
|
15323
|
+
process.exit(0);
|
|
15324
|
+
});
|
|
15325
|
+
}
|
|
15326
|
+
async function readStdin() {
|
|
15327
|
+
const chunks = [];
|
|
15328
|
+
for await (const chunk of process.stdin) {
|
|
15329
|
+
chunks.push(chunk);
|
|
15330
|
+
}
|
|
15331
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
15332
|
+
}
|
|
15333
|
+
|
|
14844
15334
|
// src/commands/review.ts
|
|
14845
|
-
var
|
|
15335
|
+
var import_node_fs21 = require("node:fs");
|
|
14846
15336
|
function registerReviewCommand(program2) {
|
|
14847
15337
|
program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
14848
15338
|
const globals = program2.opts();
|
|
@@ -14861,7 +15351,7 @@ async function runReview(opts, globals) {
|
|
|
14861
15351
|
const securityFiles = filterSecurity(allFiles);
|
|
14862
15352
|
let staticResults;
|
|
14863
15353
|
if (isCodacyAvailable()) {
|
|
14864
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
15354
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs21.existsSync)(f) || resolveFile(f) !== null);
|
|
14865
15355
|
staticResults = runCodacyAnalysis(scannable);
|
|
14866
15356
|
} else {
|
|
14867
15357
|
staticResults = {
|
|
@@ -14886,10 +15376,10 @@ async function runReview(opts, globals) {
|
|
|
14886
15376
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
14887
15377
|
specs = [];
|
|
14888
15378
|
for (const p of specPaths) {
|
|
14889
|
-
if (!(0,
|
|
15379
|
+
if (!(0, import_node_fs21.existsSync)(p)) continue;
|
|
14890
15380
|
try {
|
|
14891
|
-
const { readFileSync:
|
|
14892
|
-
const content =
|
|
15381
|
+
const { readFileSync: readFileSync12 } = await import("node:fs");
|
|
15382
|
+
const content = readFileSync12(p, "utf-8");
|
|
14893
15383
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
14894
15384
|
} catch {
|
|
14895
15385
|
}
|
|
@@ -14946,10 +15436,10 @@ async function runReview(opts, globals) {
|
|
|
14946
15436
|
}
|
|
14947
15437
|
|
|
14948
15438
|
// src/commands/guard.ts
|
|
14949
|
-
var
|
|
14950
|
-
var
|
|
15439
|
+
var import_node_fs22 = require("node:fs");
|
|
15440
|
+
var import_node_path15 = require("node:path");
|
|
14951
15441
|
var GUARD_BLOCK_CAP = 2;
|
|
14952
|
-
var GUARD_ITER_FILE = (0,
|
|
15442
|
+
var GUARD_ITER_FILE = (0, import_node_path15.join)(VERITY_DIR, ".guard-iteration");
|
|
14953
15443
|
function readPreToolUseStdin() {
|
|
14954
15444
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
14955
15445
|
return new Promise((resolve) => {
|
|
@@ -15013,7 +15503,7 @@ function classifyCommand(command, on) {
|
|
|
15013
15503
|
}
|
|
15014
15504
|
function readIterMap() {
|
|
15015
15505
|
try {
|
|
15016
|
-
const raw = JSON.parse((0,
|
|
15506
|
+
const raw = JSON.parse((0, import_node_fs22.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
15017
15507
|
if (raw && typeof raw === "object") {
|
|
15018
15508
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
15019
15509
|
return { [raw.moment]: raw.count };
|
|
@@ -15033,10 +15523,10 @@ function readIter(moment) {
|
|
|
15033
15523
|
}
|
|
15034
15524
|
function writeIter(moment, count) {
|
|
15035
15525
|
try {
|
|
15036
|
-
(0,
|
|
15526
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15037
15527
|
const map = readIterMap();
|
|
15038
15528
|
map[moment] = count;
|
|
15039
|
-
(0,
|
|
15529
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15040
15530
|
} catch {
|
|
15041
15531
|
}
|
|
15042
15532
|
}
|
|
@@ -15046,10 +15536,10 @@ function resetIter(moment) {
|
|
|
15046
15536
|
if (!(moment in map)) return;
|
|
15047
15537
|
delete map[moment];
|
|
15048
15538
|
if (Object.keys(map).length === 0) {
|
|
15049
|
-
if ((0,
|
|
15539
|
+
if ((0, import_node_fs22.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs22.unlinkSync)(GUARD_ITER_FILE);
|
|
15050
15540
|
} else {
|
|
15051
|
-
(0,
|
|
15052
|
-
(0,
|
|
15541
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15542
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15053
15543
|
}
|
|
15054
15544
|
} catch {
|
|
15055
15545
|
}
|
|
@@ -15113,7 +15603,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
15113
15603
|
const securityFiles = filterSecurity(files);
|
|
15114
15604
|
let staticResults;
|
|
15115
15605
|
if (isCodacyAvailable()) {
|
|
15116
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
15606
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs22.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
15117
15607
|
staticResults = runCodacyAnalysis(scannable);
|
|
15118
15608
|
} else {
|
|
15119
15609
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
@@ -15158,7 +15648,7 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
15158
15648
|
async function runGuard(opts, globals) {
|
|
15159
15649
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
15160
15650
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
15161
|
-
if (cwd && (0,
|
|
15651
|
+
if (cwd && (0, import_node_fs22.existsSync)(cwd)) {
|
|
15162
15652
|
try {
|
|
15163
15653
|
process.chdir(cwd);
|
|
15164
15654
|
} catch {
|
|
@@ -15263,14 +15753,14 @@ function writeBlockMessage(moment, response) {
|
|
|
15263
15753
|
}
|
|
15264
15754
|
|
|
15265
15755
|
// src/commands/init.ts
|
|
15266
|
-
var
|
|
15756
|
+
var import_node_fs24 = require("node:fs");
|
|
15267
15757
|
var import_promises12 = require("node:fs/promises");
|
|
15268
|
-
var
|
|
15758
|
+
var import_node_path17 = require("node:path");
|
|
15269
15759
|
var import_node_child_process9 = require("node:child_process");
|
|
15270
15760
|
|
|
15271
15761
|
// src/commands/migrate.ts
|
|
15272
|
-
var
|
|
15273
|
-
var
|
|
15762
|
+
var import_node_fs23 = require("node:fs");
|
|
15763
|
+
var import_node_path16 = require("node:path");
|
|
15274
15764
|
var import_node_child_process8 = require("node:child_process");
|
|
15275
15765
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
15276
15766
|
function defaultNpmRemover(pkg) {
|
|
@@ -15306,12 +15796,12 @@ async function runMigration(opts = {}) {
|
|
|
15306
15796
|
return { actions, migrated: actions.length > 0 };
|
|
15307
15797
|
}
|
|
15308
15798
|
function migrateProjectDir(root, actions) {
|
|
15309
|
-
const gateDir = (0,
|
|
15310
|
-
const verityDir = (0,
|
|
15311
|
-
if ((0,
|
|
15799
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
15800
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
15801
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
|
|
15312
15802
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
15313
15803
|
}
|
|
15314
|
-
if ((0,
|
|
15804
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
15315
15805
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
15316
15806
|
}
|
|
15317
15807
|
return false;
|
|
@@ -15332,13 +15822,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
15332
15822
|
}
|
|
15333
15823
|
}
|
|
15334
15824
|
if (moved) {
|
|
15335
|
-
if ((0,
|
|
15825
|
+
if ((0, import_node_fs23.existsSync)(gateDir)) {
|
|
15336
15826
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
15337
15827
|
if (carried > 0) {
|
|
15338
15828
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
15339
15829
|
}
|
|
15340
15830
|
try {
|
|
15341
|
-
(0,
|
|
15831
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15342
15832
|
} catch {
|
|
15343
15833
|
}
|
|
15344
15834
|
}
|
|
@@ -15354,18 +15844,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
15354
15844
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
15355
15845
|
}
|
|
15356
15846
|
try {
|
|
15357
|
-
(0,
|
|
15847
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15358
15848
|
} catch {
|
|
15359
15849
|
}
|
|
15360
15850
|
return carried > 0;
|
|
15361
15851
|
}
|
|
15362
15852
|
function migrateGlobalCredentials(home, actions) {
|
|
15363
15853
|
if (!home) return;
|
|
15364
|
-
const gateCreds = (0,
|
|
15365
|
-
const verityCreds = (0,
|
|
15366
|
-
if (!(0,
|
|
15367
|
-
if (!(0,
|
|
15368
|
-
(0,
|
|
15854
|
+
const gateCreds = (0, import_node_path16.join)(home, ".gate", "credentials");
|
|
15855
|
+
const verityCreds = (0, import_node_path16.join)(home, ".verity", "credentials");
|
|
15856
|
+
if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
|
|
15857
|
+
if (!(0, import_node_fs23.existsSync)(verityCreds)) {
|
|
15858
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.join)(home, ".verity"), { recursive: true });
|
|
15369
15859
|
moveFile(gateCreds, verityCreds);
|
|
15370
15860
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
15371
15861
|
return;
|
|
@@ -15387,8 +15877,8 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
15387
15877
|
}
|
|
15388
15878
|
}
|
|
15389
15879
|
async function migrateClaudeMd(root, actions) {
|
|
15390
|
-
const claudeMd = (0,
|
|
15391
|
-
const hadLegacyBlock = (0,
|
|
15880
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
15881
|
+
const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
15392
15882
|
if (!hadLegacyBlock) return;
|
|
15393
15883
|
try {
|
|
15394
15884
|
await ensureClaudeMdPointer(root);
|
|
@@ -15398,9 +15888,9 @@ async function migrateClaudeMd(root, actions) {
|
|
|
15398
15888
|
}
|
|
15399
15889
|
}
|
|
15400
15890
|
function migrateStandardFile(root, actions) {
|
|
15401
|
-
const gateMd = (0,
|
|
15402
|
-
const verityMd = (0,
|
|
15403
|
-
if (!(0,
|
|
15891
|
+
const gateMd = (0, import_node_path16.join)(root, "GATE.md");
|
|
15892
|
+
const verityMd = (0, import_node_path16.join)(root, "VERITY.md");
|
|
15893
|
+
if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
|
|
15404
15894
|
let moved = false;
|
|
15405
15895
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
15406
15896
|
try {
|
|
@@ -15412,7 +15902,7 @@ function migrateStandardFile(root, actions) {
|
|
|
15412
15902
|
if (!moved) moveFile(gateMd, verityMd);
|
|
15413
15903
|
const content = readFileSyncSafe(verityMd);
|
|
15414
15904
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
15415
|
-
if (refreshed !== content) (0,
|
|
15905
|
+
if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
|
|
15416
15906
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
15417
15907
|
}
|
|
15418
15908
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
@@ -15446,14 +15936,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
15446
15936
|
}
|
|
15447
15937
|
if (toAppend.length > 0) {
|
|
15448
15938
|
const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
15449
|
-
(0,
|
|
15939
|
+
(0, import_node_fs23.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
|
|
15450
15940
|
}
|
|
15451
|
-
(0,
|
|
15941
|
+
(0, import_node_fs23.rmSync)(gateCreds, { force: true });
|
|
15452
15942
|
return toAppend.length;
|
|
15453
15943
|
}
|
|
15454
15944
|
function readFileSyncSafe(path) {
|
|
15455
15945
|
try {
|
|
15456
|
-
return (0,
|
|
15946
|
+
return (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
15457
15947
|
} catch {
|
|
15458
15948
|
return "";
|
|
15459
15949
|
}
|
|
@@ -15468,35 +15958,35 @@ function hasStagedChanges(root) {
|
|
|
15468
15958
|
}
|
|
15469
15959
|
function moveDir(from, to) {
|
|
15470
15960
|
try {
|
|
15471
|
-
(0,
|
|
15961
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15472
15962
|
} catch (err) {
|
|
15473
15963
|
if (err.code !== "EXDEV") throw err;
|
|
15474
|
-
(0,
|
|
15475
|
-
(0,
|
|
15964
|
+
(0, import_node_fs23.cpSync)(from, to, { recursive: true });
|
|
15965
|
+
(0, import_node_fs23.rmSync)(from, { recursive: true, force: true });
|
|
15476
15966
|
}
|
|
15477
15967
|
}
|
|
15478
15968
|
function moveFile(from, to) {
|
|
15479
15969
|
try {
|
|
15480
|
-
(0,
|
|
15970
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15481
15971
|
} catch (err) {
|
|
15482
15972
|
if (err.code !== "EXDEV") throw err;
|
|
15483
|
-
(0,
|
|
15484
|
-
(0,
|
|
15973
|
+
(0, import_node_fs23.cpSync)(from, to);
|
|
15974
|
+
(0, import_node_fs23.rmSync)(from, { force: true });
|
|
15485
15975
|
}
|
|
15486
15976
|
}
|
|
15487
15977
|
function carryLegacyContents(gateDir, verityDir) {
|
|
15488
15978
|
let copied = 0;
|
|
15489
15979
|
const walk = (relDir) => {
|
|
15490
|
-
const srcDir = (0,
|
|
15491
|
-
for (const entry of (0,
|
|
15492
|
-
const rel = relDir ? (0,
|
|
15493
|
-
const src = (0,
|
|
15494
|
-
const dest = (0,
|
|
15495
|
-
if ((0,
|
|
15980
|
+
const srcDir = (0, import_node_path16.join)(gateDir, relDir);
|
|
15981
|
+
for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
|
|
15982
|
+
const rel = relDir ? (0, import_node_path16.join)(relDir, entry) : entry;
|
|
15983
|
+
const src = (0, import_node_path16.join)(gateDir, rel);
|
|
15984
|
+
const dest = (0, import_node_path16.join)(verityDir, rel);
|
|
15985
|
+
if ((0, import_node_fs23.statSync)(src).isDirectory()) {
|
|
15496
15986
|
walk(rel);
|
|
15497
|
-
} else if (!(0,
|
|
15498
|
-
(0,
|
|
15499
|
-
(0,
|
|
15987
|
+
} else if (!(0, import_node_fs23.existsSync)(dest)) {
|
|
15988
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
15989
|
+
(0, import_node_fs23.cpSync)(src, dest);
|
|
15500
15990
|
copied++;
|
|
15501
15991
|
}
|
|
15502
15992
|
}
|
|
@@ -15505,22 +15995,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
15505
15995
|
return copied;
|
|
15506
15996
|
}
|
|
15507
15997
|
async function needsMigration(root = repoRoot()) {
|
|
15508
|
-
const gateDir = (0,
|
|
15509
|
-
const verityDir = (0,
|
|
15510
|
-
if ((0,
|
|
15511
|
-
if ((0,
|
|
15512
|
-
if ((0,
|
|
15998
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
15999
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
16000
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
|
|
16001
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
16002
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "credentials"))) {
|
|
15513
16003
|
return true;
|
|
15514
16004
|
}
|
|
15515
|
-
if ((0,
|
|
16005
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "memory"))) {
|
|
15516
16006
|
return true;
|
|
15517
16007
|
}
|
|
15518
16008
|
}
|
|
15519
|
-
const claudeMd = (0,
|
|
15520
|
-
if ((0,
|
|
16009
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
16010
|
+
if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
15521
16011
|
return true;
|
|
15522
16012
|
}
|
|
15523
|
-
if ((0,
|
|
16013
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "VERITY.md"))) {
|
|
15524
16014
|
return true;
|
|
15525
16015
|
}
|
|
15526
16016
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -15548,15 +16038,15 @@ function registerMigrateCommand(program2) {
|
|
|
15548
16038
|
// src/commands/init.ts
|
|
15549
16039
|
function resolveDataDir() {
|
|
15550
16040
|
const candidates = [
|
|
15551
|
-
(0,
|
|
16041
|
+
(0, import_node_path17.join)(__dirname, "..", "data"),
|
|
15552
16042
|
// installed: node_modules/@codacy/verity-cli/data
|
|
15553
|
-
(0,
|
|
16043
|
+
(0, import_node_path17.join)(__dirname, "..", "..", "data"),
|
|
15554
16044
|
// edge case: nested resolution
|
|
15555
|
-
(0,
|
|
16045
|
+
(0, import_node_path17.join)(process.cwd(), "cli", "data")
|
|
15556
16046
|
// local dev: running from repo root
|
|
15557
16047
|
];
|
|
15558
16048
|
for (const candidate of candidates) {
|
|
15559
|
-
if ((0,
|
|
16049
|
+
if ((0, import_node_fs24.existsSync)((0, import_node_path17.join)(candidate, "skills"))) {
|
|
15560
16050
|
return candidate;
|
|
15561
16051
|
}
|
|
15562
16052
|
}
|
|
@@ -15572,7 +16062,7 @@ function registerInitCommand(program2) {
|
|
|
15572
16062
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
15573
16063
|
const force = opts.force ?? false;
|
|
15574
16064
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
15575
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
16065
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs24.existsSync)(m));
|
|
15576
16066
|
if (!isProject) {
|
|
15577
16067
|
printError("No project detected in the current directory.");
|
|
15578
16068
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -15635,21 +16125,21 @@ function registerInitCommand(program2) {
|
|
|
15635
16125
|
console.log("");
|
|
15636
16126
|
printInfo("Installing skills...");
|
|
15637
16127
|
const dataDir = resolveDataDir();
|
|
15638
|
-
const skillsSource = (0,
|
|
16128
|
+
const skillsSource = (0, import_node_path17.join)(dataDir, "skills");
|
|
15639
16129
|
const skillsDest = ".claude/skills";
|
|
15640
16130
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
15641
16131
|
let skillsInstalled = 0;
|
|
15642
16132
|
for (const skill of skills) {
|
|
15643
|
-
const src = (0,
|
|
15644
|
-
const dest = (0,
|
|
15645
|
-
if (!(0,
|
|
16133
|
+
const src = (0, import_node_path17.join)(skillsSource, skill);
|
|
16134
|
+
const dest = (0, import_node_path17.join)(skillsDest, skill);
|
|
16135
|
+
if (!(0, import_node_fs24.existsSync)(src)) {
|
|
15646
16136
|
printWarn(` Skill data not found: ${skill}`);
|
|
15647
16137
|
continue;
|
|
15648
16138
|
}
|
|
15649
|
-
if ((0,
|
|
15650
|
-
const srcSkill = (0,
|
|
15651
|
-
const destSkill = (0,
|
|
15652
|
-
if ((0,
|
|
16139
|
+
if ((0, import_node_fs24.existsSync)(dest) && !force) {
|
|
16140
|
+
const srcSkill = (0, import_node_path17.join)(src, "SKILL.md");
|
|
16141
|
+
const destSkill = (0, import_node_path17.join)(dest, "SKILL.md");
|
|
16142
|
+
if ((0, import_node_fs24.existsSync)(destSkill)) {
|
|
15653
16143
|
try {
|
|
15654
16144
|
const srcContent = await (0, import_promises12.readFile)(srcSkill, "utf-8");
|
|
15655
16145
|
const destContent = await (0, import_promises12.readFile)(destSkill, "utf-8");
|
|
@@ -15673,6 +16163,7 @@ function registerInitCommand(program2) {
|
|
|
15673
16163
|
await writeSettings(hookResult.data);
|
|
15674
16164
|
printInfo(" Stop hook: verity analyze \u2713");
|
|
15675
16165
|
printInfo(" Intent hook: verity intent capture \u2713");
|
|
16166
|
+
printInfo(" Baseline hook: verity baseline capture \u2713");
|
|
15676
16167
|
} else {
|
|
15677
16168
|
printWarn(` ${hookResult.error}`);
|
|
15678
16169
|
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
@@ -15685,7 +16176,7 @@ function registerInitCommand(program2) {
|
|
|
15685
16176
|
} catch (err) {
|
|
15686
16177
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
15687
16178
|
}
|
|
15688
|
-
const globalVerityDir = (0,
|
|
16179
|
+
const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
|
|
15689
16180
|
await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
|
|
15690
16181
|
console.log("");
|
|
15691
16182
|
printInfo("Verity initialized!");
|
|
@@ -15708,8 +16199,8 @@ function registerInitCommand(program2) {
|
|
|
15708
16199
|
}
|
|
15709
16200
|
|
|
15710
16201
|
// src/commands/uninstall.ts
|
|
15711
|
-
var
|
|
15712
|
-
var
|
|
16202
|
+
var import_node_fs25 = require("node:fs");
|
|
16203
|
+
var import_node_path18 = require("node:path");
|
|
15713
16204
|
var SKILL_NAMES = [
|
|
15714
16205
|
"verity-setup",
|
|
15715
16206
|
"verity-analyze",
|
|
@@ -15728,11 +16219,11 @@ function registerUninstallCommand(program2) {
|
|
|
15728
16219
|
const actions = [];
|
|
15729
16220
|
const skillsRoot = projectPath(".claude/skills");
|
|
15730
16221
|
for (const name of SKILL_NAMES) {
|
|
15731
|
-
const dir = (0,
|
|
15732
|
-
if ((0,
|
|
16222
|
+
const dir = (0, import_node_path18.join)(skillsRoot, name);
|
|
16223
|
+
if ((0, import_node_fs25.existsSync)(dir)) {
|
|
15733
16224
|
actions.push({
|
|
15734
16225
|
label: `Remove .claude/skills/${name}/`,
|
|
15735
|
-
apply: () => (0,
|
|
16226
|
+
apply: () => (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true })
|
|
15736
16227
|
});
|
|
15737
16228
|
}
|
|
15738
16229
|
}
|
|
@@ -15746,24 +16237,24 @@ function registerUninstallCommand(program2) {
|
|
|
15746
16237
|
});
|
|
15747
16238
|
}
|
|
15748
16239
|
const verityDir = projectPath(VERITY_DIR);
|
|
15749
|
-
if ((0,
|
|
16240
|
+
if ((0, import_node_fs25.existsSync)(verityDir)) {
|
|
15750
16241
|
actions.push({
|
|
15751
16242
|
label: `Remove ${VERITY_DIR}/`,
|
|
15752
|
-
apply: () => (0,
|
|
16243
|
+
apply: () => (0, import_node_fs25.rmSync)(verityDir, { recursive: true, force: true })
|
|
15753
16244
|
});
|
|
15754
16245
|
}
|
|
15755
16246
|
if (!keepVerityMd) {
|
|
15756
16247
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
15757
|
-
if ((0,
|
|
16248
|
+
if ((0, import_node_fs25.existsSync)(verityMd)) {
|
|
15758
16249
|
actions.push({
|
|
15759
16250
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
15760
|
-
apply: () => (0,
|
|
16251
|
+
apply: () => (0, import_node_fs25.rmSync)(verityMd, { force: true })
|
|
15761
16252
|
});
|
|
15762
16253
|
}
|
|
15763
16254
|
}
|
|
15764
16255
|
const cleanupEmptyDir = (path) => {
|
|
15765
|
-
if ((0,
|
|
15766
|
-
(0,
|
|
16256
|
+
if ((0, import_node_fs25.existsSync)(path) && (0, import_node_fs25.statSync)(path).isDirectory() && (0, import_node_fs25.readdirSync)(path).length === 0) {
|
|
16257
|
+
(0, import_node_fs25.rmdirSync)(path);
|
|
15767
16258
|
}
|
|
15768
16259
|
};
|
|
15769
16260
|
actions.push({
|
|
@@ -15774,11 +16265,11 @@ function registerUninstallCommand(program2) {
|
|
|
15774
16265
|
}
|
|
15775
16266
|
});
|
|
15776
16267
|
const home = process.env.HOME ?? "";
|
|
15777
|
-
const globalVerityDir = (0,
|
|
15778
|
-
if (purgeGlobal && (0,
|
|
16268
|
+
const globalVerityDir = (0, import_node_path18.join)(home, ".verity");
|
|
16269
|
+
if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
|
|
15779
16270
|
actions.push({
|
|
15780
16271
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
15781
|
-
apply: () => (0,
|
|
16272
|
+
apply: () => (0, import_node_fs25.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
15782
16273
|
});
|
|
15783
16274
|
}
|
|
15784
16275
|
if (actions.length === 0) {
|
|
@@ -15972,8 +16463,8 @@ function registerTaskCommands(program2) {
|
|
|
15972
16463
|
}
|
|
15973
16464
|
|
|
15974
16465
|
// src/commands/reset.ts
|
|
15975
|
-
var
|
|
15976
|
-
var
|
|
16466
|
+
var import_node_fs26 = require("node:fs");
|
|
16467
|
+
var import_node_path19 = require("node:path");
|
|
15977
16468
|
function registerResetCommand(program2) {
|
|
15978
16469
|
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) => {
|
|
15979
16470
|
const globals = program2.opts();
|
|
@@ -16010,11 +16501,11 @@ function registerResetCommand(program2) {
|
|
|
16010
16501
|
}
|
|
16011
16502
|
const cacheDir = projectPath(CACHE_DIR);
|
|
16012
16503
|
let purged = 0;
|
|
16013
|
-
if ((0,
|
|
16014
|
-
for (const entry of (0,
|
|
16504
|
+
if ((0, import_node_fs26.existsSync)(cacheDir)) {
|
|
16505
|
+
for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
|
|
16015
16506
|
if (entry.startsWith("pending-")) {
|
|
16016
16507
|
try {
|
|
16017
|
-
(0,
|
|
16508
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(cacheDir, entry));
|
|
16018
16509
|
purged++;
|
|
16019
16510
|
} catch {
|
|
16020
16511
|
}
|
|
@@ -16029,19 +16520,19 @@ function registerResetCommand(program2) {
|
|
|
16029
16520
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
16030
16521
|
];
|
|
16031
16522
|
for (const file of filesToClear) {
|
|
16032
|
-
if ((0,
|
|
16523
|
+
if ((0, import_node_fs26.existsSync)(file)) {
|
|
16033
16524
|
try {
|
|
16034
|
-
(0,
|
|
16525
|
+
(0, import_node_fs26.writeFileSync)(file, "");
|
|
16035
16526
|
} catch {
|
|
16036
16527
|
}
|
|
16037
16528
|
}
|
|
16038
16529
|
}
|
|
16039
16530
|
if (opts.all) {
|
|
16040
16531
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
16041
|
-
if ((0,
|
|
16042
|
-
for (const entry of (0,
|
|
16532
|
+
if ((0, import_node_fs26.existsSync)(logsDir)) {
|
|
16533
|
+
for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
|
|
16043
16534
|
try {
|
|
16044
|
-
(0,
|
|
16535
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(logsDir, entry));
|
|
16045
16536
|
} catch {
|
|
16046
16537
|
}
|
|
16047
16538
|
}
|
|
@@ -16300,7 +16791,7 @@ function registerRunCommand(program2) {
|
|
|
16300
16791
|
|
|
16301
16792
|
// src/lib/telemetry.ts
|
|
16302
16793
|
var import_promises13 = require("node:fs/promises");
|
|
16303
|
-
var
|
|
16794
|
+
var import_node_path20 = require("node:path");
|
|
16304
16795
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
16305
16796
|
var GITIGNORE_FILE = ".gitignore";
|
|
16306
16797
|
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
@@ -16338,7 +16829,7 @@ async function readSettingsLocal() {
|
|
|
16338
16829
|
}
|
|
16339
16830
|
async function writeSettingsLocal(settings) {
|
|
16340
16831
|
const file = projectPath(SETTINGS_LOCAL_FILE2);
|
|
16341
|
-
await (0, import_promises13.mkdir)((0,
|
|
16832
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
|
|
16342
16833
|
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
16343
16834
|
}
|
|
16344
16835
|
async function ensureGitignore() {
|
|
@@ -16434,7 +16925,7 @@ function registerTelemetryCommands(program2) {
|
|
|
16434
16925
|
}
|
|
16435
16926
|
|
|
16436
16927
|
// src/cli.ts
|
|
16437
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
16928
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.e3da48a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
16438
16929
|
registerAuthCommands(program);
|
|
16439
16930
|
registerHooksCommands(program);
|
|
16440
16931
|
registerIntentCommands(program);
|
|
@@ -16443,6 +16934,7 @@ registerConfigCommands(program);
|
|
|
16443
16934
|
registerStatusCommand(program);
|
|
16444
16935
|
registerFeedbackCommand(program);
|
|
16445
16936
|
registerAnalyzeCommand(program);
|
|
16937
|
+
registerBaselineCommands(program);
|
|
16446
16938
|
registerReviewCommand(program);
|
|
16447
16939
|
registerGuardCommand(program);
|
|
16448
16940
|
registerInitCommand(program);
|