aislop 0.8.1 → 0.8.3

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 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 (NON_PRODUCTION_DIR_PATTERN$2.test(relativePath)) continue;
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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++) {
@@ -1448,6 +1452,27 @@ const collectJsDeps = (rootDir, jsDeps) => {
1448
1452
  collectNestedManifests(rootDir, jsDeps);
1449
1453
  return true;
1450
1454
  };
1455
+ const TS_CONFIG_FILES = ["tsconfig.json", "jsconfig.json"];
1456
+ const buildAliasMatcher = (key) => {
1457
+ const starIdx = key.indexOf("*");
1458
+ if (starIdx === -1) return (spec) => spec === key;
1459
+ const before = key.slice(0, starIdx);
1460
+ const after = key.slice(starIdx + 1);
1461
+ return (spec) => spec.length >= before.length + after.length && spec.startsWith(before) && spec.endsWith(after);
1462
+ };
1463
+ const collectAliasMatchersFromConfig = (configPath, matchers) => {
1464
+ const opts = readJson(configPath)?.compilerOptions;
1465
+ if (!opts || typeof opts !== "object") return;
1466
+ const paths = opts.paths;
1467
+ if (!paths || typeof paths !== "object") return;
1468
+ for (const key of Object.keys(paths)) matchers.push(buildAliasMatcher(key));
1469
+ };
1470
+ const collectTsPathAliases = (rootDir) => {
1471
+ const matchers = [];
1472
+ const dirs = [rootDir, ...expandWorkspaceDirs(rootDir, readWorkspaceGlobs(rootDir, readJson(path.join(rootDir, "package.json"))))];
1473
+ for (const dir of dirs) for (const fname of TS_CONFIG_FILES) collectAliasMatchersFromConfig(path.join(dir, fname), matchers);
1474
+ return matchers;
1475
+ };
1451
1476
  const addPyDep = (pyDeps, name) => {
1452
1477
  const normalized = name.toLowerCase().replace(/_/g, "-");
1453
1478
  pyDeps.add(normalized);
@@ -1605,10 +1630,11 @@ const extractPyImports = (content) => {
1605
1630
  }
1606
1631
  return results;
1607
1632
  };
1608
- const checkJsImport = (spec, manifest) => {
1633
+ const checkJsImport = (spec, manifest, tsAliasMatchers) => {
1609
1634
  if (isJsRelativeOrAbsolute(spec)) return null;
1610
1635
  if (isJsBuiltin(spec)) return null;
1611
1636
  if (isJsVirtualModule(spec)) return null;
1637
+ if (tsAliasMatchers.some((m) => m(spec))) return null;
1612
1638
  const pkg = packageNameFromImport(spec);
1613
1639
  if (manifest.jsDeps.has(pkg)) return null;
1614
1640
  if (pkg.startsWith("@types/")) {
@@ -1629,6 +1655,7 @@ const checkPyImport = (spec, manifest) => {
1629
1655
  const detectHallucinatedImports = async (context) => {
1630
1656
  const manifest = loadManifest(context.rootDirectory);
1631
1657
  if (!manifest.hasJsManifest && !manifest.hasPyManifest) return [];
1658
+ const tsAliasMatchers = manifest.hasJsManifest ? collectTsPathAliases(context.rootDirectory) : [];
1632
1659
  const diagnostics = [];
1633
1660
  const files = getSourceFiles(context);
1634
1661
  for (const filePath of files) {
@@ -1648,7 +1675,7 @@ const detectHallucinatedImports = async (context) => {
1648
1675
  const relPath = path.relative(context.rootDirectory, filePath);
1649
1676
  const imports = isJs ? extractJsImports(content) : extractPyImports(content);
1650
1677
  for (const { spec, line } of imports) {
1651
- const hallucinated = isJs ? checkJsImport(spec, manifest) : checkPyImport(spec, manifest);
1678
+ const hallucinated = isJs ? checkJsImport(spec, manifest, tsAliasMatchers) : checkPyImport(spec, manifest);
1652
1679
  if (!hallucinated) continue;
1653
1680
  const manifestLabel = isJs ? "package.json" : "requirements.txt / pyproject.toml / Pipfile";
1654
1681
  diagnostics.push({
@@ -1784,7 +1811,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
1784
1811
 
1785
1812
  //#endregion
1786
1813
  //#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
1814
  const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
1789
1815
  const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
1790
1816
  const getCommentSyntax = (ext) => {
@@ -1935,13 +1961,21 @@ const looksLikeGoDocComment = (block, ext) => {
1935
1961
  if (!declMatch) return false;
1936
1962
  return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
1937
1963
  };
1938
- const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):/i;
1964
+ const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
1939
1965
  const hasDocIndicator = (block) => {
1940
1966
  const joined = block.prose.join(" ");
1941
1967
  if (DOC_INDICATOR_RE.test(joined)) return true;
1942
1968
  for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
1943
1969
  return false;
1944
1970
  };
1971
+ const hasPreambleSlopSignal = (block) => {
1972
+ const joined = block.prose.join(" ");
1973
+ for (const l of block.prose) {
1974
+ if (EXPLANATORY_OPENERS.test(l)) return true;
1975
+ if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
1976
+ }
1977
+ return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
1978
+ };
1945
1979
  const detectNarrativeInBlock = (block, ext) => {
1946
1980
  if (looksLikeLicenseHeader(block)) return {
1947
1981
  matched: false,
@@ -1981,9 +2015,13 @@ const detectNarrativeInBlock = (block, ext) => {
1981
2015
  matched: false,
1982
2016
  reason: ""
1983
2017
  };
1984
- if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
2018
+ if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
2019
+ matched: true,
2020
+ reason: "multi-line preamble before declaration"
2021
+ };
2022
+ if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
1985
2023
  matched: true,
1986
- reason: block.kind === "jsdoc" ? "JSDoc preamble before declaration" : "multi-line preamble before declaration"
2024
+ reason: "JSDoc preamble with slop signal"
1987
2025
  };
1988
2026
  if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
1989
2027
  matched: true,
@@ -2023,7 +2061,7 @@ const detectNarrativeComments = async (context) => {
2023
2061
  const syntax = getCommentSyntax(ext);
2024
2062
  if (!syntax) continue;
2025
2063
  const relativePath = path.relative(context.rootDirectory, filePath);
2026
- if (NON_PRODUCTION_DIR_PATTERN.test(relativePath)) continue;
2064
+ if (isNonProductionPath(relativePath)) continue;
2027
2065
  let content;
2028
2066
  try {
2029
2067
  content = fs.readFileSync(filePath, "utf-8");
@@ -7556,7 +7594,7 @@ const renderCleanRun = (input, deps = {}) => {
7556
7594
 
7557
7595
  //#endregion
7558
7596
  //#region src/version.ts
7559
- const APP_VERSION = "0.8.1";
7597
+ const APP_VERSION = "0.8.3";
7560
7598
 
7561
7599
  //#endregion
7562
7600
  //#region src/utils/telemetry.ts
@@ -9382,13 +9420,77 @@ const fixDependencyAudit = async (context, onProgress) => {
9382
9420
  }
9383
9421
  onProgress?.("Dependency audit fixes · skipping (pnpm audit unavailable and no package-lock.json for npm fallback)");
9384
9422
  };
9423
+ const SEMVER_PREFIX_RE = /^[~^]?/;
9424
+ const parseSemverMin = (spec) => {
9425
+ const match = spec.replace(SEMVER_PREFIX_RE, "").match(/^(\d+|x|X|\*)(?:\.(\d+|x|X|\*))?(?:\.(\d+|x|X|\*))?/);
9426
+ if (!match) return null;
9427
+ const head = match[1];
9428
+ if (!/^\d+$/.test(head)) return null;
9429
+ const toNum = (part) => {
9430
+ if (!part) return 0;
9431
+ return /^\d+$/.test(part) ? Number(part) : 0;
9432
+ };
9433
+ return [
9434
+ Number(head),
9435
+ toNum(match[2]),
9436
+ toNum(match[3])
9437
+ ];
9438
+ };
9439
+ const isDowngrade = (oldSpec, newSpec) => {
9440
+ const oldV = parseSemverMin(oldSpec);
9441
+ const newV = parseSemverMin(newSpec);
9442
+ if (!oldV || !newV) return false;
9443
+ for (let i = 0; i < 3; i++) {
9444
+ if ((newV[i] ?? 0) < (oldV[i] ?? 0)) return true;
9445
+ if ((newV[i] ?? 0) > (oldV[i] ?? 0)) return false;
9446
+ }
9447
+ return false;
9448
+ };
9449
+ const DEP_BUCKETS = [
9450
+ "dependencies",
9451
+ "devDependencies",
9452
+ "peerDependencies",
9453
+ "optionalDependencies"
9454
+ ];
9455
+ const snapshotPackageVersions = (pkg) => {
9456
+ const map = /* @__PURE__ */ new Map();
9457
+ for (const bucket of DEP_BUCKETS) {
9458
+ const deps = pkg[bucket];
9459
+ if (!deps || typeof deps !== "object") continue;
9460
+ for (const [name, version] of Object.entries(deps)) if (typeof version === "string") map.set(`${bucket}:${name}`, version);
9461
+ }
9462
+ return map;
9463
+ };
9464
+ const revertDowngrades = (rootDir, before) => {
9465
+ const pkgPath = path.join(rootDir, "package.json");
9466
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
9467
+ const reverted = [];
9468
+ for (const bucket of DEP_BUCKETS) {
9469
+ const deps = pkg[bucket];
9470
+ if (!deps) continue;
9471
+ for (const [name, version] of Object.entries(deps)) {
9472
+ const prior = before.get(`${bucket}:${name}`);
9473
+ if (!prior) continue;
9474
+ if (isDowngrade(prior, version)) {
9475
+ deps[name] = prior;
9476
+ reverted.push(`${name} ${version} → ${prior}`);
9477
+ }
9478
+ }
9479
+ }
9480
+ if (reverted.length > 0) fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
9481
+ return reverted;
9482
+ };
9385
9483
  const runNpmAuditFix = async (rootDir, onProgress) => {
9484
+ const pkgPath = path.join(rootDir, "package.json");
9485
+ const before = snapshotPackageVersions(JSON.parse(fs.readFileSync(pkgPath, "utf-8")));
9386
9486
  onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
9387
9487
  const result = await runSubprocess("npm", ["audit", "fix"], {
9388
9488
  cwd: rootDir,
9389
9489
  timeout: INSTALL_TIMEOUT
9390
9490
  });
9391
9491
  if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
9492
+ const reverted = revertDowngrades(rootDir, before);
9493
+ if (reverted.length > 0) onProgress?.(`Dependency audit fixes · reverted ${reverted.length} downgrade(s): ${reverted.join(", ")}`);
9392
9494
  onProgress?.("Dependency audit fixes · running npm install");
9393
9495
  const installResult = await runSubprocess("npm", ["install"], {
9394
9496
  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-B7_FRirI.js";
1
+ import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-BynHxO1X.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 (NON_PRODUCTION_DIR_PATTERN$2.test(relativePath)) continue;
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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++) {
@@ -1990,6 +1994,27 @@ const collectJsDeps = (rootDir, jsDeps) => {
1990
1994
  collectNestedManifests(rootDir, jsDeps);
1991
1995
  return true;
1992
1996
  };
1997
+ const TS_CONFIG_FILES = ["tsconfig.json", "jsconfig.json"];
1998
+ const buildAliasMatcher = (key) => {
1999
+ const starIdx = key.indexOf("*");
2000
+ if (starIdx === -1) return (spec) => spec === key;
2001
+ const before = key.slice(0, starIdx);
2002
+ const after = key.slice(starIdx + 1);
2003
+ return (spec) => spec.length >= before.length + after.length && spec.startsWith(before) && spec.endsWith(after);
2004
+ };
2005
+ const collectAliasMatchersFromConfig = (configPath, matchers) => {
2006
+ const opts = readJson(configPath)?.compilerOptions;
2007
+ if (!opts || typeof opts !== "object") return;
2008
+ const paths = opts.paths;
2009
+ if (!paths || typeof paths !== "object") return;
2010
+ for (const key of Object.keys(paths)) matchers.push(buildAliasMatcher(key));
2011
+ };
2012
+ const collectTsPathAliases = (rootDir) => {
2013
+ const matchers = [];
2014
+ const dirs = [rootDir, ...expandWorkspaceDirs(rootDir, readWorkspaceGlobs(rootDir, readJson(path.join(rootDir, "package.json"))))];
2015
+ for (const dir of dirs) for (const fname of TS_CONFIG_FILES) collectAliasMatchersFromConfig(path.join(dir, fname), matchers);
2016
+ return matchers;
2017
+ };
1993
2018
  const addPyDep = (pyDeps, name) => {
1994
2019
  const normalized = name.toLowerCase().replace(/_/g, "-");
1995
2020
  pyDeps.add(normalized);
@@ -2147,10 +2172,11 @@ const extractPyImports = (content) => {
2147
2172
  }
2148
2173
  return results;
2149
2174
  };
2150
- const checkJsImport = (spec, manifest) => {
2175
+ const checkJsImport = (spec, manifest, tsAliasMatchers) => {
2151
2176
  if (isJsRelativeOrAbsolute(spec)) return null;
2152
2177
  if (isJsBuiltin(spec)) return null;
2153
2178
  if (isJsVirtualModule(spec)) return null;
2179
+ if (tsAliasMatchers.some((m) => m(spec))) return null;
2154
2180
  const pkg = packageNameFromImport(spec);
2155
2181
  if (manifest.jsDeps.has(pkg)) return null;
2156
2182
  if (pkg.startsWith("@types/")) {
@@ -2171,6 +2197,7 @@ const checkPyImport = (spec, manifest) => {
2171
2197
  const detectHallucinatedImports = async (context) => {
2172
2198
  const manifest = loadManifest(context.rootDirectory);
2173
2199
  if (!manifest.hasJsManifest && !manifest.hasPyManifest) return [];
2200
+ const tsAliasMatchers = manifest.hasJsManifest ? collectTsPathAliases(context.rootDirectory) : [];
2174
2201
  const diagnostics = [];
2175
2202
  const files = getSourceFiles(context);
2176
2203
  for (const filePath of files) {
@@ -2190,7 +2217,7 @@ const detectHallucinatedImports = async (context) => {
2190
2217
  const relPath = path.relative(context.rootDirectory, filePath);
2191
2218
  const imports = isJs ? extractJsImports(content) : extractPyImports(content);
2192
2219
  for (const { spec, line } of imports) {
2193
- const hallucinated = isJs ? checkJsImport(spec, manifest) : checkPyImport(spec, manifest);
2220
+ const hallucinated = isJs ? checkJsImport(spec, manifest, tsAliasMatchers) : checkPyImport(spec, manifest);
2194
2221
  if (!hallucinated) continue;
2195
2222
  const manifestLabel = isJs ? "package.json" : "requirements.txt / pyproject.toml / Pipfile";
2196
2223
  diagnostics.push({
@@ -2326,7 +2353,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
2326
2353
 
2327
2354
  //#endregion
2328
2355
  //#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
2356
  const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
2331
2357
  const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
2332
2358
  const getCommentSyntax = (ext) => {
@@ -2477,13 +2503,21 @@ const looksLikeGoDocComment = (block, ext) => {
2477
2503
  if (!declMatch) return false;
2478
2504
  return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
2479
2505
  };
2480
- const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):/i;
2506
+ const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
2481
2507
  const hasDocIndicator = (block) => {
2482
2508
  const joined = block.prose.join(" ");
2483
2509
  if (DOC_INDICATOR_RE.test(joined)) return true;
2484
2510
  for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
2485
2511
  return false;
2486
2512
  };
2513
+ const hasPreambleSlopSignal = (block) => {
2514
+ const joined = block.prose.join(" ");
2515
+ for (const l of block.prose) {
2516
+ if (EXPLANATORY_OPENERS.test(l)) return true;
2517
+ if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
2518
+ }
2519
+ return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
2520
+ };
2487
2521
  const detectNarrativeInBlock = (block, ext) => {
2488
2522
  if (looksLikeLicenseHeader(block)) return {
2489
2523
  matched: false,
@@ -2523,9 +2557,13 @@ const detectNarrativeInBlock = (block, ext) => {
2523
2557
  matched: false,
2524
2558
  reason: ""
2525
2559
  };
2526
- if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
2560
+ if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
2561
+ matched: true,
2562
+ reason: "multi-line preamble before declaration"
2563
+ };
2564
+ if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
2527
2565
  matched: true,
2528
- reason: block.kind === "jsdoc" ? "JSDoc preamble before declaration" : "multi-line preamble before declaration"
2566
+ reason: "JSDoc preamble with slop signal"
2529
2567
  };
2530
2568
  if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
2531
2569
  matched: true,
@@ -2565,7 +2603,7 @@ const detectNarrativeComments = async (context) => {
2565
2603
  const syntax = getCommentSyntax(ext);
2566
2604
  if (!syntax) continue;
2567
2605
  const relativePath = path.relative(context.rootDirectory, filePath);
2568
- if (NON_PRODUCTION_DIR_PATTERN.test(relativePath)) continue;
2606
+ if (isNonProductionPath(relativePath)) continue;
2569
2607
  let content;
2570
2608
  try {
2571
2609
  content = fs.readFileSync(filePath, "utf-8");
@@ -6361,7 +6399,7 @@ const scanCommand = async (directory, config, options) => {
6361
6399
  });
6362
6400
  }
6363
6401
  if (options.json) {
6364
- const { buildJsonOutput } = await import("./json-7EtVVIhd.js");
6402
+ const { buildJsonOutput } = await import("./json-D8h2EZW6.js");
6365
6403
  const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
6366
6404
  console.log(JSON.stringify(jsonOut, null, 2));
6367
6405
  return { exitCode };
@@ -7361,13 +7399,77 @@ const fixDependencyAudit = async (context, onProgress) => {
7361
7399
  }
7362
7400
  onProgress?.("Dependency audit fixes · skipping (pnpm audit unavailable and no package-lock.json for npm fallback)");
7363
7401
  };
7402
+ const SEMVER_PREFIX_RE = /^[~^]?/;
7403
+ const parseSemverMin = (spec) => {
7404
+ const match = spec.replace(SEMVER_PREFIX_RE, "").match(/^(\d+|x|X|\*)(?:\.(\d+|x|X|\*))?(?:\.(\d+|x|X|\*))?/);
7405
+ if (!match) return null;
7406
+ const head = match[1];
7407
+ if (!/^\d+$/.test(head)) return null;
7408
+ const toNum = (part) => {
7409
+ if (!part) return 0;
7410
+ return /^\d+$/.test(part) ? Number(part) : 0;
7411
+ };
7412
+ return [
7413
+ Number(head),
7414
+ toNum(match[2]),
7415
+ toNum(match[3])
7416
+ ];
7417
+ };
7418
+ const isDowngrade = (oldSpec, newSpec) => {
7419
+ const oldV = parseSemverMin(oldSpec);
7420
+ const newV = parseSemverMin(newSpec);
7421
+ if (!oldV || !newV) return false;
7422
+ for (let i = 0; i < 3; i++) {
7423
+ if ((newV[i] ?? 0) < (oldV[i] ?? 0)) return true;
7424
+ if ((newV[i] ?? 0) > (oldV[i] ?? 0)) return false;
7425
+ }
7426
+ return false;
7427
+ };
7428
+ const DEP_BUCKETS = [
7429
+ "dependencies",
7430
+ "devDependencies",
7431
+ "peerDependencies",
7432
+ "optionalDependencies"
7433
+ ];
7434
+ const snapshotPackageVersions = (pkg) => {
7435
+ const map = /* @__PURE__ */ new Map();
7436
+ for (const bucket of DEP_BUCKETS) {
7437
+ const deps = pkg[bucket];
7438
+ if (!deps || typeof deps !== "object") continue;
7439
+ for (const [name, version] of Object.entries(deps)) if (typeof version === "string") map.set(`${bucket}:${name}`, version);
7440
+ }
7441
+ return map;
7442
+ };
7443
+ const revertDowngrades = (rootDir, before) => {
7444
+ const pkgPath = path.join(rootDir, "package.json");
7445
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
7446
+ const reverted = [];
7447
+ for (const bucket of DEP_BUCKETS) {
7448
+ const deps = pkg[bucket];
7449
+ if (!deps) continue;
7450
+ for (const [name, version] of Object.entries(deps)) {
7451
+ const prior = before.get(`${bucket}:${name}`);
7452
+ if (!prior) continue;
7453
+ if (isDowngrade(prior, version)) {
7454
+ deps[name] = prior;
7455
+ reverted.push(`${name} ${version} → ${prior}`);
7456
+ }
7457
+ }
7458
+ }
7459
+ if (reverted.length > 0) fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
7460
+ return reverted;
7461
+ };
7364
7462
  const runNpmAuditFix = async (rootDir, onProgress) => {
7463
+ const pkgPath = path.join(rootDir, "package.json");
7464
+ const before = snapshotPackageVersions(JSON.parse(fs.readFileSync(pkgPath, "utf-8")));
7365
7465
  onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
7366
7466
  const result = await runSubprocess("npm", ["audit", "fix"], {
7367
7467
  cwd: rootDir,
7368
7468
  timeout: INSTALL_TIMEOUT
7369
7469
  });
7370
7470
  if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
7471
+ const reverted = revertDowngrades(rootDir, before);
7472
+ if (reverted.length > 0) onProgress?.(`Dependency audit fixes · reverted ${reverted.length} downgrade(s): ${reverted.join(", ")}`);
7371
7473
  onProgress?.("Dependency audit fixes · running npm install");
7372
7474
  const installResult = await runSubprocess("npm", ["install"], {
7373
7475
  cwd: rootDir,
@@ -1,4 +1,4 @@
1
- import { n as ENGINE_INFO, t as APP_VERSION } from "./version-B7_FRirI.js";
1
+ import { n as ENGINE_INFO, t as APP_VERSION } from "./version-BynHxO1X.js";
2
2
 
3
3
  //#region src/output/json.ts
4
4
  const buildJsonOutput = (results, scoreResult, fileCount, elapsedMs) => {
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 (NON_PRODUCTION_DIR_PATTERN$2.test(relativePath)) continue;
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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 (NON_PRODUCTION_DIR_PATTERN$1.test(relativePath)) return [];
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++) {
@@ -1249,6 +1253,27 @@ const collectJsDeps = (rootDir, jsDeps) => {
1249
1253
  collectNestedManifests(rootDir, jsDeps);
1250
1254
  return true;
1251
1255
  };
1256
+ const TS_CONFIG_FILES = ["tsconfig.json", "jsconfig.json"];
1257
+ const buildAliasMatcher = (key) => {
1258
+ const starIdx = key.indexOf("*");
1259
+ if (starIdx === -1) return (spec) => spec === key;
1260
+ const before = key.slice(0, starIdx);
1261
+ const after = key.slice(starIdx + 1);
1262
+ return (spec) => spec.length >= before.length + after.length && spec.startsWith(before) && spec.endsWith(after);
1263
+ };
1264
+ const collectAliasMatchersFromConfig = (configPath, matchers) => {
1265
+ const opts = readJson(configPath)?.compilerOptions;
1266
+ if (!opts || typeof opts !== "object") return;
1267
+ const paths = opts.paths;
1268
+ if (!paths || typeof paths !== "object") return;
1269
+ for (const key of Object.keys(paths)) matchers.push(buildAliasMatcher(key));
1270
+ };
1271
+ const collectTsPathAliases = (rootDir) => {
1272
+ const matchers = [];
1273
+ const dirs = [rootDir, ...expandWorkspaceDirs(rootDir, readWorkspaceGlobs(rootDir, readJson(path.join(rootDir, "package.json"))))];
1274
+ for (const dir of dirs) for (const fname of TS_CONFIG_FILES) collectAliasMatchersFromConfig(path.join(dir, fname), matchers);
1275
+ return matchers;
1276
+ };
1252
1277
  const addPyDep = (pyDeps, name) => {
1253
1278
  const normalized = name.toLowerCase().replace(/_/g, "-");
1254
1279
  pyDeps.add(normalized);
@@ -1406,10 +1431,11 @@ const extractPyImports = (content) => {
1406
1431
  }
1407
1432
  return results;
1408
1433
  };
1409
- const checkJsImport = (spec, manifest) => {
1434
+ const checkJsImport = (spec, manifest, tsAliasMatchers) => {
1410
1435
  if (isJsRelativeOrAbsolute(spec)) return null;
1411
1436
  if (isJsBuiltin(spec)) return null;
1412
1437
  if (isJsVirtualModule(spec)) return null;
1438
+ if (tsAliasMatchers.some((m) => m(spec))) return null;
1413
1439
  const pkg = packageNameFromImport(spec);
1414
1440
  if (manifest.jsDeps.has(pkg)) return null;
1415
1441
  if (pkg.startsWith("@types/")) {
@@ -1430,6 +1456,7 @@ const checkPyImport = (spec, manifest) => {
1430
1456
  const detectHallucinatedImports = async (context) => {
1431
1457
  const manifest = loadManifest(context.rootDirectory);
1432
1458
  if (!manifest.hasJsManifest && !manifest.hasPyManifest) return [];
1459
+ const tsAliasMatchers = manifest.hasJsManifest ? collectTsPathAliases(context.rootDirectory) : [];
1433
1460
  const diagnostics = [];
1434
1461
  const files = getSourceFiles(context);
1435
1462
  for (const filePath of files) {
@@ -1449,7 +1476,7 @@ const detectHallucinatedImports = async (context) => {
1449
1476
  const relPath = path.relative(context.rootDirectory, filePath);
1450
1477
  const imports = isJs ? extractJsImports(content) : extractPyImports(content);
1451
1478
  for (const { spec, line } of imports) {
1452
- const hallucinated = isJs ? checkJsImport(spec, manifest) : checkPyImport(spec, manifest);
1479
+ const hallucinated = isJs ? checkJsImport(spec, manifest, tsAliasMatchers) : checkPyImport(spec, manifest);
1453
1480
  if (!hallucinated) continue;
1454
1481
  const manifestLabel = isJs ? "package.json" : "requirements.txt / pyproject.toml / Pipfile";
1455
1482
  diagnostics.push({
@@ -1585,7 +1612,6 @@ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|re
1585
1612
 
1586
1613
  //#endregion
1587
1614
  //#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
1615
  const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
1590
1616
  const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
1591
1617
  const getCommentSyntax = (ext) => {
@@ -1736,13 +1762,21 @@ const looksLikeGoDocComment = (block, ext) => {
1736
1762
  if (!declMatch) return false;
1737
1763
  return ((block.prose.find((l) => l.length > 0) ?? "").split(/\s+/)[0] ?? "") === declMatch[1];
1738
1764
  };
1739
- const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):/i;
1765
+ const DOC_INDICATOR_RE = /`[^`]+`|\|\s*[-:]+\s*\||```|\b(?:note|warning|warn|caveat|example|caution|see):|\(e\.g\.[^)]+\)|\(i\.e\.[^)]+\)/i;
1740
1766
  const hasDocIndicator = (block) => {
1741
1767
  const joined = block.prose.join(" ");
1742
1768
  if (DOC_INDICATOR_RE.test(joined)) return true;
1743
1769
  for (const l of block.prose) if (/^[-]\s/.test(l)) return true;
1744
1770
  return false;
1745
1771
  };
1772
+ const hasPreambleSlopSignal = (block) => {
1773
+ const joined = block.prose.join(" ");
1774
+ for (const l of block.prose) {
1775
+ if (EXPLANATORY_OPENERS.test(l)) return true;
1776
+ if (JUSTIFICATION_OPENERS.some((re) => re.test(l))) return true;
1777
+ }
1778
+ return CROSS_REFERENCE_PHRASES.some((re) => re.test(joined));
1779
+ };
1746
1780
  const detectNarrativeInBlock = (block, ext) => {
1747
1781
  if (looksLikeLicenseHeader(block)) return {
1748
1782
  matched: false,
@@ -1782,9 +1816,13 @@ const detectNarrativeInBlock = (block, ext) => {
1782
1816
  matched: false,
1783
1817
  reason: ""
1784
1818
  };
1785
- if (block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
1819
+ if (block.kind === "line" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext)) return {
1820
+ matched: true,
1821
+ reason: "multi-line preamble before declaration"
1822
+ };
1823
+ if (block.kind === "jsdoc" && block.prose.length >= 3 && looksLikeDeclarationPreamble(block.nextNonBlankLine, ext) && hasPreambleSlopSignal(block)) return {
1786
1824
  matched: true,
1787
- reason: block.kind === "jsdoc" ? "JSDoc preamble before declaration" : "multi-line preamble before declaration"
1825
+ reason: "JSDoc preamble with slop signal"
1788
1826
  };
1789
1827
  if (CROSS_REFERENCE_PHRASES.some((re) => re.test(joined))) return {
1790
1828
  matched: true,
@@ -1824,7 +1862,7 @@ const detectNarrativeComments = async (context) => {
1824
1862
  const syntax = getCommentSyntax(ext);
1825
1863
  if (!syntax) continue;
1826
1864
  const relativePath = path.relative(context.rootDirectory, filePath);
1827
- if (NON_PRODUCTION_DIR_PATTERN.test(relativePath)) continue;
1865
+ if (isNonProductionPath(relativePath)) continue;
1828
1866
  let content;
1829
1867
  try {
1830
1868
  content = fs.readFileSync(filePath, "utf-8");
@@ -5056,7 +5094,7 @@ const handleAislopBaseline = (input) => {
5056
5094
 
5057
5095
  //#endregion
5058
5096
  //#region src/version.ts
5059
- const APP_VERSION = "0.8.1";
5097
+ const APP_VERSION = "0.8.3";
5060
5098
 
5061
5099
  //#endregion
5062
5100
  //#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.1";
32
+ const APP_VERSION = "0.8.3";
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.1",
3
+ "version": "0.8.3",
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": {