@codacy/verity-cli 0.24.0-experimental.b2914b9 → 0.24.0-experimental.b35bbc7

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_process3 = require("node:child_process");
10330
- var import_node_path2 = require("node:path");
10329
+ var import_node_child_process4 = require("node:child_process");
10330
+ var import_node_path3 = require("node:path");
10331
10331
 
10332
10332
  // src/lib/auth.ts
10333
10333
  var import_promises = require("node:fs/promises");
@@ -10379,6 +10379,7 @@ var MAX_PLAN_FILES = 3;
10379
10379
  var MAX_PLAN_FILE_BYTES = 10240;
10380
10380
  var MAX_INTENT_CHARS = 2e3;
10381
10381
  var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
10382
+ var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
10382
10383
  var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
10383
10384
  var CONVERSATION_MAX_ENTRIES = 10;
10384
10385
  var CONVERSATION_WINDOW_MINUTES = 15;
@@ -10474,7 +10475,11 @@ var SECURITY_PATTERNS = [
10474
10475
  /Dockerfile/
10475
10476
  ];
10476
10477
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10477
- var DEFAULT_SERVICE_URL = "https://yykkfdexzljmmkklpgyz.supabase.co".length > 0 ? "https://yykkfdexzljmmkklpgyz.supabase.co" : PROD_SERVICE_URL;
10478
+ var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
10479
+ var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
10480
+ var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10481
+ var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
10482
+ var GITHUB_OAUTH_SCOPES = "repo user:email";
10478
10483
 
10479
10484
  // src/lib/auth.ts
10480
10485
  async function resolveToken(flagToken) {
@@ -10640,7 +10645,8 @@ async function apiRequest(options) {
10640
10645
  timeout = 9e4,
10641
10646
  cmd = "unknown",
10642
10647
  retry = false,
10643
- encodeBody = false
10648
+ encodeBody = false,
10649
+ extraHeaders
10644
10650
  } = options;
10645
10651
  const url = `${serviceUrl}${path}`;
10646
10652
  const headers = {
@@ -10649,6 +10655,9 @@ async function apiRequest(options) {
10649
10655
  if (token) {
10650
10656
  headers["Authorization"] = `Bearer ${token}`;
10651
10657
  }
10658
+ if (extraHeaders) {
10659
+ Object.assign(headers, extraHeaders);
10660
+ }
10652
10661
  const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
10653
10662
  if (testMockScenario) {
10654
10663
  headers["X-Verity-Mock-Scenario"] = testMockScenario;
@@ -10739,6 +10748,324 @@ function analyzeRequest(options) {
10739
10748
  });
10740
10749
  }
10741
10750
 
10751
+ // src/lib/provider-auth.ts
10752
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10753
+ var form = (fields) => new URLSearchParams(fields).toString();
10754
+ async function githubDeviceFlow() {
10755
+ const override = process.env.VERITY_PROVIDER_TOKEN;
10756
+ if (override) return { ok: true, data: override };
10757
+ let dc;
10758
+ try {
10759
+ const res = await fetch(GITHUB_DEVICE_CODE_URL, {
10760
+ method: "POST",
10761
+ headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
10762
+ body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_OAUTH_SCOPES })
10763
+ });
10764
+ if (!res.ok) {
10765
+ return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
10766
+ }
10767
+ dc = await res.json();
10768
+ } catch (err) {
10769
+ return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
10770
+ }
10771
+ if (!dc.device_code || !dc.user_code) {
10772
+ return {
10773
+ ok: false,
10774
+ error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
10775
+ };
10776
+ }
10777
+ printInfo("");
10778
+ printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
10779
+ printInfo(`And enter the code: ${dc.user_code}`);
10780
+ printInfo("Waiting for authorization\u2026");
10781
+ const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
10782
+ let interval = dc.interval || 5;
10783
+ while (Date.now() < deadline) {
10784
+ await sleep(interval * 1e3);
10785
+ let data;
10786
+ try {
10787
+ const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
10788
+ method: "POST",
10789
+ headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
10790
+ body: form({
10791
+ client_id: GITHUB_CLIENT_ID,
10792
+ device_code: dc.device_code,
10793
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
10794
+ })
10795
+ });
10796
+ data = await res.json().catch(() => ({}));
10797
+ } catch {
10798
+ continue;
10799
+ }
10800
+ if (data.access_token) return { ok: true, data: data.access_token };
10801
+ switch (data.error) {
10802
+ case "authorization_pending":
10803
+ break;
10804
+ case "slow_down":
10805
+ interval += 5;
10806
+ break;
10807
+ case "access_denied":
10808
+ return { ok: false, error: "Authorization was denied on GitHub." };
10809
+ case "expired_token":
10810
+ return { ok: false, error: "The authorization code expired. Re-run register." };
10811
+ default:
10812
+ if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
10813
+ }
10814
+ }
10815
+ return { ok: false, error: "Timed out waiting for GitHub authorization." };
10816
+ }
10817
+
10818
+ // src/lib/git.ts
10819
+ var import_node_child_process3 = require("node:child_process");
10820
+ var import_node_fs2 = require("node:fs");
10821
+ var import_node_path2 = require("node:path");
10822
+ function resolveFile(relpath) {
10823
+ if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
10824
+ if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
10825
+ try {
10826
+ const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
10827
+ for (const entry of entries) {
10828
+ if (!entry.isDirectory()) continue;
10829
+ const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
10830
+ if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
10831
+ }
10832
+ } catch {
10833
+ }
10834
+ }
10835
+ return null;
10836
+ }
10837
+ function execGit(cmd) {
10838
+ try {
10839
+ return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10840
+ } catch {
10841
+ return "";
10842
+ }
10843
+ }
10844
+ function splitLines(s) {
10845
+ return s.split("\n").filter((l) => l.length > 0);
10846
+ }
10847
+ var SHA_RE = /^[0-9a-f]{40}$/;
10848
+ function readBaselineSha() {
10849
+ if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
10850
+ let sha;
10851
+ try {
10852
+ sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
10853
+ } catch {
10854
+ return null;
10855
+ }
10856
+ if (!SHA_RE.test(sha)) return null;
10857
+ const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
10858
+ if (!reachable) {
10859
+ try {
10860
+ (0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
10861
+ } catch {
10862
+ }
10863
+ return null;
10864
+ }
10865
+ return sha;
10866
+ }
10867
+ function writeBaselineSha(sha) {
10868
+ if (!SHA_RE.test(sha)) return;
10869
+ try {
10870
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
10871
+ (0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
10872
+ } catch {
10873
+ }
10874
+ }
10875
+ function getChangedFiles() {
10876
+ const sets = /* @__PURE__ */ new Set();
10877
+ let hasRecentCommitFiles = false;
10878
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
10879
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
10880
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
10881
+ const baseline = readBaselineSha();
10882
+ if (baseline) {
10883
+ const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
10884
+ if (committed.length > 0) {
10885
+ hasRecentCommitFiles = true;
10886
+ for (const f of committed) sets.add(f);
10887
+ }
10888
+ } else {
10889
+ const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
10890
+ const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
10891
+ const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
10892
+ const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
10893
+ if (commitAge < 120 && !hasUnstaged && !hasStaged) {
10894
+ const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
10895
+ if (recentFiles.length > 0) {
10896
+ hasRecentCommitFiles = true;
10897
+ for (const f of recentFiles) sets.add(f);
10898
+ }
10899
+ }
10900
+ }
10901
+ for (const f of getWorktreeFiles()) sets.add(f);
10902
+ const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10903
+ return { files: filtered, hasRecentCommitFiles };
10904
+ }
10905
+ function getStagedFiles() {
10906
+ return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10907
+ }
10908
+ function getDirtyFiles() {
10909
+ const set = /* @__PURE__ */ new Set();
10910
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
10911
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
10912
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
10913
+ return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10914
+ }
10915
+ function showContentAtRef(ref, repoRelPath) {
10916
+ if (!ref || ref === "no-git") return null;
10917
+ const normalizedPath = repoRelPath.replace(/\\/g, "/");
10918
+ try {
10919
+ return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
10920
+ encoding: "utf-8",
10921
+ maxBuffer: 64 * 1024 * 1024,
10922
+ stdio: ["pipe", "pipe", "pipe"]
10923
+ });
10924
+ } catch {
10925
+ return null;
10926
+ }
10927
+ }
10928
+ function getPushRangeFiles() {
10929
+ const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10930
+ const resolvers = [
10931
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
10932
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
10933
+ () => {
10934
+ const branch = execGit("git rev-parse --abbrev-ref HEAD");
10935
+ return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
10936
+ }
10937
+ ];
10938
+ for (const resolve of resolvers) {
10939
+ const range = resolve();
10940
+ if (range) return { files: diff(range), range };
10941
+ }
10942
+ const baseline = readBaselineSha();
10943
+ if (baseline) {
10944
+ const files = diff(`${baseline}..HEAD`);
10945
+ if (files.length > 0) return { files, range: `${baseline}..HEAD` };
10946
+ }
10947
+ const last = diff("HEAD~1..HEAD");
10948
+ return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
10949
+ }
10950
+ function getPushRangeMessages() {
10951
+ const { range } = getPushRangeFiles();
10952
+ if (!range) return "";
10953
+ return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
10954
+ }
10955
+ function getWorktreeFiles() {
10956
+ const result = [];
10957
+ const worktreeDir = ".claude/worktrees";
10958
+ if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
10959
+ try {
10960
+ const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
10961
+ const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
10962
+ for (const entry of entries) {
10963
+ if (!entry.isDirectory()) continue;
10964
+ const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
10965
+ scanDir(wtDir, wtDir, fiveMinAgo, result);
10966
+ }
10967
+ } catch {
10968
+ }
10969
+ return result;
10970
+ }
10971
+ function scanDir(baseDir, dir, minMtime, result) {
10972
+ try {
10973
+ const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
10974
+ for (const entry of entries) {
10975
+ const fullPath = (0, import_node_path2.join)(dir, entry.name);
10976
+ if (entry.isDirectory()) {
10977
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
10978
+ scanDir(baseDir, fullPath, minMtime, result);
10979
+ } else if (entry.isFile()) {
10980
+ const ext = (0, import_node_path2.extname)(entry.name).slice(1);
10981
+ if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
10982
+ try {
10983
+ const stat3 = (0, import_node_fs2.statSync)(fullPath);
10984
+ if (stat3.mtimeMs >= minMtime) {
10985
+ const relPath = fullPath.slice(baseDir.length + 1);
10986
+ result.push(relPath);
10987
+ }
10988
+ } catch {
10989
+ }
10990
+ }
10991
+ }
10992
+ } catch {
10993
+ }
10994
+ }
10995
+ function filterAnalyzable(files) {
10996
+ return files.filter((f) => {
10997
+ const ext = (0, import_node_path2.extname)(f).slice(1);
10998
+ return ANALYZABLE_EXTENSIONS.has(ext);
10999
+ });
11000
+ }
11001
+ function filterReviewable(files) {
11002
+ return files.filter((f) => {
11003
+ const ext = (0, import_node_path2.extname)(f).slice(1);
11004
+ if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
11005
+ if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
11006
+ const basename2 = f.split("/").pop() ?? "";
11007
+ if (REVIEWABLE_FILENAMES.has(basename2)) return true;
11008
+ if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
11009
+ return false;
11010
+ });
11011
+ }
11012
+ function filterSecurity(files) {
11013
+ return files.filter(
11014
+ (f) => SECURITY_PATTERNS.some((p) => p.test(f))
11015
+ );
11016
+ }
11017
+ function getCurrentCommit() {
11018
+ return execGit("git rev-parse HEAD") || "no-git";
11019
+ }
11020
+ function detectProvider(host) {
11021
+ const h = host.toLowerCase();
11022
+ if (h.includes("github")) return "github";
11023
+ if (h.includes("gitlab")) return "gitlab";
11024
+ if (h.includes("bitbucket")) return "bitbucket";
11025
+ return "unknown";
11026
+ }
11027
+ function parseRemote(raw) {
11028
+ if (!raw || typeof raw !== "string") return null;
11029
+ let s = raw.trim();
11030
+ if (!s) return null;
11031
+ let host = "";
11032
+ let path = "";
11033
+ const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
11034
+ if (scp) {
11035
+ host = scp[1];
11036
+ path = scp[2];
11037
+ } else {
11038
+ s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
11039
+ s = s.replace(/^[^/@]+@/, "");
11040
+ const slash = s.indexOf("/");
11041
+ if (slash === -1) return null;
11042
+ host = s.slice(0, slash);
11043
+ path = s.slice(slash + 1);
11044
+ }
11045
+ host = host.toLowerCase().trim();
11046
+ path = path.replace(/\/+$/, "").replace(/\.git$/, "");
11047
+ if (!host || !path) return null;
11048
+ const segments = path.split("/").filter(Boolean);
11049
+ if (segments.length < 2) return null;
11050
+ const owner = segments[0];
11051
+ const repo = segments[segments.length - 1];
11052
+ if (!owner || !repo) return null;
11053
+ return {
11054
+ host,
11055
+ owner,
11056
+ repo,
11057
+ provider: detectProvider(host),
11058
+ orgUrl: `https://${host}/${owner}`,
11059
+ orgName: owner
11060
+ };
11061
+ }
11062
+ function listTrackedFiles() {
11063
+ const set = /* @__PURE__ */ new Set();
11064
+ for (const f of splitLines(execGit("git ls-files"))) set.add(f);
11065
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
11066
+ return Array.from(set);
11067
+ }
11068
+
10742
11069
  // src/commands/auth.ts
10743
11070
  function registerAuthCommands(program2) {
10744
11071
  const auth = program2.command("auth").description("Manage project authentication");
@@ -10748,35 +11075,65 @@ function registerAuthCommands(program2) {
10748
11075
  let remote = opts.remote;
10749
11076
  if (!remote) {
10750
11077
  try {
10751
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11078
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10752
11079
  } catch {
10753
11080
  printError("No git remote found. Use --remote to specify one.");
10754
11081
  process.exit(1);
10755
11082
  }
10756
11083
  }
11084
+ const parsed = parseRemote(remote);
11085
+ if (!parsed) {
11086
+ printError(`Could not parse git remote: ${remote}`);
11087
+ process.exit(1);
11088
+ }
11089
+ if (parsed.provider !== "github") {
11090
+ printError(`Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.`);
11091
+ process.exit(1);
11092
+ }
11093
+ const providerAuth = await githubDeviceFlow();
11094
+ if (!providerAuth.ok) {
11095
+ printError(providerAuth.error);
11096
+ process.exit(1);
11097
+ }
11098
+ const providerToken = providerAuth.data;
10757
11099
  const result = await apiRequest({
10758
11100
  method: "POST",
10759
11101
  path: "/auth/register",
10760
11102
  serviceUrl,
10761
11103
  body: { project_name: opts.project, git_remote_url: remote },
11104
+ extraHeaders: { "X-Provider-Token": providerToken },
10762
11105
  verbose: globals.verbose
10763
11106
  });
10764
11107
  if (!result.ok) {
10765
11108
  printError(result.error);
10766
11109
  process.exit(1);
10767
11110
  }
10768
- const { project_id, token, service_url } = result.data;
11111
+ const { project_id, token, service_url, user } = result.data;
10769
11112
  await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
10770
- await (0, import_promises3.writeFile)(CREDENTIALS_FILE, `token: ${token}
11113
+ await (0, import_promises3.writeFile)(
11114
+ CREDENTIALS_FILE,
11115
+ `token: ${token}
10771
11116
  service_url: ${service_url}
10772
- `);
11117
+ provider_token: ${providerToken}
11118
+ `,
11119
+ { mode: 384 }
11120
+ );
11121
+ await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11122
+ });
10773
11123
  try {
10774
- await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
10775
- await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
10776
- `);
11124
+ await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11125
+ await (0, import_promises3.appendFile)(
11126
+ GLOBAL_CREDENTIALS_FILE,
11127
+ `${remote} token: ${token}
11128
+ ${remote} provider_token: ${providerToken}
11129
+ `
11130
+ );
11131
+ await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11132
+ });
10777
11133
  } catch {
10778
11134
  }
10779
11135
  printInfo(`Project registered: ${project_id}`);
11136
+ if (user?.email) printInfo(`Authenticated as: ${user.email}`);
10780
11137
  printJson({ project_id, service_url });
10781
11138
  });
10782
11139
  auth.command("verify").description("Verify the current token is valid").action(async () => {
@@ -10810,7 +11167,7 @@ service_url: ${service_url}
10810
11167
  let remote = opts.remote;
10811
11168
  if (!remote) {
10812
11169
  try {
10813
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11170
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10814
11171
  } catch {
10815
11172
  printError("No git remote found. Use --remote to specify one.");
10816
11173
  process.exit(1);
@@ -10838,7 +11195,7 @@ service_url: ${service_url}
10838
11195
 
10839
11196
  // src/lib/hooks.ts
10840
11197
  var import_promises4 = require("node:fs/promises");
10841
- var import_node_path3 = require("node:path");
11198
+ var import_node_path4 = require("node:path");
10842
11199
  var VERITY_STOP_HOOK = {
10843
11200
  type: "command",
10844
11201
  command: "verity analyze",
@@ -10849,6 +11206,10 @@ var VERITY_INTENT_HOOK = {
10849
11206
  type: "command",
10850
11207
  command: "verity intent capture"
10851
11208
  };
11209
+ var VERITY_BASELINE_HOOK = {
11210
+ type: "command",
11211
+ command: "verity baseline capture"
11212
+ };
10852
11213
  var GUARD_TIMEOUT = 300;
10853
11214
  function buildGuardHook(on) {
10854
11215
  return {
@@ -10861,9 +11222,13 @@ function buildGuardHook(on) {
10861
11222
  var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
10862
11223
  var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
10863
11224
  var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
11225
+ var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
10864
11226
  function isVerityGuardHook(entry) {
10865
11227
  return VERITY_GUARD_RE.test(entry.command ?? "");
10866
11228
  }
11229
+ function isVerityBaselineHook(entry) {
11230
+ return VERITY_BASELINE_RE.test(entry.command ?? "");
11231
+ }
10867
11232
  var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
10868
11233
  var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
10869
11234
  function isVerityStopHook(entry) {
@@ -10875,7 +11240,7 @@ function isVerityIntentHook(entry) {
10875
11240
  return VERITY_INTENT_RE.test(c) || LEGACY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh") || c.includes(".gate/hooks/capture-intent.sh");
10876
11241
  }
10877
11242
  function isVerityHook(entry) {
10878
- return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry);
11243
+ return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
10879
11244
  }
10880
11245
  function isCurrentVerityStopHook(entry) {
10881
11246
  const c = entry.command ?? "";
@@ -10886,7 +11251,7 @@ function isCurrentVerityIntentHook(entry) {
10886
11251
  return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
10887
11252
  }
10888
11253
  function isCurrentVerityHook(entry) {
10889
- return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry);
11254
+ return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
10890
11255
  }
10891
11256
  function settingsHasLegacyHook(settings) {
10892
11257
  for (const groups of Object.values(settings.hooks ?? {})) {
@@ -10928,16 +11293,18 @@ async function readAllSettings() {
10928
11293
  async function checkAllVerityHooks() {
10929
11294
  let stop = false;
10930
11295
  let intent = false;
11296
+ let baseline = false;
10931
11297
  let guard = false;
10932
11298
  let guardOn = [];
10933
11299
  for (const settings of await readAllSettings()) {
10934
11300
  const r = checkVerityHooks(settings);
10935
11301
  stop = stop || r.stop;
10936
11302
  intent = intent || r.intent;
11303
+ baseline = baseline || r.baseline;
10937
11304
  guard = guard || r.guard;
10938
11305
  if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
10939
11306
  }
10940
- return { stop, intent, guard, guardOn };
11307
+ return { stop, intent, baseline, guard, guardOn };
10941
11308
  }
10942
11309
  async function checkExternalVerityHooks() {
10943
11310
  let stop = false;
@@ -10960,12 +11327,14 @@ async function checkExternalVerityHooks() {
10960
11327
  async function checkAllVerityHooksDetailed() {
10961
11328
  let stop = false;
10962
11329
  let intent = false;
11330
+ let baseline = false;
10963
11331
  let hasCurrent = false;
10964
11332
  let hasLegacy = false;
10965
11333
  for (const settings of await readAllSettings()) {
10966
11334
  const r = checkVerityHooks(settings);
10967
11335
  stop = stop || r.stop;
10968
11336
  intent = intent || r.intent;
11337
+ baseline = baseline || r.baseline;
10969
11338
  for (const groups of Object.values(settings.hooks ?? {})) {
10970
11339
  for (const g of groups ?? []) {
10971
11340
  for (const h of g.hooks ?? []) {
@@ -10975,22 +11344,22 @@ async function checkAllVerityHooksDetailed() {
10975
11344
  }
10976
11345
  }
10977
11346
  }
10978
- return { stop, intent, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
11347
+ return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
10979
11348
  }
10980
11349
  async function writeSettings(settings) {
10981
- await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
11350
+ await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(CLAUDE_SETTINGS_FILE), { recursive: true });
10982
11351
  await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
10983
11352
  }
10984
11353
  async function readSettingsAt(root) {
10985
11354
  try {
10986
- return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path3.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11355
+ return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
10987
11356
  } catch {
10988
11357
  return {};
10989
11358
  }
10990
11359
  }
10991
11360
  async function writeSettingsAt(root, settings) {
10992
- const file = (0, import_node_path3.join)(root, CLAUDE_SETTINGS_FILE);
10993
- await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(file), { recursive: true });
11361
+ const file = (0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE);
11362
+ await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
10994
11363
  await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
10995
11364
  }
10996
11365
  async function hasLegacyHooksAt(root) {
@@ -11018,6 +11387,10 @@ function checkVerityHooks(settings) {
11018
11387
  const hasIntent = intentGroups.some(
11019
11388
  (g) => g.hooks?.some((h) => isCurrentVerityIntentHook(h))
11020
11389
  );
11390
+ const sessionStartGroups = hooks["SessionStart"] ?? [];
11391
+ const hasBaseline = sessionStartGroups.some(
11392
+ (g) => g.hooks?.some((h) => isVerityBaselineHook(h))
11393
+ );
11021
11394
  let guard = false;
11022
11395
  let guardOn = [];
11023
11396
  for (const g of hooks["PreToolUse"] ?? []) {
@@ -11030,16 +11403,17 @@ function checkVerityHooks(settings) {
11030
11403
  }
11031
11404
  }
11032
11405
  }
11033
- return { stop: hasStop, intent: hasIntent, guard, guardOn };
11406
+ return { stop: hasStop, intent: hasIntent, baseline: hasBaseline, guard, guardOn };
11034
11407
  }
11035
- function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false }) {
11408
+ function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false, baseline: false }) {
11036
11409
  const local = checkVerityHooks(settings);
11037
11410
  const existing = {
11038
11411
  stop: local.stop || externalPresent.stop,
11039
- intent: local.intent || externalPresent.intent
11412
+ intent: local.intent || externalPresent.intent,
11413
+ baseline: local.baseline || (externalPresent.baseline ?? false)
11040
11414
  };
11041
11415
  const hasLocalLegacy = settingsHasLegacyHook(settings);
11042
- if (existing.stop && existing.intent && !force && !hasLocalLegacy) {
11416
+ if (existing.stop && existing.intent && existing.baseline && !force && !hasLocalLegacy) {
11043
11417
  return { ok: false, error: "Verity hooks already installed. Use --force to overwrite." };
11044
11418
  }
11045
11419
  const newSettings = { ...settings };
@@ -11047,7 +11421,7 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
11047
11421
  newSettings.hooks = {};
11048
11422
  }
11049
11423
  const shouldStrip = force ? isVerityHook : isLegacyHook;
11050
- for (const key of ["Stop", "UserPromptSubmit"]) {
11424
+ for (const key of ["Stop", "UserPromptSubmit", "SessionStart"]) {
11051
11425
  const groups = newSettings.hooks[key];
11052
11426
  if (groups) {
11053
11427
  newSettings.hooks[key] = groups.map((g) => ({
@@ -11074,6 +11448,15 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
11074
11448
  if (!newSettings.hooks["UserPromptSubmit"]) newSettings.hooks["UserPromptSubmit"] = [];
11075
11449
  newSettings.hooks["UserPromptSubmit"].push({ hooks: [VERITY_INTENT_HOOK] });
11076
11450
  }
11451
+ if (!existing.baseline) {
11452
+ if (!newSettings.hooks["SessionStart"]) {
11453
+ newSettings.hooks["SessionStart"] = [];
11454
+ }
11455
+ newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
11456
+ } else if (force && local.baseline) {
11457
+ if (!newSettings.hooks["SessionStart"]) newSettings.hooks["SessionStart"] = [];
11458
+ newSettings.hooks["SessionStart"].push({ hooks: [VERITY_BASELINE_HOOK] });
11459
+ }
11077
11460
  return { ok: true, data: newSettings };
11078
11461
  }
11079
11462
  function removeVerityHooks(settings) {
@@ -11107,6 +11490,7 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
11107
11490
  (s.hooks[event] ??= []).push(group);
11108
11491
  };
11109
11492
  if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
11493
+ push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
11110
11494
  if (moments.includes("stop") && !externalPresent.stop) {
11111
11495
  push("Stop", { hooks: [VERITY_STOP_HOOK] });
11112
11496
  }
@@ -11155,7 +11539,7 @@ function registerHooksCommands(program2) {
11155
11539
  return;
11156
11540
  }
11157
11541
  const detail = await checkAllVerityHooksDetailed();
11158
- const present = { stop: detail.stop, intent: detail.intent };
11542
+ const present = { stop: detail.stop, intent: detail.intent, baseline: detail.baseline };
11159
11543
  if (detail.legacyOnly) {
11160
11544
  printInfo("Found a legacy GATE.md hook \u2014 upgrading it to Verity...");
11161
11545
  const settings2 = removeVerityHooks(await readSettings());
@@ -11168,11 +11552,12 @@ function registerHooksCommands(program2) {
11168
11552
  printInfo("Verity hooks installed in .claude/settings.json (legacy hook removed)");
11169
11553
  printInfo(" Stop hook: verity analyze");
11170
11554
  printInfo(" UserPromptSubmit hook: verity intent capture");
11555
+ printInfo(" SessionStart hook: verity baseline capture");
11171
11556
  return;
11172
11557
  }
11173
11558
  const settings = await readSettings();
11174
11559
  const hasLocalLegacy = settingsHasLegacyHook(settings);
11175
- if (!force && present.stop && present.intent && !hasLocalLegacy) {
11560
+ if (!force && present.stop && present.intent && present.baseline && !hasLocalLegacy) {
11176
11561
  printInfo("Verity hooks already installed (found in .claude/settings.json, settings.local.json, or your global settings).");
11177
11562
  printInfo("Use --force to rewrite the project settings.json copy.");
11178
11563
  return;
@@ -11186,11 +11571,13 @@ function registerHooksCommands(program2) {
11186
11571
  printInfo("Verity hooks installed in .claude/settings.json");
11187
11572
  printInfo(" Stop hook: verity analyze");
11188
11573
  printInfo(" UserPromptSubmit hook: verity intent capture");
11574
+ printInfo(" SessionStart hook: verity baseline capture");
11189
11575
  });
11190
11576
  hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
11191
11577
  const status = await checkAllVerityHooks();
11192
11578
  printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
11193
11579
  printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
11580
+ printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
11194
11581
  const gates = status.guard ? status.guardOn.join(", ") : "none";
11195
11582
  printInfo(`Git-moment gate (verity guard): ${status.guard ? `installed [${gates}]` : "not installed"}`);
11196
11583
  if (!status.stop && !status.guard) {
@@ -11213,8 +11600,8 @@ var import_node_crypto2 = require("node:crypto");
11213
11600
 
11214
11601
  // src/lib/conversation-buffer.ts
11215
11602
  var import_promises5 = require("node:fs/promises");
11216
- var import_node_fs2 = require("node:fs");
11217
- var import_node_child_process4 = require("node:child_process");
11603
+ var import_node_fs3 = require("node:fs");
11604
+ var import_node_child_process5 = require("node:child_process");
11218
11605
  function stripImageReferences(text) {
11219
11606
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
11220
11607
  }
@@ -11243,20 +11630,33 @@ async function appendToConversationBuffer(prompt, sessionId) {
11243
11630
  } catch {
11244
11631
  }
11245
11632
  }
11246
- async function readAndClearConversationBuffer() {
11633
+ async function readAndClearConversationBuffer(currentSessionId) {
11247
11634
  try {
11248
- if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
11635
+ if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
11249
11636
  const entries = await readBufferEntries();
11250
- await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
11251
- });
11252
- if (entries.length > 0) {
11637
+ let mine = entries;
11638
+ let others = [];
11639
+ if (currentSessionId) {
11640
+ mine = entries.filter((e) => e.session_id === currentSessionId || !e.session_id);
11641
+ others = entries.filter((e) => e.session_id && e.session_id !== currentSessionId);
11642
+ }
11643
+ if (others.length > 0) {
11644
+ const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
11645
+ const tmpFile = `${CONVERSATION_BUFFER_FILE}.tmp`;
11646
+ await (0, import_promises5.writeFile)(tmpFile, remaining);
11647
+ await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
11648
+ } else {
11649
+ await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
11650
+ });
11651
+ }
11652
+ if (mine.length > 0) {
11253
11653
  return {
11254
- prompts: entries,
11654
+ prompts: mine,
11255
11655
  recent_commits: getRecentCommitMessages()
11256
11656
  };
11257
11657
  }
11258
11658
  }
11259
- if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
11659
+ if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
11260
11660
  try {
11261
11661
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
11262
11662
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -11300,7 +11700,7 @@ async function readBufferEntries() {
11300
11700
  }
11301
11701
  function getRecentCommitMessages() {
11302
11702
  try {
11303
- const output = (0, import_node_child_process4.execSync)(
11703
+ const output = (0, import_node_child_process5.execSync)(
11304
11704
  'git log --since="30 minutes ago" --format="%s" -5',
11305
11705
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11306
11706
  ).trim();
@@ -11313,8 +11713,8 @@ function getRecentCommitMessages() {
11313
11713
 
11314
11714
  // src/lib/task-context-buffer.ts
11315
11715
  var import_promises6 = require("node:fs/promises");
11316
- var import_node_fs3 = require("node:fs");
11317
- var import_node_path4 = require("node:path");
11716
+ var import_node_fs4 = require("node:fs");
11717
+ var import_node_path5 = require("node:path");
11318
11718
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11319
11719
  var MAX_BUFFER_BYTES = 500 * 1024;
11320
11720
  var MAX_PROMPT_CHARS = 2e3;
@@ -11353,7 +11753,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
11353
11753
  }
11354
11754
  async function readTaskContextBuffer(taskId) {
11355
11755
  const filePath = bufferPath(taskId);
11356
- if (!(0, import_node_fs3.existsSync)(filePath)) return null;
11756
+ if (!(0, import_node_fs4.existsSync)(filePath)) return null;
11357
11757
  try {
11358
11758
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
11359
11759
  if (!content.trim()) return null;
@@ -11387,12 +11787,12 @@ async function readTaskContextBuffer(taskId) {
11387
11787
  }
11388
11788
  async function cleanupTaskContextBuffers() {
11389
11789
  try {
11390
- if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
11790
+ if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
11391
11791
  const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
11392
11792
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11393
11793
  for (const file of files) {
11394
11794
  if (!file.endsWith(".jsonl")) continue;
11395
- const filePath = (0, import_node_path4.join)(TASK_CONTEXT_DIR, file);
11795
+ const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
11396
11796
  try {
11397
11797
  const stats = await (0, import_promises6.stat)(filePath);
11398
11798
  if (stats.mtimeMs < cutoffMs) {
@@ -11406,13 +11806,13 @@ async function cleanupTaskContextBuffers() {
11406
11806
  }
11407
11807
  function bufferPath(taskId) {
11408
11808
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11409
- return (0, import_node_path4.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11809
+ return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11410
11810
  }
11411
11811
  async function appendEntry(taskId, entry) {
11412
11812
  try {
11413
11813
  await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
11414
11814
  const filePath = bufferPath(taskId);
11415
- if ((0, import_node_fs3.existsSync)(filePath)) {
11815
+ if ((0, import_node_fs4.existsSync)(filePath)) {
11416
11816
  const stats = await (0, import_promises6.stat)(filePath);
11417
11817
  if (stats.size >= MAX_BUFFER_BYTES) {
11418
11818
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
@@ -11423,7 +11823,7 @@ async function appendEntry(taskId, entry) {
11423
11823
  }
11424
11824
  }
11425
11825
  const line = JSON.stringify(entry) + "\n";
11426
- const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11826
+ const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11427
11827
  await (0, import_promises6.writeFile)(filePath, existing + line);
11428
11828
  } catch {
11429
11829
  }
@@ -11431,8 +11831,8 @@ async function appendEntry(taskId, entry) {
11431
11831
 
11432
11832
  // src/lib/memory-retrieval.ts
11433
11833
  var import_promises7 = require("node:fs/promises");
11434
- var import_node_fs4 = require("node:fs");
11435
- var import_node_path5 = require("node:path");
11834
+ var import_node_fs5 = require("node:fs");
11835
+ var import_node_path6 = require("node:path");
11436
11836
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11437
11837
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11438
11838
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -11525,19 +11925,19 @@ function parseFrontmatter(content) {
11525
11925
  return { fm, body: match[2].trim() };
11526
11926
  }
11527
11927
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
11528
- if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
11928
+ if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
11529
11929
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
11530
11930
  const promptTokens = tokenize(promptText);
11531
11931
  const nodes = [];
11532
11932
  for (const domain of DOMAINS) {
11533
- const domainDir = (0, import_node_path5.join)(memoryDir(), domain);
11534
- if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
11933
+ const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
11934
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
11535
11935
  try {
11536
11936
  const files = await (0, import_promises7.readdir)(domainDir);
11537
11937
  for (const file of files) {
11538
11938
  if (!file.endsWith(".md")) continue;
11539
11939
  try {
11540
- const content = await (0, import_promises7.readFile)((0, import_node_path5.join)(domainDir, file), "utf-8");
11940
+ const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
11541
11941
  const { fm, body } = parseFrontmatter(content);
11542
11942
  if (fm.status && fm.status !== "active") continue;
11543
11943
  nodes.push({
@@ -11596,8 +11996,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
11596
11996
 
11597
11997
  // src/lib/memory-sync.ts
11598
11998
  var import_promises8 = require("node:fs/promises");
11599
- var import_node_fs5 = require("node:fs");
11600
- var import_node_path6 = require("node:path");
11999
+ var import_node_fs6 = require("node:fs");
12000
+ var import_node_path7 = require("node:path");
11601
12001
  var import_node_crypto = require("node:crypto");
11602
12002
 
11603
12003
  // src/lib/glob-match.ts
@@ -11668,32 +12068,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
11668
12068
  async function ensureMemoryDir() {
11669
12069
  await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
11670
12070
  for (const domain of DOMAINS2) {
11671
- await (0, import_promises8.mkdir)((0, import_node_path6.join)(memoryDir2(), domain), { recursive: true });
12071
+ await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
11672
12072
  }
11673
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"))) {
11674
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
12073
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
12074
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
11675
12075
  }
11676
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "index.md"))) {
11677
- 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");
12076
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
12077
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
11678
12078
  }
11679
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "log.md"))) {
11680
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
12079
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
12080
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
11681
12081
  }
11682
12082
  }
11683
12083
  async function buildManifest() {
11684
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
12084
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
11685
12085
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
11686
12086
  }
11687
12087
  const nodes = [];
11688
12088
  for (const domain of DOMAINS2) {
11689
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11690
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12089
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12090
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11691
12091
  try {
11692
12092
  const files = await (0, import_promises8.readdir)(domainDir);
11693
12093
  for (const file of files) {
11694
12094
  if (!file.endsWith(".md")) continue;
11695
12095
  const filePath = `${domain}/${file}`;
11696
- const fullPath = (0, import_node_path6.join)(memoryDir2(), filePath);
12096
+ const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
11697
12097
  try {
11698
12098
  const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
11699
12099
  const hash = (0, import_node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -11706,13 +12106,13 @@ async function buildManifest() {
11706
12106
  }
11707
12107
  let indexHash = null;
11708
12108
  try {
11709
- const indexContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "index.md"), "utf-8");
12109
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
11710
12110
  indexHash = `sha256:${(0, import_node_crypto.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
11711
12111
  } catch {
11712
12112
  }
11713
12113
  let logLength = 0;
11714
12114
  try {
11715
- const logContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "utf-8");
12115
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
11716
12116
  logLength = logContent.split("\n").length;
11717
12117
  } catch {
11718
12118
  }
@@ -11723,15 +12123,15 @@ function hashContent(content) {
11723
12123
  }
11724
12124
  async function readOnDiskNodes() {
11725
12125
  const out = /* @__PURE__ */ new Map();
11726
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12126
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
11727
12127
  for (const domain of DOMAINS2) {
11728
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11729
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12128
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12129
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11730
12130
  try {
11731
12131
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
11732
12132
  if (!file.endsWith(".md")) continue;
11733
12133
  try {
11734
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8")));
12134
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
11735
12135
  } catch {
11736
12136
  }
11737
12137
  }
@@ -11777,8 +12177,8 @@ async function computeEditedNodeUploads() {
11777
12177
  const uploads = [];
11778
12178
  for (const [path, prevHash] of prev) {
11779
12179
  if (prevHash == null) continue;
11780
- const full = (0, import_node_path6.join)(memoryDir2(), path);
11781
- if (!(0, import_node_fs5.existsSync)(full)) continue;
12180
+ const full = (0, import_node_path7.join)(memoryDir2(), path);
12181
+ if (!(0, import_node_fs6.existsSync)(full)) continue;
11782
12182
  let content;
11783
12183
  try {
11784
12184
  content = await (0, import_promises8.readFile)(full, "utf-8");
@@ -11814,15 +12214,15 @@ async function applyMemoryWrites(writes, opts = {}) {
11814
12214
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
11815
12215
  for (const n of notes) logLines.push(` - ${n}`);
11816
12216
  try {
11817
- const existing = (0, 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";
11818
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
12217
+ const existing = (0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12218
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
11819
12219
  } catch {
11820
12220
  }
11821
12221
  await recordSyncedNodePaths();
11822
12222
  return count;
11823
12223
  }
11824
12224
  async function applyOneWrite(write, treePaths) {
11825
- const fullPath = (0, import_node_path6.join)(memoryDir2(), write.path);
12225
+ const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
11826
12226
  const notes = [];
11827
12227
  let content = write.content;
11828
12228
  if (treePaths && treePaths.length > 0) {
@@ -11832,7 +12232,7 @@ async function applyOneWrite(write, treePaths) {
11832
12232
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
11833
12233
  }
11834
12234
  }
11835
- if ((0, import_node_fs5.existsSync)(fullPath)) {
12235
+ if ((0, import_node_fs6.existsSync)(fullPath)) {
11836
12236
  let existing = "";
11837
12237
  try {
11838
12238
  existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
@@ -11844,7 +12244,7 @@ async function applyOneWrite(write, treePaths) {
11844
12244
  return { written: false, notes };
11845
12245
  }
11846
12246
  }
11847
- await (0, import_promises8.mkdir)((0, import_node_path6.dirname)(fullPath), { recursive: true });
12247
+ await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
11848
12248
  await (0, import_promises8.writeFile)(fullPath, content);
11849
12249
  return { written: true, notes };
11850
12250
  }
@@ -11885,8 +12285,8 @@ async function regenerateIndex() {
11885
12285
  ];
11886
12286
  let totalNodes = 0;
11887
12287
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
11888
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11889
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12288
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12289
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11890
12290
  try {
11891
12291
  const files = await (0, import_promises8.readdir)(domainDir);
11892
12292
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -11896,7 +12296,7 @@ async function regenerateIndex() {
11896
12296
  for (const file of mdFiles.sort()) {
11897
12297
  const slug = file.replace(/\.md$/, "");
11898
12298
  try {
11899
- const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
12299
+ const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
11900
12300
  const title = pickFrontmatter(content, "title") ?? slug;
11901
12301
  const kind = pickFrontmatter(content, "kind") ?? "-";
11902
12302
  const confidence = pickFrontmatter(content, "confidence");
@@ -11920,7 +12320,7 @@ async function regenerateIndex() {
11920
12320
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
11921
12321
  }
11922
12322
  const next = lines.join("\n") + "\n";
11923
- const indexPath = (0, import_node_path6.join)(memoryDir2(), "index.md");
12323
+ const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
11924
12324
  let existing = null;
11925
12325
  try {
11926
12326
  existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
@@ -12002,9 +12402,9 @@ function hasLegacyMemoryBlock(text) {
12002
12402
  return findMarker(text, LEGACY_MD_START) !== -1;
12003
12403
  }
12004
12404
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12005
- const claudeMdPath = (0, import_node_path6.join)(cwd, "CLAUDE.md");
12405
+ const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
12006
12406
  let existing = "";
12007
- if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
12407
+ if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12008
12408
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
12009
12409
  }
12010
12410
  let startTag = CLAUDE_MD_START;
@@ -12134,7 +12534,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
12134
12534
  `;
12135
12535
 
12136
12536
  // src/commands/intent.ts
12137
- var import_node_fs6 = require("node:fs");
12537
+ var import_node_fs7 = require("node:fs");
12138
12538
  function registerIntentCommands(program2) {
12139
12539
  const intent = program2.command("intent").description("Manage intent capture");
12140
12540
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
@@ -12143,7 +12543,7 @@ function registerIntentCommands(program2) {
12143
12543
  process.chdir(repoRoot());
12144
12544
  } catch {
12145
12545
  }
12146
- if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
12546
+ if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
12147
12547
  process.exit(0);
12148
12548
  }
12149
12549
  const chunks = [];
@@ -12636,281 +13036,92 @@ function registerFeedbackCommand(program2) {
12636
13036
  const feedbackCmd = program2.command("feedback").description("Send feedback to the Verity team");
12637
13037
  feedbackCmd.command("message <text>").description("Send general feedback").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
12638
13038
  const globals = program2.opts();
12639
- await sendGeneralFeedback(message, opts, globals);
12640
- });
12641
- feedbackCmd.command("finding <run-id> <pattern-id> <action> [note]").description("Submit per-finding feedback (false_positive, acknowledged, useful, etc.)").option("--file <path>", "File path the finding applies to").option("--line <n>", "Line number", parseInt).action(async (runId, patternId, action, note, opts) => {
12642
- const globals = program2.opts();
12643
- const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
12644
- if (!validActions.includes(action)) {
12645
- printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
12646
- process.exit(1);
12647
- }
12648
- const tokenResult = await resolveToken(globals.token);
12649
- if (!tokenResult.ok) {
12650
- printError(tokenResult.error);
12651
- process.exit(1);
12652
- }
12653
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
12654
- if (!urlResult.ok) {
12655
- printError(urlResult.error);
12656
- process.exit(1);
12657
- }
12658
- const body = {
12659
- run_id: runId,
12660
- pattern_id: patternId,
12661
- action
12662
- };
12663
- if (note) body.note = note;
12664
- if (opts.file) body.file_path = opts.file;
12665
- if (opts.line != null) body.line = opts.line;
12666
- const result = await apiRequest({
12667
- method: "POST",
12668
- path: "/feedback/findings",
12669
- serviceUrl: urlResult.data,
12670
- token: tokenResult.data.token,
12671
- body,
12672
- verbose: globals.verbose
12673
- });
12674
- if (!result.ok) {
12675
- printError(`Couldn't submit finding feedback: ${result.error}`);
12676
- process.exit(1);
12677
- }
12678
- const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
12679
- printInfo(status);
12680
- });
12681
- feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
12682
- if (!message) return;
12683
- const globals = program2.opts();
12684
- await sendGeneralFeedback(message, opts, globals);
12685
- });
12686
- }
12687
- async function sendGeneralFeedback(message, opts, globals) {
12688
- const tokenResult = await resolveToken(globals.token);
12689
- if (!tokenResult.ok) {
12690
- printError(tokenResult.error);
12691
- printInfo(`Your message: ${message}`);
12692
- process.exit(1);
12693
- }
12694
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
12695
- if (!urlResult.ok) {
12696
- printError(urlResult.error);
12697
- printInfo(`Your message: ${message}`);
12698
- process.exit(1);
12699
- }
12700
- const body = { message };
12701
- const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
12702
- const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
12703
- if (sessionId) body.session_id = sessionId;
12704
- if (model) body.agent_model = model;
12705
- const result = await apiRequest({
12706
- method: "POST",
12707
- path: "/feedback",
12708
- serviceUrl: urlResult.data,
12709
- token: tokenResult.data.token,
12710
- body,
12711
- verbose: globals.verbose
12712
- });
12713
- if (!result.ok) {
12714
- printError(`Couldn't send feedback: ${result.error}`);
12715
- printInfo(`Your message: ${message}`);
12716
- process.exit(1);
12717
- }
12718
- printInfo("Thanks, feedback sent!");
12719
- }
12720
-
12721
- // src/commands/analyze.ts
12722
- var import_node_fs18 = require("node:fs");
12723
- var import_node_path13 = require("node:path");
12724
-
12725
- // src/lib/git.ts
12726
- var import_node_child_process5 = require("node:child_process");
12727
- var import_node_fs7 = require("node:fs");
12728
- var import_node_path7 = require("node:path");
12729
- function resolveFile(relpath) {
12730
- if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
12731
- if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
12732
- try {
12733
- const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
12734
- for (const entry of entries) {
12735
- if (!entry.isDirectory()) continue;
12736
- const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
12737
- if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
12738
- }
12739
- } catch {
12740
- }
12741
- }
12742
- return null;
12743
- }
12744
- function execGit(cmd) {
12745
- try {
12746
- return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12747
- } catch {
12748
- return "";
12749
- }
12750
- }
12751
- function splitLines(s) {
12752
- return s.split("\n").filter((l) => l.length > 0);
12753
- }
12754
- var SHA_RE = /^[0-9a-f]{40}$/;
12755
- function readBaselineSha() {
12756
- if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
12757
- let sha;
12758
- try {
12759
- sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
12760
- } catch {
12761
- return null;
12762
- }
12763
- if (!SHA_RE.test(sha)) return null;
12764
- const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
12765
- if (!reachable) {
12766
- try {
12767
- (0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
12768
- } catch {
12769
- }
12770
- return null;
12771
- }
12772
- return sha;
12773
- }
12774
- function writeBaselineSha(sha) {
12775
- if (!SHA_RE.test(sha)) return;
12776
- try {
12777
- (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
12778
- (0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
12779
- } catch {
12780
- }
12781
- }
12782
- function getChangedFiles() {
12783
- const sets = /* @__PURE__ */ new Set();
12784
- let hasRecentCommitFiles = false;
12785
- for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
12786
- for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
12787
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
12788
- const baseline = readBaselineSha();
12789
- if (baseline) {
12790
- const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
12791
- if (committed.length > 0) {
12792
- hasRecentCommitFiles = true;
12793
- for (const f of committed) sets.add(f);
12794
- }
12795
- } else {
12796
- const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
12797
- const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
12798
- const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
12799
- const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
12800
- if (commitAge < 120 && !hasUnstaged && !hasStaged) {
12801
- const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
12802
- if (recentFiles.length > 0) {
12803
- hasRecentCommitFiles = true;
12804
- for (const f of recentFiles) sets.add(f);
12805
- }
12806
- }
12807
- }
12808
- for (const f of getWorktreeFiles()) sets.add(f);
12809
- const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12810
- return { files: filtered, hasRecentCommitFiles };
12811
- }
12812
- function getStagedFiles() {
12813
- return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12814
- }
12815
- function getPushRangeFiles() {
12816
- const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12817
- const resolvers = [
12818
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
12819
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
12820
- () => {
12821
- const branch = execGit("git rev-parse --abbrev-ref HEAD");
12822
- return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
12823
- }
12824
- ];
12825
- for (const resolve of resolvers) {
12826
- const range = resolve();
12827
- if (range) return { files: diff(range), range };
12828
- }
12829
- const baseline = readBaselineSha();
12830
- if (baseline) {
12831
- const files = diff(`${baseline}..HEAD`);
12832
- if (files.length > 0) return { files, range: `${baseline}..HEAD` };
12833
- }
12834
- const last = diff("HEAD~1..HEAD");
12835
- return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
12836
- }
12837
- function getPushRangeMessages() {
12838
- const { range } = getPushRangeFiles();
12839
- if (!range) return "";
12840
- return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
12841
- }
12842
- function getWorktreeFiles() {
12843
- const result = [];
12844
- const worktreeDir = ".claude/worktrees";
12845
- if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
12846
- try {
12847
- const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
12848
- const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
12849
- for (const entry of entries) {
12850
- if (!entry.isDirectory()) continue;
12851
- const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
12852
- scanDir(wtDir, wtDir, fiveMinAgo, result);
12853
- }
12854
- } catch {
12855
- }
12856
- return result;
12857
- }
12858
- function scanDir(baseDir, dir, minMtime, result) {
12859
- try {
12860
- const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
12861
- for (const entry of entries) {
12862
- const fullPath = (0, import_node_path7.join)(dir, entry.name);
12863
- if (entry.isDirectory()) {
12864
- if (entry.name === "node_modules" || entry.name === ".git") continue;
12865
- scanDir(baseDir, fullPath, minMtime, result);
12866
- } else if (entry.isFile()) {
12867
- const ext = (0, import_node_path7.extname)(entry.name).slice(1);
12868
- if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
12869
- try {
12870
- const stat3 = (0, import_node_fs7.statSync)(fullPath);
12871
- if (stat3.mtimeMs >= minMtime) {
12872
- const relPath = fullPath.slice(baseDir.length + 1);
12873
- result.push(relPath);
12874
- }
12875
- } catch {
12876
- }
12877
- }
13039
+ await sendGeneralFeedback(message, opts, globals);
13040
+ });
13041
+ feedbackCmd.command("finding <run-id> <pattern-id> <action> [note]").description("Submit per-finding feedback (false_positive, acknowledged, useful, etc.)").option("--file <path>", "File path the finding applies to").option("--line <n>", "Line number", parseInt).action(async (runId, patternId, action, note, opts) => {
13042
+ const globals = program2.opts();
13043
+ const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
13044
+ if (!validActions.includes(action)) {
13045
+ printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
13046
+ process.exit(1);
12878
13047
  }
12879
- } catch {
12880
- }
12881
- }
12882
- function filterAnalyzable(files) {
12883
- return files.filter((f) => {
12884
- const ext = (0, import_node_path7.extname)(f).slice(1);
12885
- return ANALYZABLE_EXTENSIONS.has(ext);
13048
+ const tokenResult = await resolveToken(globals.token);
13049
+ if (!tokenResult.ok) {
13050
+ printError(tokenResult.error);
13051
+ process.exit(1);
13052
+ }
13053
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
13054
+ if (!urlResult.ok) {
13055
+ printError(urlResult.error);
13056
+ process.exit(1);
13057
+ }
13058
+ const body = {
13059
+ run_id: runId,
13060
+ pattern_id: patternId,
13061
+ action
13062
+ };
13063
+ if (note) body.note = note;
13064
+ if (opts.file) body.file_path = opts.file;
13065
+ if (opts.line != null) body.line = opts.line;
13066
+ const result = await apiRequest({
13067
+ method: "POST",
13068
+ path: "/feedback/findings",
13069
+ serviceUrl: urlResult.data,
13070
+ token: tokenResult.data.token,
13071
+ body,
13072
+ verbose: globals.verbose
13073
+ });
13074
+ if (!result.ok) {
13075
+ printError(`Couldn't submit finding feedback: ${result.error}`);
13076
+ process.exit(1);
13077
+ }
13078
+ const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
13079
+ printInfo(status);
12886
13080
  });
12887
- }
12888
- function filterReviewable(files) {
12889
- return files.filter((f) => {
12890
- const ext = (0, import_node_path7.extname)(f).slice(1);
12891
- if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
12892
- if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
12893
- const basename2 = f.split("/").pop() ?? "";
12894
- if (REVIEWABLE_FILENAMES.has(basename2)) return true;
12895
- if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
12896
- return false;
13081
+ feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
13082
+ if (!message) return;
13083
+ const globals = program2.opts();
13084
+ await sendGeneralFeedback(message, opts, globals);
12897
13085
  });
12898
13086
  }
12899
- function filterSecurity(files) {
12900
- return files.filter(
12901
- (f) => SECURITY_PATTERNS.some((p) => p.test(f))
12902
- );
12903
- }
12904
- function getCurrentCommit() {
12905
- return execGit("git rev-parse HEAD") || "no-git";
12906
- }
12907
- function listTrackedFiles() {
12908
- const set = /* @__PURE__ */ new Set();
12909
- for (const f of splitLines(execGit("git ls-files"))) set.add(f);
12910
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
12911
- return Array.from(set);
13087
+ async function sendGeneralFeedback(message, opts, globals) {
13088
+ const tokenResult = await resolveToken(globals.token);
13089
+ if (!tokenResult.ok) {
13090
+ printError(tokenResult.error);
13091
+ printInfo(`Your message: ${message}`);
13092
+ process.exit(1);
13093
+ }
13094
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
13095
+ if (!urlResult.ok) {
13096
+ printError(urlResult.error);
13097
+ printInfo(`Your message: ${message}`);
13098
+ process.exit(1);
13099
+ }
13100
+ const body = { message };
13101
+ const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
13102
+ const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
13103
+ if (sessionId) body.session_id = sessionId;
13104
+ if (model) body.agent_model = model;
13105
+ const result = await apiRequest({
13106
+ method: "POST",
13107
+ path: "/feedback",
13108
+ serviceUrl: urlResult.data,
13109
+ token: tokenResult.data.token,
13110
+ body,
13111
+ verbose: globals.verbose
13112
+ });
13113
+ if (!result.ok) {
13114
+ printError(`Couldn't send feedback: ${result.error}`);
13115
+ printInfo(`Your message: ${message}`);
13116
+ process.exit(1);
13117
+ }
13118
+ printInfo("Thanks, feedback sent!");
12912
13119
  }
12913
13120
 
13121
+ // src/commands/analyze.ts
13122
+ var import_node_fs19 = require("node:fs");
13123
+ var import_node_path14 = require("node:path");
13124
+
12914
13125
  // src/lib/files.ts
12915
13126
  var import_node_fs8 = require("node:fs");
12916
13127
  var import_node_path8 = require("node:path");
@@ -13044,10 +13255,15 @@ function collectCodeDelta(files, opts) {
13044
13255
  // src/lib/debounce.ts
13045
13256
  var import_node_fs9 = require("node:fs");
13046
13257
  var import_node_crypto3 = require("node:crypto");
13047
- function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
13048
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
13258
+ function scopedFile(base, sessionId) {
13259
+ if (!sessionId) return base;
13260
+ return `${base}.${(0, import_node_crypto3.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
13261
+ }
13262
+ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
13263
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13264
+ if (!(0, import_node_fs9.existsSync)(file)) return null;
13049
13265
  try {
13050
- const lastTs = parseInt((0, import_node_fs9.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
13266
+ const lastTs = parseInt((0, import_node_fs9.readFileSync)(file, "utf-8").trim(), 10);
13051
13267
  const nowTs = Math.floor(Date.now() / 1e3);
13052
13268
  const elapsed = nowTs - lastTs;
13053
13269
  if (elapsed < debounceSeconds) {
@@ -13057,12 +13273,13 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
13057
13273
  }
13058
13274
  return null;
13059
13275
  }
13060
- function checkMtime(files, bypassForRecentCommits) {
13276
+ function checkMtime(files, bypassForRecentCommits, sessionId) {
13061
13277
  if (bypassForRecentCommits) return null;
13062
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
13278
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13279
+ if (!(0, import_node_fs9.existsSync)(file)) return null;
13063
13280
  let debounceTime;
13064
13281
  try {
13065
- debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
13282
+ debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
13066
13283
  } catch {
13067
13284
  return null;
13068
13285
  }
@@ -13094,11 +13311,12 @@ function computeContentHash(files) {
13094
13311
  }
13095
13312
  return hash.digest("hex");
13096
13313
  }
13097
- function checkContentHash(files) {
13314
+ function checkContentHash(files, sessionId) {
13098
13315
  const hash = computeContentHash(files);
13099
- if ((0, import_node_fs9.existsSync)(HASH_FILE)) {
13316
+ const file = scopedFile(HASH_FILE, sessionId);
13317
+ if ((0, import_node_fs9.existsSync)(file)) {
13100
13318
  try {
13101
- const storedHash = (0, import_node_fs9.readFileSync)(HASH_FILE, "utf-8").trim();
13319
+ const storedHash = (0, import_node_fs9.readFileSync)(file, "utf-8").trim();
13102
13320
  if (hash === storedHash) {
13103
13321
  return { skip: "No source changes since last analysis", hash };
13104
13322
  }
@@ -13107,18 +13325,19 @@ function checkContentHash(files) {
13107
13325
  }
13108
13326
  return { skip: null, hash };
13109
13327
  }
13110
- function recordAnalysisStart() {
13328
+ function recordAnalysisStart(sessionId) {
13111
13329
  (0, import_node_fs9.mkdirSync)(VERITY_DIR, { recursive: true });
13112
- (0, import_node_fs9.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
13330
+ (0, import_node_fs9.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
13113
13331
  }
13114
- function recordPassHash(hash) {
13115
- (0, import_node_fs9.writeFileSync)(HASH_FILE, hash);
13332
+ function recordPassHash(hash, sessionId) {
13333
+ (0, import_node_fs9.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
13116
13334
  }
13117
- function narrowToRecent(files) {
13118
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return files;
13335
+ function narrowToRecent(files, sessionId) {
13336
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13337
+ if (!(0, import_node_fs9.existsSync)(file)) return files;
13119
13338
  let debounceTime;
13120
13339
  try {
13121
- debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
13340
+ debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
13122
13341
  } catch {
13123
13342
  return files;
13124
13343
  }
@@ -13464,15 +13683,207 @@ function cleanStaleSnapshots(dir, keepSet) {
13464
13683
  }
13465
13684
  }
13466
13685
 
13467
- // src/lib/offline.ts
13686
+ // src/lib/baseline.ts
13468
13687
  var import_node_fs13 = require("node:fs");
13688
+ var import_node_path11 = require("node:path");
13469
13689
  var import_node_crypto4 = require("node:crypto");
13690
+ var BASELINE_VERSION = 1;
13691
+ var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
13692
+ var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
13693
+ var DEFAULT_SESSION_KEY = "_default";
13694
+ function sessionKey(sessionId) {
13695
+ if (!sessionId) return DEFAULT_SESSION_KEY;
13696
+ return (0, import_node_crypto4.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
13697
+ }
13698
+ function sessionDir(key) {
13699
+ return (0, import_node_path11.join)(projectPath(BASELINE_DIR), key);
13700
+ }
13701
+ function manifestPath(dir) {
13702
+ return (0, import_node_path11.join)(dir, "manifest.json");
13703
+ }
13704
+ function mirrorPath(dir, repoRelPath) {
13705
+ return (0, import_node_path11.join)(dir, "files", repoRelPath);
13706
+ }
13707
+ function captureBaseline(opts = {}) {
13708
+ const key = sessionKey(opts.sessionId);
13709
+ const dir = sessionDir(key);
13710
+ const existing = readManifest(dir);
13711
+ const freshStart = opts.source === "startup" || opts.source === "clear";
13712
+ if (existing && !freshStart) {
13713
+ return { baseline: existing, created: false };
13714
+ }
13715
+ const head_sha = getCurrentCommit();
13716
+ const dirty = getDirtyFiles();
13717
+ try {
13718
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13719
+ } catch {
13720
+ }
13721
+ const filesDir = (0, import_node_path11.join)(dir, "files");
13722
+ const mirrored = [];
13723
+ try {
13724
+ (0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
13725
+ for (const p of dirty) {
13726
+ if (p.includes("..")) continue;
13727
+ const content = safeReadForMirror(projectPath(p));
13728
+ if (content === null) continue;
13729
+ const dest = mirrorPath(dir, p);
13730
+ try {
13731
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(dest), { recursive: true });
13732
+ (0, import_node_fs13.writeFileSync)(dest, content);
13733
+ mirrored.push(p);
13734
+ } catch {
13735
+ }
13736
+ }
13737
+ } catch {
13738
+ }
13739
+ const baseline = {
13740
+ session_id: opts.sessionId ?? "",
13741
+ head_sha,
13742
+ captured_at: Date.now(),
13743
+ dirty_paths: mirrored,
13744
+ version: BASELINE_VERSION
13745
+ };
13746
+ try {
13747
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
13748
+ (0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
13749
+ } catch {
13750
+ }
13751
+ pruneOldBaselines();
13752
+ return { baseline, created: true };
13753
+ }
13754
+ function readBaseline(sessionId) {
13755
+ return readManifest(sessionDir(sessionKey(sessionId)));
13756
+ }
13757
+ function readManifest(dir) {
13758
+ const mp = manifestPath(dir);
13759
+ if (!(0, import_node_fs13.existsSync)(mp)) return null;
13760
+ try {
13761
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(mp, "utf-8"));
13762
+ if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
13763
+ return null;
13764
+ }
13765
+ return {
13766
+ session_id: typeof parsed.session_id === "string" ? parsed.session_id : "",
13767
+ head_sha: parsed.head_sha,
13768
+ captured_at: parsed.captured_at,
13769
+ dirty_paths: parsed.dirty_paths.filter((p) => typeof p === "string"),
13770
+ version: parsed.version
13771
+ };
13772
+ } catch {
13773
+ return null;
13774
+ }
13775
+ }
13776
+ var preImageCache = /* @__PURE__ */ new WeakMap();
13777
+ function preImage(repoRelPath, baseline) {
13778
+ let perBaseline = preImageCache.get(baseline);
13779
+ if (!perBaseline) {
13780
+ perBaseline = /* @__PURE__ */ new Map();
13781
+ preImageCache.set(baseline, perBaseline);
13782
+ }
13783
+ const cached = perBaseline.get(repoRelPath);
13784
+ if (cached) return cached;
13785
+ const resolved = resolvePreImage(repoRelPath, baseline);
13786
+ perBaseline.set(repoRelPath, resolved);
13787
+ return resolved;
13788
+ }
13789
+ function resolvePreImage(repoRelPath, baseline) {
13790
+ if (baseline.dirty_paths.includes(repoRelPath)) {
13791
+ const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
13792
+ if ((0, import_node_fs13.existsSync)(mp)) {
13793
+ try {
13794
+ return { content: (0, import_node_fs13.readFileSync)(mp, "utf-8"), existed: true };
13795
+ } catch {
13796
+ }
13797
+ }
13798
+ }
13799
+ const atHead = showContentAtRef(baseline.head_sha, repoRelPath);
13800
+ if (atHead !== null) return { content: atHead, existed: true };
13801
+ return { content: "", existed: false };
13802
+ }
13803
+ function generateBaselineDiffs(files, baseline) {
13804
+ if (!baseline) return { diffs: [], has_baseline: false };
13805
+ const diffs = [];
13806
+ for (const file of files) {
13807
+ const language = file.language ?? detectLanguage(file.path);
13808
+ const pre = preImage(file.path, baseline);
13809
+ if (pre.existed) {
13810
+ if (pre.content === file.content) continue;
13811
+ const diff = computeDiff(pre.content, file.content, file.path);
13812
+ if (diff) diffs.push({ path: file.path, language, diff, status: "modified" });
13813
+ } else {
13814
+ const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
13815
+ diffs.push({
13816
+ path: file.path,
13817
+ language,
13818
+ diff: `--- /dev/null
13819
+ +++ b/${file.path}
13820
+ @@ -0,0 +1,${file.content.split("\n").length} @@
13821
+ ${addedLines}`,
13822
+ status: "added"
13823
+ });
13824
+ }
13825
+ }
13826
+ return { diffs, has_baseline: true };
13827
+ }
13828
+ function changedSinceBaseline(repoRelPath, baseline) {
13829
+ const pre = preImage(repoRelPath, baseline);
13830
+ let current;
13831
+ try {
13832
+ current = (0, import_node_fs13.readFileSync)(projectPath(repoRelPath), "utf-8");
13833
+ } catch {
13834
+ return pre.existed;
13835
+ }
13836
+ if (!pre.existed) return true;
13837
+ return current !== pre.content;
13838
+ }
13839
+ function safeReadForMirror(absPath) {
13840
+ try {
13841
+ if ((0, import_node_fs13.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
13842
+ const buf = (0, import_node_fs13.readFileSync)(absPath);
13843
+ if (buf.includes(0)) return null;
13844
+ return buf.toString("utf-8");
13845
+ } catch {
13846
+ return null;
13847
+ }
13848
+ }
13849
+ function pruneOldBaselines() {
13850
+ const root = projectPath(BASELINE_DIR);
13851
+ let entries;
13852
+ try {
13853
+ entries = (0, import_node_fs13.readdirSync)(root);
13854
+ } catch {
13855
+ return;
13856
+ }
13857
+ const now = Date.now();
13858
+ for (const name of entries) {
13859
+ const dir = (0, import_node_path11.join)(root, name);
13860
+ const manifest = readManifest(dir);
13861
+ if (!manifest) {
13862
+ try {
13863
+ if (now - (0, import_node_fs13.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
13864
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13865
+ }
13866
+ } catch {
13867
+ }
13868
+ continue;
13869
+ }
13870
+ if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
13871
+ try {
13872
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13873
+ } catch {
13874
+ }
13875
+ }
13876
+ }
13877
+
13878
+ // src/lib/offline.ts
13879
+ var import_node_fs14 = require("node:fs");
13880
+ var import_node_crypto5 = require("node:crypto");
13470
13881
  function cacheRequest(body) {
13471
13882
  try {
13472
- (0, import_node_fs13.mkdirSync)(CACHE_DIR, { recursive: true });
13473
- const suffix = (0, import_node_crypto4.randomBytes)(4).toString("hex");
13883
+ (0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
13884
+ const suffix = (0, import_node_crypto5.randomBytes)(4).toString("hex");
13474
13885
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
13475
- (0, import_node_fs13.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
13886
+ (0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
13476
13887
  } catch {
13477
13888
  }
13478
13889
  }
@@ -13486,7 +13897,7 @@ function buildOfflineFallback(reason, staticResults) {
13486
13897
  }
13487
13898
 
13488
13899
  // src/lib/context-files.ts
13489
- var import_node_fs14 = require("node:fs");
13900
+ var import_node_fs15 = require("node:fs");
13490
13901
  var MAX_CONTEXT_FILES = 10;
13491
13902
  var MAX_CONTEXT_FILE_BYTES = 10240;
13492
13903
  var MAX_CONTEXT_TOTAL_BYTES = 51200;
@@ -13498,7 +13909,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
13498
13909
  if (result.length >= MAX_CONTEXT_FILES) break;
13499
13910
  if (deltaPaths.has(filePath)) continue;
13500
13911
  try {
13501
- const content = (0, import_node_fs14.readFileSync)(filePath, "utf8");
13912
+ const content = (0, import_node_fs15.readFileSync)(filePath, "utf8");
13502
13913
  const bytes = Buffer.byteLength(content);
13503
13914
  if (bytes > MAX_CONTEXT_FILE_BYTES) {
13504
13915
  logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
@@ -13543,20 +13954,20 @@ function gatherContextFiles(contextPaths, deltaFiles) {
13543
13954
  }
13544
13955
 
13545
13956
  // src/lib/cache-cleanup.ts
13546
- var import_node_fs15 = require("node:fs");
13547
- var import_node_path11 = require("node:path");
13957
+ var import_node_fs16 = require("node:fs");
13958
+ var import_node_path12 = require("node:path");
13548
13959
  var CACHE_TTL_DAYS = 7;
13549
13960
  function pruneStaleCache() {
13550
13961
  try {
13551
13962
  const dir = projectPath(CACHE_DIR);
13552
13963
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
13553
- for (const entry of (0, import_node_fs15.readdirSync)(dir)) {
13964
+ for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
13554
13965
  if (!entry.startsWith("pending-")) continue;
13555
- const path = (0, import_node_path11.join)(dir, entry);
13966
+ const path = (0, import_node_path12.join)(dir, entry);
13556
13967
  try {
13557
- const stat3 = (0, import_node_fs15.statSync)(path);
13968
+ const stat3 = (0, import_node_fs16.statSync)(path);
13558
13969
  if (stat3.mtimeMs < cutoff) {
13559
- (0, import_node_fs15.unlinkSync)(path);
13970
+ (0, import_node_fs16.unlinkSync)(path);
13560
13971
  logEvent("cache_entry_pruned", {
13561
13972
  path: entry,
13562
13973
  age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
@@ -13628,10 +14039,11 @@ function reconcileAnalysisMode(predictedMode, signals) {
13628
14039
  signals.noFilesChanged,
13629
14040
  signals.assistantResponse,
13630
14041
  signals.conversationPrompts,
13631
- signals.actionSummary
14042
+ signals.actionSummary,
14043
+ signals.sessionAuthoredCode
13632
14044
  );
13633
14045
  }
13634
- const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0));
14046
+ const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
13635
14047
  const agentInvestigated = didAgentInvestigate(signals.actionSummary);
13636
14048
  switch (predictedMode) {
13637
14049
  case "skip":
@@ -13656,8 +14068,8 @@ function didAgentInvestigate(summary) {
13656
14068
  function isValidMode(mode) {
13657
14069
  return mode === "standard" || mode === "plan" || mode === "debug" || mode === "skip";
13658
14070
  }
13659
- function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary) {
13660
- const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
14071
+ function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
14072
+ const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
13661
14073
  if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
13662
14074
  if (!agentAuthoredCode) return "skip";
13663
14075
  }
@@ -13737,7 +14149,7 @@ function isMetaTaskLabel(label2) {
13737
14149
  }
13738
14150
 
13739
14151
  // src/lib/transcript.ts
13740
- var import_node_fs16 = require("node:fs");
14152
+ var import_node_fs17 = require("node:fs");
13741
14153
  var MAX_READ_BYTES = 256 * 1024;
13742
14154
  var SMALL_FILE_BYTES = 64 * 1024;
13743
14155
  var MAX_FILES_LIST = 20;
@@ -13759,14 +14171,14 @@ async function extractActionSummary(transcriptPath) {
13759
14171
  function readTurnLines(transcriptPath) {
13760
14172
  let size;
13761
14173
  try {
13762
- size = (0, import_node_fs16.statSync)(transcriptPath).size;
14174
+ size = (0, import_node_fs17.statSync)(transcriptPath).size;
13763
14175
  } catch {
13764
14176
  return null;
13765
14177
  }
13766
14178
  if (size === 0) return null;
13767
14179
  let raw;
13768
14180
  if (size <= SMALL_FILE_BYTES) {
13769
- raw = (0, import_node_fs16.readFileSync)(transcriptPath, "utf-8");
14181
+ raw = (0, import_node_fs17.readFileSync)(transcriptPath, "utf-8");
13770
14182
  } else {
13771
14183
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
13772
14184
  const fd = require("node:fs").openSync(transcriptPath, "r");
@@ -13854,6 +14266,12 @@ function buildSummary(lines) {
13854
14266
  case "Edit":
13855
14267
  addPath(filesEdited, input.file_path);
13856
14268
  break;
14269
+ case "MultiEdit":
14270
+ addPath(filesEdited, input.file_path);
14271
+ break;
14272
+ case "NotebookEdit":
14273
+ addPath(filesEdited, input.notebook_path ?? input.file_path);
14274
+ break;
13857
14275
  case "Write":
13858
14276
  addPath(filesCreated, input.file_path);
13859
14277
  addPath(filesEdited, input.file_path);
@@ -13937,8 +14355,8 @@ function capArray(set, max) {
13937
14355
 
13938
14356
  // src/lib/seed-runner.ts
13939
14357
  var import_promises11 = require("node:fs/promises");
13940
- var import_node_fs17 = require("node:fs");
13941
- var import_node_path12 = require("node:path");
14358
+ var import_node_fs18 = require("node:fs");
14359
+ var import_node_path13 = require("node:path");
13942
14360
  var import_yaml2 = __toESM(require_dist());
13943
14361
 
13944
14362
  // src/lib/seed.ts
@@ -14177,7 +14595,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
14177
14595
  return fm;
14178
14596
  }
14179
14597
  async function runSeed(opts) {
14180
- if (!(0, import_node_fs17.existsSync)(STANDARD_FILE)) {
14598
+ if (!(0, import_node_fs18.existsSync)(STANDARD_FILE)) {
14181
14599
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
14182
14600
  }
14183
14601
  let standardDoc;
@@ -14189,7 +14607,7 @@ async function runSeed(opts) {
14189
14607
  }
14190
14608
  const knowledgeSpec = standardDoc.knowledge_spec ?? {};
14191
14609
  let readmeContent;
14192
- if ((0, import_node_fs17.existsSync)("README.md")) {
14610
+ if ((0, import_node_fs18.existsSync)("README.md")) {
14193
14611
  try {
14194
14612
  readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
14195
14613
  } catch {
@@ -14197,7 +14615,7 @@ async function runSeed(opts) {
14197
14615
  }
14198
14616
  let claudeMdContent;
14199
14617
  for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
14200
- if ((0, import_node_fs17.existsSync)(p)) {
14618
+ if ((0, import_node_fs18.existsSync)(p)) {
14201
14619
  try {
14202
14620
  claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
14203
14621
  break;
@@ -14220,8 +14638,8 @@ async function runSeed(opts) {
14220
14638
  if (candidates.length === 0) {
14221
14639
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
14222
14640
  }
14223
- const overviewPath = (0, import_node_path12.join)(MEMORY_DIR, "domain", "project-overview.md");
14224
- if ((0, import_node_fs17.existsSync)(overviewPath) && !opts.force) {
14641
+ const overviewPath = (0, import_node_path13.join)(MEMORY_DIR, "domain", "project-overview.md");
14642
+ if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
14225
14643
  return { created: 0, failed: 0, skipped: "already_seeded", candidates };
14226
14644
  }
14227
14645
  if (opts.dryRun) {
@@ -14256,9 +14674,9 @@ async function runSeed(opts) {
14256
14674
  }
14257
14675
  const nodeId = res.data.node_id;
14258
14676
  const filePathRel = res.data.file_path;
14259
- const targetPath = (0, import_node_path12.join)(MEMORY_DIR, filePathRel);
14677
+ const targetPath = (0, import_node_path13.join)(MEMORY_DIR, filePathRel);
14260
14678
  try {
14261
- await (0, import_promises11.mkdir)((0, import_node_path12.dirname)(targetPath), { recursive: true });
14679
+ await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(targetPath), { recursive: true });
14262
14680
  await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
14263
14681
  created++;
14264
14682
  opts.onCreated?.(nodeId, filePathRel, c);
@@ -14328,6 +14746,15 @@ async function runAnalyze(opts, globals) {
14328
14746
  }
14329
14747
  const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
14330
14748
  const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
14749
+ const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
14750
+ const baseline = readBaseline(baselineSessionId);
14751
+ if (baseline) {
14752
+ logEvent("baseline_loaded", {
14753
+ head: baseline.head_sha.slice(0, 12),
14754
+ dirty_count: baseline.dirty_paths.length,
14755
+ age_ms: Date.now() - baseline.captured_at
14756
+ });
14757
+ }
14331
14758
  const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
14332
14759
  const analyzable = filterAnalyzable(allChanged);
14333
14760
  const reviewable = filterReviewable(allChanged);
@@ -14337,7 +14764,7 @@ async function runAnalyze(opts, globals) {
14337
14764
  passAndExit("No analyzable files changed");
14338
14765
  }
14339
14766
  const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
14340
- const conversation = await readAndClearConversationBuffer();
14767
+ const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
14341
14768
  const specs = discoverSpecs();
14342
14769
  const plans = discoverPlans();
14343
14770
  const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
@@ -14387,13 +14814,14 @@ async function runAnalyze(opts, globals) {
14387
14814
  }
14388
14815
  const conversationPrompts = (conversation?.prompts ?? []).map((p) => p.prompt);
14389
14816
  let analysisMode;
14817
+ const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
14390
14818
  const modeOverride = opts.mode;
14391
14819
  if (modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride)) {
14392
14820
  analysisMode = modeOverride;
14393
14821
  } else {
14394
14822
  analysisMode = reconcileAnalysisMode(
14395
14823
  predictedMode,
14396
- { noFilesChanged, assistantResponse, actionSummary, conversationPrompts }
14824
+ { noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
14397
14825
  );
14398
14826
  }
14399
14827
  if (analysisMode === "skip") {
@@ -14415,7 +14843,7 @@ async function runAnalyze(opts, globals) {
14415
14843
  let currentCommit = "";
14416
14844
  if (analysisMode !== "plan") {
14417
14845
  const debounceSeconds = parseInt(opts.debounce, 10);
14418
- const debounceSkip = checkDebounce(debounceSeconds);
14846
+ const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
14419
14847
  if (debounceSkip) {
14420
14848
  if (assistantResponse) {
14421
14849
  analysisMode = "plan";
@@ -14425,7 +14853,7 @@ async function runAnalyze(opts, globals) {
14425
14853
  }
14426
14854
  if (analysisMode !== "plan") {
14427
14855
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
14428
- const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
14856
+ const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
14429
14857
  if (mtimeSkip) {
14430
14858
  if (assistantResponse) {
14431
14859
  analysisMode = "plan";
@@ -14435,9 +14863,9 @@ async function runAnalyze(opts, globals) {
14435
14863
  }
14436
14864
  }
14437
14865
  if (analysisMode !== "plan") {
14438
- recordAnalysisStart();
14866
+ recordAnalysisStart(baselineSessionId);
14439
14867
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
14440
- const hashResult = checkContentHash(allCheckable);
14868
+ const hashResult = checkContentHash(allCheckable, baselineSessionId);
14441
14869
  if (hashResult.skip) {
14442
14870
  if (assistantResponse) {
14443
14871
  analysisMode = "plan";
@@ -14449,12 +14877,18 @@ async function runAnalyze(opts, globals) {
14449
14877
  if (analysisMode !== "plan") {
14450
14878
  const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
14451
14879
  const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
14452
- const recentForReview = narrowToRecent(baseForReview);
14880
+ const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
14453
14881
  if (!opts.skipStatic && isCodacyAvailable()) {
14454
- const allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14455
- staticResults = runCodacyAnalysis(allScannable);
14882
+ let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14883
+ if (baseline) {
14884
+ allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
14885
+ }
14886
+ if (allScannable.length > 0) {
14887
+ staticResults = runCodacyAnalysis(allScannable);
14888
+ }
14456
14889
  }
14457
- codeDelta = collectCodeDelta(recentForReview, {
14890
+ const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
14891
+ codeDelta = collectCodeDelta(deltaSet, {
14458
14892
  maxFiles: parseInt(opts.maxFiles, 10),
14459
14893
  maxFileBytes: parseInt(opts.maxFileSize, 10),
14460
14894
  maxTotalBytes: parseInt(opts.maxTotalSize, 10)
@@ -14469,7 +14903,12 @@ async function runAnalyze(opts, globals) {
14469
14903
  }
14470
14904
  }
14471
14905
  if (analysisMode !== "plan") {
14472
- snapshotResult = generateSnapshotDiffs(codeDelta.files);
14906
+ if (baseline) {
14907
+ const baselineDiffs = generateBaselineDiffs(codeDelta.files, baseline);
14908
+ snapshotResult = { has_snapshots: baselineDiffs.diffs.length > 0, diffs: baselineDiffs.diffs };
14909
+ } else {
14910
+ snapshotResult = generateSnapshotDiffs(codeDelta.files);
14911
+ }
14473
14912
  currentCommit = getCurrentCommit();
14474
14913
  const maxIterations = parseInt(opts.maxIterations, 10);
14475
14914
  const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
@@ -14501,9 +14940,9 @@ async function runAnalyze(opts, globals) {
14501
14940
  let autoSeedNotice = null;
14502
14941
  try {
14503
14942
  await ensureMemoryDir();
14504
- const seedMarker = (0, import_node_path13.join)(VERITY_DIR, ".seeded");
14505
- const hasStandard = (0, import_node_fs18.existsSync)(STANDARD_FILE);
14506
- const alreadyTried = (0, import_node_fs18.existsSync)(seedMarker);
14943
+ const seedMarker = (0, import_node_path14.join)(VERITY_DIR, ".seeded");
14944
+ const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
14945
+ const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
14507
14946
  if (hasStandard && !alreadyTried) {
14508
14947
  const preManifest = await buildManifest();
14509
14948
  if (preManifest.nodes.length === 0) {
@@ -14516,7 +14955,7 @@ async function runAnalyze(opts, globals) {
14516
14955
  dryRun: false
14517
14956
  });
14518
14957
  if (seedResult.created > 0) {
14519
- (0, import_node_fs18.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
14958
+ (0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
14520
14959
  `);
14521
14960
  autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
14522
14961
  logEvent("auto_seed_ran", {
@@ -14524,7 +14963,7 @@ async function runAnalyze(opts, globals) {
14524
14963
  failed: seedResult.failed
14525
14964
  });
14526
14965
  } else if (seedResult.skipped === "already_seeded") {
14527
- (0, import_node_fs18.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
14966
+ (0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
14528
14967
  `);
14529
14968
  } else {
14530
14969
  logEvent("auto_seed_noop", {
@@ -14813,7 +15252,7 @@ async function runAnalyze(opts, globals) {
14813
15252
  }
14814
15253
  case "PASS": {
14815
15254
  writeIteration(1, currentCommit, contentHash ?? void 0);
14816
- if (contentHash) recordPassHash(contentHash);
15255
+ if (contentHash) recordPassHash(contentHash, baselineSessionId);
14817
15256
  if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
14818
15257
  let userSummary = response.user_summary ?? "Verity: PASS";
14819
15258
  const viewUrl = response.view_url ?? "";
@@ -14824,7 +15263,7 @@ async function runAnalyze(opts, globals) {
14824
15263
  break;
14825
15264
  }
14826
15265
  case "WARN": {
14827
- if (contentHash) recordPassHash(contentHash);
15266
+ if (contentHash) recordPassHash(contentHash, baselineSessionId);
14828
15267
  if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
14829
15268
  let userSummary = response.user_summary ?? "Verity: WARN";
14830
15269
  const viewUrl = response.view_url ?? "";
@@ -14842,8 +15281,54 @@ async function runAnalyze(opts, globals) {
14842
15281
  }
14843
15282
  }
14844
15283
 
15284
+ // src/commands/baseline.ts
15285
+ var import_node_fs20 = require("node:fs");
15286
+ function registerBaselineCommands(program2) {
15287
+ const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
15288
+ baseline.command("capture").description("Snapshot the working tree at task start (used by SessionStart hook)").option("--session-id <id>", "Session id (overrides any value from stdin)").option("--source <source>", "Lifecycle hint: startup|resume|clear|compact").action(async (opts) => {
15289
+ try {
15290
+ try {
15291
+ process.chdir(repoRoot());
15292
+ } catch {
15293
+ }
15294
+ if (!(0, import_node_fs20.existsSync)(VERITY_DIR)) {
15295
+ process.exit(0);
15296
+ }
15297
+ let sessionId = opts.sessionId;
15298
+ let source = opts.source;
15299
+ if (!process.stdin.isTTY) {
15300
+ const input = (await readStdin()).trim();
15301
+ if (input) {
15302
+ try {
15303
+ const event = JSON.parse(input);
15304
+ sessionId = sessionId ?? event.session_id;
15305
+ source = source ?? event.source;
15306
+ } catch {
15307
+ }
15308
+ }
15309
+ }
15310
+ const result = captureBaseline({ sessionId, source });
15311
+ logEvent("baseline_capture", {
15312
+ created: result.created,
15313
+ source: source ?? null,
15314
+ dirty_count: result.baseline.dirty_paths.length,
15315
+ head: result.baseline.head_sha.slice(0, 12)
15316
+ });
15317
+ } catch {
15318
+ }
15319
+ process.exit(0);
15320
+ });
15321
+ }
15322
+ async function readStdin() {
15323
+ const chunks = [];
15324
+ for await (const chunk of process.stdin) {
15325
+ chunks.push(chunk);
15326
+ }
15327
+ return Buffer.concat(chunks).toString("utf-8");
15328
+ }
15329
+
14845
15330
  // src/commands/review.ts
14846
- var import_node_fs19 = require("node:fs");
15331
+ var import_node_fs21 = require("node:fs");
14847
15332
  function registerReviewCommand(program2) {
14848
15333
  program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
14849
15334
  const globals = program2.opts();
@@ -14862,7 +15347,7 @@ async function runReview(opts, globals) {
14862
15347
  const securityFiles = filterSecurity(allFiles);
14863
15348
  let staticResults;
14864
15349
  if (isCodacyAvailable()) {
14865
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs19.existsSync)(f) || resolveFile(f) !== null);
15350
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs21.existsSync)(f) || resolveFile(f) !== null);
14866
15351
  staticResults = runCodacyAnalysis(scannable);
14867
15352
  } else {
14868
15353
  staticResults = {
@@ -14887,10 +15372,10 @@ async function runReview(opts, globals) {
14887
15372
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
14888
15373
  specs = [];
14889
15374
  for (const p of specPaths) {
14890
- if (!(0, import_node_fs19.existsSync)(p)) continue;
15375
+ if (!(0, import_node_fs21.existsSync)(p)) continue;
14891
15376
  try {
14892
- const { readFileSync: readFileSync11 } = await import("node:fs");
14893
- const content = readFileSync11(p, "utf-8");
15377
+ const { readFileSync: readFileSync12 } = await import("node:fs");
15378
+ const content = readFileSync12(p, "utf-8");
14894
15379
  specs.push({ path: p, content: content.slice(0, 10240) });
14895
15380
  } catch {
14896
15381
  }
@@ -14947,10 +15432,10 @@ async function runReview(opts, globals) {
14947
15432
  }
14948
15433
 
14949
15434
  // src/commands/guard.ts
14950
- var import_node_fs20 = require("node:fs");
14951
- var import_node_path14 = require("node:path");
15435
+ var import_node_fs22 = require("node:fs");
15436
+ var import_node_path15 = require("node:path");
14952
15437
  var GUARD_BLOCK_CAP = 2;
14953
- var GUARD_ITER_FILE = (0, import_node_path14.join)(VERITY_DIR, ".guard-iteration");
15438
+ var GUARD_ITER_FILE = (0, import_node_path15.join)(VERITY_DIR, ".guard-iteration");
14954
15439
  function readPreToolUseStdin() {
14955
15440
  const empty = { command: "", cwd: null, sessionId: null };
14956
15441
  return new Promise((resolve) => {
@@ -15014,7 +15499,7 @@ function classifyCommand(command, on) {
15014
15499
  }
15015
15500
  function readIterMap() {
15016
15501
  try {
15017
- const raw = JSON.parse((0, import_node_fs20.readFileSync)(GUARD_ITER_FILE, "utf-8"));
15502
+ const raw = JSON.parse((0, import_node_fs22.readFileSync)(GUARD_ITER_FILE, "utf-8"));
15018
15503
  if (raw && typeof raw === "object") {
15019
15504
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
15020
15505
  return { [raw.moment]: raw.count };
@@ -15034,10 +15519,10 @@ function readIter(moment) {
15034
15519
  }
15035
15520
  function writeIter(moment, count) {
15036
15521
  try {
15037
- (0, import_node_fs20.mkdirSync)(VERITY_DIR, { recursive: true });
15522
+ (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
15038
15523
  const map = readIterMap();
15039
15524
  map[moment] = count;
15040
- (0, import_node_fs20.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15525
+ (0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15041
15526
  } catch {
15042
15527
  }
15043
15528
  }
@@ -15047,10 +15532,10 @@ function resetIter(moment) {
15047
15532
  if (!(moment in map)) return;
15048
15533
  delete map[moment];
15049
15534
  if (Object.keys(map).length === 0) {
15050
- if ((0, import_node_fs20.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs20.unlinkSync)(GUARD_ITER_FILE);
15535
+ if ((0, import_node_fs22.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs22.unlinkSync)(GUARD_ITER_FILE);
15051
15536
  } else {
15052
- (0, import_node_fs20.mkdirSync)(VERITY_DIR, { recursive: true });
15053
- (0, import_node_fs20.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15537
+ (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
15538
+ (0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15054
15539
  }
15055
15540
  } catch {
15056
15541
  }
@@ -15114,7 +15599,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
15114
15599
  const securityFiles = filterSecurity(files);
15115
15600
  let staticResults;
15116
15601
  if (isCodacyAvailable()) {
15117
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs20.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
15602
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs22.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
15118
15603
  staticResults = runCodacyAnalysis(scannable);
15119
15604
  } else {
15120
15605
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -15159,7 +15644,7 @@ function emitAllowNotice(userMsg, agentMsg) {
15159
15644
  async function runGuard(opts, globals) {
15160
15645
  const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
15161
15646
  const { command, cwd, sessionId } = await readPreToolUseStdin();
15162
- if (cwd && (0, import_node_fs20.existsSync)(cwd)) {
15647
+ if (cwd && (0, import_node_fs22.existsSync)(cwd)) {
15163
15648
  try {
15164
15649
  process.chdir(cwd);
15165
15650
  } catch {
@@ -15264,14 +15749,14 @@ function writeBlockMessage(moment, response) {
15264
15749
  }
15265
15750
 
15266
15751
  // src/commands/init.ts
15267
- var import_node_fs22 = require("node:fs");
15752
+ var import_node_fs24 = require("node:fs");
15268
15753
  var import_promises12 = require("node:fs/promises");
15269
- var import_node_path16 = require("node:path");
15754
+ var import_node_path17 = require("node:path");
15270
15755
  var import_node_child_process9 = require("node:child_process");
15271
15756
 
15272
15757
  // src/commands/migrate.ts
15273
- var import_node_fs21 = require("node:fs");
15274
- var import_node_path15 = require("node:path");
15758
+ var import_node_fs23 = require("node:fs");
15759
+ var import_node_path16 = require("node:path");
15275
15760
  var import_node_child_process8 = require("node:child_process");
15276
15761
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
15277
15762
  function defaultNpmRemover(pkg) {
@@ -15307,12 +15792,12 @@ async function runMigration(opts = {}) {
15307
15792
  return { actions, migrated: actions.length > 0 };
15308
15793
  }
15309
15794
  function migrateProjectDir(root, actions) {
15310
- const gateDir = (0, import_node_path15.join)(root, ".gate");
15311
- const verityDir = (0, import_node_path15.join)(root, ".verity");
15312
- if ((0, import_node_fs21.existsSync)(gateDir) && !(0, import_node_fs21.existsSync)(verityDir)) {
15795
+ const gateDir = (0, import_node_path16.join)(root, ".gate");
15796
+ const verityDir = (0, import_node_path16.join)(root, ".verity");
15797
+ if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
15313
15798
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
15314
15799
  }
15315
- if ((0, import_node_fs21.existsSync)(gateDir) && (0, import_node_fs21.existsSync)(verityDir)) {
15800
+ if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
15316
15801
  return migrateProjectDirCarry(gateDir, verityDir, actions);
15317
15802
  }
15318
15803
  return false;
@@ -15333,13 +15818,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
15333
15818
  }
15334
15819
  }
15335
15820
  if (moved) {
15336
- if ((0, import_node_fs21.existsSync)(gateDir)) {
15821
+ if ((0, import_node_fs23.existsSync)(gateDir)) {
15337
15822
  const carried = carryLegacyContents(gateDir, verityDir);
15338
15823
  if (carried > 0) {
15339
15824
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
15340
15825
  }
15341
15826
  try {
15342
- (0, import_node_fs21.rmSync)(gateDir, { recursive: true, force: true });
15827
+ (0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
15343
15828
  } catch {
15344
15829
  }
15345
15830
  }
@@ -15355,18 +15840,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
15355
15840
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
15356
15841
  }
15357
15842
  try {
15358
- (0, import_node_fs21.rmSync)(gateDir, { recursive: true, force: true });
15843
+ (0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
15359
15844
  } catch {
15360
15845
  }
15361
15846
  return carried > 0;
15362
15847
  }
15363
15848
  function migrateGlobalCredentials(home, actions) {
15364
15849
  if (!home) return;
15365
- const gateCreds = (0, import_node_path15.join)(home, ".gate", "credentials");
15366
- const verityCreds = (0, import_node_path15.join)(home, ".verity", "credentials");
15367
- if (!(0, import_node_fs21.existsSync)(gateCreds)) return;
15368
- if (!(0, import_node_fs21.existsSync)(verityCreds)) {
15369
- (0, import_node_fs21.mkdirSync)((0, import_node_path15.join)(home, ".verity"), { recursive: true });
15850
+ const gateCreds = (0, import_node_path16.join)(home, ".gate", "credentials");
15851
+ const verityCreds = (0, import_node_path16.join)(home, ".verity", "credentials");
15852
+ if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
15853
+ if (!(0, import_node_fs23.existsSync)(verityCreds)) {
15854
+ (0, import_node_fs23.mkdirSync)((0, import_node_path16.join)(home, ".verity"), { recursive: true });
15370
15855
  moveFile(gateCreds, verityCreds);
15371
15856
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
15372
15857
  return;
@@ -15388,8 +15873,8 @@ async function migrateLegacyHooks(root, actions) {
15388
15873
  }
15389
15874
  }
15390
15875
  async function migrateClaudeMd(root, actions) {
15391
- const claudeMd = (0, import_node_path15.join)(root, "CLAUDE.md");
15392
- const hadLegacyBlock = (0, import_node_fs21.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
15876
+ const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
15877
+ const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
15393
15878
  if (!hadLegacyBlock) return;
15394
15879
  try {
15395
15880
  await ensureClaudeMdPointer(root);
@@ -15399,9 +15884,9 @@ async function migrateClaudeMd(root, actions) {
15399
15884
  }
15400
15885
  }
15401
15886
  function migrateStandardFile(root, actions) {
15402
- const gateMd = (0, import_node_path15.join)(root, "GATE.md");
15403
- const verityMd = (0, import_node_path15.join)(root, "VERITY.md");
15404
- if (!(0, import_node_fs21.existsSync)(gateMd) || (0, import_node_fs21.existsSync)(verityMd)) return;
15887
+ const gateMd = (0, import_node_path16.join)(root, "GATE.md");
15888
+ const verityMd = (0, import_node_path16.join)(root, "VERITY.md");
15889
+ if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
15405
15890
  let moved = false;
15406
15891
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
15407
15892
  try {
@@ -15413,7 +15898,7 @@ function migrateStandardFile(root, actions) {
15413
15898
  if (!moved) moveFile(gateMd, verityMd);
15414
15899
  const content = readFileSyncSafe(verityMd);
15415
15900
  const refreshed = content.split("GATE.md").join("VERITY.md");
15416
- if (refreshed !== content) (0, import_node_fs21.writeFileSync)(verityMd, refreshed);
15901
+ if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
15417
15902
  actions.push("Renamed GATE.md \u2192 VERITY.md");
15418
15903
  }
15419
15904
  function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
@@ -15447,14 +15932,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
15447
15932
  }
15448
15933
  if (toAppend.length > 0) {
15449
15934
  const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
15450
- (0, import_node_fs21.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
15935
+ (0, import_node_fs23.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
15451
15936
  }
15452
- (0, import_node_fs21.rmSync)(gateCreds, { force: true });
15937
+ (0, import_node_fs23.rmSync)(gateCreds, { force: true });
15453
15938
  return toAppend.length;
15454
15939
  }
15455
15940
  function readFileSyncSafe(path) {
15456
15941
  try {
15457
- return (0, import_node_fs21.readFileSync)(path, "utf-8");
15942
+ return (0, import_node_fs23.readFileSync)(path, "utf-8");
15458
15943
  } catch {
15459
15944
  return "";
15460
15945
  }
@@ -15469,35 +15954,35 @@ function hasStagedChanges(root) {
15469
15954
  }
15470
15955
  function moveDir(from, to) {
15471
15956
  try {
15472
- (0, import_node_fs21.renameSync)(from, to);
15957
+ (0, import_node_fs23.renameSync)(from, to);
15473
15958
  } catch (err) {
15474
15959
  if (err.code !== "EXDEV") throw err;
15475
- (0, import_node_fs21.cpSync)(from, to, { recursive: true });
15476
- (0, import_node_fs21.rmSync)(from, { recursive: true, force: true });
15960
+ (0, import_node_fs23.cpSync)(from, to, { recursive: true });
15961
+ (0, import_node_fs23.rmSync)(from, { recursive: true, force: true });
15477
15962
  }
15478
15963
  }
15479
15964
  function moveFile(from, to) {
15480
15965
  try {
15481
- (0, import_node_fs21.renameSync)(from, to);
15966
+ (0, import_node_fs23.renameSync)(from, to);
15482
15967
  } catch (err) {
15483
15968
  if (err.code !== "EXDEV") throw err;
15484
- (0, import_node_fs21.cpSync)(from, to);
15485
- (0, import_node_fs21.rmSync)(from, { force: true });
15969
+ (0, import_node_fs23.cpSync)(from, to);
15970
+ (0, import_node_fs23.rmSync)(from, { force: true });
15486
15971
  }
15487
15972
  }
15488
15973
  function carryLegacyContents(gateDir, verityDir) {
15489
15974
  let copied = 0;
15490
15975
  const walk = (relDir) => {
15491
- const srcDir = (0, import_node_path15.join)(gateDir, relDir);
15492
- for (const entry of (0, import_node_fs21.readdirSync)(srcDir)) {
15493
- const rel = relDir ? (0, import_node_path15.join)(relDir, entry) : entry;
15494
- const src = (0, import_node_path15.join)(gateDir, rel);
15495
- const dest = (0, import_node_path15.join)(verityDir, rel);
15496
- if ((0, import_node_fs21.statSync)(src).isDirectory()) {
15976
+ const srcDir = (0, import_node_path16.join)(gateDir, relDir);
15977
+ for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
15978
+ const rel = relDir ? (0, import_node_path16.join)(relDir, entry) : entry;
15979
+ const src = (0, import_node_path16.join)(gateDir, rel);
15980
+ const dest = (0, import_node_path16.join)(verityDir, rel);
15981
+ if ((0, import_node_fs23.statSync)(src).isDirectory()) {
15497
15982
  walk(rel);
15498
- } else if (!(0, import_node_fs21.existsSync)(dest)) {
15499
- (0, import_node_fs21.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
15500
- (0, import_node_fs21.cpSync)(src, dest);
15983
+ } else if (!(0, import_node_fs23.existsSync)(dest)) {
15984
+ (0, import_node_fs23.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
15985
+ (0, import_node_fs23.cpSync)(src, dest);
15501
15986
  copied++;
15502
15987
  }
15503
15988
  }
@@ -15506,22 +15991,22 @@ function carryLegacyContents(gateDir, verityDir) {
15506
15991
  return copied;
15507
15992
  }
15508
15993
  async function needsMigration(root = repoRoot()) {
15509
- const gateDir = (0, import_node_path15.join)(root, ".gate");
15510
- const verityDir = (0, import_node_path15.join)(root, ".verity");
15511
- if ((0, import_node_fs21.existsSync)(gateDir) && !(0, import_node_fs21.existsSync)(verityDir)) return true;
15512
- if ((0, import_node_fs21.existsSync)(gateDir) && (0, import_node_fs21.existsSync)(verityDir)) {
15513
- if ((0, import_node_fs21.existsSync)((0, import_node_path15.join)(gateDir, "credentials")) && !(0, import_node_fs21.existsSync)((0, import_node_path15.join)(verityDir, "credentials"))) {
15994
+ const gateDir = (0, import_node_path16.join)(root, ".gate");
15995
+ const verityDir = (0, import_node_path16.join)(root, ".verity");
15996
+ if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
15997
+ if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
15998
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "credentials"))) {
15514
15999
  return true;
15515
16000
  }
15516
- if ((0, import_node_fs21.existsSync)((0, import_node_path15.join)(gateDir, "memory")) && !(0, import_node_fs21.existsSync)((0, import_node_path15.join)(verityDir, "memory"))) {
16001
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "memory"))) {
15517
16002
  return true;
15518
16003
  }
15519
16004
  }
15520
- const claudeMd = (0, import_node_path15.join)(root, "CLAUDE.md");
15521
- if ((0, import_node_fs21.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
16005
+ const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
16006
+ if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
15522
16007
  return true;
15523
16008
  }
15524
- if ((0, import_node_fs21.existsSync)((0, import_node_path15.join)(root, "GATE.md")) && !(0, import_node_fs21.existsSync)((0, import_node_path15.join)(root, "VERITY.md"))) {
16009
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "VERITY.md"))) {
15525
16010
  return true;
15526
16011
  }
15527
16012
  if (await hasLegacyHooksAt(root)) return true;
@@ -15549,15 +16034,15 @@ function registerMigrateCommand(program2) {
15549
16034
  // src/commands/init.ts
15550
16035
  function resolveDataDir() {
15551
16036
  const candidates = [
15552
- (0, import_node_path16.join)(__dirname, "..", "data"),
16037
+ (0, import_node_path17.join)(__dirname, "..", "data"),
15553
16038
  // installed: node_modules/@codacy/verity-cli/data
15554
- (0, import_node_path16.join)(__dirname, "..", "..", "data"),
16039
+ (0, import_node_path17.join)(__dirname, "..", "..", "data"),
15555
16040
  // edge case: nested resolution
15556
- (0, import_node_path16.join)(process.cwd(), "cli", "data")
16041
+ (0, import_node_path17.join)(process.cwd(), "cli", "data")
15557
16042
  // local dev: running from repo root
15558
16043
  ];
15559
16044
  for (const candidate of candidates) {
15560
- if ((0, import_node_fs22.existsSync)((0, import_node_path16.join)(candidate, "skills"))) {
16045
+ if ((0, import_node_fs24.existsSync)((0, import_node_path17.join)(candidate, "skills"))) {
15561
16046
  return candidate;
15562
16047
  }
15563
16048
  }
@@ -15573,7 +16058,7 @@ function registerInitCommand(program2) {
15573
16058
  program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
15574
16059
  const force = opts.force ?? false;
15575
16060
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
15576
- const isProject = projectMarkers.some((m) => (0, import_node_fs22.existsSync)(m));
16061
+ const isProject = projectMarkers.some((m) => (0, import_node_fs24.existsSync)(m));
15577
16062
  if (!isProject) {
15578
16063
  printError("No project detected in the current directory.");
15579
16064
  printInfo('Run "verity init" from your project root.');
@@ -15636,21 +16121,21 @@ function registerInitCommand(program2) {
15636
16121
  console.log("");
15637
16122
  printInfo("Installing skills...");
15638
16123
  const dataDir = resolveDataDir();
15639
- const skillsSource = (0, import_node_path16.join)(dataDir, "skills");
16124
+ const skillsSource = (0, import_node_path17.join)(dataDir, "skills");
15640
16125
  const skillsDest = ".claude/skills";
15641
16126
  const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
15642
16127
  let skillsInstalled = 0;
15643
16128
  for (const skill of skills) {
15644
- const src = (0, import_node_path16.join)(skillsSource, skill);
15645
- const dest = (0, import_node_path16.join)(skillsDest, skill);
15646
- if (!(0, import_node_fs22.existsSync)(src)) {
16129
+ const src = (0, import_node_path17.join)(skillsSource, skill);
16130
+ const dest = (0, import_node_path17.join)(skillsDest, skill);
16131
+ if (!(0, import_node_fs24.existsSync)(src)) {
15647
16132
  printWarn(` Skill data not found: ${skill}`);
15648
16133
  continue;
15649
16134
  }
15650
- if ((0, import_node_fs22.existsSync)(dest) && !force) {
15651
- const srcSkill = (0, import_node_path16.join)(src, "SKILL.md");
15652
- const destSkill = (0, import_node_path16.join)(dest, "SKILL.md");
15653
- if ((0, import_node_fs22.existsSync)(destSkill)) {
16135
+ if ((0, import_node_fs24.existsSync)(dest) && !force) {
16136
+ const srcSkill = (0, import_node_path17.join)(src, "SKILL.md");
16137
+ const destSkill = (0, import_node_path17.join)(dest, "SKILL.md");
16138
+ if ((0, import_node_fs24.existsSync)(destSkill)) {
15654
16139
  try {
15655
16140
  const srcContent = await (0, import_promises12.readFile)(srcSkill, "utf-8");
15656
16141
  const destContent = await (0, import_promises12.readFile)(destSkill, "utf-8");
@@ -15674,6 +16159,7 @@ function registerInitCommand(program2) {
15674
16159
  await writeSettings(hookResult.data);
15675
16160
  printInfo(" Stop hook: verity analyze \u2713");
15676
16161
  printInfo(" Intent hook: verity intent capture \u2713");
16162
+ printInfo(" Baseline hook: verity baseline capture \u2713");
15677
16163
  } else {
15678
16164
  printWarn(` ${hookResult.error}`);
15679
16165
  printInfo(' Run "verity hooks install --force" to overwrite.');
@@ -15686,7 +16172,7 @@ function registerInitCommand(program2) {
15686
16172
  } catch (err) {
15687
16173
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
15688
16174
  }
15689
- const globalVerityDir = (0, import_node_path16.join)(process.env.HOME ?? "", ".verity");
16175
+ const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
15690
16176
  await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
15691
16177
  console.log("");
15692
16178
  printInfo("Verity initialized!");
@@ -15709,8 +16195,8 @@ function registerInitCommand(program2) {
15709
16195
  }
15710
16196
 
15711
16197
  // src/commands/uninstall.ts
15712
- var import_node_fs23 = require("node:fs");
15713
- var import_node_path17 = require("node:path");
16198
+ var import_node_fs25 = require("node:fs");
16199
+ var import_node_path18 = require("node:path");
15714
16200
  var SKILL_NAMES = [
15715
16201
  "verity-setup",
15716
16202
  "verity-analyze",
@@ -15729,11 +16215,11 @@ function registerUninstallCommand(program2) {
15729
16215
  const actions = [];
15730
16216
  const skillsRoot = projectPath(".claude/skills");
15731
16217
  for (const name of SKILL_NAMES) {
15732
- const dir = (0, import_node_path17.join)(skillsRoot, name);
15733
- if ((0, import_node_fs23.existsSync)(dir)) {
16218
+ const dir = (0, import_node_path18.join)(skillsRoot, name);
16219
+ if ((0, import_node_fs25.existsSync)(dir)) {
15734
16220
  actions.push({
15735
16221
  label: `Remove .claude/skills/${name}/`,
15736
- apply: () => (0, import_node_fs23.rmSync)(dir, { recursive: true, force: true })
16222
+ apply: () => (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true })
15737
16223
  });
15738
16224
  }
15739
16225
  }
@@ -15747,24 +16233,24 @@ function registerUninstallCommand(program2) {
15747
16233
  });
15748
16234
  }
15749
16235
  const verityDir = projectPath(VERITY_DIR);
15750
- if ((0, import_node_fs23.existsSync)(verityDir)) {
16236
+ if ((0, import_node_fs25.existsSync)(verityDir)) {
15751
16237
  actions.push({
15752
16238
  label: `Remove ${VERITY_DIR}/`,
15753
- apply: () => (0, import_node_fs23.rmSync)(verityDir, { recursive: true, force: true })
16239
+ apply: () => (0, import_node_fs25.rmSync)(verityDir, { recursive: true, force: true })
15754
16240
  });
15755
16241
  }
15756
16242
  if (!keepVerityMd) {
15757
16243
  const verityMd = projectPath(VERITY_MD_FILE);
15758
- if ((0, import_node_fs23.existsSync)(verityMd)) {
16244
+ if ((0, import_node_fs25.existsSync)(verityMd)) {
15759
16245
  actions.push({
15760
16246
  label: `Remove ${VERITY_MD_FILE}`,
15761
- apply: () => (0, import_node_fs23.rmSync)(verityMd, { force: true })
16247
+ apply: () => (0, import_node_fs25.rmSync)(verityMd, { force: true })
15762
16248
  });
15763
16249
  }
15764
16250
  }
15765
16251
  const cleanupEmptyDir = (path) => {
15766
- if ((0, import_node_fs23.existsSync)(path) && (0, import_node_fs23.statSync)(path).isDirectory() && (0, import_node_fs23.readdirSync)(path).length === 0) {
15767
- (0, import_node_fs23.rmdirSync)(path);
16252
+ if ((0, import_node_fs25.existsSync)(path) && (0, import_node_fs25.statSync)(path).isDirectory() && (0, import_node_fs25.readdirSync)(path).length === 0) {
16253
+ (0, import_node_fs25.rmdirSync)(path);
15768
16254
  }
15769
16255
  };
15770
16256
  actions.push({
@@ -15775,11 +16261,11 @@ function registerUninstallCommand(program2) {
15775
16261
  }
15776
16262
  });
15777
16263
  const home = process.env.HOME ?? "";
15778
- const globalVerityDir = (0, import_node_path17.join)(home, ".verity");
15779
- if (purgeGlobal && (0, import_node_fs23.existsSync)(globalVerityDir)) {
16264
+ const globalVerityDir = (0, import_node_path18.join)(home, ".verity");
16265
+ if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
15780
16266
  actions.push({
15781
16267
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
15782
- apply: () => (0, import_node_fs23.rmSync)(globalVerityDir, { recursive: true, force: true })
16268
+ apply: () => (0, import_node_fs25.rmSync)(globalVerityDir, { recursive: true, force: true })
15783
16269
  });
15784
16270
  }
15785
16271
  if (actions.length === 0) {
@@ -15973,8 +16459,8 @@ function registerTaskCommands(program2) {
15973
16459
  }
15974
16460
 
15975
16461
  // src/commands/reset.ts
15976
- var import_node_fs24 = require("node:fs");
15977
- var import_node_path18 = require("node:path");
16462
+ var import_node_fs26 = require("node:fs");
16463
+ var import_node_path19 = require("node:path");
15978
16464
  function registerResetCommand(program2) {
15979
16465
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
15980
16466
  const globals = program2.opts();
@@ -16011,11 +16497,11 @@ function registerResetCommand(program2) {
16011
16497
  }
16012
16498
  const cacheDir = projectPath(CACHE_DIR);
16013
16499
  let purged = 0;
16014
- if ((0, import_node_fs24.existsSync)(cacheDir)) {
16015
- for (const entry of (0, import_node_fs24.readdirSync)(cacheDir)) {
16500
+ if ((0, import_node_fs26.existsSync)(cacheDir)) {
16501
+ for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
16016
16502
  if (entry.startsWith("pending-")) {
16017
16503
  try {
16018
- (0, import_node_fs24.unlinkSync)((0, import_node_path18.join)(cacheDir, entry));
16504
+ (0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(cacheDir, entry));
16019
16505
  purged++;
16020
16506
  } catch {
16021
16507
  }
@@ -16030,19 +16516,19 @@ function registerResetCommand(program2) {
16030
16516
  projectPath(`${VERITY_DIR}/.last-analysis`)
16031
16517
  ];
16032
16518
  for (const file of filesToClear) {
16033
- if ((0, import_node_fs24.existsSync)(file)) {
16519
+ if ((0, import_node_fs26.existsSync)(file)) {
16034
16520
  try {
16035
- (0, import_node_fs24.writeFileSync)(file, "");
16521
+ (0, import_node_fs26.writeFileSync)(file, "");
16036
16522
  } catch {
16037
16523
  }
16038
16524
  }
16039
16525
  }
16040
16526
  if (opts.all) {
16041
16527
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
16042
- if ((0, import_node_fs24.existsSync)(logsDir)) {
16043
- for (const entry of (0, import_node_fs24.readdirSync)(logsDir)) {
16528
+ if ((0, import_node_fs26.existsSync)(logsDir)) {
16529
+ for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
16044
16530
  try {
16045
- (0, import_node_fs24.unlinkSync)((0, import_node_path18.join)(logsDir, entry));
16531
+ (0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(logsDir, entry));
16046
16532
  } catch {
16047
16533
  }
16048
16534
  }
@@ -16301,7 +16787,7 @@ function registerRunCommand(program2) {
16301
16787
 
16302
16788
  // src/lib/telemetry.ts
16303
16789
  var import_promises13 = require("node:fs/promises");
16304
- var import_node_path19 = require("node:path");
16790
+ var import_node_path20 = require("node:path");
16305
16791
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
16306
16792
  var GITIGNORE_FILE = ".gitignore";
16307
16793
  var GITIGNORE_ENTRY = ".claude/settings.local.json";
@@ -16339,7 +16825,7 @@ async function readSettingsLocal() {
16339
16825
  }
16340
16826
  async function writeSettingsLocal(settings) {
16341
16827
  const file = projectPath(SETTINGS_LOCAL_FILE2);
16342
- await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(file), { recursive: true });
16828
+ await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
16343
16829
  await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
16344
16830
  }
16345
16831
  async function ensureGitignore() {
@@ -16435,7 +16921,7 @@ function registerTelemetryCommands(program2) {
16435
16921
  }
16436
16922
 
16437
16923
  // src/cli.ts
16438
- program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.b2914b9").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16924
+ program.name("verity").description("CLI for Verity quality gate service").version("0.24.0-experimental.b35bbc7").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16439
16925
  registerAuthCommands(program);
16440
16926
  registerHooksCommands(program);
16441
16927
  registerIntentCommands(program);
@@ -16444,6 +16930,7 @@ registerConfigCommands(program);
16444
16930
  registerStatusCommand(program);
16445
16931
  registerFeedbackCommand(program);
16446
16932
  registerAnalyzeCommand(program);
16933
+ registerBaselineCommands(program);
16447
16934
  registerReviewCommand(program);
16448
16935
  registerGuardCommand(program);
16449
16936
  registerInitCommand(program);