eslint-plugin-ai-guard 1.2.2 → 1.2.4

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 CHANGED
@@ -201,14 +201,14 @@ __export(config_manager_exports, {
201
201
  });
202
202
  function backupConfig(configPath) {
203
203
  const backupPath = `${configPath}.bak`;
204
- import_fs6.default.copyFileSync(configPath, backupPath);
204
+ import_fs7.default.copyFileSync(configPath, backupPath);
205
205
  return backupPath;
206
206
  }
207
207
  function readConfig(configPath) {
208
- return import_fs6.default.readFileSync(configPath, "utf-8");
208
+ return import_fs7.default.readFileSync(configPath, "utf-8");
209
209
  }
210
210
  function writeConfig(configPath, content) {
211
- import_fs6.default.writeFileSync(configPath, content, "utf-8");
211
+ import_fs7.default.writeFileSync(configPath, content, "utf-8");
212
212
  }
213
213
  function generateFlatConfig(preset) {
214
214
  return `import aiGuard from 'eslint-plugin-ai-guard';
@@ -461,11 +461,11 @@ function validateLegacyConfigText(content) {
461
461
  }
462
462
  return problems;
463
463
  }
464
- var import_fs6, import_path6, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
464
+ var import_fs7, import_path6, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
465
465
  var init_config_manager = __esm({
466
466
  "cli/utils/config-manager.ts"() {
467
467
  "use strict";
468
- import_fs6 = __toESM(require("fs"));
468
+ import_fs7 = __toESM(require("fs"));
469
469
  import_path6 = __toESM(require("path"));
470
470
  AI_GUARD_MARKER = "eslint-plugin-ai-guard";
471
471
  DEFAULT_FLAT_IGNORES = [
@@ -489,10 +489,11 @@ var init_config_manager = __esm({
489
489
 
490
490
  // cli/index.ts
491
491
  var import_commander = require("commander");
492
- var import_fs11 = __toESM(require("fs"));
492
+ var import_fs12 = __toESM(require("fs"));
493
493
  var import_path14 = __toESM(require("path"));
494
494
 
495
495
  // cli/commands/run.ts
496
+ var import_fs4 = __toESM(require("fs"));
496
497
  var import_ora = __toESM(require("ora"));
497
498
  var import_chalk3 = __toESM(require("chalk"));
498
499
 
@@ -1250,6 +1251,80 @@ var RULE_DOCS = {
1250
1251
  "ai-guard/no-dead-branch": { shortDesc: "Dead code branch that can never execute", tags: ["ai-patterns"] }
1251
1252
  };
1252
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
+ const confidence = props.confidence ?? "low";
1263
+ return {
1264
+ tags: sanitizeSarifTags(props.tags),
1265
+ confidence,
1266
+ category: props.category ?? "AI Patterns",
1267
+ "security-severity": confidenceToSecuritySeverity(confidence),
1268
+ precision: confidenceToPrecision(confidence)
1269
+ };
1270
+ }
1271
+ function sanitizeSarifRule(rule) {
1272
+ return {
1273
+ ...rule,
1274
+ shortDescription: {
1275
+ text: rule.shortDescription.text || `ai-guard rule: ${rule.id}`
1276
+ },
1277
+ fullDescription: rule.fullDescription?.text ? rule.fullDescription : { text: rule.shortDescription.text || `ai-guard rule: ${rule.id}` },
1278
+ properties: rule.properties ? {
1279
+ ...rule.properties,
1280
+ // Re-deduplicate tags at the rule level as a final safety net
1281
+ tags: rule.properties.tags ? sanitizeSarifTags(rule.properties.tags) : []
1282
+ } : void 0
1283
+ };
1284
+ }
1285
+ function sanitizeSarifLog(log2) {
1286
+ return {
1287
+ ...log2,
1288
+ runs: log2.runs.map((run) => ({
1289
+ ...run,
1290
+ tool: {
1291
+ ...run.tool,
1292
+ driver: {
1293
+ ...run.tool.driver,
1294
+ rules: run.tool.driver.rules.map(sanitizeSarifRule)
1295
+ }
1296
+ }
1297
+ }))
1298
+ };
1299
+ }
1300
+ function confidenceToSecuritySeverity(confidence) {
1301
+ switch (confidence) {
1302
+ case "high":
1303
+ return "8.0";
1304
+ case "medium":
1305
+ return "5.0";
1306
+ case "low":
1307
+ return "3.0";
1308
+ case "informational":
1309
+ return "1.0";
1310
+ default:
1311
+ return "3.0";
1312
+ }
1313
+ }
1314
+ function confidenceToPrecision(confidence) {
1315
+ switch (confidence) {
1316
+ case "high":
1317
+ return "high";
1318
+ case "medium":
1319
+ return "medium";
1320
+ case "low":
1321
+ return "low";
1322
+ case "informational":
1323
+ return "low";
1324
+ default:
1325
+ return "low";
1326
+ }
1327
+ }
1253
1328
  function confidenceToSarifLevel(confidence) {
1254
1329
  switch (confidence) {
1255
1330
  case "high":
@@ -1278,20 +1353,6 @@ function confidenceToRank(confidence) {
1278
1353
  return 30;
1279
1354
  }
1280
1355
  }
1281
- function confidenceToGitHubSeverity(confidence) {
1282
- switch (confidence) {
1283
- case "high":
1284
- return "high";
1285
- case "medium":
1286
- return "medium";
1287
- case "low":
1288
- return "low";
1289
- case "informational":
1290
- return "recommendation";
1291
- default:
1292
- return "low";
1293
- }
1294
- }
1295
1356
  function filePathToUri(filePath) {
1296
1357
  return filePath.replace(/\\/g, "/");
1297
1358
  }
@@ -1311,6 +1372,8 @@ function buildRuleDescriptors(ruleIds) {
1311
1372
  const confidence = ISSUE_CONFIDENCE[id];
1312
1373
  const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
1313
1374
  const remediation = ISSUE_REMEDIATION[id];
1375
+ const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
1376
+ const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
1314
1377
  const helpMarkdown = [
1315
1378
  `**${meta?.shortDesc ?? shortName}**`,
1316
1379
  "",
@@ -1318,7 +1381,7 @@ function buildRuleDescriptors(ruleIds) {
1318
1381
  "",
1319
1382
  `[View rule documentation](${BASE_DOCS_URL}/${shortName}.md)`
1320
1383
  ].filter(Boolean).join("\n");
1321
- return {
1384
+ return sanitizeSarifRule({
1322
1385
  id,
1323
1386
  shortDescription: {
1324
1387
  text: meta?.shortDesc ?? `ai-guard rule: ${shortName}`
@@ -1334,13 +1397,12 @@ function buildRuleDescriptors(ruleIds) {
1334
1397
  defaultConfiguration: {
1335
1398
  level: confidenceToSarifLevel(confidence)
1336
1399
  },
1337
- properties: {
1338
- tags: [...meta?.tags ?? ["ai-guard"], category.toLowerCase().replace(/ /g, "-")],
1339
- confidence: confidence ?? "low",
1340
- category,
1341
- "problem.severity": confidenceToGitHubSeverity(confidence)
1342
- }
1343
- };
1400
+ properties: sanitizeSarifProperties({
1401
+ tags: rawTags,
1402
+ confidence,
1403
+ category
1404
+ })
1405
+ });
1344
1406
  });
1345
1407
  }
1346
1408
  function buildSarifResult(issue, file) {
@@ -1372,11 +1434,87 @@ function buildSarifResult(issue, file) {
1372
1434
  ],
1373
1435
  properties: {
1374
1436
  confidence: confidence ?? "low",
1437
+ "security-severity": confidenceToSecuritySeverity(confidence),
1438
+ precision: confidenceToPrecision(confidence),
1375
1439
  ...asyncRiskType && { asyncRiskType },
1376
1440
  ...category && { category }
1377
1441
  }
1378
1442
  };
1379
1443
  }
1444
+ function enhanceTagsWithGitHubSemantics(ruleId, baseTags) {
1445
+ const category = ISSUE_CATEGORY[ruleId];
1446
+ const tags = [...baseTags];
1447
+ if (category === "Security" || ruleId.includes("secret") || ruleId.includes("eval") || ruleId.includes("sql") || ruleId.includes("deserialize") || ruleId.includes("auth")) {
1448
+ tags.push("security", "correctness", "reliability");
1449
+ } else {
1450
+ tags.push("correctness", "reliability");
1451
+ }
1452
+ return sanitizeSarifTags(tags);
1453
+ }
1454
+ function normalizeSarifForGitHub(log2) {
1455
+ const normalizedRuns = log2.runs.map((run) => {
1456
+ const rules = run.tool.driver.rules.map((rule) => {
1457
+ const confidence = ISSUE_CONFIDENCE[rule.id];
1458
+ const properties = rule.properties || {};
1459
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1460
+ properties["precision"] = confidenceToPrecision(confidence);
1461
+ const currentTags = properties.tags || [];
1462
+ properties.tags = enhanceTagsWithGitHubSemantics(rule.id, currentTags);
1463
+ delete properties["problem.severity"];
1464
+ const defaultConfiguration = rule.defaultConfiguration || { level: "warning" };
1465
+ let normalizedConfigLevel = defaultConfiguration.level;
1466
+ if (!["error", "warning", "note", "none"].includes(normalizedConfigLevel)) {
1467
+ normalizedConfigLevel = confidenceToSarifLevel(confidence);
1468
+ }
1469
+ if (normalizedConfigLevel === "none") {
1470
+ normalizedConfigLevel = "note";
1471
+ }
1472
+ return {
1473
+ ...rule,
1474
+ defaultConfiguration: {
1475
+ ...defaultConfiguration,
1476
+ level: normalizedConfigLevel
1477
+ },
1478
+ properties
1479
+ };
1480
+ });
1481
+ const results = run.results.map((result) => {
1482
+ const confidence = ISSUE_CONFIDENCE[result.ruleId];
1483
+ const properties = result.properties || {};
1484
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1485
+ properties["precision"] = confidenceToPrecision(confidence);
1486
+ let normalizedLevel = result.level;
1487
+ if (!["error", "warning", "note", "none"].includes(normalizedLevel)) {
1488
+ normalizedLevel = confidenceToSarifLevel(confidence);
1489
+ }
1490
+ if (normalizedLevel === "none") {
1491
+ normalizedLevel = "note";
1492
+ }
1493
+ const kind = confidence === "informational" ? "informational" : "fail";
1494
+ return {
1495
+ ...result,
1496
+ level: normalizedLevel,
1497
+ kind,
1498
+ properties
1499
+ };
1500
+ });
1501
+ return {
1502
+ ...run,
1503
+ tool: {
1504
+ ...run.tool,
1505
+ driver: {
1506
+ ...run.tool.driver,
1507
+ rules
1508
+ }
1509
+ },
1510
+ results
1511
+ };
1512
+ });
1513
+ return {
1514
+ ...log2,
1515
+ runs: normalizedRuns
1516
+ };
1517
+ }
1380
1518
  function buildSarifLog(result, version = PKG_VERSION) {
1381
1519
  const usedRuleIds = collectUsedRuleIds(result);
1382
1520
  const rules = buildRuleDescriptors(usedRuleIds);
@@ -1386,7 +1524,7 @@ function buildSarifLog(result, version = PKG_VERSION) {
1386
1524
  sarifResults.push(buildSarifResult(issue, file));
1387
1525
  }
1388
1526
  }
1389
- return {
1527
+ const log2 = {
1390
1528
  $schema: "https://json.schemastore.org/sarif-2.1.0.json",
1391
1529
  version: "2.1.0",
1392
1530
  runs: [
@@ -1410,10 +1548,54 @@ function buildSarifLog(result, version = PKG_VERSION) {
1410
1548
  }
1411
1549
  ]
1412
1550
  };
1551
+ return normalizeSarifForGitHub(sanitizeSarifLog(log2));
1413
1552
  }
1414
1553
  function sarifToJson(log2) {
1415
1554
  return JSON.stringify(log2, null, 2);
1416
1555
  }
1556
+ function buildSarifDebugInfo(result) {
1557
+ const usedRuleIds = collectUsedRuleIds(result);
1558
+ const rulesEmitted = usedRuleIds.map((id) => {
1559
+ const meta = RULE_DOCS[id];
1560
+ const confidence = ISSUE_CONFIDENCE[id];
1561
+ const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
1562
+ const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
1563
+ const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
1564
+ const sanitizedTags = enhanceTagsWithGitHubSemantics(id, rawTags);
1565
+ return {
1566
+ id,
1567
+ rawTags,
1568
+ sanitizedTags,
1569
+ hasDuplicatesInRaw: rawTags.length !== new Set(rawTags).size,
1570
+ confidence: confidence ?? "low",
1571
+ category,
1572
+ level: confidenceToSarifLevel(confidence),
1573
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1574
+ precision: confidenceToPrecision(confidence)
1575
+ };
1576
+ });
1577
+ const resultsEmitted = [];
1578
+ for (const file of result.files) {
1579
+ for (const issue of file.issues) {
1580
+ const confidence = ISSUE_CONFIDENCE[issue.ruleId];
1581
+ resultsEmitted.push({
1582
+ ruleId: issue.ruleId,
1583
+ level: confidenceToSarifLevel(confidence),
1584
+ kind: confidence === "informational" ? "informational" : "fail",
1585
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1586
+ precision: confidenceToPrecision(confidence),
1587
+ filePath: file.filePath,
1588
+ line: issue.line
1589
+ });
1590
+ }
1591
+ }
1592
+ return {
1593
+ rulesEmitted,
1594
+ resultsEmitted,
1595
+ totalResults: result.files.reduce((n, f) => n + f.issues.length, 0),
1596
+ version: PKG_VERSION
1597
+ };
1598
+ }
1417
1599
 
1418
1600
  // cli/utils/github-summary.ts
1419
1601
  var import_fs2 = __toESM(require("fs"));
@@ -1891,7 +2073,7 @@ function registerRunCommand(program2) {
1891
2073
  "--max-warnings <n>",
1892
2074
  "Fail with exit code 1 if warnings exceed this count",
1893
2075
  (value) => Number.parseInt(value, 10)
1894
- ).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) => {
2076
+ ).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) => {
1895
2077
  if (opts.maxWarnings !== void 0 && (!Number.isInteger(opts.maxWarnings) || opts.maxWarnings < 0)) {
1896
2078
  log.blank();
1897
2079
  log.error("--max-warnings must be a non-negative integer.");
@@ -1943,9 +2125,62 @@ function registerRunCommand(program2) {
1943
2125
  writeGitHubSummary(result, { preset, scanMode: "full" });
1944
2126
  writeGitHubOutputs(result);
1945
2127
  }
1946
- if (opts.sarif) {
2128
+ if (opts.sarif || opts.sarifOutput) {
1947
2129
  const sarifLog = buildSarifLog(result);
1948
- console.log(sarifToJson(sarifLog));
2130
+ const sarifJson = sarifToJson(sarifLog);
2131
+ if (opts.debugSarif) {
2132
+ const debugInfo = buildSarifDebugInfo(result);
2133
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2134
+ 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");
2135
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2136
+ for (const r of debugInfo.rulesEmitted) {
2137
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2138
+ `);
2139
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2140
+ `);
2141
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2142
+ `);
2143
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2144
+ `);
2145
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2146
+ `);
2147
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2148
+ `);
2149
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2150
+ `);
2151
+ if (r.hasDuplicatesInRaw) {
2152
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2153
+ `);
2154
+ }
2155
+ }
2156
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2157
+ if (debugInfo.resultsEmitted.length === 0) {
2158
+ process.stderr.write("[debug-sarif] (none)\n");
2159
+ } else {
2160
+ for (const res of debugInfo.resultsEmitted) {
2161
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2162
+ `);
2163
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2164
+ `);
2165
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2166
+ `);
2167
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2168
+ `);
2169
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2170
+ `);
2171
+ }
2172
+ }
2173
+ 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\n");
2174
+ }
2175
+ if (opts.sarifOutput) {
2176
+ import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
2177
+ if (opts.debugSarif) {
2178
+ process.stderr.write(`[debug-sarif] SARIF written to: ${opts.sarifOutput}
2179
+ `);
2180
+ }
2181
+ } else {
2182
+ console.log(sarifJson);
2183
+ }
1949
2184
  process.exit(resolveExitCode(result, opts.failOn ?? "errors", opts.maxWarnings));
1950
2185
  return;
1951
2186
  }
@@ -2280,7 +2515,7 @@ function resolveExitCode(result, failOn, maxWarnings) {
2280
2515
 
2281
2516
  // cli/commands/changed.ts
2282
2517
  var import_path4 = __toESM(require("path"));
2283
- var import_fs4 = __toESM(require("fs"));
2518
+ var import_fs5 = __toESM(require("fs"));
2284
2519
  var import_ora2 = __toESM(require("ora"));
2285
2520
  var import_chalk5 = __toESM(require("chalk"));
2286
2521
  function registerChangedCommand(program2) {
@@ -2288,7 +2523,7 @@ function registerChangedCommand(program2) {
2288
2523
  "--max-warnings <n>",
2289
2524
  "Fail if warnings exceed this count",
2290
2525
  (v) => Number.parseInt(v, 10)
2291
- ).option("--verbose", "Expand all findings (disable grouping)").option("--quiet", "Show errors only").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-git", "Print full git resolution trace (useful for diagnosing zero-file issues)").option("--debug-ci", "Print CI environment state (GITHUB_* vars, SARIF path, summary path)").action(async (opts) => {
2526
+ ).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)").option("--debug-sarif", "Print SARIF tag normalization trace \u2014 diagnose duplicate-tag schema issues").action(async (opts) => {
2292
2527
  const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
2293
2528
  const inCI = isGitHubActions();
2294
2529
  const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
@@ -2400,8 +2635,56 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI,
2400
2635
  if (opts.sarif) {
2401
2636
  const sarifLog = buildSarifLog(result);
2402
2637
  const sarifJson = sarifToJson(sarifLog);
2638
+ if (opts.debugSarif) {
2639
+ const debugInfo = buildSarifDebugInfo(result);
2640
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2641
+ 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");
2642
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2643
+ for (const r of debugInfo.rulesEmitted) {
2644
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2645
+ `);
2646
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2647
+ `);
2648
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2649
+ `);
2650
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2651
+ `);
2652
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2653
+ `);
2654
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2655
+ `);
2656
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2657
+ `);
2658
+ if (r.hasDuplicatesInRaw) {
2659
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2660
+ `);
2661
+ }
2662
+ }
2663
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2664
+ if (debugInfo.resultsEmitted.length === 0) {
2665
+ process.stderr.write("[debug-sarif] (none)\n");
2666
+ } else {
2667
+ for (const res of debugInfo.resultsEmitted) {
2668
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2669
+ `);
2670
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2671
+ `);
2672
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2673
+ `);
2674
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2675
+ `);
2676
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2677
+ `);
2678
+ }
2679
+ }
2680
+ 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\n");
2681
+ }
2403
2682
  if (resolvedSarifPath) {
2404
- import_fs4.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
2683
+ import_fs5.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
2684
+ if (opts.debugSarif) {
2685
+ process.stderr.write(`[debug-sarif] SARIF written to: ${resolvedSarifPath}
2686
+ `);
2687
+ }
2405
2688
  } else {
2406
2689
  console.log(sarifJson);
2407
2690
  }
@@ -2532,10 +2815,10 @@ function getFailOnExitCode(result, failOn, maxWarnings) {
2532
2815
  // cli/commands/init.ts
2533
2816
  var import_chalk6 = __toESM(require("chalk"));
2534
2817
  var import_path7 = __toESM(require("path"));
2535
- var import_fs7 = __toESM(require("fs"));
2818
+ var import_fs8 = __toESM(require("fs"));
2536
2819
 
2537
2820
  // cli/utils/detector.ts
2538
- var import_fs5 = __toESM(require("fs"));
2821
+ var import_fs6 = __toESM(require("fs"));
2539
2822
  var import_path5 = __toESM(require("path"));
2540
2823
  var import_module2 = require("module");
2541
2824
  var FLAT_CONFIG_FILES = [
@@ -2554,7 +2837,7 @@ var ALL_CONFIG_FILES = [...FLAT_CONFIG_FILES, ...LEGACY_CONFIG_FILES];
2554
2837
  function isPackageInstalled(pkgName, cwd = process.cwd()) {
2555
2838
  try {
2556
2839
  const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
2557
- if (import_fs5.default.existsSync(pkgPath)) {
2840
+ if (import_fs6.default.existsSync(pkgPath)) {
2558
2841
  return true;
2559
2842
  }
2560
2843
  const req = (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json"));
@@ -2567,8 +2850,8 @@ function isPackageInstalled(pkgName, cwd = process.cwd()) {
2567
2850
  function getPackageVersion(pkgName, cwd = process.cwd()) {
2568
2851
  try {
2569
2852
  const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
2570
- const resolvedPath = import_fs5.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
2571
- const raw = import_fs5.default.readFileSync(resolvedPath, "utf-8");
2853
+ const resolvedPath = import_fs6.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
2854
+ const raw = import_fs6.default.readFileSync(resolvedPath, "utf-8");
2572
2855
  const json = JSON.parse(raw);
2573
2856
  return json.version ?? null;
2574
2857
  } catch {
@@ -2585,7 +2868,7 @@ function detectConfigType(cwd = process.cwd()) {
2585
2868
  let first = null;
2586
2869
  for (const { file, type } of ALL_CONFIG_FILES) {
2587
2870
  const fullPath = import_path5.default.join(cwd, file);
2588
- if (import_fs5.default.existsSync(fullPath)) {
2871
+ if (import_fs6.default.existsSync(fullPath)) {
2589
2872
  found.push(fullPath);
2590
2873
  if (!first) first = { type, path: fullPath };
2591
2874
  }
@@ -2608,7 +2891,7 @@ function requiresFlatConfig(eslintMajor) {
2608
2891
  function detectNukeIgnore(configPath) {
2609
2892
  if (!configPath) return false;
2610
2893
  try {
2611
- const content = import_fs5.default.readFileSync(configPath, "utf-8");
2894
+ const content = import_fs6.default.readFileSync(configPath, "utf-8");
2612
2895
  return content.includes('"**/*"') || content.includes("'**/*'");
2613
2896
  } catch {
2614
2897
  return false;
@@ -2843,10 +3126,10 @@ function registerInitCommand(program2) {
2843
3126
  const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
2844
3127
  let cleanedUp = false;
2845
3128
  for (const p of legacyConfigs) {
2846
- if (import_fs7.default.existsSync(p)) {
3129
+ if (import_fs8.default.existsSync(p)) {
2847
3130
  if (!isDryRun) {
2848
3131
  try {
2849
- import_fs7.default.renameSync(p, `${p}.bak`);
3132
+ import_fs8.default.renameSync(p, `${p}.bak`);
2850
3133
  cleanedUp = true;
2851
3134
  } catch (err) {
2852
3135
  const reason = err instanceof Error ? err.message : String(err);
@@ -2872,7 +3155,7 @@ function registerInitCommand(program2) {
2872
3155
  }
2873
3156
  log.blank();
2874
3157
  log.section("Validation");
2875
- if (!import_fs7.default.existsSync(finalConfigPath)) {
3158
+ if (!import_fs8.default.existsSync(finalConfigPath)) {
2876
3159
  log.error(`Config file not found after write: ${import_path7.default.relative(cwd, finalConfigPath)}`);
2877
3160
  log.blank();
2878
3161
  log.print(` ${import_chalk6.default.bold("Problem:")} File write may have failed.`);
@@ -2929,7 +3212,7 @@ function registerInitCommand(program2) {
2929
3212
  let cleanedUpInfo = false;
2930
3213
  if (eslintNeedsFlat) {
2931
3214
  const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
2932
- cleanedUpInfo = legacyConfigs.some((p) => import_fs7.default.existsSync(`${p}.bak`) && !import_fs7.default.existsSync(p));
3215
+ cleanedUpInfo = legacyConfigs.some((p) => import_fs8.default.existsSync(`${p}.bak`) && !import_fs8.default.existsSync(p));
2933
3216
  }
2934
3217
  const lingeringConflicts = env.hasConflictingConfigs && !eslintNeedsFlat && !cleanedUpInfo;
2935
3218
  if (loadError === null && !lingeringConflicts) {
@@ -3319,7 +3602,7 @@ function registerIgnoreCommand(program2) {
3319
3602
  }
3320
3603
 
3321
3604
  // cli/commands/baseline.ts
3322
- var import_fs8 = __toESM(require("fs"));
3605
+ var import_fs9 = __toESM(require("fs"));
3323
3606
  var import_path11 = __toESM(require("path"));
3324
3607
  var import_chalk10 = __toESM(require("chalk"));
3325
3608
  var import_ora3 = __toESM(require("ora"));
@@ -3342,14 +3625,14 @@ function saveBaseline(result, preset, mode, cwd) {
3342
3625
  entries
3343
3626
  };
3344
3627
  const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
3345
- import_fs8.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
3628
+ import_fs9.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
3346
3629
  return baselinePath;
3347
3630
  }
3348
3631
  function loadBaseline(cwd) {
3349
3632
  const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
3350
- if (!import_fs8.default.existsSync(baselinePath)) return null;
3633
+ if (!import_fs9.default.existsSync(baselinePath)) return null;
3351
3634
  try {
3352
- const raw = import_fs8.default.readFileSync(baselinePath, "utf-8");
3635
+ const raw = import_fs9.default.readFileSync(baselinePath, "utf-8");
3353
3636
  return JSON.parse(raw);
3354
3637
  } catch {
3355
3638
  return null;
@@ -3516,7 +3799,7 @@ function registerBaselineCommand(program2) {
3516
3799
  }
3517
3800
 
3518
3801
  // cli/commands/init-context.ts
3519
- var import_fs9 = __toESM(require("fs"));
3802
+ var import_fs10 = __toESM(require("fs"));
3520
3803
  var import_path12 = __toESM(require("path"));
3521
3804
  var import_chalk11 = __toESM(require("chalk"));
3522
3805
  var import_prompts2 = require("@inquirer/prompts");
@@ -3888,16 +4171,16 @@ var AGENTS = [
3888
4171
  function getPackageVersion2(cwd) {
3889
4172
  try {
3890
4173
  const pkgPath = import_path12.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
3891
- if (import_fs9.default.existsSync(pkgPath)) {
3892
- const pkg = JSON.parse(import_fs9.default.readFileSync(pkgPath, "utf-8"));
4174
+ if (import_fs10.default.existsSync(pkgPath)) {
4175
+ const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
3893
4176
  if (pkg.version) return pkg.version;
3894
4177
  }
3895
4178
  } catch {
3896
4179
  }
3897
4180
  try {
3898
4181
  const pkgPath = import_path12.default.join(cwd, "package.json");
3899
- if (import_fs9.default.existsSync(pkgPath)) {
3900
- const pkg = JSON.parse(import_fs9.default.readFileSync(pkgPath, "utf-8"));
4182
+ if (import_fs10.default.existsSync(pkgPath)) {
4183
+ const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
3901
4184
  if (pkg.version) return pkg.version;
3902
4185
  }
3903
4186
  } catch {
@@ -3965,7 +4248,7 @@ function registerInitContextCommand(program2) {
3965
4248
  const results = [];
3966
4249
  for (const agent of selectedAgents) {
3967
4250
  const fullPath = import_path12.default.join(cwd, agent.filePath);
3968
- const exists = import_fs9.default.existsSync(fullPath);
4251
+ const exists = import_fs10.default.existsSync(fullPath);
3969
4252
  if (exists && !isForce && !isDryRun) {
3970
4253
  const overwrite = await (0, import_prompts2.confirm)({
3971
4254
  message: `${agent.filePath} already exists. Overwrite?`,
@@ -3990,10 +4273,10 @@ function registerInitContextCommand(program2) {
3990
4273
  continue;
3991
4274
  }
3992
4275
  const parentDir = import_path12.default.dirname(fullPath);
3993
- if (!import_fs9.default.existsSync(parentDir)) {
3994
- import_fs9.default.mkdirSync(parentDir, { recursive: true });
4276
+ if (!import_fs10.default.existsSync(parentDir)) {
4277
+ import_fs10.default.mkdirSync(parentDir, { recursive: true });
3995
4278
  }
3996
- import_fs9.default.writeFileSync(fullPath, content, "utf-8");
4279
+ import_fs10.default.writeFileSync(fullPath, content, "utf-8");
3997
4280
  results.push({ file: agent.filePath, status: "created" });
3998
4281
  }
3999
4282
  log.section("Summary");
@@ -4027,7 +4310,7 @@ function registerInitContextCommand(program2) {
4027
4310
  }
4028
4311
 
4029
4312
  // cli/commands/report.ts
4030
- var import_fs10 = __toESM(require("fs"));
4313
+ var import_fs11 = __toESM(require("fs"));
4031
4314
  var import_path13 = __toESM(require("path"));
4032
4315
  var import_ora4 = __toESM(require("ora"));
4033
4316
  var RULE_META = {
@@ -4053,8 +4336,8 @@ var RULE_META = {
4053
4336
  function getProjectName(cwd) {
4054
4337
  try {
4055
4338
  const pkgPath = import_path13.default.join(cwd, "package.json");
4056
- if (import_fs10.default.existsSync(pkgPath)) {
4057
- const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
4339
+ if (import_fs11.default.existsSync(pkgPath)) {
4340
+ const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4058
4341
  if (pkg.name) return pkg.name;
4059
4342
  }
4060
4343
  } catch {
@@ -4064,16 +4347,16 @@ function getProjectName(cwd) {
4064
4347
  function getPluginVersion(cwd) {
4065
4348
  try {
4066
4349
  const pkgPath = import_path13.default.resolve(__dirname, "../../package.json");
4067
- if (import_fs10.default.existsSync(pkgPath)) {
4068
- const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
4350
+ if (import_fs11.default.existsSync(pkgPath)) {
4351
+ const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4069
4352
  if (pkg.version) return pkg.version;
4070
4353
  }
4071
4354
  } catch {
4072
4355
  }
4073
4356
  try {
4074
4357
  const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
4075
- if (import_fs10.default.existsSync(pkgPath)) {
4076
- const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
4358
+ if (import_fs11.default.existsSync(pkgPath)) {
4359
+ const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4077
4360
  if (pkg.version) return pkg.version;
4078
4361
  }
4079
4362
  } catch {
@@ -4451,7 +4734,7 @@ function registerReportCommand(program2) {
4451
4734
  log.blank();
4452
4735
  const html = generateHtml(reportData);
4453
4736
  const outputPath = import_path13.default.resolve(cwd, opts.output);
4454
- import_fs10.default.writeFileSync(outputPath, html, "utf-8");
4737
+ import_fs11.default.writeFileSync(outputPath, html, "utf-8");
4455
4738
  log.success(`Report saved: ${opts.output}`);
4456
4739
  log.info(
4457
4740
  `Score: ${reportData.score}/100 \xB7 ${reportData.totalErrors} errors \xB7 ${reportData.totalWarnings} warnings`
@@ -4485,7 +4768,7 @@ function registerReportCommand(program2) {
4485
4768
  function getVersion() {
4486
4769
  try {
4487
4770
  const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
4488
- const raw = import_fs11.default.readFileSync(pkgPath, "utf-8");
4771
+ const raw = import_fs12.default.readFileSync(pkgPath, "utf-8");
4489
4772
  const pkg = JSON.parse(raw);
4490
4773
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
4491
4774
  } catch {