claude-crap 0.3.4 → 0.3.5
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/CHANGELOG.md +16 -0
- package/README.md +116 -472
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +112 -48
- package/dist/dashboard/server.js.map +1 -1
- package/dist/sarif/sarif-store.d.ts.map +1 -1
- package/dist/sarif/sarif-store.js +1 -1
- package/dist/sarif/sarif-store.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -2
- package/plugin/bundle/mcp-server.mjs +119 -85
- package/plugin/bundle/mcp-server.mjs.map +3 -3
- package/plugin/eslint.config.mjs +12 -0
- package/plugin/hooks/lib/hook-io.mjs +1 -1
- package/plugin/hooks/pre-tool-use.mjs +1 -1
- package/plugin/package-lock.json +2 -2
- package/plugin/package.json +1 -1
- package/scripts/doctor.mjs +2 -2
- package/src/dashboard/server.ts +137 -61
- package/src/sarif/sarif-store.ts +1 -0
|
@@ -7235,9 +7235,8 @@ function loadConfig() {
|
|
|
7235
7235
|
}
|
|
7236
7236
|
|
|
7237
7237
|
// src/dashboard/server.ts
|
|
7238
|
-
import { promises as fs2 } from "node:fs";
|
|
7239
|
-
import {
|
|
7240
|
-
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
7238
|
+
import { promises as fs2, existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
7239
|
+
import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
|
|
7241
7240
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7242
7241
|
import Fastify from "fastify";
|
|
7243
7242
|
import fastifyStatic from "@fastify/static";
|
|
@@ -7417,7 +7416,7 @@ async function startDashboard(options) {
|
|
|
7417
7416
|
root: publicRoot,
|
|
7418
7417
|
prefix: "/"
|
|
7419
7418
|
});
|
|
7420
|
-
fastify.get("/api/health", async () => ({ status: "ok", server: "claude-crap", version: "0.3.
|
|
7419
|
+
fastify.get("/api/health", async () => ({ status: "ok", server: "claude-crap", version: "0.3.5" }));
|
|
7421
7420
|
fastify.get("/api/score", async () => {
|
|
7422
7421
|
const stats = await workspaceStatsProvider();
|
|
7423
7422
|
const score = await buildScore(config, sarifStore, stats, urlOf(fastify, config));
|
|
@@ -7427,20 +7426,16 @@ async function startDashboard(options) {
|
|
|
7427
7426
|
fastify.get("/", async (_request, reply) => {
|
|
7428
7427
|
return reply.sendFile("index.html");
|
|
7429
7428
|
});
|
|
7430
|
-
const
|
|
7431
|
-
|
|
7432
|
-
await fastify.listen({ port:
|
|
7433
|
-
const url = `http://127.0.0.1:${
|
|
7434
|
-
if (boundPort !== config.dashboardPort) {
|
|
7435
|
-
logger2.warn(
|
|
7436
|
-
{ url, configuredPort: config.dashboardPort, actualPort: boundPort },
|
|
7437
|
-
"claude-crap dashboard bound to fallback port (configured port was in use)"
|
|
7438
|
-
);
|
|
7439
|
-
}
|
|
7429
|
+
const pidFilePath = resolvePidFilePath(config);
|
|
7430
|
+
await killStaleDashboard(pidFilePath, config.dashboardPort, logger2);
|
|
7431
|
+
await fastify.listen({ port: config.dashboardPort, host: "127.0.0.1" });
|
|
7432
|
+
const url = `http://127.0.0.1:${config.dashboardPort}`;
|
|
7440
7433
|
logger2.info({ url, publicRoot }, "claude-crap dashboard listening");
|
|
7434
|
+
writePidFile(pidFilePath, config.dashboardPort);
|
|
7441
7435
|
return {
|
|
7442
7436
|
url,
|
|
7443
7437
|
async close() {
|
|
7438
|
+
removePidFile(pidFilePath);
|
|
7444
7439
|
await fastify.close();
|
|
7445
7440
|
}
|
|
7446
7441
|
};
|
|
@@ -7480,33 +7475,71 @@ function urlOf(fastify, config) {
|
|
|
7480
7475
|
}
|
|
7481
7476
|
return `http://127.0.0.1:${config.dashboardPort}`;
|
|
7482
7477
|
}
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
probe.listen({ port: candidatePort, host: "127.0.0.1" }, () => {
|
|
7496
|
-
probe.close(() => resolvePromise(true));
|
|
7497
|
-
});
|
|
7498
|
-
});
|
|
7499
|
-
if (isFree) return candidatePort;
|
|
7500
|
-
if (attempt < maxRetries) {
|
|
7501
|
-
logger2.info(
|
|
7502
|
-
{ port: candidatePort, nextPort: candidatePort + 1 },
|
|
7503
|
-
"dashboard port in use, trying next"
|
|
7504
|
-
);
|
|
7505
|
-
}
|
|
7478
|
+
function resolvePidFilePath(config) {
|
|
7479
|
+
return join2(config.pluginRoot, ".claude-crap", "dashboard.pid");
|
|
7480
|
+
}
|
|
7481
|
+
function writePidFile(path, port) {
|
|
7482
|
+
const data = {
|
|
7483
|
+
pid: process.pid,
|
|
7484
|
+
port,
|
|
7485
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7486
|
+
};
|
|
7487
|
+
try {
|
|
7488
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
|
|
7489
|
+
} catch {
|
|
7506
7490
|
}
|
|
7507
|
-
|
|
7508
|
-
|
|
7491
|
+
}
|
|
7492
|
+
function removePidFile(path) {
|
|
7493
|
+
try {
|
|
7494
|
+
unlinkSync(path);
|
|
7495
|
+
} catch {
|
|
7496
|
+
}
|
|
7497
|
+
}
|
|
7498
|
+
function isPidAlive(pid) {
|
|
7499
|
+
try {
|
|
7500
|
+
process.kill(pid, 0);
|
|
7501
|
+
return true;
|
|
7502
|
+
} catch {
|
|
7503
|
+
return false;
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
async function killStaleDashboard(pidFilePath, port, logger2) {
|
|
7507
|
+
if (!existsSync(pidFilePath)) return;
|
|
7508
|
+
let stale;
|
|
7509
|
+
try {
|
|
7510
|
+
stale = JSON.parse(readFileSync(pidFilePath, "utf8"));
|
|
7511
|
+
} catch {
|
|
7512
|
+
removePidFile(pidFilePath);
|
|
7513
|
+
return;
|
|
7514
|
+
}
|
|
7515
|
+
if (!isPidAlive(stale.pid)) {
|
|
7516
|
+
logger2.info({ stalePid: stale.pid }, "stale dashboard PID file found (process dead), removing");
|
|
7517
|
+
removePidFile(pidFilePath);
|
|
7518
|
+
return;
|
|
7519
|
+
}
|
|
7520
|
+
logger2.info(
|
|
7521
|
+
{ stalePid: stale.pid, port: stale.port, startedAt: stale.startedAt },
|
|
7522
|
+
"killing stale dashboard process from previous session"
|
|
7509
7523
|
);
|
|
7524
|
+
try {
|
|
7525
|
+
process.kill(stale.pid, "SIGTERM");
|
|
7526
|
+
} catch {
|
|
7527
|
+
removePidFile(pidFilePath);
|
|
7528
|
+
return;
|
|
7529
|
+
}
|
|
7530
|
+
for (let i = 0; i < 30; i++) {
|
|
7531
|
+
if (!isPidAlive(stale.pid)) break;
|
|
7532
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
7533
|
+
}
|
|
7534
|
+
if (isPidAlive(stale.pid)) {
|
|
7535
|
+
try {
|
|
7536
|
+
process.kill(stale.pid, "SIGKILL");
|
|
7537
|
+
} catch {
|
|
7538
|
+
}
|
|
7539
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
7540
|
+
}
|
|
7541
|
+
removePidFile(pidFilePath);
|
|
7542
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
7510
7543
|
}
|
|
7511
7544
|
async function buildScore(config, sarifStore, workspace, dashboardUrl) {
|
|
7512
7545
|
return computeProjectScore({
|
|
@@ -7551,7 +7584,7 @@ function computeCrap(input, threshold) {
|
|
|
7551
7584
|
|
|
7552
7585
|
// src/metrics/workspace-walker.ts
|
|
7553
7586
|
import { promises as fs3 } from "node:fs";
|
|
7554
|
-
import { join as
|
|
7587
|
+
import { join as join3 } from "node:path";
|
|
7555
7588
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
7556
7589
|
"node_modules",
|
|
7557
7590
|
".git",
|
|
@@ -7606,7 +7639,7 @@ async function estimateWorkspaceLoc(workspaceRoot) {
|
|
|
7606
7639
|
for (const entry of entries) {
|
|
7607
7640
|
if (truncated) return;
|
|
7608
7641
|
if (entry.name.startsWith(".") && entry.name !== ".claude-plugin") continue;
|
|
7609
|
-
const full =
|
|
7642
|
+
const full = join3(dir, entry.name);
|
|
7610
7643
|
if (entry.isDirectory()) {
|
|
7611
7644
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
7612
7645
|
await walk2(full);
|
|
@@ -7639,7 +7672,7 @@ async function estimateWorkspaceLoc(workspaceRoot) {
|
|
|
7639
7672
|
|
|
7640
7673
|
// src/sarif/sarif-store.ts
|
|
7641
7674
|
import { promises as fs4 } from "node:fs";
|
|
7642
|
-
import { dirname as dirname3, isAbsolute, join as
|
|
7675
|
+
import { dirname as dirname3, isAbsolute, join as join4, resolve as resolve3 } from "node:path";
|
|
7643
7676
|
|
|
7644
7677
|
// src/sarif/sarif-builder.ts
|
|
7645
7678
|
function buildSarifDocument(tool, findings) {
|
|
@@ -7702,7 +7735,7 @@ var SarifStore = class {
|
|
|
7702
7735
|
toolInvocations = 0;
|
|
7703
7736
|
constructor(options) {
|
|
7704
7737
|
const dir = isAbsolute(options.outputDir) ? options.outputDir : resolve3(options.workspaceRoot, options.outputDir);
|
|
7705
|
-
this.filePath =
|
|
7738
|
+
this.filePath = join4(dir, options.fileName ?? "latest.sarif");
|
|
7706
7739
|
}
|
|
7707
7740
|
/**
|
|
7708
7741
|
* Absolute path to the consolidated SARIF file on disk.
|
|
@@ -7773,7 +7806,8 @@ var SarifStore = class {
|
|
|
7773
7806
|
return;
|
|
7774
7807
|
}
|
|
7775
7808
|
throw new Error(
|
|
7776
|
-
`[sarif-store] Failed to load consolidated report at ${this.filePath}: ${error.message}
|
|
7809
|
+
`[sarif-store] Failed to load consolidated report at ${this.filePath}: ${error.message}`,
|
|
7810
|
+
{ cause: err }
|
|
7777
7811
|
);
|
|
7778
7812
|
}
|
|
7779
7813
|
}
|
|
@@ -7975,8 +8009,8 @@ function validateSarifDocument(doc) {
|
|
|
7975
8009
|
}
|
|
7976
8010
|
|
|
7977
8011
|
// src/crap-config.ts
|
|
7978
|
-
import { readFileSync } from "node:fs";
|
|
7979
|
-
import { join as
|
|
8012
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
8013
|
+
import { join as join5 } from "node:path";
|
|
7980
8014
|
var STRICTNESS_VALUES = ["strict", "warn", "advisory"];
|
|
7981
8015
|
var DEFAULT_STRICTNESS = "strict";
|
|
7982
8016
|
var CrapConfigError = class extends Error {
|
|
@@ -8001,10 +8035,10 @@ function loadCrapConfig(options) {
|
|
|
8001
8035
|
return { strictness: DEFAULT_STRICTNESS, strictnessSource: "default" };
|
|
8002
8036
|
}
|
|
8003
8037
|
function readFromFile(workspaceRoot) {
|
|
8004
|
-
const filePath =
|
|
8038
|
+
const filePath = join5(workspaceRoot, ".claude-crap.json");
|
|
8005
8039
|
let raw;
|
|
8006
8040
|
try {
|
|
8007
|
-
raw =
|
|
8041
|
+
raw = readFileSync2(filePath, "utf8");
|
|
8008
8042
|
} catch (err) {
|
|
8009
8043
|
const error = err;
|
|
8010
8044
|
if (error.code === "ENOENT") return null;
|
|
@@ -8047,7 +8081,7 @@ function isStrictness(value) {
|
|
|
8047
8081
|
|
|
8048
8082
|
// src/tools/test-harness.ts
|
|
8049
8083
|
import { promises as fs5 } from "node:fs";
|
|
8050
|
-
import { basename, dirname as dirname4, extname, isAbsolute as isAbsolute2, join as
|
|
8084
|
+
import { basename, dirname as dirname4, extname, isAbsolute as isAbsolute2, join as join6, relative, resolve as resolve4, sep } from "node:path";
|
|
8051
8085
|
var TEST_SUFFIX_PATTERN = /\.(test|spec)\./;
|
|
8052
8086
|
function isTestFile(filePath) {
|
|
8053
8087
|
const base = basename(filePath);
|
|
@@ -8065,21 +8099,21 @@ function candidatePaths(workspaceRoot, filePath) {
|
|
|
8065
8099
|
const relFromRoot = relative(absWorkspace, absSource);
|
|
8066
8100
|
const relDir = dirname4(relFromRoot);
|
|
8067
8101
|
const candidates = /* @__PURE__ */ new Set();
|
|
8068
|
-
candidates.add(
|
|
8069
|
-
candidates.add(
|
|
8070
|
-
candidates.add(
|
|
8071
|
-
candidates.add(
|
|
8102
|
+
candidates.add(join6(dir, `${base}.test${ext}`));
|
|
8103
|
+
candidates.add(join6(dir, `${base}.spec${ext}`));
|
|
8104
|
+
candidates.add(join6(dir, "__tests__", `${base}.test${ext}`));
|
|
8105
|
+
candidates.add(join6(dir, "__tests__", `${base}.spec${ext}`));
|
|
8072
8106
|
for (const testRoot of ["tests", "test", "__tests__"]) {
|
|
8073
|
-
candidates.add(
|
|
8074
|
-
candidates.add(
|
|
8075
|
-
candidates.add(
|
|
8107
|
+
candidates.add(join6(absWorkspace, testRoot, relDir, `${base}.test${ext}`));
|
|
8108
|
+
candidates.add(join6(absWorkspace, testRoot, relDir, `${base}.spec${ext}`));
|
|
8109
|
+
candidates.add(join6(absWorkspace, testRoot, relDir, `${base}${ext}`));
|
|
8076
8110
|
}
|
|
8077
8111
|
let current = dir;
|
|
8078
8112
|
while (current.length >= absWorkspace.length) {
|
|
8079
8113
|
for (const testRoot of ["tests", "test", "__tests__"]) {
|
|
8080
|
-
candidates.add(
|
|
8081
|
-
candidates.add(
|
|
8082
|
-
candidates.add(
|
|
8114
|
+
candidates.add(join6(current, testRoot, `${base}.test${ext}`));
|
|
8115
|
+
candidates.add(join6(current, testRoot, `${base}.spec${ext}`));
|
|
8116
|
+
candidates.add(join6(current, testRoot, `${base}${ext}`));
|
|
8083
8117
|
}
|
|
8084
8118
|
if (current === absWorkspace) break;
|
|
8085
8119
|
const parent = dirname4(current);
|
|
@@ -8087,9 +8121,9 @@ function candidatePaths(workspaceRoot, filePath) {
|
|
|
8087
8121
|
current = parent;
|
|
8088
8122
|
}
|
|
8089
8123
|
if (ext === ".py") {
|
|
8090
|
-
candidates.add(
|
|
8091
|
-
candidates.add(
|
|
8092
|
-
candidates.add(
|
|
8124
|
+
candidates.add(join6(dir, `test_${base}.py`));
|
|
8125
|
+
candidates.add(join6(absWorkspace, "tests", `test_${base}.py`));
|
|
8126
|
+
candidates.add(join6(absWorkspace, "tests", relDir, `test_${base}.py`));
|
|
8093
8127
|
}
|
|
8094
8128
|
return Array.from(candidates);
|
|
8095
8129
|
}
|
|
@@ -8123,12 +8157,12 @@ function resolveWithinWorkspace(workspaceRoot, filePath) {
|
|
|
8123
8157
|
}
|
|
8124
8158
|
|
|
8125
8159
|
// src/scanner/auto-scan.ts
|
|
8126
|
-
import { existsSync as
|
|
8127
|
-
import { join as
|
|
8160
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
8161
|
+
import { join as join10 } from "node:path";
|
|
8128
8162
|
|
|
8129
8163
|
// src/scanner/detector.ts
|
|
8130
|
-
import { existsSync, readFileSync as
|
|
8131
|
-
import { join as
|
|
8164
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
8165
|
+
import { join as join7 } from "node:path";
|
|
8132
8166
|
import { execFile } from "node:child_process";
|
|
8133
8167
|
var SCANNER_SIGNALS = {
|
|
8134
8168
|
eslint: {
|
|
@@ -8182,8 +8216,8 @@ var SCANNER_SIGNALS = {
|
|
|
8182
8216
|
function probeConfigFiles(workspaceRoot, scanner) {
|
|
8183
8217
|
const signals = SCANNER_SIGNALS[scanner];
|
|
8184
8218
|
for (const file of signals.configFiles) {
|
|
8185
|
-
const fullPath =
|
|
8186
|
-
if (
|
|
8219
|
+
const fullPath = join7(workspaceRoot, file);
|
|
8220
|
+
if (existsSync2(fullPath)) {
|
|
8187
8221
|
return { found: true, path: fullPath };
|
|
8188
8222
|
}
|
|
8189
8223
|
}
|
|
@@ -8192,10 +8226,10 @@ function probeConfigFiles(workspaceRoot, scanner) {
|
|
|
8192
8226
|
function probePackageJson(workspaceRoot, scanner) {
|
|
8193
8227
|
const signals = SCANNER_SIGNALS[scanner];
|
|
8194
8228
|
if (signals.packageJsonKeys.length === 0) return false;
|
|
8195
|
-
const pkgPath =
|
|
8196
|
-
if (!
|
|
8229
|
+
const pkgPath = join7(workspaceRoot, "package.json");
|
|
8230
|
+
if (!existsSync2(pkgPath)) return false;
|
|
8197
8231
|
try {
|
|
8198
|
-
const raw =
|
|
8232
|
+
const raw = readFileSync3(pkgPath, "utf-8");
|
|
8199
8233
|
const pkg = JSON.parse(raw);
|
|
8200
8234
|
const deps = {
|
|
8201
8235
|
...typeof pkg.dependencies === "object" && pkg.dependencies !== null ? pkg.dependencies : {},
|
|
@@ -8255,8 +8289,8 @@ async function detectScanners(workspaceRoot) {
|
|
|
8255
8289
|
|
|
8256
8290
|
// src/scanner/runner.ts
|
|
8257
8291
|
import { execFile as execFile2 } from "node:child_process";
|
|
8258
|
-
import { readFileSync as
|
|
8259
|
-
import { join as
|
|
8292
|
+
import { readFileSync as readFileSync4, existsSync as existsSync3 } from "node:fs";
|
|
8293
|
+
import { join as join8 } from "node:path";
|
|
8260
8294
|
function getScannerCommand(scanner, workspaceRoot) {
|
|
8261
8295
|
switch (scanner) {
|
|
8262
8296
|
case "eslint":
|
|
@@ -8286,7 +8320,7 @@ function getScannerCommand(scanner, workspaceRoot) {
|
|
|
8286
8320
|
args: ["stryker", "run"],
|
|
8287
8321
|
timeoutMs: 3e5,
|
|
8288
8322
|
nonZeroIsNormal: false,
|
|
8289
|
-
outputFile:
|
|
8323
|
+
outputFile: join8(workspaceRoot, "reports", "mutation", "mutation.json")
|
|
8290
8324
|
};
|
|
8291
8325
|
}
|
|
8292
8326
|
}
|
|
@@ -8309,9 +8343,9 @@ function runScanner(scanner, workspaceRoot) {
|
|
|
8309
8343
|
const durationMs = Date.now() - start;
|
|
8310
8344
|
const isFatalError = cmd.nonZeroIsNormal && err && (!stdout?.trim() || stderr?.includes("Oops!") || stderr?.includes("couldn't find"));
|
|
8311
8345
|
if (err && (!cmd.nonZeroIsNormal || isFatalError)) {
|
|
8312
|
-
if (cmd.outputFile &&
|
|
8346
|
+
if (cmd.outputFile && existsSync3(cmd.outputFile)) {
|
|
8313
8347
|
try {
|
|
8314
|
-
const fileOutput =
|
|
8348
|
+
const fileOutput = readFileSync4(cmd.outputFile, "utf-8");
|
|
8315
8349
|
resolve6({
|
|
8316
8350
|
scanner,
|
|
8317
8351
|
success: true,
|
|
@@ -8332,9 +8366,9 @@ function runScanner(scanner, workspaceRoot) {
|
|
|
8332
8366
|
return;
|
|
8333
8367
|
}
|
|
8334
8368
|
if (cmd.outputFile) {
|
|
8335
|
-
if (
|
|
8369
|
+
if (existsSync3(cmd.outputFile)) {
|
|
8336
8370
|
try {
|
|
8337
|
-
const fileOutput =
|
|
8371
|
+
const fileOutput = readFileSync4(cmd.outputFile, "utf-8");
|
|
8338
8372
|
resolve6({
|
|
8339
8373
|
scanner,
|
|
8340
8374
|
success: true,
|
|
@@ -8385,11 +8419,11 @@ function runScanner(scanner, workspaceRoot) {
|
|
|
8385
8419
|
}
|
|
8386
8420
|
|
|
8387
8421
|
// src/scanner/bootstrap.ts
|
|
8388
|
-
import { existsSync as
|
|
8389
|
-
import { join as
|
|
8422
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync2, readdirSync } from "node:fs";
|
|
8423
|
+
import { join as join9 } from "node:path";
|
|
8390
8424
|
import { execFile as execFile3 } from "node:child_process";
|
|
8391
8425
|
function detectProjectType(workspaceRoot) {
|
|
8392
|
-
const has = (file) =>
|
|
8426
|
+
const has = (file) => existsSync4(join9(workspaceRoot, file));
|
|
8393
8427
|
if (has("package.json")) {
|
|
8394
8428
|
if (has("tsconfig.json")) return "typescript";
|
|
8395
8429
|
return "javascript";
|
|
@@ -8477,8 +8511,8 @@ function npmInstall(workspaceRoot, packages) {
|
|
|
8477
8511
|
});
|
|
8478
8512
|
}
|
|
8479
8513
|
function writeEslintConfigFile(workspaceRoot, isTypeScript) {
|
|
8480
|
-
const configPath =
|
|
8481
|
-
if (
|
|
8514
|
+
const configPath = join9(workspaceRoot, "eslint.config.mjs");
|
|
8515
|
+
if (existsSync4(configPath)) {
|
|
8482
8516
|
return {
|
|
8483
8517
|
action: "create eslint.config.mjs",
|
|
8484
8518
|
success: true,
|
|
@@ -8486,7 +8520,7 @@ function writeEslintConfigFile(workspaceRoot, isTypeScript) {
|
|
|
8486
8520
|
};
|
|
8487
8521
|
}
|
|
8488
8522
|
try {
|
|
8489
|
-
|
|
8523
|
+
writeFileSync2(configPath, generateEslintConfig(isTypeScript), "utf-8");
|
|
8490
8524
|
return {
|
|
8491
8525
|
action: "create eslint.config.mjs",
|
|
8492
8526
|
success: true,
|
|
@@ -8703,7 +8737,7 @@ async function autoScan(workspaceRoot, sarifStore, logger2) {
|
|
|
8703
8737
|
".eslintrc.json"
|
|
8704
8738
|
];
|
|
8705
8739
|
const eslintDetected = available.some((d) => d.scanner === "eslint");
|
|
8706
|
-
const hasEslintConfig = eslintConfigFiles.some((f) =>
|
|
8740
|
+
const hasEslintConfig = eslintConfigFiles.some((f) => existsSync5(join10(workspaceRoot, f)));
|
|
8707
8741
|
if (eslintDetected && !hasEslintConfig) {
|
|
8708
8742
|
logger2.info("auto-scan: ESLint detected but no config \u2014 running bootstrap");
|
|
8709
8743
|
try {
|