@codacy/verity-cli 0.31.1-experimental.be74f71 → 0.31.2

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
@@ -10395,6 +10395,7 @@ var MAX_DELTA_BYTES = 194560;
10395
10395
  var MAX_FILES = 40;
10396
10396
  var MAX_FILE_BYTES = 51200;
10397
10397
  var DEBOUNCE_SECONDS = 30;
10398
+ var MAX_ITERATIONS = 2;
10398
10399
  var MAX_SPEC_FILES = 6;
10399
10400
  var MAX_SPEC_FILE_BYTES = 512e3;
10400
10401
  var MAX_TOTAL_SPEC_BYTES = 512e3;
@@ -10404,6 +10405,8 @@ var MAX_INTENT_CHARS = 2e3;
10404
10405
  var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
10405
10406
  var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
10406
10407
  var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
10408
+ var PLUGIN_MARKER_FILE = `${VERITY_DIR}/.plugin-active`;
10409
+ var PROJECT_CONFIG_FILE = `${VERITY_DIR}/config.json`;
10407
10410
  var CONVERSATION_MAX_ENTRIES = 10;
10408
10411
  var CONVERSATION_WINDOW_MINUTES = 15;
10409
10412
  var MAX_FINDINGS = 25;
@@ -10506,7 +10509,7 @@ var SECURITY_PATTERNS = [
10506
10509
  /Dockerfile/
10507
10510
  ];
10508
10511
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10509
- var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10512
+ var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10510
10513
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10511
10514
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10512
10515
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -12700,9 +12703,155 @@ async function applyMomentSelection(moments) {
12700
12703
  return settings;
12701
12704
  }
12702
12705
 
12706
+ // src/lib/project-config.ts
12707
+ var import_node_fs4 = require("node:fs");
12708
+ var DEFAULTS = { git_moments: [] };
12709
+ function isMoment(value) {
12710
+ return value === "commit" || value === "push";
12711
+ }
12712
+ function parseMoments(raw) {
12713
+ return [...new Set(raw.split(",").map((s) => s.trim()).filter(isMoment))];
12714
+ }
12715
+ function readProjectConfig() {
12716
+ try {
12717
+ if (!(0, import_node_fs4.existsSync)(PROJECT_CONFIG_FILE)) return DEFAULTS;
12718
+ const raw = JSON.parse((0, import_node_fs4.readFileSync)(PROJECT_CONFIG_FILE, "utf-8"));
12719
+ const moments = Array.isArray(raw.git_moments) ? raw.git_moments.filter(isMoment) : [];
12720
+ return { git_moments: [...new Set(moments)] };
12721
+ } catch {
12722
+ return DEFAULTS;
12723
+ }
12724
+ }
12725
+ function writeProjectConfig(patch) {
12726
+ const next = { ...readProjectConfig(), ...patch };
12727
+ (0, import_node_fs4.mkdirSync)(VERITY_DIR, { recursive: true });
12728
+ (0, import_node_fs4.writeFileSync)(PROJECT_CONFIG_FILE, JSON.stringify(next, null, 2) + "\n");
12729
+ return next;
12730
+ }
12731
+ function resolveGuardMoments(explicit) {
12732
+ if (explicit !== void 0) return parseMoments(explicit);
12733
+ return readProjectConfig().git_moments;
12734
+ }
12735
+
12736
+ // src/lib/plugin-ownership.ts
12737
+ var import_node_fs6 = require("node:fs");
12738
+
12739
+ // src/lib/stderr-log.ts
12740
+ var import_node_fs5 = require("node:fs");
12741
+ var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
12742
+ var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
12743
+ function scrub(s) {
12744
+ return s.replace(TOKEN_RE2, "verity_***REDACTED***").replace(ANSI_RE, "");
12745
+ }
12746
+ var installed = false;
12747
+ var wroteBanner = false;
12748
+ var banner = "";
12749
+ function append(text) {
12750
+ try {
12751
+ const dir = projectPath(DEBUG_LOG_DIR);
12752
+ const file = projectPath(STDERR_LOG_FILE);
12753
+ (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
12754
+ rotateIfNeeded(file);
12755
+ (0, import_node_fs5.appendFileSync)(file, text);
12756
+ } catch {
12757
+ }
12758
+ }
12759
+ function ensureBanner() {
12760
+ if (wroteBanner) return;
12761
+ wroteBanner = true;
12762
+ append(banner);
12763
+ }
12764
+ function installStderrLog(cmd, argv, version) {
12765
+ if (installed || !isDebugEnabled()) return;
12766
+ installed = true;
12767
+ banner = `
12768
+ \u2501\u2501 verity ${cmd} \xB7 ${(/* @__PURE__ */ new Date()).toISOString()} \xB7 pid ${process.pid}
12769
+ v${version} \xB7 ${process.cwd()}
12770
+ argv: ${scrub(argv.join(" "))}
12771
+ `;
12772
+ const original = process.stderr.write.bind(process.stderr);
12773
+ const tee = (...args) => {
12774
+ const result = original(...args);
12775
+ try {
12776
+ const chunk = args[0];
12777
+ const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
12778
+ ensureBanner();
12779
+ append(scrub(text));
12780
+ } catch {
12781
+ }
12782
+ return result;
12783
+ };
12784
+ process.stderr.write = tee;
12785
+ }
12786
+ function logToFileOnly(text) {
12787
+ if (!installed) return;
12788
+ ensureBanner();
12789
+ append(text.endsWith("\n") ? text : `${text}
12790
+ `);
12791
+ }
12792
+
12793
+ // src/lib/plugin-ownership.ts
12794
+ var MARKER_TTL_SECONDS = 24 * 60 * 60;
12795
+ function isPluginInvocation() {
12796
+ return !!process.env.VERITY_PLUGIN_ROOT;
12797
+ }
12798
+ function readMarker() {
12799
+ try {
12800
+ if (!(0, import_node_fs6.existsSync)(PLUGIN_MARKER_FILE)) return null;
12801
+ const raw = JSON.parse((0, import_node_fs6.readFileSync)(PLUGIN_MARKER_FILE, "utf-8"));
12802
+ const pluginRoot = typeof raw.plugin_root === "string" ? raw.plugin_root : "";
12803
+ if (!pluginRoot) return null;
12804
+ return {
12805
+ session_id: typeof raw.session_id === "string" ? raw.session_id : null,
12806
+ plugin_root: pluginRoot,
12807
+ version: typeof raw.version === "string" ? raw.version : null,
12808
+ ts: typeof raw.ts === "number" ? raw.ts : 0
12809
+ };
12810
+ } catch {
12811
+ return null;
12812
+ }
12813
+ }
12814
+ function recordPluginOwnership(sessionId) {
12815
+ const pluginRoot = process.env.VERITY_PLUGIN_ROOT;
12816
+ if (!pluginRoot) return;
12817
+ try {
12818
+ (0, import_node_fs6.mkdirSync)(VERITY_DIR, { recursive: true });
12819
+ const marker = {
12820
+ session_id: sessionId,
12821
+ plugin_root: pluginRoot,
12822
+ version: process.env.VERITY_PLUGIN_VERSION || null,
12823
+ ts: Math.floor(Date.now() / 1e3)
12824
+ };
12825
+ (0, import_node_fs6.writeFileSync)(PLUGIN_MARKER_FILE, JSON.stringify(marker));
12826
+ } catch {
12827
+ }
12828
+ }
12829
+ function shouldDeferToPlugin(sessionId) {
12830
+ if (isPluginInvocation()) {
12831
+ recordPluginOwnership(sessionId);
12832
+ return false;
12833
+ }
12834
+ const marker = readMarker();
12835
+ if (!marker) return false;
12836
+ if (!(0, import_node_fs6.existsSync)(marker.plugin_root)) return false;
12837
+ if (sessionId && marker.session_id) return sessionId === marker.session_id;
12838
+ return Math.floor(Date.now() / 1e3) - marker.ts < MARKER_TTL_SECONDS;
12839
+ }
12840
+ function pluginActiveHere() {
12841
+ const marker = readMarker();
12842
+ return !!marker && (0, import_node_fs6.existsSync)(marker.plugin_root);
12843
+ }
12844
+ function deferredToPlugin(command, sessionId) {
12845
+ if (!shouldDeferToPlugin(sessionId)) return false;
12846
+ logToFileOnly(
12847
+ `${command}: the Verity Claude Code plugin owns this session's hooks \u2014 standing down so the turn is not gated twice. Remove the duplicate settings.json hooks with \`verity init --plugin-mode\`.`
12848
+ );
12849
+ return true;
12850
+ }
12851
+
12703
12852
  // src/commands/hooks.ts
12704
12853
  var ALL_MOMENTS = ["stop", "pre-commit", "pre-push"];
12705
- function parseMoments(raw) {
12854
+ function parseMoments2(raw) {
12706
12855
  const seen = /* @__PURE__ */ new Set();
12707
12856
  for (const part of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
12708
12857
  if (ALL_MOMENTS.includes(part)) seen.add(part);
@@ -12714,7 +12863,22 @@ function registerHooksCommands(program2) {
12714
12863
  hooks.command("install").description("Install Verity hooks into Claude Code settings").option("--force", "Overwrite existing Verity hooks").option("--moments <list>", "Reconcile to exactly these moments: stop,pre-commit,pre-push").action(async (opts) => {
12715
12864
  const force = opts.force ?? false;
12716
12865
  if (opts.moments != null) {
12717
- const moments = parseMoments(opts.moments);
12866
+ const moments = parseMoments2(opts.moments);
12867
+ const gitMoments = [
12868
+ ...moments.includes("pre-commit") ? ["commit"] : [],
12869
+ ...moments.includes("pre-push") ? ["push"] : []
12870
+ ];
12871
+ writeProjectConfig({ git_moments: gitMoments });
12872
+ if (pluginActiveHere()) {
12873
+ printInfo("The Verity plugin wires the hooks; recorded your selection in .verity/config.json:");
12874
+ printInfo(` Stop (analysis on every turn): ${moments.includes("stop") ? "on" : "off \u2014 the plugin still wires it; see below"}`);
12875
+ printInfo(` Pre-commit gate: ${gitMoments.includes("commit") ? "on" : "off"}`);
12876
+ printInfo(` Pre-push/PR gate: ${gitMoments.includes("push") ? "on" : "off"}`);
12877
+ if (!moments.includes("stop")) {
12878
+ printWarn(" Turning the Stop review off is not yet supported under the plugin \u2014 it stays on.");
12879
+ }
12880
+ return;
12881
+ }
12718
12882
  await applyMomentSelection(moments);
12719
12883
  const status = await checkAllVerityHooks();
12720
12884
  printInfo("Verity hooks reconciled in .claude/settings.json:");
@@ -12761,6 +12925,24 @@ function registerHooksCommands(program2) {
12761
12925
  });
12762
12926
  hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
12763
12927
  const status = await checkAllVerityHooks();
12928
+ if (pluginActiveHere()) {
12929
+ const moments = readProjectConfig().git_moments;
12930
+ printInfo("Wired by the Verity Claude Code plugin (not .claude/settings.json):");
12931
+ printInfo(" Stop hook (verity analyze): installed");
12932
+ printInfo(" Intent hook (verity intent capture): installed");
12933
+ printInfo(" Baseline hook (verity baseline capture): installed");
12934
+ printInfo(
12935
+ ` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]` : "wired but gating nothing"}`
12936
+ );
12937
+ if (!moments.length) {
12938
+ printInfo(' Enable it with "verity config git-moments commit,push".');
12939
+ }
12940
+ if (status.stop || status.intent || status.baseline || status.guard) {
12941
+ printWarn(" Duplicate hooks also exist in .claude/settings.json. They stand down at run time,");
12942
+ printWarn(' but remove them with "verity init --plugin-mode" so the wiring says what it does.');
12943
+ }
12944
+ return;
12945
+ }
12764
12946
  printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
12765
12947
  printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
12766
12948
  printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
@@ -12786,7 +12968,7 @@ var import_node_crypto8 = require("node:crypto");
12786
12968
 
12787
12969
  // src/lib/conversation-buffer.ts
12788
12970
  var import_promises5 = require("node:fs/promises");
12789
- var import_node_fs4 = require("node:fs");
12971
+ var import_node_fs7 = require("node:fs");
12790
12972
  var import_node_child_process5 = require("node:child_process");
12791
12973
  var import_node_crypto = require("node:crypto");
12792
12974
  function stripImageReferences(text) {
@@ -12822,7 +13004,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
12822
13004
  }
12823
13005
  async function readAndClearConversationBuffer(currentSessionId) {
12824
13006
  try {
12825
- if ((0, import_node_fs4.existsSync)(CONVERSATION_BUFFER_FILE)) {
13007
+ if ((0, import_node_fs7.existsSync)(CONVERSATION_BUFFER_FILE)) {
12826
13008
  const entries = await readBufferEntries();
12827
13009
  let mine = entries;
12828
13010
  let others = [];
@@ -12846,7 +13028,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
12846
13028
  };
12847
13029
  }
12848
13030
  }
12849
- if ((0, import_node_fs4.existsSync)(INTENT_FILE)) {
13031
+ if ((0, import_node_fs7.existsSync)(INTENT_FILE)) {
12850
13032
  try {
12851
13033
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
12852
13034
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -12903,7 +13085,7 @@ function getRecentCommitMessages() {
12903
13085
 
12904
13086
  // src/lib/context-identity.ts
12905
13087
  var import_node_crypto2 = require("node:crypto");
12906
- var import_node_fs5 = require("node:fs");
13088
+ var import_node_fs8 = require("node:fs");
12907
13089
  var import_node_os2 = require("node:os");
12908
13090
  var import_node_path6 = require("node:path");
12909
13091
  var SHARED_SENTINELS = /* @__PURE__ */ new Set([
@@ -12943,7 +13125,7 @@ function contextIdentity(input) {
12943
13125
  if (rawTree && !isSharedSentinel(rawTree)) {
12944
13126
  let resolved = rawTree;
12945
13127
  try {
12946
- resolved = import_node_fs5.realpathSync.native(rawTree);
13128
+ resolved = import_node_fs8.realpathSync.native(rawTree);
12947
13129
  } catch {
12948
13130
  }
12949
13131
  treeKey = (0, import_node_crypto2.createHash)("sha256").update(resolved).digest("hex").slice(0, 12);
@@ -12980,7 +13162,7 @@ function sessionScopeKey(token, sessionId) {
12980
13162
 
12981
13163
  // src/lib/task-context-buffer.ts
12982
13164
  var import_promises6 = require("node:fs/promises");
12983
- var import_node_fs6 = require("node:fs");
13165
+ var import_node_fs9 = require("node:fs");
12984
13166
  var import_node_path7 = require("node:path");
12985
13167
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
12986
13168
  var MAX_BUFFER_BYTES = 500 * 1024;
@@ -13020,7 +13202,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
13020
13202
  }
13021
13203
  async function readTaskContextBuffer(taskId) {
13022
13204
  const filePath = bufferPath(taskId);
13023
- if (!(0, import_node_fs6.existsSync)(filePath)) return null;
13205
+ if (!(0, import_node_fs9.existsSync)(filePath)) return null;
13024
13206
  try {
13025
13207
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
13026
13208
  if (!content.trim()) return null;
@@ -13054,7 +13236,7 @@ async function readTaskContextBuffer(taskId) {
13054
13236
  }
13055
13237
  async function cleanupTaskContextBuffers() {
13056
13238
  try {
13057
- if (!(0, import_node_fs6.existsSync)(TASK_CONTEXT_DIR)) return;
13239
+ if (!(0, import_node_fs9.existsSync)(TASK_CONTEXT_DIR)) return;
13058
13240
  const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
13059
13241
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
13060
13242
  for (const file of files) {
@@ -13079,7 +13261,7 @@ async function appendEntry(taskId, entry) {
13079
13261
  try {
13080
13262
  await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
13081
13263
  const filePath = bufferPath(taskId);
13082
- if ((0, import_node_fs6.existsSync)(filePath)) {
13264
+ if ((0, import_node_fs9.existsSync)(filePath)) {
13083
13265
  const stats = await (0, import_promises6.stat)(filePath);
13084
13266
  if (stats.size >= MAX_BUFFER_BYTES) {
13085
13267
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
@@ -13090,7 +13272,7 @@ async function appendEntry(taskId, entry) {
13090
13272
  }
13091
13273
  }
13092
13274
  const line = JSON.stringify(entry) + "\n";
13093
- const existing = (0, import_node_fs6.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
13275
+ const existing = (0, import_node_fs9.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
13094
13276
  await (0, import_promises6.writeFile)(filePath, existing + line);
13095
13277
  } catch {
13096
13278
  }
@@ -13098,7 +13280,7 @@ async function appendEntry(taskId, entry) {
13098
13280
 
13099
13281
  // src/lib/memory-retrieval.ts
13100
13282
  var import_promises7 = require("node:fs/promises");
13101
- var import_node_fs7 = require("node:fs");
13283
+ var import_node_fs10 = require("node:fs");
13102
13284
  var import_node_path8 = require("node:path");
13103
13285
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
13104
13286
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
@@ -13192,13 +13374,13 @@ function parseFrontmatter(content) {
13192
13374
  return { fm, body: match[2].trim() };
13193
13375
  }
13194
13376
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
13195
- if (!(0, import_node_fs7.existsSync)(memoryDir())) return null;
13377
+ if (!(0, import_node_fs10.existsSync)(memoryDir())) return null;
13196
13378
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
13197
13379
  const promptTokens = tokenize(promptText);
13198
13380
  const nodes = [];
13199
13381
  for (const domain of DOMAINS) {
13200
13382
  const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
13201
- if (!(0, import_node_fs7.existsSync)(domainDir)) continue;
13383
+ if (!(0, import_node_fs10.existsSync)(domainDir)) continue;
13202
13384
  try {
13203
13385
  const files = await (0, import_promises7.readdir)(domainDir);
13204
13386
  for (const file of files) {
@@ -13263,12 +13445,12 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
13263
13445
 
13264
13446
  // src/lib/memory-sync.ts
13265
13447
  var import_promises8 = require("node:fs/promises");
13266
- var import_node_fs9 = require("node:fs");
13448
+ var import_node_fs12 = require("node:fs");
13267
13449
  var import_node_path10 = require("node:path");
13268
13450
  var import_node_crypto3 = require("node:crypto");
13269
13451
 
13270
13452
  // src/lib/safe-path.ts
13271
- var import_node_fs8 = require("node:fs");
13453
+ var import_node_fs11 = require("node:fs");
13272
13454
  var import_node_path9 = require("node:path");
13273
13455
  function resolveInside(baseDir, candidate) {
13274
13456
  if (typeof candidate !== "string" || candidate.length === 0) return null;
@@ -13278,16 +13460,16 @@ function resolveInside(baseDir, candidate) {
13278
13460
  const baseSep = baseAbs.endsWith(import_node_path9.sep) ? baseAbs : baseAbs + import_node_path9.sep;
13279
13461
  if (full !== baseAbs && !full.startsWith(baseSep)) return null;
13280
13462
  try {
13281
- if ((0, import_node_fs8.existsSync)(baseAbs)) {
13282
- const realBase = (0, import_node_fs8.realpathSync)(baseAbs);
13463
+ if ((0, import_node_fs11.existsSync)(baseAbs)) {
13464
+ const realBase = (0, import_node_fs11.realpathSync)(baseAbs);
13283
13465
  const realBaseSep = realBase.endsWith(import_node_path9.sep) ? realBase : realBase + import_node_path9.sep;
13284
13466
  let probe = full;
13285
- while (!(0, import_node_fs8.existsSync)(probe)) {
13467
+ while (!(0, import_node_fs11.existsSync)(probe)) {
13286
13468
  const parent = (0, import_node_path9.dirname)(probe);
13287
13469
  if (parent === probe) break;
13288
13470
  probe = parent;
13289
13471
  }
13290
- const realProbe = (0, import_node_fs8.realpathSync)(probe);
13472
+ const realProbe = (0, import_node_fs11.realpathSync)(probe);
13291
13473
  if (realProbe !== realBase && !realProbe.startsWith(realBaseSep)) return null;
13292
13474
  }
13293
13475
  } catch {
@@ -13366,24 +13548,24 @@ async function ensureMemoryDir() {
13366
13548
  for (const domain of DOMAINS2) {
13367
13549
  await (0, import_promises8.mkdir)((0, import_node_path10.join)(memoryDir2(), domain), { recursive: true });
13368
13550
  }
13369
- if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"))) {
13551
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"))) {
13370
13552
  await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
13371
13553
  }
13372
- if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "index.md"))) {
13554
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "index.md"))) {
13373
13555
  await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
13374
13556
  }
13375
- if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md"))) {
13557
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md"))) {
13376
13558
  await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
13377
13559
  }
13378
13560
  }
13379
13561
  async function buildManifest() {
13380
- if (!(0, import_node_fs9.existsSync)(memoryDir2())) {
13562
+ if (!(0, import_node_fs12.existsSync)(memoryDir2())) {
13381
13563
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
13382
13564
  }
13383
13565
  const nodes = [];
13384
13566
  for (const domain of DOMAINS2) {
13385
13567
  const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
13386
- if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
13568
+ if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13387
13569
  try {
13388
13570
  const files = await (0, import_promises8.readdir)(domainDir);
13389
13571
  for (const file of files) {
@@ -13419,10 +13601,10 @@ function hashContent(content) {
13419
13601
  }
13420
13602
  async function readOnDiskNodes() {
13421
13603
  const out = /* @__PURE__ */ new Map();
13422
- if (!(0, import_node_fs9.existsSync)(memoryDir2())) return out;
13604
+ if (!(0, import_node_fs12.existsSync)(memoryDir2())) return out;
13423
13605
  for (const domain of DOMAINS2) {
13424
13606
  const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
13425
- if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
13607
+ if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13426
13608
  try {
13427
13609
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
13428
13610
  if (!file.endsWith(".md")) continue;
@@ -13474,7 +13656,7 @@ async function computeEditedNodeUploads() {
13474
13656
  for (const [path, prevHash] of prev) {
13475
13657
  if (prevHash == null) continue;
13476
13658
  const full = (0, import_node_path10.join)(memoryDir2(), path);
13477
- if (!(0, import_node_fs9.existsSync)(full)) continue;
13659
+ if (!(0, import_node_fs12.existsSync)(full)) continue;
13478
13660
  let content;
13479
13661
  try {
13480
13662
  content = await (0, import_promises8.readFile)(full, "utf-8");
@@ -13510,7 +13692,7 @@ async function applyMemoryWrites(writes, opts = {}) {
13510
13692
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
13511
13693
  for (const n of notes) logLines.push(` - ${n}`);
13512
13694
  try {
13513
- const existing = (0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
13695
+ const existing = (0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
13514
13696
  await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
13515
13697
  } catch {
13516
13698
  }
@@ -13531,7 +13713,7 @@ async function applyOneWrite(write, treePaths) {
13531
13713
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
13532
13714
  }
13533
13715
  }
13534
- if ((0, import_node_fs9.existsSync)(fullPath)) {
13716
+ if ((0, import_node_fs12.existsSync)(fullPath)) {
13535
13717
  let existing = "";
13536
13718
  try {
13537
13719
  existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
@@ -13585,7 +13767,7 @@ async function regenerateIndex() {
13585
13767
  let totalNodes = 0;
13586
13768
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
13587
13769
  const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
13588
- if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
13770
+ if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13589
13771
  try {
13590
13772
  const files = await (0, import_promises8.readdir)(domainDir);
13591
13773
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -13855,7 +14037,7 @@ function hasLegacyMemoryBlock(text) {
13855
14037
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
13856
14038
  const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
13857
14039
  let existing = "";
13858
- if ((0, import_node_fs9.existsSync)(claudeMdPath)) {
14040
+ if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
13859
14041
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
13860
14042
  }
13861
14043
  let startTag = CLAUDE_MD_START;
@@ -13991,7 +14173,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
13991
14173
  `;
13992
14174
 
13993
14175
  // src/lib/dossier-session.ts
13994
- var import_node_fs14 = require("node:fs");
14176
+ var import_node_fs17 = require("node:fs");
13995
14177
  var import_node_crypto7 = require("node:crypto");
13996
14178
  var import_node_path13 = require("node:path");
13997
14179
 
@@ -14325,7 +14507,7 @@ function statementAnchorKey(file, patternId) {
14325
14507
 
14326
14508
  // src/lib/dossier/log.ts
14327
14509
  var import_node_crypto4 = require("node:crypto");
14328
- var import_node_fs10 = require("node:fs");
14510
+ var import_node_fs13 = require("node:fs");
14329
14511
  var import_node_path11 = require("node:path");
14330
14512
  var CRC_TABLE = (() => {
14331
14513
  const t = new Int32Array(256);
@@ -14345,7 +14527,7 @@ function crc32(s) {
14345
14527
  function openDossier(identity) {
14346
14528
  try {
14347
14529
  const dir = dossierDir(identity);
14348
- (0, import_node_fs10.mkdirSync)(dir, { recursive: true, mode: 448 });
14530
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true, mode: 448 });
14349
14531
  return {
14350
14532
  dir,
14351
14533
  identity,
@@ -14413,7 +14595,7 @@ function appendEvent(d, ev) {
14413
14595
  at: ev.at ?? (/* @__PURE__ */ new Date()).toISOString(),
14414
14596
  ...ev
14415
14597
  });
14416
- (0, import_node_fs10.appendFileSync)(d.eventsPath, line, { mode: 384 });
14598
+ (0, import_node_fs13.appendFileSync)(d.eventsPath, line, { mode: 384 });
14417
14599
  return true;
14418
14600
  } catch {
14419
14601
  return false;
@@ -14421,14 +14603,14 @@ function appendEvent(d, ev) {
14421
14603
  }
14422
14604
  function rotateIfNeeded2(d) {
14423
14605
  try {
14424
- if (!(0, import_node_fs10.existsSync)(d.eventsPath)) return;
14425
- if ((0, import_node_fs10.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
14426
- (0, import_node_fs10.mkdirSync)(d.rotatedDir, { recursive: true, mode: 448 });
14427
- (0, import_node_fs10.renameSync)(d.eventsPath, (0, import_node_path11.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
14428
- const kept = (0, import_node_fs10.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14606
+ if (!(0, import_node_fs13.existsSync)(d.eventsPath)) return;
14607
+ if ((0, import_node_fs13.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
14608
+ (0, import_node_fs13.mkdirSync)(d.rotatedDir, { recursive: true, mode: 448 });
14609
+ (0, import_node_fs13.renameSync)(d.eventsPath, (0, import_node_path11.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
14610
+ const kept = (0, import_node_fs13.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14429
14611
  for (const stale of kept.slice(0, Math.max(0, kept.length - ROTATE_KEEP))) {
14430
14612
  try {
14431
- (0, import_node_fs10.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
14613
+ (0, import_node_fs13.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
14432
14614
  } catch {
14433
14615
  }
14434
14616
  }
@@ -14438,7 +14620,7 @@ function rotateIfNeeded2(d) {
14438
14620
 
14439
14621
  // src/lib/dossier/fold-dossier.ts
14440
14622
  var import_node_crypto5 = require("node:crypto");
14441
- var import_node_fs11 = require("node:fs");
14623
+ var import_node_fs14 = require("node:fs");
14442
14624
  var import_node_path12 = require("node:path");
14443
14625
  var EMPTY_CAPABILITIES = () => ({
14444
14626
  human_reachable: { value: "unknown", tier: "unknown" },
@@ -14490,12 +14672,12 @@ function foldDossier(d, opts = {}) {
14490
14672
  }
14491
14673
  };
14492
14674
  try {
14493
- if ((0, import_node_fs11.existsSync)(d.rotatedDir)) {
14494
- const files = (0, import_node_fs11.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14675
+ if ((0, import_node_fs14.existsSync)(d.rotatedDir)) {
14676
+ const files = (0, import_node_fs14.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14495
14677
  state.meta.rotations = files.length;
14496
14678
  for (const f of files) {
14497
14679
  try {
14498
- ingest((0, import_node_fs11.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
14680
+ ingest((0, import_node_fs14.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
14499
14681
  } catch {
14500
14682
  state.meta.dropped_lines++;
14501
14683
  }
@@ -14504,9 +14686,9 @@ function foldDossier(d, opts = {}) {
14504
14686
  } catch {
14505
14687
  }
14506
14688
  try {
14507
- if ((0, import_node_fs11.existsSync)(d.eventsPath)) {
14508
- state.meta.upto_offset = (0, import_node_fs11.statSync)(d.eventsPath).size;
14509
- ingest((0, import_node_fs11.readFileSync)(d.eventsPath, "utf8"));
14689
+ if ((0, import_node_fs14.existsSync)(d.eventsPath)) {
14690
+ state.meta.upto_offset = (0, import_node_fs14.statSync)(d.eventsPath).size;
14691
+ ingest((0, import_node_fs14.readFileSync)(d.eventsPath, "utf8"));
14510
14692
  }
14511
14693
  } catch {
14512
14694
  }
@@ -14777,7 +14959,7 @@ function applyBounds(state, input) {
14777
14959
  }
14778
14960
 
14779
14961
  // src/lib/dossier/cache.ts
14780
- var import_node_fs12 = require("node:fs");
14962
+ var import_node_fs15 = require("node:fs");
14781
14963
  function compactState(s) {
14782
14964
  const ms = (iso) => Date.parse(iso) || 0;
14783
14965
  return {
@@ -14906,20 +15088,20 @@ function encodeState(s) {
14906
15088
  function writeFoldCache(d, state) {
14907
15089
  try {
14908
15090
  const tmp = `${d.foldPath}.${process.pid}.tmp`;
14909
- (0, import_node_fs12.writeFileSync)(tmp, encodeState(state), { mode: 384 });
14910
- (0, import_node_fs12.renameSync)(tmp, d.foldPath);
15091
+ (0, import_node_fs15.writeFileSync)(tmp, encodeState(state), { mode: 384 });
15092
+ (0, import_node_fs15.renameSync)(tmp, d.foldPath);
14911
15093
  } catch {
14912
15094
  }
14913
15095
  }
14914
15096
  function readFoldCache(d) {
14915
15097
  try {
14916
- if (!(0, import_node_fs12.existsSync)(d.foldPath)) return null;
14917
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(d.foldPath, "utf8"));
15098
+ if (!(0, import_node_fs15.existsSync)(d.foldPath)) return null;
15099
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(d.foldPath, "utf8"));
14918
15100
  if (raw?.v !== 1) return null;
14919
15101
  const cached2 = expandState(raw);
14920
15102
  if (!cached2?.meta) return null;
14921
- const size = (0, import_node_fs12.existsSync)(d.eventsPath) ? (0, import_node_fs12.statSync)(d.eventsPath).size : 0;
14922
- const rotations = (0, import_node_fs12.existsSync)(d.rotatedDir) ? (0, import_node_fs12.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).length : 0;
15103
+ const size = (0, import_node_fs15.existsSync)(d.eventsPath) ? (0, import_node_fs15.statSync)(d.eventsPath).size : 0;
15104
+ const rotations = (0, import_node_fs15.existsSync)(d.rotatedDir) ? (0, import_node_fs15.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).length : 0;
14923
15105
  if (cached2.meta.upto_offset !== size || cached2.meta.rotations !== rotations) return null;
14924
15106
  return cached2;
14925
15107
  } catch {
@@ -14970,13 +15152,13 @@ function assessContinuity(i) {
14970
15152
 
14971
15153
  // src/lib/dossier/reanchor.ts
14972
15154
  var import_node_crypto6 = require("node:crypto");
14973
- var import_node_fs13 = require("node:fs");
15155
+ var import_node_fs16 = require("node:fs");
14974
15156
  function lineSha(text) {
14975
15157
  return (0, import_node_crypto6.createHash)("sha256").update(text.trim()).digest("hex").slice(0, HASH_WIDTH);
14976
15158
  }
14977
15159
  function fileHash(path) {
14978
15160
  try {
14979
- return (0, import_node_crypto6.createHash)("sha256").update((0, import_node_fs13.readFileSync)(path)).digest("hex").slice(0, HASH_WIDTH);
15161
+ return (0, import_node_crypto6.createHash)("sha256").update((0, import_node_fs16.readFileSync)(path)).digest("hex").slice(0, HASH_WIDTH);
14980
15162
  } catch {
14981
15163
  return null;
14982
15164
  }
@@ -15348,14 +15530,14 @@ function foreignAuthoredPaths(identity, opts = {}) {
15348
15530
  let sessions = 0;
15349
15531
  try {
15350
15532
  const dir = treeDir(identity);
15351
- if (!(0, import_node_fs14.existsSync)(dir)) return { paths: [], sessions: 0 };
15352
- for (const entry of (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true })) {
15533
+ if (!(0, import_node_fs17.existsSync)(dir)) return { paths: [], sessions: 0 };
15534
+ for (const entry of (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true })) {
15353
15535
  if (!entry.isDirectory()) continue;
15354
15536
  if (entry.name === identity.sessionKey) continue;
15355
15537
  const log = (0, import_node_path13.join)(dir, entry.name, "events.jsonl");
15356
15538
  try {
15357
- if (!(0, import_node_fs14.existsSync)(log)) continue;
15358
- if (now - (0, import_node_fs14.statSync)(log).mtimeMs > windowMs) continue;
15539
+ if (!(0, import_node_fs17.existsSync)(log)) continue;
15540
+ if (now - (0, import_node_fs17.statSync)(log).mtimeMs > windowMs) continue;
15359
15541
  const sib = {
15360
15542
  dir: (0, import_node_path13.join)(dir, entry.name),
15361
15543
  identity,
@@ -15390,13 +15572,13 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
15390
15572
  try {
15391
15573
  const mine = dossierDir(identity);
15392
15574
  const userDir = (0, import_node_path13.dirname)((0, import_node_path13.dirname)(mine));
15393
- if (!(0, import_node_fs14.existsSync)(userDir)) return 0;
15575
+ if (!(0, import_node_fs17.existsSync)(userDir)) return 0;
15394
15576
  const cutoff = Date.now() - maxAgeMs;
15395
- for (const tree of (0, import_node_fs14.readdirSync)(userDir, { withFileTypes: true })) {
15577
+ for (const tree of (0, import_node_fs17.readdirSync)(userDir, { withFileTypes: true })) {
15396
15578
  if (!tree.isDirectory()) continue;
15397
15579
  const treePath = (0, import_node_path13.join)(userDir, tree.name);
15398
15580
  let live = 0;
15399
- for (const entry of (0, import_node_fs14.readdirSync)(treePath, { withFileTypes: true })) {
15581
+ for (const entry of (0, import_node_fs17.readdirSync)(treePath, { withFileTypes: true })) {
15400
15582
  if (!entry.isDirectory()) continue;
15401
15583
  const dir = (0, import_node_path13.join)(treePath, entry.name);
15402
15584
  if (dir === mine) {
@@ -15405,9 +15587,9 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
15405
15587
  }
15406
15588
  try {
15407
15589
  const log = (0, import_node_path13.join)(dir, "events.jsonl");
15408
- const at = (0, import_node_fs14.existsSync)(log) ? (0, import_node_fs14.statSync)(log).mtimeMs : (0, import_node_fs14.statSync)(dir).mtimeMs;
15590
+ const at = (0, import_node_fs17.existsSync)(log) ? (0, import_node_fs17.statSync)(log).mtimeMs : (0, import_node_fs17.statSync)(dir).mtimeMs;
15409
15591
  if (at < cutoff) {
15410
- (0, import_node_fs14.rmSync)(dir, { recursive: true, force: true });
15592
+ (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
15411
15593
  removed++;
15412
15594
  } else {
15413
15595
  live++;
@@ -15417,7 +15599,7 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
15417
15599
  }
15418
15600
  if (live === 0) {
15419
15601
  try {
15420
- (0, import_node_fs14.rmSync)(treePath, { recursive: false, force: false });
15602
+ (0, import_node_fs17.rmSync)(treePath, { recursive: false, force: false });
15421
15603
  } catch {
15422
15604
  }
15423
15605
  }
@@ -15434,8 +15616,8 @@ function sessionDossier(token, sessionId) {
15434
15616
  }
15435
15617
  function hasActiveGoal(d) {
15436
15618
  try {
15437
- if (!(0, import_node_fs14.existsSync)(d.eventsPath)) return false;
15438
- return (0, import_node_fs14.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
15619
+ if (!(0, import_node_fs17.existsSync)(d.eventsPath)) return false;
15620
+ return (0, import_node_fs17.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
15439
15621
  } catch {
15440
15622
  return false;
15441
15623
  }
@@ -15528,7 +15710,7 @@ function recordVerdict(d, v) {
15528
15710
  if (!lines.has(f.file)) {
15529
15711
  try {
15530
15712
  const abs = (0, import_node_path13.join)(root, f.file);
15531
- lines.set(f.file, (0, import_node_fs14.existsSync)(abs) ? (0, import_node_fs14.readFileSync)(abs, "utf8").split("\n") : null);
15713
+ lines.set(f.file, (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null);
15532
15714
  } catch {
15533
15715
  lines.set(f.file, null);
15534
15716
  }
@@ -15629,7 +15811,7 @@ function recallMemory(d, identity, opts) {
15629
15811
  readFileLines: (file) => {
15630
15812
  try {
15631
15813
  const abs = (0, import_node_path13.join)(root, file);
15632
- return (0, import_node_fs14.existsSync)(abs) ? (0, import_node_fs14.readFileSync)(abs, "utf8").split("\n") : null;
15814
+ return (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null;
15633
15815
  } catch {
15634
15816
  return null;
15635
15817
  }
@@ -15678,6 +15860,9 @@ function registerIntentCommands(program2) {
15678
15860
  if (!prompt) {
15679
15861
  process.exit(0);
15680
15862
  }
15863
+ if (deferredToPlugin("intent capture", event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null)) {
15864
+ process.exit(0);
15865
+ }
15681
15866
  const authForScope = await resolveToken(program2.opts().token);
15682
15867
  const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
15683
15868
  const scopeSession = event.session_id || process.env.CLAUDE_SESSION_ID || "";
@@ -15767,21 +15952,21 @@ async function fireClassify(prompt, sessionId, globals) {
15767
15952
  }
15768
15953
 
15769
15954
  // src/commands/lifecycle.ts
15770
- var import_node_fs18 = require("node:fs");
15955
+ var import_node_fs21 = require("node:fs");
15771
15956
  var import_node_path17 = require("node:path");
15772
15957
 
15773
15958
  // src/lib/baseline.ts
15774
- var import_node_fs17 = require("node:fs");
15959
+ var import_node_fs20 = require("node:fs");
15775
15960
  var import_node_path16 = require("node:path");
15776
15961
  var import_node_crypto9 = require("node:crypto");
15777
15962
 
15778
15963
  // src/lib/snapshot.ts
15779
- var import_node_fs16 = require("node:fs");
15964
+ var import_node_fs19 = require("node:fs");
15780
15965
  var import_node_path15 = require("node:path");
15781
15966
  var import_node_child_process6 = require("node:child_process");
15782
15967
 
15783
15968
  // src/lib/files.ts
15784
- var import_node_fs15 = require("node:fs");
15969
+ var import_node_fs18 = require("node:fs");
15785
15970
  var import_node_path14 = require("node:path");
15786
15971
  var LANG_MAP = {
15787
15972
  // Analyzable (static analysis + Gemini)
@@ -15858,7 +16043,7 @@ function sortByMtime(files) {
15858
16043
  const resolved = resolveFile(f);
15859
16044
  if (!resolved) return null;
15860
16045
  try {
15861
- const stat3 = (0, import_node_fs15.statSync)(resolved);
16046
+ const stat3 = (0, import_node_fs18.statSync)(resolved);
15862
16047
  return { path: f, resolved, mtime: stat3.mtimeMs };
15863
16048
  } catch {
15864
16049
  return null;
@@ -15891,7 +16076,7 @@ function collectCodeDelta(files, opts) {
15891
16076
  }
15892
16077
  let size;
15893
16078
  try {
15894
- size = (0, import_node_fs15.statSync)(resolved).size;
16079
+ size = (0, import_node_fs18.statSync)(resolved).size;
15895
16080
  } catch {
15896
16081
  exclude(filepath, "not-stattable");
15897
16082
  continue;
@@ -15908,7 +16093,7 @@ function collectCodeDelta(files, opts) {
15908
16093
  }
15909
16094
  let content;
15910
16095
  try {
15911
- content = (0, import_node_fs15.readFileSync)(resolved, "utf-8");
16096
+ content = (0, import_node_fs18.readFileSync)(resolved, "utf-8");
15912
16097
  } catch {
15913
16098
  exclude(filepath, "not-readable");
15914
16099
  continue;
@@ -15947,7 +16132,7 @@ function collectCodeDelta(files, opts) {
15947
16132
 
15948
16133
  // src/lib/snapshot.ts
15949
16134
  function generateSnapshotDiffs(files) {
15950
- if (!(0, import_node_fs16.existsSync)(SNAPSHOT_DIR)) {
16135
+ if (!(0, import_node_fs19.existsSync)(SNAPSHOT_DIR)) {
15951
16136
  return { diffs: [], has_snapshots: false };
15952
16137
  }
15953
16138
  const diffs = [];
@@ -15955,8 +16140,8 @@ function generateSnapshotDiffs(files) {
15955
16140
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
15956
16141
  const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
15957
16142
  const language = file.language ?? detectLanguage(file.path);
15958
- if ((0, import_node_fs16.existsSync)(snapshotPath)) {
15959
- const oldContent = (0, import_node_fs16.readFileSync)(snapshotPath, "utf-8");
16143
+ if ((0, import_node_fs19.existsSync)(snapshotPath)) {
16144
+ const oldContent = (0, import_node_fs19.readFileSync)(snapshotPath, "utf-8");
15960
16145
  if (oldContent === file.content) continue;
15961
16146
  const diff = computeDiff(oldContent, file.content, file.path);
15962
16147
  if (diff) {
@@ -15983,8 +16168,8 @@ function saveSnapshots(files) {
15983
16168
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
15984
16169
  const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
15985
16170
  snapshotPaths.add(snapshotPath);
15986
- (0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
15987
- (0, import_node_fs16.writeFileSync)(snapshotPath, file.content);
16171
+ (0, import_node_fs19.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
16172
+ (0, import_node_fs19.writeFileSync)(snapshotPath, file.content);
15988
16173
  }
15989
16174
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
15990
16175
  }
@@ -15992,9 +16177,9 @@ function computeDiff(oldContent, newContent, filePath) {
15992
16177
  const tmpOld = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-old.tmp");
15993
16178
  const tmpNew = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-new.tmp");
15994
16179
  try {
15995
- (0, import_node_fs16.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
15996
- (0, import_node_fs16.writeFileSync)(tmpOld, oldContent);
15997
- (0, import_node_fs16.writeFileSync)(tmpNew, newContent);
16180
+ (0, import_node_fs19.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
16181
+ (0, import_node_fs19.writeFileSync)(tmpOld, oldContent);
16182
+ (0, import_node_fs19.writeFileSync)(tmpNew, newContent);
15998
16183
  const result = (0, import_node_child_process6.execSync)(
15999
16184
  `git diff --no-index --unified=10 -- "${tmpOld}" "${tmpNew}"`,
16000
16185
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
@@ -16008,32 +16193,32 @@ function computeDiff(oldContent, newContent, filePath) {
16008
16193
  return null;
16009
16194
  } finally {
16010
16195
  try {
16011
- (0, import_node_fs16.unlinkSync)(tmpOld);
16196
+ (0, import_node_fs19.unlinkSync)(tmpOld);
16012
16197
  } catch {
16013
16198
  }
16014
16199
  try {
16015
- (0, import_node_fs16.unlinkSync)(tmpNew);
16200
+ (0, import_node_fs19.unlinkSync)(tmpNew);
16016
16201
  } catch {
16017
16202
  }
16018
16203
  }
16019
16204
  }
16020
16205
  function cleanStaleSnapshots(dir, keepSet) {
16021
- if (!(0, import_node_fs16.existsSync)(dir)) return;
16206
+ if (!(0, import_node_fs19.existsSync)(dir)) return;
16022
16207
  try {
16023
- const entries = (0, import_node_fs16.readdirSync)(dir, { withFileTypes: true });
16208
+ const entries = (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true });
16024
16209
  for (const entry of entries) {
16025
16210
  if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
16026
16211
  const fullPath = (0, import_node_path15.join)(dir, entry.name);
16027
16212
  if (entry.isDirectory()) {
16028
16213
  cleanStaleSnapshots(fullPath, keepSet);
16029
16214
  try {
16030
- const remaining = (0, import_node_fs16.readdirSync)(fullPath);
16031
- if (remaining.length === 0) (0, import_node_fs16.rmdirSync)(fullPath);
16215
+ const remaining = (0, import_node_fs19.readdirSync)(fullPath);
16216
+ if (remaining.length === 0) (0, import_node_fs19.rmdirSync)(fullPath);
16032
16217
  } catch {
16033
16218
  }
16034
16219
  } else if (!keepSet.has(fullPath)) {
16035
16220
  try {
16036
- (0, import_node_fs16.unlinkSync)(fullPath);
16221
+ (0, import_node_fs19.unlinkSync)(fullPath);
16037
16222
  } catch {
16038
16223
  }
16039
16224
  }
@@ -16064,8 +16249,8 @@ var CARRY_FILE = `${BASELINE_DIR}/.carry`;
16064
16249
  var CARRY_WINDOW_MS = 12e4;
16065
16250
  function writeCarry(sessionId, headSha) {
16066
16251
  try {
16067
- (0, import_node_fs17.mkdirSync)(projectPath(BASELINE_DIR), { recursive: true });
16068
- (0, import_node_fs17.writeFileSync)(
16252
+ (0, import_node_fs20.mkdirSync)(projectPath(BASELINE_DIR), { recursive: true });
16253
+ (0, import_node_fs20.writeFileSync)(
16069
16254
  projectPath(CARRY_FILE),
16070
16255
  JSON.stringify({ from_key: sessionKey(sessionId), head_sha: headSha, ts: Date.now() })
16071
16256
  );
@@ -16075,10 +16260,10 @@ function writeCarry(sessionId, headSha) {
16075
16260
  function claimCarry(newKey) {
16076
16261
  const carryPath = projectPath(CARRY_FILE);
16077
16262
  try {
16078
- if (!(0, import_node_fs17.existsSync)(carryPath)) return null;
16079
- const carry = JSON.parse((0, import_node_fs17.readFileSync)(carryPath, "utf-8"));
16263
+ if (!(0, import_node_fs20.existsSync)(carryPath)) return null;
16264
+ const carry = JSON.parse((0, import_node_fs20.readFileSync)(carryPath, "utf-8"));
16080
16265
  try {
16081
- (0, import_node_fs17.rmSync)(carryPath, { force: true });
16266
+ (0, import_node_fs20.rmSync)(carryPath, { force: true });
16082
16267
  } catch {
16083
16268
  }
16084
16269
  if (!carry?.from_key || typeof carry.ts !== "number") return null;
@@ -16089,11 +16274,11 @@ function claimCarry(newKey) {
16089
16274
  if (!prior) return null;
16090
16275
  const toDir = sessionDir(newKey);
16091
16276
  try {
16092
- (0, import_node_fs17.rmSync)(toDir, { recursive: true, force: true });
16277
+ (0, import_node_fs20.rmSync)(toDir, { recursive: true, force: true });
16093
16278
  } catch {
16094
16279
  }
16095
- (0, import_node_fs17.renameSync)(fromDir, toDir);
16096
- (0, import_node_fs17.writeFileSync)(manifestPath(toDir), JSON.stringify({ ...prior, session_id: newKey }) + "\n");
16280
+ (0, import_node_fs20.renameSync)(fromDir, toDir);
16281
+ (0, import_node_fs20.writeFileSync)(manifestPath(toDir), JSON.stringify({ ...prior, session_id: newKey }) + "\n");
16097
16282
  return readManifest(toDir);
16098
16283
  } catch {
16099
16284
  return null;
@@ -16117,21 +16302,21 @@ function captureBaseline(opts = {}) {
16117
16302
  const head_sha = getCurrentCommit();
16118
16303
  const dirty = getDirtyFiles();
16119
16304
  try {
16120
- (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
16305
+ (0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
16121
16306
  } catch {
16122
16307
  }
16123
16308
  const filesDir = (0, import_node_path16.join)(dir, "files");
16124
16309
  const mirrored = [];
16125
16310
  try {
16126
- (0, import_node_fs17.mkdirSync)(filesDir, { recursive: true });
16311
+ (0, import_node_fs20.mkdirSync)(filesDir, { recursive: true });
16127
16312
  for (const p of dirty) {
16128
16313
  if (p.includes("..")) continue;
16129
16314
  const content = safeReadForMirror(projectPath(p));
16130
16315
  if (content === null) continue;
16131
16316
  const dest = mirrorPath(dir, p);
16132
16317
  try {
16133
- (0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16134
- (0, import_node_fs17.writeFileSync)(dest, content);
16318
+ (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16319
+ (0, import_node_fs20.writeFileSync)(dest, content);
16135
16320
  mirrored.push(p);
16136
16321
  } catch {
16137
16322
  }
@@ -16146,8 +16331,8 @@ function captureBaseline(opts = {}) {
16146
16331
  version: BASELINE_VERSION
16147
16332
  };
16148
16333
  try {
16149
- (0, import_node_fs17.mkdirSync)(dir, { recursive: true });
16150
- (0, import_node_fs17.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
16334
+ (0, import_node_fs20.mkdirSync)(dir, { recursive: true });
16335
+ (0, import_node_fs20.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
16151
16336
  } catch {
16152
16337
  }
16153
16338
  pruneOldBaselines();
@@ -16158,9 +16343,9 @@ function readBaseline(sessionId) {
16158
16343
  }
16159
16344
  function readManifest(dir) {
16160
16345
  const mp = manifestPath(dir);
16161
- if (!(0, import_node_fs17.existsSync)(mp)) return null;
16346
+ if (!(0, import_node_fs20.existsSync)(mp)) return null;
16162
16347
  try {
16163
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(mp, "utf-8"));
16348
+ const parsed = JSON.parse((0, import_node_fs20.readFileSync)(mp, "utf-8"));
16164
16349
  if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
16165
16350
  return null;
16166
16351
  }
@@ -16191,9 +16376,9 @@ function preImage(repoRelPath, baseline) {
16191
16376
  function resolvePreImage(repoRelPath, baseline) {
16192
16377
  if (baseline.dirty_paths.includes(repoRelPath)) {
16193
16378
  const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
16194
- if ((0, import_node_fs17.existsSync)(mp)) {
16379
+ if ((0, import_node_fs20.existsSync)(mp)) {
16195
16380
  try {
16196
- return { content: (0, import_node_fs17.readFileSync)(mp, "utf-8"), existed: true };
16381
+ return { content: (0, import_node_fs20.readFileSync)(mp, "utf-8"), existed: true };
16197
16382
  } catch {
16198
16383
  }
16199
16384
  }
@@ -16238,8 +16423,8 @@ function absorbIntoBaseline(paths, sessionId) {
16238
16423
  const content = safeReadForMirror(projectPath(p));
16239
16424
  if (content === null) continue;
16240
16425
  const dest = mirrorPath(dir, p);
16241
- (0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16242
- (0, import_node_fs17.writeFileSync)(dest, content);
16426
+ (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16427
+ (0, import_node_fs20.writeFileSync)(dest, content);
16243
16428
  dirty.add(p);
16244
16429
  adopted++;
16245
16430
  } catch {
@@ -16248,7 +16433,7 @@ function absorbIntoBaseline(paths, sessionId) {
16248
16433
  if (adopted === 0) return 0;
16249
16434
  try {
16250
16435
  const updated = { ...baseline, dirty_paths: [...dirty] };
16251
- (0, import_node_fs17.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
16436
+ (0, import_node_fs20.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
16252
16437
  preImageCache.delete(baseline);
16253
16438
  } catch {
16254
16439
  return 0;
@@ -16259,7 +16444,7 @@ function changedSinceBaseline(repoRelPath, baseline) {
16259
16444
  const pre = preImage(repoRelPath, baseline);
16260
16445
  let current;
16261
16446
  try {
16262
- current = (0, import_node_fs17.readFileSync)(projectPath(repoRelPath), "utf-8");
16447
+ current = (0, import_node_fs20.readFileSync)(projectPath(repoRelPath), "utf-8");
16263
16448
  } catch {
16264
16449
  return pre.existed;
16265
16450
  }
@@ -16268,8 +16453,8 @@ function changedSinceBaseline(repoRelPath, baseline) {
16268
16453
  }
16269
16454
  function safeReadForMirror(absPath) {
16270
16455
  try {
16271
- if ((0, import_node_fs17.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
16272
- const buf = (0, import_node_fs17.readFileSync)(absPath);
16456
+ if ((0, import_node_fs20.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
16457
+ const buf = (0, import_node_fs20.readFileSync)(absPath);
16273
16458
  if (buf.includes(0)) return null;
16274
16459
  return buf.toString("utf-8");
16275
16460
  } catch {
@@ -16280,7 +16465,7 @@ function pruneOldBaselines() {
16280
16465
  const root = projectPath(BASELINE_DIR);
16281
16466
  let entries;
16282
16467
  try {
16283
- entries = (0, import_node_fs17.readdirSync)(root);
16468
+ entries = (0, import_node_fs20.readdirSync)(root);
16284
16469
  } catch {
16285
16470
  return;
16286
16471
  }
@@ -16290,8 +16475,8 @@ function pruneOldBaselines() {
16290
16475
  const manifest = readManifest(dir);
16291
16476
  if (!manifest) {
16292
16477
  try {
16293
- if (now - (0, import_node_fs17.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
16294
- (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
16478
+ if (now - (0, import_node_fs20.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
16479
+ (0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
16295
16480
  }
16296
16481
  } catch {
16297
16482
  }
@@ -16299,7 +16484,7 @@ function pruneOldBaselines() {
16299
16484
  }
16300
16485
  if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
16301
16486
  try {
16302
- (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
16487
+ (0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
16303
16488
  } catch {
16304
16489
  }
16305
16490
  }
@@ -16393,6 +16578,7 @@ function registerLifecycleCommands(program2) {
16393
16578
  if (!verityConfigured()) process.exit(0);
16394
16579
  const event = await readHookStdin();
16395
16580
  const sessionId = opts.sessionId ?? event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null;
16581
+ if (deferredToPlugin("compact", sessionId)) process.exit(0);
16396
16582
  const globals = program2.opts();
16397
16583
  const tok = await resolveToken(globals.token);
16398
16584
  const session2 = sessionDossier(tok.ok ? tok.data.token : null, sessionId);
@@ -16421,6 +16607,7 @@ function registerLifecycleCommands(program2) {
16421
16607
  if (!verityConfigured()) process.exit(0);
16422
16608
  const event = await readHookStdin();
16423
16609
  const sessionId = opts.sessionId ?? event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null;
16610
+ if (deferredToPlugin("session end", sessionId)) process.exit(0);
16424
16611
  const reason = opts.reason ?? event.session_end_reason ?? event.reason ?? "other";
16425
16612
  if (reason === "clear") {
16426
16613
  writeCarry(sessionId ?? void 0, getCurrentCommit());
@@ -16473,7 +16660,7 @@ function buildCompactionContext(session) {
16473
16660
  readFileLines: (file) => {
16474
16661
  try {
16475
16662
  const abs = (0, import_node_path17.join)(root, file);
16476
- return (0, import_node_fs18.existsSync)(abs) ? (0, import_node_fs18.readFileSync)(abs, "utf8").split("\n") : null;
16663
+ return (0, import_node_fs21.existsSync)(abs) ? (0, import_node_fs21.readFileSync)(abs, "utf8").split("\n") : null;
16477
16664
  } catch {
16478
16665
  return null;
16479
16666
  }
@@ -16530,11 +16717,11 @@ async function readHookStdin() {
16530
16717
 
16531
16718
  // src/commands/standard.ts
16532
16719
  var import_promises9 = require("node:fs/promises");
16533
- var import_node_fs20 = require("node:fs");
16720
+ var import_node_fs23 = require("node:fs");
16534
16721
  var import_yaml = __toESM(require_dist());
16535
16722
 
16536
16723
  // src/lib/verityignore.ts
16537
- var import_node_fs19 = require("node:fs");
16724
+ var import_node_fs22 = require("node:fs");
16538
16725
  var EMPTY = { rules: [], securityOverlap: [], problems: [] };
16539
16726
  var SECURITY_PROBES = [
16540
16727
  ".env",
@@ -16627,9 +16814,9 @@ function isIgnored(ig, path) {
16627
16814
  }
16628
16815
  function loadVerityIgnore() {
16629
16816
  const file = projectPath(VERITYIGNORE_FILE);
16630
- if (!(0, import_node_fs19.existsSync)(file)) return EMPTY;
16817
+ if (!(0, import_node_fs22.existsSync)(file)) return EMPTY;
16631
16818
  try {
16632
- return parseVerityIgnore((0, import_node_fs19.readFileSync)(file, "utf-8"));
16819
+ return parseVerityIgnore((0, import_node_fs22.readFileSync)(file, "utf-8"));
16633
16820
  } catch {
16634
16821
  return EMPTY;
16635
16822
  }
@@ -16699,9 +16886,9 @@ function registerStandardCommands(program2) {
16699
16886
  }
16700
16887
  let ignoreRaw = null;
16701
16888
  const ignorePath = projectPath(VERITYIGNORE_FILE);
16702
- if ((0, import_node_fs20.existsSync)(ignorePath)) {
16889
+ if ((0, import_node_fs23.existsSync)(ignorePath)) {
16703
16890
  try {
16704
- ignoreRaw = (0, import_node_fs20.readFileSync)(ignorePath, "utf-8");
16891
+ ignoreRaw = (0, import_node_fs23.readFileSync)(ignorePath, "utf-8");
16705
16892
  const overlap = describeSecurityOverlap(parseVerityIgnore(ignoreRaw));
16706
16893
  if (overlap) printWarn(overlap);
16707
16894
  } catch {
@@ -16801,6 +16988,22 @@ function registerConfigCommands(program2) {
16801
16988
  }
16802
16989
  process.stdout.write(urlResult.data + "\n");
16803
16990
  });
16991
+ config.command("git-moments [moments]").description('Get or set the git moments the guard reviews: commit,push \u2014 or "none"').action((moments) => {
16992
+ if (moments === void 0) {
16993
+ const current = readProjectConfig().git_moments;
16994
+ process.stdout.write((current.length ? current.join(",") : "none") + "\n");
16995
+ return;
16996
+ }
16997
+ const next = moments === "none" ? [] : parseMoments(moments);
16998
+ if (moments !== "none" && next.length === 0) {
16999
+ printError(`Unrecognised moments: ${moments}. Use "commit", "push", "commit,push", or "none".`);
17000
+ process.exit(1);
17001
+ }
17002
+ writeProjectConfig({ git_moments: next });
17003
+ printInfo(
17004
+ next.length ? `Git-moment review enabled for: ${next.join(", ")}` : "Git-moment review disabled \u2014 commits and pushes are no longer gated."
17005
+ );
17006
+ });
16804
17007
  config.command("push").description("Upload the analysis config to the service").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
16805
17008
  const globals = program2.opts();
16806
17009
  const tokenResult = await resolveToken(globals.token);
@@ -16943,10 +17146,10 @@ function formatRunDetail(run2) {
16943
17146
  }
16944
17147
 
16945
17148
  // src/lib/ignore-declaration.ts
16946
- var import_node_fs22 = require("node:fs");
17149
+ var import_node_fs25 = require("node:fs");
16947
17150
 
16948
17151
  // src/lib/debounce.ts
16949
- var import_node_fs21 = require("node:fs");
17152
+ var import_node_fs24 = require("node:fs");
16950
17153
  var import_node_crypto10 = require("node:crypto");
16951
17154
  function scopedFile(base, sessionId) {
16952
17155
  if (!sessionId) return base;
@@ -16954,9 +17157,9 @@ function scopedFile(base, sessionId) {
16954
17157
  }
16955
17158
  function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
16956
17159
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
16957
- if (!(0, import_node_fs21.existsSync)(file)) return null;
17160
+ if (!(0, import_node_fs24.existsSync)(file)) return null;
16958
17161
  try {
16959
- const lastTs = parseInt((0, import_node_fs21.readFileSync)(file, "utf-8").trim(), 10);
17162
+ const lastTs = parseInt((0, import_node_fs24.readFileSync)(file, "utf-8").trim(), 10);
16960
17163
  const nowTs = Math.floor(Date.now() / 1e3);
16961
17164
  const elapsed = nowTs - lastTs;
16962
17165
  if (elapsed < debounceSeconds) {
@@ -16969,10 +17172,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
16969
17172
  function checkMtime(files, bypassForRecentCommits, sessionId) {
16970
17173
  if (bypassForRecentCommits) return null;
16971
17174
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
16972
- if (!(0, import_node_fs21.existsSync)(file)) return null;
17175
+ if (!(0, import_node_fs24.existsSync)(file)) return null;
16973
17176
  let debounceTime;
16974
17177
  try {
16975
- debounceTime = (0, import_node_fs21.statSync)(file).mtimeMs;
17178
+ debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
16976
17179
  } catch {
16977
17180
  return null;
16978
17181
  }
@@ -16980,7 +17183,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
16980
17183
  const resolved = resolveFile(f);
16981
17184
  if (!resolved) continue;
16982
17185
  try {
16983
- const stat3 = (0, import_node_fs21.statSync)(resolved);
17186
+ const stat3 = (0, import_node_fs24.statSync)(resolved);
16984
17187
  if (stat3.mtimeMs > debounceTime) {
16985
17188
  return null;
16986
17189
  }
@@ -16996,8 +17199,8 @@ function computeContentHash(files) {
16996
17199
  for (const f of sorted) {
16997
17200
  const resolved = resolveFile(f) ?? f;
16998
17201
  try {
16999
- if ((0, import_node_fs21.existsSync)(resolved)) {
17000
- hash.update((0, import_node_fs21.readFileSync)(resolved));
17202
+ if ((0, import_node_fs24.existsSync)(resolved)) {
17203
+ hash.update((0, import_node_fs24.readFileSync)(resolved));
17001
17204
  }
17002
17205
  } catch {
17003
17206
  }
@@ -17007,9 +17210,9 @@ function computeContentHash(files) {
17007
17210
  function checkContentHash(files, sessionId) {
17008
17211
  const hash = computeContentHash(files);
17009
17212
  const file = scopedFile(HASH_FILE, sessionId);
17010
- if ((0, import_node_fs21.existsSync)(file)) {
17213
+ if ((0, import_node_fs24.existsSync)(file)) {
17011
17214
  try {
17012
- const storedHash = (0, import_node_fs21.readFileSync)(file, "utf-8").trim();
17215
+ const storedHash = (0, import_node_fs24.readFileSync)(file, "utf-8").trim();
17013
17216
  if (hash === storedHash) {
17014
17217
  return { skip: "No source changes since last analysis", hash };
17015
17218
  }
@@ -17019,24 +17222,24 @@ function checkContentHash(files, sessionId) {
17019
17222
  return { skip: null, hash };
17020
17223
  }
17021
17224
  function recordAnalysisStart(sessionId) {
17022
- (0, import_node_fs21.mkdirSync)(VERITY_DIR, { recursive: true });
17023
- (0, import_node_fs21.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
17225
+ (0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
17226
+ (0, import_node_fs24.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
17024
17227
  }
17025
17228
  function recordPassHash(hash, sessionId) {
17026
- (0, import_node_fs21.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
17229
+ (0, import_node_fs24.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
17027
17230
  }
17028
17231
  function narrowToRecent(files, sessionId) {
17029
17232
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
17030
- if (!(0, import_node_fs21.existsSync)(file)) return files;
17233
+ if (!(0, import_node_fs24.existsSync)(file)) return files;
17031
17234
  let debounceTime;
17032
17235
  try {
17033
- debounceTime = (0, import_node_fs21.statSync)(file).mtimeMs;
17236
+ debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
17034
17237
  } catch {
17035
17238
  return files;
17036
17239
  }
17037
17240
  const recent = files.filter((f) => {
17038
17241
  try {
17039
- return (0, import_node_fs21.existsSync)(f) && (0, import_node_fs21.statSync)(f).mtimeMs > debounceTime;
17242
+ return (0, import_node_fs24.existsSync)(f) && (0, import_node_fs24.statSync)(f).mtimeMs > debounceTime;
17040
17243
  } catch {
17041
17244
  return false;
17042
17245
  }
@@ -17049,9 +17252,9 @@ function readIteration(currentCommit, _contentHash) {
17049
17252
  var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
17050
17253
  function readBlockState(currentCommit, opts) {
17051
17254
  if (opts?.newUserPrompt) return NO_BLOCKS;
17052
- if (!(0, import_node_fs21.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
17255
+ if (!(0, import_node_fs24.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
17053
17256
  try {
17054
- const stored = (0, import_node_fs21.readFileSync)(ITERATION_FILE, "utf-8").trim();
17257
+ const stored = (0, import_node_fs24.readFileSync)(ITERATION_FILE, "utf-8").trim();
17055
17258
  const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
17056
17259
  if (!parsed) return NO_BLOCKS;
17057
17260
  if (parsed.commit !== currentCommit) return NO_BLOCKS;
@@ -17097,8 +17300,8 @@ function isSameProblem(previous, current) {
17097
17300
  return current.split(",").some((k) => prev.has(k));
17098
17301
  }
17099
17302
  function writeBlockState(commit, state) {
17100
- (0, import_node_fs21.mkdirSync)(VERITY_DIR, { recursive: true });
17101
- (0, import_node_fs21.writeFileSync)(
17303
+ (0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
17304
+ (0, import_node_fs24.writeFileSync)(
17102
17305
  ITERATION_FILE,
17103
17306
  JSON.stringify({
17104
17307
  v: 2,
@@ -17203,9 +17406,9 @@ function resolveIgnoreState(keys) {
17203
17406
  }
17204
17407
  function readIgnoreState(sessionId) {
17205
17408
  const file = stateFile(sessionId);
17206
- if (!(0, import_node_fs22.existsSync)(file)) return null;
17409
+ if (!(0, import_node_fs25.existsSync)(file)) return null;
17207
17410
  try {
17208
- const o = JSON.parse((0, import_node_fs22.readFileSync)(file, "utf-8")) ?? {};
17411
+ const o = JSON.parse((0, import_node_fs25.readFileSync)(file, "utf-8")) ?? {};
17209
17412
  const spent = typeof o.spent === "number" ? o.spent : 0;
17210
17413
  const raw = o.active;
17211
17414
  let active = null;
@@ -17229,8 +17432,8 @@ function readIgnoreState(sessionId) {
17229
17432
  }
17230
17433
  function writeIgnoreState(state, sessionId) {
17231
17434
  try {
17232
- (0, import_node_fs22.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
17233
- (0, import_node_fs22.writeFileSync)(stateFile(sessionId), JSON.stringify({ v: 1, active: state.active, spent: state.spent }));
17435
+ (0, import_node_fs25.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
17436
+ (0, import_node_fs25.writeFileSync)(stateFile(sessionId), JSON.stringify({ v: 1, active: state.active, spent: state.spent }));
17234
17437
  } catch {
17235
17438
  }
17236
17439
  }
@@ -17567,7 +17770,6 @@ function createRun(opts, globals) {
17567
17770
  token: "",
17568
17771
  modeDecision: null,
17569
17772
  sessionIdForMemory: "",
17570
- contextFilePaths: [],
17571
17773
  analysisMode: "standard",
17572
17774
  sessionAuthoredCode: false,
17573
17775
  staticResults: {
@@ -17577,6 +17779,7 @@ function createRun(opts, globals) {
17577
17779
  },
17578
17780
  codeDelta: { files: [], total_lines: 0, total_files: 0, excluded: [] },
17579
17781
  snapshotResult: { has_snapshots: false, diffs: [] },
17782
+ repoContext: null,
17580
17783
  contentHash: null,
17581
17784
  iteration: 1,
17582
17785
  currentCommit: "",
@@ -17604,58 +17807,702 @@ function createRun(opts, globals) {
17604
17807
  };
17605
17808
  }
17606
17809
 
17607
- // src/lib/stderr-log.ts
17608
- var import_node_fs23 = require("node:fs");
17609
- var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
17610
- var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
17611
- function scrub(s) {
17612
- return s.replace(TOKEN_RE2, "verity_***REDACTED***").replace(ANSI_RE, "");
17810
+ // src/lib/repo-context.ts
17811
+ var import_node_child_process7 = require("node:child_process");
17812
+ var import_node_os3 = require("node:os");
17813
+ function rgInvocations(env = process.env) {
17814
+ const out = [{ cmd: "rg" }];
17815
+ if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
17816
+ out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
17817
+ return out;
17613
17818
  }
17614
- var installed = false;
17615
- var wroteBanner = false;
17616
- var banner = "";
17617
- function append(text) {
17618
- try {
17619
- const dir = projectPath(DEBUG_LOG_DIR);
17620
- const file = projectPath(STDERR_LOG_FILE);
17621
- (0, import_node_fs23.mkdirSync)(dir, { recursive: true });
17622
- rotateIfNeeded(file);
17623
- (0, import_node_fs23.appendFileSync)(file, text);
17624
- } catch {
17819
+ var MAX_SYMBOLS = 12;
17820
+ var MAX_SITES = 24;
17821
+ var MAX_SITES_PER_FILE = 3;
17822
+ var MAX_HITS_PER_SYMBOL = 50;
17823
+ var MAX_TEST_SLOTS = 8;
17824
+ var SITE_TEXT_MAX = 160;
17825
+ var ENCLOSING_SCAN_LINES = 200;
17826
+ var RG_TIMEOUT_MS = 1500;
17827
+ var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
17828
+ var STOPLIST = /* @__PURE__ */ new Set([
17829
+ // keyword-shaped captures
17830
+ "if",
17831
+ "for",
17832
+ "while",
17833
+ "switch",
17834
+ "catch",
17835
+ "return",
17836
+ "function",
17837
+ "class",
17838
+ "const",
17839
+ "let",
17840
+ "var",
17841
+ "new",
17842
+ "else",
17843
+ "try",
17844
+ "finally",
17845
+ "throw",
17846
+ "await",
17847
+ "async",
17848
+ "yield",
17849
+ "delete",
17850
+ "typeof",
17851
+ "instanceof",
17852
+ "void",
17853
+ "this",
17854
+ "super",
17855
+ "import",
17856
+ "export",
17857
+ "default",
17858
+ "extends",
17859
+ "implements",
17860
+ "interface",
17861
+ "enum",
17862
+ "type",
17863
+ "public",
17864
+ "private",
17865
+ "protected",
17866
+ "static",
17867
+ "get",
17868
+ "set",
17869
+ "constructor",
17870
+ "def",
17871
+ "elif",
17872
+ "lambda",
17873
+ "with",
17874
+ "pass",
17875
+ "self",
17876
+ "cls",
17877
+ "not",
17878
+ "and",
17879
+ "or",
17880
+ "raise",
17881
+ "except",
17882
+ "func",
17883
+ "defer",
17884
+ "chan",
17885
+ "select",
17886
+ "range",
17887
+ "module",
17888
+ "struct",
17889
+ "trait",
17890
+ "impl",
17891
+ "using",
17892
+ "namespace",
17893
+ // universal noise
17894
+ "main",
17895
+ "init",
17896
+ "index",
17897
+ "data",
17898
+ "value",
17899
+ "result",
17900
+ "item",
17901
+ "name",
17902
+ "key",
17903
+ "run",
17904
+ "test",
17905
+ "setup",
17906
+ "update",
17907
+ "create",
17908
+ "handle",
17909
+ "check",
17910
+ "load",
17911
+ "save",
17912
+ "list",
17913
+ "map",
17914
+ "args",
17915
+ "params",
17916
+ "props",
17917
+ "state",
17918
+ "error",
17919
+ "err",
17920
+ "res",
17921
+ "req",
17922
+ "ctx",
17923
+ "config",
17924
+ "options",
17925
+ "util",
17926
+ "utils",
17927
+ "helper",
17928
+ "render",
17929
+ "build",
17930
+ "parse",
17931
+ "format",
17932
+ "apply",
17933
+ "process",
17934
+ "start",
17935
+ "stop"
17936
+ ]);
17937
+ var TS_RULES = [
17938
+ /\b(?:function|class|interface|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/,
17939
+ /\btype\s+([A-Za-z_][A-Za-z0-9_]*)\s*=/,
17940
+ // const foo = (…) => · const foo = x => · const foo = function.
17941
+ // ⚠ REQUIRES the arrow or `function` ON THE LINE. The first version accepted
17942
+ // any `= (` — and `const started = (rows?.[0] as Row)?.at` is a PARENTHESIZED
17943
+ // CAST, not a function. Replayed over 12 real commits, that one shape put
17944
+ // three local variables into the symbol set per commit. A multi-line arrow
17945
+ // is the accepted false negative; a cast is not an accepted false positive.
17946
+ /\b(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::[^=\n]+)?=\s*(?:async\s+)?(?:function\b|\([^)]*\)(?:\s*:[^=\n]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,
17947
+ // method shape: name(…) { — keyword captures die at the stoplist
17948
+ /^\s*(?:(?:public|private|protected|static|readonly|async|override)\s+)*(?:\*\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*(?::[^{;\n]+)?\s*\{/
17949
+ ];
17950
+ var PY_RULES = [
17951
+ /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/,
17952
+ /^\s*class\s+([A-Za-z_]\w*)/
17953
+ ];
17954
+ var GO_RULES = [
17955
+ /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/,
17956
+ /^type\s+([A-Za-z_]\w*)/
17957
+ ];
17958
+ var CLIKE_RULES = [
17959
+ /\b(?:class|interface|enum|record|struct)\s+([A-Za-z_]\w*)/,
17960
+ // access-modifier method shape: `public async Task<Foo> BarBaz(…`
17961
+ /(?:public|private|protected|internal|static|final|virtual|override|sealed|abstract)[\w<>[\],?\s]*?\s([A-Za-z_]\w*)\s*\(/
17962
+ ];
17963
+ var RB_RULES = [
17964
+ /^\s*def\s+(?:self\.)?([A-Za-z_]\w*)/,
17965
+ /^\s*(?:class|module)\s+([A-Z]\w*)/
17966
+ ];
17967
+ var RS_RULES = [
17968
+ /\bfn\s+([A-Za-z_]\w*)/,
17969
+ /\b(?:struct|enum|trait)\s+([A-Za-z_]\w*)/
17970
+ ];
17971
+ var PHP_RULES = [
17972
+ /\bfunction\s+([A-Za-z_]\w*)/,
17973
+ /\bclass\s+([A-Za-z_]\w*)/
17974
+ ];
17975
+ var C_RULES = [
17976
+ /^(?:static\s+|inline\s+|extern\s+|constexpr\s+)*(?:struct\s+|enum\s+|union\s+|unsigned\s+|const\s+)*[A-Za-z_]\w*(?:\s*[*&]+\s*|\s+)([A-Za-z_]\w*)\s*\([^;]*$/,
17977
+ /\b(?:struct|enum|union|class)\s+([A-Za-z_]\w*)/,
17978
+ /::\s*~?([A-Za-z_]\w*)\s*\([^;]*$/
17979
+ // out-of-line C++ method definition
17980
+ ];
17981
+ var SH_RULES = [
17982
+ /^\s*(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/,
17983
+ /^function\s+([A-Za-z_]\w*)/
17984
+ ];
17985
+ var SQL_RULES = [
17986
+ /\bcreate\s+(?:or\s+replace\s+)?(?:table|view|materialized\s+view|function|procedure|index|trigger|type|policy)\s+(?:if\s+not\s+exists\s+)?(?:[\w".]*\.)?"?([A-Za-z_]\w*)"?/i
17987
+ ];
17988
+ var TF_RULES = [
17989
+ /^\s*(?:resource|data)\s+"[^"]+"\s+"([A-Za-z_]\w*)"/,
17990
+ /^\s*(?:module|variable|output)\s+"([A-Za-z_]\w*)"/
17991
+ ];
17992
+ var SWIFT_RULES = [
17993
+ /\bfunc\s+([A-Za-z_]\w*)/,
17994
+ /\b(?:class|struct|enum|protocol|extension|actor)\s+([A-Za-z_]\w*)/
17995
+ ];
17996
+ var DART_RULES = [
17997
+ /\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/,
17998
+ /^\s*(?:static\s+)?(?:Future<[^>]*>|Stream<[^>]*>|void|int|double|bool|String|num|dynamic|[A-Z]\w*(?:<[^>]*>)?)\s+([a-z_]\w*)\s*\(/
17999
+ ];
18000
+ var LUA_RULES = [
18001
+ /^\s*(?:local\s+)?function\s+(?:[\w.]+[.:])?([A-Za-z_]\w*)/
18002
+ ];
18003
+ var EX_RULES = [
18004
+ /^\s*def(?:p|macro)?\s+([a-z_]\w*)/,
18005
+ /^\s*defmodule\s+(?:[\w.]*\.)?([A-Z]\w*)/
18006
+ ];
18007
+ var PROTO_RULES = [
18008
+ /^\s*(?:message|service|enum)\s+([A-Za-z_]\w*)/,
18009
+ /^\s*rpc\s+([A-Za-z_]\w*)/
18010
+ ];
18011
+ var GRAPHQL_RULES = [
18012
+ /^\s*(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/
18013
+ ];
18014
+ var RULES_BY_EXT = {
18015
+ ts: TS_RULES,
18016
+ tsx: TS_RULES,
18017
+ js: TS_RULES,
18018
+ jsx: TS_RULES,
18019
+ mjs: TS_RULES,
18020
+ cjs: TS_RULES,
18021
+ svelte: TS_RULES,
18022
+ vue: TS_RULES,
18023
+ // script blocks
18024
+ py: PY_RULES,
18025
+ go: GO_RULES,
18026
+ java: CLIKE_RULES,
18027
+ cs: CLIKE_RULES,
18028
+ kt: CLIKE_RULES,
18029
+ scala: CLIKE_RULES,
18030
+ rb: RB_RULES,
18031
+ rs: RS_RULES,
18032
+ php: PHP_RULES,
18033
+ c: C_RULES,
18034
+ cpp: C_RULES,
18035
+ cc: C_RULES,
18036
+ h: C_RULES,
18037
+ hpp: C_RULES,
18038
+ sh: SH_RULES,
18039
+ bash: SH_RULES,
18040
+ zsh: SH_RULES,
18041
+ sql: SQL_RULES,
18042
+ tf: TF_RULES,
18043
+ hcl: TF_RULES,
18044
+ swift: SWIFT_RULES,
18045
+ dart: DART_RULES,
18046
+ lua: LUA_RULES,
18047
+ ex: EX_RULES,
18048
+ exs: EX_RULES,
18049
+ proto: PROTO_RULES,
18050
+ graphql: GRAPHQL_RULES,
18051
+ gql: GRAPHQL_RULES
18052
+ };
18053
+ var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
18054
+ function parseDiffSignals(diff) {
18055
+ const addedRanges = [];
18056
+ const touchPoints = [];
18057
+ const deletedLines = [];
18058
+ let newLine = 0;
18059
+ let oldRemaining = 0;
18060
+ let newRemaining = 0;
18061
+ let runStart = -1;
18062
+ let deletionRun = false;
18063
+ const closeAddedRun = () => {
18064
+ if (runStart >= 0) addedRanges.push([runStart, newLine - 1]);
18065
+ runStart = -1;
18066
+ };
18067
+ const closeDeletionRun = () => {
18068
+ if (deletionRun) touchPoints.push(Math.max(1, newLine));
18069
+ deletionRun = false;
18070
+ };
18071
+ for (const line of diff.split("\n")) {
18072
+ const inHunk = oldRemaining > 0 || newRemaining > 0;
18073
+ if (!inHunk) {
18074
+ closeAddedRun();
18075
+ closeDeletionRun();
18076
+ const header = HUNK_HEADER.exec(line);
18077
+ if (header) {
18078
+ newLine = parseInt(header[3], 10);
18079
+ oldRemaining = header[2] === void 0 ? 1 : parseInt(header[2], 10);
18080
+ newRemaining = header[4] === void 0 ? 1 : parseInt(header[4], 10);
18081
+ if (newRemaining === 0) newLine = Math.max(1, newLine);
18082
+ }
18083
+ continue;
18084
+ }
18085
+ if (line.startsWith("\\")) continue;
18086
+ if (line.startsWith("+") && newRemaining > 0) {
18087
+ deletionRun = false;
18088
+ if (runStart < 0) runStart = newLine;
18089
+ newLine++;
18090
+ newRemaining--;
18091
+ continue;
18092
+ }
18093
+ if (line.startsWith("-") && oldRemaining > 0) {
18094
+ closeAddedRun();
18095
+ deletionRun = true;
18096
+ deletedLines.push(line.slice(1));
18097
+ oldRemaining--;
18098
+ continue;
18099
+ }
18100
+ closeAddedRun();
18101
+ closeDeletionRun();
18102
+ newLine++;
18103
+ if (oldRemaining > 0) oldRemaining--;
18104
+ if (newRemaining > 0) newRemaining--;
17625
18105
  }
18106
+ closeAddedRun();
18107
+ closeDeletionRun();
18108
+ return { addedRanges, touchPoints, deletedLines };
17626
18109
  }
17627
- function ensureBanner() {
17628
- if (wroteBanner) return;
17629
- wroteBanner = true;
17630
- append(banner);
18110
+ function declNameOn(line, rules) {
18111
+ for (const r of rules) {
18112
+ const m = r.exec(line);
18113
+ if (m?.[1]) return m[1];
18114
+ }
18115
+ return null;
17631
18116
  }
17632
- function installStderrLog(cmd, argv, version) {
17633
- if (installed || !isDebugEnabled()) return;
17634
- installed = true;
17635
- banner = `
17636
- \u2501\u2501 verity ${cmd} \xB7 ${(/* @__PURE__ */ new Date()).toISOString()} \xB7 pid ${process.pid}
17637
- v${version} \xB7 ${process.cwd()}
17638
- argv: ${scrub(argv.join(" "))}
17639
- `;
17640
- const original = process.stderr.write.bind(process.stderr);
17641
- const tee = (...args) => {
17642
- const result = original(...args);
17643
- try {
17644
- const chunk = args[0];
17645
- const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
17646
- ensureBanner();
17647
- append(scrub(text));
17648
- } catch {
18117
+ function acceptable(name) {
18118
+ if (!name || !IDENTIFIER.test(name) || STOPLIST.has(name.toLowerCase())) return false;
18119
+ if (name.length < 3) return false;
18120
+ if (name.length === 3 && name === name.toLowerCase() && !name.includes("_")) return false;
18121
+ return true;
18122
+ }
18123
+ function isMultiSegment(name) {
18124
+ return /[a-z][A-Z]/.test(name) || name.includes("_");
18125
+ }
18126
+ function extractFileSymbols(path, content, signals) {
18127
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18128
+ const rules = RULES_BY_EXT[ext];
18129
+ if (!rules) return [];
18130
+ if (signals.addedRanges.length === 0 && signals.touchPoints.length === 0 && signals.deletedLines.length === 0) return [];
18131
+ const lines = content.split("\n");
18132
+ const found = [];
18133
+ const seen = /* @__PURE__ */ new Set();
18134
+ const add = (name) => {
18135
+ if (acceptable(name) && !seen.has(name)) {
18136
+ seen.add(name);
18137
+ found.push(name);
17649
18138
  }
17650
- return result;
17651
18139
  };
17652
- process.stderr.write = tee;
18140
+ for (const deleted of signals.deletedLines) {
18141
+ add(declNameOn(deleted, rules));
18142
+ }
18143
+ for (const [start, end] of signals.addedRanges) {
18144
+ for (let n = start; n <= Math.min(end, lines.length); n++) {
18145
+ add(declNameOn(lines[n - 1] ?? "", rules));
18146
+ }
18147
+ }
18148
+ const scanStarts = [
18149
+ ...signals.addedRanges.map(([start]) => start),
18150
+ ...signals.touchPoints
18151
+ ];
18152
+ for (const start of scanStarts) {
18153
+ const floor = Math.max(1, start - ENCLOSING_SCAN_LINES);
18154
+ for (let n = Math.min(start, lines.length); n >= floor; n--) {
18155
+ const name = declNameOn(lines[n - 1] ?? "", rules);
18156
+ if (acceptable(name)) {
18157
+ add(name);
18158
+ break;
18159
+ }
18160
+ }
18161
+ }
18162
+ return found;
17653
18163
  }
17654
- function logToFileOnly(text) {
17655
- if (!installed) return;
17656
- ensureBanner();
17657
- append(text.endsWith("\n") ? text : `${text}
17658
- `);
18164
+ function rankSymbols(symbols) {
18165
+ return symbols.map((s, i) => ({ s, i })).sort((a, b) => {
18166
+ const seg = Number(isMultiSegment(b.s)) - Number(isMultiSegment(a.s));
18167
+ if (seg !== 0) return seg;
18168
+ if (b.s.length !== a.s.length) return b.s.length - a.s.length;
18169
+ return a.i - b.i;
18170
+ }).slice(0, MAX_SYMBOLS).map((x) => x.s);
18171
+ }
18172
+ var TEST_PATH = /(^|\/)(tests?|specs?|__tests__|e2e)(\/|$)/i;
18173
+ var TEST_FILE = /(\.(test|spec|e2e)\.[^./]+|_test\.[^./]+|_spec\.rb)$/i;
18174
+ var TEST_PY_PREFIX = /(^|\/)test_[^/]+\.py$/i;
18175
+ var IMPORT_LINE = /^\s*(import\s|from\s+\S+\s+import\s|const\s+.*=\s*require\s*\(|require\s*\(|using\s+[\w.]+;|#include|export\s+\{[^}]*\}\s+from|export\s+\*\s+from)/;
18176
+ var BARE_MEMBER_LINE = /^(type\s+)?[A-Za-z_$][\w$]*\s*,?$/;
18177
+ var GO_IMPORT_PATH_LINE = /^"[^"]+",?$/;
18178
+ var SITE_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
18179
+ "ts",
18180
+ "tsx",
18181
+ "js",
18182
+ "jsx",
18183
+ "mjs",
18184
+ "cjs",
18185
+ "py",
18186
+ "go",
18187
+ "java",
18188
+ "kt",
18189
+ "rb",
18190
+ "rs",
18191
+ "scala",
18192
+ "c",
18193
+ "cpp",
18194
+ "cc",
18195
+ "h",
18196
+ "hpp",
18197
+ "cs",
18198
+ "php",
18199
+ "swift",
18200
+ "dart",
18201
+ "lua",
18202
+ "sh",
18203
+ "bash",
18204
+ "zsh",
18205
+ "svelte",
18206
+ "vue",
18207
+ "ex",
18208
+ "exs",
18209
+ // sql/tf carry REAL call sites (SELECT my_function(...), module.name) —
18210
+ // excluded in an earlier round because of migration-comment noise, which the
18211
+ // COMMENT_LINE filter now handles on its own.
18212
+ "sql",
18213
+ "tf",
18214
+ "hcl"
18215
+ ]);
18216
+ var COMMENT_LINE = /^(\/\/|#(?!\[)|\*|\/\*|--\s|<!--)/;
18217
+ function isCodeSiteFile(path) {
18218
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18219
+ return SITE_CODE_EXTENSIONS.has(ext);
18220
+ }
18221
+ function isTestPath(path) {
18222
+ return TEST_PATH.test(path) || TEST_FILE.test(path) || TEST_PY_PREFIX.test(path);
18223
+ }
18224
+ function parseRgLine(line) {
18225
+ const first = line.indexOf(":");
18226
+ if (first <= 0) return null;
18227
+ const second = line.indexOf(":", first + 1);
18228
+ if (second < 0) return null;
18229
+ const n = parseInt(line.slice(first + 1, second), 10);
18230
+ if (!Number.isFinite(n) || n < 1) return null;
18231
+ return { file: line.slice(0, first), line: n, text: line.slice(second + 1) };
18232
+ }
18233
+ var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
18234
+ function wordHit(text, symbol) {
18235
+ let from = 0;
18236
+ for (; ; ) {
18237
+ const at = text.indexOf(symbol, from);
18238
+ if (at < 0) return false;
18239
+ const before = at === 0 ? void 0 : text[at - 1];
18240
+ const after = text[at + symbol.length];
18241
+ if (!isWordChar(before) && !isWordChar(after)) return true;
18242
+ from = at + 1;
18243
+ }
18244
+ }
18245
+ function isContractLine(text, symbol) {
18246
+ for (const kw of ["implements", "extends"]) {
18247
+ let from = 0;
18248
+ for (; ; ) {
18249
+ const at = text.indexOf(kw, from);
18250
+ if (at < 0) break;
18251
+ from = at + 1;
18252
+ const before = at === 0 ? void 0 : text[at - 1];
18253
+ const after = text[at + kw.length];
18254
+ if (isWordChar(before) || isWordChar(after)) continue;
18255
+ let clause = text.slice(at + kw.length);
18256
+ const stop = Math.min(
18257
+ ...[clause.indexOf(";"), clause.indexOf("{")].filter((i) => i >= 0)
18258
+ );
18259
+ if (Number.isFinite(stop)) clause = clause.slice(0, stop);
18260
+ if (wordHit(clause, symbol)) return true;
18261
+ }
18262
+ }
18263
+ return false;
18264
+ }
18265
+ function partitionSites(rgLines, symbols, opts) {
18266
+ const hits = [];
18267
+ const hitCount = /* @__PURE__ */ new Map();
18268
+ for (const line of rgLines) {
18269
+ const hit = parseRgLine(line);
18270
+ if (!hit) continue;
18271
+ const matched = symbols.filter((s) => wordHit(hit.text, s));
18272
+ for (const s of matched) hitCount.set(s, (hitCount.get(s) ?? 0) + 1);
18273
+ if (matched.length === 0) continue;
18274
+ hits.push({ ...hit, symbol: matched[0] });
18275
+ }
18276
+ hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
18277
+ const dropped = symbols.filter((s) => (hitCount.get(s) ?? 0) > MAX_HITS_PER_SYMBOL);
18278
+ const droppedSet = new Set(dropped);
18279
+ const perFile = /* @__PURE__ */ new Map();
18280
+ const callers = [];
18281
+ const tests = [];
18282
+ for (const h of hits) {
18283
+ if (droppedSet.has(h.symbol)) continue;
18284
+ if (opts.sentPaths.has(h.file)) continue;
18285
+ if (opts.isExcluded(h.file)) continue;
18286
+ if (!isCodeSiteFile(h.file)) continue;
18287
+ const text = h.text.trim();
18288
+ if (text.length === 0 || IMPORT_LINE.test(h.text)) continue;
18289
+ if (COMMENT_LINE.test(text)) continue;
18290
+ if (BARE_MEMBER_LINE.test(text) || GO_IMPORT_PATH_LINE.test(text)) continue;
18291
+ const n = perFile.get(h.file) ?? 0;
18292
+ if (n >= MAX_SITES_PER_FILE) continue;
18293
+ const site = {
18294
+ file: h.file,
18295
+ line: h.line,
18296
+ text: text.slice(0, SITE_TEXT_MAX),
18297
+ symbol: h.symbol,
18298
+ // R2 falls out of R1 for free: a word search for `Sym` already matches
18299
+ // `implements Sym` / `extends Sym` lines — classification is all R2 is.
18300
+ ...isContractLine(text, h.symbol) ? { kind: "contract" } : {}
18301
+ };
18302
+ if (isTestPath(h.file)) {
18303
+ if (tests.length < MAX_TEST_SLOTS) {
18304
+ tests.push(site);
18305
+ perFile.set(h.file, n + 1);
18306
+ }
18307
+ } else if (callers.length + tests.length < MAX_SITES) {
18308
+ callers.push(site);
18309
+ perFile.set(h.file, n + 1);
18310
+ }
18311
+ }
18312
+ while (callers.length + tests.length > MAX_SITES) callers.pop();
18313
+ return { callers, tests, dropped };
18314
+ }
18315
+ function buildRepoContext(input) {
18316
+ const started = Date.now();
18317
+ let signalsByPath;
18318
+ if (input.signalsByPath) {
18319
+ if (input.signalsByPath.size === 0) return { state: "absent", reason: "no-diffs" };
18320
+ signalsByPath = input.signalsByPath;
18321
+ } else {
18322
+ if (input.diffs.length === 0) return { state: "absent", reason: "no-diffs" };
18323
+ signalsByPath = /* @__PURE__ */ new Map();
18324
+ for (const d of input.diffs) signalsByPath.set(d.path, parseDiffSignals(d.diff));
18325
+ }
18326
+ const collected = [];
18327
+ const unsupported = /* @__PURE__ */ new Set();
18328
+ const noteUnsupported = (path) => {
18329
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18330
+ if (ext && !RULES_BY_EXT[ext]) unsupported.add(ext);
18331
+ };
18332
+ const deltaPathSet = new Set(input.deltaFiles.map((f) => f.path));
18333
+ for (const f of input.deltaFiles) {
18334
+ const signals = signalsByPath.get(f.path);
18335
+ if (!signals) continue;
18336
+ noteUnsupported(f.path);
18337
+ collected.push(...extractFileSymbols(f.path, f.content, signals));
18338
+ }
18339
+ for (const [path, signals] of signalsByPath) {
18340
+ if (deltaPathSet.has(path)) continue;
18341
+ if (signals.deletedLines.length > 0) {
18342
+ noteUnsupported(path);
18343
+ collected.push(...extractFileSymbols(path, "", signals));
18344
+ }
18345
+ }
18346
+ const unsupportedExts = [...unsupported].sort().slice(0, 8);
18347
+ const audit = unsupportedExts.length > 0 ? { unsupported_exts: unsupportedExts } : {};
18348
+ const symbols = rankSymbols([...new Set(collected)]);
18349
+ if (symbols.length === 0) {
18350
+ const everySupportedFileFoundNothing = unsupportedExts.length > 0;
18351
+ return {
18352
+ state: "absent",
18353
+ reason: everySupportedFileFoundNothing ? "unsupported-language" : "no-symbols",
18354
+ ...audit
18355
+ };
18356
+ }
18357
+ const args = [
18358
+ "-n",
18359
+ "-w",
18360
+ "-F",
18361
+ "--no-heading",
18362
+ "--color",
18363
+ "never",
18364
+ // ⚠ NO `--sort path` — it single-threads rg, and on a large worktree that
18365
+ // is the difference between 17ms and a timeout. Determinism is restored by
18366
+ // sorting the hits in partitionSites instead.
18367
+ //
18368
+ // `-m 8` bounds output PER FILE (shared across all patterns), so a noisy
18369
+ // repo cannot blow the 4MB read buffer and turn the whole feature into
18370
+ // `absent/error`. Cost, accepted: the frequency gate sees per-file-capped
18371
+ // counts, so a symbol concentrated in a handful of files can slip a gate
18372
+ // a full count would have tripped — but the ≤3-sites-per-file cap already
18373
+ // bounds exactly that shape's damage; the gate exists for the many-file
18374
+ // 'init' shape, which 8-per-file still trips (>6 files ⇒ >50).
18375
+ "-m",
18376
+ "8",
18377
+ "--max-columns",
18378
+ "300",
18379
+ "--max-columns-preview",
18380
+ ...symbols.flatMap((s) => ["-e", s]),
18381
+ "-g",
18382
+ "!**/{dist,build,out,vendor,node_modules,.git,coverage,target,__pycache__}/**",
18383
+ "./"
18384
+ ];
18385
+ let res = null;
18386
+ for (const inv of rgInvocations()) {
18387
+ res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
18388
+ ...inv.argv0 ? { argv0: inv.argv0 } : {},
18389
+ cwd: input.cwd ?? process.cwd(),
18390
+ timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
18391
+ maxBuffer: 4 * 1024 * 1024,
18392
+ encoding: "utf8"
18393
+ });
18394
+ if (res.error?.code !== "ENOENT") break;
18395
+ }
18396
+ if (!res || res.error?.code === "ENOENT") {
18397
+ return { state: "absent", reason: "no-tool", symbols, ...audit };
18398
+ }
18399
+ if (res.error) {
18400
+ const code = res.error.code;
18401
+ if (code === "ETIMEDOUT") return { state: "absent", reason: "timeout", symbols, ...audit };
18402
+ return { state: "absent", reason: "error", symbols, ...audit };
18403
+ }
18404
+ if (res.signal) return { state: "absent", reason: "timeout", symbols, ...audit };
18405
+ if (res.status !== 0 && res.status !== 1) return { state: "absent", reason: "error", symbols, ...audit };
18406
+ const lines = (res.stdout ?? "").split("\n").map((l) => l.replace(/^\.\//, "")).filter(Boolean);
18407
+ const { callers, tests, dropped } = partitionSites(lines, symbols, {
18408
+ sentPaths: input.sentPaths,
18409
+ isExcluded: input.isExcluded
18410
+ });
18411
+ if (callers.length === 0 && tests.length === 0) {
18412
+ return {
18413
+ state: "absent",
18414
+ reason: "no-sites",
18415
+ symbols,
18416
+ ...dropped.length > 0 ? { dropped_symbols: dropped } : {},
18417
+ ...audit,
18418
+ elapsed_ms: Date.now() - started
18419
+ };
18420
+ }
18421
+ return {
18422
+ state: "ok",
18423
+ symbols,
18424
+ ...dropped.length > 0 ? { dropped_symbols: dropped } : {},
18425
+ ...audit,
18426
+ callers,
18427
+ tests,
18428
+ elapsed_ms: Date.now() - started
18429
+ };
18430
+ }
18431
+ var MAX_EXCERPTS = 12;
18432
+ var MAX_EXCERPTS_PER_FILE = 2;
18433
+ var EXCERPT_MAX_LINES = 30;
18434
+ var EXCERPT_MAX_CHARS = 2400;
18435
+ var EXCERPT_TOTAL_BYTES = 24576;
18436
+ var EXCERPT_DECL_SCAN = 40;
18437
+ function extractEnclosingExcerpt(content, siteLine, path) {
18438
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
18439
+ const rules = RULES_BY_EXT[ext] ?? [];
18440
+ const lines = content.split("\n");
18441
+ if (siteLine < 1 || siteLine > lines.length) return null;
18442
+ let declStart = null;
18443
+ const floor = Math.max(1, siteLine - EXCERPT_DECL_SCAN);
18444
+ for (let n = siteLine; n >= floor; n--) {
18445
+ if (declNameOn(lines[n - 1] ?? "", rules) !== null) {
18446
+ declStart = n;
18447
+ break;
18448
+ }
18449
+ }
18450
+ let start;
18451
+ if (declStart !== null && siteLine - declStart < EXCERPT_MAX_LINES) {
18452
+ start = declStart;
18453
+ } else {
18454
+ start = Math.max(1, siteLine - (EXCERPT_MAX_LINES - 6));
18455
+ }
18456
+ const end = Math.min(lines.length, start + EXCERPT_MAX_LINES - 1);
18457
+ const text = lines.slice(start - 1, end).join("\n").slice(0, EXCERPT_MAX_CHARS);
18458
+ return { start_line: start, text };
18459
+ }
18460
+ function upgradeToExcerpts(rc, opts) {
18461
+ if (rc.state !== "ok") return;
18462
+ const ranked = [
18463
+ ...(rc.callers ?? []).filter((s) => s.kind === "contract"),
18464
+ ...rc.tests ?? [],
18465
+ ...(rc.callers ?? []).filter((s) => s.kind !== "contract")
18466
+ ];
18467
+ const perFile = /* @__PURE__ */ new Map();
18468
+ const contentCache = /* @__PURE__ */ new Map();
18469
+ const excerpts = [];
18470
+ let totalBytes = 0;
18471
+ for (const site of ranked) {
18472
+ if (excerpts.length >= MAX_EXCERPTS) break;
18473
+ const used = perFile.get(site.file) ?? 0;
18474
+ if (used >= MAX_EXCERPTS_PER_FILE) continue;
18475
+ if (!contentCache.has(site.file)) contentCache.set(site.file, opts.readFile(site.file));
18476
+ const content = contentCache.get(site.file);
18477
+ if (content === null || content === void 0) continue;
18478
+ const ex = extractEnclosingExcerpt(content, site.line, site.file);
18479
+ if (!ex) continue;
18480
+ if (totalBytes + ex.text.length > EXCERPT_TOTAL_BYTES) break;
18481
+ excerpts.push({
18482
+ file: site.file,
18483
+ start_line: ex.start_line,
18484
+ symbol: site.symbol,
18485
+ kind: site.kind === "contract" ? "contract" : isTestPath(site.file) ? "test" : "caller",
18486
+ text: ex.text
18487
+ });
18488
+ totalBytes += ex.text.length;
18489
+ perFile.set(site.file, used + 1);
18490
+ }
18491
+ if (excerpts.length > 0) rc.excerpts = excerpts;
18492
+ }
18493
+ function describeRepoContext(rc) {
18494
+ if (rc.state !== "ok") {
18495
+ const exts = rc.unsupported_exts?.length ? ` \xB7 no rules for: ${rc.unsupported_exts.join(", ")}` : "";
18496
+ return `absent (${rc.reason ?? "unknown"})${exts}`;
18497
+ }
18498
+ const parts = [
18499
+ `${rc.symbols?.length ?? 0} symbol(s) \u2192 ${rc.callers?.length ?? 0} caller(s) \xB7 ${rc.tests?.length ?? 0} test(s)`
18500
+ ];
18501
+ if (rc.excerpts?.length) parts.push(`${rc.excerpts.length} excerpt(s)`);
18502
+ if (rc.dropped_symbols?.length) parts.push(`dropped too-common: ${rc.dropped_symbols.join(", ")}`);
18503
+ if (rc.unsupported_exts?.length) parts.push(`no rules for: ${rc.unsupported_exts.join(", ")}`);
18504
+ if (typeof rc.elapsed_ms === "number") parts.push(`${rc.elapsed_ms}ms`);
18505
+ return parts.join(" \xB7 ");
17659
18506
  }
17660
18507
 
17661
18508
  // src/commands/analyze/evidence-log.ts
@@ -17696,6 +18543,7 @@ function formatRunEvidence(run2, startedAt) {
17696
18543
  }
17697
18544
  out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
17698
18545
  if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
18546
+ if (run2.repoContext) out += row("repo", describeRepoContext(run2.repoContext));
17699
18547
  const withheld = run2.reviewCoverage.notReviewed;
17700
18548
  if (withheld.length > 0) {
17701
18549
  const byReason = /* @__PURE__ */ new Map();
@@ -17777,9 +18625,9 @@ function installRunEvidence(run2) {
17777
18625
  }
17778
18626
 
17779
18627
  // src/lib/git-frame.ts
17780
- var import_node_child_process7 = require("node:child_process");
17781
- var import_node_fs24 = require("node:fs");
17782
- var import_node_os3 = require("node:os");
18628
+ var import_node_child_process8 = require("node:child_process");
18629
+ var import_node_fs26 = require("node:fs");
18630
+ var import_node_os4 = require("node:os");
17783
18631
  var import_node_path18 = require("node:path");
17784
18632
  var import_node_path19 = require("node:path");
17785
18633
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
@@ -17827,14 +18675,14 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
17827
18675
  if (!m) continue;
17828
18676
  named = true;
17829
18677
  if (m[1] === void 0) {
17830
- dir = (0, import_node_os3.homedir)();
18678
+ dir = (0, import_node_os4.homedir)();
17831
18679
  continue;
17832
18680
  }
17833
18681
  const raw = unquote(m[1]);
17834
18682
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
17835
18683
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
17836
18684
  }
17837
- const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
18685
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
17838
18686
  dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
17839
18687
  }
17840
18688
  const seg = segments[segmentIndex];
@@ -17853,7 +18701,7 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
17853
18701
  if (SHELL_DYNAMIC.test(raw)) {
17854
18702
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
17855
18703
  }
17856
- const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
18704
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
17857
18705
  dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
17858
18706
  }
17859
18707
  }
@@ -17910,21 +18758,21 @@ function parsePushTarget(segment) {
17910
18758
  }
17911
18759
  function gitAt(dir, args) {
17912
18760
  try {
17913
- return (0, import_node_child_process7.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
18761
+ return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
17914
18762
  } catch {
17915
18763
  return "";
17916
18764
  }
17917
18765
  }
17918
18766
  function realpathOr(p) {
17919
18767
  try {
17920
- return import_node_fs24.realpathSync.native(p);
18768
+ return import_node_fs26.realpathSync.native(p);
17921
18769
  } catch {
17922
18770
  return (0, import_node_path18.resolve)(p);
17923
18771
  }
17924
18772
  }
17925
18773
  function resolveFrame(input) {
17926
18774
  const found = findMomentSegment(input.command, input.on);
17927
- const hookDirUsable = !!input.hookCwd && (0, import_node_fs24.existsSync)(input.hookCwd);
18775
+ const hookDirUsable = !!input.hookCwd && (0, import_node_fs26.existsSync)(input.hookCwd);
17928
18776
  const baseDir = hookDirUsable ? input.hookCwd : process.cwd();
17929
18777
  let anchor = hookDirUsable ? "hook-cwd" : "process-cwd";
17930
18778
  const refuse = (refusal) => ({
@@ -17947,7 +18795,7 @@ function resolveFrame(input) {
17947
18795
  if (dirs.size > 1) return refuse(`target:multiple ${found.moment} targets in one command`);
17948
18796
  const targetDir = dirs.size === 1 ? [...dirs][0] : baseDir;
17949
18797
  if (targetDir !== baseDir) {
17950
- if (!(0, import_node_fs24.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
18798
+ if (!(0, import_node_fs26.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
17951
18799
  dir = targetDir;
17952
18800
  }
17953
18801
  }
@@ -17986,7 +18834,7 @@ var SHA_RE2 = /^[0-9a-f]{40}$/;
17986
18834
  function baselineShaAt(frame) {
17987
18835
  if (!frame.worktreeRoot) return null;
17988
18836
  try {
17989
- const sha = (0, import_node_fs24.readFileSync)((0, import_node_path19.join)(frame.worktreeRoot, BASELINE_SHA_FILE), "utf-8").trim();
18837
+ const sha = (0, import_node_fs26.readFileSync)((0, import_node_path19.join)(frame.worktreeRoot, BASELINE_SHA_FILE), "utf-8").trim();
17990
18838
  if (!SHA_RE2.test(sha)) return null;
17991
18839
  return refResolves(frame, sha) ? sha : null;
17992
18840
  } catch {
@@ -18049,6 +18897,40 @@ function rangeFiles(frame, range) {
18049
18897
  }
18050
18898
  return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
18051
18899
  }
18900
+ function rangeChangeSignals(frame, range, paths) {
18901
+ const out = /* @__PURE__ */ new Map();
18902
+ if (range.kind === "nothing" || paths.length === 0) return out;
18903
+ const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
18904
+ const diff = frameGit(frame, [...args, "--", ...paths]);
18905
+ let current = null;
18906
+ let oldSide = null;
18907
+ let buf = [];
18908
+ const flush = () => {
18909
+ if (current !== null && buf.length > 0) out.set(current, parseDiffSignals(buf.join("\n")));
18910
+ buf = [];
18911
+ };
18912
+ for (const line of diff.split("\n")) {
18913
+ if (line.startsWith("diff --git ")) {
18914
+ flush();
18915
+ current = null;
18916
+ oldSide = null;
18917
+ continue;
18918
+ }
18919
+ const minusM = /^--- (?:a\/)?(.+)$/.exec(line);
18920
+ if (minusM) {
18921
+ oldSide = minusM[1] === "/dev/null" ? null : minusM[1];
18922
+ continue;
18923
+ }
18924
+ const plusM = /^\+\+\+ (?:b\/)?(.+)$/.exec(line);
18925
+ if (plusM) {
18926
+ current = plusM[1] === "/dev/null" ? oldSide : plusM[1];
18927
+ continue;
18928
+ }
18929
+ if (current !== null) buf.push(line);
18930
+ }
18931
+ flush();
18932
+ return out;
18933
+ }
18052
18934
  function rangeMessages(frame, range) {
18053
18935
  if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
18054
18936
  return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
@@ -18099,7 +18981,7 @@ function truthy(v) {
18099
18981
  }
18100
18982
 
18101
18983
  // src/lib/transcript.ts
18102
- var import_node_fs25 = require("node:fs");
18984
+ var import_node_fs27 = require("node:fs");
18103
18985
  var MAX_READ_BYTES = 256 * 1024;
18104
18986
  var SMALL_FILE_BYTES = 64 * 1024;
18105
18987
  var MAX_FILES_LIST = 20;
@@ -18108,6 +18990,7 @@ var MAX_COMMANDS = 10;
18108
18990
  var MAX_COMMAND_CHARS = 80;
18109
18991
  var MAX_TOOL_BLOCKS = 200;
18110
18992
  var MAX_SUMMARY_BYTES = 4096;
18993
+ var MAX_SEARCHES_DETAIL = 10;
18111
18994
  var HOME = process.env.HOME ?? "";
18112
18995
  var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
18113
18996
  var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
@@ -18125,7 +19008,7 @@ async function extractActionSummary(transcriptPath) {
18125
19008
  function readTurnLines(transcriptPath) {
18126
19009
  let size;
18127
19010
  try {
18128
- size = (0, import_node_fs25.statSync)(transcriptPath).size;
19011
+ size = (0, import_node_fs27.statSync)(transcriptPath).size;
18129
19012
  } catch {
18130
19013
  return null;
18131
19014
  }
@@ -18133,7 +19016,7 @@ function readTurnLines(transcriptPath) {
18133
19016
  let raw;
18134
19017
  let windowed = false;
18135
19018
  if (size <= SMALL_FILE_BYTES) {
18136
- raw = (0, import_node_fs25.readFileSync)(transcriptPath, "utf-8");
19019
+ raw = (0, import_node_fs27.readFileSync)(transcriptPath, "utf-8");
18137
19020
  } else {
18138
19021
  windowed = true;
18139
19022
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
@@ -18193,6 +19076,7 @@ function buildSummary(lines) {
18193
19076
  let userCommandsTruncated = false;
18194
19077
  let commandsTruncated = false;
18195
19078
  let searches = 0;
19079
+ const searchesDetail = [];
18196
19080
  let subagents = 0;
18197
19081
  let webFetches = 0;
18198
19082
  let totalToolCalls = 0;
@@ -18264,9 +19148,19 @@ function buildSummary(lines) {
18264
19148
  break;
18265
19149
  }
18266
19150
  case "Grep":
18267
- case "Glob":
19151
+ case "Glob": {
18268
19152
  searches++;
19153
+ const pattern = typeof input.pattern === "string" ? input.pattern.slice(0, 120) : "";
19154
+ if (pattern && searchesDetail.length < MAX_SEARCHES_DETAIL) {
19155
+ const scopePath = typeof input.path === "string" ? input.path.slice(0, 200) : void 0;
19156
+ searchesDetail.push({
19157
+ tool: toolName,
19158
+ pattern,
19159
+ ...scopePath ? { path: scopePath } : {}
19160
+ });
19161
+ }
18269
19162
  break;
19163
+ }
18270
19164
  case "Agent":
18271
19165
  case "Task":
18272
19166
  case "Workflow":
@@ -18301,6 +19195,7 @@ function buildSummary(lines) {
18301
19195
  ...cappedOut(filesCreated, MAX_CREATED_LIST)
18302
19196
  ],
18303
19197
  searches,
19198
+ ...searchesDetail.length > 0 ? { searches_detail: searchesDetail } : {},
18304
19199
  commands,
18305
19200
  ...commandsTruncated ? { commands_truncated: true } : {},
18306
19201
  user_commands: userCommands,
@@ -18311,6 +19206,9 @@ function buildSummary(lines) {
18311
19206
  turn_messages: turnMessages,
18312
19207
  turn_duration_ms: turnDurationMs
18313
19208
  };
19209
+ if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
19210
+ delete summary.searches_detail;
19211
+ }
18314
19212
  if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
18315
19213
  summary.commands = [];
18316
19214
  summary.commands_truncated = true;
@@ -18435,6 +19333,9 @@ async function bootstrap(run2) {
18435
19333
  isTTY: process.stdout.isTTY === true
18436
19334
  });
18437
19335
  const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
19336
+ if (deferredToPlugin("analyze", sessionId ?? process.env.CLAUDE_SESSION_ID ?? null)) {
19337
+ process.exit(0);
19338
+ }
18438
19339
  const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
18439
19340
  const tokenResult = await resolveToken(globals.token);
18440
19341
  const scopeToken = tokenResult.ok ? tokenResult.data.token : void 0;
@@ -18614,7 +19515,7 @@ function channelSilence(input) {
18614
19515
  // src/lib/cli-version.ts
18615
19516
  function cliVersion() {
18616
19517
  try {
18617
- return true ? "0.31.1-experimental.be74f71" : "dev";
19518
+ return true ? "0.31.2" : "dev";
18618
19519
  } catch {
18619
19520
  return "dev";
18620
19521
  }
@@ -18654,8 +19555,8 @@ async function sendSkipBeacon(ctx, reason) {
18654
19555
  }
18655
19556
 
18656
19557
  // src/lib/static-analysis.ts
18657
- var import_node_child_process8 = require("node:child_process");
18658
- var import_node_fs26 = require("node:fs");
19558
+ var import_node_child_process9 = require("node:child_process");
19559
+ var import_node_fs28 = require("node:fs");
18659
19560
  var SEVERITY_ORDER = {
18660
19561
  Error: 0,
18661
19562
  Critical: 0,
@@ -18667,7 +19568,7 @@ var SEVERITY_ORDER = {
18667
19568
  };
18668
19569
  function isCodacyAvailable() {
18669
19570
  try {
18670
- (0, import_node_child_process8.execSync)("which codacy-analysis", { stdio: "pipe" });
19571
+ (0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
18671
19572
  return true;
18672
19573
  } catch {
18673
19574
  return false;
@@ -18703,13 +19604,13 @@ function runCodacyAnalysis(files) {
18703
19604
  if (files.length === 0) return empty;
18704
19605
  const existingFiles = files.filter((f) => {
18705
19606
  try {
18706
- return (0, import_node_fs26.existsSync)(f);
19607
+ return (0, import_node_fs28.existsSync)(f);
18707
19608
  } catch {
18708
19609
  return false;
18709
19610
  }
18710
19611
  });
18711
19612
  if (existingFiles.length === 0) return empty;
18712
- const proc = (0, import_node_child_process8.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
19613
+ const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
18713
19614
  encoding: "utf-8",
18714
19615
  maxBuffer: 10 * 1024 * 1024
18715
19616
  });
@@ -18972,7 +19873,7 @@ async function scope(run2) {
18972
19873
  }
18973
19874
 
18974
19875
  // src/lib/specs.ts
18975
- var import_node_fs27 = require("node:fs");
19876
+ var import_node_fs29 = require("node:fs");
18976
19877
  var import_node_path20 = require("node:path");
18977
19878
  var SPEC_CANDIDATES = [
18978
19879
  "CLAUDE.md",
@@ -19004,16 +19905,16 @@ function discoverSpecs(consulted = []) {
19004
19905
  const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
19005
19906
  if (totalBytes >= totalCap) return false;
19006
19907
  if (seen.has(specPath)) return true;
19007
- if (!(0, import_node_fs27.existsSync)(specPath)) return true;
19908
+ if (!(0, import_node_fs29.existsSync)(specPath)) return true;
19008
19909
  seen.add(specPath);
19009
19910
  const remaining = totalCap - totalBytes;
19010
19911
  const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
19011
19912
  const readBytes = Math.min(fileCap, remaining);
19012
19913
  try {
19013
19914
  const buf = Buffer.alloc(readBytes);
19014
- const fd = (0, import_node_fs27.openSync)(specPath, "r");
19015
- const bytesRead = (0, import_node_fs27.readSync)(fd, buf, 0, readBytes, 0);
19016
- (0, import_node_fs27.closeSync)(fd);
19915
+ const fd = (0, import_node_fs29.openSync)(specPath, "r");
19916
+ const bytesRead = (0, import_node_fs29.readSync)(fd, buf, 0, readBytes, 0);
19917
+ (0, import_node_fs29.closeSync)(fd);
19017
19918
  const content = buf.slice(0, bytesRead).toString("utf-8");
19018
19919
  if (!content) return true;
19019
19920
  result.push({ path: specPath, content });
@@ -19029,7 +19930,7 @@ function discoverSpecs(consulted = []) {
19029
19930
  if (!addSpec(candidate)) break;
19030
19931
  }
19031
19932
  for (const dir of ["spec", "docs"]) {
19032
- if (!(0, import_node_fs27.existsSync)(dir)) continue;
19933
+ if (!(0, import_node_fs29.existsSync)(dir)) continue;
19033
19934
  try {
19034
19935
  const mdFiles = findMdFiles(dir, 2).sort();
19035
19936
  for (const mdFile of mdFiles) {
@@ -19044,7 +19945,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
19044
19945
  if (depth >= maxDepth) return [];
19045
19946
  const result = [];
19046
19947
  try {
19047
- const entries = (0, import_node_fs27.readdirSync)(dir, { withFileTypes: true });
19948
+ const entries = (0, import_node_fs29.readdirSync)(dir, { withFileTypes: true });
19048
19949
  for (const entry of entries) {
19049
19950
  const fullPath = (0, import_node_path20.join)(dir, entry.name);
19050
19951
  if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -19063,14 +19964,14 @@ function discoverPlans() {
19063
19964
  const candidates = [];
19064
19965
  const seen = /* @__PURE__ */ new Set();
19065
19966
  for (const plansDir of [localPlansDir, homePlansDir]) {
19066
- if (!(0, import_node_fs27.existsSync)(plansDir)) continue;
19967
+ if (!(0, import_node_fs29.existsSync)(plansDir)) continue;
19067
19968
  try {
19068
- for (const f of (0, import_node_fs27.readdirSync)(plansDir)) {
19969
+ for (const f of (0, import_node_fs29.readdirSync)(plansDir)) {
19069
19970
  if (!f.endsWith(".md") || seen.has(f)) continue;
19070
19971
  seen.add(f);
19071
19972
  const fullPath = (0, import_node_path20.join)(plansDir, f);
19072
19973
  try {
19073
- const stat3 = (0, import_node_fs27.statSync)(fullPath);
19974
+ const stat3 = (0, import_node_fs29.statSync)(fullPath);
19074
19975
  candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
19075
19976
  } catch {
19076
19977
  }
@@ -19083,7 +19984,7 @@ function discoverPlans() {
19083
19984
  for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
19084
19985
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
19085
19986
  try {
19086
- const content = (0, import_node_fs27.readFileSync)(entry.path, "utf-8");
19987
+ const content = (0, import_node_fs29.readFileSync)(entry.path, "utf-8");
19087
19988
  result.push({ name: entry.name, content });
19088
19989
  } catch {
19089
19990
  }
@@ -19098,12 +19999,12 @@ function discoverGuardDocs(rangeFiles2) {
19098
19999
  if (result.length >= MAX_SPEC_FILES) break;
19099
20000
  if (!GUARD_DOC_EXT.test(path)) continue;
19100
20001
  if (path.startsWith("/") || path.includes("..")) continue;
19101
- if (!(0, import_node_fs27.existsSync)(path)) continue;
20002
+ if (!(0, import_node_fs29.existsSync)(path)) continue;
19102
20003
  try {
19103
- const stat3 = (0, import_node_fs27.statSync)(path);
20004
+ const stat3 = (0, import_node_fs29.statSync)(path);
19104
20005
  if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
19105
20006
  if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
19106
- const content = (0, import_node_fs27.readFileSync)(path, "utf-8");
20007
+ const content = (0, import_node_fs29.readFileSync)(path, "utf-8");
19107
20008
  if (!content) continue;
19108
20009
  result.push({ name: path, content });
19109
20010
  totalBytes += content.length;
@@ -19214,7 +20115,6 @@ async function mode(run2) {
19214
20115
  const { opts, globals } = run2;
19215
20116
  const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
19216
20117
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
19217
- let contextFilePaths = [];
19218
20118
  let predictedMode;
19219
20119
  try {
19220
20120
  const memoryPath2 = sessionIdForMemory ? `/memory?session_id=${encodeURIComponent(sessionIdForMemory)}` : "/memory";
@@ -19228,9 +20128,6 @@ async function mode(run2) {
19228
20128
  cmd: "analyze_context"
19229
20129
  });
19230
20130
  if (memoryResult.ok) {
19231
- if (Array.isArray(memoryResult.data.context_files)) {
19232
- contextFilePaths = memoryResult.data.context_files;
19233
- }
19234
20131
  const rawMode = memoryResult.data.predicted_mode;
19235
20132
  if (rawMode && ["standard", "plan", "debug", "skip"].includes(rawMode)) {
19236
20133
  predictedMode = rawMode;
@@ -19279,11 +20176,11 @@ async function mode(run2) {
19279
20176
  turnAuthoredCode ? "capacity" : void 0
19280
20177
  );
19281
20178
  }
19282
- Object.assign(run2, { analysisMode, contextFilePaths, sessionAuthoredCode, sessionIdForMemory });
20179
+ Object.assign(run2, { analysisMode, sessionAuthoredCode, sessionIdForMemory });
19283
20180
  }
19284
20181
 
19285
20182
  // src/lib/fold.ts
19286
- var import_node_fs28 = require("node:fs");
20183
+ var import_node_fs30 = require("node:fs");
19287
20184
  var import_node_path21 = require("node:path");
19288
20185
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
19289
20186
  "user",
@@ -19421,7 +20318,7 @@ function candidateRoots(repoRoot2) {
19421
20318
  const norm = repoRoot2.replace(/\\/g, "/").replace(/\/+$/, "");
19422
20319
  const out = [norm];
19423
20320
  try {
19424
- const real = import_node_fs28.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
20321
+ const real = import_node_fs30.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
19425
20322
  if (real !== norm) out.push(real);
19426
20323
  } catch {
19427
20324
  }
@@ -19509,8 +20406,8 @@ function fold(transcriptPath, opts = {}) {
19509
20406
  }
19510
20407
  };
19511
20408
  try {
19512
- if (!(0, import_node_fs28.existsSync)(transcriptPath)) return result;
19513
- ingest((0, import_node_fs28.readFileSync)(transcriptPath, "utf8"), "agent");
20409
+ if (!(0, import_node_fs30.existsSync)(transcriptPath)) return result;
20410
+ ingest((0, import_node_fs30.readFileSync)(transcriptPath, "utf8"), "agent");
19514
20411
  result.coverage.complete = true;
19515
20412
  } catch {
19516
20413
  return result;
@@ -19521,19 +20418,19 @@ function fold(transcriptPath, opts = {}) {
19521
20418
  (0, import_node_path21.basename)(transcriptPath).replace(/\.jsonl$/, ""),
19522
20419
  "subagents"
19523
20420
  );
19524
- if ((0, import_node_fs28.existsSync)(sidecarDir)) {
20421
+ if ((0, import_node_fs30.existsSync)(sidecarDir)) {
19525
20422
  const maxFiles = opts.maxSidecars ?? 200;
19526
20423
  const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
19527
20424
  const found = [];
19528
20425
  const walk = (d, depth) => {
19529
20426
  if (depth > 4) return;
19530
- for (const e of (0, import_node_fs28.readdirSync)(d, { withFileTypes: true })) {
20427
+ for (const e of (0, import_node_fs30.readdirSync)(d, { withFileTypes: true })) {
19531
20428
  const p = (0, import_node_path21.join)(d, e.name);
19532
20429
  if (e.isDirectory()) {
19533
20430
  walk(p, depth + 1);
19534
20431
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
19535
20432
  try {
19536
- const st = (0, import_node_fs28.statSync)(p);
20433
+ const st = (0, import_node_fs30.statSync)(p);
19537
20434
  found.push({ path: p, size: st.size, mtimeMs: st.mtimeMs });
19538
20435
  } catch {
19539
20436
  result.coverage.malformed++;
@@ -19550,7 +20447,7 @@ function fold(transcriptPath, opts = {}) {
19550
20447
  continue;
19551
20448
  }
19552
20449
  try {
19553
- ingest((0, import_node_fs28.readFileSync)(f.path, "utf8"), "subagent");
20450
+ ingest((0, import_node_fs30.readFileSync)(f.path, "utf8"), "subagent");
19554
20451
  bytes += f.size;
19555
20452
  result.coverage.subagentFiles++;
19556
20453
  } catch {
@@ -19585,7 +20482,7 @@ function fold(transcriptPath, opts = {}) {
19585
20482
  }
19586
20483
  function classifyUnobserved(path) {
19587
20484
  try {
19588
- const st = (0, import_node_fs28.statSync)(path);
20485
+ const st = (0, import_node_fs30.statSync)(path);
19589
20486
  if (!st.isFile()) return "unreadable";
19590
20487
  } catch {
19591
20488
  return "unreadable";
@@ -19889,20 +20786,20 @@ async function evidence(run2) {
19889
20786
  }
19890
20787
 
19891
20788
  // src/lib/cache-cleanup.ts
19892
- var import_node_fs29 = require("node:fs");
20789
+ var import_node_fs31 = require("node:fs");
19893
20790
  var import_node_path22 = require("node:path");
19894
20791
  var CACHE_TTL_DAYS = 7;
19895
20792
  function pruneStaleCache() {
19896
20793
  try {
19897
20794
  const dir = projectPath(CACHE_DIR);
19898
20795
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
19899
- for (const entry of (0, import_node_fs29.readdirSync)(dir)) {
20796
+ for (const entry of (0, import_node_fs31.readdirSync)(dir)) {
19900
20797
  if (!entry.startsWith("pending-")) continue;
19901
20798
  const path = (0, import_node_path22.join)(dir, entry);
19902
20799
  try {
19903
- const stat3 = (0, import_node_fs29.statSync)(path);
20800
+ const stat3 = (0, import_node_fs31.statSync)(path);
19904
20801
  if (stat3.mtimeMs < cutoff) {
19905
- (0, import_node_fs29.unlinkSync)(path);
20802
+ (0, import_node_fs31.unlinkSync)(path);
19906
20803
  logEvent("cache_entry_pruned", {
19907
20804
  path: entry,
19908
20805
  age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
@@ -19916,16 +20813,53 @@ function pruneStaleCache() {
19916
20813
  }
19917
20814
 
19918
20815
  // src/lib/context-files.ts
19919
- var import_node_fs30 = require("node:fs");
20816
+ var import_node_fs32 = require("node:fs");
20817
+ var import_node_os5 = require("node:os");
19920
20818
  var MAX_CONTEXT_FILES = 10;
19921
20819
  var MAX_CONTEXT_FILE_BYTES = 10240;
19922
- var MAX_CONTEXT_TOTAL_BYTES = 51200;
19923
- function gatherContextFiles(contextPaths, deltaFiles) {
20820
+ var MAX_CONTEXT_TOTAL_BYTES = 24576;
20821
+ function readSetContextPaths(summary, deltaFiles) {
20822
+ const reads = summary?.files_read ?? [];
20823
+ if (reads.length === 0) return [];
20824
+ const root = process.cwd().replace(/\/+$/, "");
20825
+ const home = (0, import_node_os5.homedir)();
20826
+ const toRepoRelative2 = (p) => {
20827
+ if (!p) return null;
20828
+ let abs;
20829
+ if (p.startsWith("/")) {
20830
+ abs = p;
20831
+ } else {
20832
+ const rebuilt = `${home}/${p}`;
20833
+ abs = rebuilt.startsWith(`${root}/`) ? rebuilt : `${root}/${p}`;
20834
+ }
20835
+ if (!abs.startsWith(`${root}/`)) return null;
20836
+ return abs.slice(root.length + 1);
20837
+ };
20838
+ const authored = /* @__PURE__ */ new Set();
20839
+ for (const p of [...summary?.files_edited ?? [], ...summary?.files_created ?? []]) {
20840
+ const rel = toRepoRelative2(p);
20841
+ if (rel) authored.add(rel);
20842
+ }
20843
+ for (const f of deltaFiles) authored.add(f.path);
20844
+ const out = [];
20845
+ const seen = /* @__PURE__ */ new Set();
20846
+ for (const p of reads) {
20847
+ const rel = toRepoRelative2(p);
20848
+ if (!rel || seen.has(rel) || authored.has(rel)) continue;
20849
+ seen.add(rel);
20850
+ const ext = rel.split(".").pop()?.toLowerCase() ?? "";
20851
+ if (!ANALYZABLE_EXTENSIONS.has(ext) && !REVIEWABLE_EXTENSIONS.has(ext)) continue;
20852
+ out.push(rel);
20853
+ }
20854
+ return out;
20855
+ }
20856
+ function gatherContextFiles(contextPaths, deltaFiles, opts) {
20857
+ const fileCap = Math.max(0, Math.min(MAX_CONTEXT_FILES, opts?.maxFiles ?? MAX_CONTEXT_FILES));
19924
20858
  const deltaPaths = new Set(deltaFiles.map((f) => f.path));
19925
20859
  const result = [];
19926
20860
  let totalBytes = 0;
19927
20861
  for (const filePath of contextPaths) {
19928
- if (result.length >= MAX_CONTEXT_FILES) break;
20862
+ if (result.length >= fileCap) break;
19929
20863
  if (deltaPaths.has(filePath)) continue;
19930
20864
  if (isVerityOwnedPath(filePath)) {
19931
20865
  logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
@@ -19937,7 +20871,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
19937
20871
  continue;
19938
20872
  }
19939
20873
  try {
19940
- const content = (0, import_node_fs30.readFileSync)(safePath, "utf8");
20874
+ const content = (0, import_node_fs32.readFileSync)(safePath, "utf8");
19941
20875
  const bytes = Buffer.byteLength(content);
19942
20876
  if (bytes > MAX_CONTEXT_FILE_BYTES) {
19943
20877
  logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
@@ -19983,9 +20917,14 @@ function gatherContextFiles(contextPaths, deltaFiles) {
19983
20917
 
19984
20918
  // src/commands/analyze/phases/07-context-files.ts
19985
20919
  async function contextFiles(run2) {
19986
- const { codeDelta, contextFilePaths } = run2;
19987
- const { kept: externalContext } = partitionVerityOwned(contextFilePaths ?? []);
19988
- const contextFiles2 = gatherContextFiles(externalContext, codeDelta.files);
20920
+ const { codeDelta } = run2;
20921
+ const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
20922
+ const { kept: externalContext } = partitionVerityOwned(readSet);
20923
+ const ig = loadVerityIgnore();
20924
+ const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
20925
+ const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
20926
+ maxFiles: MAX_FILES - codeDelta.files.length
20927
+ });
19989
20928
  for (const f of codeDelta.files) {
19990
20929
  f.role = "delta";
19991
20930
  }
@@ -19997,9 +20936,33 @@ async function contextFiles(run2) {
19997
20936
  });
19998
20937
  }
19999
20938
 
20939
+ // src/commands/analyze/phases/07b-repo-context.ts
20940
+ async function repoContext(run2) {
20941
+ const { codeDelta, snapshotResult } = run2;
20942
+ const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
20943
+ const sentPaths = new Set(codeDelta.files.map((f) => f.path));
20944
+ const ig = loadVerityIgnore();
20945
+ const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
20946
+ run2.repoContext = buildRepoContext({
20947
+ deltaFiles,
20948
+ diffs: snapshotResult.diffs,
20949
+ sentPaths,
20950
+ isExcluded
20951
+ });
20952
+ logEvent("repo_context", {
20953
+ state: run2.repoContext.state,
20954
+ reason: run2.repoContext.reason ?? null,
20955
+ symbols: run2.repoContext.symbols?.length ?? 0,
20956
+ dropped: run2.repoContext.dropped_symbols?.length ?? 0,
20957
+ callers: run2.repoContext.callers?.length ?? 0,
20958
+ tests: run2.repoContext.tests?.length ?? 0,
20959
+ elapsed_ms: run2.repoContext.elapsed_ms ?? null
20960
+ });
20961
+ }
20962
+
20000
20963
  // src/lib/seed-runner.ts
20001
20964
  var import_promises11 = require("node:fs/promises");
20002
- var import_node_fs31 = require("node:fs");
20965
+ var import_node_fs33 = require("node:fs");
20003
20966
  var import_node_path23 = require("node:path");
20004
20967
  var import_yaml2 = __toESM(require_dist());
20005
20968
 
@@ -20239,7 +21202,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
20239
21202
  return fm;
20240
21203
  }
20241
21204
  async function runSeed(opts) {
20242
- if (!(0, import_node_fs31.existsSync)(STANDARD_FILE)) {
21205
+ if (!(0, import_node_fs33.existsSync)(STANDARD_FILE)) {
20243
21206
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
20244
21207
  }
20245
21208
  let standardDoc;
@@ -20251,7 +21214,7 @@ async function runSeed(opts) {
20251
21214
  }
20252
21215
  const knowledgeSpec = standardDoc.knowledge_spec ?? {};
20253
21216
  let readmeContent;
20254
- if ((0, import_node_fs31.existsSync)("README.md")) {
21217
+ if ((0, import_node_fs33.existsSync)("README.md")) {
20255
21218
  try {
20256
21219
  readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
20257
21220
  } catch {
@@ -20259,7 +21222,7 @@ async function runSeed(opts) {
20259
21222
  }
20260
21223
  let claudeMdContent;
20261
21224
  for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
20262
- if ((0, import_node_fs31.existsSync)(p)) {
21225
+ if ((0, import_node_fs33.existsSync)(p)) {
20263
21226
  try {
20264
21227
  claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
20265
21228
  break;
@@ -20283,7 +21246,7 @@ async function runSeed(opts) {
20283
21246
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
20284
21247
  }
20285
21248
  const overviewPath = (0, import_node_path23.join)(MEMORY_DIR, "domain", "project-overview.md");
20286
- if ((0, import_node_fs31.existsSync)(overviewPath) && !opts.force) {
21249
+ if ((0, import_node_fs33.existsSync)(overviewPath) && !opts.force) {
20287
21250
  return { created: 0, failed: 0, skipped: "already_seeded", candidates };
20288
21251
  }
20289
21252
  if (opts.dryRun) {
@@ -20338,7 +21301,7 @@ async function runSeed(opts) {
20338
21301
  }
20339
21302
 
20340
21303
  // src/commands/analyze/phases/08-memory-manifest.ts
20341
- var import_node_fs32 = require("node:fs");
21304
+ var import_node_fs34 = require("node:fs");
20342
21305
  var import_node_path24 = require("node:path");
20343
21306
  async function memoryManifest(run2) {
20344
21307
  const { globals } = run2;
@@ -20350,8 +21313,8 @@ async function memoryManifest(run2) {
20350
21313
  try {
20351
21314
  await ensureMemoryDir();
20352
21315
  const seedMarker = (0, import_node_path24.join)(VERITY_DIR, ".seeded");
20353
- const hasStandard = (0, import_node_fs32.existsSync)(STANDARD_FILE);
20354
- const alreadyTried = (0, import_node_fs32.existsSync)(seedMarker);
21316
+ const hasStandard = (0, import_node_fs34.existsSync)(STANDARD_FILE);
21317
+ const alreadyTried = (0, import_node_fs34.existsSync)(seedMarker);
20355
21318
  if (hasStandard && !alreadyTried) {
20356
21319
  const preManifest = await buildManifest();
20357
21320
  if (preManifest.nodes.length === 0) {
@@ -20364,7 +21327,7 @@ async function memoryManifest(run2) {
20364
21327
  dryRun: false
20365
21328
  });
20366
21329
  if (seedResult.created > 0) {
20367
- (0, import_node_fs32.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
21330
+ (0, import_node_fs34.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
20368
21331
  `);
20369
21332
  autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
20370
21333
  logEvent("auto_seed_ran", {
@@ -20372,7 +21335,7 @@ async function memoryManifest(run2) {
20372
21335
  failed: seedResult.failed
20373
21336
  });
20374
21337
  } else if (seedResult.skipped === "already_seeded") {
20375
- (0, import_node_fs32.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
21338
+ (0, import_node_fs34.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
20376
21339
  `);
20377
21340
  } else {
20378
21341
  logEvent("auto_seed_noop", {
@@ -20561,7 +21524,7 @@ async function workingMemory(run2) {
20561
21524
  }
20562
21525
 
20563
21526
  // src/lib/note-budget.ts
20564
- var import_node_fs33 = require("node:fs");
21527
+ var import_node_fs35 = require("node:fs");
20565
21528
  var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
20566
21529
  var EPISODE_STALE_SECONDS = 30 * 60;
20567
21530
  var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
@@ -20583,9 +21546,9 @@ function advisoryBudgetSpent(episode, rawDecision) {
20583
21546
  }
20584
21547
  function readAdvisoryEpisode(sessionId) {
20585
21548
  const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
20586
- if (!(0, import_node_fs33.existsSync)(file)) return null;
21549
+ if (!(0, import_node_fs35.existsSync)(file)) return null;
20587
21550
  try {
20588
- const o = JSON.parse((0, import_node_fs33.readFileSync)(file, "utf-8")) ?? {};
21551
+ const o = JSON.parse((0, import_node_fs35.readFileSync)(file, "utf-8")) ?? {};
20589
21552
  const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
20590
21553
  if (isNaN(delivered)) return null;
20591
21554
  return {
@@ -20599,8 +21562,8 @@ function readAdvisoryEpisode(sessionId) {
20599
21562
  }
20600
21563
  function writeAdvisoryEpisode(episode, sessionId) {
20601
21564
  try {
20602
- (0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
20603
- (0, import_node_fs33.writeFileSync)(
21565
+ (0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
21566
+ (0, import_node_fs35.writeFileSync)(
20604
21567
  scopedFile(ADVISORY_EPISODE_FILE, sessionId),
20605
21568
  JSON.stringify({ v: 1, ...episode })
20606
21569
  );
@@ -20630,7 +21593,7 @@ function isExplicitlyAutonomous(env = process.env) {
20630
21593
  }
20631
21594
 
20632
21595
  // src/lib/task-context.ts
20633
- var import_node_child_process9 = require("node:child_process");
21596
+ var import_node_child_process10 = require("node:child_process");
20634
21597
  var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
20635
21598
  var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
20636
21599
  function parseLinkedIssue(sources) {
@@ -20646,7 +21609,7 @@ function parseLinkedIssue(sources) {
20646
21609
  }
20647
21610
  function safeExec(cmd, timeout) {
20648
21611
  try {
20649
- return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
21612
+ return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
20650
21613
  } catch {
20651
21614
  return "";
20652
21615
  }
@@ -20853,6 +21816,9 @@ async function buildRequest(run2) {
20853
21816
  if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
20854
21817
  requestBody.snapshot_diffs = snapshotResult.diffs;
20855
21818
  }
21819
+ if (run2.repoContext) {
21820
+ requestBody.repo_context = run2.repoContext;
21821
+ }
20856
21822
  const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
20857
21823
  const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
20858
21824
  const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
@@ -20914,14 +21880,14 @@ async function buildRequest(run2) {
20914
21880
  }
20915
21881
 
20916
21882
  // src/lib/offline.ts
20917
- var import_node_fs34 = require("node:fs");
21883
+ var import_node_fs36 = require("node:fs");
20918
21884
  var import_node_crypto11 = require("node:crypto");
20919
21885
  function cacheRequest(body) {
20920
21886
  try {
20921
- (0, import_node_fs34.mkdirSync)(CACHE_DIR, { recursive: true });
21887
+ (0, import_node_fs36.mkdirSync)(CACHE_DIR, { recursive: true });
20922
21888
  const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
20923
21889
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
20924
- (0, import_node_fs34.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
21890
+ (0, import_node_fs36.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
20925
21891
  } catch {
20926
21892
  }
20927
21893
  }
@@ -21040,7 +22006,7 @@ async function transmit(run2) {
21040
22006
  }
21041
22007
 
21042
22008
  // src/commands/analyze/phases/13-reconcile.ts
21043
- var import_node_fs35 = require("node:fs");
22009
+ var import_node_fs37 = require("node:fs");
21044
22010
  var import_node_path26 = require("node:path");
21045
22011
  async function reconcile(run2) {
21046
22012
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
@@ -21070,7 +22036,7 @@ async function reconcile(run2) {
21070
22036
  const st = foldDossier(memorySession.d);
21071
22037
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
21072
22038
  try {
21073
- const src = (0, import_node_fs35.readFileSync)((0, import_node_path26.join)(repoRoot(), file), "utf8").split("\n");
22039
+ const src = (0, import_node_fs37.readFileSync)((0, import_node_path26.join)(repoRoot(), file), "utf8").split("\n");
21074
22040
  const at = src[line - 1];
21075
22041
  return at === void 0 ? null : lineSha(at);
21076
22042
  } catch {
@@ -21701,6 +22667,8 @@ var PIPELINE = [
21701
22667
  // ← THE NARROWING. what gets sent, and why not the rest
21702
22668
  ["contextFiles", contextFiles],
21703
22669
  // supporting files, merged INTO the delta array
22670
+ ["repoContext", repoContext],
22671
+ // R1/R3 — call sites of changed symbols, one line each
21704
22672
  ["memoryManifest", memoryManifest],
21705
22673
  // knowledge-graph manifest + one-time auto-seed
21706
22674
  ["foldTranscript", foldTranscript],
@@ -21717,7 +22685,7 @@ var PIPELINE = [
21717
22685
  // say it — stderr, stdout, disk
21718
22686
  ];
21719
22687
  function registerAnalyzeCommand(program2) {
21720
- program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
22688
+ program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", String(DEBOUNCE_SECONDS)).option("--max-iterations <n>", "Force PASS after N FAIL cycles", String(MAX_ITERATIONS)).option("--max-files <n>", "Max files to send for review", String(MAX_FILES)).option("--max-file-size <bytes>", "Skip files larger than N bytes", String(MAX_FILE_BYTES)).option("--max-total-size <bytes>", "Stop collecting files at N total bytes", String(MAX_DELTA_BYTES)).option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
21721
22689
  const globals = program2.opts();
21722
22690
  try {
21723
22691
  await runAnalyze(opts, globals);
@@ -21750,7 +22718,7 @@ async function runAnalyze(opts, globals) {
21750
22718
  }
21751
22719
 
21752
22720
  // src/commands/baseline.ts
21753
- var import_node_fs36 = require("node:fs");
22721
+ var import_node_fs38 = require("node:fs");
21754
22722
  function registerBaselineCommands(program2) {
21755
22723
  const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
21756
22724
  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) => {
@@ -21759,7 +22727,7 @@ function registerBaselineCommands(program2) {
21759
22727
  process.chdir(repoRoot());
21760
22728
  } catch {
21761
22729
  }
21762
- if (!(0, import_node_fs36.existsSync)(VERITY_DIR)) {
22730
+ if (!(0, import_node_fs38.existsSync)(VERITY_DIR)) {
21763
22731
  process.exit(0);
21764
22732
  }
21765
22733
  let sessionId = opts.sessionId;
@@ -21775,6 +22743,9 @@ function registerBaselineCommands(program2) {
21775
22743
  }
21776
22744
  }
21777
22745
  }
22746
+ if (deferredToPlugin("baseline capture", sessionId ?? process.env.CLAUDE_SESSION_ID ?? null)) {
22747
+ process.exit(0);
22748
+ }
21778
22749
  const authForScope = await resolveToken(program2.opts().token);
21779
22750
  const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
21780
22751
  const scopeSession = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
@@ -21799,7 +22770,7 @@ async function readStdin() {
21799
22770
  }
21800
22771
 
21801
22772
  // src/commands/review.ts
21802
- var import_node_fs37 = require("node:fs");
22773
+ var import_node_fs39 = require("node:fs");
21803
22774
  function registerReviewCommand(program2) {
21804
22775
  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) => {
21805
22776
  const globals = program2.opts();
@@ -21818,7 +22789,7 @@ async function runReview(opts, globals) {
21818
22789
  const securityFiles = filterSecurity(allFiles);
21819
22790
  let staticResults;
21820
22791
  if (isCodacyAvailable()) {
21821
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs37.existsSync)(f) || resolveFile(f) !== null);
22792
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs39.existsSync)(f) || resolveFile(f) !== null);
21822
22793
  staticResults = runCodacyAnalysis(scannable);
21823
22794
  } else {
21824
22795
  staticResults = {
@@ -21844,10 +22815,10 @@ async function runReview(opts, globals) {
21844
22815
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
21845
22816
  specs = [];
21846
22817
  for (const p of specPaths) {
21847
- if (!(0, import_node_fs37.existsSync)(p)) continue;
22818
+ if (!(0, import_node_fs39.existsSync)(p)) continue;
21848
22819
  try {
21849
- const { readFileSync: readFileSync25 } = await import("node:fs");
21850
- const content = readFileSync25(p, "utf-8");
22820
+ const { readFileSync: readFileSync27 } = await import("node:fs");
22821
+ const content = readFileSync27(p, "utf-8");
21851
22822
  specs.push({ path: p, content: content.slice(0, 10240) });
21852
22823
  } catch {
21853
22824
  }
@@ -21904,7 +22875,7 @@ async function runReview(opts, globals) {
21904
22875
  }
21905
22876
 
21906
22877
  // src/commands/guard.ts
21907
- var import_node_fs38 = require("node:fs");
22878
+ var import_node_fs40 = require("node:fs");
21908
22879
  var import_node_path27 = require("node:path");
21909
22880
  var GUARD_BLOCK_CAP = 2;
21910
22881
  var GUARD_ITER_FILE = (0, import_node_path27.join)(VERITY_DIR, ".guard-iteration");
@@ -21952,7 +22923,7 @@ function readPreToolUseStdin() {
21952
22923
  }
21953
22924
  function readIterMap() {
21954
22925
  try {
21955
- const raw = JSON.parse((0, import_node_fs38.readFileSync)(GUARD_ITER_FILE, "utf-8"));
22926
+ const raw = JSON.parse((0, import_node_fs40.readFileSync)(GUARD_ITER_FILE, "utf-8"));
21956
22927
  if (raw && typeof raw === "object") {
21957
22928
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
21958
22929
  return { [raw.moment]: raw.count };
@@ -21972,10 +22943,10 @@ function readIter(moment) {
21972
22943
  }
21973
22944
  function writeIter(moment, count) {
21974
22945
  try {
21975
- (0, import_node_fs38.mkdirSync)(VERITY_DIR, { recursive: true });
22946
+ (0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
21976
22947
  const map = readIterMap();
21977
22948
  map[moment] = count;
21978
- (0, import_node_fs38.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
22949
+ (0, import_node_fs40.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
21979
22950
  } catch {
21980
22951
  }
21981
22952
  }
@@ -21985,16 +22956,16 @@ function resetIter(moment) {
21985
22956
  if (!(moment in map)) return;
21986
22957
  delete map[moment];
21987
22958
  if (Object.keys(map).length === 0) {
21988
- if ((0, import_node_fs38.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs38.unlinkSync)(GUARD_ITER_FILE);
22959
+ if ((0, import_node_fs40.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs40.unlinkSync)(GUARD_ITER_FILE);
21989
22960
  } else {
21990
- (0, import_node_fs38.mkdirSync)(VERITY_DIR, { recursive: true });
21991
- (0, import_node_fs38.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
22961
+ (0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
22962
+ (0, import_node_fs40.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
21992
22963
  }
21993
22964
  } catch {
21994
22965
  }
21995
22966
  }
21996
22967
  function registerGuardCommand(program2) {
21997
- program2.command("guard").description("Git-moment gate: review staged/to-push changes before a commit/push (PreToolUse hook)").option("--on <moments>", "Which git moments to gate: commit,push", "commit,push").option("--json", "Output raw JSON response (debug)").action(async (opts) => {
22968
+ program2.command("guard").description("Git-moment gate: review staged/to-push changes before a commit/push (PreToolUse hook)").option("--on <moments>", "Which git moments to gate: commit,push (default: the project config)").option("--json", "Output raw JSON response (debug)").action(async (opts) => {
21998
22969
  const globals = program2.opts();
21999
22970
  try {
22000
22971
  await runGuard(opts, globals);
@@ -22059,7 +23030,7 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
22059
23030
  const securityFiles = filterSecurity(files);
22060
23031
  let staticResults;
22061
23032
  if (isCodacyAvailable()) {
22062
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs38.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
23033
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs40.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
22063
23034
  staticResults = runCodacyAnalysis(scannable);
22064
23035
  } else {
22065
23036
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -22116,6 +23087,7 @@ function coverageBlock(c) {
22116
23087
  const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
22117
23088
  lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
22118
23089
  lines.push(` Tree: ${tree}`);
23090
+ if (c.repoContext) lines.push(` Repo context: ${describeRepoContext(c.repoContext)}`);
22119
23091
  for (const f of c.sent) lines.push(` - ${f}`);
22120
23092
  if (c.excluded.length > 0) {
22121
23093
  lines.push(` Excluded (${c.excluded.length}):`);
@@ -22131,8 +23103,10 @@ function emitAllowNotice(userMsg, agentMsg) {
22131
23103
  process.exit(0);
22132
23104
  }
22133
23105
  async function runGuard(opts, globals) {
22134
- const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
23106
+ const on = resolveGuardMoments(opts.on);
23107
+ if (on.length === 0) process.exit(0);
22135
23108
  const { command, cwd, sessionId } = await readPreToolUseStdin();
23109
+ if (deferredToPlugin("guard", sessionId)) process.exit(0);
22136
23110
  const moment = classifyCommand(command, on);
22137
23111
  if (!moment) process.exit(0);
22138
23112
  const verb = moment === "pre-commit" ? "commit" : "push";
@@ -22182,6 +23156,39 @@ async function runGuard(opts, globals) {
22182
23156
  statedIntent,
22183
23157
  buildGuardCoverage(files, codeDelta, frame, range)
22184
23158
  );
23159
+ try {
23160
+ const ig = loadVerityIgnore();
23161
+ const repoContext2 = buildRepoContext({
23162
+ deltaFiles: codeDelta.files,
23163
+ diffs: [],
23164
+ signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
23165
+ sentPaths: new Set(codeDelta.files.map((f) => f.path)),
23166
+ isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
23167
+ cwd: frame.worktreeRoot ?? process.cwd()
23168
+ });
23169
+ upgradeToExcerpts(repoContext2, {
23170
+ readFile: (rel) => {
23171
+ try {
23172
+ return (0, import_node_fs40.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
23173
+ } catch {
23174
+ return null;
23175
+ }
23176
+ }
23177
+ });
23178
+ requestBody.repo_context = repoContext2;
23179
+ logEvent("repo_context", {
23180
+ moment,
23181
+ state: repoContext2.state,
23182
+ reason: repoContext2.reason ?? null,
23183
+ symbols: repoContext2.symbols?.length ?? 0,
23184
+ callers: repoContext2.callers?.length ?? 0,
23185
+ tests: repoContext2.tests?.length ?? 0,
23186
+ excerpts: repoContext2.excerpts?.length ?? 0,
23187
+ elapsed_ms: repoContext2.elapsed_ms ?? null
23188
+ });
23189
+ } catch (e) {
23190
+ logEvent("repo_context", { moment, state: "absent", reason: "exception", message: e.message });
23191
+ }
22185
23192
  const coverage = {
22186
23193
  moment,
22187
23194
  root: frame.worktreeRoot,
@@ -22189,7 +23196,8 @@ async function runGuard(opts, globals) {
22189
23196
  linked: frame.isLinkedWorktree,
22190
23197
  range: describeRange(range),
22191
23198
  sent: codeDelta.files.map((f) => f.path),
22192
- excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
23199
+ excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason })),
23200
+ ...requestBody.repo_context ? { repoContext: requestBody.repo_context } : {}
22193
23201
  };
22194
23202
  logToFileOnly(coverageBlock(coverage));
22195
23203
  const reviewStart = Date.now();
@@ -22382,7 +23390,7 @@ function registerIgnoreCommand(program2) {
22382
23390
 
22383
23391
  // src/commands/waive.ts
22384
23392
  var import_node_crypto12 = require("node:crypto");
22385
- var import_node_fs39 = require("node:fs");
23393
+ var import_node_fs41 = require("node:fs");
22386
23394
  function registerWaiveCommand(program2) {
22387
23395
  program2.command("waive <pattern-id>").description("Record an accepted-risk disposition for an open finding (voids when the file changes)").option("--file <path>", "File the finding is anchored to, REPO-RELATIVE (recommended \u2014 narrows the waive)").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action(async (patternId, opts) => {
22388
23396
  const globals = program2.opts();
@@ -22411,7 +23419,7 @@ function registerWaiveCommand(program2) {
22411
23419
  if (opts.file) {
22412
23420
  body.file = opts.file;
22413
23421
  try {
22414
- body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs39.readFileSync)(opts.file)).digest("hex");
23422
+ body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs41.readFileSync)(opts.file)).digest("hex");
22415
23423
  } catch {
22416
23424
  printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
22417
23425
  process.exit(1);
@@ -22436,10 +23444,10 @@ function registerWaiveCommand(program2) {
22436
23444
  }
22437
23445
 
22438
23446
  // src/commands/init.ts
22439
- var import_node_fs44 = require("node:fs");
23447
+ var import_node_fs46 = require("node:fs");
22440
23448
  var import_promises14 = require("node:fs/promises");
22441
23449
  var import_node_path29 = require("node:path");
22442
- var import_node_child_process13 = require("node:child_process");
23450
+ var import_node_child_process14 = require("node:child_process");
22443
23451
 
22444
23452
  // src/lib/banner.ts
22445
23453
  var WORDMARK = [
@@ -22517,14 +23525,14 @@ function printPhase(n, of, title, subtitle) {
22517
23525
  }
22518
23526
 
22519
23527
  // src/commands/doctor.ts
22520
- var import_node_fs42 = require("node:fs");
23528
+ var import_node_fs44 = require("node:fs");
22521
23529
 
22522
23530
  // src/lib/prereqs.ts
22523
- var import_node_child_process10 = require("node:child_process");
23531
+ var import_node_child_process11 = require("node:child_process");
22524
23532
  var MIN_NODE_MAJOR = 20;
22525
23533
  function which(bin) {
22526
23534
  try {
22527
- const out = (0, import_node_child_process10.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23535
+ const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22528
23536
  return out || null;
22529
23537
  } catch {
22530
23538
  return null;
@@ -22546,7 +23554,7 @@ function checkNode() {
22546
23554
  function checkGit() {
22547
23555
  let detail = "";
22548
23556
  try {
22549
- detail = (0, import_node_child_process10.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23557
+ detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22550
23558
  } catch {
22551
23559
  return {
22552
23560
  id: "git",
@@ -22584,7 +23592,7 @@ function checkAnalysisCli() {
22584
23592
  var INSTALL_TIMEOUT_MS = 12e4;
22585
23593
  function run(command, args, opts = {}) {
22586
23594
  return new Promise((resolve4) => {
22587
- const child = (0, import_node_child_process10.spawn)(command, args, {
23595
+ const child = (0, import_node_child_process11.spawn)(command, args, {
22588
23596
  stdio: opts.inherit ? "inherit" : "pipe",
22589
23597
  timeout: INSTALL_TIMEOUT_MS
22590
23598
  });
@@ -22636,8 +23644,8 @@ async function checkPrereqs(opts = {}) {
22636
23644
  var import_promises12 = require("node:fs/promises");
22637
23645
 
22638
23646
  // src/lib/gitignore.ts
22639
- var import_node_child_process11 = require("node:child_process");
22640
- var import_node_fs40 = require("node:fs");
23647
+ var import_node_child_process12 = require("node:child_process");
23648
+ var import_node_fs42 = require("node:fs");
22641
23649
  var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
22642
23650
  var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
22643
23651
  var VERITY_GITIGNORE_BLOCK = [
@@ -22653,7 +23661,7 @@ var VERITY_GITIGNORE_BLOCK = [
22653
23661
  var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
22654
23662
  function isIgnored2(path) {
22655
23663
  try {
22656
- (0, import_node_child_process11.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
23664
+ (0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
22657
23665
  return true;
22658
23666
  } catch (err) {
22659
23667
  return err.status === 1 ? false : null;
@@ -22670,7 +23678,7 @@ function semanticsHold() {
22670
23678
  function ensureVerityGitignore() {
22671
23679
  let content = "";
22672
23680
  try {
22673
- content = (0, import_node_fs40.readFileSync)(".gitignore", "utf-8");
23681
+ content = (0, import_node_fs42.readFileSync)(".gitignore", "utf-8");
22674
23682
  } catch {
22675
23683
  }
22676
23684
  const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
@@ -22691,7 +23699,7 @@ function ensureVerityGitignore() {
22691
23699
  const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
22692
23700
  next = next + sep2 + VERITY_GITIGNORE_BLOCK;
22693
23701
  }
22694
- (0, import_node_fs40.writeFileSync)(".gitignore", next);
23702
+ (0, import_node_fs42.writeFileSync)(".gitignore", next);
22695
23703
  return verified(needsRepair ? "repaired" : "added");
22696
23704
  } catch {
22697
23705
  return "failed";
@@ -22700,7 +23708,7 @@ function ensureVerityGitignore() {
22700
23708
  function committedVerityState() {
22701
23709
  let out = "";
22702
23710
  try {
22703
- out = (0, import_node_child_process11.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
23711
+ out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
22704
23712
  } catch {
22705
23713
  return [];
22706
23714
  }
@@ -22710,10 +23718,10 @@ function untrackVerityState() {
22710
23718
  const tracked = committedVerityState();
22711
23719
  if (tracked.length === 0) return "none";
22712
23720
  try {
22713
- (0, import_node_child_process11.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
23721
+ (0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
22714
23722
  for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
22715
23723
  try {
22716
- (0, import_node_child_process11.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
23724
+ (0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
22717
23725
  } catch {
22718
23726
  }
22719
23727
  }
@@ -22814,11 +23822,11 @@ async function uninstallTelemetry() {
22814
23822
 
22815
23823
  // src/lib/setup-state.ts
22816
23824
  var import_promises13 = require("node:fs/promises");
22817
- var import_node_fs41 = require("node:fs");
23825
+ var import_node_fs43 = require("node:fs");
22818
23826
  var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
22819
23827
  async function readSetupState() {
22820
23828
  const path = projectPath(SETUP_STATE_FILE);
22821
- if (!(0, import_node_fs41.existsSync)(path)) return null;
23829
+ if (!(0, import_node_fs43.existsSync)(path)) return null;
22822
23830
  try {
22823
23831
  const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
22824
23832
  return parsed && typeof parsed === "object" ? parsed : null;
@@ -22840,9 +23848,9 @@ async function buildReport() {
22840
23848
  const hooks = await checkAllVerityHooks();
22841
23849
  const telemetry = await checkTelemetry();
22842
23850
  const artifacts = {
22843
- standard: (0, import_node_fs42.existsSync)(projectPath(STANDARD_FILE)),
22844
- analysisConfig: (0, import_node_fs42.existsSync)(projectPath(CODACY_CONFIG_FILE)),
22845
- verityMd: (0, import_node_fs42.existsSync)(projectPath(VERITY_MD_FILE))
23851
+ standard: (0, import_node_fs44.existsSync)(projectPath(STANDARD_FILE)),
23852
+ analysisConfig: (0, import_node_fs44.existsSync)(projectPath(CODACY_CONFIG_FILE)),
23853
+ verityMd: (0, import_node_fs44.existsSync)(projectPath(VERITY_MD_FILE))
22846
23854
  };
22847
23855
  const next = [];
22848
23856
  for (const c of prereqs.checks) {
@@ -22915,16 +23923,16 @@ function registerDoctorCommand(program2) {
22915
23923
  }
22916
23924
 
22917
23925
  // src/commands/migrate.ts
22918
- var import_node_fs43 = require("node:fs");
23926
+ var import_node_fs45 = require("node:fs");
22919
23927
  var import_node_path28 = require("node:path");
22920
- var import_node_child_process12 = require("node:child_process");
23928
+ var import_node_child_process13 = require("node:child_process");
22921
23929
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
22922
23930
  function defaultNpmRemover(pkg) {
22923
- (0, import_node_child_process12.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
23931
+ (0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
22924
23932
  }
22925
23933
  function isGitTracked(cwd, relPath) {
22926
23934
  try {
22927
- (0, import_node_child_process12.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
23935
+ (0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
22928
23936
  return true;
22929
23937
  } catch {
22930
23938
  return false;
@@ -22932,7 +23940,7 @@ function isGitTracked(cwd, relPath) {
22932
23940
  }
22933
23941
  function isGitRepo(cwd) {
22934
23942
  try {
22935
- (0, import_node_child_process12.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
23943
+ (0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
22936
23944
  return true;
22937
23945
  } catch {
22938
23946
  return false;
@@ -22955,10 +23963,10 @@ async function runMigration(opts = {}) {
22955
23963
  function migrateProjectDir(root, actions) {
22956
23964
  const gateDir = (0, import_node_path28.join)(root, ".gate");
22957
23965
  const verityDir = (0, import_node_path28.join)(root, ".verity");
22958
- if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) {
23966
+ if ((0, import_node_fs45.existsSync)(gateDir) && !(0, import_node_fs45.existsSync)(verityDir)) {
22959
23967
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
22960
23968
  }
22961
- if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
23969
+ if ((0, import_node_fs45.existsSync)(gateDir) && (0, import_node_fs45.existsSync)(verityDir)) {
22962
23970
  return migrateProjectDirCarry(gateDir, verityDir, actions);
22963
23971
  }
22964
23972
  return false;
@@ -22972,20 +23980,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
22972
23980
  );
22973
23981
  }
22974
23982
  try {
22975
- (0, import_node_child_process12.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
23983
+ (0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
22976
23984
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
22977
23985
  moved = true;
22978
23986
  } catch {
22979
23987
  }
22980
23988
  }
22981
23989
  if (moved) {
22982
- if ((0, import_node_fs43.existsSync)(gateDir)) {
23990
+ if ((0, import_node_fs45.existsSync)(gateDir)) {
22983
23991
  const carried = carryLegacyContents(gateDir, verityDir);
22984
23992
  if (carried > 0) {
22985
23993
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
22986
23994
  }
22987
23995
  try {
22988
- (0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
23996
+ (0, import_node_fs45.rmSync)(gateDir, { recursive: true, force: true });
22989
23997
  } catch {
22990
23998
  }
22991
23999
  }
@@ -23001,7 +24009,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
23001
24009
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
23002
24010
  }
23003
24011
  try {
23004
- (0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
24012
+ (0, import_node_fs45.rmSync)(gateDir, { recursive: true, force: true });
23005
24013
  } catch {
23006
24014
  }
23007
24015
  return carried > 0;
@@ -23010,9 +24018,9 @@ function migrateGlobalCredentials(home, actions) {
23010
24018
  if (!home) return;
23011
24019
  const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
23012
24020
  const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
23013
- if (!(0, import_node_fs43.existsSync)(gateCreds)) return;
23014
- if (!(0, import_node_fs43.existsSync)(verityCreds)) {
23015
- (0, import_node_fs43.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
24021
+ if (!(0, import_node_fs45.existsSync)(gateCreds)) return;
24022
+ if (!(0, import_node_fs45.existsSync)(verityCreds)) {
24023
+ (0, import_node_fs45.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
23016
24024
  moveFile(gateCreds, verityCreds);
23017
24025
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
23018
24026
  return;
@@ -23035,7 +24043,7 @@ async function migrateLegacyHooks(root, actions) {
23035
24043
  }
23036
24044
  async function migrateClaudeMd(root, actions) {
23037
24045
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
23038
- const hadLegacyBlock = (0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
24046
+ const hadLegacyBlock = (0, import_node_fs45.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
23039
24047
  if (!hadLegacyBlock) return;
23040
24048
  try {
23041
24049
  await ensureClaudeMdPointer(root);
@@ -23047,11 +24055,11 @@ async function migrateClaudeMd(root, actions) {
23047
24055
  function migrateStandardFile(root, actions) {
23048
24056
  const gateMd = (0, import_node_path28.join)(root, "GATE.md");
23049
24057
  const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
23050
- if (!(0, import_node_fs43.existsSync)(gateMd) || (0, import_node_fs43.existsSync)(verityMd)) return;
24058
+ if (!(0, import_node_fs45.existsSync)(gateMd) || (0, import_node_fs45.existsSync)(verityMd)) return;
23051
24059
  let moved = false;
23052
24060
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
23053
24061
  try {
23054
- (0, import_node_child_process12.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
24062
+ (0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
23055
24063
  moved = true;
23056
24064
  } catch {
23057
24065
  }
@@ -23059,12 +24067,12 @@ function migrateStandardFile(root, actions) {
23059
24067
  if (!moved) moveFile(gateMd, verityMd);
23060
24068
  const content = readFileSyncSafe(verityMd);
23061
24069
  const refreshed = content.split("GATE.md").join("VERITY.md");
23062
- if (refreshed !== content) (0, import_node_fs43.writeFileSync)(verityMd, refreshed);
24070
+ if (refreshed !== content) (0, import_node_fs45.writeFileSync)(verityMd, refreshed);
23063
24071
  actions.push("Renamed GATE.md \u2192 VERITY.md");
23064
24072
  }
23065
24073
  async function migrateTelemetryHeaders(root, actions) {
23066
24074
  const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
23067
- if (!(0, import_node_fs43.existsSync)(file)) return;
24075
+ if (!(0, import_node_fs45.existsSync)(file)) return;
23068
24076
  let settings;
23069
24077
  try {
23070
24078
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -23112,21 +24120,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
23112
24120
  }
23113
24121
  if (toAppend.length > 0) {
23114
24122
  const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
23115
- (0, import_node_fs43.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
24123
+ (0, import_node_fs45.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
23116
24124
  }
23117
- (0, import_node_fs43.rmSync)(gateCreds, { force: true });
24125
+ (0, import_node_fs45.rmSync)(gateCreds, { force: true });
23118
24126
  return toAppend.length;
23119
24127
  }
23120
24128
  function readFileSyncSafe(path) {
23121
24129
  try {
23122
- return (0, import_node_fs43.readFileSync)(path, "utf-8");
24130
+ return (0, import_node_fs45.readFileSync)(path, "utf-8");
23123
24131
  } catch {
23124
24132
  return "";
23125
24133
  }
23126
24134
  }
23127
24135
  function hasStagedChanges(root) {
23128
24136
  try {
23129
- (0, import_node_child_process12.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
24137
+ (0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
23130
24138
  return false;
23131
24139
  } catch {
23132
24140
  return true;
@@ -23134,35 +24142,35 @@ function hasStagedChanges(root) {
23134
24142
  }
23135
24143
  function moveDir(from, to) {
23136
24144
  try {
23137
- (0, import_node_fs43.renameSync)(from, to);
24145
+ (0, import_node_fs45.renameSync)(from, to);
23138
24146
  } catch (err) {
23139
24147
  if (err.code !== "EXDEV") throw err;
23140
- (0, import_node_fs43.cpSync)(from, to, { recursive: true });
23141
- (0, import_node_fs43.rmSync)(from, { recursive: true, force: true });
24148
+ (0, import_node_fs45.cpSync)(from, to, { recursive: true });
24149
+ (0, import_node_fs45.rmSync)(from, { recursive: true, force: true });
23142
24150
  }
23143
24151
  }
23144
24152
  function moveFile(from, to) {
23145
24153
  try {
23146
- (0, import_node_fs43.renameSync)(from, to);
24154
+ (0, import_node_fs45.renameSync)(from, to);
23147
24155
  } catch (err) {
23148
24156
  if (err.code !== "EXDEV") throw err;
23149
- (0, import_node_fs43.cpSync)(from, to);
23150
- (0, import_node_fs43.rmSync)(from, { force: true });
24157
+ (0, import_node_fs45.cpSync)(from, to);
24158
+ (0, import_node_fs45.rmSync)(from, { force: true });
23151
24159
  }
23152
24160
  }
23153
24161
  function carryLegacyContents(gateDir, verityDir) {
23154
24162
  let copied = 0;
23155
24163
  const walk = (relDir) => {
23156
24164
  const srcDir = (0, import_node_path28.join)(gateDir, relDir);
23157
- for (const entry of (0, import_node_fs43.readdirSync)(srcDir)) {
24165
+ for (const entry of (0, import_node_fs45.readdirSync)(srcDir)) {
23158
24166
  const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
23159
24167
  const src = (0, import_node_path28.join)(gateDir, rel);
23160
24168
  const dest = (0, import_node_path28.join)(verityDir, rel);
23161
- if ((0, import_node_fs43.statSync)(src).isDirectory()) {
24169
+ if ((0, import_node_fs45.statSync)(src).isDirectory()) {
23162
24170
  walk(rel);
23163
- } else if (!(0, import_node_fs43.existsSync)(dest)) {
23164
- (0, import_node_fs43.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
23165
- (0, import_node_fs43.cpSync)(src, dest);
24171
+ } else if (!(0, import_node_fs45.existsSync)(dest)) {
24172
+ (0, import_node_fs45.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
24173
+ (0, import_node_fs45.cpSync)(src, dest);
23166
24174
  copied++;
23167
24175
  }
23168
24176
  }
@@ -23173,20 +24181,20 @@ function carryLegacyContents(gateDir, verityDir) {
23173
24181
  async function needsMigration(root = repoRoot()) {
23174
24182
  const gateDir = (0, import_node_path28.join)(root, ".gate");
23175
24183
  const verityDir = (0, import_node_path28.join)(root, ".verity");
23176
- if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) return true;
23177
- if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
23178
- if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
24184
+ if ((0, import_node_fs45.existsSync)(gateDir) && !(0, import_node_fs45.existsSync)(verityDir)) return true;
24185
+ if ((0, import_node_fs45.existsSync)(gateDir) && (0, import_node_fs45.existsSync)(verityDir)) {
24186
+ if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
23179
24187
  return true;
23180
24188
  }
23181
- if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
24189
+ if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
23182
24190
  return true;
23183
24191
  }
23184
24192
  }
23185
24193
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
23186
- if ((0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
24194
+ if ((0, import_node_fs45.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
23187
24195
  return true;
23188
24196
  }
23189
- if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
24197
+ if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
23190
24198
  return true;
23191
24199
  }
23192
24200
  if (await hasLegacyHooksAt(root)) return true;
@@ -23358,7 +24366,7 @@ async function runOptionalAuth(resolution, opts = {}) {
23358
24366
  }
23359
24367
  let remote = "";
23360
24368
  try {
23361
- remote = (0, import_node_child_process13.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
24369
+ remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23362
24370
  } catch {
23363
24371
  }
23364
24372
  if (!healed) {
@@ -23412,7 +24420,7 @@ function resolveDataDir() {
23412
24420
  // local dev: running from repo root
23413
24421
  ];
23414
24422
  for (const candidate of candidates) {
23415
- if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
24423
+ if ((0, import_node_fs46.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
23416
24424
  return candidate;
23417
24425
  }
23418
24426
  }
@@ -23428,7 +24436,7 @@ async function skillIsCurrent(src, dest) {
23428
24436
  const list2 = (dir) => {
23429
24437
  const out = [];
23430
24438
  const walk = (d, prefix) => {
23431
- for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
24439
+ for (const e of (0, import_node_fs46.readdirSync)(d, { withFileTypes: true })) {
23432
24440
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23433
24441
  if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
23434
24442
  else if (e.isFile()) out.push(rel);
@@ -23578,20 +24586,76 @@ async function handoffToSetup(enabled, claudeInstalled) {
23578
24586
  console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
23579
24587
  console.log("");
23580
24588
  const startedAt = Date.now();
23581
- const run2 = (0, import_node_child_process13.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
24589
+ const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
23582
24590
  if (run2.error) {
23583
24591
  printWarn(`Could not start Claude Code: ${run2.error.message}`);
23584
24592
  return instruct("start it yourself and run the skill there");
23585
24593
  }
23586
24594
  await reportPhaseTwo(startedAt);
23587
24595
  }
24596
+ async function installSkills(force, step) {
24597
+ step("Installing skills");
24598
+ const dataDir = resolveDataDir();
24599
+ const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
24600
+ const skillsDest = ".claude/skills";
24601
+ let skillsInstalled = 0;
24602
+ for (const skill of SKILLS) {
24603
+ const src = (0, import_node_path29.join)(skillsSource, skill);
24604
+ const dest = (0, import_node_path29.join)(skillsDest, skill);
24605
+ if (!(0, import_node_fs46.existsSync)(src)) {
24606
+ printWarn(` Skill data not found: ${skill}`);
24607
+ continue;
24608
+ }
24609
+ if ((0, import_node_fs46.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
24610
+ skillsInstalled++;
24611
+ continue;
24612
+ }
24613
+ await copyDir(src, dest);
24614
+ skillsInstalled++;
24615
+ }
24616
+ printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
24617
+ }
24618
+ async function adoptPluginWiring(gitMoments, moments) {
24619
+ const settings = await readSettings();
24620
+ const stripped = removeVerityHooks(settings);
24621
+ const hadAny = JSON.stringify(settings.hooks ?? {}) !== JSON.stringify(stripped.hooks ?? {});
24622
+ if (hadAny) {
24623
+ await writeSettings(stripped);
24624
+ printInfo(" Removed this project's Verity hooks from .claude/settings.json \u2713");
24625
+ printInfo(" The plugin wires them now, so each turn is reviewed once.");
24626
+ } else {
24627
+ printInfo(" Wired by the Verity plugin \u2014 nothing to reconcile here \u2713");
24628
+ }
24629
+ printInfo(` Pre-commit gate: ${gitMoments.includes("commit") ? "on" : "off"}`);
24630
+ printInfo(` Pre-push/PR gate: ${gitMoments.includes("push") ? "on" : "off"}`);
24631
+ printInfo(" Stop + intent + baseline + compact + session-end: always on \u2713");
24632
+ if (!moments.includes("stop")) {
24633
+ printWarn(" Turning the Stop review off is not yet supported under the plugin \u2014 it stays on.");
24634
+ }
24635
+ }
24636
+ async function reconcileOwnWiring(moments) {
24637
+ await applyMomentSelection(moments);
24638
+ const hookStatus = await checkAllVerityHooks();
24639
+ printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
24640
+ printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
24641
+ printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
24642
+ printInfo(" Intent + baseline + compact + session-end: always on \u2713");
24643
+ if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
24644
+ printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
24645
+ printWarn(" Enable one: verity hooks install --moments stop");
24646
+ }
24647
+ }
23588
24648
  function registerInitCommand(program2) {
23589
- program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").action(async (opts) => {
24649
+ program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option(
24650
+ "--plugin-mode",
24651
+ "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has"
24652
+ ).action(async (opts) => {
23590
24653
  const force = opts.force ?? false;
23591
24654
  const wantsHandoff = opts.setup !== false;
23592
24655
  const defaultsOnly = (opts.yes ?? false) || !interactive();
24656
+ const pluginMode = opts.pluginMode ?? pluginActiveHere();
23593
24657
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
23594
- const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
24658
+ const isProject = projectMarkers.some((m) => (0, import_node_fs46.existsSync)(m));
23595
24659
  if (!isProject) {
23596
24660
  printError("No project detected in the current directory.");
23597
24661
  printInfo('Run "verity init" from your project root.');
@@ -23639,26 +24703,11 @@ function registerInitCommand(program2) {
23639
24703
  }
23640
24704
  const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
23641
24705
  console.log("");
23642
- step("Installing skills");
23643
- const dataDir = resolveDataDir();
23644
- const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
23645
- const skillsDest = ".claude/skills";
23646
- let skillsInstalled = 0;
23647
- for (const skill of SKILLS) {
23648
- const src = (0, import_node_path29.join)(skillsSource, skill);
23649
- const dest = (0, import_node_path29.join)(skillsDest, skill);
23650
- if (!(0, import_node_fs44.existsSync)(src)) {
23651
- printWarn(` Skill data not found: ${skill}`);
23652
- continue;
23653
- }
23654
- if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
23655
- skillsInstalled++;
23656
- continue;
23657
- }
23658
- await copyDir(src, dest);
23659
- skillsInstalled++;
24706
+ if (pluginMode) {
24707
+ printInfo("Skipping skills \u2014 the Verity plugin provides them, namespaced as /verity:<name>.");
24708
+ } else {
24709
+ await installSkills(force, step);
23660
24710
  }
23661
- printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
23662
24711
  step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
23663
24712
  const previous = await readSetupState();
23664
24713
  const answers = await askSetupQuestions(defaultsOnly, previous);
@@ -23705,15 +24754,15 @@ function registerInitCommand(program2) {
23705
24754
  await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
23706
24755
  console.log("");
23707
24756
  step("Wiring Claude Code hooks");
23708
- await applyMomentSelection(moments);
23709
- const hookStatus = await checkAllVerityHooks();
23710
- printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
23711
- printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
23712
- printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
23713
- printInfo(` Intent + baseline + compact + session-end: always on \u2713`);
23714
- if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
23715
- printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
23716
- printWarn(" Enable one: verity hooks install --moments stop");
24757
+ const gitMoments = [
24758
+ ...moments.includes("pre-commit") ? ["commit"] : [],
24759
+ ...moments.includes("pre-push") ? ["push"] : []
24760
+ ];
24761
+ writeProjectConfig({ git_moments: gitMoments });
24762
+ if (pluginMode) {
24763
+ await adoptPluginWiring(gitMoments, moments);
24764
+ } else {
24765
+ await reconcileOwnWiring(moments);
23717
24766
  }
23718
24767
  console.log("");
23719
24768
  step("Sign in to Verity (optional)");
@@ -23762,7 +24811,7 @@ function registerInitCommand(program2) {
23762
24811
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
23763
24812
  init: {
23764
24813
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
23765
- cli_version: true ? "0.31.1-experimental.be74f71" : "dev"
24814
+ cli_version: true ? "0.31.2" : "dev"
23766
24815
  }
23767
24816
  });
23768
24817
  } catch (err) {
@@ -23771,9 +24820,13 @@ function registerInitCommand(program2) {
23771
24820
  console.log("");
23772
24821
  printInfo("This machine is set up.");
23773
24822
  console.log("");
23774
- console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
23775
- console.log(" learn, memory, insights, reflect)");
23776
- console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
24823
+ if (pluginMode) {
24824
+ console.log(" (skills and hooks come from the Verity plugin, not this project)");
24825
+ } else {
24826
+ console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
24827
+ console.log(" learn, memory, insights, reflect)");
24828
+ console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
24829
+ }
23777
24830
  console.log(" .verity/memory/ knowledge base (commit to git)");
23778
24831
  console.log(" .verity/setup.json your answers, read by /verity-setup");
23779
24832
  console.log(" .gitignore Verity block (whitelist form)");
@@ -23786,7 +24839,7 @@ function registerInitCommand(program2) {
23786
24839
  }
23787
24840
 
23788
24841
  // src/commands/uninstall.ts
23789
- var import_node_fs45 = require("node:fs");
24842
+ var import_node_fs47 = require("node:fs");
23790
24843
  var import_node_path30 = require("node:path");
23791
24844
  var SKILL_NAMES = [
23792
24845
  "verity-setup",
@@ -23807,10 +24860,10 @@ function registerUninstallCommand(program2) {
23807
24860
  const skillsRoot = projectPath(".claude/skills");
23808
24861
  for (const name of SKILL_NAMES) {
23809
24862
  const dir = (0, import_node_path30.join)(skillsRoot, name);
23810
- if ((0, import_node_fs45.existsSync)(dir)) {
24863
+ if ((0, import_node_fs47.existsSync)(dir)) {
23811
24864
  actions.push({
23812
24865
  label: `Remove .claude/skills/${name}/`,
23813
- apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
24866
+ apply: () => (0, import_node_fs47.rmSync)(dir, { recursive: true, force: true })
23814
24867
  });
23815
24868
  }
23816
24869
  }
@@ -23824,24 +24877,24 @@ function registerUninstallCommand(program2) {
23824
24877
  });
23825
24878
  }
23826
24879
  const verityDir = projectPath(VERITY_DIR);
23827
- if ((0, import_node_fs45.existsSync)(verityDir)) {
24880
+ if ((0, import_node_fs47.existsSync)(verityDir)) {
23828
24881
  actions.push({
23829
24882
  label: `Remove ${VERITY_DIR}/`,
23830
- apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
24883
+ apply: () => (0, import_node_fs47.rmSync)(verityDir, { recursive: true, force: true })
23831
24884
  });
23832
24885
  }
23833
24886
  if (!keepVerityMd) {
23834
24887
  const verityMd = projectPath(VERITY_MD_FILE);
23835
- if ((0, import_node_fs45.existsSync)(verityMd)) {
24888
+ if ((0, import_node_fs47.existsSync)(verityMd)) {
23836
24889
  actions.push({
23837
24890
  label: `Remove ${VERITY_MD_FILE}`,
23838
- apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
24891
+ apply: () => (0, import_node_fs47.rmSync)(verityMd, { force: true })
23839
24892
  });
23840
24893
  }
23841
24894
  }
23842
24895
  const cleanupEmptyDir = (path) => {
23843
- if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
23844
- (0, import_node_fs45.rmdirSync)(path);
24896
+ if ((0, import_node_fs47.existsSync)(path) && (0, import_node_fs47.statSync)(path).isDirectory() && (0, import_node_fs47.readdirSync)(path).length === 0) {
24897
+ (0, import_node_fs47.rmdirSync)(path);
23845
24898
  }
23846
24899
  };
23847
24900
  actions.push({
@@ -23853,10 +24906,10 @@ function registerUninstallCommand(program2) {
23853
24906
  });
23854
24907
  const home = process.env.HOME ?? "";
23855
24908
  const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
23856
- if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
24909
+ if (purgeGlobal && (0, import_node_fs47.existsSync)(globalVerityDir)) {
23857
24910
  actions.push({
23858
24911
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
23859
- apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
24912
+ apply: () => (0, import_node_fs47.rmSync)(globalVerityDir, { recursive: true, force: true })
23860
24913
  });
23861
24914
  }
23862
24915
  if (actions.length === 0) {
@@ -24050,7 +25103,7 @@ function registerTaskCommands(program2) {
24050
25103
  }
24051
25104
 
24052
25105
  // src/commands/reset.ts
24053
- var import_node_fs46 = require("node:fs");
25106
+ var import_node_fs48 = require("node:fs");
24054
25107
  var import_node_path31 = require("node:path");
24055
25108
  function registerResetCommand(program2) {
24056
25109
  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) => {
@@ -24088,11 +25141,11 @@ function registerResetCommand(program2) {
24088
25141
  }
24089
25142
  const cacheDir = projectPath(CACHE_DIR);
24090
25143
  let purged = 0;
24091
- if ((0, import_node_fs46.existsSync)(cacheDir)) {
24092
- for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
25144
+ if ((0, import_node_fs48.existsSync)(cacheDir)) {
25145
+ for (const entry of (0, import_node_fs48.readdirSync)(cacheDir)) {
24093
25146
  if (entry.startsWith("pending-")) {
24094
25147
  try {
24095
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
25148
+ (0, import_node_fs48.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
24096
25149
  purged++;
24097
25150
  } catch {
24098
25151
  }
@@ -24107,19 +25160,19 @@ function registerResetCommand(program2) {
24107
25160
  projectPath(`${VERITY_DIR}/.last-analysis`)
24108
25161
  ];
24109
25162
  for (const file of filesToClear) {
24110
- if ((0, import_node_fs46.existsSync)(file)) {
25163
+ if ((0, import_node_fs48.existsSync)(file)) {
24111
25164
  try {
24112
- (0, import_node_fs46.writeFileSync)(file, "");
25165
+ (0, import_node_fs48.writeFileSync)(file, "");
24113
25166
  } catch {
24114
25167
  }
24115
25168
  }
24116
25169
  }
24117
25170
  if (opts.all) {
24118
25171
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
24119
- if ((0, import_node_fs46.existsSync)(logsDir)) {
24120
- for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
25172
+ if ((0, import_node_fs48.existsSync)(logsDir)) {
25173
+ for (const entry of (0, import_node_fs48.readdirSync)(logsDir)) {
24121
25174
  try {
24122
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
25175
+ (0, import_node_fs48.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
24123
25176
  } catch {
24124
25177
  }
24125
25178
  }
@@ -24427,8 +25480,8 @@ function registerTelemetryCommands(program2) {
24427
25480
  }
24428
25481
 
24429
25482
  // src/cli.ts
24430
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.be74f71").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
24431
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.be74f71");
25483
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
25484
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.2");
24432
25485
  setUserNamedServiceUrl(program.opts().serviceUrl);
24433
25486
  try {
24434
25487
  await foldLegacyLocalCredential();