@codacy/gate-cli 0.14.1 → 0.14.2

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 +188 -95
  2. package/package.json +1 -1
package/bin/gate.js CHANGED
@@ -10355,6 +10355,9 @@ var ITERATION_FILE = `${GATE_DIR}/.iteration-count`;
10355
10355
  var HASH_FILE = `${GATE_DIR}/.last-pass-hash`;
10356
10356
  var INTENT_FILE = `${GATE_DIR}/.last-intent`;
10357
10357
  var CACHE_DIR = `${GATE_DIR}/.cache`;
10358
+ var DEBUG_LOG_DIR = `${GATE_DIR}/.logs`;
10359
+ var DEBUG_LOG_FILE = `${DEBUG_LOG_DIR}/cli.log`;
10360
+ var DEBUG_LOG_MAX_BYTES = 1048576;
10358
10361
  var GATE_MD_FILE = "GATE.md";
10359
10362
  var CLAUDE_SETTINGS_FILE = ".claude/settings.json";
10360
10363
  var STANDARD_FILE = `${GATE_DIR}/standard.yaml`;
@@ -10588,9 +10591,43 @@ function printVerbose(msg, verbose) {
10588
10591
  }
10589
10592
  }
10590
10593
 
10594
+ // src/lib/debug-log.ts
10595
+ var import_node_fs = require("node:fs");
10596
+ function isEnabled() {
10597
+ const v = process.env.GATE_DEBUG;
10598
+ if (v === "0" || v === "false" || v === "off") return false;
10599
+ return true;
10600
+ }
10601
+ function logHttpCall(entry) {
10602
+ if (!isEnabled()) return;
10603
+ writeLine({ ts: (/* @__PURE__ */ new Date()).toISOString(), kind: "http", ...entry });
10604
+ }
10605
+ function logEvent(event, data) {
10606
+ if (!isEnabled()) return;
10607
+ writeLine({ ts: (/* @__PURE__ */ new Date()).toISOString(), kind: "event", event, ...data ?? {} });
10608
+ }
10609
+ function writeLine(obj) {
10610
+ try {
10611
+ const dir = projectPath(DEBUG_LOG_DIR);
10612
+ const file = projectPath(DEBUG_LOG_FILE);
10613
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
10614
+ rotateIfNeeded(file);
10615
+ (0, import_node_fs.appendFileSync)(file, JSON.stringify(obj) + "\n");
10616
+ } catch {
10617
+ }
10618
+ }
10619
+ function rotateIfNeeded(file) {
10620
+ try {
10621
+ const s = (0, import_node_fs.statSync)(file);
10622
+ if (s.size < DEBUG_LOG_MAX_BYTES) return;
10623
+ (0, import_node_fs.renameSync)(file, `${file}.1`);
10624
+ } catch {
10625
+ }
10626
+ }
10627
+
10591
10628
  // src/lib/api-client.ts
10592
10629
  async function apiRequest(options) {
10593
- const { method, path, serviceUrl, token, body, verbose, timeout = 9e4 } = options;
10630
+ const { method, path, serviceUrl, token, body, verbose, timeout = 9e4, cmd = "unknown", retry = false } = options;
10594
10631
  const url = `${serviceUrl}${path}`;
10595
10632
  const headers = {
10596
10633
  "Content-Type": "application/json"
@@ -10599,36 +10636,67 @@ async function apiRequest(options) {
10599
10636
  headers["Authorization"] = `Bearer ${token}`;
10600
10637
  }
10601
10638
  printVerbose(`${method} ${url}`, verbose);
10639
+ const serializedBody = body ? JSON.stringify(body) : void 0;
10602
10640
  if (body) {
10603
- printVerbose(`Body: ${JSON.stringify(body).slice(0, 500)}`, verbose);
10641
+ printVerbose(`Body: ${serializedBody.slice(0, 500)}`, verbose);
10604
10642
  }
10643
+ const startedAt = Date.now();
10644
+ const bodyBytes = serializedBody ? Buffer.byteLength(serializedBody) : 0;
10645
+ const logBase = { cmd, method, url, body_bytes: bodyBytes, retry };
10605
10646
  let response;
10606
10647
  try {
10607
10648
  response = await fetch(url, {
10608
10649
  method,
10609
10650
  headers,
10610
- body: body ? JSON.stringify(body) : void 0,
10651
+ body: serializedBody,
10611
10652
  signal: AbortSignal.timeout(timeout)
10612
10653
  });
10613
10654
  } catch (err) {
10614
- if (err instanceof DOMException && err.name === "TimeoutError") {
10615
- return { ok: false, error: `Request timed out after ${timeout}ms` };
10616
- }
10617
- return { ok: false, error: `Network error: ${err.message}` };
10655
+ const duration2 = Date.now() - startedAt;
10656
+ const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
10657
+ const category = isTimeout ? "timeout" : "network";
10658
+ const error = isTimeout ? `Request timed out after ${timeout}ms` : `Network error: ${err.message}`;
10659
+ logHttpCall({ ...logBase, duration_ms: duration2, http_status: null, category, error });
10660
+ return { ok: false, error, category };
10618
10661
  }
10619
10662
  let data;
10620
10663
  try {
10621
10664
  data = await response.json();
10622
10665
  } catch {
10623
- return { ok: false, error: `Invalid JSON response (HTTP ${response.status})` };
10666
+ const duration2 = Date.now() - startedAt;
10667
+ const error = `Invalid JSON response (HTTP ${response.status})`;
10668
+ logHttpCall({
10669
+ ...logBase,
10670
+ duration_ms: duration2,
10671
+ http_status: response.status,
10672
+ category: "invalid_json",
10673
+ error
10674
+ });
10675
+ return { ok: false, error, category: "invalid_json" };
10624
10676
  }
10625
10677
  printVerbose(`Response ${response.status}: ${JSON.stringify(data).slice(0, 500)}`, verbose);
10678
+ const duration = Date.now() - startedAt;
10626
10679
  if (!response.ok) {
10627
10680
  const apiErr = data;
10628
10681
  const code = apiErr?.error?.code ?? "UNKNOWN";
10629
10682
  const message = apiErr?.error?.message ?? `HTTP ${response.status}`;
10630
- return { ok: false, error: `${code}: ${message}` };
10683
+ const category = response.status >= 500 ? "http_5xx" : "http_4xx";
10684
+ const error = `${code}: ${message}`;
10685
+ logHttpCall({
10686
+ ...logBase,
10687
+ duration_ms: duration,
10688
+ http_status: response.status,
10689
+ category,
10690
+ error
10691
+ });
10692
+ return { ok: false, error, category };
10631
10693
  }
10694
+ logHttpCall({
10695
+ ...logBase,
10696
+ duration_ms: duration,
10697
+ http_status: response.status,
10698
+ category: "ok"
10699
+ });
10632
10700
  return { ok: true, data };
10633
10701
  }
10634
10702
 
@@ -10858,7 +10926,7 @@ function registerHooksCommands(program2) {
10858
10926
 
10859
10927
  // src/lib/conversation-buffer.ts
10860
10928
  var import_promises5 = require("node:fs/promises");
10861
- var import_node_fs = require("node:fs");
10929
+ var import_node_fs2 = require("node:fs");
10862
10930
  var import_node_child_process4 = require("node:child_process");
10863
10931
  function stripImageReferences(text) {
10864
10932
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
@@ -10890,7 +10958,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
10890
10958
  }
10891
10959
  async function readAndClearConversationBuffer() {
10892
10960
  try {
10893
- if ((0, import_node_fs.existsSync)(CONVERSATION_BUFFER_FILE)) {
10961
+ if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
10894
10962
  const entries = await readBufferEntries();
10895
10963
  await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
10896
10964
  });
@@ -10901,7 +10969,7 @@ async function readAndClearConversationBuffer() {
10901
10969
  };
10902
10970
  }
10903
10971
  }
10904
- if ((0, import_node_fs.existsSync)(INTENT_FILE)) {
10972
+ if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
10905
10973
  try {
10906
10974
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
10907
10975
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -10957,12 +11025,12 @@ function getRecentCommitMessages() {
10957
11025
  }
10958
11026
 
10959
11027
  // src/commands/intent.ts
10960
- var import_node_fs2 = require("node:fs");
11028
+ var import_node_fs3 = require("node:fs");
10961
11029
  function registerIntentCommands(program2) {
10962
11030
  const intent = program2.command("intent").description("Manage intent capture");
10963
11031
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
10964
11032
  try {
10965
- if (!(0, import_node_fs2.existsSync)(GATE_DIR)) {
11033
+ if (!(0, import_node_fs3.existsSync)(GATE_DIR)) {
10966
11034
  process.exit(0);
10967
11035
  }
10968
11036
  const chunks = [];
@@ -11301,17 +11369,17 @@ function registerFeedbackCommand(program2) {
11301
11369
 
11302
11370
  // src/lib/git.ts
11303
11371
  var import_node_child_process5 = require("node:child_process");
11304
- var import_node_fs3 = require("node:fs");
11372
+ var import_node_fs4 = require("node:fs");
11305
11373
  var import_node_path4 = require("node:path");
11306
11374
  function resolveFile(relpath) {
11307
- if ((0, import_node_fs3.existsSync)(relpath)) return relpath;
11308
- if ((0, import_node_fs3.existsSync)(".claude/worktrees")) {
11375
+ if ((0, import_node_fs4.existsSync)(relpath)) return relpath;
11376
+ if ((0, import_node_fs4.existsSync)(".claude/worktrees")) {
11309
11377
  try {
11310
- const entries = (0, import_node_fs3.readdirSync)(".claude/worktrees", { withFileTypes: true });
11378
+ const entries = (0, import_node_fs4.readdirSync)(".claude/worktrees", { withFileTypes: true });
11311
11379
  for (const entry of entries) {
11312
11380
  if (!entry.isDirectory()) continue;
11313
11381
  const candidate = (0, import_node_path4.join)(".claude/worktrees", entry.name, relpath);
11314
- if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
11382
+ if ((0, import_node_fs4.existsSync)(candidate)) return candidate;
11315
11383
  }
11316
11384
  } catch {
11317
11385
  }
@@ -11363,10 +11431,10 @@ function getChangedFiles() {
11363
11431
  function getWorktreeFiles() {
11364
11432
  const result = [];
11365
11433
  const worktreeDir = ".claude/worktrees";
11366
- if (!(0, import_node_fs3.existsSync)(worktreeDir)) return result;
11434
+ if (!(0, import_node_fs4.existsSync)(worktreeDir)) return result;
11367
11435
  try {
11368
11436
  const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
11369
- const entries = (0, import_node_fs3.readdirSync)(worktreeDir, { withFileTypes: true });
11437
+ const entries = (0, import_node_fs4.readdirSync)(worktreeDir, { withFileTypes: true });
11370
11438
  for (const entry of entries) {
11371
11439
  if (!entry.isDirectory()) continue;
11372
11440
  const wtDir = (0, import_node_path4.join)(worktreeDir, entry.name);
@@ -11378,7 +11446,7 @@ function getWorktreeFiles() {
11378
11446
  }
11379
11447
  function scanDir(baseDir, dir, minMtime, result) {
11380
11448
  try {
11381
- const entries = (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true });
11449
+ const entries = (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true });
11382
11450
  for (const entry of entries) {
11383
11451
  const fullPath = (0, import_node_path4.join)(dir, entry.name);
11384
11452
  if (entry.isDirectory()) {
@@ -11388,7 +11456,7 @@ function scanDir(baseDir, dir, minMtime, result) {
11388
11456
  const ext = (0, import_node_path4.extname)(entry.name).slice(1);
11389
11457
  if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
11390
11458
  try {
11391
- const stat = (0, import_node_fs3.statSync)(fullPath);
11459
+ const stat = (0, import_node_fs4.statSync)(fullPath);
11392
11460
  if (stat.mtimeMs >= minMtime) {
11393
11461
  const relPath = fullPath.slice(baseDir.length + 1);
11394
11462
  result.push(relPath);
@@ -11427,7 +11495,7 @@ function getCurrentCommit() {
11427
11495
  }
11428
11496
 
11429
11497
  // src/lib/files.ts
11430
- var import_node_fs4 = require("node:fs");
11498
+ var import_node_fs5 = require("node:fs");
11431
11499
  var import_node_path5 = require("node:path");
11432
11500
  var LANG_MAP = {
11433
11501
  // Analyzable (static analysis + Gemini)
@@ -11504,7 +11572,7 @@ function sortByMtime(files) {
11504
11572
  const resolved = resolveFile(f);
11505
11573
  if (!resolved) return null;
11506
11574
  try {
11507
- const stat = (0, import_node_fs4.statSync)(resolved);
11575
+ const stat = (0, import_node_fs5.statSync)(resolved);
11508
11576
  return { path: f, resolved, mtime: stat.mtimeMs };
11509
11577
  } catch {
11510
11578
  return null;
@@ -11526,7 +11594,7 @@ function collectCodeDelta(files, opts) {
11526
11594
  if (!resolved) continue;
11527
11595
  let size;
11528
11596
  try {
11529
- size = (0, import_node_fs4.statSync)(resolved).size;
11597
+ size = (0, import_node_fs5.statSync)(resolved).size;
11530
11598
  } catch {
11531
11599
  continue;
11532
11600
  }
@@ -11534,7 +11602,7 @@ function collectCodeDelta(files, opts) {
11534
11602
  if (totalSize + size > maxTotalBytes) break;
11535
11603
  let content;
11536
11604
  try {
11537
- content = (0, import_node_fs4.readFileSync)(resolved, "utf-8");
11605
+ content = (0, import_node_fs5.readFileSync)(resolved, "utf-8");
11538
11606
  } catch {
11539
11607
  continue;
11540
11608
  }
@@ -11557,12 +11625,12 @@ function collectCodeDelta(files, opts) {
11557
11625
  }
11558
11626
 
11559
11627
  // src/lib/debounce.ts
11560
- var import_node_fs5 = require("node:fs");
11628
+ var import_node_fs6 = require("node:fs");
11561
11629
  var import_node_crypto = require("node:crypto");
11562
11630
  function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
11563
- if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return null;
11631
+ if (!(0, import_node_fs6.existsSync)(DEBOUNCE_FILE)) return null;
11564
11632
  try {
11565
- const lastTs = parseInt((0, import_node_fs5.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
11633
+ const lastTs = parseInt((0, import_node_fs6.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
11566
11634
  const nowTs = Math.floor(Date.now() / 1e3);
11567
11635
  const elapsed = nowTs - lastTs;
11568
11636
  if (elapsed < debounceSeconds) {
@@ -11574,10 +11642,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
11574
11642
  }
11575
11643
  function checkMtime(files, bypassForRecentCommits) {
11576
11644
  if (bypassForRecentCommits) return null;
11577
- if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return null;
11645
+ if (!(0, import_node_fs6.existsSync)(DEBOUNCE_FILE)) return null;
11578
11646
  let debounceTime;
11579
11647
  try {
11580
- debounceTime = (0, import_node_fs5.statSync)(DEBOUNCE_FILE).mtimeMs;
11648
+ debounceTime = (0, import_node_fs6.statSync)(DEBOUNCE_FILE).mtimeMs;
11581
11649
  } catch {
11582
11650
  return null;
11583
11651
  }
@@ -11585,7 +11653,7 @@ function checkMtime(files, bypassForRecentCommits) {
11585
11653
  const resolved = resolveFile(f);
11586
11654
  if (!resolved) continue;
11587
11655
  try {
11588
- const stat = (0, import_node_fs5.statSync)(resolved);
11656
+ const stat = (0, import_node_fs6.statSync)(resolved);
11589
11657
  if (stat.mtimeMs > debounceTime) {
11590
11658
  return null;
11591
11659
  }
@@ -11601,8 +11669,8 @@ function computeContentHash(files) {
11601
11669
  for (const f of sorted) {
11602
11670
  const resolved = resolveFile(f) ?? f;
11603
11671
  try {
11604
- if ((0, import_node_fs5.existsSync)(resolved)) {
11605
- hash.update((0, import_node_fs5.readFileSync)(resolved));
11672
+ if ((0, import_node_fs6.existsSync)(resolved)) {
11673
+ hash.update((0, import_node_fs6.readFileSync)(resolved));
11606
11674
  }
11607
11675
  } catch {
11608
11676
  }
@@ -11611,9 +11679,9 @@ function computeContentHash(files) {
11611
11679
  }
11612
11680
  function checkContentHash(files) {
11613
11681
  const hash = computeContentHash(files);
11614
- if ((0, import_node_fs5.existsSync)(HASH_FILE)) {
11682
+ if ((0, import_node_fs6.existsSync)(HASH_FILE)) {
11615
11683
  try {
11616
- const storedHash = (0, import_node_fs5.readFileSync)(HASH_FILE, "utf-8").trim();
11684
+ const storedHash = (0, import_node_fs6.readFileSync)(HASH_FILE, "utf-8").trim();
11617
11685
  if (hash === storedHash) {
11618
11686
  return { skip: "No source changes since last analysis", hash };
11619
11687
  }
@@ -11623,23 +11691,23 @@ function checkContentHash(files) {
11623
11691
  return { skip: null, hash };
11624
11692
  }
11625
11693
  function recordAnalysisStart() {
11626
- (0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
11627
- (0, import_node_fs5.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
11694
+ (0, import_node_fs6.mkdirSync)(GATE_DIR, { recursive: true });
11695
+ (0, import_node_fs6.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
11628
11696
  }
11629
11697
  function recordPassHash(hash) {
11630
- (0, import_node_fs5.writeFileSync)(HASH_FILE, hash);
11698
+ (0, import_node_fs6.writeFileSync)(HASH_FILE, hash);
11631
11699
  }
11632
11700
  function narrowToRecent(files) {
11633
- if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return files;
11701
+ if (!(0, import_node_fs6.existsSync)(DEBOUNCE_FILE)) return files;
11634
11702
  let debounceTime;
11635
11703
  try {
11636
- debounceTime = (0, import_node_fs5.statSync)(DEBOUNCE_FILE).mtimeMs;
11704
+ debounceTime = (0, import_node_fs6.statSync)(DEBOUNCE_FILE).mtimeMs;
11637
11705
  } catch {
11638
11706
  return files;
11639
11707
  }
11640
11708
  const recent = files.filter((f) => {
11641
11709
  try {
11642
- return (0, import_node_fs5.existsSync)(f) && (0, import_node_fs5.statSync)(f).mtimeMs > debounceTime;
11710
+ return (0, import_node_fs6.existsSync)(f) && (0, import_node_fs6.statSync)(f).mtimeMs > debounceTime;
11643
11711
  } catch {
11644
11712
  return false;
11645
11713
  }
@@ -11647,9 +11715,9 @@ function narrowToRecent(files) {
11647
11715
  return recent.length > 0 ? recent : files;
11648
11716
  }
11649
11717
  function readIteration(currentCommit, _contentHash) {
11650
- if (!(0, import_node_fs5.existsSync)(ITERATION_FILE)) return 1;
11718
+ if (!(0, import_node_fs6.existsSync)(ITERATION_FILE)) return 1;
11651
11719
  try {
11652
- const stored = (0, import_node_fs5.readFileSync)(ITERATION_FILE, "utf-8").trim();
11720
+ const stored = (0, import_node_fs6.readFileSync)(ITERATION_FILE, "utf-8").trim();
11653
11721
  const parts = stored.split(":");
11654
11722
  const iter = parseInt(parts[0], 10);
11655
11723
  const storedCommit = parts[1] ?? "";
@@ -11677,14 +11745,14 @@ function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS, conte
11677
11745
  return { skip: null, iteration };
11678
11746
  }
11679
11747
  function writeIteration(iteration, commit, _contentHash) {
11680
- (0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
11748
+ (0, import_node_fs6.mkdirSync)(GATE_DIR, { recursive: true });
11681
11749
  const ts = Math.floor(Date.now() / 1e3);
11682
- (0, import_node_fs5.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}`);
11750
+ (0, import_node_fs6.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}`);
11683
11751
  }
11684
11752
 
11685
11753
  // src/lib/static-analysis.ts
11686
11754
  var import_node_child_process6 = require("node:child_process");
11687
- var import_node_fs6 = require("node:fs");
11755
+ var import_node_fs7 = require("node:fs");
11688
11756
  var SEVERITY_ORDER = {
11689
11757
  Error: 0,
11690
11758
  Critical: 0,
@@ -11711,7 +11779,7 @@ function runCodacyAnalysis(files) {
11711
11779
  if (files.length === 0) return empty;
11712
11780
  const existingFiles = files.filter((f) => {
11713
11781
  try {
11714
- return (0, import_node_fs6.existsSync)(f);
11782
+ return (0, import_node_fs7.existsSync)(f);
11715
11783
  } catch {
11716
11784
  return false;
11717
11785
  }
@@ -11773,7 +11841,7 @@ function runCodacyAnalysis(files) {
11773
11841
  }
11774
11842
 
11775
11843
  // src/lib/specs.ts
11776
- var import_node_fs7 = require("node:fs");
11844
+ var import_node_fs8 = require("node:fs");
11777
11845
  var import_node_path6 = require("node:path");
11778
11846
  var SPEC_CANDIDATES = [
11779
11847
  "CLAUDE.md",
@@ -11798,15 +11866,15 @@ function discoverSpecs() {
11798
11866
  if (result.length >= MAX_SPEC_FILES) return false;
11799
11867
  if (totalBytes >= MAX_TOTAL_SPEC_BYTES) return false;
11800
11868
  if (seen.has(specPath)) return true;
11801
- if (!(0, import_node_fs7.existsSync)(specPath)) return true;
11869
+ if (!(0, import_node_fs8.existsSync)(specPath)) return true;
11802
11870
  seen.add(specPath);
11803
11871
  const remaining = MAX_TOTAL_SPEC_BYTES - totalBytes;
11804
11872
  const readBytes = Math.min(MAX_SPEC_FILE_BYTES, remaining);
11805
11873
  try {
11806
11874
  const buf = Buffer.alloc(readBytes);
11807
- const fd = (0, import_node_fs7.openSync)(specPath, "r");
11808
- const bytesRead = (0, import_node_fs7.readSync)(fd, buf, 0, readBytes, 0);
11809
- (0, import_node_fs7.closeSync)(fd);
11875
+ const fd = (0, import_node_fs8.openSync)(specPath, "r");
11876
+ const bytesRead = (0, import_node_fs8.readSync)(fd, buf, 0, readBytes, 0);
11877
+ (0, import_node_fs8.closeSync)(fd);
11810
11878
  const content = buf.slice(0, bytesRead).toString("utf-8");
11811
11879
  if (!content) return true;
11812
11880
  result.push({ path: specPath, content });
@@ -11819,7 +11887,7 @@ function discoverSpecs() {
11819
11887
  if (!addSpec(candidate)) break;
11820
11888
  }
11821
11889
  for (const dir of ["spec", "docs"]) {
11822
- if (!(0, import_node_fs7.existsSync)(dir)) continue;
11890
+ if (!(0, import_node_fs8.existsSync)(dir)) continue;
11823
11891
  try {
11824
11892
  const mdFiles = findMdFiles(dir, 2).sort();
11825
11893
  for (const mdFile of mdFiles) {
@@ -11834,7 +11902,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
11834
11902
  if (depth >= maxDepth) return [];
11835
11903
  const result = [];
11836
11904
  try {
11837
- const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
11905
+ const entries = (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true });
11838
11906
  for (const entry of entries) {
11839
11907
  const fullPath = (0, import_node_path6.join)(dir, entry.name);
11840
11908
  if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -11853,14 +11921,14 @@ function discoverPlans() {
11853
11921
  const candidates = [];
11854
11922
  const seen = /* @__PURE__ */ new Set();
11855
11923
  for (const plansDir of [localPlansDir, homePlansDir]) {
11856
- if (!(0, import_node_fs7.existsSync)(plansDir)) continue;
11924
+ if (!(0, import_node_fs8.existsSync)(plansDir)) continue;
11857
11925
  try {
11858
- for (const f of (0, import_node_fs7.readdirSync)(plansDir)) {
11926
+ for (const f of (0, import_node_fs8.readdirSync)(plansDir)) {
11859
11927
  if (!f.endsWith(".md") || seen.has(f)) continue;
11860
11928
  seen.add(f);
11861
11929
  const fullPath = (0, import_node_path6.join)(plansDir, f);
11862
11930
  try {
11863
- const stat = (0, import_node_fs7.statSync)(fullPath);
11931
+ const stat = (0, import_node_fs8.statSync)(fullPath);
11864
11932
  candidates.push({ name: f, path: fullPath, mtime: stat.mtimeMs, size: stat.size });
11865
11933
  } catch {
11866
11934
  }
@@ -11873,7 +11941,7 @@ function discoverPlans() {
11873
11941
  for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
11874
11942
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
11875
11943
  try {
11876
- const content = (0, import_node_fs7.readFileSync)(entry.path, "utf-8");
11944
+ const content = (0, import_node_fs8.readFileSync)(entry.path, "utf-8");
11877
11945
  result.push({ name: entry.name, content });
11878
11946
  } catch {
11879
11947
  }
@@ -11882,19 +11950,19 @@ function discoverPlans() {
11882
11950
  }
11883
11951
 
11884
11952
  // src/lib/snapshot.ts
11885
- var import_node_fs8 = require("node:fs");
11953
+ var import_node_fs9 = require("node:fs");
11886
11954
  var import_node_path7 = require("node:path");
11887
11955
  var import_node_child_process7 = require("node:child_process");
11888
11956
  function generateSnapshotDiffs(files) {
11889
- if (!(0, import_node_fs8.existsSync)(SNAPSHOT_DIR)) {
11957
+ if (!(0, import_node_fs9.existsSync)(SNAPSHOT_DIR)) {
11890
11958
  return { diffs: [], has_snapshots: false };
11891
11959
  }
11892
11960
  const diffs = [];
11893
11961
  for (const file of files) {
11894
11962
  const snapshotPath = (0, import_node_path7.join)(SNAPSHOT_DIR, file.path);
11895
11963
  const language = file.language ?? detectLanguage(file.path);
11896
- if ((0, import_node_fs8.existsSync)(snapshotPath)) {
11897
- const oldContent = (0, import_node_fs8.readFileSync)(snapshotPath, "utf-8");
11964
+ if ((0, import_node_fs9.existsSync)(snapshotPath)) {
11965
+ const oldContent = (0, import_node_fs9.readFileSync)(snapshotPath, "utf-8");
11898
11966
  if (oldContent === file.content) continue;
11899
11967
  const diff = computeDiff(oldContent, file.content, file.path);
11900
11968
  if (diff) {
@@ -11920,8 +11988,8 @@ function saveSnapshots(files) {
11920
11988
  for (const file of files) {
11921
11989
  const snapshotPath = (0, import_node_path7.join)(SNAPSHOT_DIR, file.path);
11922
11990
  snapshotPaths.add(snapshotPath);
11923
- (0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(snapshotPath), { recursive: true });
11924
- (0, import_node_fs8.writeFileSync)(snapshotPath, file.content);
11991
+ (0, import_node_fs9.mkdirSync)((0, import_node_path7.dirname)(snapshotPath), { recursive: true });
11992
+ (0, import_node_fs9.writeFileSync)(snapshotPath, file.content);
11925
11993
  }
11926
11994
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
11927
11995
  }
@@ -11929,9 +11997,9 @@ function computeDiff(oldContent, newContent, filePath) {
11929
11997
  const tmpOld = (0, import_node_path7.join)(SNAPSHOT_DIR, ".diff-old.tmp");
11930
11998
  const tmpNew = (0, import_node_path7.join)(SNAPSHOT_DIR, ".diff-new.tmp");
11931
11999
  try {
11932
- (0, import_node_fs8.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
11933
- (0, import_node_fs8.writeFileSync)(tmpOld, oldContent);
11934
- (0, import_node_fs8.writeFileSync)(tmpNew, newContent);
12000
+ (0, import_node_fs9.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
12001
+ (0, import_node_fs9.writeFileSync)(tmpOld, oldContent);
12002
+ (0, import_node_fs9.writeFileSync)(tmpNew, newContent);
11935
12003
  const result = (0, import_node_child_process7.execSync)(
11936
12004
  `git diff --no-index --unified=10 -- "${tmpOld}" "${tmpNew}"`,
11937
12005
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
@@ -11945,32 +12013,32 @@ function computeDiff(oldContent, newContent, filePath) {
11945
12013
  return null;
11946
12014
  } finally {
11947
12015
  try {
11948
- (0, import_node_fs8.unlinkSync)(tmpOld);
12016
+ (0, import_node_fs9.unlinkSync)(tmpOld);
11949
12017
  } catch {
11950
12018
  }
11951
12019
  try {
11952
- (0, import_node_fs8.unlinkSync)(tmpNew);
12020
+ (0, import_node_fs9.unlinkSync)(tmpNew);
11953
12021
  } catch {
11954
12022
  }
11955
12023
  }
11956
12024
  }
11957
12025
  function cleanStaleSnapshots(dir, keepSet) {
11958
- if (!(0, import_node_fs8.existsSync)(dir)) return;
12026
+ if (!(0, import_node_fs9.existsSync)(dir)) return;
11959
12027
  try {
11960
- const entries = (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true });
12028
+ const entries = (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true });
11961
12029
  for (const entry of entries) {
11962
12030
  if (entry.name.startsWith(".")) continue;
11963
12031
  const fullPath = (0, import_node_path7.join)(dir, entry.name);
11964
12032
  if (entry.isDirectory()) {
11965
12033
  cleanStaleSnapshots(fullPath, keepSet);
11966
12034
  try {
11967
- const remaining = (0, import_node_fs8.readdirSync)(fullPath);
11968
- if (remaining.length === 0) (0, import_node_fs8.rmdirSync)(fullPath);
12035
+ const remaining = (0, import_node_fs9.readdirSync)(fullPath);
12036
+ if (remaining.length === 0) (0, import_node_fs9.rmdirSync)(fullPath);
11969
12037
  } catch {
11970
12038
  }
11971
12039
  } else if (!keepSet.has(fullPath)) {
11972
12040
  try {
11973
- (0, import_node_fs8.unlinkSync)(fullPath);
12041
+ (0, import_node_fs9.unlinkSync)(fullPath);
11974
12042
  } catch {
11975
12043
  }
11976
12044
  }
@@ -11980,14 +12048,14 @@ function cleanStaleSnapshots(dir, keepSet) {
11980
12048
  }
11981
12049
 
11982
12050
  // src/lib/offline.ts
11983
- var import_node_fs9 = require("node:fs");
12051
+ var import_node_fs10 = require("node:fs");
11984
12052
  var import_node_crypto2 = require("node:crypto");
11985
12053
  function cacheRequest(body) {
11986
12054
  try {
11987
- (0, import_node_fs9.mkdirSync)(CACHE_DIR, { recursive: true });
12055
+ (0, import_node_fs10.mkdirSync)(CACHE_DIR, { recursive: true });
11988
12056
  const suffix = (0, import_node_crypto2.randomBytes)(4).toString("hex");
11989
12057
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
11990
- (0, import_node_fs9.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
12058
+ (0, import_node_fs10.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
11991
12059
  } catch {
11992
12060
  }
11993
12061
  }
@@ -12070,7 +12138,7 @@ function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPromp
12070
12138
  }
12071
12139
 
12072
12140
  // src/lib/transcript.ts
12073
- var import_node_fs10 = require("node:fs");
12141
+ var import_node_fs11 = require("node:fs");
12074
12142
  var MAX_READ_BYTES = 256 * 1024;
12075
12143
  var SMALL_FILE_BYTES = 64 * 1024;
12076
12144
  var MAX_FILES_LIST = 20;
@@ -12092,14 +12160,14 @@ async function extractActionSummary(transcriptPath) {
12092
12160
  function readTurnLines(transcriptPath) {
12093
12161
  let size;
12094
12162
  try {
12095
- size = (0, import_node_fs10.statSync)(transcriptPath).size;
12163
+ size = (0, import_node_fs11.statSync)(transcriptPath).size;
12096
12164
  } catch {
12097
12165
  return null;
12098
12166
  }
12099
12167
  if (size === 0) return null;
12100
12168
  let raw;
12101
12169
  if (size <= SMALL_FILE_BYTES) {
12102
- raw = (0, import_node_fs10.readFileSync)(transcriptPath, "utf-8");
12170
+ raw = (0, import_node_fs11.readFileSync)(transcriptPath, "utf-8");
12103
12171
  } else {
12104
12172
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
12105
12173
  const fd = require("node:fs").openSync(transcriptPath, "r");
@@ -12463,19 +12531,44 @@ async function runAnalyze(opts, globals) {
12463
12531
  }
12464
12532
  requestBody.intent_context = intentContext;
12465
12533
  }
12466
- const result = await apiRequest({
12534
+ const ANALYZE_TIMEOUT_MS = 1e5;
12535
+ let result = await apiRequest({
12467
12536
  method: "POST",
12468
12537
  path: "/analyze",
12469
12538
  serviceUrl: urlResult.data,
12470
12539
  token: tokenResult.data.token,
12471
12540
  body: requestBody,
12472
12541
  verbose: globals.verbose,
12473
- timeout: 12e4
12542
+ timeout: ANALYZE_TIMEOUT_MS,
12543
+ cmd: "analyze"
12474
12544
  });
12545
+ if (!result.ok && (result.category === "network" || result.category === "timeout")) {
12546
+ logEvent("analyze_warm_retry", { first_error: result.error, category: result.category });
12547
+ await apiRequest({
12548
+ method: "GET",
12549
+ path: "/memory",
12550
+ serviceUrl: urlResult.data,
12551
+ token: tokenResult.data.token,
12552
+ verbose: globals.verbose,
12553
+ timeout: 15e3,
12554
+ cmd: "analyze_warmup"
12555
+ });
12556
+ result = await apiRequest({
12557
+ method: "POST",
12558
+ path: "/analyze",
12559
+ serviceUrl: urlResult.data,
12560
+ token: tokenResult.data.token,
12561
+ body: requestBody,
12562
+ verbose: globals.verbose,
12563
+ timeout: ANALYZE_TIMEOUT_MS,
12564
+ cmd: "analyze",
12565
+ retry: true
12566
+ });
12567
+ }
12475
12568
  if (!result.ok) {
12476
12569
  cacheRequest(requestBody);
12477
12570
  const fallback = buildOfflineFallback(
12478
- `GATE.md service error \u2014 offline mode`,
12571
+ `GATE.md offline \u2014 ${result.error}`,
12479
12572
  staticResults
12480
12573
  );
12481
12574
  printJsonCompact(fallback);
@@ -12587,7 +12680,7 @@ async function runAnalyze(opts, globals) {
12587
12680
  }
12588
12681
 
12589
12682
  // src/commands/review.ts
12590
- var import_node_fs11 = require("node:fs");
12683
+ var import_node_fs12 = require("node:fs");
12591
12684
  function registerReviewCommand(program2) {
12592
12685
  program2.command("review").description("Run on-demand GATE.md analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
12593
12686
  const globals = program2.opts();
@@ -12606,7 +12699,7 @@ async function runReview(opts, globals) {
12606
12699
  const securityFiles = filterSecurity(allFiles);
12607
12700
  let staticResults;
12608
12701
  if (isCodacyAvailable()) {
12609
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs11.existsSync)(f) || resolveFile(f) !== null);
12702
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs12.existsSync)(f) || resolveFile(f) !== null);
12610
12703
  staticResults = runCodacyAnalysis(scannable);
12611
12704
  } else {
12612
12705
  staticResults = {
@@ -12631,7 +12724,7 @@ async function runReview(opts, globals) {
12631
12724
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
12632
12725
  specs = [];
12633
12726
  for (const p of specPaths) {
12634
- if (!(0, import_node_fs11.existsSync)(p)) continue;
12727
+ if (!(0, import_node_fs12.existsSync)(p)) continue;
12635
12728
  try {
12636
12729
  const { readFileSync: readFileSync6 } = await import("node:fs");
12637
12730
  const content = readFileSync6(p, "utf-8");
@@ -12692,7 +12785,7 @@ async function runReview(opts, globals) {
12692
12785
  }
12693
12786
 
12694
12787
  // src/commands/init.ts
12695
- var import_node_fs12 = require("node:fs");
12788
+ var import_node_fs13 = require("node:fs");
12696
12789
  var import_promises8 = require("node:fs/promises");
12697
12790
  var import_node_path8 = require("node:path");
12698
12791
  var import_node_child_process8 = require("node:child_process");
@@ -12706,7 +12799,7 @@ function resolveDataDir() {
12706
12799
  // local dev: running from repo root
12707
12800
  ];
12708
12801
  for (const candidate of candidates) {
12709
- if ((0, import_node_fs12.existsSync)((0, import_node_path8.join)(candidate, "skills"))) {
12802
+ if ((0, import_node_fs13.existsSync)((0, import_node_path8.join)(candidate, "skills"))) {
12710
12803
  return candidate;
12711
12804
  }
12712
12805
  }
@@ -12722,7 +12815,7 @@ function registerInitCommand(program2) {
12722
12815
  program2.command("init").description("Initialize GATE.md in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
12723
12816
  const force = opts.force ?? false;
12724
12817
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
12725
- const isProject = projectMarkers.some((m) => (0, import_node_fs12.existsSync)(m));
12818
+ const isProject = projectMarkers.some((m) => (0, import_node_fs13.existsSync)(m));
12726
12819
  if (!isProject) {
12727
12820
  printError("No project detected in the current directory.");
12728
12821
  printInfo('Run "gate init" from your project root.');
@@ -12782,14 +12875,14 @@ function registerInitCommand(program2) {
12782
12875
  for (const skill of skills) {
12783
12876
  const src = (0, import_node_path8.join)(skillsSource, skill);
12784
12877
  const dest = (0, import_node_path8.join)(skillsDest, skill);
12785
- if (!(0, import_node_fs12.existsSync)(src)) {
12878
+ if (!(0, import_node_fs13.existsSync)(src)) {
12786
12879
  printWarn(` Skill data not found: ${skill}`);
12787
12880
  continue;
12788
12881
  }
12789
- if ((0, import_node_fs12.existsSync)(dest) && !force) {
12882
+ if ((0, import_node_fs13.existsSync)(dest) && !force) {
12790
12883
  const srcSkill = (0, import_node_path8.join)(src, "SKILL.md");
12791
12884
  const destSkill = (0, import_node_path8.join)(dest, "SKILL.md");
12792
- if ((0, import_node_fs12.existsSync)(destSkill)) {
12885
+ if ((0, import_node_fs13.existsSync)(destSkill)) {
12793
12886
  try {
12794
12887
  const srcContent = await (0, import_promises8.readFile)(srcSkill, "utf-8");
12795
12888
  const destContent = await (0, import_promises8.readFile)(destSkill, "utf-8");
@@ -12835,7 +12928,7 @@ function registerInitCommand(program2) {
12835
12928
  }
12836
12929
 
12837
12930
  // src/cli.ts
12838
- program.name("gate").description("CLI for GATE.md quality gate service").version("0.14.1").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
12931
+ program.name("gate").description("CLI for GATE.md quality gate service").version("0.14.2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
12839
12932
  registerAuthCommands(program);
12840
12933
  registerHooksCommands(program);
12841
12934
  registerIntentCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/gate-cli",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "CLI for GATE.md quality gate service",
5
5
  "bin": {
6
6
  "gate": "./bin/gate.js"