eslint-plugin-ai-guard 1.2.1 → 1.2.3
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 +748 -290
- 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, {
|
|
@@ -53,14 +201,14 @@ __export(config_manager_exports, {
|
|
|
53
201
|
});
|
|
54
202
|
function backupConfig(configPath) {
|
|
55
203
|
const backupPath = `${configPath}.bak`;
|
|
56
|
-
|
|
204
|
+
import_fs7.default.copyFileSync(configPath, backupPath);
|
|
57
205
|
return backupPath;
|
|
58
206
|
}
|
|
59
207
|
function readConfig(configPath) {
|
|
60
|
-
return
|
|
208
|
+
return import_fs7.default.readFileSync(configPath, "utf-8");
|
|
61
209
|
}
|
|
62
210
|
function writeConfig(configPath, content) {
|
|
63
|
-
|
|
211
|
+
import_fs7.default.writeFileSync(configPath, content, "utf-8");
|
|
64
212
|
}
|
|
65
213
|
function generateFlatConfig(preset) {
|
|
66
214
|
return `import aiGuard from 'eslint-plugin-ai-guard';
|
|
@@ -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
|
|
464
|
+
var import_fs7, 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
|
-
|
|
321
|
-
|
|
468
|
+
import_fs7 = __toESM(require("fs"));
|
|
469
|
+
import_path6 = __toESM(require("path"));
|
|
322
470
|
AI_GUARD_MARKER = "eslint-plugin-ai-guard";
|
|
323
471
|
DEFAULT_FLAT_IGNORES = [
|
|
324
472
|
"node_modules/**",
|
|
@@ -341,10 +489,11 @@ var init_config_manager = __esm({
|
|
|
341
489
|
|
|
342
490
|
// cli/index.ts
|
|
343
491
|
var import_commander = require("commander");
|
|
344
|
-
var
|
|
345
|
-
var
|
|
492
|
+
var import_fs12 = __toESM(require("fs"));
|
|
493
|
+
var import_path14 = __toESM(require("path"));
|
|
346
494
|
|
|
347
495
|
// cli/commands/run.ts
|
|
496
|
+
var import_fs4 = __toESM(require("fs"));
|
|
348
497
|
var import_ora = __toESM(require("ora"));
|
|
349
498
|
var import_chalk3 = __toESM(require("chalk"));
|
|
350
499
|
|
|
@@ -544,24 +693,46 @@ var log = {
|
|
|
544
693
|
|
|
545
694
|
// cli/utils/eslint-runner.ts
|
|
546
695
|
var ISSUE_CONFIDENCE = {
|
|
696
|
+
// HIGH — near-certain bugs: false positive rate <2% in real-world codebases
|
|
547
697
|
"ai-guard/no-hardcoded-secret": "high",
|
|
698
|
+
// Literal credential string in source: almost always a mistake
|
|
548
699
|
"ai-guard/no-eval-dynamic": "high",
|
|
700
|
+
// Dynamic eval: no legitimate modern use case
|
|
549
701
|
"ai-guard/no-floating-promise": "high",
|
|
550
|
-
|
|
702
|
+
// Unhandled promise: async failures silently swallowed
|
|
703
|
+
// MEDIUM — likely bugs, but legitimate patterns exist
|
|
704
|
+
"ai-guard/no-empty-catch": "medium",
|
|
705
|
+
// CHANGED from high: empty catch is valid for optional ops (JSON.parse, feature detection)
|
|
551
706
|
"ai-guard/no-sql-string-concat": "medium",
|
|
707
|
+
// SQL injection risk, but ORM template literals can FP
|
|
552
708
|
"ai-guard/no-await-in-loop": "medium",
|
|
709
|
+
// Sequential await is often intentional (rate limiting, ordering)
|
|
553
710
|
"ai-guard/require-auth-middleware": "medium",
|
|
711
|
+
// Context-blind: public routes are valid
|
|
554
712
|
"ai-guard/require-authz-check": "medium",
|
|
713
|
+
// Context-blind: some resources are public
|
|
555
714
|
"ai-guard/no-catch-log-rethrow": "medium",
|
|
715
|
+
// Log-then-rethrow is a known antipattern but sometimes intentional
|
|
556
716
|
"ai-guard/no-catch-without-use": "medium",
|
|
717
|
+
// Unused catch param common in TS
|
|
557
718
|
"ai-guard/no-unsafe-deserialize": "medium",
|
|
719
|
+
// JSON.parse is sometimes safe with trusted input
|
|
720
|
+
// LOW — possible improvement, high false-positive rate
|
|
558
721
|
"ai-guard/no-async-array-callback": "low",
|
|
722
|
+
// Promise.all is better, but forEach async is not always a bug
|
|
559
723
|
"ai-guard/no-dead-branch": "low",
|
|
724
|
+
// Static analysis of conditions is inherently imprecise
|
|
560
725
|
"ai-guard/no-broad-exception": "low",
|
|
726
|
+
// Catching Error is common and sometimes correct
|
|
561
727
|
"ai-guard/no-console-in-handler": "low",
|
|
728
|
+
// Stylistic: debug logs left in vs intentional logging
|
|
562
729
|
"ai-guard/no-duplicate-logic-block": "low",
|
|
730
|
+
// May be coincidental similarity, not true duplication
|
|
731
|
+
// INFORMATIONAL — hints only, never fail CI
|
|
563
732
|
"ai-guard/no-async-without-await": "informational",
|
|
733
|
+
// Framework patterns (Next.js RSC, etc.) make this very noisy
|
|
564
734
|
"ai-guard/no-redundant-await": "informational"
|
|
735
|
+
// Explicit await in return is idiomatic in many codebases
|
|
565
736
|
};
|
|
566
737
|
var ISSUE_CATEGORY = {
|
|
567
738
|
"ai-guard/no-floating-promise": "Async Reliability",
|
|
@@ -742,8 +913,8 @@ function classifyEcosystemType(msg) {
|
|
|
742
913
|
return "foreign-rule";
|
|
743
914
|
}
|
|
744
915
|
async function loadPluginModuleFromCwd(cwd) {
|
|
745
|
-
const { createRequire:
|
|
746
|
-
const requireFromCwd =
|
|
916
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
917
|
+
const requireFromCwd = createRequire3(import_path.default.join(cwd, "package.json"));
|
|
747
918
|
try {
|
|
748
919
|
const resolved = requireFromCwd.resolve("eslint-plugin-ai-guard");
|
|
749
920
|
return import((0, import_url.pathToFileURL)(resolved).href);
|
|
@@ -1034,6 +1205,30 @@ async function runEslint(options) {
|
|
|
1034
1205
|
};
|
|
1035
1206
|
}
|
|
1036
1207
|
|
|
1208
|
+
// cli/utils/version.ts
|
|
1209
|
+
var import_module = require("module");
|
|
1210
|
+
var import_url2 = require("url");
|
|
1211
|
+
var import_path2 = __toESM(require("path"));
|
|
1212
|
+
var import_meta = {};
|
|
1213
|
+
var _version;
|
|
1214
|
+
function readVersion() {
|
|
1215
|
+
try {
|
|
1216
|
+
const require_ = (0, import_module.createRequire)(import_meta.url);
|
|
1217
|
+
const pkgPath = import_path2.default.resolve(
|
|
1218
|
+
import_path2.default.dirname((0, import_url2.fileURLToPath)(import_meta.url)),
|
|
1219
|
+
"../../package.json"
|
|
1220
|
+
);
|
|
1221
|
+
const pkg = require_(pkgPath);
|
|
1222
|
+
if (pkg.version) return pkg.version;
|
|
1223
|
+
} catch {
|
|
1224
|
+
}
|
|
1225
|
+
if (typeof __PKG_VERSION__ !== "undefined") {
|
|
1226
|
+
return __PKG_VERSION__;
|
|
1227
|
+
}
|
|
1228
|
+
return "0.0.0";
|
|
1229
|
+
}
|
|
1230
|
+
var PKG_VERSION = _version ?? (_version = readVersion());
|
|
1231
|
+
|
|
1037
1232
|
// cli/utils/sarif.ts
|
|
1038
1233
|
var RULE_DOCS = {
|
|
1039
1234
|
"ai-guard/no-empty-catch": { shortDesc: "Empty catch block silently swallows errors", tags: ["reliability"] },
|
|
@@ -1056,6 +1251,50 @@ var RULE_DOCS = {
|
|
|
1056
1251
|
"ai-guard/no-dead-branch": { shortDesc: "Dead code branch that can never execute", tags: ["ai-patterns"] }
|
|
1057
1252
|
};
|
|
1058
1253
|
var BASE_DOCS_URL = "https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules";
|
|
1254
|
+
function sanitizeSarifTags(tags) {
|
|
1255
|
+
return Array.from(
|
|
1256
|
+
new Set(
|
|
1257
|
+
tags.filter((t) => typeof t === "string" && t.trim().length > 0).map((t) => t.toLowerCase().trim().replace(/\s+/g, "-"))
|
|
1258
|
+
)
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
function sanitizeSarifProperties(props) {
|
|
1262
|
+
return {
|
|
1263
|
+
tags: sanitizeSarifTags(props.tags),
|
|
1264
|
+
confidence: props.confidence ?? "low",
|
|
1265
|
+
category: props.category ?? "AI Patterns",
|
|
1266
|
+
"problem.severity": props.problemSeverity ?? "low"
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
function sanitizeSarifRule(rule) {
|
|
1270
|
+
return {
|
|
1271
|
+
...rule,
|
|
1272
|
+
shortDescription: {
|
|
1273
|
+
text: rule.shortDescription.text || `ai-guard rule: ${rule.id}`
|
|
1274
|
+
},
|
|
1275
|
+
fullDescription: rule.fullDescription?.text ? rule.fullDescription : { text: rule.shortDescription.text || `ai-guard rule: ${rule.id}` },
|
|
1276
|
+
properties: rule.properties ? {
|
|
1277
|
+
...rule.properties,
|
|
1278
|
+
// Re-deduplicate tags at the rule level as a final safety net
|
|
1279
|
+
tags: rule.properties.tags ? sanitizeSarifTags(rule.properties.tags) : []
|
|
1280
|
+
} : void 0
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function sanitizeSarifLog(log2) {
|
|
1284
|
+
return {
|
|
1285
|
+
...log2,
|
|
1286
|
+
runs: log2.runs.map((run) => ({
|
|
1287
|
+
...run,
|
|
1288
|
+
tool: {
|
|
1289
|
+
...run.tool,
|
|
1290
|
+
driver: {
|
|
1291
|
+
...run.tool.driver,
|
|
1292
|
+
rules: run.tool.driver.rules.map(sanitizeSarifRule)
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}))
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1059
1298
|
function confidenceToSarifLevel(confidence) {
|
|
1060
1299
|
switch (confidence) {
|
|
1061
1300
|
case "high":
|
|
@@ -1117,6 +1356,8 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1117
1356
|
const confidence = ISSUE_CONFIDENCE[id];
|
|
1118
1357
|
const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
|
|
1119
1358
|
const remediation = ISSUE_REMEDIATION[id];
|
|
1359
|
+
const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
|
|
1360
|
+
const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
|
|
1120
1361
|
const helpMarkdown = [
|
|
1121
1362
|
`**${meta?.shortDesc ?? shortName}**`,
|
|
1122
1363
|
"",
|
|
@@ -1124,7 +1365,7 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1124
1365
|
"",
|
|
1125
1366
|
`[View rule documentation](${BASE_DOCS_URL}/${shortName}.md)`
|
|
1126
1367
|
].filter(Boolean).join("\n");
|
|
1127
|
-
return {
|
|
1368
|
+
return sanitizeSarifRule({
|
|
1128
1369
|
id,
|
|
1129
1370
|
shortDescription: {
|
|
1130
1371
|
text: meta?.shortDesc ?? `ai-guard rule: ${shortName}`
|
|
@@ -1140,13 +1381,13 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1140
1381
|
defaultConfiguration: {
|
|
1141
1382
|
level: confidenceToSarifLevel(confidence)
|
|
1142
1383
|
},
|
|
1143
|
-
properties: {
|
|
1144
|
-
tags:
|
|
1145
|
-
confidence
|
|
1384
|
+
properties: sanitizeSarifProperties({
|
|
1385
|
+
tags: rawTags,
|
|
1386
|
+
confidence,
|
|
1146
1387
|
category,
|
|
1147
|
-
|
|
1148
|
-
}
|
|
1149
|
-
};
|
|
1388
|
+
problemSeverity: confidenceToGitHubSeverity(confidence)
|
|
1389
|
+
})
|
|
1390
|
+
});
|
|
1150
1391
|
});
|
|
1151
1392
|
}
|
|
1152
1393
|
function buildSarifResult(issue, file) {
|
|
@@ -1183,7 +1424,7 @@ function buildSarifResult(issue, file) {
|
|
|
1183
1424
|
}
|
|
1184
1425
|
};
|
|
1185
1426
|
}
|
|
1186
|
-
function buildSarifLog(result, version =
|
|
1427
|
+
function buildSarifLog(result, version = PKG_VERSION) {
|
|
1187
1428
|
const usedRuleIds = collectUsedRuleIds(result);
|
|
1188
1429
|
const rules = buildRuleDescriptors(usedRuleIds);
|
|
1189
1430
|
const sarifResults = [];
|
|
@@ -1192,7 +1433,7 @@ function buildSarifLog(result, version = "1.3.0") {
|
|
|
1192
1433
|
sarifResults.push(buildSarifResult(issue, file));
|
|
1193
1434
|
}
|
|
1194
1435
|
}
|
|
1195
|
-
|
|
1436
|
+
const log2 = {
|
|
1196
1437
|
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
1197
1438
|
version: "2.1.0",
|
|
1198
1439
|
runs: [
|
|
@@ -1216,13 +1457,46 @@ function buildSarifLog(result, version = "1.3.0") {
|
|
|
1216
1457
|
}
|
|
1217
1458
|
]
|
|
1218
1459
|
};
|
|
1460
|
+
return sanitizeSarifLog(log2);
|
|
1219
1461
|
}
|
|
1220
1462
|
function sarifToJson(log2) {
|
|
1221
1463
|
return JSON.stringify(log2, null, 2);
|
|
1222
1464
|
}
|
|
1465
|
+
function buildSarifDebugInfo(result) {
|
|
1466
|
+
const usedRuleIds = collectUsedRuleIds(result);
|
|
1467
|
+
const rulesEmitted = usedRuleIds.map((id) => {
|
|
1468
|
+
const meta = RULE_DOCS[id];
|
|
1469
|
+
const confidence = ISSUE_CONFIDENCE[id];
|
|
1470
|
+
const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
|
|
1471
|
+
const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
|
|
1472
|
+
const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
|
|
1473
|
+
const sanitizedTags = sanitizeSarifTags(rawTags);
|
|
1474
|
+
return {
|
|
1475
|
+
id,
|
|
1476
|
+
rawTags,
|
|
1477
|
+
sanitizedTags,
|
|
1478
|
+
hasDuplicatesInRaw: rawTags.length !== new Set(rawTags).size,
|
|
1479
|
+
confidence: confidence ?? "low",
|
|
1480
|
+
category,
|
|
1481
|
+
level: confidenceToSarifLevel(confidence)
|
|
1482
|
+
};
|
|
1483
|
+
});
|
|
1484
|
+
return {
|
|
1485
|
+
rulesEmitted,
|
|
1486
|
+
totalResults: result.files.reduce((n, f) => n + f.issues.length, 0),
|
|
1487
|
+
version: PKG_VERSION
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1223
1490
|
|
|
1224
1491
|
// cli/utils/github-summary.ts
|
|
1225
1492
|
var import_fs2 = __toESM(require("fs"));
|
|
1493
|
+
function warnEnvIssue(name, filePath, err) {
|
|
1494
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1495
|
+
process.stderr.write(
|
|
1496
|
+
`[ai-guard] Warning: Could not write to ${name}=${filePath}: ${msg}
|
|
1497
|
+
`
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1226
1500
|
function escapeMarkdown(text) {
|
|
1227
1501
|
return text.replace(/[|`*_[\]<>]/g, "\\$&");
|
|
1228
1502
|
}
|
|
@@ -1413,8 +1687,9 @@ function writeGitHubSummary(result, options) {
|
|
|
1413
1687
|
if (!summaryPath) return;
|
|
1414
1688
|
try {
|
|
1415
1689
|
const markdown = buildGitHubSummaryMarkdown(result, options);
|
|
1416
|
-
import_fs2.default.appendFileSync(summaryPath, markdown + "\n");
|
|
1417
|
-
} catch {
|
|
1690
|
+
import_fs2.default.appendFileSync(summaryPath, markdown + "\n", { encoding: "utf-8" });
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
warnEnvIssue("GITHUB_STEP_SUMMARY", summaryPath, err);
|
|
1418
1693
|
}
|
|
1419
1694
|
}
|
|
1420
1695
|
function writeGitHubOutputs(result, sarifPath) {
|
|
@@ -1432,14 +1707,15 @@ function writeGitHubOutputs(result, sarifPath) {
|
|
|
1432
1707
|
...sarifPath ? [`sarif-file=${sarifPath}`] : []
|
|
1433
1708
|
];
|
|
1434
1709
|
try {
|
|
1435
|
-
import_fs2.default.appendFileSync(outputPath, outputs.join("\n") + "\n");
|
|
1436
|
-
} catch {
|
|
1710
|
+
import_fs2.default.appendFileSync(outputPath, outputs.join("\n") + "\n", { encoding: "utf-8" });
|
|
1711
|
+
} catch (err) {
|
|
1712
|
+
warnEnvIssue("GITHUB_OUTPUT", outputPath, err);
|
|
1437
1713
|
}
|
|
1438
1714
|
}
|
|
1439
1715
|
|
|
1440
1716
|
// cli/utils/git-diff.ts
|
|
1441
1717
|
var import_child_process = require("child_process");
|
|
1442
|
-
var
|
|
1718
|
+
var import_path3 = __toESM(require("path"));
|
|
1443
1719
|
var import_fs3 = __toESM(require("fs"));
|
|
1444
1720
|
var DEFAULT_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
1445
1721
|
var IGNORE_PREFIXES = [
|
|
@@ -1466,7 +1742,8 @@ function runGit(args, cwd) {
|
|
|
1466
1742
|
return (0, import_child_process.execSync)(`git ${args}`, {
|
|
1467
1743
|
cwd,
|
|
1468
1744
|
encoding: "utf-8",
|
|
1469
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1745
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1746
|
+
timeout: 15e3
|
|
1470
1747
|
}).trim();
|
|
1471
1748
|
} catch {
|
|
1472
1749
|
return null;
|
|
@@ -1475,35 +1752,103 @@ function runGit(args, cwd) {
|
|
|
1475
1752
|
function isGitAvailable(cwd) {
|
|
1476
1753
|
return runGit("rev-parse --is-inside-work-tree", cwd) === "true";
|
|
1477
1754
|
}
|
|
1478
|
-
function
|
|
1479
|
-
|
|
1755
|
+
function isShallowClone(cwd) {
|
|
1756
|
+
return runGit("rev-parse --is-shallow-repository", cwd) === "true";
|
|
1757
|
+
}
|
|
1758
|
+
function isDetachedHead(cwd) {
|
|
1759
|
+
const result = runGit("symbolic-ref --quiet HEAD", cwd);
|
|
1760
|
+
return result === null;
|
|
1761
|
+
}
|
|
1762
|
+
function normalizeBranchName(ref) {
|
|
1763
|
+
return ref.replace(/^origin\//, "").trim();
|
|
1764
|
+
}
|
|
1765
|
+
function tryPrDiff(branch, cwd, debugInfo) {
|
|
1766
|
+
const rawBranch = normalizeBranchName(branch);
|
|
1767
|
+
{
|
|
1768
|
+
const strategy = `merge-base HEAD origin/${rawBranch}`;
|
|
1769
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1770
|
+
const mergeBase = runGit(`merge-base HEAD origin/${rawBranch}`, cwd);
|
|
1771
|
+
if (mergeBase) {
|
|
1772
|
+
debugInfo.mergeBase = mergeBase;
|
|
1773
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${mergeBase}`, cwd);
|
|
1774
|
+
if (out !== null) {
|
|
1775
|
+
debugInfo.strategySucceeded = strategy;
|
|
1776
|
+
return { output: out, strategy, mergeBase };
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
{
|
|
1781
|
+
const strategy = `merge-base HEAD ${rawBranch}`;
|
|
1782
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1783
|
+
const mergeBase = runGit(`merge-base HEAD ${rawBranch}`, cwd);
|
|
1784
|
+
if (mergeBase) {
|
|
1785
|
+
if (!debugInfo.mergeBase) debugInfo.mergeBase = mergeBase;
|
|
1786
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${mergeBase}`, cwd);
|
|
1787
|
+
if (out !== null) {
|
|
1788
|
+
debugInfo.strategySucceeded = strategy;
|
|
1789
|
+
return { output: out, strategy, mergeBase };
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
{
|
|
1794
|
+
const strategy = `diff origin/${rawBranch}...HEAD`;
|
|
1795
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1796
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT origin/${rawBranch}...HEAD`, cwd);
|
|
1797
|
+
if (out !== null) {
|
|
1798
|
+
debugInfo.strategySucceeded = strategy;
|
|
1799
|
+
return { output: out, strategy };
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
{
|
|
1803
|
+
const strategy = `diff ${rawBranch}...HEAD`;
|
|
1804
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1805
|
+
const out = runGit(`diff --name-only --diff-filter=ACMRT ${rawBranch}...HEAD`, cwd);
|
|
1806
|
+
if (out !== null) {
|
|
1807
|
+
debugInfo.strategySucceeded = strategy;
|
|
1808
|
+
return { output: out, strategy };
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
{
|
|
1812
|
+
const strategy = "diff HEAD~1 (shallow fallback)";
|
|
1813
|
+
debugInfo.strategiesAttempted.push(strategy);
|
|
1814
|
+
const out = runGit("diff --name-only --diff-filter=ACMRT HEAD~1", cwd);
|
|
1815
|
+
if (out !== null) {
|
|
1816
|
+
debugInfo.strategySucceeded = strategy;
|
|
1817
|
+
return { output: out, strategy };
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
debugInfo.strategySucceeded = null;
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
function filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo) {
|
|
1480
1824
|
const kept = [];
|
|
1481
1825
|
for (const rel of rawFiles) {
|
|
1482
|
-
const
|
|
1826
|
+
const normalized = rel.replace(/\\/g, "/").trim();
|
|
1827
|
+
if (!normalized) continue;
|
|
1828
|
+
const ext = import_path3.default.extname(normalized).toLowerCase();
|
|
1483
1829
|
if (!extensions.includes(ext)) {
|
|
1484
|
-
|
|
1830
|
+
debugInfo.droppedByExtension++;
|
|
1485
1831
|
continue;
|
|
1486
1832
|
}
|
|
1487
|
-
const normalized = rel.replace(/\\/g, "/");
|
|
1488
1833
|
if (IGNORE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
1489
|
-
|
|
1834
|
+
debugInfo.droppedByIgnore++;
|
|
1490
1835
|
continue;
|
|
1491
1836
|
}
|
|
1492
1837
|
if (workingDirectory) {
|
|
1493
1838
|
const wdNorm = workingDirectory.replace(/\\/g, "/").replace(/\/$/, "");
|
|
1494
1839
|
if (!normalized.startsWith(wdNorm + "/") && normalized !== wdNorm) {
|
|
1495
|
-
|
|
1840
|
+
debugInfo.droppedByWorkingDir++;
|
|
1496
1841
|
continue;
|
|
1497
1842
|
}
|
|
1498
1843
|
}
|
|
1499
|
-
const abs =
|
|
1844
|
+
const abs = import_path3.default.resolve(cwd, normalized);
|
|
1500
1845
|
if (!import_fs3.default.existsSync(abs)) {
|
|
1501
|
-
|
|
1846
|
+
debugInfo.droppedMissing++;
|
|
1502
1847
|
continue;
|
|
1503
1848
|
}
|
|
1504
1849
|
kept.push(abs);
|
|
1505
1850
|
}
|
|
1506
|
-
return
|
|
1851
|
+
return kept;
|
|
1507
1852
|
}
|
|
1508
1853
|
function parseFileList(output) {
|
|
1509
1854
|
return output.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
@@ -1514,46 +1859,96 @@ function getChangedFiles(options = {}) {
|
|
|
1514
1859
|
base,
|
|
1515
1860
|
cwd = process.cwd(),
|
|
1516
1861
|
extensions = DEFAULT_EXTENSIONS,
|
|
1517
|
-
workingDirectory
|
|
1862
|
+
workingDirectory,
|
|
1863
|
+
debug = false
|
|
1518
1864
|
} = options;
|
|
1519
|
-
const
|
|
1865
|
+
const debugInfo = {
|
|
1866
|
+
strategiesAttempted: [],
|
|
1867
|
+
strategySucceeded: null,
|
|
1868
|
+
isShallowClone: false,
|
|
1869
|
+
isDetachedHead: false,
|
|
1870
|
+
rawFileCount: 0,
|
|
1871
|
+
droppedByExtension: 0,
|
|
1872
|
+
droppedByIgnore: 0,
|
|
1873
|
+
droppedMissing: 0,
|
|
1874
|
+
droppedByWorkingDir: 0
|
|
1875
|
+
};
|
|
1520
1876
|
if (!isGitAvailable(cwd)) {
|
|
1521
|
-
return {
|
|
1877
|
+
return {
|
|
1878
|
+
files: [],
|
|
1879
|
+
mode: "fallback",
|
|
1880
|
+
totalDetected: 0,
|
|
1881
|
+
filteredOut: 0,
|
|
1882
|
+
debugInfo,
|
|
1883
|
+
zeroFilesReason: "Git is not available in this directory. Run inside a git repository."
|
|
1884
|
+
};
|
|
1522
1885
|
}
|
|
1886
|
+
debugInfo.isShallowClone = isShallowClone(cwd);
|
|
1887
|
+
debugInfo.isDetachedHead = isDetachedHead(cwd);
|
|
1888
|
+
const envBase = process.env.GITHUB_BASE_REF ? normalizeBranchName(process.env.GITHUB_BASE_REF) : void 0;
|
|
1889
|
+
const resolvedBranch = base ? normalizeBranchName(base) : envBase;
|
|
1890
|
+
debugInfo.resolvedBase = resolvedBranch;
|
|
1523
1891
|
let rawOutput = null;
|
|
1524
1892
|
let mode;
|
|
1525
1893
|
let usedBase;
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1894
|
+
let zeroFilesReason;
|
|
1895
|
+
if (staged) {
|
|
1896
|
+
rawOutput = runGit("diff --name-only --cached --diff-filter=ACMRT", cwd);
|
|
1897
|
+
mode = "staged";
|
|
1898
|
+
debugInfo.strategiesAttempted.push("git diff --cached");
|
|
1899
|
+
if (rawOutput !== null) debugInfo.strategySucceeded = "git diff --cached";
|
|
1531
1900
|
if (!rawOutput) {
|
|
1532
|
-
|
|
1901
|
+
zeroFilesReason = "No staged files found. Run `git add <files>` before using --staged.";
|
|
1533
1902
|
}
|
|
1903
|
+
} else if (resolvedBranch) {
|
|
1534
1904
|
mode = "pr-diff";
|
|
1535
|
-
usedBase =
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1905
|
+
usedBase = resolvedBranch;
|
|
1906
|
+
const diffResult = tryPrDiff(resolvedBranch, cwd, debugInfo);
|
|
1907
|
+
if (diffResult) {
|
|
1908
|
+
rawOutput = diffResult.output;
|
|
1909
|
+
} else {
|
|
1910
|
+
const shallow = debugInfo.isShallowClone ? " Shallow clone detected \u2014 try fetch-depth: 0 in your workflow." : "";
|
|
1911
|
+
const detached = debugInfo.isDetachedHead ? " HEAD is detached \u2014 ensure checkout fetches the branch." : "";
|
|
1912
|
+
zeroFilesReason = `PR diff failed against "${resolvedBranch}". Tried: ${debugInfo.strategiesAttempted.join(", ")}.${shallow}${detached} Run with --debug-git for full diagnosis.`;
|
|
1913
|
+
}
|
|
1539
1914
|
} else {
|
|
1915
|
+
mode = "uncommitted";
|
|
1540
1916
|
const staged_ = runGit("diff --name-only --cached --diff-filter=ACMRT", cwd) ?? "";
|
|
1541
1917
|
const unstaged = runGit("diff --name-only --diff-filter=ACMRT", cwd) ?? "";
|
|
1918
|
+
debugInfo.strategiesAttempted.push("git diff --cached", "git diff (unstaged)");
|
|
1542
1919
|
const combined = /* @__PURE__ */ new Set([...parseFileList(staged_), ...parseFileList(unstaged)]);
|
|
1543
1920
|
rawOutput = [...combined].join("\n");
|
|
1544
|
-
|
|
1921
|
+
if (rawOutput) debugInfo.strategySucceeded = "uncommitted changes";
|
|
1922
|
+
if (!rawOutput) {
|
|
1923
|
+
zeroFilesReason = "No uncommitted changes found. Use --pr to scan PR changes, or --staged for staged files.";
|
|
1924
|
+
}
|
|
1545
1925
|
}
|
|
1546
1926
|
if (!rawOutput) {
|
|
1547
|
-
return {
|
|
1927
|
+
return {
|
|
1928
|
+
files: [],
|
|
1929
|
+
mode,
|
|
1930
|
+
base: usedBase,
|
|
1931
|
+
totalDetected: 0,
|
|
1932
|
+
filteredOut: 0,
|
|
1933
|
+
debugInfo,
|
|
1934
|
+
zeroFilesReason
|
|
1935
|
+
};
|
|
1548
1936
|
}
|
|
1549
1937
|
const rawFiles = parseFileList(rawOutput);
|
|
1550
|
-
|
|
1938
|
+
debugInfo.rawFileCount = rawFiles.length;
|
|
1939
|
+
const kept = filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo);
|
|
1940
|
+
const dropped = debugInfo.droppedByExtension + debugInfo.droppedByIgnore + debugInfo.droppedByWorkingDir + debugInfo.droppedMissing;
|
|
1941
|
+
if (kept.length === 0 && rawFiles.length > 0) {
|
|
1942
|
+
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}.`;
|
|
1943
|
+
}
|
|
1551
1944
|
return {
|
|
1552
1945
|
files: kept,
|
|
1553
1946
|
mode,
|
|
1554
1947
|
base: usedBase,
|
|
1555
1948
|
totalDetected: rawFiles.length,
|
|
1556
|
-
filteredOut: dropped
|
|
1949
|
+
filteredOut: dropped,
|
|
1950
|
+
debugInfo,
|
|
1951
|
+
zeroFilesReason: kept.length === 0 ? zeroFilesReason : void 0
|
|
1557
1952
|
};
|
|
1558
1953
|
}
|
|
1559
1954
|
function isGitHubActions() {
|
|
@@ -1569,7 +1964,7 @@ function registerRunCommand(program2) {
|
|
|
1569
1964
|
"--max-warnings <n>",
|
|
1570
1965
|
"Fail with exit code 1 if warnings exceed this count",
|
|
1571
1966
|
(value) => Number.parseInt(value, 10)
|
|
1572
|
-
).option("--verbose", "Show all issues \u2014 disable grouping of repeated warnings").option("--quiet", "Only show errors \u2014 suppress warnings and informational hints").option("--debug-timing", "Print per-phase timing diagnostics").action(async (opts) => {
|
|
1967
|
+
).option("--verbose", "Show all issues \u2014 disable grouping of repeated warnings").option("--quiet", "Only show errors \u2014 suppress warnings and informational hints").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-sarif", "Print SARIF tag normalization trace \u2014 diagnose duplicate-tag schema issues").option("--sarif-output <file>", "Write SARIF to this file instead of stdout").action(async (opts) => {
|
|
1573
1968
|
if (opts.maxWarnings !== void 0 && (!Number.isInteger(opts.maxWarnings) || opts.maxWarnings < 0)) {
|
|
1574
1969
|
log.blank();
|
|
1575
1970
|
log.error("--max-warnings must be a non-negative integer.");
|
|
@@ -1621,9 +2016,36 @@ function registerRunCommand(program2) {
|
|
|
1621
2016
|
writeGitHubSummary(result, { preset, scanMode: "full" });
|
|
1622
2017
|
writeGitHubOutputs(result);
|
|
1623
2018
|
}
|
|
1624
|
-
if (opts.sarif) {
|
|
2019
|
+
if (opts.sarif || opts.sarifOutput) {
|
|
1625
2020
|
const sarifLog = buildSarifLog(result);
|
|
1626
|
-
|
|
2021
|
+
const sarifJson = sarifToJson(sarifLog);
|
|
2022
|
+
if (opts.debugSarif) {
|
|
2023
|
+
const debugInfo = buildSarifDebugInfo(result);
|
|
2024
|
+
process.stderr.write("\n[debug-sarif] SARIF Tag Normalization Trace\n");
|
|
2025
|
+
process.stderr.write("[debug-sarif] \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\n");
|
|
2026
|
+
for (const r of debugInfo.rulesEmitted) {
|
|
2027
|
+
process.stderr.write(`[debug-sarif] ${r.id}
|
|
2028
|
+
`);
|
|
2029
|
+
process.stderr.write(`[debug-sarif] raw: ${JSON.stringify(r.rawTags)}
|
|
2030
|
+
`);
|
|
2031
|
+
process.stderr.write(`[debug-sarif] sanitized: ${JSON.stringify(r.sanitizedTags)}
|
|
2032
|
+
`);
|
|
2033
|
+
if (r.hasDuplicatesInRaw) {
|
|
2034
|
+
process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
|
|
2035
|
+
`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
process.stderr.write("\n");
|
|
2039
|
+
}
|
|
2040
|
+
if (opts.sarifOutput) {
|
|
2041
|
+
import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
|
|
2042
|
+
if (opts.debugSarif) {
|
|
2043
|
+
process.stderr.write(`[debug-sarif] SARIF written to: ${opts.sarifOutput}
|
|
2044
|
+
`);
|
|
2045
|
+
}
|
|
2046
|
+
} else {
|
|
2047
|
+
console.log(sarifJson);
|
|
2048
|
+
}
|
|
1627
2049
|
process.exit(resolveExitCode(result, opts.failOn ?? "errors", opts.maxWarnings));
|
|
1628
2050
|
return;
|
|
1629
2051
|
}
|
|
@@ -1957,32 +2379,58 @@ function resolveExitCode(result, failOn, maxWarnings) {
|
|
|
1957
2379
|
}
|
|
1958
2380
|
|
|
1959
2381
|
// cli/commands/changed.ts
|
|
1960
|
-
var
|
|
1961
|
-
var
|
|
2382
|
+
var import_path4 = __toESM(require("path"));
|
|
2383
|
+
var import_fs5 = __toESM(require("fs"));
|
|
1962
2384
|
var import_ora2 = __toESM(require("ora"));
|
|
1963
|
-
var
|
|
2385
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
1964
2386
|
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 (
|
|
2387
|
+
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
2388
|
"--max-warnings <n>",
|
|
1967
2389
|
"Fail if warnings exceed this count",
|
|
1968
2390
|
(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) => {
|
|
2391
|
+
).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
2392
|
const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
|
|
1971
2393
|
const inCI = isGitHubActions();
|
|
1972
2394
|
const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
|
|
2395
|
+
const sarifToFile = opts.sarif && !opts.sarifStdout && (opts.sarifOutput || inCI);
|
|
2396
|
+
const resolvedSarifPath = opts.sarifOutput ?? (sarifToFile ? "ai-guard-results.sarif" : void 0);
|
|
1973
2397
|
if (!opts.json && !opts.sarif) {
|
|
1974
2398
|
log.banner("AI GUARD");
|
|
1975
2399
|
}
|
|
2400
|
+
if (opts.debugCi) {
|
|
2401
|
+
const { printCIDebug: printCIDebug2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|
|
2402
|
+
printCIDebug2({
|
|
2403
|
+
isGitHubActions: inCI,
|
|
2404
|
+
githubBaseRef: process.env.GITHUB_BASE_REF,
|
|
2405
|
+
githubSha: process.env.GITHUB_SHA,
|
|
2406
|
+
githubRef: process.env.GITHUB_REF,
|
|
2407
|
+
githubRepository: process.env.GITHUB_REPOSITORY,
|
|
2408
|
+
githubWorkflow: process.env.GITHUB_WORKFLOW,
|
|
2409
|
+
githubRunId: process.env.GITHUB_RUN_ID,
|
|
2410
|
+
githubEventName: process.env.GITHUB_EVENT_NAME,
|
|
2411
|
+
githubStepSummary: process.env.GITHUB_STEP_SUMMARY,
|
|
2412
|
+
githubOutput: process.env.GITHUB_OUTPUT,
|
|
2413
|
+
sarifOutputPath: resolvedSarifPath,
|
|
2414
|
+
preset,
|
|
2415
|
+
failOn: opts.failOn,
|
|
2416
|
+
scanMode: opts.staged ? "staged" : baseRef ? "pr-diff" : "uncommitted"
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
1976
2419
|
const cwd = process.cwd();
|
|
1977
|
-
const resolvedPath =
|
|
2420
|
+
const resolvedPath = import_path4.default.resolve(opts.path);
|
|
1978
2421
|
const workingDirectory = opts.path !== "." ? opts.path : void 0;
|
|
1979
|
-
const spinner = opts.json || opts.sarif ? null : (0, import_ora2.default)({ text:
|
|
2422
|
+
const spinner = opts.json || opts.sarif ? null : (0, import_ora2.default)({ text: import_chalk5.default.dim("Detecting changed files\u2026"), color: "cyan" }).start();
|
|
1980
2423
|
const changedResult = getChangedFiles({
|
|
1981
2424
|
staged: opts.staged ?? false,
|
|
1982
|
-
base: baseRef
|
|
2425
|
+
base: baseRef,
|
|
1983
2426
|
cwd,
|
|
1984
|
-
workingDirectory
|
|
2427
|
+
workingDirectory,
|
|
2428
|
+
debug: opts.debugGit
|
|
1985
2429
|
});
|
|
2430
|
+
if (opts.debugGit) {
|
|
2431
|
+
const { printGitDebug: printGitDebug2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|
|
2432
|
+
printGitDebug2(changedResult);
|
|
2433
|
+
}
|
|
1986
2434
|
if (changedResult.files.length === 0) {
|
|
1987
2435
|
spinner?.stop();
|
|
1988
2436
|
if (changedResult.mode === "fallback") {
|
|
@@ -1995,18 +2443,25 @@ function registerChangedCommand(program2) {
|
|
|
1995
2443
|
targetPath: resolvedPath.toString(),
|
|
1996
2444
|
debugTiming: opts.debugTiming
|
|
1997
2445
|
});
|
|
1998
|
-
handleResults(result2, opts, preset, "full", 0, inCI);
|
|
2446
|
+
handleResults(result2, opts, preset, "full", 0, inCI, resolvedSarifPath);
|
|
1999
2447
|
return;
|
|
2000
2448
|
}
|
|
2001
2449
|
if (!opts.json && !opts.sarif) {
|
|
2002
2450
|
log.blank();
|
|
2003
|
-
log.print(` ${
|
|
2451
|
+
log.print(` ${import_chalk5.default.yellow("\u26A0")} No changed JS/TS files detected \u2014 nothing to scan`);
|
|
2452
|
+
if (changedResult.zeroFilesReason) {
|
|
2453
|
+
log.print(import_chalk5.default.dim(` ${changedResult.zeroFilesReason}`));
|
|
2454
|
+
}
|
|
2004
2455
|
if (changedResult.filteredOut > 0) {
|
|
2005
|
-
log.print(
|
|
2456
|
+
log.print(import_chalk5.default.dim(` (${changedResult.filteredOut} file${changedResult.filteredOut !== 1 ? "s" : ""} filtered out)`));
|
|
2457
|
+
}
|
|
2458
|
+
if (!opts.debugGit) {
|
|
2459
|
+
log.print(import_chalk5.default.dim(" Run with --debug-git for full git resolution trace."));
|
|
2006
2460
|
}
|
|
2007
2461
|
log.blank();
|
|
2008
2462
|
} else if (opts.json) {
|
|
2009
2463
|
console.log(JSON.stringify({
|
|
2464
|
+
version: PKG_VERSION,
|
|
2010
2465
|
preset,
|
|
2011
2466
|
scanMode: changedResult.mode,
|
|
2012
2467
|
changedFiles: 0,
|
|
@@ -2014,6 +2469,8 @@ function registerChangedCommand(program2) {
|
|
|
2014
2469
|
totalIssues: 0,
|
|
2015
2470
|
totalErrors: 0,
|
|
2016
2471
|
totalWarnings: 0,
|
|
2472
|
+
zeroFilesReason: changedResult.zeroFilesReason,
|
|
2473
|
+
debugInfo: opts.debugGit ? changedResult.debugInfo : void 0,
|
|
2017
2474
|
signalSummary: { high: 0, medium: 0, low: 0, informational: 0, ecosystemIssues: 0, parserErrors: 0 }
|
|
2018
2475
|
}, null, 2));
|
|
2019
2476
|
}
|
|
@@ -2021,7 +2478,7 @@ function registerChangedCommand(program2) {
|
|
|
2021
2478
|
return;
|
|
2022
2479
|
}
|
|
2023
2480
|
if (spinner) {
|
|
2024
|
-
spinner.text =
|
|
2481
|
+
spinner.text = import_chalk5.default.dim(`Scanning ${changedResult.files.length} changed file${changedResult.files.length !== 1 ? "s" : ""}\u2026`);
|
|
2025
2482
|
}
|
|
2026
2483
|
const result = await runEslint({
|
|
2027
2484
|
preset,
|
|
@@ -2030,10 +2487,11 @@ function registerChangedCommand(program2) {
|
|
|
2030
2487
|
debugTiming: opts.debugTiming
|
|
2031
2488
|
});
|
|
2032
2489
|
spinner?.stop();
|
|
2033
|
-
|
|
2490
|
+
const scanMode = changedResult.mode === "staged" ? "staged" : "changed";
|
|
2491
|
+
handleResults(result, opts, preset, scanMode, changedResult.files.length, inCI, resolvedSarifPath);
|
|
2034
2492
|
});
|
|
2035
2493
|
}
|
|
2036
|
-
function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI) {
|
|
2494
|
+
function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI, resolvedSarifPath) {
|
|
2037
2495
|
if (inCI || opts.githubSummary) {
|
|
2038
2496
|
writeGitHubSummary(result, { preset, scanMode, changedFilesCount });
|
|
2039
2497
|
writeGitHubOutputs(result, opts.sarifOutput);
|
|
@@ -2042,8 +2500,8 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2042
2500
|
if (opts.sarif) {
|
|
2043
2501
|
const sarifLog = buildSarifLog(result);
|
|
2044
2502
|
const sarifJson = sarifToJson(sarifLog);
|
|
2045
|
-
if (
|
|
2046
|
-
|
|
2503
|
+
if (resolvedSarifPath) {
|
|
2504
|
+
import_fs5.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
|
|
2047
2505
|
} else {
|
|
2048
2506
|
console.log(sarifJson);
|
|
2049
2507
|
}
|
|
@@ -2053,7 +2511,7 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2053
2511
|
if (opts.json) {
|
|
2054
2512
|
const signalSummary = buildSignalSummary(result);
|
|
2055
2513
|
const jsonOutput = {
|
|
2056
|
-
version:
|
|
2514
|
+
version: PKG_VERSION,
|
|
2057
2515
|
preset,
|
|
2058
2516
|
scanMode,
|
|
2059
2517
|
scannedPath: opts.path,
|
|
@@ -2115,7 +2573,7 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2115
2573
|
log.category(icon, cat, categoryErrors[cat] ?? 0, categoryWarnings[cat] ?? 0);
|
|
2116
2574
|
}
|
|
2117
2575
|
log.blank();
|
|
2118
|
-
log.print(` ${
|
|
2576
|
+
log.print(` ${import_chalk5.default.bold("Total:")} ${formatIssueCount(result.totalErrors, result.totalWarnings)}`);
|
|
2119
2577
|
log.blank();
|
|
2120
2578
|
if (actionableFiles.length > 0) {
|
|
2121
2579
|
log.section("Issues by File");
|
|
@@ -2126,18 +2584,18 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2126
2584
|
if (totalInfoCount > 0 && !quiet) {
|
|
2127
2585
|
if (verbose) {
|
|
2128
2586
|
log.section("Informational Hints");
|
|
2129
|
-
log.print(
|
|
2587
|
+
log.print(import_chalk5.default.dim(" Stylistic hints with higher false-positive rate \u2014 review before acting."));
|
|
2130
2588
|
log.blank();
|
|
2131
2589
|
renderIssuesByFile({ ...result, files: informationalFiles }, true, false);
|
|
2132
2590
|
} else {
|
|
2133
2591
|
log.blank();
|
|
2134
|
-
log.print(` ${
|
|
2592
|
+
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
2593
|
}
|
|
2136
2594
|
}
|
|
2137
2595
|
}
|
|
2138
2596
|
if (result.ecosystemIssues.length > 0) {
|
|
2139
2597
|
log.ecosystemSection("ESLint Config Issues");
|
|
2140
|
-
log.print(
|
|
2598
|
+
log.print(import_chalk5.default.dim(" These issues come from your project's ESLint config, not from ai-guard."));
|
|
2141
2599
|
log.blank();
|
|
2142
2600
|
const seen = /* @__PURE__ */ new Map();
|
|
2143
2601
|
for (const issue of result.ecosystemIssues) {
|
|
@@ -2158,11 +2616,11 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI)
|
|
|
2158
2616
|
log.blank();
|
|
2159
2617
|
log.section("Next Steps");
|
|
2160
2618
|
if (result.totalIssues > 0) {
|
|
2161
|
-
log.info(`Run ${
|
|
2162
|
-
log.info(`Run ${
|
|
2619
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard baseline")} to save these issues and track only new ones`);
|
|
2620
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard report")} to generate a shareable HTML report`);
|
|
2163
2621
|
}
|
|
2164
2622
|
if (result.ecosystemIssues.length > 0) {
|
|
2165
|
-
log.info(`Run ${
|
|
2623
|
+
log.info(`Run ${import_chalk5.default.cyan("ai-guard doctor")} to diagnose ESLint config issues`);
|
|
2166
2624
|
}
|
|
2167
2625
|
log.blank();
|
|
2168
2626
|
process.exit(getFailOnExitCode(result, opts.failOn, opts.maxWarnings));
|
|
@@ -2172,14 +2630,14 @@ function getFailOnExitCode(result, failOn, maxWarnings) {
|
|
|
2172
2630
|
}
|
|
2173
2631
|
|
|
2174
2632
|
// cli/commands/init.ts
|
|
2175
|
-
var
|
|
2176
|
-
var
|
|
2177
|
-
var
|
|
2633
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
2634
|
+
var import_path7 = __toESM(require("path"));
|
|
2635
|
+
var import_fs8 = __toESM(require("fs"));
|
|
2178
2636
|
|
|
2179
2637
|
// cli/utils/detector.ts
|
|
2180
|
-
var
|
|
2181
|
-
var
|
|
2182
|
-
var
|
|
2638
|
+
var import_fs6 = __toESM(require("fs"));
|
|
2639
|
+
var import_path5 = __toESM(require("path"));
|
|
2640
|
+
var import_module2 = require("module");
|
|
2183
2641
|
var FLAT_CONFIG_FILES = [
|
|
2184
2642
|
{ file: "eslint.config.js", type: "flat-js" },
|
|
2185
2643
|
{ file: "eslint.config.mjs", type: "flat-mjs" },
|
|
@@ -2195,11 +2653,11 @@ var LEGACY_CONFIG_FILES = [
|
|
|
2195
2653
|
var ALL_CONFIG_FILES = [...FLAT_CONFIG_FILES, ...LEGACY_CONFIG_FILES];
|
|
2196
2654
|
function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
2197
2655
|
try {
|
|
2198
|
-
const pkgPath =
|
|
2199
|
-
if (
|
|
2656
|
+
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2657
|
+
if (import_fs6.default.existsSync(pkgPath)) {
|
|
2200
2658
|
return true;
|
|
2201
2659
|
}
|
|
2202
|
-
const req = (0,
|
|
2660
|
+
const req = (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json"));
|
|
2203
2661
|
req.resolve(`${pkgName}/package.json`);
|
|
2204
2662
|
return true;
|
|
2205
2663
|
} catch {
|
|
@@ -2208,9 +2666,9 @@ function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
|
2208
2666
|
}
|
|
2209
2667
|
function getPackageVersion(pkgName, cwd = process.cwd()) {
|
|
2210
2668
|
try {
|
|
2211
|
-
const pkgPath =
|
|
2212
|
-
const resolvedPath =
|
|
2213
|
-
const raw =
|
|
2669
|
+
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2670
|
+
const resolvedPath = import_fs6.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
|
|
2671
|
+
const raw = import_fs6.default.readFileSync(resolvedPath, "utf-8");
|
|
2214
2672
|
const json = JSON.parse(raw);
|
|
2215
2673
|
return json.version ?? null;
|
|
2216
2674
|
} catch {
|
|
@@ -2226,8 +2684,8 @@ function detectConfigType(cwd = process.cwd()) {
|
|
|
2226
2684
|
const found = [];
|
|
2227
2685
|
let first = null;
|
|
2228
2686
|
for (const { file, type } of ALL_CONFIG_FILES) {
|
|
2229
|
-
const fullPath =
|
|
2230
|
-
if (
|
|
2687
|
+
const fullPath = import_path5.default.join(cwd, file);
|
|
2688
|
+
if (import_fs6.default.existsSync(fullPath)) {
|
|
2231
2689
|
found.push(fullPath);
|
|
2232
2690
|
if (!first) first = { type, path: fullPath };
|
|
2233
2691
|
}
|
|
@@ -2250,7 +2708,7 @@ function requiresFlatConfig(eslintMajor) {
|
|
|
2250
2708
|
function detectNukeIgnore(configPath) {
|
|
2251
2709
|
if (!configPath) return false;
|
|
2252
2710
|
try {
|
|
2253
|
-
const content =
|
|
2711
|
+
const content = import_fs6.default.readFileSync(configPath, "utf-8");
|
|
2254
2712
|
return content.includes('"**/*"') || content.includes("'**/*'");
|
|
2255
2713
|
} catch {
|
|
2256
2714
|
return false;
|
|
@@ -2306,7 +2764,7 @@ async function verifyConfigLoads(configPath, cwd) {
|
|
|
2306
2764
|
overrideConfigFile: configPath
|
|
2307
2765
|
});
|
|
2308
2766
|
await eslint.lintText("", {
|
|
2309
|
-
filePath:
|
|
2767
|
+
filePath: import_path7.default.join(cwd, "_ai_guard_probe_.js")
|
|
2310
2768
|
});
|
|
2311
2769
|
return null;
|
|
2312
2770
|
} catch (err) {
|
|
@@ -2324,7 +2782,7 @@ function registerInitCommand(program2) {
|
|
|
2324
2782
|
const isDryRun = opts.dryRun === true;
|
|
2325
2783
|
log.banner("AI GUARD INIT");
|
|
2326
2784
|
if (isDryRun) {
|
|
2327
|
-
log.print(` ${
|
|
2785
|
+
log.print(` ${import_chalk6.default.yellow("\u2691 DRY RUN \u2014 no files will be written")}`);
|
|
2328
2786
|
log.blank();
|
|
2329
2787
|
}
|
|
2330
2788
|
log.info("Detecting your project setup\u2026");
|
|
@@ -2342,7 +2800,7 @@ function registerInitCommand(program2) {
|
|
|
2342
2800
|
log.warn("eslint-plugin-ai-guard not found");
|
|
2343
2801
|
}
|
|
2344
2802
|
if (env.configType !== "none") {
|
|
2345
|
-
log.success(`ESLint config found: ${
|
|
2803
|
+
log.success(`ESLint config found: ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath ?? env.configType))}`);
|
|
2346
2804
|
} else {
|
|
2347
2805
|
log.info("No ESLint config found \u2014 will generate one");
|
|
2348
2806
|
}
|
|
@@ -2351,9 +2809,9 @@ function registerInitCommand(program2) {
|
|
|
2351
2809
|
log.blank();
|
|
2352
2810
|
log.warn("Conflicting configs detected:");
|
|
2353
2811
|
for (const p of env.allConfigPaths) {
|
|
2354
|
-
log.print(` ${
|
|
2812
|
+
log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(import_path7.default.relative(cwd, p))}`);
|
|
2355
2813
|
}
|
|
2356
|
-
log.print(` ${
|
|
2814
|
+
log.print(` ${import_chalk6.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
|
|
2357
2815
|
}
|
|
2358
2816
|
log.blank();
|
|
2359
2817
|
const toInstall = [];
|
|
@@ -2364,22 +2822,22 @@ function registerInitCommand(program2) {
|
|
|
2364
2822
|
log.warn("The following packages are not installed:");
|
|
2365
2823
|
log.blank();
|
|
2366
2824
|
for (const pkg of toInstall) {
|
|
2367
|
-
log.print(` ${
|
|
2825
|
+
log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(pkg)}`);
|
|
2368
2826
|
}
|
|
2369
2827
|
log.blank();
|
|
2370
|
-
log.print(` ${
|
|
2828
|
+
log.print(` ${import_chalk6.default.bold("Run this first:")}`);
|
|
2371
2829
|
log.blank();
|
|
2372
|
-
log.print(` ${
|
|
2830
|
+
log.print(` ${import_chalk6.default.cyan(`npm install --save-dev ${toInstall.join(" ")}`)}`);
|
|
2373
2831
|
log.blank();
|
|
2374
|
-
log.print(` Then re-run ${
|
|
2832
|
+
log.print(` Then re-run ${import_chalk6.default.cyan("ai-guard init")} to complete setup.`);
|
|
2375
2833
|
log.blank();
|
|
2376
2834
|
process.exit(1);
|
|
2377
2835
|
return;
|
|
2378
2836
|
}
|
|
2379
2837
|
try {
|
|
2380
|
-
const { createRequire:
|
|
2838
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
2381
2839
|
const { pathToFileURL: pathToFileURL2 } = await import("url");
|
|
2382
|
-
const req =
|
|
2840
|
+
const req = createRequire3(import_path7.default.join(cwd, "_probe.js"));
|
|
2383
2841
|
const resolvedPath = req.resolve("eslint-plugin-ai-guard");
|
|
2384
2842
|
const aiGuard = await import(pathToFileURL2(resolvedPath).href);
|
|
2385
2843
|
const plugin = aiGuard.default || aiGuard;
|
|
@@ -2398,9 +2856,9 @@ function registerInitCommand(program2) {
|
|
|
2398
2856
|
if (eslintNeedsFlat && existingIsLegacy) {
|
|
2399
2857
|
log.section("Config Migration Required");
|
|
2400
2858
|
log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
|
|
2401
|
-
log.print(` ${
|
|
2859
|
+
log.print(` ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2402
2860
|
log.blank();
|
|
2403
|
-
log.print(` ${
|
|
2861
|
+
log.print(` ${import_chalk6.default.bold("ai-guard will create a new flat config.")}`);
|
|
2404
2862
|
log.blank();
|
|
2405
2863
|
}
|
|
2406
2864
|
log.section(`Configuring ESLint${isDryRun ? " (dry run)" : ""}`);
|
|
@@ -2410,34 +2868,34 @@ function registerInitCommand(program2) {
|
|
|
2410
2868
|
const raw = readConfig(env.configPath);
|
|
2411
2869
|
const { content: fixed, changed } = removeNukeIgnore(raw);
|
|
2412
2870
|
if (changed) {
|
|
2413
|
-
log.warn(`Removed "**/*" ignore pattern from ${
|
|
2871
|
+
log.warn(`Removed "**/*" ignore pattern from ${import_path7.default.relative(cwd, env.configPath)}`);
|
|
2414
2872
|
if (!isDryRun) {
|
|
2415
2873
|
backupConfig(env.configPath);
|
|
2416
2874
|
writeConfig(env.configPath, fixed);
|
|
2417
2875
|
log.success("Ignore pattern fixed");
|
|
2418
2876
|
} else {
|
|
2419
|
-
log.print(` ${
|
|
2877
|
+
log.print(` ${import_chalk6.default.gray("[dry-run] would remove nuke-ignore from existing config")}`);
|
|
2420
2878
|
}
|
|
2421
2879
|
}
|
|
2422
2880
|
}
|
|
2423
2881
|
if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
|
|
2424
2882
|
const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
|
|
2425
|
-
finalConfigPath =
|
|
2883
|
+
finalConfigPath = import_path7.default.join(cwd, targetName);
|
|
2426
2884
|
const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2427
2885
|
if (isDryRun) {
|
|
2428
|
-
log.print(` ${
|
|
2886
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would create: ${targetName}`)}`);
|
|
2429
2887
|
log.blank();
|
|
2430
|
-
log.print(
|
|
2888
|
+
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
2889
|
log.blank();
|
|
2432
2890
|
for (const line of content2.split("\n")) {
|
|
2433
|
-
log.print(
|
|
2891
|
+
log.print(import_chalk6.default.gray(` ${line}`));
|
|
2434
2892
|
}
|
|
2435
2893
|
log.blank();
|
|
2436
2894
|
} else {
|
|
2437
2895
|
writeConfig(finalConfigPath, content2);
|
|
2438
|
-
log.success(`Created ${
|
|
2439
|
-
log.info(` Preset: ${
|
|
2440
|
-
log.info(` Format: ${
|
|
2896
|
+
log.success(`Created ${import_chalk6.default.white(targetName)}`);
|
|
2897
|
+
log.info(` Preset: ${import_chalk6.default.cyan(preset)}`);
|
|
2898
|
+
log.info(` Format: ${import_chalk6.default.cyan(useFlat ? "ESLint v9 flat config (eslint.config.mjs)" : "ESLint v8 legacy config (.eslintrc.js)")}`);
|
|
2441
2899
|
configWritten = true;
|
|
2442
2900
|
}
|
|
2443
2901
|
} else if (useFlat && existingIsFlat) {
|
|
@@ -2450,15 +2908,15 @@ function registerInitCommand(program2) {
|
|
|
2450
2908
|
finalConfigPath = env.configPath;
|
|
2451
2909
|
} else if (isDryRun) {
|
|
2452
2910
|
finalConfigPath = env.configPath;
|
|
2453
|
-
log.print(` ${
|
|
2911
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
|
|
2454
2912
|
} else {
|
|
2455
2913
|
const bak = backupConfig(env.configPath);
|
|
2456
|
-
log.info(`Backed up \u2192 ${
|
|
2914
|
+
log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
|
|
2457
2915
|
writeConfig(env.configPath, patched);
|
|
2458
2916
|
if (isInvalid) {
|
|
2459
|
-
log.success(`Detected invalid ai-guard config \u2014 repairing ${
|
|
2917
|
+
log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2460
2918
|
} else {
|
|
2461
|
-
log.success(`Patched ${
|
|
2919
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2462
2920
|
}
|
|
2463
2921
|
finalConfigPath = env.configPath;
|
|
2464
2922
|
configWritten = true;
|
|
@@ -2471,12 +2929,12 @@ function registerInitCommand(program2) {
|
|
|
2471
2929
|
finalConfigPath = env.configPath;
|
|
2472
2930
|
} else if (isDryRun) {
|
|
2473
2931
|
finalConfigPath = env.configPath;
|
|
2474
|
-
log.print(` ${
|
|
2932
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
|
|
2475
2933
|
} else {
|
|
2476
2934
|
const bak = backupConfig(env.configPath);
|
|
2477
|
-
log.info(`Backed up \u2192 ${
|
|
2935
|
+
log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
|
|
2478
2936
|
writeConfig(env.configPath, patched);
|
|
2479
|
-
log.success(`Patched ${
|
|
2937
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
|
|
2480
2938
|
finalConfigPath = env.configPath;
|
|
2481
2939
|
configWritten = true;
|
|
2482
2940
|
}
|
|
@@ -2485,18 +2943,18 @@ function registerInitCommand(program2) {
|
|
|
2485
2943
|
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2486
2944
|
let cleanedUp = false;
|
|
2487
2945
|
for (const p of legacyConfigs) {
|
|
2488
|
-
if (
|
|
2946
|
+
if (import_fs8.default.existsSync(p)) {
|
|
2489
2947
|
if (!isDryRun) {
|
|
2490
2948
|
try {
|
|
2491
|
-
|
|
2949
|
+
import_fs8.default.renameSync(p, `${p}.bak`);
|
|
2492
2950
|
cleanedUp = true;
|
|
2493
2951
|
} catch (err) {
|
|
2494
2952
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2495
|
-
log.warn(`Could not back up ${
|
|
2953
|
+
log.warn(`Could not back up ${import_path7.default.relative(cwd, p)}: ${reason}`);
|
|
2496
2954
|
}
|
|
2497
2955
|
} else {
|
|
2498
2956
|
cleanedUp = true;
|
|
2499
|
-
log.print(` ${
|
|
2957
|
+
log.print(` ${import_chalk6.default.gray(`[dry-run] would rename ${import_path7.default.relative(cwd, p)} to ${import_path7.default.basename(p)}.bak`)}`);
|
|
2500
2958
|
}
|
|
2501
2959
|
}
|
|
2502
2960
|
}
|
|
@@ -2506,19 +2964,19 @@ function registerInitCommand(program2) {
|
|
|
2506
2964
|
}
|
|
2507
2965
|
if (isDryRun) {
|
|
2508
2966
|
log.blank();
|
|
2509
|
-
log.print(` ${
|
|
2510
|
-
log.print(` Re-run without ${
|
|
2967
|
+
log.print(` ${import_chalk6.default.yellow("Dry run complete \u2014 no files written.")}`);
|
|
2968
|
+
log.print(` Re-run without ${import_chalk6.default.cyan("--dry-run")} to apply.`);
|
|
2511
2969
|
log.blank();
|
|
2512
2970
|
process.exit(0);
|
|
2513
2971
|
return;
|
|
2514
2972
|
}
|
|
2515
2973
|
log.blank();
|
|
2516
2974
|
log.section("Validation");
|
|
2517
|
-
if (!
|
|
2518
|
-
log.error(`Config file not found after write: ${
|
|
2975
|
+
if (!import_fs8.default.existsSync(finalConfigPath)) {
|
|
2976
|
+
log.error(`Config file not found after write: ${import_path7.default.relative(cwd, finalConfigPath)}`);
|
|
2519
2977
|
log.blank();
|
|
2520
|
-
log.print(` ${
|
|
2521
|
-
log.print(` ${
|
|
2978
|
+
log.print(` ${import_chalk6.default.bold("Problem:")} File write may have failed.`);
|
|
2979
|
+
log.print(` ${import_chalk6.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
|
|
2522
2980
|
log.blank();
|
|
2523
2981
|
process.exit(1);
|
|
2524
2982
|
return;
|
|
@@ -2528,36 +2986,36 @@ function registerInitCommand(program2) {
|
|
|
2528
2986
|
if (validationErrors.length > 0) {
|
|
2529
2987
|
log.warn("Config validation found issues:");
|
|
2530
2988
|
for (const e of validationErrors) {
|
|
2531
|
-
log.print(` ${
|
|
2989
|
+
log.print(` ${import_chalk6.default.red("\u2716")} ${e}`);
|
|
2532
2990
|
}
|
|
2533
2991
|
} else {
|
|
2534
|
-
log.success(`Config structure valid: ${
|
|
2992
|
+
log.success(`Config structure valid: ${import_chalk6.default.white(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
2535
2993
|
}
|
|
2536
2994
|
log.info("Verifying ESLint can load the config\u2026");
|
|
2537
2995
|
const loadError = await verifyConfigLoads(finalConfigPath, cwd);
|
|
2538
2996
|
if (loadError !== null) {
|
|
2539
2997
|
log.blank();
|
|
2540
2998
|
if (loadError.kind === "module-not-found") {
|
|
2541
|
-
log.warn(`Could not verify: module not found: ${
|
|
2999
|
+
log.warn(`Could not verify: module not found: ${import_chalk6.default.yellow(loadError.pkg)}`);
|
|
2542
3000
|
log.blank();
|
|
2543
|
-
log.print(` ${
|
|
2544
|
-
log.print(` ${
|
|
2545
|
-
log.print(` Then run ${
|
|
3001
|
+
log.print(` ${import_chalk6.default.bold("Install the missing package:")}`);
|
|
3002
|
+
log.print(` ${import_chalk6.default.cyan(`npm install --save-dev ${loadError.pkg}`)}`);
|
|
3003
|
+
log.print(` Then run ${import_chalk6.default.cyan("ai-guard doctor")} to confirm.`);
|
|
2546
3004
|
log.blank();
|
|
2547
3005
|
} else {
|
|
2548
3006
|
log.error("ESLint failed to load the generated config.");
|
|
2549
3007
|
log.blank();
|
|
2550
|
-
log.print(` ${
|
|
2551
|
-
log.print(` ${
|
|
3008
|
+
log.print(` ${import_chalk6.default.bold("Problem:")}`);
|
|
3009
|
+
log.print(` ${import_chalk6.default.red(loadError.message)}`);
|
|
2552
3010
|
if (loadError.kind === "unknown") {
|
|
2553
3011
|
log.blank();
|
|
2554
|
-
log.print(` ${
|
|
3012
|
+
log.print(` ${import_chalk6.default.yellow("Generated config may be using incorrect plugin structure.")}`);
|
|
2555
3013
|
}
|
|
2556
3014
|
log.blank();
|
|
2557
|
-
log.print(` ${
|
|
2558
|
-
log.print(` 1. Check the syntax in ${
|
|
2559
|
-
log.print(` 2. Make sure ${
|
|
2560
|
-
log.print(` 3. Run ${
|
|
3015
|
+
log.print(` ${import_chalk6.default.bold("Fix:")}`);
|
|
3016
|
+
log.print(` 1. Check the syntax in ${import_chalk6.default.cyan(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
3017
|
+
log.print(` 2. Make sure ${import_chalk6.default.cyan("eslint-plugin-ai-guard")} is installed`);
|
|
3018
|
+
log.print(` 3. Run ${import_chalk6.default.cyan("ai-guard doctor")} for full diagnostics`);
|
|
2561
3019
|
log.blank();
|
|
2562
3020
|
process.exit(1);
|
|
2563
3021
|
return;
|
|
@@ -2571,34 +3029,34 @@ function registerInitCommand(program2) {
|
|
|
2571
3029
|
let cleanedUpInfo = false;
|
|
2572
3030
|
if (eslintNeedsFlat) {
|
|
2573
3031
|
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2574
|
-
cleanedUpInfo = legacyConfigs.some((p) =>
|
|
3032
|
+
cleanedUpInfo = legacyConfigs.some((p) => import_fs8.default.existsSync(`${p}.bak`) && !import_fs8.default.existsSync(p));
|
|
2575
3033
|
}
|
|
2576
3034
|
const lingeringConflicts = env.hasConflictingConfigs && !eslintNeedsFlat && !cleanedUpInfo;
|
|
2577
3035
|
if (loadError === null && !lingeringConflicts) {
|
|
2578
|
-
log.print(` ${
|
|
3036
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold.green("Configuration validated successfully")}`);
|
|
2579
3037
|
}
|
|
2580
|
-
log.print(` ${
|
|
2581
|
-
log.print(` ${
|
|
2582
|
-
log.print(` ${
|
|
3038
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("ESLint config:")} ${import_chalk6.default.cyan(import_path7.default.relative(cwd, finalConfigPath))}`);
|
|
3039
|
+
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})`)}`);
|
|
3040
|
+
log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("Preset:")} ${import_chalk6.default.cyan(preset)}`);
|
|
2583
3041
|
log.blank();
|
|
2584
|
-
log.info(`Run ${
|
|
2585
|
-
log.info(`Run ${
|
|
2586
|
-
log.info(`Run ${
|
|
2587
|
-
log.info(`Run ${
|
|
3042
|
+
log.info(`Run ${import_chalk6.default.cyan("npx eslint .")} \u2192 lint with your editor integration`);
|
|
3043
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard run")} \u2192 zero-config scan`);
|
|
3044
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard doctor")} \u2192 verify the full setup`);
|
|
3045
|
+
log.info(`Run ${import_chalk6.default.cyan("ai-guard baseline")} \u2192 save baseline, track only new issues`);
|
|
2588
3046
|
log.blank();
|
|
2589
3047
|
});
|
|
2590
3048
|
}
|
|
2591
3049
|
|
|
2592
3050
|
// cli/commands/doctor.ts
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
3051
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
3052
|
+
var import_path8 = __toESM(require("path"));
|
|
2595
3053
|
init_config_manager();
|
|
2596
|
-
var
|
|
3054
|
+
var import_meta2 = {};
|
|
2597
3055
|
async function probeConfigLoad(configPath, cwd) {
|
|
2598
3056
|
try {
|
|
2599
3057
|
const { ESLint } = await import("eslint");
|
|
2600
3058
|
const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
|
|
2601
|
-
await eslint.lintText("", { filePath:
|
|
3059
|
+
await eslint.lintText("", { filePath: import_path8.default.join(cwd, "_probe_.js") });
|
|
2602
3060
|
return { ok: true, error: null };
|
|
2603
3061
|
} catch (err) {
|
|
2604
3062
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2633,7 +3091,7 @@ function registerDoctorCommand(program2) {
|
|
|
2633
3091
|
checks.push({
|
|
2634
3092
|
label: "ESLint config present",
|
|
2635
3093
|
pass: hasConfig,
|
|
2636
|
-
detail: hasConfig ? `${
|
|
3094
|
+
detail: hasConfig ? `${import_path8.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
|
|
2637
3095
|
fix: hasConfig ? void 0 : "ai-guard init",
|
|
2638
3096
|
note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
|
|
2639
3097
|
});
|
|
@@ -2665,7 +3123,7 @@ function registerDoctorCommand(program2) {
|
|
|
2665
3123
|
checks.push({
|
|
2666
3124
|
label: "Plugin wired in config",
|
|
2667
3125
|
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 ${
|
|
3126
|
+
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
3127
|
fix: isInvalidConfig || !pluginWired && !readError ? "ai-guard init (will patch/repair your existing config)" : void 0
|
|
2670
3128
|
});
|
|
2671
3129
|
}
|
|
@@ -2673,7 +3131,7 @@ function registerDoctorCommand(program2) {
|
|
|
2673
3131
|
label: "No conflicting config files",
|
|
2674
3132
|
pass: !env.hasConflictingConfigs,
|
|
2675
3133
|
detail: env.hasConflictingConfigs ? `Conflicting config detected:
|
|
2676
|
-
${env.allConfigPaths.map((p) => ` ${
|
|
3134
|
+
${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
|
|
2677
3135
|
fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
|
|
2678
3136
|
note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
|
|
2679
3137
|
});
|
|
@@ -2700,7 +3158,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2700
3158
|
label: "ESLint config loads without errors",
|
|
2701
3159
|
pass: probe.ok,
|
|
2702
3160
|
detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
|
|
2703
|
-
fix: probe.ok ? void 0 : `Check syntax in ${
|
|
3161
|
+
fix: probe.ok ? void 0 : `Check syntax in ${import_path8.default.relative(cwd, env.configPath)}`,
|
|
2704
3162
|
note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
|
|
2705
3163
|
});
|
|
2706
3164
|
}
|
|
@@ -2708,8 +3166,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2708
3166
|
let utilsOk = false;
|
|
2709
3167
|
let utilsVersion = null;
|
|
2710
3168
|
try {
|
|
2711
|
-
const { createRequire:
|
|
2712
|
-
const req =
|
|
3169
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
3170
|
+
const req = createRequire3(import_path8.default.join(cwd, "package.json"));
|
|
2713
3171
|
const pkgPath = req.resolve("@typescript-eslint/utils/package.json");
|
|
2714
3172
|
const pkg = JSON.parse(
|
|
2715
3173
|
(await import("fs")).readFileSync(pkgPath, "utf-8")
|
|
@@ -2720,10 +3178,10 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2720
3178
|
}
|
|
2721
3179
|
if (!utilsOk) {
|
|
2722
3180
|
try {
|
|
2723
|
-
const selfDir =
|
|
2724
|
-
new URL(
|
|
3181
|
+
const selfDir = import_path8.default.dirname(
|
|
3182
|
+
new URL(import_meta2.url).pathname.replace(/^\/([A-Za-z]:)/, "$1")
|
|
2725
3183
|
);
|
|
2726
|
-
const pkgPath =
|
|
3184
|
+
const pkgPath = import_path8.default.join(selfDir, "../../node_modules/@typescript-eslint/utils/package.json");
|
|
2727
3185
|
if ((await import("fs")).existsSync(pkgPath)) {
|
|
2728
3186
|
utilsOk = true;
|
|
2729
3187
|
}
|
|
@@ -2742,8 +3200,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2742
3200
|
let parserOk = false;
|
|
2743
3201
|
let parserVersion = null;
|
|
2744
3202
|
try {
|
|
2745
|
-
const { createRequire:
|
|
2746
|
-
const req =
|
|
3203
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
3204
|
+
const req = createRequire3(import_path8.default.join(cwd, "package.json"));
|
|
2747
3205
|
const pkgPath = req.resolve("@typescript-eslint/parser/package.json");
|
|
2748
3206
|
const pkg = JSON.parse(
|
|
2749
3207
|
(await import("fs")).readFileSync(pkgPath, "utf-8")
|
|
@@ -2774,17 +3232,17 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2774
3232
|
log.section("Diagnostics");
|
|
2775
3233
|
let allPassed = true;
|
|
2776
3234
|
for (const check of checks) {
|
|
2777
|
-
const icon = check.pass ?
|
|
2778
|
-
const label = check.pass ?
|
|
3235
|
+
const icon = check.pass ? import_chalk7.default.green("\u2714") : import_chalk7.default.red("\u2716");
|
|
3236
|
+
const label = check.pass ? import_chalk7.default.white(check.label) : import_chalk7.default.red.bold(check.label);
|
|
2779
3237
|
log.print(` ${icon} ${label}`);
|
|
2780
|
-
log.print(` ${
|
|
3238
|
+
log.print(` ${import_chalk7.default.gray(check.detail)}`);
|
|
2781
3239
|
if (!check.pass) {
|
|
2782
3240
|
allPassed = false;
|
|
2783
3241
|
if (check.fix) {
|
|
2784
|
-
log.print(` ${
|
|
3242
|
+
log.print(` ${import_chalk7.default.cyan("\u2192 Fix:")} ${import_chalk7.default.yellow(check.fix)}`);
|
|
2785
3243
|
}
|
|
2786
3244
|
if (check.note) {
|
|
2787
|
-
log.print(` ${
|
|
3245
|
+
log.print(` ${import_chalk7.default.gray(check.note)}`);
|
|
2788
3246
|
}
|
|
2789
3247
|
}
|
|
2790
3248
|
log.blank();
|
|
@@ -2792,10 +3250,10 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2792
3250
|
log.divider();
|
|
2793
3251
|
log.blank();
|
|
2794
3252
|
if (allPassed) {
|
|
2795
|
-
log.print(` ${
|
|
3253
|
+
log.print(` ${import_chalk7.default.green("\u2714")} ${import_chalk7.default.bold.green("All checks passed! Your setup is correct.")}`);
|
|
2796
3254
|
log.blank();
|
|
2797
|
-
log.info(`Run ${
|
|
2798
|
-
log.info(`Run ${
|
|
3255
|
+
log.info(`Run ${import_chalk7.default.cyan("ai-guard run")} to start scanning.`);
|
|
3256
|
+
log.info(`Run ${import_chalk7.default.cyan("npx eslint .")} to lint with ESLint directly.`);
|
|
2799
3257
|
} else {
|
|
2800
3258
|
log.error("Some checks failed. Follow the fixes above.");
|
|
2801
3259
|
log.blank();
|
|
@@ -2810,15 +3268,15 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2810
3268
|
);
|
|
2811
3269
|
if (pluginMissing) {
|
|
2812
3270
|
log.print(
|
|
2813
|
-
` Install missing packages first, then re-run ${
|
|
3271
|
+
` Install missing packages first, then re-run ${import_chalk7.default.cyan("ai-guard doctor")}.`
|
|
2814
3272
|
);
|
|
2815
3273
|
} else if (configMissing || configFormatMismatch) {
|
|
2816
3274
|
log.print(
|
|
2817
|
-
` Run ${
|
|
3275
|
+
` Run ${import_chalk7.default.cyan("ai-guard init")} to generate the correct config for your ESLint version.`
|
|
2818
3276
|
);
|
|
2819
3277
|
} else {
|
|
2820
3278
|
log.print(
|
|
2821
|
-
` Run ${
|
|
3279
|
+
` Run ${import_chalk7.default.cyan("ai-guard init --dry-run")} to preview what would change.`
|
|
2822
3280
|
);
|
|
2823
3281
|
}
|
|
2824
3282
|
}
|
|
@@ -2829,9 +3287,9 @@ ${env.allConfigPaths.map((p) => ` ${import_path7.default.relative(cwd, p)}
|
|
|
2829
3287
|
|
|
2830
3288
|
// cli/commands/preset.ts
|
|
2831
3289
|
var import_prompts = require("@inquirer/prompts");
|
|
2832
|
-
var
|
|
3290
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
2833
3291
|
init_config_manager();
|
|
2834
|
-
var
|
|
3292
|
+
var import_path9 = __toESM(require("path"));
|
|
2835
3293
|
function registerPresetCommand(program2) {
|
|
2836
3294
|
program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
|
|
2837
3295
|
const cwd = process.cwd();
|
|
@@ -2841,32 +3299,32 @@ function registerPresetCommand(program2) {
|
|
|
2841
3299
|
message: "Choose a preset:",
|
|
2842
3300
|
choices: [
|
|
2843
3301
|
{
|
|
2844
|
-
name: `${
|
|
3302
|
+
name: `${import_chalk8.default.green("recommended")} \u2014 balanced defaults, low noise. Best starting point.`,
|
|
2845
3303
|
value: "recommended"
|
|
2846
3304
|
},
|
|
2847
3305
|
{
|
|
2848
|
-
name: `${
|
|
3306
|
+
name: `${import_chalk8.default.yellow("strict")} \u2014 all rules at error. For mature codebases.`,
|
|
2849
3307
|
value: "strict"
|
|
2850
3308
|
},
|
|
2851
3309
|
{
|
|
2852
|
-
name: `${
|
|
3310
|
+
name: `${import_chalk8.default.red("security")} \u2014 security rules only. For AppSec teams.`,
|
|
2853
3311
|
value: "security"
|
|
2854
3312
|
}
|
|
2855
3313
|
]
|
|
2856
3314
|
});
|
|
2857
3315
|
log.blank();
|
|
2858
|
-
log.info(`Applying preset: ${
|
|
3316
|
+
log.info(`Applying preset: ${import_chalk8.default.cyan(preset)}`);
|
|
2859
3317
|
const env = detect(cwd);
|
|
2860
3318
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
|
|
2861
3319
|
if (env.configType === "none") {
|
|
2862
|
-
const configPath = useFlat ?
|
|
3320
|
+
const configPath = useFlat ? import_path9.default.join(cwd, "eslint.config.mjs") : import_path9.default.join(cwd, ".eslintrc.js");
|
|
2863
3321
|
const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2864
3322
|
writeConfig(configPath, content);
|
|
2865
|
-
log.success(`Created ${
|
|
3323
|
+
log.success(`Created ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} with ${import_chalk8.default.cyan(preset)} preset`);
|
|
2866
3324
|
} else {
|
|
2867
3325
|
const configPath = env.configPath;
|
|
2868
3326
|
const backupPath = backupConfig(configPath);
|
|
2869
|
-
log.info(`Backed up \u2192 ${
|
|
3327
|
+
log.info(`Backed up \u2192 ${import_chalk8.default.gray(import_path9.default.relative(cwd, backupPath))}`);
|
|
2870
3328
|
const existing = readConfig(configPath);
|
|
2871
3329
|
const withPlugin = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
|
|
2872
3330
|
const patched = isFlat(env.configType) ? switchFlatPreset(withPlugin, preset) : switchLegacyPreset(withPlugin, preset);
|
|
@@ -2874,47 +3332,47 @@ function registerPresetCommand(program2) {
|
|
|
2874
3332
|
log.warn("No preset changes needed. Config already matches selected preset.");
|
|
2875
3333
|
} else {
|
|
2876
3334
|
writeConfig(configPath, patched);
|
|
2877
|
-
log.success(`Updated ${
|
|
3335
|
+
log.success(`Updated ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} to ${import_chalk8.default.cyan(preset)} preset`);
|
|
2878
3336
|
}
|
|
2879
3337
|
}
|
|
2880
3338
|
log.blank();
|
|
2881
3339
|
log.section("Preset Details");
|
|
2882
3340
|
const presetDetails = {
|
|
2883
3341
|
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:
|
|
3342
|
+
{ rule: "no-empty-catch", level: import_chalk8.default.red("error") },
|
|
3343
|
+
{ rule: "no-floating-promise", level: import_chalk8.default.red("error") },
|
|
3344
|
+
{ rule: "no-hardcoded-secret", level: import_chalk8.default.red("error") },
|
|
3345
|
+
{ rule: "no-eval-dynamic", level: import_chalk8.default.red("error") },
|
|
3346
|
+
{ rule: "no-broad-exception", level: import_chalk8.default.yellow("warn") },
|
|
3347
|
+
{ rule: "no-async-array-callback", level: import_chalk8.default.yellow("warn") },
|
|
3348
|
+
{ rule: "no-await-in-loop", level: import_chalk8.default.yellow("warn") },
|
|
3349
|
+
{ rule: "+ 5 more\u2026", level: import_chalk8.default.gray("warn") }
|
|
2892
3350
|
],
|
|
2893
3351
|
strict: [
|
|
2894
|
-
{ rule: "All 17 rules", level:
|
|
3352
|
+
{ rule: "All 17 rules", level: import_chalk8.default.red("error") }
|
|
2895
3353
|
],
|
|
2896
3354
|
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:
|
|
3355
|
+
{ rule: "no-hardcoded-secret", level: import_chalk8.default.red("error") },
|
|
3356
|
+
{ rule: "no-eval-dynamic", level: import_chalk8.default.red("error") },
|
|
3357
|
+
{ rule: "no-sql-string-concat", level: import_chalk8.default.red("error") },
|
|
3358
|
+
{ rule: "no-unsafe-deserialize", level: import_chalk8.default.yellow("warn") },
|
|
3359
|
+
{ rule: "require-auth-middleware", level: import_chalk8.default.yellow("warn") },
|
|
3360
|
+
{ rule: "require-authz-check", level: import_chalk8.default.yellow("warn") }
|
|
2903
3361
|
]
|
|
2904
3362
|
};
|
|
2905
3363
|
for (const { rule, level } of presetDetails[preset]) {
|
|
2906
|
-
log.print(` ${
|
|
3364
|
+
log.print(` ${import_chalk8.default.gray("\u2022")} ${import_chalk8.default.white(rule).padEnd(32)}${level}`);
|
|
2907
3365
|
}
|
|
2908
3366
|
log.blank();
|
|
2909
|
-
log.info(`Run ${
|
|
3367
|
+
log.info(`Run ${import_chalk8.default.cyan("ai-guard run")} to see results with the new preset.`);
|
|
2910
3368
|
log.blank();
|
|
2911
3369
|
});
|
|
2912
3370
|
}
|
|
2913
3371
|
|
|
2914
3372
|
// cli/commands/ignore.ts
|
|
2915
|
-
var
|
|
3373
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
2916
3374
|
init_config_manager();
|
|
2917
|
-
var
|
|
3375
|
+
var import_path10 = __toESM(require("path"));
|
|
2918
3376
|
var DEFAULT_IGNORES = [
|
|
2919
3377
|
".next/",
|
|
2920
3378
|
"dist/",
|
|
@@ -2930,40 +3388,40 @@ function registerIgnoreCommand(program2) {
|
|
|
2930
3388
|
log.blank();
|
|
2931
3389
|
log.section("Default Ignores");
|
|
2932
3390
|
for (const p of DEFAULT_IGNORES) {
|
|
2933
|
-
log.print(` ${
|
|
3391
|
+
log.print(` ${import_chalk9.default.gray("\u2022")} ${import_chalk9.default.yellow(p)}`);
|
|
2934
3392
|
}
|
|
2935
3393
|
log.blank();
|
|
2936
3394
|
const env = detect(cwd);
|
|
2937
3395
|
if (env.configType === "none") {
|
|
2938
3396
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
|
|
2939
|
-
const configPath = useFlat ?
|
|
3397
|
+
const configPath = useFlat ? import_path10.default.join(cwd, "eslint.config.mjs") : import_path10.default.join(cwd, ".eslintrc.js");
|
|
2940
3398
|
const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
|
|
2941
3399
|
writeConfig(configPath, content);
|
|
2942
|
-
log.success(`No config found \u2014 generated ${
|
|
3400
|
+
log.success(`No config found \u2014 generated ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignores included`);
|
|
2943
3401
|
} else {
|
|
2944
3402
|
const configPath = env.configPath;
|
|
2945
3403
|
const backupPath = backupConfig(configPath);
|
|
2946
|
-
log.info(`Backed up \u2192 ${
|
|
3404
|
+
log.info(`Backed up \u2192 ${import_chalk9.default.gray(import_path10.default.relative(cwd, backupPath))}`);
|
|
2947
3405
|
const existing = readConfig(configPath);
|
|
2948
3406
|
const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
|
|
2949
3407
|
if (patched === existing) {
|
|
2950
3408
|
log.warn("Ignore patterns already present in config \u2014 no changes made.");
|
|
2951
3409
|
} else {
|
|
2952
3410
|
writeConfig(configPath, patched);
|
|
2953
|
-
log.success(`Patched ${
|
|
3411
|
+
log.success(`Patched ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignore patterns`);
|
|
2954
3412
|
}
|
|
2955
3413
|
}
|
|
2956
3414
|
log.blank();
|
|
2957
3415
|
log.info("Noisy directories will now be skipped in all future ESLint runs.");
|
|
2958
|
-
log.info(`Run ${
|
|
3416
|
+
log.info(`Run ${import_chalk9.default.cyan("ai-guard run")} to verify cleaner results.`);
|
|
2959
3417
|
log.blank();
|
|
2960
3418
|
});
|
|
2961
3419
|
}
|
|
2962
3420
|
|
|
2963
3421
|
// cli/commands/baseline.ts
|
|
2964
|
-
var
|
|
2965
|
-
var
|
|
2966
|
-
var
|
|
3422
|
+
var import_fs9 = __toESM(require("fs"));
|
|
3423
|
+
var import_path11 = __toESM(require("path"));
|
|
3424
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
2967
3425
|
var import_ora3 = __toESM(require("ora"));
|
|
2968
3426
|
var BASELINE_FILE = ".ai-guard-baseline.json";
|
|
2969
3427
|
function saveBaseline(result, preset, mode, cwd) {
|
|
@@ -2983,15 +3441,15 @@ function saveBaseline(result, preset, mode, cwd) {
|
|
|
2983
3441
|
totalIssues: result.totalIssues,
|
|
2984
3442
|
entries
|
|
2985
3443
|
};
|
|
2986
|
-
const baselinePath =
|
|
2987
|
-
|
|
3444
|
+
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
3445
|
+
import_fs9.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
2988
3446
|
return baselinePath;
|
|
2989
3447
|
}
|
|
2990
3448
|
function loadBaseline(cwd) {
|
|
2991
|
-
const baselinePath =
|
|
2992
|
-
if (!
|
|
3449
|
+
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
3450
|
+
if (!import_fs9.default.existsSync(baselinePath)) return null;
|
|
2993
3451
|
try {
|
|
2994
|
-
const raw =
|
|
3452
|
+
const raw = import_fs9.default.readFileSync(baselinePath, "utf-8");
|
|
2995
3453
|
return JSON.parse(raw);
|
|
2996
3454
|
} catch {
|
|
2997
3455
|
return null;
|
|
@@ -3075,28 +3533,28 @@ function registerBaselineCommand(program2) {
|
|
|
3075
3533
|
}
|
|
3076
3534
|
const baselinePath = saveBaseline(result, preset, mode, cwd);
|
|
3077
3535
|
log.success(
|
|
3078
|
-
`Baseline saved \u2192 ${
|
|
3536
|
+
`Baseline saved \u2192 ${import_chalk10.default.white(import_path11.default.relative(cwd, baselinePath))}`
|
|
3079
3537
|
);
|
|
3080
3538
|
log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
|
|
3081
|
-
log.info(` Preset: ${
|
|
3082
|
-
log.info(` Mode: ${
|
|
3539
|
+
log.info(` Preset: ${import_chalk10.default.cyan(preset)}`);
|
|
3540
|
+
log.info(` Mode: ${import_chalk10.default.cyan(mode)}`);
|
|
3083
3541
|
log.blank();
|
|
3084
|
-
log.info("Future runs of " +
|
|
3085
|
-
log.info("Add " +
|
|
3542
|
+
log.info("Future runs of " + import_chalk10.default.cyan("ai-guard baseline --check") + " will show only NEW issues.");
|
|
3543
|
+
log.info("Add " + import_chalk10.default.white(BASELINE_FILE) + " to your git repository to share baseline with your team.");
|
|
3086
3544
|
log.blank();
|
|
3087
3545
|
return;
|
|
3088
3546
|
}
|
|
3089
3547
|
if (doCheck) {
|
|
3090
3548
|
if (!existingBaseline) {
|
|
3091
|
-
log.error(`No baseline found at ${
|
|
3092
|
-
log.info(`Run ${
|
|
3549
|
+
log.error(`No baseline found at ${import_chalk10.default.white(BASELINE_FILE)}`);
|
|
3550
|
+
log.info(`Run ${import_chalk10.default.cyan("ai-guard baseline --save")} first to create one.`);
|
|
3093
3551
|
process.exit(1);
|
|
3094
3552
|
}
|
|
3095
3553
|
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: ${
|
|
3554
|
+
log.info(`Baseline from: ${import_chalk10.default.gray(baselineDate)}`);
|
|
3555
|
+
log.info(`Baseline preset: ${import_chalk10.default.cyan(existingBaseline.preset)}`);
|
|
3556
|
+
log.info(`Baseline mode: ${import_chalk10.default.cyan(existingBaseline.mode ?? "strict")}`);
|
|
3557
|
+
log.info(`Baseline issues: ${import_chalk10.default.gray(existingBaseline.totalIssues)}`);
|
|
3100
3558
|
log.blank();
|
|
3101
3559
|
const spinner = (0, import_ora3.default)({ text: "Scanning for new issues\u2026", color: "cyan" }).start();
|
|
3102
3560
|
let result;
|
|
@@ -3121,7 +3579,7 @@ function registerBaselineCommand(program2) {
|
|
|
3121
3579
|
return;
|
|
3122
3580
|
}
|
|
3123
3581
|
log.print(
|
|
3124
|
-
|
|
3582
|
+
import_chalk10.default.bold.red(` \u2716 ${newResult.totalIssues} new issue${newResult.totalIssues !== 1 ? "s" : ""} since baseline`)
|
|
3125
3583
|
);
|
|
3126
3584
|
log.blank();
|
|
3127
3585
|
if (newResult.ruleBreakdown.size > 0) {
|
|
@@ -3137,7 +3595,7 @@ function registerBaselineCommand(program2) {
|
|
|
3137
3595
|
log.blank();
|
|
3138
3596
|
for (const file of newResult.files) {
|
|
3139
3597
|
log.print(
|
|
3140
|
-
` ${
|
|
3598
|
+
` ${import_chalk10.default.bold.white(file.filePath)} ` + import_chalk10.default.gray(`(${file.issues.length} new)`)
|
|
3141
3599
|
);
|
|
3142
3600
|
for (const issue of file.issues) {
|
|
3143
3601
|
log.issue(issue.message, issue.severity, issue.line, issue.column);
|
|
@@ -3150,7 +3608,7 @@ function registerBaselineCommand(program2) {
|
|
|
3150
3608
|
log.error(
|
|
3151
3609
|
`${newResult.totalIssues} new issue${newResult.totalIssues !== 1 ? "s" : ""} introduced since baseline.`
|
|
3152
3610
|
);
|
|
3153
|
-
log.info(`Run ${
|
|
3611
|
+
log.info(`Run ${import_chalk10.default.cyan("ai-guard baseline --save")} to update the baseline after fixing them.`);
|
|
3154
3612
|
log.blank();
|
|
3155
3613
|
process.exit(1);
|
|
3156
3614
|
}
|
|
@@ -3158,9 +3616,9 @@ function registerBaselineCommand(program2) {
|
|
|
3158
3616
|
}
|
|
3159
3617
|
|
|
3160
3618
|
// cli/commands/init-context.ts
|
|
3161
|
-
var
|
|
3162
|
-
var
|
|
3163
|
-
var
|
|
3619
|
+
var import_fs10 = __toESM(require("fs"));
|
|
3620
|
+
var import_path12 = __toESM(require("path"));
|
|
3621
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
3164
3622
|
var import_prompts2 = require("@inquirer/prompts");
|
|
3165
3623
|
var RULE_CATEGORIES = {
|
|
3166
3624
|
"error-handling": [
|
|
@@ -3523,23 +3981,23 @@ var AGENTS = [
|
|
|
3523
3981
|
{
|
|
3524
3982
|
id: "copilot",
|
|
3525
3983
|
label: "GitHub Copilot",
|
|
3526
|
-
filePath:
|
|
3984
|
+
filePath: import_path12.default.join(".github", "copilot-instructions.md"),
|
|
3527
3985
|
generate: generateCopilotFile
|
|
3528
3986
|
}
|
|
3529
3987
|
];
|
|
3530
3988
|
function getPackageVersion2(cwd) {
|
|
3531
3989
|
try {
|
|
3532
|
-
const pkgPath =
|
|
3533
|
-
if (
|
|
3534
|
-
const pkg = JSON.parse(
|
|
3990
|
+
const pkgPath = import_path12.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
3991
|
+
if (import_fs10.default.existsSync(pkgPath)) {
|
|
3992
|
+
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3535
3993
|
if (pkg.version) return pkg.version;
|
|
3536
3994
|
}
|
|
3537
3995
|
} catch {
|
|
3538
3996
|
}
|
|
3539
3997
|
try {
|
|
3540
|
-
const pkgPath =
|
|
3541
|
-
if (
|
|
3542
|
-
const pkg = JSON.parse(
|
|
3998
|
+
const pkgPath = import_path12.default.join(cwd, "package.json");
|
|
3999
|
+
if (import_fs10.default.existsSync(pkgPath)) {
|
|
4000
|
+
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3543
4001
|
if (pkg.version) return pkg.version;
|
|
3544
4002
|
}
|
|
3545
4003
|
} catch {
|
|
@@ -3568,7 +4026,7 @@ function registerInitContextCommand(program2) {
|
|
|
3568
4026
|
const isForce = opts.force === true;
|
|
3569
4027
|
log.banner("AI GUARD INIT-CONTEXT");
|
|
3570
4028
|
if (isDryRun) {
|
|
3571
|
-
log.print(` ${
|
|
4029
|
+
log.print(` ${import_chalk11.default.yellow("\u2691 DRY RUN \u2014 no files will be written")}`);
|
|
3572
4030
|
log.blank();
|
|
3573
4031
|
}
|
|
3574
4032
|
let selectedAgents;
|
|
@@ -3601,13 +4059,13 @@ function registerInitContextCommand(program2) {
|
|
|
3601
4059
|
selectedAgents = AGENTS.filter((a) => selected.includes(a.id));
|
|
3602
4060
|
}
|
|
3603
4061
|
}
|
|
3604
|
-
log.info(`Categories: ${
|
|
3605
|
-
log.info(`Version: ${
|
|
4062
|
+
log.info(`Categories: ${import_chalk11.default.cyan(categories.join(", "))}`);
|
|
4063
|
+
log.info(`Version: ${import_chalk11.default.cyan(version)}`);
|
|
3606
4064
|
log.blank();
|
|
3607
4065
|
const results = [];
|
|
3608
4066
|
for (const agent of selectedAgents) {
|
|
3609
|
-
const fullPath =
|
|
3610
|
-
const exists =
|
|
4067
|
+
const fullPath = import_path12.default.join(cwd, agent.filePath);
|
|
4068
|
+
const exists = import_fs10.default.existsSync(fullPath);
|
|
3611
4069
|
if (exists && !isForce && !isDryRun) {
|
|
3612
4070
|
const overwrite = await (0, import_prompts2.confirm)({
|
|
3613
4071
|
message: `${agent.filePath} already exists. Overwrite?`,
|
|
@@ -3621,41 +4079,41 @@ function registerInitContextCommand(program2) {
|
|
|
3621
4079
|
const content = agent.generate(version, categories);
|
|
3622
4080
|
if (isDryRun) {
|
|
3623
4081
|
log.section(`Preview: ${agent.filePath}`);
|
|
3624
|
-
log.print(
|
|
4082
|
+
log.print(import_chalk11.default.gray("\u2500".repeat(60)));
|
|
3625
4083
|
for (const line of content.split("\n").slice(0, 20)) {
|
|
3626
|
-
log.print(
|
|
4084
|
+
log.print(import_chalk11.default.gray(` ${line}`));
|
|
3627
4085
|
}
|
|
3628
|
-
log.print(
|
|
3629
|
-
log.print(
|
|
4086
|
+
log.print(import_chalk11.default.gray(" ... (truncated)"));
|
|
4087
|
+
log.print(import_chalk11.default.gray("\u2500".repeat(60)));
|
|
3630
4088
|
log.blank();
|
|
3631
4089
|
results.push({ file: agent.filePath, status: "dry-run" });
|
|
3632
4090
|
continue;
|
|
3633
4091
|
}
|
|
3634
|
-
const parentDir =
|
|
3635
|
-
if (!
|
|
3636
|
-
|
|
4092
|
+
const parentDir = import_path12.default.dirname(fullPath);
|
|
4093
|
+
if (!import_fs10.default.existsSync(parentDir)) {
|
|
4094
|
+
import_fs10.default.mkdirSync(parentDir, { recursive: true });
|
|
3637
4095
|
}
|
|
3638
|
-
|
|
4096
|
+
import_fs10.default.writeFileSync(fullPath, content, "utf-8");
|
|
3639
4097
|
results.push({ file: agent.filePath, status: "created" });
|
|
3640
4098
|
}
|
|
3641
4099
|
log.section("Summary");
|
|
3642
4100
|
for (const result of results) {
|
|
3643
4101
|
switch (result.status) {
|
|
3644
4102
|
case "created":
|
|
3645
|
-
log.print(` ${
|
|
4103
|
+
log.print(` ${import_chalk11.default.green("\u2705")} Created ${import_chalk11.default.white(result.file)}`);
|
|
3646
4104
|
break;
|
|
3647
4105
|
case "skipped":
|
|
3648
|
-
log.print(` ${
|
|
4106
|
+
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
4107
|
break;
|
|
3650
4108
|
case "dry-run":
|
|
3651
|
-
log.print(` ${
|
|
4109
|
+
log.print(` ${import_chalk11.default.cyan("\u{1F4DD}")} Would create ${import_chalk11.default.white(result.file)}`);
|
|
3652
4110
|
break;
|
|
3653
4111
|
}
|
|
3654
4112
|
}
|
|
3655
4113
|
log.blank();
|
|
3656
4114
|
if (isDryRun) {
|
|
3657
4115
|
log.info("Dry run complete \u2014 no files were written.");
|
|
3658
|
-
log.info(`Re-run without ${
|
|
4116
|
+
log.info(`Re-run without ${import_chalk11.default.cyan("--dry-run")} to apply.`);
|
|
3659
4117
|
} else {
|
|
3660
4118
|
const createdCount = results.filter((r) => r.status === "created").length;
|
|
3661
4119
|
if (createdCount > 0) {
|
|
@@ -3669,8 +4127,8 @@ function registerInitContextCommand(program2) {
|
|
|
3669
4127
|
}
|
|
3670
4128
|
|
|
3671
4129
|
// cli/commands/report.ts
|
|
3672
|
-
var
|
|
3673
|
-
var
|
|
4130
|
+
var import_fs11 = __toESM(require("fs"));
|
|
4131
|
+
var import_path13 = __toESM(require("path"));
|
|
3674
4132
|
var import_ora4 = __toESM(require("ora"));
|
|
3675
4133
|
var RULE_META = {
|
|
3676
4134
|
"ai-guard/no-empty-catch": { description: "Empty catch blocks silently swallow errors", category: "Reliability" },
|
|
@@ -3694,28 +4152,28 @@ var RULE_META = {
|
|
|
3694
4152
|
};
|
|
3695
4153
|
function getProjectName(cwd) {
|
|
3696
4154
|
try {
|
|
3697
|
-
const pkgPath =
|
|
3698
|
-
if (
|
|
3699
|
-
const pkg = JSON.parse(
|
|
4155
|
+
const pkgPath = import_path13.default.join(cwd, "package.json");
|
|
4156
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4157
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
3700
4158
|
if (pkg.name) return pkg.name;
|
|
3701
4159
|
}
|
|
3702
4160
|
} catch {
|
|
3703
4161
|
}
|
|
3704
|
-
return
|
|
4162
|
+
return import_path13.default.basename(cwd);
|
|
3705
4163
|
}
|
|
3706
4164
|
function getPluginVersion(cwd) {
|
|
3707
4165
|
try {
|
|
3708
|
-
const pkgPath =
|
|
3709
|
-
if (
|
|
3710
|
-
const pkg = JSON.parse(
|
|
4166
|
+
const pkgPath = import_path13.default.resolve(__dirname, "../../package.json");
|
|
4167
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4168
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
3711
4169
|
if (pkg.version) return pkg.version;
|
|
3712
4170
|
}
|
|
3713
4171
|
} catch {
|
|
3714
4172
|
}
|
|
3715
4173
|
try {
|
|
3716
|
-
const pkgPath =
|
|
3717
|
-
if (
|
|
3718
|
-
const pkg = JSON.parse(
|
|
4174
|
+
const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
4175
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4176
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
3719
4177
|
if (pkg.version) return pkg.version;
|
|
3720
4178
|
}
|
|
3721
4179
|
} catch {
|
|
@@ -4092,8 +4550,8 @@ function registerReportCommand(program2) {
|
|
|
4092
4550
|
log.banner("AI GUARD REPORT");
|
|
4093
4551
|
log.blank();
|
|
4094
4552
|
const html = generateHtml(reportData);
|
|
4095
|
-
const outputPath =
|
|
4096
|
-
|
|
4553
|
+
const outputPath = import_path13.default.resolve(cwd, opts.output);
|
|
4554
|
+
import_fs11.default.writeFileSync(outputPath, html, "utf-8");
|
|
4097
4555
|
log.success(`Report saved: ${opts.output}`);
|
|
4098
4556
|
log.info(
|
|
4099
4557
|
`Score: ${reportData.score}/100 \xB7 ${reportData.totalErrors} errors \xB7 ${reportData.totalWarnings} warnings`
|
|
@@ -4126,8 +4584,8 @@ function registerReportCommand(program2) {
|
|
|
4126
4584
|
// cli/index.ts
|
|
4127
4585
|
function getVersion() {
|
|
4128
4586
|
try {
|
|
4129
|
-
const pkgPath =
|
|
4130
|
-
const raw =
|
|
4587
|
+
const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
|
|
4588
|
+
const raw = import_fs12.default.readFileSync(pkgPath, "utf-8");
|
|
4131
4589
|
const pkg = JSON.parse(raw);
|
|
4132
4590
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
4133
4591
|
} catch {
|