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,68 @@
1
+ # Show HN: Distyll — a linter that catches AI-generated code slop in PRs
2
+
3
+ I've been using Claude Code and Cursor daily for the past year. They're genuinely great. But I kept noticing the same patterns creeping into my codebase:
4
+
5
+ - Wrapper functions that exist for no reason (called exactly once, add zero logic)
6
+ - Try-catch blocks around synchronous pure functions that literally cannot throw
7
+ - Comments like `// increment the counter` above `counter++`
8
+ - Options objects with a single property that will never grow
9
+ - Copy-paste functions that are 90% identical with one variable renamed
10
+
11
+ None of these break anything. The code works. But it compounds. After a few months of merging AI-generated PRs without scrutiny, you've got a codebase that's 30% bigger than it needs to be, full of abstractions that abstract nothing.
12
+
13
+ The HN thread about "comfortable drift" nailed it — the threat isn't that AI writes broken code. It's that it writes *bloated* code and we stop noticing.
14
+
15
+ ## So I built Distyll
16
+
17
+ It's a CLI tool that scans your code (or just your git diff) for 12 anti-patterns that are characteristic of AI-generated output. It gives you a "slop score" from 0-100 with specific findings and fix suggestions.
18
+
19
+ ```bash
20
+ npx distyll scan .
21
+ ```
22
+
23
+ It uses tree-sitter for AST parsing (JS/TS and Python), not regex. This was a deliberate choice — I looked at every existing slop detector I could find, and the ones using regex have brutal false positive rates (flagging patterns inside string literals, matching substrings of function names, etc.).
24
+
25
+ ## How it works
26
+
27
+ The core is a rule engine. Each rule gets an AST and returns findings. Examples:
28
+
29
+ - **unnecessary-try-catch**: Finds try-catch wrapping code that can't throw. It checks whether the body contains async ops, throw statements, or calls to functions that could fail. If not, the try-catch is just noise.
30
+ - **single-use-wrapper**: Finds functions called exactly once that just forward to another function. The kind of abstraction AI loves to create "for clarity" that actually obscures.
31
+ - **hallucinated-imports**: Checks if imported modules actually exist in your project, node_modules, or the language's stdlib. AI models sometimes confidently import things that don't exist.
32
+ - **near-duplicate-functions**: Uses string similarity to find function bodies that are >85% identical. Classic AI pattern: asked to add a similar feature, it copy-pastes and tweaks instead of refactoring.
33
+
34
+ Every rule is conservative by design. I'd rather miss some slop than cry wolf on legitimate code. That's the lesson from every prior tool in this space.
35
+
36
+ ## Git integration
37
+
38
+ The part I actually use most: `distyll diff --staged` in a pre-commit hook. It only scans changed lines, so it's fast and doesn't yell at you about legacy code.
39
+
40
+ ```bash
41
+ distyll hook install --threshold 70
42
+ ```
43
+
44
+ There's also a `distyll ci` command that outputs GitHub Actions annotations, so findings show up inline on PRs.
45
+
46
+ ## Style fingerprinting
47
+
48
+ `distyll fingerprint` analyzes your existing codebase and builds a statistical profile — median function length, naming conventions, comment density, nesting depth. Then `distyll scan --style` flags new code that deviates from YOUR patterns, not some generic ruleset.
49
+
50
+ ## What it's not
51
+
52
+ It's not a replacement for ESLint or code review. ESLint checks syntax and formatting. Code review catches logic bugs and design issues. Distyll catches the structural bloat in between — the stuff that's technically fine but makes your codebase worse over time.
53
+
54
+ It's also not using AI to detect AI code (tempting, but that would be slow and expensive for a git hook). It's heuristic + AST analysis, runs in under 10 seconds on typical PRs, and works completely offline.
55
+
56
+ ## Limitations
57
+
58
+ - Only supports JS/TS and Python right now. Go and Rust are next.
59
+ - The style fingerprinting is statistical, not semantic — it'll catch obvious deviations but won't understand *why* your team uses certain patterns.
60
+ - Some rules work better than others. `unnecessary-try-catch` and `single-use-wrapper` are very accurate. `magic-values` still has some false positives on well-known domain constants.
61
+ - It's v0.1.0. I've been using it on my own projects but it hasn't been battle-tested on large codebases.
62
+
63
+ ## Links
64
+
65
+ - Install: `npm install -g distyll`
66
+ - MIT licensed
67
+
68
+ Would love feedback, especially on false positive rates. If Distyll flags something in your codebase that's actually fine, I want to know about it.
@@ -0,0 +1,39 @@
1
+ # Product Hunt Listing
2
+
3
+ ## Tagline
4
+ Catch AI-generated code slop before it ships
5
+
6
+ ## Short Description (< 260 chars)
7
+ A CLI tool that detects AI-generated code anti-patterns in your PRs. 12 AST-based detection rules, a 0-100 slop score, fix suggestions, git hooks, and CI integration. Works offline on JS/TS and Python.
8
+
9
+ ## Full Description
10
+ AI coding tools generate code that works — but also code that's bloated. Unnecessary wrapper functions, try-catch blocks around code that can't throw, comments restating the obvious, copy-paste with subtle variations.
11
+
12
+ Distyll catches these patterns automatically using tree-sitter AST analysis (not regex, which causes false positives). It assigns a slop score from 0-100 and tells you exactly what to fix.
13
+
14
+ **How developers use it:**
15
+ 1. `npx distyll scan .` to check a project
16
+ 2. `distyll diff --staged` as a pre-commit hook to catch slop before it's committed
17
+ 3. `distyll ci` in GitHub Actions to block merges above a slop threshold
18
+
19
+ It also learns your team's code style via `distyll fingerprint` and flags AI-generated code that deviates from your actual patterns — not generic rules.
20
+
21
+ Every detection rule is conservative by design. We'd rather miss some slop than false-flag your code.
22
+
23
+ ## Key Features
24
+ - **12 detection rules** targeting AI-specific anti-patterns: unnecessary try-catch, single-use wrappers, verbose comments, hallucinated imports, near-duplicate functions, and more
25
+ - **Slop score (0-100)** with weighted severity — scan whole projects or just your git diff
26
+ - **Fix suggestions** for every finding — shows you the minimal change to remove the bloat
27
+ - **Style fingerprinting** — learns your codebase's patterns and flags deviations
28
+ - **Git hooks + CI** — pre-commit blocking and GitHub Actions with inline PR annotations
29
+
30
+ ## Maker Comment
31
+ I built Distyll because I kept merging AI-generated PRs that "worked" but made the codebase worse over time. Wrapper functions that wrap nothing. Try-catch around pure functions. Comments that just restate the code.
32
+
33
+ The existing slop detectors I found all had the same problem: brutal false positive rates from regex-based detection. So I built one using tree-sitter AST parsing, with every rule tuned to be conservative.
34
+
35
+ I've been running it as a pre-commit hook on my own projects for the past few weeks. The diff-only mode (`distyll diff --staged`) is what I use most — it checks only your changes, doesn't nag about legacy code, and runs in seconds.
36
+
37
+ Currently supports JavaScript, TypeScript, and Python. Go and Rust support is planned. It's MIT licensed and works completely offline.
38
+
39
+ Would love your feedback, especially on false positive rates. If Distyll flags something in your code that's actually fine, that's the most useful thing you can tell me.
@@ -0,0 +1,70 @@
1
+ # Twitter/X Launch Thread
2
+
3
+ ## Tweet 1 (Hook)
4
+ I ran a slop detector on 6 months of AI-assisted PRs in my codebase.
5
+
6
+ 30% of the code was unnecessary wrappers, dead try-catch blocks, and comments that just restate the line below them.
7
+
8
+ So I built a tool to catch it automatically. Thread 🧵
9
+
10
+ ## Tweet 2 (Problem)
11
+ AI coding tools are great at generating code that works.
12
+
13
+ They're also great at generating:
14
+ - Functions called exactly once that wrap a single call
15
+ - Try-catch around code that can't throw
16
+ - `// increment counter` above `counter++`
17
+ - Options objects with one field
18
+
19
+ It compounds fast.
20
+
21
+ ## Tweet 3 (Solution)
22
+ Distyll scans your code (or just your git diff) for 12 AI-generated anti-patterns using tree-sitter AST analysis.
23
+
24
+ You get a slop score (0-100) with specific findings and fix suggestions.
25
+
26
+ ```
27
+ npx distyll scan .
28
+ ```
29
+
30
+ Works on JS/TS and Python. Runs in <10 seconds. Fully offline.
31
+
32
+ ## Tweet 4 (How it works)
33
+ The key insight: AI slop has predictable patterns that AST analysis can catch.
34
+
35
+ - Wrapper functions called once → inline them
36
+ - Try-catch on pure sync code → remove it
37
+ - Imports that don't exist → AI hallucinated them
38
+ - 85%+ similar function bodies → refactor the duplication
39
+
40
+ No regex. No AI-to-detect-AI. Just structural analysis.
41
+
42
+ ## Tweet 5 (Git integration)
43
+ The killer feature: `distyll diff --staged` as a pre-commit hook.
44
+
45
+ It only checks YOUR changes. Doesn't yell about legacy code. Blocks commits above a configurable slop threshold.
46
+
47
+ ```
48
+ distyll hook install --threshold 70
49
+ ```
50
+
51
+ Also works as a GitHub Actions CI check with inline PR annotations.
52
+
53
+ ## Tweet 6 (Style fingerprinting)
54
+ Distyll can also learn YOUR codebase's patterns.
55
+
56
+ `distyll fingerprint` builds a style profile — function length, naming conventions, comment density.
57
+
58
+ Then `--style` mode flags AI-generated code that "works" but doesn't match how YOUR team writes code.
59
+
60
+ ## Tweet 7 (CTA)
61
+ Every rule is conservative by design. I'd rather miss slop than false-flag your code.
62
+
63
+ MIT licensed. Try it:
64
+
65
+ ```
66
+ npm install -g distyll
67
+ distyll scan .
68
+ ```
69
+
70
+ If it flags something that's actually fine in your codebase, tell me. That's the feedback I need most.
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "distyll",
3
+ "version": "0.1.0",
4
+ "description": "Catch AI-generated code slop before it ships — a quality gate for the vibe-coding era",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "distyll": "dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "dev": "tsc --watch"
14
+ },
15
+ "keywords": [
16
+ "code-quality",
17
+ "ai",
18
+ "slop",
19
+ "linter",
20
+ "ast",
21
+ "tree-sitter"
22
+ ],
23
+ "license": "MIT",
24
+ "devDependencies": {
25
+ "@types/node": "^22.0.0",
26
+ "typescript": "^5.7.0",
27
+ "vitest": "^3.1.1"
28
+ },
29
+ "dependencies": {
30
+ "@types/string-similarity": "^4.0.2",
31
+ "chalk": "^4.1.2",
32
+ "commander": "^13.1.0",
33
+ "fast-glob": "^3.3.3",
34
+ "simple-git": "^3.33.0",
35
+ "string-similarity": "^4.0.4",
36
+ "tree-sitter": "^0.21.1",
37
+ "tree-sitter-javascript": "^0.23.1",
38
+ "tree-sitter-python": "^0.21.0",
39
+ "tree-sitter-typescript": "^0.23.2"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ }
44
+ }
package/producthunt.md ADDED
@@ -0,0 +1,52 @@
1
+ # Product Hunt Launch
2
+
3
+ ## Tagline
4
+ Catch AI-generated code slop before it ships
5
+
6
+ ## Short Description
7
+ Distyll is a CLI that detects the structural anti-patterns AI coding tools leave behind — unnecessary abstractions, hallucinated imports, verbose comments, defensive code for impossible scenarios. It uses AST analysis to assign a slop score (0-100) with fix suggestions. Run it as a git hook, in CI, or on demand.
8
+
9
+ ## Full Description
10
+
11
+ AI coding tools like Copilot, Claude Code, and Cursor have changed how we write code. But they produce predictable anti-patterns that existing linters completely miss.
12
+
13
+ **The problem:** Your codebase is quietly accumulating "slop" — AI-generated code that works but is bloated, over-abstracted, and inconsistent with your team's style. Code review catches some of it, but the patterns are subtle enough that they slip through daily.
14
+
15
+ **The solution:** Distyll scans your JavaScript, TypeScript, and Python code using tree-sitter AST analysis (no LLM required) and detects 12 AI-specific anti-patterns:
16
+
17
+ - Unnecessary try-catch around synchronous functions
18
+ - Single-use wrapper abstractions that add indirection without value
19
+ - Verbose comments that just restate the code
20
+ - Hallucinated or unused imports
21
+ - Near-duplicate functions with >85% similarity
22
+ - Over-engineered options objects with single properties
23
+ - Dead code paths and redundant null checks
24
+
25
+ Each finding includes a severity level, explanation, and suggested fix. Your project gets a slop score from 0-100.
26
+
27
+ Run it however fits your workflow: scan a directory, analyze only your git diff, install as a pre-commit hook, or add to GitHub Actions CI. The fingerprint command learns your codebase's existing patterns and flags deviations.
28
+
29
+ Configure everything via `.distyll.json` — override rule severity, set merge-blocking thresholds, define ignore patterns.
30
+
31
+ ## Key Features
32
+
33
+ - **Slop Score (0-100):** Instant quality signal on any codebase or PR, based on 12 detection rules weighted by severity
34
+ - **AST-Powered Detection:** Uses tree-sitter for fast, accurate structural analysis — no LLM needed, runs in seconds
35
+ - **Git Integration:** Pre-commit hooks, diff-only scanning, and GitHub Actions CI with inline annotations
36
+ - **Style Fingerprinting:** Learns your team's actual coding patterns and flags AI code that doesn't match your conventions
37
+ - **Actionable Fix Suggestions:** Every finding includes a specific fix — not just "this is bad" but "replace with this"
38
+
39
+ ## Maker Comment
40
+
41
+ I built Distyll after noticing a pattern in my own work: I was approving AI-generated PRs faster than I could properly review them. The code worked, tests passed, but my codebase was slowly filling with unnecessary abstractions, defensive code for impossible scenarios, and subtle copy-paste variations.
42
+
43
+ The moment that pushed me to build this was finding a 3-layer function abstraction that was only called once — generated by Claude Code, approved by me in review, and adding nothing but indirection. I realized if I couldn't catch these patterns manually, I needed a tool.
44
+
45
+ Distyll uses tree-sitter AST analysis to detect the structural anti-patterns that are unique to AI-generated code. It's fast, configurable, and designed to fit into your existing workflow — whether that's a pre-commit hook or a CI check.
46
+
47
+ I'd love your feedback, especially if you're on a team that's heavily using AI coding tools. What patterns are you seeing in your AI-generated code?
48
+
49
+ ## Links
50
+
51
+ - Website: [DEPLOY_URL]
52
+ - GitHub: [GITHUB_URL]
package/reddit_post.md ADDED
@@ -0,0 +1,39 @@
1
+ # r/programming
2
+
3
+ **Title:** I built a linter that catches AI-generated code slop — the anti-patterns Copilot and Claude Code leave behind
4
+
5
+ **Body:**
6
+
7
+ After watching the "Can We Measure Software Slop?" discussion on Lobsters and seeing r/programming ban LLM content entirely, I realized: we have linters for syntax, formatters for style, but nothing that catches the *structural* bloat that AI coding tools produce.
8
+
9
+ So I built **Distyll** — a CLI that scans your code (JS/TS/Python) for 12 AI-generated anti-patterns and assigns a slop score from 0-100.
10
+
11
+ **What it catches:**
12
+
13
+ - **Unnecessary try-catch** wrapping synchronous pure functions (AI loves defensive coding for impossible scenarios)
14
+ - **Single-use wrapper functions** — that 3-layer abstraction for something called once
15
+ - **Verbose comments** that just restate the code (`// increment counter` above `counter++`)
16
+ - **Hallucinated imports** — modules that are imported but never used
17
+ - **Over-engineered options objects** with a single property ever passed
18
+ - **Near-duplicate functions** with >85% similarity (copy-paste with subtle variations)
19
+ - **Redundant null checks** on values that can't be null
20
+ - **Dead code paths** that are never reachable
21
+
22
+ **How it works:**
23
+
24
+ It uses tree-sitter for AST parsing — no LLM needed for the core analysis. It's fast (under 10 seconds on typical PRs). You can run it as:
25
+
26
+ - `distyll scan .` — scan your project
27
+ - `distyll diff` — scan only changed lines (git diff)
28
+ - `distyll hook install` — pre-commit git hook
29
+ - `distyll ci` — GitHub Actions integration with annotations
30
+
31
+ There's also a `fingerprint` command that builds a style profile of your existing codebase, so it flags AI code that doesn't match *your* conventions — not generic rules.
32
+
33
+ Configurable via `.distyll.json` — override severity per rule, set your slop threshold, ignore patterns.
34
+
35
+ I ran it on a few of my own repos that heavily used Cursor and Claude Code. The results were... humbling. Found dozens of pointless try-catch wrappers and single-use abstractions I'd rubber-stamped in code review.
36
+
37
+ Would love feedback from anyone else drowning in AI-generated PRs.
38
+
39
+ [GITHUB_URL]
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#6366f1"/>
5
+ <stop offset="100%" stop-color="#a78bfa"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="32" height="32" rx="6" fill="#0f172a"/>
9
+ <text x="16" y="23" text-anchor="middle" font-family="monospace" font-weight="bold" font-size="20" fill="url(#g)">D</text>
10
+ </svg>
@@ -0,0 +1,281 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Distyll — Catch AI-generated code slop before it ships</title>
7
+ <meta name="description" content="A quality gate for the vibe-coding era. Detect AI code anti-patterns, get a slop score, and fix bloat automatically in your PRs.">
8
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
9
+ <link rel="stylesheet" href="style.css">
10
+ </head>
11
+ <body>
12
+
13
+ <!-- NAV -->
14
+ <nav>
15
+ <div class="container">
16
+ <div class="logo">distyll<span>.</span></div>
17
+ <ul>
18
+ <li><a href="#features">Features</a></li>
19
+ <li><a href="#how-it-works">How It Works</a></li>
20
+ <li><a href="#compare">Compare</a></li>
21
+ <li><a href="#pricing">Pricing</a></li>
22
+ <li><a href="https://github.com/distyll-dev/distyll">GitHub</a></li>
23
+ </ul>
24
+ </div>
25
+ </nav>
26
+
27
+ <!-- HERO -->
28
+ <section class="hero">
29
+ <div class="container">
30
+ <h1>Catch AI-generated <span class="highlight">code slop</span> before it ships</h1>
31
+ <p class="subtitle">A quality gate for the vibe-coding era. Detect bloat, score your PRs, and fix anti-patterns — in seconds.</p>
32
+
33
+ <div class="cta-row">
34
+ <a href="#how-it-works" class="btn btn-primary">Get Started</a>
35
+ <a href="#features" class="btn btn-outline">See Features</a>
36
+ </div>
37
+
38
+ <div class="install-cmd" data-cmd="npm install -g distyll" role="button" tabindex="0" aria-label="Copy install command">
39
+ <code>npm install -g distyll</code>
40
+ <span class="copy-icon">Copy</span>
41
+ </div>
42
+
43
+ <!-- TERMINAL DEMO -->
44
+ <div class="terminal-wrapper">
45
+ <div class="terminal">
46
+ <div class="terminal-bar">
47
+ <div class="terminal-dot red"></div>
48
+ <div class="terminal-dot yellow"></div>
49
+ <div class="terminal-dot green"></div>
50
+ <div class="terminal-title">terminal</div>
51
+ </div>
52
+ <div class="terminal-body" id="terminal-output"></div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </section>
57
+
58
+ <!-- FEATURES -->
59
+ <section id="features">
60
+ <div class="container">
61
+ <h2>Built for the AI Coding Era</h2>
62
+ <p class="section-sub">12+ detection rules purpose-built for the patterns AI tools produce — not generic lint rules.</p>
63
+
64
+ <div class="features-grid">
65
+ <div class="feature-card">
66
+ <div class="feature-icon">&#x1F50D;</div>
67
+ <h3>12+ Slop Detection Rules</h3>
68
+ <p>Catches unnecessary try-catch, single-use wrappers, verbose comments, hallucinated imports, dead code, and more — the patterns AI tools specifically produce.</p>
69
+ </div>
70
+ <div class="feature-card">
71
+ <div class="feature-icon">&#x1F3AF;</div>
72
+ <h3>Slop Score (0–100)</h3>
73
+ <p>Every scan produces a single score so you can track quality over time. Set thresholds to block merges when slop creeps above your standard.</p>
74
+ </div>
75
+ <div class="feature-card">
76
+ <div class="feature-icon">&#x1F310;</div>
77
+ <h3>Multi-Language</h3>
78
+ <p>JavaScript, TypeScript, and Python supported via tree-sitter AST parsing. No regex guessing — accurate, structural analysis.</p>
79
+ </div>
80
+ <div class="feature-card">
81
+ <div class="feature-icon">&#x1F517;</div>
82
+ <h3>Git Hooks &amp; CI</h3>
83
+ <p>Install a pre-commit hook in one command or add to GitHub Actions. Catch slop before it reaches code review.</p>
84
+ </div>
85
+ <div class="feature-card">
86
+ <div class="feature-icon">&#x1F9EC;</div>
87
+ <h3>Style Fingerprinting</h3>
88
+ <p>Learns your team's actual patterns — naming, abstraction depth, comment density — and flags AI code that doesn't match YOUR style.</p>
89
+ </div>
90
+ <div class="feature-card">
91
+ <div class="feature-icon">&#x1F527;</div>
92
+ <h3>Fix Suggestions</h3>
93
+ <p>Each finding includes a concrete fix. Inline the wrapper, remove the dead catch, delete the restated comment — apply with one command.</p>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </section>
98
+
99
+ <!-- HOW IT WORKS -->
100
+ <section id="how-it-works">
101
+ <div class="container">
102
+ <h2>Three Steps to Cleaner Code</h2>
103
+ <p class="section-sub">From install to your first scan in under a minute. No config required.</p>
104
+
105
+ <div class="steps">
106
+ <div class="step">
107
+ <div class="step-number">1</div>
108
+ <h3>Install</h3>
109
+ <p>Add Distyll to your project — globally or as a dev dependency.</p>
110
+ <code>npx distyll init</code>
111
+ </div>
112
+ <div class="step">
113
+ <div class="step-number">2</div>
114
+ <h3>Scan</h3>
115
+ <p>Run on your codebase, a directory, or just the changed files in a PR.</p>
116
+ <code>distyll scan src/</code>
117
+ </div>
118
+ <div class="step">
119
+ <div class="step-number">3</div>
120
+ <h3>Fix or Block</h3>
121
+ <p>Apply suggested fixes or set a threshold to block merges above your slop limit.</p>
122
+ <code>distyll scan --fix</code>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </section>
127
+
128
+ <!-- COMPARISON -->
129
+ <section id="compare">
130
+ <div class="container">
131
+ <h2>How Distyll Compares</h2>
132
+ <p class="section-sub">Existing tools check syntax and style. Distyll catches the structural bloat AI tools produce.</p>
133
+
134
+ <div class="comparison-wrapper">
135
+ <table class="comparison">
136
+ <thead>
137
+ <tr>
138
+ <th>Feature</th>
139
+ <th class="highlight-col">Distyll</th>
140
+ <th>ESLint</th>
141
+ <th>CodeRabbit</th>
142
+ <th>SonarQube</th>
143
+ </tr>
144
+ </thead>
145
+ <tbody>
146
+ <tr>
147
+ <td>AI slop detection</td>
148
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
149
+ <td><span class="cross">&#10007;</span></td>
150
+ <td><span class="partial">partial</span></td>
151
+ <td><span class="cross">&#10007;</span></td>
152
+ </tr>
153
+ <tr>
154
+ <td>Slop score (0–100)</td>
155
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
156
+ <td><span class="cross">&#10007;</span></td>
157
+ <td><span class="cross">&#10007;</span></td>
158
+ <td><span class="cross">&#10007;</span></td>
159
+ </tr>
160
+ <tr>
161
+ <td>Fix suggestions</td>
162
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
163
+ <td><span class="check">&#10003;</span></td>
164
+ <td><span class="check">&#10003;</span></td>
165
+ <td><span class="partial">partial</span></td>
166
+ </tr>
167
+ <tr>
168
+ <td>Style fingerprinting</td>
169
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
170
+ <td><span class="cross">&#10007;</span></td>
171
+ <td><span class="cross">&#10007;</span></td>
172
+ <td><span class="cross">&#10007;</span></td>
173
+ </tr>
174
+ <tr>
175
+ <td>Git hook integration</td>
176
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
177
+ <td><span class="partial">via husky</span></td>
178
+ <td><span class="cross">&#10007;</span></td>
179
+ <td><span class="cross">&#10007;</span></td>
180
+ </tr>
181
+ <tr>
182
+ <td>CI / GitHub Actions</td>
183
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
184
+ <td><span class="check">&#10003;</span></td>
185
+ <td><span class="check">&#10003;</span></td>
186
+ <td><span class="check">&#10003;</span></td>
187
+ </tr>
188
+ <tr>
189
+ <td>AST-based (no regex)</td>
190
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
191
+ <td><span class="check">&#10003;</span></td>
192
+ <td><span class="cross">&#10007;</span></td>
193
+ <td><span class="check">&#10003;</span></td>
194
+ </tr>
195
+ <tr>
196
+ <td>Free for open source</td>
197
+ <td class="highlight-col"><span class="check">&#10003;</span></td>
198
+ <td><span class="check">&#10003;</span></td>
199
+ <td><span class="check">&#10003;</span></td>
200
+ <td><span class="check">&#10003;</span></td>
201
+ </tr>
202
+ </tbody>
203
+ </table>
204
+ </div>
205
+ </div>
206
+ </section>
207
+
208
+ <!-- PRICING -->
209
+ <section id="pricing">
210
+ <div class="container">
211
+ <h2>Simple Pricing</h2>
212
+ <p class="section-sub">Free for open source. Pay only when your team needs advanced features.</p>
213
+
214
+ <div class="pricing-grid">
215
+ <!-- Free -->
216
+ <div class="price-card">
217
+ <h3>Free</h3>
218
+ <div class="price">$0</div>
219
+ <div class="price-desc">For individuals &amp; open source</div>
220
+ <ul>
221
+ <li>Up to 5 repositories</li>
222
+ <li>All 12+ heuristic rules</li>
223
+ <li>Slop score on every scan</li>
224
+ <li>Git hook integration</li>
225
+ <li>JSON &amp; terminal output</li>
226
+ </ul>
227
+ <a href="#how-it-works" class="btn btn-outline">Get Started</a>
228
+ </div>
229
+
230
+ <!-- Pro -->
231
+ <div class="price-card featured">
232
+ <div class="price-badge">Popular</div>
233
+ <h3>Pro</h3>
234
+ <div class="price">$9 <span>/user/mo</span></div>
235
+ <div class="price-desc">For developers who ship fast</div>
236
+ <ul>
237
+ <li>Unlimited repositories</li>
238
+ <li>Everything in Free</li>
239
+ <li>Style fingerprinting</li>
240
+ <li>AI-powered fix suggestions</li>
241
+ <li>Slop score trend tracking</li>
242
+ <li>Priority support</li>
243
+ </ul>
244
+ <a href="#how-it-works" class="btn btn-primary">Start Free Trial</a>
245
+ </div>
246
+
247
+ <!-- Team -->
248
+ <div class="price-card">
249
+ <h3>Team</h3>
250
+ <div class="price">$19 <span>/user/mo</span></div>
251
+ <div class="price-desc">For engineering teams</div>
252
+ <ul>
253
+ <li>Everything in Pro</li>
254
+ <li>Org-wide dashboards</li>
255
+ <li>Custom rule authoring</li>
256
+ <li>Slack &amp; email alerts</li>
257
+ <li>GitHub Actions integration</li>
258
+ <li>Dedicated support</li>
259
+ </ul>
260
+ <a href="#how-it-works" class="btn btn-outline">Contact Sales</a>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </section>
265
+
266
+ <!-- FOOTER -->
267
+ <footer>
268
+ <div class="container">
269
+ <div class="footer-links">
270
+ <a href="https://github.com/distyll-dev/distyll">GitHub</a>
271
+ <a href="#features">Docs</a>
272
+ <a href="#pricing">Pricing</a>
273
+ <a href="mailto:hello@distyll.dev">Contact</a>
274
+ </div>
275
+ <p>&copy; 2026 Distyll. MIT License. Built for developers who care about code quality.</p>
276
+ </div>
277
+ </footer>
278
+
279
+ <script src="script.js"></script>
280
+ </body>
281
+ </html>