@codacy/verity-cli 0.24.0-experimental.bdf0db7 → 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 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");
@@ -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, 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_crypto2 = 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
  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, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
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, import_node_fs3.existsSync)(INTENT_FILE)) {
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, import_node_child_process5.execSync)(
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 import_node_fs4 = require("node:fs");
11717
- var import_node_path5 = require("node:path");
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, import_node_fs4.existsSync)(filePath)) return null;
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, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
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, import_node_path5.join)(TASK_CONTEXT_DIR, file);
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, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
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, import_node_fs4.existsSync)(filePath)) {
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, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
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 import_node_fs5 = require("node:fs");
11835
- var import_node_path6 = require("node:path");
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, import_node_fs5.existsSync)(memoryDir())) return null;
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, import_node_path6.join)(memoryDir(), domain);
11934
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
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, import_node_path6.join)(domainDir, file), "utf-8");
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 import_node_fs6 = require("node:fs");
12000
- var import_node_path7 = require("node:path");
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, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
11715
+ await (0, import_promises8.mkdir)((0, import_node_path6.join)(memoryDir2(), domain), { recursive: true });
12072
11716
  }
12073
- if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
12074
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
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, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
12077
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
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, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
12080
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
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, import_node_fs6.existsSync)(memoryDir2())) {
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, import_node_path7.join)(memoryDir2(), domain);
12090
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
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, import_node_path7.join)(memoryDir2(), filePath);
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, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
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, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
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, import_node_fs6.existsSync)(memoryDir2())) return out;
11770
+ if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12127
11771
  for (const domain of DOMAINS2) {
12128
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12129
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
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, import_node_path7.join)(domainDir, file), "utf-8")));
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, import_node_path7.join)(memoryDir2(), path);
12181
- if (!(0, import_node_fs6.existsSync)(full)) continue;
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, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12218
- await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
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, import_node_path7.join)(memoryDir2(), write.path);
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, import_node_fs6.existsSync)(fullPath)) {
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, import_node_path7.dirname)(fullPath), { recursive: true });
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, import_node_path7.join)(memoryDir2(), domain);
12289
- if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
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, import_node_path7.join)(domainDir, file), "utf-8");
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, import_node_path7.join)(memoryDir2(), "index.md");
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, import_node_path7.join)(cwd, "CLAUDE.md");
12049
+ const claudeMdPath = (0, import_node_path6.join)(cwd, "CLAUDE.md");
12406
12050
  let existing = "";
12407
- if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
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 import_node_fs7 = require("node:fs");
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, import_node_fs7.existsSync)(VERITY_DIR)) {
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");
@@ -16913,7 +16766,7 @@ function registerTelemetryCommands(program2) {
16913
16766
  }
16914
16767
 
16915
16768
  // src/cli.ts
16916
- program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.bdf0db7").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
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");
16917
16770
  registerAuthCommands(program);
16918
16771
  registerHooksCommands(program);
16919
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 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.24.0-experimental.bdf0db7",
3
+ "version": "0.24.0-experimental.fe39839",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "repository": {