@codacy/verity-cli 0.29.4-experimental.cfb4bd2 → 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 +357 -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";
@@ -16719,7 +16720,7 @@ function formatRunEvidence(run, startedAt) {
16719
16720
  const md = run.modeDecision;
16720
16721
  if (md) {
16721
16722
  const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
16722
- 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"}`);
16723
16724
  } else {
16724
16725
  out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
16725
16726
  }
@@ -17157,6 +17158,29 @@ function renderItem(label2, text, patternId, file, line) {
17157
17158
  const id = patternId ? ` [${patternId}]` : "";
17158
17159
  return `- ${label2}${text}${where}${id}`;
17159
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
+ }
17160
17184
  function buildAgentContext(input) {
17161
17185
  const lines = [];
17162
17186
  if (input.reviewStatus === "not_reviewed") {
@@ -17242,7 +17266,7 @@ function channelSilence(input) {
17242
17266
  // src/lib/cli-version.ts
17243
17267
  function cliVersion() {
17244
17268
  try {
17245
- return true ? "0.29.4-experimental.cfb4bd2" : "dev";
17269
+ return true ? "0.29.4-experimental.d8678a8" : "dev";
17246
17270
  } catch {
17247
17271
  return "dev";
17248
17272
  }
@@ -18059,26 +18083,50 @@ function narrowToRecent(files, sessionId) {
18059
18083
  });
18060
18084
  return recent.length > 0 ? recent : files;
18061
18085
  }
18062
- function readIterationState(currentCommit) {
18063
- 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;
18064
18093
  try {
18065
18094
  const stored = (0, import_node_fs22.readFileSync)(ITERATION_FILE, "utf-8").trim();
18066
- const parts = stored.split(":");
18067
- const iter = parseInt(parts[0], 10);
18068
- const storedCommit = parts[1] ?? "";
18069
- const storedTimestamp = parseInt(parts[2] ?? "0", 10);
18070
- const fingerprint = parts.slice(3).join(":") || null;
18071
- if (isNaN(iter)) return { iteration: 1, fingerprint: null };
18072
- if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
18073
- if (storedTimestamp > 0) {
18074
- const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
18075
- if (elapsed > 600) return { iteration: 1, fingerprint: null };
18076
- }
18077
- 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 };
18078
18100
  } catch {
18079
- return { iteration: 1, fingerprint: null };
18101
+ return NO_BLOCKS;
18080
18102
  }
18081
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
+ }
18082
18130
  function findingsFingerprint(findings) {
18083
18131
  const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
18084
18132
  return [...new Set(keys)].sort().join(",");
@@ -18088,11 +18136,22 @@ function isSameProblem(previous, current) {
18088
18136
  const prev = new Set(previous.split(","));
18089
18137
  return current.split(",").some((k) => prev.has(k));
18090
18138
  }
18091
- function writeIteration(iteration, commit, _contentHash, fingerprint) {
18139
+ function writeBlockState(commit, state) {
18092
18140
  (0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
18093
- const ts = Math.floor(Date.now() / 1e3);
18094
- const fp = fingerprint ? `:${fingerprint}` : "";
18095
- (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 });
18096
18155
  }
18097
18156
 
18098
18157
  // src/lib/fold.ts
@@ -18284,8 +18343,10 @@ function fold(transcriptPath, opts = {}) {
18284
18343
  subagentSkipped: 0,
18285
18344
  compactions: 0,
18286
18345
  complete: false
18287
- }
18346
+ },
18347
+ planApproval: { approvals: 0, activeSinceLastPrompt: false }
18288
18348
  };
18349
+ const flow = { seq: 0, lastPrompt: -1, lastApproval: -1, approvals: 0 };
18289
18350
  const byPath = /* @__PURE__ */ new Map();
18290
18351
  const commandStats = /* @__PURE__ */ new Map();
18291
18352
  const pendingByToolUse = /* @__PURE__ */ new Map();
@@ -18311,8 +18372,12 @@ function fold(transcriptPath, opts = {}) {
18311
18372
  if (type === "system" && record.subtype === "compact_boundary") {
18312
18373
  result.coverage.compactions++;
18313
18374
  }
18314
- if (type === "user" && hasUserText(record)) result.coverage.userMessages++;
18315
- 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);
18316
18381
  }
18317
18382
  };
18318
18383
  try {
@@ -18384,6 +18449,10 @@ function fold(transcriptPath, opts = {}) {
18384
18449
  if (!p || authoredPaths.has(p)) continue;
18385
18450
  result.unobserved.push({ p, cause: classifyUnobserved(raw) });
18386
18451
  }
18452
+ result.planApproval = {
18453
+ approvals: flow.approvals,
18454
+ activeSinceLastPrompt: flow.lastApproval >= 0 && flow.lastApproval > flow.lastPrompt
18455
+ };
18387
18456
  return result;
18388
18457
  }
18389
18458
  function classifyUnobserved(path) {
@@ -18396,7 +18465,7 @@ function classifyUnobserved(path) {
18396
18465
  if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
18397
18466
  return "no_edit_record";
18398
18467
  }
18399
- 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) {
18400
18469
  const message = record.message;
18401
18470
  const content = message?.content ?? record.content;
18402
18471
  const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
@@ -18480,6 +18549,13 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
18480
18549
  }
18481
18550
  }
18482
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
+ }
18483
18559
  if (toolName) {
18484
18560
  pendingToolName.delete(id);
18485
18561
  const prevTool = toolStats.get(toolName);
@@ -18537,8 +18613,13 @@ function checkConservation(changedFiles, result, repoRoot2) {
18537
18613
  // src/commands/analyze/phases/06-evidence.ts
18538
18614
  async function evidence(run) {
18539
18615
  const { opts } = run;
18540
- 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;
18541
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;
18542
18623
  let staticResults = {
18543
18624
  tool: "@codacy/analysis-cli",
18544
18625
  findings: [],
@@ -18558,8 +18639,9 @@ async function evidence(run) {
18558
18639
  const debounceSeconds = parseInt(opts.debounce, 10);
18559
18640
  const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
18560
18641
  if (debounceSkip) {
18561
- if (assistantResponse) {
18642
+ if (planWorthy) {
18562
18643
  analysisMode = "plan";
18644
+ recordFlip("debounce");
18563
18645
  } else {
18564
18646
  await passAndExit(run, debounceSkip, "debounce");
18565
18647
  }
@@ -18568,8 +18650,9 @@ async function evidence(run) {
18568
18650
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
18569
18651
  const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
18570
18652
  if (mtimeSkip) {
18571
- if (assistantResponse) {
18653
+ if (planWorthy) {
18572
18654
  analysisMode = "plan";
18655
+ recordFlip("mtime");
18573
18656
  } else {
18574
18657
  await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
18575
18658
  }
@@ -18580,8 +18663,9 @@ async function evidence(run) {
18580
18663
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
18581
18664
  const hashResult = checkContentHash(allCheckable, baselineSessionId);
18582
18665
  if (hashResult.skip) {
18583
- if (assistantResponse) {
18666
+ if (planWorthy) {
18584
18667
  analysisMode = "plan";
18668
+ recordFlip("content-hash");
18585
18669
  } else {
18586
18670
  await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
18587
18671
  }
@@ -18643,8 +18727,9 @@ async function evidence(run) {
18643
18727
  maxTotalBytes: parseInt(opts.maxTotalSize, 10)
18644
18728
  });
18645
18729
  if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
18646
- if (assistantResponse) {
18730
+ if (planWorthy) {
18647
18731
  analysisMode = "plan";
18732
+ recordFlip("empty-after-scoping");
18648
18733
  } else {
18649
18734
  await passAndExit(
18650
18735
  run,
@@ -18664,13 +18749,13 @@ async function evidence(run) {
18664
18749
  snapshotResult = generateSnapshotDiffs(codeDelta.files);
18665
18750
  }
18666
18751
  currentCommit = getCurrentCommit();
18667
- iteration = readIterationState(currentCommit).iteration;
18752
+ iteration = readIteration(currentCommit);
18668
18753
  }
18669
18754
  }
18670
18755
  if (analysisMode === "plan") {
18671
18756
  recordAnalysisStart();
18672
18757
  currentCommit = getCurrentCommit();
18673
- iteration = readIterationState(currentCommit).iteration;
18758
+ iteration = readIteration(currentCommit);
18674
18759
  }
18675
18760
  Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
18676
18761
  }
@@ -19336,6 +19421,54 @@ async function workingMemory(run) {
19336
19421
  Object.assign(run, { incrementReport, memory, memorySession, reachability });
19337
19422
  }
19338
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
+
19339
19472
  // src/lib/run-mode.ts
19340
19473
  function parseAutonomousEnv(raw) {
19341
19474
  if (raw === void 0) return void 0;
@@ -19429,7 +19562,13 @@ async function buildRequest(run) {
19429
19562
  excluded_by_reason: excludedByReason,
19430
19563
  // was the transcript itself truncated? The 256 KB window means "this turn"
19431
19564
  // can quietly mean "the last 256 KB of it".
19432
- 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
19433
19572
  };
19434
19573
  const requestBody = {
19435
19574
  coverage_telemetry: coverageTelemetry,
@@ -19568,7 +19707,8 @@ async function buildRequest(run) {
19568
19707
  }
19569
19708
  const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
19570
19709
  const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
19571
- 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;
19572
19712
  if (hasIntent) {
19573
19713
  const intentContext = {};
19574
19714
  if (conversation && conversation.prompts.length > 0) {
@@ -19600,6 +19740,10 @@ async function buildRequest(run) {
19600
19740
  intentContext.user_prompt = w4Task.goal;
19601
19741
  logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
19602
19742
  }
19743
+ if (planApprovalActive) {
19744
+ intentContext.plan_approved = true;
19745
+ logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
19746
+ }
19603
19747
  if (assistantResponse) {
19604
19748
  const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
19605
19749
  intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
@@ -19619,14 +19763,14 @@ async function buildRequest(run) {
19619
19763
  }
19620
19764
 
19621
19765
  // src/lib/offline.ts
19622
- var import_node_fs28 = require("node:fs");
19766
+ var import_node_fs29 = require("node:fs");
19623
19767
  var import_node_crypto11 = require("node:crypto");
19624
19768
  function cacheRequest(body) {
19625
19769
  try {
19626
- (0, import_node_fs28.mkdirSync)(CACHE_DIR, { recursive: true });
19770
+ (0, import_node_fs29.mkdirSync)(CACHE_DIR, { recursive: true });
19627
19771
  const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
19628
19772
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
19629
- (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)));
19630
19774
  } catch {
19631
19775
  }
19632
19776
  }
@@ -19745,10 +19889,10 @@ async function transmit(run) {
19745
19889
  }
19746
19890
 
19747
19891
  // src/commands/analyze/phases/13-reconcile.ts
19748
- var import_node_fs29 = require("node:fs");
19892
+ var import_node_fs30 = require("node:fs");
19749
19893
  var import_node_path23 = require("node:path");
19750
19894
  async function reconcile(run) {
19751
- 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;
19752
19896
  const sentPaths = codeDelta.files.map((f) => f.path);
19753
19897
  let openElsewhere = [];
19754
19898
  if (memorySession) {
@@ -19756,7 +19900,7 @@ async function reconcile(run) {
19756
19900
  const st = foldDossier(memorySession.d);
19757
19901
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
19758
19902
  try {
19759
- 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");
19760
19904
  const at = src[line - 1];
19761
19905
  return at === void 0 ? null : lineSha(at);
19762
19906
  } catch {
@@ -19848,6 +19992,29 @@ async function reconcile(run) {
19848
19992
  decision
19849
19993
  });
19850
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
+ );
19851
20018
  let intentRepeatCount = 0;
19852
20019
  const priorPendingFingerprints = memorySession ? (() => {
19853
20020
  try {
@@ -19934,6 +20101,39 @@ ${YELLOW2}${note}${NC2}
19934
20101
  return exit(0);
19935
20102
  }
19936
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
+
19937
20137
  // src/lib/remediation-guard.ts
19938
20138
  var TOOL_CONFIG_PATTERNS = [
19939
20139
  /(^|\/)\.codacy\//,
@@ -19976,19 +20176,7 @@ function screenRemediation(fix, findingFile) {
19976
20176
 
19977
20177
  // src/commands/analyze/phases/14-render.ts
19978
20178
  function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
19979
- const metadata = response.metadata ?? {};
19980
- const intent = response.intent_alignment ?? {};
19981
- return buildAgentContext({
19982
- intentRepeat,
19983
- priorPendingFingerprints,
19984
- gateDecision: String(response.gate_decision ?? ""),
19985
- findings: response.findings ?? [],
19986
- pendingItems: response.pending_items ?? [],
19987
- reviewStatus: metadata.review_status,
19988
- coverage: metadata.coverage,
19989
- intentVerdict: intent.verdict,
19990
- intentGaps: intent.gaps
19991
- });
20179
+ return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
19992
20180
  }
19993
20181
  async function render(run) {
19994
20182
  const { opts, globals } = run;
@@ -20082,38 +20270,63 @@ async function render(run) {
20082
20270
  reverify_by: response.reverify_by
20083
20271
  });
20084
20272
  const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
20085
- let capReleased = false;
20273
+ let release = null;
20086
20274
  let effectiveDecision = decision;
20087
20275
  if (decision === "FAIL") {
20088
- const blocking = (response.findings ?? []).filter((f) => {
20276
+ const findings = response.findings ?? [];
20277
+ const blocking = findings.filter((f) => {
20089
20278
  const sev = String(f.severity ?? "").toLowerCase();
20090
20279
  return sev === "critical" || sev === "high";
20091
20280
  });
20092
20281
  const fingerprint = findingsFingerprint(blocking);
20093
- const prior = readIterationState(currentCommit);
20282
+ const prior = readBlockState(currentCommit, {
20283
+ newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
20284
+ });
20094
20285
  const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
20095
- const nextIteration = sameProblem ? prior.iteration + 1 : 1;
20096
20286
  const maxIterations = parseInt(opts.maxIterations, 10);
20097
- writeIteration(nextIteration, currentCommit, contentHash ?? void 0, fingerprint);
20098
- iteration = nextIteration;
20099
- if (nextIteration > maxIterations) {
20100
- 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;
20101
20302
  effectiveDecision = "WARN";
20102
- 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
+ });
20103
20311
  }
20104
20312
  }
20105
- if (capReleased) {
20313
+ if (release) {
20106
20314
  const findings = response.findings ?? [];
20107
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
+ });
20108
20320
  emitVerdict({
20109
20321
  proposed: "WARN",
20110
20322
  changed: run.changedUniverse,
20111
20323
  coverage: reviewCoverage,
20112
- 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.
20113
- ${lines.join("\n")}`,
20324
+ userSummary: lines.length > 0 ? `${summary}
20325
+ ${lines.join("\n")}` : summary,
20114
20326
  agentContext: null,
20115
20327
  silenced: true
20116
20328
  });
20329
+ return;
20117
20330
  }
20118
20331
  switch (effectiveDecision) {
20119
20332
  case "FAIL": {
@@ -20212,7 +20425,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
20212
20425
  break;
20213
20426
  }
20214
20427
  case "PASS": {
20215
- writeIteration(1, currentCommit, contentHash ?? void 0);
20428
+ resetBlockState(currentCommit);
20216
20429
  if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
20217
20430
  if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
20218
20431
  let userSummary = response.user_summary ?? "Verity: PASS";
@@ -20233,6 +20446,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
20233
20446
  break;
20234
20447
  }
20235
20448
  case "WARN": {
20449
+ if (decision !== "FAIL") resetBlockState(currentCommit);
20236
20450
  if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
20237
20451
  if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
20238
20452
  let userSummary = response.user_summary ?? "Verity: WARN";
@@ -20331,7 +20545,7 @@ async function runAnalyze(opts, globals) {
20331
20545
  }
20332
20546
 
20333
20547
  // src/commands/baseline.ts
20334
- var import_node_fs30 = require("node:fs");
20548
+ var import_node_fs31 = require("node:fs");
20335
20549
  function registerBaselineCommands(program2) {
20336
20550
  const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
20337
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) => {
@@ -20340,7 +20554,7 @@ function registerBaselineCommands(program2) {
20340
20554
  process.chdir(repoRoot());
20341
20555
  } catch {
20342
20556
  }
20343
- if (!(0, import_node_fs30.existsSync)(VERITY_DIR)) {
20557
+ if (!(0, import_node_fs31.existsSync)(VERITY_DIR)) {
20344
20558
  process.exit(0);
20345
20559
  }
20346
20560
  let sessionId = opts.sessionId;
@@ -20380,7 +20594,7 @@ async function readStdin() {
20380
20594
  }
20381
20595
 
20382
20596
  // src/commands/review.ts
20383
- var import_node_fs31 = require("node:fs");
20597
+ var import_node_fs32 = require("node:fs");
20384
20598
  function registerReviewCommand(program2) {
20385
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) => {
20386
20600
  const globals = program2.opts();
@@ -20399,7 +20613,7 @@ async function runReview(opts, globals) {
20399
20613
  const securityFiles = filterSecurity(allFiles);
20400
20614
  let staticResults;
20401
20615
  if (isCodacyAvailable()) {
20402
- 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);
20403
20617
  staticResults = runCodacyAnalysis(scannable);
20404
20618
  } else {
20405
20619
  staticResults = {
@@ -20425,10 +20639,10 @@ async function runReview(opts, globals) {
20425
20639
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
20426
20640
  specs = [];
20427
20641
  for (const p of specPaths) {
20428
- if (!(0, import_node_fs31.existsSync)(p)) continue;
20642
+ if (!(0, import_node_fs32.existsSync)(p)) continue;
20429
20643
  try {
20430
- const { readFileSync: readFileSync18 } = await import("node:fs");
20431
- const content = readFileSync18(p, "utf-8");
20644
+ const { readFileSync: readFileSync19 } = await import("node:fs");
20645
+ const content = readFileSync19(p, "utf-8");
20432
20646
  specs.push({ path: p, content: content.slice(0, 10240) });
20433
20647
  } catch {
20434
20648
  }
@@ -20485,7 +20699,7 @@ async function runReview(opts, globals) {
20485
20699
  }
20486
20700
 
20487
20701
  // src/commands/guard.ts
20488
- var import_node_fs32 = require("node:fs");
20702
+ var import_node_fs33 = require("node:fs");
20489
20703
  var import_node_path24 = require("node:path");
20490
20704
  var GUARD_BLOCK_CAP = 2;
20491
20705
  var GUARD_ITER_FILE = (0, import_node_path24.join)(VERITY_DIR, ".guard-iteration");
@@ -20552,7 +20766,7 @@ function classifyCommand2(command, on) {
20552
20766
  }
20553
20767
  function readIterMap() {
20554
20768
  try {
20555
- 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"));
20556
20770
  if (raw && typeof raw === "object") {
20557
20771
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
20558
20772
  return { [raw.moment]: raw.count };
@@ -20572,10 +20786,10 @@ function readIter(moment) {
20572
20786
  }
20573
20787
  function writeIter(moment, count) {
20574
20788
  try {
20575
- (0, import_node_fs32.mkdirSync)(VERITY_DIR, { recursive: true });
20789
+ (0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
20576
20790
  const map = readIterMap();
20577
20791
  map[moment] = count;
20578
- (0, import_node_fs32.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20792
+ (0, import_node_fs33.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
20579
20793
  } catch {
20580
20794
  }
20581
20795
  }
@@ -20585,10 +20799,10 @@ function resetIter(moment) {
20585
20799
  if (!(moment in map)) return;
20586
20800
  delete map[moment];
20587
20801
  if (Object.keys(map).length === 0) {
20588
- 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);
20589
20803
  } else {
20590
- (0, import_node_fs32.mkdirSync)(VERITY_DIR, { recursive: true });
20591
- (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));
20592
20806
  }
20593
20807
  } catch {
20594
20808
  }
@@ -20652,7 +20866,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
20652
20866
  const securityFiles = filterSecurity(files);
20653
20867
  let staticResults;
20654
20868
  if (isCodacyAvailable()) {
20655
- 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);
20656
20870
  staticResults = runCodacyAnalysis(scannable);
20657
20871
  } else {
20658
20872
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -20697,7 +20911,7 @@ function emitAllowNotice(userMsg, agentMsg) {
20697
20911
  async function runGuard(opts, globals) {
20698
20912
  const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
20699
20913
  const { command, cwd, sessionId } = await readPreToolUseStdin();
20700
- if (cwd && (0, import_node_fs32.existsSync)(cwd)) {
20914
+ if (cwd && (0, import_node_fs33.existsSync)(cwd)) {
20701
20915
  try {
20702
20916
  process.chdir(cwd);
20703
20917
  } catch {
@@ -20803,14 +21017,14 @@ function writeBlockMessage(moment, response) {
20803
21017
  }
20804
21018
 
20805
21019
  // src/commands/init.ts
20806
- var import_node_fs34 = require("node:fs");
21020
+ var import_node_fs35 = require("node:fs");
20807
21021
  var import_promises13 = require("node:fs/promises");
20808
21022
  var import_node_path26 = require("node:path");
20809
21023
  var import_node_child_process10 = require("node:child_process");
20810
21024
  var readline2 = __toESM(require("node:readline/promises"));
20811
21025
 
20812
21026
  // src/commands/migrate.ts
20813
- var import_node_fs33 = require("node:fs");
21027
+ var import_node_fs34 = require("node:fs");
20814
21028
  var import_node_path25 = require("node:path");
20815
21029
  var import_node_child_process9 = require("node:child_process");
20816
21030
 
@@ -20942,10 +21156,10 @@ async function runMigration(opts = {}) {
20942
21156
  function migrateProjectDir(root, actions) {
20943
21157
  const gateDir = (0, import_node_path25.join)(root, ".gate");
20944
21158
  const verityDir = (0, import_node_path25.join)(root, ".verity");
20945
- 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)) {
20946
21160
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
20947
21161
  }
20948
- 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)) {
20949
21163
  return migrateProjectDirCarry(gateDir, verityDir, actions);
20950
21164
  }
20951
21165
  return false;
@@ -20966,13 +21180,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
20966
21180
  }
20967
21181
  }
20968
21182
  if (moved) {
20969
- if ((0, import_node_fs33.existsSync)(gateDir)) {
21183
+ if ((0, import_node_fs34.existsSync)(gateDir)) {
20970
21184
  const carried = carryLegacyContents(gateDir, verityDir);
20971
21185
  if (carried > 0) {
20972
21186
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
20973
21187
  }
20974
21188
  try {
20975
- (0, import_node_fs33.rmSync)(gateDir, { recursive: true, force: true });
21189
+ (0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
20976
21190
  } catch {
20977
21191
  }
20978
21192
  }
@@ -20988,7 +21202,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
20988
21202
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
20989
21203
  }
20990
21204
  try {
20991
- (0, import_node_fs33.rmSync)(gateDir, { recursive: true, force: true });
21205
+ (0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
20992
21206
  } catch {
20993
21207
  }
20994
21208
  return carried > 0;
@@ -20997,9 +21211,9 @@ function migrateGlobalCredentials(home, actions) {
20997
21211
  if (!home) return;
20998
21212
  const gateCreds = (0, import_node_path25.join)(home, ".gate", "credentials");
20999
21213
  const verityCreds = (0, import_node_path25.join)(home, ".verity", "credentials");
21000
- if (!(0, import_node_fs33.existsSync)(gateCreds)) return;
21001
- if (!(0, import_node_fs33.existsSync)(verityCreds)) {
21002
- (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 });
21003
21217
  moveFile(gateCreds, verityCreds);
21004
21218
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
21005
21219
  return;
@@ -21022,7 +21236,7 @@ async function migrateLegacyHooks(root, actions) {
21022
21236
  }
21023
21237
  async function migrateClaudeMd(root, actions) {
21024
21238
  const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
21025
- const hadLegacyBlock = (0, import_node_fs33.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
21239
+ const hadLegacyBlock = (0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
21026
21240
  if (!hadLegacyBlock) return;
21027
21241
  try {
21028
21242
  await ensureClaudeMdPointer(root);
@@ -21034,7 +21248,7 @@ async function migrateClaudeMd(root, actions) {
21034
21248
  function migrateStandardFile(root, actions) {
21035
21249
  const gateMd = (0, import_node_path25.join)(root, "GATE.md");
21036
21250
  const verityMd = (0, import_node_path25.join)(root, "VERITY.md");
21037
- 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;
21038
21252
  let moved = false;
21039
21253
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
21040
21254
  try {
@@ -21046,12 +21260,12 @@ function migrateStandardFile(root, actions) {
21046
21260
  if (!moved) moveFile(gateMd, verityMd);
21047
21261
  const content = readFileSyncSafe(verityMd);
21048
21262
  const refreshed = content.split("GATE.md").join("VERITY.md");
21049
- if (refreshed !== content) (0, import_node_fs33.writeFileSync)(verityMd, refreshed);
21263
+ if (refreshed !== content) (0, import_node_fs34.writeFileSync)(verityMd, refreshed);
21050
21264
  actions.push("Renamed GATE.md \u2192 VERITY.md");
21051
21265
  }
21052
21266
  async function migrateTelemetryHeaders(root, actions) {
21053
21267
  const file = (0, import_node_path25.join)(root, ".claude", "settings.local.json");
21054
- if (!(0, import_node_fs33.existsSync)(file)) return;
21268
+ if (!(0, import_node_fs34.existsSync)(file)) return;
21055
21269
  let settings;
21056
21270
  try {
21057
21271
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -21099,14 +21313,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
21099
21313
  }
21100
21314
  if (toAppend.length > 0) {
21101
21315
  const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
21102
- (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");
21103
21317
  }
21104
- (0, import_node_fs33.rmSync)(gateCreds, { force: true });
21318
+ (0, import_node_fs34.rmSync)(gateCreds, { force: true });
21105
21319
  return toAppend.length;
21106
21320
  }
21107
21321
  function readFileSyncSafe(path) {
21108
21322
  try {
21109
- return (0, import_node_fs33.readFileSync)(path, "utf-8");
21323
+ return (0, import_node_fs34.readFileSync)(path, "utf-8");
21110
21324
  } catch {
21111
21325
  return "";
21112
21326
  }
@@ -21121,35 +21335,35 @@ function hasStagedChanges(root) {
21121
21335
  }
21122
21336
  function moveDir(from, to) {
21123
21337
  try {
21124
- (0, import_node_fs33.renameSync)(from, to);
21338
+ (0, import_node_fs34.renameSync)(from, to);
21125
21339
  } catch (err) {
21126
21340
  if (err.code !== "EXDEV") throw err;
21127
- (0, import_node_fs33.cpSync)(from, to, { recursive: true });
21128
- (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 });
21129
21343
  }
21130
21344
  }
21131
21345
  function moveFile(from, to) {
21132
21346
  try {
21133
- (0, import_node_fs33.renameSync)(from, to);
21347
+ (0, import_node_fs34.renameSync)(from, to);
21134
21348
  } catch (err) {
21135
21349
  if (err.code !== "EXDEV") throw err;
21136
- (0, import_node_fs33.cpSync)(from, to);
21137
- (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 });
21138
21352
  }
21139
21353
  }
21140
21354
  function carryLegacyContents(gateDir, verityDir) {
21141
21355
  let copied = 0;
21142
21356
  const walk = (relDir) => {
21143
21357
  const srcDir = (0, import_node_path25.join)(gateDir, relDir);
21144
- for (const entry of (0, import_node_fs33.readdirSync)(srcDir)) {
21358
+ for (const entry of (0, import_node_fs34.readdirSync)(srcDir)) {
21145
21359
  const rel = relDir ? (0, import_node_path25.join)(relDir, entry) : entry;
21146
21360
  const src = (0, import_node_path25.join)(gateDir, rel);
21147
21361
  const dest = (0, import_node_path25.join)(verityDir, rel);
21148
- if ((0, import_node_fs33.statSync)(src).isDirectory()) {
21362
+ if ((0, import_node_fs34.statSync)(src).isDirectory()) {
21149
21363
  walk(rel);
21150
- } else if (!(0, import_node_fs33.existsSync)(dest)) {
21151
- (0, import_node_fs33.mkdirSync)((0, import_node_path25.dirname)(dest), { recursive: true });
21152
- (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);
21153
21367
  copied++;
21154
21368
  }
21155
21369
  }
@@ -21160,20 +21374,20 @@ function carryLegacyContents(gateDir, verityDir) {
21160
21374
  async function needsMigration(root = repoRoot()) {
21161
21375
  const gateDir = (0, import_node_path25.join)(root, ".gate");
21162
21376
  const verityDir = (0, import_node_path25.join)(root, ".verity");
21163
- if ((0, import_node_fs33.existsSync)(gateDir) && !(0, import_node_fs33.existsSync)(verityDir)) return true;
21164
- if ((0, import_node_fs33.existsSync)(gateDir) && (0, import_node_fs33.existsSync)(verityDir)) {
21165
- 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"))) {
21166
21380
  return true;
21167
21381
  }
21168
- 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"))) {
21169
21383
  return true;
21170
21384
  }
21171
21385
  }
21172
21386
  const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
21173
- if ((0, import_node_fs33.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
21387
+ if ((0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
21174
21388
  return true;
21175
21389
  }
21176
- 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"))) {
21177
21391
  return true;
21178
21392
  }
21179
21393
  if (await hasLegacyHooksAt(root)) return true;
@@ -21315,7 +21529,7 @@ function resolveDataDir() {
21315
21529
  // local dev: running from repo root
21316
21530
  ];
21317
21531
  for (const candidate of candidates) {
21318
- 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"))) {
21319
21533
  return candidate;
21320
21534
  }
21321
21535
  }
@@ -21331,7 +21545,7 @@ function registerInitCommand(program2) {
21331
21545
  program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
21332
21546
  const force = opts.force ?? false;
21333
21547
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
21334
- const isProject = projectMarkers.some((m) => (0, import_node_fs34.existsSync)(m));
21548
+ const isProject = projectMarkers.some((m) => (0, import_node_fs35.existsSync)(m));
21335
21549
  if (!isProject) {
21336
21550
  printError("No project detected in the current directory.");
21337
21551
  printInfo('Run "verity init" from your project root.');
@@ -21401,14 +21615,14 @@ function registerInitCommand(program2) {
21401
21615
  for (const skill of skills) {
21402
21616
  const src = (0, import_node_path26.join)(skillsSource, skill);
21403
21617
  const dest = (0, import_node_path26.join)(skillsDest, skill);
21404
- if (!(0, import_node_fs34.existsSync)(src)) {
21618
+ if (!(0, import_node_fs35.existsSync)(src)) {
21405
21619
  printWarn(` Skill data not found: ${skill}`);
21406
21620
  continue;
21407
21621
  }
21408
- if ((0, import_node_fs34.existsSync)(dest) && !force) {
21622
+ if ((0, import_node_fs35.existsSync)(dest) && !force) {
21409
21623
  const srcSkill = (0, import_node_path26.join)(src, "SKILL.md");
21410
21624
  const destSkill = (0, import_node_path26.join)(dest, "SKILL.md");
21411
- if ((0, import_node_fs34.existsSync)(destSkill)) {
21625
+ if ((0, import_node_fs35.existsSync)(destSkill)) {
21412
21626
  try {
21413
21627
  const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
21414
21628
  const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
@@ -21480,7 +21694,7 @@ function registerInitCommand(program2) {
21480
21694
  }
21481
21695
 
21482
21696
  // src/commands/uninstall.ts
21483
- var import_node_fs35 = require("node:fs");
21697
+ var import_node_fs36 = require("node:fs");
21484
21698
  var import_node_path27 = require("node:path");
21485
21699
  var SKILL_NAMES = [
21486
21700
  "verity-setup",
@@ -21501,10 +21715,10 @@ function registerUninstallCommand(program2) {
21501
21715
  const skillsRoot = projectPath(".claude/skills");
21502
21716
  for (const name of SKILL_NAMES) {
21503
21717
  const dir = (0, import_node_path27.join)(skillsRoot, name);
21504
- if ((0, import_node_fs35.existsSync)(dir)) {
21718
+ if ((0, import_node_fs36.existsSync)(dir)) {
21505
21719
  actions.push({
21506
21720
  label: `Remove .claude/skills/${name}/`,
21507
- apply: () => (0, import_node_fs35.rmSync)(dir, { recursive: true, force: true })
21721
+ apply: () => (0, import_node_fs36.rmSync)(dir, { recursive: true, force: true })
21508
21722
  });
21509
21723
  }
21510
21724
  }
@@ -21518,24 +21732,24 @@ function registerUninstallCommand(program2) {
21518
21732
  });
21519
21733
  }
21520
21734
  const verityDir = projectPath(VERITY_DIR);
21521
- if ((0, import_node_fs35.existsSync)(verityDir)) {
21735
+ if ((0, import_node_fs36.existsSync)(verityDir)) {
21522
21736
  actions.push({
21523
21737
  label: `Remove ${VERITY_DIR}/`,
21524
- apply: () => (0, import_node_fs35.rmSync)(verityDir, { recursive: true, force: true })
21738
+ apply: () => (0, import_node_fs36.rmSync)(verityDir, { recursive: true, force: true })
21525
21739
  });
21526
21740
  }
21527
21741
  if (!keepVerityMd) {
21528
21742
  const verityMd = projectPath(VERITY_MD_FILE);
21529
- if ((0, import_node_fs35.existsSync)(verityMd)) {
21743
+ if ((0, import_node_fs36.existsSync)(verityMd)) {
21530
21744
  actions.push({
21531
21745
  label: `Remove ${VERITY_MD_FILE}`,
21532
- apply: () => (0, import_node_fs35.rmSync)(verityMd, { force: true })
21746
+ apply: () => (0, import_node_fs36.rmSync)(verityMd, { force: true })
21533
21747
  });
21534
21748
  }
21535
21749
  }
21536
21750
  const cleanupEmptyDir = (path) => {
21537
- if ((0, import_node_fs35.existsSync)(path) && (0, import_node_fs35.statSync)(path).isDirectory() && (0, import_node_fs35.readdirSync)(path).length === 0) {
21538
- (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);
21539
21753
  }
21540
21754
  };
21541
21755
  actions.push({
@@ -21547,10 +21761,10 @@ function registerUninstallCommand(program2) {
21547
21761
  });
21548
21762
  const home = process.env.HOME ?? "";
21549
21763
  const globalVerityDir = (0, import_node_path27.join)(home, ".verity");
21550
- if (purgeGlobal && (0, import_node_fs35.existsSync)(globalVerityDir)) {
21764
+ if (purgeGlobal && (0, import_node_fs36.existsSync)(globalVerityDir)) {
21551
21765
  actions.push({
21552
21766
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
21553
- apply: () => (0, import_node_fs35.rmSync)(globalVerityDir, { recursive: true, force: true })
21767
+ apply: () => (0, import_node_fs36.rmSync)(globalVerityDir, { recursive: true, force: true })
21554
21768
  });
21555
21769
  }
21556
21770
  if (actions.length === 0) {
@@ -21744,7 +21958,7 @@ function registerTaskCommands(program2) {
21744
21958
  }
21745
21959
 
21746
21960
  // src/commands/reset.ts
21747
- var import_node_fs36 = require("node:fs");
21961
+ var import_node_fs37 = require("node:fs");
21748
21962
  var import_node_path28 = require("node:path");
21749
21963
  function registerResetCommand(program2) {
21750
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) => {
@@ -21782,11 +21996,11 @@ function registerResetCommand(program2) {
21782
21996
  }
21783
21997
  const cacheDir = projectPath(CACHE_DIR);
21784
21998
  let purged = 0;
21785
- if ((0, import_node_fs36.existsSync)(cacheDir)) {
21786
- 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)) {
21787
22001
  if (entry.startsWith("pending-")) {
21788
22002
  try {
21789
- (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));
21790
22004
  purged++;
21791
22005
  } catch {
21792
22006
  }
@@ -21801,19 +22015,19 @@ function registerResetCommand(program2) {
21801
22015
  projectPath(`${VERITY_DIR}/.last-analysis`)
21802
22016
  ];
21803
22017
  for (const file of filesToClear) {
21804
- if ((0, import_node_fs36.existsSync)(file)) {
22018
+ if ((0, import_node_fs37.existsSync)(file)) {
21805
22019
  try {
21806
- (0, import_node_fs36.writeFileSync)(file, "");
22020
+ (0, import_node_fs37.writeFileSync)(file, "");
21807
22021
  } catch {
21808
22022
  }
21809
22023
  }
21810
22024
  }
21811
22025
  if (opts.all) {
21812
22026
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
21813
- if ((0, import_node_fs36.existsSync)(logsDir)) {
21814
- 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)) {
21815
22029
  try {
21816
- (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));
21817
22031
  } catch {
21818
22032
  }
21819
22033
  }
@@ -22121,8 +22335,8 @@ function registerTelemetryCommands(program2) {
22121
22335
  }
22122
22336
 
22123
22337
  // src/cli.ts
22124
- program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.cfb4bd2").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) => {
22125
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.cfb4bd2");
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");
22126
22340
  setUserNamedServiceUrl(program.opts().serviceUrl);
22127
22341
  try {
22128
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.cfb4bd2",
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",