@codacy/verity-cli 0.28.1-experimental.99781f8 → 0.28.1-experimental.a72d2d9

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 +151 -52
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -13591,6 +13591,70 @@ var import_node_fs10 = require("node:fs");
13591
13591
  var import_node_crypto5 = require("node:crypto");
13592
13592
  var import_node_path11 = require("node:path");
13593
13593
 
13594
+ // src/lib/skip-detection.ts
13595
+ function isBareAckPrompt(prompt) {
13596
+ if (typeof prompt !== "string") return false;
13597
+ const trimmed = prompt.trim();
13598
+ if (trimmed.length === 0) return false;
13599
+ if (trimmed.length > 20) return false;
13600
+ const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
13601
+ return bareAckPattern.test(trimmed);
13602
+ }
13603
+ function isContinuationPrompt(prompt) {
13604
+ if (typeof prompt !== "string") return false;
13605
+ const trimmed = prompt.trim();
13606
+ if (trimmed.length === 0) return false;
13607
+ if (trimmed.length > 24) return false;
13608
+ const continuation = /^(let['’]?s\s+(go|do\s+it|start|continue)|go|go\s+ahead|go\s+on|proceed|continue|carry\s+on|keep\s+going|do\s+it|make\s+it\s+so|next|start|begin|ship\s+it|yes\s+please|please\s+continue|perfect|great|nice|excellent|agreed)[.!]*$/i;
13609
+ return continuation.test(trimmed) || isBareAckPrompt(trimmed);
13610
+ }
13611
+ function resolveGoalPrompt(prompts) {
13612
+ if (prompts.length === 0) return null;
13613
+ const latest = prompts[prompts.length - 1];
13614
+ if (!isContinuationPrompt(latest.prompt)) return { entry: latest, turnsBack: 0 };
13615
+ for (let i = prompts.length - 2; i >= 0; i--) {
13616
+ if (!isContinuationPrompt(prompts[i].prompt)) {
13617
+ return { entry: prompts[i], turnsBack: prompts.length - 1 - i };
13618
+ }
13619
+ }
13620
+ return { entry: latest, turnsBack: 0 };
13621
+ }
13622
+ function isReflectionQuestion(response) {
13623
+ if (!response || typeof response !== "string") return false;
13624
+ const markers = [
13625
+ /reflection\s+for\s+future\s+agents/i,
13626
+ /what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
13627
+ /say\s+['"]?skip['"]?\s+to\s+skip/i,
13628
+ /quick\s+reflection\s+question/i,
13629
+ // Post-flip (VRT-21): the agent drafts the reflection itself and, when
13630
+ // interactive, asks the user to confirm/correct before recording. That
13631
+ // turn authors no code either, so it's still a reflection turn.
13632
+ /reflection\s+draft/i,
13633
+ /confirm,?\s+correct,?\s+or\s+add/i
13634
+ ];
13635
+ return markers.some((m) => m.test(response));
13636
+ }
13637
+ function isMetaTaskLabel(label2) {
13638
+ if (label2 === null || label2 === void 0) return false;
13639
+ if (typeof label2 !== "string") return false;
13640
+ const trimmed = label2.trim();
13641
+ if (trimmed.length === 0) return true;
13642
+ const metaPatterns = [
13643
+ /^verity\s+[\w-]+\s+response$/i,
13644
+ // "Verity reflect response"
13645
+ /^simple user response$/i,
13646
+ /^verity\s+command$/i,
13647
+ // "Verity command"
13648
+ /^user\s+(question|reply|response|ack)$/i
13649
+ ];
13650
+ return metaPatterns.some((p) => p.test(trimmed));
13651
+ }
13652
+ function shouldSkipForBareAck(input) {
13653
+ if (!isBareAckPrompt(input.prompt)) return false;
13654
+ if (input.turnAuthoredCode) return false;
13655
+ return input.canSeeTurnAuthorship;
13656
+ }
13657
+
13594
13658
  // src/lib/dossier.ts
13595
13659
  var import_node_fs9 = require("node:fs");
13596
13660
  var import_node_crypto4 = require("node:crypto");
@@ -14695,7 +14759,19 @@ function sessionDossier(token, sessionId) {
14695
14759
  const d = openDossier(identity);
14696
14760
  return d ? { d, identity } : null;
14697
14761
  }
14762
+ function hasActiveGoal(d) {
14763
+ try {
14764
+ if (!(0, import_node_fs10.existsSync)(d.eventsPath)) return false;
14765
+ return (0, import_node_fs10.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
14766
+ } catch {
14767
+ return false;
14768
+ }
14769
+ }
14698
14770
  function recordGoal(d, prompt, source = "prompt") {
14771
+ if (source === "prompt" && isContinuationPrompt(prompt) && hasActiveGoal(d)) {
14772
+ appendEvent(d, { k: "goal_continue", text: prompt.slice(0, 64) });
14773
+ return;
14774
+ }
14699
14775
  const text = prompt.slice(0, MAX_GOAL_CHARS);
14700
14776
  appendEvent(d, {
14701
14777
  k: "goal",
@@ -16625,7 +16701,7 @@ function resolveTaskContext(opts) {
16625
16701
  // src/lib/cli-version.ts
16626
16702
  function cliVersion() {
16627
16703
  try {
16628
- return true ? "0.28.1-experimental.99781f8" : "dev";
16704
+ return true ? "0.28.1-experimental.a72d2d9" : "dev";
16629
16705
  } catch {
16630
16706
  return "dev";
16631
16707
  }
@@ -17222,6 +17298,33 @@ function describeCoverage(coverage, maxPaths = 5) {
17222
17298
  ${lines.join("\n")}
17223
17299
  Treat those files as UNCHECKED, not as approved.`;
17224
17300
  }
17301
+ function openBlockingElsewhere(statements, reviewedNow, lineShaAt) {
17302
+ const reviewed = new Set(reviewedNow);
17303
+ const out = [];
17304
+ const seen = /* @__PURE__ */ new Set();
17305
+ for (const s of statements) {
17306
+ if (s.outcome !== "open") continue;
17307
+ if (s.register !== "BLOCK") continue;
17308
+ if (s.carried) continue;
17309
+ if (reviewed.has(s.file)) continue;
17310
+ if (!s.line_sha) continue;
17311
+ if (lineShaAt(s.file, s.line) !== s.line_sha) continue;
17312
+ const key = `${s.file}::${s.pattern_id}`;
17313
+ if (seen.has(key)) continue;
17314
+ seen.add(key);
17315
+ out.push({ file: s.file, line: s.line, pattern_id: s.pattern_id });
17316
+ }
17317
+ return out;
17318
+ }
17319
+ function describeOpenElsewhere(open) {
17320
+ if (open.length === 0) return null;
17321
+ const lines = open.slice(0, 5).map((o) => ` ${o.file}:${o.line} [${o.pattern_id}]`);
17322
+ const more = open.length > 5 ? `
17323
+ (+${open.length - 5} more)` : "";
17324
+ return `STILL OPEN ELSEWHERE. ${open.length} blocking finding(s) Verity raised earlier are still present in files this run did not review:
17325
+ ${lines.join("\n")}${more}
17326
+ This verdict covers the current change only. The tree is not clean.`;
17327
+ }
17225
17328
 
17226
17329
  // src/lib/channel.ts
17227
17330
  var MAX_AGENT_CONTEXT_CHARS = 1500;
@@ -17316,8 +17419,10 @@ var NC2 = "\x1B[0m";
17316
17419
  function emitVerdict(input) {
17317
17420
  const exit = input.exit ?? ((code) => process.exit(code));
17318
17421
  const { coverage, unaccounted } = reconcileCoverage(input.changed, input.coverage);
17319
- const verdict = resolveVerdict(input.proposed, coverage);
17320
- const note = describeCoverage(coverage);
17422
+ let verdict = resolveVerdict(input.proposed, coverage);
17423
+ const openElsewhere = input.openElsewhere ?? [];
17424
+ if (verdict === "PASS" && openElsewhere.length > 0) verdict = "WARN";
17425
+ const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
17321
17426
  if (unaccounted.length > 0) {
17322
17427
  process.stderr.write(
17323
17428
  `${YELLOW2}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
@@ -17520,46 +17625,6 @@ function shouldWarmRetryAnalyze(result) {
17520
17625
  return false;
17521
17626
  }
17522
17627
 
17523
- // src/lib/skip-detection.ts
17524
- function isBareAckPrompt(prompt) {
17525
- if (typeof prompt !== "string") return false;
17526
- const trimmed = prompt.trim();
17527
- if (trimmed.length === 0) return false;
17528
- if (trimmed.length > 20) return false;
17529
- const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
17530
- return bareAckPattern.test(trimmed);
17531
- }
17532
- function isReflectionQuestion(response) {
17533
- if (!response || typeof response !== "string") return false;
17534
- const markers = [
17535
- /reflection\s+for\s+future\s+agents/i,
17536
- /what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
17537
- /say\s+['"]?skip['"]?\s+to\s+skip/i,
17538
- /quick\s+reflection\s+question/i,
17539
- // Post-flip (VRT-21): the agent drafts the reflection itself and, when
17540
- // interactive, asks the user to confirm/correct before recording. That
17541
- // turn authors no code either, so it's still a reflection turn.
17542
- /reflection\s+draft/i,
17543
- /confirm,?\s+correct,?\s+or\s+add/i
17544
- ];
17545
- return markers.some((m) => m.test(response));
17546
- }
17547
- function isMetaTaskLabel(label2) {
17548
- if (label2 === null || label2 === void 0) return false;
17549
- if (typeof label2 !== "string") return false;
17550
- const trimmed = label2.trim();
17551
- if (trimmed.length === 0) return true;
17552
- const metaPatterns = [
17553
- /^verity\s+[\w-]+\s+response$/i,
17554
- // "Verity reflect response"
17555
- /^simple user response$/i,
17556
- /^verity\s+command$/i,
17557
- // "Verity command"
17558
- /^user\s+(question|reply|response|ack)$/i
17559
- ];
17560
- return metaPatterns.some((p) => p.test(trimmed));
17561
- }
17562
-
17563
17628
  // src/lib/transcript.ts
17564
17629
  var import_node_fs22 = require("node:fs");
17565
17630
  var MAX_READ_BYTES = 256 * 1024;
@@ -18321,10 +18386,12 @@ async function runAnalyze(opts, globals) {
18321
18386
  if (/^\s*\/verity-/i.test(latestPrompt)) {
18322
18387
  await passAndExit("Verity command \u2014 skipping analysis", "verity-command");
18323
18388
  }
18324
- if (isBareAckPrompt(latestPrompt)) {
18389
+ const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
18390
+ const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
18391
+ const canSeeTurnAuthorship = !!actionSummary || !!baseline;
18392
+ if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
18325
18393
  await passAndExit("Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
18326
18394
  }
18327
- const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
18328
18395
  if (isReflectionQuestion(assistantResponse) && !agentAuthoredCodeThisTurn) {
18329
18396
  await passAndExit("Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
18330
18397
  }
@@ -18767,7 +18834,20 @@ async function runAnalyze(opts, globals) {
18767
18834
  const intentContext = {};
18768
18835
  if (conversation && conversation.prompts.length > 0) {
18769
18836
  const latest = conversation.prompts[conversation.prompts.length - 1];
18770
- intentContext.user_prompt = latest.prompt;
18837
+ const goalPrompt = resolveGoalPrompt(conversation.prompts) ?? { entry: latest, turnsBack: 0 };
18838
+ intentContext.user_prompt = goalPrompt.entry.prompt;
18839
+ if (isContinuationPrompt(intentContext.user_prompt)) {
18840
+ const carried = memory?.projection.goal?.text;
18841
+ if (carried && !isContinuationPrompt(carried)) {
18842
+ intentContext.continuation_prompt = latest.prompt;
18843
+ intentContext.user_prompt = carried;
18844
+ logEvent("goal_from_dossier", { chars: carried.length });
18845
+ }
18846
+ }
18847
+ if (goalPrompt.turnsBack > 0) {
18848
+ intentContext.continuation_prompt = latest.prompt;
18849
+ logEvent("goal_walked_back", { turns_back: goalPrompt.turnsBack });
18850
+ }
18771
18851
  intentContext.session_id = latest.session_id || void 0;
18772
18852
  intentContext.prompt_captured_at = latest.captured_at || void 0;
18773
18853
  if (conversation.prompts.length > 1) {
@@ -18859,6 +18939,22 @@ async function runAnalyze(opts, globals) {
18859
18939
  const response = result.data;
18860
18940
  const decision = response.gate_decision ?? "(unrecognised)";
18861
18941
  const sentPaths = codeDelta.files.map((f) => f.path);
18942
+ let openElsewhere = [];
18943
+ if (memorySession) {
18944
+ try {
18945
+ const st = foldDossier(memorySession.d);
18946
+ openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
18947
+ try {
18948
+ const src = (0, import_node_fs24.readFileSync)((0, import_node_path20.join)(repoRoot(), file), "utf8").split("\n");
18949
+ const at = src[line - 1];
18950
+ return at === void 0 ? null : lineSha(at);
18951
+ } catch {
18952
+ return null;
18953
+ }
18954
+ });
18955
+ } catch {
18956
+ }
18957
+ }
18862
18958
  const reviewCoverage = {
18863
18959
  reviewed: sentPaths,
18864
18960
  // Declared drops from the stages that DO report themselves today. The other
@@ -19163,7 +19259,8 @@ ${YELLOW}${grantNudge.trim()}${NC}
19163
19259
  // so what the cut removes is the repeated commentary, never the defect.
19164
19260
  agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19165
19261
  // The coverage note is silenced with it — half a channel is still a channel.
19166
- silenced: !!silenced
19262
+ silenced: !!silenced,
19263
+ openElsewhere
19167
19264
  });
19168
19265
  break;
19169
19266
  }
@@ -19183,7 +19280,8 @@ ${YELLOW}${grantNudge.trim()}${NC}
19183
19280
  userSummary,
19184
19281
  agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19185
19282
  // The coverage note is silenced with it — half a channel is still a channel.
19186
- silenced: !!silenced
19283
+ silenced: !!silenced,
19284
+ openElsewhere
19187
19285
  });
19188
19286
  break;
19189
19287
  }
@@ -19202,7 +19300,8 @@ ${YELLOW}${grantNudge.trim()}${NC}
19202
19300
  userSummary,
19203
19301
  agentContext: silenced ? null : agentContextFor(response, intentRepeatCount),
19204
19302
  // The coverage note is silenced with it — half a channel is still a channel.
19205
- silenced: !!silenced
19303
+ silenced: !!silenced,
19304
+ openElsewhere
19206
19305
  });
19207
19306
  break;
19208
19307
  }
@@ -19316,8 +19415,8 @@ async function runReview(opts, globals) {
19316
19415
  for (const p of specPaths) {
19317
19416
  if (!(0, import_node_fs26.existsSync)(p)) continue;
19318
19417
  try {
19319
- const { readFileSync: readFileSync15 } = await import("node:fs");
19320
- const content = readFileSync15(p, "utf-8");
19418
+ const { readFileSync: readFileSync16 } = await import("node:fs");
19419
+ const content = readFileSync16(p, "utf-8");
19321
19420
  specs.push({ path: p, content: content.slice(0, 10240) });
19322
19421
  } catch {
19323
19422
  }
@@ -21003,7 +21102,7 @@ function registerTelemetryCommands(program2) {
21003
21102
  }
21004
21103
 
21005
21104
  // src/cli.ts
21006
- program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.99781f8").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21105
+ program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.a72d2d9").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21007
21106
  try {
21008
21107
  await foldLegacyLocalCredential();
21009
21108
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.28.1-experimental.99781f8",
3
+ "version": "0.28.1-experimental.a72d2d9",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "bugs": {