@codacy/verity-cli 0.28.1-experimental.af3c52d → 0.28.1-experimental.c2dc717

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.
Files changed (2) hide show
  1. package/bin/verity.js +506 -75
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -10636,14 +10636,14 @@ async function readGlobalCredential(remote) {
10636
10636
  const parsed = parseCredentialLine(line);
10637
10637
  if (parsed && parsed.remote === key) last = parsed.rec;
10638
10638
  }
10639
- if (last) return last;
10639
+ if (last) return { ...last, keyed: true };
10640
10640
  }
10641
10641
  let plain = null;
10642
10642
  for (const line of lines) {
10643
10643
  const parsed = parseCredentialLine(line);
10644
10644
  if (parsed && parsed.remote === "") plain = parsed.rec;
10645
10645
  }
10646
- return plain;
10646
+ return plain ? { ...plain, keyed: false } : null;
10647
10647
  }
10648
10648
  async function upsertGlobalCredential(remote, rec) {
10649
10649
  const path = globalCredentialsPath();
@@ -11218,7 +11218,13 @@ async function resolveToken(flagToken) {
11218
11218
  if (rec) {
11219
11219
  return {
11220
11220
  ok: true,
11221
- data: { token: rec.token, source: "global", userId: rec.userId, email: rec.email }
11221
+ data: {
11222
+ token: rec.token,
11223
+ source: "global",
11224
+ userId: rec.userId,
11225
+ email: rec.email,
11226
+ keyed: rec.keyed
11227
+ }
11222
11228
  };
11223
11229
  }
11224
11230
  const local = await readLegacyLocalCredential();
@@ -11250,6 +11256,16 @@ function reverifyNudge(who) {
11250
11256
  }
11251
11257
  return null;
11252
11258
  }
11259
+ function isLegacyPerRepoCredential(auth2) {
11260
+ return auth2.source === "local" || auth2.source === "global" && auth2.keyed === true;
11261
+ }
11262
+ async function shouldUpgradeOnLogin(auth2) {
11263
+ if (!isLegacyPerRepoCredential(auth2)) return false;
11264
+ if (auth2.userId == null) return true;
11265
+ const bare = await readGlobalCredential("");
11266
+ if (bare?.userId == null) return true;
11267
+ return bare.userId === auth2.userId;
11268
+ }
11253
11269
  function authDenialRemedy(error) {
11254
11270
  if (error.startsWith("STALE_VERIFICATION")) {
11255
11271
  return {
@@ -11680,13 +11696,19 @@ function registerLoginCommand(program2) {
11680
11696
  const who = await whoami(existing.data.token, serviceUrl, globals.verbose);
11681
11697
  if (who.ok && who.data.logged_in) {
11682
11698
  const nudge = reverifyNudge(who.data);
11683
- if (!nudge) {
11699
+ const upgrade = await shouldUpgradeOnLogin(existing.data);
11700
+ if (!nudge && !upgrade) {
11684
11701
  printInfo(`Already logged in as ${who.data.email ?? `user #${who.data.user_id}`}. \u2713`);
11685
11702
  printInfo(" Re-authenticate with: verity login --force");
11686
11703
  return;
11687
11704
  }
11688
- printWarn(nudge);
11689
- printInfo("Re-verifying your repository access\u2026");
11705
+ if (nudge) {
11706
+ printWarn(nudge);
11707
+ printInfo("Re-verifying your repository access\u2026");
11708
+ } else {
11709
+ printInfo("You are signed in with a per-repository token (the old format).");
11710
+ printInfo(" Upgrading to a single login that covers every repository you can write to\u2026");
11711
+ }
11690
11712
  } else if (who.ok && who.data.anonymous) {
11691
11713
  printInfo("You have an anonymous token (the gate runs, but nothing is saved). Logging you in\u2026");
11692
11714
  } else if (!who.ok) {
@@ -11737,14 +11759,24 @@ function registerLoginCommand(program2) {
11737
11759
  const rec = await readGlobalCredential(remote);
11738
11760
  if (rec && rec.token !== out.token) {
11739
11761
  const otherBackend = rec.serviceUrl != null && rec.serviceUrl !== out.serviceUrl;
11762
+ const otherIdentity = rec.userId != null && out.userId != null && rec.userId !== out.userId;
11740
11763
  if (otherBackend) {
11741
11764
  printWarn(` Note: this repository is pinned to a different Verity service (${rec.serviceUrl})`);
11742
11765
  printWarn(" by its own credential line, which takes precedence here \u2014 this login does not");
11743
11766
  printWarn(" change that. To move the repository, remove its line from ~/.verity/credentials.");
11767
+ printWarn(' Until that line is removed, "verity login" here cannot fast-path and will run');
11768
+ printWarn(" the full GitHub flow every time.");
11769
+ } else if (otherIdentity) {
11770
+ printWarn(" Note: this repository uses a different account's credential, which takes");
11771
+ printWarn(' precedence here \u2014 this login leaves it in place, and "verity login" in this');
11772
+ printWarn(" repository will report that account. Remove its line from ~/.verity/credentials");
11773
+ printWarn(" only if you want this repository on the login you just completed.");
11744
11774
  } else {
11745
11775
  const kind = rec.userId != null ? "superseded per-repository" : "anonymous project-specific";
11746
11776
  printWarn(` Note: this repository has a ${kind} credential that takes`);
11747
11777
  printWarn(" precedence here. Remove its line from ~/.verity/credentials to use your login.");
11778
+ printWarn(' Until then, "verity login" in this repository re-runs the full GitHub flow');
11779
+ printWarn(" every time.");
11748
11780
  }
11749
11781
  }
11750
11782
  }
@@ -13559,6 +13591,65 @@ var import_node_fs10 = require("node:fs");
13559
13591
  var import_node_crypto5 = require("node:crypto");
13560
13592
  var import_node_path11 = require("node:path");
13561
13593
 
13594
+ // src/lib/skip-detection.ts
13595
+ function isBareAckPrompt(prompt) {
13596
+ if (typeof prompt !== "string") return false;
13597
+ const trimmed = prompt.trim();
13598
+ if (trimmed.length === 0) return false;
13599
+ if (trimmed.length > 20) return false;
13600
+ const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
13601
+ return bareAckPattern.test(trimmed);
13602
+ }
13603
+ function isContinuationPrompt(prompt) {
13604
+ if (typeof prompt !== "string") return false;
13605
+ const trimmed = prompt.trim();
13606
+ if (trimmed.length === 0) return false;
13607
+ if (trimmed.length > 24) return false;
13608
+ const continuation = /^(let['’]?s\s+(go|do\s+it|start|continue)|go|go\s+ahead|go\s+on|proceed|continue|carry\s+on|keep\s+going|do\s+it|make\s+it\s+so|next|start|begin|ship\s+it|yes\s+please|please\s+continue|perfect|great|nice|excellent|agreed)[.!]*$/i;
13609
+ return continuation.test(trimmed) || isBareAckPrompt(trimmed);
13610
+ }
13611
+ function resolveGoalPrompt(prompts) {
13612
+ if (prompts.length === 0) return null;
13613
+ const latest = prompts[prompts.length - 1];
13614
+ if (!isContinuationPrompt(latest.prompt)) return { entry: latest, turnsBack: 0 };
13615
+ for (let i = prompts.length - 2; i >= 0; i--) {
13616
+ if (!isContinuationPrompt(prompts[i].prompt)) {
13617
+ return { entry: prompts[i], turnsBack: prompts.length - 1 - i };
13618
+ }
13619
+ }
13620
+ return { entry: latest, turnsBack: 0 };
13621
+ }
13622
+ function isReflectionQuestion(response) {
13623
+ if (!response || typeof response !== "string") return false;
13624
+ const markers = [
13625
+ /reflection\s+for\s+future\s+agents/i,
13626
+ /what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
13627
+ /say\s+['"]?skip['"]?\s+to\s+skip/i,
13628
+ /quick\s+reflection\s+question/i,
13629
+ // Post-flip (VRT-21): the agent drafts the reflection itself and, when
13630
+ // interactive, asks the user to confirm/correct before recording. That
13631
+ // turn authors no code either, so it's still a reflection turn.
13632
+ /reflection\s+draft/i,
13633
+ /confirm,?\s+correct,?\s+or\s+add/i
13634
+ ];
13635
+ return markers.some((m) => m.test(response));
13636
+ }
13637
+ function isMetaTaskLabel(label2) {
13638
+ if (label2 === null || label2 === void 0) return false;
13639
+ if (typeof label2 !== "string") return false;
13640
+ const trimmed = label2.trim();
13641
+ if (trimmed.length === 0) return true;
13642
+ const metaPatterns = [
13643
+ /^verity\s+[\w-]+\s+response$/i,
13644
+ // "Verity reflect response"
13645
+ /^simple user response$/i,
13646
+ /^verity\s+command$/i,
13647
+ // "Verity command"
13648
+ /^user\s+(question|reply|response|ack)$/i
13649
+ ];
13650
+ return metaPatterns.some((p) => p.test(trimmed));
13651
+ }
13652
+
13562
13653
  // src/lib/dossier.ts
13563
13654
  var import_node_fs9 = require("node:fs");
13564
13655
  var import_node_crypto4 = require("node:crypto");
@@ -13928,6 +14019,12 @@ function reduce(state, events, now) {
13928
14019
  }
13929
14020
  case "verdict": {
13930
14021
  state.meta.last_verdict_seq = ev.seq;
14022
+ state.meta.channel = {
14023
+ emittedLast: ev.emitted === true,
14024
+ // Reset by ANY movement, so the counter measures a standstill rather
14025
+ // than session length.
14026
+ consecutiveIdle: ev.idle === false ? 0 : (state.meta.channel?.consecutiveIdle ?? 0) + 1
14027
+ };
13931
14028
  state.meta.last_adjudication = ev.intent_verdict ? { verdict: ev.intent_verdict, score: ev.intent_score ?? null, at: ev.at, decision: ev.decision } : void 0;
13932
14029
  if (ev.intent_sig) {
13933
14030
  state.meta.intent_repeat = state.meta.intent_repeat && state.meta.intent_repeat.sig === ev.intent_sig ? { sig: ev.intent_sig, consecutive: state.meta.intent_repeat.consecutive + 1 } : { sig: ev.intent_sig, consecutive: 1 };
@@ -14295,6 +14392,10 @@ function projectMemory(state, opts) {
14295
14392
  ...active.delivered && { delivered: active.delivered }
14296
14393
  };
14297
14394
  }
14395
+ if (opts.capture) {
14396
+ const missed = Math.max(0, opts.capture.seen - opts.capture.captured);
14397
+ p.capture = { seen: opts.capture.seen, captured: opts.capture.captured, missed };
14398
+ }
14298
14399
  if (state.meta.last_adjudication) {
14299
14400
  const a = state.meta.last_adjudication;
14300
14401
  p.last_adjudication = { verdict: a.verdict, score: a.score, at: a.at };
@@ -14541,7 +14642,8 @@ function recall(d, input) {
14541
14642
  continuity,
14542
14643
  spoken: reanchored.spoken,
14543
14644
  refused: reanchored.dropped.length,
14544
- lastVerdictSeq
14645
+ lastVerdictSeq,
14646
+ ...input.capture && { capture: input.capture }
14545
14647
  });
14546
14648
  return {
14547
14649
  state: effective,
@@ -14652,7 +14754,19 @@ function sessionDossier(token, sessionId) {
14652
14754
  const d = openDossier(identity);
14653
14755
  return d ? { d, identity } : null;
14654
14756
  }
14757
+ function hasActiveGoal(d) {
14758
+ try {
14759
+ if (!(0, import_node_fs10.existsSync)(d.eventsPath)) return false;
14760
+ return (0, import_node_fs10.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
14761
+ } catch {
14762
+ return false;
14763
+ }
14764
+ }
14655
14765
  function recordGoal(d, prompt, source = "prompt") {
14766
+ if (source === "prompt" && isContinuationPrompt(prompt) && hasActiveGoal(d)) {
14767
+ appendEvent(d, { k: "goal_continue", text: prompt.slice(0, 64) });
14768
+ return;
14769
+ }
14656
14770
  const text = prompt.slice(0, MAX_GOAL_CHARS);
14657
14771
  appendEvent(d, {
14658
14772
  k: "goal",
@@ -14755,6 +14869,11 @@ function recordVerdict(d, v) {
14755
14869
  line_sha: at !== void 0 ? lineSha(at) : null
14756
14870
  });
14757
14871
  }
14872
+ const foldedNow = foldDossier(d);
14873
+ const sig = intentSignature(v.intent, {
14874
+ goalSeq: foldedNow.goal.find((g) => g.status === "active")?.seq ?? 0,
14875
+ idle: v.idle !== false
14876
+ });
14758
14877
  appendEvent(d, {
14759
14878
  k: "verdict",
14760
14879
  run_id: v.runId,
@@ -14762,15 +14881,19 @@ function recordVerdict(d, v) {
14762
14881
  watermark_sha: v.watermarkSha,
14763
14882
  branch: v.branch,
14764
14883
  decision: v.decision,
14765
- ...intentSignature(v.intent) && { intent_sig: intentSignature(v.intent) },
14884
+ ...sig && { intent_sig: sig },
14885
+ emitted: v.emitted === true,
14886
+ idle: v.idle !== false,
14766
14887
  ...v.intent?.verdict && { intent_verdict: v.intent.verdict },
14767
14888
  ...typeof v.intent?.score === "number" && { intent_score: v.intent.score }
14768
14889
  });
14769
14890
  }
14770
- function intentSignature(intent) {
14891
+ function intentSignature(intent, ctx) {
14771
14892
  if (!intent?.verdict) return null;
14772
14893
  if (intent.verdict !== "misaligned" && intent.verdict !== "partial") return null;
14773
- return `${intent.verdict}:${lineSha(intent.gaps?.[0] ?? "")}`;
14894
+ const goal = ctx ? `g${ctx.goalSeq}` : "g?";
14895
+ const moved = ctx?.idle === false ? "active" : "idle";
14896
+ return `${intent.verdict}:${goal}:${moved}`;
14774
14897
  }
14775
14898
  function toRegister(severity) {
14776
14899
  switch (severity) {
@@ -14807,7 +14930,9 @@ function recallMemory(d, identity, opts) {
14807
14930
  const state = foldDossier(d);
14808
14931
  const watermark = state.meta.watermark?.sha ?? null;
14809
14932
  const watermarkPaths = (state.authored ?? []).map((a) => a.path);
14933
+ const captureCmp = typeof opts.userMessagesSeen === "number" ? { seen: opts.userMessagesSeen, captured: state.meta.goal_chain } : void 0;
14810
14934
  const r = recall(d, {
14935
+ ...captureCmp && { capture: captureCmp },
14811
14936
  identity,
14812
14937
  currentSessionKey: opts.currentSessionKey,
14813
14938
  branchNow: getCurrentBranch(),
@@ -15064,6 +15189,8 @@ function collectCodeDelta(files, opts) {
15064
15189
  let totalSize = 0;
15065
15190
  let truncationReason = null;
15066
15191
  const droppedPaths = [];
15192
+ const excluded = [];
15193
+ const exclude = (path, reason) => excluded.push({ path, reason, stage: "collectCodeDelta", kind: "capacity" });
15067
15194
  for (const filepath of sorted) {
15068
15195
  if (result.length >= maxFiles) {
15069
15196
  truncationReason ??= "max_files";
@@ -15071,14 +15198,21 @@ function collectCodeDelta(files, opts) {
15071
15198
  continue;
15072
15199
  }
15073
15200
  const resolved = resolveFile(filepath);
15074
- if (!resolved) continue;
15201
+ if (!resolved) {
15202
+ exclude(filepath, "path-not-resolvable");
15203
+ continue;
15204
+ }
15075
15205
  let size;
15076
15206
  try {
15077
15207
  size = (0, import_node_fs11.statSync)(resolved).size;
15078
15208
  } catch {
15209
+ exclude(filepath, "not-stattable");
15210
+ continue;
15211
+ }
15212
+ if (size > maxFileBytes) {
15213
+ exclude(filepath, `over-file-size-limit-${maxFileBytes}b`);
15079
15214
  continue;
15080
15215
  }
15081
- if (size > maxFileBytes) continue;
15082
15216
  if (totalSize + size > maxTotalBytes) {
15083
15217
  truncationReason ??= "max_total_bytes";
15084
15218
  const idx = sorted.indexOf(filepath);
@@ -15089,6 +15223,7 @@ function collectCodeDelta(files, opts) {
15089
15223
  try {
15090
15224
  content = (0, import_node_fs11.readFileSync)(resolved, "utf-8");
15091
15225
  } catch {
15226
+ exclude(filepath, "not-readable");
15092
15227
  continue;
15093
15228
  }
15094
15229
  totalSize += size;
@@ -15102,10 +15237,14 @@ function collectCodeDelta(files, opts) {
15102
15237
  (sum, f) => sum + f.content.split("\n").length,
15103
15238
  0
15104
15239
  );
15240
+ for (const path of droppedPaths) {
15241
+ exclude(path, truncationReason === "max_files" ? "max-files-cap" : "max-total-bytes-cap");
15242
+ }
15105
15243
  return {
15106
15244
  files: result,
15107
15245
  total_lines: totalLines,
15108
15246
  total_files: result.length,
15247
+ excluded,
15109
15248
  ...truncationReason && {
15110
15249
  truncated: {
15111
15250
  reason: truncationReason,
@@ -16557,7 +16696,7 @@ function resolveTaskContext(opts) {
16557
16696
  // src/lib/cli-version.ts
16558
16697
  function cliVersion() {
16559
16698
  try {
16560
- return true ? "0.28.1-experimental.af3c52d" : "dev";
16699
+ return true ? "0.28.1-experimental.c2dc717" : "dev";
16561
16700
  } catch {
16562
16701
  return "dev";
16563
16702
  }
@@ -16782,6 +16921,16 @@ var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
16782
16921
  "subagent"
16783
16922
  ]);
16784
16923
  var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "NotebookEdit", "MultiEdit"]);
16924
+ var DISPATCH_TOOLS = /* @__PURE__ */ new Set(["Task", "Workflow"]);
16925
+ function hasUserText(record) {
16926
+ const message = record.message;
16927
+ const content = message?.content ?? record.content;
16928
+ if (typeof content === "string") return content.trim().length > 0;
16929
+ if (!Array.isArray(content)) return false;
16930
+ return content.some(
16931
+ (b) => b?.type === "text" && typeof b.text === "string" && b.text.trim().length > 0
16932
+ );
16933
+ }
16785
16934
  var COMMAND_CLASSES = [
16786
16935
  [/\b(npm|yarn|pnpm|bun)\s+(run\s+)?test\b|\bvitest\b|\bjest\b|\bpytest\b|\bgo test\b/, "test"],
16787
16936
  [/\b(npm|yarn|pnpm|bun)\s+(run\s+)?build\b|\btsc\b|\bwebpack\b|\bcargo build\b/, "build"],
@@ -16908,6 +17057,8 @@ function fold(transcriptPath, opts = {}) {
16908
17057
  totalRecords: 0,
16909
17058
  malformed: 0,
16910
17059
  subagentFiles: 0,
17060
+ dispatched: 0,
17061
+ userMessages: 0,
16911
17062
  subagentSkipped: 0,
16912
17063
  compactions: 0,
16913
17064
  complete: false
@@ -16934,7 +17085,8 @@ function fold(transcriptPath, opts = {}) {
16934
17085
  if (type === "system" && record.subtype === "compact_boundary") {
16935
17086
  result.coverage.compactions++;
16936
17087
  }
16937
- collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, opts.repoRoot);
17088
+ if (type === "user" && hasUserText(record)) result.coverage.userMessages++;
17089
+ collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, opts.repoRoot, result.coverage);
16938
17090
  }
16939
17091
  };
16940
17092
  try {
@@ -17006,7 +17158,7 @@ function classifyUnobserved(path) {
17006
17158
  if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
17007
17159
  return "no_edit_record";
17008
17160
  }
17009
- function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, repoRoot2) {
17161
+ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, repoRoot2, tally) {
17010
17162
  const message = record.message;
17011
17163
  const content = message?.content ?? record.content;
17012
17164
  const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
@@ -17028,6 +17180,9 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
17028
17180
  byPath.set(path, entry);
17029
17181
  }
17030
17182
  }
17183
+ if (DISPATCH_TOOLS.has(name) && tally) {
17184
+ tally.dispatched += 1;
17185
+ }
17031
17186
  if (name === "Bash") {
17032
17187
  const cmd = typeof input.command === "string" ? input.command : "";
17033
17188
  if (cmd) {
@@ -17086,6 +17241,86 @@ function checkConservation(changedFiles, result, repoRoot2) {
17086
17241
  };
17087
17242
  }
17088
17243
 
17244
+ // src/lib/verdict.ts
17245
+ function reconcileCoverage(changed, coverage) {
17246
+ const changedSet = new Set(changed);
17247
+ const reviewed = coverage.reviewed.filter((p) => changedSet.has(p));
17248
+ const claimed = /* @__PURE__ */ new Set([...reviewed, ...coverage.notReviewed.map((n) => n.path)]);
17249
+ const unaccounted = [...changedSet].filter((p) => !claimed.has(p)).sort();
17250
+ const notReviewed = [
17251
+ ...coverage.notReviewed.filter((n) => changedSet.has(n.path)),
17252
+ ...unaccounted.map((path) => ({
17253
+ path,
17254
+ reason: "unaccounted",
17255
+ // Named so the eventual bug report writes itself: some stage removed this
17256
+ // path and did not say so.
17257
+ stage: "unknown-stage",
17258
+ // An undeclared drop is CAPACITY by default. A stage that cannot be
17259
+ // bothered to say why it dropped a file does not get the benefit of the
17260
+ // doubt — that default is what makes forgetting expensive.
17261
+ kind: "capacity"
17262
+ }))
17263
+ ];
17264
+ return {
17265
+ coverage: { reviewed: [...new Set(reviewed)].sort(), notReviewed },
17266
+ unaccounted,
17267
+ balances: unaccounted.length === 0
17268
+ };
17269
+ }
17270
+ function resolveVerdict(proposed, coverage) {
17271
+ if (proposed === "FAIL") return "FAIL";
17272
+ const blocking = coverage.notReviewed.filter((n) => (n.kind ?? "capacity") !== "policy");
17273
+ if (blocking.length === 0) return proposed;
17274
+ return "WARN";
17275
+ }
17276
+ function describeCoverage(coverage, maxPaths = 5) {
17277
+ const relevant = coverage.notReviewed.filter((n) => (n.kind ?? "capacity") !== "policy");
17278
+ if (relevant.length === 0) return null;
17279
+ const byReason = /* @__PURE__ */ new Map();
17280
+ for (const n of relevant) {
17281
+ const key = `${n.reason}`;
17282
+ const list = byReason.get(key) ?? [];
17283
+ list.push(n.path);
17284
+ byReason.set(key, list);
17285
+ }
17286
+ const lines = [];
17287
+ for (const [reason, paths] of [...byReason.entries()].sort()) {
17288
+ const shown = paths.slice(0, maxPaths).join(", ");
17289
+ const more = paths.length > maxPaths ? ` (+${paths.length - maxPaths} more)` : "";
17290
+ lines.push(` ${paths.length} not reviewed \u2014 ${reason}: ${shown}${more}`);
17291
+ }
17292
+ return `NOT A CLEAN REVIEW. ${relevant.length} changed file(s) never reached the reviewer, so this verdict does not cover them:
17293
+ ${lines.join("\n")}
17294
+ Treat those files as UNCHECKED, not as approved.`;
17295
+ }
17296
+ function openBlockingElsewhere(statements, reviewedNow, lineShaAt) {
17297
+ const reviewed = new Set(reviewedNow);
17298
+ const out = [];
17299
+ const seen = /* @__PURE__ */ new Set();
17300
+ for (const s of statements) {
17301
+ if (s.outcome !== "open") continue;
17302
+ if (s.register !== "BLOCK") continue;
17303
+ if (s.carried) continue;
17304
+ if (reviewed.has(s.file)) continue;
17305
+ if (!s.line_sha) continue;
17306
+ if (lineShaAt(s.file, s.line) !== s.line_sha) continue;
17307
+ const key = `${s.file}::${s.pattern_id}`;
17308
+ if (seen.has(key)) continue;
17309
+ seen.add(key);
17310
+ out.push({ file: s.file, line: s.line, pattern_id: s.pattern_id });
17311
+ }
17312
+ return out;
17313
+ }
17314
+ function describeOpenElsewhere(open) {
17315
+ if (open.length === 0) return null;
17316
+ const lines = open.slice(0, 5).map((o) => ` ${o.file}:${o.line} [${o.pattern_id}]`);
17317
+ const more = open.length > 5 ? `
17318
+ (+${open.length - 5} more)` : "";
17319
+ return `STILL OPEN ELSEWHERE. ${open.length} blocking finding(s) Verity raised earlier are still present in files this run did not review:
17320
+ ${lines.join("\n")}${more}
17321
+ This verdict covers the current change only. The tree is not clean.`;
17322
+ }
17323
+
17089
17324
  // src/lib/channel.ts
17090
17325
  var MAX_AGENT_CONTEXT_CHARS = 1500;
17091
17326
  var MAX_AGENT_ITEMS = 5;
@@ -17164,6 +17399,47 @@ function buildHookOutput(gateDecision, systemMessage, agentContext) {
17164
17399
  } : {}
17165
17400
  };
17166
17401
  }
17402
+ var IDLE_EPISODE_CAP = 3;
17403
+ function channelSilence(input) {
17404
+ const movedSomething = input.newUserPrompt || input.newAuthorship;
17405
+ if (movedSomething) return null;
17406
+ if (input.consecutiveIdle >= IDLE_EPISODE_CAP) return "idle-episode-cap";
17407
+ if (input.emittedLast) return "caused-by-our-own-emission";
17408
+ return null;
17409
+ }
17410
+
17411
+ // src/lib/emit.ts
17412
+ var YELLOW2 = "\x1B[33m";
17413
+ var NC2 = "\x1B[0m";
17414
+ function emitVerdict(input) {
17415
+ const exit = input.exit ?? ((code) => process.exit(code));
17416
+ const { coverage, unaccounted } = reconcileCoverage(input.changed, input.coverage);
17417
+ let verdict = resolveVerdict(input.proposed, coverage);
17418
+ const openElsewhere = input.openElsewhere ?? [];
17419
+ if (verdict === "PASS" && openElsewhere.length > 0) verdict = "WARN";
17420
+ const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
17421
+ if (unaccounted.length > 0) {
17422
+ process.stderr.write(
17423
+ `${YELLOW2}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
17424
+ `
17425
+ );
17426
+ }
17427
+ if (verdict === "FAIL") {
17428
+ input.renderBlocking?.();
17429
+ if (input.agentContext) {
17430
+ process.stderr.write(`
17431
+ ${input.agentContext}
17432
+ `);
17433
+ }
17434
+ if (note && !input.silenced) process.stderr.write(`
17435
+ ${YELLOW2}${note}${NC2}
17436
+ `);
17437
+ return exit(2);
17438
+ }
17439
+ const agentBlock = input.silenced ? null : [input.agentContext, note].filter(Boolean).join("\n\n") || null;
17440
+ printJsonCompact(buildHookOutput(verdict, input.userSummary, agentBlock));
17441
+ return exit(0);
17442
+ }
17167
17443
 
17168
17444
  // src/lib/cache-cleanup.ts
17169
17445
  var import_node_fs21 = require("node:fs");
@@ -17344,46 +17620,6 @@ function shouldWarmRetryAnalyze(result) {
17344
17620
  return false;
17345
17621
  }
17346
17622
 
17347
- // src/lib/skip-detection.ts
17348
- function isBareAckPrompt(prompt) {
17349
- if (typeof prompt !== "string") return false;
17350
- const trimmed = prompt.trim();
17351
- if (trimmed.length === 0) return false;
17352
- if (trimmed.length > 20) return false;
17353
- const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
17354
- return bareAckPattern.test(trimmed);
17355
- }
17356
- function isReflectionQuestion(response) {
17357
- if (!response || typeof response !== "string") return false;
17358
- const markers = [
17359
- /reflection\s+for\s+future\s+agents/i,
17360
- /what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
17361
- /say\s+['"]?skip['"]?\s+to\s+skip/i,
17362
- /quick\s+reflection\s+question/i,
17363
- // Post-flip (VRT-21): the agent drafts the reflection itself and, when
17364
- // interactive, asks the user to confirm/correct before recording. That
17365
- // turn authors no code either, so it's still a reflection turn.
17366
- /reflection\s+draft/i,
17367
- /confirm,?\s+correct,?\s+or\s+add/i
17368
- ];
17369
- return markers.some((m) => m.test(response));
17370
- }
17371
- function isMetaTaskLabel(label2) {
17372
- if (label2 === null || label2 === void 0) return false;
17373
- if (typeof label2 !== "string") return false;
17374
- const trimmed = label2.trim();
17375
- if (trimmed.length === 0) return true;
17376
- const metaPatterns = [
17377
- /^verity\s+[\w-]+\s+response$/i,
17378
- // "Verity reflect response"
17379
- /^simple user response$/i,
17380
- /^verity\s+command$/i,
17381
- // "Verity command"
17382
- /^user\s+(question|reply|response|ack)$/i
17383
- ];
17384
- return metaPatterns.some((p) => p.test(trimmed));
17385
- }
17386
-
17387
17623
  // src/lib/transcript.ts
17388
17624
  var import_node_fs22 = require("node:fs");
17389
17625
  var MAX_READ_BYTES = 256 * 1024;
@@ -17547,6 +17783,13 @@ function buildSummary(lines) {
17547
17783
  files_read: capArray(filesRead, MAX_FILES_LIST),
17548
17784
  files_edited: capArray(filesEdited, MAX_FILES_LIST),
17549
17785
  files_created: capArray(filesCreated, MAX_CREATED_LIST),
17786
+ // The complement of the two caps that affect SCOPE. `files_read` is excluded
17787
+ // deliberately: reading a file is not authoring it, so a capped read list
17788
+ // narrows nothing.
17789
+ capped_out: [
17790
+ ...cappedOut(filesEdited, MAX_FILES_LIST),
17791
+ ...cappedOut(filesCreated, MAX_CREATED_LIST)
17792
+ ],
17550
17793
  searches,
17551
17794
  commands,
17552
17795
  subagents,
@@ -17597,6 +17840,9 @@ function sanitizeCommand(rawCmd) {
17597
17840
  function capArray(set, max) {
17598
17841
  return Array.from(set).slice(0, max);
17599
17842
  }
17843
+ function cappedOut(set, max) {
17844
+ return Array.from(set).slice(max);
17845
+ }
17600
17846
 
17601
17847
  // src/lib/run-mode.ts
17602
17848
  function parseAutonomousEnv(raw) {
@@ -18006,12 +18252,45 @@ function agentContextFor(response, intentRepeat = 0) {
18006
18252
  });
18007
18253
  }
18008
18254
  var beaconCtx = null;
18009
- async function passAndExit(reason, skip) {
18255
+ async function passAndExit(reason, skip, kindOverride) {
18010
18256
  const sent = await sendSkipBeacon(beaconCtx, skip);
18011
18257
  logEvent("skip", { reason: skip, beacon: sent });
18012
- printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
18258
+ const POLICY_SKIPS = /* @__PURE__ */ new Set([
18259
+ "no-analyzable-files",
18260
+ "verity-command",
18261
+ "bare-acknowledgment",
18262
+ "reflection-prompt",
18263
+ "skip-mode",
18264
+ "zero-increment",
18265
+ "debounce",
18266
+ "no-delta-since-last-review"
18267
+ ]);
18268
+ const skipKind = kindOverride ?? (POLICY_SKIPS.has(skip) ? "policy" : "capacity");
18269
+ const changed = skipCoverageChanged;
18270
+ const { coverage, unaccounted } = reconcileCoverage(changed, {
18271
+ reviewed: [],
18272
+ notReviewed: changed.map((path) => ({ path, reason: skip, stage: "pre-flight", kind: skipKind }))
18273
+ });
18274
+ const verdict = resolveVerdict("PASS", coverage);
18275
+ const note = describeCoverage(coverage);
18276
+ if (unaccounted.length > 0) {
18277
+ logEvent("coverage_unaccounted", { where: "passAndExit", skip, count: unaccounted.length });
18278
+ }
18279
+ const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set(["iteration-cap"]);
18280
+ const agentNote = AGENT_SILENT_SKIPS.has(skip) ? null : note;
18281
+ printJsonCompact(
18282
+ buildHookOutput(
18283
+ verdict,
18284
+ `Verity: ${reason}`,
18285
+ // The agent's ONLY input is additionalContext. Sixteen of the nineteen
18286
+ // terminating paths wrote `systemMessage` — the human's field — and told
18287
+ // the agent nothing at all.
18288
+ agentNote
18289
+ )
18290
+ );
18013
18291
  process.exit(0);
18014
18292
  }
18293
+ var skipCoverageChanged = [];
18015
18294
  var EMPTY_STATIC = {
18016
18295
  tool: "@codacy/analysis-cli",
18017
18296
  findings: [],
@@ -18026,7 +18305,7 @@ function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
18026
18305
  }
18027
18306
  function localOnlyAndExit(staticResults) {
18028
18307
  printJsonCompact({
18029
- gate_decision: "PASS",
18308
+ gate_decision: "WARN",
18030
18309
  systemMessage: "Verity: not authenticated \u2014 ran a local static-only check (no deep review, no upload). Run `verity init` to authenticate and enable the full quality gate.",
18031
18310
  unauthenticated: true,
18032
18311
  static_results: staticResults
@@ -18086,6 +18365,7 @@ async function runAnalyze(opts, globals) {
18086
18365
  });
18087
18366
  }
18088
18367
  const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
18368
+ skipCoverageChanged = allChanged;
18089
18369
  const analyzable = filterAnalyzable(allChanged);
18090
18370
  const reviewable = filterReviewable(allChanged);
18091
18371
  const securityFiles = filterSecurity(allChanged);
@@ -18161,7 +18441,8 @@ async function runAnalyze(opts, globals) {
18161
18441
  let codeDelta = {
18162
18442
  files: [],
18163
18443
  total_lines: 0,
18164
- total_files: 0
18444
+ total_files: 0,
18445
+ excluded: []
18165
18446
  };
18166
18447
  let snapshotResult = { has_snapshots: false, diffs: [] };
18167
18448
  let contentHash = null;
@@ -18226,7 +18507,11 @@ async function runAnalyze(opts, globals) {
18226
18507
  if (assistantResponse) {
18227
18508
  analysisMode = "plan";
18228
18509
  } else {
18229
- await passAndExit("No files within size limits to analyze", "size-limit");
18510
+ await passAndExit(
18511
+ "No files within size limits to analyze",
18512
+ "size-limit",
18513
+ codeDelta.excluded.length > 0 ? "capacity" : "policy"
18514
+ );
18230
18515
  }
18231
18516
  }
18232
18517
  }
@@ -18400,7 +18685,10 @@ async function runAnalyze(opts, globals) {
18400
18685
  priorState.capabilities
18401
18686
  );
18402
18687
  memory = recallMemory(memorySession.d, memorySession.identity, {
18403
- currentSessionKey: memorySession.identity.sessionKey
18688
+ currentSessionKey: memorySession.identity.sessionKey,
18689
+ // The independent witness. Only meaningful when a transcript was folded —
18690
+ // otherwise it stays undefined and capture coverage reads as UNKNOWN.
18691
+ ...foldResult && { userMessagesSeen: foldResult.coverage.userMessages }
18404
18692
  });
18405
18693
  if (memory && !memory.provenanceHolds) {
18406
18694
  process.stderr.write("Verity: working-memory provenance check failed \u2014 recall suppressed.\n");
@@ -18539,7 +18827,12 @@ async function runAnalyze(opts, globals) {
18539
18827
  const intentContext = {};
18540
18828
  if (conversation && conversation.prompts.length > 0) {
18541
18829
  const latest = conversation.prompts[conversation.prompts.length - 1];
18542
- intentContext.user_prompt = latest.prompt;
18830
+ const goalPrompt = resolveGoalPrompt(conversation.prompts) ?? { entry: latest, turnsBack: 0 };
18831
+ intentContext.user_prompt = goalPrompt.entry.prompt;
18832
+ if (goalPrompt.turnsBack > 0) {
18833
+ intentContext.continuation_prompt = latest.prompt;
18834
+ logEvent("goal_walked_back", { turns_back: goalPrompt.turnsBack });
18835
+ }
18543
18836
  intentContext.session_id = latest.session_id || void 0;
18544
18837
  intentContext.prompt_captured_at = latest.captured_at || void 0;
18545
18838
  if (conversation.prompts.length > 1) {
@@ -18631,8 +18924,114 @@ async function runAnalyze(opts, globals) {
18631
18924
  const response = result.data;
18632
18925
  const decision = response.gate_decision ?? "(unrecognised)";
18633
18926
  const sentPaths = codeDelta.files.map((f) => f.path);
18927
+ let openElsewhere = [];
18928
+ if (memorySession) {
18929
+ try {
18930
+ const st = foldDossier(memorySession.d);
18931
+ openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
18932
+ try {
18933
+ const src = (0, import_node_fs24.readFileSync)((0, import_node_path20.join)(repoRoot(), file), "utf8").split("\n");
18934
+ const at = src[line - 1];
18935
+ return at === void 0 ? null : lineSha(at);
18936
+ } catch {
18937
+ return null;
18938
+ }
18939
+ });
18940
+ } catch {
18941
+ }
18942
+ }
18943
+ const reviewCoverage = {
18944
+ reviewed: sentPaths,
18945
+ // Declared drops from the stages that DO report themselves today. The other
18946
+ // stages surface via `unaccounted`, which is the tripwire, not the design.
18947
+ notReviewed: [
18948
+ // Every exit from the collection loop, each named. Six reasons where there
18949
+ // used to be two recorded and four silent — the silent ones including the
18950
+ // per-file size cap, which could drop a whole source file without leaving a
18951
+ // trace anywhere in the payload or the run row.
18952
+ ...codeDelta.excluded,
18953
+ // The server-side 300-line middle-out truncation. It only bites on the
18954
+ // full-file branch (a first analysis, before snapshots exist) because
18955
+ // analyze normally sends diffs — but on that branch the reviewer sees the
18956
+ // first and last 100 lines and nothing between, and until now said so to
18957
+ // nobody. CAPACITY: a partial look is not a look.
18958
+ ...(response.metadata?.truncated_files ?? []).map((path) => ({
18959
+ path,
18960
+ reason: "file-middle-truncated-300-lines",
18961
+ stage: "prompt-builder",
18962
+ kind: "capacity"
18963
+ })),
18964
+ // The 20-entry edit cap. CAPACITY, and the sharpest of the lot: it narrows
18965
+ // what is REVIEWED, not merely what is summarised — a session editing 25
18966
+ // files had five silently excluded from the reviewed set.
18967
+ ...(actionSummary?.capped_out ?? []).map((path) => ({
18968
+ path,
18969
+ reason: "edit-list-cap-20",
18970
+ stage: "extractActionSummary",
18971
+ kind: "capacity"
18972
+ })),
18973
+ // ⚠ BASELINE SCOPING — the biggest source of false NOT A CLEAN REVIEW.
18974
+ //
18975
+ // The universe is `allChanged`, git's whole dirty tree. The reviewed set is
18976
+ // scoped to what THIS SESSION authored (the VRT-26 contamination cure), so
18977
+ // every pre-existing dirty file is in the universe, absent from `reviewed`,
18978
+ // and — until now — declared by nobody. It fell through to `unaccounted`,
18979
+ // became capacity, and produced "NOT A CLEAN REVIEW: admin.js" over a file
18980
+ // that was never this session's to review.
18981
+ //
18982
+ // Measured 2026-08-04: three consecutive runs over an untouched tree gave
18983
+ // three different answers — .claude/settings.json, then admin.js, then six
18984
+ // files — because each run took a different path and each path had a
18985
+ // different idea of the universe. POLICY: not this session's work is not a
18986
+ // coverage gap, it is the cure working.
18987
+ ...allChanged.filter((p) => !sentPaths.includes(p) && !codeDelta.excluded.some((e) => e.path === p)).filter((p) => analyzable.includes(p) || reviewable.includes(p) || securityFiles.includes(p)).map((path) => ({
18988
+ path,
18989
+ reason: "not-authored-this-session",
18990
+ stage: "baseline-scoping",
18991
+ kind: "policy"
18992
+ })),
18993
+ // The extension allowlist, and it is POLICY rather than capacity: a changed
18994
+ // README was never going to be reviewed, and treating that as a coverage
18995
+ // gap would downgrade nearly every PASS to WARN until WARN meant nothing.
18996
+ // Recorded so the ledger balances and so "what did Verity ignore entirely"
18997
+ // is answerable — but it never touches the verdict.
18998
+ ...allChanged.filter((p) => !analyzable.includes(p) && !reviewable.includes(p) && !securityFiles.includes(p)).map((path) => ({
18999
+ path,
19000
+ reason: "not-a-reviewed-file-type",
19001
+ stage: "extension-allowlist",
19002
+ kind: "policy"
19003
+ }))
19004
+ ]
19005
+ };
18634
19006
  const watermarkHash = sentPaths.length > 0 ? computeContentHash(sentPaths) : contentHash;
18635
19007
  const watermarkIsPartial = !!codeDelta.truncated;
19008
+ let silenced = null;
19009
+ let turnIsIdleForChannel = true;
19010
+ if (memorySession) {
19011
+ try {
19012
+ const st = foldDossier(memorySession.d);
19013
+ turnIsIdleForChannel = st.authored_all.every((a) => a.hash_now === a.hash_at_last_verdict);
19014
+ silenced = channelSilence({
19015
+ // The BUFFER, not intentContext.user_prompt: the latter falls back to a
19016
+ // linked issue (VRT-53 W4) when no human spoke, and a fallback goal is
19017
+ // not a user utterance. Treating it as one would keep the loop alive on
19018
+ // exactly the autonomous cohort.
19019
+ newUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
19020
+ newAuthorship: !turnIsIdleForChannel,
19021
+ emittedLast: st.meta.channel?.emittedLast === true,
19022
+ consecutiveIdle: st.meta.channel?.consecutiveIdle ?? 0
19023
+ });
19024
+ } catch {
19025
+ silenced = null;
19026
+ }
19027
+ }
19028
+ if (silenced) {
19029
+ logEvent("channel_silenced", {
19030
+ reason: silenced,
19031
+ run_id: response.run_id ?? turnId,
19032
+ decision
19033
+ });
19034
+ }
18636
19035
  let intentRepeatCount = 0;
18637
19036
  if (memorySession) {
18638
19037
  try {
@@ -18648,7 +19047,15 @@ async function runAnalyze(opts, globals) {
18648
19047
  title: f.title,
18649
19048
  severity: f.severity
18650
19049
  })) ?? [],
18651
- intent: response.intent_alignment ?? null
19050
+ intent: response.intent_alignment ?? null,
19051
+ // The same signal F1 introduced: bytes differing from the hash frozen at
19052
+ // the last verdict. A turn that moved nothing is the only kind that can
19053
+ // accumulate a repeat.
19054
+ idle: turnIsIdleForChannel,
19055
+ // What next turn reads as `emittedLast`. A suppressed turn did not
19056
+ // speak, so it cannot be the cause of the turn after it — which is what
19057
+ // keeps this from becoming a permanent gag.
19058
+ emitted: !silenced
18652
19059
  });
18653
19060
  intentRepeatCount = Math.max(0, (foldDossier(memorySession.d).meta.intent_repeat?.consecutive ?? 1) - 1);
18654
19061
  } catch {
@@ -18827,7 +19234,19 @@ ${YELLOW}${loginNudge.trim()}${NC}
18827
19234
  if (grantNudge) process.stderr.write(`
18828
19235
  ${YELLOW}${grantNudge.trim()}${NC}
18829
19236
  `);
18830
- process.exit(2);
19237
+ emitVerdict({
19238
+ proposed: "FAIL",
19239
+ changed: skipCoverageChanged,
19240
+ coverage: reviewCoverage,
19241
+ userSummary: "",
19242
+ // Subject to the SAME cycle cut as PASS/WARN. Suppressing here is safe:
19243
+ // the findings themselves are rendered above by the blocking renderer,
19244
+ // so what the cut removes is the repeated commentary, never the defect.
19245
+ agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19246
+ // The coverage note is silenced with it — half a channel is still a channel.
19247
+ silenced: !!silenced,
19248
+ openElsewhere
19249
+ });
18831
19250
  break;
18832
19251
  }
18833
19252
  case "PASS": {
@@ -18839,10 +19258,16 @@ ${YELLOW}${grantNudge.trim()}${NC}
18839
19258
  if (viewUrl) userSummary += ` Report: ${viewUrl}`;
18840
19259
  if (autoSeedNotice) userSummary = `${autoSeedNotice} ${userSummary}`;
18841
19260
  userSummary += loginNudge + grantNudge;
18842
- printJsonCompact(
18843
- buildHookOutput("PASS", userSummary, agentContextFor(response, intentRepeatCount))
18844
- );
18845
- process.exit(0);
19261
+ emitVerdict({
19262
+ proposed: "PASS",
19263
+ changed: skipCoverageChanged,
19264
+ coverage: reviewCoverage,
19265
+ userSummary,
19266
+ agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19267
+ // The coverage note is silenced with it — half a channel is still a channel.
19268
+ silenced: !!silenced,
19269
+ openElsewhere
19270
+ });
18846
19271
  break;
18847
19272
  }
18848
19273
  case "WARN": {
@@ -18853,10 +19278,16 @@ ${YELLOW}${grantNudge.trim()}${NC}
18853
19278
  if (viewUrl) userSummary += ` Report: ${viewUrl}`;
18854
19279
  if (autoSeedNotice) userSummary = `${autoSeedNotice} ${userSummary}`;
18855
19280
  userSummary += loginNudge + grantNudge;
18856
- printJsonCompact(
18857
- buildHookOutput("WARN", userSummary, agentContextFor(response, intentRepeatCount))
18858
- );
18859
- process.exit(0);
19281
+ emitVerdict({
19282
+ proposed: "WARN",
19283
+ changed: skipCoverageChanged,
19284
+ coverage: reviewCoverage,
19285
+ userSummary,
19286
+ agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19287
+ // The coverage note is silenced with it — half a channel is still a channel.
19288
+ silenced: !!silenced,
19289
+ openElsewhere
19290
+ });
18860
19291
  break;
18861
19292
  }
18862
19293
  default: {
@@ -18969,8 +19400,8 @@ async function runReview(opts, globals) {
18969
19400
  for (const p of specPaths) {
18970
19401
  if (!(0, import_node_fs26.existsSync)(p)) continue;
18971
19402
  try {
18972
- const { readFileSync: readFileSync15 } = await import("node:fs");
18973
- const content = readFileSync15(p, "utf-8");
19403
+ const { readFileSync: readFileSync16 } = await import("node:fs");
19404
+ const content = readFileSync16(p, "utf-8");
18974
19405
  specs.push({ path: p, content: content.slice(0, 10240) });
18975
19406
  } catch {
18976
19407
  }
@@ -20656,7 +21087,7 @@ function registerTelemetryCommands(program2) {
20656
21087
  }
20657
21088
 
20658
21089
  // src/cli.ts
20659
- program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.af3c52d").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21090
+ program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.c2dc717").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
20660
21091
  try {
20661
21092
  await foldLegacyLocalCredential();
20662
21093
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.28.1-experimental.af3c52d",
3
+ "version": "0.28.1-experimental.c2dc717",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "bugs": {