eslint-plugin-ai-guard 1.1.3 → 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 +345 -271
- 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,225 +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: {
|
|
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
2604
|
// cli/commands/init.ts
|
|
2605
|
+
init_config_manager();
|
|
2544
2606
|
function classifyVerifyError(err) {
|
|
2545
2607
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2546
2608
|
const clean = msg.replace(/\s+at\s+.+/gm, "").trim();
|
|
@@ -2564,7 +2626,7 @@ async function verifyConfigLoads(configPath, cwd) {
|
|
|
2564
2626
|
overrideConfigFile: configPath
|
|
2565
2627
|
});
|
|
2566
2628
|
await eslint.lintText("", {
|
|
2567
|
-
filePath:
|
|
2629
|
+
filePath: import_path4.default.join(cwd, "_ai_guard_probe_.js")
|
|
2568
2630
|
});
|
|
2569
2631
|
return null;
|
|
2570
2632
|
} catch (err) {
|
|
@@ -2600,7 +2662,7 @@ function registerInitCommand(program2) {
|
|
|
2600
2662
|
log.warn("eslint-plugin-ai-guard not found");
|
|
2601
2663
|
}
|
|
2602
2664
|
if (env.configType !== "none") {
|
|
2603
|
-
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))}`);
|
|
2604
2666
|
} else {
|
|
2605
2667
|
log.info("No ESLint config found \u2014 will generate one");
|
|
2606
2668
|
}
|
|
@@ -2609,7 +2671,7 @@ function registerInitCommand(program2) {
|
|
|
2609
2671
|
log.blank();
|
|
2610
2672
|
log.warn("Conflicting configs detected:");
|
|
2611
2673
|
for (const p of env.allConfigPaths) {
|
|
2612
|
-
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))}`);
|
|
2613
2675
|
}
|
|
2614
2676
|
log.print(` ${import_chalk3.default.gray("Multiple config formats found \u2014 this can cause unpredictable behavior.")}`);
|
|
2615
2677
|
}
|
|
@@ -2637,7 +2699,7 @@ function registerInitCommand(program2) {
|
|
|
2637
2699
|
try {
|
|
2638
2700
|
const { createRequire } = await import("module");
|
|
2639
2701
|
const { pathToFileURL } = await import("url");
|
|
2640
|
-
const req = createRequire(
|
|
2702
|
+
const req = createRequire(import_path4.default.join(cwd, "_probe.js"));
|
|
2641
2703
|
const resolvedPath = req.resolve("eslint-plugin-ai-guard");
|
|
2642
2704
|
const aiGuard = await import(pathToFileURL(resolvedPath).href);
|
|
2643
2705
|
const plugin2 = aiGuard.default || aiGuard;
|
|
@@ -2655,7 +2717,7 @@ function registerInitCommand(program2) {
|
|
|
2655
2717
|
if (eslintNeedsFlat && existingIsLegacy) {
|
|
2656
2718
|
log.section("Config Migration Required");
|
|
2657
2719
|
log.warn(`ESLint v${env.eslintMajor} requires flat config, but a legacy config was found:`);
|
|
2658
|
-
log.print(` ${import_chalk3.default.white(
|
|
2720
|
+
log.print(` ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2659
2721
|
log.blank();
|
|
2660
2722
|
log.print(` ${import_chalk3.default.bold("ai-guard will create a new flat config.")}`);
|
|
2661
2723
|
log.blank();
|
|
@@ -2667,7 +2729,7 @@ function registerInitCommand(program2) {
|
|
|
2667
2729
|
const raw = readConfig(env.configPath);
|
|
2668
2730
|
const { content: fixed, changed } = removeNukeIgnore(raw);
|
|
2669
2731
|
if (changed) {
|
|
2670
|
-
log.warn(`Removed "**/*" ignore pattern from ${
|
|
2732
|
+
log.warn(`Removed "**/*" ignore pattern from ${import_path4.default.relative(cwd, env.configPath)}`);
|
|
2671
2733
|
if (!isDryRun) {
|
|
2672
2734
|
backupConfig(env.configPath);
|
|
2673
2735
|
writeConfig(env.configPath, fixed);
|
|
@@ -2679,7 +2741,7 @@ function registerInitCommand(program2) {
|
|
|
2679
2741
|
}
|
|
2680
2742
|
if (env.configType === "none" || eslintNeedsFlat && existingIsLegacy) {
|
|
2681
2743
|
const targetName = useFlat ? "eslint.config.mjs" : ".eslintrc.js";
|
|
2682
|
-
finalConfigPath =
|
|
2744
|
+
finalConfigPath = import_path4.default.join(cwd, targetName);
|
|
2683
2745
|
const content2 = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
2684
2746
|
if (isDryRun) {
|
|
2685
2747
|
log.print(` ${import_chalk3.default.gray(`[dry-run] would create: ${targetName}`)}`);
|
|
@@ -2698,19 +2760,25 @@ function registerInitCommand(program2) {
|
|
|
2698
2760
|
configWritten = true;
|
|
2699
2761
|
}
|
|
2700
2762
|
} else if (useFlat && existingIsFlat) {
|
|
2763
|
+
const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
2701
2764
|
const existing = readConfig(env.configPath);
|
|
2765
|
+
const isInvalid = isInvalidAiGuardConfig2(existing);
|
|
2702
2766
|
const patched = patchFlatConfig(existing, preset);
|
|
2703
|
-
if (patched === existing) {
|
|
2767
|
+
if (patched === existing && !isInvalid) {
|
|
2704
2768
|
log.warn("ai-guard is already present in your config \u2014 no changes made");
|
|
2705
2769
|
finalConfigPath = env.configPath;
|
|
2706
2770
|
} else if (isDryRun) {
|
|
2707
2771
|
finalConfigPath = env.configPath;
|
|
2708
|
-
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)}`)}`);
|
|
2709
2773
|
} else {
|
|
2710
2774
|
const bak = backupConfig(env.configPath);
|
|
2711
|
-
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))}`);
|
|
2712
2776
|
writeConfig(env.configPath, patched);
|
|
2713
|
-
|
|
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
|
+
}
|
|
2714
2782
|
finalConfigPath = env.configPath;
|
|
2715
2783
|
configWritten = true;
|
|
2716
2784
|
}
|
|
@@ -2722,12 +2790,12 @@ function registerInitCommand(program2) {
|
|
|
2722
2790
|
finalConfigPath = env.configPath;
|
|
2723
2791
|
} else if (isDryRun) {
|
|
2724
2792
|
finalConfigPath = env.configPath;
|
|
2725
|
-
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)}`)}`);
|
|
2726
2794
|
} else {
|
|
2727
2795
|
const bak = backupConfig(env.configPath);
|
|
2728
|
-
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))}`);
|
|
2729
2797
|
writeConfig(env.configPath, patched);
|
|
2730
|
-
log.success(`Patched ${import_chalk3.default.white(
|
|
2798
|
+
log.success(`Patched ${import_chalk3.default.white(import_path4.default.relative(cwd, env.configPath))}`);
|
|
2731
2799
|
finalConfigPath = env.configPath;
|
|
2732
2800
|
configWritten = true;
|
|
2733
2801
|
}
|
|
@@ -2745,7 +2813,7 @@ function registerInitCommand(program2) {
|
|
|
2745
2813
|
}
|
|
2746
2814
|
} else {
|
|
2747
2815
|
cleanedUp = true;
|
|
2748
|
-
log.print(` ${import_chalk3.default.gray(`[dry-run] would rename ${
|
|
2816
|
+
log.print(` ${import_chalk3.default.gray(`[dry-run] would rename ${import_path4.default.relative(cwd, p)} to ${import_path4.default.basename(p)}.bak`)}`);
|
|
2749
2817
|
}
|
|
2750
2818
|
}
|
|
2751
2819
|
}
|
|
@@ -2764,7 +2832,7 @@ function registerInitCommand(program2) {
|
|
|
2764
2832
|
log.blank();
|
|
2765
2833
|
log.section("Validation");
|
|
2766
2834
|
if (!import_fs3.default.existsSync(finalConfigPath)) {
|
|
2767
|
-
log.error(`Config file not found after write: ${
|
|
2835
|
+
log.error(`Config file not found after write: ${import_path4.default.relative(cwd, finalConfigPath)}`);
|
|
2768
2836
|
log.blank();
|
|
2769
2837
|
log.print(` ${import_chalk3.default.bold("Problem:")} File write may have failed.`);
|
|
2770
2838
|
log.print(` ${import_chalk3.default.bold("Fix:")} Check disk space and file permissions, then re-run.`);
|
|
@@ -2780,7 +2848,7 @@ function registerInitCommand(program2) {
|
|
|
2780
2848
|
log.print(` ${import_chalk3.default.red("\u2716")} ${e}`);
|
|
2781
2849
|
}
|
|
2782
2850
|
} else {
|
|
2783
|
-
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))}`);
|
|
2784
2852
|
}
|
|
2785
2853
|
log.info("Verifying ESLint can load the config\u2026");
|
|
2786
2854
|
const loadError = await verifyConfigLoads(finalConfigPath, cwd);
|
|
@@ -2804,7 +2872,7 @@ function registerInitCommand(program2) {
|
|
|
2804
2872
|
}
|
|
2805
2873
|
log.blank();
|
|
2806
2874
|
log.print(` ${import_chalk3.default.bold("Fix:")}`);
|
|
2807
|
-
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))}`);
|
|
2808
2876
|
log.print(` 2. Make sure ${import_chalk3.default.cyan("eslint-plugin-ai-guard")} is installed`);
|
|
2809
2877
|
log.print(` 3. Run ${import_chalk3.default.cyan("ai-guard doctor")} for full diagnostics`);
|
|
2810
2878
|
log.blank();
|
|
@@ -2826,7 +2894,7 @@ function registerInitCommand(program2) {
|
|
|
2826
2894
|
if (loadError === null && !lingeringConflicts) {
|
|
2827
2895
|
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold.green("Configuration validated successfully")}`);
|
|
2828
2896
|
}
|
|
2829
|
-
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("ESLint config:")} ${import_chalk3.default.cyan(
|
|
2897
|
+
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("ESLint config:")} ${import_chalk3.default.cyan(import_path4.default.relative(cwd, finalConfigPath))}`);
|
|
2830
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})`)}`);
|
|
2831
2899
|
log.print(` ${import_chalk3.default.green("\u2714")} ${import_chalk3.default.bold("Preset:")} ${import_chalk3.default.cyan(preset)}`);
|
|
2832
2900
|
log.blank();
|
|
@@ -2840,12 +2908,13 @@ function registerInitCommand(program2) {
|
|
|
2840
2908
|
|
|
2841
2909
|
// cli/commands/doctor.ts
|
|
2842
2910
|
var import_chalk4 = __toESM(require("chalk"));
|
|
2843
|
-
var
|
|
2911
|
+
var import_path5 = __toESM(require("path"));
|
|
2912
|
+
init_config_manager();
|
|
2844
2913
|
async function probeConfigLoad(configPath, cwd) {
|
|
2845
2914
|
try {
|
|
2846
2915
|
const { ESLint } = await import("eslint");
|
|
2847
2916
|
const eslint = new ESLint({ cwd, overrideConfigFile: configPath });
|
|
2848
|
-
await eslint.lintText("", { filePath:
|
|
2917
|
+
await eslint.lintText("", { filePath: import_path5.default.join(cwd, "_probe_.js") });
|
|
2849
2918
|
return { ok: true, error: null };
|
|
2850
2919
|
} catch (err) {
|
|
2851
2920
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2880,7 +2949,7 @@ function registerDoctorCommand(program2) {
|
|
|
2880
2949
|
checks.push({
|
|
2881
2950
|
label: "ESLint config present",
|
|
2882
2951
|
pass: hasConfig,
|
|
2883
|
-
detail: hasConfig ? `${
|
|
2952
|
+
detail: hasConfig ? `${import_path5.default.relative(cwd, env.configPath ?? "")} (${env.configType})` : "No eslint.config.* or .eslintrc.* found",
|
|
2884
2953
|
fix: hasConfig ? void 0 : "ai-guard init",
|
|
2885
2954
|
note: hasConfig ? void 0 : "This will generate the correct config format for your ESLint version"
|
|
2886
2955
|
});
|
|
@@ -2899,25 +2968,28 @@ function registerDoctorCommand(program2) {
|
|
|
2899
2968
|
}
|
|
2900
2969
|
if (hasConfig && env.configPath) {
|
|
2901
2970
|
let pluginWired = false;
|
|
2971
|
+
let isInvalidConfig = false;
|
|
2902
2972
|
let readError = null;
|
|
2903
2973
|
try {
|
|
2974
|
+
const { isInvalidAiGuardConfig: isInvalidAiGuardConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
2904
2975
|
const content = readConfig(env.configPath);
|
|
2905
2976
|
pluginWired = content.includes("ai-guard") || content.includes("eslint-plugin-ai-guard");
|
|
2977
|
+
isInvalidConfig = isInvalidAiGuardConfig2(content);
|
|
2906
2978
|
} catch (err) {
|
|
2907
2979
|
readError = err instanceof Error ? err.message : String(err);
|
|
2908
2980
|
}
|
|
2909
2981
|
checks.push({
|
|
2910
2982
|
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 ${
|
|
2913
|
-
fix: pluginWired
|
|
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
|
|
2914
2986
|
});
|
|
2915
2987
|
}
|
|
2916
2988
|
checks.push({
|
|
2917
2989
|
label: "No conflicting config files",
|
|
2918
2990
|
pass: !env.hasConflictingConfigs,
|
|
2919
2991
|
detail: env.hasConflictingConfigs ? `Conflicting config detected:
|
|
2920
|
-
${env.allConfigPaths.map((p) => ` ${
|
|
2992
|
+
${env.allConfigPaths.map((p) => ` ${import_path5.default.relative(cwd, p)}`).join("\n")}` : "Only one config format detected",
|
|
2921
2993
|
fix: env.hasConflictingConfigs ? "Remove or backup legacy config" : void 0,
|
|
2922
2994
|
note: env.hasConflictingConfigs ? "ESLint v9 silently ignores .eslintrc.* when a flat config exists. This can cause confusion." : void 0
|
|
2923
2995
|
});
|
|
@@ -2944,7 +3016,7 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
|
|
|
2944
3016
|
label: "ESLint config loads without errors",
|
|
2945
3017
|
pass: probe.ok,
|
|
2946
3018
|
detail: probe.ok ? "ESLint loaded config successfully" : probe.error ?? "Unknown error",
|
|
2947
|
-
fix: probe.ok ? void 0 : `Check syntax in ${
|
|
3019
|
+
fix: probe.ok ? void 0 : `Check syntax in ${import_path5.default.relative(cwd, env.configPath)}`,
|
|
2948
3020
|
note: probe.ok ? void 0 : "Run: ai-guard init --dry-run to preview a fresh config"
|
|
2949
3021
|
});
|
|
2950
3022
|
}
|
|
@@ -3007,7 +3079,8 @@ ${env.allConfigPaths.map((p) => ` ${import_path4.default.relative(cwd, p)}
|
|
|
3007
3079
|
// cli/commands/preset.ts
|
|
3008
3080
|
var import_prompts = require("@inquirer/prompts");
|
|
3009
3081
|
var import_chalk5 = __toESM(require("chalk"));
|
|
3010
|
-
|
|
3082
|
+
init_config_manager();
|
|
3083
|
+
var import_path6 = __toESM(require("path"));
|
|
3011
3084
|
function registerPresetCommand(program2) {
|
|
3012
3085
|
program2.command("preset").description("Interactively select and apply an ai-guard preset to your ESLint config").action(async () => {
|
|
3013
3086
|
const cwd = process.cwd();
|
|
@@ -3035,21 +3108,21 @@ function registerPresetCommand(program2) {
|
|
|
3035
3108
|
const env = detect(cwd);
|
|
3036
3109
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9 || isFlat(env.configType);
|
|
3037
3110
|
if (env.configType === "none") {
|
|
3038
|
-
const configPath = useFlat ?
|
|
3111
|
+
const configPath = useFlat ? import_path6.default.join(cwd, "eslint.config.mjs") : import_path6.default.join(cwd, ".eslintrc.js");
|
|
3039
3112
|
const content = useFlat ? generateFlatConfig(preset) : generateLegacyConfig(preset);
|
|
3040
3113
|
writeConfig(configPath, content);
|
|
3041
|
-
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`);
|
|
3042
3115
|
} else {
|
|
3043
3116
|
const configPath = env.configPath;
|
|
3044
3117
|
const backupPath = backupConfig(configPath);
|
|
3045
|
-
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))}`);
|
|
3046
3119
|
const existing = readConfig(configPath);
|
|
3047
3120
|
const patched = isFlat(env.configType) ? patchFlatConfig(existing, preset) : patchLegacyConfig(existing, preset);
|
|
3048
3121
|
if (patched === existing) {
|
|
3049
3122
|
log.warn("Config already contains ai-guard. Edit manually to change preset.");
|
|
3050
3123
|
} else {
|
|
3051
3124
|
writeConfig(configPath, patched);
|
|
3052
|
-
log.success(`Patched ${import_chalk5.default.white(
|
|
3125
|
+
log.success(`Patched ${import_chalk5.default.white(import_path6.default.relative(cwd, configPath))}`);
|
|
3053
3126
|
}
|
|
3054
3127
|
}
|
|
3055
3128
|
log.blank();
|
|
@@ -3088,7 +3161,8 @@ function registerPresetCommand(program2) {
|
|
|
3088
3161
|
|
|
3089
3162
|
// cli/commands/ignore.ts
|
|
3090
3163
|
var import_chalk6 = __toESM(require("chalk"));
|
|
3091
|
-
|
|
3164
|
+
init_config_manager();
|
|
3165
|
+
var import_path7 = __toESM(require("path"));
|
|
3092
3166
|
var DEFAULT_IGNORES = [
|
|
3093
3167
|
".next/",
|
|
3094
3168
|
"dist/",
|
|
@@ -3110,21 +3184,21 @@ function registerIgnoreCommand(program2) {
|
|
|
3110
3184
|
const env = detect(cwd);
|
|
3111
3185
|
if (env.configType === "none") {
|
|
3112
3186
|
const useFlat = env.eslintMajor !== null && env.eslintMajor >= 9;
|
|
3113
|
-
const configPath = useFlat ?
|
|
3187
|
+
const configPath = useFlat ? import_path7.default.join(cwd, "eslint.config.mjs") : import_path7.default.join(cwd, ".eslintrc.js");
|
|
3114
3188
|
const content = useFlat ? generateFlatConfig("recommended") : generateLegacyConfig("recommended");
|
|
3115
3189
|
writeConfig(configPath, content);
|
|
3116
|
-
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`);
|
|
3117
3191
|
} else {
|
|
3118
3192
|
const configPath = env.configPath;
|
|
3119
3193
|
const backupPath = backupConfig(configPath);
|
|
3120
|
-
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))}`);
|
|
3121
3195
|
const existing = readConfig(configPath);
|
|
3122
3196
|
const patched = isFlat(env.configType) ? addIgnoresToFlatConfig(existing) : addIgnoresToLegacyConfig(existing);
|
|
3123
3197
|
if (patched === existing) {
|
|
3124
3198
|
log.warn("Ignore patterns already present in config \u2014 no changes made.");
|
|
3125
3199
|
} else {
|
|
3126
3200
|
writeConfig(configPath, patched);
|
|
3127
|
-
log.success(`Patched ${import_chalk6.default.white(
|
|
3201
|
+
log.success(`Patched ${import_chalk6.default.white(import_path7.default.relative(cwd, configPath))} with ignore patterns`);
|
|
3128
3202
|
}
|
|
3129
3203
|
}
|
|
3130
3204
|
log.blank();
|
|
@@ -3136,7 +3210,7 @@ function registerIgnoreCommand(program2) {
|
|
|
3136
3210
|
|
|
3137
3211
|
// cli/commands/baseline.ts
|
|
3138
3212
|
var import_fs4 = __toESM(require("fs"));
|
|
3139
|
-
var
|
|
3213
|
+
var import_path8 = __toESM(require("path"));
|
|
3140
3214
|
var import_chalk7 = __toESM(require("chalk"));
|
|
3141
3215
|
var import_ora2 = __toESM(require("ora"));
|
|
3142
3216
|
var BASELINE_FILE = ".ai-guard-baseline.json";
|
|
@@ -3156,12 +3230,12 @@ function saveBaseline(result, preset, cwd) {
|
|
|
3156
3230
|
totalIssues: result.totalIssues,
|
|
3157
3231
|
entries
|
|
3158
3232
|
};
|
|
3159
|
-
const baselinePath =
|
|
3233
|
+
const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
|
|
3160
3234
|
import_fs4.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
3161
3235
|
return baselinePath;
|
|
3162
3236
|
}
|
|
3163
3237
|
function loadBaseline(cwd) {
|
|
3164
|
-
const baselinePath =
|
|
3238
|
+
const baselinePath = import_path8.default.join(cwd, BASELINE_FILE);
|
|
3165
3239
|
if (!import_fs4.default.existsSync(baselinePath)) return null;
|
|
3166
3240
|
try {
|
|
3167
3241
|
const raw = import_fs4.default.readFileSync(baselinePath, "utf-8");
|
|
@@ -3238,7 +3312,7 @@ function registerBaselineCommand(program2) {
|
|
|
3238
3312
|
}
|
|
3239
3313
|
const baselinePath = saveBaseline(result, preset, cwd);
|
|
3240
3314
|
log.success(
|
|
3241
|
-
`Baseline saved \u2192 ${import_chalk7.default.white(
|
|
3315
|
+
`Baseline saved \u2192 ${import_chalk7.default.white(import_path8.default.relative(cwd, baselinePath))}`
|
|
3242
3316
|
);
|
|
3243
3317
|
log.info(` ${result.totalIssues} issues recorded (${result.totalErrors} errors, ${result.totalWarnings} warnings)`);
|
|
3244
3318
|
log.info(` Preset: ${import_chalk7.default.cyan(preset)}`);
|