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

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;
@@ -10473,7 +10474,12 @@ var SECURITY_PATTERNS = [
10473
10474
  /Cargo\.lock$/,
10474
10475
  /Dockerfile/
10475
10476
  ];
10476
- var DEFAULT_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10477
+ var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10478
+ var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
10479
+ var GITHUB_CLIENT_ID = "Ov23liBpj42KMTtUtN10";
10480
+ var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10481
+ var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
10482
+ var GITHUB_OAUTH_SCOPES = "repo user:email";
10477
10483
 
10478
10484
  // src/lib/auth.ts
10479
10485
  async function resolveToken(flagToken) {
@@ -10639,7 +10645,8 @@ async function apiRequest(options) {
10639
10645
  timeout = 9e4,
10640
10646
  cmd = "unknown",
10641
10647
  retry = false,
10642
- encodeBody = false
10648
+ encodeBody = false,
10649
+ extraHeaders
10643
10650
  } = options;
10644
10651
  const url = `${serviceUrl}${path}`;
10645
10652
  const headers = {
@@ -10648,6 +10655,9 @@ async function apiRequest(options) {
10648
10655
  if (token) {
10649
10656
  headers["Authorization"] = `Bearer ${token}`;
10650
10657
  }
10658
+ if (extraHeaders) {
10659
+ Object.assign(headers, extraHeaders);
10660
+ }
10651
10661
  const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
10652
10662
  if (testMockScenario) {
10653
10663
  headers["X-Verity-Mock-Scenario"] = testMockScenario;
@@ -10738,6 +10748,324 @@ function analyzeRequest(options) {
10738
10748
  });
10739
10749
  }
10740
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
+
10741
11069
  // src/commands/auth.ts
10742
11070
  function registerAuthCommands(program2) {
10743
11071
  const auth = program2.command("auth").description("Manage project authentication");
@@ -10747,35 +11075,65 @@ function registerAuthCommands(program2) {
10747
11075
  let remote = opts.remote;
10748
11076
  if (!remote) {
10749
11077
  try {
10750
- 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();
10751
11079
  } catch {
10752
11080
  printError("No git remote found. Use --remote to specify one.");
10753
11081
  process.exit(1);
10754
11082
  }
10755
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;
10756
11099
  const result = await apiRequest({
10757
11100
  method: "POST",
10758
11101
  path: "/auth/register",
10759
11102
  serviceUrl,
10760
11103
  body: { project_name: opts.project, git_remote_url: remote },
11104
+ extraHeaders: { "X-Provider-Token": providerToken },
10761
11105
  verbose: globals.verbose
10762
11106
  });
10763
11107
  if (!result.ok) {
10764
11108
  printError(result.error);
10765
11109
  process.exit(1);
10766
11110
  }
10767
- const { project_id, token, service_url } = result.data;
11111
+ const { project_id, token, service_url, user } = result.data;
10768
11112
  await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
10769
- await (0, import_promises3.writeFile)(CREDENTIALS_FILE, `token: ${token}
11113
+ await (0, import_promises3.writeFile)(
11114
+ CREDENTIALS_FILE,
11115
+ `token: ${token}
10770
11116
  service_url: ${service_url}
10771
- `);
11117
+ provider_token: ${providerToken}
11118
+ `,
11119
+ { mode: 384 }
11120
+ );
11121
+ await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11122
+ });
10772
11123
  try {
10773
- await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
10774
- await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
10775
- `);
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
+ });
10776
11133
  } catch {
10777
11134
  }
10778
11135
  printInfo(`Project registered: ${project_id}`);
11136
+ if (user?.email) printInfo(`Authenticated as: ${user.email}`);
10779
11137
  printJson({ project_id, service_url });
10780
11138
  });
10781
11139
  auth.command("verify").description("Verify the current token is valid").action(async () => {
@@ -10809,7 +11167,7 @@ service_url: ${service_url}
10809
11167
  let remote = opts.remote;
10810
11168
  if (!remote) {
10811
11169
  try {
10812
- 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();
10813
11171
  } catch {
10814
11172
  printError("No git remote found. Use --remote to specify one.");
10815
11173
  process.exit(1);
@@ -10837,7 +11195,7 @@ service_url: ${service_url}
10837
11195
 
10838
11196
  // src/lib/hooks.ts
10839
11197
  var import_promises4 = require("node:fs/promises");
10840
- var import_node_path3 = require("node:path");
11198
+ var import_node_path4 = require("node:path");
10841
11199
  var VERITY_STOP_HOOK = {
10842
11200
  type: "command",
10843
11201
  command: "verity analyze",
@@ -10848,6 +11206,10 @@ var VERITY_INTENT_HOOK = {
10848
11206
  type: "command",
10849
11207
  command: "verity intent capture"
10850
11208
  };
11209
+ var VERITY_BASELINE_HOOK = {
11210
+ type: "command",
11211
+ command: "verity baseline capture"
11212
+ };
10851
11213
  var GUARD_TIMEOUT = 300;
10852
11214
  function buildGuardHook(on) {
10853
11215
  return {
@@ -10860,9 +11222,13 @@ function buildGuardHook(on) {
10860
11222
  var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
10861
11223
  var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
10862
11224
  var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
11225
+ var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
10863
11226
  function isVerityGuardHook(entry) {
10864
11227
  return VERITY_GUARD_RE.test(entry.command ?? "");
10865
11228
  }
11229
+ function isVerityBaselineHook(entry) {
11230
+ return VERITY_BASELINE_RE.test(entry.command ?? "");
11231
+ }
10866
11232
  var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
10867
11233
  var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
10868
11234
  function isVerityStopHook(entry) {
@@ -10874,7 +11240,7 @@ function isVerityIntentHook(entry) {
10874
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");
10875
11241
  }
10876
11242
  function isVerityHook(entry) {
10877
- return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry);
11243
+ return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
10878
11244
  }
10879
11245
  function isCurrentVerityStopHook(entry) {
10880
11246
  const c = entry.command ?? "";
@@ -10885,7 +11251,7 @@ function isCurrentVerityIntentHook(entry) {
10885
11251
  return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
10886
11252
  }
10887
11253
  function isCurrentVerityHook(entry) {
10888
- return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry);
11254
+ return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
10889
11255
  }
10890
11256
  function settingsHasLegacyHook(settings) {
10891
11257
  for (const groups of Object.values(settings.hooks ?? {})) {
@@ -10927,16 +11293,18 @@ async function readAllSettings() {
10927
11293
  async function checkAllVerityHooks() {
10928
11294
  let stop = false;
10929
11295
  let intent = false;
11296
+ let baseline = false;
10930
11297
  let guard = false;
10931
11298
  let guardOn = [];
10932
11299
  for (const settings of await readAllSettings()) {
10933
11300
  const r = checkVerityHooks(settings);
10934
11301
  stop = stop || r.stop;
10935
11302
  intent = intent || r.intent;
11303
+ baseline = baseline || r.baseline;
10936
11304
  guard = guard || r.guard;
10937
11305
  if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
10938
11306
  }
10939
- return { stop, intent, guard, guardOn };
11307
+ return { stop, intent, baseline, guard, guardOn };
10940
11308
  }
10941
11309
  async function checkExternalVerityHooks() {
10942
11310
  let stop = false;
@@ -10959,12 +11327,14 @@ async function checkExternalVerityHooks() {
10959
11327
  async function checkAllVerityHooksDetailed() {
10960
11328
  let stop = false;
10961
11329
  let intent = false;
11330
+ let baseline = false;
10962
11331
  let hasCurrent = false;
10963
11332
  let hasLegacy = false;
10964
11333
  for (const settings of await readAllSettings()) {
10965
11334
  const r = checkVerityHooks(settings);
10966
11335
  stop = stop || r.stop;
10967
11336
  intent = intent || r.intent;
11337
+ baseline = baseline || r.baseline;
10968
11338
  for (const groups of Object.values(settings.hooks ?? {})) {
10969
11339
  for (const g of groups ?? []) {
10970
11340
  for (const h of g.hooks ?? []) {
@@ -10974,22 +11344,22 @@ async function checkAllVerityHooksDetailed() {
10974
11344
  }
10975
11345
  }
10976
11346
  }
10977
- return { stop, intent, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
11347
+ return { stop, intent, baseline, current: hasCurrent, legacyOnly: hasLegacy && !hasCurrent };
10978
11348
  }
10979
11349
  async function writeSettings(settings) {
10980
- 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 });
10981
11351
  await (0, import_promises4.writeFile)(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
10982
11352
  }
10983
11353
  async function readSettingsAt(root) {
10984
11354
  try {
10985
- 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"));
10986
11356
  } catch {
10987
11357
  return {};
10988
11358
  }
10989
11359
  }
10990
11360
  async function writeSettingsAt(root, settings) {
10991
- const file = (0, import_node_path3.join)(root, CLAUDE_SETTINGS_FILE);
10992
- 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 });
10993
11363
  await (0, import_promises4.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
10994
11364
  }
10995
11365
  async function hasLegacyHooksAt(root) {
@@ -11017,6 +11387,10 @@ function checkVerityHooks(settings) {
11017
11387
  const hasIntent = intentGroups.some(
11018
11388
  (g) => g.hooks?.some((h) => isCurrentVerityIntentHook(h))
11019
11389
  );
11390
+ const sessionStartGroups = hooks["SessionStart"] ?? [];
11391
+ const hasBaseline = sessionStartGroups.some(
11392
+ (g) => g.hooks?.some((h) => isVerityBaselineHook(h))
11393
+ );
11020
11394
  let guard = false;
11021
11395
  let guardOn = [];
11022
11396
  for (const g of hooks["PreToolUse"] ?? []) {
@@ -11029,16 +11403,17 @@ function checkVerityHooks(settings) {
11029
11403
  }
11030
11404
  }
11031
11405
  }
11032
- return { stop: hasStop, intent: hasIntent, guard, guardOn };
11406
+ return { stop: hasStop, intent: hasIntent, baseline: hasBaseline, guard, guardOn };
11033
11407
  }
11034
- function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false }) {
11408
+ function installVerityHooks(settings, force, externalPresent = { stop: false, intent: false, baseline: false }) {
11035
11409
  const local = checkVerityHooks(settings);
11036
11410
  const existing = {
11037
11411
  stop: local.stop || externalPresent.stop,
11038
- intent: local.intent || externalPresent.intent
11412
+ intent: local.intent || externalPresent.intent,
11413
+ baseline: local.baseline || (externalPresent.baseline ?? false)
11039
11414
  };
11040
11415
  const hasLocalLegacy = settingsHasLegacyHook(settings);
11041
- if (existing.stop && existing.intent && !force && !hasLocalLegacy) {
11416
+ if (existing.stop && existing.intent && existing.baseline && !force && !hasLocalLegacy) {
11042
11417
  return { ok: false, error: "Verity hooks already installed. Use --force to overwrite." };
11043
11418
  }
11044
11419
  const newSettings = { ...settings };
@@ -11046,7 +11421,7 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
11046
11421
  newSettings.hooks = {};
11047
11422
  }
11048
11423
  const shouldStrip = force ? isVerityHook : isLegacyHook;
11049
- for (const key of ["Stop", "UserPromptSubmit"]) {
11424
+ for (const key of ["Stop", "UserPromptSubmit", "SessionStart"]) {
11050
11425
  const groups = newSettings.hooks[key];
11051
11426
  if (groups) {
11052
11427
  newSettings.hooks[key] = groups.map((g) => ({
@@ -11073,6 +11448,15 @@ function installVerityHooks(settings, force, externalPresent = { stop: false, in
11073
11448
  if (!newSettings.hooks["UserPromptSubmit"]) newSettings.hooks["UserPromptSubmit"] = [];
11074
11449
  newSettings.hooks["UserPromptSubmit"].push({ hooks: [VERITY_INTENT_HOOK] });
11075
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
+ }
11076
11460
  return { ok: true, data: newSettings };
11077
11461
  }
11078
11462
  function removeVerityHooks(settings) {
@@ -11106,6 +11490,7 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
11106
11490
  (s.hooks[event] ??= []).push(group);
11107
11491
  };
11108
11492
  if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
11493
+ push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
11109
11494
  if (moments.includes("stop") && !externalPresent.stop) {
11110
11495
  push("Stop", { hooks: [VERITY_STOP_HOOK] });
11111
11496
  }
@@ -11154,7 +11539,7 @@ function registerHooksCommands(program2) {
11154
11539
  return;
11155
11540
  }
11156
11541
  const detail = await checkAllVerityHooksDetailed();
11157
- const present = { stop: detail.stop, intent: detail.intent };
11542
+ const present = { stop: detail.stop, intent: detail.intent, baseline: detail.baseline };
11158
11543
  if (detail.legacyOnly) {
11159
11544
  printInfo("Found a legacy GATE.md hook \u2014 upgrading it to Verity...");
11160
11545
  const settings2 = removeVerityHooks(await readSettings());
@@ -11167,11 +11552,12 @@ function registerHooksCommands(program2) {
11167
11552
  printInfo("Verity hooks installed in .claude/settings.json (legacy hook removed)");
11168
11553
  printInfo(" Stop hook: verity analyze");
11169
11554
  printInfo(" UserPromptSubmit hook: verity intent capture");
11555
+ printInfo(" SessionStart hook: verity baseline capture");
11170
11556
  return;
11171
11557
  }
11172
11558
  const settings = await readSettings();
11173
11559
  const hasLocalLegacy = settingsHasLegacyHook(settings);
11174
- if (!force && present.stop && present.intent && !hasLocalLegacy) {
11560
+ if (!force && present.stop && present.intent && present.baseline && !hasLocalLegacy) {
11175
11561
  printInfo("Verity hooks already installed (found in .claude/settings.json, settings.local.json, or your global settings).");
11176
11562
  printInfo("Use --force to rewrite the project settings.json copy.");
11177
11563
  return;
@@ -11185,11 +11571,13 @@ function registerHooksCommands(program2) {
11185
11571
  printInfo("Verity hooks installed in .claude/settings.json");
11186
11572
  printInfo(" Stop hook: verity analyze");
11187
11573
  printInfo(" UserPromptSubmit hook: verity intent capture");
11574
+ printInfo(" SessionStart hook: verity baseline capture");
11188
11575
  });
11189
11576
  hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
11190
11577
  const status = await checkAllVerityHooks();
11191
11578
  printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
11192
11579
  printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
11580
+ printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
11193
11581
  const gates = status.guard ? status.guardOn.join(", ") : "none";
11194
11582
  printInfo(`Git-moment gate (verity guard): ${status.guard ? `installed [${gates}]` : "not installed"}`);
11195
11583
  if (!status.stop && !status.guard) {
@@ -11208,15 +11596,19 @@ function registerHooksCommands(program2) {
11208
11596
  }
11209
11597
 
11210
11598
  // src/commands/intent.ts
11211
- var import_node_crypto2 = require("node:crypto");
11599
+ var import_node_crypto3 = require("node:crypto");
11212
11600
 
11213
11601
  // src/lib/conversation-buffer.ts
11214
11602
  var import_promises5 = require("node:fs/promises");
11215
- var import_node_fs2 = require("node:fs");
11216
- 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");
11605
+ var import_node_crypto = require("node:crypto");
11217
11606
  function stripImageReferences(text) {
11218
11607
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
11219
11608
  }
11609
+ function bufferTmpPath() {
11610
+ return `${CONVERSATION_BUFFER_FILE}.${process.pid}.${(0, import_node_crypto.randomBytes)(4).toString("hex")}.tmp`;
11611
+ }
11220
11612
  async function appendToConversationBuffer(prompt, sessionId) {
11221
11613
  try {
11222
11614
  await (0, import_promises5.mkdir)(VERITY_DIR, { recursive: true });
@@ -11236,26 +11628,39 @@ async function appendToConversationBuffer(prompt, sessionId) {
11236
11628
  recent.push(entry);
11237
11629
  const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
11238
11630
  const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
11239
- const tmpFile = `${CONVERSATION_BUFFER_FILE}.tmp`;
11631
+ const tmpFile = bufferTmpPath();
11240
11632
  await (0, import_promises5.writeFile)(tmpFile, content);
11241
11633
  await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
11242
11634
  } catch {
11243
11635
  }
11244
11636
  }
11245
- async function readAndClearConversationBuffer() {
11637
+ async function readAndClearConversationBuffer(currentSessionId) {
11246
11638
  try {
11247
- if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
11639
+ if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
11248
11640
  const entries = await readBufferEntries();
11249
- await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
11250
- });
11251
- if (entries.length > 0) {
11641
+ let mine = entries;
11642
+ let others = [];
11643
+ if (currentSessionId) {
11644
+ mine = entries.filter((e) => e.session_id === currentSessionId || !e.session_id);
11645
+ others = entries.filter((e) => e.session_id && e.session_id !== currentSessionId);
11646
+ }
11647
+ if (others.length > 0) {
11648
+ const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
11649
+ const tmpFile = bufferTmpPath();
11650
+ await (0, import_promises5.writeFile)(tmpFile, remaining);
11651
+ await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
11652
+ } else {
11653
+ await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
11654
+ });
11655
+ }
11656
+ if (mine.length > 0) {
11252
11657
  return {
11253
- prompts: entries,
11658
+ prompts: mine,
11254
11659
  recent_commits: getRecentCommitMessages()
11255
11660
  };
11256
11661
  }
11257
11662
  }
11258
- if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
11663
+ if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
11259
11664
  try {
11260
11665
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
11261
11666
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -11299,7 +11704,7 @@ async function readBufferEntries() {
11299
11704
  }
11300
11705
  function getRecentCommitMessages() {
11301
11706
  try {
11302
- const output = (0, import_node_child_process4.execSync)(
11707
+ const output = (0, import_node_child_process5.execSync)(
11303
11708
  'git log --since="30 minutes ago" --format="%s" -5',
11304
11709
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11305
11710
  ).trim();
@@ -11312,8 +11717,8 @@ function getRecentCommitMessages() {
11312
11717
 
11313
11718
  // src/lib/task-context-buffer.ts
11314
11719
  var import_promises6 = require("node:fs/promises");
11315
- var import_node_fs3 = require("node:fs");
11316
- var import_node_path4 = require("node:path");
11720
+ var import_node_fs4 = require("node:fs");
11721
+ var import_node_path5 = require("node:path");
11317
11722
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11318
11723
  var MAX_BUFFER_BYTES = 500 * 1024;
11319
11724
  var MAX_PROMPT_CHARS = 2e3;
@@ -11352,7 +11757,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
11352
11757
  }
11353
11758
  async function readTaskContextBuffer(taskId) {
11354
11759
  const filePath = bufferPath(taskId);
11355
- if (!(0, import_node_fs3.existsSync)(filePath)) return null;
11760
+ if (!(0, import_node_fs4.existsSync)(filePath)) return null;
11356
11761
  try {
11357
11762
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
11358
11763
  if (!content.trim()) return null;
@@ -11386,12 +11791,12 @@ async function readTaskContextBuffer(taskId) {
11386
11791
  }
11387
11792
  async function cleanupTaskContextBuffers() {
11388
11793
  try {
11389
- if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
11794
+ if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
11390
11795
  const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
11391
11796
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11392
11797
  for (const file of files) {
11393
11798
  if (!file.endsWith(".jsonl")) continue;
11394
- const filePath = (0, import_node_path4.join)(TASK_CONTEXT_DIR, file);
11799
+ const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
11395
11800
  try {
11396
11801
  const stats = await (0, import_promises6.stat)(filePath);
11397
11802
  if (stats.mtimeMs < cutoffMs) {
@@ -11405,13 +11810,13 @@ async function cleanupTaskContextBuffers() {
11405
11810
  }
11406
11811
  function bufferPath(taskId) {
11407
11812
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11408
- return (0, import_node_path4.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11813
+ return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11409
11814
  }
11410
11815
  async function appendEntry(taskId, entry) {
11411
11816
  try {
11412
11817
  await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
11413
11818
  const filePath = bufferPath(taskId);
11414
- if ((0, import_node_fs3.existsSync)(filePath)) {
11819
+ if ((0, import_node_fs4.existsSync)(filePath)) {
11415
11820
  const stats = await (0, import_promises6.stat)(filePath);
11416
11821
  if (stats.size >= MAX_BUFFER_BYTES) {
11417
11822
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
@@ -11422,7 +11827,7 @@ async function appendEntry(taskId, entry) {
11422
11827
  }
11423
11828
  }
11424
11829
  const line = JSON.stringify(entry) + "\n";
11425
- const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11830
+ const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
11426
11831
  await (0, import_promises6.writeFile)(filePath, existing + line);
11427
11832
  } catch {
11428
11833
  }
@@ -11430,8 +11835,8 @@ async function appendEntry(taskId, entry) {
11430
11835
 
11431
11836
  // src/lib/memory-retrieval.ts
11432
11837
  var import_promises7 = require("node:fs/promises");
11433
- var import_node_fs4 = require("node:fs");
11434
- var import_node_path5 = require("node:path");
11838
+ var import_node_fs5 = require("node:fs");
11839
+ var import_node_path6 = require("node:path");
11435
11840
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11436
11841
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11437
11842
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -11524,19 +11929,19 @@ function parseFrontmatter(content) {
11524
11929
  return { fm, body: match[2].trim() };
11525
11930
  }
11526
11931
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
11527
- if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
11932
+ if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
11528
11933
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
11529
11934
  const promptTokens = tokenize(promptText);
11530
11935
  const nodes = [];
11531
11936
  for (const domain of DOMAINS) {
11532
- const domainDir = (0, import_node_path5.join)(memoryDir(), domain);
11533
- if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
11937
+ const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
11938
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
11534
11939
  try {
11535
11940
  const files = await (0, import_promises7.readdir)(domainDir);
11536
11941
  for (const file of files) {
11537
11942
  if (!file.endsWith(".md")) continue;
11538
11943
  try {
11539
- const content = await (0, import_promises7.readFile)((0, import_node_path5.join)(domainDir, file), "utf-8");
11944
+ const content = await (0, import_promises7.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
11540
11945
  const { fm, body } = parseFrontmatter(content);
11541
11946
  if (fm.status && fm.status !== "active") continue;
11542
11947
  nodes.push({
@@ -11595,9 +12000,9 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
11595
12000
 
11596
12001
  // src/lib/memory-sync.ts
11597
12002
  var import_promises8 = require("node:fs/promises");
11598
- var import_node_fs5 = require("node:fs");
11599
- var import_node_path6 = require("node:path");
11600
- var import_node_crypto = require("node:crypto");
12003
+ var import_node_fs6 = require("node:fs");
12004
+ var import_node_path7 = require("node:path");
12005
+ var import_node_crypto2 = require("node:crypto");
11601
12006
 
11602
12007
  // src/lib/glob-match.ts
11603
12008
  function globToRegex(glob) {
@@ -11667,35 +12072,35 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
11667
12072
  async function ensureMemoryDir() {
11668
12073
  await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
11669
12074
  for (const domain of DOMAINS2) {
11670
- await (0, import_promises8.mkdir)((0, import_node_path6.join)(memoryDir2(), domain), { recursive: true });
12075
+ await (0, import_promises8.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
11671
12076
  }
11672
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"))) {
11673
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
12077
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
12078
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
11674
12079
  }
11675
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "index.md"))) {
11676
- 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");
12080
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
12081
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
11677
12082
  }
11678
- if (!(0, import_node_fs5.existsSync)((0, import_node_path6.join)(memoryDir2(), "log.md"))) {
11679
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
12083
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
12084
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
11680
12085
  }
11681
12086
  }
11682
12087
  async function buildManifest() {
11683
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
12088
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
11684
12089
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
11685
12090
  }
11686
12091
  const nodes = [];
11687
12092
  for (const domain of DOMAINS2) {
11688
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11689
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12093
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12094
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11690
12095
  try {
11691
12096
  const files = await (0, import_promises8.readdir)(domainDir);
11692
12097
  for (const file of files) {
11693
12098
  if (!file.endsWith(".md")) continue;
11694
12099
  const filePath = `${domain}/${file}`;
11695
- const fullPath = (0, import_node_path6.join)(memoryDir2(), filePath);
12100
+ const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
11696
12101
  try {
11697
12102
  const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
11698
- const hash = (0, import_node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16);
12103
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
11699
12104
  nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
11700
12105
  } catch {
11701
12106
  }
@@ -11705,32 +12110,32 @@ async function buildManifest() {
11705
12110
  }
11706
12111
  let indexHash = null;
11707
12112
  try {
11708
- const indexContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "index.md"), "utf-8");
11709
- indexHash = `sha256:${(0, import_node_crypto.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
12113
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
12114
+ indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
11710
12115
  } catch {
11711
12116
  }
11712
12117
  let logLength = 0;
11713
12118
  try {
11714
- const logContent = await (0, import_promises8.readFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), "utf-8");
12119
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
11715
12120
  logLength = logContent.split("\n").length;
11716
12121
  } catch {
11717
12122
  }
11718
12123
  return { schema_version: 1, nodes, index_hash: indexHash, log_length: logLength };
11719
12124
  }
11720
12125
  function hashContent(content) {
11721
- return `sha256:${(0, import_node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
12126
+ return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
11722
12127
  }
11723
12128
  async function readOnDiskNodes() {
11724
12129
  const out = /* @__PURE__ */ new Map();
11725
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12130
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
11726
12131
  for (const domain of DOMAINS2) {
11727
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11728
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12132
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12133
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11729
12134
  try {
11730
12135
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
11731
12136
  if (!file.endsWith(".md")) continue;
11732
12137
  try {
11733
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8")));
12138
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
11734
12139
  } catch {
11735
12140
  }
11736
12141
  }
@@ -11776,8 +12181,8 @@ async function computeEditedNodeUploads() {
11776
12181
  const uploads = [];
11777
12182
  for (const [path, prevHash] of prev) {
11778
12183
  if (prevHash == null) continue;
11779
- const full = (0, import_node_path6.join)(memoryDir2(), path);
11780
- if (!(0, import_node_fs5.existsSync)(full)) continue;
12184
+ const full = (0, import_node_path7.join)(memoryDir2(), path);
12185
+ if (!(0, import_node_fs6.existsSync)(full)) continue;
11781
12186
  let content;
11782
12187
  try {
11783
12188
  content = await (0, import_promises8.readFile)(full, "utf-8");
@@ -11813,15 +12218,15 @@ async function applyMemoryWrites(writes, opts = {}) {
11813
12218
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
11814
12219
  for (const n of notes) logLines.push(` - ${n}`);
11815
12220
  try {
11816
- 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";
11817
- await (0, import_promises8.writeFile)((0, import_node_path6.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
12221
+ const existing = (0, import_node_fs6.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12222
+ await (0, import_promises8.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
11818
12223
  } catch {
11819
12224
  }
11820
12225
  await recordSyncedNodePaths();
11821
12226
  return count;
11822
12227
  }
11823
12228
  async function applyOneWrite(write, treePaths) {
11824
- const fullPath = (0, import_node_path6.join)(memoryDir2(), write.path);
12229
+ const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
11825
12230
  const notes = [];
11826
12231
  let content = write.content;
11827
12232
  if (treePaths && treePaths.length > 0) {
@@ -11831,7 +12236,7 @@ async function applyOneWrite(write, treePaths) {
11831
12236
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
11832
12237
  }
11833
12238
  }
11834
- if ((0, import_node_fs5.existsSync)(fullPath)) {
12239
+ if ((0, import_node_fs6.existsSync)(fullPath)) {
11835
12240
  let existing = "";
11836
12241
  try {
11837
12242
  existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
@@ -11843,7 +12248,7 @@ async function applyOneWrite(write, treePaths) {
11843
12248
  return { written: false, notes };
11844
12249
  }
11845
12250
  }
11846
- await (0, import_promises8.mkdir)((0, import_node_path6.dirname)(fullPath), { recursive: true });
12251
+ await (0, import_promises8.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
11847
12252
  await (0, import_promises8.writeFile)(fullPath, content);
11848
12253
  return { written: true, notes };
11849
12254
  }
@@ -11884,8 +12289,8 @@ async function regenerateIndex() {
11884
12289
  ];
11885
12290
  let totalNodes = 0;
11886
12291
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
11887
- const domainDir = (0, import_node_path6.join)(memoryDir2(), domain);
11888
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12292
+ const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
12293
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11889
12294
  try {
11890
12295
  const files = await (0, import_promises8.readdir)(domainDir);
11891
12296
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -11895,7 +12300,7 @@ async function regenerateIndex() {
11895
12300
  for (const file of mdFiles.sort()) {
11896
12301
  const slug = file.replace(/\.md$/, "");
11897
12302
  try {
11898
- const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
12303
+ const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
11899
12304
  const title = pickFrontmatter(content, "title") ?? slug;
11900
12305
  const kind = pickFrontmatter(content, "kind") ?? "-";
11901
12306
  const confidence = pickFrontmatter(content, "confidence");
@@ -11919,7 +12324,7 @@ async function regenerateIndex() {
11919
12324
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
11920
12325
  }
11921
12326
  const next = lines.join("\n") + "\n";
11922
- const indexPath = (0, import_node_path6.join)(memoryDir2(), "index.md");
12327
+ const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
11923
12328
  let existing = null;
11924
12329
  try {
11925
12330
  existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
@@ -12001,9 +12406,9 @@ function hasLegacyMemoryBlock(text) {
12001
12406
  return findMarker(text, LEGACY_MD_START) !== -1;
12002
12407
  }
12003
12408
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12004
- const claudeMdPath = (0, import_node_path6.join)(cwd, "CLAUDE.md");
12409
+ const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
12005
12410
  let existing = "";
12006
- if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
12411
+ if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12007
12412
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
12008
12413
  }
12009
12414
  let startTag = CLAUDE_MD_START;
@@ -12133,7 +12538,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
12133
12538
  `;
12134
12539
 
12135
12540
  // src/commands/intent.ts
12136
- var import_node_fs6 = require("node:fs");
12541
+ var import_node_fs7 = require("node:fs");
12137
12542
  function registerIntentCommands(program2) {
12138
12543
  const intent = program2.command("intent").description("Manage intent capture");
12139
12544
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
@@ -12142,7 +12547,7 @@ function registerIntentCommands(program2) {
12142
12547
  process.chdir(repoRoot());
12143
12548
  } catch {
12144
12549
  }
12145
- if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
12550
+ if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
12146
12551
  process.exit(0);
12147
12552
  }
12148
12553
  const chunks = [];
@@ -12205,7 +12610,7 @@ async function fireClassify(prompt, sessionId) {
12205
12610
  logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
12206
12611
  return;
12207
12612
  }
12208
- const promptHash = (0, import_node_crypto2.createHash)("sha256").update(prompt).digest("hex");
12613
+ const promptHash = (0, import_node_crypto3.createHash)("sha256").update(prompt).digest("hex");
12209
12614
  const result = await apiRequest({
12210
12615
  method: "POST",
12211
12616
  path: "/classify-task",
@@ -12638,278 +13043,89 @@ function registerFeedbackCommand(program2) {
12638
13043
  await sendGeneralFeedback(message, opts, globals);
12639
13044
  });
12640
13045
  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) => {
12641
- const globals = program2.opts();
12642
- const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
12643
- if (!validActions.includes(action)) {
12644
- printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
12645
- process.exit(1);
12646
- }
12647
- const tokenResult = await resolveToken(globals.token);
12648
- if (!tokenResult.ok) {
12649
- printError(tokenResult.error);
12650
- process.exit(1);
12651
- }
12652
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
12653
- if (!urlResult.ok) {
12654
- printError(urlResult.error);
12655
- process.exit(1);
12656
- }
12657
- const body = {
12658
- run_id: runId,
12659
- pattern_id: patternId,
12660
- action
12661
- };
12662
- if (note) body.note = note;
12663
- if (opts.file) body.file_path = opts.file;
12664
- if (opts.line != null) body.line = opts.line;
12665
- const result = await apiRequest({
12666
- method: "POST",
12667
- path: "/feedback/findings",
12668
- serviceUrl: urlResult.data,
12669
- token: tokenResult.data.token,
12670
- body,
12671
- verbose: globals.verbose
12672
- });
12673
- if (!result.ok) {
12674
- printError(`Couldn't submit finding feedback: ${result.error}`);
12675
- process.exit(1);
12676
- }
12677
- const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
12678
- printInfo(status);
12679
- });
12680
- feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
12681
- if (!message) return;
12682
- const globals = program2.opts();
12683
- await sendGeneralFeedback(message, opts, globals);
12684
- });
12685
- }
12686
- async function sendGeneralFeedback(message, opts, globals) {
12687
- const tokenResult = await resolveToken(globals.token);
12688
- if (!tokenResult.ok) {
12689
- printError(tokenResult.error);
12690
- printInfo(`Your message: ${message}`);
12691
- process.exit(1);
12692
- }
12693
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
12694
- if (!urlResult.ok) {
12695
- printError(urlResult.error);
12696
- printInfo(`Your message: ${message}`);
12697
- process.exit(1);
12698
- }
12699
- const body = { message };
12700
- const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
12701
- const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
12702
- if (sessionId) body.session_id = sessionId;
12703
- if (model) body.agent_model = model;
12704
- const result = await apiRequest({
12705
- method: "POST",
12706
- path: "/feedback",
12707
- serviceUrl: urlResult.data,
12708
- token: tokenResult.data.token,
12709
- body,
12710
- verbose: globals.verbose
12711
- });
12712
- if (!result.ok) {
12713
- printError(`Couldn't send feedback: ${result.error}`);
12714
- printInfo(`Your message: ${message}`);
12715
- process.exit(1);
12716
- }
12717
- printInfo("Thanks, feedback sent!");
12718
- }
12719
-
12720
- // src/commands/analyze.ts
12721
- var import_node_fs18 = require("node:fs");
12722
- var import_node_path13 = require("node:path");
12723
-
12724
- // src/lib/git.ts
12725
- var import_node_child_process5 = require("node:child_process");
12726
- var import_node_fs7 = require("node:fs");
12727
- var import_node_path7 = require("node:path");
12728
- function resolveFile(relpath) {
12729
- if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
12730
- if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
12731
- try {
12732
- const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
12733
- for (const entry of entries) {
12734
- if (!entry.isDirectory()) continue;
12735
- const candidate = (0, import_node_path7.join)(".claude/worktrees", entry.name, relpath);
12736
- if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
12737
- }
12738
- } catch {
12739
- }
12740
- }
12741
- return null;
12742
- }
12743
- function execGit(cmd) {
12744
- try {
12745
- return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12746
- } catch {
12747
- return "";
12748
- }
12749
- }
12750
- function splitLines(s) {
12751
- return s.split("\n").filter((l) => l.length > 0);
12752
- }
12753
- var SHA_RE = /^[0-9a-f]{40}$/;
12754
- function readBaselineSha() {
12755
- if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
12756
- let sha;
12757
- try {
12758
- sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
12759
- } catch {
12760
- return null;
12761
- }
12762
- if (!SHA_RE.test(sha)) return null;
12763
- const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
12764
- if (!reachable) {
12765
- try {
12766
- (0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
12767
- } catch {
12768
- }
12769
- return null;
12770
- }
12771
- return sha;
12772
- }
12773
- function writeBaselineSha(sha) {
12774
- if (!SHA_RE.test(sha)) return;
12775
- try {
12776
- (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(BASELINE_SHA_FILE), { recursive: true });
12777
- (0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
12778
- } catch {
12779
- }
12780
- }
12781
- function getChangedFiles() {
12782
- const sets = /* @__PURE__ */ new Set();
12783
- let hasRecentCommitFiles = false;
12784
- for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
12785
- for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
12786
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
12787
- const baseline = readBaselineSha();
12788
- if (baseline) {
12789
- const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
12790
- if (committed.length > 0) {
12791
- hasRecentCommitFiles = true;
12792
- for (const f of committed) sets.add(f);
12793
- }
12794
- } else {
12795
- const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
12796
- const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
12797
- const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
12798
- const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
12799
- if (commitAge < 120 && !hasUnstaged && !hasStaged) {
12800
- const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
12801
- if (recentFiles.length > 0) {
12802
- hasRecentCommitFiles = true;
12803
- for (const f of recentFiles) sets.add(f);
12804
- }
12805
- }
12806
- }
12807
- for (const f of getWorktreeFiles()) sets.add(f);
12808
- const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12809
- return { files: filtered, hasRecentCommitFiles };
12810
- }
12811
- function getStagedFiles() {
12812
- return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12813
- }
12814
- function getPushRangeFiles() {
12815
- const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12816
- const resolvers = [
12817
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
12818
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
12819
- () => {
12820
- const branch = execGit("git rev-parse --abbrev-ref HEAD");
12821
- return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
12822
- }
12823
- ];
12824
- for (const resolve of resolvers) {
12825
- const range = resolve();
12826
- if (range) return { files: diff(range), range };
12827
- }
12828
- const baseline = readBaselineSha();
12829
- if (baseline) {
12830
- const files = diff(`${baseline}..HEAD`);
12831
- if (files.length > 0) return { files, range: `${baseline}..HEAD` };
12832
- }
12833
- const last = diff("HEAD~1..HEAD");
12834
- return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
12835
- }
12836
- function getPushRangeMessages() {
12837
- const { range } = getPushRangeFiles();
12838
- if (!range) return "";
12839
- return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
12840
- }
12841
- function getWorktreeFiles() {
12842
- const result = [];
12843
- const worktreeDir = ".claude/worktrees";
12844
- if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
12845
- try {
12846
- const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
12847
- const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
12848
- for (const entry of entries) {
12849
- if (!entry.isDirectory()) continue;
12850
- const wtDir = (0, import_node_path7.join)(worktreeDir, entry.name);
12851
- scanDir(wtDir, wtDir, fiveMinAgo, result);
12852
- }
12853
- } catch {
12854
- }
12855
- return result;
12856
- }
12857
- function scanDir(baseDir, dir, minMtime, result) {
12858
- try {
12859
- const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
12860
- for (const entry of entries) {
12861
- const fullPath = (0, import_node_path7.join)(dir, entry.name);
12862
- if (entry.isDirectory()) {
12863
- if (entry.name === "node_modules" || entry.name === ".git") continue;
12864
- scanDir(baseDir, fullPath, minMtime, result);
12865
- } else if (entry.isFile()) {
12866
- const ext = (0, import_node_path7.extname)(entry.name).slice(1);
12867
- if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
12868
- try {
12869
- const stat3 = (0, import_node_fs7.statSync)(fullPath);
12870
- if (stat3.mtimeMs >= minMtime) {
12871
- const relPath = fullPath.slice(baseDir.length + 1);
12872
- result.push(relPath);
12873
- }
12874
- } catch {
12875
- }
12876
- }
13046
+ const globals = program2.opts();
13047
+ const validActions = ["false_positive", "acknowledged", "will_fix_later", "wrong_severity", "useful"];
13048
+ if (!validActions.includes(action)) {
13049
+ printError(`Invalid action "${action}". Must be one of: ${validActions.join(", ")}`);
13050
+ process.exit(1);
12877
13051
  }
12878
- } catch {
12879
- }
12880
- }
12881
- function filterAnalyzable(files) {
12882
- return files.filter((f) => {
12883
- const ext = (0, import_node_path7.extname)(f).slice(1);
12884
- return ANALYZABLE_EXTENSIONS.has(ext);
13052
+ const tokenResult = await resolveToken(globals.token);
13053
+ if (!tokenResult.ok) {
13054
+ printError(tokenResult.error);
13055
+ process.exit(1);
13056
+ }
13057
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
13058
+ if (!urlResult.ok) {
13059
+ printError(urlResult.error);
13060
+ process.exit(1);
13061
+ }
13062
+ const body = {
13063
+ run_id: runId,
13064
+ pattern_id: patternId,
13065
+ action
13066
+ };
13067
+ if (note) body.note = note;
13068
+ if (opts.file) body.file_path = opts.file;
13069
+ if (opts.line != null) body.line = opts.line;
13070
+ const result = await apiRequest({
13071
+ method: "POST",
13072
+ path: "/feedback/findings",
13073
+ serviceUrl: urlResult.data,
13074
+ token: tokenResult.data.token,
13075
+ body,
13076
+ verbose: globals.verbose
13077
+ });
13078
+ if (!result.ok) {
13079
+ printError(`Couldn't submit finding feedback: ${result.error}`);
13080
+ process.exit(1);
13081
+ }
13082
+ const status = result.data.suppression_active ? "Suppression active \u2014 this pattern will be skipped in future runs for matching files." : "Feedback recorded.";
13083
+ printInfo(status);
12885
13084
  });
12886
- }
12887
- function filterReviewable(files) {
12888
- return files.filter((f) => {
12889
- const ext = (0, import_node_path7.extname)(f).slice(1);
12890
- if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
12891
- if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
12892
- const basename2 = f.split("/").pop() ?? "";
12893
- if (REVIEWABLE_FILENAMES.has(basename2)) return true;
12894
- if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
12895
- return false;
13085
+ feedbackCmd.argument("[message]", "Feedback message (for backwards compat)").option("--session-id <id>", "Session identifier").option("--model <name>", "Agent model name").action(async (message, opts) => {
13086
+ if (!message) return;
13087
+ const globals = program2.opts();
13088
+ await sendGeneralFeedback(message, opts, globals);
12896
13089
  });
12897
13090
  }
12898
- function filterSecurity(files) {
12899
- return files.filter(
12900
- (f) => SECURITY_PATTERNS.some((p) => p.test(f))
12901
- );
12902
- }
12903
- function getCurrentCommit() {
12904
- return execGit("git rev-parse HEAD") || "no-git";
12905
- }
12906
- function listTrackedFiles() {
12907
- const set = /* @__PURE__ */ new Set();
12908
- for (const f of splitLines(execGit("git ls-files"))) set.add(f);
12909
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
12910
- return Array.from(set);
13091
+ async function sendGeneralFeedback(message, opts, globals) {
13092
+ const tokenResult = await resolveToken(globals.token);
13093
+ if (!tokenResult.ok) {
13094
+ printError(tokenResult.error);
13095
+ printInfo(`Your message: ${message}`);
13096
+ process.exit(1);
13097
+ }
13098
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
13099
+ if (!urlResult.ok) {
13100
+ printError(urlResult.error);
13101
+ printInfo(`Your message: ${message}`);
13102
+ process.exit(1);
13103
+ }
13104
+ const body = { message };
13105
+ const sessionId = opts.sessionId ?? process.env.CLAUDE_SESSION_ID;
13106
+ const model = opts.model ?? process.env.CLAUDE_MODEL ?? "unknown";
13107
+ if (sessionId) body.session_id = sessionId;
13108
+ if (model) body.agent_model = model;
13109
+ const result = await apiRequest({
13110
+ method: "POST",
13111
+ path: "/feedback",
13112
+ serviceUrl: urlResult.data,
13113
+ token: tokenResult.data.token,
13114
+ body,
13115
+ verbose: globals.verbose
13116
+ });
13117
+ if (!result.ok) {
13118
+ printError(`Couldn't send feedback: ${result.error}`);
13119
+ printInfo(`Your message: ${message}`);
13120
+ process.exit(1);
13121
+ }
13122
+ printInfo("Thanks, feedback sent!");
12911
13123
  }
12912
13124
 
13125
+ // src/commands/analyze.ts
13126
+ var import_node_fs19 = require("node:fs");
13127
+ var import_node_path14 = require("node:path");
13128
+
12913
13129
  // src/lib/files.ts
12914
13130
  var import_node_fs8 = require("node:fs");
12915
13131
  var import_node_path8 = require("node:path");
@@ -13042,11 +13258,16 @@ function collectCodeDelta(files, opts) {
13042
13258
 
13043
13259
  // src/lib/debounce.ts
13044
13260
  var import_node_fs9 = require("node:fs");
13045
- var import_node_crypto3 = require("node:crypto");
13046
- function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
13047
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
13261
+ var import_node_crypto4 = require("node:crypto");
13262
+ function scopedFile(base, sessionId) {
13263
+ if (!sessionId) return base;
13264
+ return `${base}.${(0, import_node_crypto4.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
13265
+ }
13266
+ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
13267
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13268
+ if (!(0, import_node_fs9.existsSync)(file)) return null;
13048
13269
  try {
13049
- const lastTs = parseInt((0, import_node_fs9.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
13270
+ const lastTs = parseInt((0, import_node_fs9.readFileSync)(file, "utf-8").trim(), 10);
13050
13271
  const nowTs = Math.floor(Date.now() / 1e3);
13051
13272
  const elapsed = nowTs - lastTs;
13052
13273
  if (elapsed < debounceSeconds) {
@@ -13056,12 +13277,13 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
13056
13277
  }
13057
13278
  return null;
13058
13279
  }
13059
- function checkMtime(files, bypassForRecentCommits) {
13280
+ function checkMtime(files, bypassForRecentCommits, sessionId) {
13060
13281
  if (bypassForRecentCommits) return null;
13061
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return null;
13282
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13283
+ if (!(0, import_node_fs9.existsSync)(file)) return null;
13062
13284
  let debounceTime;
13063
13285
  try {
13064
- debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
13286
+ debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
13065
13287
  } catch {
13066
13288
  return null;
13067
13289
  }
@@ -13080,7 +13302,7 @@ function checkMtime(files, bypassForRecentCommits) {
13080
13302
  return "No files modified since last analysis";
13081
13303
  }
13082
13304
  function computeContentHash(files) {
13083
- const hash = (0, import_node_crypto3.createHash)("sha1");
13305
+ const hash = (0, import_node_crypto4.createHash)("sha1");
13084
13306
  const sorted = [...files].sort();
13085
13307
  for (const f of sorted) {
13086
13308
  const resolved = resolveFile(f) ?? f;
@@ -13093,11 +13315,12 @@ function computeContentHash(files) {
13093
13315
  }
13094
13316
  return hash.digest("hex");
13095
13317
  }
13096
- function checkContentHash(files) {
13318
+ function checkContentHash(files, sessionId) {
13097
13319
  const hash = computeContentHash(files);
13098
- if ((0, import_node_fs9.existsSync)(HASH_FILE)) {
13320
+ const file = scopedFile(HASH_FILE, sessionId);
13321
+ if ((0, import_node_fs9.existsSync)(file)) {
13099
13322
  try {
13100
- const storedHash = (0, import_node_fs9.readFileSync)(HASH_FILE, "utf-8").trim();
13323
+ const storedHash = (0, import_node_fs9.readFileSync)(file, "utf-8").trim();
13101
13324
  if (hash === storedHash) {
13102
13325
  return { skip: "No source changes since last analysis", hash };
13103
13326
  }
@@ -13106,18 +13329,19 @@ function checkContentHash(files) {
13106
13329
  }
13107
13330
  return { skip: null, hash };
13108
13331
  }
13109
- function recordAnalysisStart() {
13332
+ function recordAnalysisStart(sessionId) {
13110
13333
  (0, import_node_fs9.mkdirSync)(VERITY_DIR, { recursive: true });
13111
- (0, import_node_fs9.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
13334
+ (0, import_node_fs9.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
13112
13335
  }
13113
- function recordPassHash(hash) {
13114
- (0, import_node_fs9.writeFileSync)(HASH_FILE, hash);
13336
+ function recordPassHash(hash, sessionId) {
13337
+ (0, import_node_fs9.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
13115
13338
  }
13116
- function narrowToRecent(files) {
13117
- if (!(0, import_node_fs9.existsSync)(DEBOUNCE_FILE)) return files;
13339
+ function narrowToRecent(files, sessionId) {
13340
+ const file = scopedFile(DEBOUNCE_FILE, sessionId);
13341
+ if (!(0, import_node_fs9.existsSync)(file)) return files;
13118
13342
  let debounceTime;
13119
13343
  try {
13120
- debounceTime = (0, import_node_fs9.statSync)(DEBOUNCE_FILE).mtimeMs;
13344
+ debounceTime = (0, import_node_fs9.statSync)(file).mtimeMs;
13121
13345
  } catch {
13122
13346
  return files;
13123
13347
  }
@@ -13463,15 +13687,207 @@ function cleanStaleSnapshots(dir, keepSet) {
13463
13687
  }
13464
13688
  }
13465
13689
 
13466
- // src/lib/offline.ts
13690
+ // src/lib/baseline.ts
13467
13691
  var import_node_fs13 = require("node:fs");
13468
- var import_node_crypto4 = require("node:crypto");
13692
+ var import_node_path11 = require("node:path");
13693
+ var import_node_crypto5 = require("node:crypto");
13694
+ var BASELINE_VERSION = 1;
13695
+ var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
13696
+ var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
13697
+ var DEFAULT_SESSION_KEY = "_default";
13698
+ function sessionKey(sessionId) {
13699
+ if (!sessionId) return DEFAULT_SESSION_KEY;
13700
+ return (0, import_node_crypto5.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
13701
+ }
13702
+ function sessionDir(key) {
13703
+ return (0, import_node_path11.join)(projectPath(BASELINE_DIR), key);
13704
+ }
13705
+ function manifestPath(dir) {
13706
+ return (0, import_node_path11.join)(dir, "manifest.json");
13707
+ }
13708
+ function mirrorPath(dir, repoRelPath) {
13709
+ return (0, import_node_path11.join)(dir, "files", repoRelPath);
13710
+ }
13711
+ function captureBaseline(opts = {}) {
13712
+ const key = sessionKey(opts.sessionId);
13713
+ const dir = sessionDir(key);
13714
+ const existing = readManifest(dir);
13715
+ const freshStart = opts.source === "startup" || opts.source === "clear";
13716
+ if (existing && !freshStart) {
13717
+ return { baseline: existing, created: false };
13718
+ }
13719
+ const head_sha = getCurrentCommit();
13720
+ const dirty = getDirtyFiles();
13721
+ try {
13722
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13723
+ } catch {
13724
+ }
13725
+ const filesDir = (0, import_node_path11.join)(dir, "files");
13726
+ const mirrored = [];
13727
+ try {
13728
+ (0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
13729
+ for (const p of dirty) {
13730
+ if (p.includes("..")) continue;
13731
+ const content = safeReadForMirror(projectPath(p));
13732
+ if (content === null) continue;
13733
+ const dest = mirrorPath(dir, p);
13734
+ try {
13735
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(dest), { recursive: true });
13736
+ (0, import_node_fs13.writeFileSync)(dest, content);
13737
+ mirrored.push(p);
13738
+ } catch {
13739
+ }
13740
+ }
13741
+ } catch {
13742
+ }
13743
+ const baseline = {
13744
+ session_id: opts.sessionId ?? "",
13745
+ head_sha,
13746
+ captured_at: Date.now(),
13747
+ dirty_paths: mirrored,
13748
+ version: BASELINE_VERSION
13749
+ };
13750
+ try {
13751
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
13752
+ (0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
13753
+ } catch {
13754
+ }
13755
+ pruneOldBaselines();
13756
+ return { baseline, created: true };
13757
+ }
13758
+ function readBaseline(sessionId) {
13759
+ return readManifest(sessionDir(sessionKey(sessionId)));
13760
+ }
13761
+ function readManifest(dir) {
13762
+ const mp = manifestPath(dir);
13763
+ if (!(0, import_node_fs13.existsSync)(mp)) return null;
13764
+ try {
13765
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(mp, "utf-8"));
13766
+ if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
13767
+ return null;
13768
+ }
13769
+ return {
13770
+ session_id: typeof parsed.session_id === "string" ? parsed.session_id : "",
13771
+ head_sha: parsed.head_sha,
13772
+ captured_at: parsed.captured_at,
13773
+ dirty_paths: parsed.dirty_paths.filter((p) => typeof p === "string"),
13774
+ version: parsed.version
13775
+ };
13776
+ } catch {
13777
+ return null;
13778
+ }
13779
+ }
13780
+ var preImageCache = /* @__PURE__ */ new WeakMap();
13781
+ function preImage(repoRelPath, baseline) {
13782
+ let perBaseline = preImageCache.get(baseline);
13783
+ if (!perBaseline) {
13784
+ perBaseline = /* @__PURE__ */ new Map();
13785
+ preImageCache.set(baseline, perBaseline);
13786
+ }
13787
+ const cached = perBaseline.get(repoRelPath);
13788
+ if (cached) return cached;
13789
+ const resolved = resolvePreImage(repoRelPath, baseline);
13790
+ perBaseline.set(repoRelPath, resolved);
13791
+ return resolved;
13792
+ }
13793
+ function resolvePreImage(repoRelPath, baseline) {
13794
+ if (baseline.dirty_paths.includes(repoRelPath)) {
13795
+ const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
13796
+ if ((0, import_node_fs13.existsSync)(mp)) {
13797
+ try {
13798
+ return { content: (0, import_node_fs13.readFileSync)(mp, "utf-8"), existed: true };
13799
+ } catch {
13800
+ }
13801
+ }
13802
+ }
13803
+ const atHead = showContentAtRef(baseline.head_sha, repoRelPath);
13804
+ if (atHead !== null) return { content: atHead, existed: true };
13805
+ return { content: "", existed: false };
13806
+ }
13807
+ function generateBaselineDiffs(files, baseline) {
13808
+ if (!baseline) return { diffs: [], has_baseline: false };
13809
+ const diffs = [];
13810
+ for (const file of files) {
13811
+ const language = file.language ?? detectLanguage(file.path);
13812
+ const pre = preImage(file.path, baseline);
13813
+ if (pre.existed) {
13814
+ if (pre.content === file.content) continue;
13815
+ const diff = computeDiff(pre.content, file.content, file.path);
13816
+ if (diff) diffs.push({ path: file.path, language, diff, status: "modified" });
13817
+ } else {
13818
+ const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
13819
+ diffs.push({
13820
+ path: file.path,
13821
+ language,
13822
+ diff: `--- /dev/null
13823
+ +++ b/${file.path}
13824
+ @@ -0,0 +1,${file.content.split("\n").length} @@
13825
+ ${addedLines}`,
13826
+ status: "added"
13827
+ });
13828
+ }
13829
+ }
13830
+ return { diffs, has_baseline: true };
13831
+ }
13832
+ function changedSinceBaseline(repoRelPath, baseline) {
13833
+ const pre = preImage(repoRelPath, baseline);
13834
+ let current;
13835
+ try {
13836
+ current = (0, import_node_fs13.readFileSync)(projectPath(repoRelPath), "utf-8");
13837
+ } catch {
13838
+ return pre.existed;
13839
+ }
13840
+ if (!pre.existed) return true;
13841
+ return current !== pre.content;
13842
+ }
13843
+ function safeReadForMirror(absPath) {
13844
+ try {
13845
+ if ((0, import_node_fs13.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
13846
+ const buf = (0, import_node_fs13.readFileSync)(absPath);
13847
+ if (buf.includes(0)) return null;
13848
+ return buf.toString("utf-8");
13849
+ } catch {
13850
+ return null;
13851
+ }
13852
+ }
13853
+ function pruneOldBaselines() {
13854
+ const root = projectPath(BASELINE_DIR);
13855
+ let entries;
13856
+ try {
13857
+ entries = (0, import_node_fs13.readdirSync)(root);
13858
+ } catch {
13859
+ return;
13860
+ }
13861
+ const now = Date.now();
13862
+ for (const name of entries) {
13863
+ const dir = (0, import_node_path11.join)(root, name);
13864
+ const manifest = readManifest(dir);
13865
+ if (!manifest) {
13866
+ try {
13867
+ if (now - (0, import_node_fs13.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
13868
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13869
+ }
13870
+ } catch {
13871
+ }
13872
+ continue;
13873
+ }
13874
+ if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
13875
+ try {
13876
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13877
+ } catch {
13878
+ }
13879
+ }
13880
+ }
13881
+
13882
+ // src/lib/offline.ts
13883
+ var import_node_fs14 = require("node:fs");
13884
+ var import_node_crypto6 = require("node:crypto");
13469
13885
  function cacheRequest(body) {
13470
13886
  try {
13471
- (0, import_node_fs13.mkdirSync)(CACHE_DIR, { recursive: true });
13472
- const suffix = (0, import_node_crypto4.randomBytes)(4).toString("hex");
13887
+ (0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
13888
+ const suffix = (0, import_node_crypto6.randomBytes)(4).toString("hex");
13473
13889
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
13474
- (0, import_node_fs13.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
13890
+ (0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
13475
13891
  } catch {
13476
13892
  }
13477
13893
  }
@@ -13485,7 +13901,7 @@ function buildOfflineFallback(reason, staticResults) {
13485
13901
  }
13486
13902
 
13487
13903
  // src/lib/context-files.ts
13488
- var import_node_fs14 = require("node:fs");
13904
+ var import_node_fs15 = require("node:fs");
13489
13905
  var MAX_CONTEXT_FILES = 10;
13490
13906
  var MAX_CONTEXT_FILE_BYTES = 10240;
13491
13907
  var MAX_CONTEXT_TOTAL_BYTES = 51200;
@@ -13497,7 +13913,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
13497
13913
  if (result.length >= MAX_CONTEXT_FILES) break;
13498
13914
  if (deltaPaths.has(filePath)) continue;
13499
13915
  try {
13500
- const content = (0, import_node_fs14.readFileSync)(filePath, "utf8");
13916
+ const content = (0, import_node_fs15.readFileSync)(filePath, "utf8");
13501
13917
  const bytes = Buffer.byteLength(content);
13502
13918
  if (bytes > MAX_CONTEXT_FILE_BYTES) {
13503
13919
  logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
@@ -13542,20 +13958,20 @@ function gatherContextFiles(contextPaths, deltaFiles) {
13542
13958
  }
13543
13959
 
13544
13960
  // src/lib/cache-cleanup.ts
13545
- var import_node_fs15 = require("node:fs");
13546
- var import_node_path11 = require("node:path");
13961
+ var import_node_fs16 = require("node:fs");
13962
+ var import_node_path12 = require("node:path");
13547
13963
  var CACHE_TTL_DAYS = 7;
13548
13964
  function pruneStaleCache() {
13549
13965
  try {
13550
13966
  const dir = projectPath(CACHE_DIR);
13551
13967
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
13552
- for (const entry of (0, import_node_fs15.readdirSync)(dir)) {
13968
+ for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
13553
13969
  if (!entry.startsWith("pending-")) continue;
13554
- const path = (0, import_node_path11.join)(dir, entry);
13970
+ const path = (0, import_node_path12.join)(dir, entry);
13555
13971
  try {
13556
- const stat3 = (0, import_node_fs15.statSync)(path);
13972
+ const stat3 = (0, import_node_fs16.statSync)(path);
13557
13973
  if (stat3.mtimeMs < cutoff) {
13558
- (0, import_node_fs15.unlinkSync)(path);
13974
+ (0, import_node_fs16.unlinkSync)(path);
13559
13975
  logEvent("cache_entry_pruned", {
13560
13976
  path: entry,
13561
13977
  age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
@@ -13627,10 +14043,11 @@ function reconcileAnalysisMode(predictedMode, signals) {
13627
14043
  signals.noFilesChanged,
13628
14044
  signals.assistantResponse,
13629
14045
  signals.conversationPrompts,
13630
- signals.actionSummary
14046
+ signals.actionSummary,
14047
+ signals.sessionAuthoredCode
13631
14048
  );
13632
14049
  }
13633
- const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0));
14050
+ const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
13634
14051
  const agentInvestigated = didAgentInvestigate(signals.actionSummary);
13635
14052
  switch (predictedMode) {
13636
14053
  case "skip":
@@ -13655,8 +14072,8 @@ function didAgentInvestigate(summary) {
13655
14072
  function isValidMode(mode) {
13656
14073
  return mode === "standard" || mode === "plan" || mode === "debug" || mode === "skip";
13657
14074
  }
13658
- function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary) {
13659
- const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
14075
+ function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
14076
+ const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
13660
14077
  if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
13661
14078
  if (!agentAuthoredCode) return "skip";
13662
14079
  }
@@ -13736,7 +14153,7 @@ function isMetaTaskLabel(label2) {
13736
14153
  }
13737
14154
 
13738
14155
  // src/lib/transcript.ts
13739
- var import_node_fs16 = require("node:fs");
14156
+ var import_node_fs17 = require("node:fs");
13740
14157
  var MAX_READ_BYTES = 256 * 1024;
13741
14158
  var SMALL_FILE_BYTES = 64 * 1024;
13742
14159
  var MAX_FILES_LIST = 20;
@@ -13758,14 +14175,14 @@ async function extractActionSummary(transcriptPath) {
13758
14175
  function readTurnLines(transcriptPath) {
13759
14176
  let size;
13760
14177
  try {
13761
- size = (0, import_node_fs16.statSync)(transcriptPath).size;
14178
+ size = (0, import_node_fs17.statSync)(transcriptPath).size;
13762
14179
  } catch {
13763
14180
  return null;
13764
14181
  }
13765
14182
  if (size === 0) return null;
13766
14183
  let raw;
13767
14184
  if (size <= SMALL_FILE_BYTES) {
13768
- raw = (0, import_node_fs16.readFileSync)(transcriptPath, "utf-8");
14185
+ raw = (0, import_node_fs17.readFileSync)(transcriptPath, "utf-8");
13769
14186
  } else {
13770
14187
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
13771
14188
  const fd = require("node:fs").openSync(transcriptPath, "r");
@@ -13853,6 +14270,12 @@ function buildSummary(lines) {
13853
14270
  case "Edit":
13854
14271
  addPath(filesEdited, input.file_path);
13855
14272
  break;
14273
+ case "MultiEdit":
14274
+ addPath(filesEdited, input.file_path);
14275
+ break;
14276
+ case "NotebookEdit":
14277
+ addPath(filesEdited, input.notebook_path ?? input.file_path);
14278
+ break;
13856
14279
  case "Write":
13857
14280
  addPath(filesCreated, input.file_path);
13858
14281
  addPath(filesEdited, input.file_path);
@@ -13936,8 +14359,8 @@ function capArray(set, max) {
13936
14359
 
13937
14360
  // src/lib/seed-runner.ts
13938
14361
  var import_promises11 = require("node:fs/promises");
13939
- var import_node_fs17 = require("node:fs");
13940
- var import_node_path12 = require("node:path");
14362
+ var import_node_fs18 = require("node:fs");
14363
+ var import_node_path13 = require("node:path");
13941
14364
  var import_yaml2 = __toESM(require_dist());
13942
14365
 
13943
14366
  // src/lib/seed.ts
@@ -14176,7 +14599,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
14176
14599
  return fm;
14177
14600
  }
14178
14601
  async function runSeed(opts) {
14179
- if (!(0, import_node_fs17.existsSync)(STANDARD_FILE)) {
14602
+ if (!(0, import_node_fs18.existsSync)(STANDARD_FILE)) {
14180
14603
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
14181
14604
  }
14182
14605
  let standardDoc;
@@ -14188,7 +14611,7 @@ async function runSeed(opts) {
14188
14611
  }
14189
14612
  const knowledgeSpec = standardDoc.knowledge_spec ?? {};
14190
14613
  let readmeContent;
14191
- if ((0, import_node_fs17.existsSync)("README.md")) {
14614
+ if ((0, import_node_fs18.existsSync)("README.md")) {
14192
14615
  try {
14193
14616
  readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
14194
14617
  } catch {
@@ -14196,7 +14619,7 @@ async function runSeed(opts) {
14196
14619
  }
14197
14620
  let claudeMdContent;
14198
14621
  for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
14199
- if ((0, import_node_fs17.existsSync)(p)) {
14622
+ if ((0, import_node_fs18.existsSync)(p)) {
14200
14623
  try {
14201
14624
  claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
14202
14625
  break;
@@ -14219,8 +14642,8 @@ async function runSeed(opts) {
14219
14642
  if (candidates.length === 0) {
14220
14643
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
14221
14644
  }
14222
- const overviewPath = (0, import_node_path12.join)(MEMORY_DIR, "domain", "project-overview.md");
14223
- if ((0, import_node_fs17.existsSync)(overviewPath) && !opts.force) {
14645
+ const overviewPath = (0, import_node_path13.join)(MEMORY_DIR, "domain", "project-overview.md");
14646
+ if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
14224
14647
  return { created: 0, failed: 0, skipped: "already_seeded", candidates };
14225
14648
  }
14226
14649
  if (opts.dryRun) {
@@ -14255,9 +14678,9 @@ async function runSeed(opts) {
14255
14678
  }
14256
14679
  const nodeId = res.data.node_id;
14257
14680
  const filePathRel = res.data.file_path;
14258
- const targetPath = (0, import_node_path12.join)(MEMORY_DIR, filePathRel);
14681
+ const targetPath = (0, import_node_path13.join)(MEMORY_DIR, filePathRel);
14259
14682
  try {
14260
- await (0, import_promises11.mkdir)((0, import_node_path12.dirname)(targetPath), { recursive: true });
14683
+ await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(targetPath), { recursive: true });
14261
14684
  await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
14262
14685
  created++;
14263
14686
  opts.onCreated?.(nodeId, filePathRel, c);
@@ -14327,6 +14750,15 @@ async function runAnalyze(opts, globals) {
14327
14750
  }
14328
14751
  const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
14329
14752
  const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
14753
+ const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
14754
+ const baseline = readBaseline(baselineSessionId);
14755
+ if (baseline) {
14756
+ logEvent("baseline_loaded", {
14757
+ head: baseline.head_sha.slice(0, 12),
14758
+ dirty_count: baseline.dirty_paths.length,
14759
+ age_ms: Date.now() - baseline.captured_at
14760
+ });
14761
+ }
14330
14762
  const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
14331
14763
  const analyzable = filterAnalyzable(allChanged);
14332
14764
  const reviewable = filterReviewable(allChanged);
@@ -14336,7 +14768,7 @@ async function runAnalyze(opts, globals) {
14336
14768
  passAndExit("No analyzable files changed");
14337
14769
  }
14338
14770
  const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
14339
- const conversation = await readAndClearConversationBuffer();
14771
+ const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
14340
14772
  const specs = discoverSpecs();
14341
14773
  const plans = discoverPlans();
14342
14774
  const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
@@ -14386,13 +14818,14 @@ async function runAnalyze(opts, globals) {
14386
14818
  }
14387
14819
  const conversationPrompts = (conversation?.prompts ?? []).map((p) => p.prompt);
14388
14820
  let analysisMode;
14821
+ const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
14389
14822
  const modeOverride = opts.mode;
14390
14823
  if (modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride)) {
14391
14824
  analysisMode = modeOverride;
14392
14825
  } else {
14393
14826
  analysisMode = reconcileAnalysisMode(
14394
14827
  predictedMode,
14395
- { noFilesChanged, assistantResponse, actionSummary, conversationPrompts }
14828
+ { noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
14396
14829
  );
14397
14830
  }
14398
14831
  if (analysisMode === "skip") {
@@ -14414,7 +14847,7 @@ async function runAnalyze(opts, globals) {
14414
14847
  let currentCommit = "";
14415
14848
  if (analysisMode !== "plan") {
14416
14849
  const debounceSeconds = parseInt(opts.debounce, 10);
14417
- const debounceSkip = checkDebounce(debounceSeconds);
14850
+ const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
14418
14851
  if (debounceSkip) {
14419
14852
  if (assistantResponse) {
14420
14853
  analysisMode = "plan";
@@ -14424,7 +14857,7 @@ async function runAnalyze(opts, globals) {
14424
14857
  }
14425
14858
  if (analysisMode !== "plan") {
14426
14859
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
14427
- const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
14860
+ const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
14428
14861
  if (mtimeSkip) {
14429
14862
  if (assistantResponse) {
14430
14863
  analysisMode = "plan";
@@ -14434,9 +14867,9 @@ async function runAnalyze(opts, globals) {
14434
14867
  }
14435
14868
  }
14436
14869
  if (analysisMode !== "plan") {
14437
- recordAnalysisStart();
14870
+ recordAnalysisStart(baselineSessionId);
14438
14871
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
14439
- const hashResult = checkContentHash(allCheckable);
14872
+ const hashResult = checkContentHash(allCheckable, baselineSessionId);
14440
14873
  if (hashResult.skip) {
14441
14874
  if (assistantResponse) {
14442
14875
  analysisMode = "plan";
@@ -14448,12 +14881,18 @@ async function runAnalyze(opts, globals) {
14448
14881
  if (analysisMode !== "plan") {
14449
14882
  const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
14450
14883
  const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
14451
- const recentForReview = narrowToRecent(baseForReview);
14884
+ const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
14452
14885
  if (!opts.skipStatic && isCodacyAvailable()) {
14453
- const allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14454
- staticResults = runCodacyAnalysis(allScannable);
14886
+ let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14887
+ if (baseline) {
14888
+ allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
14889
+ }
14890
+ if (allScannable.length > 0) {
14891
+ staticResults = runCodacyAnalysis(allScannable);
14892
+ }
14455
14893
  }
14456
- codeDelta = collectCodeDelta(recentForReview, {
14894
+ const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
14895
+ codeDelta = collectCodeDelta(deltaSet, {
14457
14896
  maxFiles: parseInt(opts.maxFiles, 10),
14458
14897
  maxFileBytes: parseInt(opts.maxFileSize, 10),
14459
14898
  maxTotalBytes: parseInt(opts.maxTotalSize, 10)
@@ -14468,7 +14907,12 @@ async function runAnalyze(opts, globals) {
14468
14907
  }
14469
14908
  }
14470
14909
  if (analysisMode !== "plan") {
14471
- snapshotResult = generateSnapshotDiffs(codeDelta.files);
14910
+ if (baseline) {
14911
+ const baselineDiffs = generateBaselineDiffs(codeDelta.files, baseline);
14912
+ snapshotResult = { has_snapshots: baselineDiffs.diffs.length > 0, diffs: baselineDiffs.diffs };
14913
+ } else {
14914
+ snapshotResult = generateSnapshotDiffs(codeDelta.files);
14915
+ }
14472
14916
  currentCommit = getCurrentCommit();
14473
14917
  const maxIterations = parseInt(opts.maxIterations, 10);
14474
14918
  const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
@@ -14500,9 +14944,9 @@ async function runAnalyze(opts, globals) {
14500
14944
  let autoSeedNotice = null;
14501
14945
  try {
14502
14946
  await ensureMemoryDir();
14503
- const seedMarker = (0, import_node_path13.join)(VERITY_DIR, ".seeded");
14504
- const hasStandard = (0, import_node_fs18.existsSync)(STANDARD_FILE);
14505
- const alreadyTried = (0, import_node_fs18.existsSync)(seedMarker);
14947
+ const seedMarker = (0, import_node_path14.join)(VERITY_DIR, ".seeded");
14948
+ const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
14949
+ const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
14506
14950
  if (hasStandard && !alreadyTried) {
14507
14951
  const preManifest = await buildManifest();
14508
14952
  if (preManifest.nodes.length === 0) {
@@ -14515,7 +14959,7 @@ async function runAnalyze(opts, globals) {
14515
14959
  dryRun: false
14516
14960
  });
14517
14961
  if (seedResult.created > 0) {
14518
- (0, import_node_fs18.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
14962
+ (0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
14519
14963
  `);
14520
14964
  autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
14521
14965
  logEvent("auto_seed_ran", {
@@ -14523,7 +14967,7 @@ async function runAnalyze(opts, globals) {
14523
14967
  failed: seedResult.failed
14524
14968
  });
14525
14969
  } else if (seedResult.skipped === "already_seeded") {
14526
- (0, import_node_fs18.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
14970
+ (0, import_node_fs19.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
14527
14971
  `);
14528
14972
  } else {
14529
14973
  logEvent("auto_seed_noop", {
@@ -14812,7 +15256,7 @@ async function runAnalyze(opts, globals) {
14812
15256
  }
14813
15257
  case "PASS": {
14814
15258
  writeIteration(1, currentCommit, contentHash ?? void 0);
14815
- if (contentHash) recordPassHash(contentHash);
15259
+ if (contentHash) recordPassHash(contentHash, baselineSessionId);
14816
15260
  if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
14817
15261
  let userSummary = response.user_summary ?? "Verity: PASS";
14818
15262
  const viewUrl = response.view_url ?? "";
@@ -14823,7 +15267,7 @@ async function runAnalyze(opts, globals) {
14823
15267
  break;
14824
15268
  }
14825
15269
  case "WARN": {
14826
- if (contentHash) recordPassHash(contentHash);
15270
+ if (contentHash) recordPassHash(contentHash, baselineSessionId);
14827
15271
  if (currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
14828
15272
  let userSummary = response.user_summary ?? "Verity: WARN";
14829
15273
  const viewUrl = response.view_url ?? "";
@@ -14841,8 +15285,54 @@ async function runAnalyze(opts, globals) {
14841
15285
  }
14842
15286
  }
14843
15287
 
15288
+ // src/commands/baseline.ts
15289
+ var import_node_fs20 = require("node:fs");
15290
+ function registerBaselineCommands(program2) {
15291
+ const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
15292
+ 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) => {
15293
+ try {
15294
+ try {
15295
+ process.chdir(repoRoot());
15296
+ } catch {
15297
+ }
15298
+ if (!(0, import_node_fs20.existsSync)(VERITY_DIR)) {
15299
+ process.exit(0);
15300
+ }
15301
+ let sessionId = opts.sessionId;
15302
+ let source = opts.source;
15303
+ if (!process.stdin.isTTY) {
15304
+ const input = (await readStdin()).trim();
15305
+ if (input) {
15306
+ try {
15307
+ const event = JSON.parse(input);
15308
+ sessionId = sessionId ?? event.session_id;
15309
+ source = source ?? event.source;
15310
+ } catch {
15311
+ }
15312
+ }
15313
+ }
15314
+ const result = captureBaseline({ sessionId, source });
15315
+ logEvent("baseline_capture", {
15316
+ created: result.created,
15317
+ source: source ?? null,
15318
+ dirty_count: result.baseline.dirty_paths.length,
15319
+ head: result.baseline.head_sha.slice(0, 12)
15320
+ });
15321
+ } catch {
15322
+ }
15323
+ process.exit(0);
15324
+ });
15325
+ }
15326
+ async function readStdin() {
15327
+ const chunks = [];
15328
+ for await (const chunk of process.stdin) {
15329
+ chunks.push(chunk);
15330
+ }
15331
+ return Buffer.concat(chunks).toString("utf-8");
15332
+ }
15333
+
14844
15334
  // src/commands/review.ts
14845
- var import_node_fs19 = require("node:fs");
15335
+ var import_node_fs21 = require("node:fs");
14846
15336
  function registerReviewCommand(program2) {
14847
15337
  program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
14848
15338
  const globals = program2.opts();
@@ -14861,7 +15351,7 @@ async function runReview(opts, globals) {
14861
15351
  const securityFiles = filterSecurity(allFiles);
14862
15352
  let staticResults;
14863
15353
  if (isCodacyAvailable()) {
14864
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs19.existsSync)(f) || resolveFile(f) !== null);
15354
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs21.existsSync)(f) || resolveFile(f) !== null);
14865
15355
  staticResults = runCodacyAnalysis(scannable);
14866
15356
  } else {
14867
15357
  staticResults = {
@@ -14886,10 +15376,10 @@ async function runReview(opts, globals) {
14886
15376
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
14887
15377
  specs = [];
14888
15378
  for (const p of specPaths) {
14889
- if (!(0, import_node_fs19.existsSync)(p)) continue;
15379
+ if (!(0, import_node_fs21.existsSync)(p)) continue;
14890
15380
  try {
14891
- const { readFileSync: readFileSync11 } = await import("node:fs");
14892
- const content = readFileSync11(p, "utf-8");
15381
+ const { readFileSync: readFileSync12 } = await import("node:fs");
15382
+ const content = readFileSync12(p, "utf-8");
14893
15383
  specs.push({ path: p, content: content.slice(0, 10240) });
14894
15384
  } catch {
14895
15385
  }
@@ -14946,10 +15436,10 @@ async function runReview(opts, globals) {
14946
15436
  }
14947
15437
 
14948
15438
  // src/commands/guard.ts
14949
- var import_node_fs20 = require("node:fs");
14950
- var import_node_path14 = require("node:path");
15439
+ var import_node_fs22 = require("node:fs");
15440
+ var import_node_path15 = require("node:path");
14951
15441
  var GUARD_BLOCK_CAP = 2;
14952
- var GUARD_ITER_FILE = (0, import_node_path14.join)(VERITY_DIR, ".guard-iteration");
15442
+ var GUARD_ITER_FILE = (0, import_node_path15.join)(VERITY_DIR, ".guard-iteration");
14953
15443
  function readPreToolUseStdin() {
14954
15444
  const empty = { command: "", cwd: null, sessionId: null };
14955
15445
  return new Promise((resolve) => {
@@ -15013,7 +15503,7 @@ function classifyCommand(command, on) {
15013
15503
  }
15014
15504
  function readIterMap() {
15015
15505
  try {
15016
- const raw = JSON.parse((0, import_node_fs20.readFileSync)(GUARD_ITER_FILE, "utf-8"));
15506
+ const raw = JSON.parse((0, import_node_fs22.readFileSync)(GUARD_ITER_FILE, "utf-8"));
15017
15507
  if (raw && typeof raw === "object") {
15018
15508
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
15019
15509
  return { [raw.moment]: raw.count };
@@ -15033,10 +15523,10 @@ function readIter(moment) {
15033
15523
  }
15034
15524
  function writeIter(moment, count) {
15035
15525
  try {
15036
- (0, import_node_fs20.mkdirSync)(VERITY_DIR, { recursive: true });
15526
+ (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
15037
15527
  const map = readIterMap();
15038
15528
  map[moment] = count;
15039
- (0, import_node_fs20.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15529
+ (0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15040
15530
  } catch {
15041
15531
  }
15042
15532
  }
@@ -15046,10 +15536,10 @@ function resetIter(moment) {
15046
15536
  if (!(moment in map)) return;
15047
15537
  delete map[moment];
15048
15538
  if (Object.keys(map).length === 0) {
15049
- if ((0, import_node_fs20.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs20.unlinkSync)(GUARD_ITER_FILE);
15539
+ if ((0, import_node_fs22.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs22.unlinkSync)(GUARD_ITER_FILE);
15050
15540
  } else {
15051
- (0, import_node_fs20.mkdirSync)(VERITY_DIR, { recursive: true });
15052
- (0, import_node_fs20.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15541
+ (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
15542
+ (0, import_node_fs22.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
15053
15543
  }
15054
15544
  } catch {
15055
15545
  }
@@ -15113,7 +15603,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
15113
15603
  const securityFiles = filterSecurity(files);
15114
15604
  let staticResults;
15115
15605
  if (isCodacyAvailable()) {
15116
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs20.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
15606
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs22.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
15117
15607
  staticResults = runCodacyAnalysis(scannable);
15118
15608
  } else {
15119
15609
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -15158,7 +15648,7 @@ function emitAllowNotice(userMsg, agentMsg) {
15158
15648
  async function runGuard(opts, globals) {
15159
15649
  const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
15160
15650
  const { command, cwd, sessionId } = await readPreToolUseStdin();
15161
- if (cwd && (0, import_node_fs20.existsSync)(cwd)) {
15651
+ if (cwd && (0, import_node_fs22.existsSync)(cwd)) {
15162
15652
  try {
15163
15653
  process.chdir(cwd);
15164
15654
  } catch {
@@ -15263,14 +15753,14 @@ function writeBlockMessage(moment, response) {
15263
15753
  }
15264
15754
 
15265
15755
  // src/commands/init.ts
15266
- var import_node_fs22 = require("node:fs");
15756
+ var import_node_fs24 = require("node:fs");
15267
15757
  var import_promises12 = require("node:fs/promises");
15268
- var import_node_path16 = require("node:path");
15758
+ var import_node_path17 = require("node:path");
15269
15759
  var import_node_child_process9 = require("node:child_process");
15270
15760
 
15271
15761
  // src/commands/migrate.ts
15272
- var import_node_fs21 = require("node:fs");
15273
- var import_node_path15 = require("node:path");
15762
+ var import_node_fs23 = require("node:fs");
15763
+ var import_node_path16 = require("node:path");
15274
15764
  var import_node_child_process8 = require("node:child_process");
15275
15765
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
15276
15766
  function defaultNpmRemover(pkg) {
@@ -15306,12 +15796,12 @@ async function runMigration(opts = {}) {
15306
15796
  return { actions, migrated: actions.length > 0 };
15307
15797
  }
15308
15798
  function migrateProjectDir(root, actions) {
15309
- const gateDir = (0, import_node_path15.join)(root, ".gate");
15310
- const verityDir = (0, import_node_path15.join)(root, ".verity");
15311
- if ((0, import_node_fs21.existsSync)(gateDir) && !(0, import_node_fs21.existsSync)(verityDir)) {
15799
+ const gateDir = (0, import_node_path16.join)(root, ".gate");
15800
+ const verityDir = (0, import_node_path16.join)(root, ".verity");
15801
+ if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
15312
15802
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
15313
15803
  }
15314
- if ((0, import_node_fs21.existsSync)(gateDir) && (0, import_node_fs21.existsSync)(verityDir)) {
15804
+ if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
15315
15805
  return migrateProjectDirCarry(gateDir, verityDir, actions);
15316
15806
  }
15317
15807
  return false;
@@ -15332,13 +15822,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
15332
15822
  }
15333
15823
  }
15334
15824
  if (moved) {
15335
- if ((0, import_node_fs21.existsSync)(gateDir)) {
15825
+ if ((0, import_node_fs23.existsSync)(gateDir)) {
15336
15826
  const carried = carryLegacyContents(gateDir, verityDir);
15337
15827
  if (carried > 0) {
15338
15828
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
15339
15829
  }
15340
15830
  try {
15341
- (0, import_node_fs21.rmSync)(gateDir, { recursive: true, force: true });
15831
+ (0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
15342
15832
  } catch {
15343
15833
  }
15344
15834
  }
@@ -15354,18 +15844,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
15354
15844
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
15355
15845
  }
15356
15846
  try {
15357
- (0, import_node_fs21.rmSync)(gateDir, { recursive: true, force: true });
15847
+ (0, import_node_fs23.rmSync)(gateDir, { recursive: true, force: true });
15358
15848
  } catch {
15359
15849
  }
15360
15850
  return carried > 0;
15361
15851
  }
15362
15852
  function migrateGlobalCredentials(home, actions) {
15363
15853
  if (!home) return;
15364
- const gateCreds = (0, import_node_path15.join)(home, ".gate", "credentials");
15365
- const verityCreds = (0, import_node_path15.join)(home, ".verity", "credentials");
15366
- if (!(0, import_node_fs21.existsSync)(gateCreds)) return;
15367
- if (!(0, import_node_fs21.existsSync)(verityCreds)) {
15368
- (0, import_node_fs21.mkdirSync)((0, import_node_path15.join)(home, ".verity"), { recursive: true });
15854
+ const gateCreds = (0, import_node_path16.join)(home, ".gate", "credentials");
15855
+ const verityCreds = (0, import_node_path16.join)(home, ".verity", "credentials");
15856
+ if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
15857
+ if (!(0, import_node_fs23.existsSync)(verityCreds)) {
15858
+ (0, import_node_fs23.mkdirSync)((0, import_node_path16.join)(home, ".verity"), { recursive: true });
15369
15859
  moveFile(gateCreds, verityCreds);
15370
15860
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
15371
15861
  return;
@@ -15387,8 +15877,8 @@ async function migrateLegacyHooks(root, actions) {
15387
15877
  }
15388
15878
  }
15389
15879
  async function migrateClaudeMd(root, actions) {
15390
- const claudeMd = (0, import_node_path15.join)(root, "CLAUDE.md");
15391
- const hadLegacyBlock = (0, import_node_fs21.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
15880
+ const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
15881
+ const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
15392
15882
  if (!hadLegacyBlock) return;
15393
15883
  try {
15394
15884
  await ensureClaudeMdPointer(root);
@@ -15398,9 +15888,9 @@ async function migrateClaudeMd(root, actions) {
15398
15888
  }
15399
15889
  }
15400
15890
  function migrateStandardFile(root, actions) {
15401
- const gateMd = (0, import_node_path15.join)(root, "GATE.md");
15402
- const verityMd = (0, import_node_path15.join)(root, "VERITY.md");
15403
- if (!(0, import_node_fs21.existsSync)(gateMd) || (0, import_node_fs21.existsSync)(verityMd)) return;
15891
+ const gateMd = (0, import_node_path16.join)(root, "GATE.md");
15892
+ const verityMd = (0, import_node_path16.join)(root, "VERITY.md");
15893
+ if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
15404
15894
  let moved = false;
15405
15895
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
15406
15896
  try {
@@ -15412,7 +15902,7 @@ function migrateStandardFile(root, actions) {
15412
15902
  if (!moved) moveFile(gateMd, verityMd);
15413
15903
  const content = readFileSyncSafe(verityMd);
15414
15904
  const refreshed = content.split("GATE.md").join("VERITY.md");
15415
- if (refreshed !== content) (0, import_node_fs21.writeFileSync)(verityMd, refreshed);
15905
+ if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
15416
15906
  actions.push("Renamed GATE.md \u2192 VERITY.md");
15417
15907
  }
15418
15908
  function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
@@ -15446,14 +15936,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
15446
15936
  }
15447
15937
  if (toAppend.length > 0) {
15448
15938
  const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
15449
- (0, import_node_fs21.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
15939
+ (0, import_node_fs23.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
15450
15940
  }
15451
- (0, import_node_fs21.rmSync)(gateCreds, { force: true });
15941
+ (0, import_node_fs23.rmSync)(gateCreds, { force: true });
15452
15942
  return toAppend.length;
15453
15943
  }
15454
15944
  function readFileSyncSafe(path) {
15455
15945
  try {
15456
- return (0, import_node_fs21.readFileSync)(path, "utf-8");
15946
+ return (0, import_node_fs23.readFileSync)(path, "utf-8");
15457
15947
  } catch {
15458
15948
  return "";
15459
15949
  }
@@ -15468,35 +15958,35 @@ function hasStagedChanges(root) {
15468
15958
  }
15469
15959
  function moveDir(from, to) {
15470
15960
  try {
15471
- (0, import_node_fs21.renameSync)(from, to);
15961
+ (0, import_node_fs23.renameSync)(from, to);
15472
15962
  } catch (err) {
15473
15963
  if (err.code !== "EXDEV") throw err;
15474
- (0, import_node_fs21.cpSync)(from, to, { recursive: true });
15475
- (0, import_node_fs21.rmSync)(from, { recursive: true, force: true });
15964
+ (0, import_node_fs23.cpSync)(from, to, { recursive: true });
15965
+ (0, import_node_fs23.rmSync)(from, { recursive: true, force: true });
15476
15966
  }
15477
15967
  }
15478
15968
  function moveFile(from, to) {
15479
15969
  try {
15480
- (0, import_node_fs21.renameSync)(from, to);
15970
+ (0, import_node_fs23.renameSync)(from, to);
15481
15971
  } catch (err) {
15482
15972
  if (err.code !== "EXDEV") throw err;
15483
- (0, import_node_fs21.cpSync)(from, to);
15484
- (0, import_node_fs21.rmSync)(from, { force: true });
15973
+ (0, import_node_fs23.cpSync)(from, to);
15974
+ (0, import_node_fs23.rmSync)(from, { force: true });
15485
15975
  }
15486
15976
  }
15487
15977
  function carryLegacyContents(gateDir, verityDir) {
15488
15978
  let copied = 0;
15489
15979
  const walk = (relDir) => {
15490
- const srcDir = (0, import_node_path15.join)(gateDir, relDir);
15491
- for (const entry of (0, import_node_fs21.readdirSync)(srcDir)) {
15492
- const rel = relDir ? (0, import_node_path15.join)(relDir, entry) : entry;
15493
- const src = (0, import_node_path15.join)(gateDir, rel);
15494
- const dest = (0, import_node_path15.join)(verityDir, rel);
15495
- if ((0, import_node_fs21.statSync)(src).isDirectory()) {
15980
+ const srcDir = (0, import_node_path16.join)(gateDir, relDir);
15981
+ for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
15982
+ const rel = relDir ? (0, import_node_path16.join)(relDir, entry) : entry;
15983
+ const src = (0, import_node_path16.join)(gateDir, rel);
15984
+ const dest = (0, import_node_path16.join)(verityDir, rel);
15985
+ if ((0, import_node_fs23.statSync)(src).isDirectory()) {
15496
15986
  walk(rel);
15497
- } else if (!(0, import_node_fs21.existsSync)(dest)) {
15498
- (0, import_node_fs21.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
15499
- (0, import_node_fs21.cpSync)(src, dest);
15987
+ } else if (!(0, import_node_fs23.existsSync)(dest)) {
15988
+ (0, import_node_fs23.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
15989
+ (0, import_node_fs23.cpSync)(src, dest);
15500
15990
  copied++;
15501
15991
  }
15502
15992
  }
@@ -15505,22 +15995,22 @@ function carryLegacyContents(gateDir, verityDir) {
15505
15995
  return copied;
15506
15996
  }
15507
15997
  async function needsMigration(root = repoRoot()) {
15508
- const gateDir = (0, import_node_path15.join)(root, ".gate");
15509
- const verityDir = (0, import_node_path15.join)(root, ".verity");
15510
- if ((0, import_node_fs21.existsSync)(gateDir) && !(0, import_node_fs21.existsSync)(verityDir)) return true;
15511
- if ((0, import_node_fs21.existsSync)(gateDir) && (0, import_node_fs21.existsSync)(verityDir)) {
15512
- 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"))) {
15998
+ const gateDir = (0, import_node_path16.join)(root, ".gate");
15999
+ const verityDir = (0, import_node_path16.join)(root, ".verity");
16000
+ if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
16001
+ if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
16002
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "credentials"))) {
15513
16003
  return true;
15514
16004
  }
15515
- 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"))) {
16005
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(verityDir, "memory"))) {
15516
16006
  return true;
15517
16007
  }
15518
16008
  }
15519
- const claudeMd = (0, import_node_path15.join)(root, "CLAUDE.md");
15520
- if ((0, import_node_fs21.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
16009
+ const claudeMd = (0, import_node_path16.join)(root, "CLAUDE.md");
16010
+ if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
15521
16011
  return true;
15522
16012
  }
15523
- 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"))) {
16013
+ if ((0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path16.join)(root, "VERITY.md"))) {
15524
16014
  return true;
15525
16015
  }
15526
16016
  if (await hasLegacyHooksAt(root)) return true;
@@ -15548,15 +16038,15 @@ function registerMigrateCommand(program2) {
15548
16038
  // src/commands/init.ts
15549
16039
  function resolveDataDir() {
15550
16040
  const candidates = [
15551
- (0, import_node_path16.join)(__dirname, "..", "data"),
16041
+ (0, import_node_path17.join)(__dirname, "..", "data"),
15552
16042
  // installed: node_modules/@codacy/verity-cli/data
15553
- (0, import_node_path16.join)(__dirname, "..", "..", "data"),
16043
+ (0, import_node_path17.join)(__dirname, "..", "..", "data"),
15554
16044
  // edge case: nested resolution
15555
- (0, import_node_path16.join)(process.cwd(), "cli", "data")
16045
+ (0, import_node_path17.join)(process.cwd(), "cli", "data")
15556
16046
  // local dev: running from repo root
15557
16047
  ];
15558
16048
  for (const candidate of candidates) {
15559
- if ((0, import_node_fs22.existsSync)((0, import_node_path16.join)(candidate, "skills"))) {
16049
+ if ((0, import_node_fs24.existsSync)((0, import_node_path17.join)(candidate, "skills"))) {
15560
16050
  return candidate;
15561
16051
  }
15562
16052
  }
@@ -15572,7 +16062,7 @@ function registerInitCommand(program2) {
15572
16062
  program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
15573
16063
  const force = opts.force ?? false;
15574
16064
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
15575
- const isProject = projectMarkers.some((m) => (0, import_node_fs22.existsSync)(m));
16065
+ const isProject = projectMarkers.some((m) => (0, import_node_fs24.existsSync)(m));
15576
16066
  if (!isProject) {
15577
16067
  printError("No project detected in the current directory.");
15578
16068
  printInfo('Run "verity init" from your project root.');
@@ -15635,21 +16125,21 @@ function registerInitCommand(program2) {
15635
16125
  console.log("");
15636
16126
  printInfo("Installing skills...");
15637
16127
  const dataDir = resolveDataDir();
15638
- const skillsSource = (0, import_node_path16.join)(dataDir, "skills");
16128
+ const skillsSource = (0, import_node_path17.join)(dataDir, "skills");
15639
16129
  const skillsDest = ".claude/skills";
15640
16130
  const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
15641
16131
  let skillsInstalled = 0;
15642
16132
  for (const skill of skills) {
15643
- const src = (0, import_node_path16.join)(skillsSource, skill);
15644
- const dest = (0, import_node_path16.join)(skillsDest, skill);
15645
- if (!(0, import_node_fs22.existsSync)(src)) {
16133
+ const src = (0, import_node_path17.join)(skillsSource, skill);
16134
+ const dest = (0, import_node_path17.join)(skillsDest, skill);
16135
+ if (!(0, import_node_fs24.existsSync)(src)) {
15646
16136
  printWarn(` Skill data not found: ${skill}`);
15647
16137
  continue;
15648
16138
  }
15649
- if ((0, import_node_fs22.existsSync)(dest) && !force) {
15650
- const srcSkill = (0, import_node_path16.join)(src, "SKILL.md");
15651
- const destSkill = (0, import_node_path16.join)(dest, "SKILL.md");
15652
- if ((0, import_node_fs22.existsSync)(destSkill)) {
16139
+ if ((0, import_node_fs24.existsSync)(dest) && !force) {
16140
+ const srcSkill = (0, import_node_path17.join)(src, "SKILL.md");
16141
+ const destSkill = (0, import_node_path17.join)(dest, "SKILL.md");
16142
+ if ((0, import_node_fs24.existsSync)(destSkill)) {
15653
16143
  try {
15654
16144
  const srcContent = await (0, import_promises12.readFile)(srcSkill, "utf-8");
15655
16145
  const destContent = await (0, import_promises12.readFile)(destSkill, "utf-8");
@@ -15673,6 +16163,7 @@ function registerInitCommand(program2) {
15673
16163
  await writeSettings(hookResult.data);
15674
16164
  printInfo(" Stop hook: verity analyze \u2713");
15675
16165
  printInfo(" Intent hook: verity intent capture \u2713");
16166
+ printInfo(" Baseline hook: verity baseline capture \u2713");
15676
16167
  } else {
15677
16168
  printWarn(` ${hookResult.error}`);
15678
16169
  printInfo(' Run "verity hooks install --force" to overwrite.');
@@ -15685,7 +16176,7 @@ function registerInitCommand(program2) {
15685
16176
  } catch (err) {
15686
16177
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
15687
16178
  }
15688
- const globalVerityDir = (0, import_node_path16.join)(process.env.HOME ?? "", ".verity");
16179
+ const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
15689
16180
  await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
15690
16181
  console.log("");
15691
16182
  printInfo("Verity initialized!");
@@ -15708,8 +16199,8 @@ function registerInitCommand(program2) {
15708
16199
  }
15709
16200
 
15710
16201
  // src/commands/uninstall.ts
15711
- var import_node_fs23 = require("node:fs");
15712
- var import_node_path17 = require("node:path");
16202
+ var import_node_fs25 = require("node:fs");
16203
+ var import_node_path18 = require("node:path");
15713
16204
  var SKILL_NAMES = [
15714
16205
  "verity-setup",
15715
16206
  "verity-analyze",
@@ -15728,11 +16219,11 @@ function registerUninstallCommand(program2) {
15728
16219
  const actions = [];
15729
16220
  const skillsRoot = projectPath(".claude/skills");
15730
16221
  for (const name of SKILL_NAMES) {
15731
- const dir = (0, import_node_path17.join)(skillsRoot, name);
15732
- if ((0, import_node_fs23.existsSync)(dir)) {
16222
+ const dir = (0, import_node_path18.join)(skillsRoot, name);
16223
+ if ((0, import_node_fs25.existsSync)(dir)) {
15733
16224
  actions.push({
15734
16225
  label: `Remove .claude/skills/${name}/`,
15735
- apply: () => (0, import_node_fs23.rmSync)(dir, { recursive: true, force: true })
16226
+ apply: () => (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true })
15736
16227
  });
15737
16228
  }
15738
16229
  }
@@ -15746,24 +16237,24 @@ function registerUninstallCommand(program2) {
15746
16237
  });
15747
16238
  }
15748
16239
  const verityDir = projectPath(VERITY_DIR);
15749
- if ((0, import_node_fs23.existsSync)(verityDir)) {
16240
+ if ((0, import_node_fs25.existsSync)(verityDir)) {
15750
16241
  actions.push({
15751
16242
  label: `Remove ${VERITY_DIR}/`,
15752
- apply: () => (0, import_node_fs23.rmSync)(verityDir, { recursive: true, force: true })
16243
+ apply: () => (0, import_node_fs25.rmSync)(verityDir, { recursive: true, force: true })
15753
16244
  });
15754
16245
  }
15755
16246
  if (!keepVerityMd) {
15756
16247
  const verityMd = projectPath(VERITY_MD_FILE);
15757
- if ((0, import_node_fs23.existsSync)(verityMd)) {
16248
+ if ((0, import_node_fs25.existsSync)(verityMd)) {
15758
16249
  actions.push({
15759
16250
  label: `Remove ${VERITY_MD_FILE}`,
15760
- apply: () => (0, import_node_fs23.rmSync)(verityMd, { force: true })
16251
+ apply: () => (0, import_node_fs25.rmSync)(verityMd, { force: true })
15761
16252
  });
15762
16253
  }
15763
16254
  }
15764
16255
  const cleanupEmptyDir = (path) => {
15765
- if ((0, import_node_fs23.existsSync)(path) && (0, import_node_fs23.statSync)(path).isDirectory() && (0, import_node_fs23.readdirSync)(path).length === 0) {
15766
- (0, import_node_fs23.rmdirSync)(path);
16256
+ if ((0, import_node_fs25.existsSync)(path) && (0, import_node_fs25.statSync)(path).isDirectory() && (0, import_node_fs25.readdirSync)(path).length === 0) {
16257
+ (0, import_node_fs25.rmdirSync)(path);
15767
16258
  }
15768
16259
  };
15769
16260
  actions.push({
@@ -15774,11 +16265,11 @@ function registerUninstallCommand(program2) {
15774
16265
  }
15775
16266
  });
15776
16267
  const home = process.env.HOME ?? "";
15777
- const globalVerityDir = (0, import_node_path17.join)(home, ".verity");
15778
- if (purgeGlobal && (0, import_node_fs23.existsSync)(globalVerityDir)) {
16268
+ const globalVerityDir = (0, import_node_path18.join)(home, ".verity");
16269
+ if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
15779
16270
  actions.push({
15780
16271
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
15781
- apply: () => (0, import_node_fs23.rmSync)(globalVerityDir, { recursive: true, force: true })
16272
+ apply: () => (0, import_node_fs25.rmSync)(globalVerityDir, { recursive: true, force: true })
15782
16273
  });
15783
16274
  }
15784
16275
  if (actions.length === 0) {
@@ -15972,8 +16463,8 @@ function registerTaskCommands(program2) {
15972
16463
  }
15973
16464
 
15974
16465
  // src/commands/reset.ts
15975
- var import_node_fs24 = require("node:fs");
15976
- var import_node_path18 = require("node:path");
16466
+ var import_node_fs26 = require("node:fs");
16467
+ var import_node_path19 = require("node:path");
15977
16468
  function registerResetCommand(program2) {
15978
16469
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
15979
16470
  const globals = program2.opts();
@@ -16010,11 +16501,11 @@ function registerResetCommand(program2) {
16010
16501
  }
16011
16502
  const cacheDir = projectPath(CACHE_DIR);
16012
16503
  let purged = 0;
16013
- if ((0, import_node_fs24.existsSync)(cacheDir)) {
16014
- for (const entry of (0, import_node_fs24.readdirSync)(cacheDir)) {
16504
+ if ((0, import_node_fs26.existsSync)(cacheDir)) {
16505
+ for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
16015
16506
  if (entry.startsWith("pending-")) {
16016
16507
  try {
16017
- (0, import_node_fs24.unlinkSync)((0, import_node_path18.join)(cacheDir, entry));
16508
+ (0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(cacheDir, entry));
16018
16509
  purged++;
16019
16510
  } catch {
16020
16511
  }
@@ -16029,19 +16520,19 @@ function registerResetCommand(program2) {
16029
16520
  projectPath(`${VERITY_DIR}/.last-analysis`)
16030
16521
  ];
16031
16522
  for (const file of filesToClear) {
16032
- if ((0, import_node_fs24.existsSync)(file)) {
16523
+ if ((0, import_node_fs26.existsSync)(file)) {
16033
16524
  try {
16034
- (0, import_node_fs24.writeFileSync)(file, "");
16525
+ (0, import_node_fs26.writeFileSync)(file, "");
16035
16526
  } catch {
16036
16527
  }
16037
16528
  }
16038
16529
  }
16039
16530
  if (opts.all) {
16040
16531
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
16041
- if ((0, import_node_fs24.existsSync)(logsDir)) {
16042
- for (const entry of (0, import_node_fs24.readdirSync)(logsDir)) {
16532
+ if ((0, import_node_fs26.existsSync)(logsDir)) {
16533
+ for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
16043
16534
  try {
16044
- (0, import_node_fs24.unlinkSync)((0, import_node_path18.join)(logsDir, entry));
16535
+ (0, import_node_fs26.unlinkSync)((0, import_node_path19.join)(logsDir, entry));
16045
16536
  } catch {
16046
16537
  }
16047
16538
  }
@@ -16300,7 +16791,7 @@ function registerRunCommand(program2) {
16300
16791
 
16301
16792
  // src/lib/telemetry.ts
16302
16793
  var import_promises13 = require("node:fs/promises");
16303
- var import_node_path19 = require("node:path");
16794
+ var import_node_path20 = require("node:path");
16304
16795
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
16305
16796
  var GITIGNORE_FILE = ".gitignore";
16306
16797
  var GITIGNORE_ENTRY = ".claude/settings.local.json";
@@ -16338,7 +16829,7 @@ async function readSettingsLocal() {
16338
16829
  }
16339
16830
  async function writeSettingsLocal(settings) {
16340
16831
  const file = projectPath(SETTINGS_LOCAL_FILE2);
16341
- await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(file), { recursive: true });
16832
+ await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(file), { recursive: true });
16342
16833
  await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
16343
16834
  }
16344
16835
  async function ensureGitignore() {
@@ -16434,7 +16925,7 @@ function registerTelemetryCommands(program2) {
16434
16925
  }
16435
16926
 
16436
16927
  // src/cli.ts
16437
- program.name("verity").description("CLI for Verity quality gate service").version("0.24.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16928
+ program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.e3da48a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16438
16929
  registerAuthCommands(program);
16439
16930
  registerHooksCommands(program);
16440
16931
  registerIntentCommands(program);
@@ -16443,6 +16934,7 @@ registerConfigCommands(program);
16443
16934
  registerStatusCommand(program);
16444
16935
  registerFeedbackCommand(program);
16445
16936
  registerAnalyzeCommand(program);
16937
+ registerBaselineCommands(program);
16446
16938
  registerReviewCommand(program);
16447
16939
  registerGuardCommand(program);
16448
16940
  registerInitCommand(program);