@codacy/gate-cli 0.4.1 → 0.6.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 +316 -121
  2. package/package.json +1 -1
package/bin/gate.js CHANGED
@@ -10373,6 +10373,10 @@ var MAX_TOTAL_SPEC_BYTES = 30720;
10373
10373
  var MAX_PLAN_FILES = 3;
10374
10374
  var MAX_PLAN_FILE_BYTES = 10240;
10375
10375
  var MAX_INTENT_CHARS = 2e3;
10376
+ var SNAPSHOT_DIR = `${GATE_DIR}/.snapshot`;
10377
+ var CONVERSATION_BUFFER_FILE = `${GATE_DIR}/.conversation-buffer`;
10378
+ var CONVERSATION_MAX_ENTRIES = 10;
10379
+ var CONVERSATION_WINDOW_MINUTES = 15;
10376
10380
  var MAX_FINDINGS = 25;
10377
10381
  var ANALYZABLE_EXTENSIONS = /* @__PURE__ */ new Set([
10378
10382
  "ts",
@@ -10852,41 +10856,113 @@ function registerHooksCommands(program2) {
10852
10856
  });
10853
10857
  }
10854
10858
 
10855
- // src/lib/intent.ts
10859
+ // src/lib/conversation-buffer.ts
10856
10860
  var import_promises5 = require("node:fs/promises");
10857
- async function writeIntent(prompt, sessionId) {
10861
+ var import_node_fs = require("node:fs");
10862
+ var import_node_child_process4 = require("node:child_process");
10863
+ function stripImageReferences(text) {
10864
+ return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
10865
+ }
10866
+ async function appendToConversationBuffer(prompt, sessionId) {
10858
10867
  try {
10859
10868
  await (0, import_promises5.mkdir)(GATE_DIR, { recursive: true });
10860
- const truncated = prompt.length > MAX_INTENT_CHARS ? prompt.slice(0, MAX_INTENT_CHARS) : prompt;
10861
- const data = {
10862
- prompt: truncated,
10869
+ let sanitized = prompt.length > MAX_INTENT_CHARS ? prompt.slice(0, MAX_INTENT_CHARS) : prompt;
10870
+ sanitized = stripImageReferences(sanitized);
10871
+ const entry = {
10872
+ prompt: sanitized,
10863
10873
  session_id: sessionId,
10864
10874
  captured_at: (/* @__PURE__ */ new Date()).toISOString()
10865
10875
  };
10866
- const tmpFile = `${INTENT_FILE}.tmp`;
10867
- await (0, import_promises5.writeFile)(tmpFile, JSON.stringify(data) + "\n");
10868
- await (0, import_promises5.rename)(tmpFile, INTENT_FILE);
10876
+ const entries = await readBufferEntries();
10877
+ const cutoff = Date.now() - CONVERSATION_WINDOW_MINUTES * 60 * 1e3;
10878
+ const recent = entries.filter((e) => {
10879
+ const ts = new Date(e.captured_at).getTime();
10880
+ return !isNaN(ts) && ts > cutoff;
10881
+ });
10882
+ recent.push(entry);
10883
+ const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
10884
+ const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
10885
+ const tmpFile = `${CONVERSATION_BUFFER_FILE}.tmp`;
10886
+ await (0, import_promises5.writeFile)(tmpFile, content);
10887
+ await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
10869
10888
  } catch {
10870
10889
  }
10871
10890
  }
10872
- async function readAndClearIntent() {
10891
+ async function readAndClearConversationBuffer() {
10873
10892
  try {
10874
- const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
10875
- await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
10876
- });
10877
- return JSON.parse(content);
10893
+ if ((0, import_node_fs.existsSync)(CONVERSATION_BUFFER_FILE)) {
10894
+ const entries = await readBufferEntries();
10895
+ await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
10896
+ });
10897
+ if (entries.length > 0) {
10898
+ return {
10899
+ prompts: entries,
10900
+ recent_commits: getRecentCommitMessages()
10901
+ };
10902
+ }
10903
+ }
10904
+ if ((0, import_node_fs.existsSync)(INTENT_FILE)) {
10905
+ try {
10906
+ const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
10907
+ await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
10908
+ });
10909
+ const data = JSON.parse(content);
10910
+ if (data.prompt) {
10911
+ return {
10912
+ prompts: [{
10913
+ prompt: stripImageReferences(data.prompt),
10914
+ session_id: data.session_id ?? "",
10915
+ captured_at: data.captured_at ?? (/* @__PURE__ */ new Date()).toISOString()
10916
+ }],
10917
+ recent_commits: getRecentCommitMessages()
10918
+ };
10919
+ }
10920
+ } catch {
10921
+ }
10922
+ }
10923
+ return null;
10878
10924
  } catch {
10879
10925
  return null;
10880
10926
  }
10881
10927
  }
10928
+ async function readBufferEntries() {
10929
+ try {
10930
+ const content = await (0, import_promises5.readFile)(CONVERSATION_BUFFER_FILE, "utf-8");
10931
+ const entries = [];
10932
+ for (const line of content.split("\n")) {
10933
+ const trimmed = line.trim();
10934
+ if (!trimmed) continue;
10935
+ try {
10936
+ const parsed = JSON.parse(trimmed);
10937
+ if (parsed.prompt) entries.push(parsed);
10938
+ } catch {
10939
+ }
10940
+ }
10941
+ return entries;
10942
+ } catch {
10943
+ return [];
10944
+ }
10945
+ }
10946
+ function getRecentCommitMessages() {
10947
+ try {
10948
+ const output = (0, import_node_child_process4.execSync)(
10949
+ 'git log --since="30 minutes ago" --format="%s" -5',
10950
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
10951
+ ).trim();
10952
+ if (!output) return [];
10953
+ return output.split("\n").filter((l) => l.length > 0);
10954
+ } catch {
10955
+ return [];
10956
+ }
10957
+ }
10882
10958
 
10883
10959
  // src/commands/intent.ts
10884
- var import_node_fs = require("node:fs");
10960
+ var import_node_fs2 = require("node:fs");
10885
10961
  function registerIntentCommands(program2) {
10886
10962
  const intent = program2.command("intent").description("Manage intent capture");
10887
10963
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
10888
10964
  try {
10889
- if (!(0, import_node_fs.existsSync)(GATE_DIR)) {
10965
+ if (!(0, import_node_fs2.existsSync)(GATE_DIR)) {
10890
10966
  process.exit(0);
10891
10967
  }
10892
10968
  const chunks = [];
@@ -10907,7 +10983,7 @@ function registerIntentCommands(program2) {
10907
10983
  if (!prompt) {
10908
10984
  process.exit(0);
10909
10985
  }
10910
- await writeIntent(prompt, event.session_id ?? "");
10986
+ await appendToConversationBuffer(prompt, event.session_id ?? "");
10911
10987
  } catch {
10912
10988
  }
10913
10989
  process.exit(0);
@@ -11224,18 +11300,18 @@ function registerFeedbackCommand(program2) {
11224
11300
  }
11225
11301
 
11226
11302
  // src/lib/git.ts
11227
- var import_node_child_process4 = require("node:child_process");
11228
- var import_node_fs2 = require("node:fs");
11303
+ var import_node_child_process5 = require("node:child_process");
11304
+ var import_node_fs3 = require("node:fs");
11229
11305
  var import_node_path4 = require("node:path");
11230
11306
  function resolveFile(relpath) {
11231
- if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
11232
- if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
11307
+ if ((0, import_node_fs3.existsSync)(relpath)) return relpath;
11308
+ if ((0, import_node_fs3.existsSync)(".claude/worktrees")) {
11233
11309
  try {
11234
- const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
11310
+ const entries = (0, import_node_fs3.readdirSync)(".claude/worktrees", { withFileTypes: true });
11235
11311
  for (const entry of entries) {
11236
11312
  if (!entry.isDirectory()) continue;
11237
11313
  const candidate = (0, import_node_path4.join)(".claude/worktrees", entry.name, relpath);
11238
- if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
11314
+ if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
11239
11315
  }
11240
11316
  } catch {
11241
11317
  }
@@ -11244,7 +11320,7 @@ function resolveFile(relpath) {
11244
11320
  }
11245
11321
  function execGit(cmd) {
11246
11322
  try {
11247
- return (0, import_node_child_process4.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
11323
+ return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
11248
11324
  } catch {
11249
11325
  return "";
11250
11326
  }
@@ -11254,6 +11330,7 @@ function splitLines(s) {
11254
11330
  }
11255
11331
  function getChangedFiles() {
11256
11332
  const sets = /* @__PURE__ */ new Set();
11333
+ let hasRecentCommitFiles = false;
11257
11334
  for (const f of splitLines(execGit("git diff --name-only HEAD"))) {
11258
11335
  sets.add(f);
11259
11336
  }
@@ -11269,22 +11346,26 @@ function getChangedFiles() {
11269
11346
  const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
11270
11347
  const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
11271
11348
  if (commitAge < 120 && !hasUnstaged && !hasStaged) {
11272
- for (const f of splitLines(execGit("git diff --name-only HEAD~1..HEAD"))) {
11273
- sets.add(f);
11349
+ const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
11350
+ if (recentFiles.length > 0) {
11351
+ hasRecentCommitFiles = true;
11352
+ for (const f of recentFiles) {
11353
+ sets.add(f);
11354
+ }
11274
11355
  }
11275
11356
  }
11276
11357
  for (const f of getWorktreeFiles()) {
11277
11358
  sets.add(f);
11278
11359
  }
11279
- return Array.from(sets);
11360
+ return { files: Array.from(sets), hasRecentCommitFiles };
11280
11361
  }
11281
11362
  function getWorktreeFiles() {
11282
11363
  const result = [];
11283
11364
  const worktreeDir = ".claude/worktrees";
11284
- if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
11365
+ if (!(0, import_node_fs3.existsSync)(worktreeDir)) return result;
11285
11366
  try {
11286
11367
  const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
11287
- const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
11368
+ const entries = (0, import_node_fs3.readdirSync)(worktreeDir, { withFileTypes: true });
11288
11369
  for (const entry of entries) {
11289
11370
  if (!entry.isDirectory()) continue;
11290
11371
  const wtDir = (0, import_node_path4.join)(worktreeDir, entry.name);
@@ -11296,7 +11377,7 @@ function getWorktreeFiles() {
11296
11377
  }
11297
11378
  function scanDir(baseDir, dir, minMtime, result) {
11298
11379
  try {
11299
- const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
11380
+ const entries = (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true });
11300
11381
  for (const entry of entries) {
11301
11382
  const fullPath = (0, import_node_path4.join)(dir, entry.name);
11302
11383
  if (entry.isDirectory()) {
@@ -11306,7 +11387,7 @@ function scanDir(baseDir, dir, minMtime, result) {
11306
11387
  const ext = (0, import_node_path4.extname)(entry.name).slice(1);
11307
11388
  if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
11308
11389
  try {
11309
- const stat = (0, import_node_fs2.statSync)(fullPath);
11390
+ const stat = (0, import_node_fs3.statSync)(fullPath);
11310
11391
  if (stat.mtimeMs >= minMtime) {
11311
11392
  const relPath = fullPath.slice(baseDir.length + 1);
11312
11393
  result.push(relPath);
@@ -11345,7 +11426,7 @@ function getCurrentCommit() {
11345
11426
  }
11346
11427
 
11347
11428
  // src/lib/files.ts
11348
- var import_node_fs3 = require("node:fs");
11429
+ var import_node_fs4 = require("node:fs");
11349
11430
  var import_node_path5 = require("node:path");
11350
11431
  var LANG_MAP = {
11351
11432
  // Analyzable (static analysis + Gemini)
@@ -11422,7 +11503,7 @@ function sortByMtime(files) {
11422
11503
  const resolved = resolveFile(f);
11423
11504
  if (!resolved) return null;
11424
11505
  try {
11425
- const stat = (0, import_node_fs3.statSync)(resolved);
11506
+ const stat = (0, import_node_fs4.statSync)(resolved);
11426
11507
  return { path: f, resolved, mtime: stat.mtimeMs };
11427
11508
  } catch {
11428
11509
  return null;
@@ -11444,7 +11525,7 @@ function collectCodeDelta(files, opts) {
11444
11525
  if (!resolved) continue;
11445
11526
  let size;
11446
11527
  try {
11447
- size = (0, import_node_fs3.statSync)(resolved).size;
11528
+ size = (0, import_node_fs4.statSync)(resolved).size;
11448
11529
  } catch {
11449
11530
  continue;
11450
11531
  }
@@ -11452,7 +11533,7 @@ function collectCodeDelta(files, opts) {
11452
11533
  if (totalSize + size > maxTotalBytes) break;
11453
11534
  let content;
11454
11535
  try {
11455
- content = (0, import_node_fs3.readFileSync)(resolved, "utf-8");
11536
+ content = (0, import_node_fs4.readFileSync)(resolved, "utf-8");
11456
11537
  } catch {
11457
11538
  continue;
11458
11539
  }
@@ -11475,12 +11556,12 @@ function collectCodeDelta(files, opts) {
11475
11556
  }
11476
11557
 
11477
11558
  // src/lib/debounce.ts
11478
- var import_node_fs4 = require("node:fs");
11559
+ var import_node_fs5 = require("node:fs");
11479
11560
  var import_node_crypto = require("node:crypto");
11480
11561
  function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
11481
- if (!(0, import_node_fs4.existsSync)(DEBOUNCE_FILE)) return null;
11562
+ if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return null;
11482
11563
  try {
11483
- const lastTs = parseInt((0, import_node_fs4.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
11564
+ const lastTs = parseInt((0, import_node_fs5.readFileSync)(DEBOUNCE_FILE, "utf-8").trim(), 10);
11484
11565
  const nowTs = Math.floor(Date.now() / 1e3);
11485
11566
  const elapsed = nowTs - lastTs;
11486
11567
  if (elapsed < debounceSeconds) {
@@ -11490,11 +11571,12 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS) {
11490
11571
  }
11491
11572
  return null;
11492
11573
  }
11493
- function checkMtime(files) {
11494
- if (!(0, import_node_fs4.existsSync)(DEBOUNCE_FILE)) return null;
11574
+ function checkMtime(files, bypassForRecentCommits) {
11575
+ if (bypassForRecentCommits) return null;
11576
+ if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return null;
11495
11577
  let debounceTime;
11496
11578
  try {
11497
- debounceTime = (0, import_node_fs4.statSync)(DEBOUNCE_FILE).mtimeMs;
11579
+ debounceTime = (0, import_node_fs5.statSync)(DEBOUNCE_FILE).mtimeMs;
11498
11580
  } catch {
11499
11581
  return null;
11500
11582
  }
@@ -11502,7 +11584,7 @@ function checkMtime(files) {
11502
11584
  const resolved = resolveFile(f);
11503
11585
  if (!resolved) continue;
11504
11586
  try {
11505
- const stat = (0, import_node_fs4.statSync)(resolved);
11587
+ const stat = (0, import_node_fs5.statSync)(resolved);
11506
11588
  if (stat.mtimeMs > debounceTime) {
11507
11589
  return null;
11508
11590
  }
@@ -11518,8 +11600,8 @@ function computeContentHash(files) {
11518
11600
  for (const f of sorted) {
11519
11601
  const resolved = resolveFile(f) ?? f;
11520
11602
  try {
11521
- if ((0, import_node_fs4.existsSync)(resolved)) {
11522
- hash.update((0, import_node_fs4.readFileSync)(resolved));
11603
+ if ((0, import_node_fs5.existsSync)(resolved)) {
11604
+ hash.update((0, import_node_fs5.readFileSync)(resolved));
11523
11605
  }
11524
11606
  } catch {
11525
11607
  }
@@ -11528,9 +11610,9 @@ function computeContentHash(files) {
11528
11610
  }
11529
11611
  function checkContentHash(files) {
11530
11612
  const hash = computeContentHash(files);
11531
- if ((0, import_node_fs4.existsSync)(HASH_FILE)) {
11613
+ if ((0, import_node_fs5.existsSync)(HASH_FILE)) {
11532
11614
  try {
11533
- const storedHash = (0, import_node_fs4.readFileSync)(HASH_FILE, "utf-8").trim();
11615
+ const storedHash = (0, import_node_fs5.readFileSync)(HASH_FILE, "utf-8").trim();
11534
11616
  if (hash === storedHash) {
11535
11617
  return { skip: "No source changes since last analysis", hash };
11536
11618
  }
@@ -11540,46 +11622,49 @@ function checkContentHash(files) {
11540
11622
  return { skip: null, hash };
11541
11623
  }
11542
11624
  function recordAnalysisStart() {
11543
- (0, import_node_fs4.mkdirSync)(GATE_DIR, { recursive: true });
11544
- (0, import_node_fs4.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
11625
+ (0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
11626
+ (0, import_node_fs5.writeFileSync)(DEBOUNCE_FILE, String(Math.floor(Date.now() / 1e3)));
11545
11627
  }
11546
11628
  function recordPassHash(hash) {
11547
- (0, import_node_fs4.writeFileSync)(HASH_FILE, hash);
11629
+ (0, import_node_fs5.writeFileSync)(HASH_FILE, hash);
11548
11630
  }
11549
11631
  function narrowToRecent(files) {
11550
- if (!(0, import_node_fs4.existsSync)(DEBOUNCE_FILE)) return files;
11632
+ if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return files;
11551
11633
  let debounceTime;
11552
11634
  try {
11553
- debounceTime = (0, import_node_fs4.statSync)(DEBOUNCE_FILE).mtimeMs;
11635
+ debounceTime = (0, import_node_fs5.statSync)(DEBOUNCE_FILE).mtimeMs;
11554
11636
  } catch {
11555
11637
  return files;
11556
11638
  }
11557
11639
  const recent = files.filter((f) => {
11558
11640
  try {
11559
- return (0, import_node_fs4.existsSync)(f) && (0, import_node_fs4.statSync)(f).mtimeMs > debounceTime;
11641
+ return (0, import_node_fs5.existsSync)(f) && (0, import_node_fs5.statSync)(f).mtimeMs > debounceTime;
11560
11642
  } catch {
11561
11643
  return false;
11562
11644
  }
11563
11645
  });
11564
11646
  return recent.length > 0 ? recent : files;
11565
11647
  }
11566
- function readIteration(currentCommit) {
11567
- if (!(0, import_node_fs4.existsSync)(ITERATION_FILE)) return 1;
11648
+ function readIteration(currentCommit, contentHash) {
11649
+ if (!(0, import_node_fs5.existsSync)(ITERATION_FILE)) return 1;
11568
11650
  try {
11569
- const stored = (0, import_node_fs4.readFileSync)(ITERATION_FILE, "utf-8").trim();
11570
- const [iterStr, storedCommit] = stored.split(":");
11571
- const iter = parseInt(iterStr, 10);
11651
+ const stored = (0, import_node_fs5.readFileSync)(ITERATION_FILE, "utf-8").trim();
11652
+ const parts = stored.split(":");
11653
+ const iter = parseInt(parts[0], 10);
11654
+ const storedCommit = parts[1] ?? "";
11655
+ const storedContentHash = parts[2] ?? "";
11572
11656
  if (isNaN(iter)) return 1;
11573
- if (storedCommit === currentCommit) return iter;
11574
- return 1;
11657
+ if (storedCommit !== currentCommit) return 1;
11658
+ if (contentHash && storedContentHash && storedContentHash !== contentHash) return 1;
11659
+ return iter;
11575
11660
  } catch {
11576
11661
  return 1;
11577
11662
  }
11578
11663
  }
11579
- function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS) {
11580
- const iteration = readIteration(currentCommit);
11664
+ function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS, contentHash) {
11665
+ const iteration = readIteration(currentCommit, contentHash);
11581
11666
  if (iteration > maxIterations) {
11582
- (0, import_node_fs4.writeFileSync)(ITERATION_FILE, `1:${currentCommit}`);
11667
+ writeIteration(1, currentCommit, contentHash);
11583
11668
  return {
11584
11669
  skip: `Max gate iterations (${maxIterations}) reached \u2014 accepting to prevent infinite loop. Human review required before deploying.`,
11585
11670
  iteration
@@ -11587,14 +11672,14 @@ function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS) {
11587
11672
  }
11588
11673
  return { skip: null, iteration };
11589
11674
  }
11590
- function writeIteration(iteration, commit) {
11591
- (0, import_node_fs4.mkdirSync)(GATE_DIR, { recursive: true });
11592
- (0, import_node_fs4.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}`);
11675
+ function writeIteration(iteration, commit, contentHash) {
11676
+ (0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
11677
+ (0, import_node_fs5.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${contentHash ?? ""}`);
11593
11678
  }
11594
11679
 
11595
11680
  // src/lib/static-analysis.ts
11596
- var import_node_child_process5 = require("node:child_process");
11597
- var import_node_fs5 = require("node:fs");
11681
+ var import_node_child_process6 = require("node:child_process");
11682
+ var import_node_fs6 = require("node:fs");
11598
11683
  var SEVERITY_ORDER = {
11599
11684
  Error: 0,
11600
11685
  Critical: 0,
@@ -11606,7 +11691,7 @@ var SEVERITY_ORDER = {
11606
11691
  };
11607
11692
  function isCodacyAvailable() {
11608
11693
  try {
11609
- (0, import_node_child_process5.execSync)("which codacy-analysis", { stdio: "pipe" });
11694
+ (0, import_node_child_process6.execSync)("which codacy-analysis", { stdio: "pipe" });
11610
11695
  return true;
11611
11696
  } catch {
11612
11697
  return false;
@@ -11621,7 +11706,7 @@ function runCodacyAnalysis(files) {
11621
11706
  if (files.length === 0) return empty;
11622
11707
  const existingFiles = files.filter((f) => {
11623
11708
  try {
11624
- return (0, import_node_fs5.existsSync)(f);
11709
+ return (0, import_node_fs6.existsSync)(f);
11625
11710
  } catch {
11626
11711
  return false;
11627
11712
  }
@@ -11630,7 +11715,7 @@ function runCodacyAnalysis(files) {
11630
11715
  const fileArgs = existingFiles.join(" ");
11631
11716
  let output;
11632
11717
  try {
11633
- output = (0, import_node_child_process5.execSync)(
11718
+ output = (0, import_node_child_process6.execSync)(
11634
11719
  `codacy-analysis analyze --install-dependencies --files ${fileArgs} --output-format json --log-level error --parallel-tools 3`,
11635
11720
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], maxBuffer: 10 * 1024 * 1024 }
11636
11721
  );
@@ -11683,7 +11768,7 @@ function runCodacyAnalysis(files) {
11683
11768
  }
11684
11769
 
11685
11770
  // src/lib/specs.ts
11686
- var import_node_fs6 = require("node:fs");
11771
+ var import_node_fs7 = require("node:fs");
11687
11772
  var import_node_path6 = require("node:path");
11688
11773
  var SPEC_CANDIDATES = [
11689
11774
  "CLAUDE.md",
@@ -11708,15 +11793,15 @@ function discoverSpecs() {
11708
11793
  if (result.length >= MAX_SPEC_FILES) return false;
11709
11794
  if (totalBytes >= MAX_TOTAL_SPEC_BYTES) return false;
11710
11795
  if (seen.has(specPath)) return true;
11711
- if (!(0, import_node_fs6.existsSync)(specPath)) return true;
11796
+ if (!(0, import_node_fs7.existsSync)(specPath)) return true;
11712
11797
  seen.add(specPath);
11713
11798
  const remaining = MAX_TOTAL_SPEC_BYTES - totalBytes;
11714
11799
  const readBytes = Math.min(MAX_SPEC_FILE_BYTES, remaining);
11715
11800
  try {
11716
11801
  const buf = Buffer.alloc(readBytes);
11717
- const fd = (0, import_node_fs6.openSync)(specPath, "r");
11718
- const bytesRead = (0, import_node_fs6.readSync)(fd, buf, 0, readBytes, 0);
11719
- (0, import_node_fs6.closeSync)(fd);
11802
+ const fd = (0, import_node_fs7.openSync)(specPath, "r");
11803
+ const bytesRead = (0, import_node_fs7.readSync)(fd, buf, 0, readBytes, 0);
11804
+ (0, import_node_fs7.closeSync)(fd);
11720
11805
  const content = buf.slice(0, bytesRead).toString("utf-8");
11721
11806
  if (!content) return true;
11722
11807
  result.push({ path: specPath, content });
@@ -11729,7 +11814,7 @@ function discoverSpecs() {
11729
11814
  if (!addSpec(candidate)) break;
11730
11815
  }
11731
11816
  for (const dir of ["spec", "docs"]) {
11732
- if (!(0, import_node_fs6.existsSync)(dir)) continue;
11817
+ if (!(0, import_node_fs7.existsSync)(dir)) continue;
11733
11818
  try {
11734
11819
  const mdFiles = findMdFiles(dir, 2).sort();
11735
11820
  for (const mdFile of mdFiles) {
@@ -11744,7 +11829,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
11744
11829
  if (depth >= maxDepth) return [];
11745
11830
  const result = [];
11746
11831
  try {
11747
- const entries = (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true });
11832
+ const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
11748
11833
  for (const entry of entries) {
11749
11834
  const fullPath = (0, import_node_path6.join)(dir, entry.name);
11750
11835
  if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -11759,13 +11844,13 @@ function findMdFiles(dir, maxDepth, depth = 0) {
11759
11844
  }
11760
11845
  function discoverPlans() {
11761
11846
  const plansDir = ".claude/plans";
11762
- if (!(0, import_node_fs6.existsSync)(plansDir)) return [];
11847
+ if (!(0, import_node_fs7.existsSync)(plansDir)) return [];
11763
11848
  const result = [];
11764
11849
  try {
11765
- const entries = (0, import_node_fs6.readdirSync)(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
11850
+ const entries = (0, import_node_fs7.readdirSync)(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
11766
11851
  const fullPath = (0, import_node_path6.join)(plansDir, f);
11767
11852
  try {
11768
- const stat = (0, import_node_fs6.statSync)(fullPath);
11853
+ const stat = (0, import_node_fs7.statSync)(fullPath);
11769
11854
  return { name: f, path: fullPath, mtime: stat.mtimeMs, size: stat.size };
11770
11855
  } catch {
11771
11856
  return null;
@@ -11774,7 +11859,7 @@ function discoverPlans() {
11774
11859
  for (const entry of entries) {
11775
11860
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
11776
11861
  try {
11777
- const content = (0, import_node_fs6.readFileSync)(entry.path, "utf-8");
11862
+ const content = (0, import_node_fs7.readFileSync)(entry.path, "utf-8");
11778
11863
  result.push({ name: entry.name, content });
11779
11864
  } catch {
11780
11865
  }
@@ -11784,15 +11869,113 @@ function discoverPlans() {
11784
11869
  return result;
11785
11870
  }
11786
11871
 
11872
+ // src/lib/snapshot.ts
11873
+ var import_node_fs8 = require("node:fs");
11874
+ var import_node_path7 = require("node:path");
11875
+ var import_node_child_process7 = require("node:child_process");
11876
+ function generateSnapshotDiffs(files) {
11877
+ if (!(0, import_node_fs8.existsSync)(SNAPSHOT_DIR)) {
11878
+ return { diffs: [], has_snapshots: false };
11879
+ }
11880
+ const diffs = [];
11881
+ for (const file of files) {
11882
+ const snapshotPath = (0, import_node_path7.join)(SNAPSHOT_DIR, file.path);
11883
+ const language = file.language ?? detectLanguage(file.path);
11884
+ if ((0, import_node_fs8.existsSync)(snapshotPath)) {
11885
+ const oldContent = (0, import_node_fs8.readFileSync)(snapshotPath, "utf-8");
11886
+ if (oldContent === file.content) continue;
11887
+ const diff = computeDiff(oldContent, file.content, file.path);
11888
+ if (diff) {
11889
+ diffs.push({ path: file.path, language, diff, status: "modified" });
11890
+ }
11891
+ } else {
11892
+ const addedLines = file.content.split("\n").map((l) => `+${l}`).join("\n");
11893
+ diffs.push({
11894
+ path: file.path,
11895
+ language,
11896
+ diff: `--- /dev/null
11897
+ +++ b/${file.path}
11898
+ @@ -0,0 +1,${file.content.split("\n").length} @@
11899
+ ${addedLines}`,
11900
+ status: "added"
11901
+ });
11902
+ }
11903
+ }
11904
+ return { diffs, has_snapshots: true };
11905
+ }
11906
+ function saveSnapshots(files) {
11907
+ const snapshotPaths = /* @__PURE__ */ new Set();
11908
+ for (const file of files) {
11909
+ const snapshotPath = (0, import_node_path7.join)(SNAPSHOT_DIR, file.path);
11910
+ snapshotPaths.add(snapshotPath);
11911
+ (0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(snapshotPath), { recursive: true });
11912
+ (0, import_node_fs8.writeFileSync)(snapshotPath, file.content);
11913
+ }
11914
+ cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
11915
+ }
11916
+ function computeDiff(oldContent, newContent, filePath) {
11917
+ const tmpOld = (0, import_node_path7.join)(SNAPSHOT_DIR, ".diff-old.tmp");
11918
+ const tmpNew = (0, import_node_path7.join)(SNAPSHOT_DIR, ".diff-new.tmp");
11919
+ try {
11920
+ (0, import_node_fs8.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
11921
+ (0, import_node_fs8.writeFileSync)(tmpOld, oldContent);
11922
+ (0, import_node_fs8.writeFileSync)(tmpNew, newContent);
11923
+ const result = (0, import_node_child_process7.execSync)(
11924
+ `git diff --no-index --unified=3 -- "${tmpOld}" "${tmpNew}"`,
11925
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11926
+ );
11927
+ return result || null;
11928
+ } catch (err) {
11929
+ const error = err;
11930
+ if (error.status === 1 && error.stdout) {
11931
+ return error.stdout.replace(/^diff --git a\/.*$/m, `diff --git a/${filePath} b/${filePath}`).replace(/^--- a\/.*$/m, `--- a/${filePath}`).replace(/^\+\+\+ b\/.*$/m, `+++ b/${filePath}`);
11932
+ }
11933
+ return null;
11934
+ } finally {
11935
+ try {
11936
+ (0, import_node_fs8.unlinkSync)(tmpOld);
11937
+ } catch {
11938
+ }
11939
+ try {
11940
+ (0, import_node_fs8.unlinkSync)(tmpNew);
11941
+ } catch {
11942
+ }
11943
+ }
11944
+ }
11945
+ function cleanStaleSnapshots(dir, keepSet) {
11946
+ if (!(0, import_node_fs8.existsSync)(dir)) return;
11947
+ try {
11948
+ const entries = (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true });
11949
+ for (const entry of entries) {
11950
+ if (entry.name.startsWith(".")) continue;
11951
+ const fullPath = (0, import_node_path7.join)(dir, entry.name);
11952
+ if (entry.isDirectory()) {
11953
+ cleanStaleSnapshots(fullPath, keepSet);
11954
+ try {
11955
+ const remaining = (0, import_node_fs8.readdirSync)(fullPath);
11956
+ if (remaining.length === 0) (0, import_node_fs8.rmdirSync)(fullPath);
11957
+ } catch {
11958
+ }
11959
+ } else if (!keepSet.has(fullPath)) {
11960
+ try {
11961
+ (0, import_node_fs8.unlinkSync)(fullPath);
11962
+ } catch {
11963
+ }
11964
+ }
11965
+ }
11966
+ } catch {
11967
+ }
11968
+ }
11969
+
11787
11970
  // src/lib/offline.ts
11788
- var import_node_fs7 = require("node:fs");
11971
+ var import_node_fs9 = require("node:fs");
11789
11972
  var import_node_crypto2 = require("node:crypto");
11790
11973
  function cacheRequest(body) {
11791
11974
  try {
11792
- (0, import_node_fs7.mkdirSync)(CACHE_DIR, { recursive: true });
11975
+ (0, import_node_fs9.mkdirSync)(CACHE_DIR, { recursive: true });
11793
11976
  const suffix = (0, import_node_crypto2.randomBytes)(4).toString("hex");
11794
11977
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
11795
- (0, import_node_fs7.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
11978
+ (0, import_node_fs9.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
11796
11979
  } catch {
11797
11980
  }
11798
11981
  }
@@ -11821,7 +12004,7 @@ function registerAnalyzeCommand(program2) {
11821
12004
  });
11822
12005
  }
11823
12006
  async function runAnalyze(opts, globals) {
11824
- const allChanged = getChangedFiles();
12007
+ const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
11825
12008
  const analyzable = filterAnalyzable(allChanged);
11826
12009
  const reviewable = filterReviewable(allChanged);
11827
12010
  const securityFiles = filterSecurity(allChanged);
@@ -11833,13 +12016,13 @@ async function runAnalyze(opts, globals) {
11833
12016
  const debounceSkip = checkDebounce(debounceSeconds);
11834
12017
  if (debounceSkip) passAndExit(debounceSkip);
11835
12018
  const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
11836
- const mtimeSkip = checkMtime(allCheckable);
12019
+ const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
11837
12020
  if (mtimeSkip) passAndExit(mtimeSkip);
11838
12021
  recordAnalysisStart();
11839
12022
  const { skip: hashSkip, hash: contentHash } = checkContentHash(allCheckable);
11840
12023
  if (hashSkip) passAndExit(hashSkip);
11841
12024
  const recentForReview = narrowToRecent(allForReview);
11842
- const intent = await readAndClearIntent();
12025
+ const conversation = await readAndClearConversationBuffer();
11843
12026
  const specs = discoverSpecs();
11844
12027
  const plans = discoverPlans();
11845
12028
  let staticResults;
@@ -11861,6 +12044,7 @@ async function runAnalyze(opts, globals) {
11861
12044
  if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
11862
12045
  passAndExit("No files within size limits to analyze");
11863
12046
  }
12047
+ const snapshotResult = generateSnapshotDiffs(codeDelta.files);
11864
12048
  const tokenResult = await resolveToken(globals.token);
11865
12049
  if (!tokenResult.ok) {
11866
12050
  passAndExit("Not configured yet \u2014 run /gate-setup in Claude Code to enable quality gates.");
@@ -11871,7 +12055,7 @@ async function runAnalyze(opts, globals) {
11871
12055
  }
11872
12056
  const currentCommit = getCurrentCommit();
11873
12057
  const maxIterations = parseInt(opts.maxIterations, 10);
11874
- const { skip: iterSkip, iteration } = checkMaxIterations(currentCommit, maxIterations);
12058
+ const { skip: iterSkip, iteration } = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
11875
12059
  if (iterSkip) passAndExit(iterSkip);
11876
12060
  const requestBody = {
11877
12061
  static_results: staticResults,
@@ -11885,13 +12069,23 @@ async function runAnalyze(opts, globals) {
11885
12069
  iteration
11886
12070
  }
11887
12071
  };
11888
- const hasIntent = intent?.prompt || specs.length > 0 || plans.length > 0;
12072
+ if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
12073
+ requestBody.snapshot_diffs = snapshotResult.diffs;
12074
+ }
12075
+ const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0;
11889
12076
  if (hasIntent) {
11890
12077
  const intentContext = {};
11891
- if (intent?.prompt) {
11892
- intentContext.user_prompt = intent.prompt;
11893
- intentContext.session_id = intent.session_id || void 0;
11894
- intentContext.prompt_captured_at = intent.captured_at || void 0;
12078
+ if (conversation && conversation.prompts.length > 0) {
12079
+ const latest = conversation.prompts[conversation.prompts.length - 1];
12080
+ intentContext.user_prompt = latest.prompt;
12081
+ intentContext.session_id = latest.session_id || void 0;
12082
+ intentContext.prompt_captured_at = latest.captured_at || void 0;
12083
+ if (conversation.prompts.length > 1) {
12084
+ intentContext.conversation = conversation.prompts;
12085
+ }
12086
+ if (conversation.recent_commits.length > 0) {
12087
+ intentContext.recent_commits = conversation.recent_commits;
12088
+ }
11895
12089
  }
11896
12090
  if (specs.length > 0) intentContext.specs = specs;
11897
12091
  if (plans.length > 0) intentContext.plans = plans;
@@ -11920,9 +12114,10 @@ async function runAnalyze(opts, globals) {
11920
12114
  }
11921
12115
  const response = result.data;
11922
12116
  const decision = response.gate_decision ?? "PASS";
12117
+ saveSnapshots(codeDelta.files.map((f) => ({ path: f.path, content: f.content })));
11923
12118
  switch (decision) {
11924
12119
  case "FAIL": {
11925
- writeIteration(iteration + 1, currentCommit);
12120
+ writeIteration(iteration + 1, currentCommit, contentHash ?? void 0);
11926
12121
  const assessment = response.assessment;
11927
12122
  const narrative = assessment?.narrative ?? "";
11928
12123
  const findings = response.findings ?? [];
@@ -11979,7 +12174,7 @@ async function runAnalyze(opts, globals) {
11979
12174
  break;
11980
12175
  }
11981
12176
  case "PASS": {
11982
- writeIteration(1, currentCommit);
12177
+ writeIteration(1, currentCommit, contentHash ?? void 0);
11983
12178
  if (contentHash) recordPassHash(contentHash);
11984
12179
  let userSummary = response.user_summary ?? "GATE.md: PASS";
11985
12180
  const viewUrl = response.view_url ?? "";
@@ -12005,7 +12200,7 @@ async function runAnalyze(opts, globals) {
12005
12200
  }
12006
12201
 
12007
12202
  // src/commands/review.ts
12008
- var import_node_fs8 = require("node:fs");
12203
+ var import_node_fs10 = require("node:fs");
12009
12204
  function registerReviewCommand(program2) {
12010
12205
  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) => {
12011
12206
  const globals = program2.opts();
@@ -12024,7 +12219,7 @@ async function runReview(opts, globals) {
12024
12219
  const securityFiles = filterSecurity(allFiles);
12025
12220
  let staticResults;
12026
12221
  if (isCodacyAvailable()) {
12027
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs8.existsSync)(f) || resolveFile(f) !== null);
12222
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs10.existsSync)(f) || resolveFile(f) !== null);
12028
12223
  staticResults = runCodacyAnalysis(scannable);
12029
12224
  } else {
12030
12225
  staticResults = {
@@ -12049,10 +12244,10 @@ async function runReview(opts, globals) {
12049
12244
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
12050
12245
  specs = [];
12051
12246
  for (const p of specPaths) {
12052
- if (!(0, import_node_fs8.existsSync)(p)) continue;
12247
+ if (!(0, import_node_fs10.existsSync)(p)) continue;
12053
12248
  try {
12054
- const { readFileSync: readFileSync4 } = await import("node:fs");
12055
- const content = readFileSync4(p, "utf-8");
12249
+ const { readFileSync: readFileSync5 } = await import("node:fs");
12250
+ const content = readFileSync5(p, "utf-8");
12056
12251
  specs.push({ path: p, content: content.slice(0, 10240) });
12057
12252
  } catch {
12058
12253
  }
@@ -12110,21 +12305,21 @@ async function runReview(opts, globals) {
12110
12305
  }
12111
12306
 
12112
12307
  // src/commands/init.ts
12113
- var import_node_fs9 = require("node:fs");
12308
+ var import_node_fs11 = require("node:fs");
12114
12309
  var import_promises8 = require("node:fs/promises");
12115
- var import_node_path7 = require("node:path");
12116
- var import_node_child_process6 = require("node:child_process");
12310
+ var import_node_path8 = require("node:path");
12311
+ var import_node_child_process8 = require("node:child_process");
12117
12312
  function resolveDataDir() {
12118
12313
  const candidates = [
12119
- (0, import_node_path7.join)(__dirname, "..", "data"),
12314
+ (0, import_node_path8.join)(__dirname, "..", "data"),
12120
12315
  // installed: node_modules/@codacy/gate-cli/data
12121
- (0, import_node_path7.join)(__dirname, "..", "..", "data"),
12316
+ (0, import_node_path8.join)(__dirname, "..", "..", "data"),
12122
12317
  // edge case: nested resolution
12123
- (0, import_node_path7.join)(process.cwd(), "cli", "data")
12318
+ (0, import_node_path8.join)(process.cwd(), "cli", "data")
12124
12319
  // local dev: running from repo root
12125
12320
  ];
12126
12321
  for (const candidate of candidates) {
12127
- if ((0, import_node_fs9.existsSync)((0, import_node_path7.join)(candidate, "skills"))) {
12322
+ if ((0, import_node_fs11.existsSync)((0, import_node_path8.join)(candidate, "skills"))) {
12128
12323
  return candidate;
12129
12324
  }
12130
12325
  }
@@ -12140,7 +12335,7 @@ function registerInitCommand(program2) {
12140
12335
  program2.command("init").description("Initialize GATE.md in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
12141
12336
  const force = opts.force ?? false;
12142
12337
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
12143
- const isProject = projectMarkers.some((m) => (0, import_node_fs9.existsSync)(m));
12338
+ const isProject = projectMarkers.some((m) => (0, import_node_fs11.existsSync)(m));
12144
12339
  if (!isProject) {
12145
12340
  printError("No project detected in the current directory.");
12146
12341
  printInfo('Run "gate init" from your project root.');
@@ -12158,30 +12353,30 @@ function registerInitCommand(program2) {
12158
12353
  }
12159
12354
  printInfo(` Node.js ${nodeVersion} \u2713`);
12160
12355
  try {
12161
- const gitVersion = (0, import_node_child_process6.execSync)("git --version", { encoding: "utf-8" }).trim();
12356
+ const gitVersion = (0, import_node_child_process8.execSync)("git --version", { encoding: "utf-8" }).trim();
12162
12357
  printInfo(` ${gitVersion} \u2713`);
12163
12358
  } catch {
12164
12359
  printError("git is required but not installed. Install from https://git-scm.com");
12165
12360
  process.exit(1);
12166
12361
  }
12167
12362
  try {
12168
- (0, import_node_child_process6.execSync)("which claude", { encoding: "utf-8" });
12363
+ (0, import_node_child_process8.execSync)("which claude", { encoding: "utf-8" });
12169
12364
  printInfo(" Claude Code \u2713");
12170
12365
  } catch {
12171
12366
  printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
12172
12367
  }
12173
12368
  try {
12174
- (0, import_node_child_process6.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
12369
+ (0, import_node_child_process8.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
12175
12370
  printInfo(" @codacy/analysis-cli \u2713");
12176
12371
  } catch {
12177
12372
  printInfo(" Installing @codacy/analysis-cli...");
12178
12373
  try {
12179
- (0, import_node_child_process6.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
12374
+ (0, import_node_child_process8.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
12180
12375
  printInfo(" @codacy/analysis-cli installed \u2713");
12181
12376
  } catch {
12182
12377
  try {
12183
12378
  printWarn(" Retrying with sudo...");
12184
- (0, import_node_child_process6.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
12379
+ (0, import_node_child_process8.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
12185
12380
  printInfo(" @codacy/analysis-cli installed \u2713");
12186
12381
  } catch {
12187
12382
  printWarn(" Could not install @codacy/analysis-cli automatically.");
@@ -12193,21 +12388,21 @@ function registerInitCommand(program2) {
12193
12388
  console.log("");
12194
12389
  printInfo("Installing skills...");
12195
12390
  const dataDir = resolveDataDir();
12196
- const skillsSource = (0, import_node_path7.join)(dataDir, "skills");
12391
+ const skillsSource = (0, import_node_path8.join)(dataDir, "skills");
12197
12392
  const skillsDest = ".claude/skills";
12198
12393
  const skills = ["gate-setup", "gate-analyze", "gate-status", "gate-feedback"];
12199
12394
  let skillsInstalled = 0;
12200
12395
  for (const skill of skills) {
12201
- const src = (0, import_node_path7.join)(skillsSource, skill);
12202
- const dest = (0, import_node_path7.join)(skillsDest, skill);
12203
- if (!(0, import_node_fs9.existsSync)(src)) {
12396
+ const src = (0, import_node_path8.join)(skillsSource, skill);
12397
+ const dest = (0, import_node_path8.join)(skillsDest, skill);
12398
+ if (!(0, import_node_fs11.existsSync)(src)) {
12204
12399
  printWarn(` Skill data not found: ${skill}`);
12205
12400
  continue;
12206
12401
  }
12207
- if ((0, import_node_fs9.existsSync)(dest) && !force) {
12208
- const srcSkill = (0, import_node_path7.join)(src, "SKILL.md");
12209
- const destSkill = (0, import_node_path7.join)(dest, "SKILL.md");
12210
- if ((0, import_node_fs9.existsSync)(destSkill)) {
12402
+ if ((0, import_node_fs11.existsSync)(dest) && !force) {
12403
+ const srcSkill = (0, import_node_path8.join)(src, "SKILL.md");
12404
+ const destSkill = (0, import_node_path8.join)(dest, "SKILL.md");
12405
+ if ((0, import_node_fs11.existsSync)(destSkill)) {
12211
12406
  try {
12212
12407
  const srcContent = await (0, import_promises8.readFile)(srcSkill, "utf-8");
12213
12408
  const destContent = await (0, import_promises8.readFile)(destSkill, "utf-8");
@@ -12235,7 +12430,7 @@ function registerInitCommand(program2) {
12235
12430
  printInfo(' Run "gate hooks install --force" to overwrite.');
12236
12431
  }
12237
12432
  await (0, import_promises8.mkdir)(GATE_DIR, { recursive: true });
12238
- const globalGateDir = (0, import_node_path7.join)(process.env.HOME ?? "", ".gate");
12433
+ const globalGateDir = (0, import_node_path8.join)(process.env.HOME ?? "", ".gate");
12239
12434
  await (0, import_promises8.mkdir)(globalGateDir, { recursive: true });
12240
12435
  console.log("");
12241
12436
  printInfo("GATE.md initialized!");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/gate-cli",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for GATE.md quality gate service",
5
5
  "bin": {
6
6
  "gate": "./bin/gate.js"