@codacy/verity-cli 0.23.2 → 0.23.3

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 +88 -21
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -12833,6 +12833,11 @@ function getPushRangeFiles() {
12833
12833
  const last = diff("HEAD~1..HEAD");
12834
12834
  return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
12835
12835
  }
12836
+ function getPushRangeMessages() {
12837
+ const { range } = getPushRangeFiles();
12838
+ if (!range) return "";
12839
+ return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
12840
+ }
12836
12841
  function getWorktreeFiles() {
12837
12842
  const result = [];
12838
12843
  const worktreeDir = ".claude/worktrees";
@@ -15057,7 +15062,48 @@ function registerGuardCommand(program2) {
15057
15062
  function getMomentFiles(moment) {
15058
15063
  return moment === "pre-commit" ? getStagedFiles() : getPushRangeFiles().files;
15059
15064
  }
15060
- function buildGuardRequest(moment, files, iter, sessionId) {
15065
+ function matchFlagValue(command, flags) {
15066
+ const re = new RegExp(`(?<![\\w-])(?:${flags})(?:=|\\s+)('((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|([^\\s'"-][^\\s]*))`);
15067
+ const m = re.exec(command);
15068
+ if (!m) return null;
15069
+ const v = m[2] ?? m[3] ?? m[4];
15070
+ return v != null ? v.replace(/\\(["'])/g, "$1") : null;
15071
+ }
15072
+ function parseCommitMessage(command) {
15073
+ const parts = [];
15074
+ const re = /(?<![\w-])(?:--message|-am|-m)(?:=|\s+)('((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)"|([^\s'"-][^\s]*))/g;
15075
+ let m;
15076
+ while ((m = re.exec(command)) !== null) {
15077
+ const v = m[2] ?? m[3] ?? m[4];
15078
+ if (v != null && v.length) parts.push(v.replace(/\\(["'])/g, "$1"));
15079
+ }
15080
+ return parts.length ? parts.join("\n\n") : null;
15081
+ }
15082
+ function parsePrIntent(command) {
15083
+ const title = matchFlagValue(command, "--title|-t");
15084
+ const body = matchFlagValue(command, "--body|-b");
15085
+ const parts = [title, body].filter((x) => !!x);
15086
+ return parts.length ? parts.join("\n\n") : null;
15087
+ }
15088
+ var TRIVIAL_INTENT = /^(wip|tmp|temp|test|fix|chore|amend|\.+|fixup!.*|squash!.*|merge\b.*)$/i;
15089
+ var SHELL_PLUMBING = /\$\(|`|<<-?\s*['"]?[A-Za-z_]/;
15090
+ function isSubstantiveIntent(text) {
15091
+ if (!text) return false;
15092
+ const t = text.trim();
15093
+ if (t.length < 12) return false;
15094
+ if (TRIVIAL_INTENT.test(t)) return false;
15095
+ if (SHELL_PLUMBING.test(t)) return false;
15096
+ return true;
15097
+ }
15098
+ function extractStatedIntent(moment, command) {
15099
+ const text = moment === "pre-commit" ? parseCommitMessage(command) : parsePrIntent(command) ?? (getPushRangeMessages() || null);
15100
+ return isSubstantiveIntent(text) ? text : null;
15101
+ }
15102
+ function hasBlockingFinding(response) {
15103
+ const findings = response.findings ?? [];
15104
+ return findings.some((f) => f.scope !== "pre-existing" && ["critical", "high"].includes((f.severity ?? "").toLowerCase()));
15105
+ }
15106
+ function buildGuardRequest(moment, files, iter, sessionId, command) {
15061
15107
  const analyzable = filterAnalyzable(files);
15062
15108
  const securityFiles = filterSecurity(files);
15063
15109
  let staticResults;
@@ -15087,20 +15133,21 @@ function buildGuardRequest(moment, files, iter, sessionId) {
15087
15133
  };
15088
15134
  const specs = discoverSpecs();
15089
15135
  const plans = discoverPlans();
15090
- if (specs.length > 0 || plans.length > 0) {
15136
+ const statedIntent = extractStatedIntent(moment, command);
15137
+ if (specs.length > 0 || plans.length > 0 || statedIntent) {
15091
15138
  const intentContext = {};
15139
+ if (statedIntent) intentContext.user_prompt = statedIntent;
15092
15140
  if (specs.length > 0) intentContext.specs = specs;
15093
15141
  if (plans.length > 0) intentContext.plans = plans;
15094
15142
  requestBody.intent_context = intentContext;
15095
15143
  }
15096
15144
  return requestBody;
15097
15145
  }
15098
- function allowWithNotice(moment, decision) {
15099
- resetIter(moment);
15100
- if (decision === "WARN") {
15101
- process.stderr.write(`${YELLOW}Verity ${moment}: WARN \u2014 proceeding. See findings in the report.${NC}
15102
- `);
15103
- }
15146
+ function emitAllowNotice(userMsg, agentMsg) {
15147
+ process.stdout.write(JSON.stringify({
15148
+ systemMessage: userMsg,
15149
+ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: agentMsg }
15150
+ }) + "\n");
15104
15151
  process.exit(0);
15105
15152
  }
15106
15153
  async function runGuard(opts, globals) {
@@ -15114,19 +15161,21 @@ async function runGuard(opts, globals) {
15114
15161
  }
15115
15162
  const moment = classifyCommand(command, on);
15116
15163
  if (!moment) process.exit(0);
15164
+ const verb = moment === "pre-commit" ? "commit" : "push";
15117
15165
  const iter = readIter(moment);
15118
15166
  if (iter >= GUARD_BLOCK_CAP) {
15119
- process.stderr.write(`${DIM}Verity ${moment}: ${GUARD_BLOCK_CAP} review cycles reached \u2014 allowing through.${NC}
15120
- `);
15121
15167
  resetIter(moment);
15122
- process.exit(0);
15168
+ emitAllowNotice(
15169
+ `Verity ${moment}: ${GUARD_BLOCK_CAP} review cycles reached \u2014 allowing the ${verb} through`,
15170
+ `Verity ${moment}: review-cycle cap (${GUARD_BLOCK_CAP}) reached; the ${verb} was allowed without a further block.`
15171
+ );
15123
15172
  }
15124
15173
  const files = getMomentFiles(moment);
15125
15174
  if (files.length === 0) process.exit(0);
15126
15175
  const tokenResult = await resolveToken(globals.token);
15127
15176
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
15128
15177
  if (!tokenResult.ok || !urlResult.ok) process.exit(0);
15129
- const requestBody = buildGuardRequest(moment, files, iter, sessionId);
15178
+ const requestBody = buildGuardRequest(moment, files, iter, sessionId, command);
15130
15179
  if (!requestBody) process.exit(0);
15131
15180
  const result = await analyzeRequest({
15132
15181
  serviceUrl: urlResult.data,
@@ -15137,21 +15186,39 @@ async function runGuard(opts, globals) {
15137
15186
  cmd: "guard"
15138
15187
  });
15139
15188
  if (!result.ok) {
15140
- process.stderr.write(`${DIM}Verity ${moment}: service unavailable \u2014 allowing commit/push (${result.error}).${NC}
15141
- `);
15142
- process.exit(0);
15143
- }
15144
- if (opts.json) {
15145
- process.stdout.write(JSON.stringify(result.data) + "\n");
15189
+ emitAllowNotice(
15190
+ `\u26A0 Verity ${moment}: service offline \u2014 ${verb}ed WITHOUT review`,
15191
+ `Verity ${moment}: service unavailable (${result.error}); the ${verb} was allowed WITHOUT a Verity review.`
15192
+ );
15146
15193
  }
15194
+ if (opts.json) process.stderr.write(JSON.stringify(result.data) + "\n");
15147
15195
  const response = result.data;
15148
15196
  const decision = response.gate_decision ?? "PASS";
15149
- if (decision === "FAIL") {
15197
+ const viewUrl = response.view_url ?? "";
15198
+ const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
15199
+ if (decision === "FAIL" && hasBlockingFinding(response)) {
15150
15200
  writeIter(moment, iter + 1);
15151
15201
  writeBlockMessage(moment, response);
15152
15202
  process.exit(2);
15153
15203
  }
15154
- allowWithNotice(moment, decision);
15204
+ resetIter(moment);
15205
+ if (decision === "FAIL") {
15206
+ const narrative = response.assessment?.narrative ?? "";
15207
+ emitAllowNotice(
15208
+ `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding${link}`,
15209
+ `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}${viewUrl ? ` Report: ${viewUrl}` : ""}`
15210
+ );
15211
+ }
15212
+ if (decision === "WARN") {
15213
+ emitAllowNotice(
15214
+ `\u26A0 Verity ${moment}: WARN \u2014 proceeding${link}`,
15215
+ `Verity ${moment} review: WARN (proceeding).${viewUrl ? ` Report: ${viewUrl}` : ""}`
15216
+ );
15217
+ }
15218
+ emitAllowNotice(
15219
+ `\u2713 Verity ${moment}: PASS${link}`,
15220
+ `Verity ${moment} review: PASS.${viewUrl ? ` Report: ${viewUrl}` : ""}`
15221
+ );
15155
15222
  }
15156
15223
  function writeBlockMessage(moment, response) {
15157
15224
  const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
@@ -16341,7 +16408,7 @@ function registerTelemetryCommands(program2) {
16341
16408
  }
16342
16409
 
16343
16410
  // src/cli.ts
16344
- program.name("verity").description("CLI for Verity quality gate service").version("0.23.2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16411
+ program.name("verity").description("CLI for Verity quality gate service").version("0.23.3").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16345
16412
  registerAuthCommands(program);
16346
16413
  registerHooksCommands(program);
16347
16414
  registerIntentCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.23.2",
3
+ "version": "0.23.3",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "repository": {