@codacy/verity-cli 0.31.0-experimental.48d33ea → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/verity.js CHANGED
@@ -10407,6 +10407,13 @@ var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
10407
10407
  var CONVERSATION_MAX_ENTRIES = 10;
10408
10408
  var CONVERSATION_WINDOW_MINUTES = 15;
10409
10409
  var MAX_FINDINGS = 25;
10410
+ var GIT_MOMENT_BUDGET = {
10411
+ maxFiles: 120,
10412
+ maxFileBytes: 98304,
10413
+ // 96KB, under the server's 100k chars
10414
+ maxTotalBytes: 583680
10415
+ // 570KB, under the server's 600k chars
10416
+ };
10410
10417
  var ANALYZABLE_EXTENSIONS = /* @__PURE__ */ new Set([
10411
10418
  "ts",
10412
10419
  "tsx",
@@ -10499,7 +10506,7 @@ var SECURITY_PATTERNS = [
10499
10506
  /Dockerfile/
10500
10507
  ];
10501
10508
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10502
- var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10509
+ var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10503
10510
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10504
10511
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10505
10512
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -13533,6 +13540,54 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
13533
13540
  var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
13534
13541
  var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
13535
13542
  var CLAUDE_MD_PROSE = [
13543
+ "## Project Memory",
13544
+ "",
13545
+ "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
13546
+ "non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
13547
+ "that may apply to the change you are about to make. Open specific node files via",
13548
+ "the Read tool when the title or scope suggests relevance.",
13549
+ "",
13550
+ "The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
13551
+ "superseded \u2014 ignore them unless investigating history.",
13552
+ "",
13553
+ "## Quality gate: accepted risks",
13554
+ "",
13555
+ "When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
13556
+ 'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
13557
+ "a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
13558
+ "the user saying so in this conversation. The --reason must cite that source.",
13559
+ "",
13560
+ "Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
13561
+ "binds to the file's current bytes and voids automatically when the file changes,",
13562
+ "and every waive is recorded in the run ledger. For a pattern-level false positive",
13563
+ "use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
13564
+ "",
13565
+ "> Durable, hand-curated guidance goes in the preserve region below (it survives",
13566
+ "> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
13567
+ "> markers is tool-owned and overwritten on each run.",
13568
+ "",
13569
+ "## Housekeeping Turns",
13570
+ "",
13571
+ "When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
13572
+ "rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
13573
+ "",
13574
+ "```bash",
13575
+ 'verity ignore --turn --agent --reason "pulling latest before starting"',
13576
+ "```",
13577
+ "",
13578
+ "This skips the review for that turn, which saves the turn Verity would",
13579
+ "otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
13580
+ "`--turn` when a single piece of housekeeping spans several turns.",
13581
+ "",
13582
+ "**It is a claim about the turn, not a way to silence review.** The declaration",
13583
+ "is checked against what the turn actually did: if anything is authored \u2014 by you,",
13584
+ "by a subagent, or by a shell command that can write files \u2014 it voids, the review",
13585
+ "runs anyway, and the broken declaration is reported. So declare housekeeping you",
13586
+ "are about to do, never work you have already done, and never as a way to get past",
13587
+ "a finding. Declarations are budgeted per session and every one is recorded with",
13588
+ "its reason."
13589
+ ].join("\n");
13590
+ var CLAUDE_MD_PROSE_PRE_WAIVE = [
13536
13591
  "## Project Memory",
13537
13592
  "",
13538
13593
  "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
@@ -13693,7 +13748,12 @@ function extractPreserveContent(interior) {
13693
13748
  }
13694
13749
  function stripKnownProse(interior) {
13695
13750
  const trimmed = interior.replace(/^\n+/, "");
13696
- for (const prose of [CLAUDE_MD_PROSE, CLAUDE_MD_PROSE_PRE_IGNORE, CLAUDE_MD_PROSE_LEGACY]) {
13751
+ for (const prose of [
13752
+ CLAUDE_MD_PROSE,
13753
+ CLAUDE_MD_PROSE_PRE_WAIVE,
13754
+ CLAUDE_MD_PROSE_PRE_IGNORE,
13755
+ CLAUDE_MD_PROSE_LEGACY
13756
+ ]) {
13697
13757
  if (trimmed.startsWith(prose)) return trimmed.slice(prose.length);
13698
13758
  }
13699
13759
  return trimmed;
@@ -16571,6 +16631,15 @@ function registerStandardCommands(program2) {
16571
16631
  var import_promises10 = require("node:fs/promises");
16572
16632
  function registerConfigCommands(program2) {
16573
16633
  const config = program2.command("config").description("Manage analysis configuration");
16634
+ config.command("service-url").description("Print the resolved service URL (used by the shipped skills)").action(async () => {
16635
+ const globals = program2.opts();
16636
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
16637
+ if (!urlResult.ok) {
16638
+ printError(urlResult.error);
16639
+ process.exit(1);
16640
+ }
16641
+ process.stdout.write(urlResult.data + "\n");
16642
+ });
16574
16643
  config.command("push").description("Upload the analysis config to the service").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
16575
16644
  const globals = program2.opts();
16576
16645
  const tokenResult = await resolveToken(globals.token);
@@ -18383,7 +18452,7 @@ function channelSilence(input) {
18383
18452
  // src/lib/cli-version.ts
18384
18453
  function cliVersion() {
18385
18454
  try {
18386
- return true ? "0.31.0-experimental.48d33ea" : "dev";
18455
+ return true ? "0.31.0" : "dev";
18387
18456
  } catch {
18388
18457
  return "dev";
18389
18458
  }
@@ -18858,6 +18927,28 @@ function discoverPlans() {
18858
18927
  }
18859
18928
  return result;
18860
18929
  }
18930
+ var GUARD_DOC_EXT = /\.(md|mdx|txt|rst|adoc)$/i;
18931
+ function discoverGuardDocs(rangeFiles2) {
18932
+ const result = [];
18933
+ let totalBytes = 0;
18934
+ for (const path of rangeFiles2) {
18935
+ if (result.length >= MAX_SPEC_FILES) break;
18936
+ if (!GUARD_DOC_EXT.test(path)) continue;
18937
+ if (path.startsWith("/") || path.includes("..")) continue;
18938
+ if (!(0, import_node_fs27.existsSync)(path)) continue;
18939
+ try {
18940
+ const stat3 = (0, import_node_fs27.statSync)(path);
18941
+ if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
18942
+ if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
18943
+ const content = (0, import_node_fs27.readFileSync)(path, "utf-8");
18944
+ if (!content) continue;
18945
+ result.push({ name: path, content });
18946
+ totalBytes += content.length;
18947
+ } catch {
18948
+ }
18949
+ }
18950
+ return result;
18951
+ }
18861
18952
 
18862
18953
  // src/commands/analyze/phases/03-intent-inputs.ts
18863
18954
  async function intentInputs(run) {
@@ -21573,8 +21664,8 @@ async function runReview(opts, globals) {
21573
21664
  for (const p of specPaths) {
21574
21665
  if (!(0, import_node_fs37.existsSync)(p)) continue;
21575
21666
  try {
21576
- const { readFileSync: readFileSync23 } = await import("node:fs");
21577
- const content = readFileSync23(p, "utf-8");
21667
+ const { readFileSync: readFileSync24 } = await import("node:fs");
21668
+ const content = readFileSync24(p, "utf-8");
21578
21669
  specs.push({ path: p, content: content.slice(0, 10240) });
21579
21670
  } catch {
21580
21671
  }
@@ -21808,13 +21899,11 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
21808
21899
  }
21809
21900
  };
21810
21901
  if (coverageTelemetry) requestBody.coverage_telemetry = coverageTelemetry;
21811
- const specs = discoverSpecs();
21812
- const plans = discoverPlans();
21813
- if (specs.length > 0 || plans.length > 0 || statedIntent) {
21902
+ const docs = discoverGuardDocs(files);
21903
+ if (docs.length > 0 || statedIntent) {
21814
21904
  const intentContext = {};
21815
21905
  if (statedIntent) intentContext.user_prompt = statedIntent;
21816
- if (specs.length > 0) intentContext.specs = specs;
21817
- if (plans.length > 0) intentContext.plans = plans;
21906
+ if (docs.length > 0) intentContext.plans = docs;
21818
21907
  requestBody.intent_context = intentContext;
21819
21908
  }
21820
21909
  return requestBody;
@@ -21895,7 +21984,7 @@ async function runGuard(opts, globals) {
21895
21984
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
21896
21985
  if (!tokenResult.ok || !urlResult.ok) process.exit(0);
21897
21986
  logEvent("guard_frame", { moment, ...frameTelemetry(frame, range) });
21898
- const codeDelta = collectCodeDelta(files);
21987
+ const codeDelta = collectCodeDelta(files, GIT_MOMENT_BUDGET);
21899
21988
  if (codeDelta.total_files === 0) process.exit(0);
21900
21989
  const statedIntent = extractStatedIntent(
21901
21990
  moment,
@@ -21921,6 +22010,7 @@ async function runGuard(opts, globals) {
21921
22010
  excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
21922
22011
  };
21923
22012
  logToFileOnly(coverageBlock(coverage));
22013
+ const reviewStart = Date.now();
21924
22014
  const result = await analyzeRequest({
21925
22015
  serviceUrl: urlResult.data,
21926
22016
  token: tokenResult.data.token,
@@ -21929,6 +22019,7 @@ async function runGuard(opts, globals) {
21929
22019
  timeout: 29e4,
21930
22020
  cmd: "guard"
21931
22021
  });
22022
+ const reviewSecs = Math.max(1, Math.round((Date.now() - reviewStart) / 1e3));
21932
22023
  if (!result.ok) {
21933
22024
  const authRemedy = result.error.startsWith("STALE_VERIFICATION") ? " Your GitHub verification expired \u2014 run `verity login` to re-verify." : result.error.startsWith("FORBIDDEN") ? " No access grant for this repository \u2014 run `verity login` to refresh your grants." : result.error.startsWith("INVALID_TOKEN") ? " Your Verity login expired or was revoked \u2014 run `verity login` to sign in again." : "";
21934
22025
  emitAllowNotice(
@@ -21941,7 +22032,7 @@ async function runGuard(opts, globals) {
21941
22032
  const decision = response.gate_decision ?? "(unrecognised)";
21942
22033
  const viewUrl = response.view_url ?? "";
21943
22034
  const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
21944
- const covLine = coverageSummary(coverage);
22035
+ const covLine = `${coverageSummary(coverage)} in ${reviewSecs}s`;
21945
22036
  const covDetail = coverageBlock(coverage);
21946
22037
  if (decision === "FAIL" && hasBlockingFinding(response, codeDelta.files.map((f) => f.path))) {
21947
22038
  writeIter(moment, iter + 1);
@@ -22010,6 +22101,8 @@ function writeBlockMessage(moment, response, covDetail) {
22010
22101
 
22011
22102
  `);
22012
22103
  process.stderr.write(`${BOLD}Fix these, then re-run the ${verb}.${NC}
22104
+ `);
22105
+ process.stderr.write(`${DIM}Accepted risk? Record it: verity waive <pattern-id> --file <path> --reason "\u2026" (voids automatically when the file changes).${NC}
22013
22106
  `);
22014
22107
  }
22015
22108
 
@@ -22105,15 +22198,70 @@ function registerIgnoreCommand(program2) {
22105
22198
  });
22106
22199
  }
22107
22200
 
22201
+ // src/commands/waive.ts
22202
+ var import_node_crypto12 = require("node:crypto");
22203
+ var import_node_fs39 = require("node:fs");
22204
+ function registerWaiveCommand(program2) {
22205
+ 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) => {
22206
+ const globals = program2.opts();
22207
+ try {
22208
+ process.chdir(repoRoot());
22209
+ } catch {
22210
+ }
22211
+ if (opts.reason.trim().length < 20) {
22212
+ printError("A waive needs a real reason (>= 20 chars) \u2014 name the human disposition it records.");
22213
+ process.exit(1);
22214
+ }
22215
+ const tokenResult = await resolveToken(globals.token);
22216
+ if (!tokenResult.ok) {
22217
+ printError(tokenResult.error);
22218
+ process.exit(1);
22219
+ }
22220
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
22221
+ if (!urlResult.ok) {
22222
+ printError(urlResult.error);
22223
+ process.exit(1);
22224
+ }
22225
+ const body = {
22226
+ pattern_id: patternId,
22227
+ reason: opts.reason.trim()
22228
+ };
22229
+ if (opts.file) {
22230
+ body.file = opts.file;
22231
+ try {
22232
+ body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs39.readFileSync)(opts.file)).digest("hex");
22233
+ } catch {
22234
+ printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
22235
+ process.exit(1);
22236
+ }
22237
+ }
22238
+ const result = await apiRequest({
22239
+ method: "POST",
22240
+ path: "/feedback/waive",
22241
+ serviceUrl: urlResult.data,
22242
+ token: tokenResult.data.token,
22243
+ body,
22244
+ verbose: globals.verbose
22245
+ });
22246
+ if (!result.ok) {
22247
+ printError(`Couldn't record the waive: ${result.error}`);
22248
+ process.exit(1);
22249
+ }
22250
+ logEvent("waive_recorded", { pattern: patternId, file: opts.file ?? null, waived: result.data.waived });
22251
+ printInfo(`Waived ${result.data.waived} open statement(s) for '${patternId}'${opts.file ? ` on ${opts.file}` : ""}.`);
22252
+ printInfo(result.data.note);
22253
+ });
22254
+ }
22255
+
22108
22256
  // src/commands/init.ts
22109
- var import_node_fs40 = require("node:fs");
22257
+ var import_node_fs41 = require("node:fs");
22110
22258
  var import_promises13 = require("node:fs/promises");
22111
22259
  var import_node_path29 = require("node:path");
22112
22260
  var import_node_child_process11 = require("node:child_process");
22113
22261
  var readline2 = __toESM(require("node:readline/promises"));
22114
22262
 
22115
22263
  // src/commands/migrate.ts
22116
- var import_node_fs39 = require("node:fs");
22264
+ var import_node_fs40 = require("node:fs");
22117
22265
  var import_node_path28 = require("node:path");
22118
22266
  var import_node_child_process10 = require("node:child_process");
22119
22267
 
@@ -22245,10 +22393,10 @@ async function runMigration(opts = {}) {
22245
22393
  function migrateProjectDir(root, actions) {
22246
22394
  const gateDir = (0, import_node_path28.join)(root, ".gate");
22247
22395
  const verityDir = (0, import_node_path28.join)(root, ".verity");
22248
- if ((0, import_node_fs39.existsSync)(gateDir) && !(0, import_node_fs39.existsSync)(verityDir)) {
22396
+ if ((0, import_node_fs40.existsSync)(gateDir) && !(0, import_node_fs40.existsSync)(verityDir)) {
22249
22397
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
22250
22398
  }
22251
- if ((0, import_node_fs39.existsSync)(gateDir) && (0, import_node_fs39.existsSync)(verityDir)) {
22399
+ if ((0, import_node_fs40.existsSync)(gateDir) && (0, import_node_fs40.existsSync)(verityDir)) {
22252
22400
  return migrateProjectDirCarry(gateDir, verityDir, actions);
22253
22401
  }
22254
22402
  return false;
@@ -22269,13 +22417,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
22269
22417
  }
22270
22418
  }
22271
22419
  if (moved) {
22272
- if ((0, import_node_fs39.existsSync)(gateDir)) {
22420
+ if ((0, import_node_fs40.existsSync)(gateDir)) {
22273
22421
  const carried = carryLegacyContents(gateDir, verityDir);
22274
22422
  if (carried > 0) {
22275
22423
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
22276
22424
  }
22277
22425
  try {
22278
- (0, import_node_fs39.rmSync)(gateDir, { recursive: true, force: true });
22426
+ (0, import_node_fs40.rmSync)(gateDir, { recursive: true, force: true });
22279
22427
  } catch {
22280
22428
  }
22281
22429
  }
@@ -22291,7 +22439,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
22291
22439
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
22292
22440
  }
22293
22441
  try {
22294
- (0, import_node_fs39.rmSync)(gateDir, { recursive: true, force: true });
22442
+ (0, import_node_fs40.rmSync)(gateDir, { recursive: true, force: true });
22295
22443
  } catch {
22296
22444
  }
22297
22445
  return carried > 0;
@@ -22300,9 +22448,9 @@ function migrateGlobalCredentials(home, actions) {
22300
22448
  if (!home) return;
22301
22449
  const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
22302
22450
  const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
22303
- if (!(0, import_node_fs39.existsSync)(gateCreds)) return;
22304
- if (!(0, import_node_fs39.existsSync)(verityCreds)) {
22305
- (0, import_node_fs39.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
22451
+ if (!(0, import_node_fs40.existsSync)(gateCreds)) return;
22452
+ if (!(0, import_node_fs40.existsSync)(verityCreds)) {
22453
+ (0, import_node_fs40.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
22306
22454
  moveFile(gateCreds, verityCreds);
22307
22455
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
22308
22456
  return;
@@ -22325,7 +22473,7 @@ async function migrateLegacyHooks(root, actions) {
22325
22473
  }
22326
22474
  async function migrateClaudeMd(root, actions) {
22327
22475
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
22328
- const hadLegacyBlock = (0, import_node_fs39.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
22476
+ const hadLegacyBlock = (0, import_node_fs40.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
22329
22477
  if (!hadLegacyBlock) return;
22330
22478
  try {
22331
22479
  await ensureClaudeMdPointer(root);
@@ -22337,7 +22485,7 @@ async function migrateClaudeMd(root, actions) {
22337
22485
  function migrateStandardFile(root, actions) {
22338
22486
  const gateMd = (0, import_node_path28.join)(root, "GATE.md");
22339
22487
  const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
22340
- if (!(0, import_node_fs39.existsSync)(gateMd) || (0, import_node_fs39.existsSync)(verityMd)) return;
22488
+ if (!(0, import_node_fs40.existsSync)(gateMd) || (0, import_node_fs40.existsSync)(verityMd)) return;
22341
22489
  let moved = false;
22342
22490
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
22343
22491
  try {
@@ -22349,12 +22497,12 @@ function migrateStandardFile(root, actions) {
22349
22497
  if (!moved) moveFile(gateMd, verityMd);
22350
22498
  const content = readFileSyncSafe(verityMd);
22351
22499
  const refreshed = content.split("GATE.md").join("VERITY.md");
22352
- if (refreshed !== content) (0, import_node_fs39.writeFileSync)(verityMd, refreshed);
22500
+ if (refreshed !== content) (0, import_node_fs40.writeFileSync)(verityMd, refreshed);
22353
22501
  actions.push("Renamed GATE.md \u2192 VERITY.md");
22354
22502
  }
22355
22503
  async function migrateTelemetryHeaders(root, actions) {
22356
22504
  const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
22357
- if (!(0, import_node_fs39.existsSync)(file)) return;
22505
+ if (!(0, import_node_fs40.existsSync)(file)) return;
22358
22506
  let settings;
22359
22507
  try {
22360
22508
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -22402,14 +22550,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
22402
22550
  }
22403
22551
  if (toAppend.length > 0) {
22404
22552
  const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
22405
- (0, import_node_fs39.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
22553
+ (0, import_node_fs40.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
22406
22554
  }
22407
- (0, import_node_fs39.rmSync)(gateCreds, { force: true });
22555
+ (0, import_node_fs40.rmSync)(gateCreds, { force: true });
22408
22556
  return toAppend.length;
22409
22557
  }
22410
22558
  function readFileSyncSafe(path) {
22411
22559
  try {
22412
- return (0, import_node_fs39.readFileSync)(path, "utf-8");
22560
+ return (0, import_node_fs40.readFileSync)(path, "utf-8");
22413
22561
  } catch {
22414
22562
  return "";
22415
22563
  }
@@ -22424,35 +22572,35 @@ function hasStagedChanges(root) {
22424
22572
  }
22425
22573
  function moveDir(from, to) {
22426
22574
  try {
22427
- (0, import_node_fs39.renameSync)(from, to);
22575
+ (0, import_node_fs40.renameSync)(from, to);
22428
22576
  } catch (err) {
22429
22577
  if (err.code !== "EXDEV") throw err;
22430
- (0, import_node_fs39.cpSync)(from, to, { recursive: true });
22431
- (0, import_node_fs39.rmSync)(from, { recursive: true, force: true });
22578
+ (0, import_node_fs40.cpSync)(from, to, { recursive: true });
22579
+ (0, import_node_fs40.rmSync)(from, { recursive: true, force: true });
22432
22580
  }
22433
22581
  }
22434
22582
  function moveFile(from, to) {
22435
22583
  try {
22436
- (0, import_node_fs39.renameSync)(from, to);
22584
+ (0, import_node_fs40.renameSync)(from, to);
22437
22585
  } catch (err) {
22438
22586
  if (err.code !== "EXDEV") throw err;
22439
- (0, import_node_fs39.cpSync)(from, to);
22440
- (0, import_node_fs39.rmSync)(from, { force: true });
22587
+ (0, import_node_fs40.cpSync)(from, to);
22588
+ (0, import_node_fs40.rmSync)(from, { force: true });
22441
22589
  }
22442
22590
  }
22443
22591
  function carryLegacyContents(gateDir, verityDir) {
22444
22592
  let copied = 0;
22445
22593
  const walk = (relDir) => {
22446
22594
  const srcDir = (0, import_node_path28.join)(gateDir, relDir);
22447
- for (const entry of (0, import_node_fs39.readdirSync)(srcDir)) {
22595
+ for (const entry of (0, import_node_fs40.readdirSync)(srcDir)) {
22448
22596
  const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
22449
22597
  const src = (0, import_node_path28.join)(gateDir, rel);
22450
22598
  const dest = (0, import_node_path28.join)(verityDir, rel);
22451
- if ((0, import_node_fs39.statSync)(src).isDirectory()) {
22599
+ if ((0, import_node_fs40.statSync)(src).isDirectory()) {
22452
22600
  walk(rel);
22453
- } else if (!(0, import_node_fs39.existsSync)(dest)) {
22454
- (0, import_node_fs39.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
22455
- (0, import_node_fs39.cpSync)(src, dest);
22601
+ } else if (!(0, import_node_fs40.existsSync)(dest)) {
22602
+ (0, import_node_fs40.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
22603
+ (0, import_node_fs40.cpSync)(src, dest);
22456
22604
  copied++;
22457
22605
  }
22458
22606
  }
@@ -22463,20 +22611,20 @@ function carryLegacyContents(gateDir, verityDir) {
22463
22611
  async function needsMigration(root = repoRoot()) {
22464
22612
  const gateDir = (0, import_node_path28.join)(root, ".gate");
22465
22613
  const verityDir = (0, import_node_path28.join)(root, ".verity");
22466
- if ((0, import_node_fs39.existsSync)(gateDir) && !(0, import_node_fs39.existsSync)(verityDir)) return true;
22467
- if ((0, import_node_fs39.existsSync)(gateDir) && (0, import_node_fs39.existsSync)(verityDir)) {
22468
- if ((0, import_node_fs39.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs39.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
22614
+ if ((0, import_node_fs40.existsSync)(gateDir) && !(0, import_node_fs40.existsSync)(verityDir)) return true;
22615
+ if ((0, import_node_fs40.existsSync)(gateDir) && (0, import_node_fs40.existsSync)(verityDir)) {
22616
+ if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
22469
22617
  return true;
22470
22618
  }
22471
- if ((0, import_node_fs39.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs39.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
22619
+ if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
22472
22620
  return true;
22473
22621
  }
22474
22622
  }
22475
22623
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
22476
- if ((0, import_node_fs39.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
22624
+ if ((0, import_node_fs40.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
22477
22625
  return true;
22478
22626
  }
22479
- if ((0, import_node_fs39.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs39.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
22627
+ if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
22480
22628
  return true;
22481
22629
  }
22482
22630
  if (await hasLegacyHooksAt(root)) return true;
@@ -22620,7 +22768,7 @@ function resolveDataDir() {
22620
22768
  // local dev: running from repo root
22621
22769
  ];
22622
22770
  for (const candidate of candidates) {
22623
- if ((0, import_node_fs40.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
22771
+ if ((0, import_node_fs41.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
22624
22772
  return candidate;
22625
22773
  }
22626
22774
  }
@@ -22636,7 +22784,7 @@ function registerInitCommand(program2) {
22636
22784
  program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
22637
22785
  const force = opts.force ?? false;
22638
22786
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
22639
- const isProject = projectMarkers.some((m) => (0, import_node_fs40.existsSync)(m));
22787
+ const isProject = projectMarkers.some((m) => (0, import_node_fs41.existsSync)(m));
22640
22788
  if (!isProject) {
22641
22789
  printError("No project detected in the current directory.");
22642
22790
  printInfo('Run "verity init" from your project root.');
@@ -22706,14 +22854,14 @@ function registerInitCommand(program2) {
22706
22854
  for (const skill of skills) {
22707
22855
  const src = (0, import_node_path29.join)(skillsSource, skill);
22708
22856
  const dest = (0, import_node_path29.join)(skillsDest, skill);
22709
- if (!(0, import_node_fs40.existsSync)(src)) {
22857
+ if (!(0, import_node_fs41.existsSync)(src)) {
22710
22858
  printWarn(` Skill data not found: ${skill}`);
22711
22859
  continue;
22712
22860
  }
22713
- if ((0, import_node_fs40.existsSync)(dest) && !force) {
22861
+ if ((0, import_node_fs41.existsSync)(dest) && !force) {
22714
22862
  const srcSkill = (0, import_node_path29.join)(src, "SKILL.md");
22715
22863
  const destSkill = (0, import_node_path29.join)(dest, "SKILL.md");
22716
- if ((0, import_node_fs40.existsSync)(destSkill)) {
22864
+ if ((0, import_node_fs41.existsSync)(destSkill)) {
22717
22865
  try {
22718
22866
  const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
22719
22867
  const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
@@ -22791,7 +22939,7 @@ function registerInitCommand(program2) {
22791
22939
  }
22792
22940
 
22793
22941
  // src/commands/uninstall.ts
22794
- var import_node_fs41 = require("node:fs");
22942
+ var import_node_fs42 = require("node:fs");
22795
22943
  var import_node_path30 = require("node:path");
22796
22944
  var SKILL_NAMES = [
22797
22945
  "verity-setup",
@@ -22812,10 +22960,10 @@ function registerUninstallCommand(program2) {
22812
22960
  const skillsRoot = projectPath(".claude/skills");
22813
22961
  for (const name of SKILL_NAMES) {
22814
22962
  const dir = (0, import_node_path30.join)(skillsRoot, name);
22815
- if ((0, import_node_fs41.existsSync)(dir)) {
22963
+ if ((0, import_node_fs42.existsSync)(dir)) {
22816
22964
  actions.push({
22817
22965
  label: `Remove .claude/skills/${name}/`,
22818
- apply: () => (0, import_node_fs41.rmSync)(dir, { recursive: true, force: true })
22966
+ apply: () => (0, import_node_fs42.rmSync)(dir, { recursive: true, force: true })
22819
22967
  });
22820
22968
  }
22821
22969
  }
@@ -22829,24 +22977,24 @@ function registerUninstallCommand(program2) {
22829
22977
  });
22830
22978
  }
22831
22979
  const verityDir = projectPath(VERITY_DIR);
22832
- if ((0, import_node_fs41.existsSync)(verityDir)) {
22980
+ if ((0, import_node_fs42.existsSync)(verityDir)) {
22833
22981
  actions.push({
22834
22982
  label: `Remove ${VERITY_DIR}/`,
22835
- apply: () => (0, import_node_fs41.rmSync)(verityDir, { recursive: true, force: true })
22983
+ apply: () => (0, import_node_fs42.rmSync)(verityDir, { recursive: true, force: true })
22836
22984
  });
22837
22985
  }
22838
22986
  if (!keepVerityMd) {
22839
22987
  const verityMd = projectPath(VERITY_MD_FILE);
22840
- if ((0, import_node_fs41.existsSync)(verityMd)) {
22988
+ if ((0, import_node_fs42.existsSync)(verityMd)) {
22841
22989
  actions.push({
22842
22990
  label: `Remove ${VERITY_MD_FILE}`,
22843
- apply: () => (0, import_node_fs41.rmSync)(verityMd, { force: true })
22991
+ apply: () => (0, import_node_fs42.rmSync)(verityMd, { force: true })
22844
22992
  });
22845
22993
  }
22846
22994
  }
22847
22995
  const cleanupEmptyDir = (path) => {
22848
- if ((0, import_node_fs41.existsSync)(path) && (0, import_node_fs41.statSync)(path).isDirectory() && (0, import_node_fs41.readdirSync)(path).length === 0) {
22849
- (0, import_node_fs41.rmdirSync)(path);
22996
+ if ((0, import_node_fs42.existsSync)(path) && (0, import_node_fs42.statSync)(path).isDirectory() && (0, import_node_fs42.readdirSync)(path).length === 0) {
22997
+ (0, import_node_fs42.rmdirSync)(path);
22850
22998
  }
22851
22999
  };
22852
23000
  actions.push({
@@ -22858,10 +23006,10 @@ function registerUninstallCommand(program2) {
22858
23006
  });
22859
23007
  const home = process.env.HOME ?? "";
22860
23008
  const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
22861
- if (purgeGlobal && (0, import_node_fs41.existsSync)(globalVerityDir)) {
23009
+ if (purgeGlobal && (0, import_node_fs42.existsSync)(globalVerityDir)) {
22862
23010
  actions.push({
22863
23011
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
22864
- apply: () => (0, import_node_fs41.rmSync)(globalVerityDir, { recursive: true, force: true })
23012
+ apply: () => (0, import_node_fs42.rmSync)(globalVerityDir, { recursive: true, force: true })
22865
23013
  });
22866
23014
  }
22867
23015
  if (actions.length === 0) {
@@ -23055,7 +23203,7 @@ function registerTaskCommands(program2) {
23055
23203
  }
23056
23204
 
23057
23205
  // src/commands/reset.ts
23058
- var import_node_fs42 = require("node:fs");
23206
+ var import_node_fs43 = require("node:fs");
23059
23207
  var import_node_path31 = require("node:path");
23060
23208
  function registerResetCommand(program2) {
23061
23209
  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) => {
@@ -23093,11 +23241,11 @@ function registerResetCommand(program2) {
23093
23241
  }
23094
23242
  const cacheDir = projectPath(CACHE_DIR);
23095
23243
  let purged = 0;
23096
- if ((0, import_node_fs42.existsSync)(cacheDir)) {
23097
- for (const entry of (0, import_node_fs42.readdirSync)(cacheDir)) {
23244
+ if ((0, import_node_fs43.existsSync)(cacheDir)) {
23245
+ for (const entry of (0, import_node_fs43.readdirSync)(cacheDir)) {
23098
23246
  if (entry.startsWith("pending-")) {
23099
23247
  try {
23100
- (0, import_node_fs42.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
23248
+ (0, import_node_fs43.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
23101
23249
  purged++;
23102
23250
  } catch {
23103
23251
  }
@@ -23112,19 +23260,19 @@ function registerResetCommand(program2) {
23112
23260
  projectPath(`${VERITY_DIR}/.last-analysis`)
23113
23261
  ];
23114
23262
  for (const file of filesToClear) {
23115
- if ((0, import_node_fs42.existsSync)(file)) {
23263
+ if ((0, import_node_fs43.existsSync)(file)) {
23116
23264
  try {
23117
- (0, import_node_fs42.writeFileSync)(file, "");
23265
+ (0, import_node_fs43.writeFileSync)(file, "");
23118
23266
  } catch {
23119
23267
  }
23120
23268
  }
23121
23269
  }
23122
23270
  if (opts.all) {
23123
23271
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
23124
- if ((0, import_node_fs42.existsSync)(logsDir)) {
23125
- for (const entry of (0, import_node_fs42.readdirSync)(logsDir)) {
23272
+ if ((0, import_node_fs43.existsSync)(logsDir)) {
23273
+ for (const entry of (0, import_node_fs43.readdirSync)(logsDir)) {
23126
23274
  try {
23127
- (0, import_node_fs42.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
23275
+ (0, import_node_fs43.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
23128
23276
  } catch {
23129
23277
  }
23130
23278
  }
@@ -23432,8 +23580,8 @@ function registerTelemetryCommands(program2) {
23432
23580
  }
23433
23581
 
23434
23582
  // src/cli.ts
23435
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.0-experimental.48d33ea").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) => {
23436
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.0-experimental.48d33ea");
23583
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.0").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) => {
23584
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.0");
23437
23585
  setUserNamedServiceUrl(program.opts().serviceUrl);
23438
23586
  try {
23439
23587
  await foldLegacyLocalCredential();
@@ -23457,6 +23605,7 @@ registerBaselineCommands(program);
23457
23605
  registerReviewCommand(program);
23458
23606
  registerGuardCommand(program);
23459
23607
  registerIgnoreCommand(program);
23608
+ registerWaiveCommand(program);
23460
23609
  registerInitCommand(program);
23461
23610
  registerUninstallCommand(program);
23462
23611
  registerTaskCommands(program);
@@ -85,3 +85,27 @@ curl -s -X POST -H "Authorization: Bearer $VERITY_TOKEN" \
85
85
  3. **Review**: User sees suggestions with evidence (run counts, FP rates, citation counts)
86
86
  4. **Apply**: Approved suggestions become a new Standard version (immutable, rollback-safe)
87
87
  5. **No autonomous changes** in v1 — every suggestion requires human approval
88
+
89
+ ## Recording an accepted-risk disposition (unblocking a guard deadlock)
90
+
91
+ When the pre-commit/pre-push guard blocks on a finding that humans have already
92
+ adjudicated the other way (a reviewer finding, an ADR, phase exit criteria),
93
+ record the disposition with:
94
+
95
+ ```bash
96
+ verity waive <pattern-id> --file <path> --reason "per reviewer finding 5 — no code change needed"
97
+ ```
98
+
99
+ What this does — and deliberately does not do:
100
+
101
+ - Marks the open statement `waived` with your reason; the next guard/analyze run
102
+ on the **same file bytes** no longer blocks on it, and the disposition is
103
+ visible in the run report.
104
+ - **It voids automatically when the file changes** — a waive is an assertion
105
+ about specific bytes, never a standing mute. Edit the file and the finding is
106
+ reconsidered fresh.
107
+ - The reason is mandatory (≥ 20 chars) and should name the human decision it
108
+ traces to. At most 3 statements are waived per invocation.
109
+ - `verity feedback finding <run-id> <pattern-id> false_positive` remains the
110
+ right tool for *pattern-level* feedback (it feeds suppression/evolution);
111
+ `verity waive` is the *per-site, accepted-risk* path that unblocks a push.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.31.0-experimental.48d33ea",
3
+ "version": "0.31.0",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",