eslint-plugin-ai-guard 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +599 -241
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -30,6 +30,154 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
mod
|
|
31
31
|
));
|
|
32
32
|
|
|
33
|
+
// cli/utils/debug.ts
|
|
34
|
+
var debug_exports = {};
|
|
35
|
+
__export(debug_exports, {
|
|
36
|
+
debugLog: () => debugLog,
|
|
37
|
+
printCIDebug: () => printCIDebug,
|
|
38
|
+
printGitDebug: () => printGitDebug
|
|
39
|
+
});
|
|
40
|
+
function debugLog(message) {
|
|
41
|
+
process.stderr.write(`${DIM("[debug]")} ${message}
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
function printGitDebug(result) {
|
|
45
|
+
const d = result.debugInfo;
|
|
46
|
+
process.stderr.write("\n");
|
|
47
|
+
process.stderr.write(`${LABEL(" Git Resolution Trace")}
|
|
48
|
+
`);
|
|
49
|
+
process.stderr.write(`${DIM(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
|
|
50
|
+
`);
|
|
51
|
+
process.stderr.write(` ${DIM("Mode:")} ${import_chalk4.default.white(result.mode)}
|
|
52
|
+
`);
|
|
53
|
+
if (d.resolvedBase) {
|
|
54
|
+
process.stderr.write(` ${DIM("Base branch:")} ${import_chalk4.default.white(d.resolvedBase)}
|
|
55
|
+
`);
|
|
56
|
+
} else {
|
|
57
|
+
process.stderr.write(` ${DIM("Base branch:")} ${import_chalk4.default.dim("(none \u2014 uncommitted mode)")}
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
if (d.mergeBase) {
|
|
61
|
+
process.stderr.write(` ${DIM("Merge base:")} ${import_chalk4.default.white(d.mergeBase.slice(0, 12))}\u2026
|
|
62
|
+
`);
|
|
63
|
+
}
|
|
64
|
+
if (d.isShallowClone) {
|
|
65
|
+
process.stderr.write(` ${WARN} ${import_chalk4.default.yellow("Shallow clone detected")} \u2014 use fetch-depth: 0 in workflow
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
if (d.isDetachedHead) {
|
|
69
|
+
process.stderr.write(` ${WARN} ${import_chalk4.default.yellow("Detached HEAD")} \u2014 branch context unavailable
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
process.stderr.write("\n");
|
|
73
|
+
process.stderr.write(` ${DIM("Strategies attempted:")}
|
|
74
|
+
`);
|
|
75
|
+
for (const strategy of d.strategiesAttempted) {
|
|
76
|
+
const succeeded = strategy === d.strategySucceeded;
|
|
77
|
+
const icon = succeeded ? OK : ERR;
|
|
78
|
+
const label = succeeded ? import_chalk4.default.green(strategy) : import_chalk4.default.dim(strategy);
|
|
79
|
+
process.stderr.write(` ${icon} ${label}
|
|
80
|
+
`);
|
|
81
|
+
}
|
|
82
|
+
if (d.strategySucceeded) {
|
|
83
|
+
process.stderr.write(`
|
|
84
|
+
${OK} ${import_chalk4.default.green("Succeeded:")} ${d.strategySucceeded}
|
|
85
|
+
`);
|
|
86
|
+
} else {
|
|
87
|
+
process.stderr.write(`
|
|
88
|
+
${ERR} ${import_chalk4.default.red("All strategies failed")}
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
process.stderr.write("\n");
|
|
92
|
+
process.stderr.write(` ${DIM("Files:")}
|
|
93
|
+
`);
|
|
94
|
+
process.stderr.write(` ${ARROW} Detected raw: ${import_chalk4.default.white(d.rawFileCount)}
|
|
95
|
+
`);
|
|
96
|
+
process.stderr.write(` ${ARROW} Kept: ${import_chalk4.default.green(result.files.length)}
|
|
97
|
+
`);
|
|
98
|
+
if (d.droppedByExtension > 0) {
|
|
99
|
+
process.stderr.write(` ${ARROW} Dropped (ext): ${import_chalk4.default.dim(d.droppedByExtension)}
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
if (d.droppedByIgnore > 0) {
|
|
103
|
+
process.stderr.write(` ${ARROW} Dropped (ignored): ${import_chalk4.default.dim(d.droppedByIgnore)}
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
if (d.droppedByWorkingDir > 0) {
|
|
107
|
+
process.stderr.write(` ${ARROW} Dropped (dir): ${import_chalk4.default.dim(d.droppedByWorkingDir)}
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
if (d.droppedMissing > 0) {
|
|
111
|
+
process.stderr.write(` ${ARROW} Dropped (missing): ${import_chalk4.default.dim(d.droppedMissing)}
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
if (result.zeroFilesReason) {
|
|
115
|
+
process.stderr.write("\n");
|
|
116
|
+
process.stderr.write(` ${WARN} ${import_chalk4.default.yellow("Zero files reason:")}
|
|
117
|
+
`);
|
|
118
|
+
process.stderr.write(` ${import_chalk4.default.dim(result.zeroFilesReason)}
|
|
119
|
+
`);
|
|
120
|
+
}
|
|
121
|
+
if (result.files.length > 0) {
|
|
122
|
+
process.stderr.write("\n");
|
|
123
|
+
process.stderr.write(` ${DIM("Changed files (first 20):")}
|
|
124
|
+
`);
|
|
125
|
+
for (const f of result.files.slice(0, 20)) {
|
|
126
|
+
process.stderr.write(` ${import_chalk4.default.dim("\xB7")} ${import_chalk4.default.white(f)}
|
|
127
|
+
`);
|
|
128
|
+
}
|
|
129
|
+
if (result.files.length > 20) {
|
|
130
|
+
process.stderr.write(` ${import_chalk4.default.dim(`\u2026 and ${result.files.length - 20} more`)}
|
|
131
|
+
`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
process.stderr.write("\n");
|
|
135
|
+
}
|
|
136
|
+
function printCIDebug(info) {
|
|
137
|
+
process.stderr.write("\n");
|
|
138
|
+
process.stderr.write(`${LABEL(" CI Environment")}
|
|
139
|
+
`);
|
|
140
|
+
process.stderr.write(`${DIM(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
|
|
141
|
+
`);
|
|
142
|
+
const env = (name, value) => {
|
|
143
|
+
const val = value ? import_chalk4.default.white(value) : import_chalk4.default.dim("(not set)");
|
|
144
|
+
process.stderr.write(` ${DIM(name.padEnd(26))} ${val}
|
|
145
|
+
`);
|
|
146
|
+
};
|
|
147
|
+
env("GITHUB_ACTIONS:", info.isGitHubActions ? "true" : "false");
|
|
148
|
+
env("GITHUB_BASE_REF:", info.githubBaseRef);
|
|
149
|
+
env("GITHUB_SHA:", info.githubSha);
|
|
150
|
+
env("GITHUB_REF:", info.githubRef);
|
|
151
|
+
env("GITHUB_REPOSITORY:", info.githubRepository);
|
|
152
|
+
env("GITHUB_WORKFLOW:", info.githubWorkflow);
|
|
153
|
+
env("GITHUB_RUN_ID:", info.githubRunId);
|
|
154
|
+
env("GITHUB_EVENT_NAME:", info.githubEventName);
|
|
155
|
+
process.stderr.write("\n");
|
|
156
|
+
env("GITHUB_STEP_SUMMARY:", info.githubStepSummary);
|
|
157
|
+
env("GITHUB_OUTPUT:", info.githubOutput);
|
|
158
|
+
process.stderr.write("\n");
|
|
159
|
+
env("Scan preset:", info.preset);
|
|
160
|
+
env("Fail on:", info.failOn);
|
|
161
|
+
env("Scan mode:", info.scanMode);
|
|
162
|
+
if (info.sarifOutputPath) {
|
|
163
|
+
env("SARIF output:", info.sarifOutputPath);
|
|
164
|
+
}
|
|
165
|
+
process.stderr.write("\n");
|
|
166
|
+
}
|
|
167
|
+
var import_chalk4, DIM, LABEL, OK, WARN, ERR, ARROW;
|
|
168
|
+
var init_debug = __esm({
|
|
169
|
+
"cli/utils/debug.ts"() {
|
|
170
|
+
"use strict";
|
|
171
|
+
import_chalk4 = __toESM(require("chalk"));
|
|
172
|
+
DIM = import_chalk4.default.dim;
|
|
173
|
+
LABEL = import_chalk4.default.cyan.bold;
|
|
174
|
+
OK = import_chalk4.default.green("\u2714");
|
|
175
|
+
WARN = import_chalk4.default.yellow("\u26A0");
|
|
176
|
+
ERR = import_chalk4.default.red("\u2716");
|
|
177
|
+
ARROW = import_chalk4.default.dim("\u2192");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
33
181
|
// cli/utils/config-manager.ts
|
|
34
182
|
var config_manager_exports = {};
|
|
35
183
|
__export(config_manager_exports, {
|
|
@@ -266,19 +414,19 @@ function addIgnoresToLegacyConfig(existing) {
|
|
|
266
414
|
function getConfigFilePath(configType, cwd = process.cwd()) {
|
|
267
415
|
switch (configType) {
|
|
268
416
|
case "flat-js":
|
|
269
|
-
return
|
|
417
|
+
return import_path6.default.join(cwd, "eslint.config.js");
|
|
270
418
|
case "flat-mjs":
|
|
271
|
-
return
|
|
419
|
+
return import_path6.default.join(cwd, "eslint.config.mjs");
|
|
272
420
|
case "flat-cjs":
|
|
273
|
-
return
|
|
421
|
+
return import_path6.default.join(cwd, "eslint.config.cjs");
|
|
274
422
|
case "eslintrc-js":
|
|
275
|
-
return
|
|
423
|
+
return import_path6.default.join(cwd, ".eslintrc.js");
|
|
276
424
|
case "eslintrc-cjs":
|
|
277
|
-
return
|
|
425
|
+
return import_path6.default.join(cwd, ".eslintrc.cjs");
|
|
278
426
|
case "eslintrc-json":
|
|
279
|
-
return
|
|
427
|
+
return import_path6.default.join(cwd, ".eslintrc.json");
|
|
280
428
|
default:
|
|
281
|
-
return
|
|
429
|
+
return import_path6.default.join(cwd, ".eslintrc.js");
|
|
282
430
|
}
|
|
283
431
|
}
|
|
284
432
|
function validateFlatConfigText(content) {
|
|
@@ -313,12 +461,12 @@ function validateLegacyConfigText(content) {
|
|
|
313
461
|
}
|
|
314
462
|
return problems;
|
|
315
463
|
}
|
|
316
|
-
var import_fs6,
|
|
464
|
+
var import_fs6, import_path6, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
|
|
317
465
|
var init_config_manager = __esm({
|
|
318
466
|
"cli/utils/config-manager.ts"() {
|
|
319
467
|
"use strict";
|
|
320
468
|
import_fs6 = __toESM(require("fs"));
|
|
321
|
-
|
|
469
|
+
import_path6 = __toESM(require("path"));
|
|
322
470
|
AI_GUARD_MARKER = "eslint-plugin-ai-guard";
|
|
323
471
|
DEFAULT_FLAT_IGNORES = [
|
|
324
472
|
"node_modules/**",
|
|
@@ -342,7 +490,7 @@ var init_config_manager = __esm({
|
|
|
342
490
|
// cli/index.ts
|
|
343
491
|
var import_commander = require("commander");
|
|
344
492
|
var import_fs11 = __toESM(require("fs"));
|
|
345
|
-
var
|
|
493
|
+
var import_path14 = __toESM(require("path"));
|
|
346
494
|
|
|
347
495
|
// cli/commands/run.ts
|
|
348
496
|
var import_ora = __toESM(require("ora"));
|
|
@@ -544,24 +692,46 @@ var log = {
|
|
|
544
692
|
|
|
545
693
|
// cli/utils/eslint-runner.ts
|
|
546
694
|
var ISSUE_CONFIDENCE = {
|
|
695
|
+
// HIGH — near-certain bugs: false positive rate <2% in real-world codebases
|
|
547
696
|
"ai-guard/no-hardcoded-secret": "high",
|
|
697
|
+
// Literal credential string in source: almost always a mistake
|
|
548
698
|
"ai-guard/no-eval-dynamic": "high",
|
|
699
|
+
// Dynamic eval: no legitimate modern use case
|
|
549
700
|
"ai-guard/no-floating-promise": "high",
|
|
550
|
-
|
|
701
|
+
// Unhandled promise: async failures silently swallowed
|
|
702
|
+
// MEDIUM — likely bugs, but legitimate patterns exist
|
|
703
|
+
"ai-guard/no-empty-catch": "medium",
|
|
704
|
+
// CHANGED from high: empty catch is valid for optional ops (JSON.parse, feature detection)
|
|
551
705
|
"ai-guard/no-sql-string-concat": "medium",
|
|
706
|
+
// SQL injection risk, but ORM template literals can FP
|
|
552
707
|
"ai-guard/no-await-in-loop": "medium",
|
|
708
|
+
// Sequential await is often intentional (rate limiting, ordering)
|
|
553
709
|
"ai-guard/require-auth-middleware": "medium",
|
|
710
|
+
// Context-blind: public routes are valid
|
|
554
711
|
"ai-guard/require-authz-check": "medium",
|
|
712
|
+
// Context-blind: some resources are public
|
|
555
713
|
"ai-guard/no-catch-log-rethrow": "medium",
|
|
714
|
+
// Log-then-rethrow is a known antipattern but sometimes intentional
|
|
556
715
|
"ai-guard/no-catch-without-use": "medium",
|
|
716
|
+
// Unused catch param common in TS
|
|
557
717
|
"ai-guard/no-unsafe-deserialize": "medium",
|
|
718
|
+
// JSON.parse is sometimes safe with trusted input
|
|
719
|
+
// LOW — possible improvement, high false-positive rate
|
|
558
720
|
"ai-guard/no-async-array-callback": "low",
|
|
721
|
+
// Promise.all is better, but forEach async is not always a bug
|
|
559
722
|
"ai-guard/no-dead-branch": "low",
|
|
723
|
+
// Static analysis of conditions is inherently imprecise
|
|
560
724
|
"ai-guard/no-broad-exception": "low",
|
|
725
|
+
// Catching Error is common and sometimes correct
|
|
561
726
|
"ai-guard/no-console-in-handler": "low",
|
|
727
|
+
// Stylistic: debug logs left in vs intentional logging
|
|
562
728
|
"ai-guard/no-duplicate-logic-block": "low",
|
|
729
|
+
// May be coincidental similarity, not true duplication
|
|
730
|
+
// INFORMATIONAL — hints only, never fail CI
|
|
563
731
|
"ai-guard/no-async-without-await": "informational",
|
|
732
|
+
// Framework patterns (Next.js RSC, etc.) make this very noisy
|
|
564
733
|
"ai-guard/no-redundant-await": "informational"
|
|
734
|
+
// Explicit await in return is idiomatic in many codebases
|
|
565
735
|
};
|
|
566
736
|
var ISSUE_CATEGORY = {
|
|
567
737
|
"ai-guard/no-floating-promise": "Async Reliability",
|
|
@@ -742,8 +912,8 @@ function classifyEcosystemType(msg) {
|
|
|
742
912
|
return "foreign-rule";
|
|
743
913
|
}
|
|
744
914
|
async function loadPluginModuleFromCwd(cwd) {
|
|
745
|
-
const { createRequire:
|
|
746
|
-
const requireFromCwd =
|
|
915
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
916
|
+
const requireFromCwd = createRequire3(import_path.default.join(cwd, "package.json"));
|
|
747
917
|
try {
|
|
748
918
|
const resolved = requireFromCwd.resolve("eslint-plugin-ai-guard");
|
|
749
919
|
return import((0, import_url.pathToFileURL)(resolved).href);
|
|
@@ -1034,6 +1204,30 @@ async function runEslint(options) {
|
|
|
1034
1204
|
};
|
|
1035
1205
|
}
|
|
1036
1206
|
|
|
1207
|
+
// cli/utils/version.ts
|
|
1208
|
+
var import_module = require("module");
|
|
1209
|
+
var import_url2 = require("url");
|
|
1210
|
+
var import_path2 = __toESM(require("path"));
|
|
1211
|
+
var import_meta = {};
|
|
1212
|
+
var _version;
|
|
1213
|
+
function readVersion() {
|
|
1214
|
+
try {
|
|
1215
|
+
const require_ = (0, import_module.createRequire)(import_meta.url);
|
|
1216
|
+
const pkgPath = import_path2.default.resolve(
|
|
1217
|
+
import_path2.default.dirname((0, import_url2.fileURLToPath)(import_meta.url)),
|
|
1218
|
+
"../../package.json"
|
|
1219
|
+
);
|
|
1220
|
+
const pkg = require_(pkgPath);
|
|
1221
|
+
if (pkg.version) return pkg.version;
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
if (typeof __PKG_VERSION__ !== "undefined") {
|
|
1225
|
+
return __PKG_VERSION__;
|
|
1226
|
+
}
|
|
1227
|
+
return "0.0.0";
|
|
1228
|
+
}
|
|
1229
|
+
var PKG_VERSION = _version ?? (_version = readVersion());
|
|
1230
|
+
|
|
1037
1231
|
// cli/utils/sarif.ts
|
|
1038
1232
|
var RULE_DOCS = {
|
|
1039
1233
|
"ai-guard/no-empty-catch": { shortDesc: "Empty catch block silently swallows errors", tags: ["reliability"] },
|
|
@@ -1183,7 +1377,7 @@ function buildSarifResult(issue, file) {
|
|
|
1183
1377
|
}
|
|
1184
1378
|
};
|
|
1185
1379
|
}
|
|
1186
|
-
function buildSarifLog(result, version =
|
|
1380
|
+
function buildSarifLog(result, version = PKG_VERSION) {
|
|
1187
1381
|
const usedRuleIds = collectUsedRuleIds(result);
|
|
1188
1382
|
const rules = buildRuleDescriptors(usedRuleIds);
|
|
1189
1383
|
const sarifResults = [];
|
|
@@ -1223,6 +1417,13 @@ function sarifToJson(log2) {
|
|
|
1223
1417
|
|
|
1224
1418
|
// cli/utils/github-summary.ts
|
|
1225
1419
|
var import_fs2 = __toESM(require("fs"));
|
|
1420
|
+
function warnEnvIssue(name, filePath, err) {
|
|
1421
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1422
|
+
process.stderr.write(
|
|
1423
|
+
`[ai-guard] Warning: Could not write to ${name}=${filePath}: ${msg}
|
|
1424
|
+
`
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1226
1427
|
function escapeMarkdown(text) {
|
|
1227
1428
|
return text.replace(/[|`*_[\]<>]/g, "\\$&");
|
|
1228
1429
|
}
|
|
@@ -1413,8 +1614,9 @@ function writeGitHubSummary(result, options) {
|
|
|
1413
1614
|
if (!summaryPath) return;
|
|
1414
1615
|
try {
|
|
1415
1616
|
const markdown = buildGitHubSummaryMarkdown(result, options);
|
|
1416
|
-
import_fs2.default.appendFileSync(summaryPath, markdown + "\n");
|
|
1417
|
-
} catch {
|
|
1617
|
+
import_fs2.default.appendFileSync(summaryPath, markdown + "\n", { encoding: "utf-8" });
|
|
1618
|
+
} catch (err) {
|
|
1619
|
+
warnEnvIssue("GITHUB_STEP_SUMMARY", summaryPath, err);
|
|
1418
1620
|
}
|
|
1419
1621
|
}
|
|
1420
1622
|
function writeGitHubOutputs(result, sarifPath) {
|
|
@@ -1432,14 +1634,15 @@ function writeGitHubOutputs(result, sarifPath) {
|
|
|
1432
1634
|
...sarifPath ? [`sarif-file=${sarifPath}`] : []
|
|
1433
1635
|
];
|
|
1434
1636
|
try {
|
|
1435
|
-
import_fs2.default.appendFileSync(outputPath, outputs.join("\n") + "\n");
|
|
1436
|
-
} catch {
|
|
1637
|
+
import_fs2.default.appendFileSync(outputPath, outputs.join("\n") + "\n", { encoding: "utf-8" });
|
|
1638
|
+
} catch (err) {
|
|
1639
|
+
warnEnvIssue("GITHUB_OUTPUT", outputPath, err);
|
|
1437
1640
|
}
|
|
1438
1641
|
}
|
|
1439
1642
|
|
|
1440
1643
|
// cli/utils/git-diff.ts
|
|
1441
1644
|
var import_child_process = require("child_process");
|
|
1442
|
-
var
|
|
1645
|
+
var import_path3 = __toESM(require("path"));
|
|
1443
1646
|
var import_fs3 = __toESM(require("fs"));
|
|
1444
1647
|
var DEFAULT_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
1445
1648
|
var IGNORE_PREFIXES = [
|
|
@@ -1466,7 +1669,8 @@ function runGit(args, cwd) {
|
|
|
1466
1669
|
return (0, import_child_process.execSync)(`git ${args}`, {
|
|
1467
1670
|
cwd,
|
|
1468
1671
|
encoding: "utf-8",
|
|
1469
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1672
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1673
|
+
timeout: 15e3
|
|
1470
1674
|
}).trim();
|
|
1471
1675
|
} catch {
|
|
1472
1676
|
return null;
|
|
@@ -1475,35 +1679,103 @@ function runGit(args, cwd) {
|
|
|
1475
1679
|
function isGitAvailable(cwd) {
|
|
1476
1680
|
return runGit("rev-parse --is-inside-work-tree", cwd) === "true";
|
|
1477
1681
|
}
|
|
1478
|
-
function
|
|
1479
|
-
|
|
1682
|
+
function isShallowClone(cwd) {
|
|
1683
|
+
return runGit("rev-parse --is-shallow-repository", cwd) === "true";
|
|
1684
|
+
}
|
|
1685
|
+
function isDetachedHead(cwd) {
|
|
1686
|
+
const result = runGit("symbolic-ref --quiet HEAD", cwd);
|
|
1687
|
+
return result === null;
|
|
1688
|
+
}
|
|
1689
|
+
function normalizeBranchName(ref) {
|
|
1690
|
+
return ref.replace(/^origin\//, "").trim();
|
|
1691
|
+
}
|
|
1692
|
+
function tryPrDiff(branch, cwd, debugInfo) {
|
|
1693
|
+
const rawBranch = normalizeBranchName(branch);
|
|
1694
|
+
{
|
|
1695
|
+
const strategy = `merge-base HEAD origin/${rawBranch}`;
|
|
1696
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1697
|
+
const mergeBase = runGit(`merge-base HEAD origin/${rawBranch}`, cwd);
|
|
1698
|
+
if (mergeBase) {
|
|
1699
|
+
debugInfo.mergeBase = mergeBase;
|
|
1700
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${mergeBase}`, cwd);
|
|
1701
|
+
if (out !== null) {
|
|
1702
|
+
debugInfo.strategySucceeded = strategy;
|
|
1703
|
+
return { output: out, strategy, mergeBase };
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
{
|
|
1708
|
+
const strategy = `merge-base HEAD ${rawBranch}`;
|
|
1709
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1710
|
+
const mergeBase = runGit(`merge-base HEAD ${rawBranch}`, cwd);
|
|
1711
|
+
if (mergeBase) {
|
|
1712
|
+
if (!debugInfo.mergeBase) debugInfo.mergeBase = mergeBase;
|
|
1713
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${mergeBase}`, cwd);
|
|
1714
|
+
if (out !== null) {
|
|
1715
|
+
debugInfo.strategySucceeded = strategy;
|
|
1716
|
+
return { output: out, strategy, mergeBase };
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
{
|
|
1721
|
+
const strategy = `diff origin/${rawBranch}...HEAD`;
|
|
1722
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1723
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT origin/${rawBranch}...HEAD`, cwd);
|
|
1724
|
+
if (out !== null) {
|
|
1725
|
+
debugInfo.strategySucceeded = strategy;
|
|
1726
|
+
return { output: out, strategy };
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
{
|
|
1730
|
+
const strategy = `diff ${rawBranch}...HEAD`;
|
|
1731
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1732
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${rawBranch}...HEAD`, cwd);
|
|
1733
|
+
if (out !== null) {
|
|
1734
|
+
debugInfo.strategySucceeded = strategy;
|
|
1735
|
+
return { output: out, strategy };
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
{
|
|
1739
|
+
const strategy = "diff HEAD~1 (shallow fallback)";
|
|
1740
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1741
|
+
const out = runGit("diff --name-only --diff-filter=ACMRT HEAD~1", cwd);
|
|
1742
|
+
if (out !== null) {
|
|
1743
|
+
debugInfo.strategySucceeded = strategy;
|
|
1744
|
+
return { output: out, strategy };
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
debugInfo.strategySucceeded = null;
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1750
|
+
function filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo) {
|
|
1480
1751
|
const kept = [];
|
|
1481
1752
|
for (const rel of rawFiles) {
|
|
1482
|
-
const
|
|
1753
|
+
const normalized = rel.replace(/\\/g, "/").trim();
|
|
1754
|
+
if (!normalized) continue;
|
|
1755
|
+
const ext = import_path3.default.extname(normalized).toLowerCase();
|
|
1483
1756
|
if (!extensions.includes(ext)) {
|
|
1484
|
-
|
|
1757
|
+
debugInfo.droppedByExtension++;
|
|
1485
1758
|
continue;
|
|
1486
1759
|
}
|
|
1487
|
-
const normalized = rel.replace(/\\/g, "/");
|
|
1488
1760
|
if (IGNORE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
1489
|
-
|
|
1761
|
+
debugInfo.droppedByIgnore++;
|
|
1490
1762
|
continue;
|
|
1491
1763
|
}
|
|
1492
1764
|
if (workingDirectory) {
|
|
1493
1765
|
const wdNorm = workingDirectory.replace(/\\/g, "/").replace(/\/$/, "");
|
|
1494
1766
|
if (!normalized.startsWith(wdNorm + "/") && normalized !== wdNorm) {
|
|
1495
|
-
|
|
1767
|
+
debugInfo.droppedByWorkingDir++;
|
|
1496
1768
|
continue;
|
|
1497
1769
|
}
|
|
1498
1770
|
}
|
|
1499
|
-
const abs =
|
|
1771
|
+
const abs = import_path3.default.resolve(cwd, normalized);
|
|
1500
1772
|
if (!import_fs3.default.existsSync(abs)) {
|
|
1501
|
-
|
|
1773
|
+
debugInfo.droppedMissing++;
|
|
1502
1774
|
continue;
|
|
1503
1775
|
}
|
|
1504
1776
|
kept.push(abs);
|
|
1505
1777
|
}
|
|
1506
|
-
return
|
|
1778
|
+
return kept;
|
|
1507
1779
|
}
|
|
1508
1780
|
function parseFileList(output) {
|
|
1509
1781
|
return output.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
@@ -1514,46 +1786,96 @@ function getChangedFiles(options = {}) {
|
|
|
1514
1786
|
base,
|
|
1515
1787
|
cwd = process.cwd(),
|
|
1516
1788
|
extensions = DEFAULT_EXTENSIONS,
|
|
1517
|
-
workingDirectory
|
|
1789
|
+
workingDirectory,
|
|
1790
|
+
debug = false
|
|
1518
1791
|
} = options;
|
|
1519
|
-
const
|
|
1792
|
+
const debugInfo = {
|
|
1793
|
+
strategiesAttempted: [],
|
|
1794
|
+
strategySucceeded: null,
|
|
1795
|
+
isShallowClone: false,
|
|
1796
|
+
isDetachedHead: false,
|
|
1797
|
+
rawFileCount: 0,
|
|
1798
|
+
droppedByExtension: 0,
|
|
1799
|
+
droppedByIgnore: 0,
|
|
1800
|
+
droppedMissing: 0,
|
|
1801
|
+
droppedByWorkingDir: 0
|
|
1802
|
+
};
|
|
1520
1803
|
if (!isGitAvailable(cwd)) {
|
|
1521
|
-
return {
|
|
1804
|
+
return {
|
|
1805
|
+
files: [],
|
|
1806
|
+
mode: "fallback",
|
|
1807
|
+
totalDetected: 0,
|
|
1808
|
+
filteredOut: 0,
|
|
1809
|
+
debugInfo,
|
|
1810
|
+
zeroFilesReason: "Git is not available in this directory. Run inside a git repository."
|
|
1811
|
+
};
|
|
1522
1812
|
}
|
|
1813
|
+
debugInfo.isShallowClone = isShallowClone(cwd);
|
|
1814
|
+
debugInfo.isDetachedHead = isDetachedHead(cwd);
|
|
1815
|
+
const envBase = process.env.GITHUB_BASE_REF ? normalizeBranchName(process.env.GITHUB_BASE_REF) : void 0;
|
|
1816
|
+
const resolvedBranch = base ? normalizeBranchName(base) : envBase;
|
|
1817
|
+
debugInfo.resolvedBase = resolvedBranch;
|
|
1523
1818
|
let rawOutput = null;
|
|
1524
1819
|
let mode;
|
|
1525
1820
|
let usedBase;
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1821
|
+
let zeroFilesReason;
|
|
1822
|
+
if (staged) {
|
|
1823
|
+
rawOutput = runGit("diff --name-only --cached --diff-filter=ACMRT", cwd);
|
|
1824
|
+
mode = "staged";
|
|
1825
|
+
debugInfo.strategiesAttempted.push("git diff --cached");
|
|
1826
|
+
if (rawOutput !== null) debugInfo.strategySucceeded = "git diff --cached";
|
|
1531
1827
|
if (!rawOutput) {
|
|
1532
|
-
|
|
1828
|
+
zeroFilesReason = "No staged files found. Run `git add <files>` before using --staged.";
|
|
1533
1829
|
}
|
|
1830
|
+
} else if (resolvedBranch) {
|
|
1534
1831
|
mode = "pr-diff";
|
|
1535
|
-
usedBase =
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1832
|
+
usedBase = resolvedBranch;
|
|
1833
|
+
const diffResult = tryPrDiff(resolvedBranch, cwd, debugInfo);
|
|
1834
|
+
if (diffResult) {
|
|
1835
|
+
rawOutput = diffResult.output;
|
|
1836
|
+
} else {
|
|
1837
|
+
const shallow = debugInfo.isShallowClone ? " Shallow clone detected \u2014 try fetch-depth: 0 in your workflow." : "";
|
|
1838
|
+
const detached = debugInfo.isDetachedHead ? " HEAD is detached \u2014 ensure checkout fetches the branch." : "";
|
|
1839
|
+
zeroFilesReason = `PR diff failed against "${resolvedBranch}". Tried: ${debugInfo.strategiesAttempted.join(", ")}.${shallow}${detached} Run with --debug-git for full diagnosis.`;
|
|
1840
|
+
}
|
|
1539
1841
|
} else {
|
|
1842
|
+
mode = "uncommitted";
|
|
1540
1843
|
const staged_ = runGit("diff --name-only --cached --diff-filter=ACMRT", cwd) ?? "";
|
|
1541
1844
|
const unstaged = runGit("diff --name-only --diff-filter=ACMRT", cwd) ?? "";
|
|
1845
|
+
debugInfo.strategiesAttempted.push("git diff --cached", "git diff (unstaged)");
|
|
1542
1846
|
const combined = /* @__PURE__ */ new Set([...parseFileList(staged_), ...parseFileList(unstaged)]);
|
|
1543
1847
|
rawOutput = [...combined].join("\n");
|
|
1544
|
-
|
|
1848
|
+
if (rawOutput) debugInfo.strategySucceeded = "uncommitted changes";
|
|
1849
|
+
if (!rawOutput) {
|
|
1850
|
+
zeroFilesReason = "No uncommitted changes found. Use --pr to scan PR changes, or --staged for staged files.";
|
|
1851
|
+
}
|
|
1545
1852
|
}
|
|
1546
1853
|
if (!rawOutput) {
|
|
1547
|
-
return {
|
|
1854
|
+
return {
|
|
1855
|
+
files: [],
|
|
1856
|
+
mode,
|
|
1857
|
+
base: usedBase,
|
|
1858
|
+
totalDetected: 0,
|
|
1859
|
+
filteredOut: 0,
|
|
1860
|
+
debugInfo,
|
|
1861
|
+
zeroFilesReason
|
|
1862
|
+
};
|
|
1548
1863
|
}
|
|
1549
1864
|
const rawFiles = parseFileList(rawOutput);
|
|
1550
|
-
|
|
1865
|
+
debugInfo.rawFileCount = rawFiles.length;
|
|
1866
|
+
const kept = filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo);
|
|
1867
|
+
const dropped = debugInfo.droppedByExtension + debugInfo.droppedByIgnore + debugInfo.droppedByWorkingDir + debugInfo.droppedMissing;
|
|
1868
|
+
if (kept.length === 0 && rawFiles.length > 0) {
|
|
1869
|
+
zeroFilesReason = `${rawFiles.length} changed file${rawFiles.length !== 1 ? "s" : ""} detected but all filtered out. Extension filter: ${debugInfo.droppedByExtension}, ignored paths: ${debugInfo.droppedByIgnore}, missing from disk: ${debugInfo.droppedMissing}.`;
|
|
1870
|
+
}
|
|
1551
1871
|
return {
|
|
1552
1872
|
files: kept,
|
|
1553
1873
|
mode,
|
|
1554
1874
|
base: usedBase,
|
|
1555
1875
|
totalDetected: rawFiles.length,
|
|
1556
|
-
filteredOut: dropped
|
|
1876
|
+
filteredOut: dropped,
|
|
1877
|
+
debugInfo,
|
|
1878
|
+
zeroFilesReason: kept.length === 0 ? zeroFilesReason : void 0
|
|
1557
1879
|
};
|
|
1558
1880
|
}
|
|
1559
1881
|
function isGitHubActions() {
|
|
@@ -1957,32 +2279,58 @@ function resolveExitCode(result, failOn, maxWarnings) {
|
|
|
1957
2279
|
}
|
|
1958
2280
|
|
|
1959
2281
|
// cli/commands/changed.ts
|
|
1960
|
-
var
|
|
2282
|
+
var import_path4 = __toESM(require("path"));
|
|
1961
2283
|
var import_fs4 = __toESM(require("fs"));
|
|
1962
2284
|
var import_ora2 = __toESM(require("ora"));
|
|
1963
|
-
var
|
|
2285
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
1964
2286
|
function registerChangedCommand(program2) {
|
|
1965
|
-
program2.command("changed").description("Scan only changed files in the current PR or working tree (fastest CI mode)").option("--pr", "Scan files changed vs base branch (auto-detects GITHUB_BASE_REF in CI)").option("--staged", "Scan only staged (git add'd) files").option("--base <branch>", "Base branch to diff against (
|
|
2287
|
+
program2.command("changed").description("Scan only changed files in the current PR or working tree (fastest CI mode)").option("--pr", "Scan files changed vs base branch (auto-detects GITHUB_BASE_REF in CI)").option("--staged", "Scan only staged (git add'd) files").option("--base <branch>", "Base branch to diff against (raw name: main, develop \u2014 not origin/main)").option("--path <dir>", "Root path / working directory for scanning", ".").option("--strict", "Use the strict rule preset (all rules at error)").option("--security", "Use the security-only rule preset").option("--fail-on <level>", "Fail CI on: high | medium | any | errors | none", "high").option("--json", "Output results as JSON").option("--sarif", "Output results as SARIF 2.1.0 (for GitHub Code Scanning)").option("--sarif-output <file>", "Write SARIF to this file path").option("--sarif-stdout", "Force SARIF to stdout even in CI (overrides auto-file behavior)").option("--github-summary", "Write GitHub step summary (auto-enabled in GitHub Actions)").option(
|
|
1966
2288
|
"--max-warnings <n>",
|
|
1967
2289
|
"Fail if warnings exceed this count",
|
|
1968
2290
|
(v) => Number.parseInt(v, 10)
|
|
1969
|
-
).option("--verbose", "Expand all findings (disable grouping)").option("--quiet", "Show errors only").option("--debug-timing", "Print per-phase timing diagnostics").action(async (opts) => {
|
|
2291
|
+
).option("--verbose", "Expand all findings (disable grouping)").option("--quiet", "Show errors only").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-git", "Print full git resolution trace (useful for diagnosing zero-file issues)").option("--debug-ci", "Print CI environment state (GITHUB_* vars, SARIF path, summary path)").action(async (opts) => {
|
|
1970
2292
|
const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
|
|
1971
2293
|
const inCI = isGitHubActions();
|
|
1972
2294
|
const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
|
|
2295
|
+
const sarifToFile = opts.sarif && !opts.sarifStdout && (opts.sarifOutput || inCI);
|
|
2296
|
+
const resolvedSarifPath = opts.sarifOutput ?? (sarifToFile ? "ai-guard-results.sarif" : void 0);
|
|
1973
2297
|
if (!opts.json && !opts.sarif) {
|
|
1974
2298
|
log.banner("AI GUARD");
|
|
1975
2299
|
}
|
|
2300
|
+
if (opts.debugCi) {
|
|
2301
|
+
const { printCIDebug: printCIDebug2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|
|
2302
|
+
printCIDebug2({
|
|
2303
|
+
isGitHubActions: inCI,
|
|
2304
|
+
githubBaseRef: process.env.GITHUB_BASE_REF,
|
|
2305
|
+
githubSha: process.env.GITHUB_SHA,
|
|
2306
|
+
githubRef: process.env.GITHUB_REF,
|
|
2307
|
+
githubRepository: process.env.GITHUB_REPOSITORY,
|
|
2308
|
+
githubWorkflow: process.env.GITHUB_WORKFLOW,
|
|
2309
|
+
githubRunId: process.env.GITHUB_RUN_ID,
|
|
2310
|
+
githubEventName: process.env.GITHUB_EVENT_NAME,
|
|
2311
|
+
githubStepSummary: process.env.GITHUB_STEP_SUMMARY,
|
|
2312
|
+
githubOutput: process.env.GITHUB_OUTPUT,
|
|
2313
|
+
sarifOutputPath: resolvedSarifPath,
|
|
2314
|
+
preset,
|
|
2315
|
+
failOn: opts.failOn,
|
|
2316
|
+
scanMode: opts.staged ? "staged" : baseRef ? "pr-diff" : "uncommitted"
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
1976
2319
|
const cwd = process.cwd();
|
|
1977
|
-
const resolvedPath =
|
|
2320
|
+
const resolvedPath = import_path4.default.resolve(opts.path);
|
|
1978
2321
|
const workingDirectory = opts.path !== "." ? opts.path : void 0;
|
|
1979
|
-
const spinner = opts.json || opts.sarif ? null : (0, import_ora2.default)({ text:
|
|
2322
|
+
const spinner = opts.json || opts.sarif ? null : (0, import_ora2.default)({ text: import_chalk5.default.dim("Detecting changed files\u2026"), color: "cyan" }).start();
|
|
1980
2323
|
const changedResult = getChangedFiles({
|
|
1981
2324
|
staged: opts.staged ?? false,
|
|
1982
|
-
base: baseRef
|
|
2325
|
+
base: baseRef,
|
|
1983
2326
|
cwd,
|
|
1984
|
-
workingDirectory
|
|
2327
|
+
workingDirectory,
|
|
2328
|
+
debug: opts.debugGit
|
|
1985
2329
|
});
|
|
2330
|
+
if (opts.debugGit) {
|
|
2331
|
+
const { printGitDebug: printGitDebug2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|
|
2332
|
+
printGitDebug2(changedResult);
|
|
2333
|
+
}
|
|
1986
2334
|
if (changedResult.files.length === 0) {
|
|
1987
2335
|
spinner?.stop();
|
|
1988
2336
|
if (changedResult.mode === "fallback") {
|
|
@@ -1995,18 +2343,25 @@ function registerChangedCommand(program2) {
|
|
|
1995
2343
|
targetPath: resolvedPath.toString(),
|
|
1996
2344
|
debugTiming: opts.debugTiming
|
|
1997
2345
|
});
|
|
1998
|
-
handleResults(result2, opts, preset, "full", 0, inCI);
|
|
2346
|
+
handleResults(result2, opts, preset, "full", 0, inCI, resolvedSarifPath);
|
|
1999
2347
|
return;
|
|
2000
2348
|
}
|
|
2001
2349
|
if (!opts.json && !opts.sarif) {
|
|
2002
2350
|
log.blank();
|
|
2003
|
-
log.print(` ${
|
|
2351
|
+
log.print(` ${import_chalk5.default.yellow("\u26A0")} No changed JS/TS files detected \u2014 nothing to scan`);
|
|
2352
|
+
if (changedResult.zeroFilesReason) {
|
|
2353
|
+
log.print(import_chalk5.default.dim(` ${changedResult.zeroFilesReason}`));
|
|
2354
|
+
}
|
|
2004
2355
|
if (changedResult.filteredOut > 0) {
|
|
2005
|
-
log.print(
|
|
2356
|
+
log.print(import_chalk5.default.dim(` (${changedResult.filteredOut} file${changedResult.filteredOut !== 1 ? "s" : ""} filtered out)`));
|
|
2357
|
+
}
|
|
2358
|
+
if (!opts.debugGit) {
|
|
2359
|
+
log.print(import_chalk5.default.dim(" Run with --debug-git for full git resolution trace."));
|
|
2006
2360
|
}
|
|
2007
2361
|
log.blank();
|
|
2008
2362
|
} else if (opts.json) {
|
|
2009
2363
|
console.log(JSON.stringify({
|
|
2364
|
+
version: PKG_VERSION,
|
|
2010
2365
|
preset,
|
|
2011
2366
|
scanMode: changedResult.mode,
|
|
2012
2367
|
changedFiles: 0,
|
|
@@ -2014,6 +2369,8 @@ function registerChangedCommand(program2) {
|
|
|
2014
2369
|
totalIssues: 0,
|
|
2015
2370
|
totalErrors: 0,
|
|
2016
2371
|
totalWarnings: 0,
|
|
2372
|
+
zeroFilesReason: changedResult.zeroFilesReason,
|
|
2373
|
+
debugInfo: opts.debugGit ? changedResult.debugInfo : void 0,
|
|
2017
2374
|
signalSummary: { high: 0, medium: 0, low: 0, informational: 0, ecosystemIssues: 0, parserErrors: 0 }
|
|
2018
2375
|
}, null, 2));
|
|
2019
2376
|
}
|
|
@@ -2021,7 +2378,7 @@ function registerChangedCommand(program2) {
|
|
|
2021
2378
|
return;
|
|
2022
2379
|
}
|
|
2023
2380
|
if (spinner) {
|
|
2024
|
-
spinner.text =
|
|
2381
|
+
spinner.text = import_chalk5.default.dim(`Scanning ${changedResult.files.length} changed file${changedResult.files.length !== 1 ? "s" : ""}\u2026`);
|
|
2025
2382
|
}
|
|
2026
2383
|
const result = await runEslint({
|
|
2027
2384
|
preset,
|
|
@@ -2030,10 +2387,11 @@ function registerChangedCommand(program2) {
|
|
|
2030
2387
|
debugTiming: opts.debugTiming
|
|
2031
2388
|
});
|
|
2032
2389
|
spinner?.stop();
|
|
2033
|
-
|
|
2390
|
+
const scanMode = changedResult.mode === "staged" ? "staged" : "changed";
|
|
2391
|
+
handleResults(result, opts, preset, scanMode, changedResult.files.length, inCI, resolvedSarifPath);
|
|
2034
2392
|
});
|
|
2035
2393
|
}
|
|
2036
|
-
function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI) {
|
|
2394
|
+
function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI, resolvedSarifPath) {
|
|
2037
2395
|
if (inCI || opts.githubSummary) {
|
|
2038
2396
|
writeGitHubSummary(result, { preset, scanMode, changedFilesCount });
|
|
2039
2397
|
writeGitHubOutputs(result, opts.sarifOutput);
|
|
@@ -2042,8 +2400,8 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2042
2400
|
if (opts.sarif) {
|
|
2043
2401
|
const sarifLog = buildSarifLog(result);
|
|
2044
2402
|
const sarifJson = sarifToJson(sarifLog);
|
|
2045
|
-
if (
|
|
2046
|
-
import_fs4.default.writeFileSync(
|
|
2403
|
+
if (resolvedSarifPath) {
|
|
2404
|
+
import_fs4.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
|
|
2047
2405
|
} else {
|
|
2048
2406
|
console.log(sarifJson);
|
|
2049
2407
|
}
|
|
@@ -2053,7 +2411,7 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2053
2411
|
if (opts.json) {
|
|
2054
2412
|
const signalSummary = buildSignalSummary(result);
|
|
2055
2413
|
const jsonOutput = {
|
|
2056
|
-
version:
|
|
2414
|
+
version: PKG_VERSION,
|
|
2057
2415
|
preset,
|
|
2058
2416
|
scanMode,
|
|
2059
2417
|
scannedPath: opts.path,
|
|
@@ -2115,7 +2473,7 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2115
2473
|
log.category(icon, cat, categoryErrors[cat] ?? 0, categoryWarnings[cat] ?? 0);
|
|
2116
2474
|
}
|
|
2117
2475
|
log.blank();
|
|
2118
|
-
log.print(` ${
|
|
2476
|
+
log.print(` ${import_chalk5.default.bold("Total:")} ${formatIssueCount(result.totalErrors, result.totalWarnings)}`);
|
|
2119
2477
|
log.blank();
|
|
2120
2478
|
if (actionableFiles.length > 0) {
|
|
2121
2479
|
log.section("Issues by File");
|
|
@@ -2126,18 +2484,18 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2126
2484
|
if (totalInfoCount > 0 && !quiet) {
|
|
2127
2485
|
if (verbose) {
|
|
2128
2486
|
log.section("Informational Hints");
|
|
2129
|
-
log.print(
|
|
2487
|
+
log.print(import_chalk5.default.dim(" Stylistic hints with higher false-positive rate \u2014 review before acting."));
|
|
2130
2488
|
log.blank();
|
|
2131
2489
|
renderIssuesByFile({ ...result, files: informationalFiles }, true, false);
|
|
2132
2490
|
} else {
|
|
2133
2491
|
log.blank();
|
|
2134
|
-
log.print(` ${
|
|
2492
|
+
log.print(` ${import_chalk5.default.gray("\u25B8")} ${import_chalk5.default.gray(`${totalInfoCount} informational hint${totalInfoCount !== 1 ? "s" : ""} \u2014 run with ${import_chalk5.default.cyan("--verbose")} to expand`)}`);
|
|
2135
2493
|
}
|
|
2136
2494
|
}
|
|
2137
2495
|
}
|
|
2138
2496
|
if (result.ecosystemIssues.length > 0) {
|
|
2139
2497
|
log.ecosystemSection("ESLint Config Issues");
|
|
2140
|
-
log.print(
|
|
2498
|
+
log.print(import_chalk5.default.dim(" These issues come from your project's ESLint config, not from ai-guard."));
|
|
2141
2499
|
log.blank();
|
|
2142
2500
|
const seen = /* @__PURE__ */ new Map();
|
|
2143
2501
|
for (const issue of result.ecosystemIssues) {
|
|
@@ -2158,11 +2516,11 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2158
2516
|
log.blank();
|
|
2159
2517
|
log.section("Next Steps");
|
|
2160
2518
|
if (result.totalIssues > 0) {
|
|
2161
|
-
log.info(`Run ${
|
|
2162
|
-
log.info(`Run ${
|
|
2519
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard baseline")} to save these issues and track only new ones`);
|
|
2520
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard report")} to generate a shareable HTML report`);
|
|
2163
2521
|
}
|
|
2164
2522
|
if (result.ecosystemIssues.length > 0) {
|
|
2165
|
-
log.info(`Run ${
|
|
2523
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard doctor")} to diagnose ESLint config issues`);
|
|
2166
2524
|
}
|
|
2167
2525
|
log.blank();
|
|
2168
2526
|
process.exit(getFailOnExitCode(result, opts.failOn, opts.maxWarnings));
|
|
@@ -2172,14 +2530,14 @@ function getFailOnExitCode(result, failOn, maxWarnings) {
|
|
|
2172
2530
|
}
|
|
2173
2531
|
|
|
2174
2532
|
// cli/commands/init.ts
|
|
2175
|
-
var
|
|
2176
|
-
var
|
|
2533
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
2534
|
+
var import_path7 = __toESM(require("path"));
|
|
2177
2535
|
var import_fs7 = __toESM(require("fs"));
|
|
2178
2536
|
|
|
2179
2537
|
// cli/utils/detector.ts
|
|
2180
2538
|
var import_fs5 = __toESM(require("fs"));
|
|
2181
|
-
var
|
|
2182
|
-
var
|
|
2539
|
+
var import_path5 = __toESM(require("path"));
|
|
2540
|
+
var import_module2 = require("module");
|
|
2183
2541
|
var FLAT_CONFIG_FILES = [
|
|
2184
2542
|
{ file: "eslint.config.js", type: "flat-js" },
|
|
2185
2543
|
{ file: "eslint.config.mjs", type: "flat-mjs" },
|
|
@@ -2195,11 +2553,11 @@ var LEGACY_CONFIG_FILES = [
|
|
|
2195
2553
|
var ALL_CONFIG_FILES = [...FLAT_CONFIG_FILES, ...LEGACY_CONFIG_FILES];
|
|
2196
2554
|
function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
2197
2555
|
try {
|
|
2198
|
-
const pkgPath =
|
|
2556
|
+
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2199
2557
|
if (import_fs5.default.existsSync(pkgPath)) {
|
|
2200
2558
|
return true;
|
|
2201
2559
|
}
|
|
2202
|
-
const req = (0,
|
|
2560
|
+
const req = (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json"));
|
|
2203
2561
|
req.resolve(`${pkgName}/package.json`);
|
|
2204
2562
|
return true;
|
|
2205
2563
|
} catch {
|
|
@@ -2208,8 +2566,8 @@ function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
|
2208
2566
|
}
|
|
2209
2567
|
function getPackageVersion(pkgName, cwd = process.cwd()) {
|
|
2210
2568
|
try {
|
|
2211
|
-
const pkgPath =
|
|
2212
|
-
const resolvedPath = import_fs5.default.existsSync(pkgPath) ? pkgPath : (0,
|
|
2569
|
+
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2570
|
+
const resolvedPath = import_fs5.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
|
|
2213
2571
|
const raw = import_fs5.default.readFileSync(resolvedPath, "utf-8");
|
|
2214
2572
|
const json = JSON.parse(raw);
|
|
2215
2573
|
return json.version ?? null;
|
|
@@ -2226,7 +2584,7 @@ function detectConfigType(cwd = process.cwd()) {
|
|
|
2226
2584
|
const found = [];
|
|
2227
2585
|
let first = null;
|
|
2228
2586
|
for (const { file, type } of ALL_CONFIG_FILES) {
|
|
2229
|
-
const fullPath =
|
|
2587
|
+
const fullPath = import_path5.default.join(cwd, file);
|
|
2230
2588
|
if (import_fs5.default.existsSync(fullPath)) {
|
|
2231
2589
|
found.push(fullPath);
|
|
2232
2590
|
if (!first) first = { type, path: fullPath };
|
|
@@ -2306,7 +2664,7 @@ async function verifyConfigLoads(configPath, cwd) {
|
|
|
2306
2664
|
overrideConfigFile: configPath
|
|
2307
2665
|
});
|
|
2308
2666
|
await eslint.lintText("", {
|
|
2309
|
-
filePath:
|
|
2667
|
+
filePath: import_path7.default.join(cwd, "_ai_guard_probe_.js")
|
|
2310
2668
|
});
|
|
2311
2669
|
return null;
|
|
2312
2670
|
} catch (err) {
|
|
@@ -2324,7 +2682,7 @@ function registerInitCommand(program2) {
|
|
|
2324
2682
|
const isDryRun = opts.dryRun === true;
|
|
2325
2683
|
log.banner("AI GUARD INIT");
|
|
2326
2684
|
if (isDryRun) {
|
|
2327
|
-
log.print(` ${
|
|
2685
|
+
log.print(` ${import_chalk6.default.yellow("\u2691 DRY RUN \u2014 no files will be written")}`);
|
|
2328
2686
|
log.blank();
|
|
2329
2687
|
}
|
|
2330
2688
|
log.info("Detecting your project setup\u2026");
|
|
@@ -2342,7 +2700,7 @@ function registerInitCommand(program2) {
|
|
|
2342
2700
|
log.warn("eslint-plugin-ai-guard not found");
|
|
2343
2701
|
}
|
|
2344
2702
|
if (env.configType !== "none") {
|
|
2345
|
-
log.success(`ESLint config found: ${
|
|
2703
|
+
log.success(`ESLint config found: ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath ?? env.configType))}`);
|
|
2346
2704
|
} else {
|
|
2347
2705
|
log.info("No ESLint config found \u2014 will generate one");
|
|
2348
2706
|
}
|
|
@@ -2351,9 +2709,9 @@ function registerInitCommand(program2) {
|
|
|
2351
2709
|
log.blank();
|
|
2352
2710
|
log.warn("Conflicting configs detected:");
|
|
2353
2711
|
for (const p of env.allConfigPaths) {
|
|
2354
|
-
log.print(` ${
|
|
2712
|
+
log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(import_path7.default.relative(cwd, p))}`);
|
|
2355
2713
|
}
|
|
2356
|
-
log.print(` ${
|
|
2714
|
+
log.print(` ${import_chalk6.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
|
|
2357
2715
|
}
|
|
2358
2716
|
log.blank();
|
|
2359
2717
|
const toInstall = [];
|
|
@@ -2364,22 +2722,22 @@ function registerInitCommand(program2) {
|
|
|
2364
2722
|
log.warn("The following packages are not installed:");
|
|
2365
2723
|
log.blank();
|
|
2366
2724
|
for (const pkg of toInstall) {
|
|
2367
|
-
log.print(` ${
|
|
2725
|
+
log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(pkg)}`);
|
|
2368
2726
|
}
|
|
2369
2727
|
log.blank();
|
|
2370
|
-
log.print(` ${
|
|
2728
|
+
log.print(` ${import_chalk6.default.bold("Run this first:")}`);
|
|
2371
2729
|
log.blank();
|
|
2372
|
-
log.print(` ${
|
|
2730
|
+
log.print(` ${import_chalk6.default.cyan(`npm install --save-dev ${toInstall.join(" ")}`)}`);
|
|
2373
2731
|
log.blank();
|
|
2374
|
-
log.print(` Then re-run ${
|
|
2732
|
+
log.print(` Then re-run ${import_chalk6.default.cyan("ai-guard init")} to complete setup.`);
|
|
2375
2733
|
log.blank();
|
|
2376
2734
|
process.exit(1);
|
|
2377
2735
|
return;
|
|
2378
2736
|
}
|
|
2379
2737
|
try {
|
|
2380
|
-
const { createRequire:
|
|
2738
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
2381
2739
|
const { pathToFileURL: pathToFileURL2 } = await import("url");
|
|
2382
|
-
const req =
|
|
2740
|
+
const req = createRequire3(import_path7.default.join(cwd, "_probe.js"));
|
|
2383
2741
|
const resolvedPath = req.resolve("eslint-plugin-ai-guard");
|
|
2384
2742
|
const aiGuard = await import(pathToFileURL2(resolvedPath).href);
|
|
2385
2743
|
const plugin = aiGuard.default || aiGuard;
|
|
@@ -2398,9 +2756,9 @@ function registerInitCommand(program2) {
|
|
|
2398
2756
|
if (eslintNeedsFlat && existingIsLegacy) {
|
|
2399
2757
|
log.section("Config Migration Required");
|
|
2400
2758
|
log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
|
|
2401
|
-
log.print(` ${
|
|
2759
|
+
log.print(` ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2402
2760
|
log.blank();
|
|
2403
|
-
log.print(` ${
|
|
2761
|
+
log.print(` ${import_chalk6.default.bold("ai-guard will create a new flat config.")}`);
|
|
2404
2762
|
log.blank();
|
|
2405
2763
|
}
|
|
2406
2764
|
log.section(`Configuring ESLint${isDryRun ? " (dry run)" : ""}`);
|
|
@@ -2410,34 +2768,34 @@ function registerInitCommand(program2) {
|
|
|
2410
2768
|
const raw = readConfig(env.configPath);
|
|
2411
2769
|
const { content: fixed, changed } = removeNukeIgnore(raw);
|
|
2412
2770
|
if (changed) {
|
|
2413
|
-
log.warn(`Removed "**/*" ignore pattern from ${
|
|
2771
|
+
log.warn(`Removed "**/*" ignore pattern from ${import_path7.default.relative(cwd, env.configPath)}`);
|
|
2414
2772
|
if (!isDryRun) {
|
|
2415
2773
|
backupConfig(env.configPath);
|
|
2416
2774
|
writeConfig(env.configPath, fixed);
|
|
2417
2775
|
log.success("Ignore pattern fixed");
|
|
2418
2776
|
} else {
|
|
2419
|
-
log.print(` ${
|
|
2777
|
+
log.print(` ${import_chalk6.default.gray("[dry-run] would remove nuke-ignore from existing config")}`);
|
|
2420
2778
|
}
|
|
2421
2779
|
}
|
|
2422
2780
|
}
|
|
2423
2781
|
if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
|
|
2424
2782
|
const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
|
|
2425
|
-
finalConfigPath =
|
|
2783
|
+
finalConfigPath = import_path7.default.join(cwd, targetName);
|
|
2426
2784
|
const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2427
2785
|
if (isDryRun) {
|
|
2428
|
-
log.print(` ${
|
|
2786
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would create: ${targetName}`)}`);
|
|
2429
2787
|
log.blank();
|
|
2430
|
-
log.print(
|
|
2788
|
+
log.print(import_chalk6.default.gray("\u2500\u2500\u2500 Preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2431
2789
|
log.blank();
|
|
2432
2790
|
for (const line of content2.split("\n")) {
|
|
2433
|
-
log.print(
|
|
2791
|
+
log.print(import_chalk6.default.gray(` ${line}`));
|
|
2434
2792
|
}
|
|
2435
2793
|
log.blank();
|
|
2436
2794
|
} else {
|
|
2437
2795
|
writeConfig(finalConfigPath, content2);
|
|
2438
|
-
log.success(`Created ${
|
|
2439
|
-
log.info(` Preset: ${
|
|
2440
|
-
log.info(` Format: ${
|
|
2796
|
+
log.success(`Created ${import_chalk6.default.white(targetName)}`);
|
|
2797
|
+
log.info(` Preset: ${import_chalk6.default.cyan(preset)}`);
|
|
2798
|
+
log.info(` Format: ${import_chalk6.default.cyan(useFlat ? "ESLint v9 flat config (eslint.config.mjs)" : "ESLint v8 legacy config (.eslintrc.js)")}`);
|
|
2441
2799
|
configWritten = true;
|
|
2442
2800
|
}
|
|
2443
2801
|
} else if (useFlat && existingIsFlat) {
|
|
@@ -2450,15 +2808,15 @@ function registerInitCommand(program2) {
|
|
|
2450
2808
|
finalConfigPath = env.configPath;
|
|
2451
2809
|
} else if (isDryRun) {
|
|
2452
2810
|
finalConfigPath = env.configPath;
|
|
2453
|
-
log.print(` ${
|
|
2811
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
|
|
2454
2812
|
} else {
|
|
2455
2813
|
const bak = backupConfig(env.configPath);
|
|
2456
|
-
log.info(`Backed up \u2192 ${
|
|
2814
|
+
log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
|
|
2457
2815
|
writeConfig(env.configPath, patched);
|
|
2458
2816
|
if (isInvalid) {
|
|
2459
|
-
log.success(`Detected invalid ai-guard config \u2014 repairing ${
|
|
2817
|
+
log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2460
2818
|
} else {
|
|
2461
|
-
log.success(`Patched ${
|
|
2819
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2462
2820
|
}
|
|
2463
2821
|
finalConfigPath = env.configPath;
|
|
2464
2822
|
configWritten = true;
|
|
@@ -2471,12 +2829,12 @@ function registerInitCommand(program2) {
|
|
|
2471
2829
|
finalConfigPath = env.configPath;
|
|
2472
2830
|
} else if (isDryRun) {
|
|
2473
2831
|
finalConfigPath = env.configPath;
|
|
2474
|
-
log.print(` ${
|
|
2832
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
|
|
2475
2833
|
} else {
|
|
2476
2834
|
const bak = backupConfig(env.configPath);
|
|
2477
|
-
log.info(`Backed up \u2192 ${
|
|
2835
|
+
log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
|
|
2478
2836
|
writeConfig(env.configPath, patched);
|
|
2479
|
-
log.success(`Patched ${
|
|
2837
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2480
2838
|
finalConfigPath = env.configPath;
|
|
2481
2839
|
configWritten = true;
|
|
2482
2840
|
}
|
|
@@ -2492,11 +2850,11 @@ function registerInitCommand(program2) {
|
|
|
2492
2850
|
cleanedUp = true;
|
|
2493
2851
|
} catch (err) {
|
|
2494
2852
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2495
|
-
log.warn(`Could not back up ${
|
|
2853
|
+
log.warn(`Could not back up ${import_path7.default.relative(cwd, p)}: ${reason}`);
|
|
2496
2854
|
}
|
|
2497
2855
|
} else {
|
|
2498
2856
|
cleanedUp = true;
|
|
2499
|
-
log.print(` ${
|
|
2857
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would rename ${import_path7.default.relative(cwd, p)} to ${import_path7.default.basename(p)}.bak`)}`);
|
|
2500
2858
|
}
|
|
2501
2859
|
}
|
|
2502
2860
|
}
|
|
@@ -2506,8 +2864,8 @@ function registerInitCommand(program2) {
|
|
|
2506
2864
|
}
|
|
2507
2865
|
if (isDryRun) {
|
|
2508
2866
|
log.blank();
|
|
2509
|
-
log.print(` ${
|
|
2510
|
-
log.print(` Re-run without ${
|
|
2867
|
+
log.print(` ${import_chalk6.default.yellow("Dry run complete \u2014 no files written.")}`);
|
|
2868
|
+
log.print(` Re-run without ${import_chalk6.default.cyan("--dry-run")} to apply.`);
|
|
2511
2869
|
log.blank();
|
|
2512
2870
|
process.exit(0);
|
|
2513
2871
|
return;
|
|
@@ -2515,10 +2873,10 @@ function registerInitCommand(program2) {
|
|
|
2515
2873
|
log.blank();
|
|
2516
2874
|
log.section("Validation");
|
|
2517
2875
|
if (!import_fs7.default.existsSync(finalConfigPath)) {
|
|
2518
|
-
log.error(`Config file not found after write: ${
|
|
2876
|
+
log.error(`Config file not found after write: ${import_path7.default.relative(cwd, finalConfigPath)}`);
|
|
2519
2877
|
log.blank();
|
|
2520
|
-
log.print(` ${
|
|
2521
|
-
log.print(` ${
|
|
2878
|
+
log.print(` ${import_chalk6.default.bold("Problem:")} File write may have failed.`);
|
|
2879
|
+
log.print(` ${import_chalk6.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
|
|
2522
2880
|
log.blank();
|
|
2523
2881
|
process.exit(1);
|
|
2524
2882
|
return;
|
|
@@ -2528,36 +2886,36 @@ function registerInitCommand(program2) {
|
|
|
2528
2886
|
if (validationErrors.length > 0) {
|
|
2529
2887
|
log.warn("Config validation found issues:");
|
|
2530
2888
|
for (const e of validationErrors) {
|
|
2531
|
-
log.print(` ${
|
|
2889
|
+
log.print(` ${import_chalk6.default.red("\u2716")} ${e}`);
|
|
2532
2890
|
}
|
|
2533
2891
|
} else {
|
|
2534
|
-
log.success(`Config structure valid: ${
|
|
2892
|
+
log.success(`Config structure valid: ${import_chalk6.default.white(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
2535
2893
|
}
|
|
2536
2894
|
log.info("Verifying ESLint can load the config\u2026");
|
|
2537
2895
|
const loadError = await verifyConfigLoads(finalConfigPath, cwd);
|
|
2538
2896
|
if (loadError !== null) {
|
|
2539
2897
|
log.blank();
|
|
2540
2898
|
if (loadError.kind === "module-not-found") {
|
|
2541
|
-
log.warn(`Could not verify: module not found: ${
|
|
2899
|
+
log.warn(`Could not verify: module not found: ${import_chalk6.default.yellow(loadError.pkg)}`);
|
|
2542
2900
|
log.blank();
|
|
2543
|
-
log.print(` ${
|
|
2544
|
-
log.print(` ${
|
|
2545
|
-
log.print(` Then run ${
|
|
2901
|
+
log.print(` ${import_chalk6.default.bold("Install the missing package:")}`);
|
|
2902
|
+
log.print(` ${import_chalk6.default.cyan(`npm install --save-dev ${loadError.pkg}`)}`);
|
|
2903
|
+
log.print(` Then run ${import_chalk6.default.cyan("ai-guard doctor")} to confirm.`);
|
|
2546
2904
|
log.blank();
|
|
2547
2905
|
} else {
|
|
2548
2906
|
log.error("ESLint failed to load the generated config.");
|
|
2549
2907
|
log.blank();
|
|
2550
|
-
log.print(` ${
|
|
2551
|
-
log.print(` ${
|
|
2908
|
+
log.print(` ${import_chalk6.default.bold("Problem:")}`);
|
|
2909
|
+
log.print(` ${import_chalk6.default.red(loadError.message)}`);
|
|
2552
2910
|
if (loadError.kind === "unknown") {
|
|
2553
2911
|
log.blank();
|
|
2554
|
-
log.print(` ${
|
|
2912
|
+
log.print(` ${import_chalk6.default.yellow("Generated config may be using incorrect plugin structure.")}`);
|
|
2555
2913
|
}
|
|
2556
2914
|
log.blank();
|
|
2557
|
-
log.print(` ${
|
|
2558
|
-
log.print(` 1. Check the syntax in ${
|
|
2559
|
-
log.print(` 2. Make sure ${
|
|
2560
|
-
log.print(` 3. Run ${
|
|
2915
|
+
log.print(` ${import_chalk6.default.bold("Fix:")}`);
|
|
2916
|
+
log.print(` 1. Check the syntax in ${import_chalk6.default.cyan(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
2917
|
+
log.print(` 2. Make sure ${import_chalk6.default.cyan("eslint-plugin-ai-guard")} is installed`);
|
|
2918
|
+
log.print(` 3. Run ${import_chalk6.default.cyan("ai-guard doctor")} for full diagnostics`);
|
|
2561
2919
|
log.blank();
|
|
2562
2920
|
process.exit(1);
|
|
2563
2921
|
return;
|
|
@@ -2575,30 +2933,30 @@ function registerInitCommand(program2) {
|
|
|
2575
2933
|
}
|
|
2576
2934
|
const lingeringConflicts = env.hasConflictingConfigs && !eslintNeedsFlat && !cleanedUpInfo;
|
|
2577
2935
|
if (loadError === null && !lingeringConflicts) {
|
|
2578
|
-
log.print(` ${
|
|
2936
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold.green("Configuration validated successfully")}`);
|
|
2579
2937
|
}
|
|
2580
|
-
log.print(` ${
|
|
2581
|
-
log.print(` ${
|
|
2582
|
-
log.print(` ${
|
|
2938
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("ESLint config:")} ${import_chalk6.default.cyan(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
2939
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("Mode:")} ${import_chalk6.default.cyan(useFlat ? `Flat config (ESLint v${env.eslintMajor ?? 9})` : `Legacy config (ESLint v${env.eslintMajor ?? 8})`)}`);
|
|
2940
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("Preset:")} ${import_chalk6.default.cyan(preset)}`);
|
|
2583
2941
|
log.blank();
|
|
2584
|
-
log.info(`Run ${
|
|
2585
|
-
log.info(`Run ${
|
|
2586
|
-
log.info(`Run ${
|
|
2587
|
-
log.info(`Run ${
|
|
2942
|
+
log.info(`Run ${import_chalk6.default.cyan("npx eslint .")} \u2192 lint with your editor integration`);
|
|
2943
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard run")} \u2192 zero-config scan`);
|
|
2944
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard doctor")} \u2192 verify the full setup`);
|
|
2945
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard baseline")} \u2192 save baseline, track only new issues`);
|
|
2588
2946
|
log.blank();
|
|
2589
2947
|
});
|
|
2590
2948
|
}
|
|
2591
2949
|
|
|
2592
2950
|
// cli/commands/doctor.ts
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
2951
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
2952
|
+
var import_path8 = __toESM(require("path"));
|
|
2595
2953
|
init_config_manager();
|
|
2596
|
-
var
|
|
2954
|
+
var import_meta2 = {};
|
|
2597
2955
|
async function probeConfigLoad(configPath, cwd) {
|
|
2598
2956
|
try {
|
|
2599
2957
|
const { ESLint } = await import("eslint");
|
|
2600
2958
|
const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
|
|
2601
|
-
await eslint.lintText("", { filePath:
|
|
2959
|
+
await eslint.lintText("", { filePath: import_path8.default.join(cwd, "_probe_.js") });
|
|
2602
2960
|
return { ok: true, error: null };
|
|
2603
2961
|
} catch (err) {
|
|
2604
2962
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2633,7 +2991,7 @@ function registerDoctorCommand(program2) {
|
|
|
2633
2991
|
checks.push({
|
|
2634
2992
|
label: "ESLint config present",
|
|
2635
2993
|
pass: hasConfig,
|
|
2636
|
-
detail: hasConfig ? `${
|
|
2994
|
+
detail: hasConfig ? `${import_path8.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
|
|
2637
2995
|
fix: hasConfig ? void 0 : "ai-guard init",
|
|
2638
2996
|
note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
|
|
2639
2997
|
});
|
|
@@ -2665,7 +3023,7 @@ function registerDoctorCommand(program2) {
|
|
|
2665
3023
|
checks.push({
|
|
2666
3024
|
label: "Plugin wired in config",
|
|
2667
3025
|
pass: pluginWired && !isInvalidConfig,
|
|
2668
|
-
detail: readError ? `Could not read config: ${readError}` : isInvalidConfig ? "Invalid ai-guard config detected (wrong export usage)" : pluginWired ? "eslint-plugin-ai-guard referenced in config" : `Config exists but ai-guard plugin not found in ${
|
|
3026
|
+
detail: readError ? `Could not read config: ${readError}` : isInvalidConfig ? "Invalid ai-guard config detected (wrong export usage)" : pluginWired ? "eslint-plugin-ai-guard referenced in config" : `Config exists but ai-guard plugin not found in ${import_path8.default.relative(cwd, env.configPath)}`,
|
|
2669
3027
|
fix: isInvalidConfig || !pluginWired && !readError ? "ai-guard init (will patch/repair your existing config)" : void 0
|
|
2670
3028
|
});
|
|
2671
3029
|
}
|
|
@@ -2673,7 +3031,7 @@ function registerDoctorCommand(program2) {
|
|
|
2673
3031
|
label: "No conflicting config files",
|
|
2674
3032
|
pass: !env.hasConflictingConfigs,
|
|
2675
3033
|
detail: env.hasConflictingConfigs ? `Conflicting config detected:
|
|
2676
|
-
${env.allConfigPaths.map((p) => ` ${
|
|
3034
|
+
${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
|
|
2677
3035
|
fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
|
|
2678
3036
|
note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
|
|
2679
3037
|
});
|
|
@@ -2700,7 +3058,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2700
3058
|
label: "ESLint config loads without errors",
|
|
2701
3059
|
pass: probe.ok,
|
|
2702
3060
|
detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
|
|
2703
|
-
fix: probe.ok ? void 0 : `Check syntax in ${
|
|
3061
|
+
fix: probe.ok ? void 0 : `Check syntax in ${import_path8.default.relative(cwd, env.configPath)}`,
|
|
2704
3062
|
note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
|
|
2705
3063
|
});
|
|
2706
3064
|
}
|
|
@@ -2708,8 +3066,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2708
3066
|
let utilsOk = false;
|
|
2709
3067
|
let utilsVersion = null;
|
|
2710
3068
|
try {
|
|
2711
|
-
const { createRequire:
|
|
2712
|
-
const req =
|
|
3069
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
3070
|
+
const req = createRequire3(import_path8.default.join(cwd, "package.json"));
|
|
2713
3071
|
const pkgPath = req.resolve("@typescript-eslint/utils/package.json");
|
|
2714
3072
|
const pkg = JSON.parse(
|
|
2715
3073
|
(await import("fs")).readFileSync(pkgPath, "utf-8")
|
|
@@ -2720,10 +3078,10 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2720
3078
|
}
|
|
2721
3079
|
if (!utilsOk) {
|
|
2722
3080
|
try {
|
|
2723
|
-
const selfDir =
|
|
2724
|
-
new URL(
|
|
3081
|
+
const selfDir = import_path8.default.dirname(
|
|
3082
|
+
new URL(import_meta2.url).pathname.replace(/^\/([A-Za-z]:)/, "$1")
|
|
2725
3083
|
);
|
|
2726
|
-
const pkgPath =
|
|
3084
|
+
const pkgPath = import_path8.default.join(selfDir, "../../node_modules/@typescript-eslint/utils/package.json");
|
|
2727
3085
|
if ((await import("fs")).existsSync(pkgPath)) {
|
|
2728
3086
|
utilsOk = true;
|
|
2729
3087
|
}
|
|
@@ -2742,8 +3100,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2742
3100
|
let parserOk = false;
|
|
2743
3101
|
let parserVersion = null;
|
|
2744
3102
|
try {
|
|
2745
|
-
const { createRequire:
|
|
2746
|
-
const req =
|
|
3103
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
3104
|
+
const req = createRequire3(import_path8.default.join(cwd, "package.json"));
|
|
2747
3105
|
const pkgPath = req.resolve("@typescript-eslint/parser/package.json");
|
|
2748
3106
|
const pkg = JSON.parse(
|
|
2749
3107
|
(await import("fs")).readFileSync(pkgPath, "utf-8")
|
|
@@ -2774,17 +3132,17 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2774
3132
|
log.section("Diagnostics");
|
|
2775
3133
|
let allPassed = true;
|
|
2776
3134
|
for (const check of checks) {
|
|
2777
|
-
const icon = check.pass ?
|
|
2778
|
-
const label = check.pass ?
|
|
3135
|
+
const icon = check.pass ? import_chalk7.default.green("\u2714") : import_chalk7.default.red("\u2716");
|
|
3136
|
+
const label = check.pass ? import_chalk7.default.white(check.label) : import_chalk7.default.red.bold(check.label);
|
|
2779
3137
|
log.print(` ${icon} ${label}`);
|
|
2780
|
-
log.print(` ${
|
|
3138
|
+
log.print(` ${import_chalk7.default.gray(check.detail)}`);
|
|
2781
3139
|
if (!check.pass) {
|
|
2782
3140
|
allPassed = false;
|
|
2783
3141
|
if (check.fix) {
|
|
2784
|
-
log.print(` ${
|
|
3142
|
+
log.print(` ${import_chalk7.default.cyan("\u2192 Fix:")} ${import_chalk7.default.yellow(check.fix)}`);
|
|
2785
3143
|
}
|
|
2786
3144
|
if (check.note) {
|
|
2787
|
-
log.print(` ${
|
|
3145
|
+
log.print(` ${import_chalk7.default.gray(check.note)}`);
|
|
2788
3146
|
}
|
|
2789
3147
|
}
|
|
2790
3148
|
log.blank();
|
|
@@ -2792,10 +3150,10 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2792
3150
|
log.divider();
|
|
2793
3151
|
log.blank();
|
|
2794
3152
|
if (allPassed) {
|
|
2795
|
-
log.print(` ${
|
|
3153
|
+
log.print(` ${import_chalk7.default.green("\u2714")} ${import_chalk7.default.bold.green("All checks passed! Your setup is correct.")}`);
|
|
2796
3154
|
log.blank();
|
|
2797
|
-
log.info(`Run ${
|
|
2798
|
-
log.info(`Run ${
|
|
3155
|
+
log.info(`Run ${import_chalk7.default.cyan("ai-guard run")} to start scanning.`);
|
|
3156
|
+
log.info(`Run ${import_chalk7.default.cyan("npx eslint .")} to lint with ESLint directly.`);
|
|
2799
3157
|
} else {
|
|
2800
3158
|
log.error("Some checks failed. Follow the fixes above.");
|
|
2801
3159
|
log.blank();
|
|
@@ -2810,15 +3168,15 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2810
3168
|
);
|
|
2811
3169
|
if (pluginMissing) {
|
|
2812
3170
|
log.print(
|
|
2813
|
-
` Install missing packages first, then re-run ${
|
|
3171
|
+
` Install missing packages first, then re-run ${import_chalk7.default.cyan("ai-guard doctor")}.`
|
|
2814
3172
|
);
|
|
2815
3173
|
} else if (configMissing || configFormatMismatch) {
|
|
2816
3174
|
log.print(
|
|
2817
|
-
` Run ${
|
|
3175
|
+
` Run ${import_chalk7.default.cyan("ai-guard init")} to generate the correct config for your ESLint version.`
|
|
2818
3176
|
);
|
|
2819
3177
|
} else {
|
|
2820
3178
|
log.print(
|
|
2821
|
-
` Run ${
|
|
3179
|
+
` Run ${import_chalk7.default.cyan("ai-guard init --dry-run")} to preview what would change.`
|
|
2822
3180
|
);
|
|
2823
3181
|
}
|
|
2824
3182
|
}
|
|
@@ -2829,9 +3187,9 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2829
3187
|
|
|
2830
3188
|
// cli/commands/preset.ts
|
|
2831
3189
|
var import_prompts = require("@inquirer/prompts");
|
|
2832
|
-
var
|
|
3190
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
2833
3191
|
init_config_manager();
|
|
2834
|
-
var
|
|
3192
|
+
var import_path9 = __toESM(require("path"));
|
|
2835
3193
|
function registerPresetCommand(program2) {
|
|
2836
3194
|
program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
|
|
2837
3195
|
const cwd = process.cwd();
|
|
@@ -2841,32 +3199,32 @@ function registerPresetCommand(program2) {
|
|
|
2841
3199
|
message: "Choose a preset:",
|
|
2842
3200
|
choices: [
|
|
2843
3201
|
{
|
|
2844
|
-
name: `${
|
|
3202
|
+
name: `${import_chalk8.default.green("recommended")} \u2014 balanced defaults, low noise. Best starting point.`,
|
|
2845
3203
|
value: "recommended"
|
|
2846
3204
|
},
|
|
2847
3205
|
{
|
|
2848
|
-
name: `${
|
|
3206
|
+
name: `${import_chalk8.default.yellow("strict")} \u2014 all rules at error. For mature codebases.`,
|
|
2849
3207
|
value: "strict"
|
|
2850
3208
|
},
|
|
2851
3209
|
{
|
|
2852
|
-
name: `${
|
|
3210
|
+
name: `${import_chalk8.default.red("security")} \u2014 security rules only. For AppSec teams.`,
|
|
2853
3211
|
value: "security"
|
|
2854
3212
|
}
|
|
2855
3213
|
]
|
|
2856
3214
|
});
|
|
2857
3215
|
log.blank();
|
|
2858
|
-
log.info(`Applying preset: ${
|
|
3216
|
+
log.info(`Applying preset: ${import_chalk8.default.cyan(preset)}`);
|
|
2859
3217
|
const env = detect(cwd);
|
|
2860
3218
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
|
|
2861
3219
|
if (env.configType === "none") {
|
|
2862
|
-
const configPath = useFlat ?
|
|
3220
|
+
const configPath = useFlat ? import_path9.default.join(cwd, "eslint.config.mjs") : import_path9.default.join(cwd, ".eslintrc.js");
|
|
2863
3221
|
const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2864
3222
|
writeConfig(configPath, content);
|
|
2865
|
-
log.success(`Created ${
|
|
3223
|
+
log.success(`Created ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} with ${import_chalk8.default.cyan(preset)} preset`);
|
|
2866
3224
|
} else {
|
|
2867
3225
|
const configPath = env.configPath;
|
|
2868
3226
|
const backupPath = backupConfig(configPath);
|
|
2869
|
-
log.info(`Backed up \u2192 ${
|
|
3227
|
+
log.info(`Backed up \u2192 ${import_chalk8.default.gray(import_path9.default.relative(cwd, backupPath))}`);
|
|
2870
3228
|
const existing = readConfig(configPath);
|
|
2871
3229
|
const withPlugin = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
|
|
2872
3230
|
const patched = isFlat(env.configType) ? switchFlatPreset(withPlugin, preset) : switchLegacyPreset(withPlugin, preset);
|
|
@@ -2874,47 +3232,47 @@ function registerPresetCommand(program2) {
|
|
|
2874
3232
|
log.warn("No preset changes needed. Config already matches selected preset.");
|
|
2875
3233
|
} else {
|
|
2876
3234
|
writeConfig(configPath, patched);
|
|
2877
|
-
log.success(`Updated ${
|
|
3235
|
+
log.success(`Updated ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} to ${import_chalk8.default.cyan(preset)} preset`);
|
|
2878
3236
|
}
|
|
2879
3237
|
}
|
|
2880
3238
|
log.blank();
|
|
2881
3239
|
log.section("Preset Details");
|
|
2882
3240
|
const presetDetails = {
|
|
2883
3241
|
recommended: [
|
|
2884
|
-
{ rule: "no-empty-catch", level:
|
|
2885
|
-
{ rule: "no-floating-promise", level:
|
|
2886
|
-
{ rule: "no-hardcoded-secret", level:
|
|
2887
|
-
{ rule: "no-eval-dynamic", level:
|
|
2888
|
-
{ rule: "no-broad-exception", level:
|
|
2889
|
-
{ rule: "no-async-array-callback", level:
|
|
2890
|
-
{ rule: "no-await-in-loop", level:
|
|
2891
|
-
{ rule: "+ 5 more\u2026", level:
|
|
3242
|
+
{ rule: "no-empty-catch", level: import_chalk8.default.red("error") },
|
|
3243
|
+
{ rule: "no-floating-promise", level: import_chalk8.default.red("error") },
|
|
3244
|
+
{ rule: "no-hardcoded-secret", level: import_chalk8.default.red("error") },
|
|
3245
|
+
{ rule: "no-eval-dynamic", level: import_chalk8.default.red("error") },
|
|
3246
|
+
{ rule: "no-broad-exception", level: import_chalk8.default.yellow("warn") },
|
|
3247
|
+
{ rule: "no-async-array-callback", level: import_chalk8.default.yellow("warn") },
|
|
3248
|
+
{ rule: "no-await-in-loop", level: import_chalk8.default.yellow("warn") },
|
|
3249
|
+
{ rule: "+ 5 more\u2026", level: import_chalk8.default.gray("warn") }
|
|
2892
3250
|
],
|
|
2893
3251
|
strict: [
|
|
2894
|
-
{ rule: "All 17 rules", level:
|
|
3252
|
+
{ rule: "All 17 rules", level: import_chalk8.default.red("error") }
|
|
2895
3253
|
],
|
|
2896
3254
|
security: [
|
|
2897
|
-
{ rule: "no-hardcoded-secret", level:
|
|
2898
|
-
{ rule: "no-eval-dynamic", level:
|
|
2899
|
-
{ rule: "no-sql-string-concat", level:
|
|
2900
|
-
{ rule: "no-unsafe-deserialize", level:
|
|
2901
|
-
{ rule: "require-auth-middleware", level:
|
|
2902
|
-
{ rule: "require-authz-check", level:
|
|
3255
|
+
{ rule: "no-hardcoded-secret", level: import_chalk8.default.red("error") },
|
|
3256
|
+
{ rule: "no-eval-dynamic", level: import_chalk8.default.red("error") },
|
|
3257
|
+
{ rule: "no-sql-string-concat", level: import_chalk8.default.red("error") },
|
|
3258
|
+
{ rule: "no-unsafe-deserialize", level: import_chalk8.default.yellow("warn") },
|
|
3259
|
+
{ rule: "require-auth-middleware", level: import_chalk8.default.yellow("warn") },
|
|
3260
|
+
{ rule: "require-authz-check", level: import_chalk8.default.yellow("warn") }
|
|
2903
3261
|
]
|
|
2904
3262
|
};
|
|
2905
3263
|
for (const { rule, level } of presetDetails[preset]) {
|
|
2906
|
-
log.print(` ${
|
|
3264
|
+
log.print(` ${import_chalk8.default.gray("\u2022")} ${import_chalk8.default.white(rule).padEnd(32)}${level}`);
|
|
2907
3265
|
}
|
|
2908
3266
|
log.blank();
|
|
2909
|
-
log.info(`Run ${
|
|
3267
|
+
log.info(`Run ${import_chalk8.default.cyan("ai-guard run")} to see results with the new preset.`);
|
|
2910
3268
|
log.blank();
|
|
2911
3269
|
});
|
|
2912
3270
|
}
|
|
2913
3271
|
|
|
2914
3272
|
// cli/commands/ignore.ts
|
|
2915
|
-
var
|
|
3273
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
2916
3274
|
init_config_manager();
|
|
2917
|
-
var
|
|
3275
|
+
var import_path10 = __toESM(require("path"));
|
|
2918
3276
|
var DEFAULT_IGNORES = [
|
|
2919
3277
|
".next/",
|
|
2920
3278
|
"dist/",
|
|
@@ -2930,40 +3288,40 @@ function registerIgnoreCommand(program2) {
|
|
|
2930
3288
|
log.blank();
|
|
2931
3289
|
log.section("Default Ignores");
|
|
2932
3290
|
for (const p of DEFAULT_IGNORES) {
|
|
2933
|
-
log.print(` ${
|
|
3291
|
+
log.print(` ${import_chalk9.default.gray("\u2022")} ${import_chalk9.default.yellow(p)}`);
|
|
2934
3292
|
}
|
|
2935
3293
|
log.blank();
|
|
2936
3294
|
const env = detect(cwd);
|
|
2937
3295
|
if (env.configType === "none") {
|
|
2938
3296
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
|
|
2939
|
-
const configPath = useFlat ?
|
|
3297
|
+
const configPath = useFlat ? import_path10.default.join(cwd, "eslint.config.mjs") : import_path10.default.join(cwd, ".eslintrc.js");
|
|
2940
3298
|
const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
|
|
2941
3299
|
writeConfig(configPath, content);
|
|
2942
|
-
log.success(`No config found \u2014 generated ${
|
|
3300
|
+
log.success(`No config found \u2014 generated ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignores included`);
|
|
2943
3301
|
} else {
|
|
2944
3302
|
const configPath = env.configPath;
|
|
2945
3303
|
const backupPath = backupConfig(configPath);
|
|
2946
|
-
log.info(`Backed up \u2192 ${
|
|
3304
|
+
log.info(`Backed up \u2192 ${import_chalk9.default.gray(import_path10.default.relative(cwd, backupPath))}`);
|
|
2947
3305
|
const existing = readConfig(configPath);
|
|
2948
3306
|
const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
|
|
2949
3307
|
if (patched === existing) {
|
|
2950
3308
|
log.warn("Ignore patterns already present in config \u2014 no changes made.");
|
|
2951
3309
|
} else {
|
|
2952
3310
|
writeConfig(configPath, patched);
|
|
2953
|
-
log.success(`Patched ${
|
|
3311
|
+
log.success(`Patched ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignore patterns`);
|
|
2954
3312
|
}
|
|
2955
3313
|
}
|
|
2956
3314
|
log.blank();
|
|
2957
3315
|
log.info("Noisy directories will now be skipped in all future ESLint runs.");
|
|
2958
|
-
log.info(`Run ${
|
|
3316
|
+
log.info(`Run ${import_chalk9.default.cyan("ai-guard run")} to verify cleaner results.`);
|
|
2959
3317
|
log.blank();
|
|
2960
3318
|
});
|
|
2961
3319
|
}
|
|
2962
3320
|
|
|
2963
3321
|
// cli/commands/baseline.ts
|
|
2964
3322
|
var import_fs8 = __toESM(require("fs"));
|
|
2965
|
-
var
|
|
2966
|
-
var
|
|
3323
|
+
var import_path11 = __toESM(require("path"));
|
|
3324
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
2967
3325
|
var import_ora3 = __toESM(require("ora"));
|
|
2968
3326
|
var BASELINE_FILE = ".ai-guard-baseline.json";
|
|
2969
3327
|
function saveBaseline(result, preset, mode, cwd) {
|
|
@@ -2983,12 +3341,12 @@ function saveBaseline(result, preset, mode, cwd) {
|
|
|
2983
3341
|
totalIssues: result.totalIssues,
|
|
2984
3342
|
entries
|
|
2985
3343
|
};
|
|
2986
|
-
const baselinePath =
|
|
3344
|
+
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
2987
3345
|
import_fs8.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
2988
3346
|
return baselinePath;
|
|
2989
3347
|
}
|
|
2990
3348
|
function loadBaseline(cwd) {
|
|
2991
|
-
const baselinePath =
|
|
3349
|
+
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
2992
3350
|
if (!import_fs8.default.existsSync(baselinePath)) return null;
|
|
2993
3351
|
try {
|
|
2994
3352
|
const raw = import_fs8.default.readFileSync(baselinePath, "utf-8");
|
|
@@ -3075,28 +3433,28 @@ function registerBaselineCommand(program2) {
|
|
|
3075
3433
|
}
|
|
3076
3434
|
const baselinePath = saveBaseline(result, preset, mode, cwd);
|
|
3077
3435
|
log.success(
|
|
3078
|
-
`Baseline saved \u2192 ${
|
|
3436
|
+
`Baseline saved \u2192 ${import_chalk10.default.white(import_path11.default.relative(cwd, baselinePath))}`
|
|
3079
3437
|
);
|
|
3080
3438
|
log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
|
|
3081
|
-
log.info(` Preset: ${
|
|
3082
|
-
log.info(` Mode: ${
|
|
3439
|
+
log.info(` Preset: ${import_chalk10.default.cyan(preset)}`);
|
|
3440
|
+
log.info(` Mode: ${import_chalk10.default.cyan(mode)}`);
|
|
3083
3441
|
log.blank();
|
|
3084
|
-
log.info("Future runs of " +
|
|
3085
|
-
log.info("Add " +
|
|
3442
|
+
log.info("Future runs of " + import_chalk10.default.cyan("ai-guard baseline --check") + " will show only NEW issues.");
|
|
3443
|
+
log.info("Add " + import_chalk10.default.white(BASELINE_FILE) + " to your git repository to share baseline with your team.");
|
|
3086
3444
|
log.blank();
|
|
3087
3445
|
return;
|
|
3088
3446
|
}
|
|
3089
3447
|
if (doCheck) {
|
|
3090
3448
|
if (!existingBaseline) {
|
|
3091
|
-
log.error(`No baseline found at ${
|
|
3092
|
-
log.info(`Run ${
|
|
3449
|
+
log.error(`No baseline found at ${import_chalk10.default.white(BASELINE_FILE)}`);
|
|
3450
|
+
log.info(`Run ${import_chalk10.default.cyan("ai-guard baseline --save")} first to create one.`);
|
|
3093
3451
|
process.exit(1);
|
|
3094
3452
|
}
|
|
3095
3453
|
const baselineDate = new Date(existingBaseline.createdAt).toLocaleString();
|
|
3096
|
-
log.info(`Baseline from: ${
|
|
3097
|
-
log.info(`Baseline preset: ${
|
|
3098
|
-
log.info(`Baseline mode: ${
|
|
3099
|
-
log.info(`Baseline issues: ${
|
|
3454
|
+
log.info(`Baseline from: ${import_chalk10.default.gray(baselineDate)}`);
|
|
3455
|
+
log.info(`Baseline preset: ${import_chalk10.default.cyan(existingBaseline.preset)}`);
|
|
3456
|
+
log.info(`Baseline mode: ${import_chalk10.default.cyan(existingBaseline.mode ?? "strict")}`);
|
|
3457
|
+
log.info(`Baseline issues: ${import_chalk10.default.gray(existingBaseline.totalIssues)}`);
|
|
3100
3458
|
log.blank();
|
|
3101
3459
|
const spinner = (0, import_ora3.default)({ text: "Scanning for new issues\u2026", color: "cyan" }).start();
|
|
3102
3460
|
let result;
|
|
@@ -3121,7 +3479,7 @@ function registerBaselineCommand(program2) {
|
|
|
3121
3479
|
return;
|
|
3122
3480
|
}
|
|
3123
3481
|
log.print(
|
|
3124
|
-
|
|
3482
|
+
import_chalk10.default.bold.red(` \u2716 ${newResult.totalIssues} new issue${newResult.totalIssues !== 1 ? "s" : ""} since baseline`)
|
|
3125
3483
|
);
|
|
3126
3484
|
log.blank();
|
|
3127
3485
|
if (newResult.ruleBreakdown.size > 0) {
|
|
@@ -3137,7 +3495,7 @@ function registerBaselineCommand(program2) {
|
|
|
3137
3495
|
log.blank();
|
|
3138
3496
|
for (const file of newResult.files) {
|
|
3139
3497
|
log.print(
|
|
3140
|
-
` ${
|
|
3498
|
+
` ${import_chalk10.default.bold.white(file.filePath)} ` + import_chalk10.default.gray(`(${file.issues.length} new)`)
|
|
3141
3499
|
);
|
|
3142
3500
|
for (const issue of file.issues) {
|
|
3143
3501
|
log.issue(issue.message, issue.severity, issue.line, issue.column);
|
|
@@ -3150,7 +3508,7 @@ function registerBaselineCommand(program2) {
|
|
|
3150
3508
|
log.error(
|
|
3151
3509
|
`${newResult.totalIssues} new issue${newResult.totalIssues !== 1 ? "s" : ""} introduced since baseline.`
|
|
3152
3510
|
);
|
|
3153
|
-
log.info(`Run ${
|
|
3511
|
+
log.info(`Run ${import_chalk10.default.cyan("ai-guard baseline --save")} to update the baseline after fixing them.`);
|
|
3154
3512
|
log.blank();
|
|
3155
3513
|
process.exit(1);
|
|
3156
3514
|
}
|
|
@@ -3159,8 +3517,8 @@ function registerBaselineCommand(program2) {
|
|
|
3159
3517
|
|
|
3160
3518
|
// cli/commands/init-context.ts
|
|
3161
3519
|
var import_fs9 = __toESM(require("fs"));
|
|
3162
|
-
var
|
|
3163
|
-
var
|
|
3520
|
+
var import_path12 = __toESM(require("path"));
|
|
3521
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
3164
3522
|
var import_prompts2 = require("@inquirer/prompts");
|
|
3165
3523
|
var RULE_CATEGORIES = {
|
|
3166
3524
|
"error-handling": [
|
|
@@ -3523,13 +3881,13 @@ var AGENTS = [
|
|
|
3523
3881
|
{
|
|
3524
3882
|
id: "copilot",
|
|
3525
3883
|
label: "GitHub Copilot",
|
|
3526
|
-
filePath:
|
|
3884
|
+
filePath: import_path12.default.join(".github", "copilot-instructions.md"),
|
|
3527
3885
|
generate: generateCopilotFile
|
|
3528
3886
|
}
|
|
3529
3887
|
];
|
|
3530
3888
|
function getPackageVersion2(cwd) {
|
|
3531
3889
|
try {
|
|
3532
|
-
const pkgPath =
|
|
3890
|
+
const pkgPath = import_path12.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
3533
3891
|
if (import_fs9.default.existsSync(pkgPath)) {
|
|
3534
3892
|
const pkg = JSON.parse(import_fs9.default.readFileSync(pkgPath, "utf-8"));
|
|
3535
3893
|
if (pkg.version) return pkg.version;
|
|
@@ -3537,7 +3895,7 @@ function getPackageVersion2(cwd) {
|
|
|
3537
3895
|
} catch {
|
|
3538
3896
|
}
|
|
3539
3897
|
try {
|
|
3540
|
-
const pkgPath =
|
|
3898
|
+
const pkgPath = import_path12.default.join(cwd, "package.json");
|
|
3541
3899
|
if (import_fs9.default.existsSync(pkgPath)) {
|
|
3542
3900
|
const pkg = JSON.parse(import_fs9.default.readFileSync(pkgPath, "utf-8"));
|
|
3543
3901
|
if (pkg.version) return pkg.version;
|
|
@@ -3568,7 +3926,7 @@ function registerInitContextCommand(program2) {
|
|
|
3568
3926
|
const isForce = opts.force === true;
|
|
3569
3927
|
log.banner("AI GUARD INIT-CONTEXT");
|
|
3570
3928
|
if (isDryRun) {
|
|
3571
|
-
log.print(` ${
|
|
3929
|
+
log.print(` ${import_chalk11.default.yellow("\u2691 DRY RUN \u2014 no files will be written")}`);
|
|
3572
3930
|
log.blank();
|
|
3573
3931
|
}
|
|
3574
3932
|
let selectedAgents;
|
|
@@ -3601,12 +3959,12 @@ function registerInitContextCommand(program2) {
|
|
|
3601
3959
|
selectedAgents = AGENTS.filter((a) => selected.includes(a.id));
|
|
3602
3960
|
}
|
|
3603
3961
|
}
|
|
3604
|
-
log.info(`Categories: ${
|
|
3605
|
-
log.info(`Version: ${
|
|
3962
|
+
log.info(`Categories: ${import_chalk11.default.cyan(categories.join(", "))}`);
|
|
3963
|
+
log.info(`Version: ${import_chalk11.default.cyan(version)}`);
|
|
3606
3964
|
log.blank();
|
|
3607
3965
|
const results = [];
|
|
3608
3966
|
for (const agent of selectedAgents) {
|
|
3609
|
-
const fullPath =
|
|
3967
|
+
const fullPath = import_path12.default.join(cwd, agent.filePath);
|
|
3610
3968
|
const exists = import_fs9.default.existsSync(fullPath);
|
|
3611
3969
|
if (exists && !isForce && !isDryRun) {
|
|
3612
3970
|
const overwrite = await (0, import_prompts2.confirm)({
|
|
@@ -3621,17 +3979,17 @@ function registerInitContextCommand(program2) {
|
|
|
3621
3979
|
const content = agent.generate(version, categories);
|
|
3622
3980
|
if (isDryRun) {
|
|
3623
3981
|
log.section(`Preview: ${agent.filePath}`);
|
|
3624
|
-
log.print(
|
|
3982
|
+
log.print(import_chalk11.default.gray("\u2500".repeat(60)));
|
|
3625
3983
|
for (const line of content.split("\n").slice(0, 20)) {
|
|
3626
|
-
log.print(
|
|
3984
|
+
log.print(import_chalk11.default.gray(` ${line}`));
|
|
3627
3985
|
}
|
|
3628
|
-
log.print(
|
|
3629
|
-
log.print(
|
|
3986
|
+
log.print(import_chalk11.default.gray(" ... (truncated)"));
|
|
3987
|
+
log.print(import_chalk11.default.gray("\u2500".repeat(60)));
|
|
3630
3988
|
log.blank();
|
|
3631
3989
|
results.push({ file: agent.filePath, status: "dry-run" });
|
|
3632
3990
|
continue;
|
|
3633
3991
|
}
|
|
3634
|
-
const parentDir =
|
|
3992
|
+
const parentDir = import_path12.default.dirname(fullPath);
|
|
3635
3993
|
if (!import_fs9.default.existsSync(parentDir)) {
|
|
3636
3994
|
import_fs9.default.mkdirSync(parentDir, { recursive: true });
|
|
3637
3995
|
}
|
|
@@ -3642,20 +4000,20 @@ function registerInitContextCommand(program2) {
|
|
|
3642
4000
|
for (const result of results) {
|
|
3643
4001
|
switch (result.status) {
|
|
3644
4002
|
case "created":
|
|
3645
|
-
log.print(` ${
|
|
4003
|
+
log.print(` ${import_chalk11.default.green("\u2705")} Created ${import_chalk11.default.white(result.file)}`);
|
|
3646
4004
|
break;
|
|
3647
4005
|
case "skipped":
|
|
3648
|
-
log.print(` ${
|
|
4006
|
+
log.print(` ${import_chalk11.default.yellow("\u23ED")} Skipped ${import_chalk11.default.white(result.file)} ${import_chalk11.default.gray("(already exists \u2014 use --force to overwrite)")}`);
|
|
3649
4007
|
break;
|
|
3650
4008
|
case "dry-run":
|
|
3651
|
-
log.print(` ${
|
|
4009
|
+
log.print(` ${import_chalk11.default.cyan("\u{1F4DD}")} Would create ${import_chalk11.default.white(result.file)}`);
|
|
3652
4010
|
break;
|
|
3653
4011
|
}
|
|
3654
4012
|
}
|
|
3655
4013
|
log.blank();
|
|
3656
4014
|
if (isDryRun) {
|
|
3657
4015
|
log.info("Dry run complete \u2014 no files were written.");
|
|
3658
|
-
log.info(`Re-run without ${
|
|
4016
|
+
log.info(`Re-run without ${import_chalk11.default.cyan("--dry-run")} to apply.`);
|
|
3659
4017
|
} else {
|
|
3660
4018
|
const createdCount = results.filter((r) => r.status === "created").length;
|
|
3661
4019
|
if (createdCount > 0) {
|
|
@@ -3670,7 +4028,7 @@ function registerInitContextCommand(program2) {
|
|
|
3670
4028
|
|
|
3671
4029
|
// cli/commands/report.ts
|
|
3672
4030
|
var import_fs10 = __toESM(require("fs"));
|
|
3673
|
-
var
|
|
4031
|
+
var import_path13 = __toESM(require("path"));
|
|
3674
4032
|
var import_ora4 = __toESM(require("ora"));
|
|
3675
4033
|
var RULE_META = {
|
|
3676
4034
|
"ai-guard/no-empty-catch": { description: "Empty catch blocks silently swallow errors", category: "Reliability" },
|
|
@@ -3694,18 +4052,18 @@ var RULE_META = {
|
|
|
3694
4052
|
};
|
|
3695
4053
|
function getProjectName(cwd) {
|
|
3696
4054
|
try {
|
|
3697
|
-
const pkgPath =
|
|
4055
|
+
const pkgPath = import_path13.default.join(cwd, "package.json");
|
|
3698
4056
|
if (import_fs10.default.existsSync(pkgPath)) {
|
|
3699
4057
|
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3700
4058
|
if (pkg.name) return pkg.name;
|
|
3701
4059
|
}
|
|
3702
4060
|
} catch {
|
|
3703
4061
|
}
|
|
3704
|
-
return
|
|
4062
|
+
return import_path13.default.basename(cwd);
|
|
3705
4063
|
}
|
|
3706
4064
|
function getPluginVersion(cwd) {
|
|
3707
4065
|
try {
|
|
3708
|
-
const pkgPath =
|
|
4066
|
+
const pkgPath = import_path13.default.resolve(__dirname, "../../package.json");
|
|
3709
4067
|
if (import_fs10.default.existsSync(pkgPath)) {
|
|
3710
4068
|
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3711
4069
|
if (pkg.version) return pkg.version;
|
|
@@ -3713,7 +4071,7 @@ function getPluginVersion(cwd) {
|
|
|
3713
4071
|
} catch {
|
|
3714
4072
|
}
|
|
3715
4073
|
try {
|
|
3716
|
-
const pkgPath =
|
|
4074
|
+
const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
3717
4075
|
if (import_fs10.default.existsSync(pkgPath)) {
|
|
3718
4076
|
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3719
4077
|
if (pkg.version) return pkg.version;
|
|
@@ -4092,7 +4450,7 @@ function registerReportCommand(program2) {
|
|
|
4092
4450
|
log.banner("AI GUARD REPORT");
|
|
4093
4451
|
log.blank();
|
|
4094
4452
|
const html = generateHtml(reportData);
|
|
4095
|
-
const outputPath =
|
|
4453
|
+
const outputPath = import_path13.default.resolve(cwd, opts.output);
|
|
4096
4454
|
import_fs10.default.writeFileSync(outputPath, html, "utf-8");
|
|
4097
4455
|
log.success(`Report saved: ${opts.output}`);
|
|
4098
4456
|
log.info(
|
|
@@ -4126,7 +4484,7 @@ function registerReportCommand(program2) {
|
|
|
4126
4484
|
// cli/index.ts
|
|
4127
4485
|
function getVersion() {
|
|
4128
4486
|
try {
|
|
4129
|
-
const pkgPath =
|
|
4487
|
+
const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
|
|
4130
4488
|
const raw = import_fs11.default.readFileSync(pkgPath, "utf-8");
|
|
4131
4489
|
const pkg = JSON.parse(raw);
|
|
4132
4490
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|