@codacy/gate-cli 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/gate.js +302 -110
- package/package.json +2 -2
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/
|
|
10859
|
+
// src/lib/conversation-buffer.ts
|
|
10856
10860
|
var import_promises5 = require("node:fs/promises");
|
|
10857
|
-
|
|
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
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
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
|
|
10867
|
-
|
|
10868
|
-
|
|
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
|
|
10891
|
+
async function readAndClearConversationBuffer() {
|
|
10873
10892
|
try {
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
11228
|
-
var
|
|
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,
|
|
11232
|
-
if ((0,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
11273
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
11562
|
+
if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return null;
|
|
11482
11563
|
try {
|
|
11483
|
-
const lastTs = parseInt((0,
|
|
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 (
|
|
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,
|
|
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,
|
|
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,
|
|
11522
|
-
hash.update((0,
|
|
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,
|
|
11613
|
+
if ((0, import_node_fs5.existsSync)(HASH_FILE)) {
|
|
11532
11614
|
try {
|
|
11533
|
-
const storedHash = (0,
|
|
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,23 +11622,23 @@ function checkContentHash(files) {
|
|
|
11540
11622
|
return { skip: null, hash };
|
|
11541
11623
|
}
|
|
11542
11624
|
function recordAnalysisStart() {
|
|
11543
|
-
(0,
|
|
11544
|
-
(0,
|
|
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,
|
|
11629
|
+
(0, import_node_fs5.writeFileSync)(HASH_FILE, hash);
|
|
11548
11630
|
}
|
|
11549
11631
|
function narrowToRecent(files) {
|
|
11550
|
-
if (!(0,
|
|
11632
|
+
if (!(0, import_node_fs5.existsSync)(DEBOUNCE_FILE)) return files;
|
|
11551
11633
|
let debounceTime;
|
|
11552
11634
|
try {
|
|
11553
|
-
debounceTime = (0,
|
|
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,
|
|
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
|
}
|
|
@@ -11564,9 +11646,9 @@ function narrowToRecent(files) {
|
|
|
11564
11646
|
return recent.length > 0 ? recent : files;
|
|
11565
11647
|
}
|
|
11566
11648
|
function readIteration(currentCommit, contentHash) {
|
|
11567
|
-
if (!(0,
|
|
11649
|
+
if (!(0, import_node_fs5.existsSync)(ITERATION_FILE)) return 1;
|
|
11568
11650
|
try {
|
|
11569
|
-
const stored = (0,
|
|
11651
|
+
const stored = (0, import_node_fs5.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
11570
11652
|
const parts = stored.split(":");
|
|
11571
11653
|
const iter = parseInt(parts[0], 10);
|
|
11572
11654
|
const storedCommit = parts[1] ?? "";
|
|
@@ -11591,13 +11673,13 @@ function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS, conte
|
|
|
11591
11673
|
return { skip: null, iteration };
|
|
11592
11674
|
}
|
|
11593
11675
|
function writeIteration(iteration, commit, contentHash) {
|
|
11594
|
-
(0,
|
|
11595
|
-
(0,
|
|
11676
|
+
(0, import_node_fs5.mkdirSync)(GATE_DIR, { recursive: true });
|
|
11677
|
+
(0, import_node_fs5.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${contentHash ?? ""}`);
|
|
11596
11678
|
}
|
|
11597
11679
|
|
|
11598
11680
|
// src/lib/static-analysis.ts
|
|
11599
|
-
var
|
|
11600
|
-
var
|
|
11681
|
+
var import_node_child_process6 = require("node:child_process");
|
|
11682
|
+
var import_node_fs6 = require("node:fs");
|
|
11601
11683
|
var SEVERITY_ORDER = {
|
|
11602
11684
|
Error: 0,
|
|
11603
11685
|
Critical: 0,
|
|
@@ -11609,7 +11691,7 @@ var SEVERITY_ORDER = {
|
|
|
11609
11691
|
};
|
|
11610
11692
|
function isCodacyAvailable() {
|
|
11611
11693
|
try {
|
|
11612
|
-
(0,
|
|
11694
|
+
(0, import_node_child_process6.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
11613
11695
|
return true;
|
|
11614
11696
|
} catch {
|
|
11615
11697
|
return false;
|
|
@@ -11624,7 +11706,7 @@ function runCodacyAnalysis(files) {
|
|
|
11624
11706
|
if (files.length === 0) return empty;
|
|
11625
11707
|
const existingFiles = files.filter((f) => {
|
|
11626
11708
|
try {
|
|
11627
|
-
return (0,
|
|
11709
|
+
return (0, import_node_fs6.existsSync)(f);
|
|
11628
11710
|
} catch {
|
|
11629
11711
|
return false;
|
|
11630
11712
|
}
|
|
@@ -11633,7 +11715,7 @@ function runCodacyAnalysis(files) {
|
|
|
11633
11715
|
const fileArgs = existingFiles.join(" ");
|
|
11634
11716
|
let output;
|
|
11635
11717
|
try {
|
|
11636
|
-
output = (0,
|
|
11718
|
+
output = (0, import_node_child_process6.execSync)(
|
|
11637
11719
|
`codacy-analysis analyze --install-dependencies --files ${fileArgs} --output-format json --log-level error --parallel-tools 3`,
|
|
11638
11720
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], maxBuffer: 10 * 1024 * 1024 }
|
|
11639
11721
|
);
|
|
@@ -11686,7 +11768,7 @@ function runCodacyAnalysis(files) {
|
|
|
11686
11768
|
}
|
|
11687
11769
|
|
|
11688
11770
|
// src/lib/specs.ts
|
|
11689
|
-
var
|
|
11771
|
+
var import_node_fs7 = require("node:fs");
|
|
11690
11772
|
var import_node_path6 = require("node:path");
|
|
11691
11773
|
var SPEC_CANDIDATES = [
|
|
11692
11774
|
"CLAUDE.md",
|
|
@@ -11711,15 +11793,15 @@ function discoverSpecs() {
|
|
|
11711
11793
|
if (result.length >= MAX_SPEC_FILES) return false;
|
|
11712
11794
|
if (totalBytes >= MAX_TOTAL_SPEC_BYTES) return false;
|
|
11713
11795
|
if (seen.has(specPath)) return true;
|
|
11714
|
-
if (!(0,
|
|
11796
|
+
if (!(0, import_node_fs7.existsSync)(specPath)) return true;
|
|
11715
11797
|
seen.add(specPath);
|
|
11716
11798
|
const remaining = MAX_TOTAL_SPEC_BYTES - totalBytes;
|
|
11717
11799
|
const readBytes = Math.min(MAX_SPEC_FILE_BYTES, remaining);
|
|
11718
11800
|
try {
|
|
11719
11801
|
const buf = Buffer.alloc(readBytes);
|
|
11720
|
-
const fd = (0,
|
|
11721
|
-
const bytesRead = (0,
|
|
11722
|
-
(0,
|
|
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);
|
|
11723
11805
|
const content = buf.slice(0, bytesRead).toString("utf-8");
|
|
11724
11806
|
if (!content) return true;
|
|
11725
11807
|
result.push({ path: specPath, content });
|
|
@@ -11732,7 +11814,7 @@ function discoverSpecs() {
|
|
|
11732
11814
|
if (!addSpec(candidate)) break;
|
|
11733
11815
|
}
|
|
11734
11816
|
for (const dir of ["spec", "docs"]) {
|
|
11735
|
-
if (!(0,
|
|
11817
|
+
if (!(0, import_node_fs7.existsSync)(dir)) continue;
|
|
11736
11818
|
try {
|
|
11737
11819
|
const mdFiles = findMdFiles(dir, 2).sort();
|
|
11738
11820
|
for (const mdFile of mdFiles) {
|
|
@@ -11747,7 +11829,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
11747
11829
|
if (depth >= maxDepth) return [];
|
|
11748
11830
|
const result = [];
|
|
11749
11831
|
try {
|
|
11750
|
-
const entries = (0,
|
|
11832
|
+
const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
|
|
11751
11833
|
for (const entry of entries) {
|
|
11752
11834
|
const fullPath = (0, import_node_path6.join)(dir, entry.name);
|
|
11753
11835
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -11762,13 +11844,13 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
11762
11844
|
}
|
|
11763
11845
|
function discoverPlans() {
|
|
11764
11846
|
const plansDir = ".claude/plans";
|
|
11765
|
-
if (!(0,
|
|
11847
|
+
if (!(0, import_node_fs7.existsSync)(plansDir)) return [];
|
|
11766
11848
|
const result = [];
|
|
11767
11849
|
try {
|
|
11768
|
-
const entries = (0,
|
|
11850
|
+
const entries = (0, import_node_fs7.readdirSync)(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
11769
11851
|
const fullPath = (0, import_node_path6.join)(plansDir, f);
|
|
11770
11852
|
try {
|
|
11771
|
-
const stat = (0,
|
|
11853
|
+
const stat = (0, import_node_fs7.statSync)(fullPath);
|
|
11772
11854
|
return { name: f, path: fullPath, mtime: stat.mtimeMs, size: stat.size };
|
|
11773
11855
|
} catch {
|
|
11774
11856
|
return null;
|
|
@@ -11777,7 +11859,7 @@ function discoverPlans() {
|
|
|
11777
11859
|
for (const entry of entries) {
|
|
11778
11860
|
if (entry.size > MAX_PLAN_FILE_BYTES) continue;
|
|
11779
11861
|
try {
|
|
11780
|
-
const content = (0,
|
|
11862
|
+
const content = (0, import_node_fs7.readFileSync)(entry.path, "utf-8");
|
|
11781
11863
|
result.push({ name: entry.name, content });
|
|
11782
11864
|
} catch {
|
|
11783
11865
|
}
|
|
@@ -11787,15 +11869,113 @@ function discoverPlans() {
|
|
|
11787
11869
|
return result;
|
|
11788
11870
|
}
|
|
11789
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
|
+
|
|
11790
11970
|
// src/lib/offline.ts
|
|
11791
|
-
var
|
|
11971
|
+
var import_node_fs9 = require("node:fs");
|
|
11792
11972
|
var import_node_crypto2 = require("node:crypto");
|
|
11793
11973
|
function cacheRequest(body) {
|
|
11794
11974
|
try {
|
|
11795
|
-
(0,
|
|
11975
|
+
(0, import_node_fs9.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
11796
11976
|
const suffix = (0, import_node_crypto2.randomBytes)(4).toString("hex");
|
|
11797
11977
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
11798
|
-
(0,
|
|
11978
|
+
(0, import_node_fs9.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
|
|
11799
11979
|
} catch {
|
|
11800
11980
|
}
|
|
11801
11981
|
}
|
|
@@ -11824,7 +12004,7 @@ function registerAnalyzeCommand(program2) {
|
|
|
11824
12004
|
});
|
|
11825
12005
|
}
|
|
11826
12006
|
async function runAnalyze(opts, globals) {
|
|
11827
|
-
const allChanged = getChangedFiles();
|
|
12007
|
+
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
11828
12008
|
const analyzable = filterAnalyzable(allChanged);
|
|
11829
12009
|
const reviewable = filterReviewable(allChanged);
|
|
11830
12010
|
const securityFiles = filterSecurity(allChanged);
|
|
@@ -11836,13 +12016,13 @@ async function runAnalyze(opts, globals) {
|
|
|
11836
12016
|
const debounceSkip = checkDebounce(debounceSeconds);
|
|
11837
12017
|
if (debounceSkip) passAndExit(debounceSkip);
|
|
11838
12018
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
11839
|
-
const mtimeSkip = checkMtime(allCheckable);
|
|
12019
|
+
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles);
|
|
11840
12020
|
if (mtimeSkip) passAndExit(mtimeSkip);
|
|
11841
12021
|
recordAnalysisStart();
|
|
11842
12022
|
const { skip: hashSkip, hash: contentHash } = checkContentHash(allCheckable);
|
|
11843
12023
|
if (hashSkip) passAndExit(hashSkip);
|
|
11844
12024
|
const recentForReview = narrowToRecent(allForReview);
|
|
11845
|
-
const
|
|
12025
|
+
const conversation = await readAndClearConversationBuffer();
|
|
11846
12026
|
const specs = discoverSpecs();
|
|
11847
12027
|
const plans = discoverPlans();
|
|
11848
12028
|
let staticResults;
|
|
@@ -11864,6 +12044,7 @@ async function runAnalyze(opts, globals) {
|
|
|
11864
12044
|
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
11865
12045
|
passAndExit("No files within size limits to analyze");
|
|
11866
12046
|
}
|
|
12047
|
+
const snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
11867
12048
|
const tokenResult = await resolveToken(globals.token);
|
|
11868
12049
|
if (!tokenResult.ok) {
|
|
11869
12050
|
passAndExit("Not configured yet \u2014 run /gate-setup in Claude Code to enable quality gates.");
|
|
@@ -11888,13 +12069,23 @@ async function runAnalyze(opts, globals) {
|
|
|
11888
12069
|
iteration
|
|
11889
12070
|
}
|
|
11890
12071
|
};
|
|
11891
|
-
|
|
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;
|
|
11892
12076
|
if (hasIntent) {
|
|
11893
12077
|
const intentContext = {};
|
|
11894
|
-
if (
|
|
11895
|
-
|
|
11896
|
-
intentContext.
|
|
11897
|
-
intentContext.
|
|
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
|
+
}
|
|
11898
12089
|
}
|
|
11899
12090
|
if (specs.length > 0) intentContext.specs = specs;
|
|
11900
12091
|
if (plans.length > 0) intentContext.plans = plans;
|
|
@@ -11923,6 +12114,7 @@ async function runAnalyze(opts, globals) {
|
|
|
11923
12114
|
}
|
|
11924
12115
|
const response = result.data;
|
|
11925
12116
|
const decision = response.gate_decision ?? "PASS";
|
|
12117
|
+
saveSnapshots(codeDelta.files.map((f) => ({ path: f.path, content: f.content })));
|
|
11926
12118
|
switch (decision) {
|
|
11927
12119
|
case "FAIL": {
|
|
11928
12120
|
writeIteration(iteration + 1, currentCommit, contentHash ?? void 0);
|
|
@@ -12008,7 +12200,7 @@ async function runAnalyze(opts, globals) {
|
|
|
12008
12200
|
}
|
|
12009
12201
|
|
|
12010
12202
|
// src/commands/review.ts
|
|
12011
|
-
var
|
|
12203
|
+
var import_node_fs10 = require("node:fs");
|
|
12012
12204
|
function registerReviewCommand(program2) {
|
|
12013
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) => {
|
|
12014
12206
|
const globals = program2.opts();
|
|
@@ -12027,7 +12219,7 @@ async function runReview(opts, globals) {
|
|
|
12027
12219
|
const securityFiles = filterSecurity(allFiles);
|
|
12028
12220
|
let staticResults;
|
|
12029
12221
|
if (isCodacyAvailable()) {
|
|
12030
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
12222
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs10.existsSync)(f) || resolveFile(f) !== null);
|
|
12031
12223
|
staticResults = runCodacyAnalysis(scannable);
|
|
12032
12224
|
} else {
|
|
12033
12225
|
staticResults = {
|
|
@@ -12052,10 +12244,10 @@ async function runReview(opts, globals) {
|
|
|
12052
12244
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
12053
12245
|
specs = [];
|
|
12054
12246
|
for (const p of specPaths) {
|
|
12055
|
-
if (!(0,
|
|
12247
|
+
if (!(0, import_node_fs10.existsSync)(p)) continue;
|
|
12056
12248
|
try {
|
|
12057
|
-
const { readFileSync:
|
|
12058
|
-
const content =
|
|
12249
|
+
const { readFileSync: readFileSync5 } = await import("node:fs");
|
|
12250
|
+
const content = readFileSync5(p, "utf-8");
|
|
12059
12251
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
12060
12252
|
} catch {
|
|
12061
12253
|
}
|
|
@@ -12113,21 +12305,21 @@ async function runReview(opts, globals) {
|
|
|
12113
12305
|
}
|
|
12114
12306
|
|
|
12115
12307
|
// src/commands/init.ts
|
|
12116
|
-
var
|
|
12308
|
+
var import_node_fs11 = require("node:fs");
|
|
12117
12309
|
var import_promises8 = require("node:fs/promises");
|
|
12118
|
-
var
|
|
12119
|
-
var
|
|
12310
|
+
var import_node_path8 = require("node:path");
|
|
12311
|
+
var import_node_child_process8 = require("node:child_process");
|
|
12120
12312
|
function resolveDataDir() {
|
|
12121
12313
|
const candidates = [
|
|
12122
|
-
(0,
|
|
12314
|
+
(0, import_node_path8.join)(__dirname, "..", "data"),
|
|
12123
12315
|
// installed: node_modules/@codacy/gate-cli/data
|
|
12124
|
-
(0,
|
|
12316
|
+
(0, import_node_path8.join)(__dirname, "..", "..", "data"),
|
|
12125
12317
|
// edge case: nested resolution
|
|
12126
|
-
(0,
|
|
12318
|
+
(0, import_node_path8.join)(process.cwd(), "cli", "data")
|
|
12127
12319
|
// local dev: running from repo root
|
|
12128
12320
|
];
|
|
12129
12321
|
for (const candidate of candidates) {
|
|
12130
|
-
if ((0,
|
|
12322
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path8.join)(candidate, "skills"))) {
|
|
12131
12323
|
return candidate;
|
|
12132
12324
|
}
|
|
12133
12325
|
}
|
|
@@ -12143,7 +12335,7 @@ function registerInitCommand(program2) {
|
|
|
12143
12335
|
program2.command("init").description("Initialize GATE.md in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
12144
12336
|
const force = opts.force ?? false;
|
|
12145
12337
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
12146
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
12338
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs11.existsSync)(m));
|
|
12147
12339
|
if (!isProject) {
|
|
12148
12340
|
printError("No project detected in the current directory.");
|
|
12149
12341
|
printInfo('Run "gate init" from your project root.');
|
|
@@ -12161,30 +12353,30 @@ function registerInitCommand(program2) {
|
|
|
12161
12353
|
}
|
|
12162
12354
|
printInfo(` Node.js ${nodeVersion} \u2713`);
|
|
12163
12355
|
try {
|
|
12164
|
-
const gitVersion = (0,
|
|
12356
|
+
const gitVersion = (0, import_node_child_process8.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
12165
12357
|
printInfo(` ${gitVersion} \u2713`);
|
|
12166
12358
|
} catch {
|
|
12167
12359
|
printError("git is required but not installed. Install from https://git-scm.com");
|
|
12168
12360
|
process.exit(1);
|
|
12169
12361
|
}
|
|
12170
12362
|
try {
|
|
12171
|
-
(0,
|
|
12363
|
+
(0, import_node_child_process8.execSync)("which claude", { encoding: "utf-8" });
|
|
12172
12364
|
printInfo(" Claude Code \u2713");
|
|
12173
12365
|
} catch {
|
|
12174
12366
|
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
12175
12367
|
}
|
|
12176
12368
|
try {
|
|
12177
|
-
(0,
|
|
12369
|
+
(0, import_node_child_process8.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
12178
12370
|
printInfo(" @codacy/analysis-cli \u2713");
|
|
12179
12371
|
} catch {
|
|
12180
12372
|
printInfo(" Installing @codacy/analysis-cli...");
|
|
12181
12373
|
try {
|
|
12182
|
-
(0,
|
|
12374
|
+
(0, import_node_child_process8.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
12183
12375
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
12184
12376
|
} catch {
|
|
12185
12377
|
try {
|
|
12186
12378
|
printWarn(" Retrying with sudo...");
|
|
12187
|
-
(0,
|
|
12379
|
+
(0, import_node_child_process8.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
12188
12380
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
12189
12381
|
} catch {
|
|
12190
12382
|
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
@@ -12196,21 +12388,21 @@ function registerInitCommand(program2) {
|
|
|
12196
12388
|
console.log("");
|
|
12197
12389
|
printInfo("Installing skills...");
|
|
12198
12390
|
const dataDir = resolveDataDir();
|
|
12199
|
-
const skillsSource = (0,
|
|
12391
|
+
const skillsSource = (0, import_node_path8.join)(dataDir, "skills");
|
|
12200
12392
|
const skillsDest = ".claude/skills";
|
|
12201
12393
|
const skills = ["gate-setup", "gate-analyze", "gate-status", "gate-feedback"];
|
|
12202
12394
|
let skillsInstalled = 0;
|
|
12203
12395
|
for (const skill of skills) {
|
|
12204
|
-
const src = (0,
|
|
12205
|
-
const dest = (0,
|
|
12206
|
-
if (!(0,
|
|
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)) {
|
|
12207
12399
|
printWarn(` Skill data not found: ${skill}`);
|
|
12208
12400
|
continue;
|
|
12209
12401
|
}
|
|
12210
|
-
if ((0,
|
|
12211
|
-
const srcSkill = (0,
|
|
12212
|
-
const destSkill = (0,
|
|
12213
|
-
if ((0,
|
|
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)) {
|
|
12214
12406
|
try {
|
|
12215
12407
|
const srcContent = await (0, import_promises8.readFile)(srcSkill, "utf-8");
|
|
12216
12408
|
const destContent = await (0, import_promises8.readFile)(destSkill, "utf-8");
|
|
@@ -12238,7 +12430,7 @@ function registerInitCommand(program2) {
|
|
|
12238
12430
|
printInfo(' Run "gate hooks install --force" to overwrite.');
|
|
12239
12431
|
}
|
|
12240
12432
|
await (0, import_promises8.mkdir)(GATE_DIR, { recursive: true });
|
|
12241
|
-
const globalGateDir = (0,
|
|
12433
|
+
const globalGateDir = (0, import_node_path8.join)(process.env.HOME ?? "", ".gate");
|
|
12242
12434
|
await (0, import_promises8.mkdir)(globalGateDir, { recursive: true });
|
|
12243
12435
|
console.log("");
|
|
12244
12436
|
printInfo("GATE.md initialized!");
|
|
@@ -12256,7 +12448,7 @@ function registerInitCommand(program2) {
|
|
|
12256
12448
|
}
|
|
12257
12449
|
|
|
12258
12450
|
// src/cli.ts
|
|
12259
|
-
program.name("gate").description("CLI for GATE.md quality gate service").version("0.
|
|
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");
|
|
12260
12452
|
registerAuthCommands(program);
|
|
12261
12453
|
registerHooksCommands(program);
|
|
12262
12454
|
registerIntentCommands(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/gate-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "CLI for GATE.md quality gate service",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gate": "./bin/gate.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=20"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "
|
|
18
|
+
"build": "node scripts/build.js",
|
|
19
19
|
"test": "node --import tsx --test $(find tests -name '*.test.ts' | sort)",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"prepublishOnly": "npm run typecheck && npm run build"
|