@codacy/verity-cli 0.24.0-experimental.b2914b9 → 0.24.0-experimental.bdf0db7
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 +960 -481
- 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;
|
|
@@ -10474,7 +10475,11 @@ var SECURITY_PATTERNS = [
|
|
|
10474
10475
|
/Dockerfile/
|
|
10475
10476
|
];
|
|
10476
10477
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10477
|
-
var DEFAULT_SERVICE_URL = "https://
|
|
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";
|
|
10478
10483
|
|
|
10479
10484
|
// src/lib/auth.ts
|
|
10480
10485
|
async function resolveToken(flagToken) {
|
|
@@ -10640,7 +10645,8 @@ async function apiRequest(options) {
|
|
|
10640
10645
|
timeout = 9e4,
|
|
10641
10646
|
cmd = "unknown",
|
|
10642
10647
|
retry = false,
|
|
10643
|
-
encodeBody = false
|
|
10648
|
+
encodeBody = false,
|
|
10649
|
+
extraHeaders
|
|
10644
10650
|
} = options;
|
|
10645
10651
|
const url = `${serviceUrl}${path}`;
|
|
10646
10652
|
const headers = {
|
|
@@ -10649,6 +10655,9 @@ async function apiRequest(options) {
|
|
|
10649
10655
|
if (token) {
|
|
10650
10656
|
headers["Authorization"] = `Bearer ${token}`;
|
|
10651
10657
|
}
|
|
10658
|
+
if (extraHeaders) {
|
|
10659
|
+
Object.assign(headers, extraHeaders);
|
|
10660
|
+
}
|
|
10652
10661
|
const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
|
|
10653
10662
|
if (testMockScenario) {
|
|
10654
10663
|
headers["X-Verity-Mock-Scenario"] = testMockScenario;
|
|
@@ -10739,6 +10748,324 @@ function analyzeRequest(options) {
|
|
|
10739
10748
|
});
|
|
10740
10749
|
}
|
|
10741
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
|
+
|
|
10742
11069
|
// src/commands/auth.ts
|
|
10743
11070
|
function registerAuthCommands(program2) {
|
|
10744
11071
|
const auth = program2.command("auth").description("Manage project authentication");
|
|
@@ -10748,35 +11075,65 @@ function registerAuthCommands(program2) {
|
|
|
10748
11075
|
let remote = opts.remote;
|
|
10749
11076
|
if (!remote) {
|
|
10750
11077
|
try {
|
|
10751
|
-
remote = (0,
|
|
11078
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10752
11079
|
} catch {
|
|
10753
11080
|
printError("No git remote found. Use --remote to specify one.");
|
|
10754
11081
|
process.exit(1);
|
|
10755
11082
|
}
|
|
10756
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;
|
|
10757
11099
|
const result = await apiRequest({
|
|
10758
11100
|
method: "POST",
|
|
10759
11101
|
path: "/auth/register",
|
|
10760
11102
|
serviceUrl,
|
|
10761
11103
|
body: { project_name: opts.project, git_remote_url: remote },
|
|
11104
|
+
extraHeaders: { "X-Provider-Token": providerToken },
|
|
10762
11105
|
verbose: globals.verbose
|
|
10763
11106
|
});
|
|
10764
11107
|
if (!result.ok) {
|
|
10765
11108
|
printError(result.error);
|
|
10766
11109
|
process.exit(1);
|
|
10767
11110
|
}
|
|
10768
|
-
const { project_id, token, service_url } = result.data;
|
|
11111
|
+
const { project_id, token, service_url, user } = result.data;
|
|
10769
11112
|
await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
|
|
10770
|
-
await (0, import_promises3.writeFile)(
|
|
11113
|
+
await (0, import_promises3.writeFile)(
|
|
11114
|
+
CREDENTIALS_FILE,
|
|
11115
|
+
`token: ${token}
|
|
10771
11116
|
service_url: ${service_url}
|
|
10772
|
-
|
|
11117
|
+
provider_token: ${providerToken}
|
|
11118
|
+
`,
|
|
11119
|
+
{ mode: 384 }
|
|
11120
|
+
);
|
|
11121
|
+
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11122
|
+
});
|
|
10773
11123
|
try {
|
|
10774
|
-
await (0, import_promises3.mkdir)((0,
|
|
10775
|
-
await (0, import_promises3.appendFile)(
|
|
10776
|
-
|
|
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
|
+
});
|
|
10777
11133
|
} catch {
|
|
10778
11134
|
}
|
|
10779
11135
|
printInfo(`Project registered: ${project_id}`);
|
|
11136
|
+
if (user?.email) printInfo(`Authenticated as: ${user.email}`);
|
|
10780
11137
|
printJson({ project_id, service_url });
|
|
10781
11138
|
});
|
|
10782
11139
|
auth.command("verify").description("Verify the current token is valid").action(async () => {
|
|
@@ -10810,7 +11167,7 @@ service_url: ${service_url}
|
|
|
10810
11167
|
let remote = opts.remote;
|
|
10811
11168
|
if (!remote) {
|
|
10812
11169
|
try {
|
|
10813
|
-
remote = (0,
|
|
11170
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10814
11171
|
} catch {
|
|
10815
11172
|
printError("No git remote found. Use --remote to specify one.");
|
|
10816
11173
|
process.exit(1);
|
|
@@ -10838,7 +11195,7 @@ service_url: ${service_url}
|
|
|
10838
11195
|
|
|
10839
11196
|
// src/lib/hooks.ts
|
|
10840
11197
|
var import_promises4 = require("node:fs/promises");
|
|
10841
|
-
var
|
|
11198
|
+
var import_node_path4 = require("node:path");
|
|
10842
11199
|
var VERITY_STOP_HOOK = {
|
|
10843
11200
|
type: "command",
|
|
10844
11201
|
command: "verity analyze",
|
|
@@ -10849,6 +11206,10 @@ var VERITY_INTENT_HOOK = {
|
|
|
10849
11206
|
type: "command",
|
|
10850
11207
|
command: "verity intent capture"
|
|
10851
11208
|
};
|
|
11209
|
+
var VERITY_BASELINE_HOOK = {
|
|
11210
|
+
type: "command",
|
|
11211
|
+
command: "verity baseline capture"
|
|
11212
|
+
};
|
|
10852
11213
|
var GUARD_TIMEOUT = 300;
|
|
10853
11214
|
function buildGuardHook(on) {
|
|
10854
11215
|
return {
|
|
@@ -10861,9 +11222,13 @@ function buildGuardHook(on) {
|
|
|
10861
11222
|
var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
|
|
10862
11223
|
var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
|
|
10863
11224
|
var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
|
|
11225
|
+
var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
|
|
10864
11226
|
function isVerityGuardHook(entry) {
|
|
10865
11227
|
return VERITY_GUARD_RE.test(entry.command ?? "");
|
|
10866
11228
|
}
|
|
11229
|
+
function isVerityBaselineHook(entry) {
|
|
11230
|
+
return VERITY_BASELINE_RE.test(entry.command ?? "");
|
|
11231
|
+
}
|
|
10867
11232
|
var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
|
|
10868
11233
|
var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
|
|
10869
11234
|
function isVerityStopHook(entry) {
|
|
@@ -10875,7 +11240,7 @@ function isVerityIntentHook(entry) {
|
|
|
10875
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");
|
|
10876
11241
|
}
|
|
10877
11242
|
function isVerityHook(entry) {
|
|
10878
|
-
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11243
|
+
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10879
11244
|
}
|
|
10880
11245
|
function isCurrentVerityStopHook(entry) {
|
|
10881
11246
|
const c = entry.command ?? "";
|
|
@@ -10886,7 +11251,7 @@ function isCurrentVerityIntentHook(entry) {
|
|
|
10886
11251
|
return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
|
|
10887
11252
|
}
|
|
10888
11253
|
function isCurrentVerityHook(entry) {
|
|
10889
|
-
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11254
|
+
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10890
11255
|
}
|
|
10891
11256
|
function settingsHasLegacyHook(settings) {
|
|
10892
11257
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
@@ -10928,16 +11293,18 @@ async function readAllSettings() {
|
|
|
10928
11293
|
async function checkAllVerityHooks() {
|
|
10929
11294
|
let stop = false;
|
|
10930
11295
|
let intent = false;
|
|
11296
|
+
let baseline = false;
|
|
10931
11297
|
let guard = false;
|
|
10932
11298
|
let guardOn = [];
|
|
10933
11299
|
for (const settings of await readAllSettings()) {
|
|
10934
11300
|
const r = checkVerityHooks(settings);
|
|
10935
11301
|
stop = stop || r.stop;
|
|
10936
11302
|
intent = intent || r.intent;
|
|
11303
|
+
baseline = baseline || r.baseline;
|
|
10937
11304
|
guard = guard || r.guard;
|
|
10938
11305
|
if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
|
|
10939
11306
|
}
|
|
10940
|
-
return { stop, intent, guard, guardOn };
|
|
11307
|
+
return { stop, intent, baseline, guard, guardOn };
|
|
10941
11308
|
}
|
|
10942
11309
|
async function checkExternalVerityHooks() {
|
|
10943
11310
|
let stop = false;
|
|
@@ -10960,12 +11327,14 @@ async function checkExternalVerityHooks() {
|
|
|
10960
11327
|
async function checkAllVerityHooksDetailed() {
|
|
10961
11328
|
let stop = false;
|
|
10962
11329
|
let intent = false;
|
|
11330
|
+
let baseline = false;
|
|
10963
11331
|
let hasCurrent = false;
|
|
10964
11332
|
let hasLegacy = false;
|
|
10965
11333
|
for (const settings of await readAllSettings()) {
|
|
10966
11334
|
const r = checkVerityHooks(settings);
|
|
10967
11335
|
stop = stop || r.stop;
|
|
10968
11336
|
intent = intent || r.intent;
|
|
11337
|
+
baseline = baseline || r.baseline;
|
|
10969
11338
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
10970
11339
|
for (const g of groups ?? []) {
|
|
10971
11340
|
for (const h of g.hooks ?? []) {
|
|
@@ -10975,22 +11344,22 @@ async function checkAllVerityHooksDetailed() {
|
|
|
10975
11344
|
}
|
|
10976
11345
|
}
|
|
10977
11346
|
}
|
|
10978
|
-
return { stop, intent, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
11347
|
+
return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
10979
11348
|
}
|
|
10980
11349
|
async function writeSettings(settings) {
|
|
10981
|
-
await (0, import_promises4.mkdir)((0,
|
|
11350
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
|
|
10982
11351
|
await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
10983
11352
|
}
|
|
10984
11353
|
async function readSettingsAt(root) {
|
|
10985
11354
|
try {
|
|
10986
|
-
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"));
|
|
10987
11356
|
} catch {
|
|
10988
11357
|
return {};
|
|
10989
11358
|
}
|
|
10990
11359
|
}
|
|
10991
11360
|
async function writeSettingsAt(root, settings) {
|
|
10992
|
-
const file = (0,
|
|
10993
|
-
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 });
|
|
10994
11363
|
await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
10995
11364
|
}
|
|
10996
11365
|
async function hasLegacyHooksAt(root) {
|
|
@@ -11018,6 +11387,10 @@ function checkVerityHooks(settings) {
|
|
|
11018
11387
|
const hasIntent = intentGroups.some(
|
|
11019
11388
|
(g) => g.hooks?.some((h) => isCurrentVerityIntentHook(h))
|
|
11020
11389
|
);
|
|
11390
|
+
const sessionStartGroups = hooks["SessionStart"] ?? [];
|
|
11391
|
+
const hasBaseline = sessionStartGroups.some(
|
|
11392
|
+
(g) => g.hooks?.some((h) => isVerityBaselineHook(h))
|
|
11393
|
+
);
|
|
11021
11394
|
let guard = false;
|
|
11022
11395
|
let guardOn = [];
|
|
11023
11396
|
for (const g of hooks["PreToolUse"] ?? []) {
|
|
@@ -11030,16 +11403,17 @@ function checkVerityHooks(settings) {
|
|
|
11030
11403
|
}
|
|
11031
11404
|
}
|
|
11032
11405
|
}
|
|
11033
|
-
return { stop: hasStop, intent: hasIntent, guard, guardOn };
|
|
11406
|
+
return { stop: hasStop, intent: hasIntent, baseline: hasBaseline, guard, guardOn };
|
|
11034
11407
|
}
|
|
11035
|
-
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false }) {
|
|
11408
|
+
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false, baseline: false }) {
|
|
11036
11409
|
const local = checkVerityHooks(settings);
|
|
11037
11410
|
const existing = {
|
|
11038
11411
|
stop: local.stop || externalPresent.stop,
|
|
11039
|
-
intent: local.intent || externalPresent.intent
|
|
11412
|
+
intent: local.intent || externalPresent.intent,
|
|
11413
|
+
baseline: local.baseline || (externalPresent.baseline ?? false)
|
|
11040
11414
|
};
|
|
11041
11415
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11042
|
-
if (existing.stop && existing.intent && !force && !hasLocalLegacy) {
|
|
11416
|
+
if (existing.stop && existing.intent && existing.baseline && !force && !hasLocalLegacy) {
|
|
11043
11417
|
return { ok: false, error: "Verity hooks already installed. Use --force to overwrite." };
|
|
11044
11418
|
}
|
|
11045
11419
|
const newSettings = { ...settings };
|
|
@@ -11047,7 +11421,7 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11047
11421
|
newSettings.hooks = {};
|
|
11048
11422
|
}
|
|
11049
11423
|
const shouldStrip = force ? isVerityHook : isLegacyHook;
|
|
11050
|
-
for (const key of ["Stop", "UserPromptSubmit"]) {
|
|
11424
|
+
for (const key of ["Stop", "UserPromptSubmit", "SessionStart"]) {
|
|
11051
11425
|
const groups = newSettings.hooks[key];
|
|
11052
11426
|
if (groups) {
|
|
11053
11427
|
newSettings.hooks[key] = groups.map((g) => ({
|
|
@@ -11074,6 +11448,15 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11074
11448
|
if (!newSettings.hooks["UserPromptSubmit"]) newSettings.hooks["UserPromptSubmit"] = [];
|
|
11075
11449
|
newSettings.hooks["UserPromptSubmit"].push({ hooks: [VERITY_INTENT_HOOK] });
|
|
11076
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
|
+
}
|
|
11077
11460
|
return { ok: true, data: newSettings };
|
|
11078
11461
|
}
|
|
11079
11462
|
function removeVerityHooks(settings) {
|
|
@@ -11107,6 +11490,7 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
|
|
|
11107
11490
|
(s.hooks[event] ??= []).push(group);
|
|
11108
11491
|
};
|
|
11109
11492
|
if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
|
|
11493
|
+
push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
|
|
11110
11494
|
if (moments.includes("stop") && !externalPresent.stop) {
|
|
11111
11495
|
push("Stop", { hooks: [VERITY_STOP_HOOK] });
|
|
11112
11496
|
}
|
|
@@ -11155,7 +11539,7 @@ function registerHooksCommands(program2) {
|
|
|
11155
11539
|
return;
|
|
11156
11540
|
}
|
|
11157
11541
|
const detail = await checkAllVerityHooksDetailed();
|
|
11158
|
-
const present = { stop: detail.stop, intent: detail.intent };
|
|
11542
|
+
const present = { stop: detail.stop, intent: detail.intent, baseline: detail.baseline };
|
|
11159
11543
|
if (detail.legacyOnly) {
|
|
11160
11544
|
printInfo("Found a legacy GATE.md hook \u2014 upgrading it to Verity...");
|
|
11161
11545
|
const settings2 = removeVerityHooks(await readSettings());
|
|
@@ -11168,11 +11552,12 @@ function registerHooksCommands(program2) {
|
|
|
11168
11552
|
printInfo("Verity hooks installed in .claude/settings.json (legacy hook removed)");
|
|
11169
11553
|
printInfo(" Stop hook: verity analyze");
|
|
11170
11554
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11555
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11171
11556
|
return;
|
|
11172
11557
|
}
|
|
11173
11558
|
const settings = await readSettings();
|
|
11174
11559
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11175
|
-
if (!force && present.stop && present.intent && !hasLocalLegacy) {
|
|
11560
|
+
if (!force && present.stop && present.intent && present.baseline && !hasLocalLegacy) {
|
|
11176
11561
|
printInfo("Verity hooks already installed (found in .claude/settings.json, settings.local.json, or your global settings).");
|
|
11177
11562
|
printInfo("Use --force to rewrite the project settings.json copy.");
|
|
11178
11563
|
return;
|
|
@@ -11186,11 +11571,13 @@ function registerHooksCommands(program2) {
|
|
|
11186
11571
|
printInfo("Verity hooks installed in .claude/settings.json");
|
|
11187
11572
|
printInfo(" Stop hook: verity analyze");
|
|
11188
11573
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11574
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11189
11575
|
});
|
|
11190
11576
|
hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
|
|
11191
11577
|
const status = await checkAllVerityHooks();
|
|
11192
11578
|
printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
|
|
11193
11579
|
printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
|
|
11580
|
+
printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
|
|
11194
11581
|
const gates = status.guard ? status.guardOn.join(", ") : "none";
|
|
11195
11582
|
printInfo(`Git-moment gate (verity guard): ${status.guard ? `installed [${gates}]` : "not installed"}`);
|
|
11196
11583
|
if (!status.stop && !status.guard) {
|
|
@@ -11213,8 +11600,8 @@ var import_node_crypto2 = require("node:crypto");
|
|
|
11213
11600
|
|
|
11214
11601
|
// src/lib/conversation-buffer.ts
|
|
11215
11602
|
var import_promises5 = require("node:fs/promises");
|
|
11216
|
-
var
|
|
11217
|
-
var
|
|
11603
|
+
var import_node_fs3 = require("node:fs");
|
|
11604
|
+
var import_node_child_process5 = require("node:child_process");
|
|
11218
11605
|
function stripImageReferences(text) {
|
|
11219
11606
|
return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
|
|
11220
11607
|
}
|
|
@@ -11243,20 +11630,33 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11243
11630
|
} catch {
|
|
11244
11631
|
}
|
|
11245
11632
|
}
|
|
11246
|
-
async function readAndClearConversationBuffer() {
|
|
11633
|
+
async function readAndClearConversationBuffer(currentSessionId) {
|
|
11247
11634
|
try {
|
|
11248
|
-
if ((0,
|
|
11635
|
+
if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
11249
11636
|
const entries = await readBufferEntries();
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
if (
|
|
11637
|
+
let mine = entries;
|
|
11638
|
+
let others = [];
|
|
11639
|
+
if (currentSessionId) {
|
|
11640
|
+
mine = entries.filter((e) => e.session_id === currentSessionId || !e.session_id);
|
|
11641
|
+
others = entries.filter((e) => e.session_id && e.session_id !== currentSessionId);
|
|
11642
|
+
}
|
|
11643
|
+
if (others.length > 0) {
|
|
11644
|
+
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11645
|
+
const tmpFile = `${CONVERSATION_BUFFER_FILE}.tmp`;
|
|
11646
|
+
await (0, import_promises5.writeFile)(tmpFile, remaining);
|
|
11647
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11648
|
+
} else {
|
|
11649
|
+
await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11650
|
+
});
|
|
11651
|
+
}
|
|
11652
|
+
if (mine.length > 0) {
|
|
11253
11653
|
return {
|
|
11254
|
-
prompts:
|
|
11654
|
+
prompts: mine,
|
|
11255
11655
|
recent_commits: getRecentCommitMessages()
|
|
11256
11656
|
};
|
|
11257
11657
|
}
|
|
11258
11658
|
}
|
|
11259
|
-
if ((0,
|
|
11659
|
+
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11260
11660
|
try {
|
|
11261
11661
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
11262
11662
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -11300,7 +11700,7 @@ async function readBufferEntries() {
|
|
|
11300
11700
|
}
|
|
11301
11701
|
function getRecentCommitMessages() {
|
|
11302
11702
|
try {
|
|
11303
|
-
const output = (0,
|
|
11703
|
+
const output = (0, import_node_child_process5.execSync)(
|
|
11304
11704
|
'git log --since="30 minutes ago" --format="%s" -5',
|
|
11305
11705
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
11306
11706
|
).trim();
|
|
@@ -11313,8 +11713,8 @@ function getRecentCommitMessages() {
|
|
|
11313
11713
|
|
|
11314
11714
|
// src/lib/task-context-buffer.ts
|
|
11315
11715
|
var import_promises6 = require("node:fs/promises");
|
|
11316
|
-
var
|
|
11317
|
-
var
|
|
11716
|
+
var import_node_fs4 = require("node:fs");
|
|
11717
|
+
var import_node_path5 = require("node:path");
|
|
11318
11718
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
11319
11719
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
11320
11720
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -11353,7 +11753,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
11353
11753
|
}
|
|
11354
11754
|
async function readTaskContextBuffer(taskId) {
|
|
11355
11755
|
const filePath = bufferPath(taskId);
|
|
11356
|
-
if (!(0,
|
|
11756
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
11357
11757
|
try {
|
|
11358
11758
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
11359
11759
|
if (!content.trim()) return null;
|
|
@@ -11387,12 +11787,12 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11387
11787
|
}
|
|
11388
11788
|
async function cleanupTaskContextBuffers() {
|
|
11389
11789
|
try {
|
|
11390
|
-
if (!(0,
|
|
11790
|
+
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
11391
11791
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
11392
11792
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
11393
11793
|
for (const file of files) {
|
|
11394
11794
|
if (!file.endsWith(".jsonl")) continue;
|
|
11395
|
-
const filePath = (0,
|
|
11795
|
+
const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
|
|
11396
11796
|
try {
|
|
11397
11797
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11398
11798
|
if (stats.mtimeMs < cutoffMs) {
|
|
@@ -11406,13 +11806,13 @@ async function cleanupTaskContextBuffers() {
|
|
|
11406
11806
|
}
|
|
11407
11807
|
function bufferPath(taskId) {
|
|
11408
11808
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
11409
|
-
return (0,
|
|
11809
|
+
return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
11410
11810
|
}
|
|
11411
11811
|
async function appendEntry(taskId, entry) {
|
|
11412
11812
|
try {
|
|
11413
11813
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
11414
11814
|
const filePath = bufferPath(taskId);
|
|
11415
|
-
if ((0,
|
|
11815
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
11416
11816
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11417
11817
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
11418
11818
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -11423,7 +11823,7 @@ async function appendEntry(taskId, entry) {
|
|
|
11423
11823
|
}
|
|
11424
11824
|
}
|
|
11425
11825
|
const line = JSON.stringify(entry) + "\n";
|
|
11426
|
-
const existing = (0,
|
|
11826
|
+
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
11427
11827
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
11428
11828
|
} catch {
|
|
11429
11829
|
}
|
|
@@ -11431,8 +11831,8 @@ async function appendEntry(taskId, entry) {
|
|
|
11431
11831
|
|
|
11432
11832
|
// src/lib/memory-retrieval.ts
|
|
11433
11833
|
var import_promises7 = require("node:fs/promises");
|
|
11434
|
-
var
|
|
11435
|
-
var
|
|
11834
|
+
var import_node_fs5 = require("node:fs");
|
|
11835
|
+
var import_node_path6 = require("node:path");
|
|
11436
11836
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
11437
11837
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
11438
11838
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
@@ -11525,19 +11925,19 @@ function parseFrontmatter(content) {
|
|
|
11525
11925
|
return { fm, body: match[2].trim() };
|
|
11526
11926
|
}
|
|
11527
11927
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
11528
|
-
if (!(0,
|
|
11928
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
|
|
11529
11929
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
11530
11930
|
const promptTokens = tokenize(promptText);
|
|
11531
11931
|
const nodes = [];
|
|
11532
11932
|
for (const domain of DOMAINS) {
|
|
11533
|
-
const domainDir = (0,
|
|
11534
|
-
if (!(0,
|
|
11933
|
+
const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
|
|
11934
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
11535
11935
|
try {
|
|
11536
11936
|
const files = await (0, import_promises7.readdir)(domainDir);
|
|
11537
11937
|
for (const file of files) {
|
|
11538
11938
|
if (!file.endsWith(".md")) continue;
|
|
11539
11939
|
try {
|
|
11540
|
-
const content = await (0, import_promises7.readFile)((0,
|
|
11940
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
|
|
11541
11941
|
const { fm, body } = parseFrontmatter(content);
|
|
11542
11942
|
if (fm.status && fm.status !== "active") continue;
|
|
11543
11943
|
nodes.push({
|
|
@@ -11596,8 +11996,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
11596
11996
|
|
|
11597
11997
|
// src/lib/memory-sync.ts
|
|
11598
11998
|
var import_promises8 = require("node:fs/promises");
|
|
11599
|
-
var
|
|
11600
|
-
var
|
|
11999
|
+
var import_node_fs6 = require("node:fs");
|
|
12000
|
+
var import_node_path7 = require("node:path");
|
|
11601
12001
|
var import_node_crypto = require("node:crypto");
|
|
11602
12002
|
|
|
11603
12003
|
// src/lib/glob-match.ts
|
|
@@ -11668,32 +12068,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
|
11668
12068
|
async function ensureMemoryDir() {
|
|
11669
12069
|
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
11670
12070
|
for (const domain of DOMAINS2) {
|
|
11671
|
-
await (0, import_promises8.mkdir)((0,
|
|
12071
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
|
|
11672
12072
|
}
|
|
11673
|
-
if (!(0,
|
|
11674
|
-
await (0, import_promises8.writeFile)((0,
|
|
12073
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12074
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
11675
12075
|
}
|
|
11676
|
-
if (!(0,
|
|
11677
|
-
await (0, import_promises8.writeFile)((0,
|
|
12076
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
|
|
12077
|
+
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");
|
|
11678
12078
|
}
|
|
11679
|
-
if (!(0,
|
|
11680
|
-
await (0, import_promises8.writeFile)((0,
|
|
12079
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
|
|
12080
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
11681
12081
|
}
|
|
11682
12082
|
}
|
|
11683
12083
|
async function buildManifest() {
|
|
11684
|
-
if (!(0,
|
|
12084
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
|
|
11685
12085
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
11686
12086
|
}
|
|
11687
12087
|
const nodes = [];
|
|
11688
12088
|
for (const domain of DOMAINS2) {
|
|
11689
|
-
const domainDir = (0,
|
|
11690
|
-
if (!(0,
|
|
12089
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12090
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11691
12091
|
try {
|
|
11692
12092
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11693
12093
|
for (const file of files) {
|
|
11694
12094
|
if (!file.endsWith(".md")) continue;
|
|
11695
12095
|
const filePath = `${domain}/${file}`;
|
|
11696
|
-
const fullPath = (0,
|
|
12096
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
|
|
11697
12097
|
try {
|
|
11698
12098
|
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
11699
12099
|
const hash = (0, import_node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
@@ -11706,13 +12106,13 @@ async function buildManifest() {
|
|
|
11706
12106
|
}
|
|
11707
12107
|
let indexHash = null;
|
|
11708
12108
|
try {
|
|
11709
|
-
const indexContent = await (0, import_promises8.readFile)((0,
|
|
12109
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
|
|
11710
12110
|
indexHash = `sha256:${(0, import_node_crypto.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
11711
12111
|
} catch {
|
|
11712
12112
|
}
|
|
11713
12113
|
let logLength = 0;
|
|
11714
12114
|
try {
|
|
11715
|
-
const logContent = await (0, import_promises8.readFile)((0,
|
|
12115
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
|
|
11716
12116
|
logLength = logContent.split("\n").length;
|
|
11717
12117
|
} catch {
|
|
11718
12118
|
}
|
|
@@ -11723,15 +12123,15 @@ function hashContent(content) {
|
|
|
11723
12123
|
}
|
|
11724
12124
|
async function readOnDiskNodes() {
|
|
11725
12125
|
const out = /* @__PURE__ */ new Map();
|
|
11726
|
-
if (!(0,
|
|
12126
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
|
|
11727
12127
|
for (const domain of DOMAINS2) {
|
|
11728
|
-
const domainDir = (0,
|
|
11729
|
-
if (!(0,
|
|
12128
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12129
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11730
12130
|
try {
|
|
11731
12131
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
11732
12132
|
if (!file.endsWith(".md")) continue;
|
|
11733
12133
|
try {
|
|
11734
|
-
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0,
|
|
12134
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
|
|
11735
12135
|
} catch {
|
|
11736
12136
|
}
|
|
11737
12137
|
}
|
|
@@ -11777,8 +12177,8 @@ async function computeEditedNodeUploads() {
|
|
|
11777
12177
|
const uploads = [];
|
|
11778
12178
|
for (const [path, prevHash] of prev) {
|
|
11779
12179
|
if (prevHash == null) continue;
|
|
11780
|
-
const full = (0,
|
|
11781
|
-
if (!(0,
|
|
12180
|
+
const full = (0, import_node_path7.join)(memoryDir2(), path);
|
|
12181
|
+
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
11782
12182
|
let content;
|
|
11783
12183
|
try {
|
|
11784
12184
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -11814,15 +12214,15 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
11814
12214
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
11815
12215
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
11816
12216
|
try {
|
|
11817
|
-
const existing = (0,
|
|
11818
|
-
await (0, import_promises8.writeFile)((0,
|
|
12217
|
+
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";
|
|
12218
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
11819
12219
|
} catch {
|
|
11820
12220
|
}
|
|
11821
12221
|
await recordSyncedNodePaths();
|
|
11822
12222
|
return count;
|
|
11823
12223
|
}
|
|
11824
12224
|
async function applyOneWrite(write, treePaths) {
|
|
11825
|
-
const fullPath = (0,
|
|
12225
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
|
|
11826
12226
|
const notes = [];
|
|
11827
12227
|
let content = write.content;
|
|
11828
12228
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -11832,7 +12232,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11832
12232
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
11833
12233
|
}
|
|
11834
12234
|
}
|
|
11835
|
-
if ((0,
|
|
12235
|
+
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
11836
12236
|
let existing = "";
|
|
11837
12237
|
try {
|
|
11838
12238
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -11844,7 +12244,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11844
12244
|
return { written: false, notes };
|
|
11845
12245
|
}
|
|
11846
12246
|
}
|
|
11847
|
-
await (0, import_promises8.mkdir)((0,
|
|
12247
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
|
|
11848
12248
|
await (0, import_promises8.writeFile)(fullPath, content);
|
|
11849
12249
|
return { written: true, notes };
|
|
11850
12250
|
}
|
|
@@ -11885,8 +12285,8 @@ async function regenerateIndex() {
|
|
|
11885
12285
|
];
|
|
11886
12286
|
let totalNodes = 0;
|
|
11887
12287
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
11888
|
-
const domainDir = (0,
|
|
11889
|
-
if (!(0,
|
|
12288
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12289
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11890
12290
|
try {
|
|
11891
12291
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11892
12292
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -11896,7 +12296,7 @@ async function regenerateIndex() {
|
|
|
11896
12296
|
for (const file of mdFiles.sort()) {
|
|
11897
12297
|
const slug = file.replace(/\.md$/, "");
|
|
11898
12298
|
try {
|
|
11899
|
-
const content = await (0, import_promises8.readFile)((0,
|
|
12299
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
|
|
11900
12300
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
11901
12301
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
11902
12302
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -11920,7 +12320,7 @@ async function regenerateIndex() {
|
|
|
11920
12320
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
11921
12321
|
}
|
|
11922
12322
|
const next = lines.join("\n") + "\n";
|
|
11923
|
-
const indexPath = (0,
|
|
12323
|
+
const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
|
|
11924
12324
|
let existing = null;
|
|
11925
12325
|
try {
|
|
11926
12326
|
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
@@ -12002,9 +12402,9 @@ function hasLegacyMemoryBlock(text) {
|
|
|
12002
12402
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
12003
12403
|
}
|
|
12004
12404
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
12005
|
-
const claudeMdPath = (0,
|
|
12405
|
+
const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
|
|
12006
12406
|
let existing = "";
|
|
12007
|
-
if ((0,
|
|
12407
|
+
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12008
12408
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12009
12409
|
}
|
|
12010
12410
|
let startTag = CLAUDE_MD_START;
|
|
@@ -12134,7 +12534,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
12134
12534
|
`;
|
|
12135
12535
|
|
|
12136
12536
|
// src/commands/intent.ts
|
|
12137
|
-
var
|
|
12537
|
+
var import_node_fs7 = require("node:fs");
|
|
12138
12538
|
function registerIntentCommands(program2) {
|
|
12139
12539
|
const intent = program2.command("intent").description("Manage intent capture");
|
|
12140
12540
|
intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
|
|
@@ -12143,7 +12543,7 @@ function registerIntentCommands(program2) {
|
|
|
12143
12543
|
process.chdir(repoRoot());
|
|
12144
12544
|
} catch {
|
|
12145
12545
|
}
|
|
12146
|
-
if (!(0,
|
|
12546
|
+
if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
|
|
12147
12547
|
process.exit(0);
|
|
12148
12548
|
}
|
|
12149
12549
|
const chunks = [];
|
|
@@ -12655,262 +13055,73 @@ function registerFeedbackCommand(program2) {
|
|
|
12655
13055
|
printError(urlResult.error);
|
|
12656
13056
|
process.exit(1);
|
|
12657
13057
|
}
|
|
12658
|
-
const body = {
|
|
12659
|
-
run_id: runId,
|
|
12660
|
-
pattern_id: patternId,
|
|
12661
|
-
action
|
|
12662
|
-
};
|
|
12663
|
-
if (note) body.note = note;
|
|
12664
|
-
if (opts.file) body.file_path = opts.file;
|
|
12665
|
-
if (opts.line != null) body.line = opts.line;
|
|
12666
|
-
const result = await apiRequest({
|
|
12667
|
-
method: "POST",
|
|
12668
|
-
path: "/feedback/findings",
|
|
12669
|
-
serviceUrl: urlResult.data,
|
|
12670
|
-
token: tokenResult.data.token,
|
|
12671
|
-
body,
|
|
12672
|
-
verbose: globals.verbose
|
|
12673
|
-
});
|
|
12674
|
-
if (!result.ok) {
|
|
12675
|
-
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
12676
|
-
process.exit(1);
|
|
12677
|
-
}
|
|
12678
|
-
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
12679
|
-
printInfo(status);
|
|
12680
|
-
});
|
|
12681
|
-
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
12682
|
-
if (!message) return;
|
|
12683
|
-
const globals = program2.opts();
|
|
12684
|
-
await sendGeneralFeedback(message, opts, globals);
|
|
12685
|
-
});
|
|
12686
|
-
}
|
|
12687
|
-
async function sendGeneralFeedback(message, opts, globals) {
|
|
12688
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12689
|
-
if (!tokenResult.ok) {
|
|
12690
|
-
printError(tokenResult.error);
|
|
12691
|
-
printInfo(`Your message: ${message}`);
|
|
12692
|
-
process.exit(1);
|
|
12693
|
-
}
|
|
12694
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12695
|
-
if (!urlResult.ok) {
|
|
12696
|
-
printError(urlResult.error);
|
|
12697
|
-
printInfo(`Your message: ${message}`);
|
|
12698
|
-
process.exit(1);
|
|
12699
|
-
}
|
|
12700
|
-
const body = { message };
|
|
12701
|
-
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
12702
|
-
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
12703
|
-
if (sessionId) body.session_id = sessionId;
|
|
12704
|
-
if (model) body.agent_model = model;
|
|
12705
|
-
const result = await apiRequest({
|
|
12706
|
-
method: "POST",
|
|
12707
|
-
path: "/feedback",
|
|
12708
|
-
serviceUrl: urlResult.data,
|
|
12709
|
-
token: tokenResult.data.token,
|
|
12710
|
-
body,
|
|
12711
|
-
verbose: globals.verbose
|
|
12712
|
-
});
|
|
12713
|
-
if (!result.ok) {
|
|
12714
|
-
printError(`Couldn't send feedback: ${result.error}`);
|
|
12715
|
-
printInfo(`Your message: ${message}`);
|
|
12716
|
-
process.exit(1);
|
|
12717
|
-
}
|
|
12718
|
-
printInfo("Thanks, feedback sent!");
|
|
12719
|
-
}
|
|
12720
|
-
|
|
12721
|
-
// src/commands/analyze.ts
|
|
12722
|
-
var import_node_fs18 = require("node:fs");
|
|
12723
|
-
var import_node_path13 = require("node:path");
|
|
12724
|
-
|
|
12725
|
-
// src/lib/git.ts
|
|
12726
|
-
var import_node_child_process5 = require("node:child_process");
|
|
12727
|
-
var import_node_fs7 = require("node:fs");
|
|
12728
|
-
var import_node_path7 = require("node:path");
|
|
12729
|
-
function resolveFile(relpath) {
|
|
12730
|
-
if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
|
|
12731
|
-
if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
|
|
12732
|
-
try {
|
|
12733
|
-
const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
12734
|
-
for (const entry of entries) {
|
|
12735
|
-
if (!entry.isDirectory()) continue;
|
|
12736
|
-
const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
|
|
12737
|
-
if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
|
|
12738
|
-
}
|
|
12739
|
-
} catch {
|
|
12740
|
-
}
|
|
12741
|
-
}
|
|
12742
|
-
return null;
|
|
12743
|
-
}
|
|
12744
|
-
function execGit(cmd) {
|
|
12745
|
-
try {
|
|
12746
|
-
return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12747
|
-
} catch {
|
|
12748
|
-
return "";
|
|
12749
|
-
}
|
|
12750
|
-
}
|
|
12751
|
-
function splitLines(s) {
|
|
12752
|
-
return s.split("\n").filter((l) => l.length > 0);
|
|
12753
|
-
}
|
|
12754
|
-
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
12755
|
-
function readBaselineSha() {
|
|
12756
|
-
if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
12757
|
-
let sha;
|
|
12758
|
-
try {
|
|
12759
|
-
sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
12760
|
-
} catch {
|
|
12761
|
-
return null;
|
|
12762
|
-
}
|
|
12763
|
-
if (!SHA_RE.test(sha)) return null;
|
|
12764
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
12765
|
-
if (!reachable) {
|
|
12766
|
-
try {
|
|
12767
|
-
(0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
|
|
12768
|
-
} catch {
|
|
12769
|
-
}
|
|
12770
|
-
return null;
|
|
12771
|
-
}
|
|
12772
|
-
return sha;
|
|
12773
|
-
}
|
|
12774
|
-
function writeBaselineSha(sha) {
|
|
12775
|
-
if (!SHA_RE.test(sha)) return;
|
|
12776
|
-
try {
|
|
12777
|
-
(0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
12778
|
-
(0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
12779
|
-
} catch {
|
|
12780
|
-
}
|
|
12781
|
-
}
|
|
12782
|
-
function getChangedFiles() {
|
|
12783
|
-
const sets = /* @__PURE__ */ new Set();
|
|
12784
|
-
let hasRecentCommitFiles = false;
|
|
12785
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
12786
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
12787
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
12788
|
-
const baseline = readBaselineSha();
|
|
12789
|
-
if (baseline) {
|
|
12790
|
-
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
12791
|
-
if (committed.length > 0) {
|
|
12792
|
-
hasRecentCommitFiles = true;
|
|
12793
|
-
for (const f of committed) sets.add(f);
|
|
12794
|
-
}
|
|
12795
|
-
} else {
|
|
12796
|
-
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
12797
|
-
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
12798
|
-
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
12799
|
-
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
12800
|
-
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
12801
|
-
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
12802
|
-
if (recentFiles.length > 0) {
|
|
12803
|
-
hasRecentCommitFiles = true;
|
|
12804
|
-
for (const f of recentFiles) sets.add(f);
|
|
12805
|
-
}
|
|
12806
|
-
}
|
|
12807
|
-
}
|
|
12808
|
-
for (const f of getWorktreeFiles()) sets.add(f);
|
|
12809
|
-
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12810
|
-
return { files: filtered, hasRecentCommitFiles };
|
|
12811
|
-
}
|
|
12812
|
-
function getStagedFiles() {
|
|
12813
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12814
|
-
}
|
|
12815
|
-
function getPushRangeFiles() {
|
|
12816
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12817
|
-
const resolvers = [
|
|
12818
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
12819
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
12820
|
-
() => {
|
|
12821
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
12822
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
12823
|
-
}
|
|
12824
|
-
];
|
|
12825
|
-
for (const resolve of resolvers) {
|
|
12826
|
-
const range = resolve();
|
|
12827
|
-
if (range) return { files: diff(range), range };
|
|
12828
|
-
}
|
|
12829
|
-
const baseline = readBaselineSha();
|
|
12830
|
-
if (baseline) {
|
|
12831
|
-
const files = diff(`${baseline}..HEAD`);
|
|
12832
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
12833
|
-
}
|
|
12834
|
-
const last = diff("HEAD~1..HEAD");
|
|
12835
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
12836
|
-
}
|
|
12837
|
-
function getPushRangeMessages() {
|
|
12838
|
-
const { range } = getPushRangeFiles();
|
|
12839
|
-
if (!range) return "";
|
|
12840
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
12841
|
-
}
|
|
12842
|
-
function getWorktreeFiles() {
|
|
12843
|
-
const result = [];
|
|
12844
|
-
const worktreeDir = ".claude/worktrees";
|
|
12845
|
-
if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
|
|
12846
|
-
try {
|
|
12847
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
12848
|
-
const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
12849
|
-
for (const entry of entries) {
|
|
12850
|
-
if (!entry.isDirectory()) continue;
|
|
12851
|
-
const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
|
|
12852
|
-
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
12853
|
-
}
|
|
12854
|
-
} catch {
|
|
12855
|
-
}
|
|
12856
|
-
return result;
|
|
12857
|
-
}
|
|
12858
|
-
function scanDir(baseDir, dir, minMtime, result) {
|
|
12859
|
-
try {
|
|
12860
|
-
const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
|
|
12861
|
-
for (const entry of entries) {
|
|
12862
|
-
const fullPath = (0, import_node_path7.join)(dir, entry.name);
|
|
12863
|
-
if (entry.isDirectory()) {
|
|
12864
|
-
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12865
|
-
scanDir(baseDir, fullPath, minMtime, result);
|
|
12866
|
-
} else if (entry.isFile()) {
|
|
12867
|
-
const ext = (0, import_node_path7.extname)(entry.name).slice(1);
|
|
12868
|
-
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
12869
|
-
try {
|
|
12870
|
-
const stat3 = (0, import_node_fs7.statSync)(fullPath);
|
|
12871
|
-
if (stat3.mtimeMs >= minMtime) {
|
|
12872
|
-
const relPath = fullPath.slice(baseDir.length + 1);
|
|
12873
|
-
result.push(relPath);
|
|
12874
|
-
}
|
|
12875
|
-
} catch {
|
|
12876
|
-
}
|
|
12877
|
-
}
|
|
13058
|
+
const body = {
|
|
13059
|
+
run_id: runId,
|
|
13060
|
+
pattern_id: patternId,
|
|
13061
|
+
action
|
|
13062
|
+
};
|
|
13063
|
+
if (note) body.note = note;
|
|
13064
|
+
if (opts.file) body.file_path = opts.file;
|
|
13065
|
+
if (opts.line != null) body.line = opts.line;
|
|
13066
|
+
const result = await apiRequest({
|
|
13067
|
+
method: "POST",
|
|
13068
|
+
path: "/feedback/findings",
|
|
13069
|
+
serviceUrl: urlResult.data,
|
|
13070
|
+
token: tokenResult.data.token,
|
|
13071
|
+
body,
|
|
13072
|
+
verbose: globals.verbose
|
|
13073
|
+
});
|
|
13074
|
+
if (!result.ok) {
|
|
13075
|
+
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
13076
|
+
process.exit(1);
|
|
12878
13077
|
}
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
}
|
|
12882
|
-
function filterAnalyzable(files) {
|
|
12883
|
-
return files.filter((f) => {
|
|
12884
|
-
const ext = (0, import_node_path7.extname)(f).slice(1);
|
|
12885
|
-
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
13078
|
+
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
13079
|
+
printInfo(status);
|
|
12886
13080
|
});
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
12892
|
-
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
12893
|
-
const basename2 = f.split("/").pop() ?? "";
|
|
12894
|
-
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
12895
|
-
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
12896
|
-
return false;
|
|
13081
|
+
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
13082
|
+
if (!message) return;
|
|
13083
|
+
const globals = program2.opts();
|
|
13084
|
+
await sendGeneralFeedback(message, opts, globals);
|
|
12897
13085
|
});
|
|
12898
13086
|
}
|
|
12899
|
-
function
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
}
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
13087
|
+
async function sendGeneralFeedback(message, opts, globals) {
|
|
13088
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13089
|
+
if (!tokenResult.ok) {
|
|
13090
|
+
printError(tokenResult.error);
|
|
13091
|
+
printInfo(`Your message: ${message}`);
|
|
13092
|
+
process.exit(1);
|
|
13093
|
+
}
|
|
13094
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13095
|
+
if (!urlResult.ok) {
|
|
13096
|
+
printError(urlResult.error);
|
|
13097
|
+
printInfo(`Your message: ${message}`);
|
|
13098
|
+
process.exit(1);
|
|
13099
|
+
}
|
|
13100
|
+
const body = { message };
|
|
13101
|
+
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
13102
|
+
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
13103
|
+
if (sessionId) body.session_id = sessionId;
|
|
13104
|
+
if (model) body.agent_model = model;
|
|
13105
|
+
const result = await apiRequest({
|
|
13106
|
+
method: "POST",
|
|
13107
|
+
path: "/feedback",
|
|
13108
|
+
serviceUrl: urlResult.data,
|
|
13109
|
+
token: tokenResult.data.token,
|
|
13110
|
+
body,
|
|
13111
|
+
verbose: globals.verbose
|
|
13112
|
+
});
|
|
13113
|
+
if (!result.ok) {
|
|
13114
|
+
printError(`Couldn't send feedback: ${result.error}`);
|
|
13115
|
+
printInfo(`Your message: ${message}`);
|
|
13116
|
+
process.exit(1);
|
|
13117
|
+
}
|
|
13118
|
+
printInfo("Thanks, feedback sent!");
|
|
12912
13119
|
}
|
|
12913
13120
|
|
|
13121
|
+
// src/commands/analyze.ts
|
|
13122
|
+
var import_node_fs19 = require("node:fs");
|
|
13123
|
+
var import_node_path14 = require("node:path");
|
|
13124
|
+
|
|
12914
13125
|
// src/lib/files.ts
|
|
12915
13126
|
var import_node_fs8 = require("node:fs");
|
|
12916
13127
|
var import_node_path8 = require("node:path");
|
|
@@ -13464,15 +13675,207 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13464
13675
|
}
|
|
13465
13676
|
}
|
|
13466
13677
|
|
|
13467
|
-
// src/lib/
|
|
13678
|
+
// src/lib/baseline.ts
|
|
13468
13679
|
var import_node_fs13 = require("node:fs");
|
|
13680
|
+
var import_node_path11 = require("node:path");
|
|
13469
13681
|
var import_node_crypto4 = require("node:crypto");
|
|
13682
|
+
var BASELINE_VERSION = 1;
|
|
13683
|
+
var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13684
|
+
var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
|
|
13685
|
+
var DEFAULT_SESSION_KEY = "_default";
|
|
13686
|
+
function sessionKey(sessionId) {
|
|
13687
|
+
if (!sessionId) return DEFAULT_SESSION_KEY;
|
|
13688
|
+
return (0, import_node_crypto4.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
13689
|
+
}
|
|
13690
|
+
function sessionDir(key) {
|
|
13691
|
+
return (0, import_node_path11.join)(projectPath(BASELINE_DIR), key);
|
|
13692
|
+
}
|
|
13693
|
+
function manifestPath(dir) {
|
|
13694
|
+
return (0, import_node_path11.join)(dir, "manifest.json");
|
|
13695
|
+
}
|
|
13696
|
+
function mirrorPath(dir, repoRelPath) {
|
|
13697
|
+
return (0, import_node_path11.join)(dir, "files", repoRelPath);
|
|
13698
|
+
}
|
|
13699
|
+
function captureBaseline(opts = {}) {
|
|
13700
|
+
const key = sessionKey(opts.sessionId);
|
|
13701
|
+
const dir = sessionDir(key);
|
|
13702
|
+
const existing = readManifest(dir);
|
|
13703
|
+
const freshStart = opts.source === "startup" || opts.source === "clear";
|
|
13704
|
+
if (existing && !freshStart) {
|
|
13705
|
+
return { baseline: existing, created: false };
|
|
13706
|
+
}
|
|
13707
|
+
const head_sha = getCurrentCommit();
|
|
13708
|
+
const dirty = getDirtyFiles();
|
|
13709
|
+
try {
|
|
13710
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13711
|
+
} catch {
|
|
13712
|
+
}
|
|
13713
|
+
const filesDir = (0, import_node_path11.join)(dir, "files");
|
|
13714
|
+
const mirrored = [];
|
|
13715
|
+
try {
|
|
13716
|
+
(0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
|
|
13717
|
+
for (const p of dirty) {
|
|
13718
|
+
if (p.includes("..")) continue;
|
|
13719
|
+
const content = safeReadForMirror(projectPath(p));
|
|
13720
|
+
if (content === null) continue;
|
|
13721
|
+
const dest = mirrorPath(dir, p);
|
|
13722
|
+
try {
|
|
13723
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(dest), { recursive: true });
|
|
13724
|
+
(0, import_node_fs13.writeFileSync)(dest, content);
|
|
13725
|
+
mirrored.push(p);
|
|
13726
|
+
} catch {
|
|
13727
|
+
}
|
|
13728
|
+
}
|
|
13729
|
+
} catch {
|
|
13730
|
+
}
|
|
13731
|
+
const baseline = {
|
|
13732
|
+
session_id: opts.sessionId ?? "",
|
|
13733
|
+
head_sha,
|
|
13734
|
+
captured_at: Date.now(),
|
|
13735
|
+
dirty_paths: mirrored,
|
|
13736
|
+
version: BASELINE_VERSION
|
|
13737
|
+
};
|
|
13738
|
+
try {
|
|
13739
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
13740
|
+
(0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
|
|
13741
|
+
} catch {
|
|
13742
|
+
}
|
|
13743
|
+
pruneOldBaselines();
|
|
13744
|
+
return { baseline, created: true };
|
|
13745
|
+
}
|
|
13746
|
+
function readBaseline(sessionId) {
|
|
13747
|
+
return readManifest(sessionDir(sessionKey(sessionId)));
|
|
13748
|
+
}
|
|
13749
|
+
function readManifest(dir) {
|
|
13750
|
+
const mp = manifestPath(dir);
|
|
13751
|
+
if (!(0, import_node_fs13.existsSync)(mp)) return null;
|
|
13752
|
+
try {
|
|
13753
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(mp, "utf-8"));
|
|
13754
|
+
if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
|
|
13755
|
+
return null;
|
|
13756
|
+
}
|
|
13757
|
+
return {
|
|
13758
|
+
session_id: typeof parsed.session_id === "string" ? parsed.session_id : "",
|
|
13759
|
+
head_sha: parsed.head_sha,
|
|
13760
|
+
captured_at: parsed.captured_at,
|
|
13761
|
+
dirty_paths: parsed.dirty_paths.filter((p) => typeof p === "string"),
|
|
13762
|
+
version: parsed.version
|
|
13763
|
+
};
|
|
13764
|
+
} catch {
|
|
13765
|
+
return null;
|
|
13766
|
+
}
|
|
13767
|
+
}
|
|
13768
|
+
var preImageCache = /* @__PURE__ */ new WeakMap();
|
|
13769
|
+
function preImage(repoRelPath, baseline) {
|
|
13770
|
+
let perBaseline = preImageCache.get(baseline);
|
|
13771
|
+
if (!perBaseline) {
|
|
13772
|
+
perBaseline = /* @__PURE__ */ new Map();
|
|
13773
|
+
preImageCache.set(baseline, perBaseline);
|
|
13774
|
+
}
|
|
13775
|
+
const cached = perBaseline.get(repoRelPath);
|
|
13776
|
+
if (cached) return cached;
|
|
13777
|
+
const resolved = resolvePreImage(repoRelPath, baseline);
|
|
13778
|
+
perBaseline.set(repoRelPath, resolved);
|
|
13779
|
+
return resolved;
|
|
13780
|
+
}
|
|
13781
|
+
function resolvePreImage(repoRelPath, baseline) {
|
|
13782
|
+
if (baseline.dirty_paths.includes(repoRelPath)) {
|
|
13783
|
+
const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
|
|
13784
|
+
if ((0, import_node_fs13.existsSync)(mp)) {
|
|
13785
|
+
try {
|
|
13786
|
+
return { content: (0, import_node_fs13.readFileSync)(mp, "utf-8"), existed: true };
|
|
13787
|
+
} catch {
|
|
13788
|
+
}
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
const atHead = showContentAtRef(baseline.head_sha, repoRelPath);
|
|
13792
|
+
if (atHead !== null) return { content: atHead, existed: true };
|
|
13793
|
+
return { content: "", existed: false };
|
|
13794
|
+
}
|
|
13795
|
+
function generateBaselineDiffs(files, baseline) {
|
|
13796
|
+
if (!baseline) return { diffs: [], has_baseline: false };
|
|
13797
|
+
const diffs = [];
|
|
13798
|
+
for (const file of files) {
|
|
13799
|
+
const language = file.language ?? detectLanguage(file.path);
|
|
13800
|
+
const pre = preImage(file.path, baseline);
|
|
13801
|
+
if (pre.existed) {
|
|
13802
|
+
if (pre.content === file.content) continue;
|
|
13803
|
+
const diff = computeDiff(pre.content, file.content, file.path);
|
|
13804
|
+
if (diff) diffs.push({ path: file.path, language, diff, status: "modified" });
|
|
13805
|
+
} else {
|
|
13806
|
+
const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
|
|
13807
|
+
diffs.push({
|
|
13808
|
+
path: file.path,
|
|
13809
|
+
language,
|
|
13810
|
+
diff: `--- /dev/null
|
|
13811
|
+
+++ b/${file.path}
|
|
13812
|
+
@@ -0,0 +1,${file.content.split("\n").length} @@
|
|
13813
|
+
${addedLines}`,
|
|
13814
|
+
status: "added"
|
|
13815
|
+
});
|
|
13816
|
+
}
|
|
13817
|
+
}
|
|
13818
|
+
return { diffs, has_baseline: true };
|
|
13819
|
+
}
|
|
13820
|
+
function changedSinceBaseline(repoRelPath, baseline) {
|
|
13821
|
+
const pre = preImage(repoRelPath, baseline);
|
|
13822
|
+
let current;
|
|
13823
|
+
try {
|
|
13824
|
+
current = (0, import_node_fs13.readFileSync)(projectPath(repoRelPath), "utf-8");
|
|
13825
|
+
} catch {
|
|
13826
|
+
return pre.existed;
|
|
13827
|
+
}
|
|
13828
|
+
if (!pre.existed) return true;
|
|
13829
|
+
return current !== pre.content;
|
|
13830
|
+
}
|
|
13831
|
+
function safeReadForMirror(absPath) {
|
|
13832
|
+
try {
|
|
13833
|
+
if ((0, import_node_fs13.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
|
|
13834
|
+
const buf = (0, import_node_fs13.readFileSync)(absPath);
|
|
13835
|
+
if (buf.includes(0)) return null;
|
|
13836
|
+
return buf.toString("utf-8");
|
|
13837
|
+
} catch {
|
|
13838
|
+
return null;
|
|
13839
|
+
}
|
|
13840
|
+
}
|
|
13841
|
+
function pruneOldBaselines() {
|
|
13842
|
+
const root = projectPath(BASELINE_DIR);
|
|
13843
|
+
let entries;
|
|
13844
|
+
try {
|
|
13845
|
+
entries = (0, import_node_fs13.readdirSync)(root);
|
|
13846
|
+
} catch {
|
|
13847
|
+
return;
|
|
13848
|
+
}
|
|
13849
|
+
const now = Date.now();
|
|
13850
|
+
for (const name of entries) {
|
|
13851
|
+
const dir = (0, import_node_path11.join)(root, name);
|
|
13852
|
+
const manifest = readManifest(dir);
|
|
13853
|
+
if (!manifest) {
|
|
13854
|
+
try {
|
|
13855
|
+
if (now - (0, import_node_fs13.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
|
|
13856
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13857
|
+
}
|
|
13858
|
+
} catch {
|
|
13859
|
+
}
|
|
13860
|
+
continue;
|
|
13861
|
+
}
|
|
13862
|
+
if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
|
|
13863
|
+
try {
|
|
13864
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13865
|
+
} catch {
|
|
13866
|
+
}
|
|
13867
|
+
}
|
|
13868
|
+
}
|
|
13869
|
+
|
|
13870
|
+
// src/lib/offline.ts
|
|
13871
|
+
var import_node_fs14 = require("node:fs");
|
|
13872
|
+
var import_node_crypto5 = require("node:crypto");
|
|
13470
13873
|
function cacheRequest(body) {
|
|
13471
13874
|
try {
|
|
13472
|
-
(0,
|
|
13473
|
-
const suffix = (0,
|
|
13875
|
+
(0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
13876
|
+
const suffix = (0, import_node_crypto5.randomBytes)(4).toString("hex");
|
|
13474
13877
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
13475
|
-
(0,
|
|
13878
|
+
(0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
|
|
13476
13879
|
} catch {
|
|
13477
13880
|
}
|
|
13478
13881
|
}
|
|
@@ -13486,7 +13889,7 @@ function buildOfflineFallback(reason, staticResults) {
|
|
|
13486
13889
|
}
|
|
13487
13890
|
|
|
13488
13891
|
// src/lib/context-files.ts
|
|
13489
|
-
var
|
|
13892
|
+
var import_node_fs15 = require("node:fs");
|
|
13490
13893
|
var MAX_CONTEXT_FILES = 10;
|
|
13491
13894
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
13492
13895
|
var MAX_CONTEXT_TOTAL_BYTES = 51200;
|
|
@@ -13498,7 +13901,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13498
13901
|
if (result.length >= MAX_CONTEXT_FILES) break;
|
|
13499
13902
|
if (deltaPaths.has(filePath)) continue;
|
|
13500
13903
|
try {
|
|
13501
|
-
const content = (0,
|
|
13904
|
+
const content = (0, import_node_fs15.readFileSync)(filePath, "utf8");
|
|
13502
13905
|
const bytes = Buffer.byteLength(content);
|
|
13503
13906
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
13504
13907
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -13543,20 +13946,20 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13543
13946
|
}
|
|
13544
13947
|
|
|
13545
13948
|
// src/lib/cache-cleanup.ts
|
|
13546
|
-
var
|
|
13547
|
-
var
|
|
13949
|
+
var import_node_fs16 = require("node:fs");
|
|
13950
|
+
var import_node_path12 = require("node:path");
|
|
13548
13951
|
var CACHE_TTL_DAYS = 7;
|
|
13549
13952
|
function pruneStaleCache() {
|
|
13550
13953
|
try {
|
|
13551
13954
|
const dir = projectPath(CACHE_DIR);
|
|
13552
13955
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
13553
|
-
for (const entry of (0,
|
|
13956
|
+
for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
|
|
13554
13957
|
if (!entry.startsWith("pending-")) continue;
|
|
13555
|
-
const path = (0,
|
|
13958
|
+
const path = (0, import_node_path12.join)(dir, entry);
|
|
13556
13959
|
try {
|
|
13557
|
-
const stat3 = (0,
|
|
13960
|
+
const stat3 = (0, import_node_fs16.statSync)(path);
|
|
13558
13961
|
if (stat3.mtimeMs < cutoff) {
|
|
13559
|
-
(0,
|
|
13962
|
+
(0, import_node_fs16.unlinkSync)(path);
|
|
13560
13963
|
logEvent("cache_entry_pruned", {
|
|
13561
13964
|
path: entry,
|
|
13562
13965
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -13628,10 +14031,11 @@ function reconcileAnalysisMode(predictedMode, signals) {
|
|
|
13628
14031
|
signals.noFilesChanged,
|
|
13629
14032
|
signals.assistantResponse,
|
|
13630
14033
|
signals.conversationPrompts,
|
|
13631
|
-
signals.actionSummary
|
|
14034
|
+
signals.actionSummary,
|
|
14035
|
+
signals.sessionAuthoredCode
|
|
13632
14036
|
);
|
|
13633
14037
|
}
|
|
13634
|
-
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0));
|
|
14038
|
+
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
13635
14039
|
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
13636
14040
|
switch (predictedMode) {
|
|
13637
14041
|
case "skip":
|
|
@@ -13656,8 +14060,8 @@ function didAgentInvestigate(summary) {
|
|
|
13656
14060
|
function isValidMode(mode) {
|
|
13657
14061
|
return mode === "standard" || mode === "plan" || mode === "debug" || mode === "skip";
|
|
13658
14062
|
}
|
|
13659
|
-
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary) {
|
|
13660
|
-
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
14063
|
+
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
14064
|
+
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
13661
14065
|
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
13662
14066
|
if (!agentAuthoredCode) return "skip";
|
|
13663
14067
|
}
|
|
@@ -13737,7 +14141,7 @@ function isMetaTaskLabel(label2) {
|
|
|
13737
14141
|
}
|
|
13738
14142
|
|
|
13739
14143
|
// src/lib/transcript.ts
|
|
13740
|
-
var
|
|
14144
|
+
var import_node_fs17 = require("node:fs");
|
|
13741
14145
|
var MAX_READ_BYTES = 256 * 1024;
|
|
13742
14146
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
13743
14147
|
var MAX_FILES_LIST = 20;
|
|
@@ -13759,14 +14163,14 @@ async function extractActionSummary(transcriptPath) {
|
|
|
13759
14163
|
function readTurnLines(transcriptPath) {
|
|
13760
14164
|
let size;
|
|
13761
14165
|
try {
|
|
13762
|
-
size = (0,
|
|
14166
|
+
size = (0, import_node_fs17.statSync)(transcriptPath).size;
|
|
13763
14167
|
} catch {
|
|
13764
14168
|
return null;
|
|
13765
14169
|
}
|
|
13766
14170
|
if (size === 0) return null;
|
|
13767
14171
|
let raw;
|
|
13768
14172
|
if (size <= SMALL_FILE_BYTES) {
|
|
13769
|
-
raw = (0,
|
|
14173
|
+
raw = (0, import_node_fs17.readFileSync)(transcriptPath, "utf-8");
|
|
13770
14174
|
} else {
|
|
13771
14175
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
13772
14176
|
const fd = require("node:fs").openSync(transcriptPath, "r");
|
|
@@ -13854,6 +14258,12 @@ function buildSummary(lines) {
|
|
|
13854
14258
|
case "Edit":
|
|
13855
14259
|
addPath(filesEdited, input.file_path);
|
|
13856
14260
|
break;
|
|
14261
|
+
case "MultiEdit":
|
|
14262
|
+
addPath(filesEdited, input.file_path);
|
|
14263
|
+
break;
|
|
14264
|
+
case "NotebookEdit":
|
|
14265
|
+
addPath(filesEdited, input.notebook_path ?? input.file_path);
|
|
14266
|
+
break;
|
|
13857
14267
|
case "Write":
|
|
13858
14268
|
addPath(filesCreated, input.file_path);
|
|
13859
14269
|
addPath(filesEdited, input.file_path);
|
|
@@ -13937,8 +14347,8 @@ function capArray(set, max) {
|
|
|
13937
14347
|
|
|
13938
14348
|
// src/lib/seed-runner.ts
|
|
13939
14349
|
var import_promises11 = require("node:fs/promises");
|
|
13940
|
-
var
|
|
13941
|
-
var
|
|
14350
|
+
var import_node_fs18 = require("node:fs");
|
|
14351
|
+
var import_node_path13 = require("node:path");
|
|
13942
14352
|
var import_yaml2 = __toESM(require_dist());
|
|
13943
14353
|
|
|
13944
14354
|
// src/lib/seed.ts
|
|
@@ -14177,7 +14587,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
14177
14587
|
return fm;
|
|
14178
14588
|
}
|
|
14179
14589
|
async function runSeed(opts) {
|
|
14180
|
-
if (!(0,
|
|
14590
|
+
if (!(0, import_node_fs18.existsSync)(STANDARD_FILE)) {
|
|
14181
14591
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
14182
14592
|
}
|
|
14183
14593
|
let standardDoc;
|
|
@@ -14189,7 +14599,7 @@ async function runSeed(opts) {
|
|
|
14189
14599
|
}
|
|
14190
14600
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
14191
14601
|
let readmeContent;
|
|
14192
|
-
if ((0,
|
|
14602
|
+
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
14193
14603
|
try {
|
|
14194
14604
|
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
14195
14605
|
} catch {
|
|
@@ -14197,7 +14607,7 @@ async function runSeed(opts) {
|
|
|
14197
14607
|
}
|
|
14198
14608
|
let claudeMdContent;
|
|
14199
14609
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
14200
|
-
if ((0,
|
|
14610
|
+
if ((0, import_node_fs18.existsSync)(p)) {
|
|
14201
14611
|
try {
|
|
14202
14612
|
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
14203
14613
|
break;
|
|
@@ -14220,8 +14630,8 @@ async function runSeed(opts) {
|
|
|
14220
14630
|
if (candidates.length === 0) {
|
|
14221
14631
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
14222
14632
|
}
|
|
14223
|
-
const overviewPath = (0,
|
|
14224
|
-
if ((0,
|
|
14633
|
+
const overviewPath = (0, import_node_path13.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
14634
|
+
if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
|
|
14225
14635
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
14226
14636
|
}
|
|
14227
14637
|
if (opts.dryRun) {
|
|
@@ -14256,9 +14666,9 @@ async function runSeed(opts) {
|
|
|
14256
14666
|
}
|
|
14257
14667
|
const nodeId = res.data.node_id;
|
|
14258
14668
|
const filePathRel = res.data.file_path;
|
|
14259
|
-
const targetPath = (0,
|
|
14669
|
+
const targetPath = (0, import_node_path13.join)(MEMORY_DIR, filePathRel);
|
|
14260
14670
|
try {
|
|
14261
|
-
await (0, import_promises11.mkdir)((0,
|
|
14671
|
+
await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(targetPath), { recursive: true });
|
|
14262
14672
|
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
14263
14673
|
created++;
|
|
14264
14674
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
@@ -14328,6 +14738,15 @@ async function runAnalyze(opts, globals) {
|
|
|
14328
14738
|
}
|
|
14329
14739
|
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
14330
14740
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
14741
|
+
const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
14742
|
+
const baseline = readBaseline(baselineSessionId);
|
|
14743
|
+
if (baseline) {
|
|
14744
|
+
logEvent("baseline_loaded", {
|
|
14745
|
+
head: baseline.head_sha.slice(0, 12),
|
|
14746
|
+
dirty_count: baseline.dirty_paths.length,
|
|
14747
|
+
age_ms: Date.now() - baseline.captured_at
|
|
14748
|
+
});
|
|
14749
|
+
}
|
|
14331
14750
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
14332
14751
|
const analyzable = filterAnalyzable(allChanged);
|
|
14333
14752
|
const reviewable = filterReviewable(allChanged);
|
|
@@ -14337,7 +14756,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14337
14756
|
passAndExit("No analyzable files changed");
|
|
14338
14757
|
}
|
|
14339
14758
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
14340
|
-
const conversation = await readAndClearConversationBuffer();
|
|
14759
|
+
const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
|
|
14341
14760
|
const specs = discoverSpecs();
|
|
14342
14761
|
const plans = discoverPlans();
|
|
14343
14762
|
const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
|
|
@@ -14387,13 +14806,14 @@ async function runAnalyze(opts, globals) {
|
|
|
14387
14806
|
}
|
|
14388
14807
|
const conversationPrompts = (conversation?.prompts ?? []).map((p) => p.prompt);
|
|
14389
14808
|
let analysisMode;
|
|
14809
|
+
const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
14390
14810
|
const modeOverride = opts.mode;
|
|
14391
14811
|
if (modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride)) {
|
|
14392
14812
|
analysisMode = modeOverride;
|
|
14393
14813
|
} else {
|
|
14394
14814
|
analysisMode = reconcileAnalysisMode(
|
|
14395
14815
|
predictedMode,
|
|
14396
|
-
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts }
|
|
14816
|
+
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
|
|
14397
14817
|
);
|
|
14398
14818
|
}
|
|
14399
14819
|
if (analysisMode === "skip") {
|
|
@@ -14451,10 +14871,16 @@ async function runAnalyze(opts, globals) {
|
|
|
14451
14871
|
const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
|
|
14452
14872
|
const recentForReview = narrowToRecent(baseForReview);
|
|
14453
14873
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
14454
|
-
|
|
14455
|
-
|
|
14874
|
+
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14875
|
+
if (baseline) {
|
|
14876
|
+
allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
14877
|
+
}
|
|
14878
|
+
if (allScannable.length > 0) {
|
|
14879
|
+
staticResults = runCodacyAnalysis(allScannable);
|
|
14880
|
+
}
|
|
14456
14881
|
}
|
|
14457
|
-
|
|
14882
|
+
const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
|
|
14883
|
+
codeDelta = collectCodeDelta(deltaSet, {
|
|
14458
14884
|
maxFiles: parseInt(opts.maxFiles, 10),
|
|
14459
14885
|
maxFileBytes: parseInt(opts.maxFileSize, 10),
|
|
14460
14886
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
@@ -14469,7 +14895,12 @@ async function runAnalyze(opts, globals) {
|
|
|
14469
14895
|
}
|
|
14470
14896
|
}
|
|
14471
14897
|
if (analysisMode !== "plan") {
|
|
14472
|
-
|
|
14898
|
+
if (baseline) {
|
|
14899
|
+
const baselineDiffs = generateBaselineDiffs(codeDelta.files, baseline);
|
|
14900
|
+
snapshotResult = { has_snapshots: baselineDiffs.diffs.length > 0, diffs: baselineDiffs.diffs };
|
|
14901
|
+
} else {
|
|
14902
|
+
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
14903
|
+
}
|
|
14473
14904
|
currentCommit = getCurrentCommit();
|
|
14474
14905
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
14475
14906
|
const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
@@ -14501,9 +14932,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14501
14932
|
let autoSeedNotice = null;
|
|
14502
14933
|
try {
|
|
14503
14934
|
await ensureMemoryDir();
|
|
14504
|
-
const seedMarker = (0,
|
|
14505
|
-
const hasStandard = (0,
|
|
14506
|
-
const alreadyTried = (0,
|
|
14935
|
+
const seedMarker = (0, import_node_path14.join)(VERITY_DIR, ".seeded");
|
|
14936
|
+
const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
|
|
14937
|
+
const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
|
|
14507
14938
|
if (hasStandard && !alreadyTried) {
|
|
14508
14939
|
const preManifest = await buildManifest();
|
|
14509
14940
|
if (preManifest.nodes.length === 0) {
|
|
@@ -14516,7 +14947,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14516
14947
|
dryRun: false
|
|
14517
14948
|
});
|
|
14518
14949
|
if (seedResult.created > 0) {
|
|
14519
|
-
(0,
|
|
14950
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
14520
14951
|
`);
|
|
14521
14952
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
14522
14953
|
logEvent("auto_seed_ran", {
|
|
@@ -14524,7 +14955,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14524
14955
|
failed: seedResult.failed
|
|
14525
14956
|
});
|
|
14526
14957
|
} else if (seedResult.skipped === "already_seeded") {
|
|
14527
|
-
(0,
|
|
14958
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
14528
14959
|
`);
|
|
14529
14960
|
} else {
|
|
14530
14961
|
logEvent("auto_seed_noop", {
|
|
@@ -14842,8 +15273,54 @@ async function runAnalyze(opts, globals) {
|
|
|
14842
15273
|
}
|
|
14843
15274
|
}
|
|
14844
15275
|
|
|
15276
|
+
// src/commands/baseline.ts
|
|
15277
|
+
var import_node_fs20 = require("node:fs");
|
|
15278
|
+
function registerBaselineCommands(program2) {
|
|
15279
|
+
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
15280
|
+
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) => {
|
|
15281
|
+
try {
|
|
15282
|
+
try {
|
|
15283
|
+
process.chdir(repoRoot());
|
|
15284
|
+
} catch {
|
|
15285
|
+
}
|
|
15286
|
+
if (!(0, import_node_fs20.existsSync)(VERITY_DIR)) {
|
|
15287
|
+
process.exit(0);
|
|
15288
|
+
}
|
|
15289
|
+
let sessionId = opts.sessionId;
|
|
15290
|
+
let source = opts.source;
|
|
15291
|
+
if (!process.stdin.isTTY) {
|
|
15292
|
+
const input = (await readStdin()).trim();
|
|
15293
|
+
if (input) {
|
|
15294
|
+
try {
|
|
15295
|
+
const event = JSON.parse(input);
|
|
15296
|
+
sessionId = sessionId ?? event.session_id;
|
|
15297
|
+
source = source ?? event.source;
|
|
15298
|
+
} catch {
|
|
15299
|
+
}
|
|
15300
|
+
}
|
|
15301
|
+
}
|
|
15302
|
+
const result = captureBaseline({ sessionId, source });
|
|
15303
|
+
logEvent("baseline_capture", {
|
|
15304
|
+
created: result.created,
|
|
15305
|
+
source: source ?? null,
|
|
15306
|
+
dirty_count: result.baseline.dirty_paths.length,
|
|
15307
|
+
head: result.baseline.head_sha.slice(0, 12)
|
|
15308
|
+
});
|
|
15309
|
+
} catch {
|
|
15310
|
+
}
|
|
15311
|
+
process.exit(0);
|
|
15312
|
+
});
|
|
15313
|
+
}
|
|
15314
|
+
async function readStdin() {
|
|
15315
|
+
const chunks = [];
|
|
15316
|
+
for await (const chunk of process.stdin) {
|
|
15317
|
+
chunks.push(chunk);
|
|
15318
|
+
}
|
|
15319
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
15320
|
+
}
|
|
15321
|
+
|
|
14845
15322
|
// src/commands/review.ts
|
|
14846
|
-
var
|
|
15323
|
+
var import_node_fs21 = require("node:fs");
|
|
14847
15324
|
function registerReviewCommand(program2) {
|
|
14848
15325
|
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) => {
|
|
14849
15326
|
const globals = program2.opts();
|
|
@@ -14862,7 +15339,7 @@ async function runReview(opts, globals) {
|
|
|
14862
15339
|
const securityFiles = filterSecurity(allFiles);
|
|
14863
15340
|
let staticResults;
|
|
14864
15341
|
if (isCodacyAvailable()) {
|
|
14865
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
15342
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs21.existsSync)(f) || resolveFile(f) !== null);
|
|
14866
15343
|
staticResults = runCodacyAnalysis(scannable);
|
|
14867
15344
|
} else {
|
|
14868
15345
|
staticResults = {
|
|
@@ -14887,10 +15364,10 @@ async function runReview(opts, globals) {
|
|
|
14887
15364
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
14888
15365
|
specs = [];
|
|
14889
15366
|
for (const p of specPaths) {
|
|
14890
|
-
if (!(0,
|
|
15367
|
+
if (!(0, import_node_fs21.existsSync)(p)) continue;
|
|
14891
15368
|
try {
|
|
14892
|
-
const { readFileSync:
|
|
14893
|
-
const content =
|
|
15369
|
+
const { readFileSync: readFileSync12 } = await import("node:fs");
|
|
15370
|
+
const content = readFileSync12(p, "utf-8");
|
|
14894
15371
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
14895
15372
|
} catch {
|
|
14896
15373
|
}
|
|
@@ -14947,10 +15424,10 @@ async function runReview(opts, globals) {
|
|
|
14947
15424
|
}
|
|
14948
15425
|
|
|
14949
15426
|
// src/commands/guard.ts
|
|
14950
|
-
var
|
|
14951
|
-
var
|
|
15427
|
+
var import_node_fs22 = require("node:fs");
|
|
15428
|
+
var import_node_path15 = require("node:path");
|
|
14952
15429
|
var GUARD_BLOCK_CAP = 2;
|
|
14953
|
-
var GUARD_ITER_FILE = (0,
|
|
15430
|
+
var GUARD_ITER_FILE = (0, import_node_path15.join)(VERITY_DIR, ".guard-iteration");
|
|
14954
15431
|
function readPreToolUseStdin() {
|
|
14955
15432
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
14956
15433
|
return new Promise((resolve) => {
|
|
@@ -15014,7 +15491,7 @@ function classifyCommand(command, on) {
|
|
|
15014
15491
|
}
|
|
15015
15492
|
function readIterMap() {
|
|
15016
15493
|
try {
|
|
15017
|
-
const raw = JSON.parse((0,
|
|
15494
|
+
const raw = JSON.parse((0, import_node_fs22.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
15018
15495
|
if (raw && typeof raw === "object") {
|
|
15019
15496
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
15020
15497
|
return { [raw.moment]: raw.count };
|
|
@@ -15034,10 +15511,10 @@ function readIter(moment) {
|
|
|
15034
15511
|
}
|
|
15035
15512
|
function writeIter(moment, count) {
|
|
15036
15513
|
try {
|
|
15037
|
-
(0,
|
|
15514
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15038
15515
|
const map = readIterMap();
|
|
15039
15516
|
map[moment] = count;
|
|
15040
|
-
(0,
|
|
15517
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15041
15518
|
} catch {
|
|
15042
15519
|
}
|
|
15043
15520
|
}
|
|
@@ -15047,10 +15524,10 @@ function resetIter(moment) {
|
|
|
15047
15524
|
if (!(moment in map)) return;
|
|
15048
15525
|
delete map[moment];
|
|
15049
15526
|
if (Object.keys(map).length === 0) {
|
|
15050
|
-
if ((0,
|
|
15527
|
+
if ((0, import_node_fs22.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs22.unlinkSync)(GUARD_ITER_FILE);
|
|
15051
15528
|
} else {
|
|
15052
|
-
(0,
|
|
15053
|
-
(0,
|
|
15529
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15530
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15054
15531
|
}
|
|
15055
15532
|
} catch {
|
|
15056
15533
|
}
|
|
@@ -15114,7 +15591,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
15114
15591
|
const securityFiles = filterSecurity(files);
|
|
15115
15592
|
let staticResults;
|
|
15116
15593
|
if (isCodacyAvailable()) {
|
|
15117
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
15594
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs22.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
15118
15595
|
staticResults = runCodacyAnalysis(scannable);
|
|
15119
15596
|
} else {
|
|
15120
15597
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
@@ -15159,7 +15636,7 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
15159
15636
|
async function runGuard(opts, globals) {
|
|
15160
15637
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
15161
15638
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
15162
|
-
if (cwd && (0,
|
|
15639
|
+
if (cwd && (0, import_node_fs22.existsSync)(cwd)) {
|
|
15163
15640
|
try {
|
|
15164
15641
|
process.chdir(cwd);
|
|
15165
15642
|
} catch {
|
|
@@ -15264,14 +15741,14 @@ function writeBlockMessage(moment, response) {
|
|
|
15264
15741
|
}
|
|
15265
15742
|
|
|
15266
15743
|
// src/commands/init.ts
|
|
15267
|
-
var
|
|
15744
|
+
var import_node_fs24 = require("node:fs");
|
|
15268
15745
|
var import_promises12 = require("node:fs/promises");
|
|
15269
|
-
var
|
|
15746
|
+
var import_node_path17 = require("node:path");
|
|
15270
15747
|
var import_node_child_process9 = require("node:child_process");
|
|
15271
15748
|
|
|
15272
15749
|
// src/commands/migrate.ts
|
|
15273
|
-
var
|
|
15274
|
-
var
|
|
15750
|
+
var import_node_fs23 = require("node:fs");
|
|
15751
|
+
var import_node_path16 = require("node:path");
|
|
15275
15752
|
var import_node_child_process8 = require("node:child_process");
|
|
15276
15753
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
15277
15754
|
function defaultNpmRemover(pkg) {
|
|
@@ -15307,12 +15784,12 @@ async function runMigration(opts = {}) {
|
|
|
15307
15784
|
return { actions, migrated: actions.length > 0 };
|
|
15308
15785
|
}
|
|
15309
15786
|
function migrateProjectDir(root, actions) {
|
|
15310
|
-
const gateDir = (0,
|
|
15311
|
-
const verityDir = (0,
|
|
15312
|
-
if ((0,
|
|
15787
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
15788
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
15789
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
|
|
15313
15790
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
15314
15791
|
}
|
|
15315
|
-
if ((0,
|
|
15792
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
15316
15793
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
15317
15794
|
}
|
|
15318
15795
|
return false;
|
|
@@ -15333,13 +15810,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
15333
15810
|
}
|
|
15334
15811
|
}
|
|
15335
15812
|
if (moved) {
|
|
15336
|
-
if ((0,
|
|
15813
|
+
if ((0, import_node_fs23.existsSync)(gateDir)) {
|
|
15337
15814
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
15338
15815
|
if (carried > 0) {
|
|
15339
15816
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
15340
15817
|
}
|
|
15341
15818
|
try {
|
|
15342
|
-
(0,
|
|
15819
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15343
15820
|
} catch {
|
|
15344
15821
|
}
|
|
15345
15822
|
}
|
|
@@ -15355,18 +15832,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
15355
15832
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
15356
15833
|
}
|
|
15357
15834
|
try {
|
|
15358
|
-
(0,
|
|
15835
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15359
15836
|
} catch {
|
|
15360
15837
|
}
|
|
15361
15838
|
return carried > 0;
|
|
15362
15839
|
}
|
|
15363
15840
|
function migrateGlobalCredentials(home, actions) {
|
|
15364
15841
|
if (!home) return;
|
|
15365
|
-
const gateCreds = (0,
|
|
15366
|
-
const verityCreds = (0,
|
|
15367
|
-
if (!(0,
|
|
15368
|
-
if (!(0,
|
|
15369
|
-
(0,
|
|
15842
|
+
const gateCreds = (0, import_node_path16.join)(home, ".gate", "credentials");
|
|
15843
|
+
const verityCreds = (0, import_node_path16.join)(home, ".verity", "credentials");
|
|
15844
|
+
if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
|
|
15845
|
+
if (!(0, import_node_fs23.existsSync)(verityCreds)) {
|
|
15846
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.join)(home, ".verity"), { recursive: true });
|
|
15370
15847
|
moveFile(gateCreds, verityCreds);
|
|
15371
15848
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
15372
15849
|
return;
|
|
@@ -15388,8 +15865,8 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
15388
15865
|
}
|
|
15389
15866
|
}
|
|
15390
15867
|
async function migrateClaudeMd(root, actions) {
|
|
15391
|
-
const claudeMd = (0,
|
|
15392
|
-
const hadLegacyBlock = (0,
|
|
15868
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
15869
|
+
const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
15393
15870
|
if (!hadLegacyBlock) return;
|
|
15394
15871
|
try {
|
|
15395
15872
|
await ensureClaudeMdPointer(root);
|
|
@@ -15399,9 +15876,9 @@ async function migrateClaudeMd(root, actions) {
|
|
|
15399
15876
|
}
|
|
15400
15877
|
}
|
|
15401
15878
|
function migrateStandardFile(root, actions) {
|
|
15402
|
-
const gateMd = (0,
|
|
15403
|
-
const verityMd = (0,
|
|
15404
|
-
if (!(0,
|
|
15879
|
+
const gateMd = (0, import_node_path16.join)(root, "GATE.md");
|
|
15880
|
+
const verityMd = (0, import_node_path16.join)(root, "VERITY.md");
|
|
15881
|
+
if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
|
|
15405
15882
|
let moved = false;
|
|
15406
15883
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
15407
15884
|
try {
|
|
@@ -15413,7 +15890,7 @@ function migrateStandardFile(root, actions) {
|
|
|
15413
15890
|
if (!moved) moveFile(gateMd, verityMd);
|
|
15414
15891
|
const content = readFileSyncSafe(verityMd);
|
|
15415
15892
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
15416
|
-
if (refreshed !== content) (0,
|
|
15893
|
+
if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
|
|
15417
15894
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
15418
15895
|
}
|
|
15419
15896
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
@@ -15447,14 +15924,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
15447
15924
|
}
|
|
15448
15925
|
if (toAppend.length > 0) {
|
|
15449
15926
|
const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
15450
|
-
(0,
|
|
15927
|
+
(0, import_node_fs23.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
|
|
15451
15928
|
}
|
|
15452
|
-
(0,
|
|
15929
|
+
(0, import_node_fs23.rmSync)(gateCreds, { force: true });
|
|
15453
15930
|
return toAppend.length;
|
|
15454
15931
|
}
|
|
15455
15932
|
function readFileSyncSafe(path) {
|
|
15456
15933
|
try {
|
|
15457
|
-
return (0,
|
|
15934
|
+
return (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
15458
15935
|
} catch {
|
|
15459
15936
|
return "";
|
|
15460
15937
|
}
|
|
@@ -15469,35 +15946,35 @@ function hasStagedChanges(root) {
|
|
|
15469
15946
|
}
|
|
15470
15947
|
function moveDir(from, to) {
|
|
15471
15948
|
try {
|
|
15472
|
-
(0,
|
|
15949
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15473
15950
|
} catch (err) {
|
|
15474
15951
|
if (err.code !== "EXDEV") throw err;
|
|
15475
|
-
(0,
|
|
15476
|
-
(0,
|
|
15952
|
+
(0, import_node_fs23.cpSync)(from, to, { recursive: true });
|
|
15953
|
+
(0, import_node_fs23.rmSync)(from, { recursive: true, force: true });
|
|
15477
15954
|
}
|
|
15478
15955
|
}
|
|
15479
15956
|
function moveFile(from, to) {
|
|
15480
15957
|
try {
|
|
15481
|
-
(0,
|
|
15958
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15482
15959
|
} catch (err) {
|
|
15483
15960
|
if (err.code !== "EXDEV") throw err;
|
|
15484
|
-
(0,
|
|
15485
|
-
(0,
|
|
15961
|
+
(0, import_node_fs23.cpSync)(from, to);
|
|
15962
|
+
(0, import_node_fs23.rmSync)(from, { force: true });
|
|
15486
15963
|
}
|
|
15487
15964
|
}
|
|
15488
15965
|
function carryLegacyContents(gateDir, verityDir) {
|
|
15489
15966
|
let copied = 0;
|
|
15490
15967
|
const walk = (relDir) => {
|
|
15491
|
-
const srcDir = (0,
|
|
15492
|
-
for (const entry of (0,
|
|
15493
|
-
const rel = relDir ? (0,
|
|
15494
|
-
const src = (0,
|
|
15495
|
-
const dest = (0,
|
|
15496
|
-
if ((0,
|
|
15968
|
+
const srcDir = (0, import_node_path16.join)(gateDir, relDir);
|
|
15969
|
+
for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
|
|
15970
|
+
const rel = relDir ? (0, import_node_path16.join)(relDir, entry) : entry;
|
|
15971
|
+
const src = (0, import_node_path16.join)(gateDir, rel);
|
|
15972
|
+
const dest = (0, import_node_path16.join)(verityDir, rel);
|
|
15973
|
+
if ((0, import_node_fs23.statSync)(src).isDirectory()) {
|
|
15497
15974
|
walk(rel);
|
|
15498
|
-
} else if (!(0,
|
|
15499
|
-
(0,
|
|
15500
|
-
(0,
|
|
15975
|
+
} else if (!(0, import_node_fs23.existsSync)(dest)) {
|
|
15976
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
15977
|
+
(0, import_node_fs23.cpSync)(src, dest);
|
|
15501
15978
|
copied++;
|
|
15502
15979
|
}
|
|
15503
15980
|
}
|
|
@@ -15506,22 +15983,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
15506
15983
|
return copied;
|
|
15507
15984
|
}
|
|
15508
15985
|
async function needsMigration(root = repoRoot()) {
|
|
15509
|
-
const gateDir = (0,
|
|
15510
|
-
const verityDir = (0,
|
|
15511
|
-
if ((0,
|
|
15512
|
-
if ((0,
|
|
15513
|
-
if ((0,
|
|
15986
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
15987
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
15988
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
|
|
15989
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
15990
|
+
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"))) {
|
|
15514
15991
|
return true;
|
|
15515
15992
|
}
|
|
15516
|
-
if ((0,
|
|
15993
|
+
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"))) {
|
|
15517
15994
|
return true;
|
|
15518
15995
|
}
|
|
15519
15996
|
}
|
|
15520
|
-
const claudeMd = (0,
|
|
15521
|
-
if ((0,
|
|
15997
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
15998
|
+
if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
15522
15999
|
return true;
|
|
15523
16000
|
}
|
|
15524
|
-
if ((0,
|
|
16001
|
+
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"))) {
|
|
15525
16002
|
return true;
|
|
15526
16003
|
}
|
|
15527
16004
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -15549,15 +16026,15 @@ function registerMigrateCommand(program2) {
|
|
|
15549
16026
|
// src/commands/init.ts
|
|
15550
16027
|
function resolveDataDir() {
|
|
15551
16028
|
const candidates = [
|
|
15552
|
-
(0,
|
|
16029
|
+
(0, import_node_path17.join)(__dirname, "..", "data"),
|
|
15553
16030
|
// installed: node_modules/@codacy/verity-cli/data
|
|
15554
|
-
(0,
|
|
16031
|
+
(0, import_node_path17.join)(__dirname, "..", "..", "data"),
|
|
15555
16032
|
// edge case: nested resolution
|
|
15556
|
-
(0,
|
|
16033
|
+
(0, import_node_path17.join)(process.cwd(), "cli", "data")
|
|
15557
16034
|
// local dev: running from repo root
|
|
15558
16035
|
];
|
|
15559
16036
|
for (const candidate of candidates) {
|
|
15560
|
-
if ((0,
|
|
16037
|
+
if ((0, import_node_fs24.existsSync)((0, import_node_path17.join)(candidate, "skills"))) {
|
|
15561
16038
|
return candidate;
|
|
15562
16039
|
}
|
|
15563
16040
|
}
|
|
@@ -15573,7 +16050,7 @@ function registerInitCommand(program2) {
|
|
|
15573
16050
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
15574
16051
|
const force = opts.force ?? false;
|
|
15575
16052
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
15576
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
16053
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs24.existsSync)(m));
|
|
15577
16054
|
if (!isProject) {
|
|
15578
16055
|
printError("No project detected in the current directory.");
|
|
15579
16056
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -15636,21 +16113,21 @@ function registerInitCommand(program2) {
|
|
|
15636
16113
|
console.log("");
|
|
15637
16114
|
printInfo("Installing skills...");
|
|
15638
16115
|
const dataDir = resolveDataDir();
|
|
15639
|
-
const skillsSource = (0,
|
|
16116
|
+
const skillsSource = (0, import_node_path17.join)(dataDir, "skills");
|
|
15640
16117
|
const skillsDest = ".claude/skills";
|
|
15641
16118
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
15642
16119
|
let skillsInstalled = 0;
|
|
15643
16120
|
for (const skill of skills) {
|
|
15644
|
-
const src = (0,
|
|
15645
|
-
const dest = (0,
|
|
15646
|
-
if (!(0,
|
|
16121
|
+
const src = (0, import_node_path17.join)(skillsSource, skill);
|
|
16122
|
+
const dest = (0, import_node_path17.join)(skillsDest, skill);
|
|
16123
|
+
if (!(0, import_node_fs24.existsSync)(src)) {
|
|
15647
16124
|
printWarn(` Skill data not found: ${skill}`);
|
|
15648
16125
|
continue;
|
|
15649
16126
|
}
|
|
15650
|
-
if ((0,
|
|
15651
|
-
const srcSkill = (0,
|
|
15652
|
-
const destSkill = (0,
|
|
15653
|
-
if ((0,
|
|
16127
|
+
if ((0, import_node_fs24.existsSync)(dest) && !force) {
|
|
16128
|
+
const srcSkill = (0, import_node_path17.join)(src, "SKILL.md");
|
|
16129
|
+
const destSkill = (0, import_node_path17.join)(dest, "SKILL.md");
|
|
16130
|
+
if ((0, import_node_fs24.existsSync)(destSkill)) {
|
|
15654
16131
|
try {
|
|
15655
16132
|
const srcContent = await (0, import_promises12.readFile)(srcSkill, "utf-8");
|
|
15656
16133
|
const destContent = await (0, import_promises12.readFile)(destSkill, "utf-8");
|
|
@@ -15674,6 +16151,7 @@ function registerInitCommand(program2) {
|
|
|
15674
16151
|
await writeSettings(hookResult.data);
|
|
15675
16152
|
printInfo(" Stop hook: verity analyze \u2713");
|
|
15676
16153
|
printInfo(" Intent hook: verity intent capture \u2713");
|
|
16154
|
+
printInfo(" Baseline hook: verity baseline capture \u2713");
|
|
15677
16155
|
} else {
|
|
15678
16156
|
printWarn(` ${hookResult.error}`);
|
|
15679
16157
|
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
@@ -15686,7 +16164,7 @@ function registerInitCommand(program2) {
|
|
|
15686
16164
|
} catch (err) {
|
|
15687
16165
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
15688
16166
|
}
|
|
15689
|
-
const globalVerityDir = (0,
|
|
16167
|
+
const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
|
|
15690
16168
|
await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
|
|
15691
16169
|
console.log("");
|
|
15692
16170
|
printInfo("Verity initialized!");
|
|
@@ -15709,8 +16187,8 @@ function registerInitCommand(program2) {
|
|
|
15709
16187
|
}
|
|
15710
16188
|
|
|
15711
16189
|
// src/commands/uninstall.ts
|
|
15712
|
-
var
|
|
15713
|
-
var
|
|
16190
|
+
var import_node_fs25 = require("node:fs");
|
|
16191
|
+
var import_node_path18 = require("node:path");
|
|
15714
16192
|
var SKILL_NAMES = [
|
|
15715
16193
|
"verity-setup",
|
|
15716
16194
|
"verity-analyze",
|
|
@@ -15729,11 +16207,11 @@ function registerUninstallCommand(program2) {
|
|
|
15729
16207
|
const actions = [];
|
|
15730
16208
|
const skillsRoot = projectPath(".claude/skills");
|
|
15731
16209
|
for (const name of SKILL_NAMES) {
|
|
15732
|
-
const dir = (0,
|
|
15733
|
-
if ((0,
|
|
16210
|
+
const dir = (0, import_node_path18.join)(skillsRoot, name);
|
|
16211
|
+
if ((0, import_node_fs25.existsSync)(dir)) {
|
|
15734
16212
|
actions.push({
|
|
15735
16213
|
label: `Remove .claude/skills/${name}/`,
|
|
15736
|
-
apply: () => (0,
|
|
16214
|
+
apply: () => (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true })
|
|
15737
16215
|
});
|
|
15738
16216
|
}
|
|
15739
16217
|
}
|
|
@@ -15747,24 +16225,24 @@ function registerUninstallCommand(program2) {
|
|
|
15747
16225
|
});
|
|
15748
16226
|
}
|
|
15749
16227
|
const verityDir = projectPath(VERITY_DIR);
|
|
15750
|
-
if ((0,
|
|
16228
|
+
if ((0, import_node_fs25.existsSync)(verityDir)) {
|
|
15751
16229
|
actions.push({
|
|
15752
16230
|
label: `Remove ${VERITY_DIR}/`,
|
|
15753
|
-
apply: () => (0,
|
|
16231
|
+
apply: () => (0, import_node_fs25.rmSync)(verityDir, { recursive: true, force: true })
|
|
15754
16232
|
});
|
|
15755
16233
|
}
|
|
15756
16234
|
if (!keepVerityMd) {
|
|
15757
16235
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
15758
|
-
if ((0,
|
|
16236
|
+
if ((0, import_node_fs25.existsSync)(verityMd)) {
|
|
15759
16237
|
actions.push({
|
|
15760
16238
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
15761
|
-
apply: () => (0,
|
|
16239
|
+
apply: () => (0, import_node_fs25.rmSync)(verityMd, { force: true })
|
|
15762
16240
|
});
|
|
15763
16241
|
}
|
|
15764
16242
|
}
|
|
15765
16243
|
const cleanupEmptyDir = (path) => {
|
|
15766
|
-
if ((0,
|
|
15767
|
-
(0,
|
|
16244
|
+
if ((0, import_node_fs25.existsSync)(path) && (0, import_node_fs25.statSync)(path).isDirectory() && (0, import_node_fs25.readdirSync)(path).length === 0) {
|
|
16245
|
+
(0, import_node_fs25.rmdirSync)(path);
|
|
15768
16246
|
}
|
|
15769
16247
|
};
|
|
15770
16248
|
actions.push({
|
|
@@ -15775,11 +16253,11 @@ function registerUninstallCommand(program2) {
|
|
|
15775
16253
|
}
|
|
15776
16254
|
});
|
|
15777
16255
|
const home = process.env.HOME ?? "";
|
|
15778
|
-
const globalVerityDir = (0,
|
|
15779
|
-
if (purgeGlobal && (0,
|
|
16256
|
+
const globalVerityDir = (0, import_node_path18.join)(home, ".verity");
|
|
16257
|
+
if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
|
|
15780
16258
|
actions.push({
|
|
15781
16259
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
15782
|
-
apply: () => (0,
|
|
16260
|
+
apply: () => (0, import_node_fs25.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
15783
16261
|
});
|
|
15784
16262
|
}
|
|
15785
16263
|
if (actions.length === 0) {
|
|
@@ -15973,8 +16451,8 @@ function registerTaskCommands(program2) {
|
|
|
15973
16451
|
}
|
|
15974
16452
|
|
|
15975
16453
|
// src/commands/reset.ts
|
|
15976
|
-
var
|
|
15977
|
-
var
|
|
16454
|
+
var import_node_fs26 = require("node:fs");
|
|
16455
|
+
var import_node_path19 = require("node:path");
|
|
15978
16456
|
function registerResetCommand(program2) {
|
|
15979
16457
|
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) => {
|
|
15980
16458
|
const globals = program2.opts();
|
|
@@ -16011,11 +16489,11 @@ function registerResetCommand(program2) {
|
|
|
16011
16489
|
}
|
|
16012
16490
|
const cacheDir = projectPath(CACHE_DIR);
|
|
16013
16491
|
let purged = 0;
|
|
16014
|
-
if ((0,
|
|
16015
|
-
for (const entry of (0,
|
|
16492
|
+
if ((0, import_node_fs26.existsSync)(cacheDir)) {
|
|
16493
|
+
for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
|
|
16016
16494
|
if (entry.startsWith("pending-")) {
|
|
16017
16495
|
try {
|
|
16018
|
-
(0,
|
|
16496
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(cacheDir, entry));
|
|
16019
16497
|
purged++;
|
|
16020
16498
|
} catch {
|
|
16021
16499
|
}
|
|
@@ -16030,19 +16508,19 @@ function registerResetCommand(program2) {
|
|
|
16030
16508
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
16031
16509
|
];
|
|
16032
16510
|
for (const file of filesToClear) {
|
|
16033
|
-
if ((0,
|
|
16511
|
+
if ((0, import_node_fs26.existsSync)(file)) {
|
|
16034
16512
|
try {
|
|
16035
|
-
(0,
|
|
16513
|
+
(0, import_node_fs26.writeFileSync)(file, "");
|
|
16036
16514
|
} catch {
|
|
16037
16515
|
}
|
|
16038
16516
|
}
|
|
16039
16517
|
}
|
|
16040
16518
|
if (opts.all) {
|
|
16041
16519
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
16042
|
-
if ((0,
|
|
16043
|
-
for (const entry of (0,
|
|
16520
|
+
if ((0, import_node_fs26.existsSync)(logsDir)) {
|
|
16521
|
+
for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
|
|
16044
16522
|
try {
|
|
16045
|
-
(0,
|
|
16523
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(logsDir, entry));
|
|
16046
16524
|
} catch {
|
|
16047
16525
|
}
|
|
16048
16526
|
}
|
|
@@ -16301,7 +16779,7 @@ function registerRunCommand(program2) {
|
|
|
16301
16779
|
|
|
16302
16780
|
// src/lib/telemetry.ts
|
|
16303
16781
|
var import_promises13 = require("node:fs/promises");
|
|
16304
|
-
var
|
|
16782
|
+
var import_node_path20 = require("node:path");
|
|
16305
16783
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
16306
16784
|
var GITIGNORE_FILE = ".gitignore";
|
|
16307
16785
|
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
@@ -16339,7 +16817,7 @@ async function readSettingsLocal() {
|
|
|
16339
16817
|
}
|
|
16340
16818
|
async function writeSettingsLocal(settings) {
|
|
16341
16819
|
const file = projectPath(SETTINGS_LOCAL_FILE2);
|
|
16342
|
-
await (0, import_promises13.mkdir)((0,
|
|
16820
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
|
|
16343
16821
|
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
16344
16822
|
}
|
|
16345
16823
|
async function ensureGitignore() {
|
|
@@ -16435,7 +16913,7 @@ function registerTelemetryCommands(program2) {
|
|
|
16435
16913
|
}
|
|
16436
16914
|
|
|
16437
16915
|
// src/cli.ts
|
|
16438
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.
|
|
16916
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.bdf0db7").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
16439
16917
|
registerAuthCommands(program);
|
|
16440
16918
|
registerHooksCommands(program);
|
|
16441
16919
|
registerIntentCommands(program);
|
|
@@ -16444,6 +16922,7 @@ registerConfigCommands(program);
|
|
|
16444
16922
|
registerStatusCommand(program);
|
|
16445
16923
|
registerFeedbackCommand(program);
|
|
16446
16924
|
registerAnalyzeCommand(program);
|
|
16925
|
+
registerBaselineCommands(program);
|
|
16447
16926
|
registerReviewCommand(program);
|
|
16448
16927
|
registerGuardCommand(program);
|
|
16449
16928
|
registerInitCommand(program);
|