@codacy/verity-cli 0.24.0 → 0.25.0-experimental.56ef7d8
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 +1120 -519
- package/data/skills/verity-analyze/SKILL.md +7 -1
- package/data/skills/verity-setup/SKILL.md +49 -10
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10325,9 +10325,7 @@ var {
|
|
|
10325
10325
|
} = import_index.default;
|
|
10326
10326
|
|
|
10327
10327
|
// src/commands/auth.ts
|
|
10328
|
-
var
|
|
10329
|
-
var import_node_child_process3 = require("node:child_process");
|
|
10330
|
-
var import_node_path2 = require("node:path");
|
|
10328
|
+
var import_node_child_process4 = require("node:child_process");
|
|
10331
10329
|
|
|
10332
10330
|
// src/lib/auth.ts
|
|
10333
10331
|
var import_promises = require("node:fs/promises");
|
|
@@ -10379,6 +10377,7 @@ var MAX_PLAN_FILES = 3;
|
|
|
10379
10377
|
var MAX_PLAN_FILE_BYTES = 10240;
|
|
10380
10378
|
var MAX_INTENT_CHARS = 2e3;
|
|
10381
10379
|
var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
|
|
10380
|
+
var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
|
|
10382
10381
|
var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
|
|
10383
10382
|
var CONVERSATION_MAX_ENTRIES = 10;
|
|
10384
10383
|
var CONVERSATION_WINDOW_MINUTES = 15;
|
|
@@ -10473,7 +10472,12 @@ var SECURITY_PATTERNS = [
|
|
|
10473
10472
|
/Cargo\.lock$/,
|
|
10474
10473
|
/Dockerfile/
|
|
10475
10474
|
];
|
|
10476
|
-
var
|
|
10475
|
+
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10476
|
+
var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10477
|
+
var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
|
|
10478
|
+
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10479
|
+
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
10480
|
+
var GITHUB_OAUTH_SCOPES = "repo user:email";
|
|
10477
10481
|
|
|
10478
10482
|
// src/lib/auth.ts
|
|
10479
10483
|
async function resolveToken(flagToken) {
|
|
@@ -10639,7 +10643,8 @@ async function apiRequest(options) {
|
|
|
10639
10643
|
timeout = 9e4,
|
|
10640
10644
|
cmd = "unknown",
|
|
10641
10645
|
retry = false,
|
|
10642
|
-
encodeBody = false
|
|
10646
|
+
encodeBody = false,
|
|
10647
|
+
extraHeaders
|
|
10643
10648
|
} = options;
|
|
10644
10649
|
const url = `${serviceUrl}${path}`;
|
|
10645
10650
|
const headers = {
|
|
@@ -10648,6 +10653,9 @@ async function apiRequest(options) {
|
|
|
10648
10653
|
if (token) {
|
|
10649
10654
|
headers["Authorization"] = `Bearer ${token}`;
|
|
10650
10655
|
}
|
|
10656
|
+
if (extraHeaders) {
|
|
10657
|
+
Object.assign(headers, extraHeaders);
|
|
10658
|
+
}
|
|
10651
10659
|
const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
|
|
10652
10660
|
if (testMockScenario) {
|
|
10653
10661
|
headers["X-Verity-Mock-Scenario"] = testMockScenario;
|
|
@@ -10738,6 +10746,388 @@ function analyzeRequest(options) {
|
|
|
10738
10746
|
});
|
|
10739
10747
|
}
|
|
10740
10748
|
|
|
10749
|
+
// src/lib/register.ts
|
|
10750
|
+
var import_promises3 = require("node:fs/promises");
|
|
10751
|
+
var import_node_path3 = require("node:path");
|
|
10752
|
+
|
|
10753
|
+
// src/lib/provider-auth.ts
|
|
10754
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10755
|
+
var form = (fields) => new URLSearchParams(fields).toString();
|
|
10756
|
+
async function githubDeviceFlow() {
|
|
10757
|
+
const override = process.env.VERITY_PROVIDER_TOKEN;
|
|
10758
|
+
if (override) return { ok: true, data: override };
|
|
10759
|
+
let dc;
|
|
10760
|
+
try {
|
|
10761
|
+
const res = await fetch(GITHUB_DEVICE_CODE_URL, {
|
|
10762
|
+
method: "POST",
|
|
10763
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10764
|
+
body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_OAUTH_SCOPES })
|
|
10765
|
+
});
|
|
10766
|
+
if (!res.ok) {
|
|
10767
|
+
return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
|
|
10768
|
+
}
|
|
10769
|
+
dc = await res.json();
|
|
10770
|
+
} catch (err) {
|
|
10771
|
+
return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
|
|
10772
|
+
}
|
|
10773
|
+
if (!dc.device_code || !dc.user_code) {
|
|
10774
|
+
return {
|
|
10775
|
+
ok: false,
|
|
10776
|
+
error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
|
|
10777
|
+
};
|
|
10778
|
+
}
|
|
10779
|
+
printInfo("");
|
|
10780
|
+
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
10781
|
+
printInfo(`And enter the code: ${dc.user_code}`);
|
|
10782
|
+
printInfo("Waiting for authorization\u2026");
|
|
10783
|
+
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
10784
|
+
let interval = dc.interval || 5;
|
|
10785
|
+
while (Date.now() < deadline) {
|
|
10786
|
+
await sleep(interval * 1e3);
|
|
10787
|
+
let data;
|
|
10788
|
+
try {
|
|
10789
|
+
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
10790
|
+
method: "POST",
|
|
10791
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10792
|
+
body: form({
|
|
10793
|
+
client_id: GITHUB_CLIENT_ID,
|
|
10794
|
+
device_code: dc.device_code,
|
|
10795
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
10796
|
+
})
|
|
10797
|
+
});
|
|
10798
|
+
data = await res.json().catch(() => ({}));
|
|
10799
|
+
} catch {
|
|
10800
|
+
continue;
|
|
10801
|
+
}
|
|
10802
|
+
if (data.access_token) return { ok: true, data: data.access_token };
|
|
10803
|
+
switch (data.error) {
|
|
10804
|
+
case "authorization_pending":
|
|
10805
|
+
break;
|
|
10806
|
+
case "slow_down":
|
|
10807
|
+
interval += 5;
|
|
10808
|
+
break;
|
|
10809
|
+
case "access_denied":
|
|
10810
|
+
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
10811
|
+
case "expired_token":
|
|
10812
|
+
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
10813
|
+
default:
|
|
10814
|
+
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
10818
|
+
}
|
|
10819
|
+
|
|
10820
|
+
// src/lib/git.ts
|
|
10821
|
+
var import_node_child_process3 = require("node:child_process");
|
|
10822
|
+
var import_node_fs2 = require("node:fs");
|
|
10823
|
+
var import_node_path2 = require("node:path");
|
|
10824
|
+
function resolveFile(relpath) {
|
|
10825
|
+
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10826
|
+
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
10827
|
+
try {
|
|
10828
|
+
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10829
|
+
for (const entry of entries) {
|
|
10830
|
+
if (!entry.isDirectory()) continue;
|
|
10831
|
+
const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
|
|
10832
|
+
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10833
|
+
}
|
|
10834
|
+
} catch {
|
|
10835
|
+
}
|
|
10836
|
+
}
|
|
10837
|
+
return null;
|
|
10838
|
+
}
|
|
10839
|
+
function execGit(cmd) {
|
|
10840
|
+
try {
|
|
10841
|
+
return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10842
|
+
} catch {
|
|
10843
|
+
return "";
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10846
|
+
function splitLines(s) {
|
|
10847
|
+
return s.split("\n").filter((l) => l.length > 0);
|
|
10848
|
+
}
|
|
10849
|
+
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
10850
|
+
function readBaselineSha() {
|
|
10851
|
+
if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
10852
|
+
let sha;
|
|
10853
|
+
try {
|
|
10854
|
+
sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10855
|
+
} catch {
|
|
10856
|
+
return null;
|
|
10857
|
+
}
|
|
10858
|
+
if (!SHA_RE.test(sha)) return null;
|
|
10859
|
+
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10860
|
+
if (!reachable) {
|
|
10861
|
+
try {
|
|
10862
|
+
(0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
|
|
10863
|
+
} catch {
|
|
10864
|
+
}
|
|
10865
|
+
return null;
|
|
10866
|
+
}
|
|
10867
|
+
return sha;
|
|
10868
|
+
}
|
|
10869
|
+
function writeBaselineSha(sha) {
|
|
10870
|
+
if (!SHA_RE.test(sha)) return;
|
|
10871
|
+
try {
|
|
10872
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10873
|
+
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10874
|
+
} catch {
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
function getChangedFiles() {
|
|
10878
|
+
const sets = /* @__PURE__ */ new Set();
|
|
10879
|
+
let hasRecentCommitFiles = false;
|
|
10880
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
10881
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
10882
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
10883
|
+
const baseline = readBaselineSha();
|
|
10884
|
+
if (baseline) {
|
|
10885
|
+
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
10886
|
+
if (committed.length > 0) {
|
|
10887
|
+
hasRecentCommitFiles = true;
|
|
10888
|
+
for (const f of committed) sets.add(f);
|
|
10889
|
+
}
|
|
10890
|
+
} else {
|
|
10891
|
+
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
10892
|
+
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
10893
|
+
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
10894
|
+
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
10895
|
+
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
10896
|
+
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
10897
|
+
if (recentFiles.length > 0) {
|
|
10898
|
+
hasRecentCommitFiles = true;
|
|
10899
|
+
for (const f of recentFiles) sets.add(f);
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
}
|
|
10903
|
+
for (const f of getWorktreeFiles()) sets.add(f);
|
|
10904
|
+
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10905
|
+
return { files: filtered, hasRecentCommitFiles };
|
|
10906
|
+
}
|
|
10907
|
+
function getStagedFiles() {
|
|
10908
|
+
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10909
|
+
}
|
|
10910
|
+
function getDirtyFiles() {
|
|
10911
|
+
const set = /* @__PURE__ */ new Set();
|
|
10912
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
10913
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
10914
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
10915
|
+
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10916
|
+
}
|
|
10917
|
+
function showContentAtRef(ref, repoRelPath) {
|
|
10918
|
+
if (!ref || ref === "no-git") return null;
|
|
10919
|
+
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
10920
|
+
try {
|
|
10921
|
+
return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
10922
|
+
encoding: "utf-8",
|
|
10923
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
10924
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10925
|
+
});
|
|
10926
|
+
} catch {
|
|
10927
|
+
return null;
|
|
10928
|
+
}
|
|
10929
|
+
}
|
|
10930
|
+
function getPushRangeFiles() {
|
|
10931
|
+
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10932
|
+
const resolvers = [
|
|
10933
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
10934
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
10935
|
+
() => {
|
|
10936
|
+
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
10937
|
+
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
10938
|
+
}
|
|
10939
|
+
];
|
|
10940
|
+
for (const resolve of resolvers) {
|
|
10941
|
+
const range = resolve();
|
|
10942
|
+
if (range) return { files: diff(range), range };
|
|
10943
|
+
}
|
|
10944
|
+
const baseline = readBaselineSha();
|
|
10945
|
+
if (baseline) {
|
|
10946
|
+
const files = diff(`${baseline}..HEAD`);
|
|
10947
|
+
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10948
|
+
}
|
|
10949
|
+
const last = diff("HEAD~1..HEAD");
|
|
10950
|
+
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10951
|
+
}
|
|
10952
|
+
function getPushRangeMessages() {
|
|
10953
|
+
const { range } = getPushRangeFiles();
|
|
10954
|
+
if (!range) return "";
|
|
10955
|
+
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10956
|
+
}
|
|
10957
|
+
function getWorktreeFiles() {
|
|
10958
|
+
const result = [];
|
|
10959
|
+
const worktreeDir = ".claude/worktrees";
|
|
10960
|
+
if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
|
|
10961
|
+
try {
|
|
10962
|
+
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
10963
|
+
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
10964
|
+
for (const entry of entries) {
|
|
10965
|
+
if (!entry.isDirectory()) continue;
|
|
10966
|
+
const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
|
|
10967
|
+
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
10968
|
+
}
|
|
10969
|
+
} catch {
|
|
10970
|
+
}
|
|
10971
|
+
return result;
|
|
10972
|
+
}
|
|
10973
|
+
function scanDir(baseDir, dir, minMtime, result) {
|
|
10974
|
+
try {
|
|
10975
|
+
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
10976
|
+
for (const entry of entries) {
|
|
10977
|
+
const fullPath = (0, import_node_path2.join)(dir, entry.name);
|
|
10978
|
+
if (entry.isDirectory()) {
|
|
10979
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
10980
|
+
scanDir(baseDir, fullPath, minMtime, result);
|
|
10981
|
+
} else if (entry.isFile()) {
|
|
10982
|
+
const ext = (0, import_node_path2.extname)(entry.name).slice(1);
|
|
10983
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
10984
|
+
try {
|
|
10985
|
+
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
10986
|
+
if (stat3.mtimeMs >= minMtime) {
|
|
10987
|
+
const relPath = fullPath.slice(baseDir.length + 1);
|
|
10988
|
+
result.push(relPath);
|
|
10989
|
+
}
|
|
10990
|
+
} catch {
|
|
10991
|
+
}
|
|
10992
|
+
}
|
|
10993
|
+
}
|
|
10994
|
+
} catch {
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
function filterAnalyzable(files) {
|
|
10998
|
+
return files.filter((f) => {
|
|
10999
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11000
|
+
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
11001
|
+
});
|
|
11002
|
+
}
|
|
11003
|
+
function filterReviewable(files) {
|
|
11004
|
+
return files.filter((f) => {
|
|
11005
|
+
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11006
|
+
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11007
|
+
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11008
|
+
const basename3 = f.split("/").pop() ?? "";
|
|
11009
|
+
if (REVIEWABLE_FILENAMES.has(basename3)) return true;
|
|
11010
|
+
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
11011
|
+
return false;
|
|
11012
|
+
});
|
|
11013
|
+
}
|
|
11014
|
+
function filterSecurity(files) {
|
|
11015
|
+
return files.filter(
|
|
11016
|
+
(f) => SECURITY_PATTERNS.some((p) => p.test(f))
|
|
11017
|
+
);
|
|
11018
|
+
}
|
|
11019
|
+
function getCurrentCommit() {
|
|
11020
|
+
return execGit("git rev-parse HEAD") || "no-git";
|
|
11021
|
+
}
|
|
11022
|
+
function detectProvider(host) {
|
|
11023
|
+
const h = host.toLowerCase();
|
|
11024
|
+
if (h.includes("github")) return "github";
|
|
11025
|
+
if (h.includes("gitlab")) return "gitlab";
|
|
11026
|
+
if (h.includes("bitbucket")) return "bitbucket";
|
|
11027
|
+
return "unknown";
|
|
11028
|
+
}
|
|
11029
|
+
function parseRemote(raw) {
|
|
11030
|
+
if (!raw || typeof raw !== "string") return null;
|
|
11031
|
+
let s = raw.trim();
|
|
11032
|
+
if (!s) return null;
|
|
11033
|
+
let host = "";
|
|
11034
|
+
let path = "";
|
|
11035
|
+
const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
|
|
11036
|
+
if (scp) {
|
|
11037
|
+
host = scp[1];
|
|
11038
|
+
path = scp[2];
|
|
11039
|
+
} else {
|
|
11040
|
+
s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
|
|
11041
|
+
s = s.replace(/^[^/@]+@/, "");
|
|
11042
|
+
const slash = s.indexOf("/");
|
|
11043
|
+
if (slash === -1) return null;
|
|
11044
|
+
host = s.slice(0, slash);
|
|
11045
|
+
path = s.slice(slash + 1);
|
|
11046
|
+
}
|
|
11047
|
+
host = host.toLowerCase().trim();
|
|
11048
|
+
path = path.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
11049
|
+
if (!host || !path) return null;
|
|
11050
|
+
const segments = path.split("/").filter(Boolean);
|
|
11051
|
+
if (segments.length < 2) return null;
|
|
11052
|
+
const owner = segments[0];
|
|
11053
|
+
const repo = segments[segments.length - 1];
|
|
11054
|
+
if (!owner || !repo) return null;
|
|
11055
|
+
return {
|
|
11056
|
+
host,
|
|
11057
|
+
owner,
|
|
11058
|
+
repo,
|
|
11059
|
+
provider: detectProvider(host),
|
|
11060
|
+
orgUrl: `https://${host}/${owner}`,
|
|
11061
|
+
orgName: owner
|
|
11062
|
+
};
|
|
11063
|
+
}
|
|
11064
|
+
function listTrackedFiles() {
|
|
11065
|
+
const set = /* @__PURE__ */ new Set();
|
|
11066
|
+
for (const f of splitLines(execGit("git ls-files"))) set.add(f);
|
|
11067
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
11068
|
+
return Array.from(set);
|
|
11069
|
+
}
|
|
11070
|
+
|
|
11071
|
+
// src/lib/register.ts
|
|
11072
|
+
async function registerProject(opts) {
|
|
11073
|
+
const parsed = parseRemote(opts.remote);
|
|
11074
|
+
if (!parsed) {
|
|
11075
|
+
return { ok: false, error: `Could not parse git remote: ${opts.remote}` };
|
|
11076
|
+
}
|
|
11077
|
+
if (parsed.provider !== "github") {
|
|
11078
|
+
return { ok: false, error: `Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.` };
|
|
11079
|
+
}
|
|
11080
|
+
const providerAuth = await githubDeviceFlow();
|
|
11081
|
+
if (!providerAuth.ok) {
|
|
11082
|
+
return { ok: false, error: providerAuth.error };
|
|
11083
|
+
}
|
|
11084
|
+
const providerToken = providerAuth.data;
|
|
11085
|
+
const result = await apiRequest({
|
|
11086
|
+
method: "POST",
|
|
11087
|
+
path: "/auth/register",
|
|
11088
|
+
serviceUrl: opts.serviceUrl,
|
|
11089
|
+
body: { project_name: opts.projectName, git_remote_url: opts.remote },
|
|
11090
|
+
extraHeaders: { "X-Provider-Token": providerToken },
|
|
11091
|
+
verbose: opts.verbose
|
|
11092
|
+
});
|
|
11093
|
+
if (!result.ok) {
|
|
11094
|
+
return { ok: false, error: result.error };
|
|
11095
|
+
}
|
|
11096
|
+
const { project_id, token, service_url, user } = result.data;
|
|
11097
|
+
try {
|
|
11098
|
+
await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
|
|
11099
|
+
await (0, import_promises3.writeFile)(
|
|
11100
|
+
CREDENTIALS_FILE,
|
|
11101
|
+
`token: ${token}
|
|
11102
|
+
service_url: ${service_url}
|
|
11103
|
+
provider_token: ${providerToken}
|
|
11104
|
+
`,
|
|
11105
|
+
{ mode: 384 }
|
|
11106
|
+
);
|
|
11107
|
+
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11108
|
+
});
|
|
11109
|
+
} catch (err) {
|
|
11110
|
+
return {
|
|
11111
|
+
ok: false,
|
|
11112
|
+
error: `Registered with the Verity service, but could not save credentials to ${CREDENTIALS_FILE}: ${err.message}. Check filesystem permissions and re-run "verity auth register".`
|
|
11113
|
+
};
|
|
11114
|
+
}
|
|
11115
|
+
try {
|
|
11116
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
11117
|
+
await (0, import_promises3.appendFile)(
|
|
11118
|
+
GLOBAL_CREDENTIALS_FILE,
|
|
11119
|
+
`${opts.remote} token: ${token}
|
|
11120
|
+
${opts.remote} provider_token: ${providerToken}
|
|
11121
|
+
`,
|
|
11122
|
+
{ mode: 384 }
|
|
11123
|
+
);
|
|
11124
|
+
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11125
|
+
});
|
|
11126
|
+
} catch {
|
|
11127
|
+
}
|
|
11128
|
+
return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
|
|
11129
|
+
}
|
|
11130
|
+
|
|
10741
11131
|
// src/commands/auth.ts
|
|
10742
11132
|
function registerAuthCommands(program2) {
|
|
10743
11133
|
const auth = program2.command("auth").description("Manage project authentication");
|
|
@@ -10747,36 +11137,26 @@ function registerAuthCommands(program2) {
|
|
|
10747
11137
|
let remote = opts.remote;
|
|
10748
11138
|
if (!remote) {
|
|
10749
11139
|
try {
|
|
10750
|
-
remote = (0,
|
|
11140
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10751
11141
|
} catch {
|
|
10752
11142
|
printError("No git remote found. Use --remote to specify one.");
|
|
10753
11143
|
process.exit(1);
|
|
10754
11144
|
}
|
|
10755
11145
|
}
|
|
10756
|
-
const result = await
|
|
10757
|
-
|
|
10758
|
-
|
|
11146
|
+
const result = await registerProject({
|
|
11147
|
+
projectName: opts.project,
|
|
11148
|
+
remote,
|
|
10759
11149
|
serviceUrl,
|
|
10760
|
-
body: { project_name: opts.project, git_remote_url: remote },
|
|
10761
11150
|
verbose: globals.verbose
|
|
10762
11151
|
});
|
|
10763
11152
|
if (!result.ok) {
|
|
10764
11153
|
printError(result.error);
|
|
10765
11154
|
process.exit(1);
|
|
10766
11155
|
}
|
|
10767
|
-
const {
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
service_url:
|
|
10771
|
-
`);
|
|
10772
|
-
try {
|
|
10773
|
-
await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
10774
|
-
await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
|
|
10775
|
-
`);
|
|
10776
|
-
} catch {
|
|
10777
|
-
}
|
|
10778
|
-
printInfo(`Project registered: ${project_id}`);
|
|
10779
|
-
printJson({ project_id, service_url });
|
|
11156
|
+
const { projectId, serviceUrl: resolvedUrl, email } = result.data;
|
|
11157
|
+
printInfo(`Project registered: ${projectId}`);
|
|
11158
|
+
if (email) printInfo(`Authenticated as: ${email}`);
|
|
11159
|
+
printJson({ project_id: projectId, service_url: resolvedUrl });
|
|
10780
11160
|
});
|
|
10781
11161
|
auth.command("verify").description("Verify the current token is valid").action(async () => {
|
|
10782
11162
|
const globals = program2.opts();
|
|
@@ -10809,7 +11189,7 @@ service_url: ${service_url}
|
|
|
10809
11189
|
let remote = opts.remote;
|
|
10810
11190
|
if (!remote) {
|
|
10811
11191
|
try {
|
|
10812
|
-
remote = (0,
|
|
11192
|
+
remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
10813
11193
|
} catch {
|
|
10814
11194
|
printError("No git remote found. Use --remote to specify one.");
|
|
10815
11195
|
process.exit(1);
|
|
@@ -10837,7 +11217,7 @@ service_url: ${service_url}
|
|
|
10837
11217
|
|
|
10838
11218
|
// src/lib/hooks.ts
|
|
10839
11219
|
var import_promises4 = require("node:fs/promises");
|
|
10840
|
-
var
|
|
11220
|
+
var import_node_path4 = require("node:path");
|
|
10841
11221
|
var VERITY_STOP_HOOK = {
|
|
10842
11222
|
type: "command",
|
|
10843
11223
|
command: "verity analyze",
|
|
@@ -10848,6 +11228,10 @@ var VERITY_INTENT_HOOK = {
|
|
|
10848
11228
|
type: "command",
|
|
10849
11229
|
command: "verity intent capture"
|
|
10850
11230
|
};
|
|
11231
|
+
var VERITY_BASELINE_HOOK = {
|
|
11232
|
+
type: "command",
|
|
11233
|
+
command: "verity baseline capture"
|
|
11234
|
+
};
|
|
10851
11235
|
var GUARD_TIMEOUT = 300;
|
|
10852
11236
|
function buildGuardHook(on) {
|
|
10853
11237
|
return {
|
|
@@ -10860,9 +11244,13 @@ function buildGuardHook(on) {
|
|
|
10860
11244
|
var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
|
|
10861
11245
|
var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
|
|
10862
11246
|
var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
|
|
11247
|
+
var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
|
|
10863
11248
|
function isVerityGuardHook(entry) {
|
|
10864
11249
|
return VERITY_GUARD_RE.test(entry.command ?? "");
|
|
10865
11250
|
}
|
|
11251
|
+
function isVerityBaselineHook(entry) {
|
|
11252
|
+
return VERITY_BASELINE_RE.test(entry.command ?? "");
|
|
11253
|
+
}
|
|
10866
11254
|
var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
|
|
10867
11255
|
var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
|
|
10868
11256
|
function isVerityStopHook(entry) {
|
|
@@ -10874,7 +11262,7 @@ function isVerityIntentHook(entry) {
|
|
|
10874
11262
|
return VERITY_INTENT_RE.test(c) || LEGACY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh") || c.includes(".gate/hooks/capture-intent.sh");
|
|
10875
11263
|
}
|
|
10876
11264
|
function isVerityHook(entry) {
|
|
10877
|
-
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11265
|
+
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10878
11266
|
}
|
|
10879
11267
|
function isCurrentVerityStopHook(entry) {
|
|
10880
11268
|
const c = entry.command ?? "";
|
|
@@ -10885,7 +11273,7 @@ function isCurrentVerityIntentHook(entry) {
|
|
|
10885
11273
|
return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
|
|
10886
11274
|
}
|
|
10887
11275
|
function isCurrentVerityHook(entry) {
|
|
10888
|
-
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry);
|
|
11276
|
+
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
10889
11277
|
}
|
|
10890
11278
|
function settingsHasLegacyHook(settings) {
|
|
10891
11279
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
@@ -10927,16 +11315,18 @@ async function readAllSettings() {
|
|
|
10927
11315
|
async function checkAllVerityHooks() {
|
|
10928
11316
|
let stop = false;
|
|
10929
11317
|
let intent = false;
|
|
11318
|
+
let baseline = false;
|
|
10930
11319
|
let guard = false;
|
|
10931
11320
|
let guardOn = [];
|
|
10932
11321
|
for (const settings of await readAllSettings()) {
|
|
10933
11322
|
const r = checkVerityHooks(settings);
|
|
10934
11323
|
stop = stop || r.stop;
|
|
10935
11324
|
intent = intent || r.intent;
|
|
11325
|
+
baseline = baseline || r.baseline;
|
|
10936
11326
|
guard = guard || r.guard;
|
|
10937
11327
|
if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
|
|
10938
11328
|
}
|
|
10939
|
-
return { stop, intent, guard, guardOn };
|
|
11329
|
+
return { stop, intent, baseline, guard, guardOn };
|
|
10940
11330
|
}
|
|
10941
11331
|
async function checkExternalVerityHooks() {
|
|
10942
11332
|
let stop = false;
|
|
@@ -10959,12 +11349,14 @@ async function checkExternalVerityHooks() {
|
|
|
10959
11349
|
async function checkAllVerityHooksDetailed() {
|
|
10960
11350
|
let stop = false;
|
|
10961
11351
|
let intent = false;
|
|
11352
|
+
let baseline = false;
|
|
10962
11353
|
let hasCurrent = false;
|
|
10963
11354
|
let hasLegacy = false;
|
|
10964
11355
|
for (const settings of await readAllSettings()) {
|
|
10965
11356
|
const r = checkVerityHooks(settings);
|
|
10966
11357
|
stop = stop || r.stop;
|
|
10967
11358
|
intent = intent || r.intent;
|
|
11359
|
+
baseline = baseline || r.baseline;
|
|
10968
11360
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
10969
11361
|
for (const g of groups ?? []) {
|
|
10970
11362
|
for (const h of g.hooks ?? []) {
|
|
@@ -10974,22 +11366,22 @@ async function checkAllVerityHooksDetailed() {
|
|
|
10974
11366
|
}
|
|
10975
11367
|
}
|
|
10976
11368
|
}
|
|
10977
|
-
return { stop, intent, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
11369
|
+
return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
10978
11370
|
}
|
|
10979
11371
|
async function writeSettings(settings) {
|
|
10980
|
-
await (0, import_promises4.mkdir)((0,
|
|
11372
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
|
|
10981
11373
|
await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
10982
11374
|
}
|
|
10983
11375
|
async function readSettingsAt(root) {
|
|
10984
11376
|
try {
|
|
10985
|
-
return JSON.parse(await (0, import_promises4.readFile)((0,
|
|
11377
|
+
return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
|
|
10986
11378
|
} catch {
|
|
10987
11379
|
return {};
|
|
10988
11380
|
}
|
|
10989
11381
|
}
|
|
10990
11382
|
async function writeSettingsAt(root, settings) {
|
|
10991
|
-
const file = (0,
|
|
10992
|
-
await (0, import_promises4.mkdir)((0,
|
|
11383
|
+
const file = (0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE);
|
|
11384
|
+
await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
|
|
10993
11385
|
await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
10994
11386
|
}
|
|
10995
11387
|
async function hasLegacyHooksAt(root) {
|
|
@@ -11017,6 +11409,10 @@ function checkVerityHooks(settings) {
|
|
|
11017
11409
|
const hasIntent = intentGroups.some(
|
|
11018
11410
|
(g) => g.hooks?.some((h) => isCurrentVerityIntentHook(h))
|
|
11019
11411
|
);
|
|
11412
|
+
const sessionStartGroups = hooks["SessionStart"] ?? [];
|
|
11413
|
+
const hasBaseline = sessionStartGroups.some(
|
|
11414
|
+
(g) => g.hooks?.some((h) => isVerityBaselineHook(h))
|
|
11415
|
+
);
|
|
11020
11416
|
let guard = false;
|
|
11021
11417
|
let guardOn = [];
|
|
11022
11418
|
for (const g of hooks["PreToolUse"] ?? []) {
|
|
@@ -11029,16 +11425,17 @@ function checkVerityHooks(settings) {
|
|
|
11029
11425
|
}
|
|
11030
11426
|
}
|
|
11031
11427
|
}
|
|
11032
|
-
return { stop: hasStop, intent: hasIntent, guard, guardOn };
|
|
11428
|
+
return { stop: hasStop, intent: hasIntent, baseline: hasBaseline, guard, guardOn };
|
|
11033
11429
|
}
|
|
11034
|
-
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false }) {
|
|
11430
|
+
function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false, baseline: false }) {
|
|
11035
11431
|
const local = checkVerityHooks(settings);
|
|
11036
11432
|
const existing = {
|
|
11037
11433
|
stop: local.stop || externalPresent.stop,
|
|
11038
|
-
intent: local.intent || externalPresent.intent
|
|
11434
|
+
intent: local.intent || externalPresent.intent,
|
|
11435
|
+
baseline: local.baseline || (externalPresent.baseline ?? false)
|
|
11039
11436
|
};
|
|
11040
11437
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11041
|
-
if (existing.stop && existing.intent && !force && !hasLocalLegacy) {
|
|
11438
|
+
if (existing.stop && existing.intent && existing.baseline && !force && !hasLocalLegacy) {
|
|
11042
11439
|
return { ok: false, error: "Verity hooks already installed. Use --force to overwrite." };
|
|
11043
11440
|
}
|
|
11044
11441
|
const newSettings = { ...settings };
|
|
@@ -11046,7 +11443,7 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11046
11443
|
newSettings.hooks = {};
|
|
11047
11444
|
}
|
|
11048
11445
|
const shouldStrip = force ? isVerityHook : isLegacyHook;
|
|
11049
|
-
for (const key of ["Stop", "UserPromptSubmit"]) {
|
|
11446
|
+
for (const key of ["Stop", "UserPromptSubmit", "SessionStart"]) {
|
|
11050
11447
|
const groups = newSettings.hooks[key];
|
|
11051
11448
|
if (groups) {
|
|
11052
11449
|
newSettings.hooks[key] = groups.map((g) => ({
|
|
@@ -11073,6 +11470,15 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
|
|
|
11073
11470
|
if (!newSettings.hooks["UserPromptSubmit"]) newSettings.hooks["UserPromptSubmit"] = [];
|
|
11074
11471
|
newSettings.hooks["UserPromptSubmit"].push({ hooks: [VERITY_INTENT_HOOK] });
|
|
11075
11472
|
}
|
|
11473
|
+
if (!existing.baseline) {
|
|
11474
|
+
if (!newSettings.hooks["SessionStart"]) {
|
|
11475
|
+
newSettings.hooks["SessionStart"] = [];
|
|
11476
|
+
}
|
|
11477
|
+
newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
|
|
11478
|
+
} else if (force && local.baseline) {
|
|
11479
|
+
if (!newSettings.hooks["SessionStart"]) newSettings.hooks["SessionStart"] = [];
|
|
11480
|
+
newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
|
|
11481
|
+
}
|
|
11076
11482
|
return { ok: true, data: newSettings };
|
|
11077
11483
|
}
|
|
11078
11484
|
function removeVerityHooks(settings) {
|
|
@@ -11106,6 +11512,7 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
|
|
|
11106
11512
|
(s.hooks[event] ??= []).push(group);
|
|
11107
11513
|
};
|
|
11108
11514
|
if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
|
|
11515
|
+
push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
|
|
11109
11516
|
if (moments.includes("stop") && !externalPresent.stop) {
|
|
11110
11517
|
push("Stop", { hooks: [VERITY_STOP_HOOK] });
|
|
11111
11518
|
}
|
|
@@ -11154,7 +11561,7 @@ function registerHooksCommands(program2) {
|
|
|
11154
11561
|
return;
|
|
11155
11562
|
}
|
|
11156
11563
|
const detail = await checkAllVerityHooksDetailed();
|
|
11157
|
-
const present = { stop: detail.stop, intent: detail.intent };
|
|
11564
|
+
const present = { stop: detail.stop, intent: detail.intent, baseline: detail.baseline };
|
|
11158
11565
|
if (detail.legacyOnly) {
|
|
11159
11566
|
printInfo("Found a legacy GATE.md hook \u2014 upgrading it to Verity...");
|
|
11160
11567
|
const settings2 = removeVerityHooks(await readSettings());
|
|
@@ -11167,11 +11574,12 @@ function registerHooksCommands(program2) {
|
|
|
11167
11574
|
printInfo("Verity hooks installed in .claude/settings.json (legacy hook removed)");
|
|
11168
11575
|
printInfo(" Stop hook: verity analyze");
|
|
11169
11576
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11577
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11170
11578
|
return;
|
|
11171
11579
|
}
|
|
11172
11580
|
const settings = await readSettings();
|
|
11173
11581
|
const hasLocalLegacy = settingsHasLegacyHook(settings);
|
|
11174
|
-
if (!force && present.stop && present.intent && !hasLocalLegacy) {
|
|
11582
|
+
if (!force && present.stop && present.intent && present.baseline && !hasLocalLegacy) {
|
|
11175
11583
|
printInfo("Verity hooks already installed (found in .claude/settings.json, settings.local.json, or your global settings).");
|
|
11176
11584
|
printInfo("Use --force to rewrite the project settings.json copy.");
|
|
11177
11585
|
return;
|
|
@@ -11185,11 +11593,13 @@ function registerHooksCommands(program2) {
|
|
|
11185
11593
|
printInfo("Verity hooks installed in .claude/settings.json");
|
|
11186
11594
|
printInfo(" Stop hook: verity analyze");
|
|
11187
11595
|
printInfo(" UserPromptSubmit hook: verity intent capture");
|
|
11596
|
+
printInfo(" SessionStart hook: verity baseline capture");
|
|
11188
11597
|
});
|
|
11189
11598
|
hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
|
|
11190
11599
|
const status = await checkAllVerityHooks();
|
|
11191
11600
|
printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
|
|
11192
11601
|
printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
|
|
11602
|
+
printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
|
|
11193
11603
|
const gates = status.guard ? status.guardOn.join(", ") : "none";
|
|
11194
11604
|
printInfo(`Git-moment gate (verity guard): ${status.guard ? `installed [${gates}]` : "not installed"}`);
|
|
11195
11605
|
if (!status.stop && !status.guard) {
|
|
@@ -11208,15 +11618,19 @@ function registerHooksCommands(program2) {
|
|
|
11208
11618
|
}
|
|
11209
11619
|
|
|
11210
11620
|
// src/commands/intent.ts
|
|
11211
|
-
var
|
|
11621
|
+
var import_node_crypto3 = require("node:crypto");
|
|
11212
11622
|
|
|
11213
11623
|
// src/lib/conversation-buffer.ts
|
|
11214
11624
|
var import_promises5 = require("node:fs/promises");
|
|
11215
|
-
var
|
|
11216
|
-
var
|
|
11625
|
+
var import_node_fs3 = require("node:fs");
|
|
11626
|
+
var import_node_child_process5 = require("node:child_process");
|
|
11627
|
+
var import_node_crypto = require("node:crypto");
|
|
11217
11628
|
function stripImageReferences(text) {
|
|
11218
11629
|
return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
|
|
11219
11630
|
}
|
|
11631
|
+
function bufferTmpPath() {
|
|
11632
|
+
return `${CONVERSATION_BUFFER_FILE}.${process.pid}.${(0, import_node_crypto.randomBytes)(4).toString("hex")}.tmp`;
|
|
11633
|
+
}
|
|
11220
11634
|
async function appendToConversationBuffer(prompt, sessionId) {
|
|
11221
11635
|
try {
|
|
11222
11636
|
await (0, import_promises5.mkdir)(VERITY_DIR, { recursive: true });
|
|
@@ -11236,26 +11650,39 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11236
11650
|
recent.push(entry);
|
|
11237
11651
|
const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
|
|
11238
11652
|
const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11239
|
-
const tmpFile =
|
|
11653
|
+
const tmpFile = bufferTmpPath();
|
|
11240
11654
|
await (0, import_promises5.writeFile)(tmpFile, content);
|
|
11241
11655
|
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11242
11656
|
} catch {
|
|
11243
11657
|
}
|
|
11244
11658
|
}
|
|
11245
|
-
async function readAndClearConversationBuffer() {
|
|
11659
|
+
async function readAndClearConversationBuffer(currentSessionId) {
|
|
11246
11660
|
try {
|
|
11247
|
-
if ((0,
|
|
11661
|
+
if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
11248
11662
|
const entries = await readBufferEntries();
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
if (
|
|
11663
|
+
let mine = entries;
|
|
11664
|
+
let others = [];
|
|
11665
|
+
if (currentSessionId) {
|
|
11666
|
+
mine = entries.filter((e) => e.session_id === currentSessionId || !e.session_id);
|
|
11667
|
+
others = entries.filter((e) => e.session_id && e.session_id !== currentSessionId);
|
|
11668
|
+
}
|
|
11669
|
+
if (others.length > 0) {
|
|
11670
|
+
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11671
|
+
const tmpFile = bufferTmpPath();
|
|
11672
|
+
await (0, import_promises5.writeFile)(tmpFile, remaining);
|
|
11673
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11674
|
+
} else {
|
|
11675
|
+
await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11676
|
+
});
|
|
11677
|
+
}
|
|
11678
|
+
if (mine.length > 0) {
|
|
11252
11679
|
return {
|
|
11253
|
-
prompts:
|
|
11680
|
+
prompts: mine,
|
|
11254
11681
|
recent_commits: getRecentCommitMessages()
|
|
11255
11682
|
};
|
|
11256
11683
|
}
|
|
11257
11684
|
}
|
|
11258
|
-
if ((0,
|
|
11685
|
+
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11259
11686
|
try {
|
|
11260
11687
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
11261
11688
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -11299,7 +11726,7 @@ async function readBufferEntries() {
|
|
|
11299
11726
|
}
|
|
11300
11727
|
function getRecentCommitMessages() {
|
|
11301
11728
|
try {
|
|
11302
|
-
const output = (0,
|
|
11729
|
+
const output = (0, import_node_child_process5.execSync)(
|
|
11303
11730
|
'git log --since="30 minutes ago" --format="%s" -5',
|
|
11304
11731
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
11305
11732
|
).trim();
|
|
@@ -11312,8 +11739,8 @@ function getRecentCommitMessages() {
|
|
|
11312
11739
|
|
|
11313
11740
|
// src/lib/task-context-buffer.ts
|
|
11314
11741
|
var import_promises6 = require("node:fs/promises");
|
|
11315
|
-
var
|
|
11316
|
-
var
|
|
11742
|
+
var import_node_fs4 = require("node:fs");
|
|
11743
|
+
var import_node_path5 = require("node:path");
|
|
11317
11744
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
11318
11745
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
11319
11746
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -11352,7 +11779,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
11352
11779
|
}
|
|
11353
11780
|
async function readTaskContextBuffer(taskId) {
|
|
11354
11781
|
const filePath = bufferPath(taskId);
|
|
11355
|
-
if (!(0,
|
|
11782
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
11356
11783
|
try {
|
|
11357
11784
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
11358
11785
|
if (!content.trim()) return null;
|
|
@@ -11386,12 +11813,12 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11386
11813
|
}
|
|
11387
11814
|
async function cleanupTaskContextBuffers() {
|
|
11388
11815
|
try {
|
|
11389
|
-
if (!(0,
|
|
11816
|
+
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
11390
11817
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
11391
11818
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
11392
11819
|
for (const file of files) {
|
|
11393
11820
|
if (!file.endsWith(".jsonl")) continue;
|
|
11394
|
-
const filePath = (0,
|
|
11821
|
+
const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
|
|
11395
11822
|
try {
|
|
11396
11823
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11397
11824
|
if (stats.mtimeMs < cutoffMs) {
|
|
@@ -11405,13 +11832,13 @@ async function cleanupTaskContextBuffers() {
|
|
|
11405
11832
|
}
|
|
11406
11833
|
function bufferPath(taskId) {
|
|
11407
11834
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
11408
|
-
return (0,
|
|
11835
|
+
return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
11409
11836
|
}
|
|
11410
11837
|
async function appendEntry(taskId, entry) {
|
|
11411
11838
|
try {
|
|
11412
11839
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
11413
11840
|
const filePath = bufferPath(taskId);
|
|
11414
|
-
if ((0,
|
|
11841
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
11415
11842
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11416
11843
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
11417
11844
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -11422,7 +11849,7 @@ async function appendEntry(taskId, entry) {
|
|
|
11422
11849
|
}
|
|
11423
11850
|
}
|
|
11424
11851
|
const line = JSON.stringify(entry) + "\n";
|
|
11425
|
-
const existing = (0,
|
|
11852
|
+
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
11426
11853
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
11427
11854
|
} catch {
|
|
11428
11855
|
}
|
|
@@ -11430,8 +11857,8 @@ async function appendEntry(taskId, entry) {
|
|
|
11430
11857
|
|
|
11431
11858
|
// src/lib/memory-retrieval.ts
|
|
11432
11859
|
var import_promises7 = require("node:fs/promises");
|
|
11433
|
-
var
|
|
11434
|
-
var
|
|
11860
|
+
var import_node_fs5 = require("node:fs");
|
|
11861
|
+
var import_node_path6 = require("node:path");
|
|
11435
11862
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
11436
11863
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
11437
11864
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
@@ -11524,19 +11951,19 @@ function parseFrontmatter(content) {
|
|
|
11524
11951
|
return { fm, body: match[2].trim() };
|
|
11525
11952
|
}
|
|
11526
11953
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
11527
|
-
if (!(0,
|
|
11954
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
|
|
11528
11955
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
11529
11956
|
const promptTokens = tokenize(promptText);
|
|
11530
11957
|
const nodes = [];
|
|
11531
11958
|
for (const domain of DOMAINS) {
|
|
11532
|
-
const domainDir = (0,
|
|
11533
|
-
if (!(0,
|
|
11959
|
+
const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
|
|
11960
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
11534
11961
|
try {
|
|
11535
11962
|
const files = await (0, import_promises7.readdir)(domainDir);
|
|
11536
11963
|
for (const file of files) {
|
|
11537
11964
|
if (!file.endsWith(".md")) continue;
|
|
11538
11965
|
try {
|
|
11539
|
-
const content = await (0, import_promises7.readFile)((0,
|
|
11966
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
|
|
11540
11967
|
const { fm, body } = parseFrontmatter(content);
|
|
11541
11968
|
if (fm.status && fm.status !== "active") continue;
|
|
11542
11969
|
nodes.push({
|
|
@@ -11595,9 +12022,9 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
11595
12022
|
|
|
11596
12023
|
// src/lib/memory-sync.ts
|
|
11597
12024
|
var import_promises8 = require("node:fs/promises");
|
|
11598
|
-
var
|
|
11599
|
-
var
|
|
11600
|
-
var
|
|
12025
|
+
var import_node_fs6 = require("node:fs");
|
|
12026
|
+
var import_node_path7 = require("node:path");
|
|
12027
|
+
var import_node_crypto2 = require("node:crypto");
|
|
11601
12028
|
|
|
11602
12029
|
// src/lib/glob-match.ts
|
|
11603
12030
|
function globToRegex(glob) {
|
|
@@ -11667,35 +12094,35 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
|
11667
12094
|
async function ensureMemoryDir() {
|
|
11668
12095
|
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
11669
12096
|
for (const domain of DOMAINS2) {
|
|
11670
|
-
await (0, import_promises8.mkdir)((0,
|
|
12097
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
|
|
11671
12098
|
}
|
|
11672
|
-
if (!(0,
|
|
11673
|
-
await (0, import_promises8.writeFile)((0,
|
|
12099
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12100
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
11674
12101
|
}
|
|
11675
|
-
if (!(0,
|
|
11676
|
-
await (0, import_promises8.writeFile)((0,
|
|
12102
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
|
|
12103
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
11677
12104
|
}
|
|
11678
|
-
if (!(0,
|
|
11679
|
-
await (0, import_promises8.writeFile)((0,
|
|
12105
|
+
if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
|
|
12106
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
11680
12107
|
}
|
|
11681
12108
|
}
|
|
11682
12109
|
async function buildManifest() {
|
|
11683
|
-
if (!(0,
|
|
12110
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
|
|
11684
12111
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
11685
12112
|
}
|
|
11686
12113
|
const nodes = [];
|
|
11687
12114
|
for (const domain of DOMAINS2) {
|
|
11688
|
-
const domainDir = (0,
|
|
11689
|
-
if (!(0,
|
|
12115
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12116
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11690
12117
|
try {
|
|
11691
12118
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11692
12119
|
for (const file of files) {
|
|
11693
12120
|
if (!file.endsWith(".md")) continue;
|
|
11694
12121
|
const filePath = `${domain}/${file}`;
|
|
11695
|
-
const fullPath = (0,
|
|
12122
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
|
|
11696
12123
|
try {
|
|
11697
12124
|
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
11698
|
-
const hash = (0,
|
|
12125
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
11699
12126
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
11700
12127
|
} catch {
|
|
11701
12128
|
}
|
|
@@ -11705,32 +12132,32 @@ async function buildManifest() {
|
|
|
11705
12132
|
}
|
|
11706
12133
|
let indexHash = null;
|
|
11707
12134
|
try {
|
|
11708
|
-
const indexContent = await (0, import_promises8.readFile)((0,
|
|
11709
|
-
indexHash = `sha256:${(0,
|
|
12135
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
|
|
12136
|
+
indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
11710
12137
|
} catch {
|
|
11711
12138
|
}
|
|
11712
12139
|
let logLength = 0;
|
|
11713
12140
|
try {
|
|
11714
|
-
const logContent = await (0, import_promises8.readFile)((0,
|
|
12141
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
|
|
11715
12142
|
logLength = logContent.split("\n").length;
|
|
11716
12143
|
} catch {
|
|
11717
12144
|
}
|
|
11718
12145
|
return { schema_version: 1, nodes, index_hash: indexHash, log_length: logLength };
|
|
11719
12146
|
}
|
|
11720
12147
|
function hashContent(content) {
|
|
11721
|
-
return `sha256:${(0,
|
|
12148
|
+
return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
11722
12149
|
}
|
|
11723
12150
|
async function readOnDiskNodes() {
|
|
11724
12151
|
const out = /* @__PURE__ */ new Map();
|
|
11725
|
-
if (!(0,
|
|
12152
|
+
if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
|
|
11726
12153
|
for (const domain of DOMAINS2) {
|
|
11727
|
-
const domainDir = (0,
|
|
11728
|
-
if (!(0,
|
|
12154
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12155
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11729
12156
|
try {
|
|
11730
12157
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
11731
12158
|
if (!file.endsWith(".md")) continue;
|
|
11732
12159
|
try {
|
|
11733
|
-
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0,
|
|
12160
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
|
|
11734
12161
|
} catch {
|
|
11735
12162
|
}
|
|
11736
12163
|
}
|
|
@@ -11776,8 +12203,8 @@ async function computeEditedNodeUploads() {
|
|
|
11776
12203
|
const uploads = [];
|
|
11777
12204
|
for (const [path, prevHash] of prev) {
|
|
11778
12205
|
if (prevHash == null) continue;
|
|
11779
|
-
const full = (0,
|
|
11780
|
-
if (!(0,
|
|
12206
|
+
const full = (0, import_node_path7.join)(memoryDir2(), path);
|
|
12207
|
+
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
11781
12208
|
let content;
|
|
11782
12209
|
try {
|
|
11783
12210
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -11813,15 +12240,15 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
11813
12240
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
11814
12241
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
11815
12242
|
try {
|
|
11816
|
-
const existing = (0,
|
|
11817
|
-
await (0, import_promises8.writeFile)((0,
|
|
12243
|
+
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";
|
|
12244
|
+
await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
11818
12245
|
} catch {
|
|
11819
12246
|
}
|
|
11820
12247
|
await recordSyncedNodePaths();
|
|
11821
12248
|
return count;
|
|
11822
12249
|
}
|
|
11823
12250
|
async function applyOneWrite(write, treePaths) {
|
|
11824
|
-
const fullPath = (0,
|
|
12251
|
+
const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
|
|
11825
12252
|
const notes = [];
|
|
11826
12253
|
let content = write.content;
|
|
11827
12254
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -11831,7 +12258,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11831
12258
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
11832
12259
|
}
|
|
11833
12260
|
}
|
|
11834
|
-
if ((0,
|
|
12261
|
+
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
11835
12262
|
let existing = "";
|
|
11836
12263
|
try {
|
|
11837
12264
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -11843,7 +12270,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
11843
12270
|
return { written: false, notes };
|
|
11844
12271
|
}
|
|
11845
12272
|
}
|
|
11846
|
-
await (0, import_promises8.mkdir)((0,
|
|
12273
|
+
await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
|
|
11847
12274
|
await (0, import_promises8.writeFile)(fullPath, content);
|
|
11848
12275
|
return { written: true, notes };
|
|
11849
12276
|
}
|
|
@@ -11884,8 +12311,8 @@ async function regenerateIndex() {
|
|
|
11884
12311
|
];
|
|
11885
12312
|
let totalNodes = 0;
|
|
11886
12313
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
11887
|
-
const domainDir = (0,
|
|
11888
|
-
if (!(0,
|
|
12314
|
+
const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
|
|
12315
|
+
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
11889
12316
|
try {
|
|
11890
12317
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
11891
12318
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -11895,7 +12322,7 @@ async function regenerateIndex() {
|
|
|
11895
12322
|
for (const file of mdFiles.sort()) {
|
|
11896
12323
|
const slug = file.replace(/\.md$/, "");
|
|
11897
12324
|
try {
|
|
11898
|
-
const content = await (0, import_promises8.readFile)((0,
|
|
12325
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
|
|
11899
12326
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
11900
12327
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
11901
12328
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -11919,7 +12346,7 @@ async function regenerateIndex() {
|
|
|
11919
12346
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
11920
12347
|
}
|
|
11921
12348
|
const next = lines.join("\n") + "\n";
|
|
11922
|
-
const indexPath = (0,
|
|
12349
|
+
const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
|
|
11923
12350
|
let existing = null;
|
|
11924
12351
|
try {
|
|
11925
12352
|
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
@@ -12001,9 +12428,9 @@ function hasLegacyMemoryBlock(text) {
|
|
|
12001
12428
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
12002
12429
|
}
|
|
12003
12430
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
12004
|
-
const claudeMdPath = (0,
|
|
12431
|
+
const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
|
|
12005
12432
|
let existing = "";
|
|
12006
|
-
if ((0,
|
|
12433
|
+
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12007
12434
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12008
12435
|
}
|
|
12009
12436
|
let startTag = CLAUDE_MD_START;
|
|
@@ -12133,7 +12560,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
12133
12560
|
`;
|
|
12134
12561
|
|
|
12135
12562
|
// src/commands/intent.ts
|
|
12136
|
-
var
|
|
12563
|
+
var import_node_fs7 = require("node:fs");
|
|
12137
12564
|
function registerIntentCommands(program2) {
|
|
12138
12565
|
const intent = program2.command("intent").description("Manage intent capture");
|
|
12139
12566
|
intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
|
|
@@ -12142,7 +12569,7 @@ function registerIntentCommands(program2) {
|
|
|
12142
12569
|
process.chdir(repoRoot());
|
|
12143
12570
|
} catch {
|
|
12144
12571
|
}
|
|
12145
|
-
if (!(0,
|
|
12572
|
+
if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
|
|
12146
12573
|
process.exit(0);
|
|
12147
12574
|
}
|
|
12148
12575
|
const chunks = [];
|
|
@@ -12205,7 +12632,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12205
12632
|
logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
|
|
12206
12633
|
return;
|
|
12207
12634
|
}
|
|
12208
|
-
const promptHash = (0,
|
|
12635
|
+
const promptHash = (0, import_node_crypto3.createHash)("sha256").update(prompt).digest("hex");
|
|
12209
12636
|
const result = await apiRequest({
|
|
12210
12637
|
method: "POST",
|
|
12211
12638
|
path: "/classify-task",
|
|
@@ -12673,243 +13100,54 @@ function registerFeedbackCommand(program2) {
|
|
|
12673
13100
|
if (!result.ok) {
|
|
12674
13101
|
printError(`Couldn't submit finding feedback: ${result.error}`);
|
|
12675
13102
|
process.exit(1);
|
|
12676
|
-
}
|
|
12677
|
-
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
12678
|
-
printInfo(status);
|
|
12679
|
-
});
|
|
12680
|
-
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
12681
|
-
if (!message) return;
|
|
12682
|
-
const globals = program2.opts();
|
|
12683
|
-
await sendGeneralFeedback(message, opts, globals);
|
|
12684
|
-
});
|
|
12685
|
-
}
|
|
12686
|
-
async function sendGeneralFeedback(message, opts, globals) {
|
|
12687
|
-
const tokenResult = await resolveToken(globals.token);
|
|
12688
|
-
if (!tokenResult.ok) {
|
|
12689
|
-
printError(tokenResult.error);
|
|
12690
|
-
printInfo(`Your message: ${message}`);
|
|
12691
|
-
process.exit(1);
|
|
12692
|
-
}
|
|
12693
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
12694
|
-
if (!urlResult.ok) {
|
|
12695
|
-
printError(urlResult.error);
|
|
12696
|
-
printInfo(`Your message: ${message}`);
|
|
12697
|
-
process.exit(1);
|
|
12698
|
-
}
|
|
12699
|
-
const body = { message };
|
|
12700
|
-
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
12701
|
-
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
12702
|
-
if (sessionId) body.session_id = sessionId;
|
|
12703
|
-
if (model) body.agent_model = model;
|
|
12704
|
-
const result = await apiRequest({
|
|
12705
|
-
method: "POST",
|
|
12706
|
-
path: "/feedback",
|
|
12707
|
-
serviceUrl: urlResult.data,
|
|
12708
|
-
token: tokenResult.data.token,
|
|
12709
|
-
body,
|
|
12710
|
-
verbose: globals.verbose
|
|
12711
|
-
});
|
|
12712
|
-
if (!result.ok) {
|
|
12713
|
-
printError(`Couldn't send feedback: ${result.error}`);
|
|
12714
|
-
printInfo(`Your message: ${message}`);
|
|
12715
|
-
process.exit(1);
|
|
12716
|
-
}
|
|
12717
|
-
printInfo("Thanks, feedback sent!");
|
|
12718
|
-
}
|
|
12719
|
-
|
|
12720
|
-
// src/commands/analyze.ts
|
|
12721
|
-
var import_node_fs18 = require("node:fs");
|
|
12722
|
-
var import_node_path13 = require("node:path");
|
|
12723
|
-
|
|
12724
|
-
// src/lib/git.ts
|
|
12725
|
-
var import_node_child_process5 = require("node:child_process");
|
|
12726
|
-
var import_node_fs7 = require("node:fs");
|
|
12727
|
-
var import_node_path7 = require("node:path");
|
|
12728
|
-
function resolveFile(relpath) {
|
|
12729
|
-
if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
|
|
12730
|
-
if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
|
|
12731
|
-
try {
|
|
12732
|
-
const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
12733
|
-
for (const entry of entries) {
|
|
12734
|
-
if (!entry.isDirectory()) continue;
|
|
12735
|
-
const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
|
|
12736
|
-
if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
|
|
12737
|
-
}
|
|
12738
|
-
} catch {
|
|
12739
|
-
}
|
|
12740
|
-
}
|
|
12741
|
-
return null;
|
|
12742
|
-
}
|
|
12743
|
-
function execGit(cmd) {
|
|
12744
|
-
try {
|
|
12745
|
-
return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12746
|
-
} catch {
|
|
12747
|
-
return "";
|
|
12748
|
-
}
|
|
12749
|
-
}
|
|
12750
|
-
function splitLines(s) {
|
|
12751
|
-
return s.split("\n").filter((l) => l.length > 0);
|
|
12752
|
-
}
|
|
12753
|
-
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
12754
|
-
function readBaselineSha() {
|
|
12755
|
-
if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
12756
|
-
let sha;
|
|
12757
|
-
try {
|
|
12758
|
-
sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
12759
|
-
} catch {
|
|
12760
|
-
return null;
|
|
12761
|
-
}
|
|
12762
|
-
if (!SHA_RE.test(sha)) return null;
|
|
12763
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
12764
|
-
if (!reachable) {
|
|
12765
|
-
try {
|
|
12766
|
-
(0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
|
|
12767
|
-
} catch {
|
|
12768
|
-
}
|
|
12769
|
-
return null;
|
|
12770
|
-
}
|
|
12771
|
-
return sha;
|
|
12772
|
-
}
|
|
12773
|
-
function writeBaselineSha(sha) {
|
|
12774
|
-
if (!SHA_RE.test(sha)) return;
|
|
12775
|
-
try {
|
|
12776
|
-
(0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
12777
|
-
(0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
12778
|
-
} catch {
|
|
12779
|
-
}
|
|
12780
|
-
}
|
|
12781
|
-
function getChangedFiles() {
|
|
12782
|
-
const sets = /* @__PURE__ */ new Set();
|
|
12783
|
-
let hasRecentCommitFiles = false;
|
|
12784
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
12785
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
12786
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
12787
|
-
const baseline = readBaselineSha();
|
|
12788
|
-
if (baseline) {
|
|
12789
|
-
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
12790
|
-
if (committed.length > 0) {
|
|
12791
|
-
hasRecentCommitFiles = true;
|
|
12792
|
-
for (const f of committed) sets.add(f);
|
|
12793
|
-
}
|
|
12794
|
-
} else {
|
|
12795
|
-
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
12796
|
-
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
12797
|
-
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
12798
|
-
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
12799
|
-
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
12800
|
-
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
12801
|
-
if (recentFiles.length > 0) {
|
|
12802
|
-
hasRecentCommitFiles = true;
|
|
12803
|
-
for (const f of recentFiles) sets.add(f);
|
|
12804
|
-
}
|
|
12805
|
-
}
|
|
12806
|
-
}
|
|
12807
|
-
for (const f of getWorktreeFiles()) sets.add(f);
|
|
12808
|
-
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12809
|
-
return { files: filtered, hasRecentCommitFiles };
|
|
12810
|
-
}
|
|
12811
|
-
function getStagedFiles() {
|
|
12812
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12813
|
-
}
|
|
12814
|
-
function getPushRangeFiles() {
|
|
12815
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12816
|
-
const resolvers = [
|
|
12817
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
12818
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
12819
|
-
() => {
|
|
12820
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
12821
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
12822
|
-
}
|
|
12823
|
-
];
|
|
12824
|
-
for (const resolve of resolvers) {
|
|
12825
|
-
const range = resolve();
|
|
12826
|
-
if (range) return { files: diff(range), range };
|
|
12827
|
-
}
|
|
12828
|
-
const baseline = readBaselineSha();
|
|
12829
|
-
if (baseline) {
|
|
12830
|
-
const files = diff(`${baseline}..HEAD`);
|
|
12831
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
12832
|
-
}
|
|
12833
|
-
const last = diff("HEAD~1..HEAD");
|
|
12834
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
12835
|
-
}
|
|
12836
|
-
function getPushRangeMessages() {
|
|
12837
|
-
const { range } = getPushRangeFiles();
|
|
12838
|
-
if (!range) return "";
|
|
12839
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
12840
|
-
}
|
|
12841
|
-
function getWorktreeFiles() {
|
|
12842
|
-
const result = [];
|
|
12843
|
-
const worktreeDir = ".claude/worktrees";
|
|
12844
|
-
if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
|
|
12845
|
-
try {
|
|
12846
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
12847
|
-
const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
12848
|
-
for (const entry of entries) {
|
|
12849
|
-
if (!entry.isDirectory()) continue;
|
|
12850
|
-
const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
|
|
12851
|
-
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
12852
|
-
}
|
|
12853
|
-
} catch {
|
|
12854
|
-
}
|
|
12855
|
-
return result;
|
|
12856
|
-
}
|
|
12857
|
-
function scanDir(baseDir, dir, minMtime, result) {
|
|
12858
|
-
try {
|
|
12859
|
-
const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
|
|
12860
|
-
for (const entry of entries) {
|
|
12861
|
-
const fullPath = (0, import_node_path7.join)(dir, entry.name);
|
|
12862
|
-
if (entry.isDirectory()) {
|
|
12863
|
-
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12864
|
-
scanDir(baseDir, fullPath, minMtime, result);
|
|
12865
|
-
} else if (entry.isFile()) {
|
|
12866
|
-
const ext = (0, import_node_path7.extname)(entry.name).slice(1);
|
|
12867
|
-
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
12868
|
-
try {
|
|
12869
|
-
const stat3 = (0, import_node_fs7.statSync)(fullPath);
|
|
12870
|
-
if (stat3.mtimeMs >= minMtime) {
|
|
12871
|
-
const relPath = fullPath.slice(baseDir.length + 1);
|
|
12872
|
-
result.push(relPath);
|
|
12873
|
-
}
|
|
12874
|
-
} catch {
|
|
12875
|
-
}
|
|
12876
|
-
}
|
|
12877
|
-
}
|
|
12878
|
-
} catch {
|
|
12879
|
-
}
|
|
12880
|
-
}
|
|
12881
|
-
function filterAnalyzable(files) {
|
|
12882
|
-
return files.filter((f) => {
|
|
12883
|
-
const ext = (0, import_node_path7.extname)(f).slice(1);
|
|
12884
|
-
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
13103
|
+
}
|
|
13104
|
+
const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
|
|
13105
|
+
printInfo(status);
|
|
12885
13106
|
});
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
12891
|
-
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
12892
|
-
const basename2 = f.split("/").pop() ?? "";
|
|
12893
|
-
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
12894
|
-
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
12895
|
-
return false;
|
|
13107
|
+
feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
|
|
13108
|
+
if (!message) return;
|
|
13109
|
+
const globals = program2.opts();
|
|
13110
|
+
await sendGeneralFeedback(message, opts, globals);
|
|
12896
13111
|
});
|
|
12897
13112
|
}
|
|
12898
|
-
function
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
}
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
13113
|
+
async function sendGeneralFeedback(message, opts, globals) {
|
|
13114
|
+
const tokenResult = await resolveToken(globals.token);
|
|
13115
|
+
if (!tokenResult.ok) {
|
|
13116
|
+
printError(tokenResult.error);
|
|
13117
|
+
printInfo(`Your message: ${message}`);
|
|
13118
|
+
process.exit(1);
|
|
13119
|
+
}
|
|
13120
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
13121
|
+
if (!urlResult.ok) {
|
|
13122
|
+
printError(urlResult.error);
|
|
13123
|
+
printInfo(`Your message: ${message}`);
|
|
13124
|
+
process.exit(1);
|
|
13125
|
+
}
|
|
13126
|
+
const body = { message };
|
|
13127
|
+
const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
|
|
13128
|
+
const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
|
|
13129
|
+
if (sessionId) body.session_id = sessionId;
|
|
13130
|
+
if (model) body.agent_model = model;
|
|
13131
|
+
const result = await apiRequest({
|
|
13132
|
+
method: "POST",
|
|
13133
|
+
path: "/feedback",
|
|
13134
|
+
serviceUrl: urlResult.data,
|
|
13135
|
+
token: tokenResult.data.token,
|
|
13136
|
+
body,
|
|
13137
|
+
verbose: globals.verbose
|
|
13138
|
+
});
|
|
13139
|
+
if (!result.ok) {
|
|
13140
|
+
printError(`Couldn't send feedback: ${result.error}`);
|
|
13141
|
+
printInfo(`Your message: ${message}`);
|
|
13142
|
+
process.exit(1);
|
|
13143
|
+
}
|
|
13144
|
+
printInfo("Thanks, feedback sent!");
|
|
12911
13145
|
}
|
|
12912
13146
|
|
|
13147
|
+
// src/commands/analyze.ts
|
|
13148
|
+
var import_node_fs19 = require("node:fs");
|
|
13149
|
+
var import_node_path14 = require("node:path");
|
|
13150
|
+
|
|
12913
13151
|
// src/lib/files.ts
|
|
12914
13152
|
var import_node_fs8 = require("node:fs");
|
|
12915
13153
|
var import_node_path8 = require("node:path");
|
|
@@ -13042,11 +13280,16 @@ function collectCodeDelta(files, opts) {
|
|
|
13042
13280
|
|
|
13043
13281
|
// src/lib/debounce.ts
|
|
13044
13282
|
var import_node_fs9 = require("node:fs");
|
|
13045
|
-
var
|
|
13046
|
-
function
|
|
13047
|
-
if (!
|
|
13283
|
+
var import_node_crypto4 = require("node:crypto");
|
|
13284
|
+
function scopedFile(base, sessionId) {
|
|
13285
|
+
if (!sessionId) return base;
|
|
13286
|
+
return `${base}.${(0, import_node_crypto4.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
|
|
13287
|
+
}
|
|
13288
|
+
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
13289
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13290
|
+
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13048
13291
|
try {
|
|
13049
|
-
const lastTs = parseInt((0, import_node_fs9.readFileSync)(
|
|
13292
|
+
const lastTs = parseInt((0, import_node_fs9.readFileSync)(file, "utf-8").trim(), 10);
|
|
13050
13293
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
13051
13294
|
const elapsed = nowTs - lastTs;
|
|
13052
13295
|
if (elapsed < debounceSeconds) {
|
|
@@ -13056,12 +13299,13 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
|
|
|
13056
13299
|
}
|
|
13057
13300
|
return null;
|
|
13058
13301
|
}
|
|
13059
|
-
function checkMtime(files, bypassForRecentCommits) {
|
|
13302
|
+
function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
13060
13303
|
if (bypassForRecentCommits) return null;
|
|
13061
|
-
|
|
13304
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13305
|
+
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13062
13306
|
let debounceTime;
|
|
13063
13307
|
try {
|
|
13064
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13308
|
+
debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
|
|
13065
13309
|
} catch {
|
|
13066
13310
|
return null;
|
|
13067
13311
|
}
|
|
@@ -13080,7 +13324,7 @@ function checkMtime(files, bypassForRecentCommits) {
|
|
|
13080
13324
|
return "No files modified since last analysis";
|
|
13081
13325
|
}
|
|
13082
13326
|
function computeContentHash(files) {
|
|
13083
|
-
const hash = (0,
|
|
13327
|
+
const hash = (0, import_node_crypto4.createHash)("sha1");
|
|
13084
13328
|
const sorted = [...files].sort();
|
|
13085
13329
|
for (const f of sorted) {
|
|
13086
13330
|
const resolved = resolveFile(f) ?? f;
|
|
@@ -13093,11 +13337,12 @@ function computeContentHash(files) {
|
|
|
13093
13337
|
}
|
|
13094
13338
|
return hash.digest("hex");
|
|
13095
13339
|
}
|
|
13096
|
-
function checkContentHash(files) {
|
|
13340
|
+
function checkContentHash(files, sessionId) {
|
|
13097
13341
|
const hash = computeContentHash(files);
|
|
13098
|
-
|
|
13342
|
+
const file = scopedFile(HASH_FILE, sessionId);
|
|
13343
|
+
if ((0, import_node_fs9.existsSync)(file)) {
|
|
13099
13344
|
try {
|
|
13100
|
-
const storedHash = (0, import_node_fs9.readFileSync)(
|
|
13345
|
+
const storedHash = (0, import_node_fs9.readFileSync)(file, "utf-8").trim();
|
|
13101
13346
|
if (hash === storedHash) {
|
|
13102
13347
|
return { skip: "No source changes since last analysis", hash };
|
|
13103
13348
|
}
|
|
@@ -13106,18 +13351,19 @@ function checkContentHash(files) {
|
|
|
13106
13351
|
}
|
|
13107
13352
|
return { skip: null, hash };
|
|
13108
13353
|
}
|
|
13109
|
-
function recordAnalysisStart() {
|
|
13354
|
+
function recordAnalysisStart(sessionId) {
|
|
13110
13355
|
(0, import_node_fs9.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
13111
|
-
(0, import_node_fs9.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
|
|
13356
|
+
(0, import_node_fs9.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
|
|
13112
13357
|
}
|
|
13113
|
-
function recordPassHash(hash) {
|
|
13114
|
-
(0, import_node_fs9.writeFileSync)(HASH_FILE, hash);
|
|
13358
|
+
function recordPassHash(hash, sessionId) {
|
|
13359
|
+
(0, import_node_fs9.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
|
|
13115
13360
|
}
|
|
13116
|
-
function narrowToRecent(files) {
|
|
13117
|
-
|
|
13361
|
+
function narrowToRecent(files, sessionId) {
|
|
13362
|
+
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13363
|
+
if (!(0, import_node_fs9.existsSync)(file)) return files;
|
|
13118
13364
|
let debounceTime;
|
|
13119
13365
|
try {
|
|
13120
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13366
|
+
debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
|
|
13121
13367
|
} catch {
|
|
13122
13368
|
return files;
|
|
13123
13369
|
}
|
|
@@ -13463,15 +13709,207 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13463
13709
|
}
|
|
13464
13710
|
}
|
|
13465
13711
|
|
|
13466
|
-
// src/lib/
|
|
13712
|
+
// src/lib/baseline.ts
|
|
13467
13713
|
var import_node_fs13 = require("node:fs");
|
|
13468
|
-
var
|
|
13714
|
+
var import_node_path11 = require("node:path");
|
|
13715
|
+
var import_node_crypto5 = require("node:crypto");
|
|
13716
|
+
var BASELINE_VERSION = 1;
|
|
13717
|
+
var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13718
|
+
var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
|
|
13719
|
+
var DEFAULT_SESSION_KEY = "_default";
|
|
13720
|
+
function sessionKey(sessionId) {
|
|
13721
|
+
if (!sessionId) return DEFAULT_SESSION_KEY;
|
|
13722
|
+
return (0, import_node_crypto5.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
13723
|
+
}
|
|
13724
|
+
function sessionDir(key) {
|
|
13725
|
+
return (0, import_node_path11.join)(projectPath(BASELINE_DIR), key);
|
|
13726
|
+
}
|
|
13727
|
+
function manifestPath(dir) {
|
|
13728
|
+
return (0, import_node_path11.join)(dir, "manifest.json");
|
|
13729
|
+
}
|
|
13730
|
+
function mirrorPath(dir, repoRelPath) {
|
|
13731
|
+
return (0, import_node_path11.join)(dir, "files", repoRelPath);
|
|
13732
|
+
}
|
|
13733
|
+
function captureBaseline(opts = {}) {
|
|
13734
|
+
const key = sessionKey(opts.sessionId);
|
|
13735
|
+
const dir = sessionDir(key);
|
|
13736
|
+
const existing = readManifest(dir);
|
|
13737
|
+
const freshStart = opts.source === "startup" || opts.source === "clear";
|
|
13738
|
+
if (existing && !freshStart) {
|
|
13739
|
+
return { baseline: existing, created: false };
|
|
13740
|
+
}
|
|
13741
|
+
const head_sha = getCurrentCommit();
|
|
13742
|
+
const dirty = getDirtyFiles();
|
|
13743
|
+
try {
|
|
13744
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13745
|
+
} catch {
|
|
13746
|
+
}
|
|
13747
|
+
const filesDir = (0, import_node_path11.join)(dir, "files");
|
|
13748
|
+
const mirrored = [];
|
|
13749
|
+
try {
|
|
13750
|
+
(0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
|
|
13751
|
+
for (const p of dirty) {
|
|
13752
|
+
if (p.includes("..")) continue;
|
|
13753
|
+
const content = safeReadForMirror(projectPath(p));
|
|
13754
|
+
if (content === null) continue;
|
|
13755
|
+
const dest = mirrorPath(dir, p);
|
|
13756
|
+
try {
|
|
13757
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(dest), { recursive: true });
|
|
13758
|
+
(0, import_node_fs13.writeFileSync)(dest, content);
|
|
13759
|
+
mirrored.push(p);
|
|
13760
|
+
} catch {
|
|
13761
|
+
}
|
|
13762
|
+
}
|
|
13763
|
+
} catch {
|
|
13764
|
+
}
|
|
13765
|
+
const baseline = {
|
|
13766
|
+
session_id: opts.sessionId ?? "",
|
|
13767
|
+
head_sha,
|
|
13768
|
+
captured_at: Date.now(),
|
|
13769
|
+
dirty_paths: mirrored,
|
|
13770
|
+
version: BASELINE_VERSION
|
|
13771
|
+
};
|
|
13772
|
+
try {
|
|
13773
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
13774
|
+
(0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
|
|
13775
|
+
} catch {
|
|
13776
|
+
}
|
|
13777
|
+
pruneOldBaselines();
|
|
13778
|
+
return { baseline, created: true };
|
|
13779
|
+
}
|
|
13780
|
+
function readBaseline(sessionId) {
|
|
13781
|
+
return readManifest(sessionDir(sessionKey(sessionId)));
|
|
13782
|
+
}
|
|
13783
|
+
function readManifest(dir) {
|
|
13784
|
+
const mp = manifestPath(dir);
|
|
13785
|
+
if (!(0, import_node_fs13.existsSync)(mp)) return null;
|
|
13786
|
+
try {
|
|
13787
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(mp, "utf-8"));
|
|
13788
|
+
if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
|
|
13789
|
+
return null;
|
|
13790
|
+
}
|
|
13791
|
+
return {
|
|
13792
|
+
session_id: typeof parsed.session_id === "string" ? parsed.session_id : "",
|
|
13793
|
+
head_sha: parsed.head_sha,
|
|
13794
|
+
captured_at: parsed.captured_at,
|
|
13795
|
+
dirty_paths: parsed.dirty_paths.filter((p) => typeof p === "string"),
|
|
13796
|
+
version: parsed.version
|
|
13797
|
+
};
|
|
13798
|
+
} catch {
|
|
13799
|
+
return null;
|
|
13800
|
+
}
|
|
13801
|
+
}
|
|
13802
|
+
var preImageCache = /* @__PURE__ */ new WeakMap();
|
|
13803
|
+
function preImage(repoRelPath, baseline) {
|
|
13804
|
+
let perBaseline = preImageCache.get(baseline);
|
|
13805
|
+
if (!perBaseline) {
|
|
13806
|
+
perBaseline = /* @__PURE__ */ new Map();
|
|
13807
|
+
preImageCache.set(baseline, perBaseline);
|
|
13808
|
+
}
|
|
13809
|
+
const cached = perBaseline.get(repoRelPath);
|
|
13810
|
+
if (cached) return cached;
|
|
13811
|
+
const resolved = resolvePreImage(repoRelPath, baseline);
|
|
13812
|
+
perBaseline.set(repoRelPath, resolved);
|
|
13813
|
+
return resolved;
|
|
13814
|
+
}
|
|
13815
|
+
function resolvePreImage(repoRelPath, baseline) {
|
|
13816
|
+
if (baseline.dirty_paths.includes(repoRelPath)) {
|
|
13817
|
+
const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
|
|
13818
|
+
if ((0, import_node_fs13.existsSync)(mp)) {
|
|
13819
|
+
try {
|
|
13820
|
+
return { content: (0, import_node_fs13.readFileSync)(mp, "utf-8"), existed: true };
|
|
13821
|
+
} catch {
|
|
13822
|
+
}
|
|
13823
|
+
}
|
|
13824
|
+
}
|
|
13825
|
+
const atHead = showContentAtRef(baseline.head_sha, repoRelPath);
|
|
13826
|
+
if (atHead !== null) return { content: atHead, existed: true };
|
|
13827
|
+
return { content: "", existed: false };
|
|
13828
|
+
}
|
|
13829
|
+
function generateBaselineDiffs(files, baseline) {
|
|
13830
|
+
if (!baseline) return { diffs: [], has_baseline: false };
|
|
13831
|
+
const diffs = [];
|
|
13832
|
+
for (const file of files) {
|
|
13833
|
+
const language = file.language ?? detectLanguage(file.path);
|
|
13834
|
+
const pre = preImage(file.path, baseline);
|
|
13835
|
+
if (pre.existed) {
|
|
13836
|
+
if (pre.content === file.content) continue;
|
|
13837
|
+
const diff = computeDiff(pre.content, file.content, file.path);
|
|
13838
|
+
if (diff) diffs.push({ path: file.path, language, diff, status: "modified" });
|
|
13839
|
+
} else {
|
|
13840
|
+
const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
|
|
13841
|
+
diffs.push({
|
|
13842
|
+
path: file.path,
|
|
13843
|
+
language,
|
|
13844
|
+
diff: `--- /dev/null
|
|
13845
|
+
+++ b/${file.path}
|
|
13846
|
+
@@ -0,0 +1,${file.content.split("\n").length} @@
|
|
13847
|
+
${addedLines}`,
|
|
13848
|
+
status: "added"
|
|
13849
|
+
});
|
|
13850
|
+
}
|
|
13851
|
+
}
|
|
13852
|
+
return { diffs, has_baseline: true };
|
|
13853
|
+
}
|
|
13854
|
+
function changedSinceBaseline(repoRelPath, baseline) {
|
|
13855
|
+
const pre = preImage(repoRelPath, baseline);
|
|
13856
|
+
let current;
|
|
13857
|
+
try {
|
|
13858
|
+
current = (0, import_node_fs13.readFileSync)(projectPath(repoRelPath), "utf-8");
|
|
13859
|
+
} catch {
|
|
13860
|
+
return pre.existed;
|
|
13861
|
+
}
|
|
13862
|
+
if (!pre.existed) return true;
|
|
13863
|
+
return current !== pre.content;
|
|
13864
|
+
}
|
|
13865
|
+
function safeReadForMirror(absPath) {
|
|
13866
|
+
try {
|
|
13867
|
+
if ((0, import_node_fs13.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
|
|
13868
|
+
const buf = (0, import_node_fs13.readFileSync)(absPath);
|
|
13869
|
+
if (buf.includes(0)) return null;
|
|
13870
|
+
return buf.toString("utf-8");
|
|
13871
|
+
} catch {
|
|
13872
|
+
return null;
|
|
13873
|
+
}
|
|
13874
|
+
}
|
|
13875
|
+
function pruneOldBaselines() {
|
|
13876
|
+
const root = projectPath(BASELINE_DIR);
|
|
13877
|
+
let entries;
|
|
13878
|
+
try {
|
|
13879
|
+
entries = (0, import_node_fs13.readdirSync)(root);
|
|
13880
|
+
} catch {
|
|
13881
|
+
return;
|
|
13882
|
+
}
|
|
13883
|
+
const now = Date.now();
|
|
13884
|
+
for (const name of entries) {
|
|
13885
|
+
const dir = (0, import_node_path11.join)(root, name);
|
|
13886
|
+
const manifest = readManifest(dir);
|
|
13887
|
+
if (!manifest) {
|
|
13888
|
+
try {
|
|
13889
|
+
if (now - (0, import_node_fs13.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
|
|
13890
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13891
|
+
}
|
|
13892
|
+
} catch {
|
|
13893
|
+
}
|
|
13894
|
+
continue;
|
|
13895
|
+
}
|
|
13896
|
+
if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
|
|
13897
|
+
try {
|
|
13898
|
+
(0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
|
|
13899
|
+
} catch {
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
}
|
|
13903
|
+
|
|
13904
|
+
// src/lib/offline.ts
|
|
13905
|
+
var import_node_fs14 = require("node:fs");
|
|
13906
|
+
var import_node_crypto6 = require("node:crypto");
|
|
13469
13907
|
function cacheRequest(body) {
|
|
13470
13908
|
try {
|
|
13471
|
-
(0,
|
|
13472
|
-
const suffix = (0,
|
|
13909
|
+
(0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
13910
|
+
const suffix = (0, import_node_crypto6.randomBytes)(4).toString("hex");
|
|
13473
13911
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
13474
|
-
(0,
|
|
13912
|
+
(0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
|
|
13475
13913
|
} catch {
|
|
13476
13914
|
}
|
|
13477
13915
|
}
|
|
@@ -13485,7 +13923,7 @@ function buildOfflineFallback(reason, staticResults) {
|
|
|
13485
13923
|
}
|
|
13486
13924
|
|
|
13487
13925
|
// src/lib/context-files.ts
|
|
13488
|
-
var
|
|
13926
|
+
var import_node_fs15 = require("node:fs");
|
|
13489
13927
|
var MAX_CONTEXT_FILES = 10;
|
|
13490
13928
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
13491
13929
|
var MAX_CONTEXT_TOTAL_BYTES = 51200;
|
|
@@ -13497,7 +13935,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13497
13935
|
if (result.length >= MAX_CONTEXT_FILES) break;
|
|
13498
13936
|
if (deltaPaths.has(filePath)) continue;
|
|
13499
13937
|
try {
|
|
13500
|
-
const content = (0,
|
|
13938
|
+
const content = (0, import_node_fs15.readFileSync)(filePath, "utf8");
|
|
13501
13939
|
const bytes = Buffer.byteLength(content);
|
|
13502
13940
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
13503
13941
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -13542,20 +13980,20 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
13542
13980
|
}
|
|
13543
13981
|
|
|
13544
13982
|
// src/lib/cache-cleanup.ts
|
|
13545
|
-
var
|
|
13546
|
-
var
|
|
13983
|
+
var import_node_fs16 = require("node:fs");
|
|
13984
|
+
var import_node_path12 = require("node:path");
|
|
13547
13985
|
var CACHE_TTL_DAYS = 7;
|
|
13548
13986
|
function pruneStaleCache() {
|
|
13549
13987
|
try {
|
|
13550
13988
|
const dir = projectPath(CACHE_DIR);
|
|
13551
13989
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
13552
|
-
for (const entry of (0,
|
|
13990
|
+
for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
|
|
13553
13991
|
if (!entry.startsWith("pending-")) continue;
|
|
13554
|
-
const path = (0,
|
|
13992
|
+
const path = (0, import_node_path12.join)(dir, entry);
|
|
13555
13993
|
try {
|
|
13556
|
-
const stat3 = (0,
|
|
13994
|
+
const stat3 = (0, import_node_fs16.statSync)(path);
|
|
13557
13995
|
if (stat3.mtimeMs < cutoff) {
|
|
13558
|
-
(0,
|
|
13996
|
+
(0, import_node_fs16.unlinkSync)(path);
|
|
13559
13997
|
logEvent("cache_entry_pruned", {
|
|
13560
13998
|
path: entry,
|
|
13561
13999
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -13627,10 +14065,11 @@ function reconcileAnalysisMode(predictedMode, signals) {
|
|
|
13627
14065
|
signals.noFilesChanged,
|
|
13628
14066
|
signals.assistantResponse,
|
|
13629
14067
|
signals.conversationPrompts,
|
|
13630
|
-
signals.actionSummary
|
|
14068
|
+
signals.actionSummary,
|
|
14069
|
+
signals.sessionAuthoredCode
|
|
13631
14070
|
);
|
|
13632
14071
|
}
|
|
13633
|
-
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0));
|
|
14072
|
+
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
13634
14073
|
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
13635
14074
|
switch (predictedMode) {
|
|
13636
14075
|
case "skip":
|
|
@@ -13655,8 +14094,8 @@ function didAgentInvestigate(summary) {
|
|
|
13655
14094
|
function isValidMode(mode) {
|
|
13656
14095
|
return mode === "standard" || mode === "plan" || mode === "debug" || mode === "skip";
|
|
13657
14096
|
}
|
|
13658
|
-
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary) {
|
|
13659
|
-
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
14097
|
+
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
14098
|
+
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
13660
14099
|
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
13661
14100
|
if (!agentAuthoredCode) return "skip";
|
|
13662
14101
|
}
|
|
@@ -13736,7 +14175,7 @@ function isMetaTaskLabel(label2) {
|
|
|
13736
14175
|
}
|
|
13737
14176
|
|
|
13738
14177
|
// src/lib/transcript.ts
|
|
13739
|
-
var
|
|
14178
|
+
var import_node_fs17 = require("node:fs");
|
|
13740
14179
|
var MAX_READ_BYTES = 256 * 1024;
|
|
13741
14180
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
13742
14181
|
var MAX_FILES_LIST = 20;
|
|
@@ -13758,14 +14197,14 @@ async function extractActionSummary(transcriptPath) {
|
|
|
13758
14197
|
function readTurnLines(transcriptPath) {
|
|
13759
14198
|
let size;
|
|
13760
14199
|
try {
|
|
13761
|
-
size = (0,
|
|
14200
|
+
size = (0, import_node_fs17.statSync)(transcriptPath).size;
|
|
13762
14201
|
} catch {
|
|
13763
14202
|
return null;
|
|
13764
14203
|
}
|
|
13765
14204
|
if (size === 0) return null;
|
|
13766
14205
|
let raw;
|
|
13767
14206
|
if (size <= SMALL_FILE_BYTES) {
|
|
13768
|
-
raw = (0,
|
|
14207
|
+
raw = (0, import_node_fs17.readFileSync)(transcriptPath, "utf-8");
|
|
13769
14208
|
} else {
|
|
13770
14209
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
13771
14210
|
const fd = require("node:fs").openSync(transcriptPath, "r");
|
|
@@ -13853,6 +14292,12 @@ function buildSummary(lines) {
|
|
|
13853
14292
|
case "Edit":
|
|
13854
14293
|
addPath(filesEdited, input.file_path);
|
|
13855
14294
|
break;
|
|
14295
|
+
case "MultiEdit":
|
|
14296
|
+
addPath(filesEdited, input.file_path);
|
|
14297
|
+
break;
|
|
14298
|
+
case "NotebookEdit":
|
|
14299
|
+
addPath(filesEdited, input.notebook_path ?? input.file_path);
|
|
14300
|
+
break;
|
|
13856
14301
|
case "Write":
|
|
13857
14302
|
addPath(filesCreated, input.file_path);
|
|
13858
14303
|
addPath(filesEdited, input.file_path);
|
|
@@ -13936,8 +14381,8 @@ function capArray(set, max) {
|
|
|
13936
14381
|
|
|
13937
14382
|
// src/lib/seed-runner.ts
|
|
13938
14383
|
var import_promises11 = require("node:fs/promises");
|
|
13939
|
-
var
|
|
13940
|
-
var
|
|
14384
|
+
var import_node_fs18 = require("node:fs");
|
|
14385
|
+
var import_node_path13 = require("node:path");
|
|
13941
14386
|
var import_yaml2 = __toESM(require_dist());
|
|
13942
14387
|
|
|
13943
14388
|
// src/lib/seed.ts
|
|
@@ -14176,7 +14621,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
14176
14621
|
return fm;
|
|
14177
14622
|
}
|
|
14178
14623
|
async function runSeed(opts) {
|
|
14179
|
-
if (!(0,
|
|
14624
|
+
if (!(0, import_node_fs18.existsSync)(STANDARD_FILE)) {
|
|
14180
14625
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
14181
14626
|
}
|
|
14182
14627
|
let standardDoc;
|
|
@@ -14188,7 +14633,7 @@ async function runSeed(opts) {
|
|
|
14188
14633
|
}
|
|
14189
14634
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
14190
14635
|
let readmeContent;
|
|
14191
|
-
if ((0,
|
|
14636
|
+
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
14192
14637
|
try {
|
|
14193
14638
|
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
14194
14639
|
} catch {
|
|
@@ -14196,7 +14641,7 @@ async function runSeed(opts) {
|
|
|
14196
14641
|
}
|
|
14197
14642
|
let claudeMdContent;
|
|
14198
14643
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
14199
|
-
if ((0,
|
|
14644
|
+
if ((0, import_node_fs18.existsSync)(p)) {
|
|
14200
14645
|
try {
|
|
14201
14646
|
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
14202
14647
|
break;
|
|
@@ -14219,8 +14664,8 @@ async function runSeed(opts) {
|
|
|
14219
14664
|
if (candidates.length === 0) {
|
|
14220
14665
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
14221
14666
|
}
|
|
14222
|
-
const overviewPath = (0,
|
|
14223
|
-
if ((0,
|
|
14667
|
+
const overviewPath = (0, import_node_path13.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
14668
|
+
if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
|
|
14224
14669
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
14225
14670
|
}
|
|
14226
14671
|
if (opts.dryRun) {
|
|
@@ -14255,9 +14700,9 @@ async function runSeed(opts) {
|
|
|
14255
14700
|
}
|
|
14256
14701
|
const nodeId = res.data.node_id;
|
|
14257
14702
|
const filePathRel = res.data.file_path;
|
|
14258
|
-
const targetPath = (0,
|
|
14703
|
+
const targetPath = (0, import_node_path13.join)(MEMORY_DIR, filePathRel);
|
|
14259
14704
|
try {
|
|
14260
|
-
await (0, import_promises11.mkdir)((0,
|
|
14705
|
+
await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(targetPath), { recursive: true });
|
|
14261
14706
|
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
14262
14707
|
created++;
|
|
14263
14708
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
@@ -14310,6 +14755,27 @@ function passAndExit(reason) {
|
|
|
14310
14755
|
printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
|
|
14311
14756
|
process.exit(0);
|
|
14312
14757
|
}
|
|
14758
|
+
var EMPTY_STATIC = {
|
|
14759
|
+
tool: "@codacy/analysis-cli",
|
|
14760
|
+
findings: [],
|
|
14761
|
+
summary: { total_findings: 0, by_severity: {}, tools_run: [] }
|
|
14762
|
+
};
|
|
14763
|
+
function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
|
|
14764
|
+
if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
|
|
14765
|
+
let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14766
|
+
if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
14767
|
+
if (scannable.length === 0) return EMPTY_STATIC;
|
|
14768
|
+
return runCodacyAnalysis(scannable);
|
|
14769
|
+
}
|
|
14770
|
+
function localOnlyAndExit(staticResults) {
|
|
14771
|
+
printJsonCompact({
|
|
14772
|
+
gate_decision: "PASS",
|
|
14773
|
+
systemMessage: "Verity: not authenticated \u2014 ran a local static-only check (no deep review, no upload). Run `verity init` to authenticate and enable the full quality gate.",
|
|
14774
|
+
unauthenticated: true,
|
|
14775
|
+
static_results: staticResults
|
|
14776
|
+
});
|
|
14777
|
+
process.exit(0);
|
|
14778
|
+
}
|
|
14313
14779
|
function registerAnalyzeCommand(program2) {
|
|
14314
14780
|
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
14315
14781
|
const globals = program2.opts();
|
|
@@ -14327,6 +14793,15 @@ async function runAnalyze(opts, globals) {
|
|
|
14327
14793
|
}
|
|
14328
14794
|
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
14329
14795
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
14796
|
+
const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
14797
|
+
const baseline = readBaseline(baselineSessionId);
|
|
14798
|
+
if (baseline) {
|
|
14799
|
+
logEvent("baseline_loaded", {
|
|
14800
|
+
head: baseline.head_sha.slice(0, 12),
|
|
14801
|
+
dirty_count: baseline.dirty_paths.length,
|
|
14802
|
+
age_ms: Date.now() - baseline.captured_at
|
|
14803
|
+
});
|
|
14804
|
+
}
|
|
14330
14805
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
14331
14806
|
const analyzable = filterAnalyzable(allChanged);
|
|
14332
14807
|
const reviewable = filterReviewable(allChanged);
|
|
@@ -14336,7 +14811,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14336
14811
|
passAndExit("No analyzable files changed");
|
|
14337
14812
|
}
|
|
14338
14813
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
14339
|
-
const conversation = await readAndClearConversationBuffer();
|
|
14814
|
+
const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
|
|
14340
14815
|
const specs = discoverSpecs();
|
|
14341
14816
|
const plans = discoverPlans();
|
|
14342
14817
|
const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
|
|
@@ -14351,12 +14826,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14351
14826
|
passAndExit("Reflection-prompt turn \u2014 skipping analysis");
|
|
14352
14827
|
}
|
|
14353
14828
|
const tokenResult = await resolveToken(globals.token);
|
|
14354
|
-
if (!tokenResult.ok) {
|
|
14355
|
-
passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
|
|
14356
|
-
}
|
|
14357
14829
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
14358
|
-
if (!urlResult.ok) {
|
|
14359
|
-
|
|
14830
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
14831
|
+
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
14360
14832
|
}
|
|
14361
14833
|
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
14362
14834
|
let contextFilePaths = [];
|
|
@@ -14386,13 +14858,14 @@ async function runAnalyze(opts, globals) {
|
|
|
14386
14858
|
}
|
|
14387
14859
|
const conversationPrompts = (conversation?.prompts ?? []).map((p) => p.prompt);
|
|
14388
14860
|
let analysisMode;
|
|
14861
|
+
const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
14389
14862
|
const modeOverride = opts.mode;
|
|
14390
14863
|
if (modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride)) {
|
|
14391
14864
|
analysisMode = modeOverride;
|
|
14392
14865
|
} else {
|
|
14393
14866
|
analysisMode = reconcileAnalysisMode(
|
|
14394
14867
|
predictedMode,
|
|
14395
|
-
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts }
|
|
14868
|
+
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
|
|
14396
14869
|
);
|
|
14397
14870
|
}
|
|
14398
14871
|
if (analysisMode === "skip") {
|
|
@@ -14414,7 +14887,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14414
14887
|
let currentCommit = "";
|
|
14415
14888
|
if (analysisMode !== "plan") {
|
|
14416
14889
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
14417
|
-
const debounceSkip = checkDebounce(debounceSeconds);
|
|
14890
|
+
const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
|
|
14418
14891
|
if (debounceSkip) {
|
|
14419
14892
|
if (assistantResponse) {
|
|
14420
14893
|
analysisMode = "plan";
|
|
@@ -14424,7 +14897,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14424
14897
|
}
|
|
14425
14898
|
if (analysisMode !== "plan") {
|
|
14426
14899
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14427
|
-
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
14900
|
+
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
|
|
14428
14901
|
if (mtimeSkip) {
|
|
14429
14902
|
if (assistantResponse) {
|
|
14430
14903
|
analysisMode = "plan";
|
|
@@ -14434,9 +14907,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14434
14907
|
}
|
|
14435
14908
|
}
|
|
14436
14909
|
if (analysisMode !== "plan") {
|
|
14437
|
-
recordAnalysisStart();
|
|
14910
|
+
recordAnalysisStart(baselineSessionId);
|
|
14438
14911
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14439
|
-
const hashResult = checkContentHash(allCheckable);
|
|
14912
|
+
const hashResult = checkContentHash(allCheckable, baselineSessionId);
|
|
14440
14913
|
if (hashResult.skip) {
|
|
14441
14914
|
if (assistantResponse) {
|
|
14442
14915
|
analysisMode = "plan";
|
|
@@ -14448,12 +14921,18 @@ async function runAnalyze(opts, globals) {
|
|
|
14448
14921
|
if (analysisMode !== "plan") {
|
|
14449
14922
|
const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
|
|
14450
14923
|
const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
|
|
14451
|
-
const recentForReview = narrowToRecent(baseForReview);
|
|
14924
|
+
const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
|
|
14452
14925
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
14453
|
-
|
|
14454
|
-
|
|
14926
|
+
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14927
|
+
if (baseline) {
|
|
14928
|
+
allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
14929
|
+
}
|
|
14930
|
+
if (allScannable.length > 0) {
|
|
14931
|
+
staticResults = runCodacyAnalysis(allScannable);
|
|
14932
|
+
}
|
|
14455
14933
|
}
|
|
14456
|
-
|
|
14934
|
+
const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
|
|
14935
|
+
codeDelta = collectCodeDelta(deltaSet, {
|
|
14457
14936
|
maxFiles: parseInt(opts.maxFiles, 10),
|
|
14458
14937
|
maxFileBytes: parseInt(opts.maxFileSize, 10),
|
|
14459
14938
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
@@ -14468,7 +14947,12 @@ async function runAnalyze(opts, globals) {
|
|
|
14468
14947
|
}
|
|
14469
14948
|
}
|
|
14470
14949
|
if (analysisMode !== "plan") {
|
|
14471
|
-
|
|
14950
|
+
if (baseline) {
|
|
14951
|
+
const baselineDiffs = generateBaselineDiffs(codeDelta.files, baseline);
|
|
14952
|
+
snapshotResult = { has_snapshots: baselineDiffs.diffs.length > 0, diffs: baselineDiffs.diffs };
|
|
14953
|
+
} else {
|
|
14954
|
+
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
14955
|
+
}
|
|
14472
14956
|
currentCommit = getCurrentCommit();
|
|
14473
14957
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
14474
14958
|
const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
@@ -14500,9 +14984,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14500
14984
|
let autoSeedNotice = null;
|
|
14501
14985
|
try {
|
|
14502
14986
|
await ensureMemoryDir();
|
|
14503
|
-
const seedMarker = (0,
|
|
14504
|
-
const hasStandard = (0,
|
|
14505
|
-
const alreadyTried = (0,
|
|
14987
|
+
const seedMarker = (0, import_node_path14.join)(VERITY_DIR, ".seeded");
|
|
14988
|
+
const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
|
|
14989
|
+
const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
|
|
14506
14990
|
if (hasStandard && !alreadyTried) {
|
|
14507
14991
|
const preManifest = await buildManifest();
|
|
14508
14992
|
if (preManifest.nodes.length === 0) {
|
|
@@ -14515,7 +14999,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14515
14999
|
dryRun: false
|
|
14516
15000
|
});
|
|
14517
15001
|
if (seedResult.created > 0) {
|
|
14518
|
-
(0,
|
|
15002
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
14519
15003
|
`);
|
|
14520
15004
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
14521
15005
|
logEvent("auto_seed_ran", {
|
|
@@ -14523,7 +15007,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14523
15007
|
failed: seedResult.failed
|
|
14524
15008
|
});
|
|
14525
15009
|
} else if (seedResult.skipped === "already_seeded") {
|
|
14526
|
-
(0,
|
|
15010
|
+
(0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
14527
15011
|
`);
|
|
14528
15012
|
} else {
|
|
14529
15013
|
logEvent("auto_seed_noop", {
|
|
@@ -14812,7 +15296,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14812
15296
|
}
|
|
14813
15297
|
case "PASS": {
|
|
14814
15298
|
writeIteration(1, currentCommit, contentHash ?? void 0);
|
|
14815
|
-
if (contentHash) recordPassHash(contentHash);
|
|
15299
|
+
if (contentHash) recordPassHash(contentHash, baselineSessionId);
|
|
14816
15300
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
14817
15301
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
14818
15302
|
const viewUrl = response.view_url ?? "";
|
|
@@ -14823,7 +15307,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14823
15307
|
break;
|
|
14824
15308
|
}
|
|
14825
15309
|
case "WARN": {
|
|
14826
|
-
if (contentHash) recordPassHash(contentHash);
|
|
15310
|
+
if (contentHash) recordPassHash(contentHash, baselineSessionId);
|
|
14827
15311
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
14828
15312
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
14829
15313
|
const viewUrl = response.view_url ?? "";
|
|
@@ -14841,8 +15325,54 @@ async function runAnalyze(opts, globals) {
|
|
|
14841
15325
|
}
|
|
14842
15326
|
}
|
|
14843
15327
|
|
|
15328
|
+
// src/commands/baseline.ts
|
|
15329
|
+
var import_node_fs20 = require("node:fs");
|
|
15330
|
+
function registerBaselineCommands(program2) {
|
|
15331
|
+
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
15332
|
+
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) => {
|
|
15333
|
+
try {
|
|
15334
|
+
try {
|
|
15335
|
+
process.chdir(repoRoot());
|
|
15336
|
+
} catch {
|
|
15337
|
+
}
|
|
15338
|
+
if (!(0, import_node_fs20.existsSync)(VERITY_DIR)) {
|
|
15339
|
+
process.exit(0);
|
|
15340
|
+
}
|
|
15341
|
+
let sessionId = opts.sessionId;
|
|
15342
|
+
let source = opts.source;
|
|
15343
|
+
if (!process.stdin.isTTY) {
|
|
15344
|
+
const input = (await readStdin()).trim();
|
|
15345
|
+
if (input) {
|
|
15346
|
+
try {
|
|
15347
|
+
const event = JSON.parse(input);
|
|
15348
|
+
sessionId = sessionId ?? event.session_id;
|
|
15349
|
+
source = source ?? event.source;
|
|
15350
|
+
} catch {
|
|
15351
|
+
}
|
|
15352
|
+
}
|
|
15353
|
+
}
|
|
15354
|
+
const result = captureBaseline({ sessionId, source });
|
|
15355
|
+
logEvent("baseline_capture", {
|
|
15356
|
+
created: result.created,
|
|
15357
|
+
source: source ?? null,
|
|
15358
|
+
dirty_count: result.baseline.dirty_paths.length,
|
|
15359
|
+
head: result.baseline.head_sha.slice(0, 12)
|
|
15360
|
+
});
|
|
15361
|
+
} catch {
|
|
15362
|
+
}
|
|
15363
|
+
process.exit(0);
|
|
15364
|
+
});
|
|
15365
|
+
}
|
|
15366
|
+
async function readStdin() {
|
|
15367
|
+
const chunks = [];
|
|
15368
|
+
for await (const chunk of process.stdin) {
|
|
15369
|
+
chunks.push(chunk);
|
|
15370
|
+
}
|
|
15371
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
15372
|
+
}
|
|
15373
|
+
|
|
14844
15374
|
// src/commands/review.ts
|
|
14845
|
-
var
|
|
15375
|
+
var import_node_fs21 = require("node:fs");
|
|
14846
15376
|
function registerReviewCommand(program2) {
|
|
14847
15377
|
program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
14848
15378
|
const globals = program2.opts();
|
|
@@ -14861,7 +15391,7 @@ async function runReview(opts, globals) {
|
|
|
14861
15391
|
const securityFiles = filterSecurity(allFiles);
|
|
14862
15392
|
let staticResults;
|
|
14863
15393
|
if (isCodacyAvailable()) {
|
|
14864
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
15394
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs21.existsSync)(f) || resolveFile(f) !== null);
|
|
14865
15395
|
staticResults = runCodacyAnalysis(scannable);
|
|
14866
15396
|
} else {
|
|
14867
15397
|
staticResults = {
|
|
@@ -14872,13 +15402,14 @@ async function runReview(opts, globals) {
|
|
|
14872
15402
|
}
|
|
14873
15403
|
const codeDelta = collectCodeDelta(allFiles);
|
|
14874
15404
|
const tokenResult = await resolveToken(globals.token);
|
|
14875
|
-
if (!tokenResult.ok) {
|
|
14876
|
-
printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
|
|
14877
|
-
process.exit(0);
|
|
14878
|
-
}
|
|
14879
15405
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
14880
|
-
if (!urlResult.ok) {
|
|
14881
|
-
|
|
15406
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
15407
|
+
printJsonCompact({
|
|
15408
|
+
gate_decision: "PASS",
|
|
15409
|
+
systemMessage: "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
|
|
15410
|
+
unauthenticated: true,
|
|
15411
|
+
static_results: staticResults
|
|
15412
|
+
});
|
|
14882
15413
|
process.exit(0);
|
|
14883
15414
|
}
|
|
14884
15415
|
let specs;
|
|
@@ -14886,10 +15417,10 @@ async function runReview(opts, globals) {
|
|
|
14886
15417
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
14887
15418
|
specs = [];
|
|
14888
15419
|
for (const p of specPaths) {
|
|
14889
|
-
if (!(0,
|
|
15420
|
+
if (!(0, import_node_fs21.existsSync)(p)) continue;
|
|
14890
15421
|
try {
|
|
14891
|
-
const { readFileSync:
|
|
14892
|
-
const content =
|
|
15422
|
+
const { readFileSync: readFileSync12 } = await import("node:fs");
|
|
15423
|
+
const content = readFileSync12(p, "utf-8");
|
|
14893
15424
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
14894
15425
|
} catch {
|
|
14895
15426
|
}
|
|
@@ -14946,10 +15477,10 @@ async function runReview(opts, globals) {
|
|
|
14946
15477
|
}
|
|
14947
15478
|
|
|
14948
15479
|
// src/commands/guard.ts
|
|
14949
|
-
var
|
|
14950
|
-
var
|
|
15480
|
+
var import_node_fs22 = require("node:fs");
|
|
15481
|
+
var import_node_path15 = require("node:path");
|
|
14951
15482
|
var GUARD_BLOCK_CAP = 2;
|
|
14952
|
-
var GUARD_ITER_FILE = (0,
|
|
15483
|
+
var GUARD_ITER_FILE = (0, import_node_path15.join)(VERITY_DIR, ".guard-iteration");
|
|
14953
15484
|
function readPreToolUseStdin() {
|
|
14954
15485
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
14955
15486
|
return new Promise((resolve) => {
|
|
@@ -15013,7 +15544,7 @@ function classifyCommand(command, on) {
|
|
|
15013
15544
|
}
|
|
15014
15545
|
function readIterMap() {
|
|
15015
15546
|
try {
|
|
15016
|
-
const raw = JSON.parse((0,
|
|
15547
|
+
const raw = JSON.parse((0, import_node_fs22.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
15017
15548
|
if (raw && typeof raw === "object") {
|
|
15018
15549
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
15019
15550
|
return { [raw.moment]: raw.count };
|
|
@@ -15033,10 +15564,10 @@ function readIter(moment) {
|
|
|
15033
15564
|
}
|
|
15034
15565
|
function writeIter(moment, count) {
|
|
15035
15566
|
try {
|
|
15036
|
-
(0,
|
|
15567
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15037
15568
|
const map = readIterMap();
|
|
15038
15569
|
map[moment] = count;
|
|
15039
|
-
(0,
|
|
15570
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15040
15571
|
} catch {
|
|
15041
15572
|
}
|
|
15042
15573
|
}
|
|
@@ -15046,10 +15577,10 @@ function resetIter(moment) {
|
|
|
15046
15577
|
if (!(moment in map)) return;
|
|
15047
15578
|
delete map[moment];
|
|
15048
15579
|
if (Object.keys(map).length === 0) {
|
|
15049
|
-
if ((0,
|
|
15580
|
+
if ((0, import_node_fs22.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs22.unlinkSync)(GUARD_ITER_FILE);
|
|
15050
15581
|
} else {
|
|
15051
|
-
(0,
|
|
15052
|
-
(0,
|
|
15582
|
+
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
15583
|
+
(0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
15053
15584
|
}
|
|
15054
15585
|
} catch {
|
|
15055
15586
|
}
|
|
@@ -15113,7 +15644,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
15113
15644
|
const securityFiles = filterSecurity(files);
|
|
15114
15645
|
let staticResults;
|
|
15115
15646
|
if (isCodacyAvailable()) {
|
|
15116
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
15647
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs22.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
15117
15648
|
staticResults = runCodacyAnalysis(scannable);
|
|
15118
15649
|
} else {
|
|
15119
15650
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
@@ -15158,7 +15689,7 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
15158
15689
|
async function runGuard(opts, globals) {
|
|
15159
15690
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
15160
15691
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
15161
|
-
if (cwd && (0,
|
|
15692
|
+
if (cwd && (0, import_node_fs22.existsSync)(cwd)) {
|
|
15162
15693
|
try {
|
|
15163
15694
|
process.chdir(cwd);
|
|
15164
15695
|
} catch {
|
|
@@ -15263,14 +15794,15 @@ function writeBlockMessage(moment, response) {
|
|
|
15263
15794
|
}
|
|
15264
15795
|
|
|
15265
15796
|
// src/commands/init.ts
|
|
15266
|
-
var
|
|
15797
|
+
var import_node_fs24 = require("node:fs");
|
|
15267
15798
|
var import_promises12 = require("node:fs/promises");
|
|
15268
|
-
var
|
|
15799
|
+
var import_node_path17 = require("node:path");
|
|
15269
15800
|
var import_node_child_process9 = require("node:child_process");
|
|
15801
|
+
var readline = __toESM(require("node:readline/promises"));
|
|
15270
15802
|
|
|
15271
15803
|
// src/commands/migrate.ts
|
|
15272
|
-
var
|
|
15273
|
-
var
|
|
15804
|
+
var import_node_fs23 = require("node:fs");
|
|
15805
|
+
var import_node_path16 = require("node:path");
|
|
15274
15806
|
var import_node_child_process8 = require("node:child_process");
|
|
15275
15807
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
15276
15808
|
function defaultNpmRemover(pkg) {
|
|
@@ -15306,12 +15838,12 @@ async function runMigration(opts = {}) {
|
|
|
15306
15838
|
return { actions, migrated: actions.length > 0 };
|
|
15307
15839
|
}
|
|
15308
15840
|
function migrateProjectDir(root, actions) {
|
|
15309
|
-
const gateDir = (0,
|
|
15310
|
-
const verityDir = (0,
|
|
15311
|
-
if ((0,
|
|
15841
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
15842
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
15843
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
|
|
15312
15844
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
15313
15845
|
}
|
|
15314
|
-
if ((0,
|
|
15846
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
15315
15847
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
15316
15848
|
}
|
|
15317
15849
|
return false;
|
|
@@ -15332,13 +15864,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
15332
15864
|
}
|
|
15333
15865
|
}
|
|
15334
15866
|
if (moved) {
|
|
15335
|
-
if ((0,
|
|
15867
|
+
if ((0, import_node_fs23.existsSync)(gateDir)) {
|
|
15336
15868
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
15337
15869
|
if (carried > 0) {
|
|
15338
15870
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
15339
15871
|
}
|
|
15340
15872
|
try {
|
|
15341
|
-
(0,
|
|
15873
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15342
15874
|
} catch {
|
|
15343
15875
|
}
|
|
15344
15876
|
}
|
|
@@ -15354,18 +15886,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
15354
15886
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
15355
15887
|
}
|
|
15356
15888
|
try {
|
|
15357
|
-
(0,
|
|
15889
|
+
(0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
|
|
15358
15890
|
} catch {
|
|
15359
15891
|
}
|
|
15360
15892
|
return carried > 0;
|
|
15361
15893
|
}
|
|
15362
15894
|
function migrateGlobalCredentials(home, actions) {
|
|
15363
15895
|
if (!home) return;
|
|
15364
|
-
const gateCreds = (0,
|
|
15365
|
-
const verityCreds = (0,
|
|
15366
|
-
if (!(0,
|
|
15367
|
-
if (!(0,
|
|
15368
|
-
(0,
|
|
15896
|
+
const gateCreds = (0, import_node_path16.join)(home, ".gate", "credentials");
|
|
15897
|
+
const verityCreds = (0, import_node_path16.join)(home, ".verity", "credentials");
|
|
15898
|
+
if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
|
|
15899
|
+
if (!(0, import_node_fs23.existsSync)(verityCreds)) {
|
|
15900
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.join)(home, ".verity"), { recursive: true });
|
|
15369
15901
|
moveFile(gateCreds, verityCreds);
|
|
15370
15902
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
15371
15903
|
return;
|
|
@@ -15387,8 +15919,8 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
15387
15919
|
}
|
|
15388
15920
|
}
|
|
15389
15921
|
async function migrateClaudeMd(root, actions) {
|
|
15390
|
-
const claudeMd = (0,
|
|
15391
|
-
const hadLegacyBlock = (0,
|
|
15922
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
15923
|
+
const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
15392
15924
|
if (!hadLegacyBlock) return;
|
|
15393
15925
|
try {
|
|
15394
15926
|
await ensureClaudeMdPointer(root);
|
|
@@ -15398,9 +15930,9 @@ async function migrateClaudeMd(root, actions) {
|
|
|
15398
15930
|
}
|
|
15399
15931
|
}
|
|
15400
15932
|
function migrateStandardFile(root, actions) {
|
|
15401
|
-
const gateMd = (0,
|
|
15402
|
-
const verityMd = (0,
|
|
15403
|
-
if (!(0,
|
|
15933
|
+
const gateMd = (0, import_node_path16.join)(root, "GATE.md");
|
|
15934
|
+
const verityMd = (0, import_node_path16.join)(root, "VERITY.md");
|
|
15935
|
+
if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
|
|
15404
15936
|
let moved = false;
|
|
15405
15937
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
15406
15938
|
try {
|
|
@@ -15412,7 +15944,7 @@ function migrateStandardFile(root, actions) {
|
|
|
15412
15944
|
if (!moved) moveFile(gateMd, verityMd);
|
|
15413
15945
|
const content = readFileSyncSafe(verityMd);
|
|
15414
15946
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
15415
|
-
if (refreshed !== content) (0,
|
|
15947
|
+
if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
|
|
15416
15948
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
15417
15949
|
}
|
|
15418
15950
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
@@ -15446,14 +15978,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
15446
15978
|
}
|
|
15447
15979
|
if (toAppend.length > 0) {
|
|
15448
15980
|
const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
15449
|
-
(0,
|
|
15981
|
+
(0, import_node_fs23.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
|
|
15450
15982
|
}
|
|
15451
|
-
(0,
|
|
15983
|
+
(0, import_node_fs23.rmSync)(gateCreds, { force: true });
|
|
15452
15984
|
return toAppend.length;
|
|
15453
15985
|
}
|
|
15454
15986
|
function readFileSyncSafe(path) {
|
|
15455
15987
|
try {
|
|
15456
|
-
return (0,
|
|
15988
|
+
return (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
15457
15989
|
} catch {
|
|
15458
15990
|
return "";
|
|
15459
15991
|
}
|
|
@@ -15468,35 +16000,35 @@ function hasStagedChanges(root) {
|
|
|
15468
16000
|
}
|
|
15469
16001
|
function moveDir(from, to) {
|
|
15470
16002
|
try {
|
|
15471
|
-
(0,
|
|
16003
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15472
16004
|
} catch (err) {
|
|
15473
16005
|
if (err.code !== "EXDEV") throw err;
|
|
15474
|
-
(0,
|
|
15475
|
-
(0,
|
|
16006
|
+
(0, import_node_fs23.cpSync)(from, to, { recursive: true });
|
|
16007
|
+
(0, import_node_fs23.rmSync)(from, { recursive: true, force: true });
|
|
15476
16008
|
}
|
|
15477
16009
|
}
|
|
15478
16010
|
function moveFile(from, to) {
|
|
15479
16011
|
try {
|
|
15480
|
-
(0,
|
|
16012
|
+
(0, import_node_fs23.renameSync)(from, to);
|
|
15481
16013
|
} catch (err) {
|
|
15482
16014
|
if (err.code !== "EXDEV") throw err;
|
|
15483
|
-
(0,
|
|
15484
|
-
(0,
|
|
16015
|
+
(0, import_node_fs23.cpSync)(from, to);
|
|
16016
|
+
(0, import_node_fs23.rmSync)(from, { force: true });
|
|
15485
16017
|
}
|
|
15486
16018
|
}
|
|
15487
16019
|
function carryLegacyContents(gateDir, verityDir) {
|
|
15488
16020
|
let copied = 0;
|
|
15489
16021
|
const walk = (relDir) => {
|
|
15490
|
-
const srcDir = (0,
|
|
15491
|
-
for (const entry of (0,
|
|
15492
|
-
const rel = relDir ? (0,
|
|
15493
|
-
const src = (0,
|
|
15494
|
-
const dest = (0,
|
|
15495
|
-
if ((0,
|
|
16022
|
+
const srcDir = (0, import_node_path16.join)(gateDir, relDir);
|
|
16023
|
+
for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
|
|
16024
|
+
const rel = relDir ? (0, import_node_path16.join)(relDir, entry) : entry;
|
|
16025
|
+
const src = (0, import_node_path16.join)(gateDir, rel);
|
|
16026
|
+
const dest = (0, import_node_path16.join)(verityDir, rel);
|
|
16027
|
+
if ((0, import_node_fs23.statSync)(src).isDirectory()) {
|
|
15496
16028
|
walk(rel);
|
|
15497
|
-
} else if (!(0,
|
|
15498
|
-
(0,
|
|
15499
|
-
(0,
|
|
16029
|
+
} else if (!(0, import_node_fs23.existsSync)(dest)) {
|
|
16030
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
16031
|
+
(0, import_node_fs23.cpSync)(src, dest);
|
|
15500
16032
|
copied++;
|
|
15501
16033
|
}
|
|
15502
16034
|
}
|
|
@@ -15505,22 +16037,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
15505
16037
|
return copied;
|
|
15506
16038
|
}
|
|
15507
16039
|
async function needsMigration(root = repoRoot()) {
|
|
15508
|
-
const gateDir = (0,
|
|
15509
|
-
const verityDir = (0,
|
|
15510
|
-
if ((0,
|
|
15511
|
-
if ((0,
|
|
15512
|
-
if ((0,
|
|
16040
|
+
const gateDir = (0, import_node_path16.join)(root, ".gate");
|
|
16041
|
+
const verityDir = (0, import_node_path16.join)(root, ".verity");
|
|
16042
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
|
|
16043
|
+
if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
|
|
16044
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "credentials"))) {
|
|
15513
16045
|
return true;
|
|
15514
16046
|
}
|
|
15515
|
-
if ((0,
|
|
16047
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "memory"))) {
|
|
15516
16048
|
return true;
|
|
15517
16049
|
}
|
|
15518
16050
|
}
|
|
15519
|
-
const claudeMd = (0,
|
|
15520
|
-
if ((0,
|
|
16051
|
+
const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
|
|
16052
|
+
if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
15521
16053
|
return true;
|
|
15522
16054
|
}
|
|
15523
|
-
if ((0,
|
|
16055
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "VERITY.md"))) {
|
|
15524
16056
|
return true;
|
|
15525
16057
|
}
|
|
15526
16058
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -15546,17 +16078,77 @@ function registerMigrateCommand(program2) {
|
|
|
15546
16078
|
}
|
|
15547
16079
|
|
|
15548
16080
|
// src/commands/init.ts
|
|
16081
|
+
async function promptYes(question) {
|
|
16082
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
16083
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
16084
|
+
try {
|
|
16085
|
+
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
16086
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
16087
|
+
} finally {
|
|
16088
|
+
rl.close();
|
|
16089
|
+
}
|
|
16090
|
+
}
|
|
16091
|
+
async function runOptionalAuth() {
|
|
16092
|
+
const existing = await resolveToken();
|
|
16093
|
+
if (existing.ok) {
|
|
16094
|
+
printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
|
|
16095
|
+
return;
|
|
16096
|
+
}
|
|
16097
|
+
let remote = "";
|
|
16098
|
+
try {
|
|
16099
|
+
remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
16100
|
+
} catch {
|
|
16101
|
+
}
|
|
16102
|
+
const localOnlyNote = () => {
|
|
16103
|
+
printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
|
|
16104
|
+
printInfo(' Authenticate anytime: run "verity init" again, or "verity auth register".');
|
|
16105
|
+
};
|
|
16106
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
16107
|
+
console.log("");
|
|
16108
|
+
console.log(" Signing in is optional. What it does:");
|
|
16109
|
+
console.log(" - Confirms you have write access to this repository. The GitHub token");
|
|
16110
|
+
console.log(" is used once to verify that, then discarded \u2014 Verity never stores it.");
|
|
16111
|
+
console.log(" - It does NOT give Verity access to your code. Code checked by the gate");
|
|
16112
|
+
console.log(" is analyzed in memory and discarded \u2014 we never store your code.");
|
|
16113
|
+
console.log(" - It is required to store and access run history for this repo");
|
|
16114
|
+
console.log(" (past results, trends, and shareable reports).");
|
|
16115
|
+
console.log(" - Skip and Verity still works fully locally: the gate runs and shows");
|
|
16116
|
+
console.log(" findings, but nothing is uploaded.");
|
|
16117
|
+
console.log("");
|
|
16118
|
+
}
|
|
16119
|
+
const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
|
|
16120
|
+
if (!wantsAuth) {
|
|
16121
|
+
printInfo("Skipped authentication.");
|
|
16122
|
+
localOnlyNote();
|
|
16123
|
+
return;
|
|
16124
|
+
}
|
|
16125
|
+
if (!remote) {
|
|
16126
|
+
printWarn("No git remote found \u2014 cannot authenticate yet.");
|
|
16127
|
+
localOnlyNote();
|
|
16128
|
+
return;
|
|
16129
|
+
}
|
|
16130
|
+
const projectName = parseRemote(remote)?.repo ?? (0, import_node_path17.basename)(process.cwd());
|
|
16131
|
+
printInfo("Authenticating with GitHub\u2026");
|
|
16132
|
+
const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
|
|
16133
|
+
if (result.ok) {
|
|
16134
|
+
printInfo(`Project registered: ${result.data.projectId} \u2713`);
|
|
16135
|
+
if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
|
|
16136
|
+
} else {
|
|
16137
|
+
printWarn(`Authentication did not complete: ${result.error}`);
|
|
16138
|
+
localOnlyNote();
|
|
16139
|
+
}
|
|
16140
|
+
}
|
|
15549
16141
|
function resolveDataDir() {
|
|
15550
16142
|
const candidates = [
|
|
15551
|
-
(0,
|
|
16143
|
+
(0, import_node_path17.join)(__dirname, "..", "data"),
|
|
15552
16144
|
// installed: node_modules/@codacy/verity-cli/data
|
|
15553
|
-
(0,
|
|
16145
|
+
(0, import_node_path17.join)(__dirname, "..", "..", "data"),
|
|
15554
16146
|
// edge case: nested resolution
|
|
15555
|
-
(0,
|
|
16147
|
+
(0, import_node_path17.join)(process.cwd(), "cli", "data")
|
|
15556
16148
|
// local dev: running from repo root
|
|
15557
16149
|
];
|
|
15558
16150
|
for (const candidate of candidates) {
|
|
15559
|
-
if ((0,
|
|
16151
|
+
if ((0, import_node_fs24.existsSync)((0, import_node_path17.join)(candidate, "skills"))) {
|
|
15560
16152
|
return candidate;
|
|
15561
16153
|
}
|
|
15562
16154
|
}
|
|
@@ -15572,7 +16164,7 @@ function registerInitCommand(program2) {
|
|
|
15572
16164
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
15573
16165
|
const force = opts.force ?? false;
|
|
15574
16166
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
15575
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
16167
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs24.existsSync)(m));
|
|
15576
16168
|
if (!isProject) {
|
|
15577
16169
|
printError("No project detected in the current directory.");
|
|
15578
16170
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -15635,21 +16227,21 @@ function registerInitCommand(program2) {
|
|
|
15635
16227
|
console.log("");
|
|
15636
16228
|
printInfo("Installing skills...");
|
|
15637
16229
|
const dataDir = resolveDataDir();
|
|
15638
|
-
const skillsSource = (0,
|
|
16230
|
+
const skillsSource = (0, import_node_path17.join)(dataDir, "skills");
|
|
15639
16231
|
const skillsDest = ".claude/skills";
|
|
15640
16232
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
15641
16233
|
let skillsInstalled = 0;
|
|
15642
16234
|
for (const skill of skills) {
|
|
15643
|
-
const src = (0,
|
|
15644
|
-
const dest = (0,
|
|
15645
|
-
if (!(0,
|
|
16235
|
+
const src = (0, import_node_path17.join)(skillsSource, skill);
|
|
16236
|
+
const dest = (0, import_node_path17.join)(skillsDest, skill);
|
|
16237
|
+
if (!(0, import_node_fs24.existsSync)(src)) {
|
|
15646
16238
|
printWarn(` Skill data not found: ${skill}`);
|
|
15647
16239
|
continue;
|
|
15648
16240
|
}
|
|
15649
|
-
if ((0,
|
|
15650
|
-
const srcSkill = (0,
|
|
15651
|
-
const destSkill = (0,
|
|
15652
|
-
if ((0,
|
|
16241
|
+
if ((0, import_node_fs24.existsSync)(dest) && !force) {
|
|
16242
|
+
const srcSkill = (0, import_node_path17.join)(src, "SKILL.md");
|
|
16243
|
+
const destSkill = (0, import_node_path17.join)(dest, "SKILL.md");
|
|
16244
|
+
if ((0, import_node_fs24.existsSync)(destSkill)) {
|
|
15653
16245
|
try {
|
|
15654
16246
|
const srcContent = await (0, import_promises12.readFile)(srcSkill, "utf-8");
|
|
15655
16247
|
const destContent = await (0, import_promises12.readFile)(destSkill, "utf-8");
|
|
@@ -15673,6 +16265,7 @@ function registerInitCommand(program2) {
|
|
|
15673
16265
|
await writeSettings(hookResult.data);
|
|
15674
16266
|
printInfo(" Stop hook: verity analyze \u2713");
|
|
15675
16267
|
printInfo(" Intent hook: verity intent capture \u2713");
|
|
16268
|
+
printInfo(" Baseline hook: verity baseline capture \u2713");
|
|
15676
16269
|
} else {
|
|
15677
16270
|
printWarn(` ${hookResult.error}`);
|
|
15678
16271
|
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
@@ -15685,9 +16278,15 @@ function registerInitCommand(program2) {
|
|
|
15685
16278
|
} catch (err) {
|
|
15686
16279
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
15687
16280
|
}
|
|
15688
|
-
const globalVerityDir = (0,
|
|
16281
|
+
const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
|
|
15689
16282
|
await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
|
|
15690
16283
|
console.log("");
|
|
16284
|
+
try {
|
|
16285
|
+
await runOptionalAuth();
|
|
16286
|
+
} catch (err) {
|
|
16287
|
+
printWarn(`Authentication step skipped: ${err.message}`);
|
|
16288
|
+
}
|
|
16289
|
+
console.log("");
|
|
15691
16290
|
printInfo("Verity initialized!");
|
|
15692
16291
|
console.log("");
|
|
15693
16292
|
console.log(" Installed:");
|
|
@@ -15703,13 +16302,14 @@ function registerInitCommand(program2) {
|
|
|
15703
16302
|
console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
|
|
15704
16303
|
console.log("");
|
|
15705
16304
|
console.log(" Next step: open this project in Claude Code and run /verity-setup");
|
|
16305
|
+
console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity auth register".)');
|
|
15706
16306
|
console.log("");
|
|
15707
16307
|
});
|
|
15708
16308
|
}
|
|
15709
16309
|
|
|
15710
16310
|
// src/commands/uninstall.ts
|
|
15711
|
-
var
|
|
15712
|
-
var
|
|
16311
|
+
var import_node_fs25 = require("node:fs");
|
|
16312
|
+
var import_node_path18 = require("node:path");
|
|
15713
16313
|
var SKILL_NAMES = [
|
|
15714
16314
|
"verity-setup",
|
|
15715
16315
|
"verity-analyze",
|
|
@@ -15728,11 +16328,11 @@ function registerUninstallCommand(program2) {
|
|
|
15728
16328
|
const actions = [];
|
|
15729
16329
|
const skillsRoot = projectPath(".claude/skills");
|
|
15730
16330
|
for (const name of SKILL_NAMES) {
|
|
15731
|
-
const dir = (0,
|
|
15732
|
-
if ((0,
|
|
16331
|
+
const dir = (0, import_node_path18.join)(skillsRoot, name);
|
|
16332
|
+
if ((0, import_node_fs25.existsSync)(dir)) {
|
|
15733
16333
|
actions.push({
|
|
15734
16334
|
label: `Remove .claude/skills/${name}/`,
|
|
15735
|
-
apply: () => (0,
|
|
16335
|
+
apply: () => (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true })
|
|
15736
16336
|
});
|
|
15737
16337
|
}
|
|
15738
16338
|
}
|
|
@@ -15746,24 +16346,24 @@ function registerUninstallCommand(program2) {
|
|
|
15746
16346
|
});
|
|
15747
16347
|
}
|
|
15748
16348
|
const verityDir = projectPath(VERITY_DIR);
|
|
15749
|
-
if ((0,
|
|
16349
|
+
if ((0, import_node_fs25.existsSync)(verityDir)) {
|
|
15750
16350
|
actions.push({
|
|
15751
16351
|
label: `Remove ${VERITY_DIR}/`,
|
|
15752
|
-
apply: () => (0,
|
|
16352
|
+
apply: () => (0, import_node_fs25.rmSync)(verityDir, { recursive: true, force: true })
|
|
15753
16353
|
});
|
|
15754
16354
|
}
|
|
15755
16355
|
if (!keepVerityMd) {
|
|
15756
16356
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
15757
|
-
if ((0,
|
|
16357
|
+
if ((0, import_node_fs25.existsSync)(verityMd)) {
|
|
15758
16358
|
actions.push({
|
|
15759
16359
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
15760
|
-
apply: () => (0,
|
|
16360
|
+
apply: () => (0, import_node_fs25.rmSync)(verityMd, { force: true })
|
|
15761
16361
|
});
|
|
15762
16362
|
}
|
|
15763
16363
|
}
|
|
15764
16364
|
const cleanupEmptyDir = (path) => {
|
|
15765
|
-
if ((0,
|
|
15766
|
-
(0,
|
|
16365
|
+
if ((0, import_node_fs25.existsSync)(path) && (0, import_node_fs25.statSync)(path).isDirectory() && (0, import_node_fs25.readdirSync)(path).length === 0) {
|
|
16366
|
+
(0, import_node_fs25.rmdirSync)(path);
|
|
15767
16367
|
}
|
|
15768
16368
|
};
|
|
15769
16369
|
actions.push({
|
|
@@ -15774,11 +16374,11 @@ function registerUninstallCommand(program2) {
|
|
|
15774
16374
|
}
|
|
15775
16375
|
});
|
|
15776
16376
|
const home = process.env.HOME ?? "";
|
|
15777
|
-
const globalVerityDir = (0,
|
|
15778
|
-
if (purgeGlobal && (0,
|
|
16377
|
+
const globalVerityDir = (0, import_node_path18.join)(home, ".verity");
|
|
16378
|
+
if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
|
|
15779
16379
|
actions.push({
|
|
15780
16380
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
15781
|
-
apply: () => (0,
|
|
16381
|
+
apply: () => (0, import_node_fs25.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
15782
16382
|
});
|
|
15783
16383
|
}
|
|
15784
16384
|
if (actions.length === 0) {
|
|
@@ -15972,8 +16572,8 @@ function registerTaskCommands(program2) {
|
|
|
15972
16572
|
}
|
|
15973
16573
|
|
|
15974
16574
|
// src/commands/reset.ts
|
|
15975
|
-
var
|
|
15976
|
-
var
|
|
16575
|
+
var import_node_fs26 = require("node:fs");
|
|
16576
|
+
var import_node_path19 = require("node:path");
|
|
15977
16577
|
function registerResetCommand(program2) {
|
|
15978
16578
|
program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
|
|
15979
16579
|
const globals = program2.opts();
|
|
@@ -16010,11 +16610,11 @@ function registerResetCommand(program2) {
|
|
|
16010
16610
|
}
|
|
16011
16611
|
const cacheDir = projectPath(CACHE_DIR);
|
|
16012
16612
|
let purged = 0;
|
|
16013
|
-
if ((0,
|
|
16014
|
-
for (const entry of (0,
|
|
16613
|
+
if ((0, import_node_fs26.existsSync)(cacheDir)) {
|
|
16614
|
+
for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
|
|
16015
16615
|
if (entry.startsWith("pending-")) {
|
|
16016
16616
|
try {
|
|
16017
|
-
(0,
|
|
16617
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(cacheDir, entry));
|
|
16018
16618
|
purged++;
|
|
16019
16619
|
} catch {
|
|
16020
16620
|
}
|
|
@@ -16029,19 +16629,19 @@ function registerResetCommand(program2) {
|
|
|
16029
16629
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
16030
16630
|
];
|
|
16031
16631
|
for (const file of filesToClear) {
|
|
16032
|
-
if ((0,
|
|
16632
|
+
if ((0, import_node_fs26.existsSync)(file)) {
|
|
16033
16633
|
try {
|
|
16034
|
-
(0,
|
|
16634
|
+
(0, import_node_fs26.writeFileSync)(file, "");
|
|
16035
16635
|
} catch {
|
|
16036
16636
|
}
|
|
16037
16637
|
}
|
|
16038
16638
|
}
|
|
16039
16639
|
if (opts.all) {
|
|
16040
16640
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
16041
|
-
if ((0,
|
|
16042
|
-
for (const entry of (0,
|
|
16641
|
+
if ((0, import_node_fs26.existsSync)(logsDir)) {
|
|
16642
|
+
for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
|
|
16043
16643
|
try {
|
|
16044
|
-
(0,
|
|
16644
|
+
(0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(logsDir, entry));
|
|
16045
16645
|
} catch {
|
|
16046
16646
|
}
|
|
16047
16647
|
}
|
|
@@ -16300,7 +16900,7 @@ function registerRunCommand(program2) {
|
|
|
16300
16900
|
|
|
16301
16901
|
// src/lib/telemetry.ts
|
|
16302
16902
|
var import_promises13 = require("node:fs/promises");
|
|
16303
|
-
var
|
|
16903
|
+
var import_node_path20 = require("node:path");
|
|
16304
16904
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
16305
16905
|
var GITIGNORE_FILE = ".gitignore";
|
|
16306
16906
|
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
@@ -16338,7 +16938,7 @@ async function readSettingsLocal() {
|
|
|
16338
16938
|
}
|
|
16339
16939
|
async function writeSettingsLocal(settings) {
|
|
16340
16940
|
const file = projectPath(SETTINGS_LOCAL_FILE2);
|
|
16341
|
-
await (0, import_promises13.mkdir)((0,
|
|
16941
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
|
|
16342
16942
|
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
16343
16943
|
}
|
|
16344
16944
|
async function ensureGitignore() {
|
|
@@ -16434,7 +17034,7 @@ function registerTelemetryCommands(program2) {
|
|
|
16434
17034
|
}
|
|
16435
17035
|
|
|
16436
17036
|
// src/cli.ts
|
|
16437
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
17037
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.56ef7d8").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
16438
17038
|
registerAuthCommands(program);
|
|
16439
17039
|
registerHooksCommands(program);
|
|
16440
17040
|
registerIntentCommands(program);
|
|
@@ -16443,6 +17043,7 @@ registerConfigCommands(program);
|
|
|
16443
17043
|
registerStatusCommand(program);
|
|
16444
17044
|
registerFeedbackCommand(program);
|
|
16445
17045
|
registerAnalyzeCommand(program);
|
|
17046
|
+
registerBaselineCommands(program);
|
|
16446
17047
|
registerReviewCommand(program);
|
|
16447
17048
|
registerGuardCommand(program);
|
|
16448
17049
|
registerInitCommand(program);
|