@codacy/gate-cli 0.6.0 → 0.8.0
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/gate.js +103 -41
- package/package.json +2 -2
package/bin/gate.js
CHANGED
|
@@ -11357,7 +11357,8 @@ function getChangedFiles() {
|
|
|
11357
11357
|
for (const f of getWorktreeFiles()) {
|
|
11358
11358
|
sets.add(f);
|
|
11359
11359
|
}
|
|
11360
|
-
|
|
11360
|
+
const filtered = Array.from(sets).filter((f) => !f.startsWith(".gate/") && !f.startsWith(".gate\\"));
|
|
11361
|
+
return { files: filtered, hasRecentCommitFiles };
|
|
11361
11362
|
}
|
|
11362
11363
|
function getWorktreeFiles() {
|
|
11363
11364
|
const result = [];
|
|
@@ -11645,17 +11646,20 @@ function narrowToRecent(files) {
|
|
|
11645
11646
|
});
|
|
11646
11647
|
return recent.length > 0 ? recent : files;
|
|
11647
11648
|
}
|
|
11648
|
-
function readIteration(currentCommit,
|
|
11649
|
+
function readIteration(currentCommit, _contentHash) {
|
|
11649
11650
|
if (!(0, import_node_fs5.existsSync)(ITERATION_FILE)) return 1;
|
|
11650
11651
|
try {
|
|
11651
11652
|
const stored = (0, import_node_fs5.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
11652
11653
|
const parts = stored.split(":");
|
|
11653
11654
|
const iter = parseInt(parts[0], 10);
|
|
11654
11655
|
const storedCommit = parts[1] ?? "";
|
|
11655
|
-
const
|
|
11656
|
+
const storedTimestamp = parseInt(parts[2] ?? "0", 10);
|
|
11656
11657
|
if (isNaN(iter)) return 1;
|
|
11657
11658
|
if (storedCommit !== currentCommit) return 1;
|
|
11658
|
-
if (
|
|
11659
|
+
if (storedTimestamp > 0) {
|
|
11660
|
+
const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
|
|
11661
|
+
if (elapsed > 600) return 1;
|
|
11662
|
+
}
|
|
11659
11663
|
return iter;
|
|
11660
11664
|
} catch {
|
|
11661
11665
|
return 1;
|
|
@@ -11672,9 +11676,10 @@ function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS, conte
|
|
|
11672
11676
|
}
|
|
11673
11677
|
return { skip: null, iteration };
|
|
11674
11678
|
}
|
|
11675
|
-
function writeIteration(iteration, commit,
|
|
11679
|
+
function writeIteration(iteration, commit, _contentHash) {
|
|
11676
11680
|
(0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
|
|
11677
|
-
|
|
11681
|
+
const ts = Math.floor(Date.now() / 1e3);
|
|
11682
|
+
(0, import_node_fs5.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}`);
|
|
11678
11683
|
}
|
|
11679
11684
|
|
|
11680
11685
|
// src/lib/static-analysis.ts
|
|
@@ -11989,6 +11994,39 @@ function buildOfflineFallback(reason, staticResults) {
|
|
|
11989
11994
|
}
|
|
11990
11995
|
|
|
11991
11996
|
// src/commands/analyze.ts
|
|
11997
|
+
var MAX_ASSISTANT_RESPONSE_CHARS = 8e3;
|
|
11998
|
+
async function readStopHookStdin() {
|
|
11999
|
+
const empty = { assistantMessage: null, stopReason: null };
|
|
12000
|
+
try {
|
|
12001
|
+
if (process.stdin.isTTY) return empty;
|
|
12002
|
+
const chunks = [];
|
|
12003
|
+
const timeout = new Promise((resolve) => setTimeout(() => resolve(empty), 500));
|
|
12004
|
+
const read = new Promise((resolve) => {
|
|
12005
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
12006
|
+
process.stdin.on("end", () => {
|
|
12007
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
12008
|
+
if (!raw) {
|
|
12009
|
+
resolve(empty);
|
|
12010
|
+
return;
|
|
12011
|
+
}
|
|
12012
|
+
try {
|
|
12013
|
+
const data = JSON.parse(raw);
|
|
12014
|
+
resolve({
|
|
12015
|
+
assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
|
|
12016
|
+
stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null
|
|
12017
|
+
});
|
|
12018
|
+
} catch {
|
|
12019
|
+
resolve(empty);
|
|
12020
|
+
}
|
|
12021
|
+
});
|
|
12022
|
+
process.stdin.on("error", () => resolve(empty));
|
|
12023
|
+
process.stdin.resume();
|
|
12024
|
+
});
|
|
12025
|
+
return await Promise.race([read, timeout]);
|
|
12026
|
+
} catch {
|
|
12027
|
+
return empty;
|
|
12028
|
+
}
|
|
12029
|
+
}
|
|
11992
12030
|
function passAndExit(reason) {
|
|
11993
12031
|
printJsonCompact({ gate_decision: "PASS", systemMessage: `GATE.md: ${reason}` });
|
|
11994
12032
|
process.exit(0);
|
|
@@ -12004,47 +12042,68 @@ function registerAnalyzeCommand(program2) {
|
|
|
12004
12042
|
});
|
|
12005
12043
|
}
|
|
12006
12044
|
async function runAnalyze(opts, globals) {
|
|
12045
|
+
const { assistantMessage: assistantResponse, stopReason } = await readStopHookStdin();
|
|
12007
12046
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
12008
12047
|
const analyzable = filterAnalyzable(allChanged);
|
|
12009
12048
|
const reviewable = filterReviewable(allChanged);
|
|
12010
12049
|
const securityFiles = filterSecurity(allChanged);
|
|
12011
|
-
|
|
12050
|
+
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
12051
|
+
if (noFilesChanged && !assistantResponse) {
|
|
12012
12052
|
passAndExit("No analyzable files changed");
|
|
12013
12053
|
}
|
|
12054
|
+
const planOnly = noFilesChanged && !!assistantResponse;
|
|
12014
12055
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
12015
|
-
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
12016
|
-
const debounceSkip = checkDebounce(debounceSeconds);
|
|
12017
|
-
if (debounceSkip) passAndExit(debounceSkip);
|
|
12018
|
-
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
12019
|
-
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
12020
|
-
if (mtimeSkip) passAndExit(mtimeSkip);
|
|
12021
|
-
recordAnalysisStart();
|
|
12022
|
-
const { skip: hashSkip, hash: contentHash } = checkContentHash(allCheckable);
|
|
12023
|
-
if (hashSkip) passAndExit(hashSkip);
|
|
12024
|
-
const recentForReview = narrowToRecent(allForReview);
|
|
12025
12056
|
const conversation = await readAndClearConversationBuffer();
|
|
12026
12057
|
const specs = discoverSpecs();
|
|
12027
12058
|
const plans = discoverPlans();
|
|
12028
|
-
let staticResults
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12059
|
+
let staticResults = {
|
|
12060
|
+
tool: "@codacy/analysis-cli",
|
|
12061
|
+
findings: [],
|
|
12062
|
+
summary: { total_findings: 0, by_severity: {}, tools_run: [] }
|
|
12063
|
+
};
|
|
12064
|
+
let codeDelta = {
|
|
12065
|
+
files: [],
|
|
12066
|
+
total_lines: 0,
|
|
12067
|
+
total_files: 0
|
|
12068
|
+
};
|
|
12069
|
+
let snapshotResult = { has_snapshots: false, diffs: [] };
|
|
12070
|
+
let contentHash = null;
|
|
12071
|
+
let iteration = 1;
|
|
12072
|
+
let currentCommit = "";
|
|
12073
|
+
if (!planOnly) {
|
|
12074
|
+
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
12075
|
+
const debounceSkip = checkDebounce(debounceSeconds);
|
|
12076
|
+
if (debounceSkip) passAndExit(debounceSkip);
|
|
12077
|
+
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
12078
|
+
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
12079
|
+
if (mtimeSkip) passAndExit(mtimeSkip);
|
|
12080
|
+
recordAnalysisStart();
|
|
12081
|
+
const hashResult = checkContentHash(allCheckable);
|
|
12082
|
+
if (hashResult.skip) passAndExit(hashResult.skip);
|
|
12083
|
+
contentHash = hashResult.hash;
|
|
12084
|
+
const recentForReview = narrowToRecent(allForReview);
|
|
12085
|
+
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
12086
|
+
const allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
12087
|
+
staticResults = runCodacyAnalysis(allScannable);
|
|
12088
|
+
}
|
|
12089
|
+
codeDelta = collectCodeDelta(recentForReview, {
|
|
12090
|
+
maxFiles: parseInt(opts.maxFiles, 10),
|
|
12091
|
+
maxFileBytes: parseInt(opts.maxFileSize, 10),
|
|
12092
|
+
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
12093
|
+
});
|
|
12094
|
+
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
12095
|
+
passAndExit("No files within size limits to analyze");
|
|
12096
|
+
}
|
|
12097
|
+
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
12098
|
+
currentCommit = getCurrentCommit();
|
|
12099
|
+
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
12100
|
+
const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
12101
|
+
if (iterResult.skip) passAndExit(iterResult.skip);
|
|
12102
|
+
iteration = iterResult.iteration;
|
|
12035
12103
|
} else {
|
|
12036
|
-
|
|
12037
|
-
|
|
12104
|
+
recordAnalysisStart();
|
|
12105
|
+
currentCommit = getCurrentCommit();
|
|
12038
12106
|
}
|
|
12039
|
-
const codeDelta = collectCodeDelta(recentForReview, {
|
|
12040
|
-
maxFiles: parseInt(opts.maxFiles, 10),
|
|
12041
|
-
maxFileBytes: parseInt(opts.maxFileSize, 10),
|
|
12042
|
-
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
12043
|
-
});
|
|
12044
|
-
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
12045
|
-
passAndExit("No files within size limits to analyze");
|
|
12046
|
-
}
|
|
12047
|
-
const snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
12048
12107
|
const tokenResult = await resolveToken(globals.token);
|
|
12049
12108
|
if (!tokenResult.ok) {
|
|
12050
12109
|
passAndExit("Not configured yet \u2014 run /gate-setup in Claude Code to enable quality gates.");
|
|
@@ -12053,15 +12112,12 @@ async function runAnalyze(opts, globals) {
|
|
|
12053
12112
|
if (!urlResult.ok) {
|
|
12054
12113
|
passAndExit("Not configured yet \u2014 run /gate-setup in Claude Code to enable quality gates.");
|
|
12055
12114
|
}
|
|
12056
|
-
const currentCommit = getCurrentCommit();
|
|
12057
|
-
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
12058
|
-
const { skip: iterSkip, iteration } = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
12059
|
-
if (iterSkip) passAndExit(iterSkip);
|
|
12060
12115
|
const requestBody = {
|
|
12061
12116
|
static_results: staticResults,
|
|
12062
12117
|
code_delta: codeDelta,
|
|
12063
12118
|
changed_files: allForReview,
|
|
12064
12119
|
standard_version: "latest",
|
|
12120
|
+
plan_only: planOnly,
|
|
12065
12121
|
context: {
|
|
12066
12122
|
agent_model: process.env.CLAUDE_MODEL ?? "unknown",
|
|
12067
12123
|
agent_harness: "claude-code",
|
|
@@ -12072,7 +12128,7 @@ async function runAnalyze(opts, globals) {
|
|
|
12072
12128
|
if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
|
|
12073
12129
|
requestBody.snapshot_diffs = snapshotResult.diffs;
|
|
12074
12130
|
}
|
|
12075
|
-
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0;
|
|
12131
|
+
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse;
|
|
12076
12132
|
if (hasIntent) {
|
|
12077
12133
|
const intentContext = {};
|
|
12078
12134
|
if (conversation && conversation.prompts.length > 0) {
|
|
@@ -12087,6 +12143,12 @@ async function runAnalyze(opts, globals) {
|
|
|
12087
12143
|
intentContext.recent_commits = conversation.recent_commits;
|
|
12088
12144
|
}
|
|
12089
12145
|
}
|
|
12146
|
+
if (assistantResponse) {
|
|
12147
|
+
intentContext.assistant_response = assistantResponse.length > MAX_ASSISTANT_RESPONSE_CHARS ? assistantResponse.slice(0, MAX_ASSISTANT_RESPONSE_CHARS) : assistantResponse;
|
|
12148
|
+
}
|
|
12149
|
+
if (stopReason) {
|
|
12150
|
+
intentContext.stop_reason = stopReason;
|
|
12151
|
+
}
|
|
12090
12152
|
if (specs.length > 0) intentContext.specs = specs;
|
|
12091
12153
|
if (plans.length > 0) intentContext.plans = plans;
|
|
12092
12154
|
for (const key of Object.keys(intentContext)) {
|
|
@@ -12448,7 +12510,7 @@ function registerInitCommand(program2) {
|
|
|
12448
12510
|
}
|
|
12449
12511
|
|
|
12450
12512
|
// src/cli.ts
|
|
12451
|
-
program.name("gate").description("CLI for GATE.md quality gate service").version("0.
|
|
12513
|
+
program.name("gate").description("CLI for GATE.md quality gate service").version("0.8.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
12452
12514
|
registerAuthCommands(program);
|
|
12453
12515
|
registerHooksCommands(program);
|
|
12454
12516
|
registerIntentCommands(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/gate-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "CLI for GATE.md quality gate service",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gate": "./bin/gate.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=20"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "
|
|
18
|
+
"build": "node scripts/build.js",
|
|
19
19
|
"test": "node --import tsx --test $(find tests -name '*.test.ts' | sort)",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"prepublishOnly": "npm run typecheck && npm run build"
|