pi-lens 2.2.9 → 3.0.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 (304) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +709 -519
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/file-time.test.ts +276 -0
  5. package/clients/__tests__/format-service.test.js +245 -0
  6. package/clients/__tests__/format-service.test.ts +339 -0
  7. package/clients/__tests__/formatters.test.js +271 -0
  8. package/clients/__tests__/formatters.test.ts +401 -0
  9. package/clients/amain-types.js +164 -0
  10. package/clients/amain-types.ts +165 -0
  11. package/clients/architect-client.js +56 -12
  12. package/clients/architect-client.ts +81 -16
  13. package/clients/ast-grep-client.js +2 -2
  14. package/clients/ast-grep-client.ts +14 -39
  15. package/clients/ast-grep-parser.ts +1 -1
  16. package/clients/ast-grep-rule-manager.js +8 -0
  17. package/clients/ast-grep-rule-manager.ts +10 -1
  18. package/clients/ast-grep-types.js +9 -0
  19. package/clients/ast-grep-types.ts +106 -0
  20. package/clients/auto-loop.js +10 -0
  21. package/clients/auto-loop.ts +14 -1
  22. package/clients/biome-client.js +81 -19
  23. package/clients/biome-client.ts +103 -22
  24. package/clients/bus/bus.js +191 -0
  25. package/clients/bus/bus.ts +251 -0
  26. package/clients/bus/events.js +214 -0
  27. package/clients/bus/events.ts +279 -0
  28. package/clients/bus/index.js +8 -0
  29. package/clients/bus/index.ts +9 -0
  30. package/clients/bus/integration.js +158 -0
  31. package/clients/bus/integration.ts +214 -0
  32. package/clients/complexity-client.js +13 -7
  33. package/clients/complexity-client.ts +13 -7
  34. package/clients/config-validator.js +465 -0
  35. package/clients/config-validator.ts +558 -0
  36. package/clients/dependency-checker.js +4 -10
  37. package/clients/dependency-checker.ts +4 -10
  38. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  39. package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
  40. package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
  41. package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
  42. package/clients/dispatch/bus-dispatcher.js +177 -0
  43. package/clients/dispatch/bus-dispatcher.ts +251 -0
  44. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  45. package/clients/dispatch/dispatcher.edge.test.ts +100 -0
  46. package/clients/dispatch/dispatcher.format.test.js +46 -0
  47. package/clients/dispatch/dispatcher.format.test.ts +58 -0
  48. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  49. package/clients/dispatch/dispatcher.inline.test.ts +93 -0
  50. package/clients/dispatch/dispatcher.js +19 -53
  51. package/clients/dispatch/dispatcher.ts +20 -67
  52. package/clients/dispatch/plan.js +9 -4
  53. package/clients/dispatch/plan.ts +9 -4
  54. package/clients/dispatch/runners/architect.js +21 -7
  55. package/clients/dispatch/runners/architect.test.js +138 -0
  56. package/clients/dispatch/runners/architect.test.ts +162 -0
  57. package/clients/dispatch/runners/architect.ts +22 -7
  58. package/clients/dispatch/runners/ast-grep-napi.js +462 -0
  59. package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
  60. package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
  61. package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
  62. package/clients/dispatch/runners/ast-grep.js +62 -19
  63. package/clients/dispatch/runners/ast-grep.ts +70 -18
  64. package/clients/dispatch/runners/biome.js +29 -53
  65. package/clients/dispatch/runners/biome.ts +29 -63
  66. package/clients/dispatch/runners/config-validation.js +67 -0
  67. package/clients/dispatch/runners/config-validation.ts +82 -0
  68. package/clients/dispatch/runners/go-vet.js +4 -28
  69. package/clients/dispatch/runners/go-vet.ts +4 -32
  70. package/clients/dispatch/runners/index.js +30 -10
  71. package/clients/dispatch/runners/index.ts +30 -10
  72. package/clients/dispatch/runners/oxlint.js +141 -0
  73. package/clients/dispatch/runners/oxlint.test.js +230 -0
  74. package/clients/dispatch/runners/oxlint.test.ts +303 -0
  75. package/clients/dispatch/runners/oxlint.ts +175 -0
  76. package/clients/dispatch/runners/pyright.js +40 -70
  77. package/clients/dispatch/runners/pyright.test.js +16 -2
  78. package/clients/dispatch/runners/pyright.test.ts +14 -2
  79. package/clients/dispatch/runners/pyright.ts +48 -91
  80. package/clients/dispatch/runners/python-slop.js +97 -0
  81. package/clients/dispatch/runners/python-slop.test.js +203 -0
  82. package/clients/dispatch/runners/python-slop.test.ts +298 -0
  83. package/clients/dispatch/runners/python-slop.ts +124 -0
  84. package/clients/dispatch/runners/ruff.js +18 -71
  85. package/clients/dispatch/runners/ruff.ts +19 -79
  86. package/clients/dispatch/runners/rust-clippy.js +28 -32
  87. package/clients/dispatch/runners/rust-clippy.ts +29 -31
  88. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  89. package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
  90. package/clients/dispatch/runners/shellcheck.js +147 -0
  91. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  92. package/clients/dispatch/runners/shellcheck.test.ts +129 -0
  93. package/clients/dispatch/runners/shellcheck.ts +188 -0
  94. package/clients/dispatch/runners/similarity.js +230 -0
  95. package/clients/dispatch/runners/similarity.ts +339 -0
  96. package/clients/dispatch/runners/spellcheck.js +106 -0
  97. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  98. package/clients/dispatch/runners/spellcheck.test.ts +214 -0
  99. package/clients/dispatch/runners/spellcheck.ts +136 -0
  100. package/clients/dispatch/runners/tree-sitter.js +107 -0
  101. package/clients/dispatch/runners/tree-sitter.ts +135 -0
  102. package/clients/dispatch/runners/ts-lsp.js +104 -33
  103. package/clients/dispatch/runners/ts-lsp.ts +120 -38
  104. package/clients/dispatch/runners/ts-slop.js +113 -0
  105. package/clients/dispatch/runners/ts-slop.test.js +180 -0
  106. package/clients/dispatch/runners/ts-slop.test.ts +230 -0
  107. package/clients/dispatch/runners/ts-slop.ts +142 -0
  108. package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
  109. package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
  110. package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
  111. package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
  112. package/clients/dispatch/runners/utils.js +2 -4
  113. package/clients/dispatch/runners/utils.ts +2 -4
  114. package/clients/dispatch/types.ts +1 -1
  115. package/clients/dispatch/utils/format-utils.js +49 -0
  116. package/clients/dispatch/utils/format-utils.ts +60 -0
  117. package/clients/dogfood.test.js +201 -0
  118. package/clients/dogfood.test.ts +269 -0
  119. package/clients/file-time.js +152 -0
  120. package/clients/file-time.ts +208 -0
  121. package/clients/file-utils.js +40 -0
  122. package/clients/file-utils.ts +44 -0
  123. package/clients/fix-scanners.js +10 -20
  124. package/clients/fix-scanners.ts +10 -22
  125. package/clients/format-service.js +172 -0
  126. package/clients/format-service.ts +254 -0
  127. package/clients/formatters.js +435 -0
  128. package/clients/formatters.ts +508 -0
  129. package/clients/go-client.js +5 -14
  130. package/clients/go-client.ts +5 -13
  131. package/clients/installer/index.js +356 -0
  132. package/clients/installer/index.ts +426 -0
  133. package/clients/jscpd-client.js +11 -9
  134. package/clients/jscpd-client.ts +12 -8
  135. package/clients/knip-client.js +3 -7
  136. package/clients/knip-client.ts +3 -6
  137. package/clients/lsp/__tests__/client.test.js +325 -0
  138. package/clients/lsp/__tests__/client.test.ts +434 -0
  139. package/clients/lsp/__tests__/config.test.js +166 -0
  140. package/clients/lsp/__tests__/config.test.ts +209 -0
  141. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  142. package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
  143. package/clients/lsp/__tests__/integration.test.js +127 -0
  144. package/clients/lsp/__tests__/integration.test.ts +160 -0
  145. package/clients/lsp/__tests__/launch.test.js +260 -0
  146. package/clients/lsp/__tests__/launch.test.ts +329 -0
  147. package/clients/lsp/__tests__/server.test.js +259 -0
  148. package/clients/lsp/__tests__/server.test.ts +332 -0
  149. package/clients/lsp/__tests__/service.test.js +417 -0
  150. package/clients/lsp/__tests__/service.test.ts +499 -0
  151. package/clients/lsp/client.js +235 -0
  152. package/clients/lsp/client.ts +328 -0
  153. package/clients/lsp/config.js +115 -0
  154. package/clients/lsp/config.ts +149 -0
  155. package/clients/lsp/index.js +222 -0
  156. package/clients/lsp/index.ts +280 -0
  157. package/clients/lsp/installer/index.js +391 -0
  158. package/clients/lsp/interactive-install.js +210 -0
  159. package/clients/lsp/interactive-install.ts +251 -0
  160. package/clients/lsp/language.js +170 -0
  161. package/clients/lsp/language.ts +216 -0
  162. package/clients/lsp/launch.js +174 -0
  163. package/clients/lsp/launch.ts +240 -0
  164. package/clients/lsp/lsp/launch.js +116 -0
  165. package/clients/lsp/lsp/server.js +532 -0
  166. package/clients/lsp/lsp-index.js +10 -0
  167. package/clients/lsp/lsp-index.ts +11 -0
  168. package/clients/lsp/path-utils.js +48 -0
  169. package/clients/lsp/path-utils.ts +52 -0
  170. package/clients/lsp/server.js +615 -0
  171. package/clients/lsp/server.ts +800 -0
  172. package/clients/lsp/test-py-spawn/requirements.txt +1 -0
  173. package/clients/lsp/test-py-spawn/test.py +3 -0
  174. package/clients/lsp/test-py-svc/requirements.txt +1 -0
  175. package/clients/lsp/test-py-svc/test.py +3 -0
  176. package/clients/lsp/test-python-project/requirements.txt +1 -0
  177. package/clients/lsp/test-python-project/test.py +5 -0
  178. package/clients/metrics-history.js +2 -2
  179. package/clients/metrics-history.ts +2 -2
  180. package/clients/production-readiness.js +522 -0
  181. package/clients/production-readiness.ts +556 -0
  182. package/clients/project-index.js +255 -0
  183. package/clients/project-index.ts +383 -0
  184. package/clients/project-metadata.js +531 -0
  185. package/clients/project-metadata.ts +624 -0
  186. package/clients/ruff-client.js +56 -16
  187. package/clients/ruff-client.ts +72 -15
  188. package/clients/runner-tracker.js +152 -0
  189. package/clients/runner-tracker.ts +213 -0
  190. package/clients/rust-client.js +4 -11
  191. package/clients/rust-client.ts +5 -11
  192. package/clients/safe-spawn.js +96 -0
  193. package/clients/safe-spawn.ts +128 -0
  194. package/clients/scan-architectural-debt.js +3 -6
  195. package/clients/scan-architectural-debt.ts +3 -6
  196. package/clients/scan-utils.js +5 -20
  197. package/clients/scan-utils.ts +5 -29
  198. package/clients/secrets-scanner.js +3 -17
  199. package/clients/secrets-scanner.ts +4 -20
  200. package/clients/services/__tests__/effect-integration.test.js +86 -0
  201. package/clients/services/__tests__/effect-integration.test.ts +111 -0
  202. package/clients/services/effect-integration.js +194 -0
  203. package/clients/services/effect-integration.ts +268 -0
  204. package/clients/services/index.js +7 -0
  205. package/clients/services/index.ts +8 -0
  206. package/clients/services/runner-service.js +105 -0
  207. package/clients/services/runner-service.ts +179 -0
  208. package/clients/sg-runner.js +87 -13
  209. package/clients/sg-runner.ts +97 -13
  210. package/clients/state-matrix.js +160 -0
  211. package/clients/state-matrix.ts +202 -0
  212. package/clients/subprocess-client.js +10 -9
  213. package/clients/subprocess-client.ts +10 -8
  214. package/clients/test-runner-client.js +3 -7
  215. package/clients/test-runner-client.ts +3 -6
  216. package/clients/tool-availability.js +4 -10
  217. package/clients/tool-availability.ts +4 -9
  218. package/clients/tree-sitter-client.js +564 -0
  219. package/clients/tree-sitter-client.ts +797 -0
  220. package/clients/tree-sitter-query-loader.js +355 -0
  221. package/clients/tree-sitter-query-loader.ts +425 -0
  222. package/clients/type-coverage-client.js +3 -7
  223. package/clients/type-coverage-client.ts +3 -6
  224. package/clients/typescript-client.codefix.test.js +157 -0
  225. package/clients/typescript-client.codefix.test.ts +186 -0
  226. package/clients/typescript-client.js +43 -0
  227. package/clients/typescript-client.ts +98 -0
  228. package/commands/booboo.js +799 -219
  229. package/commands/booboo.ts +1004 -225
  230. package/commands/clients/ast-grep-client.js +250 -0
  231. package/commands/clients/ast-grep-parser.js +86 -0
  232. package/commands/clients/ast-grep-rule-manager.js +91 -0
  233. package/commands/clients/ast-grep-types.js +9 -0
  234. package/commands/clients/biome-client.js +380 -0
  235. package/commands/clients/complexity-client.js +667 -0
  236. package/commands/clients/file-kinds.js +177 -0
  237. package/commands/clients/file-utils.js +40 -0
  238. package/commands/clients/jscpd-client.js +169 -0
  239. package/commands/clients/knip-client.js +211 -0
  240. package/commands/clients/ruff-client.js +297 -0
  241. package/commands/clients/safe-spawn.js +88 -0
  242. package/commands/clients/scan-utils.js +83 -0
  243. package/commands/clients/sg-runner.js +190 -0
  244. package/commands/clients/types.js +11 -0
  245. package/commands/clients/typescript-client.js +505 -0
  246. package/commands/fix-from-booboo.js +398 -0
  247. package/commands/fix-from-booboo.ts +485 -0
  248. package/commands/fix-simplified.js +618 -0
  249. package/commands/fix-simplified.ts +768 -0
  250. package/commands/rate.js +10 -14
  251. package/commands/rate.ts +9 -16
  252. package/default-architect.yaml +59 -15
  253. package/index.ts +342 -429
  254. package/package.json +16 -3
  255. package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
  256. package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
  257. package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
  258. package/rules/python-slop-rules/.sgconfig.yml +4 -0
  259. package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
  260. package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
  261. package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
  262. package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
  263. package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
  264. package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
  265. package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
  266. package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
  267. package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
  268. package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
  269. package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
  270. package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
  271. package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
  272. package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
  273. package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
  274. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
  275. package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
  276. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
  277. package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
  278. package/rules/ts-slop-rules/.sgconfig.yml +4 -0
  279. package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
  280. package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
  281. package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
  282. package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
  283. package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
  284. package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
  285. package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
  286. package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
  287. package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
  288. package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
  289. package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
  290. package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
  291. package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
  292. package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
  293. package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
  294. package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
  295. package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
  296. package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
  297. package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
  298. package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
  299. package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
  300. package/skills/ast-grep/SKILL.md +182 -0
  301. package/clients/dispatch/runners/secrets.js +0 -109
  302. package/commands/fix.js +0 -244
  303. package/commands/fix.ts +0 -373
  304. package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
@@ -0,0 +1,647 @@
1
+ ---
2
+ id: chained-comparison-opportunity
3
+ language: python
4
+ severity: warning
5
+ message: Use chained comparison (e.g., a < b < c) instead of 'and'
6
+ metadata:
7
+ weight: 3
8
+ category: slop
9
+ rule:
10
+ kind: boolean_operator
11
+ any:
12
+ - pattern: $A < $B and $B < $C
13
+ - pattern: $A > $B and $B > $C
14
+ - pattern: $A <= $B and $B <= $C
15
+ - pattern: $A >= $B and $B >= $C
16
+ - pattern: $A < $B and $B <= $C
17
+ - pattern: $A <= $B and $B < $C
18
+ - pattern: $A > $B and $B >= $C
19
+ - pattern: $A >= $B and $B > $C
20
+ ---
21
+ id: chained-dict-get
22
+ language: python
23
+ severity: warning
24
+ message: Chained .get().get() - overly defensive, extract to helper
25
+ metadata:
26
+ weight: 3
27
+ category: slop
28
+ rule:
29
+ kind: call
30
+ has:
31
+ kind: attribute
32
+ has:
33
+ kind: call
34
+ has:
35
+ kind: attribute
36
+ regex: \.get$
37
+ regex: \.get$
38
+ ---
39
+ id: comprehension-used-but-ignored-result
40
+ language: python
41
+ severity: warning
42
+ message: Comprehension result is never used - this looks like a side-effect-only comprehension
43
+ metadata:
44
+ weight: 3
45
+ category: slop
46
+ rule:
47
+ kind: expression_statement
48
+ has:
49
+ any:
50
+ - kind: list_comprehension
51
+ - kind: set_comprehension
52
+ - kind: dictionary_comprehension
53
+ - kind: generator_expression
54
+ ---
55
+ id: duplicated-if-condition
56
+ language: python
57
+ severity: warning
58
+ message: Duplicated if condition in elif chain
59
+ metadata:
60
+ weight: 3
61
+ category: slop
62
+ rule:
63
+ kind: if_statement
64
+ pattern: "if $COND:\n $$$\nelif $COND:\n $$$\n"
65
+ ---
66
+ id: isinstance-return-ladder
67
+ language: python
68
+ severity: warning
69
+ message: Long isinstance/elif ladder returning simple values; prefer a dispatch table or polymorphism.
70
+ metadata:
71
+ weight: 3
72
+ category: slop
73
+ rule:
74
+ pattern: "if isinstance($X, $T1):\n return $V1\nelif isinstance($X, $T2):\n return $V2\n"
75
+ ---
76
+ id: manual-min-max
77
+ language: python
78
+ severity: warning
79
+ message: Manual min/max logic - use built-in min() or max()
80
+ metadata:
81
+ weight: 3
82
+ category: slop
83
+ rule:
84
+ kind: if_statement
85
+ any:
86
+ - pattern: "if $A > $B:\n $M = $A\nelse:\n $M = $B\n"
87
+ - pattern: "if $A < $B:\n $M = $A\nelse:\n $M = $B\n"
88
+ - pattern: "if $A >= $B:\n $M = $A\nelse:\n $M = $B\n"
89
+ - pattern: "if $A <= $B:\n $M = $A\nelse:\n $M = $B\n"
90
+ ---
91
+ id: manual-str-join
92
+ language: python
93
+ severity: warning
94
+ message: Augmented assignment in loop - if building a string, use ''.join()
95
+ metadata:
96
+ weight: 3
97
+ category: slop
98
+ rule:
99
+ kind: for_statement
100
+ has:
101
+ kind: block
102
+ has:
103
+ kind: expression_statement
104
+ has:
105
+ kind: augmented_assignment
106
+ pattern: $S += $X
107
+ ---
108
+ id: pointless-lambda-call
109
+ language: python
110
+ severity: warning
111
+ message: Immediately called lambda - just execute the code
112
+ metadata:
113
+ weight: 3
114
+ category: slop
115
+ rule:
116
+ kind: call
117
+ has:
118
+ kind: parenthesized_expression
119
+ has:
120
+ kind: lambda
121
+ ---
122
+ id: ternary-same-value
123
+ language: python
124
+ severity: warning
125
+ message: Ternary returns same value for both branches - remove condition
126
+ metadata:
127
+ weight: 3
128
+ category: slop
129
+ rule:
130
+ kind: conditional_expression
131
+ pattern: $VALUE if $COND else $VALUE
132
+ ---
133
+ id: for-range-len
134
+ language: python
135
+ severity: warning
136
+ message: range(len(seq)) loop suggests index juggling; prefer enumerate
137
+ metadata:
138
+ weight: 3
139
+ category: slop
140
+ rule:
141
+ pattern: "for $I in range(len($SEQ)):\n $$$\n"
142
+ ---
143
+ id: boolean-return-if-else
144
+ language: python
145
+ severity: warning
146
+ message: if/else returning True/False - simplify to return the condition directly
147
+ metadata:
148
+ weight: 3
149
+ category: slop
150
+ rule:
151
+ kind: if_statement
152
+ any:
153
+ - pattern: "if $COND:\n return True\nelse:\n return False\n"
154
+ - pattern: "if $COND:\n return False\nelse:\n return True\n"
155
+ ---
156
+ id: json-dumps-then-loads
157
+ language: python
158
+ severity: warning
159
+ message: json.loads(json.dumps(x)) is noisy; copy the structure directly
160
+ metadata:
161
+ weight: 3
162
+ category: slop
163
+ rule:
164
+ pattern: json.loads(json.dumps($X))
165
+ ---
166
+ id: pointless-bool-cast
167
+ language: python
168
+ severity: warning
169
+ message: Wrapping a condition in bool() before if is redundant ceremony
170
+ metadata:
171
+ weight: 3
172
+ category: slop
173
+ rule:
174
+ kind: if_statement
175
+ pattern: "if bool($COND):\n $$$\n"
176
+ ---
177
+ id: nested-attribute-guard-chain
178
+ language: python
179
+ severity: warning
180
+ message: 'Nested attribute guard chain (if x: if x.y: if x.y.z:) - use walrus operator or getattr'
181
+ metadata:
182
+ weight: 3
183
+ category: slop
184
+ rule:
185
+ kind: if_statement
186
+ all:
187
+ - has:
188
+ kind: identifier
189
+ pattern: $OBJ
190
+ - has:
191
+ kind: block
192
+ has:
193
+ kind: if_statement
194
+ all:
195
+ - has:
196
+ kind: attribute
197
+ has:
198
+ kind: identifier
199
+ pattern: $OBJ
200
+ - has:
201
+ kind: block
202
+ has:
203
+ kind: if_statement
204
+ ---
205
+ id: deep-dict-access
206
+ language: python
207
+ severity: warning
208
+ message: Deep nested dict access (4+ levels) - extract to helper or use dataclass
209
+ metadata:
210
+ weight: 3
211
+ category: slop
212
+ rule:
213
+ kind: subscript
214
+ has:
215
+ kind: subscript
216
+ has:
217
+ kind: subscript
218
+ has:
219
+ kind: subscript
220
+ ---
221
+ id: long-tuple-unpacking
222
+ language: python
223
+ severity: warning
224
+ message: Unpacking 5+ values from tuple - use named tuple or dataclass
225
+ metadata:
226
+ weight: 3
227
+ category: slop
228
+ rule:
229
+ kind: assignment
230
+ has:
231
+ kind: pattern_list
232
+ has:
233
+ nthChild:
234
+ position: 5
235
+ ---
236
+ id: dict-get-default-none
237
+ language: python
238
+ severity: warning
239
+ message: dict.get(k, None) - None is the default return value, so the second argument is redundant
240
+ metadata:
241
+ weight: 2
242
+ category: slop
243
+ rule:
244
+ kind: call
245
+ all:
246
+ - has:
247
+ kind: attribute
248
+ regex: \.get$
249
+ - has:
250
+ kind: argument_list
251
+ has:
252
+ kind: none
253
+ ---
254
+ id: empty-init
255
+ language: python
256
+ severity: warning
257
+ message: Empty __init__ method - redundant
258
+ metadata:
259
+ weight: 2
260
+ category: slop
261
+ rule:
262
+ kind: function_definition
263
+ all:
264
+ - has:
265
+ kind: identifier
266
+ regex: ^__init__$
267
+ - has:
268
+ kind: block
269
+ has:
270
+ kind: pass_statement
271
+ ---
272
+ id: explicit-bool-cast
273
+ language: python
274
+ severity: warning
275
+ message: bool() cast - usually unnecessary, use truthiness directly
276
+ metadata:
277
+ weight: 2
278
+ category: slop
279
+ rule:
280
+ kind: call
281
+ has:
282
+ kind: identifier
283
+ regex: ^bool$
284
+ ---
285
+ id: guard-return-none
286
+ language: python
287
+ severity: warning
288
+ message: 'if x is None: return None - overly defensive early guard'
289
+ metadata:
290
+ weight: 2
291
+ category: slop
292
+ rule:
293
+ kind: if_statement
294
+ pattern: 'if $X is None: return None'
295
+ ---
296
+ id: if-none-raise
297
+ language: python
298
+ severity: warning
299
+ message: 'if x is None: raise - defensive None guard before operation'
300
+ metadata:
301
+ weight: 2
302
+ category: slop
303
+ rule:
304
+ kind: if_statement
305
+ all:
306
+ - has:
307
+ kind: comparison_operator
308
+ regex: is None
309
+ - has:
310
+ kind: block
311
+ has:
312
+ kind: raise_statement
313
+ ---
314
+ id: int-float-coerce
315
+ language: python
316
+ severity: warning
317
+ message: Casting via int(float(x)) is sloppy and can mis-handle strings; parse once or validate explicitly.
318
+ metadata:
319
+ weight: 2
320
+ category: slop
321
+ rule:
322
+ kind: call
323
+ pattern: int(float($X))
324
+ ---
325
+ id: len-comparison
326
+ language: python
327
+ severity: warning
328
+ message: len() > 0 or len() != 0 - use truthiness instead
329
+ metadata:
330
+ weight: 2
331
+ category: slop
332
+ rule:
333
+ kind: comparison_operator
334
+ has:
335
+ kind: call
336
+ regex: ^len\(
337
+ regex: (>\s*0|!=\s*0|>=\s*1)
338
+ ---
339
+ id: manual-dict-setdefault
340
+ language: python
341
+ severity: warning
342
+ message: 'Manual dict setdefault pattern (if k not in d: d[k] = ...) - use d.setdefault()'
343
+ metadata:
344
+ weight: 2
345
+ category: slop
346
+ rule:
347
+ kind: if_statement
348
+ all:
349
+ - has:
350
+ kind: comparison_operator
351
+ regex: not in
352
+ - has:
353
+ kind: block
354
+ has:
355
+ kind: expression_statement
356
+ has:
357
+ kind: assignment
358
+ has:
359
+ kind: subscript
360
+ ---
361
+ id: multiple-isinstance-or
362
+ language: python
363
+ severity: warning
364
+ message: Multiple isinstance() with or - use isinstance(x, (A, B)) tuple form
365
+ metadata:
366
+ weight: 2
367
+ category: slop
368
+ rule:
369
+ kind: boolean_operator
370
+ regex: isinstance.*or.*isinstance
371
+ ---
372
+ id: range-len-pattern
373
+ language: python
374
+ severity: warning
375
+ message: range(len(x)) - use enumerate() instead
376
+ metadata:
377
+ weight: 2
378
+ category: slop
379
+ rule:
380
+ kind: call
381
+ pattern: range(len($X))
382
+ ---
383
+ id: redundant-bool-ternary
384
+ language: python
385
+ severity: warning
386
+ message: Redundant boolean ternary 'True if x else False' - use 'bool(x)'
387
+ metadata:
388
+ weight: 2
389
+ category: slop
390
+ rule:
391
+ kind: conditional_expression
392
+ regex: True\s+if.*else\s+False|False\s+if.*else\s+True
393
+ ---
394
+ id: redundant-list-comprehension
395
+ language: python
396
+ severity: warning
397
+ message: Redundant list comprehension - usage of [x for x in iterable] is unnecessary, use list(iterable)
398
+ metadata:
399
+ weight: 2
400
+ category: slop
401
+ rule:
402
+ kind: list_comprehension
403
+ pattern: '[$X for $X in $ITER]'
404
+ ---
405
+ id: redundant-return-none
406
+ language: python
407
+ severity: warning
408
+ message: Explicit 'return None' at end of function - implicit None is cleaner
409
+ metadata:
410
+ weight: 2
411
+ category: slop
412
+ rule:
413
+ kind: return_statement
414
+ regex: ^return None$
415
+ ---
416
+ id: set-literal-list
417
+ language: python
418
+ severity: warning
419
+ message: set([list]) - use set literal {x, y} instead
420
+ metadata:
421
+ weight: 2
422
+ category: slop
423
+ rule:
424
+ kind: call
425
+ has:
426
+ kind: argument_list
427
+ has:
428
+ kind: list
429
+ ---
430
+ id: unnecessary-cast-str
431
+ language: python
432
+ severity: warning
433
+ message: Unnecessary str() cast on string literal or f-string
434
+ metadata:
435
+ weight: 2
436
+ category: slop
437
+ rule:
438
+ kind: call
439
+ all:
440
+ - has:
441
+ kind: identifier
442
+ regex: ^str$
443
+ - has:
444
+ kind: argument_list
445
+ has:
446
+ kind: string
447
+ ---
448
+ id: unnecessary-elif
449
+ language: python
450
+ severity: warning
451
+ message: Unnecessary elif after return/raise - use if instead
452
+ metadata:
453
+ weight: 2
454
+ category: slop
455
+ rule:
456
+ kind: if_statement
457
+ all:
458
+ - has:
459
+ kind: block
460
+ any:
461
+ - has:
462
+ kind: return_statement
463
+ - has:
464
+ kind: raise_statement
465
+ - has:
466
+ kind: elif_clause
467
+ ---
468
+ id: unnecessary-else-raise
469
+ language: python
470
+ severity: warning
471
+ message: else after raise - the else block is redundant
472
+ metadata:
473
+ weight: 2
474
+ category: slop
475
+ rule:
476
+ kind: if_statement
477
+ all:
478
+ - has:
479
+ kind: block
480
+ has:
481
+ kind: raise_statement
482
+ - has:
483
+ kind: else_clause
484
+ ---
485
+ id: unnecessary-lambda
486
+ language: python
487
+ severity: warning
488
+ message: 'Unnecessary lambda - lambda x: func(x) can be replaced by func'
489
+ metadata:
490
+ weight: 2
491
+ category: slop
492
+ rule:
493
+ kind: lambda
494
+ pattern: 'lambda $ARG: $FN($ARG)'
495
+ ---
496
+ id: verbose-none-default
497
+ language: python
498
+ severity: warning
499
+ message: Verbose None default pattern - use 'x = x or default'
500
+ metadata:
501
+ weight: 2
502
+ category: slop
503
+ rule:
504
+ pattern: "if $VAR is None:\n $VAR = $DEFAULT\n"
505
+ ---
506
+ id: type-equality
507
+ language: python
508
+ severity: warning
509
+ message: type(x) == Type comparison - use isinstance() instead
510
+ metadata:
511
+ weight: 2
512
+ category: slop
513
+ rule:
514
+ kind: comparison_operator
515
+ has:
516
+ kind: call
517
+ regex: ^type\(
518
+ ---
519
+ id: dict-str-any
520
+ language: python
521
+ severity: warning
522
+ message: Dict[str, Any] return/field - prefer precise value types instead of Any
523
+ metadata:
524
+ weight: 2
525
+ category: slop
526
+ rule:
527
+ kind: type
528
+ regex: ^Dict\[\s*str\s*,\s*Any\s*\]$
529
+ ---
530
+ id: list-any
531
+ language: python
532
+ severity: warning
533
+ message: List[Any] annotation - tighten element typing instead of using Any
534
+ metadata:
535
+ weight: 2
536
+ category: slop
537
+ rule:
538
+ kind: type
539
+ regex: ^List\[\s*Any\s*\]$
540
+ ---
541
+ id: union-with-any
542
+ language: python
543
+ severity: warning
544
+ message: Union containing Any defeats type checking - drop Any or use a narrower union
545
+ metadata:
546
+ weight: 2
547
+ category: slop
548
+ rule:
549
+ kind: type
550
+ regex: Union\[[^\]]*Any[^\]]*\]
551
+ ---
552
+ id: verbose-list-append-loop
553
+ language: python
554
+ severity: warning
555
+ message: Loop with list.append() - consider list comprehension
556
+ metadata:
557
+ weight: 4
558
+ category: slop
559
+ rule:
560
+ kind: for_statement
561
+ has:
562
+ kind: block
563
+ has:
564
+ kind: expression_statement
565
+ has:
566
+ kind: call
567
+ has:
568
+ kind: attribute
569
+ regex: \.append$
570
+ ---
571
+ id: set-add-loop
572
+ language: python
573
+ severity: warning
574
+ message: Loop building a set with add() - use a set comprehension
575
+ metadata:
576
+ weight: 4
577
+ category: slop
578
+ rule:
579
+ kind: for_statement
580
+ pattern: "for $ITEM in $ITER:\n $SET.add($VAL)\n"
581
+ ---
582
+ id: manual-dict-get-assign
583
+ language: python
584
+ severity: warning
585
+ message: 'if key in dict: ... else: ... assignment ladder - use dict.get(...)'
586
+ metadata:
587
+ weight: 4
588
+ category: slop
589
+ rule:
590
+ pattern: "if $KEY in $DICT:\n $OUT = $DICT[$KEY]\nelse:\n $OUT = $DEFAULT\n"
591
+ ---
592
+ id: list-extend-from-loop
593
+ language: python
594
+ severity: warning
595
+ message: Loop appending items from iterable - use list.extend() instead
596
+ metadata:
597
+ weight: 4
598
+ category: slop
599
+ rule:
600
+ kind: for_statement
601
+ pattern: "for $ITEM in $ITER:\n $LIST.append($ITEM)\n"
602
+ ---
603
+ id: join-list-comprehension
604
+ language: python
605
+ severity: warning
606
+ message: join([expr for ...]) builds a throwaway list - use a generator expression
607
+ metadata:
608
+ weight: 4
609
+ category: slop
610
+ rule:
611
+ kind: call
612
+ all:
613
+ - has:
614
+ kind: attribute
615
+ regex: \.join$
616
+ - has:
617
+ kind: argument_list
618
+ has:
619
+ kind: list_comprehension
620
+ ---
621
+ id: manual-sum-loop
622
+ language: python
623
+ severity: warning
624
+ message: Manual accumulation loop - use sum(...) instead of a throwaway counter variable
625
+ metadata:
626
+ weight: 4
627
+ category: slop
628
+ rule:
629
+ kind: for_statement
630
+ pattern: "for $ITEM in $ITER:\n $TOTAL += $EXPR\n"
631
+ follows:
632
+ kind: expression_statement
633
+ has:
634
+ kind: assignment
635
+ pattern: $TOTAL = 0
636
+ stopBy: neighbor
637
+ ---
638
+ id: membership-test-list-literal
639
+ language: python
640
+ severity: warning
641
+ message: Membership test on list literal with 4+ items - use set literal {...} for O(1) lookup
642
+ metadata:
643
+ weight: 3
644
+ category: slop
645
+ rule:
646
+ kind: comparison_operator
647
+ pattern: $X in [$A, $B, $C, $D, $$$REST]
@@ -0,0 +1,54 @@
1
+ # Bare Except Clause
2
+ # Detects bare 'except:' that catches all exceptions including SystemExit
3
+ id: bare-except
4
+ name: Bare Except Clause
5
+ severity: error
6
+ category: reliability
7
+ language: python
8
+
9
+ message: "Bare 'except:' clause — catches SystemExit, KeyboardInterrupt"
10
+
11
+ description: |
12
+ Using bare 'except:' catches all exceptions including SystemExit
13
+ and KeyboardInterrupt, making the code hard to interrupt.
14
+
15
+ ✅ FIX: Use 'except Exception:' or catch specific exceptions
16
+
17
+ ```python
18
+ try:
19
+ process()
20
+ except ValueError as e: # Specific exception
21
+ handle_error(e)
22
+ ```
23
+
24
+ query: |
25
+ (except_clause
26
+ "except") @CLAUSE
27
+
28
+ metavars:
29
+ - CLAUSE
30
+
31
+ # Post-filter: Only match if no identifier follows except (bare except)
32
+ post_filter: bare_except_only
33
+
34
+ tags:
35
+ - reliability
36
+ - best-practice
37
+ - exceptions
38
+
39
+ examples:
40
+ bad: |
41
+ try:
42
+ process()
43
+ except: # BAD - catches everything
44
+ pass
45
+
46
+ good: |
47
+ try:
48
+ process()
49
+ except ValueError as e: # GOOD - specific
50
+ handle_value_error(e)
51
+ except Exception as e: # GOOD - at least Exception
52
+ logger.error(f"Unexpected: {e}")
53
+
54
+ has_fix: false