@codacy/verity-cli 0.25.0-experimental.e3da48a → 0.25.0

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 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 import_node_child_process4 = require("node:child_process");
10330
- var import_node_path3 = require("node:path");
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");
@@ -10475,11 +10475,7 @@ var SECURITY_PATTERNS = [
10475
10475
  /Dockerfile/
10476
10476
  ];
10477
10477
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10478
- var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
10479
- var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
10480
- var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10481
- var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
10482
- var GITHUB_OAUTH_SCOPES = "repo user:email";
10478
+ var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
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, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
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, user } = result.data;
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
- provider_token: ${providerToken}
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, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11125
- await (0, import_promises3.appendFile)(
11126
- GLOBAL_CREDENTIALS_FILE,
11127
- `${remote} token: ${token}
11128
- ${remote} provider_token: ${providerToken}
11129
- `
11130
- );
11131
- await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11132
- });
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, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
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 import_node_path4 = require("node:path");
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, import_node_path4.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
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, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
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, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE);
11362
- await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
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_crypto3 = require("node:crypto");
11600
11244
 
11601
11245
  // src/lib/conversation-buffer.ts
11602
11246
  var import_promises5 = require("node:fs/promises");
11603
- var import_node_fs3 = require("node:fs");
11604
- var import_node_child_process5 = require("node:child_process");
11247
+ var import_node_fs2 = require("node:fs");
11248
+ var import_node_child_process4 = require("node:child_process");
11605
11249
  var import_node_crypto = require("node:crypto");
11606
11250
  function stripImageReferences(text) {
11607
11251
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
@@ -11636,7 +11280,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
11636
11280
  }
11637
11281
  async function readAndClearConversationBuffer(currentSessionId) {
11638
11282
  try {
11639
- if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
11283
+ if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
11640
11284
  const entries = await readBufferEntries();
11641
11285
  let mine = entries;
11642
11286
  let others = [];
@@ -11660,7 +11304,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
11660
11304
  };
11661
11305
  }
11662
11306
  }
11663
- if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
11307
+ if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
11664
11308
  try {
11665
11309
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
11666
11310
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -11704,7 +11348,7 @@ async function readBufferEntries() {
11704
11348
  }
11705
11349
  function getRecentCommitMessages() {
11706
11350
  try {
11707
- const output = (0, import_node_child_process5.execSync)(
11351
+ const output = (0, import_node_child_process4.execSync)(
11708
11352
  'git log --since="30 minutes ago" --format="%s" -5',
11709
11353
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11710
11354
  ).trim();
@@ -11717,8 +11361,8 @@ function getRecentCommitMessages() {
11717
11361
 
11718
11362
  // src/lib/task-context-buffer.ts
11719
11363
  var import_promises6 = require("node:fs/promises");
11720
- var import_node_fs4 = require("node:fs");
11721
- var import_node_path5 = require("node:path");
11364
+ var import_node_fs3 = require("node:fs");
11365
+ var import_node_path4 = require("node:path");
11722
11366
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11723
11367
  var MAX_BUFFER_BYTES = 500 * 1024;
11724
11368
  var MAX_PROMPT_CHARS = 2e3;
@@ -11757,7 +11401,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
11757
11401
  }
11758
11402
  async function readTaskContextBuffer(taskId) {
11759
11403
  const filePath = bufferPath(taskId);
11760
- if (!(0, import_node_fs4.existsSync)(filePath)) return null;
11404
+ if (!(0, import_node_fs3.existsSync)(filePath)) return null;
11761
11405
  try {
11762
11406
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
11763
11407
  if (!content.trim()) return null;
@@ -11791,12 +11435,12 @@ async function readTaskContextBuffer(taskId) {
11791
11435
  }
11792
11436
  async function cleanupTaskContextBuffers() {
11793
11437
  try {
11794
- if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
11438
+ if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
11795
11439
  const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
11796
11440
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11797
11441
  for (const file of files) {
11798
11442
  if (!file.endsWith(".jsonl")) continue;
11799
- const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
11443
+ const filePath = (0, import_node_path4.join)(TASK_CONTEXT_DIR, file);
11800
11444
  try {
11801
11445
  const stats = await (0, import_promises6.stat)(filePath);
11802
11446
  if (stats.mtimeMs < cutoffMs) {
@@ -11810,13 +11454,13 @@ async function cleanupTaskContextBuffers() {
11810
11454
  }
11811
11455
  function bufferPath(taskId) {
11812
11456
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11813
- return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11457
+ return (0, import_node_path4.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11814
11458
  }
11815
11459
  async function appendEntry(taskId, entry) {
11816
11460
  try {
11817
11461
  await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
11818
11462
  const filePath = bufferPath(taskId);
11819
- if ((0, import_node_fs4.existsSync)(filePath)) {
11463
+ if ((0, import_node_fs3.existsSync)(filePath)) {
11820
11464
  const stats = await (0, import_promises6.stat)(filePath);
11821
11465
  if (stats.size >= MAX_BUFFER_BYTES) {
11822
11466
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
@@ -11827,7 +11471,7 @@ async function appendEntry(taskId, entry) {
11827
11471
  }
11828
11472
  }
11829
11473
  const line = JSON.stringify(entry) + "\n";
11830
- const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11474
+ const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11831
11475
  await (0, import_promises6.writeFile)(filePath, existing + line);
11832
11476
  } catch {
11833
11477
  }
@@ -11835,8 +11479,8 @@ async function appendEntry(taskId, entry) {
11835
11479
 
11836
11480
  // src/lib/memory-retrieval.ts
11837
11481
  var import_promises7 = require("node:fs/promises");
11838
- var import_node_fs5 = require("node:fs");
11839
- var import_node_path6 = require("node:path");
11482
+ var import_node_fs4 = require("node:fs");
11483
+ var import_node_path5 = require("node:path");
11840
11484
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11841
11485
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11842
11486
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -11929,19 +11573,19 @@ function parseFrontmatter(content) {
11929
11573
  return { fm, body: match[2].trim() };
11930
11574
  }
11931
11575
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
11932
- if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
11576
+ if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
11933
11577
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
11934
11578
  const promptTokens = tokenize(promptText);
11935
11579
  const nodes = [];
11936
11580
  for (const domain of DOMAINS) {
11937
- const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
11938
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
11581
+ const domainDir = (0, import_node_path5.join)(memoryDir(), domain);
11582
+ if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
11939
11583
  try {
11940
11584
  const files = await (0, import_promises7.readdir)(domainDir);
11941
11585
  for (const file of files) {
11942
11586
  if (!file.endsWith(".md")) continue;
11943
11587
  try {
11944
- const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
11588
+ const content = await (0, import_promises7.readFile)((0, import_node_path5.join)(domainDir, file), "utf-8");
11945
11589
  const { fm, body } = parseFrontmatter(content);
11946
11590
  if (fm.status && fm.status !== "active") continue;
11947
11591
  nodes.push({
@@ -12000,8 +11644,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
12000
11644
 
12001
11645
  // src/lib/memory-sync.ts
12002
11646
  var import_promises8 = require("node:fs/promises");
12003
- var import_node_fs6 = require("node:fs");
12004
- var import_node_path7 = require("node:path");
11647
+ var import_node_fs5 = require("node:fs");
11648
+ var import_node_path6 = require("node:path");
12005
11649
  var import_node_crypto2 = require("node:crypto");
12006
11650
 
12007
11651
  // src/lib/glob-match.ts
@@ -12072,32 +11716,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
12072
11716
  async function ensureMemoryDir() {
12073
11717
  await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
12074
11718
  for (const domain of DOMAINS2) {
12075
- await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
11719
+ await (0, import_promises8.mkdir)((0, import_node_path6.join)(memoryDir2(), domain), { recursive: true });
12076
11720
  }
12077
- if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
12078
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
11721
+ if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"))) {
11722
+ await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
12079
11723
  }
12080
- if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
12081
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
11724
+ if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "index.md"))) {
11725
+ 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");
12082
11726
  }
12083
- if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
12084
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
11727
+ if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "log.md"))) {
11728
+ await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
12085
11729
  }
12086
11730
  }
12087
11731
  async function buildManifest() {
12088
- if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
11732
+ if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
12089
11733
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
12090
11734
  }
12091
11735
  const nodes = [];
12092
11736
  for (const domain of DOMAINS2) {
12093
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12094
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11737
+ const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11738
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12095
11739
  try {
12096
11740
  const files = await (0, import_promises8.readdir)(domainDir);
12097
11741
  for (const file of files) {
12098
11742
  if (!file.endsWith(".md")) continue;
12099
11743
  const filePath = `${domain}/${file}`;
12100
- const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
11744
+ const fullPath = (0, import_node_path6.join)(memoryDir2(), filePath);
12101
11745
  try {
12102
11746
  const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
12103
11747
  const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -12110,13 +11754,13 @@ async function buildManifest() {
12110
11754
  }
12111
11755
  let indexHash = null;
12112
11756
  try {
12113
- const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
11757
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "index.md"), "utf-8");
12114
11758
  indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
12115
11759
  } catch {
12116
11760
  }
12117
11761
  let logLength = 0;
12118
11762
  try {
12119
- const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
11763
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "utf-8");
12120
11764
  logLength = logContent.split("\n").length;
12121
11765
  } catch {
12122
11766
  }
@@ -12127,15 +11771,15 @@ function hashContent(content) {
12127
11771
  }
12128
11772
  async function readOnDiskNodes() {
12129
11773
  const out = /* @__PURE__ */ new Map();
12130
- if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
11774
+ if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12131
11775
  for (const domain of DOMAINS2) {
12132
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12133
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11776
+ const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11777
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12134
11778
  try {
12135
11779
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
12136
11780
  if (!file.endsWith(".md")) continue;
12137
11781
  try {
12138
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
11782
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8")));
12139
11783
  } catch {
12140
11784
  }
12141
11785
  }
@@ -12181,8 +11825,8 @@ async function computeEditedNodeUploads() {
12181
11825
  const uploads = [];
12182
11826
  for (const [path, prevHash] of prev) {
12183
11827
  if (prevHash == null) continue;
12184
- const full = (0, import_node_path7.join)(memoryDir2(), path);
12185
- if (!(0, import_node_fs6.existsSync)(full)) continue;
11828
+ const full = (0, import_node_path6.join)(memoryDir2(), path);
11829
+ if (!(0, import_node_fs5.existsSync)(full)) continue;
12186
11830
  let content;
12187
11831
  try {
12188
11832
  content = await (0, import_promises8.readFile)(full, "utf-8");
@@ -12218,15 +11862,15 @@ async function applyMemoryWrites(writes, opts = {}) {
12218
11862
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
12219
11863
  for (const n of notes) logLines.push(` - ${n}`);
12220
11864
  try {
12221
- const existing = (0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12222
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
11865
+ 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";
11866
+ await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
12223
11867
  } catch {
12224
11868
  }
12225
11869
  await recordSyncedNodePaths();
12226
11870
  return count;
12227
11871
  }
12228
11872
  async function applyOneWrite(write, treePaths) {
12229
- const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
11873
+ const fullPath = (0, import_node_path6.join)(memoryDir2(), write.path);
12230
11874
  const notes = [];
12231
11875
  let content = write.content;
12232
11876
  if (treePaths && treePaths.length > 0) {
@@ -12236,7 +11880,7 @@ async function applyOneWrite(write, treePaths) {
12236
11880
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
12237
11881
  }
12238
11882
  }
12239
- if ((0, import_node_fs6.existsSync)(fullPath)) {
11883
+ if ((0, import_node_fs5.existsSync)(fullPath)) {
12240
11884
  let existing = "";
12241
11885
  try {
12242
11886
  existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
@@ -12248,7 +11892,7 @@ async function applyOneWrite(write, treePaths) {
12248
11892
  return { written: false, notes };
12249
11893
  }
12250
11894
  }
12251
- await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
11895
+ await (0, import_promises8.mkdir)((0, import_node_path6.dirname)(fullPath), { recursive: true });
12252
11896
  await (0, import_promises8.writeFile)(fullPath, content);
12253
11897
  return { written: true, notes };
12254
11898
  }
@@ -12289,8 +11933,8 @@ async function regenerateIndex() {
12289
11933
  ];
12290
11934
  let totalNodes = 0;
12291
11935
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
12292
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12293
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11936
+ const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11937
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12294
11938
  try {
12295
11939
  const files = await (0, import_promises8.readdir)(domainDir);
12296
11940
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -12300,7 +11944,7 @@ async function regenerateIndex() {
12300
11944
  for (const file of mdFiles.sort()) {
12301
11945
  const slug = file.replace(/\.md$/, "");
12302
11946
  try {
12303
- const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
11947
+ const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
12304
11948
  const title = pickFrontmatter(content, "title") ?? slug;
12305
11949
  const kind = pickFrontmatter(content, "kind") ?? "-";
12306
11950
  const confidence = pickFrontmatter(content, "confidence");
@@ -12324,7 +11968,7 @@ async function regenerateIndex() {
12324
11968
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
12325
11969
  }
12326
11970
  const next = lines.join("\n") + "\n";
12327
- const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
11971
+ const indexPath = (0, import_node_path6.join)(memoryDir2(), "index.md");
12328
11972
  let existing = null;
12329
11973
  try {
12330
11974
  existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
@@ -12406,9 +12050,9 @@ function hasLegacyMemoryBlock(text) {
12406
12050
  return findMarker(text, LEGACY_MD_START) !== -1;
12407
12051
  }
12408
12052
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12409
- const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
12053
+ const claudeMdPath = (0, import_node_path6.join)(cwd, "CLAUDE.md");
12410
12054
  let existing = "";
12411
- if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12055
+ if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
12412
12056
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
12413
12057
  }
12414
12058
  let startTag = CLAUDE_MD_START;
@@ -12538,7 +12182,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
12538
12182
  `;
12539
12183
 
12540
12184
  // src/commands/intent.ts
12541
- var import_node_fs7 = require("node:fs");
12185
+ var import_node_fs6 = require("node:fs");
12542
12186
  function registerIntentCommands(program2) {
12543
12187
  const intent = program2.command("intent").description("Manage intent capture");
12544
12188
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
@@ -12547,7 +12191,7 @@ function registerIntentCommands(program2) {
12547
12191
  process.chdir(repoRoot());
12548
12192
  } catch {
12549
12193
  }
12550
- if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
12194
+ if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
12551
12195
  process.exit(0);
12552
12196
  }
12553
12197
  const chunks = [];
@@ -13126,6 +12770,215 @@ async function sendGeneralFeedback(message, opts, globals) {
13126
12770
  var import_node_fs19 = require("node:fs");
13127
12771
  var import_node_path14 = require("node:path");
13128
12772
 
12773
+ // src/lib/git.ts
12774
+ var import_node_child_process5 = require("node:child_process");
12775
+ var import_node_fs7 = require("node:fs");
12776
+ var import_node_path7 = require("node:path");
12777
+ function resolveFile(relpath) {
12778
+ if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
12779
+ if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
12780
+ try {
12781
+ const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
12782
+ for (const entry of entries) {
12783
+ if (!entry.isDirectory()) continue;
12784
+ const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
12785
+ if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
12786
+ }
12787
+ } catch {
12788
+ }
12789
+ }
12790
+ return null;
12791
+ }
12792
+ function execGit(cmd) {
12793
+ try {
12794
+ return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12795
+ } catch {
12796
+ return "";
12797
+ }
12798
+ }
12799
+ function splitLines(s) {
12800
+ return s.split("\n").filter((l) => l.length > 0);
12801
+ }
12802
+ var SHA_RE = /^[0-9a-f]{40}$/;
12803
+ function readBaselineSha() {
12804
+ if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
12805
+ let sha;
12806
+ try {
12807
+ sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
12808
+ } catch {
12809
+ return null;
12810
+ }
12811
+ if (!SHA_RE.test(sha)) return null;
12812
+ const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
12813
+ if (!reachable) {
12814
+ try {
12815
+ (0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
12816
+ } catch {
12817
+ }
12818
+ return null;
12819
+ }
12820
+ return sha;
12821
+ }
12822
+ function writeBaselineSha(sha) {
12823
+ if (!SHA_RE.test(sha)) return;
12824
+ try {
12825
+ (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
12826
+ (0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
12827
+ } catch {
12828
+ }
12829
+ }
12830
+ function getChangedFiles() {
12831
+ const sets = /* @__PURE__ */ new Set();
12832
+ let hasRecentCommitFiles = false;
12833
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
12834
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
12835
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
12836
+ const baseline = readBaselineSha();
12837
+ if (baseline) {
12838
+ const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
12839
+ if (committed.length > 0) {
12840
+ hasRecentCommitFiles = true;
12841
+ for (const f of committed) sets.add(f);
12842
+ }
12843
+ } else {
12844
+ const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
12845
+ const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
12846
+ const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
12847
+ const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
12848
+ if (commitAge < 120 && !hasUnstaged && !hasStaged) {
12849
+ const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
12850
+ if (recentFiles.length > 0) {
12851
+ hasRecentCommitFiles = true;
12852
+ for (const f of recentFiles) sets.add(f);
12853
+ }
12854
+ }
12855
+ }
12856
+ for (const f of getWorktreeFiles()) sets.add(f);
12857
+ const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12858
+ return { files: filtered, hasRecentCommitFiles };
12859
+ }
12860
+ function getStagedFiles() {
12861
+ return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12862
+ }
12863
+ function getDirtyFiles() {
12864
+ const set = /* @__PURE__ */ new Set();
12865
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
12866
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
12867
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
12868
+ return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12869
+ }
12870
+ function showContentAtRef(ref, repoRelPath) {
12871
+ if (!ref || ref === "no-git") return null;
12872
+ const normalizedPath = repoRelPath.replace(/\\/g, "/");
12873
+ try {
12874
+ return (0, import_node_child_process5.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
12875
+ encoding: "utf-8",
12876
+ maxBuffer: 64 * 1024 * 1024,
12877
+ stdio: ["pipe", "pipe", "pipe"]
12878
+ });
12879
+ } catch {
12880
+ return null;
12881
+ }
12882
+ }
12883
+ function getPushRangeFiles() {
12884
+ const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12885
+ const resolvers = [
12886
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
12887
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
12888
+ () => {
12889
+ const branch = execGit("git rev-parse --abbrev-ref HEAD");
12890
+ return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
12891
+ }
12892
+ ];
12893
+ for (const resolve of resolvers) {
12894
+ const range = resolve();
12895
+ if (range) return { files: diff(range), range };
12896
+ }
12897
+ const baseline = readBaselineSha();
12898
+ if (baseline) {
12899
+ const files = diff(`${baseline}..HEAD`);
12900
+ if (files.length > 0) return { files, range: `${baseline}..HEAD` };
12901
+ }
12902
+ const last = diff("HEAD~1..HEAD");
12903
+ return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
12904
+ }
12905
+ function getPushRangeMessages() {
12906
+ const { range } = getPushRangeFiles();
12907
+ if (!range) return "";
12908
+ return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
12909
+ }
12910
+ function getWorktreeFiles() {
12911
+ const result = [];
12912
+ const worktreeDir = ".claude/worktrees";
12913
+ if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
12914
+ try {
12915
+ const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
12916
+ const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
12917
+ for (const entry of entries) {
12918
+ if (!entry.isDirectory()) continue;
12919
+ const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
12920
+ scanDir(wtDir, wtDir, fiveMinAgo, result);
12921
+ }
12922
+ } catch {
12923
+ }
12924
+ return result;
12925
+ }
12926
+ function scanDir(baseDir, dir, minMtime, result) {
12927
+ try {
12928
+ const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
12929
+ for (const entry of entries) {
12930
+ const fullPath = (0, import_node_path7.join)(dir, entry.name);
12931
+ if (entry.isDirectory()) {
12932
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
12933
+ scanDir(baseDir, fullPath, minMtime, result);
12934
+ } else if (entry.isFile()) {
12935
+ const ext = (0, import_node_path7.extname)(entry.name).slice(1);
12936
+ if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
12937
+ try {
12938
+ const stat3 = (0, import_node_fs7.statSync)(fullPath);
12939
+ if (stat3.mtimeMs >= minMtime) {
12940
+ const relPath = fullPath.slice(baseDir.length + 1);
12941
+ result.push(relPath);
12942
+ }
12943
+ } catch {
12944
+ }
12945
+ }
12946
+ }
12947
+ } catch {
12948
+ }
12949
+ }
12950
+ function filterAnalyzable(files) {
12951
+ return files.filter((f) => {
12952
+ const ext = (0, import_node_path7.extname)(f).slice(1);
12953
+ return ANALYZABLE_EXTENSIONS.has(ext);
12954
+ });
12955
+ }
12956
+ function filterReviewable(files) {
12957
+ return files.filter((f) => {
12958
+ const ext = (0, import_node_path7.extname)(f).slice(1);
12959
+ if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
12960
+ if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
12961
+ const basename2 = f.split("/").pop() ?? "";
12962
+ if (REVIEWABLE_FILENAMES.has(basename2)) return true;
12963
+ if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
12964
+ return false;
12965
+ });
12966
+ }
12967
+ function filterSecurity(files) {
12968
+ return files.filter(
12969
+ (f) => SECURITY_PATTERNS.some((p) => p.test(f))
12970
+ );
12971
+ }
12972
+ function getCurrentCommit() {
12973
+ return execGit("git rev-parse HEAD") || "no-git";
12974
+ }
12975
+ function listTrackedFiles() {
12976
+ const set = /* @__PURE__ */ new Set();
12977
+ for (const f of splitLines(execGit("git ls-files"))) set.add(f);
12978
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
12979
+ return Array.from(set);
12980
+ }
12981
+
13129
12982
  // src/lib/files.ts
13130
12983
  var import_node_fs8 = require("node:fs");
13131
12984
  var import_node_path8 = require("node:path");
@@ -16925,7 +16778,7 @@ function registerTelemetryCommands(program2) {
16925
16778
  }
16926
16779
 
16927
16780
  // src/cli.ts
16928
- program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.e3da48a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16781
+ program.name("verity").description("CLI for Verity quality gate service").version("0.25.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16929
16782
  registerAuthCommands(program);
16930
16783
  registerHooksCommands(program);
16931
16784
  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 provider auth, credential storage, and the service URL automatically.
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
- - **Write access confirmed**: Registers, stores the verity token + service URL + provider token in `.verity/credentials` (perms 600), prints `project_id` and the authenticated email. Continue.
329
- - **No write access** (`403 NO_WRITE_ACCESS`): The user lacks push rights on the repo they can't register it. Verity still runs locally as an anonymous gate, but no history/org/repo data is stored. Stop.
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`, `service_url`, and `provider_token` — all subsequent `verity` commands will work.
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.25.0-experimental.e3da48a",
3
+ "version": "0.25.0",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "repository": {