distyll 0.1.0

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.
Files changed (243) hide show
  1. package/CONTRIBUTING.md +159 -0
  2. package/POSTMORTEM.json +60 -0
  3. package/README.md +218 -0
  4. package/SETUP.md +79 -0
  5. package/action.yml +37 -0
  6. package/dist/cache.d.ts +26 -0
  7. package/dist/cache.d.ts.map +1 -0
  8. package/dist/cache.js +115 -0
  9. package/dist/cache.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +153 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/ci.d.ts +7 -0
  15. package/dist/commands/ci.d.ts.map +1 -0
  16. package/dist/commands/ci.js +101 -0
  17. package/dist/commands/ci.js.map +1 -0
  18. package/dist/commands/diff.d.ts +10 -0
  19. package/dist/commands/diff.d.ts.map +1 -0
  20. package/dist/commands/diff.js +95 -0
  21. package/dist/commands/diff.js.map +1 -0
  22. package/dist/commands/fingerprint.d.ts +2 -0
  23. package/dist/commands/fingerprint.d.ts.map +1 -0
  24. package/dist/commands/fingerprint.js +77 -0
  25. package/dist/commands/fingerprint.js.map +1 -0
  26. package/dist/commands/hook.d.ts +3 -0
  27. package/dist/commands/hook.d.ts.map +1 -0
  28. package/dist/commands/hook.js +110 -0
  29. package/dist/commands/hook.js.map +1 -0
  30. package/dist/commands/init.d.ts +2 -0
  31. package/dist/commands/init.d.ts.map +1 -0
  32. package/dist/commands/init.js +75 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/config.d.ts +7 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +100 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/errors.d.ts +30 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/errors.js +133 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/fingerprint/analyzer.d.ts +3 -0
  43. package/dist/fingerprint/analyzer.d.ts.map +1 -0
  44. package/dist/fingerprint/analyzer.js +230 -0
  45. package/dist/fingerprint/analyzer.js.map +1 -0
  46. package/dist/fingerprint/comparator.d.ts +4 -0
  47. package/dist/fingerprint/comparator.d.ts.map +1 -0
  48. package/dist/fingerprint/comparator.js +78 -0
  49. package/dist/fingerprint/comparator.js.map +1 -0
  50. package/dist/fingerprint/profile.d.ts +5 -0
  51. package/dist/fingerprint/profile.d.ts.map +1 -0
  52. package/dist/fingerprint/profile.js +68 -0
  53. package/dist/fingerprint/profile.js.map +1 -0
  54. package/dist/fixes/index.d.ts +12 -0
  55. package/dist/fixes/index.d.ts.map +1 -0
  56. package/dist/fixes/index.js +42 -0
  57. package/dist/fixes/index.js.map +1 -0
  58. package/dist/fixes/single-use-wrapper.d.ts +8 -0
  59. package/dist/fixes/single-use-wrapper.d.ts.map +1 -0
  60. package/dist/fixes/single-use-wrapper.js +54 -0
  61. package/dist/fixes/single-use-wrapper.js.map +1 -0
  62. package/dist/fixes/unnecessary-try-catch.d.ts +8 -0
  63. package/dist/fixes/unnecessary-try-catch.d.ts.map +1 -0
  64. package/dist/fixes/unnecessary-try-catch.js +37 -0
  65. package/dist/fixes/unnecessary-try-catch.js.map +1 -0
  66. package/dist/fixes/unused-imports.d.ts +7 -0
  67. package/dist/fixes/unused-imports.d.ts.map +1 -0
  68. package/dist/fixes/unused-imports.js +41 -0
  69. package/dist/fixes/unused-imports.js.map +1 -0
  70. package/dist/fixes/verbose-comments.d.ts +7 -0
  71. package/dist/fixes/verbose-comments.d.ts.map +1 -0
  72. package/dist/fixes/verbose-comments.js +29 -0
  73. package/dist/fixes/verbose-comments.js.map +1 -0
  74. package/dist/formatter.d.ts +4 -0
  75. package/dist/formatter.d.ts.map +1 -0
  76. package/dist/formatter.js +72 -0
  77. package/dist/formatter.js.map +1 -0
  78. package/dist/git.d.ts +22 -0
  79. package/dist/git.d.ts.map +1 -0
  80. package/dist/git.js +130 -0
  81. package/dist/git.js.map +1 -0
  82. package/dist/index.d.ts +16 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +40 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/languages/index.d.ts +8 -0
  87. package/dist/languages/index.d.ts.map +1 -0
  88. package/dist/languages/index.js +50 -0
  89. package/dist/languages/index.js.map +1 -0
  90. package/dist/languages/javascript.d.ts +6 -0
  91. package/dist/languages/javascript.d.ts.map +1 -0
  92. package/dist/languages/javascript.js +39 -0
  93. package/dist/languages/javascript.js.map +1 -0
  94. package/dist/languages/python.d.ts +6 -0
  95. package/dist/languages/python.d.ts.map +1 -0
  96. package/dist/languages/python.js +50 -0
  97. package/dist/languages/python.js.map +1 -0
  98. package/dist/parser.d.ts +8 -0
  99. package/dist/parser.d.ts.map +1 -0
  100. package/dist/parser.js +55 -0
  101. package/dist/parser.js.map +1 -0
  102. package/dist/reporters/github.d.ts +4 -0
  103. package/dist/reporters/github.d.ts.map +1 -0
  104. package/dist/reporters/github.js +70 -0
  105. package/dist/reporters/github.js.map +1 -0
  106. package/dist/reporters/terminal.d.ts +4 -0
  107. package/dist/reporters/terminal.d.ts.map +1 -0
  108. package/dist/reporters/terminal.js +59 -0
  109. package/dist/reporters/terminal.js.map +1 -0
  110. package/dist/rules/dead-code-paths.d.ts +3 -0
  111. package/dist/rules/dead-code-paths.d.ts.map +1 -0
  112. package/dist/rules/dead-code-paths.js +57 -0
  113. package/dist/rules/dead-code-paths.js.map +1 -0
  114. package/dist/rules/excessive-comments.d.ts +3 -0
  115. package/dist/rules/excessive-comments.d.ts.map +1 -0
  116. package/dist/rules/excessive-comments.js +86 -0
  117. package/dist/rules/excessive-comments.js.map +1 -0
  118. package/dist/rules/hallucinated-imports.d.ts +3 -0
  119. package/dist/rules/hallucinated-imports.d.ts.map +1 -0
  120. package/dist/rules/hallucinated-imports.js +228 -0
  121. package/dist/rules/hallucinated-imports.js.map +1 -0
  122. package/dist/rules/index.d.ts +4 -0
  123. package/dist/rules/index.d.ts.map +1 -0
  124. package/dist/rules/index.js +34 -0
  125. package/dist/rules/index.js.map +1 -0
  126. package/dist/rules/magic-values.d.ts +3 -0
  127. package/dist/rules/magic-values.d.ts.map +1 -0
  128. package/dist/rules/magic-values.js +168 -0
  129. package/dist/rules/magic-values.js.map +1 -0
  130. package/dist/rules/near-duplicate-functions.d.ts +3 -0
  131. package/dist/rules/near-duplicate-functions.d.ts.map +1 -0
  132. package/dist/rules/near-duplicate-functions.js +78 -0
  133. package/dist/rules/near-duplicate-functions.js.map +1 -0
  134. package/dist/rules/over-defensive-nulls.d.ts +3 -0
  135. package/dist/rules/over-defensive-nulls.d.ts.map +1 -0
  136. package/dist/rules/over-defensive-nulls.js +129 -0
  137. package/dist/rules/over-defensive-nulls.js.map +1 -0
  138. package/dist/rules/redundant-else-return.d.ts +3 -0
  139. package/dist/rules/redundant-else-return.d.ts.map +1 -0
  140. package/dist/rules/redundant-else-return.js +57 -0
  141. package/dist/rules/redundant-else-return.js.map +1 -0
  142. package/dist/rules/single-option-object.d.ts +3 -0
  143. package/dist/rules/single-option-object.d.ts.map +1 -0
  144. package/dist/rules/single-option-object.js +88 -0
  145. package/dist/rules/single-option-object.js.map +1 -0
  146. package/dist/rules/single-use-wrapper.d.ts +3 -0
  147. package/dist/rules/single-use-wrapper.d.ts.map +1 -0
  148. package/dist/rules/single-use-wrapper.js +172 -0
  149. package/dist/rules/single-use-wrapper.js.map +1 -0
  150. package/dist/rules/unnecessary-try-catch.d.ts +3 -0
  151. package/dist/rules/unnecessary-try-catch.d.ts.map +1 -0
  152. package/dist/rules/unnecessary-try-catch.js +116 -0
  153. package/dist/rules/unnecessary-try-catch.js.map +1 -0
  154. package/dist/rules/unused-imports.d.ts +3 -0
  155. package/dist/rules/unused-imports.d.ts.map +1 -0
  156. package/dist/rules/unused-imports.js +103 -0
  157. package/dist/rules/unused-imports.js.map +1 -0
  158. package/dist/rules/verbose-comments.d.ts +3 -0
  159. package/dist/rules/verbose-comments.d.ts.map +1 -0
  160. package/dist/rules/verbose-comments.js +100 -0
  161. package/dist/rules/verbose-comments.js.map +1 -0
  162. package/dist/scanner.d.ts +11 -0
  163. package/dist/scanner.d.ts.map +1 -0
  164. package/dist/scanner.js +196 -0
  165. package/dist/scanner.js.map +1 -0
  166. package/dist/scorer.d.ts +3 -0
  167. package/dist/scorer.d.ts.map +1 -0
  168. package/dist/scorer.js +23 -0
  169. package/dist/scorer.js.map +1 -0
  170. package/dist/types.d.ts +62 -0
  171. package/dist/types.d.ts.map +1 -0
  172. package/dist/types.js +3 -0
  173. package/dist/types.js.map +1 -0
  174. package/hn_post.md +13 -0
  175. package/marketing/COMPETITIVE_ANALYSIS.md +62 -0
  176. package/marketing/EMAIL_ANNOUNCEMENT.md +91 -0
  177. package/marketing/LANDING_PAGE_COPY.md +123 -0
  178. package/marketing/LAUNCH_POST.md +68 -0
  179. package/marketing/PRODUCT_HUNT.md +39 -0
  180. package/marketing/TWITTER_THREAD.md +70 -0
  181. package/package.json +44 -0
  182. package/producthunt.md +52 -0
  183. package/reddit_post.md +39 -0
  184. package/site/favicon.svg +10 -0
  185. package/site/index.html +281 -0
  186. package/site/script.js +82 -0
  187. package/site/style.css +516 -0
  188. package/src/cache.ts +114 -0
  189. package/src/cli.ts +169 -0
  190. package/src/commands/ci.ts +111 -0
  191. package/src/commands/diff.ts +108 -0
  192. package/src/commands/fingerprint.ts +47 -0
  193. package/src/commands/hook.ts +85 -0
  194. package/src/commands/init.ts +42 -0
  195. package/src/config.ts +75 -0
  196. package/src/errors.ts +105 -0
  197. package/src/fingerprint/analyzer.ts +214 -0
  198. package/src/fingerprint/comparator.ts +93 -0
  199. package/src/fingerprint/profile.ts +32 -0
  200. package/src/fixes/index.ts +58 -0
  201. package/src/fixes/single-use-wrapper.ts +60 -0
  202. package/src/fixes/unnecessary-try-catch.ts +43 -0
  203. package/src/fixes/unused-imports.ts +53 -0
  204. package/src/fixes/verbose-comments.ts +35 -0
  205. package/src/formatter.ts +79 -0
  206. package/src/git.ts +115 -0
  207. package/src/index.ts +15 -0
  208. package/src/languages/index.ts +50 -0
  209. package/src/languages/javascript.ts +36 -0
  210. package/src/languages/python.ts +47 -0
  211. package/src/parser.ts +52 -0
  212. package/src/reporters/github.ts +75 -0
  213. package/src/reporters/terminal.ts +67 -0
  214. package/src/rules/dead-code-paths.ts +62 -0
  215. package/src/rules/excessive-comments.ts +94 -0
  216. package/src/rules/hallucinated-imports.ts +195 -0
  217. package/src/rules/index.ts +32 -0
  218. package/src/rules/magic-values.ts +167 -0
  219. package/src/rules/near-duplicate-functions.ts +89 -0
  220. package/src/rules/over-defensive-nulls.ts +137 -0
  221. package/src/rules/redundant-else-return.ts +61 -0
  222. package/src/rules/single-option-object.ts +97 -0
  223. package/src/rules/single-use-wrapper.ts +184 -0
  224. package/src/rules/unnecessary-try-catch.ts +121 -0
  225. package/src/rules/unused-imports.ts +115 -0
  226. package/src/rules/verbose-comments.ts +105 -0
  227. package/src/scanner.ts +184 -0
  228. package/src/scorer.ts +26 -0
  229. package/src/types.ts +70 -0
  230. package/tests/commands/diff.test.ts +107 -0
  231. package/tests/config.test.ts +69 -0
  232. package/tests/e2e.test.ts +163 -0
  233. package/tests/edge-cases.test.ts +167 -0
  234. package/tests/fingerprint/analyzer.test.ts +131 -0
  235. package/tests/fixes/unnecessary-try-catch.test.ts +62 -0
  236. package/tests/git.test.ts +79 -0
  237. package/tests/rules/hallucinated-imports.test.ts +59 -0
  238. package/tests/rules/near-duplicate-functions.test.ts +90 -0
  239. package/tests/rules/unnecessary-try-catch.test.ts +81 -0
  240. package/tests/scanner.test.ts +88 -0
  241. package/tsconfig.json +20 -0
  242. package/twitter_thread.md +46 -0
  243. package/vitest.config.ts +7 -0
@@ -0,0 +1,8 @@
1
+ import Parser from 'tree-sitter';
2
+ import type { Language } from './types';
3
+ export declare function parse(source: string, language: Language): Parser.Tree;
4
+ export declare function detectLanguage(filePath: string): Language | null;
5
+ export declare function walkTree(node: Parser.SyntaxNode, callback: (node: Parser.SyntaxNode) => void): void;
6
+ export declare function findNodes(root: Parser.SyntaxNode, type: string): Parser.SyntaxNode[];
7
+ export declare function findNodesMulti(root: Parser.SyntaxNode, types: string[]): Parser.SyntaxNode[];
8
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAexC,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,IAAI,CAGrE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEhE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,KAAK,IAAI,GAAG,IAAI,CAKnG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAQpF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAS5F"}
package/dist/parser.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parse = parse;
7
+ exports.detectLanguage = detectLanguage;
8
+ exports.walkTree = walkTree;
9
+ exports.findNodes = findNodes;
10
+ exports.findNodesMulti = findNodesMulti;
11
+ const tree_sitter_1 = __importDefault(require("tree-sitter"));
12
+ const languages_1 = require("./languages");
13
+ const parsers = new Map();
14
+ function getParser(language) {
15
+ let parser = parsers.get(language);
16
+ if (parser)
17
+ return parser;
18
+ parser = new tree_sitter_1.default();
19
+ parser.setLanguage((0, languages_1.getGrammar)(language));
20
+ parsers.set(language, parser);
21
+ return parser;
22
+ }
23
+ function parse(source, language) {
24
+ const parser = getParser(language);
25
+ return parser.parse(source);
26
+ }
27
+ function detectLanguage(filePath) {
28
+ return (0, languages_1.detectLanguageFromExt)(filePath);
29
+ }
30
+ function walkTree(node, callback) {
31
+ callback(node);
32
+ for (const child of node.children) {
33
+ walkTree(child, callback);
34
+ }
35
+ }
36
+ function findNodes(root, type) {
37
+ const results = [];
38
+ walkTree(root, (node) => {
39
+ if (node.type === type) {
40
+ results.push(node);
41
+ }
42
+ });
43
+ return results;
44
+ }
45
+ function findNodesMulti(root, types) {
46
+ const typeSet = new Set(types);
47
+ const results = [];
48
+ walkTree(root, (node) => {
49
+ if (typeSet.has(node.type)) {
50
+ results.push(node);
51
+ }
52
+ });
53
+ return results;
54
+ }
55
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":";;;;;AAgBA,sBAGC;AAED,wCAEC;AAED,4BAKC;AAED,8BAQC;AAED,wCASC;AAnDD,8DAAiC;AAEjC,2CAAgE;AAEhE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE5C,SAAS,SAAS,CAAC,QAAkB;IACnC,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,IAAI,qBAAM,EAAE,CAAC;IACtB,MAAM,CAAC,WAAW,CAAC,IAAA,sBAAU,EAAC,QAAQ,CAAC,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,KAAK,CAAC,MAAc,EAAE,QAAkB;IACtD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,cAAc,CAAC,QAAgB;IAC7C,OAAO,IAAA,iCAAqB,EAAC,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED,SAAgB,QAAQ,CAAC,IAAuB,EAAE,QAA2C;IAC3F,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CAAC,IAAuB,EAAE,IAAY;IAC7D,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACtB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,cAAc,CAAC,IAAuB,EAAE,KAAe;IACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACtB,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ScanSummary } from '../types';
2
+ export declare function formatGitHubAnnotations(summary: ScanSummary): string;
3
+ export declare function formatGitHubSummary(summary: ScanSummary): string;
4
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/reporters/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,UAAU,CAAC;AAQ/D,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAapE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAmDhE"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatGitHubAnnotations = formatGitHubAnnotations;
4
+ exports.formatGitHubSummary = formatGitHubSummary;
5
+ const SEVERITY_MAP = {
6
+ info: 'notice',
7
+ warning: 'warning',
8
+ error: 'error',
9
+ };
10
+ function formatGitHubAnnotations(summary) {
11
+ const lines = [];
12
+ for (const result of summary.results) {
13
+ for (const finding of result.findings) {
14
+ const level = SEVERITY_MAP[finding.severity];
15
+ const file = finding.file;
16
+ const line = finding.line;
17
+ lines.push(`::${level} file=${file},line=${line}::${finding.message} (${finding.rule})`);
18
+ }
19
+ }
20
+ return lines.join('\n');
21
+ }
22
+ function formatGitHubSummary(summary) {
23
+ const lines = [];
24
+ lines.push('## Distyll Slop Report');
25
+ lines.push('');
26
+ const emoji = summary.score <= 20 ? '🟢' : summary.score <= 50 ? '🟡' : '🔴';
27
+ lines.push(`**Slop Score: ${emoji} ${summary.score}/100**`);
28
+ lines.push('');
29
+ const findingCount = summary.totalFindings;
30
+ const fileCount = summary.results.filter((r) => r.findings.length > 0).length;
31
+ lines.push(`Found ${findingCount} issue${findingCount === 1 ? '' : 's'} across ${fileCount} file${fileCount === 1 ? '' : 's'}.`);
32
+ lines.push('');
33
+ // Group findings by severity
34
+ const allFindings = summary.results.flatMap((r) => r.findings);
35
+ const errors = allFindings.filter((f) => f.severity === 'error').length;
36
+ const warnings = allFindings.filter((f) => f.severity === 'warning').length;
37
+ const infos = allFindings.filter((f) => f.severity === 'info').length;
38
+ if (errors > 0 || warnings > 0 || infos > 0) {
39
+ lines.push('| Severity | Count |');
40
+ lines.push('|----------|-------|');
41
+ if (errors > 0)
42
+ lines.push(`| 🔴 Error | ${errors} |`);
43
+ if (warnings > 0)
44
+ lines.push(`| 🟡 Warning | ${warnings} |`);
45
+ if (infos > 0)
46
+ lines.push(`| 🔵 Info | ${infos} |`);
47
+ lines.push('');
48
+ }
49
+ // List top findings (max 20)
50
+ const topFindings = allFindings.slice(0, 20);
51
+ if (topFindings.length > 0) {
52
+ lines.push('<details>');
53
+ lines.push('<summary>Findings</summary>');
54
+ lines.push('');
55
+ lines.push('| File | Line | Rule | Message |');
56
+ lines.push('|------|------|------|---------|');
57
+ for (const f of topFindings) {
58
+ const shortFile = f.file.replace(/.*\//, '');
59
+ lines.push(`| \`${shortFile}\` | ${f.line} | ${f.rule} | ${f.message} |`);
60
+ }
61
+ if (allFindings.length > 20) {
62
+ lines.push('');
63
+ lines.push(`_...and ${allFindings.length - 20} more findings._`);
64
+ }
65
+ lines.push('');
66
+ lines.push('</details>');
67
+ }
68
+ return lines.join('\n');
69
+ }
70
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/reporters/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ScanSummary } from '../types';
2
+ import type { TrendSummary } from '../cache';
3
+ export declare function formatTerminalReport(summary: ScanSummary, trend?: TrendSummary): string;
4
+ //# sourceMappingURL=terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAc7C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CA6BvF"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatTerminalReport = formatTerminalReport;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const SEVERITY_COLORS = {
9
+ info: chalk_1.default.blue,
10
+ warning: chalk_1.default.yellow,
11
+ error: chalk_1.default.red,
12
+ };
13
+ const SEVERITY_ICONS = {
14
+ info: 'i',
15
+ warning: '!',
16
+ error: 'x',
17
+ };
18
+ function formatTerminalReport(summary, trend) {
19
+ const lines = [];
20
+ for (const result of summary.results) {
21
+ if (result.findings.length === 0)
22
+ continue;
23
+ lines.push('');
24
+ lines.push(chalk_1.default.underline(result.file));
25
+ for (const finding of result.findings) {
26
+ const color = SEVERITY_COLORS[finding.severity];
27
+ const icon = SEVERITY_ICONS[finding.severity];
28
+ const location = chalk_1.default.gray(`${finding.file}:${finding.line}:${finding.column}`);
29
+ const rule = chalk_1.default.gray(`(${finding.rule})`);
30
+ lines.push(` ${color(icon)} ${location} ${finding.message} ${rule}`);
31
+ }
32
+ }
33
+ lines.push('');
34
+ lines.push(formatScoreLine(summary.score));
35
+ lines.push(chalk_1.default.gray(`${summary.totalFindings} finding${summary.totalFindings === 1 ? '' : 's'} across ${summary.results.length} file${summary.results.length === 1 ? '' : 's'}`));
36
+ if (trend) {
37
+ lines.push(formatTrendLine(trend));
38
+ }
39
+ return lines.join('\n');
40
+ }
41
+ function formatScoreLine(score) {
42
+ const label = 'Slop Score';
43
+ if (score <= 20)
44
+ return chalk_1.default.green(`${label}: ${score}/100 - Clean`);
45
+ if (score <= 50)
46
+ return chalk_1.default.yellow(`${label}: ${score}/100 - Moderate`);
47
+ return chalk_1.default.red(`${label}: ${score}/100 - High`);
48
+ }
49
+ function formatTrendLine(trend) {
50
+ if (trend.direction === 'first-scan') {
51
+ return chalk_1.default.gray('First scan — no trend data yet.');
52
+ }
53
+ const arrow = trend.direction === 'improving' ? chalk_1.default.green('↓') :
54
+ trend.direction === 'worsening' ? chalk_1.default.red('↑') :
55
+ chalk_1.default.gray('→');
56
+ const delta = trend.delta !== null ? (trend.delta > 0 ? `+${trend.delta}` : `${trend.delta}`) : '';
57
+ return `${arrow} Trend: ${delta} from previous score of ${trend.previous}`;
58
+ }
59
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const deadCodePaths: Rule;
3
+ //# sourceMappingURL=dead-code-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-code-paths.d.ts","sourceRoot":"","sources":["../../src/rules/dead-code-paths.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AAa9C,eAAO,MAAM,aAAa,EAAE,IA8C3B,CAAC"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deadCodePaths = void 0;
4
+ const parser_1 = require("../parser");
5
+ const TERMINATING_STATEMENTS = new Set([
6
+ 'return_statement',
7
+ 'throw_statement',
8
+ 'break_statement',
9
+ 'continue_statement',
10
+ ]);
11
+ function isBlockLike(node) {
12
+ return node.type === 'statement_block' || node.type === 'block';
13
+ }
14
+ exports.deadCodePaths = {
15
+ name: 'dead-code-paths',
16
+ description: 'Flags unreachable code after unconditional return, throw, break, or continue statements',
17
+ severity: 'warning',
18
+ check(tree, source, filePath) {
19
+ const findings = [];
20
+ const flagged = new Set();
21
+ (0, parser_1.walkTree)(tree.rootNode, (node) => {
22
+ if (!isBlockLike(node))
23
+ return;
24
+ const children = node.namedChildren;
25
+ for (let i = 0; i < children.length - 1; i++) {
26
+ const stmt = children[i];
27
+ // Check if this statement is a terminating statement
28
+ if (!TERMINATING_STATEMENTS.has(stmt.type))
29
+ continue;
30
+ // Everything after a terminating statement in the same block is dead code
31
+ for (let j = i + 1; j < children.length; j++) {
32
+ const dead = children[j];
33
+ // Skip comments — they're not executable code
34
+ if (dead.type === 'comment')
35
+ continue;
36
+ const lineKey = dead.startPosition.row;
37
+ if (flagged.has(lineKey))
38
+ continue;
39
+ flagged.add(lineKey);
40
+ findings.push({
41
+ file: filePath,
42
+ line: dead.startPosition.row + 1,
43
+ column: dead.startPosition.column + 1,
44
+ endLine: dead.endPosition.row + 1,
45
+ endColumn: dead.endPosition.column + 1,
46
+ rule: 'dead-code-paths',
47
+ severity: 'warning',
48
+ message: `Unreachable code after ${stmt.type.replace('_statement', '')} on line ${stmt.startPosition.row + 1}`,
49
+ });
50
+ }
51
+ break; // No need to check further in this block
52
+ }
53
+ });
54
+ return findings;
55
+ },
56
+ };
57
+ //# sourceMappingURL=dead-code-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-code-paths.js","sourceRoot":"","sources":["../../src/rules/dead-code-paths.ts"],"names":[],"mappings":";;;AACA,sCAAqC;AAGrC,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,oBAAoB;CACrB,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,IAAuB;IAC1C,OAAO,IAAI,CAAC,IAAI,KAAK,iBAAiB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;AAClE,CAAC;AAEY,QAAA,aAAa,GAAS;IACjC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,yFAAyF;IACtG,QAAQ,EAAE,SAAS;IAEnB,KAAK,CAAC,IAAiB,EAAE,MAAc,EAAE,QAAgB;QACvD,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,IAAA,iBAAQ,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YAC/B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO;YAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAEzB,qDAAqD;gBACrD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAErD,0EAA0E;gBAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACzB,8CAA8C;oBAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;wBAAE,SAAS;oBAEtC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;oBACvC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;wBAAE,SAAS;oBACnC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBAErB,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;wBAChC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;wBACrC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;wBACjC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;wBACtC,IAAI,EAAE,iBAAiB;wBACvB,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,0BAA0B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,EAAE;qBAC/G,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,CAAC,yCAAyC;YAClD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const excessiveComments: Rule;
3
+ //# sourceMappingURL=excessive-comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excessive-comments.d.ts","sourceRoot":"","sources":["../../src/rules/excessive-comments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AAK9C,eAAO,MAAM,iBAAiB,EAAE,IAoD/B,CAAC"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.excessiveComments = void 0;
4
+ const COMMENT_DENSITY_THRESHOLD = 0.5; // >50% of lines have trailing comments
5
+ const MIN_LINES_TO_CHECK = 10; // Don't flag very short files
6
+ exports.excessiveComments = {
7
+ name: 'excessive-comments',
8
+ description: 'Flags files or functions with excessive inline comment density (>50% of lines)',
9
+ severity: 'info',
10
+ check(tree, source, filePath) {
11
+ const findings = [];
12
+ const lines = source.split('\n');
13
+ if (lines.length < MIN_LINES_TO_CHECK)
14
+ return findings;
15
+ // Count lines that have trailing inline comments (not standalone comment lines)
16
+ let codeLines = 0;
17
+ let trailingCommentLines = 0;
18
+ for (const line of lines) {
19
+ const trimmed = line.trim();
20
+ if (trimmed.length === 0)
21
+ continue;
22
+ // Skip standalone comment lines — those are fine
23
+ if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
24
+ continue;
25
+ }
26
+ codeLines++;
27
+ // Check for trailing comments on code lines
28
+ // This is a heuristic — we look for // or # after code content
29
+ // Being careful to not match // inside strings
30
+ if (hasTrailingComment(trimmed)) {
31
+ trailingCommentLines++;
32
+ }
33
+ }
34
+ if (codeLines < MIN_LINES_TO_CHECK)
35
+ return findings;
36
+ const density = trailingCommentLines / codeLines;
37
+ if (density > COMMENT_DENSITY_THRESHOLD) {
38
+ findings.push({
39
+ file: filePath,
40
+ line: 1,
41
+ column: 1,
42
+ endLine: lines.length,
43
+ endColumn: 1,
44
+ rule: 'excessive-comments',
45
+ severity: 'info',
46
+ message: `${Math.round(density * 100)}% of code lines have trailing comments (${trailingCommentLines}/${codeLines}) — high comment density is a common AI code pattern`,
47
+ });
48
+ }
49
+ return findings;
50
+ },
51
+ };
52
+ function hasTrailingComment(line) {
53
+ // Simple heuristic: look for // or # that's not inside a string
54
+ let inSingleQuote = false;
55
+ let inDoubleQuote = false;
56
+ let inTemplate = false;
57
+ for (let i = 0; i < line.length; i++) {
58
+ const ch = line[i];
59
+ const prev = i > 0 ? line[i - 1] : '';
60
+ if (prev === '\\')
61
+ continue;
62
+ if (ch === "'" && !inDoubleQuote && !inTemplate)
63
+ inSingleQuote = !inSingleQuote;
64
+ if (ch === '"' && !inSingleQuote && !inTemplate)
65
+ inDoubleQuote = !inDoubleQuote;
66
+ if (ch === '`' && !inSingleQuote && !inDoubleQuote)
67
+ inTemplate = !inTemplate;
68
+ if (inSingleQuote || inDoubleQuote || inTemplate)
69
+ continue;
70
+ // JS/TS trailing comment
71
+ if (ch === '/' && i + 1 < line.length && line[i + 1] === '/') {
72
+ // Make sure there's actual code before the comment
73
+ const before = line.slice(0, i).trim();
74
+ if (before.length > 0)
75
+ return true;
76
+ }
77
+ // Python trailing comment
78
+ if (ch === '#') {
79
+ const before = line.slice(0, i).trim();
80
+ if (before.length > 0)
81
+ return true;
82
+ }
83
+ }
84
+ return false;
85
+ }
86
+ //# sourceMappingURL=excessive-comments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excessive-comments.js","sourceRoot":"","sources":["../../src/rules/excessive-comments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const hallucinatedImports: Rule;
3
+ //# sourceMappingURL=hallucinated-imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hallucinated-imports.d.ts","sourceRoot":"","sources":["../../src/rules/hallucinated-imports.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAW,IAAI,EAAY,MAAM,UAAU,CAAC;AAqIxD,eAAO,MAAM,mBAAmB,EAAE,IAgDjC,CAAC"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.hallucinatedImports = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const parser_1 = require("../parser");
40
+ const languages_1 = require("../languages");
41
+ function getJSImportSources(tree) {
42
+ const imports = [];
43
+ const importStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_statement');
44
+ for (const stmt of importStmts) {
45
+ (0, parser_1.walkTree)(stmt, (node) => {
46
+ if (node.type === 'string' && node.parent?.type === 'import_statement') {
47
+ const source = node.text.replace(/['"]/g, '');
48
+ imports.push({ source, node: stmt });
49
+ }
50
+ });
51
+ }
52
+ // Also check require() calls
53
+ const callNodes = (0, parser_1.findNodes)(tree.rootNode, 'call_expression');
54
+ for (const call of callNodes) {
55
+ const fn = call.childForFieldName('function');
56
+ if (fn?.text === 'require' && call.childForFieldName('arguments')) {
57
+ const args = call.childForFieldName('arguments');
58
+ if (args && args.namedChildren.length === 1) {
59
+ const arg = args.namedChildren[0];
60
+ if (arg.type === 'string') {
61
+ const source = arg.text.replace(/['"]/g, '');
62
+ imports.push({ source, node: call });
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return imports;
68
+ }
69
+ function getPythonImportModules(tree) {
70
+ const imports = [];
71
+ // import foo / import foo.bar
72
+ const importStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_statement');
73
+ for (const stmt of importStmts) {
74
+ (0, parser_1.walkTree)(stmt, (node) => {
75
+ if (node.type === 'dotted_name' && node.parent?.type === 'import_statement') {
76
+ // Get just the top-level module name
77
+ const fullName = node.text;
78
+ const topLevel = fullName.split('.')[0];
79
+ imports.push({ source: topLevel, node: stmt });
80
+ }
81
+ });
82
+ }
83
+ // from foo import bar / from foo.bar import baz
84
+ const fromStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_from_statement');
85
+ for (const stmt of fromStmts) {
86
+ (0, parser_1.walkTree)(stmt, (node) => {
87
+ if (node.type === 'dotted_name' && node.parent?.type === 'import_from_statement') {
88
+ // Only get the module name (first dotted_name child, not imported names)
89
+ const isModuleName = !node.previousNamedSibling || node.previousNamedSibling.type !== 'dotted_name';
90
+ if (isModuleName) {
91
+ const topLevel = node.text.split('.')[0];
92
+ imports.push({ source: topLevel, node: stmt });
93
+ }
94
+ }
95
+ // relative imports like `from .foo import bar` are always local — skip
96
+ if (node.type === 'relative_import') {
97
+ // Mark as safe — no push
98
+ }
99
+ });
100
+ }
101
+ return imports;
102
+ }
103
+ function isRelativeImport(source) {
104
+ return source.startsWith('.') || source.startsWith('/');
105
+ }
106
+ function isNodeModuleInstalled(source, filePath) {
107
+ // Check node_modules from the file's directory up
108
+ const pkgName = source.startsWith('@') ? source.split('/').slice(0, 2).join('/') : source.split('/')[0];
109
+ let dir = path.dirname(filePath);
110
+ for (let i = 0; i < 50; i++) {
111
+ const candidate = path.join(dir, 'node_modules', pkgName);
112
+ if (fs.existsSync(candidate))
113
+ return true;
114
+ const parent = path.dirname(dir);
115
+ if (parent === dir)
116
+ break;
117
+ dir = parent;
118
+ }
119
+ return false;
120
+ }
121
+ function resolveJSImport(source, filePath, language) {
122
+ // Relative imports — check if file exists
123
+ if (isRelativeImport(source)) {
124
+ const dir = path.dirname(filePath);
125
+ const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', ''];
126
+ for (const ext of extensions) {
127
+ if (fs.existsSync(path.join(dir, source + ext)))
128
+ return true;
129
+ if (fs.existsSync(path.join(dir, source, 'index' + ext)))
130
+ return true;
131
+ }
132
+ return false;
133
+ }
134
+ const stdlib = (0, languages_1.getStdlibModules)(language);
135
+ if (stdlib.has(source))
136
+ return true;
137
+ if (isNodeModuleInstalled(source, filePath))
138
+ return true;
139
+ return false;
140
+ }
141
+ function resolvePythonImport(source, filePath) {
142
+ const stdlib = (0, languages_1.getStdlibModules)('python');
143
+ if (stdlib.has(source))
144
+ return true;
145
+ // Check if it's a local file/package in the project
146
+ const dir = path.dirname(filePath);
147
+ if (fs.existsSync(path.join(dir, source + '.py')))
148
+ return true;
149
+ if (fs.existsSync(path.join(dir, source)) && fs.existsSync(path.join(dir, source, '__init__.py')))
150
+ return true;
151
+ // Check parent directories for the module (common in Python projects)
152
+ let searchDir = path.dirname(dir);
153
+ for (let i = 0; i < 5; i++) {
154
+ if (fs.existsSync(path.join(searchDir, source + '.py')))
155
+ return true;
156
+ if (fs.existsSync(path.join(searchDir, source, '__init__.py')))
157
+ return true;
158
+ const parent = path.dirname(searchDir);
159
+ if (parent === searchDir)
160
+ break;
161
+ searchDir = parent;
162
+ }
163
+ // Can't definitively say it's hallucinated — might be an installed package
164
+ // Be conservative: only flag if it looks like a plausible hallucination
165
+ return true; // Default to "resolved" to avoid false positives
166
+ }
167
+ exports.hallucinatedImports = {
168
+ name: 'hallucinated-imports',
169
+ description: 'Flags imports of modules that do not exist in the project or known standard libraries',
170
+ severity: 'error',
171
+ check(tree, source, filePath) {
172
+ const findings = [];
173
+ const language = detectLang(filePath);
174
+ if (!language)
175
+ return findings;
176
+ if ((0, languages_1.isJSLike)(language)) {
177
+ const imports = getJSImportSources(tree);
178
+ for (const { source: src, node } of imports) {
179
+ if (!src)
180
+ continue;
181
+ if (!resolveJSImport(src, filePath, language)) {
182
+ findings.push({
183
+ file: filePath,
184
+ line: node.startPosition.row + 1,
185
+ column: node.startPosition.column + 1,
186
+ endLine: node.endPosition.row + 1,
187
+ endColumn: node.endPosition.column + 1,
188
+ rule: 'hallucinated-imports',
189
+ severity: 'error',
190
+ message: `Import '${src}' could not be resolved — module not found in project or node_modules`,
191
+ });
192
+ }
193
+ }
194
+ }
195
+ else if ((0, languages_1.isPython)(language)) {
196
+ const imports = getPythonImportModules(tree);
197
+ for (const { source: src, node } of imports) {
198
+ if (!src)
199
+ continue;
200
+ if (!resolvePythonImport(src, filePath)) {
201
+ findings.push({
202
+ file: filePath,
203
+ line: node.startPosition.row + 1,
204
+ column: node.startPosition.column + 1,
205
+ endLine: node.endPosition.row + 1,
206
+ endColumn: node.endPosition.column + 1,
207
+ rule: 'hallucinated-imports',
208
+ severity: 'error',
209
+ message: `Import '${src}' could not be resolved — module not found in project or standard library`,
210
+ });
211
+ }
212
+ }
213
+ }
214
+ return findings;
215
+ },
216
+ };
217
+ function detectLang(filePath) {
218
+ if (filePath.endsWith('.tsx'))
219
+ return 'tsx';
220
+ if (filePath.endsWith('.ts'))
221
+ return 'typescript';
222
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs'))
223
+ return 'javascript';
224
+ if (filePath.endsWith('.py') || filePath.endsWith('.pyi'))
225
+ return 'python';
226
+ return null;
227
+ }
228
+ //# sourceMappingURL=hallucinated-imports.js.map