@codacy/verity-cli 0.29.4-experimental.d71617f → 0.29.4-experimental.d8678a8

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 +364 -143
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -10502,6 +10502,7 @@ var GITHUB_APP_INSTALL_URL = `https://github.com/apps/${GITHUB_APP_SLUG}/install
10502
10502
  function githubAppInstallUrl(accountId) {
10503
10503
  return accountId != null ? `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new/permissions?target_id=${accountId}` : GITHUB_APP_INSTALL_URL;
10504
10504
  }
10505
+ var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
10505
10506
 
10506
10507
  // src/lib/output.ts
10507
10508
  var RED = "\x1B[0;31m";
@@ -15060,8 +15061,10 @@ function recordVerdict(d, v) {
15060
15061
  if (summary) appendEvent(d, { k: "goal_delivered", summary });
15061
15062
  }
15062
15063
  const lines = /* @__PURE__ */ new Map();
15064
+ const sent = new Set(v.sentPaths);
15063
15065
  for (const f of v.findings) {
15064
15066
  if (!f.file || typeof f.line !== "number" || !f.pattern_id) continue;
15067
+ if (!sent.has(f.file)) continue;
15065
15068
  if (!lines.has(f.file)) {
15066
15069
  try {
15067
15070
  const abs = (0, import_node_path12.join)(root, f.file);
@@ -16717,7 +16720,7 @@ function formatRunEvidence(run, startedAt) {
16717
16720
  const md = run.modeDecision;
16718
16721
  if (md) {
16719
16722
  const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
16720
- out += row("mode", `${md.resolved} \xB7 ${how} \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
16723
+ out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
16721
16724
  } else {
16722
16725
  out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
16723
16726
  }
@@ -17155,6 +17158,29 @@ function renderItem(label2, text, patternId, file, line) {
17155
17158
  const id = patternId ? ` [${patternId}]` : "";
17156
17159
  return `- ${label2}${text}${where}${id}`;
17157
17160
  }
17161
+ function channelInputFrom(response, intentRepeat = 0, priorPendingFingerprints = []) {
17162
+ const metadata = response.metadata ?? {};
17163
+ const intent = response.intent_alignment ?? {};
17164
+ return {
17165
+ intentRepeat,
17166
+ priorPendingFingerprints,
17167
+ gateDecision: String(response.gate_decision ?? ""),
17168
+ findings: response.findings ?? [],
17169
+ pendingItems: response.pending_items ?? [],
17170
+ reviewStatus: metadata.review_status,
17171
+ coverage: metadata.coverage,
17172
+ intentVerdict: intent.verdict,
17173
+ intentGaps: intent.gaps
17174
+ };
17175
+ }
17176
+ function classifyChannelContent(input) {
17177
+ const refusal = input.reviewStatus === "not_reviewed" || input.reviewStatus === "no_authorship_evidence";
17178
+ const intentFlag = input.intentVerdict === "misaligned" || input.intentVerdict === "partial";
17179
+ const advisory = (input.findings ?? []).some((f) => f.scope !== "pre-existing") || (input.pendingItems ?? []).some(
17180
+ (p) => p.pattern_id !== "intent-misalignment" && !!(p.description ?? p.title ?? p.reason)
17181
+ );
17182
+ return { refusal, intentFlag, advisory };
17183
+ }
17158
17184
  function buildAgentContext(input) {
17159
17185
  const lines = [];
17160
17186
  if (input.reviewStatus === "not_reviewed") {
@@ -17240,7 +17266,7 @@ function channelSilence(input) {
17240
17266
  // src/lib/cli-version.ts
17241
17267
  function cliVersion() {
17242
17268
  try {
17243
- return true ? "0.29.4-experimental.d71617f" : "dev";
17269
+ return true ? "0.29.4-experimental.d8678a8" : "dev";
17244
17270
  } catch {
17245
17271
  return "dev";
17246
17272
  }
@@ -18057,26 +18083,50 @@ function narrowToRecent(files, sessionId) {
18057
18083
  });
18058
18084
  return recent.length > 0 ? recent : files;
18059
18085
  }
18060
- function readIterationState(currentCommit) {
18061
- if (!(0, import_node_fs22.existsSync)(ITERATION_FILE)) return { iteration: 1, fingerprint: null };
18086
+ function readIteration(currentCommit, _contentHash) {
18087
+ return Math.max(1, readBlockState(currentCommit).attempts);
18088
+ }
18089
+ var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
18090
+ function readBlockState(currentCommit, opts) {
18091
+ if (opts?.newUserPrompt) return NO_BLOCKS;
18092
+ if (!(0, import_node_fs22.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
18062
18093
  try {
18063
18094
  const stored = (0, import_node_fs22.readFileSync)(ITERATION_FILE, "utf-8").trim();
18064
- const parts = stored.split(":");
18065
- const iter = parseInt(parts[0], 10);
18066
- const storedCommit = parts[1] ?? "";
18067
- const storedTimestamp = parseInt(parts[2] ?? "0", 10);
18068
- const fingerprint = parts.slice(3).join(":") || null;
18069
- if (isNaN(iter)) return { iteration: 1, fingerprint: null };
18070
- if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
18071
- if (storedTimestamp > 0) {
18072
- const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
18073
- if (elapsed > 600) return { iteration: 1, fingerprint: null };
18074
- }
18075
- return { iteration: iter, fingerprint };
18095
+ const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
18096
+ if (!parsed) return NO_BLOCKS;
18097
+ if (parsed.commit !== currentCommit) return NO_BLOCKS;
18098
+ if (parsed.ts > 0 && Math.floor(Date.now() / 1e3) - parsed.ts > 600) return NO_BLOCKS;
18099
+ return { attempts: parsed.attempts, blocks: parsed.blocks, fingerprint: parsed.fingerprint };
18076
18100
  } catch {
18077
- return { iteration: 1, fingerprint: null };
18101
+ return NO_BLOCKS;
18078
18102
  }
18079
18103
  }
18104
+ function parseJsonState(raw) {
18105
+ const o = JSON.parse(raw);
18106
+ const attempts = typeof o.attempts === "number" ? o.attempts : NaN;
18107
+ if (isNaN(attempts)) return null;
18108
+ return {
18109
+ attempts,
18110
+ blocks: typeof o.blocks === "number" ? o.blocks : attempts,
18111
+ fingerprint: typeof o.fingerprint === "string" && o.fingerprint ? o.fingerprint : null,
18112
+ commit: typeof o.commit === "string" ? o.commit : "",
18113
+ ts: typeof o.ts === "number" ? o.ts : 0
18114
+ };
18115
+ }
18116
+ function parseLegacyState(raw) {
18117
+ const parts = raw.split(":");
18118
+ const n = parseInt(parts[0], 10);
18119
+ if (isNaN(n)) return null;
18120
+ return {
18121
+ attempts: n,
18122
+ // The old file has no separate block count; the old counter is the closest
18123
+ // honest answer, and it errs toward releasing sooner rather than later.
18124
+ blocks: n,
18125
+ fingerprint: parts.slice(3).join(":") || null,
18126
+ commit: parts[1] ?? "",
18127
+ ts: parseInt(parts[2] ?? "0", 10)
18128
+ };
18129
+ }
18080
18130
  function findingsFingerprint(findings) {
18081
18131
  const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
18082
18132
  return [...new Set(keys)].sort().join(",");
@@ -18086,11 +18136,22 @@ function isSameProblem(previous, current) {
18086
18136
  const prev = new Set(previous.split(","));
18087
18137
  return current.split(",").some((k) => prev.has(k));
18088
18138
  }
18089
- function writeIteration(iteration, commit, _contentHash, fingerprint) {
18139
+ function writeBlockState(commit, state) {
18090
18140
  (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
18091
- const ts = Math.floor(Date.now() / 1e3);
18092
- const fp = fingerprint ? `:${fingerprint}` : "";
18093
- (0, import_node_fs22.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}${fp}`);
18141
+ (0, import_node_fs22.writeFileSync)(
18142
+ ITERATION_FILE,
18143
+ JSON.stringify({
18144
+ v: 2,
18145
+ attempts: state.attempts,
18146
+ blocks: state.blocks,
18147
+ commit,
18148
+ ts: Math.floor(Date.now() / 1e3),
18149
+ fingerprint: state.fingerprint ?? void 0
18150
+ })
18151
+ );
18152
+ }
18153
+ function resetBlockState(commit) {
18154
+ writeBlockState(commit, { attempts: 0, blocks: 0, fingerprint: null });
18094
18155
  }
18095
18156
 
18096
18157
  // src/lib/fold.ts
@@ -18282,8 +18343,10 @@ function fold(transcriptPath, opts = {}) {
18282
18343
  subagentSkipped: 0,
18283
18344
  compactions: 0,
18284
18345
  complete: false
18285
- }
18346
+ },
18347
+ planApproval: { approvals: 0, activeSinceLastPrompt: false }
18286
18348
  };
18349
+ const flow = { seq: 0, lastPrompt: -1, lastApproval: -1, approvals: 0 };
18287
18350
  const byPath = /* @__PURE__ */ new Map();
18288
18351
  const commandStats = /* @__PURE__ */ new Map();
18289
18352
  const pendingByToolUse = /* @__PURE__ */ new Map();
@@ -18309,8 +18372,12 @@ function fold(transcriptPath, opts = {}) {
18309
18372
  if (type === "system" && record.subtype === "compact_boundary") {
18310
18373
  result.coverage.compactions++;
18311
18374
  }
18312
- if (type === "user" && hasUserText(record)) result.coverage.userMessages++;
18313
- collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, opts.repoRoot, result.coverage);
18375
+ if (type === "user" && hasUserText(record)) {
18376
+ result.coverage.userMessages++;
18377
+ if (owner === "agent") flow.lastPrompt = flow.seq;
18378
+ }
18379
+ if (owner === "agent") flow.seq++;
18380
+ collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, opts.repoRoot, result.coverage, flow);
18314
18381
  }
18315
18382
  };
18316
18383
  try {
@@ -18382,6 +18449,10 @@ function fold(transcriptPath, opts = {}) {
18382
18449
  if (!p || authoredPaths.has(p)) continue;
18383
18450
  result.unobserved.push({ p, cause: classifyUnobserved(raw) });
18384
18451
  }
18452
+ result.planApproval = {
18453
+ approvals: flow.approvals,
18454
+ activeSinceLastPrompt: flow.lastApproval >= 0 && flow.lastApproval > flow.lastPrompt
18455
+ };
18385
18456
  return result;
18386
18457
  }
18387
18458
  function classifyUnobserved(path) {
@@ -18394,7 +18465,7 @@ function classifyUnobserved(path) {
18394
18465
  if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
18395
18466
  return "no_edit_record";
18396
18467
  }
18397
- function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally) {
18468
+ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally, flow) {
18398
18469
  const message = record.message;
18399
18470
  const content = message?.content ?? record.content;
18400
18471
  const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
@@ -18478,6 +18549,13 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
18478
18549
  }
18479
18550
  }
18480
18551
  const toolName = id ? pendingToolName.get(id) : void 0;
18552
+ if (toolName === "ExitPlanMode" && flow && block.is_error !== true) {
18553
+ const body = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => typeof c.text === "string" ? c.text : "").join(" ") : "";
18554
+ if (/approved your plan/i.test(body)) {
18555
+ flow.lastApproval = flow.seq;
18556
+ flow.approvals += 1;
18557
+ }
18558
+ }
18481
18559
  if (toolName) {
18482
18560
  pendingToolName.delete(id);
18483
18561
  const prevTool = toolStats.get(toolName);
@@ -18535,8 +18613,13 @@ function checkConservation(changedFiles, result, repoRoot2) {
18535
18613
  // src/commands/analyze/phases/06-evidence.ts
18536
18614
  async function evidence(run) {
18537
18615
  const { opts } = run;
18538
- const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath } = run;
18616
+ const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run;
18539
18617
  let { analysisMode, earlyFold } = run;
18618
+ const recordFlip = (stage) => {
18619
+ if (run.modeDecision) run.modeDecision = { ...run.modeDecision, resolved: "plan", flip: stage };
18620
+ logEvent("mode_flipped", { stage, to: "plan" });
18621
+ };
18622
+ const planWorthy = !!assistantResponse && !turnAuthoredCode;
18540
18623
  let staticResults = {
18541
18624
  tool: "@codacy/analysis-cli",
18542
18625
  findings: [],
@@ -18556,8 +18639,9 @@ async function evidence(run) {
18556
18639
  const debounceSeconds = parseInt(opts.debounce, 10);
18557
18640
  const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
18558
18641
  if (debounceSkip) {
18559
- if (assistantResponse) {
18642
+ if (planWorthy) {
18560
18643
  analysisMode = "plan";
18644
+ recordFlip("debounce");
18561
18645
  } else {
18562
18646
  await passAndExit(run, debounceSkip, "debounce");
18563
18647
  }
@@ -18566,8 +18650,9 @@ async function evidence(run) {
18566
18650
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
18567
18651
  const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
18568
18652
  if (mtimeSkip) {
18569
- if (assistantResponse) {
18653
+ if (planWorthy) {
18570
18654
  analysisMode = "plan";
18655
+ recordFlip("mtime");
18571
18656
  } else {
18572
18657
  await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
18573
18658
  }
@@ -18578,8 +18663,9 @@ async function evidence(run) {
18578
18663
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
18579
18664
  const hashResult = checkContentHash(allCheckable, baselineSessionId);
18580
18665
  if (hashResult.skip) {
18581
- if (assistantResponse) {
18666
+ if (planWorthy) {
18582
18667
  analysisMode = "plan";
18668
+ recordFlip("content-hash");
18583
18669
  } else {
18584
18670
  await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
18585
18671
  }
@@ -18641,8 +18727,9 @@ async function evidence(run) {
18641
18727
  maxTotalBytes: parseInt(opts.maxTotalSize, 10)
18642
18728
  });
18643
18729
  if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
18644
- if (assistantResponse) {
18730
+ if (planWorthy) {
18645
18731
  analysisMode = "plan";
18732
+ recordFlip("empty-after-scoping");
18646
18733
  } else {
18647
18734
  await passAndExit(
18648
18735
  run,
@@ -18662,13 +18749,13 @@ async function evidence(run) {
18662
18749
  snapshotResult = generateSnapshotDiffs(codeDelta.files);
18663
18750
  }
18664
18751
  currentCommit = getCurrentCommit();
18665
- iteration = readIterationState(currentCommit).iteration;
18752
+ iteration = readIteration(currentCommit);
18666
18753
  }
18667
18754
  }
18668
18755
  if (analysisMode === "plan") {
18669
18756
  recordAnalysisStart();
18670
18757
  currentCommit = getCurrentCommit();
18671
- iteration = readIterationState(currentCommit).iteration;
18758
+ iteration = readIteration(currentCommit);
18672
18759
  }
18673
18760
  Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
18674
18761
  }
@@ -19334,6 +19421,54 @@ async function workingMemory(run) {
19334
19421
  Object.assign(run, { incrementReport, memory, memorySession, reachability });
19335
19422
  }
19336
19423
 
19424
+ // src/lib/note-budget.ts
19425
+ var import_node_fs28 = require("node:fs");
19426
+ var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
19427
+ var EPISODE_STALE_SECONDS = 30 * 60;
19428
+ var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
19429
+ function resolveEpisode(prev, signals) {
19430
+ if (!prev) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
19431
+ if (signals.humanSpoke) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
19432
+ if (signals.rawFail) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
19433
+ if (prev.tasksCompleted !== signals.tasksCompleted) {
19434
+ return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
19435
+ }
19436
+ if (prev.ts > 0 && signals.now - prev.ts > EPISODE_STALE_SECONDS) {
19437
+ return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
19438
+ }
19439
+ return prev;
19440
+ }
19441
+ function advisoryBudgetSpent(episode, rawDecision) {
19442
+ const budget = ADVISORY_BUDGET[rawDecision] ?? ADVISORY_BUDGET.WARN;
19443
+ return episode.delivered >= budget;
19444
+ }
19445
+ function readAdvisoryEpisode(sessionId) {
19446
+ const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
19447
+ if (!(0, import_node_fs28.existsSync)(file)) return null;
19448
+ try {
19449
+ const o = JSON.parse((0, import_node_fs28.readFileSync)(file, "utf-8")) ?? {};
19450
+ const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
19451
+ if (isNaN(delivered)) return null;
19452
+ return {
19453
+ delivered,
19454
+ tasksCompleted: typeof o.tasksCompleted === "number" ? o.tasksCompleted : 0,
19455
+ ts: typeof o.ts === "number" ? o.ts : 0
19456
+ };
19457
+ } catch {
19458
+ return null;
19459
+ }
19460
+ }
19461
+ function writeAdvisoryEpisode(episode, sessionId) {
19462
+ try {
19463
+ (0, import_node_fs28.mkdirSync)(VERITY_DIR, { recursive: true });
19464
+ (0, import_node_fs28.writeFileSync)(
19465
+ scopedFile(ADVISORY_EPISODE_FILE, sessionId),
19466
+ JSON.stringify({ v: 1, ...episode })
19467
+ );
19468
+ } catch {
19469
+ }
19470
+ }
19471
+
19337
19472
  // src/lib/run-mode.ts
19338
19473
  function parseAutonomousEnv(raw) {
19339
19474
  if (raw === void 0) return void 0;
@@ -19427,7 +19562,13 @@ async function buildRequest(run) {
19427
19562
  excluded_by_reason: excludedByReason,
19428
19563
  // was the transcript itself truncated? The 256 KB window means "this turn"
19429
19564
  // can quietly mean "the last 256 KB of it".
19430
- transcript_windowed: actionSummary?.transcript_windowed ?? null
19565
+ transcript_windowed: actionSummary?.transcript_windowed ?? null,
19566
+ // The advisory budget's fleet counter-metric (note-budget.ts): deliveries in
19567
+ // the episode as of the PREVIOUS turn — this runs before phase 13 updates
19568
+ // the state, so the number is one turn lagged by construction. The
19569
+ // degenerate win for the budget is a dead channel that looks like clean
19570
+ // code; this is what makes "did delivery rate collapse" a query.
19571
+ advisory_delivered_prior: readAdvisoryEpisode(run.baselineSessionId)?.delivered ?? 0
19431
19572
  };
19432
19573
  const requestBody = {
19433
19574
  coverage_telemetry: coverageTelemetry,
@@ -19566,7 +19707,8 @@ async function buildRequest(run) {
19566
19707
  }
19567
19708
  const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
19568
19709
  const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
19569
- const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task;
19710
+ const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
19711
+ const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task || planApprovalActive;
19570
19712
  if (hasIntent) {
19571
19713
  const intentContext = {};
19572
19714
  if (conversation && conversation.prompts.length > 0) {
@@ -19598,6 +19740,10 @@ async function buildRequest(run) {
19598
19740
  intentContext.user_prompt = w4Task.goal;
19599
19741
  logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
19600
19742
  }
19743
+ if (planApprovalActive) {
19744
+ intentContext.plan_approved = true;
19745
+ logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
19746
+ }
19601
19747
  if (assistantResponse) {
19602
19748
  const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
19603
19749
  intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
@@ -19617,14 +19763,14 @@ async function buildRequest(run) {
19617
19763
  }
19618
19764
 
19619
19765
  // src/lib/offline.ts
19620
- var import_node_fs28 = require("node:fs");
19766
+ var import_node_fs29 = require("node:fs");
19621
19767
  var import_node_crypto11 = require("node:crypto");
19622
19768
  function cacheRequest(body) {
19623
19769
  try {
19624
- (0, import_node_fs28.mkdirSync)(CACHE_DIR, { recursive: true });
19770
+ (0, import_node_fs29.mkdirSync)(CACHE_DIR, { recursive: true });
19625
19771
  const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
19626
19772
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
19627
- (0, import_node_fs28.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
19773
+ (0, import_node_fs29.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
19628
19774
  } catch {
19629
19775
  }
19630
19776
  }
@@ -19743,10 +19889,10 @@ async function transmit(run) {
19743
19889
  }
19744
19890
 
19745
19891
  // src/commands/analyze/phases/13-reconcile.ts
19746
- var import_node_fs29 = require("node:fs");
19892
+ var import_node_fs30 = require("node:fs");
19747
19893
  var import_node_path23 = require("node:path");
19748
19894
  async function reconcile(run) {
19749
- const { actionSummary, allChanged, analyzable, baseline, codeDelta, contentHash, conversation, decision, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
19895
+ const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
19750
19896
  const sentPaths = codeDelta.files.map((f) => f.path);
19751
19897
  let openElsewhere = [];
19752
19898
  if (memorySession) {
@@ -19754,7 +19900,7 @@ async function reconcile(run) {
19754
19900
  const st = foldDossier(memorySession.d);
19755
19901
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
19756
19902
  try {
19757
- const src = (0, import_node_fs29.readFileSync)((0, import_node_path23.join)(repoRoot(), file), "utf8").split("\n");
19903
+ const src = (0, import_node_fs30.readFileSync)((0, import_node_path23.join)(repoRoot(), file), "utf8").split("\n");
19758
19904
  const at = src[line - 1];
19759
19905
  return at === void 0 ? null : lineSha(at);
19760
19906
  } catch {
@@ -19846,6 +19992,29 @@ async function reconcile(run) {
19846
19992
  decision
19847
19993
  });
19848
19994
  }
19995
+ const episodeSignals = {
19996
+ humanSpoke: (conversation?.prompts?.length ?? 0) > 0,
19997
+ rawFail: decision === "FAIL",
19998
+ tasksCompleted: (foldResult?.tasks ?? []).filter((t) => t.status === "completed").length,
19999
+ now: Math.floor(Date.now() / 1e3)
20000
+ };
20001
+ let episode = resolveEpisode(readAdvisoryEpisode(baselineSessionId), episodeSignals);
20002
+ const contentClass = classifyChannelContent(channelInputFrom(response));
20003
+ const wouldCarryAdvisory = contentClass.advisory || openElsewhere.length > 0;
20004
+ if (decision !== "FAIL" && !silenced && wouldCarryAdvisory && !contentClass.refusal && !contentClass.intentFlag && advisoryBudgetSpent(episode, decision)) {
20005
+ silenced = "note-budget";
20006
+ logEvent("channel_silenced", {
20007
+ reason: silenced,
20008
+ run_id: response.run_id ?? turnId,
20009
+ decision,
20010
+ episode_delivered: episode.delivered
20011
+ });
20012
+ }
20013
+ const deliveringAdvisory = decision !== "FAIL" && !silenced && wouldCarryAdvisory;
20014
+ writeAdvisoryEpisode(
20015
+ { ...episode, delivered: episode.delivered + (deliveringAdvisory ? 1 : 0), ts: episodeSignals.now },
20016
+ baselineSessionId
20017
+ );
19849
20018
  let intentRepeatCount = 0;
19850
20019
  const priorPendingFingerprints = memorySession ? (() => {
19851
20020
  try {
@@ -19861,6 +20030,11 @@ async function reconcile(run) {
19861
20030
  decision,
19862
20031
  branch: getCurrentBranch(),
19863
20032
  watermarkSha: watermarkIsPartial ? null : watermarkHash,
20033
+ // The byte witness — the same "only honest definition of reviewed" the
20034
+ // coverage column uses. A finding on a path outside this set records no
20035
+ // statement (plan-mode prose anchored to unsent files must not become
20036
+ // "STILL OPEN … the tree is not clean").
20037
+ sentPaths,
19864
20038
  findings: response.findings?.map((f) => ({
19865
20039
  file: f.file,
19866
20040
  line: f.line,
@@ -19927,6 +20101,39 @@ ${YELLOW2}${note}${NC2}
19927
20101
  return exit(0);
19928
20102
  }
19929
20103
 
20104
+ // src/lib/may-block.ts
20105
+ var HARD_BLOCK_CEILING = 5;
20106
+ function mayBlock(input) {
20107
+ const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
20108
+ if (input.reviewedFileCount === 0 && input.staticFindingCount === 0) {
20109
+ return { block: false, release: "no-code-reviewed" };
20110
+ }
20111
+ if (input.cycleCutFired) {
20112
+ return { block: false, release: "nothing-moved" };
20113
+ }
20114
+ if (input.attempts > input.maxIterations) {
20115
+ return { block: false, release: "same-problem-cap" };
20116
+ }
20117
+ if (input.blocks > ceiling) {
20118
+ return { block: false, release: "block-ceiling" };
20119
+ }
20120
+ return { block: true, release: null };
20121
+ }
20122
+ function describeRelease(release, input) {
20123
+ const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
20124
+ const open = input.findingCount > 0 ? `${input.findingCount} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.` : "Human review required before deploying.";
20125
+ switch (release) {
20126
+ case "no-code-reviewed":
20127
+ return `Verity: WARN \u2014 NOT BLOCKING: no code was reviewed on this turn, so there is nothing here to fix. Reported as advice instead. ${open}`;
20128
+ case "nothing-moved":
20129
+ return `Verity: WARN \u2014 NOT BLOCKING: nothing has changed since the last verdict, so re-raising it cannot move anything forward. ${open}`;
20130
+ case "same-problem-cap":
20131
+ return `Verity: WARN \u2014 self-healing limit (${input.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${open}`;
20132
+ case "block-ceiling":
20133
+ return `Verity: WARN \u2014 ${ceiling} consecutive blocking verdicts reached; releasing the block so this cannot loop. ${open}`;
20134
+ }
20135
+ }
20136
+
19930
20137
  // src/lib/remediation-guard.ts
19931
20138
  var TOOL_CONFIG_PATTERNS = [
19932
20139
  /(^|\/)\.codacy\//,
@@ -19969,19 +20176,7 @@ function screenRemediation(fix, findingFile) {
19969
20176
 
19970
20177
  // src/commands/analyze/phases/14-render.ts
19971
20178
  function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
19972
- const metadata = response.metadata ?? {};
19973
- const intent = response.intent_alignment ?? {};
19974
- return buildAgentContext({
19975
- intentRepeat,
19976
- priorPendingFingerprints,
19977
- gateDecision: String(response.gate_decision ?? ""),
19978
- findings: response.findings ?? [],
19979
- pendingItems: response.pending_items ?? [],
19980
- reviewStatus: metadata.review_status,
19981
- coverage: metadata.coverage,
19982
- intentVerdict: intent.verdict,
19983
- intentGaps: intent.gaps
19984
- });
20179
+ return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
19985
20180
  }
19986
20181
  async function render(run) {
19987
20182
  const { opts, globals } = run;
@@ -20075,38 +20270,63 @@ async function render(run) {
20075
20270
  reverify_by: response.reverify_by
20076
20271
  });
20077
20272
  const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
20078
- let capReleased = false;
20273
+ let release = null;
20079
20274
  let effectiveDecision = decision;
20080
20275
  if (decision === "FAIL") {
20081
- const blocking = (response.findings ?? []).filter((f) => {
20276
+ const findings = response.findings ?? [];
20277
+ const blocking = findings.filter((f) => {
20082
20278
  const sev = String(f.severity ?? "").toLowerCase();
20083
20279
  return sev === "critical" || sev === "high";
20084
20280
  });
20085
20281
  const fingerprint = findingsFingerprint(blocking);
20086
- const prior = readIterationState(currentCommit);
20282
+ const prior = readBlockState(currentCommit, {
20283
+ newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
20284
+ });
20087
20285
  const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
20088
- const nextIteration = sameProblem ? prior.iteration + 1 : 1;
20089
20286
  const maxIterations = parseInt(opts.maxIterations, 10);
20090
- writeIteration(nextIteration, currentCommit, contentHash ?? void 0, fingerprint);
20091
- iteration = nextIteration;
20092
- if (nextIteration > maxIterations) {
20093
- capReleased = true;
20287
+ const attempts = sameProblem ? prior.attempts + 1 : 1;
20288
+ const blocks = prior.blocks + 1;
20289
+ const decisionNow = mayBlock({
20290
+ reviewedFileCount: codeDelta.files.length,
20291
+ staticFindingCount: run.staticResults?.findings?.length ?? 0,
20292
+ cycleCutFired: silenced !== null,
20293
+ attempts,
20294
+ blocks,
20295
+ maxIterations
20296
+ });
20297
+ if (decisionNow.block) {
20298
+ writeBlockState(currentCommit, { attempts, blocks, fingerprint });
20299
+ iteration = attempts;
20300
+ } else {
20301
+ release = decisionNow.release;
20094
20302
  effectiveDecision = "WARN";
20095
- logEvent("iteration_cap_released", { iteration: nextIteration, fingerprint });
20303
+ logEvent("block_released", {
20304
+ reason: release,
20305
+ attempts,
20306
+ blocks,
20307
+ reviewed_files: codeDelta.files.length,
20308
+ cycle_cut: silenced,
20309
+ fingerprint
20310
+ });
20096
20311
  }
20097
20312
  }
20098
- if (capReleased) {
20313
+ if (release) {
20099
20314
  const findings = response.findings ?? [];
20100
20315
  const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
20316
+ const summary = describeRelease(release, {
20317
+ findingCount: findings.length,
20318
+ maxIterations: parseInt(opts.maxIterations, 10)
20319
+ });
20101
20320
  emitVerdict({
20102
20321
  proposed: "WARN",
20103
20322
  changed: run.changedUniverse,
20104
20323
  coverage: reviewCoverage,
20105
- userSummary: `Verity: WARN \u2014 self-healing limit (${opts.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${findings.length} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.
20106
- ${lines.join("\n")}`,
20324
+ userSummary: lines.length > 0 ? `${summary}
20325
+ ${lines.join("\n")}` : summary,
20107
20326
  agentContext: null,
20108
20327
  silenced: true
20109
20328
  });
20329
+ return;
20110
20330
  }
20111
20331
  switch (effectiveDecision) {
20112
20332
  case "FAIL": {
@@ -20205,7 +20425,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
20205
20425
  break;
20206
20426
  }
20207
20427
  case "PASS": {
20208
- writeIteration(1, currentCommit, contentHash ?? void 0);
20428
+ resetBlockState(currentCommit);
20209
20429
  if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
20210
20430
  if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
20211
20431
  let userSummary = response.user_summary ?? "Verity: PASS";
@@ -20226,6 +20446,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
20226
20446
  break;
20227
20447
  }
20228
20448
  case "WARN": {
20449
+ if (decision !== "FAIL") resetBlockState(currentCommit);
20229
20450
  if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
20230
20451
  if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
20231
20452
  let userSummary = response.user_summary ?? "Verity: WARN";
@@ -20324,7 +20545,7 @@ async function runAnalyze(opts, globals) {
20324
20545
  }
20325
20546
 
20326
20547
  // src/commands/baseline.ts
20327
- var import_node_fs30 = require("node:fs");
20548
+ var import_node_fs31 = require("node:fs");
20328
20549
  function registerBaselineCommands(program2) {
20329
20550
  const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
20330
20551
  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) => {
@@ -20333,7 +20554,7 @@ function registerBaselineCommands(program2) {
20333
20554
  process.chdir(repoRoot());
20334
20555
  } catch {
20335
20556
  }
20336
- if (!(0, import_node_fs30.existsSync)(VERITY_DIR)) {
20557
+ if (!(0, import_node_fs31.existsSync)(VERITY_DIR)) {
20337
20558
  process.exit(0);
20338
20559
  }
20339
20560
  let sessionId = opts.sessionId;
@@ -20373,7 +20594,7 @@ async function readStdin() {
20373
20594
  }
20374
20595
 
20375
20596
  // src/commands/review.ts
20376
- var import_node_fs31 = require("node:fs");
20597
+ var import_node_fs32 = require("node:fs");
20377
20598
  function registerReviewCommand(program2) {
20378
20599
  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) => {
20379
20600
  const globals = program2.opts();
@@ -20392,7 +20613,7 @@ async function runReview(opts, globals) {
20392
20613
  const securityFiles = filterSecurity(allFiles);
20393
20614
  let staticResults;
20394
20615
  if (isCodacyAvailable()) {
20395
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs31.existsSync)(f) || resolveFile(f) !== null);
20616
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs32.existsSync)(f) || resolveFile(f) !== null);
20396
20617
  staticResults = runCodacyAnalysis(scannable);
20397
20618
  } else {
20398
20619
  staticResults = {
@@ -20418,10 +20639,10 @@ async function runReview(opts, globals) {
20418
20639
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
20419
20640
  specs = [];
20420
20641
  for (const p of specPaths) {
20421
- if (!(0, import_node_fs31.existsSync)(p)) continue;
20642
+ if (!(0, import_node_fs32.existsSync)(p)) continue;
20422
20643
  try {
20423
- const { readFileSync: readFileSync18 } = await import("node:fs");
20424
- const content = readFileSync18(p, "utf-8");
20644
+ const { readFileSync: readFileSync19 } = await import("node:fs");
20645
+ const content = readFileSync19(p, "utf-8");
20425
20646
  specs.push({ path: p, content: content.slice(0, 10240) });
20426
20647
  } catch {
20427
20648
  }
@@ -20478,7 +20699,7 @@ async function runReview(opts, globals) {
20478
20699
  }
20479
20700
 
20480
20701
  // src/commands/guard.ts
20481
- var import_node_fs32 = require("node:fs");
20702
+ var import_node_fs33 = require("node:fs");
20482
20703
  var import_node_path24 = require("node:path");
20483
20704
  var GUARD_BLOCK_CAP = 2;
20484
20705
  var GUARD_ITER_FILE = (0, import_node_path24.join)(VERITY_DIR, ".guard-iteration");
@@ -20545,7 +20766,7 @@ function classifyCommand2(command, on) {
20545
20766
  }
20546
20767
  function readIterMap() {
20547
20768
  try {
20548
- const raw = JSON.parse((0, import_node_fs32.readFileSync)(GUARD_ITER_FILE, "utf-8"));
20769
+ const raw = JSON.parse((0, import_node_fs33.readFileSync)(GUARD_ITER_FILE, "utf-8"));
20549
20770
  if (raw && typeof raw === "object") {
20550
20771
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
20551
20772
  return { [raw.moment]: raw.count };
@@ -20565,10 +20786,10 @@ function readIter(moment) {
20565
20786
  }
20566
20787
  function writeIter(moment, count) {
20567
20788
  try {
20568
- (0, import_node_fs32.mkdirSync)(VERITY_DIR, { recursive: true });
20789
+ (0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
20569
20790
  const map = readIterMap();
20570
20791
  map[moment] = count;
20571
- (0, import_node_fs32.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20792
+ (0, import_node_fs33.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20572
20793
  } catch {
20573
20794
  }
20574
20795
  }
@@ -20578,10 +20799,10 @@ function resetIter(moment) {
20578
20799
  if (!(moment in map)) return;
20579
20800
  delete map[moment];
20580
20801
  if (Object.keys(map).length === 0) {
20581
- if ((0, import_node_fs32.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs32.unlinkSync)(GUARD_ITER_FILE);
20802
+ if ((0, import_node_fs33.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs33.unlinkSync)(GUARD_ITER_FILE);
20582
20803
  } else {
20583
- (0, import_node_fs32.mkdirSync)(VERITY_DIR, { recursive: true });
20584
- (0, import_node_fs32.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20804
+ (0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
20805
+ (0, import_node_fs33.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20585
20806
  }
20586
20807
  } catch {
20587
20808
  }
@@ -20645,7 +20866,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
20645
20866
  const securityFiles = filterSecurity(files);
20646
20867
  let staticResults;
20647
20868
  if (isCodacyAvailable()) {
20648
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs32.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
20869
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs33.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
20649
20870
  staticResults = runCodacyAnalysis(scannable);
20650
20871
  } else {
20651
20872
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -20690,7 +20911,7 @@ function emitAllowNotice(userMsg, agentMsg) {
20690
20911
  async function runGuard(opts, globals) {
20691
20912
  const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
20692
20913
  const { command, cwd, sessionId } = await readPreToolUseStdin();
20693
- if (cwd && (0, import_node_fs32.existsSync)(cwd)) {
20914
+ if (cwd && (0, import_node_fs33.existsSync)(cwd)) {
20694
20915
  try {
20695
20916
  process.chdir(cwd);
20696
20917
  } catch {
@@ -20796,14 +21017,14 @@ function writeBlockMessage(moment, response) {
20796
21017
  }
20797
21018
 
20798
21019
  // src/commands/init.ts
20799
- var import_node_fs34 = require("node:fs");
21020
+ var import_node_fs35 = require("node:fs");
20800
21021
  var import_promises13 = require("node:fs/promises");
20801
21022
  var import_node_path26 = require("node:path");
20802
21023
  var import_node_child_process10 = require("node:child_process");
20803
21024
  var readline2 = __toESM(require("node:readline/promises"));
20804
21025
 
20805
21026
  // src/commands/migrate.ts
20806
- var import_node_fs33 = require("node:fs");
21027
+ var import_node_fs34 = require("node:fs");
20807
21028
  var import_node_path25 = require("node:path");
20808
21029
  var import_node_child_process9 = require("node:child_process");
20809
21030
 
@@ -20935,10 +21156,10 @@ async function runMigration(opts = {}) {
20935
21156
  function migrateProjectDir(root, actions) {
20936
21157
  const gateDir = (0, import_node_path25.join)(root, ".gate");
20937
21158
  const verityDir = (0, import_node_path25.join)(root, ".verity");
20938
- if ((0, import_node_fs33.existsSync)(gateDir) && !(0, import_node_fs33.existsSync)(verityDir)) {
21159
+ if ((0, import_node_fs34.existsSync)(gateDir) && !(0, import_node_fs34.existsSync)(verityDir)) {
20939
21160
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
20940
21161
  }
20941
- if ((0, import_node_fs33.existsSync)(gateDir) && (0, import_node_fs33.existsSync)(verityDir)) {
21162
+ if ((0, import_node_fs34.existsSync)(gateDir) && (0, import_node_fs34.existsSync)(verityDir)) {
20942
21163
  return migrateProjectDirCarry(gateDir, verityDir, actions);
20943
21164
  }
20944
21165
  return false;
@@ -20959,13 +21180,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
20959
21180
  }
20960
21181
  }
20961
21182
  if (moved) {
20962
- if ((0, import_node_fs33.existsSync)(gateDir)) {
21183
+ if ((0, import_node_fs34.existsSync)(gateDir)) {
20963
21184
  const carried = carryLegacyContents(gateDir, verityDir);
20964
21185
  if (carried > 0) {
20965
21186
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
20966
21187
  }
20967
21188
  try {
20968
- (0, import_node_fs33.rmSync)(gateDir, { recursive: true, force: true });
21189
+ (0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
20969
21190
  } catch {
20970
21191
  }
20971
21192
  }
@@ -20981,7 +21202,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
20981
21202
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
20982
21203
  }
20983
21204
  try {
20984
- (0, import_node_fs33.rmSync)(gateDir, { recursive: true, force: true });
21205
+ (0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
20985
21206
  } catch {
20986
21207
  }
20987
21208
  return carried > 0;
@@ -20990,9 +21211,9 @@ function migrateGlobalCredentials(home, actions) {
20990
21211
  if (!home) return;
20991
21212
  const gateCreds = (0, import_node_path25.join)(home, ".gate", "credentials");
20992
21213
  const verityCreds = (0, import_node_path25.join)(home, ".verity", "credentials");
20993
- if (!(0, import_node_fs33.existsSync)(gateCreds)) return;
20994
- if (!(0, import_node_fs33.existsSync)(verityCreds)) {
20995
- (0, import_node_fs33.mkdirSync)((0, import_node_path25.join)(home, ".verity"), { recursive: true });
21214
+ if (!(0, import_node_fs34.existsSync)(gateCreds)) return;
21215
+ if (!(0, import_node_fs34.existsSync)(verityCreds)) {
21216
+ (0, import_node_fs34.mkdirSync)((0, import_node_path25.join)(home, ".verity"), { recursive: true });
20996
21217
  moveFile(gateCreds, verityCreds);
20997
21218
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
20998
21219
  return;
@@ -21015,7 +21236,7 @@ async function migrateLegacyHooks(root, actions) {
21015
21236
  }
21016
21237
  async function migrateClaudeMd(root, actions) {
21017
21238
  const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
21018
- const hadLegacyBlock = (0, import_node_fs33.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
21239
+ const hadLegacyBlock = (0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
21019
21240
  if (!hadLegacyBlock) return;
21020
21241
  try {
21021
21242
  await ensureClaudeMdPointer(root);
@@ -21027,7 +21248,7 @@ async function migrateClaudeMd(root, actions) {
21027
21248
  function migrateStandardFile(root, actions) {
21028
21249
  const gateMd = (0, import_node_path25.join)(root, "GATE.md");
21029
21250
  const verityMd = (0, import_node_path25.join)(root, "VERITY.md");
21030
- if (!(0, import_node_fs33.existsSync)(gateMd) || (0, import_node_fs33.existsSync)(verityMd)) return;
21251
+ if (!(0, import_node_fs34.existsSync)(gateMd) || (0, import_node_fs34.existsSync)(verityMd)) return;
21031
21252
  let moved = false;
21032
21253
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
21033
21254
  try {
@@ -21039,12 +21260,12 @@ function migrateStandardFile(root, actions) {
21039
21260
  if (!moved) moveFile(gateMd, verityMd);
21040
21261
  const content = readFileSyncSafe(verityMd);
21041
21262
  const refreshed = content.split("GATE.md").join("VERITY.md");
21042
- if (refreshed !== content) (0, import_node_fs33.writeFileSync)(verityMd, refreshed);
21263
+ if (refreshed !== content) (0, import_node_fs34.writeFileSync)(verityMd, refreshed);
21043
21264
  actions.push("Renamed GATE.md \u2192 VERITY.md");
21044
21265
  }
21045
21266
  async function migrateTelemetryHeaders(root, actions) {
21046
21267
  const file = (0, import_node_path25.join)(root, ".claude", "settings.local.json");
21047
- if (!(0, import_node_fs33.existsSync)(file)) return;
21268
+ if (!(0, import_node_fs34.existsSync)(file)) return;
21048
21269
  let settings;
21049
21270
  try {
21050
21271
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -21092,14 +21313,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
21092
21313
  }
21093
21314
  if (toAppend.length > 0) {
21094
21315
  const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
21095
- (0, import_node_fs33.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
21316
+ (0, import_node_fs34.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
21096
21317
  }
21097
- (0, import_node_fs33.rmSync)(gateCreds, { force: true });
21318
+ (0, import_node_fs34.rmSync)(gateCreds, { force: true });
21098
21319
  return toAppend.length;
21099
21320
  }
21100
21321
  function readFileSyncSafe(path) {
21101
21322
  try {
21102
- return (0, import_node_fs33.readFileSync)(path, "utf-8");
21323
+ return (0, import_node_fs34.readFileSync)(path, "utf-8");
21103
21324
  } catch {
21104
21325
  return "";
21105
21326
  }
@@ -21114,35 +21335,35 @@ function hasStagedChanges(root) {
21114
21335
  }
21115
21336
  function moveDir(from, to) {
21116
21337
  try {
21117
- (0, import_node_fs33.renameSync)(from, to);
21338
+ (0, import_node_fs34.renameSync)(from, to);
21118
21339
  } catch (err) {
21119
21340
  if (err.code !== "EXDEV") throw err;
21120
- (0, import_node_fs33.cpSync)(from, to, { recursive: true });
21121
- (0, import_node_fs33.rmSync)(from, { recursive: true, force: true });
21341
+ (0, import_node_fs34.cpSync)(from, to, { recursive: true });
21342
+ (0, import_node_fs34.rmSync)(from, { recursive: true, force: true });
21122
21343
  }
21123
21344
  }
21124
21345
  function moveFile(from, to) {
21125
21346
  try {
21126
- (0, import_node_fs33.renameSync)(from, to);
21347
+ (0, import_node_fs34.renameSync)(from, to);
21127
21348
  } catch (err) {
21128
21349
  if (err.code !== "EXDEV") throw err;
21129
- (0, import_node_fs33.cpSync)(from, to);
21130
- (0, import_node_fs33.rmSync)(from, { force: true });
21350
+ (0, import_node_fs34.cpSync)(from, to);
21351
+ (0, import_node_fs34.rmSync)(from, { force: true });
21131
21352
  }
21132
21353
  }
21133
21354
  function carryLegacyContents(gateDir, verityDir) {
21134
21355
  let copied = 0;
21135
21356
  const walk = (relDir) => {
21136
21357
  const srcDir = (0, import_node_path25.join)(gateDir, relDir);
21137
- for (const entry of (0, import_node_fs33.readdirSync)(srcDir)) {
21358
+ for (const entry of (0, import_node_fs34.readdirSync)(srcDir)) {
21138
21359
  const rel = relDir ? (0, import_node_path25.join)(relDir, entry) : entry;
21139
21360
  const src = (0, import_node_path25.join)(gateDir, rel);
21140
21361
  const dest = (0, import_node_path25.join)(verityDir, rel);
21141
- if ((0, import_node_fs33.statSync)(src).isDirectory()) {
21362
+ if ((0, import_node_fs34.statSync)(src).isDirectory()) {
21142
21363
  walk(rel);
21143
- } else if (!(0, import_node_fs33.existsSync)(dest)) {
21144
- (0, import_node_fs33.mkdirSync)((0, import_node_path25.dirname)(dest), { recursive: true });
21145
- (0, import_node_fs33.cpSync)(src, dest);
21364
+ } else if (!(0, import_node_fs34.existsSync)(dest)) {
21365
+ (0, import_node_fs34.mkdirSync)((0, import_node_path25.dirname)(dest), { recursive: true });
21366
+ (0, import_node_fs34.cpSync)(src, dest);
21146
21367
  copied++;
21147
21368
  }
21148
21369
  }
@@ -21153,20 +21374,20 @@ function carryLegacyContents(gateDir, verityDir) {
21153
21374
  async function needsMigration(root = repoRoot()) {
21154
21375
  const gateDir = (0, import_node_path25.join)(root, ".gate");
21155
21376
  const verityDir = (0, import_node_path25.join)(root, ".verity");
21156
- if ((0, import_node_fs33.existsSync)(gateDir) && !(0, import_node_fs33.existsSync)(verityDir)) return true;
21157
- if ((0, import_node_fs33.existsSync)(gateDir) && (0, import_node_fs33.existsSync)(verityDir)) {
21158
- if ((0, import_node_fs33.existsSync)((0, import_node_path25.join)(gateDir, "credentials")) && !(0, import_node_fs33.existsSync)((0, import_node_path25.join)(verityDir, "credentials"))) {
21377
+ if ((0, import_node_fs34.existsSync)(gateDir) && !(0, import_node_fs34.existsSync)(verityDir)) return true;
21378
+ if ((0, import_node_fs34.existsSync)(gateDir) && (0, import_node_fs34.existsSync)(verityDir)) {
21379
+ if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(gateDir, "credentials")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(verityDir, "credentials"))) {
21159
21380
  return true;
21160
21381
  }
21161
- if ((0, import_node_fs33.existsSync)((0, import_node_path25.join)(gateDir, "memory")) && !(0, import_node_fs33.existsSync)((0, import_node_path25.join)(verityDir, "memory"))) {
21382
+ if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(gateDir, "memory")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(verityDir, "memory"))) {
21162
21383
  return true;
21163
21384
  }
21164
21385
  }
21165
21386
  const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
21166
- if ((0, import_node_fs33.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
21387
+ if ((0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
21167
21388
  return true;
21168
21389
  }
21169
- if ((0, import_node_fs33.existsSync)((0, import_node_path25.join)(root, "GATE.md")) && !(0, import_node_fs33.existsSync)((0, import_node_path25.join)(root, "VERITY.md"))) {
21390
+ if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(root, "GATE.md")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(root, "VERITY.md"))) {
21170
21391
  return true;
21171
21392
  }
21172
21393
  if (await hasLegacyHooksAt(root)) return true;
@@ -21308,7 +21529,7 @@ function resolveDataDir() {
21308
21529
  // local dev: running from repo root
21309
21530
  ];
21310
21531
  for (const candidate of candidates) {
21311
- if ((0, import_node_fs34.existsSync)((0, import_node_path26.join)(candidate, "skills"))) {
21532
+ if ((0, import_node_fs35.existsSync)((0, import_node_path26.join)(candidate, "skills"))) {
21312
21533
  return candidate;
21313
21534
  }
21314
21535
  }
@@ -21324,7 +21545,7 @@ function registerInitCommand(program2) {
21324
21545
  program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
21325
21546
  const force = opts.force ?? false;
21326
21547
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
21327
- const isProject = projectMarkers.some((m) => (0, import_node_fs34.existsSync)(m));
21548
+ const isProject = projectMarkers.some((m) => (0, import_node_fs35.existsSync)(m));
21328
21549
  if (!isProject) {
21329
21550
  printError("No project detected in the current directory.");
21330
21551
  printInfo('Run "verity init" from your project root.');
@@ -21394,14 +21615,14 @@ function registerInitCommand(program2) {
21394
21615
  for (const skill of skills) {
21395
21616
  const src = (0, import_node_path26.join)(skillsSource, skill);
21396
21617
  const dest = (0, import_node_path26.join)(skillsDest, skill);
21397
- if (!(0, import_node_fs34.existsSync)(src)) {
21618
+ if (!(0, import_node_fs35.existsSync)(src)) {
21398
21619
  printWarn(` Skill data not found: ${skill}`);
21399
21620
  continue;
21400
21621
  }
21401
- if ((0, import_node_fs34.existsSync)(dest) && !force) {
21622
+ if ((0, import_node_fs35.existsSync)(dest) && !force) {
21402
21623
  const srcSkill = (0, import_node_path26.join)(src, "SKILL.md");
21403
21624
  const destSkill = (0, import_node_path26.join)(dest, "SKILL.md");
21404
- if ((0, import_node_fs34.existsSync)(destSkill)) {
21625
+ if ((0, import_node_fs35.existsSync)(destSkill)) {
21405
21626
  try {
21406
21627
  const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
21407
21628
  const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
@@ -21473,7 +21694,7 @@ function registerInitCommand(program2) {
21473
21694
  }
21474
21695
 
21475
21696
  // src/commands/uninstall.ts
21476
- var import_node_fs35 = require("node:fs");
21697
+ var import_node_fs36 = require("node:fs");
21477
21698
  var import_node_path27 = require("node:path");
21478
21699
  var SKILL_NAMES = [
21479
21700
  "verity-setup",
@@ -21494,10 +21715,10 @@ function registerUninstallCommand(program2) {
21494
21715
  const skillsRoot = projectPath(".claude/skills");
21495
21716
  for (const name of SKILL_NAMES) {
21496
21717
  const dir = (0, import_node_path27.join)(skillsRoot, name);
21497
- if ((0, import_node_fs35.existsSync)(dir)) {
21718
+ if ((0, import_node_fs36.existsSync)(dir)) {
21498
21719
  actions.push({
21499
21720
  label: `Remove .claude/skills/${name}/`,
21500
- apply: () => (0, import_node_fs35.rmSync)(dir, { recursive: true, force: true })
21721
+ apply: () => (0, import_node_fs36.rmSync)(dir, { recursive: true, force: true })
21501
21722
  });
21502
21723
  }
21503
21724
  }
@@ -21511,24 +21732,24 @@ function registerUninstallCommand(program2) {
21511
21732
  });
21512
21733
  }
21513
21734
  const verityDir = projectPath(VERITY_DIR);
21514
- if ((0, import_node_fs35.existsSync)(verityDir)) {
21735
+ if ((0, import_node_fs36.existsSync)(verityDir)) {
21515
21736
  actions.push({
21516
21737
  label: `Remove ${VERITY_DIR}/`,
21517
- apply: () => (0, import_node_fs35.rmSync)(verityDir, { recursive: true, force: true })
21738
+ apply: () => (0, import_node_fs36.rmSync)(verityDir, { recursive: true, force: true })
21518
21739
  });
21519
21740
  }
21520
21741
  if (!keepVerityMd) {
21521
21742
  const verityMd = projectPath(VERITY_MD_FILE);
21522
- if ((0, import_node_fs35.existsSync)(verityMd)) {
21743
+ if ((0, import_node_fs36.existsSync)(verityMd)) {
21523
21744
  actions.push({
21524
21745
  label: `Remove ${VERITY_MD_FILE}`,
21525
- apply: () => (0, import_node_fs35.rmSync)(verityMd, { force: true })
21746
+ apply: () => (0, import_node_fs36.rmSync)(verityMd, { force: true })
21526
21747
  });
21527
21748
  }
21528
21749
  }
21529
21750
  const cleanupEmptyDir = (path) => {
21530
- if ((0, import_node_fs35.existsSync)(path) && (0, import_node_fs35.statSync)(path).isDirectory() && (0, import_node_fs35.readdirSync)(path).length === 0) {
21531
- (0, import_node_fs35.rmdirSync)(path);
21751
+ if ((0, import_node_fs36.existsSync)(path) && (0, import_node_fs36.statSync)(path).isDirectory() && (0, import_node_fs36.readdirSync)(path).length === 0) {
21752
+ (0, import_node_fs36.rmdirSync)(path);
21532
21753
  }
21533
21754
  };
21534
21755
  actions.push({
@@ -21540,10 +21761,10 @@ function registerUninstallCommand(program2) {
21540
21761
  });
21541
21762
  const home = process.env.HOME ?? "";
21542
21763
  const globalVerityDir = (0, import_node_path27.join)(home, ".verity");
21543
- if (purgeGlobal && (0, import_node_fs35.existsSync)(globalVerityDir)) {
21764
+ if (purgeGlobal && (0, import_node_fs36.existsSync)(globalVerityDir)) {
21544
21765
  actions.push({
21545
21766
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
21546
- apply: () => (0, import_node_fs35.rmSync)(globalVerityDir, { recursive: true, force: true })
21767
+ apply: () => (0, import_node_fs36.rmSync)(globalVerityDir, { recursive: true, force: true })
21547
21768
  });
21548
21769
  }
21549
21770
  if (actions.length === 0) {
@@ -21737,7 +21958,7 @@ function registerTaskCommands(program2) {
21737
21958
  }
21738
21959
 
21739
21960
  // src/commands/reset.ts
21740
- var import_node_fs36 = require("node:fs");
21961
+ var import_node_fs37 = require("node:fs");
21741
21962
  var import_node_path28 = require("node:path");
21742
21963
  function registerResetCommand(program2) {
21743
21964
  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) => {
@@ -21775,11 +21996,11 @@ function registerResetCommand(program2) {
21775
21996
  }
21776
21997
  const cacheDir = projectPath(CACHE_DIR);
21777
21998
  let purged = 0;
21778
- if ((0, import_node_fs36.existsSync)(cacheDir)) {
21779
- for (const entry of (0, import_node_fs36.readdirSync)(cacheDir)) {
21999
+ if ((0, import_node_fs37.existsSync)(cacheDir)) {
22000
+ for (const entry of (0, import_node_fs37.readdirSync)(cacheDir)) {
21780
22001
  if (entry.startsWith("pending-")) {
21781
22002
  try {
21782
- (0, import_node_fs36.unlinkSync)((0, import_node_path28.join)(cacheDir, entry));
22003
+ (0, import_node_fs37.unlinkSync)((0, import_node_path28.join)(cacheDir, entry));
21783
22004
  purged++;
21784
22005
  } catch {
21785
22006
  }
@@ -21794,19 +22015,19 @@ function registerResetCommand(program2) {
21794
22015
  projectPath(`${VERITY_DIR}/.last-analysis`)
21795
22016
  ];
21796
22017
  for (const file of filesToClear) {
21797
- if ((0, import_node_fs36.existsSync)(file)) {
22018
+ if ((0, import_node_fs37.existsSync)(file)) {
21798
22019
  try {
21799
- (0, import_node_fs36.writeFileSync)(file, "");
22020
+ (0, import_node_fs37.writeFileSync)(file, "");
21800
22021
  } catch {
21801
22022
  }
21802
22023
  }
21803
22024
  }
21804
22025
  if (opts.all) {
21805
22026
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
21806
- if ((0, import_node_fs36.existsSync)(logsDir)) {
21807
- for (const entry of (0, import_node_fs36.readdirSync)(logsDir)) {
22027
+ if ((0, import_node_fs37.existsSync)(logsDir)) {
22028
+ for (const entry of (0, import_node_fs37.readdirSync)(logsDir)) {
21808
22029
  try {
21809
- (0, import_node_fs36.unlinkSync)((0, import_node_path28.join)(logsDir, entry));
22030
+ (0, import_node_fs37.unlinkSync)((0, import_node_path28.join)(logsDir, entry));
21810
22031
  } catch {
21811
22032
  }
21812
22033
  }
@@ -22114,8 +22335,8 @@ function registerTelemetryCommands(program2) {
22114
22335
  }
22115
22336
 
22116
22337
  // src/cli.ts
22117
- program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.d71617f").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) => {
22118
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.d71617f");
22338
+ program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.d8678a8").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) => {
22339
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.d8678a8");
22119
22340
  setUserNamedServiceUrl(program.opts().serviceUrl);
22120
22341
  try {
22121
22342
  await foldLegacyLocalCredential();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.29.4-experimental.d71617f",
3
+ "version": "0.29.4-experimental.d8678a8",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",