eslint-plugin-ai-guard 1.2.3 → 1.2.6

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
@@ -414,19 +414,19 @@ function addIgnoresToLegacyConfig(existing) {
414
414
  function getConfigFilePath(configType, cwd = process.cwd()) {
415
415
  switch (configType) {
416
416
  case "flat-js":
417
- return import_path6.default.join(cwd, "eslint.config.js");
417
+ return import_path7.default.join(cwd, "eslint.config.js");
418
418
  case "flat-mjs":
419
- return import_path6.default.join(cwd, "eslint.config.mjs");
419
+ return import_path7.default.join(cwd, "eslint.config.mjs");
420
420
  case "flat-cjs":
421
- return import_path6.default.join(cwd, "eslint.config.cjs");
421
+ return import_path7.default.join(cwd, "eslint.config.cjs");
422
422
  case "eslintrc-js":
423
- return import_path6.default.join(cwd, ".eslintrc.js");
423
+ return import_path7.default.join(cwd, ".eslintrc.js");
424
424
  case "eslintrc-cjs":
425
- return import_path6.default.join(cwd, ".eslintrc.cjs");
425
+ return import_path7.default.join(cwd, ".eslintrc.cjs");
426
426
  case "eslintrc-json":
427
- return import_path6.default.join(cwd, ".eslintrc.json");
427
+ return import_path7.default.join(cwd, ".eslintrc.json");
428
428
  default:
429
- return import_path6.default.join(cwd, ".eslintrc.js");
429
+ return import_path7.default.join(cwd, ".eslintrc.js");
430
430
  }
431
431
  }
432
432
  function validateFlatConfigText(content) {
@@ -461,12 +461,12 @@ function validateLegacyConfigText(content) {
461
461
  }
462
462
  return problems;
463
463
  }
464
- var import_fs7, import_path6, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
464
+ var import_fs7, import_path7, 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
468
  import_fs7 = __toESM(require("fs"));
469
- import_path6 = __toESM(require("path"));
469
+ import_path7 = __toESM(require("path"));
470
470
  AI_GUARD_MARKER = "eslint-plugin-ai-guard";
471
471
  DEFAULT_FLAT_IGNORES = [
472
472
  "node_modules/**",
@@ -490,7 +490,7 @@ var init_config_manager = __esm({
490
490
  // cli/index.ts
491
491
  var import_commander = require("commander");
492
492
  var import_fs12 = __toESM(require("fs"));
493
- var import_path14 = __toESM(require("path"));
493
+ var import_path15 = __toESM(require("path"));
494
494
 
495
495
  // cli/commands/run.ts
496
496
  var import_fs4 = __toESM(require("fs"));
@@ -1205,6 +1205,9 @@ async function runEslint(options) {
1205
1205
  };
1206
1206
  }
1207
1207
 
1208
+ // cli/utils/sarif.ts
1209
+ var import_path3 = __toESM(require("path"));
1210
+
1208
1211
  // cli/utils/version.ts
1209
1212
  var import_module = require("module");
1210
1213
  var import_url2 = require("url");
@@ -1259,11 +1262,13 @@ function sanitizeSarifTags(tags) {
1259
1262
  );
1260
1263
  }
1261
1264
  function sanitizeSarifProperties(props) {
1265
+ const confidence = props.confidence ?? "low";
1262
1266
  return {
1263
1267
  tags: sanitizeSarifTags(props.tags),
1264
- confidence: props.confidence ?? "low",
1268
+ confidence,
1265
1269
  category: props.category ?? "AI Patterns",
1266
- "problem.severity": props.problemSeverity ?? "low"
1270
+ "security-severity": confidenceToSecuritySeverity(confidence),
1271
+ precision: confidenceToPrecision(confidence)
1267
1272
  };
1268
1273
  }
1269
1274
  function sanitizeSarifRule(rule) {
@@ -1295,6 +1300,34 @@ function sanitizeSarifLog(log2) {
1295
1300
  }))
1296
1301
  };
1297
1302
  }
1303
+ function confidenceToSecuritySeverity(confidence) {
1304
+ switch (confidence) {
1305
+ case "high":
1306
+ return "8.0";
1307
+ case "medium":
1308
+ return "5.0";
1309
+ case "low":
1310
+ return "3.0";
1311
+ case "informational":
1312
+ return "1.0";
1313
+ default:
1314
+ return "3.0";
1315
+ }
1316
+ }
1317
+ function confidenceToPrecision(confidence) {
1318
+ switch (confidence) {
1319
+ case "high":
1320
+ return "high";
1321
+ case "medium":
1322
+ return "medium";
1323
+ case "low":
1324
+ return "low";
1325
+ case "informational":
1326
+ return "low";
1327
+ default:
1328
+ return "low";
1329
+ }
1330
+ }
1298
1331
  function confidenceToSarifLevel(confidence) {
1299
1332
  switch (confidence) {
1300
1333
  case "high":
@@ -1323,22 +1356,24 @@ function confidenceToRank(confidence) {
1323
1356
  return 30;
1324
1357
  }
1325
1358
  }
1326
- function confidenceToGitHubSeverity(confidence) {
1327
- switch (confidence) {
1328
- case "high":
1329
- return "high";
1330
- case "medium":
1331
- return "medium";
1332
- case "low":
1333
- return "low";
1334
- case "informational":
1335
- return "recommendation";
1336
- default:
1337
- return "low";
1359
+ function normalizeSarifPath(filePath, repoRoot) {
1360
+ let normalized = filePath;
1361
+ normalized = normalized.replace(/\\/g, "/");
1362
+ normalized = normalized.replace(/^[A-Za-z]:\//, "/");
1363
+ if (repoRoot && import_path3.default.isAbsolute(filePath)) {
1364
+ const posixRoot = repoRoot.replace(/\\/g, "/").replace(/^[A-Za-z]:\//, "/").replace(/\/$/, "");
1365
+ if (normalized.startsWith(posixRoot + "/")) {
1366
+ normalized = normalized.slice(posixRoot.length + 1);
1367
+ } else if (normalized.startsWith(posixRoot)) {
1368
+ normalized = normalized.slice(posixRoot.length);
1369
+ }
1338
1370
  }
1339
- }
1340
- function filePathToUri(filePath) {
1341
- return filePath.replace(/\\/g, "/");
1371
+ normalized = normalized.replace(/^\/+/, "");
1372
+ normalized = normalized.replace(/^\.\//, "");
1373
+ if (!normalized || normalized === ".") {
1374
+ normalized = filePath.replace(/\\/g, "/");
1375
+ }
1376
+ return normalized;
1342
1377
  }
1343
1378
  function collectUsedRuleIds(result) {
1344
1379
  const ids = /* @__PURE__ */ new Set();
@@ -1384,16 +1419,16 @@ function buildRuleDescriptors(ruleIds) {
1384
1419
  properties: sanitizeSarifProperties({
1385
1420
  tags: rawTags,
1386
1421
  confidence,
1387
- category,
1388
- problemSeverity: confidenceToGitHubSeverity(confidence)
1422
+ category
1389
1423
  })
1390
1424
  });
1391
1425
  });
1392
1426
  }
1393
- function buildSarifResult(issue, file) {
1427
+ function buildSarifResult(issue, file, repoRoot) {
1394
1428
  const confidence = ISSUE_CONFIDENCE[issue.ruleId];
1395
1429
  const asyncRiskType = ISSUE_ASYNC_RISK_TYPE[issue.ruleId];
1396
1430
  const category = ISSUE_CATEGORY[issue.ruleId];
1431
+ const sarifUri = normalizeSarifPath(file.filePath, repoRoot);
1397
1432
  return {
1398
1433
  ruleId: issue.ruleId,
1399
1434
  level: confidenceToSarifLevel(confidence),
@@ -1404,8 +1439,8 @@ function buildSarifResult(issue, file) {
1404
1439
  {
1405
1440
  physicalLocation: {
1406
1441
  artifactLocation: {
1407
- uri: filePathToUri(file.filePath),
1408
- uriBaseId: "%SRCROOT%"
1442
+ // Repository-relative POSIX path — no uriBaseId
1443
+ uri: sarifUri
1409
1444
  },
1410
1445
  region: {
1411
1446
  startLine: Math.max(1, issue.line),
@@ -1419,18 +1454,94 @@ function buildSarifResult(issue, file) {
1419
1454
  ],
1420
1455
  properties: {
1421
1456
  confidence: confidence ?? "low",
1457
+ "security-severity": confidenceToSecuritySeverity(confidence),
1458
+ precision: confidenceToPrecision(confidence),
1422
1459
  ...asyncRiskType && { asyncRiskType },
1423
1460
  ...category && { category }
1424
1461
  }
1425
1462
  };
1426
1463
  }
1427
- function buildSarifLog(result, version = PKG_VERSION) {
1464
+ function enhanceTagsWithGitHubSemantics(ruleId, baseTags) {
1465
+ const category = ISSUE_CATEGORY[ruleId];
1466
+ const tags = [...baseTags];
1467
+ if (category === "Security" || ruleId.includes("secret") || ruleId.includes("eval") || ruleId.includes("sql") || ruleId.includes("deserialize") || ruleId.includes("auth")) {
1468
+ tags.push("security", "correctness", "reliability");
1469
+ } else {
1470
+ tags.push("correctness", "reliability");
1471
+ }
1472
+ return sanitizeSarifTags(tags);
1473
+ }
1474
+ function normalizeSarifForGitHub(log2) {
1475
+ const normalizedRuns = log2.runs.map((run) => {
1476
+ const rules = run.tool.driver.rules.map((rule) => {
1477
+ const confidence = ISSUE_CONFIDENCE[rule.id];
1478
+ const properties = rule.properties || {};
1479
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1480
+ properties["precision"] = confidenceToPrecision(confidence);
1481
+ const currentTags = properties.tags || [];
1482
+ properties.tags = enhanceTagsWithGitHubSemantics(rule.id, currentTags);
1483
+ delete properties["problem.severity"];
1484
+ const defaultConfiguration = rule.defaultConfiguration || { level: "warning" };
1485
+ let normalizedConfigLevel = defaultConfiguration.level;
1486
+ if (!["error", "warning", "note", "none"].includes(normalizedConfigLevel)) {
1487
+ normalizedConfigLevel = confidenceToSarifLevel(confidence);
1488
+ }
1489
+ if (normalizedConfigLevel === "none") {
1490
+ normalizedConfigLevel = "note";
1491
+ }
1492
+ return {
1493
+ ...rule,
1494
+ defaultConfiguration: {
1495
+ ...defaultConfiguration,
1496
+ level: normalizedConfigLevel
1497
+ },
1498
+ properties
1499
+ };
1500
+ });
1501
+ const results = run.results.map((result) => {
1502
+ const confidence = ISSUE_CONFIDENCE[result.ruleId];
1503
+ const properties = result.properties || {};
1504
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1505
+ properties["precision"] = confidenceToPrecision(confidence);
1506
+ let normalizedLevel = result.level;
1507
+ if (!["error", "warning", "note", "none"].includes(normalizedLevel)) {
1508
+ normalizedLevel = confidenceToSarifLevel(confidence);
1509
+ }
1510
+ if (normalizedLevel === "none") {
1511
+ normalizedLevel = "note";
1512
+ }
1513
+ const kind = confidence === "informational" ? "informational" : "fail";
1514
+ return {
1515
+ ...result,
1516
+ level: normalizedLevel,
1517
+ kind,
1518
+ properties
1519
+ };
1520
+ });
1521
+ return {
1522
+ ...run,
1523
+ tool: {
1524
+ ...run.tool,
1525
+ driver: {
1526
+ ...run.tool.driver,
1527
+ rules
1528
+ }
1529
+ },
1530
+ results
1531
+ };
1532
+ });
1533
+ return {
1534
+ ...log2,
1535
+ runs: normalizedRuns
1536
+ };
1537
+ }
1538
+ function buildSarifLog(result, version = PKG_VERSION, repoRoot) {
1428
1539
  const usedRuleIds = collectUsedRuleIds(result);
1429
1540
  const rules = buildRuleDescriptors(usedRuleIds);
1430
1541
  const sarifResults = [];
1431
1542
  for (const file of result.files) {
1432
1543
  for (const issue of file.issues) {
1433
- sarifResults.push(buildSarifResult(issue, file));
1544
+ sarifResults.push(buildSarifResult(issue, file, repoRoot));
1434
1545
  }
1435
1546
  }
1436
1547
  const log2 = {
@@ -1457,12 +1568,24 @@ function buildSarifLog(result, version = PKG_VERSION) {
1457
1568
  }
1458
1569
  ]
1459
1570
  };
1460
- return sanitizeSarifLog(log2);
1571
+ return normalizeSarifForGitHub(sanitizeSarifLog(log2));
1461
1572
  }
1462
1573
  function sarifToJson(log2) {
1463
1574
  return JSON.stringify(log2, null, 2);
1464
1575
  }
1465
- function buildSarifDebugInfo(result) {
1576
+ function debugSarifPath(filePath, repoRoot) {
1577
+ const normalizedUri = normalizeSarifPath(filePath, repoRoot);
1578
+ return {
1579
+ originalPath: filePath,
1580
+ normalizedUri,
1581
+ isRepositoryRelative: !import_path3.default.isAbsolute(normalizedUri) && !normalizedUri.startsWith("/"),
1582
+ hasLeadingSlash: normalizedUri.startsWith("/"),
1583
+ hasDriveLetter: /^[A-Za-z]:/.test(normalizedUri),
1584
+ hasBackslashes: normalizedUri.includes("\\"),
1585
+ hasLeadingDotSlash: normalizedUri.startsWith("./")
1586
+ };
1587
+ }
1588
+ function buildSarifDebugInfo(result, repoRoot) {
1466
1589
  const usedRuleIds = collectUsedRuleIds(result);
1467
1590
  const rulesEmitted = usedRuleIds.map((id) => {
1468
1591
  const meta = RULE_DOCS[id];
@@ -1470,7 +1593,7 @@ function buildSarifDebugInfo(result) {
1470
1593
  const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
1471
1594
  const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
1472
1595
  const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
1473
- const sanitizedTags = sanitizeSarifTags(rawTags);
1596
+ const sanitizedTags = enhanceTagsWithGitHubSemantics(id, rawTags);
1474
1597
  return {
1475
1598
  id,
1476
1599
  rawTags,
@@ -1478,11 +1601,37 @@ function buildSarifDebugInfo(result) {
1478
1601
  hasDuplicatesInRaw: rawTags.length !== new Set(rawTags).size,
1479
1602
  confidence: confidence ?? "low",
1480
1603
  category,
1481
- level: confidenceToSarifLevel(confidence)
1604
+ level: confidenceToSarifLevel(confidence),
1605
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1606
+ precision: confidenceToPrecision(confidence)
1482
1607
  };
1483
1608
  });
1609
+ const resultsEmitted = [];
1610
+ const pathsDebug = [];
1611
+ const seenPaths = /* @__PURE__ */ new Set();
1612
+ for (const file of result.files) {
1613
+ if (!seenPaths.has(file.filePath)) {
1614
+ seenPaths.add(file.filePath);
1615
+ pathsDebug.push(debugSarifPath(file.filePath, repoRoot));
1616
+ }
1617
+ for (const issue of file.issues) {
1618
+ const confidence = ISSUE_CONFIDENCE[issue.ruleId];
1619
+ resultsEmitted.push({
1620
+ ruleId: issue.ruleId,
1621
+ level: confidenceToSarifLevel(confidence),
1622
+ kind: confidence === "informational" ? "informational" : "fail",
1623
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1624
+ precision: confidenceToPrecision(confidence),
1625
+ filePath: file.filePath,
1626
+ normalizedUri: normalizeSarifPath(file.filePath, repoRoot),
1627
+ line: issue.line
1628
+ });
1629
+ }
1630
+ }
1484
1631
  return {
1485
1632
  rulesEmitted,
1633
+ resultsEmitted,
1634
+ pathsDebug,
1486
1635
  totalResults: result.files.reduce((n, f) => n + f.issues.length, 0),
1487
1636
  version: PKG_VERSION
1488
1637
  };
@@ -1715,7 +1864,7 @@ function writeGitHubOutputs(result, sarifPath) {
1715
1864
 
1716
1865
  // cli/utils/git-diff.ts
1717
1866
  var import_child_process = require("child_process");
1718
- var import_path3 = __toESM(require("path"));
1867
+ var import_path4 = __toESM(require("path"));
1719
1868
  var import_fs3 = __toESM(require("fs"));
1720
1869
  var DEFAULT_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"];
1721
1870
  var IGNORE_PREFIXES = [
@@ -1825,7 +1974,7 @@ function filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo) {
1825
1974
  for (const rel of rawFiles) {
1826
1975
  const normalized = rel.replace(/\\/g, "/").trim();
1827
1976
  if (!normalized) continue;
1828
- const ext = import_path3.default.extname(normalized).toLowerCase();
1977
+ const ext = import_path4.default.extname(normalized).toLowerCase();
1829
1978
  if (!extensions.includes(ext)) {
1830
1979
  debugInfo.droppedByExtension++;
1831
1980
  continue;
@@ -1841,7 +1990,7 @@ function filterFiles(rawFiles, extensions, workingDirectory, cwd, debugInfo) {
1841
1990
  continue;
1842
1991
  }
1843
1992
  }
1844
- const abs = import_path3.default.resolve(cwd, normalized);
1993
+ const abs = import_path4.default.resolve(cwd, normalized);
1845
1994
  if (!import_fs3.default.existsSync(abs)) {
1846
1995
  debugInfo.droppedMissing++;
1847
1996
  continue;
@@ -1964,7 +2113,7 @@ function registerRunCommand(program2) {
1964
2113
  "--max-warnings <n>",
1965
2114
  "Fail with exit code 1 if warnings exceed this count",
1966
2115
  (value) => Number.parseInt(value, 10)
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) => {
2116
+ ).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/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").option("--sarif-output <file>", "Write SARIF to this file instead of stdout").action(async (opts) => {
1968
2117
  if (opts.maxWarnings !== void 0 && (!Number.isInteger(opts.maxWarnings) || opts.maxWarnings < 0)) {
1969
2118
  log.blank();
1970
2119
  log.error("--max-warnings must be a non-negative integer.");
@@ -2017,25 +2166,80 @@ function registerRunCommand(program2) {
2017
2166
  writeGitHubOutputs(result);
2018
2167
  }
2019
2168
  if (opts.sarif || opts.sarifOutput) {
2020
- const sarifLog = buildSarifLog(result);
2169
+ const repoRoot = process.env.GITHUB_WORKSPACE ?? void 0;
2170
+ const sarifLog = buildSarifLog(result, void 0, repoRoot);
2021
2171
  const sarifJson = sarifToJson(sarifLog);
2022
2172
  if (opts.debugSarif) {
2023
- const debugInfo = buildSarifDebugInfo(result);
2024
- process.stderr.write("\n[debug-sarif] SARIF Tag Normalization Trace\n");
2173
+ const debugInfo = buildSarifDebugInfo(result, repoRoot);
2174
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2025
2175
  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");
2176
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2026
2177
  for (const r of debugInfo.rulesEmitted) {
2027
- process.stderr.write(`[debug-sarif] ${r.id}
2178
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2179
+ `);
2180
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2181
+ `);
2182
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2183
+ `);
2184
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2028
2185
  `);
2029
- process.stderr.write(`[debug-sarif] raw: ${JSON.stringify(r.rawTags)}
2186
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2030
2187
  `);
2031
- process.stderr.write(`[debug-sarif] sanitized: ${JSON.stringify(r.sanitizedTags)}
2188
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2189
+ `);
2190
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2032
2191
  `);
2033
2192
  if (r.hasDuplicatesInRaw) {
2034
- process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2193
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2194
+ `);
2195
+ }
2196
+ }
2197
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2198
+ if (debugInfo.resultsEmitted.length === 0) {
2199
+ process.stderr.write("[debug-sarif] (none)\n");
2200
+ } else {
2201
+ for (const res of debugInfo.resultsEmitted) {
2202
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2203
+ `);
2204
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2205
+ `);
2206
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2207
+ `);
2208
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2209
+ `);
2210
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2211
+ `);
2212
+ process.stderr.write(`[debug-sarif] artifact uri: ${res.normalizedUri}
2213
+ `);
2214
+ }
2215
+ }
2216
+ 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");
2217
+ }
2218
+ if (opts.debugSarifPaths) {
2219
+ const debugInfo = buildSarifDebugInfo(result, repoRoot);
2220
+ process.stderr.write("\n[debug-sarif-paths] Path Normalization Trace\n");
2221
+ process.stderr.write("[debug-sarif-paths] \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");
2222
+ process.stderr.write(`[debug-sarif-paths] GITHUB_WORKSPACE: ${repoRoot ?? "(not set \u2014 local run)"}
2223
+
2224
+ `);
2225
+ if (debugInfo.pathsDebug.length === 0) {
2226
+ process.stderr.write("[debug-sarif-paths] (no files in results)\n");
2227
+ } else {
2228
+ for (const p of debugInfo.pathsDebug) {
2229
+ process.stderr.write(`[debug-sarif-paths] original path: ${p.originalPath}
2230
+ `);
2231
+ process.stderr.write(`[debug-sarif-paths] artifact uri: ${p.normalizedUri}
2232
+ `);
2233
+ process.stderr.write(`[debug-sarif-paths] repo-relative: ${p.isRepositoryRelative ? "\u2705 yes" : "\u274C NO (GitHub may miss this)"}
2035
2234
  `);
2235
+ if (p.hasBackslashes) process.stderr.write("[debug-sarif-paths] \u26A0 had backslashes \u2014 converted\n");
2236
+ if (p.hasDriveLetter) process.stderr.write("[debug-sarif-paths] \u26A0 had drive letter \u2014 stripped\n");
2237
+ if (p.hasLeadingSlash) process.stderr.write("[debug-sarif-paths] \u26A0 had leading slash \u2014 stripped\n");
2238
+ if (p.hasLeadingDotSlash) process.stderr.write("[debug-sarif-paths] \u26A0 had leading ./ \u2014 stripped\n");
2239
+ process.stderr.write("\n");
2036
2240
  }
2037
2241
  }
2038
- process.stderr.write("\n");
2242
+ process.stderr.write("[debug-sarif-paths] \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");
2039
2243
  }
2040
2244
  if (opts.sarifOutput) {
2041
2245
  import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
@@ -2379,7 +2583,7 @@ function resolveExitCode(result, failOn, maxWarnings) {
2379
2583
  }
2380
2584
 
2381
2585
  // cli/commands/changed.ts
2382
- var import_path4 = __toESM(require("path"));
2586
+ var import_path5 = __toESM(require("path"));
2383
2587
  var import_fs5 = __toESM(require("fs"));
2384
2588
  var import_ora2 = __toESM(require("ora"));
2385
2589
  var import_chalk5 = __toESM(require("chalk"));
@@ -2388,7 +2592,7 @@ function registerChangedCommand(program2) {
2388
2592
  "--max-warnings <n>",
2389
2593
  "Fail if warnings exceed this count",
2390
2594
  (v) => Number.parseInt(v, 10)
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) => {
2595
+ ).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/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").action(async (opts) => {
2392
2596
  const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
2393
2597
  const inCI = isGitHubActions();
2394
2598
  const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
@@ -2417,7 +2621,7 @@ function registerChangedCommand(program2) {
2417
2621
  });
2418
2622
  }
2419
2623
  const cwd = process.cwd();
2420
- const resolvedPath = import_path4.default.resolve(opts.path);
2624
+ const resolvedPath = import_path5.default.resolve(opts.path);
2421
2625
  const workingDirectory = opts.path !== "." ? opts.path : void 0;
2422
2626
  const spinner = opts.json || opts.sarif ? null : (0, import_ora2.default)({ text: import_chalk5.default.dim("Detecting changed files\u2026"), color: "cyan" }).start();
2423
2627
  const changedResult = getChangedFiles({
@@ -2498,10 +2702,87 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI,
2498
2702
  emitGitHubAnnotations(result);
2499
2703
  }
2500
2704
  if (opts.sarif) {
2501
- const sarifLog = buildSarifLog(result);
2705
+ const repoRoot = process.env.GITHUB_WORKSPACE ?? void 0;
2706
+ const sarifLog = buildSarifLog(result, void 0, repoRoot);
2502
2707
  const sarifJson = sarifToJson(sarifLog);
2708
+ if (opts.debugSarif) {
2709
+ const debugInfo = buildSarifDebugInfo(result, repoRoot);
2710
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2711
+ 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");
2712
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2713
+ for (const r of debugInfo.rulesEmitted) {
2714
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2715
+ `);
2716
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2717
+ `);
2718
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2719
+ `);
2720
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2721
+ `);
2722
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2723
+ `);
2724
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2725
+ `);
2726
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2727
+ `);
2728
+ if (r.hasDuplicatesInRaw) {
2729
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2730
+ `);
2731
+ }
2732
+ }
2733
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2734
+ if (debugInfo.resultsEmitted.length === 0) {
2735
+ process.stderr.write("[debug-sarif] (none)\n");
2736
+ } else {
2737
+ for (const res of debugInfo.resultsEmitted) {
2738
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2739
+ `);
2740
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2741
+ `);
2742
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2743
+ `);
2744
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2745
+ `);
2746
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2747
+ `);
2748
+ process.stderr.write(`[debug-sarif] artifact uri: ${res.normalizedUri}
2749
+ `);
2750
+ }
2751
+ }
2752
+ 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");
2753
+ }
2754
+ if (opts.debugSarifPaths) {
2755
+ const debugInfo = buildSarifDebugInfo(result, repoRoot);
2756
+ process.stderr.write("\n[debug-sarif-paths] Path Normalization Trace\n");
2757
+ process.stderr.write("[debug-sarif-paths] \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");
2758
+ process.stderr.write(`[debug-sarif-paths] GITHUB_WORKSPACE: ${repoRoot ?? "(not set \u2014 local run)"}
2759
+
2760
+ `);
2761
+ if (debugInfo.pathsDebug.length === 0) {
2762
+ process.stderr.write("[debug-sarif-paths] (no files in results)\n");
2763
+ } else {
2764
+ for (const p of debugInfo.pathsDebug) {
2765
+ process.stderr.write(`[debug-sarif-paths] original path: ${p.originalPath}
2766
+ `);
2767
+ process.stderr.write(`[debug-sarif-paths] artifact uri: ${p.normalizedUri}
2768
+ `);
2769
+ process.stderr.write(`[debug-sarif-paths] repo-relative: ${p.isRepositoryRelative ? "\u2705 yes" : "\u274C NO (GitHub may miss this)"}
2770
+ `);
2771
+ if (p.hasBackslashes) process.stderr.write("[debug-sarif-paths] \u26A0 had backslashes \u2014 converted\n");
2772
+ if (p.hasDriveLetter) process.stderr.write("[debug-sarif-paths] \u26A0 had drive letter \u2014 stripped\n");
2773
+ if (p.hasLeadingSlash) process.stderr.write("[debug-sarif-paths] \u26A0 had leading slash \u2014 stripped\n");
2774
+ if (p.hasLeadingDotSlash) process.stderr.write("[debug-sarif-paths] \u26A0 had leading ./ \u2014 stripped\n");
2775
+ process.stderr.write("\n");
2776
+ }
2777
+ }
2778
+ process.stderr.write("[debug-sarif-paths] \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");
2779
+ }
2503
2780
  if (resolvedSarifPath) {
2504
2781
  import_fs5.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
2782
+ if (opts.debugSarif) {
2783
+ process.stderr.write(`[debug-sarif] SARIF written to: ${resolvedSarifPath}
2784
+ `);
2785
+ }
2505
2786
  } else {
2506
2787
  console.log(sarifJson);
2507
2788
  }
@@ -2631,12 +2912,12 @@ function getFailOnExitCode(result, failOn, maxWarnings) {
2631
2912
 
2632
2913
  // cli/commands/init.ts
2633
2914
  var import_chalk6 = __toESM(require("chalk"));
2634
- var import_path7 = __toESM(require("path"));
2915
+ var import_path8 = __toESM(require("path"));
2635
2916
  var import_fs8 = __toESM(require("fs"));
2636
2917
 
2637
2918
  // cli/utils/detector.ts
2638
2919
  var import_fs6 = __toESM(require("fs"));
2639
- var import_path5 = __toESM(require("path"));
2920
+ var import_path6 = __toESM(require("path"));
2640
2921
  var import_module2 = require("module");
2641
2922
  var FLAT_CONFIG_FILES = [
2642
2923
  { file: "eslint.config.js", type: "flat-js" },
@@ -2653,11 +2934,11 @@ var LEGACY_CONFIG_FILES = [
2653
2934
  var ALL_CONFIG_FILES = [...FLAT_CONFIG_FILES, ...LEGACY_CONFIG_FILES];
2654
2935
  function isPackageInstalled(pkgName, cwd = process.cwd()) {
2655
2936
  try {
2656
- const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
2937
+ const pkgPath = import_path6.default.join(cwd, "node_modules", pkgName, "package.json");
2657
2938
  if (import_fs6.default.existsSync(pkgPath)) {
2658
2939
  return true;
2659
2940
  }
2660
- const req = (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json"));
2941
+ const req = (0, import_module2.createRequire)(import_path6.default.join(cwd, "package.json"));
2661
2942
  req.resolve(`${pkgName}/package.json`);
2662
2943
  return true;
2663
2944
  } catch {
@@ -2666,8 +2947,8 @@ function isPackageInstalled(pkgName, cwd = process.cwd()) {
2666
2947
  }
2667
2948
  function getPackageVersion(pkgName, cwd = process.cwd()) {
2668
2949
  try {
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`);
2950
+ const pkgPath = import_path6.default.join(cwd, "node_modules", pkgName, "package.json");
2951
+ const resolvedPath = import_fs6.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path6.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
2671
2952
  const raw = import_fs6.default.readFileSync(resolvedPath, "utf-8");
2672
2953
  const json = JSON.parse(raw);
2673
2954
  return json.version ?? null;
@@ -2684,7 +2965,7 @@ function detectConfigType(cwd = process.cwd()) {
2684
2965
  const found = [];
2685
2966
  let first = null;
2686
2967
  for (const { file, type } of ALL_CONFIG_FILES) {
2687
- const fullPath = import_path5.default.join(cwd, file);
2968
+ const fullPath = import_path6.default.join(cwd, file);
2688
2969
  if (import_fs6.default.existsSync(fullPath)) {
2689
2970
  found.push(fullPath);
2690
2971
  if (!first) first = { type, path: fullPath };
@@ -2764,7 +3045,7 @@ async function verifyConfigLoads(configPath, cwd) {
2764
3045
  overrideConfigFile: configPath
2765
3046
  });
2766
3047
  await eslint.lintText("", {
2767
- filePath: import_path7.default.join(cwd, "_ai_guard_probe_.js")
3048
+ filePath: import_path8.default.join(cwd, "_ai_guard_probe_.js")
2768
3049
  });
2769
3050
  return null;
2770
3051
  } catch (err) {
@@ -2800,7 +3081,7 @@ function registerInitCommand(program2) {
2800
3081
  log.warn("eslint-plugin-ai-guard not found");
2801
3082
  }
2802
3083
  if (env.configType !== "none") {
2803
- log.success(`ESLint config found: ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath ?? env.configType))}`);
3084
+ log.success(`ESLint config found: ${import_chalk6.default.white(import_path8.default.relative(cwd, env.configPath ?? env.configType))}`);
2804
3085
  } else {
2805
3086
  log.info("No ESLint config found \u2014 will generate one");
2806
3087
  }
@@ -2809,7 +3090,7 @@ function registerInitCommand(program2) {
2809
3090
  log.blank();
2810
3091
  log.warn("Conflicting configs detected:");
2811
3092
  for (const p of env.allConfigPaths) {
2812
- log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(import_path7.default.relative(cwd, p))}`);
3093
+ log.print(` ${import_chalk6.default.yellow("\u2192")} ${import_chalk6.default.white(import_path8.default.relative(cwd, p))}`);
2813
3094
  }
2814
3095
  log.print(` ${import_chalk6.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
2815
3096
  }
@@ -2837,7 +3118,7 @@ function registerInitCommand(program2) {
2837
3118
  try {
2838
3119
  const { createRequire: createRequire3 } = await import("module");
2839
3120
  const { pathToFileURL: pathToFileURL2 } = await import("url");
2840
- const req = createRequire3(import_path7.default.join(cwd, "_probe.js"));
3121
+ const req = createRequire3(import_path8.default.join(cwd, "_probe.js"));
2841
3122
  const resolvedPath = req.resolve("eslint-plugin-ai-guard");
2842
3123
  const aiGuard = await import(pathToFileURL2(resolvedPath).href);
2843
3124
  const plugin = aiGuard.default || aiGuard;
@@ -2856,7 +3137,7 @@ function registerInitCommand(program2) {
2856
3137
  if (eslintNeedsFlat && existingIsLegacy) {
2857
3138
  log.section("Config Migration Required");
2858
3139
  log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
2859
- log.print(` ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
3140
+ log.print(` ${import_chalk6.default.white(import_path8.default.relative(cwd, env.configPath))}`);
2860
3141
  log.blank();
2861
3142
  log.print(` ${import_chalk6.default.bold("ai-guard will create a new flat config.")}`);
2862
3143
  log.blank();
@@ -2868,7 +3149,7 @@ function registerInitCommand(program2) {
2868
3149
  const raw = readConfig(env.configPath);
2869
3150
  const { content: fixed, changed } = removeNukeIgnore(raw);
2870
3151
  if (changed) {
2871
- log.warn(`Removed "**/*" ignore pattern from ${import_path7.default.relative(cwd, env.configPath)}`);
3152
+ log.warn(`Removed "**/*" ignore pattern from ${import_path8.default.relative(cwd, env.configPath)}`);
2872
3153
  if (!isDryRun) {
2873
3154
  backupConfig(env.configPath);
2874
3155
  writeConfig(env.configPath, fixed);
@@ -2880,7 +3161,7 @@ function registerInitCommand(program2) {
2880
3161
  }
2881
3162
  if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
2882
3163
  const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
2883
- finalConfigPath = import_path7.default.join(cwd, targetName);
3164
+ finalConfigPath = import_path8.default.join(cwd, targetName);
2884
3165
  const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
2885
3166
  if (isDryRun) {
2886
3167
  log.print(` ${import_chalk6.default.gray(`[dry-run] would create: ${targetName}`)}`);
@@ -2908,15 +3189,15 @@ function registerInitCommand(program2) {
2908
3189
  finalConfigPath = env.configPath;
2909
3190
  } else if (isDryRun) {
2910
3191
  finalConfigPath = env.configPath;
2911
- log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
3192
+ log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path8.default.relative(cwd, env.configPath)}`)}`);
2912
3193
  } else {
2913
3194
  const bak = backupConfig(env.configPath);
2914
- log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
3195
+ log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path8.default.relative(cwd, bak))}`);
2915
3196
  writeConfig(env.configPath, patched);
2916
3197
  if (isInvalid) {
2917
- log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
3198
+ log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk6.default.white(import_path8.default.relative(cwd, env.configPath))}`);
2918
3199
  } else {
2919
- log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
3200
+ log.success(`Patched ${import_chalk6.default.white(import_path8.default.relative(cwd, env.configPath))}`);
2920
3201
  }
2921
3202
  finalConfigPath = env.configPath;
2922
3203
  configWritten = true;
@@ -2929,12 +3210,12 @@ function registerInitCommand(program2) {
2929
3210
  finalConfigPath = env.configPath;
2930
3211
  } else if (isDryRun) {
2931
3212
  finalConfigPath = env.configPath;
2932
- log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path7.default.relative(cwd, env.configPath)}`)}`);
3213
+ log.print(` ${import_chalk6.default.gray(`[dry-run] would patch: ${import_path8.default.relative(cwd, env.configPath)}`)}`);
2933
3214
  } else {
2934
3215
  const bak = backupConfig(env.configPath);
2935
- log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, bak))}`);
3216
+ log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path8.default.relative(cwd, bak))}`);
2936
3217
  writeConfig(env.configPath, patched);
2937
- log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, env.configPath))}`);
3218
+ log.success(`Patched ${import_chalk6.default.white(import_path8.default.relative(cwd, env.configPath))}`);
2938
3219
  finalConfigPath = env.configPath;
2939
3220
  configWritten = true;
2940
3221
  }
@@ -2950,11 +3231,11 @@ function registerInitCommand(program2) {
2950
3231
  cleanedUp = true;
2951
3232
  } catch (err) {
2952
3233
  const reason = err instanceof Error ? err.message : String(err);
2953
- log.warn(`Could not back up ${import_path7.default.relative(cwd, p)}: ${reason}`);
3234
+ log.warn(`Could not back up ${import_path8.default.relative(cwd, p)}: ${reason}`);
2954
3235
  }
2955
3236
  } else {
2956
3237
  cleanedUp = true;
2957
- log.print(` ${import_chalk6.default.gray(`[dry-run] would rename ${import_path7.default.relative(cwd, p)} to ${import_path7.default.basename(p)}.bak`)}`);
3238
+ log.print(` ${import_chalk6.default.gray(`[dry-run] would rename ${import_path8.default.relative(cwd, p)} to ${import_path8.default.basename(p)}.bak`)}`);
2958
3239
  }
2959
3240
  }
2960
3241
  }
@@ -2973,7 +3254,7 @@ function registerInitCommand(program2) {
2973
3254
  log.blank();
2974
3255
  log.section("Validation");
2975
3256
  if (!import_fs8.default.existsSync(finalConfigPath)) {
2976
- log.error(`Config file not found after write: ${import_path7.default.relative(cwd, finalConfigPath)}`);
3257
+ log.error(`Config file not found after write: ${import_path8.default.relative(cwd, finalConfigPath)}`);
2977
3258
  log.blank();
2978
3259
  log.print(` ${import_chalk6.default.bold("Problem:")} File write may have failed.`);
2979
3260
  log.print(` ${import_chalk6.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
@@ -2989,7 +3270,7 @@ function registerInitCommand(program2) {
2989
3270
  log.print(` ${import_chalk6.default.red("\u2716")} ${e}`);
2990
3271
  }
2991
3272
  } else {
2992
- log.success(`Config structure valid: ${import_chalk6.default.white(import_path7.default.relative(cwd, finalConfigPath))}`);
3273
+ log.success(`Config structure valid: ${import_chalk6.default.white(import_path8.default.relative(cwd, finalConfigPath))}`);
2993
3274
  }
2994
3275
  log.info("Verifying ESLint can load the config\u2026");
2995
3276
  const loadError = await verifyConfigLoads(finalConfigPath, cwd);
@@ -3013,7 +3294,7 @@ function registerInitCommand(program2) {
3013
3294
  }
3014
3295
  log.blank();
3015
3296
  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))}`);
3297
+ log.print(` 1. Check the syntax in ${import_chalk6.default.cyan(import_path8.default.relative(cwd, finalConfigPath))}`);
3017
3298
  log.print(` 2. Make sure ${import_chalk6.default.cyan("eslint-plugin-ai-guard")} is installed`);
3018
3299
  log.print(` 3. Run ${import_chalk6.default.cyan("ai-guard doctor")} for full diagnostics`);
3019
3300
  log.blank();
@@ -3035,7 +3316,7 @@ function registerInitCommand(program2) {
3035
3316
  if (loadError === null && !lingeringConflicts) {
3036
3317
  log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold.green("Configuration validated successfully")}`);
3037
3318
  }
3038
- log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("ESLint config:")} ${import_chalk6.default.cyan(import_path7.default.relative(cwd, finalConfigPath))}`);
3319
+ log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("ESLint config:")} ${import_chalk6.default.cyan(import_path8.default.relative(cwd, finalConfigPath))}`);
3039
3320
  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
3321
  log.print(` ${import_chalk6.default.green("\u2714")} ${import_chalk6.default.bold("Preset:")} ${import_chalk6.default.cyan(preset)}`);
3041
3322
  log.blank();
@@ -3049,14 +3330,14 @@ function registerInitCommand(program2) {
3049
3330
 
3050
3331
  // cli/commands/doctor.ts
3051
3332
  var import_chalk7 = __toESM(require("chalk"));
3052
- var import_path8 = __toESM(require("path"));
3333
+ var import_path9 = __toESM(require("path"));
3053
3334
  init_config_manager();
3054
3335
  var import_meta2 = {};
3055
3336
  async function probeConfigLoad(configPath, cwd) {
3056
3337
  try {
3057
3338
  const { ESLint } = await import("eslint");
3058
3339
  const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
3059
- await eslint.lintText("", { filePath: import_path8.default.join(cwd, "_probe_.js") });
3340
+ await eslint.lintText("", { filePath: import_path9.default.join(cwd, "_probe_.js") });
3060
3341
  return { ok: true, error: null };
3061
3342
  } catch (err) {
3062
3343
  const msg = err instanceof Error ? err.message : String(err);
@@ -3091,7 +3372,7 @@ function registerDoctorCommand(program2) {
3091
3372
  checks.push({
3092
3373
  label: "ESLint config present",
3093
3374
  pass: hasConfig,
3094
- detail: hasConfig ? `${import_path8.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
3375
+ detail: hasConfig ? `${import_path9.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
3095
3376
  fix: hasConfig ? void 0 : "ai-guard init",
3096
3377
  note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
3097
3378
  });
@@ -3123,7 +3404,7 @@ function registerDoctorCommand(program2) {
3123
3404
  checks.push({
3124
3405
  label: "Plugin wired in config",
3125
3406
  pass: pluginWired && !isInvalidConfig,
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)}`,
3407
+ 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_path9.default.relative(cwd, env.configPath)}`,
3127
3408
  fix: isInvalidConfig || !pluginWired && !readError ? "ai-guard init (will patch/repair your existing config)" : void 0
3128
3409
  });
3129
3410
  }
@@ -3131,7 +3412,7 @@ function registerDoctorCommand(program2) {
3131
3412
  label: "No conflicting config files",
3132
3413
  pass: !env.hasConflictingConfigs,
3133
3414
  detail: env.hasConflictingConfigs ? `Conflicting config detected:
3134
- ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
3415
+ ${env.allConfigPaths.map((p) => ` ${import_path9.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
3135
3416
  fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
3136
3417
  note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
3137
3418
  });
@@ -3158,7 +3439,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}
3158
3439
  label: "ESLint config loads without errors",
3159
3440
  pass: probe.ok,
3160
3441
  detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
3161
- fix: probe.ok ? void 0 : `Check syntax in ${import_path8.default.relative(cwd, env.configPath)}`,
3442
+ fix: probe.ok ? void 0 : `Check syntax in ${import_path9.default.relative(cwd, env.configPath)}`,
3162
3443
  note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
3163
3444
  });
3164
3445
  }
@@ -3167,7 +3448,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}
3167
3448
  let utilsVersion = null;
3168
3449
  try {
3169
3450
  const { createRequire: createRequire3 } = await import("module");
3170
- const req = createRequire3(import_path8.default.join(cwd, "package.json"));
3451
+ const req = createRequire3(import_path9.default.join(cwd, "package.json"));
3171
3452
  const pkgPath = req.resolve("@typescript-eslint/utils/package.json");
3172
3453
  const pkg = JSON.parse(
3173
3454
  (await import("fs")).readFileSync(pkgPath, "utf-8")
@@ -3178,10 +3459,10 @@ ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}
3178
3459
  }
3179
3460
  if (!utilsOk) {
3180
3461
  try {
3181
- const selfDir = import_path8.default.dirname(
3462
+ const selfDir = import_path9.default.dirname(
3182
3463
  new URL(import_meta2.url).pathname.replace(/^\/([A-Za-z]:)/, "$1")
3183
3464
  );
3184
- const pkgPath = import_path8.default.join(selfDir, "../../node_modules/@typescript-eslint/utils/package.json");
3465
+ const pkgPath = import_path9.default.join(selfDir, "../../node_modules/@typescript-eslint/utils/package.json");
3185
3466
  if ((await import("fs")).existsSync(pkgPath)) {
3186
3467
  utilsOk = true;
3187
3468
  }
@@ -3201,7 +3482,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}
3201
3482
  let parserVersion = null;
3202
3483
  try {
3203
3484
  const { createRequire: createRequire3 } = await import("module");
3204
- const req = createRequire3(import_path8.default.join(cwd, "package.json"));
3485
+ const req = createRequire3(import_path9.default.join(cwd, "package.json"));
3205
3486
  const pkgPath = req.resolve("@typescript-eslint/parser/package.json");
3206
3487
  const pkg = JSON.parse(
3207
3488
  (await import("fs")).readFileSync(pkgPath, "utf-8")
@@ -3289,7 +3570,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path8.default.relative(cwd, p)}
3289
3570
  var import_prompts = require("@inquirer/prompts");
3290
3571
  var import_chalk8 = __toESM(require("chalk"));
3291
3572
  init_config_manager();
3292
- var import_path9 = __toESM(require("path"));
3573
+ var import_path10 = __toESM(require("path"));
3293
3574
  function registerPresetCommand(program2) {
3294
3575
  program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
3295
3576
  const cwd = process.cwd();
@@ -3317,14 +3598,14 @@ function registerPresetCommand(program2) {
3317
3598
  const env = detect(cwd);
3318
3599
  const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
3319
3600
  if (env.configType === "none") {
3320
- const configPath = useFlat ? import_path9.default.join(cwd, "eslint.config.mjs") : import_path9.default.join(cwd, ".eslintrc.js");
3601
+ const configPath = useFlat ? import_path10.default.join(cwd, "eslint.config.mjs") : import_path10.default.join(cwd, ".eslintrc.js");
3321
3602
  const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
3322
3603
  writeConfig(configPath, content);
3323
- log.success(`Created ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} with ${import_chalk8.default.cyan(preset)} preset`);
3604
+ log.success(`Created ${import_chalk8.default.white(import_path10.default.relative(cwd, configPath))} with ${import_chalk8.default.cyan(preset)} preset`);
3324
3605
  } else {
3325
3606
  const configPath = env.configPath;
3326
3607
  const backupPath = backupConfig(configPath);
3327
- log.info(`Backed up \u2192 ${import_chalk8.default.gray(import_path9.default.relative(cwd, backupPath))}`);
3608
+ log.info(`Backed up \u2192 ${import_chalk8.default.gray(import_path10.default.relative(cwd, backupPath))}`);
3328
3609
  const existing = readConfig(configPath);
3329
3610
  const withPlugin = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
3330
3611
  const patched = isFlat(env.configType) ? switchFlatPreset(withPlugin, preset) : switchLegacyPreset(withPlugin, preset);
@@ -3332,7 +3613,7 @@ function registerPresetCommand(program2) {
3332
3613
  log.warn("No preset changes needed. Config already matches selected preset.");
3333
3614
  } else {
3334
3615
  writeConfig(configPath, patched);
3335
- log.success(`Updated ${import_chalk8.default.white(import_path9.default.relative(cwd, configPath))} to ${import_chalk8.default.cyan(preset)} preset`);
3616
+ log.success(`Updated ${import_chalk8.default.white(import_path10.default.relative(cwd, configPath))} to ${import_chalk8.default.cyan(preset)} preset`);
3336
3617
  }
3337
3618
  }
3338
3619
  log.blank();
@@ -3372,7 +3653,7 @@ function registerPresetCommand(program2) {
3372
3653
  // cli/commands/ignore.ts
3373
3654
  var import_chalk9 = __toESM(require("chalk"));
3374
3655
  init_config_manager();
3375
- var import_path10 = __toESM(require("path"));
3656
+ var import_path11 = __toESM(require("path"));
3376
3657
  var DEFAULT_IGNORES = [
3377
3658
  ".next/",
3378
3659
  "dist/",
@@ -3394,21 +3675,21 @@ function registerIgnoreCommand(program2) {
3394
3675
  const env = detect(cwd);
3395
3676
  if (env.configType === "none") {
3396
3677
  const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
3397
- const configPath = useFlat ? import_path10.default.join(cwd, "eslint.config.mjs") : import_path10.default.join(cwd, ".eslintrc.js");
3678
+ const configPath = useFlat ? import_path11.default.join(cwd, "eslint.config.mjs") : import_path11.default.join(cwd, ".eslintrc.js");
3398
3679
  const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
3399
3680
  writeConfig(configPath, content);
3400
- log.success(`No config found \u2014 generated ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignores included`);
3681
+ log.success(`No config found \u2014 generated ${import_chalk9.default.white(import_path11.default.relative(cwd, configPath))} with ignores included`);
3401
3682
  } else {
3402
3683
  const configPath = env.configPath;
3403
3684
  const backupPath = backupConfig(configPath);
3404
- log.info(`Backed up \u2192 ${import_chalk9.default.gray(import_path10.default.relative(cwd, backupPath))}`);
3685
+ log.info(`Backed up \u2192 ${import_chalk9.default.gray(import_path11.default.relative(cwd, backupPath))}`);
3405
3686
  const existing = readConfig(configPath);
3406
3687
  const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
3407
3688
  if (patched === existing) {
3408
3689
  log.warn("Ignore patterns already present in config \u2014 no changes made.");
3409
3690
  } else {
3410
3691
  writeConfig(configPath, patched);
3411
- log.success(`Patched ${import_chalk9.default.white(import_path10.default.relative(cwd, configPath))} with ignore patterns`);
3692
+ log.success(`Patched ${import_chalk9.default.white(import_path11.default.relative(cwd, configPath))} with ignore patterns`);
3412
3693
  }
3413
3694
  }
3414
3695
  log.blank();
@@ -3420,7 +3701,7 @@ function registerIgnoreCommand(program2) {
3420
3701
 
3421
3702
  // cli/commands/baseline.ts
3422
3703
  var import_fs9 = __toESM(require("fs"));
3423
- var import_path11 = __toESM(require("path"));
3704
+ var import_path12 = __toESM(require("path"));
3424
3705
  var import_chalk10 = __toESM(require("chalk"));
3425
3706
  var import_ora3 = __toESM(require("ora"));
3426
3707
  var BASELINE_FILE = ".ai-guard-baseline.json";
@@ -3441,12 +3722,12 @@ function saveBaseline(result, preset, mode, cwd) {
3441
3722
  totalIssues: result.totalIssues,
3442
3723
  entries
3443
3724
  };
3444
- const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
3725
+ const baselinePath = import_path12.default.join(cwd, BASELINE_FILE);
3445
3726
  import_fs9.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
3446
3727
  return baselinePath;
3447
3728
  }
3448
3729
  function loadBaseline(cwd) {
3449
- const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
3730
+ const baselinePath = import_path12.default.join(cwd, BASELINE_FILE);
3450
3731
  if (!import_fs9.default.existsSync(baselinePath)) return null;
3451
3732
  try {
3452
3733
  const raw = import_fs9.default.readFileSync(baselinePath, "utf-8");
@@ -3533,7 +3814,7 @@ function registerBaselineCommand(program2) {
3533
3814
  }
3534
3815
  const baselinePath = saveBaseline(result, preset, mode, cwd);
3535
3816
  log.success(
3536
- `Baseline saved \u2192 ${import_chalk10.default.white(import_path11.default.relative(cwd, baselinePath))}`
3817
+ `Baseline saved \u2192 ${import_chalk10.default.white(import_path12.default.relative(cwd, baselinePath))}`
3537
3818
  );
3538
3819
  log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
3539
3820
  log.info(` Preset: ${import_chalk10.default.cyan(preset)}`);
@@ -3617,7 +3898,7 @@ function registerBaselineCommand(program2) {
3617
3898
 
3618
3899
  // cli/commands/init-context.ts
3619
3900
  var import_fs10 = __toESM(require("fs"));
3620
- var import_path12 = __toESM(require("path"));
3901
+ var import_path13 = __toESM(require("path"));
3621
3902
  var import_chalk11 = __toESM(require("chalk"));
3622
3903
  var import_prompts2 = require("@inquirer/prompts");
3623
3904
  var RULE_CATEGORIES = {
@@ -3981,13 +4262,13 @@ var AGENTS = [
3981
4262
  {
3982
4263
  id: "copilot",
3983
4264
  label: "GitHub Copilot",
3984
- filePath: import_path12.default.join(".github", "copilot-instructions.md"),
4265
+ filePath: import_path13.default.join(".github", "copilot-instructions.md"),
3985
4266
  generate: generateCopilotFile
3986
4267
  }
3987
4268
  ];
3988
4269
  function getPackageVersion2(cwd) {
3989
4270
  try {
3990
- const pkgPath = import_path12.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
4271
+ const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
3991
4272
  if (import_fs10.default.existsSync(pkgPath)) {
3992
4273
  const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
3993
4274
  if (pkg.version) return pkg.version;
@@ -3995,7 +4276,7 @@ function getPackageVersion2(cwd) {
3995
4276
  } catch {
3996
4277
  }
3997
4278
  try {
3998
- const pkgPath = import_path12.default.join(cwd, "package.json");
4279
+ const pkgPath = import_path13.default.join(cwd, "package.json");
3999
4280
  if (import_fs10.default.existsSync(pkgPath)) {
4000
4281
  const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
4001
4282
  if (pkg.version) return pkg.version;
@@ -4064,7 +4345,7 @@ function registerInitContextCommand(program2) {
4064
4345
  log.blank();
4065
4346
  const results = [];
4066
4347
  for (const agent of selectedAgents) {
4067
- const fullPath = import_path12.default.join(cwd, agent.filePath);
4348
+ const fullPath = import_path13.default.join(cwd, agent.filePath);
4068
4349
  const exists = import_fs10.default.existsSync(fullPath);
4069
4350
  if (exists && !isForce && !isDryRun) {
4070
4351
  const overwrite = await (0, import_prompts2.confirm)({
@@ -4089,7 +4370,7 @@ function registerInitContextCommand(program2) {
4089
4370
  results.push({ file: agent.filePath, status: "dry-run" });
4090
4371
  continue;
4091
4372
  }
4092
- const parentDir = import_path12.default.dirname(fullPath);
4373
+ const parentDir = import_path13.default.dirname(fullPath);
4093
4374
  if (!import_fs10.default.existsSync(parentDir)) {
4094
4375
  import_fs10.default.mkdirSync(parentDir, { recursive: true });
4095
4376
  }
@@ -4128,7 +4409,7 @@ function registerInitContextCommand(program2) {
4128
4409
 
4129
4410
  // cli/commands/report.ts
4130
4411
  var import_fs11 = __toESM(require("fs"));
4131
- var import_path13 = __toESM(require("path"));
4412
+ var import_path14 = __toESM(require("path"));
4132
4413
  var import_ora4 = __toESM(require("ora"));
4133
4414
  var RULE_META = {
4134
4415
  "ai-guard/no-empty-catch": { description: "Empty catch blocks silently swallow errors", category: "Reliability" },
@@ -4152,18 +4433,18 @@ var RULE_META = {
4152
4433
  };
4153
4434
  function getProjectName(cwd) {
4154
4435
  try {
4155
- const pkgPath = import_path13.default.join(cwd, "package.json");
4436
+ const pkgPath = import_path14.default.join(cwd, "package.json");
4156
4437
  if (import_fs11.default.existsSync(pkgPath)) {
4157
4438
  const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4158
4439
  if (pkg.name) return pkg.name;
4159
4440
  }
4160
4441
  } catch {
4161
4442
  }
4162
- return import_path13.default.basename(cwd);
4443
+ return import_path14.default.basename(cwd);
4163
4444
  }
4164
4445
  function getPluginVersion(cwd) {
4165
4446
  try {
4166
- const pkgPath = import_path13.default.resolve(__dirname, "../../package.json");
4447
+ const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
4167
4448
  if (import_fs11.default.existsSync(pkgPath)) {
4168
4449
  const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4169
4450
  if (pkg.version) return pkg.version;
@@ -4171,7 +4452,7 @@ function getPluginVersion(cwd) {
4171
4452
  } catch {
4172
4453
  }
4173
4454
  try {
4174
- const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
4455
+ const pkgPath = import_path14.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
4175
4456
  if (import_fs11.default.existsSync(pkgPath)) {
4176
4457
  const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
4177
4458
  if (pkg.version) return pkg.version;
@@ -4550,7 +4831,7 @@ function registerReportCommand(program2) {
4550
4831
  log.banner("AI GUARD REPORT");
4551
4832
  log.blank();
4552
4833
  const html = generateHtml(reportData);
4553
- const outputPath = import_path13.default.resolve(cwd, opts.output);
4834
+ const outputPath = import_path14.default.resolve(cwd, opts.output);
4554
4835
  import_fs11.default.writeFileSync(outputPath, html, "utf-8");
4555
4836
  log.success(`Report saved: ${opts.output}`);
4556
4837
  log.info(
@@ -4584,7 +4865,7 @@ function registerReportCommand(program2) {
4584
4865
  // cli/index.ts
4585
4866
  function getVersion() {
4586
4867
  try {
4587
- const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
4868
+ const pkgPath = import_path15.default.resolve(__dirname, "../../package.json");
4588
4869
  const raw = import_fs12.default.readFileSync(pkgPath, "utf-8");
4589
4870
  const pkg = JSON.parse(raw);
4590
4871
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";