eslint-plugin-ai-guard 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +402 -278
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
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(
|
|
1309
|
-
if (!
|
|
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 (
|
|
1313
|
+
if (path9[i] !== prefix[i]) {
|
|
1314
1314
|
return false;
|
|
1315
1315
|
}
|
|
1316
1316
|
}
|
|
1317
1317
|
return true;
|
|
1318
1318
|
}
|
|
1319
|
-
function isLikelyResourceIdPath(
|
|
1320
|
-
if (!
|
|
1319
|
+
function isLikelyResourceIdPath(path9) {
|
|
1320
|
+
if (!path9 || path9.length < 3) {
|
|
1321
1321
|
return false;
|
|
1322
1322
|
}
|
|
1323
|
-
if (!hasPathPrefix(
|
|
1323
|
+
if (!hasPathPrefix(path9, ["req", "params"]) && !hasPathPrefix(path9, ["req", "body"]) && !hasPathPrefix(path9, ["req", "query"])) {
|
|
1324
1324
|
return false;
|
|
1325
1325
|
}
|
|
1326
|
-
const last =
|
|
1326
|
+
const last = path9[path9.length - 1].toLowerCase();
|
|
1327
1327
|
return last === "id" || last.endsWith("id");
|
|
1328
1328
|
}
|
|
1329
|
-
function isReqUserPath(
|
|
1330
|
-
return hasPathPrefix(
|
|
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
|
|
1370
|
-
if (isLikelyResourceIdPath(
|
|
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
|
|
|
@@ -2220,7 +2499,7 @@ function formatIssueCount(errors, warnings) {
|
|
|
2220
2499
|
|
|
2221
2500
|
// cli/commands/init.ts
|
|
2222
2501
|
var import_chalk3 = __toESM(require("chalk"));
|
|
2223
|
-
var
|
|
2502
|
+
var import_path4 = __toESM(require("path"));
|
|
2224
2503
|
var import_fs3 = __toESM(require("fs"));
|
|
2225
2504
|
|
|
2226
2505
|
// cli/utils/detector.ts
|
|
@@ -2322,217 +2601,8 @@ function detect(cwd = process.cwd()) {
|
|
|
2322
2601
|
};
|
|
2323
2602
|
}
|
|
2324
2603
|
|
|
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: { 'ai-guard': aiGuard },
|
|
2357
|
-
rules: aiGuard.configs.${preset}.rules,
|
|
2358
|
-
},
|
|
2359
|
-
];
|
|
2360
|
-
`;
|
|
2361
|
-
}
|
|
2362
|
-
function generateLegacyConfig(preset) {
|
|
2363
|
-
return `module.exports = {
|
|
2364
|
-
plugins: ['ai-guard'],
|
|
2365
|
-
extends: ['plugin:ai-guard/${preset}'],
|
|
2366
|
-
ignorePatterns: [
|
|
2367
|
-
'node_modules/',
|
|
2368
|
-
'.next/',
|
|
2369
|
-
'dist/',
|
|
2370
|
-
'build/',
|
|
2371
|
-
'coverage/',
|
|
2372
|
-
'out/',
|
|
2373
|
-
],
|
|
2374
|
-
};
|
|
2375
|
-
`;
|
|
2376
|
-
}
|
|
2377
|
-
var AI_GUARD_MARKER = "eslint-plugin-ai-guard";
|
|
2378
|
-
function patchFlatConfig(existing, preset) {
|
|
2379
|
-
if (existing.includes(AI_GUARD_MARKER)) {
|
|
2380
|
-
return existing;
|
|
2381
|
-
}
|
|
2382
|
-
const importLine = `import aiGuard from 'eslint-plugin-ai-guard';
|
|
2383
|
-
`;
|
|
2384
|
-
const rulesBlock = `
|
|
2385
|
-
// ai-guard injected by ai-guard CLI
|
|
2386
|
-
{
|
|
2387
|
-
plugins: { 'ai-guard': aiGuard },
|
|
2388
|
-
rules: aiGuard.configs.${preset}.rules,
|
|
2389
|
-
},
|
|
2390
|
-
`;
|
|
2391
|
-
let patched = existing;
|
|
2392
|
-
const lastImportIdx = findLastImportEnd(patched);
|
|
2393
|
-
if (lastImportIdx > -1) {
|
|
2394
|
-
patched = patched.slice(0, lastImportIdx) + importLine + patched.slice(lastImportIdx);
|
|
2395
|
-
} else {
|
|
2396
|
-
patched = importLine + patched;
|
|
2397
|
-
}
|
|
2398
|
-
const lastBracket = patched.lastIndexOf("];");
|
|
2399
|
-
if (lastBracket !== -1) {
|
|
2400
|
-
patched = patched.slice(0, lastBracket) + rulesBlock + patched.slice(lastBracket);
|
|
2401
|
-
}
|
|
2402
|
-
return patched;
|
|
2403
|
-
}
|
|
2404
|
-
function findLastImportEnd(src) {
|
|
2405
|
-
const lines = src.split("\n");
|
|
2406
|
-
let lastImportLine = -1;
|
|
2407
|
-
let charCount = 0;
|
|
2408
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2409
|
-
const line = lines[i];
|
|
2410
|
-
if (/^\s*import\s+/.test(line)) lastImportLine = i;
|
|
2411
|
-
charCount += line.length + 1;
|
|
2412
|
-
}
|
|
2413
|
-
if (lastImportLine === -1) return -1;
|
|
2414
|
-
let offset = 0;
|
|
2415
|
-
for (let i = 0; i <= lastImportLine; i++) {
|
|
2416
|
-
offset += lines[i].length + 1;
|
|
2417
|
-
}
|
|
2418
|
-
return offset;
|
|
2419
|
-
}
|
|
2420
|
-
function patchLegacyConfig(existing, preset) {
|
|
2421
|
-
if (existing.includes("ai-guard")) {
|
|
2422
|
-
return existing;
|
|
2423
|
-
}
|
|
2424
|
-
let patched = existing;
|
|
2425
|
-
if (patched.includes("plugins:")) {
|
|
2426
|
-
patched = patched.replace(/plugins:\s*\[/, `plugins: ['ai-guard', `);
|
|
2427
|
-
} else {
|
|
2428
|
-
patched = patched.replace(
|
|
2429
|
-
"module.exports = {",
|
|
2430
|
-
`module.exports = {
|
|
2431
|
-
plugins: ['ai-guard'],`
|
|
2432
|
-
);
|
|
2433
|
-
}
|
|
2434
|
-
if (patched.includes("extends:")) {
|
|
2435
|
-
patched = patched.replace(
|
|
2436
|
-
/extends:\s*\[/,
|
|
2437
|
-
`extends: ['plugin:ai-guard/${preset}', `
|
|
2438
|
-
);
|
|
2439
|
-
} else {
|
|
2440
|
-
patched = patched.replace(
|
|
2441
|
-
"module.exports = {",
|
|
2442
|
-
`module.exports = {
|
|
2443
|
-
extends: ['plugin:ai-guard/${preset}'],`
|
|
2444
|
-
);
|
|
2445
|
-
}
|
|
2446
|
-
return patched;
|
|
2447
|
-
}
|
|
2448
|
-
function removeNukeIgnore(existing) {
|
|
2449
|
-
const nukePatterns = [/"?\*\*\/\*"?/g];
|
|
2450
|
-
const hasNuke = existing.includes('"**/*"') || existing.includes("'**/*'");
|
|
2451
|
-
if (!hasNuke) return { content: existing, changed: false };
|
|
2452
|
-
let patched = existing.replace(
|
|
2453
|
-
/ignorePatterns:\s*\[.*?\*\*\/\*.*?\]/gs,
|
|
2454
|
-
`ignorePatterns: ['node_modules/', '.next/', 'dist/', 'build/', 'coverage/']`
|
|
2455
|
-
).replace(
|
|
2456
|
-
/ignores:\s*\[.*?\*\*\/\*.*?\]/gs,
|
|
2457
|
-
`ignores: ['node_modules/**', '.next/**', 'dist/**', 'build/**', 'coverage/**']`
|
|
2458
|
-
);
|
|
2459
|
-
for (const re of nukePatterns) {
|
|
2460
|
-
patched = patched.replace(re, "");
|
|
2461
|
-
}
|
|
2462
|
-
return { content: patched, changed: patched !== existing };
|
|
2463
|
-
}
|
|
2464
|
-
var DEFAULT_FLAT_IGNORES = [
|
|
2465
|
-
"node_modules/**",
|
|
2466
|
-
".next/**",
|
|
2467
|
-
"dist/**",
|
|
2468
|
-
"build/**",
|
|
2469
|
-
"coverage/**",
|
|
2470
|
-
"out/**"
|
|
2471
|
-
];
|
|
2472
|
-
var DEFAULT_LEGACY_IGNORES = [
|
|
2473
|
-
"node_modules/",
|
|
2474
|
-
".next/",
|
|
2475
|
-
"dist/",
|
|
2476
|
-
"build/",
|
|
2477
|
-
"coverage/",
|
|
2478
|
-
"out/"
|
|
2479
|
-
];
|
|
2480
|
-
function addIgnoresToFlatConfig(existing) {
|
|
2481
|
-
if (existing.includes("ignores:")) return existing;
|
|
2482
|
-
const block = `
|
|
2483
|
-
// Default ignores added by ai-guard CLI
|
|
2484
|
-
{
|
|
2485
|
-
ignores: ${JSON.stringify(DEFAULT_FLAT_IGNORES)},
|
|
2486
|
-
},
|
|
2487
|
-
`;
|
|
2488
|
-
const lastBracket = existing.lastIndexOf("];");
|
|
2489
|
-
if (lastBracket !== -1) {
|
|
2490
|
-
return existing.slice(0, lastBracket) + block + existing.slice(lastBracket);
|
|
2491
|
-
}
|
|
2492
|
-
return existing;
|
|
2493
|
-
}
|
|
2494
|
-
function addIgnoresToLegacyConfig(existing) {
|
|
2495
|
-
if (existing.includes("ignorePatterns")) return existing;
|
|
2496
|
-
return existing.replace(
|
|
2497
|
-
"module.exports = {",
|
|
2498
|
-
`module.exports = {
|
|
2499
|
-
ignorePatterns: ${JSON.stringify(DEFAULT_LEGACY_IGNORES)},`
|
|
2500
|
-
);
|
|
2501
|
-
}
|
|
2502
|
-
function validateFlatConfigText(content) {
|
|
2503
|
-
const problems = [];
|
|
2504
|
-
if (!content.includes("export default")) {
|
|
2505
|
-
problems.push("Missing `export default` \u2014 file must be an ES module");
|
|
2506
|
-
}
|
|
2507
|
-
if (!content.includes("eslint-plugin-ai-guard") && !content.includes("aiGuard")) {
|
|
2508
|
-
problems.push("Plugin import not found \u2014 ai-guard plugin may not be referenced");
|
|
2509
|
-
}
|
|
2510
|
-
if (!content.trim().endsWith("];") && !content.trim().endsWith("];")) {
|
|
2511
|
-
}
|
|
2512
|
-
if (content.includes('"**/*"') || content.includes("'**/*'")) {
|
|
2513
|
-
problems.push(
|
|
2514
|
-
'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
|
|
2515
|
-
);
|
|
2516
|
-
}
|
|
2517
|
-
return problems;
|
|
2518
|
-
}
|
|
2519
|
-
function validateLegacyConfigText(content) {
|
|
2520
|
-
const problems = [];
|
|
2521
|
-
if (!content.includes("module.exports")) {
|
|
2522
|
-
problems.push("Missing `module.exports` \u2014 legacy config must use CJS exports");
|
|
2523
|
-
}
|
|
2524
|
-
if (!content.includes("ai-guard")) {
|
|
2525
|
-
problems.push("Plugin not referenced \u2014 add ai-guard to plugins and extends");
|
|
2526
|
-
}
|
|
2527
|
-
if (content.includes('"**/*"') || content.includes("'**/*'")) {
|
|
2528
|
-
problems.push(
|
|
2529
|
-
'Config contains "**/*" ignore pattern \u2014 this will prevent all files from being linted'
|
|
2530
|
-
);
|
|
2531
|
-
}
|
|
2532
|
-
return problems;
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
2604
|
// cli/commands/init.ts
|
|
2605
|
+
init_config_manager();
|
|
2536
2606
|
function classifyVerifyError(err) {
|
|
2537
2607
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2538
2608
|
const clean = msg.replace(/\s+at\s+.+/gm, "").trim();
|
|
@@ -2556,7 +2626,7 @@ async function verifyConfigLoads(configPath, cwd) {
|
|
|
2556
2626
|
overrideConfigFile: configPath
|
|
2557
2627
|
});
|
|
2558
2628
|
await eslint.lintText("", {
|
|
2559
|
-
filePath:
|
|
2629
|
+
filePath: import_path4.default.join(cwd, "_ai_guard_probe_.js")
|
|
2560
2630
|
});
|
|
2561
2631
|
return null;
|
|
2562
2632
|
} catch (err) {
|
|
@@ -2592,18 +2662,18 @@ function registerInitCommand(program2) {
|
|
|
2592
2662
|
log.warn("eslint-plugin-ai-guard not found");
|
|
2593
2663
|
}
|
|
2594
2664
|
if (env.configType !== "none") {
|
|
2595
|
-
log.success(`ESLint config found: ${import_chalk3.default.white(
|
|
2665
|
+
log.success(`ESLint config found: ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath ?? env.configType))}`);
|
|
2596
2666
|
} else {
|
|
2597
2667
|
log.info("No ESLint config found \u2014 will generate one");
|
|
2598
2668
|
}
|
|
2599
|
-
|
|
2669
|
+
const eslintNeedsFlat = requiresFlatConfig(env.eslintMajor);
|
|
2670
|
+
if (env.hasConflictingConfigs && !eslintNeedsFlat) {
|
|
2600
2671
|
log.blank();
|
|
2601
2672
|
log.warn("Conflicting configs detected:");
|
|
2602
2673
|
for (const p of env.allConfigPaths) {
|
|
2603
|
-
log.print(` ${import_chalk3.default.yellow("\u2192")} ${import_chalk3.default.white(
|
|
2674
|
+
log.print(` ${import_chalk3.default.yellow("\u2192")} ${import_chalk3.default.white(import_path4.default.relative(cwd, p))}`);
|
|
2604
2675
|
}
|
|
2605
|
-
log.print(` ${import_chalk3.default.gray("
|
|
2606
|
-
log.print(` ${import_chalk3.default.gray("Consider removing the legacy config to avoid confusion.")}`);
|
|
2676
|
+
log.print(` ${import_chalk3.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
|
|
2607
2677
|
}
|
|
2608
2678
|
log.blank();
|
|
2609
2679
|
const toInstall = [];
|
|
@@ -2626,19 +2696,30 @@ function registerInitCommand(program2) {
|
|
|
2626
2696
|
process.exit(1);
|
|
2627
2697
|
return;
|
|
2628
2698
|
}
|
|
2699
|
+
try {
|
|
2700
|
+
const { createRequire } = await import("module");
|
|
2701
|
+
const { pathToFileURL } = await import("url");
|
|
2702
|
+
const req = createRequire(import_path4.default.join(cwd, "_probe.js"));
|
|
2703
|
+
const resolvedPath = req.resolve("eslint-plugin-ai-guard");
|
|
2704
|
+
const aiGuard = await import(pathToFileURL(resolvedPath).href);
|
|
2705
|
+
const plugin2 = aiGuard.default || aiGuard;
|
|
2706
|
+
if (!plugin2.default || !plugin2[preset] || !plugin2[preset].rules) {
|
|
2707
|
+
log.error("Invalid plugin export structure. Try reinstalling eslint-plugin-ai-guard.");
|
|
2708
|
+
process.exit(1);
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
} catch (e) {
|
|
2712
|
+
}
|
|
2629
2713
|
const forceFlat = opts.flat === true;
|
|
2630
|
-
const eslintNeedsFlat = requiresFlatConfig(env.eslintMajor);
|
|
2631
2714
|
const existingIsFlat = isFlat(env.configType);
|
|
2632
2715
|
const existingIsLegacy = isLegacy(env.configType);
|
|
2633
2716
|
const useFlat = forceFlat || eslintNeedsFlat || existingIsFlat || env.configType === "none";
|
|
2634
2717
|
if (eslintNeedsFlat && existingIsLegacy) {
|
|
2635
2718
|
log.section("Config Migration Required");
|
|
2636
2719
|
log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
|
|
2637
|
-
log.print(` ${import_chalk3.default.white(
|
|
2720
|
+
log.print(` ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2638
2721
|
log.blank();
|
|
2639
|
-
log.print(` ${import_chalk3.default.bold("ai-guard will create a new flat config
|
|
2640
|
-
log.print(` The legacy file will remain; ESLint v9 will automatically`);
|
|
2641
|
-
log.print(` ignore it in favour of the flat config.`);
|
|
2722
|
+
log.print(` ${import_chalk3.default.bold("ai-guard will create a new flat config.")}`);
|
|
2642
2723
|
log.blank();
|
|
2643
2724
|
}
|
|
2644
2725
|
log.section(`Configuring ESLint${isDryRun ? " (dry run)" : ""}`);
|
|
@@ -2648,7 +2729,7 @@ function registerInitCommand(program2) {
|
|
|
2648
2729
|
const raw = readConfig(env.configPath);
|
|
2649
2730
|
const { content: fixed, changed } = removeNukeIgnore(raw);
|
|
2650
2731
|
if (changed) {
|
|
2651
|
-
log.warn(`Removed "**/*" ignore pattern from ${
|
|
2732
|
+
log.warn(`Removed "**/*" ignore pattern from ${import_path4.default.relative(cwd, env.configPath)}`);
|
|
2652
2733
|
if (!isDryRun) {
|
|
2653
2734
|
backupConfig(env.configPath);
|
|
2654
2735
|
writeConfig(env.configPath, fixed);
|
|
@@ -2660,7 +2741,7 @@ function registerInitCommand(program2) {
|
|
|
2660
2741
|
}
|
|
2661
2742
|
if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
|
|
2662
2743
|
const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
|
|
2663
|
-
finalConfigPath =
|
|
2744
|
+
finalConfigPath = import_path4.default.join(cwd, targetName);
|
|
2664
2745
|
const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2665
2746
|
if (isDryRun) {
|
|
2666
2747
|
log.print(` ${import_chalk3.default.gray(`[dry-run] would create: ${targetName}`)}`);
|
|
@@ -2679,19 +2760,25 @@ function registerInitCommand(program2) {
|
|
|
2679
2760
|
configWritten = true;
|
|
2680
2761
|
}
|
|
2681
2762
|
} else if (useFlat && existingIsFlat) {
|
|
2763
|
+
const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
2682
2764
|
const existing = readConfig(env.configPath);
|
|
2765
|
+
const isInvalid = isInvalidAiGuardConfig2(existing);
|
|
2683
2766
|
const patched = patchFlatConfig(existing, preset);
|
|
2684
|
-
if (patched === existing) {
|
|
2767
|
+
if (patched === existing && !isInvalid) {
|
|
2685
2768
|
log.warn("ai-guard is already present in your config \u2014 no changes made");
|
|
2686
2769
|
finalConfigPath = env.configPath;
|
|
2687
2770
|
} else if (isDryRun) {
|
|
2688
2771
|
finalConfigPath = env.configPath;
|
|
2689
|
-
log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${
|
|
2772
|
+
log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path4.default.relative(cwd, env.configPath)}`)}`);
|
|
2690
2773
|
} else {
|
|
2691
2774
|
const bak = backupConfig(env.configPath);
|
|
2692
|
-
log.info(`Backed up \u2192 ${import_chalk3.default.gray(
|
|
2775
|
+
log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path4.default.relative(cwd, bak))}`);
|
|
2693
2776
|
writeConfig(env.configPath, patched);
|
|
2694
|
-
|
|
2777
|
+
if (isInvalid) {
|
|
2778
|
+
log.success(`Detected invalid ai-guard config \u2014 repairing ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2779
|
+
} else {
|
|
2780
|
+
log.success(`Patched ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2781
|
+
}
|
|
2695
2782
|
finalConfigPath = env.configPath;
|
|
2696
2783
|
configWritten = true;
|
|
2697
2784
|
}
|
|
@@ -2703,16 +2790,37 @@ function registerInitCommand(program2) {
|
|
|
2703
2790
|
finalConfigPath = env.configPath;
|
|
2704
2791
|
} else if (isDryRun) {
|
|
2705
2792
|
finalConfigPath = env.configPath;
|
|
2706
|
-
log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${
|
|
2793
|
+
log.print(` ${import_chalk3.default.gray(`[dry-run] would patch: ${import_path4.default.relative(cwd, env.configPath)}`)}`);
|
|
2707
2794
|
} else {
|
|
2708
2795
|
const bak = backupConfig(env.configPath);
|
|
2709
|
-
log.info(`Backed up \u2192 ${import_chalk3.default.gray(
|
|
2796
|
+
log.info(`Backed up \u2192 ${import_chalk3.default.gray(import_path4.default.relative(cwd, bak))}`);
|
|
2710
2797
|
writeConfig(env.configPath, patched);
|
|
2711
|
-
log.success(`Patched ${import_chalk3.default.white(
|
|
2798
|
+
log.success(`Patched ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2712
2799
|
finalConfigPath = env.configPath;
|
|
2713
2800
|
configWritten = true;
|
|
2714
2801
|
}
|
|
2715
2802
|
}
|
|
2803
|
+
if (eslintNeedsFlat) {
|
|
2804
|
+
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2805
|
+
let cleanedUp = false;
|
|
2806
|
+
for (const p of legacyConfigs) {
|
|
2807
|
+
if (import_fs3.default.existsSync(p)) {
|
|
2808
|
+
if (!isDryRun) {
|
|
2809
|
+
try {
|
|
2810
|
+
import_fs3.default.renameSync(p, `${p}.bak`);
|
|
2811
|
+
cleanedUp = true;
|
|
2812
|
+
} catch (err) {
|
|
2813
|
+
}
|
|
2814
|
+
} else {
|
|
2815
|
+
cleanedUp = true;
|
|
2816
|
+
log.print(` ${import_chalk3.default.gray(`[dry-run] would rename ${import_path4.default.relative(cwd, p)} to ${import_path4.default.basename(p)}.bak`)}`);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
if (cleanedUp && !isDryRun) {
|
|
2821
|
+
log.success("Legacy config backed up to avoid ESLint conflict");
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2716
2824
|
if (isDryRun) {
|
|
2717
2825
|
log.blank();
|
|
2718
2826
|
log.print(` ${import_chalk3.default.yellow("Dry run complete \u2014 no files written.")}`);
|
|
@@ -2724,7 +2832,7 @@ function registerInitCommand(program2) {
|
|
|
2724
2832
|
log.blank();
|
|
2725
2833
|
log.section("Validation");
|
|
2726
2834
|
if (!import_fs3.default.existsSync(finalConfigPath)) {
|
|
2727
|
-
log.error(`Config file not found after write: ${
|
|
2835
|
+
log.error(`Config file not found after write: ${import_path4.default.relative(cwd, finalConfigPath)}`);
|
|
2728
2836
|
log.blank();
|
|
2729
2837
|
log.print(` ${import_chalk3.default.bold("Problem:")} File write may have failed.`);
|
|
2730
2838
|
log.print(` ${import_chalk3.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
|
|
@@ -2740,7 +2848,7 @@ function registerInitCommand(program2) {
|
|
|
2740
2848
|
log.print(` ${import_chalk3.default.red("\u2716")} ${e}`);
|
|
2741
2849
|
}
|
|
2742
2850
|
} else {
|
|
2743
|
-
log.success(`Config structure valid: ${import_chalk3.default.white(
|
|
2851
|
+
log.success(`Config structure valid: ${import_chalk3.default.white(import_path4.default.relative(cwd, finalConfigPath))}`);
|
|
2744
2852
|
}
|
|
2745
2853
|
log.info("Verifying ESLint can load the config\u2026");
|
|
2746
2854
|
const loadError = await verifyConfigLoads(finalConfigPath, cwd);
|
|
@@ -2758,9 +2866,13 @@ function registerInitCommand(program2) {
|
|
|
2758
2866
|
log.blank();
|
|
2759
2867
|
log.print(` ${import_chalk3.default.bold("Problem:")}`);
|
|
2760
2868
|
log.print(` ${import_chalk3.default.red(loadError.message)}`);
|
|
2869
|
+
if (loadError.kind === "unknown") {
|
|
2870
|
+
log.blank();
|
|
2871
|
+
log.print(` ${import_chalk3.default.yellow("Generated config may be using incorrect plugin structure.")}`);
|
|
2872
|
+
}
|
|
2761
2873
|
log.blank();
|
|
2762
2874
|
log.print(` ${import_chalk3.default.bold("Fix:")}`);
|
|
2763
|
-
log.print(` 1. Check the syntax in ${import_chalk3.default.cyan(
|
|
2875
|
+
log.print(` 1. Check the syntax in ${import_chalk3.default.cyan(import_path4.default.relative(cwd, finalConfigPath))}`);
|
|
2764
2876
|
log.print(` 2. Make sure ${import_chalk3.default.cyan("eslint-plugin-ai-guard")} is installed`);
|
|
2765
2877
|
log.print(` 3. Run ${import_chalk3.default.cyan("ai-guard doctor")} for full diagnostics`);
|
|
2766
2878
|
log.blank();
|
|
@@ -2773,8 +2885,16 @@ function registerInitCommand(program2) {
|
|
|
2773
2885
|
log.blank();
|
|
2774
2886
|
log.section("Setup Complete");
|
|
2775
2887
|
log.blank();
|
|
2776
|
-
|
|
2777
|
-
|
|
2888
|
+
let cleanedUpInfo = false;
|
|
2889
|
+
if (eslintNeedsFlat) {
|
|
2890
|
+
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2891
|
+
cleanedUpInfo = legacyConfigs.some((p) => import_fs3.default.existsSync(`${p}.bak`) && !import_fs3.default.existsSync(p));
|
|
2892
|
+
}
|
|
2893
|
+
const lingeringConflicts = env.hasConflictingConfigs && !eslintNeedsFlat && !cleanedUpInfo;
|
|
2894
|
+
if (loadError === null && !lingeringConflicts) {
|
|
2895
|
+
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold.green("Configuration validated successfully")}`);
|
|
2896
|
+
}
|
|
2897
|
+
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("ESLint config:")} ${import_chalk3.default.cyan(import_path4.default.relative(cwd, finalConfigPath))}`);
|
|
2778
2898
|
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})`)}`);
|
|
2779
2899
|
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("Preset:")} ${import_chalk3.default.cyan(preset)}`);
|
|
2780
2900
|
log.blank();
|
|
@@ -2788,12 +2908,13 @@ function registerInitCommand(program2) {
|
|
|
2788
2908
|
|
|
2789
2909
|
// cli/commands/doctor.ts
|
|
2790
2910
|
var import_chalk4 = __toESM(require("chalk"));
|
|
2791
|
-
var
|
|
2911
|
+
var import_path5 = __toESM(require("path"));
|
|
2912
|
+
init_config_manager();
|
|
2792
2913
|
async function probeConfigLoad(configPath, cwd) {
|
|
2793
2914
|
try {
|
|
2794
2915
|
const { ESLint } = await import("eslint");
|
|
2795
2916
|
const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
|
|
2796
|
-
await eslint.lintText("", { filePath:
|
|
2917
|
+
await eslint.lintText("", { filePath: import_path5.default.join(cwd, "_probe_.js") });
|
|
2797
2918
|
return { ok: true, error: null };
|
|
2798
2919
|
} catch (err) {
|
|
2799
2920
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2828,7 +2949,7 @@ function registerDoctorCommand(program2) {
|
|
|
2828
2949
|
checks.push({
|
|
2829
2950
|
label: "ESLint config present",
|
|
2830
2951
|
pass: hasConfig,
|
|
2831
|
-
detail: hasConfig ? `${
|
|
2952
|
+
detail: hasConfig ? `${import_path5.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
|
|
2832
2953
|
fix: hasConfig ? void 0 : "ai-guard init",
|
|
2833
2954
|
note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
|
|
2834
2955
|
});
|
|
@@ -2847,30 +2968,31 @@ function registerDoctorCommand(program2) {
|
|
|
2847
2968
|
}
|
|
2848
2969
|
if (hasConfig && env.configPath) {
|
|
2849
2970
|
let pluginWired = false;
|
|
2971
|
+
let isInvalidConfig = false;
|
|
2850
2972
|
let readError = null;
|
|
2851
2973
|
try {
|
|
2974
|
+
const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
2852
2975
|
const content = readConfig(env.configPath);
|
|
2853
2976
|
pluginWired = content.includes("ai-guard") || content.includes("eslint-plugin-ai-guard");
|
|
2977
|
+
isInvalidConfig = isInvalidAiGuardConfig2(content);
|
|
2854
2978
|
} catch (err) {
|
|
2855
2979
|
readError = err instanceof Error ? err.message : String(err);
|
|
2856
2980
|
}
|
|
2857
2981
|
checks.push({
|
|
2858
2982
|
label: "Plugin wired in config",
|
|
2859
|
-
pass: pluginWired,
|
|
2860
|
-
detail: readError ? `Could not read config: ${readError}` : pluginWired ? "eslint-plugin-ai-guard referenced in config" : `Config exists but ai-guard plugin not found in ${
|
|
2861
|
-
fix: pluginWired
|
|
2862
|
-
});
|
|
2863
|
-
}
|
|
2864
|
-
if (env.hasConflictingConfigs) {
|
|
2865
|
-
checks.push({
|
|
2866
|
-
label: "No conflicting config files",
|
|
2867
|
-
pass: false,
|
|
2868
|
-
detail: `Both flat config AND legacy config files found:
|
|
2869
|
-
${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}`).join("\n")}`,
|
|
2870
|
-
fix: "Remove the .eslintrc.* file \u2014 ESLint v9 uses only eslint.config.*",
|
|
2871
|
-
note: "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion."
|
|
2983
|
+
pass: pluginWired && !isInvalidConfig,
|
|
2984
|
+
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)}`,
|
|
2985
|
+
fix: isInvalidConfig || !pluginWired && !readError ? "ai-guard init (will patch/repair your existing config)" : void 0
|
|
2872
2986
|
});
|
|
2873
2987
|
}
|
|
2988
|
+
checks.push({
|
|
2989
|
+
label: "No conflicting config files",
|
|
2990
|
+
pass: !env.hasConflictingConfigs,
|
|
2991
|
+
detail: env.hasConflictingConfigs ? `Conflicting config detected:
|
|
2992
|
+
${env.allConfigPaths.map((p) => ` ${import_path5.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
|
|
2993
|
+
fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
|
|
2994
|
+
note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
|
|
2995
|
+
});
|
|
2874
2996
|
if (hasConfig && env.hasNukeIgnore) {
|
|
2875
2997
|
checks.push({
|
|
2876
2998
|
label: 'No "ignore everything" pattern',
|
|
@@ -2894,7 +3016,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
|
|
|
2894
3016
|
label: "ESLint config loads without errors",
|
|
2895
3017
|
pass: probe.ok,
|
|
2896
3018
|
detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
|
|
2897
|
-
fix: probe.ok ? void 0 : `Check syntax in ${
|
|
3019
|
+
fix: probe.ok ? void 0 : `Check syntax in ${import_path5.default.relative(cwd, env.configPath)}`,
|
|
2898
3020
|
note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
|
|
2899
3021
|
});
|
|
2900
3022
|
}
|
|
@@ -2957,7 +3079,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
|
|
|
2957
3079
|
// cli/commands/preset.ts
|
|
2958
3080
|
var import_prompts = require("@inquirer/prompts");
|
|
2959
3081
|
var import_chalk5 = __toESM(require("chalk"));
|
|
2960
|
-
|
|
3082
|
+
init_config_manager();
|
|
3083
|
+
var import_path6 = __toESM(require("path"));
|
|
2961
3084
|
function registerPresetCommand(program2) {
|
|
2962
3085
|
program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
|
|
2963
3086
|
const cwd = process.cwd();
|
|
@@ -2985,21 +3108,21 @@ function registerPresetCommand(program2) {
|
|
|
2985
3108
|
const env = detect(cwd);
|
|
2986
3109
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
|
|
2987
3110
|
if (env.configType === "none") {
|
|
2988
|
-
const configPath = useFlat ?
|
|
3111
|
+
const configPath = useFlat ? import_path6.default.join(cwd, "eslint.config.mjs") : import_path6.default.join(cwd, ".eslintrc.js");
|
|
2989
3112
|
const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2990
3113
|
writeConfig(configPath, content);
|
|
2991
|
-
log.success(`Created ${import_chalk5.default.white(
|
|
3114
|
+
log.success(`Created ${import_chalk5.default.white(import_path6.default.relative(cwd, configPath))} with ${import_chalk5.default.cyan(preset)} preset`);
|
|
2992
3115
|
} else {
|
|
2993
3116
|
const configPath = env.configPath;
|
|
2994
3117
|
const backupPath = backupConfig(configPath);
|
|
2995
|
-
log.info(`Backed up \u2192 ${import_chalk5.default.gray(
|
|
3118
|
+
log.info(`Backed up \u2192 ${import_chalk5.default.gray(import_path6.default.relative(cwd, backupPath))}`);
|
|
2996
3119
|
const existing = readConfig(configPath);
|
|
2997
3120
|
const patched = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
|
|
2998
3121
|
if (patched === existing) {
|
|
2999
3122
|
log.warn("Config already contains ai-guard. Edit manually to change preset.");
|
|
3000
3123
|
} else {
|
|
3001
3124
|
writeConfig(configPath, patched);
|
|
3002
|
-
log.success(`Patched ${import_chalk5.default.white(
|
|
3125
|
+
log.success(`Patched ${import_chalk5.default.white(import_path6.default.relative(cwd, configPath))}`);
|
|
3003
3126
|
}
|
|
3004
3127
|
}
|
|
3005
3128
|
log.blank();
|
|
@@ -3038,7 +3161,8 @@ function registerPresetCommand(program2) {
|
|
|
3038
3161
|
|
|
3039
3162
|
// cli/commands/ignore.ts
|
|
3040
3163
|
var import_chalk6 = __toESM(require("chalk"));
|
|
3041
|
-
|
|
3164
|
+
init_config_manager();
|
|
3165
|
+
var import_path7 = __toESM(require("path"));
|
|
3042
3166
|
var DEFAULT_IGNORES = [
|
|
3043
3167
|
".next/",
|
|
3044
3168
|
"dist/",
|
|
@@ -3060,21 +3184,21 @@ function registerIgnoreCommand(program2) {
|
|
|
3060
3184
|
const env = detect(cwd);
|
|
3061
3185
|
if (env.configType === "none") {
|
|
3062
3186
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
|
|
3063
|
-
const configPath = useFlat ?
|
|
3187
|
+
const configPath = useFlat ? import_path7.default.join(cwd, "eslint.config.mjs") : import_path7.default.join(cwd, ".eslintrc.js");
|
|
3064
3188
|
const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
|
|
3065
3189
|
writeConfig(configPath, content);
|
|
3066
|
-
log.success(`No config found \u2014 generated ${import_chalk6.default.white(
|
|
3190
|
+
log.success(`No config found \u2014 generated ${import_chalk6.default.white(import_path7.default.relative(cwd, configPath))} with ignores included`);
|
|
3067
3191
|
} else {
|
|
3068
3192
|
const configPath = env.configPath;
|
|
3069
3193
|
const backupPath = backupConfig(configPath);
|
|
3070
|
-
log.info(`Backed up \u2192 ${import_chalk6.default.gray(
|
|
3194
|
+
log.info(`Backed up \u2192 ${import_chalk6.default.gray(import_path7.default.relative(cwd, backupPath))}`);
|
|
3071
3195
|
const existing = readConfig(configPath);
|
|
3072
3196
|
const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
|
|
3073
3197
|
if (patched === existing) {
|
|
3074
3198
|
log.warn("Ignore patterns already present in config \u2014 no changes made.");
|
|
3075
3199
|
} else {
|
|
3076
3200
|
writeConfig(configPath, patched);
|
|
3077
|
-
log.success(`Patched ${import_chalk6.default.white(
|
|
3201
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, configPath))} with ignore patterns`);
|
|
3078
3202
|
}
|
|
3079
3203
|
}
|
|
3080
3204
|
log.blank();
|
|
@@ -3086,7 +3210,7 @@ function registerIgnoreCommand(program2) {
|
|
|
3086
3210
|
|
|
3087
3211
|
// cli/commands/baseline.ts
|
|
3088
3212
|
var import_fs4 = __toESM(require("fs"));
|
|
3089
|
-
var
|
|
3213
|
+
var import_path8 = __toESM(require("path"));
|
|
3090
3214
|
var import_chalk7 = __toESM(require("chalk"));
|
|
3091
3215
|
var import_ora2 = __toESM(require("ora"));
|
|
3092
3216
|
var BASELINE_FILE = ".ai-guard-baseline.json";
|
|
@@ -3106,12 +3230,12 @@ function saveBaseline(result, preset, cwd) {
|
|
|
3106
3230
|
totalIssues: result.totalIssues,
|
|
3107
3231
|
entries
|
|
3108
3232
|
};
|
|
3109
|
-
const baselinePath =
|
|
3233
|
+
const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
|
|
3110
3234
|
import_fs4.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
3111
3235
|
return baselinePath;
|
|
3112
3236
|
}
|
|
3113
3237
|
function loadBaseline(cwd) {
|
|
3114
|
-
const baselinePath =
|
|
3238
|
+
const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
|
|
3115
3239
|
if (!import_fs4.default.existsSync(baselinePath)) return null;
|
|
3116
3240
|
try {
|
|
3117
3241
|
const raw = import_fs4.default.readFileSync(baselinePath, "utf-8");
|
|
@@ -3188,7 +3312,7 @@ function registerBaselineCommand(program2) {
|
|
|
3188
3312
|
}
|
|
3189
3313
|
const baselinePath = saveBaseline(result, preset, cwd);
|
|
3190
3314
|
log.success(
|
|
3191
|
-
`Baseline saved \u2192 ${import_chalk7.default.white(
|
|
3315
|
+
`Baseline saved \u2192 ${import_chalk7.default.white(import_path8.default.relative(cwd, baselinePath))}`
|
|
3192
3316
|
);
|
|
3193
3317
|
log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
|
|
3194
3318
|
log.info(` Preset: ${import_chalk7.default.cyan(preset)}`);
|