@codacy/gate-cli 0.7.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.
Files changed (2) hide show
  1. package/bin/gate.js +103 -41
  2. package/package.json +1 -1
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
- return { files: Array.from(sets), hasRecentCommitFiles };
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, contentHash) {
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 storedContentHash = parts[2] ?? "";
11656
+ const storedTimestamp = parseInt(parts[2] ?? "0", 10);
11656
11657
  if (isNaN(iter)) return 1;
11657
11658
  if (storedCommit !== currentCommit) return 1;
11658
- if (contentHash && storedContentHash && storedContentHash !== contentHash) return 1;
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, contentHash) {
11679
+ function writeIteration(iteration, commit, _contentHash) {
11676
11680
  (0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
11677
- (0, import_node_fs5.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${contentHash ?? ""}`);
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
- if (analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0) {
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
- if (opts.skipStatic || !isCodacyAvailable()) {
12030
- staticResults = {
12031
- tool: "@codacy/analysis-cli",
12032
- findings: [],
12033
- summary: { total_findings: 0, by_severity: {}, tools_run: [] }
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
- const allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
12037
- staticResults = runCodacyAnalysis(allScannable);
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.7.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
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.7.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"