eslint-plugin-ai-guard 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1305,29 +1305,29 @@ function getMemberPath(node) {
1305
1305
  }
1306
1306
  return [...objectPath, node.property.name];
1307
1307
  }
1308
- function hasPathPrefix(path8, prefix) {
1309
- if (!path8 || path8.length < prefix.length) {
1308
+ function hasPathPrefix(path9, prefix) {
1309
+ if (!path9 || path9.length < prefix.length) {
1310
1310
  return false;
1311
1311
  }
1312
1312
  for (let i = 0; i < prefix.length; i += 1) {
1313
- if (path8[i] !== prefix[i]) {
1313
+ if (path9[i] !== prefix[i]) {
1314
1314
  return false;
1315
1315
  }
1316
1316
  }
1317
1317
  return true;
1318
1318
  }
1319
- function isLikelyResourceIdPath(path8) {
1320
- if (!path8 || path8.length < 3) {
1319
+ function isLikelyResourceIdPath(path9) {
1320
+ if (!path9 || path9.length < 3) {
1321
1321
  return false;
1322
1322
  }
1323
- if (!hasPathPrefix(path8, ["req", "params"]) && !hasPathPrefix(path8, ["req", "body"]) && !hasPathPrefix(path8, ["req", "query"])) {
1323
+ if (!hasPathPrefix(path9, ["req", "params"]) && !hasPathPrefix(path9, ["req", "body"]) && !hasPathPrefix(path9, ["req", "query"])) {
1324
1324
  return false;
1325
1325
  }
1326
- const last = path8[path8.length - 1].toLowerCase();
1326
+ const last = path9[path9.length - 1].toLowerCase();
1327
1327
  return last === "id" || last.endsWith("id");
1328
1328
  }
1329
- function isReqUserPath(path8) {
1330
- return hasPathPrefix(path8, ["req", "user"]);
1329
+ function isReqUserPath(path9) {
1330
+ return hasPathPrefix(path9, ["req", "user"]);
1331
1331
  }
1332
1332
  function containsAuthorizationHelper(node) {
1333
1333
  if (node.type === import_utils15.AST_NODE_TYPES.CallExpression && (node.callee.type === import_utils15.AST_NODE_TYPES.Identifier && AUTHZ_HELPER_NAMES.includes(node.callee.name) || node.callee.type === import_utils15.AST_NODE_TYPES.MemberExpression && node.callee.property.type === import_utils15.AST_NODE_TYPES.Identifier && AUTHZ_HELPER_NAMES.includes(node.callee.property.name))) {
@@ -1366,8 +1366,8 @@ function collectBodySignals(node) {
1366
1366
  }
1367
1367
  }
1368
1368
  if (current.type === import_utils15.AST_NODE_TYPES.MemberExpression) {
1369
- const path8 = getMemberPath(current);
1370
- if (isLikelyResourceIdPath(path8)) {
1369
+ const path9 = getMemberPath(current);
1370
+ if (isLikelyResourceIdPath(path9)) {
1371
1371
  hasResourceIdAccess = true;
1372
1372
  }
1373
1373
  }
@@ -1819,6 +1819,285 @@ var init_src = __esm({
1819
1819
  }
1820
1820
  });
1821
1821
 
1822
+ // cli/utils/config-manager.ts
1823
+ var config_manager_exports = {};
1824
+ __export(config_manager_exports, {
1825
+ addIgnoresToFlatConfig: () => addIgnoresToFlatConfig,
1826
+ addIgnoresToLegacyConfig: () => addIgnoresToLegacyConfig,
1827
+ backupConfig: () => backupConfig,
1828
+ generateFlatConfig: () => generateFlatConfig,
1829
+ generateLegacyConfig: () => generateLegacyConfig,
1830
+ getConfigFilePath: () => getConfigFilePath,
1831
+ isInvalidAiGuardConfig: () => isInvalidAiGuardConfig,
1832
+ patchFlatConfig: () => patchFlatConfig,
1833
+ patchLegacyConfig: () => patchLegacyConfig,
1834
+ readConfig: () => readConfig,
1835
+ removeNukeIgnore: () => removeNukeIgnore,
1836
+ repairInvalidFlatConfig: () => repairInvalidFlatConfig,
1837
+ validateFlatConfigText: () => validateFlatConfigText,
1838
+ validateLegacyConfigText: () => validateLegacyConfigText,
1839
+ writeConfig: () => writeConfig
1840
+ });
1841
+ function backupConfig(configPath) {
1842
+ const backupPath = `${configPath}.bak`;
1843
+ import_fs2.default.copyFileSync(configPath, backupPath);
1844
+ return backupPath;
1845
+ }
1846
+ function readConfig(configPath) {
1847
+ return import_fs2.default.readFileSync(configPath, "utf-8");
1848
+ }
1849
+ function writeConfig(configPath, content) {
1850
+ import_fs2.default.writeFileSync(configPath, content, "utf-8");
1851
+ }
1852
+ function generateFlatConfig(preset) {
1853
+ return `import aiGuard from 'eslint-plugin-ai-guard';
1854
+
1855
+ export default [
1856
+ // Ignore generated / dependency directories
1857
+ {
1858
+ ignores: [
1859
+ 'node_modules/**',
1860
+ '.next/**',
1861
+ 'dist/**',
1862
+ 'build/**',
1863
+ 'coverage/**',
1864
+ 'out/**',
1865
+ ],
1866
+ },
1867
+
1868
+ // ai-guard: catch AI-generated code patterns
1869
+ {
1870
+ plugins: {
1871
+ 'ai-guard': aiGuard.default,
1872
+ },
1873
+ rules: {
1874
+ ...aiGuard.${preset}.rules,
1875
+ },
1876
+ },
1877
+ ];
1878
+ `;
1879
+ }
1880
+ function generateLegacyConfig(preset) {
1881
+ return `module.exports = {
1882
+ plugins: ['ai-guard'],
1883
+ extends: ['plugin:ai-guard/${preset}'],
1884
+ ignorePatterns: [
1885
+ 'node_modules/',
1886
+ '.next/',
1887
+ 'dist/',
1888
+ 'build/',
1889
+ 'coverage/',
1890
+ 'out/',
1891
+ ],
1892
+ };
1893
+ `;
1894
+ }
1895
+ function isInvalidAiGuardConfig(content) {
1896
+ if (!content.includes("eslint-plugin-ai-guard") && !content.includes("ai-guard")) {
1897
+ return false;
1898
+ }
1899
+ if (content.includes(".configs.")) return true;
1900
+ if (/(['"]ai-guard['"]\s*:\s*aiGuard)(?!\.default)/.test(content)) return true;
1901
+ return false;
1902
+ }
1903
+ function repairInvalidFlatConfig(content) {
1904
+ let repaired = content;
1905
+ repaired = repaired.replace(/(['"]ai-guard['"]\s*:\s*aiGuard)(?!\.default)/g, "$1.default");
1906
+ repaired = repaired.replace(/rules\s*:\s*aiGuard\.configs\.([a-zA-Z0-9_]+)\.rules(,?)/g, "rules: {\n ...aiGuard.$1.rules,\n }$2");
1907
+ repaired = repaired.replace(/\.\.\.aiGuard\.configs\.([a-zA-Z0-9_]+)\.rules/g, "...aiGuard.$1.rules");
1908
+ return repaired;
1909
+ }
1910
+ function patchFlatConfig(existing, preset) {
1911
+ if (isInvalidAiGuardConfig(existing)) {
1912
+ return repairInvalidFlatConfig(existing);
1913
+ }
1914
+ if (existing.includes(AI_GUARD_MARKER)) {
1915
+ return existing;
1916
+ }
1917
+ const importLine = `import aiGuard from 'eslint-plugin-ai-guard';
1918
+ `;
1919
+ const rulesBlock = `
1920
+ // ai-guard injected by ai-guard CLI
1921
+ {
1922
+ plugins: {
1923
+ 'ai-guard': aiGuard.default,
1924
+ },
1925
+ rules: {
1926
+ ...aiGuard.${preset}.rules,
1927
+ },
1928
+ },
1929
+ `;
1930
+ let patched = existing;
1931
+ const lastImportIdx = findLastImportEnd(patched);
1932
+ if (lastImportIdx > -1) {
1933
+ patched = patched.slice(0, lastImportIdx) + importLine + patched.slice(lastImportIdx);
1934
+ } else {
1935
+ patched = importLine + patched;
1936
+ }
1937
+ const lastBracket = patched.lastIndexOf("];");
1938
+ if (lastBracket !== -1) {
1939
+ patched = patched.slice(0, lastBracket) + rulesBlock + patched.slice(lastBracket);
1940
+ }
1941
+ return patched;
1942
+ }
1943
+ function findLastImportEnd(src) {
1944
+ const lines = src.split("\n");
1945
+ let lastImportLine = -1;
1946
+ let charCount = 0;
1947
+ for (let i = 0; i < lines.length; i++) {
1948
+ const line = lines[i];
1949
+ if (/^\s*import\s+/.test(line)) lastImportLine = i;
1950
+ charCount += line.length + 1;
1951
+ }
1952
+ if (lastImportLine === -1) return -1;
1953
+ let offset = 0;
1954
+ for (let i = 0; i <= lastImportLine; i++) {
1955
+ offset += lines[i].length + 1;
1956
+ }
1957
+ return offset;
1958
+ }
1959
+ function patchLegacyConfig(existing, preset) {
1960
+ if (existing.includes("ai-guard")) {
1961
+ return existing;
1962
+ }
1963
+ let patched = existing;
1964
+ if (patched.includes("plugins:")) {
1965
+ patched = patched.replace(/plugins:\s*\[/, `plugins: ['ai-guard', `);
1966
+ } else {
1967
+ patched = patched.replace(
1968
+ "module.exports = {",
1969
+ `module.exports = {
1970
+ plugins: ['ai-guard'],`
1971
+ );
1972
+ }
1973
+ if (patched.includes("extends:")) {
1974
+ patched = patched.replace(
1975
+ /extends:\s*\[/,
1976
+ `extends: ['plugin:ai-guard/${preset}', `
1977
+ );
1978
+ } else {
1979
+ patched = patched.replace(
1980
+ "module.exports = {",
1981
+ `module.exports = {
1982
+ extends: ['plugin:ai-guard/${preset}'],`
1983
+ );
1984
+ }
1985
+ return patched;
1986
+ }
1987
+ function removeNukeIgnore(existing) {
1988
+ const nukePatterns = [/"?\*\*\/\*"?/g];
1989
+ const hasNuke = existing.includes('"**/*"') || existing.includes("'**/*'");
1990
+ if (!hasNuke) return { content: existing, changed: false };
1991
+ let patched = existing.replace(
1992
+ /ignorePatterns:\s*\[.*?\*\*\/\*.*?\]/gs,
1993
+ `ignorePatterns: ['node_modules/', '.next/', 'dist/', 'build/', 'coverage/']`
1994
+ ).replace(
1995
+ /ignores:\s*\[.*?\*\*\/\*.*?\]/gs,
1996
+ `ignores: ['node_modules/**', '.next/**', 'dist/**', 'build/**', 'coverage/**']`
1997
+ );
1998
+ for (const re of nukePatterns) {
1999
+ patched = patched.replace(re, "");
2000
+ }
2001
+ return { content: patched, changed: patched !== existing };
2002
+ }
2003
+ function addIgnoresToFlatConfig(existing) {
2004
+ if (existing.includes("ignores:")) return existing;
2005
+ const block = `
2006
+ // Default ignores added by ai-guard CLI
2007
+ {
2008
+ ignores: ${JSON.stringify(DEFAULT_FLAT_IGNORES)},
2009
+ },
2010
+ `;
2011
+ const lastBracket = existing.lastIndexOf("];");
2012
+ if (lastBracket !== -1) {
2013
+ return existing.slice(0, lastBracket) + block + existing.slice(lastBracket);
2014
+ }
2015
+ return existing;
2016
+ }
2017
+ function addIgnoresToLegacyConfig(existing) {
2018
+ if (existing.includes("ignorePatterns")) return existing;
2019
+ return existing.replace(
2020
+ "module.exports = {",
2021
+ `module.exports = {
2022
+ ignorePatterns: ${JSON.stringify(DEFAULT_LEGACY_IGNORES)},`
2023
+ );
2024
+ }
2025
+ function getConfigFilePath(configType, cwd = process.cwd()) {
2026
+ switch (configType) {
2027
+ case "flat-js":
2028
+ return import_path3.default.join(cwd, "eslint.config.js");
2029
+ case "flat-mjs":
2030
+ return import_path3.default.join(cwd, "eslint.config.mjs");
2031
+ case "flat-cjs":
2032
+ return import_path3.default.join(cwd, "eslint.config.cjs");
2033
+ case "eslintrc-js":
2034
+ return import_path3.default.join(cwd, ".eslintrc.js");
2035
+ case "eslintrc-cjs":
2036
+ return import_path3.default.join(cwd, ".eslintrc.cjs");
2037
+ case "eslintrc-json":
2038
+ return import_path3.default.join(cwd, ".eslintrc.json");
2039
+ default:
2040
+ return import_path3.default.join(cwd, ".eslintrc.js");
2041
+ }
2042
+ }
2043
+ function validateFlatConfigText(content) {
2044
+ const problems = [];
2045
+ if (!content.includes("export default")) {
2046
+ problems.push("Missing `export default` \u2014 file must be an ES module");
2047
+ }
2048
+ if (!content.includes("eslint-plugin-ai-guard") && !content.includes("aiGuard")) {
2049
+ problems.push("Plugin import not found \u2014 ai-guard plugin may not be referenced");
2050
+ }
2051
+ if (!content.trim().endsWith("];") && !content.trim().endsWith("];")) {
2052
+ }
2053
+ if (content.includes('"**/*"') || content.includes("'**/*'")) {
2054
+ problems.push(
2055
+ 'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
2056
+ );
2057
+ }
2058
+ return problems;
2059
+ }
2060
+ function validateLegacyConfigText(content) {
2061
+ const problems = [];
2062
+ if (!content.includes("module.exports")) {
2063
+ problems.push("Missing `module.exports` \u2014 legacy config must use CJS exports");
2064
+ }
2065
+ if (!content.includes("ai-guard")) {
2066
+ problems.push("Plugin not referenced \u2014 add ai-guard to plugins and extends");
2067
+ }
2068
+ if (content.includes('"**/*"') || content.includes("'**/*'")) {
2069
+ problems.push(
2070
+ 'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
2071
+ );
2072
+ }
2073
+ return problems;
2074
+ }
2075
+ var import_fs2, import_path3, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
2076
+ var init_config_manager = __esm({
2077
+ "cli/utils/config-manager.ts"() {
2078
+ "use strict";
2079
+ import_fs2 = __toESM(require("fs"));
2080
+ import_path3 = __toESM(require("path"));
2081
+ AI_GUARD_MARKER = "eslint-plugin-ai-guard";
2082
+ DEFAULT_FLAT_IGNORES = [
2083
+ "node_modules/**",
2084
+ ".next/**",
2085
+ "dist/**",
2086
+ "build/**",
2087
+ "coverage/**",
2088
+ "out/**"
2089
+ ];
2090
+ DEFAULT_LEGACY_IGNORES = [
2091
+ "node_modules/",
2092
+ ".next/",
2093
+ "dist/",
2094
+ "build/",
2095
+ "coverage/",
2096
+ "out/"
2097
+ ];
2098
+ }
2099
+ });
2100
+
1822
2101
  // cli/index.ts
1823
2102
  var import_commander = require("commander");
1824
2103
 
@@ -1828,6 +2107,73 @@ var import_chalk2 = __toESM(require("chalk"));
1828
2107
 
1829
2108
  // cli/utils/eslint-runner.ts
1830
2109
  var import_path = __toESM(require("path"));
2110
+
2111
+ // cli/utils/logger.ts
2112
+ var import_chalk = __toESM(require("chalk"));
2113
+ var PREFIX = {
2114
+ success: import_chalk.default.green("\u2714"),
2115
+ error: import_chalk.default.red("\u2716"),
2116
+ warn: import_chalk.default.yellow("\u26A0"),
2117
+ info: import_chalk.default.cyan("\u2139"),
2118
+ section: import_chalk.default.bold.white,
2119
+ bullet: import_chalk.default.gray("\u2022")
2120
+ };
2121
+ var log = {
2122
+ info(msg) {
2123
+ console.log(` ${PREFIX.info} ${import_chalk.default.white(msg)}`);
2124
+ },
2125
+ success(msg) {
2126
+ console.log(` ${PREFIX.success} ${import_chalk.default.green(msg)}`);
2127
+ },
2128
+ warn(msg) {
2129
+ console.log(` ${PREFIX.warn} ${import_chalk.default.yellow(msg)}`);
2130
+ },
2131
+ error(msg) {
2132
+ console.error(` ${PREFIX.error} ${import_chalk.default.red(msg)}`);
2133
+ },
2134
+ section(title) {
2135
+ console.log("");
2136
+ console.log(import_chalk.default.bold.cyan(` \u2500\u2500 ${title} \u2500\u2500`));
2137
+ console.log("");
2138
+ },
2139
+ rule(ruleName, count) {
2140
+ console.log(
2141
+ ` ${PREFIX.bullet} ${import_chalk.default.yellow(ruleName)} ${import_chalk.default.gray(`(${count} issue${count !== 1 ? "s" : ""})`)}`
2142
+ );
2143
+ },
2144
+ file(filePath, count) {
2145
+ console.log(
2146
+ ` ${PREFIX.bullet} ${import_chalk.default.white(filePath)} ${import_chalk.default.gray(`\u2192 ${count} issue${count !== 1 ? "s" : ""}`)}`
2147
+ );
2148
+ },
2149
+ issue(msg, severity, line, col) {
2150
+ const icon = severity === 2 ? import_chalk.default.red("error") : import_chalk.default.yellow(" warn");
2151
+ console.log(
2152
+ ` ${import_chalk.default.gray(`${line}:${col}`).padEnd(12)} ${icon} ${import_chalk.default.white(msg)}`
2153
+ );
2154
+ },
2155
+ divider() {
2156
+ console.log(import_chalk.default.gray(" " + "\u2500".repeat(60)));
2157
+ },
2158
+ blank() {
2159
+ console.log("");
2160
+ },
2161
+ banner(title) {
2162
+ console.log("");
2163
+ console.log(import_chalk.default.bold.bgCyan.black(` ${title} `));
2164
+ console.log("");
2165
+ },
2166
+ print(msg) {
2167
+ console.log(msg);
2168
+ },
2169
+ debug(msg) {
2170
+ if (process.env.AI_GUARD_DEBUG === "1") {
2171
+ console.log(` ${import_chalk.default.gray("\xB7")} ${import_chalk.default.gray(msg)}`);
2172
+ }
2173
+ }
2174
+ };
2175
+
2176
+ // cli/utils/eslint-runner.ts
1831
2177
  function normalizePlugin(raw) {
1832
2178
  if (raw && raw.default && raw.default.rules) return raw.default;
1833
2179
  if (raw && raw.rules) return raw;
@@ -1890,6 +2236,12 @@ var DEFAULT_IGNORE_PATTERNS = [
1890
2236
  "**/out/**",
1891
2237
  "**/.git/**"
1892
2238
  ];
2239
+ function isSkippablePatternError(error) {
2240
+ const msg = error.message.toLowerCase();
2241
+ const hasNoFilesSignal = msg.includes("no files") || msg.includes("no files matching");
2242
+ const hasIgnoredSignal = msg.includes("ignored") || msg.includes("all files matched by") || msg.includes("are ignored") || msg.includes("was ignored") || msg.includes("file ignored");
2243
+ return hasNoFilesSignal || hasIgnoredSignal;
2244
+ }
1893
2245
  async function runEslint(options) {
1894
2246
  const { ESLint } = await import("eslint").catch(() => {
1895
2247
  throw new Error(
@@ -1982,112 +2334,53 @@ async function runEslint(options) {
1982
2334
  const r = await eslint.lintFiles([pattern]);
1983
2335
  allRawResults.push(...r);
1984
2336
  } catch (err) {
1985
- const msg = err instanceof Error ? err.message : "";
1986
- if (msg.includes("No files matching") || msg.includes("no files")) {
2337
+ const error = err instanceof Error ? err : new Error(String(err));
2338
+ if (isSkippablePatternError(error)) {
2339
+ log.debug(`Skipping pattern '${pattern}' (no lintable files)`);
1987
2340
  continue;
1988
2341
  }
1989
- throw new Error(`ESLint encountered an error: ${msg}`);
2342
+ throw error;
1990
2343
  }
1991
2344
  }
1992
2345
  const rawResults = allRawResults;
1993
2346
  const durationMs = Date.now() - startMs;
1994
- const files = [];
1995
- const ruleBreakdown = /* @__PURE__ */ new Map();
1996
- let totalErrors = 0;
1997
- let totalWarnings = 0;
1998
- for (const result of rawResults) {
1999
- if (result.messages.length === 0) continue;
2000
- const issues = result.messages.map((m) => ({
2001
- ruleId: m.ruleId ?? "unknown",
2002
- severity: m.severity,
2003
- message: m.message,
2004
- line: m.line,
2005
- column: m.column
2006
- }));
2007
- const errorCount = result.errorCount;
2008
- const warningCount = result.warningCount;
2009
- totalErrors += errorCount;
2010
- totalWarnings += warningCount;
2011
- for (const issue of issues) {
2012
- ruleBreakdown.set(
2013
- issue.ruleId,
2014
- (ruleBreakdown.get(issue.ruleId) ?? 0) + 1
2015
- );
2016
- }
2017
- const relPath = import_path.default.relative(process.cwd(), result.filePath);
2018
- files.push({ filePath: relPath, issues, errorCount, warningCount });
2019
- }
2020
- const topFiles = [...files].sort((a, b) => b.issues.length - a.issues.length).slice(0, 10).map((f) => ({ path: f.filePath, count: f.issues.length }));
2021
- return {
2022
- files,
2023
- totalErrors,
2024
- totalWarnings,
2025
- totalIssues: totalErrors + totalWarnings,
2026
- ruleBreakdown,
2027
- topFiles,
2028
- durationMs
2029
- };
2030
- }
2031
-
2032
- // cli/utils/logger.ts
2033
- var import_chalk = __toESM(require("chalk"));
2034
- var PREFIX = {
2035
- success: import_chalk.default.green("\u2714"),
2036
- error: import_chalk.default.red("\u2716"),
2037
- warn: import_chalk.default.yellow("\u26A0"),
2038
- info: import_chalk.default.cyan("\u2139"),
2039
- section: import_chalk.default.bold.white,
2040
- bullet: import_chalk.default.gray("\u2022")
2041
- };
2042
- var log = {
2043
- info(msg) {
2044
- console.log(` ${PREFIX.info} ${import_chalk.default.white(msg)}`);
2045
- },
2046
- success(msg) {
2047
- console.log(` ${PREFIX.success} ${import_chalk.default.green(msg)}`);
2048
- },
2049
- warn(msg) {
2050
- console.log(` ${PREFIX.warn} ${import_chalk.default.yellow(msg)}`);
2051
- },
2052
- error(msg) {
2053
- console.error(` ${PREFIX.error} ${import_chalk.default.red(msg)}`);
2054
- },
2055
- section(title) {
2056
- console.log("");
2057
- console.log(import_chalk.default.bold.cyan(` \u2500\u2500 ${title} \u2500\u2500`));
2058
- console.log("");
2059
- },
2060
- rule(ruleName, count) {
2061
- console.log(
2062
- ` ${PREFIX.bullet} ${import_chalk.default.yellow(ruleName)} ${import_chalk.default.gray(`(${count} issue${count !== 1 ? "s" : ""})`)}`
2063
- );
2064
- },
2065
- file(filePath, count) {
2066
- console.log(
2067
- ` ${PREFIX.bullet} ${import_chalk.default.white(filePath)} ${import_chalk.default.gray(`\u2192 ${count} issue${count !== 1 ? "s" : ""}`)}`
2068
- );
2069
- },
2070
- issue(msg, severity, line, col) {
2071
- const icon = severity === 2 ? import_chalk.default.red("error") : import_chalk.default.yellow(" warn");
2072
- console.log(
2073
- ` ${import_chalk.default.gray(`${line}:${col}`).padEnd(12)} ${icon} ${import_chalk.default.white(msg)}`
2074
- );
2075
- },
2076
- divider() {
2077
- console.log(import_chalk.default.gray(" " + "\u2500".repeat(60)));
2078
- },
2079
- blank() {
2080
- console.log("");
2081
- },
2082
- banner(title) {
2083
- console.log("");
2084
- console.log(import_chalk.default.bold.bgCyan.black(` ${title} `));
2085
- console.log("");
2086
- },
2087
- print(msg) {
2088
- console.log(msg);
2347
+ const files = [];
2348
+ const ruleBreakdown = /* @__PURE__ */ new Map();
2349
+ let totalErrors = 0;
2350
+ let totalWarnings = 0;
2351
+ for (const result of rawResults) {
2352
+ if (result.messages.length === 0) continue;
2353
+ const issues = result.messages.map((m) => ({
2354
+ ruleId: m.ruleId ?? "unknown",
2355
+ severity: m.severity,
2356
+ message: m.message,
2357
+ line: m.line,
2358
+ column: m.column
2359
+ }));
2360
+ const errorCount = result.errorCount;
2361
+ const warningCount = result.warningCount;
2362
+ totalErrors += errorCount;
2363
+ totalWarnings += warningCount;
2364
+ for (const issue of issues) {
2365
+ ruleBreakdown.set(
2366
+ issue.ruleId,
2367
+ (ruleBreakdown.get(issue.ruleId) ?? 0) + 1
2368
+ );
2369
+ }
2370
+ const relPath = import_path.default.relative(process.cwd(), result.filePath);
2371
+ files.push({ filePath: relPath, issues, errorCount, warningCount });
2089
2372
  }
2090
- };
2373
+ const topFiles = [...files].sort((a, b) => b.issues.length - a.issues.length).slice(0, 10).map((f) => ({ path: f.filePath, count: f.issues.length }));
2374
+ return {
2375
+ files,
2376
+ totalErrors,
2377
+ totalWarnings,
2378
+ totalIssues: totalErrors + totalWarnings,
2379
+ ruleBreakdown,
2380
+ topFiles,
2381
+ durationMs
2382
+ };
2383
+ }
2091
2384
 
2092
2385
  // cli/commands/run.ts
2093
2386
  function registerRunCommand(program2) {
@@ -2220,7 +2513,7 @@ function formatIssueCount(errors, warnings) {
2220
2513
 
2221
2514
  // cli/commands/init.ts
2222
2515
  var import_chalk3 = __toESM(require("chalk"));
2223
- var import_path3 = __toESM(require("path"));
2516
+ var import_path4 = __toESM(require("path"));
2224
2517
  var import_fs3 = __toESM(require("fs"));
2225
2518
 
2226
2519
  // cli/utils/detector.ts
@@ -2322,225 +2615,8 @@ function detect(cwd = process.cwd()) {
2322
2615
  };
2323
2616
  }
2324
2617
 
2325
- // cli/utils/config-manager.ts
2326
- var import_fs2 = __toESM(require("fs"));
2327
- function backupConfig(configPath) {
2328
- const backupPath = `${configPath}.bak`;
2329
- import_fs2.default.copyFileSync(configPath, backupPath);
2330
- return backupPath;
2331
- }
2332
- function readConfig(configPath) {
2333
- return import_fs2.default.readFileSync(configPath, "utf-8");
2334
- }
2335
- function writeConfig(configPath, content) {
2336
- import_fs2.default.writeFileSync(configPath, content, "utf-8");
2337
- }
2338
- function generateFlatConfig(preset) {
2339
- return `import aiGuard from 'eslint-plugin-ai-guard';
2340
-
2341
- export default [
2342
- // Ignore generated / dependency directories
2343
- {
2344
- ignores: [
2345
- 'node_modules/**',
2346
- '.next/**',
2347
- 'dist/**',
2348
- 'build/**',
2349
- 'coverage/**',
2350
- 'out/**',
2351
- ],
2352
- },
2353
-
2354
- // ai-guard: catch AI-generated code patterns
2355
- {
2356
- plugins: {
2357
- 'ai-guard': aiGuard.default,
2358
- },
2359
- rules: {
2360
- ...aiGuard.${preset}.rules,
2361
- },
2362
- },
2363
- ];
2364
- `;
2365
- }
2366
- function generateLegacyConfig(preset) {
2367
- return `module.exports = {
2368
- plugins: ['ai-guard'],
2369
- extends: ['plugin:ai-guard/${preset}'],
2370
- ignorePatterns: [
2371
- 'node_modules/',
2372
- '.next/',
2373
- 'dist/',
2374
- 'build/',
2375
- 'coverage/',
2376
- 'out/',
2377
- ],
2378
- };
2379
- `;
2380
- }
2381
- var AI_GUARD_MARKER = "eslint-plugin-ai-guard";
2382
- function patchFlatConfig(existing, preset) {
2383
- if (existing.includes(AI_GUARD_MARKER)) {
2384
- return existing;
2385
- }
2386
- const importLine = `import aiGuard from 'eslint-plugin-ai-guard';
2387
- `;
2388
- const rulesBlock = `
2389
- // ai-guard injected by ai-guard CLI
2390
- {
2391
- plugins: {
2392
- 'ai-guard': aiGuard.default,
2393
- },
2394
- rules: {
2395
- ...aiGuard.${preset}.rules,
2396
- },
2397
- },
2398
- `;
2399
- let patched = existing;
2400
- const lastImportIdx = findLastImportEnd(patched);
2401
- if (lastImportIdx > -1) {
2402
- patched = patched.slice(0, lastImportIdx) + importLine + patched.slice(lastImportIdx);
2403
- } else {
2404
- patched = importLine + patched;
2405
- }
2406
- const lastBracket = patched.lastIndexOf("];");
2407
- if (lastBracket !== -1) {
2408
- patched = patched.slice(0, lastBracket) + rulesBlock + patched.slice(lastBracket);
2409
- }
2410
- return patched;
2411
- }
2412
- function findLastImportEnd(src) {
2413
- const lines = src.split("\n");
2414
- let lastImportLine = -1;
2415
- let charCount = 0;
2416
- for (let i = 0; i < lines.length; i++) {
2417
- const line = lines[i];
2418
- if (/^\s*import\s+/.test(line)) lastImportLine = i;
2419
- charCount += line.length + 1;
2420
- }
2421
- if (lastImportLine === -1) return -1;
2422
- let offset = 0;
2423
- for (let i = 0; i <= lastImportLine; i++) {
2424
- offset += lines[i].length + 1;
2425
- }
2426
- return offset;
2427
- }
2428
- function patchLegacyConfig(existing, preset) {
2429
- if (existing.includes("ai-guard")) {
2430
- return existing;
2431
- }
2432
- let patched = existing;
2433
- if (patched.includes("plugins:")) {
2434
- patched = patched.replace(/plugins:\s*\[/, `plugins: ['ai-guard', `);
2435
- } else {
2436
- patched = patched.replace(
2437
- "module.exports = {",
2438
- `module.exports = {
2439
- plugins: ['ai-guard'],`
2440
- );
2441
- }
2442
- if (patched.includes("extends:")) {
2443
- patched = patched.replace(
2444
- /extends:\s*\[/,
2445
- `extends: ['plugin:ai-guard/${preset}', `
2446
- );
2447
- } else {
2448
- patched = patched.replace(
2449
- "module.exports = {",
2450
- `module.exports = {
2451
- extends: ['plugin:ai-guard/${preset}'],`
2452
- );
2453
- }
2454
- return patched;
2455
- }
2456
- function removeNukeIgnore(existing) {
2457
- const nukePatterns = [/"?\*\*\/\*"?/g];
2458
- const hasNuke = existing.includes('"**/*"') || existing.includes("'**/*'");
2459
- if (!hasNuke) return { content: existing, changed: false };
2460
- let patched = existing.replace(
2461
- /ignorePatterns:\s*\[.*?\*\*\/\*.*?\]/gs,
2462
- `ignorePatterns: ['node_modules/', '.next/', 'dist/', 'build/', 'coverage/']`
2463
- ).replace(
2464
- /ignores:\s*\[.*?\*\*\/\*.*?\]/gs,
2465
- `ignores: ['node_modules/**', '.next/**', 'dist/**', 'build/**', 'coverage/**']`
2466
- );
2467
- for (const re of nukePatterns) {
2468
- patched = patched.replace(re, "");
2469
- }
2470
- return { content: patched, changed: patched !== existing };
2471
- }
2472
- var DEFAULT_FLAT_IGNORES = [
2473
- "node_modules/**",
2474
- ".next/**",
2475
- "dist/**",
2476
- "build/**",
2477
- "coverage/**",
2478
- "out/**"
2479
- ];
2480
- var DEFAULT_LEGACY_IGNORES = [
2481
- "node_modules/",
2482
- ".next/",
2483
- "dist/",
2484
- "build/",
2485
- "coverage/",
2486
- "out/"
2487
- ];
2488
- function addIgnoresToFlatConfig(existing) {
2489
- if (existing.includes("ignores:")) return existing;
2490
- const block = `
2491
- // Default ignores added by ai-guard CLI
2492
- {
2493
- ignores: ${JSON.stringify(DEFAULT_FLAT_IGNORES)},
2494
- },
2495
- `;
2496
- const lastBracket = existing.lastIndexOf("];");
2497
- if (lastBracket !== -1) {
2498
- return existing.slice(0, lastBracket) + block + existing.slice(lastBracket);
2499
- }
2500
- return existing;
2501
- }
2502
- function addIgnoresToLegacyConfig(existing) {
2503
- if (existing.includes("ignorePatterns")) return existing;
2504
- return existing.replace(
2505
- "module.exports = {",
2506
- `module.exports = {
2507
- ignorePatterns: ${JSON.stringify(DEFAULT_LEGACY_IGNORES)},`
2508
- );
2509
- }
2510
- function validateFlatConfigText(content) {
2511
- const problems = [];
2512
- if (!content.includes("export default")) {
2513
- problems.push("Missing `export default` \u2014 file must be an ES module");
2514
- }
2515
- if (!content.includes("eslint-plugin-ai-guard") && !content.includes("aiGuard")) {
2516
- problems.push("Plugin import not found \u2014 ai-guard plugin may not be referenced");
2517
- }
2518
- if (!content.trim().endsWith("];") && !content.trim().endsWith("];")) {
2519
- }
2520
- if (content.includes('"**/*"') || content.includes("'**/*'")) {
2521
- problems.push(
2522
- 'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
2523
- );
2524
- }
2525
- return problems;
2526
- }
2527
- function validateLegacyConfigText(content) {
2528
- const problems = [];
2529
- if (!content.includes("module.exports")) {
2530
- problems.push("Missing `module.exports` \u2014 legacy config must use CJS exports");
2531
- }
2532
- if (!content.includes("ai-guard")) {
2533
- problems.push("Plugin not referenced \u2014 add ai-guard to plugins and extends");
2534
- }
2535
- if (content.includes('"**/*"') || content.includes("'**/*'")) {
2536
- problems.push(
2537
- 'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
2538
- );
2539
- }
2540
- return problems;
2541
- }
2542
-
2543
2618
  // cli/commands/init.ts
2619
+ init_config_manager();
2544
2620
  function classifyVerifyError(err) {
2545
2621
  const msg = err instanceof Error ? err.message : String(err);
2546
2622
  const clean = msg.replace(/\s+at\s+.+/gm, "").trim();
@@ -2564,7 +2640,7 @@ async function verifyConfigLoads(configPath, cwd) {
2564
2640
  overrideConfigFile: configPath
2565
2641
  });
2566
2642
  await eslint.lintText("", {
2567
- filePath: import_path3.default.join(cwd, "_ai_guard_probe_.js")
2643
+ filePath: import_path4.default.join(cwd, "_ai_guard_probe_.js")
2568
2644
  });
2569
2645
  return null;
2570
2646
  } catch (err) {
@@ -2600,7 +2676,7 @@ function registerInitCommand(program2) {
2600
2676
  log.warn("eslint-plugin-ai-guard not found");
2601
2677
  }
2602
2678
  if (env.configType !== "none") {
2603
- log.success(`ESLint config found: ${import_chalk3.default.white(import_path3.default.relative(cwd, env.configPath ?? env.configType))}`);
2679
+ log.success(`ESLint config found: ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath ?? env.configType))}`);
2604
2680
  } else {
2605
2681
  log.info("No ESLint config found \u2014 will generate one");
2606
2682
  }
@@ -2609,7 +2685,7 @@ function registerInitCommand(program2) {
2609
2685
  log.blank();
2610
2686
  log.warn("Conflicting configs detected:");
2611
2687
  for (const p of env.allConfigPaths) {
2612
- log.print(` ${import_chalk3.default.yellow("\u2192")} ${import_chalk3.default.white(import_path3.default.relative(cwd, p))}`);
2688
+ log.print(` ${import_chalk3.default.yellow("\u2192")} ${import_chalk3.default.white(import_path4.default.relative(cwd, p))}`);
2613
2689
  }
2614
2690
  log.print(` ${import_chalk3.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
2615
2691
  }
@@ -2637,7 +2713,7 @@ function registerInitCommand(program2) {
2637
2713
  try {
2638
2714
  const { createRequire } = await import("module");
2639
2715
  const { pathToFileURL } = await import("url");
2640
- const req = createRequire(import_path3.default.join(cwd, "_probe.js"));
2716
+ const req = createRequire(import_path4.default.join(cwd, "_probe.js"));
2641
2717
  const resolvedPath = req.resolve("eslint-plugin-ai-guard");
2642
2718
  const aiGuard = await import(pathToFileURL(resolvedPath).href);
2643
2719
  const plugin2 = aiGuard.default || aiGuard;
@@ -2655,7 +2731,7 @@ function registerInitCommand(program2) {
2655
2731
  if (eslintNeedsFlat && existingIsLegacy) {
2656
2732
  log.section("Config Migration Required");
2657
2733
  log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
2658
- log.print(` ${import_chalk3.default.white(import_path3.default.relative(cwd, env.configPath))}`);
2734
+ log.print(` ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
2659
2735
  log.blank();
2660
2736
  log.print(` ${import_chalk3.default.bold("ai-guard will create a new flat config.")}`);
2661
2737
  log.blank();
@@ -2667,7 +2743,7 @@ function registerInitCommand(program2) {
2667
2743
  const raw = readConfig(env.configPath);
2668
2744
  const { content: fixed, changed } = removeNukeIgnore(raw);
2669
2745
  if (changed) {
2670
- log.warn(`Removed "**/*" ignore pattern from ${import_path3.default.relative(cwd, env.configPath)}`);
2746
+ log.warn(`Removed "**/*" ignore pattern from ${import_path4.default.relative(cwd, env.configPath)}`);
2671
2747
  if (!isDryRun) {
2672
2748
  backupConfig(env.configPath);
2673
2749
  writeConfig(env.configPath, fixed);
@@ -2679,7 +2755,7 @@ function registerInitCommand(program2) {
2679
2755
  }
2680
2756
  if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
2681
2757
  const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
2682
- finalConfigPath = import_path3.default.join(cwd, targetName);
2758
+ finalConfigPath = import_path4.default.join(cwd, targetName);
2683
2759
  const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
2684
2760
  if (isDryRun) {
2685
2761
  log.print(` ${import_chalk3.default.gray(`[dry-run] would create: ${targetName}`)}`);
@@ -2698,19 +2774,25 @@ function registerInitCommand(program2) {
2698
2774
  configWritten = true;
2699
2775
  }
2700
2776
  } else if (useFlat && existingIsFlat) {
2777
+ const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
2701
2778
  const existing = readConfig(env.configPath);
2779
+ const isInvalid = isInvalidAiGuardConfig2(existing);
2702
2780
  const patched = patchFlatConfig(existing, preset);
2703
- if (patched === existing) {
2781
+ if (patched === existing && !isInvalid) {
2704
2782
  log.warn("ai-guard is already present in your config \u2014 no changes made");
2705
2783
  finalConfigPath = env.configPath;
2706
2784
  } else if (isDryRun) {
2707
2785
  finalConfigPath = env.configPath;
2708
- log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path3.default.relative(cwd, env.configPath)}`)}`);
2786
+ log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path4.default.relative(cwd, env.configPath)}`)}`);
2709
2787
  } else {
2710
2788
  const bak = backupConfig(env.configPath);
2711
- log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path3.default.relative(cwd, bak))}`);
2789
+ log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path4.default.relative(cwd, bak))}`);
2712
2790
  writeConfig(env.configPath, patched);
2713
- log.success(`Patched ${import_chalk3.default.white(import_path3.default.relative(cwd, env.configPath))}`);
2791
+ if (isInvalid) {
2792
+ log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
2793
+ } else {
2794
+ log.success(`Patched ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
2795
+ }
2714
2796
  finalConfigPath = env.configPath;
2715
2797
  configWritten = true;
2716
2798
  }
@@ -2722,12 +2804,12 @@ function registerInitCommand(program2) {
2722
2804
  finalConfigPath = env.configPath;
2723
2805
  } else if (isDryRun) {
2724
2806
  finalConfigPath = env.configPath;
2725
- log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path3.default.relative(cwd, env.configPath)}`)}`);
2807
+ log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path4.default.relative(cwd, env.configPath)}`)}`);
2726
2808
  } else {
2727
2809
  const bak = backupConfig(env.configPath);
2728
- log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path3.default.relative(cwd, bak))}`);
2810
+ log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path4.default.relative(cwd, bak))}`);
2729
2811
  writeConfig(env.configPath, patched);
2730
- log.success(`Patched ${import_chalk3.default.white(import_path3.default.relative(cwd, env.configPath))}`);
2812
+ log.success(`Patched ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
2731
2813
  finalConfigPath = env.configPath;
2732
2814
  configWritten = true;
2733
2815
  }
@@ -2745,7 +2827,7 @@ function registerInitCommand(program2) {
2745
2827
  }
2746
2828
  } else {
2747
2829
  cleanedUp = true;
2748
- log.print(` ${import_chalk3.default.gray(`[dry-run] would rename ${import_path3.default.relative(cwd, p)} to ${import_path3.default.basename(p)}.bak`)}`);
2830
+ log.print(` ${import_chalk3.default.gray(`[dry-run] would rename ${import_path4.default.relative(cwd, p)} to ${import_path4.default.basename(p)}.bak`)}`);
2749
2831
  }
2750
2832
  }
2751
2833
  }
@@ -2764,7 +2846,7 @@ function registerInitCommand(program2) {
2764
2846
  log.blank();
2765
2847
  log.section("Validation");
2766
2848
  if (!import_fs3.default.existsSync(finalConfigPath)) {
2767
- log.error(`Config file not found after write: ${import_path3.default.relative(cwd, finalConfigPath)}`);
2849
+ log.error(`Config file not found after write: ${import_path4.default.relative(cwd, finalConfigPath)}`);
2768
2850
  log.blank();
2769
2851
  log.print(` ${import_chalk3.default.bold("Problem:")} File write may have failed.`);
2770
2852
  log.print(` ${import_chalk3.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
@@ -2780,7 +2862,7 @@ function registerInitCommand(program2) {
2780
2862
  log.print(` ${import_chalk3.default.red("\u2716")} ${e}`);
2781
2863
  }
2782
2864
  } else {
2783
- log.success(`Config structure valid: ${import_chalk3.default.white(import_path3.default.relative(cwd, finalConfigPath))}`);
2865
+ log.success(`Config structure valid: ${import_chalk3.default.white(import_path4.default.relative(cwd, finalConfigPath))}`);
2784
2866
  }
2785
2867
  log.info("Verifying ESLint can load the config\u2026");
2786
2868
  const loadError = await verifyConfigLoads(finalConfigPath, cwd);
@@ -2804,7 +2886,7 @@ function registerInitCommand(program2) {
2804
2886
  }
2805
2887
  log.blank();
2806
2888
  log.print(` ${import_chalk3.default.bold("Fix:")}`);
2807
- log.print(` 1. Check the syntax in ${import_chalk3.default.cyan(import_path3.default.relative(cwd, finalConfigPath))}`);
2889
+ log.print(` 1. Check the syntax in ${import_chalk3.default.cyan(import_path4.default.relative(cwd, finalConfigPath))}`);
2808
2890
  log.print(` 2. Make sure ${import_chalk3.default.cyan("eslint-plugin-ai-guard")} is installed`);
2809
2891
  log.print(` 3. Run ${import_chalk3.default.cyan("ai-guard doctor")} for full diagnostics`);
2810
2892
  log.blank();
@@ -2826,7 +2908,7 @@ function registerInitCommand(program2) {
2826
2908
  if (loadError === null && !lingeringConflicts) {
2827
2909
  log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold.green("Configuration validated successfully")}`);
2828
2910
  }
2829
- log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("ESLint config:")} ${import_chalk3.default.cyan(import_path3.default.relative(cwd, finalConfigPath))}`);
2911
+ log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("ESLint config:")} ${import_chalk3.default.cyan(import_path4.default.relative(cwd, finalConfigPath))}`);
2830
2912
  log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("Mode:")} ${import_chalk3.default.cyan(useFlat ? `Flat config (ESLint v${env.eslintMajor ?? 9})` : `Legacy config (ESLint v${env.eslintMajor ?? 8})`)}`);
2831
2913
  log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("Preset:")} ${import_chalk3.default.cyan(preset)}`);
2832
2914
  log.blank();
@@ -2840,12 +2922,13 @@ function registerInitCommand(program2) {
2840
2922
 
2841
2923
  // cli/commands/doctor.ts
2842
2924
  var import_chalk4 = __toESM(require("chalk"));
2843
- var import_path4 = __toESM(require("path"));
2925
+ var import_path5 = __toESM(require("path"));
2926
+ init_config_manager();
2844
2927
  async function probeConfigLoad(configPath, cwd) {
2845
2928
  try {
2846
2929
  const { ESLint } = await import("eslint");
2847
2930
  const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
2848
- await eslint.lintText("", { filePath: import_path4.default.join(cwd, "_probe_.js") });
2931
+ await eslint.lintText("", { filePath: import_path5.default.join(cwd, "_probe_.js") });
2849
2932
  return { ok: true, error: null };
2850
2933
  } catch (err) {
2851
2934
  const msg = err instanceof Error ? err.message : String(err);
@@ -2880,7 +2963,7 @@ function registerDoctorCommand(program2) {
2880
2963
  checks.push({
2881
2964
  label: "ESLint config present",
2882
2965
  pass: hasConfig,
2883
- detail: hasConfig ? `${import_path4.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
2966
+ detail: hasConfig ? `${import_path5.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
2884
2967
  fix: hasConfig ? void 0 : "ai-guard init",
2885
2968
  note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
2886
2969
  });
@@ -2899,25 +2982,28 @@ function registerDoctorCommand(program2) {
2899
2982
  }
2900
2983
  if (hasConfig && env.configPath) {
2901
2984
  let pluginWired = false;
2985
+ let isInvalidConfig = false;
2902
2986
  let readError = null;
2903
2987
  try {
2988
+ const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
2904
2989
  const content = readConfig(env.configPath);
2905
2990
  pluginWired = content.includes("ai-guard") || content.includes("eslint-plugin-ai-guard");
2991
+ isInvalidConfig = isInvalidAiGuardConfig2(content);
2906
2992
  } catch (err) {
2907
2993
  readError = err instanceof Error ? err.message : String(err);
2908
2994
  }
2909
2995
  checks.push({
2910
2996
  label: "Plugin wired in config",
2911
- pass: pluginWired,
2912
- detail: readError ? `Could not read config: ${readError}` : pluginWired ? "eslint-plugin-ai-guard referenced in config" : `Config exists but ai-guard plugin not found in ${import_path4.default.relative(cwd, env.configPath)}`,
2913
- fix: pluginWired ? void 0 : "ai-guard init (will patch your existing config)"
2997
+ pass: pluginWired && !isInvalidConfig,
2998
+ 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_path5.default.relative(cwd, env.configPath)}`,
2999
+ fix: isInvalidConfig || !pluginWired && !readError ? "ai-guard init (will patch/repair your existing config)" : void 0
2914
3000
  });
2915
3001
  }
2916
3002
  checks.push({
2917
3003
  label: "No conflicting config files",
2918
3004
  pass: !env.hasConflictingConfigs,
2919
3005
  detail: env.hasConflictingConfigs ? `Conflicting config detected:
2920
- ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
3006
+ ${env.allConfigPaths.map((p) => ` ${import_path5.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
2921
3007
  fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
2922
3008
  note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
2923
3009
  });
@@ -2944,7 +3030,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
2944
3030
  label: "ESLint config loads without errors",
2945
3031
  pass: probe.ok,
2946
3032
  detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
2947
- fix: probe.ok ? void 0 : `Check syntax in ${import_path4.default.relative(cwd, env.configPath)}`,
3033
+ fix: probe.ok ? void 0 : `Check syntax in ${import_path5.default.relative(cwd, env.configPath)}`,
2948
3034
  note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
2949
3035
  });
2950
3036
  }
@@ -3007,7 +3093,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
3007
3093
  // cli/commands/preset.ts
3008
3094
  var import_prompts = require("@inquirer/prompts");
3009
3095
  var import_chalk5 = __toESM(require("chalk"));
3010
- var import_path5 = __toESM(require("path"));
3096
+ init_config_manager();
3097
+ var import_path6 = __toESM(require("path"));
3011
3098
  function registerPresetCommand(program2) {
3012
3099
  program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
3013
3100
  const cwd = process.cwd();
@@ -3035,21 +3122,21 @@ function registerPresetCommand(program2) {
3035
3122
  const env = detect(cwd);
3036
3123
  const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
3037
3124
  if (env.configType === "none") {
3038
- const configPath = useFlat ? import_path5.default.join(cwd, "eslint.config.mjs") : import_path5.default.join(cwd, ".eslintrc.js");
3125
+ const configPath = useFlat ? import_path6.default.join(cwd, "eslint.config.mjs") : import_path6.default.join(cwd, ".eslintrc.js");
3039
3126
  const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
3040
3127
  writeConfig(configPath, content);
3041
- log.success(`Created ${import_chalk5.default.white(import_path5.default.relative(cwd, configPath))} with ${import_chalk5.default.cyan(preset)} preset`);
3128
+ log.success(`Created ${import_chalk5.default.white(import_path6.default.relative(cwd, configPath))} with ${import_chalk5.default.cyan(preset)} preset`);
3042
3129
  } else {
3043
3130
  const configPath = env.configPath;
3044
3131
  const backupPath = backupConfig(configPath);
3045
- log.info(`Backed up \u2192 ${import_chalk5.default.gray(import_path5.default.relative(cwd, backupPath))}`);
3132
+ log.info(`Backed up \u2192 ${import_chalk5.default.gray(import_path6.default.relative(cwd, backupPath))}`);
3046
3133
  const existing = readConfig(configPath);
3047
3134
  const patched = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
3048
3135
  if (patched === existing) {
3049
3136
  log.warn("Config already contains ai-guard. Edit manually to change preset.");
3050
3137
  } else {
3051
3138
  writeConfig(configPath, patched);
3052
- log.success(`Patched ${import_chalk5.default.white(import_path5.default.relative(cwd, configPath))}`);
3139
+ log.success(`Patched ${import_chalk5.default.white(import_path6.default.relative(cwd, configPath))}`);
3053
3140
  }
3054
3141
  }
3055
3142
  log.blank();
@@ -3088,7 +3175,8 @@ function registerPresetCommand(program2) {
3088
3175
 
3089
3176
  // cli/commands/ignore.ts
3090
3177
  var import_chalk6 = __toESM(require("chalk"));
3091
- var import_path6 = __toESM(require("path"));
3178
+ init_config_manager();
3179
+ var import_path7 = __toESM(require("path"));
3092
3180
  var DEFAULT_IGNORES = [
3093
3181
  ".next/",
3094
3182
  "dist/",
@@ -3110,21 +3198,21 @@ function registerIgnoreCommand(program2) {
3110
3198
  const env = detect(cwd);
3111
3199
  if (env.configType === "none") {
3112
3200
  const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
3113
- const configPath = useFlat ? import_path6.default.join(cwd, "eslint.config.mjs") : import_path6.default.join(cwd, ".eslintrc.js");
3201
+ const configPath = useFlat ? import_path7.default.join(cwd, "eslint.config.mjs") : import_path7.default.join(cwd, ".eslintrc.js");
3114
3202
  const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
3115
3203
  writeConfig(configPath, content);
3116
- log.success(`No config found \u2014 generated ${import_chalk6.default.white(import_path6.default.relative(cwd, configPath))} with ignores included`);
3204
+ log.success(`No config found \u2014 generated ${import_chalk6.default.white(import_path7.default.relative(cwd, configPath))} with ignores included`);
3117
3205
  } else {
3118
3206
  const configPath = env.configPath;
3119
3207
  const backupPath = backupConfig(configPath);
3120
- log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path6.default.relative(cwd, backupPath))}`);
3208
+ log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, backupPath))}`);
3121
3209
  const existing = readConfig(configPath);
3122
3210
  const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
3123
3211
  if (patched === existing) {
3124
3212
  log.warn("Ignore patterns already present in config \u2014 no changes made.");
3125
3213
  } else {
3126
3214
  writeConfig(configPath, patched);
3127
- log.success(`Patched ${import_chalk6.default.white(import_path6.default.relative(cwd, configPath))} with ignore patterns`);
3215
+ log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, configPath))} with ignore patterns`);
3128
3216
  }
3129
3217
  }
3130
3218
  log.blank();
@@ -3136,7 +3224,7 @@ function registerIgnoreCommand(program2) {
3136
3224
 
3137
3225
  // cli/commands/baseline.ts
3138
3226
  var import_fs4 = __toESM(require("fs"));
3139
- var import_path7 = __toESM(require("path"));
3227
+ var import_path8 = __toESM(require("path"));
3140
3228
  var import_chalk7 = __toESM(require("chalk"));
3141
3229
  var import_ora2 = __toESM(require("ora"));
3142
3230
  var BASELINE_FILE = ".ai-guard-baseline.json";
@@ -3156,12 +3244,12 @@ function saveBaseline(result, preset, cwd) {
3156
3244
  totalIssues: result.totalIssues,
3157
3245
  entries
3158
3246
  };
3159
- const baselinePath = import_path7.default.join(cwd, BASELINE_FILE);
3247
+ const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
3160
3248
  import_fs4.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
3161
3249
  return baselinePath;
3162
3250
  }
3163
3251
  function loadBaseline(cwd) {
3164
- const baselinePath = import_path7.default.join(cwd, BASELINE_FILE);
3252
+ const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
3165
3253
  if (!import_fs4.default.existsSync(baselinePath)) return null;
3166
3254
  try {
3167
3255
  const raw = import_fs4.default.readFileSync(baselinePath, "utf-8");
@@ -3238,7 +3326,7 @@ function registerBaselineCommand(program2) {
3238
3326
  }
3239
3327
  const baselinePath = saveBaseline(result, preset, cwd);
3240
3328
  log.success(
3241
- `Baseline saved \u2192 ${import_chalk7.default.white(import_path7.default.relative(cwd, baselinePath))}`
3329
+ `Baseline saved \u2192 ${import_chalk7.default.white(import_path8.default.relative(cwd, baselinePath))}`
3242
3330
  );
3243
3331
  log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
3244
3332
  log.info(` Preset: ${import_chalk7.default.cyan(preset)}`);