@codacy/verity-cli 0.24.0-experimental.b35bbc7 → 0.24.0-experimental.fe39839
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 +302 -457
- package/data/skills/verity-setup/SKILL.md +4 -11
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10326,8 +10326,8 @@ var {
|
|
|
10326
10326
|
|
|
10327
10327
|
// src/commands/auth.ts
|
|
10328
10328
|
var import_promises3 = require("node:fs/promises");
|
|
10329
|
-
var
|
|
10330
|
-
var
|
|
10329
|
+
var import_node_child_process3 = require("node:child_process");
|
|
10330
|
+
var import_node_path2 = require("node:path");
|
|
10331
10331
|
|
|
10332
10332
|
// src/lib/auth.ts
|
|
10333
10333
|
var import_promises = require("node:fs/promises");
|
|
@@ -10476,10 +10476,6 @@ var SECURITY_PATTERNS = [
|
|
|
10476
10476
|
];
|
|
10477
10477
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10478
10478
|
var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10479
|
-
var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
|
|
10480
|
-
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10481
|
-
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
10482
|
-
var GITHUB_OAUTH_SCOPES = "repo user:email";
|
|
10483
10479
|
|
|
10484
10480
|
// src/lib/auth.ts
|
|
10485
10481
|
async function resolveToken(flagToken) {
|
|
@@ -10645,8 +10641,7 @@ async function apiRequest(options) {
|
|
|
10645
10641
|
timeout = 9e4,
|
|
10646
10642
|
cmd = "unknown",
|
|
10647
10643
|
retry = false,
|
|
10648
|
-
encodeBody = false
|
|
10649
|
-
extraHeaders
|
|
10644
|
+
encodeBody = false
|
|
10650
10645
|
} = options;
|
|
10651
10646
|
const url = `${serviceUrl}${path}`;
|
|
10652
10647
|
const headers = {
|
|
@@ -10655,9 +10650,6 @@ async function apiRequest(options) {
|
|
|
10655
10650
|
if (token) {
|
|
10656
10651
|
headers["Authorization"] = `Bearer ${token}`;
|
|
10657
10652
|
}
|
|
10658
|
-
if (extraHeaders) {
|
|
10659
|
-
Object.assign(headers, extraHeaders);
|
|
10660
|
-
}
|
|
10661
10653
|
const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
|
|
10662
10654
|
if (testMockScenario) {
|
|
10663
10655
|
headers["X-Verity-Mock-Scenario"] = testMockScenario;
|
|
@@ -10748,324 +10740,6 @@ function analyzeRequest(options) {
|
|
|
10748
10740
|
});
|
|
10749
10741
|
}
|
|
10750
10742
|
|
|
10751
|
-
// src/lib/provider-auth.ts
|
|
10752
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10753
|
-
var form = (fields) => new URLSearchParams(fields).toString();
|
|
10754
|
-
async function githubDeviceFlow() {
|
|
10755
|
-
const override = process.env.VERITY_PROVIDER_TOKEN;
|
|
10756
|
-
if (override) return { ok: true, data: override };
|
|
10757
|
-
let dc;
|
|
10758
|
-
try {
|
|
10759
|
-
const res = await fetch(GITHUB_DEVICE_CODE_URL, {
|
|
10760
|
-
method: "POST",
|
|
10761
|
-
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10762
|
-
body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_OAUTH_SCOPES })
|
|
10763
|
-
});
|
|
10764
|
-
if (!res.ok) {
|
|
10765
|
-
return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
|
|
10766
|
-
}
|
|
10767
|
-
dc = await res.json();
|
|
10768
|
-
} catch (err) {
|
|
10769
|
-
return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
|
|
10770
|
-
}
|
|
10771
|
-
if (!dc.device_code || !dc.user_code) {
|
|
10772
|
-
return {
|
|
10773
|
-
ok: false,
|
|
10774
|
-
error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
|
|
10775
|
-
};
|
|
10776
|
-
}
|
|
10777
|
-
printInfo("");
|
|
10778
|
-
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
10779
|
-
printInfo(`And enter the code: ${dc.user_code}`);
|
|
10780
|
-
printInfo("Waiting for authorization\u2026");
|
|
10781
|
-
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
10782
|
-
let interval = dc.interval || 5;
|
|
10783
|
-
while (Date.now() < deadline) {
|
|
10784
|
-
await sleep(interval * 1e3);
|
|
10785
|
-
let data;
|
|
10786
|
-
try {
|
|
10787
|
-
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
10788
|
-
method: "POST",
|
|
10789
|
-
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
10790
|
-
body: form({
|
|
10791
|
-
client_id: GITHUB_CLIENT_ID,
|
|
10792
|
-
device_code: dc.device_code,
|
|
10793
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
10794
|
-
})
|
|
10795
|
-
});
|
|
10796
|
-
data = await res.json().catch(() => ({}));
|
|
10797
|
-
} catch {
|
|
10798
|
-
continue;
|
|
10799
|
-
}
|
|
10800
|
-
if (data.access_token) return { ok: true, data: data.access_token };
|
|
10801
|
-
switch (data.error) {
|
|
10802
|
-
case "authorization_pending":
|
|
10803
|
-
break;
|
|
10804
|
-
case "slow_down":
|
|
10805
|
-
interval += 5;
|
|
10806
|
-
break;
|
|
10807
|
-
case "access_denied":
|
|
10808
|
-
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
10809
|
-
case "expired_token":
|
|
10810
|
-
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
10811
|
-
default:
|
|
10812
|
-
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
10813
|
-
}
|
|
10814
|
-
}
|
|
10815
|
-
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
10816
|
-
}
|
|
10817
|
-
|
|
10818
|
-
// src/lib/git.ts
|
|
10819
|
-
var import_node_child_process3 = require("node:child_process");
|
|
10820
|
-
var import_node_fs2 = require("node:fs");
|
|
10821
|
-
var import_node_path2 = require("node:path");
|
|
10822
|
-
function resolveFile(relpath) {
|
|
10823
|
-
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10824
|
-
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
10825
|
-
try {
|
|
10826
|
-
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10827
|
-
for (const entry of entries) {
|
|
10828
|
-
if (!entry.isDirectory()) continue;
|
|
10829
|
-
const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
|
|
10830
|
-
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10831
|
-
}
|
|
10832
|
-
} catch {
|
|
10833
|
-
}
|
|
10834
|
-
}
|
|
10835
|
-
return null;
|
|
10836
|
-
}
|
|
10837
|
-
function execGit(cmd) {
|
|
10838
|
-
try {
|
|
10839
|
-
return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10840
|
-
} catch {
|
|
10841
|
-
return "";
|
|
10842
|
-
}
|
|
10843
|
-
}
|
|
10844
|
-
function splitLines(s) {
|
|
10845
|
-
return s.split("\n").filter((l) => l.length > 0);
|
|
10846
|
-
}
|
|
10847
|
-
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
10848
|
-
function readBaselineSha() {
|
|
10849
|
-
if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
10850
|
-
let sha;
|
|
10851
|
-
try {
|
|
10852
|
-
sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10853
|
-
} catch {
|
|
10854
|
-
return null;
|
|
10855
|
-
}
|
|
10856
|
-
if (!SHA_RE.test(sha)) return null;
|
|
10857
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10858
|
-
if (!reachable) {
|
|
10859
|
-
try {
|
|
10860
|
-
(0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
|
|
10861
|
-
} catch {
|
|
10862
|
-
}
|
|
10863
|
-
return null;
|
|
10864
|
-
}
|
|
10865
|
-
return sha;
|
|
10866
|
-
}
|
|
10867
|
-
function writeBaselineSha(sha) {
|
|
10868
|
-
if (!SHA_RE.test(sha)) return;
|
|
10869
|
-
try {
|
|
10870
|
-
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10871
|
-
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10872
|
-
} catch {
|
|
10873
|
-
}
|
|
10874
|
-
}
|
|
10875
|
-
function getChangedFiles() {
|
|
10876
|
-
const sets = /* @__PURE__ */ new Set();
|
|
10877
|
-
let hasRecentCommitFiles = false;
|
|
10878
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
10879
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
10880
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
10881
|
-
const baseline = readBaselineSha();
|
|
10882
|
-
if (baseline) {
|
|
10883
|
-
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
10884
|
-
if (committed.length > 0) {
|
|
10885
|
-
hasRecentCommitFiles = true;
|
|
10886
|
-
for (const f of committed) sets.add(f);
|
|
10887
|
-
}
|
|
10888
|
-
} else {
|
|
10889
|
-
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
10890
|
-
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
10891
|
-
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
10892
|
-
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
10893
|
-
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
10894
|
-
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
10895
|
-
if (recentFiles.length > 0) {
|
|
10896
|
-
hasRecentCommitFiles = true;
|
|
10897
|
-
for (const f of recentFiles) sets.add(f);
|
|
10898
|
-
}
|
|
10899
|
-
}
|
|
10900
|
-
}
|
|
10901
|
-
for (const f of getWorktreeFiles()) sets.add(f);
|
|
10902
|
-
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10903
|
-
return { files: filtered, hasRecentCommitFiles };
|
|
10904
|
-
}
|
|
10905
|
-
function getStagedFiles() {
|
|
10906
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10907
|
-
}
|
|
10908
|
-
function getDirtyFiles() {
|
|
10909
|
-
const set = /* @__PURE__ */ new Set();
|
|
10910
|
-
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
10911
|
-
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
10912
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
10913
|
-
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10914
|
-
}
|
|
10915
|
-
function showContentAtRef(ref, repoRelPath) {
|
|
10916
|
-
if (!ref || ref === "no-git") return null;
|
|
10917
|
-
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
10918
|
-
try {
|
|
10919
|
-
return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
10920
|
-
encoding: "utf-8",
|
|
10921
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
10922
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10923
|
-
});
|
|
10924
|
-
} catch {
|
|
10925
|
-
return null;
|
|
10926
|
-
}
|
|
10927
|
-
}
|
|
10928
|
-
function getPushRangeFiles() {
|
|
10929
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10930
|
-
const resolvers = [
|
|
10931
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
10932
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
10933
|
-
() => {
|
|
10934
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
10935
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
10936
|
-
}
|
|
10937
|
-
];
|
|
10938
|
-
for (const resolve of resolvers) {
|
|
10939
|
-
const range = resolve();
|
|
10940
|
-
if (range) return { files: diff(range), range };
|
|
10941
|
-
}
|
|
10942
|
-
const baseline = readBaselineSha();
|
|
10943
|
-
if (baseline) {
|
|
10944
|
-
const files = diff(`${baseline}..HEAD`);
|
|
10945
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10946
|
-
}
|
|
10947
|
-
const last = diff("HEAD~1..HEAD");
|
|
10948
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10949
|
-
}
|
|
10950
|
-
function getPushRangeMessages() {
|
|
10951
|
-
const { range } = getPushRangeFiles();
|
|
10952
|
-
if (!range) return "";
|
|
10953
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10954
|
-
}
|
|
10955
|
-
function getWorktreeFiles() {
|
|
10956
|
-
const result = [];
|
|
10957
|
-
const worktreeDir = ".claude/worktrees";
|
|
10958
|
-
if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
|
|
10959
|
-
try {
|
|
10960
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
10961
|
-
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
10962
|
-
for (const entry of entries) {
|
|
10963
|
-
if (!entry.isDirectory()) continue;
|
|
10964
|
-
const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
|
|
10965
|
-
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
10966
|
-
}
|
|
10967
|
-
} catch {
|
|
10968
|
-
}
|
|
10969
|
-
return result;
|
|
10970
|
-
}
|
|
10971
|
-
function scanDir(baseDir, dir, minMtime, result) {
|
|
10972
|
-
try {
|
|
10973
|
-
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
10974
|
-
for (const entry of entries) {
|
|
10975
|
-
const fullPath = (0, import_node_path2.join)(dir, entry.name);
|
|
10976
|
-
if (entry.isDirectory()) {
|
|
10977
|
-
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
10978
|
-
scanDir(baseDir, fullPath, minMtime, result);
|
|
10979
|
-
} else if (entry.isFile()) {
|
|
10980
|
-
const ext = (0, import_node_path2.extname)(entry.name).slice(1);
|
|
10981
|
-
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
10982
|
-
try {
|
|
10983
|
-
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
10984
|
-
if (stat3.mtimeMs >= minMtime) {
|
|
10985
|
-
const relPath = fullPath.slice(baseDir.length + 1);
|
|
10986
|
-
result.push(relPath);
|
|
10987
|
-
}
|
|
10988
|
-
} catch {
|
|
10989
|
-
}
|
|
10990
|
-
}
|
|
10991
|
-
}
|
|
10992
|
-
} catch {
|
|
10993
|
-
}
|
|
10994
|
-
}
|
|
10995
|
-
function filterAnalyzable(files) {
|
|
10996
|
-
return files.filter((f) => {
|
|
10997
|
-
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
10998
|
-
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
10999
|
-
});
|
|
11000
|
-
}
|
|
11001
|
-
function filterReviewable(files) {
|
|
11002
|
-
return files.filter((f) => {
|
|
11003
|
-
const ext = (0, import_node_path2.extname)(f).slice(1);
|
|
11004
|
-
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11005
|
-
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11006
|
-
const basename2 = f.split("/").pop() ?? "";
|
|
11007
|
-
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
11008
|
-
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
11009
|
-
return false;
|
|
11010
|
-
});
|
|
11011
|
-
}
|
|
11012
|
-
function filterSecurity(files) {
|
|
11013
|
-
return files.filter(
|
|
11014
|
-
(f) => SECURITY_PATTERNS.some((p) => p.test(f))
|
|
11015
|
-
);
|
|
11016
|
-
}
|
|
11017
|
-
function getCurrentCommit() {
|
|
11018
|
-
return execGit("git rev-parse HEAD") || "no-git";
|
|
11019
|
-
}
|
|
11020
|
-
function detectProvider(host) {
|
|
11021
|
-
const h = host.toLowerCase();
|
|
11022
|
-
if (h.includes("github")) return "github";
|
|
11023
|
-
if (h.includes("gitlab")) return "gitlab";
|
|
11024
|
-
if (h.includes("bitbucket")) return "bitbucket";
|
|
11025
|
-
return "unknown";
|
|
11026
|
-
}
|
|
11027
|
-
function parseRemote(raw) {
|
|
11028
|
-
if (!raw || typeof raw !== "string") return null;
|
|
11029
|
-
let s = raw.trim();
|
|
11030
|
-
if (!s) return null;
|
|
11031
|
-
let host = "";
|
|
11032
|
-
let path = "";
|
|
11033
|
-
const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
|
|
11034
|
-
if (scp) {
|
|
11035
|
-
host = scp[1];
|
|
11036
|
-
path = scp[2];
|
|
11037
|
-
} else {
|
|
11038
|
-
s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
|
|
11039
|
-
s = s.replace(/^[^/@]+@/, "");
|
|
11040
|
-
const slash = s.indexOf("/");
|
|
11041
|
-
if (slash === -1) return null;
|
|
11042
|
-
host = s.slice(0, slash);
|
|
11043
|
-
path = s.slice(slash + 1);
|
|
11044
|
-
}
|
|
11045
|
-
host = host.toLowerCase().trim();
|
|
11046
|
-
path = path.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
11047
|
-
if (!host || !path) return null;
|
|
11048
|
-
const segments = path.split("/").filter(Boolean);
|
|
11049
|
-
if (segments.length < 2) return null;
|
|
11050
|
-
const owner = segments[0];
|
|
11051
|
-
const repo = segments[segments.length - 1];
|
|
11052
|
-
if (!owner || !repo) return null;
|
|
11053
|
-
return {
|
|
11054
|
-
host,
|
|
11055
|
-
owner,
|
|
11056
|
-
repo,
|
|
11057
|
-
provider: detectProvider(host),
|
|
11058
|
-
orgUrl: `https://${host}/${owner}`,
|
|
11059
|
-
orgName: owner
|
|
11060
|
-
};
|
|
11061
|
-
}
|
|
11062
|
-
function listTrackedFiles() {
|
|
11063
|
-
const set = /* @__PURE__ */ new Set();
|
|
11064
|
-
for (const f of splitLines(execGit("git ls-files"))) set.add(f);
|
|
11065
|
-
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
11066
|
-
return Array.from(set);
|
|
11067
|
-
}
|
|
11068
|
-
|
|
11069
10743
|
// src/commands/auth.ts
|
|
11070
10744
|
function registerAuthCommands(program2) {
|
|
11071
10745
|
const auth = program2.command("auth").description("Manage project authentication");
|
|
@@ -11075,65 +10749,35 @@ function registerAuthCommands(program2) {
|
|
|
11075
10749
|
let remote = opts.remote;
|
|
11076
10750
|
if (!remote) {
|
|
11077
10751
|
try {
|
|
11078
|
-
remote = (0,
|
|
10752
|
+
remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
11079
10753
|
} catch {
|
|
11080
10754
|
printError("No git remote found. Use --remote to specify one.");
|
|
11081
10755
|
process.exit(1);
|
|
11082
10756
|
}
|
|
11083
10757
|
}
|
|
11084
|
-
const parsed = parseRemote(remote);
|
|
11085
|
-
if (!parsed) {
|
|
11086
|
-
printError(`Could not parse git remote: ${remote}`);
|
|
11087
|
-
process.exit(1);
|
|
11088
|
-
}
|
|
11089
|
-
if (parsed.provider !== "github") {
|
|
11090
|
-
printError(`Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.`);
|
|
11091
|
-
process.exit(1);
|
|
11092
|
-
}
|
|
11093
|
-
const providerAuth = await githubDeviceFlow();
|
|
11094
|
-
if (!providerAuth.ok) {
|
|
11095
|
-
printError(providerAuth.error);
|
|
11096
|
-
process.exit(1);
|
|
11097
|
-
}
|
|
11098
|
-
const providerToken = providerAuth.data;
|
|
11099
10758
|
const result = await apiRequest({
|
|
11100
10759
|
method: "POST",
|
|
11101
10760
|
path: "/auth/register",
|
|
11102
10761
|
serviceUrl,
|
|
11103
10762
|
body: { project_name: opts.project, git_remote_url: remote },
|
|
11104
|
-
extraHeaders: { "X-Provider-Token": providerToken },
|
|
11105
10763
|
verbose: globals.verbose
|
|
11106
10764
|
});
|
|
11107
10765
|
if (!result.ok) {
|
|
11108
10766
|
printError(result.error);
|
|
11109
10767
|
process.exit(1);
|
|
11110
10768
|
}
|
|
11111
|
-
const { project_id, token, service_url
|
|
10769
|
+
const { project_id, token, service_url } = result.data;
|
|
11112
10770
|
await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
|
|
11113
|
-
await (0, import_promises3.writeFile)(
|
|
11114
|
-
CREDENTIALS_FILE,
|
|
11115
|
-
`token: ${token}
|
|
10771
|
+
await (0, import_promises3.writeFile)(CREDENTIALS_FILE, `token: ${token}
|
|
11116
10772
|
service_url: ${service_url}
|
|
11117
|
-
|
|
11118
|
-
`,
|
|
11119
|
-
{ mode: 384 }
|
|
11120
|
-
);
|
|
11121
|
-
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11122
|
-
});
|
|
10773
|
+
`);
|
|
11123
10774
|
try {
|
|
11124
|
-
await (0, import_promises3.mkdir)((0,
|
|
11125
|
-
await (0, import_promises3.appendFile)(
|
|
11126
|
-
|
|
11127
|
-
`${remote} token: ${token}
|
|
11128
|
-
${remote} provider_token: ${providerToken}
|
|
11129
|
-
`
|
|
11130
|
-
);
|
|
11131
|
-
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11132
|
-
});
|
|
10775
|
+
await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
10776
|
+
await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
|
|
10777
|
+
`);
|
|
11133
10778
|
} catch {
|
|
11134
10779
|
}
|
|
11135
10780
|
printInfo(`Project registered: ${project_id}`);
|
|
11136
|
-
if (user?.email) printInfo(`Authenticated as: ${user.email}`);
|
|
11137
10781
|
printJson({ project_id, service_url });
|
|
11138
10782
|
});
|
|
11139
10783
|
auth.command("verify").description("Verify the current token is valid").action(async () => {
|
|
@@ -11167,7 +10811,7 @@ ${remote} provider_token: ${providerToken}
|
|
|
11167
10811
|
let remote = opts.remote;
|
|
11168
10812
|
if (!remote) {
|
|
11169
10813
|
try {
|
|
11170
|
-
remote = (0,
|
|
10814
|
+
remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
|
|
11171
10815
|
} catch {
|
|
11172
10816
|
printError("No git remote found. Use --remote to specify one.");
|
|
11173
10817
|
process.exit(1);
|
|
@@ -11195,7 +10839,7 @@ ${remote} provider_token: ${providerToken}
|
|
|
11195
10839
|
|
|
11196
10840
|
// src/lib/hooks.ts
|
|
11197
10841
|
var import_promises4 = require("node:fs/promises");
|
|
11198
|
-
var
|
|
10842
|
+
var import_node_path3 = require("node:path");
|
|
11199
10843
|
var VERITY_STOP_HOOK = {
|
|
11200
10844
|
type: "command",
|
|
11201
10845
|
command: "verity analyze",
|
|
@@ -11347,19 +10991,19 @@ async function checkAllVerityHooksDetailed() {
|
|
|
11347
10991
|
return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
|
|
11348
10992
|
}
|
|
11349
10993
|
async function writeSettings(settings) {
|
|
11350
|
-
await (0, import_promises4.mkdir)((0,
|
|
10994
|
+
await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
|
|
11351
10995
|
await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
11352
10996
|
}
|
|
11353
10997
|
async function readSettingsAt(root) {
|
|
11354
10998
|
try {
|
|
11355
|
-
return JSON.parse(await (0, import_promises4.readFile)((0,
|
|
10999
|
+
return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path3.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
|
|
11356
11000
|
} catch {
|
|
11357
11001
|
return {};
|
|
11358
11002
|
}
|
|
11359
11003
|
}
|
|
11360
11004
|
async function writeSettingsAt(root, settings) {
|
|
11361
|
-
const file = (0,
|
|
11362
|
-
await (0, import_promises4.mkdir)((0,
|
|
11005
|
+
const file = (0, import_node_path3.join)(root, CLAUDE_SETTINGS_FILE);
|
|
11006
|
+
await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(file), { recursive: true });
|
|
11363
11007
|
await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
11364
11008
|
}
|
|
11365
11009
|
async function hasLegacyHooksAt(root) {
|
|
@@ -11600,8 +11244,8 @@ var import_node_crypto2 = require("node:crypto");
|
|
|
11600
11244
|
|
|
11601
11245
|
// src/lib/conversation-buffer.ts
|
|
11602
11246
|
var import_promises5 = require("node:fs/promises");
|
|
11603
|
-
var
|
|
11604
|
-
var
|
|
11247
|
+
var import_node_fs2 = require("node:fs");
|
|
11248
|
+
var import_node_child_process4 = require("node:child_process");
|
|
11605
11249
|
function stripImageReferences(text) {
|
|
11606
11250
|
return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
|
|
11607
11251
|
}
|
|
@@ -11632,7 +11276,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11632
11276
|
}
|
|
11633
11277
|
async function readAndClearConversationBuffer(currentSessionId) {
|
|
11634
11278
|
try {
|
|
11635
|
-
if ((0,
|
|
11279
|
+
if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
11636
11280
|
const entries = await readBufferEntries();
|
|
11637
11281
|
let mine = entries;
|
|
11638
11282
|
let others = [];
|
|
@@ -11656,7 +11300,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11656
11300
|
};
|
|
11657
11301
|
}
|
|
11658
11302
|
}
|
|
11659
|
-
if ((0,
|
|
11303
|
+
if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
|
|
11660
11304
|
try {
|
|
11661
11305
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
11662
11306
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -11700,7 +11344,7 @@ async function readBufferEntries() {
|
|
|
11700
11344
|
}
|
|
11701
11345
|
function getRecentCommitMessages() {
|
|
11702
11346
|
try {
|
|
11703
|
-
const output = (0,
|
|
11347
|
+
const output = (0, import_node_child_process4.execSync)(
|
|
11704
11348
|
'git log --since="30 minutes ago" --format="%s" -5',
|
|
11705
11349
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
11706
11350
|
).trim();
|
|
@@ -11713,8 +11357,8 @@ function getRecentCommitMessages() {
|
|
|
11713
11357
|
|
|
11714
11358
|
// src/lib/task-context-buffer.ts
|
|
11715
11359
|
var import_promises6 = require("node:fs/promises");
|
|
11716
|
-
var
|
|
11717
|
-
var
|
|
11360
|
+
var import_node_fs3 = require("node:fs");
|
|
11361
|
+
var import_node_path4 = require("node:path");
|
|
11718
11362
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
11719
11363
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
11720
11364
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -11753,7 +11397,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
11753
11397
|
}
|
|
11754
11398
|
async function readTaskContextBuffer(taskId) {
|
|
11755
11399
|
const filePath = bufferPath(taskId);
|
|
11756
|
-
if (!(0,
|
|
11400
|
+
if (!(0, import_node_fs3.existsSync)(filePath)) return null;
|
|
11757
11401
|
try {
|
|
11758
11402
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
11759
11403
|
if (!content.trim()) return null;
|
|
@@ -11787,12 +11431,12 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11787
11431
|
}
|
|
11788
11432
|
async function cleanupTaskContextBuffers() {
|
|
11789
11433
|
try {
|
|
11790
|
-
if (!(0,
|
|
11434
|
+
if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
11791
11435
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
11792
11436
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
11793
11437
|
for (const file of files) {
|
|
11794
11438
|
if (!file.endsWith(".jsonl")) continue;
|
|
11795
|
-
const filePath = (0,
|
|
11439
|
+
const filePath = (0, import_node_path4.join)(TASK_CONTEXT_DIR, file);
|
|
11796
11440
|
try {
|
|
11797
11441
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11798
11442
|
if (stats.mtimeMs < cutoffMs) {
|
|
@@ -11806,13 +11450,13 @@ async function cleanupTaskContextBuffers() {
|
|
|
11806
11450
|
}
|
|
11807
11451
|
function bufferPath(taskId) {
|
|
11808
11452
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
11809
|
-
return (0,
|
|
11453
|
+
return (0, import_node_path4.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
11810
11454
|
}
|
|
11811
11455
|
async function appendEntry(taskId, entry) {
|
|
11812
11456
|
try {
|
|
11813
11457
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
11814
11458
|
const filePath = bufferPath(taskId);
|
|
11815
|
-
if ((0,
|
|
11459
|
+
if ((0, import_node_fs3.existsSync)(filePath)) {
|
|
11816
11460
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
11817
11461
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
11818
11462
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -11823,7 +11467,7 @@ async function appendEntry(taskId, entry) {
|
|
|
11823
11467
|
}
|
|
11824
11468
|
}
|
|
11825
11469
|
const line = JSON.stringify(entry) + "\n";
|
|
11826
|
-
const existing = (0,
|
|
11470
|
+
const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
11827
11471
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
11828
11472
|
} catch {
|
|
11829
11473
|
}
|
|
@@ -11831,8 +11475,8 @@ async function appendEntry(taskId, entry) {
|
|
|
11831
11475
|
|
|
11832
11476
|
// src/lib/memory-retrieval.ts
|
|
11833
11477
|
var import_promises7 = require("node:fs/promises");
|
|
11834
|
-
var
|
|
11835
|
-
var
|
|
11478
|
+
var import_node_fs4 = require("node:fs");
|
|
11479
|
+
var import_node_path5 = require("node:path");
|
|
11836
11480
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
11837
11481
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
11838
11482
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
@@ -11925,19 +11569,19 @@ function parseFrontmatter(content) {
|
|
|
11925
11569
|
return { fm, body: match[2].trim() };
|
|
11926
11570
|
}
|
|
11927
11571
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
11928
|
-
if (!(0,
|
|
11572
|
+
if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
|
|
11929
11573
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
11930
11574
|
const promptTokens = tokenize(promptText);
|
|
11931
11575
|
const nodes = [];
|
|
11932
11576
|
for (const domain of DOMAINS) {
|
|
11933
|
-
const domainDir = (0,
|
|
11934
|
-
if (!(0,
|
|
11577
|
+
const domainDir = (0, import_node_path5.join)(memoryDir(), domain);
|
|
11578
|
+
if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
|
|
11935
11579
|
try {
|
|
11936
11580
|
const files = await (0, import_promises7.readdir)(domainDir);
|
|
11937
11581
|
for (const file of files) {
|
|
11938
11582
|
if (!file.endsWith(".md")) continue;
|
|
11939
11583
|
try {
|
|
11940
|
-
const content = await (0, import_promises7.readFile)((0,
|
|
11584
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path5.join)(domainDir, file), "utf-8");
|
|
11941
11585
|
const { fm, body } = parseFrontmatter(content);
|
|
11942
11586
|
if (fm.status && fm.status !== "active") continue;
|
|
11943
11587
|
nodes.push({
|
|
@@ -11996,8 +11640,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
11996
11640
|
|
|
11997
11641
|
// src/lib/memory-sync.ts
|
|
11998
11642
|
var import_promises8 = require("node:fs/promises");
|
|
11999
|
-
var
|
|
12000
|
-
var
|
|
11643
|
+
var import_node_fs5 = require("node:fs");
|
|
11644
|
+
var import_node_path6 = require("node:path");
|
|
12001
11645
|
var import_node_crypto = require("node:crypto");
|
|
12002
11646
|
|
|
12003
11647
|
// src/lib/glob-match.ts
|
|
@@ -12068,32 +11712,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
|
12068
11712
|
async function ensureMemoryDir() {
|
|
12069
11713
|
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
12070
11714
|
for (const domain of DOMAINS2) {
|
|
12071
|
-
await (0, import_promises8.mkdir)((0,
|
|
11715
|
+
await (0, import_promises8.mkdir)((0, import_node_path6.join)(memoryDir2(), domain), { recursive: true });
|
|
12072
11716
|
}
|
|
12073
|
-
if (!(0,
|
|
12074
|
-
await (0, import_promises8.writeFile)((0,
|
|
11717
|
+
if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
11718
|
+
await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
12075
11719
|
}
|
|
12076
|
-
if (!(0,
|
|
12077
|
-
await (0, import_promises8.writeFile)((0,
|
|
11720
|
+
if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "index.md"))) {
|
|
11721
|
+
await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
12078
11722
|
}
|
|
12079
|
-
if (!(0,
|
|
12080
|
-
await (0, import_promises8.writeFile)((0,
|
|
11723
|
+
if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "log.md"))) {
|
|
11724
|
+
await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
12081
11725
|
}
|
|
12082
11726
|
}
|
|
12083
11727
|
async function buildManifest() {
|
|
12084
|
-
if (!(0,
|
|
11728
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
|
|
12085
11729
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
12086
11730
|
}
|
|
12087
11731
|
const nodes = [];
|
|
12088
11732
|
for (const domain of DOMAINS2) {
|
|
12089
|
-
const domainDir = (0,
|
|
12090
|
-
if (!(0,
|
|
11733
|
+
const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
|
|
11734
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
12091
11735
|
try {
|
|
12092
11736
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12093
11737
|
for (const file of files) {
|
|
12094
11738
|
if (!file.endsWith(".md")) continue;
|
|
12095
11739
|
const filePath = `${domain}/${file}`;
|
|
12096
|
-
const fullPath = (0,
|
|
11740
|
+
const fullPath = (0, import_node_path6.join)(memoryDir2(), filePath);
|
|
12097
11741
|
try {
|
|
12098
11742
|
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
12099
11743
|
const hash = (0, import_node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
@@ -12106,13 +11750,13 @@ async function buildManifest() {
|
|
|
12106
11750
|
}
|
|
12107
11751
|
let indexHash = null;
|
|
12108
11752
|
try {
|
|
12109
|
-
const indexContent = await (0, import_promises8.readFile)((0,
|
|
11753
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "index.md"), "utf-8");
|
|
12110
11754
|
indexHash = `sha256:${(0, import_node_crypto.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
12111
11755
|
} catch {
|
|
12112
11756
|
}
|
|
12113
11757
|
let logLength = 0;
|
|
12114
11758
|
try {
|
|
12115
|
-
const logContent = await (0, import_promises8.readFile)((0,
|
|
11759
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "utf-8");
|
|
12116
11760
|
logLength = logContent.split("\n").length;
|
|
12117
11761
|
} catch {
|
|
12118
11762
|
}
|
|
@@ -12123,15 +11767,15 @@ function hashContent(content) {
|
|
|
12123
11767
|
}
|
|
12124
11768
|
async function readOnDiskNodes() {
|
|
12125
11769
|
const out = /* @__PURE__ */ new Map();
|
|
12126
|
-
if (!(0,
|
|
11770
|
+
if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
|
|
12127
11771
|
for (const domain of DOMAINS2) {
|
|
12128
|
-
const domainDir = (0,
|
|
12129
|
-
if (!(0,
|
|
11772
|
+
const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
|
|
11773
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
12130
11774
|
try {
|
|
12131
11775
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
12132
11776
|
if (!file.endsWith(".md")) continue;
|
|
12133
11777
|
try {
|
|
12134
|
-
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0,
|
|
11778
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8")));
|
|
12135
11779
|
} catch {
|
|
12136
11780
|
}
|
|
12137
11781
|
}
|
|
@@ -12177,8 +11821,8 @@ async function computeEditedNodeUploads() {
|
|
|
12177
11821
|
const uploads = [];
|
|
12178
11822
|
for (const [path, prevHash] of prev) {
|
|
12179
11823
|
if (prevHash == null) continue;
|
|
12180
|
-
const full = (0,
|
|
12181
|
-
if (!(0,
|
|
11824
|
+
const full = (0, import_node_path6.join)(memoryDir2(), path);
|
|
11825
|
+
if (!(0, import_node_fs5.existsSync)(full)) continue;
|
|
12182
11826
|
let content;
|
|
12183
11827
|
try {
|
|
12184
11828
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -12214,15 +11858,15 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
12214
11858
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
12215
11859
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
12216
11860
|
try {
|
|
12217
|
-
const existing = (0,
|
|
12218
|
-
await (0, import_promises8.writeFile)((0,
|
|
11861
|
+
const existing = (0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
11862
|
+
await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
12219
11863
|
} catch {
|
|
12220
11864
|
}
|
|
12221
11865
|
await recordSyncedNodePaths();
|
|
12222
11866
|
return count;
|
|
12223
11867
|
}
|
|
12224
11868
|
async function applyOneWrite(write, treePaths) {
|
|
12225
|
-
const fullPath = (0,
|
|
11869
|
+
const fullPath = (0, import_node_path6.join)(memoryDir2(), write.path);
|
|
12226
11870
|
const notes = [];
|
|
12227
11871
|
let content = write.content;
|
|
12228
11872
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -12232,7 +11876,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12232
11876
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
12233
11877
|
}
|
|
12234
11878
|
}
|
|
12235
|
-
if ((0,
|
|
11879
|
+
if ((0, import_node_fs5.existsSync)(fullPath)) {
|
|
12236
11880
|
let existing = "";
|
|
12237
11881
|
try {
|
|
12238
11882
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -12244,7 +11888,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12244
11888
|
return { written: false, notes };
|
|
12245
11889
|
}
|
|
12246
11890
|
}
|
|
12247
|
-
await (0, import_promises8.mkdir)((0,
|
|
11891
|
+
await (0, import_promises8.mkdir)((0, import_node_path6.dirname)(fullPath), { recursive: true });
|
|
12248
11892
|
await (0, import_promises8.writeFile)(fullPath, content);
|
|
12249
11893
|
return { written: true, notes };
|
|
12250
11894
|
}
|
|
@@ -12285,8 +11929,8 @@ async function regenerateIndex() {
|
|
|
12285
11929
|
];
|
|
12286
11930
|
let totalNodes = 0;
|
|
12287
11931
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
12288
|
-
const domainDir = (0,
|
|
12289
|
-
if (!(0,
|
|
11932
|
+
const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
|
|
11933
|
+
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
12290
11934
|
try {
|
|
12291
11935
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12292
11936
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -12296,7 +11940,7 @@ async function regenerateIndex() {
|
|
|
12296
11940
|
for (const file of mdFiles.sort()) {
|
|
12297
11941
|
const slug = file.replace(/\.md$/, "");
|
|
12298
11942
|
try {
|
|
12299
|
-
const content = await (0, import_promises8.readFile)((0,
|
|
11943
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
|
|
12300
11944
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
12301
11945
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
12302
11946
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -12320,7 +11964,7 @@ async function regenerateIndex() {
|
|
|
12320
11964
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
12321
11965
|
}
|
|
12322
11966
|
const next = lines.join("\n") + "\n";
|
|
12323
|
-
const indexPath = (0,
|
|
11967
|
+
const indexPath = (0, import_node_path6.join)(memoryDir2(), "index.md");
|
|
12324
11968
|
let existing = null;
|
|
12325
11969
|
try {
|
|
12326
11970
|
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
@@ -12402,9 +12046,9 @@ function hasLegacyMemoryBlock(text) {
|
|
|
12402
12046
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
12403
12047
|
}
|
|
12404
12048
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
12405
|
-
const claudeMdPath = (0,
|
|
12049
|
+
const claudeMdPath = (0, import_node_path6.join)(cwd, "CLAUDE.md");
|
|
12406
12050
|
let existing = "";
|
|
12407
|
-
if ((0,
|
|
12051
|
+
if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
|
|
12408
12052
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12409
12053
|
}
|
|
12410
12054
|
let startTag = CLAUDE_MD_START;
|
|
@@ -12534,7 +12178,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
12534
12178
|
`;
|
|
12535
12179
|
|
|
12536
12180
|
// src/commands/intent.ts
|
|
12537
|
-
var
|
|
12181
|
+
var import_node_fs6 = require("node:fs");
|
|
12538
12182
|
function registerIntentCommands(program2) {
|
|
12539
12183
|
const intent = program2.command("intent").description("Manage intent capture");
|
|
12540
12184
|
intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
|
|
@@ -12543,7 +12187,7 @@ function registerIntentCommands(program2) {
|
|
|
12543
12187
|
process.chdir(repoRoot());
|
|
12544
12188
|
} catch {
|
|
12545
12189
|
}
|
|
12546
|
-
if (!(0,
|
|
12190
|
+
if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
|
|
12547
12191
|
process.exit(0);
|
|
12548
12192
|
}
|
|
12549
12193
|
const chunks = [];
|
|
@@ -13122,6 +12766,215 @@ async function sendGeneralFeedback(message, opts, globals) {
|
|
|
13122
12766
|
var import_node_fs19 = require("node:fs");
|
|
13123
12767
|
var import_node_path14 = require("node:path");
|
|
13124
12768
|
|
|
12769
|
+
// src/lib/git.ts
|
|
12770
|
+
var import_node_child_process5 = require("node:child_process");
|
|
12771
|
+
var import_node_fs7 = require("node:fs");
|
|
12772
|
+
var import_node_path7 = require("node:path");
|
|
12773
|
+
function resolveFile(relpath) {
|
|
12774
|
+
if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
|
|
12775
|
+
if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
|
|
12776
|
+
try {
|
|
12777
|
+
const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
12778
|
+
for (const entry of entries) {
|
|
12779
|
+
if (!entry.isDirectory()) continue;
|
|
12780
|
+
const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
|
|
12781
|
+
if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
|
|
12782
|
+
}
|
|
12783
|
+
} catch {
|
|
12784
|
+
}
|
|
12785
|
+
}
|
|
12786
|
+
return null;
|
|
12787
|
+
}
|
|
12788
|
+
function execGit(cmd) {
|
|
12789
|
+
try {
|
|
12790
|
+
return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12791
|
+
} catch {
|
|
12792
|
+
return "";
|
|
12793
|
+
}
|
|
12794
|
+
}
|
|
12795
|
+
function splitLines(s) {
|
|
12796
|
+
return s.split("\n").filter((l) => l.length > 0);
|
|
12797
|
+
}
|
|
12798
|
+
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
12799
|
+
function readBaselineSha() {
|
|
12800
|
+
if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
12801
|
+
let sha;
|
|
12802
|
+
try {
|
|
12803
|
+
sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
12804
|
+
} catch {
|
|
12805
|
+
return null;
|
|
12806
|
+
}
|
|
12807
|
+
if (!SHA_RE.test(sha)) return null;
|
|
12808
|
+
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
12809
|
+
if (!reachable) {
|
|
12810
|
+
try {
|
|
12811
|
+
(0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
|
|
12812
|
+
} catch {
|
|
12813
|
+
}
|
|
12814
|
+
return null;
|
|
12815
|
+
}
|
|
12816
|
+
return sha;
|
|
12817
|
+
}
|
|
12818
|
+
function writeBaselineSha(sha) {
|
|
12819
|
+
if (!SHA_RE.test(sha)) return;
|
|
12820
|
+
try {
|
|
12821
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
12822
|
+
(0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
12823
|
+
} catch {
|
|
12824
|
+
}
|
|
12825
|
+
}
|
|
12826
|
+
function getChangedFiles() {
|
|
12827
|
+
const sets = /* @__PURE__ */ new Set();
|
|
12828
|
+
let hasRecentCommitFiles = false;
|
|
12829
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
12830
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
12831
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
12832
|
+
const baseline = readBaselineSha();
|
|
12833
|
+
if (baseline) {
|
|
12834
|
+
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
12835
|
+
if (committed.length > 0) {
|
|
12836
|
+
hasRecentCommitFiles = true;
|
|
12837
|
+
for (const f of committed) sets.add(f);
|
|
12838
|
+
}
|
|
12839
|
+
} else {
|
|
12840
|
+
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
12841
|
+
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
12842
|
+
const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
|
|
12843
|
+
const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
|
|
12844
|
+
if (commitAge < 120 && !hasUnstaged && !hasStaged) {
|
|
12845
|
+
const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
|
|
12846
|
+
if (recentFiles.length > 0) {
|
|
12847
|
+
hasRecentCommitFiles = true;
|
|
12848
|
+
for (const f of recentFiles) sets.add(f);
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
}
|
|
12852
|
+
for (const f of getWorktreeFiles()) sets.add(f);
|
|
12853
|
+
const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12854
|
+
return { files: filtered, hasRecentCommitFiles };
|
|
12855
|
+
}
|
|
12856
|
+
function getStagedFiles() {
|
|
12857
|
+
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12858
|
+
}
|
|
12859
|
+
function getDirtyFiles() {
|
|
12860
|
+
const set = /* @__PURE__ */ new Set();
|
|
12861
|
+
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
12862
|
+
for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
|
|
12863
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
12864
|
+
return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12865
|
+
}
|
|
12866
|
+
function showContentAtRef(ref, repoRelPath) {
|
|
12867
|
+
if (!ref || ref === "no-git") return null;
|
|
12868
|
+
const normalizedPath = repoRelPath.replace(/\\/g, "/");
|
|
12869
|
+
try {
|
|
12870
|
+
return (0, import_node_child_process5.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
|
|
12871
|
+
encoding: "utf-8",
|
|
12872
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
12873
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
12874
|
+
});
|
|
12875
|
+
} catch {
|
|
12876
|
+
return null;
|
|
12877
|
+
}
|
|
12878
|
+
}
|
|
12879
|
+
function getPushRangeFiles() {
|
|
12880
|
+
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
12881
|
+
const resolvers = [
|
|
12882
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
12883
|
+
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
12884
|
+
() => {
|
|
12885
|
+
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
12886
|
+
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
12887
|
+
}
|
|
12888
|
+
];
|
|
12889
|
+
for (const resolve of resolvers) {
|
|
12890
|
+
const range = resolve();
|
|
12891
|
+
if (range) return { files: diff(range), range };
|
|
12892
|
+
}
|
|
12893
|
+
const baseline = readBaselineSha();
|
|
12894
|
+
if (baseline) {
|
|
12895
|
+
const files = diff(`${baseline}..HEAD`);
|
|
12896
|
+
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
12897
|
+
}
|
|
12898
|
+
const last = diff("HEAD~1..HEAD");
|
|
12899
|
+
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
12900
|
+
}
|
|
12901
|
+
function getPushRangeMessages() {
|
|
12902
|
+
const { range } = getPushRangeFiles();
|
|
12903
|
+
if (!range) return "";
|
|
12904
|
+
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
12905
|
+
}
|
|
12906
|
+
function getWorktreeFiles() {
|
|
12907
|
+
const result = [];
|
|
12908
|
+
const worktreeDir = ".claude/worktrees";
|
|
12909
|
+
if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
|
|
12910
|
+
try {
|
|
12911
|
+
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
12912
|
+
const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
12913
|
+
for (const entry of entries) {
|
|
12914
|
+
if (!entry.isDirectory()) continue;
|
|
12915
|
+
const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
|
|
12916
|
+
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
12917
|
+
}
|
|
12918
|
+
} catch {
|
|
12919
|
+
}
|
|
12920
|
+
return result;
|
|
12921
|
+
}
|
|
12922
|
+
function scanDir(baseDir, dir, minMtime, result) {
|
|
12923
|
+
try {
|
|
12924
|
+
const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
|
|
12925
|
+
for (const entry of entries) {
|
|
12926
|
+
const fullPath = (0, import_node_path7.join)(dir, entry.name);
|
|
12927
|
+
if (entry.isDirectory()) {
|
|
12928
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12929
|
+
scanDir(baseDir, fullPath, minMtime, result);
|
|
12930
|
+
} else if (entry.isFile()) {
|
|
12931
|
+
const ext = (0, import_node_path7.extname)(entry.name).slice(1);
|
|
12932
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
12933
|
+
try {
|
|
12934
|
+
const stat3 = (0, import_node_fs7.statSync)(fullPath);
|
|
12935
|
+
if (stat3.mtimeMs >= minMtime) {
|
|
12936
|
+
const relPath = fullPath.slice(baseDir.length + 1);
|
|
12937
|
+
result.push(relPath);
|
|
12938
|
+
}
|
|
12939
|
+
} catch {
|
|
12940
|
+
}
|
|
12941
|
+
}
|
|
12942
|
+
}
|
|
12943
|
+
} catch {
|
|
12944
|
+
}
|
|
12945
|
+
}
|
|
12946
|
+
function filterAnalyzable(files) {
|
|
12947
|
+
return files.filter((f) => {
|
|
12948
|
+
const ext = (0, import_node_path7.extname)(f).slice(1);
|
|
12949
|
+
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
12950
|
+
});
|
|
12951
|
+
}
|
|
12952
|
+
function filterReviewable(files) {
|
|
12953
|
+
return files.filter((f) => {
|
|
12954
|
+
const ext = (0, import_node_path7.extname)(f).slice(1);
|
|
12955
|
+
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
12956
|
+
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
12957
|
+
const basename2 = f.split("/").pop() ?? "";
|
|
12958
|
+
if (REVIEWABLE_FILENAMES.has(basename2)) return true;
|
|
12959
|
+
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
12960
|
+
return false;
|
|
12961
|
+
});
|
|
12962
|
+
}
|
|
12963
|
+
function filterSecurity(files) {
|
|
12964
|
+
return files.filter(
|
|
12965
|
+
(f) => SECURITY_PATTERNS.some((p) => p.test(f))
|
|
12966
|
+
);
|
|
12967
|
+
}
|
|
12968
|
+
function getCurrentCommit() {
|
|
12969
|
+
return execGit("git rev-parse HEAD") || "no-git";
|
|
12970
|
+
}
|
|
12971
|
+
function listTrackedFiles() {
|
|
12972
|
+
const set = /* @__PURE__ */ new Set();
|
|
12973
|
+
for (const f of splitLines(execGit("git ls-files"))) set.add(f);
|
|
12974
|
+
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
|
|
12975
|
+
return Array.from(set);
|
|
12976
|
+
}
|
|
12977
|
+
|
|
13125
12978
|
// src/lib/files.ts
|
|
13126
12979
|
var import_node_fs8 = require("node:fs");
|
|
13127
12980
|
var import_node_path8 = require("node:path");
|
|
@@ -13255,15 +13108,10 @@ function collectCodeDelta(files, opts) {
|
|
|
13255
13108
|
// src/lib/debounce.ts
|
|
13256
13109
|
var import_node_fs9 = require("node:fs");
|
|
13257
13110
|
var import_node_crypto3 = require("node:crypto");
|
|
13258
|
-
function
|
|
13259
|
-
if (!
|
|
13260
|
-
return `${base}.${(0, import_node_crypto3.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
|
|
13261
|
-
}
|
|
13262
|
-
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
13263
|
-
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
13264
|
-
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13111
|
+
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
|
|
13112
|
+
if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
|
|
13265
13113
|
try {
|
|
13266
|
-
const lastTs = parseInt((0, import_node_fs9.readFileSync)(
|
|
13114
|
+
const lastTs = parseInt((0, import_node_fs9.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
|
|
13267
13115
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
13268
13116
|
const elapsed = nowTs - lastTs;
|
|
13269
13117
|
if (elapsed < debounceSeconds) {
|
|
@@ -13273,13 +13121,12 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
|
13273
13121
|
}
|
|
13274
13122
|
return null;
|
|
13275
13123
|
}
|
|
13276
|
-
function checkMtime(files, bypassForRecentCommits
|
|
13124
|
+
function checkMtime(files, bypassForRecentCommits) {
|
|
13277
13125
|
if (bypassForRecentCommits) return null;
|
|
13278
|
-
|
|
13279
|
-
if (!(0, import_node_fs9.existsSync)(file)) return null;
|
|
13126
|
+
if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
|
|
13280
13127
|
let debounceTime;
|
|
13281
13128
|
try {
|
|
13282
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13129
|
+
debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
|
|
13283
13130
|
} catch {
|
|
13284
13131
|
return null;
|
|
13285
13132
|
}
|
|
@@ -13311,12 +13158,11 @@ function computeContentHash(files) {
|
|
|
13311
13158
|
}
|
|
13312
13159
|
return hash.digest("hex");
|
|
13313
13160
|
}
|
|
13314
|
-
function checkContentHash(files
|
|
13161
|
+
function checkContentHash(files) {
|
|
13315
13162
|
const hash = computeContentHash(files);
|
|
13316
|
-
|
|
13317
|
-
if ((0, import_node_fs9.existsSync)(file)) {
|
|
13163
|
+
if ((0, import_node_fs9.existsSync)(HASH_FILE)) {
|
|
13318
13164
|
try {
|
|
13319
|
-
const storedHash = (0, import_node_fs9.readFileSync)(
|
|
13165
|
+
const storedHash = (0, import_node_fs9.readFileSync)(HASH_FILE, "utf-8").trim();
|
|
13320
13166
|
if (hash === storedHash) {
|
|
13321
13167
|
return { skip: "No source changes since last analysis", hash };
|
|
13322
13168
|
}
|
|
@@ -13325,19 +13171,18 @@ function checkContentHash(files, sessionId) {
|
|
|
13325
13171
|
}
|
|
13326
13172
|
return { skip: null, hash };
|
|
13327
13173
|
}
|
|
13328
|
-
function recordAnalysisStart(
|
|
13174
|
+
function recordAnalysisStart() {
|
|
13329
13175
|
(0, import_node_fs9.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
13330
|
-
(0, import_node_fs9.writeFileSync)(
|
|
13176
|
+
(0, import_node_fs9.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
|
|
13331
13177
|
}
|
|
13332
|
-
function recordPassHash(hash
|
|
13333
|
-
(0, import_node_fs9.writeFileSync)(
|
|
13178
|
+
function recordPassHash(hash) {
|
|
13179
|
+
(0, import_node_fs9.writeFileSync)(HASH_FILE, hash);
|
|
13334
13180
|
}
|
|
13335
|
-
function narrowToRecent(files
|
|
13336
|
-
|
|
13337
|
-
if (!(0, import_node_fs9.existsSync)(file)) return files;
|
|
13181
|
+
function narrowToRecent(files) {
|
|
13182
|
+
if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return files;
|
|
13338
13183
|
let debounceTime;
|
|
13339
13184
|
try {
|
|
13340
|
-
debounceTime = (0, import_node_fs9.statSync)(
|
|
13185
|
+
debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
|
|
13341
13186
|
} catch {
|
|
13342
13187
|
return files;
|
|
13343
13188
|
}
|
|
@@ -14843,7 +14688,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14843
14688
|
let currentCommit = "";
|
|
14844
14689
|
if (analysisMode !== "plan") {
|
|
14845
14690
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
14846
|
-
const debounceSkip = checkDebounce(debounceSeconds
|
|
14691
|
+
const debounceSkip = checkDebounce(debounceSeconds);
|
|
14847
14692
|
if (debounceSkip) {
|
|
14848
14693
|
if (assistantResponse) {
|
|
14849
14694
|
analysisMode = "plan";
|
|
@@ -14853,7 +14698,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14853
14698
|
}
|
|
14854
14699
|
if (analysisMode !== "plan") {
|
|
14855
14700
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14856
|
-
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles
|
|
14701
|
+
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
14857
14702
|
if (mtimeSkip) {
|
|
14858
14703
|
if (assistantResponse) {
|
|
14859
14704
|
analysisMode = "plan";
|
|
@@ -14863,9 +14708,9 @@ async function runAnalyze(opts, globals) {
|
|
|
14863
14708
|
}
|
|
14864
14709
|
}
|
|
14865
14710
|
if (analysisMode !== "plan") {
|
|
14866
|
-
recordAnalysisStart(
|
|
14711
|
+
recordAnalysisStart();
|
|
14867
14712
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
14868
|
-
const hashResult = checkContentHash(allCheckable
|
|
14713
|
+
const hashResult = checkContentHash(allCheckable);
|
|
14869
14714
|
if (hashResult.skip) {
|
|
14870
14715
|
if (assistantResponse) {
|
|
14871
14716
|
analysisMode = "plan";
|
|
@@ -14877,7 +14722,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14877
14722
|
if (analysisMode !== "plan") {
|
|
14878
14723
|
const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
|
|
14879
14724
|
const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
|
|
14880
|
-
const recentForReview = narrowToRecent(baseForReview
|
|
14725
|
+
const recentForReview = narrowToRecent(baseForReview);
|
|
14881
14726
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
14882
14727
|
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
14883
14728
|
if (baseline) {
|
|
@@ -15252,7 +15097,7 @@ async function runAnalyze(opts, globals) {
|
|
|
15252
15097
|
}
|
|
15253
15098
|
case "PASS": {
|
|
15254
15099
|
writeIteration(1, currentCommit, contentHash ?? void 0);
|
|
15255
|
-
if (contentHash) recordPassHash(contentHash
|
|
15100
|
+
if (contentHash) recordPassHash(contentHash);
|
|
15256
15101
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
15257
15102
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
15258
15103
|
const viewUrl = response.view_url ?? "";
|
|
@@ -15263,7 +15108,7 @@ async function runAnalyze(opts, globals) {
|
|
|
15263
15108
|
break;
|
|
15264
15109
|
}
|
|
15265
15110
|
case "WARN": {
|
|
15266
|
-
if (contentHash) recordPassHash(contentHash
|
|
15111
|
+
if (contentHash) recordPassHash(contentHash);
|
|
15267
15112
|
if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
15268
15113
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
15269
15114
|
const viewUrl = response.view_url ?? "";
|
|
@@ -16921,7 +16766,7 @@ function registerTelemetryCommands(program2) {
|
|
|
16921
16766
|
}
|
|
16922
16767
|
|
|
16923
16768
|
// src/cli.ts
|
|
16924
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.
|
|
16769
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.fe39839").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
16925
16770
|
registerAuthCommands(program);
|
|
16926
16771
|
registerHooksCommands(program);
|
|
16927
16772
|
registerIntentCommands(program);
|
|
@@ -312,25 +312,18 @@ Expected: single-digit findings per file, not hundreds. If you see 50+ issues fr
|
|
|
312
312
|
|
|
313
313
|
## Step 6: Register with Verity service
|
|
314
314
|
|
|
315
|
-
Use the `verity` CLI to register. It handles
|
|
315
|
+
Use the `verity` CLI to register. It handles credential storage and service URL automatically.
|
|
316
316
|
|
|
317
317
|
```bash
|
|
318
318
|
verity auth register --project "PROJECT_NAME" --remote "GIT_REMOTE_URL"
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
-
Registration is **provider-gated** (GitHub today): the CLI runs a GitHub OAuth
|
|
322
|
-
**device flow** and prints something like *"open https://github.com/login/device
|
|
323
|
-
and enter code WXYZ-1234"*. The user approves in the browser; the CLI then proves
|
|
324
|
-
the user has **write access** to the repo before the service issues a token. Tell
|
|
325
|
-
the user to expect this prompt and to complete it in their browser.
|
|
326
|
-
|
|
327
321
|
This command:
|
|
328
|
-
- **
|
|
329
|
-
- **
|
|
330
|
-
- **Non-GitHub remote**: Only GitHub is supported for now; show the error and stop.
|
|
322
|
+
- **New project**: Registers, stores token + service URL in `.verity/credentials`, prints `project_id`. Continue.
|
|
323
|
+
- **Already registered**: Automatically discovers the project, ensures `.verity/credentials` has the service URL. If a token already exists in credentials, it updates the file and succeeds. If no token exists, it asks you to paste one.
|
|
331
324
|
- **Other errors**: Show the error and stop.
|
|
332
325
|
|
|
333
|
-
After this step, `.verity/credentials` will contain `token
|
|
326
|
+
After this step, `.verity/credentials` will contain both `token` and `service_url` — all subsequent `verity` commands will work.
|
|
334
327
|
|
|
335
328
|
---
|
|
336
329
|
|