@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.
- package/bin/verity.js +88 -21
- 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
|
|
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
|
-
|
|
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
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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);
|