aislop 0.8.1 → 0.8.2
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.js +90 -11
- package/dist/index.js +91 -12
- package/dist/{json-7EtVVIhd.js → json-DxLkV8n2.js} +1 -1
- package/dist/mcp.js +26 -11
- package/dist/{version-B7_FRirI.js → version-G3ekYjY1.js} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -730,9 +730,14 @@ const detectOverAbstraction = async (context) => {
|
|
|
730
730
|
return diagnostics;
|
|
731
731
|
};
|
|
732
732
|
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region src/engines/ai-slop/non-production-paths.ts
|
|
735
|
+
const DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3|cli|cli-[\w-]+|[\w-]+-cli)\//i;
|
|
736
|
+
const BASENAME_PATTERN = /(?:^|\/)(?:benchmark|bench|demo|example|script|seed|migrate|profile|smoke|stress|load|debug|repro)[-_.][^/]*\.[mc]?[jt]sx?$|(?:^|\/)[^/]+[-_](?:benchmark|bench|demo|example)\.[mc]?[jt]sx?$/i;
|
|
737
|
+
const isNonProductionPath = (relativePath) => DIR_PATTERN.test(relativePath) || BASENAME_PATTERN.test(relativePath);
|
|
738
|
+
|
|
733
739
|
//#endregion
|
|
734
740
|
//#region src/engines/ai-slop/comments.ts
|
|
735
|
-
const NON_PRODUCTION_DIR_PATTERN$2 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
736
741
|
const TRIVIAL_VERB_STEMS = "Import|Defin|Initializ|Setting|Set\\s+up|Setup|Return|Check|Loop|Iterat|Creat|Updat|Delet|Remov|Handl|Get|Fetch|Increment|Decrement|Writ|Runn|Run|Pars|Execut|Extract|Sav|Load|Build|Start|Stopp|Stop|Clean(?:up|\\s+up)?|Configur|Validat|Process|Queue|Fire|Emit|Dispatch|Log|Print|Render";
|
|
737
742
|
const TRIVIAL_JS_COMMENT_PATTERNS = [/\/\/\s*This (?:function|method|class|variable|constant) (?:will |is used to |is responsible for )?/i, new RegExp(`\\/\\/\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
738
743
|
const TRIVIAL_PYTHON_COMMENT_PATTERNS = [/^#\s*This (?:function|method|class) (?:will |is used to )?/i, new RegExp(`^#\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
@@ -789,7 +794,7 @@ const detectTrivialComments = async (context) => {
|
|
|
789
794
|
for (const filePath of files) {
|
|
790
795
|
if (isAutoGenerated(filePath)) continue;
|
|
791
796
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
792
|
-
if (
|
|
797
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
793
798
|
let content;
|
|
794
799
|
try {
|
|
795
800
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -825,11 +830,10 @@ const slop = (filePath, line, rule, severity, message, help, fixable) => ({
|
|
|
825
830
|
fixable
|
|
826
831
|
});
|
|
827
832
|
const LOGGER_FILE_PATTERN = /(?:^|\/)(?:logger|logging|log)\.[^/]+$/i;
|
|
828
|
-
const NON_PRODUCTION_DIR_PATTERN$1 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|cli|cli-[\w-]+|[\w-]+-cli)\//;
|
|
829
833
|
const detectConsoleLeftovers = (content, relativePath, ext) => {
|
|
830
834
|
if (!JS_EXTENSIONS$4.has(ext)) return [];
|
|
831
835
|
if (LOGGER_FILE_PATTERN.test(relativePath)) return [];
|
|
832
|
-
if (
|
|
836
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
833
837
|
const diagnostics = [];
|
|
834
838
|
const lines = content.split("\n");
|
|
835
839
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -879,7 +883,7 @@ const asAnyPattern = new RegExp(`\\bas\\s+any\\b`);
|
|
|
879
883
|
const doubleAssertPattern = new RegExp(`\\bas\\s+unknown\\s+as\\s+`);
|
|
880
884
|
const detectUnsafeTypePatterns = (content, relativePath, ext) => {
|
|
881
885
|
if (ext !== ".ts" && ext !== ".tsx") return [];
|
|
882
|
-
if (
|
|
886
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
883
887
|
const diagnostics = [];
|
|
884
888
|
const lines = content.split("\n");
|
|
885
889
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -1784,7 +1788,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
|
|
|
1784
1788
|
|
|
1785
1789
|
//#endregion
|
|
1786
1790
|
//#region src/engines/ai-slop/narrative-comments.ts
|
|
1787
|
-
const NON_PRODUCTION_DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
1788
1791
|
const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
|
|
1789
1792
|
const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
|
|
1790
1793
|
const getCommentSyntax = (ext) => {
|
|
@@ -1935,13 +1938,21 @@ const looksLikeGoDocComment = (block, ext) => {
|
|
|
1935
1938
|
if (!declMatch) return false;
|
|
1936
1939
|
return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
|
|
1937
1940
|
};
|
|
1938
|
-
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see)
|
|
1941
|
+
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
|
|
1939
1942
|
const hasDocIndicator = (block) => {
|
|
1940
1943
|
const joined = block.prose.join(" ");
|
|
1941
1944
|
if (DOC_INDICATOR_RE.test(joined)) return true;
|
|
1942
1945
|
for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
|
|
1943
1946
|
return false;
|
|
1944
1947
|
};
|
|
1948
|
+
const hasPreambleSlopSignal = (block) => {
|
|
1949
|
+
const joined = block.prose.join(" ");
|
|
1950
|
+
for (const l of block.prose) {
|
|
1951
|
+
if (EXPLANATORY_OPENERS.test(l)) return true;
|
|
1952
|
+
if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
|
|
1953
|
+
}
|
|
1954
|
+
return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
|
|
1955
|
+
};
|
|
1945
1956
|
const detectNarrativeInBlock = (block, ext) => {
|
|
1946
1957
|
if (looksLikeLicenseHeader(block)) return {
|
|
1947
1958
|
matched: false,
|
|
@@ -1981,9 +1992,13 @@ const detectNarrativeInBlock = (block, ext) => {
|
|
|
1981
1992
|
matched: false,
|
|
1982
1993
|
reason: ""
|
|
1983
1994
|
};
|
|
1984
|
-
if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
1995
|
+
if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
1996
|
+
matched: true,
|
|
1997
|
+
reason: "multi-line preamble before declaration"
|
|
1998
|
+
};
|
|
1999
|
+
if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
|
|
1985
2000
|
matched: true,
|
|
1986
|
-
reason:
|
|
2001
|
+
reason: "JSDoc preamble with slop signal"
|
|
1987
2002
|
};
|
|
1988
2003
|
if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
|
|
1989
2004
|
matched: true,
|
|
@@ -2023,7 +2038,7 @@ const detectNarrativeComments = async (context) => {
|
|
|
2023
2038
|
const syntax = getCommentSyntax(ext);
|
|
2024
2039
|
if (!syntax) continue;
|
|
2025
2040
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
2026
|
-
if (
|
|
2041
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
2027
2042
|
let content;
|
|
2028
2043
|
try {
|
|
2029
2044
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -7556,7 +7571,7 @@ const renderCleanRun = (input, deps = {}) => {
|
|
|
7556
7571
|
|
|
7557
7572
|
//#endregion
|
|
7558
7573
|
//#region src/version.ts
|
|
7559
|
-
const APP_VERSION = "0.8.
|
|
7574
|
+
const APP_VERSION = "0.8.2";
|
|
7560
7575
|
|
|
7561
7576
|
//#endregion
|
|
7562
7577
|
//#region src/utils/telemetry.ts
|
|
@@ -9382,13 +9397,77 @@ const fixDependencyAudit = async (context, onProgress) => {
|
|
|
9382
9397
|
}
|
|
9383
9398
|
onProgress?.("Dependency audit fixes · skipping (pnpm audit unavailable and no package-lock.json for npm fallback)");
|
|
9384
9399
|
};
|
|
9400
|
+
const SEMVER_PREFIX_RE = /^[~^]?/;
|
|
9401
|
+
const parseSemverMin = (spec) => {
|
|
9402
|
+
const match = spec.replace(SEMVER_PREFIX_RE, "").match(/^(\d+|x|X|\*)(?:\.(\d+|x|X|\*))?(?:\.(\d+|x|X|\*))?/);
|
|
9403
|
+
if (!match) return null;
|
|
9404
|
+
const head = match[1];
|
|
9405
|
+
if (!/^\d+$/.test(head)) return null;
|
|
9406
|
+
const toNum = (part) => {
|
|
9407
|
+
if (!part) return 0;
|
|
9408
|
+
return /^\d+$/.test(part) ? Number(part) : 0;
|
|
9409
|
+
};
|
|
9410
|
+
return [
|
|
9411
|
+
Number(head),
|
|
9412
|
+
toNum(match[2]),
|
|
9413
|
+
toNum(match[3])
|
|
9414
|
+
];
|
|
9415
|
+
};
|
|
9416
|
+
const isDowngrade = (oldSpec, newSpec) => {
|
|
9417
|
+
const oldV = parseSemverMin(oldSpec);
|
|
9418
|
+
const newV = parseSemverMin(newSpec);
|
|
9419
|
+
if (!oldV || !newV) return false;
|
|
9420
|
+
for (let i = 0; i < 3; i++) {
|
|
9421
|
+
if ((newV[i] ?? 0) < (oldV[i] ?? 0)) return true;
|
|
9422
|
+
if ((newV[i] ?? 0) > (oldV[i] ?? 0)) return false;
|
|
9423
|
+
}
|
|
9424
|
+
return false;
|
|
9425
|
+
};
|
|
9426
|
+
const DEP_BUCKETS = [
|
|
9427
|
+
"dependencies",
|
|
9428
|
+
"devDependencies",
|
|
9429
|
+
"peerDependencies",
|
|
9430
|
+
"optionalDependencies"
|
|
9431
|
+
];
|
|
9432
|
+
const snapshotPackageVersions = (pkg) => {
|
|
9433
|
+
const map = /* @__PURE__ */ new Map();
|
|
9434
|
+
for (const bucket of DEP_BUCKETS) {
|
|
9435
|
+
const deps = pkg[bucket];
|
|
9436
|
+
if (!deps || typeof deps !== "object") continue;
|
|
9437
|
+
for (const [name, version] of Object.entries(deps)) if (typeof version === "string") map.set(`${bucket}:${name}`, version);
|
|
9438
|
+
}
|
|
9439
|
+
return map;
|
|
9440
|
+
};
|
|
9441
|
+
const revertDowngrades = (rootDir, before) => {
|
|
9442
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
9443
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
9444
|
+
const reverted = [];
|
|
9445
|
+
for (const bucket of DEP_BUCKETS) {
|
|
9446
|
+
const deps = pkg[bucket];
|
|
9447
|
+
if (!deps) continue;
|
|
9448
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
9449
|
+
const prior = before.get(`${bucket}:${name}`);
|
|
9450
|
+
if (!prior) continue;
|
|
9451
|
+
if (isDowngrade(prior, version)) {
|
|
9452
|
+
deps[name] = prior;
|
|
9453
|
+
reverted.push(`${name} ${version} → ${prior}`);
|
|
9454
|
+
}
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
if (reverted.length > 0) fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
9458
|
+
return reverted;
|
|
9459
|
+
};
|
|
9385
9460
|
const runNpmAuditFix = async (rootDir, onProgress) => {
|
|
9461
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
9462
|
+
const before = snapshotPackageVersions(JSON.parse(fs.readFileSync(pkgPath, "utf-8")));
|
|
9386
9463
|
onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
|
|
9387
9464
|
const result = await runSubprocess("npm", ["audit", "fix"], {
|
|
9388
9465
|
cwd: rootDir,
|
|
9389
9466
|
timeout: INSTALL_TIMEOUT
|
|
9390
9467
|
});
|
|
9391
9468
|
if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
|
|
9469
|
+
const reverted = revertDowngrades(rootDir, before);
|
|
9470
|
+
if (reverted.length > 0) onProgress?.(`Dependency audit fixes · reverted ${reverted.length} downgrade(s): ${reverted.join(", ")}`);
|
|
9392
9471
|
onProgress?.("Dependency audit fixes · running npm install");
|
|
9393
9472
|
const installResult = await runSubprocess("npm", ["install"], {
|
|
9394
9473
|
cwd: rootDir,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-
|
|
1
|
+
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-G3ekYjY1.js";
|
|
2
2
|
import { n as runSubprocess, t as isToolInstalled } from "./subprocess-CQUJDGgn.js";
|
|
3
3
|
import { r as runGenericLinter, t as fixRubyLint } from "./generic-BrcWMW7E.js";
|
|
4
4
|
import { n as runExpoDoctor } from "./expo-doctor-Bz0LZhQ6.js";
|
|
@@ -1272,9 +1272,14 @@ const detectOverAbstraction = async (context) => {
|
|
|
1272
1272
|
return diagnostics;
|
|
1273
1273
|
};
|
|
1274
1274
|
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/engines/ai-slop/non-production-paths.ts
|
|
1277
|
+
const DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3|cli|cli-[\w-]+|[\w-]+-cli)\//i;
|
|
1278
|
+
const BASENAME_PATTERN = /(?:^|\/)(?:benchmark|bench|demo|example|script|seed|migrate|profile|smoke|stress|load|debug|repro)[-_.][^/]*\.[mc]?[jt]sx?$|(?:^|\/)[^/]+[-_](?:benchmark|bench|demo|example)\.[mc]?[jt]sx?$/i;
|
|
1279
|
+
const isNonProductionPath = (relativePath) => DIR_PATTERN.test(relativePath) || BASENAME_PATTERN.test(relativePath);
|
|
1280
|
+
|
|
1275
1281
|
//#endregion
|
|
1276
1282
|
//#region src/engines/ai-slop/comments.ts
|
|
1277
|
-
const NON_PRODUCTION_DIR_PATTERN$2 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
1278
1283
|
const TRIVIAL_VERB_STEMS = "Import|Defin|Initializ|Setting|Set\\s+up|Setup|Return|Check|Loop|Iterat|Creat|Updat|Delet|Remov|Handl|Get|Fetch|Increment|Decrement|Writ|Runn|Run|Pars|Execut|Extract|Sav|Load|Build|Start|Stopp|Stop|Clean(?:up|\\s+up)?|Configur|Validat|Process|Queue|Fire|Emit|Dispatch|Log|Print|Render";
|
|
1279
1284
|
const TRIVIAL_JS_COMMENT_PATTERNS = [/\/\/\s*This (?:function|method|class|variable|constant) (?:will |is used to |is responsible for )?/i, new RegExp(`\\/\\/\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
1280
1285
|
const TRIVIAL_PYTHON_COMMENT_PATTERNS = [/^#\s*This (?:function|method|class) (?:will |is used to )?/i, new RegExp(`^#\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
@@ -1331,7 +1336,7 @@ const detectTrivialComments = async (context) => {
|
|
|
1331
1336
|
for (const filePath of files) {
|
|
1332
1337
|
if (isAutoGenerated(filePath)) continue;
|
|
1333
1338
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
1334
|
-
if (
|
|
1339
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
1335
1340
|
let content;
|
|
1336
1341
|
try {
|
|
1337
1342
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -1367,11 +1372,10 @@ const slop = (filePath, line, rule, severity, message, help, fixable) => ({
|
|
|
1367
1372
|
fixable
|
|
1368
1373
|
});
|
|
1369
1374
|
const LOGGER_FILE_PATTERN = /(?:^|\/)(?:logger|logging|log)\.[^/]+$/i;
|
|
1370
|
-
const NON_PRODUCTION_DIR_PATTERN$1 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|cli|cli-[\w-]+|[\w-]+-cli)\//;
|
|
1371
1375
|
const detectConsoleLeftovers = (content, relativePath, ext) => {
|
|
1372
1376
|
if (!JS_EXTENSIONS$4.has(ext)) return [];
|
|
1373
1377
|
if (LOGGER_FILE_PATTERN.test(relativePath)) return [];
|
|
1374
|
-
if (
|
|
1378
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
1375
1379
|
const diagnostics = [];
|
|
1376
1380
|
const lines = content.split("\n");
|
|
1377
1381
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -1421,7 +1425,7 @@ const asAnyPattern = new RegExp(`\\bas\\s+any\\b`);
|
|
|
1421
1425
|
const doubleAssertPattern = new RegExp(`\\bas\\s+unknown\\s+as\\s+`);
|
|
1422
1426
|
const detectUnsafeTypePatterns = (content, relativePath, ext) => {
|
|
1423
1427
|
if (ext !== ".ts" && ext !== ".tsx") return [];
|
|
1424
|
-
if (
|
|
1428
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
1425
1429
|
const diagnostics = [];
|
|
1426
1430
|
const lines = content.split("\n");
|
|
1427
1431
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -2326,7 +2330,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
|
|
|
2326
2330
|
|
|
2327
2331
|
//#endregion
|
|
2328
2332
|
//#region src/engines/ai-slop/narrative-comments.ts
|
|
2329
|
-
const NON_PRODUCTION_DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
2330
2333
|
const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
|
|
2331
2334
|
const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
|
|
2332
2335
|
const getCommentSyntax = (ext) => {
|
|
@@ -2477,13 +2480,21 @@ const looksLikeGoDocComment = (block, ext) => {
|
|
|
2477
2480
|
if (!declMatch) return false;
|
|
2478
2481
|
return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
|
|
2479
2482
|
};
|
|
2480
|
-
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see)
|
|
2483
|
+
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
|
|
2481
2484
|
const hasDocIndicator = (block) => {
|
|
2482
2485
|
const joined = block.prose.join(" ");
|
|
2483
2486
|
if (DOC_INDICATOR_RE.test(joined)) return true;
|
|
2484
2487
|
for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
|
|
2485
2488
|
return false;
|
|
2486
2489
|
};
|
|
2490
|
+
const hasPreambleSlopSignal = (block) => {
|
|
2491
|
+
const joined = block.prose.join(" ");
|
|
2492
|
+
for (const l of block.prose) {
|
|
2493
|
+
if (EXPLANATORY_OPENERS.test(l)) return true;
|
|
2494
|
+
if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
|
|
2495
|
+
}
|
|
2496
|
+
return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
|
|
2497
|
+
};
|
|
2487
2498
|
const detectNarrativeInBlock = (block, ext) => {
|
|
2488
2499
|
if (looksLikeLicenseHeader(block)) return {
|
|
2489
2500
|
matched: false,
|
|
@@ -2523,9 +2534,13 @@ const detectNarrativeInBlock = (block, ext) => {
|
|
|
2523
2534
|
matched: false,
|
|
2524
2535
|
reason: ""
|
|
2525
2536
|
};
|
|
2526
|
-
if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
2537
|
+
if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
2538
|
+
matched: true,
|
|
2539
|
+
reason: "multi-line preamble before declaration"
|
|
2540
|
+
};
|
|
2541
|
+
if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
|
|
2527
2542
|
matched: true,
|
|
2528
|
-
reason:
|
|
2543
|
+
reason: "JSDoc preamble with slop signal"
|
|
2529
2544
|
};
|
|
2530
2545
|
if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
|
|
2531
2546
|
matched: true,
|
|
@@ -2565,7 +2580,7 @@ const detectNarrativeComments = async (context) => {
|
|
|
2565
2580
|
const syntax = getCommentSyntax(ext);
|
|
2566
2581
|
if (!syntax) continue;
|
|
2567
2582
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
2568
|
-
if (
|
|
2583
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
2569
2584
|
let content;
|
|
2570
2585
|
try {
|
|
2571
2586
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -6361,7 +6376,7 @@ const scanCommand = async (directory, config, options) => {
|
|
|
6361
6376
|
});
|
|
6362
6377
|
}
|
|
6363
6378
|
if (options.json) {
|
|
6364
|
-
const { buildJsonOutput } = await import("./json-
|
|
6379
|
+
const { buildJsonOutput } = await import("./json-DxLkV8n2.js");
|
|
6365
6380
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
6366
6381
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
6367
6382
|
return { exitCode };
|
|
@@ -7361,13 +7376,77 @@ const fixDependencyAudit = async (context, onProgress) => {
|
|
|
7361
7376
|
}
|
|
7362
7377
|
onProgress?.("Dependency audit fixes · skipping (pnpm audit unavailable and no package-lock.json for npm fallback)");
|
|
7363
7378
|
};
|
|
7379
|
+
const SEMVER_PREFIX_RE = /^[~^]?/;
|
|
7380
|
+
const parseSemverMin = (spec) => {
|
|
7381
|
+
const match = spec.replace(SEMVER_PREFIX_RE, "").match(/^(\d+|x|X|\*)(?:\.(\d+|x|X|\*))?(?:\.(\d+|x|X|\*))?/);
|
|
7382
|
+
if (!match) return null;
|
|
7383
|
+
const head = match[1];
|
|
7384
|
+
if (!/^\d+$/.test(head)) return null;
|
|
7385
|
+
const toNum = (part) => {
|
|
7386
|
+
if (!part) return 0;
|
|
7387
|
+
return /^\d+$/.test(part) ? Number(part) : 0;
|
|
7388
|
+
};
|
|
7389
|
+
return [
|
|
7390
|
+
Number(head),
|
|
7391
|
+
toNum(match[2]),
|
|
7392
|
+
toNum(match[3])
|
|
7393
|
+
];
|
|
7394
|
+
};
|
|
7395
|
+
const isDowngrade = (oldSpec, newSpec) => {
|
|
7396
|
+
const oldV = parseSemverMin(oldSpec);
|
|
7397
|
+
const newV = parseSemverMin(newSpec);
|
|
7398
|
+
if (!oldV || !newV) return false;
|
|
7399
|
+
for (let i = 0; i < 3; i++) {
|
|
7400
|
+
if ((newV[i] ?? 0) < (oldV[i] ?? 0)) return true;
|
|
7401
|
+
if ((newV[i] ?? 0) > (oldV[i] ?? 0)) return false;
|
|
7402
|
+
}
|
|
7403
|
+
return false;
|
|
7404
|
+
};
|
|
7405
|
+
const DEP_BUCKETS = [
|
|
7406
|
+
"dependencies",
|
|
7407
|
+
"devDependencies",
|
|
7408
|
+
"peerDependencies",
|
|
7409
|
+
"optionalDependencies"
|
|
7410
|
+
];
|
|
7411
|
+
const snapshotPackageVersions = (pkg) => {
|
|
7412
|
+
const map = /* @__PURE__ */ new Map();
|
|
7413
|
+
for (const bucket of DEP_BUCKETS) {
|
|
7414
|
+
const deps = pkg[bucket];
|
|
7415
|
+
if (!deps || typeof deps !== "object") continue;
|
|
7416
|
+
for (const [name, version] of Object.entries(deps)) if (typeof version === "string") map.set(`${bucket}:${name}`, version);
|
|
7417
|
+
}
|
|
7418
|
+
return map;
|
|
7419
|
+
};
|
|
7420
|
+
const revertDowngrades = (rootDir, before) => {
|
|
7421
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
7422
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
7423
|
+
const reverted = [];
|
|
7424
|
+
for (const bucket of DEP_BUCKETS) {
|
|
7425
|
+
const deps = pkg[bucket];
|
|
7426
|
+
if (!deps) continue;
|
|
7427
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
7428
|
+
const prior = before.get(`${bucket}:${name}`);
|
|
7429
|
+
if (!prior) continue;
|
|
7430
|
+
if (isDowngrade(prior, version)) {
|
|
7431
|
+
deps[name] = prior;
|
|
7432
|
+
reverted.push(`${name} ${version} → ${prior}`);
|
|
7433
|
+
}
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
if (reverted.length > 0) fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
7437
|
+
return reverted;
|
|
7438
|
+
};
|
|
7364
7439
|
const runNpmAuditFix = async (rootDir, onProgress) => {
|
|
7440
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
7441
|
+
const before = snapshotPackageVersions(JSON.parse(fs.readFileSync(pkgPath, "utf-8")));
|
|
7365
7442
|
onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
|
|
7366
7443
|
const result = await runSubprocess("npm", ["audit", "fix"], {
|
|
7367
7444
|
cwd: rootDir,
|
|
7368
7445
|
timeout: INSTALL_TIMEOUT
|
|
7369
7446
|
});
|
|
7370
7447
|
if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
|
|
7448
|
+
const reverted = revertDowngrades(rootDir, before);
|
|
7449
|
+
if (reverted.length > 0) onProgress?.(`Dependency audit fixes · reverted ${reverted.length} downgrade(s): ${reverted.join(", ")}`);
|
|
7371
7450
|
onProgress?.("Dependency audit fixes · running npm install");
|
|
7372
7451
|
const installResult = await runSubprocess("npm", ["install"], {
|
|
7373
7452
|
cwd: rootDir,
|
package/dist/mcp.js
CHANGED
|
@@ -531,9 +531,14 @@ const detectOverAbstraction = async (context) => {
|
|
|
531
531
|
return diagnostics;
|
|
532
532
|
};
|
|
533
533
|
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region src/engines/ai-slop/non-production-paths.ts
|
|
536
|
+
const DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3|cli|cli-[\w-]+|[\w-]+-cli)\//i;
|
|
537
|
+
const BASENAME_PATTERN = /(?:^|\/)(?:benchmark|bench|demo|example|script|seed|migrate|profile|smoke|stress|load|debug|repro)[-_.][^/]*\.[mc]?[jt]sx?$|(?:^|\/)[^/]+[-_](?:benchmark|bench|demo|example)\.[mc]?[jt]sx?$/i;
|
|
538
|
+
const isNonProductionPath = (relativePath) => DIR_PATTERN.test(relativePath) || BASENAME_PATTERN.test(relativePath);
|
|
539
|
+
|
|
534
540
|
//#endregion
|
|
535
541
|
//#region src/engines/ai-slop/comments.ts
|
|
536
|
-
const NON_PRODUCTION_DIR_PATTERN$2 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
537
542
|
const TRIVIAL_VERB_STEMS = "Import|Defin|Initializ|Setting|Set\\s+up|Setup|Return|Check|Loop|Iterat|Creat|Updat|Delet|Remov|Handl|Get|Fetch|Increment|Decrement|Writ|Runn|Run|Pars|Execut|Extract|Sav|Load|Build|Start|Stopp|Stop|Clean(?:up|\\s+up)?|Configur|Validat|Process|Queue|Fire|Emit|Dispatch|Log|Print|Render";
|
|
538
543
|
const TRIVIAL_JS_COMMENT_PATTERNS = [/\/\/\s*This (?:function|method|class|variable|constant) (?:will |is used to |is responsible for )?/i, new RegExp(`\\/\\/\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
539
544
|
const TRIVIAL_PYTHON_COMMENT_PATTERNS = [/^#\s*This (?:function|method|class) (?:will |is used to )?/i, new RegExp(`^#\\s*(?:${TRIVIAL_VERB_STEMS})(?:e|es|ing|s)?\\b`, "i")];
|
|
@@ -590,7 +595,7 @@ const detectTrivialComments = async (context) => {
|
|
|
590
595
|
for (const filePath of files) {
|
|
591
596
|
if (isAutoGenerated(filePath)) continue;
|
|
592
597
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
593
|
-
if (
|
|
598
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
594
599
|
let content;
|
|
595
600
|
try {
|
|
596
601
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -626,11 +631,10 @@ const slop = (filePath, line, rule, severity, message, help, fixable) => ({
|
|
|
626
631
|
fixable
|
|
627
632
|
});
|
|
628
633
|
const LOGGER_FILE_PATTERN = /(?:^|\/)(?:logger|logging|log)\.[^/]+$/i;
|
|
629
|
-
const NON_PRODUCTION_DIR_PATTERN$1 = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|cli|cli-[\w-]+|[\w-]+-cli)\//;
|
|
630
634
|
const detectConsoleLeftovers = (content, relativePath, ext) => {
|
|
631
635
|
if (!JS_EXTENSIONS$3.has(ext)) return [];
|
|
632
636
|
if (LOGGER_FILE_PATTERN.test(relativePath)) return [];
|
|
633
|
-
if (
|
|
637
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
634
638
|
const diagnostics = [];
|
|
635
639
|
const lines = content.split("\n");
|
|
636
640
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -680,7 +684,7 @@ const asAnyPattern = new RegExp(`\\bas\\s+any\\b`);
|
|
|
680
684
|
const doubleAssertPattern = new RegExp(`\\bas\\s+unknown\\s+as\\s+`);
|
|
681
685
|
const detectUnsafeTypePatterns = (content, relativePath, ext) => {
|
|
682
686
|
if (ext !== ".ts" && ext !== ".tsx") return [];
|
|
683
|
-
if (
|
|
687
|
+
if (isNonProductionPath(relativePath)) return [];
|
|
684
688
|
const diagnostics = [];
|
|
685
689
|
const lines = content.split("\n");
|
|
686
690
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -1585,7 +1589,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
|
|
|
1585
1589
|
|
|
1586
1590
|
//#endregion
|
|
1587
1591
|
//#region src/engines/ai-slop/narrative-comments.ts
|
|
1588
|
-
const NON_PRODUCTION_DIR_PATTERN = /(?:^|\/)(?:scripts|bin|examples?|demos?|bench|benches|benchmarks?|fixtures?|__fixtures__|__mocks__|__tests__|vendor|_vendor|vendored|third_party|blib2to3|lib2to3)\//i;
|
|
1589
1592
|
const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
|
|
1590
1593
|
const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
|
|
1591
1594
|
const getCommentSyntax = (ext) => {
|
|
@@ -1736,13 +1739,21 @@ const looksLikeGoDocComment = (block, ext) => {
|
|
|
1736
1739
|
if (!declMatch) return false;
|
|
1737
1740
|
return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
|
|
1738
1741
|
};
|
|
1739
|
-
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see)
|
|
1742
|
+
const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
|
|
1740
1743
|
const hasDocIndicator = (block) => {
|
|
1741
1744
|
const joined = block.prose.join(" ");
|
|
1742
1745
|
if (DOC_INDICATOR_RE.test(joined)) return true;
|
|
1743
1746
|
for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
|
|
1744
1747
|
return false;
|
|
1745
1748
|
};
|
|
1749
|
+
const hasPreambleSlopSignal = (block) => {
|
|
1750
|
+
const joined = block.prose.join(" ");
|
|
1751
|
+
for (const l of block.prose) {
|
|
1752
|
+
if (EXPLANATORY_OPENERS.test(l)) return true;
|
|
1753
|
+
if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
|
|
1754
|
+
}
|
|
1755
|
+
return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
|
|
1756
|
+
};
|
|
1746
1757
|
const detectNarrativeInBlock = (block, ext) => {
|
|
1747
1758
|
if (looksLikeLicenseHeader(block)) return {
|
|
1748
1759
|
matched: false,
|
|
@@ -1782,9 +1793,13 @@ const detectNarrativeInBlock = (block, ext) => {
|
|
|
1782
1793
|
matched: false,
|
|
1783
1794
|
reason: ""
|
|
1784
1795
|
};
|
|
1785
|
-
if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
1796
|
+
if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
|
|
1797
|
+
matched: true,
|
|
1798
|
+
reason: "multi-line preamble before declaration"
|
|
1799
|
+
};
|
|
1800
|
+
if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
|
|
1786
1801
|
matched: true,
|
|
1787
|
-
reason:
|
|
1802
|
+
reason: "JSDoc preamble with slop signal"
|
|
1788
1803
|
};
|
|
1789
1804
|
if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
|
|
1790
1805
|
matched: true,
|
|
@@ -1824,7 +1839,7 @@ const detectNarrativeComments = async (context) => {
|
|
|
1824
1839
|
const syntax = getCommentSyntax(ext);
|
|
1825
1840
|
if (!syntax) continue;
|
|
1826
1841
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
1827
|
-
if (
|
|
1842
|
+
if (isNonProductionPath(relativePath)) continue;
|
|
1828
1843
|
let content;
|
|
1829
1844
|
try {
|
|
1830
1845
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -5056,7 +5071,7 @@ const handleAislopBaseline = (input) => {
|
|
|
5056
5071
|
|
|
5057
5072
|
//#endregion
|
|
5058
5073
|
//#region src/version.ts
|
|
5059
|
-
const APP_VERSION = "0.8.
|
|
5074
|
+
const APP_VERSION = "0.8.2";
|
|
5060
5075
|
|
|
5061
5076
|
//#endregion
|
|
5062
5077
|
//#region src/mcp.ts
|
|
@@ -29,7 +29,7 @@ const getEngineLabel = (engine) => ENGINE_INFO[engine].label;
|
|
|
29
29
|
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/version.ts
|
|
32
|
-
const APP_VERSION = "0.8.
|
|
32
|
+
const APP_VERSION = "0.8.2";
|
|
33
33
|
|
|
34
34
|
//#endregion
|
|
35
35
|
export { ENGINE_INFO as n, getEngineLabel as r, APP_VERSION as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aislop",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "The engineering standards layer and quality gate for AI-written code. Define your standard once. Every agent — Claude Code, Cursor, Codex — is held to it automatically, on every edit and every PR. Catches the slop they leave behind, enforces the rules your team sets. 8+ languages. Deterministic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|