claude-dev-env 1.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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -0
  3. package/agents/agent-writer.md +157 -0
  4. package/agents/clasp-deployment-orchestrator.md +609 -0
  5. package/agents/clean-coder.md +295 -0
  6. package/agents/code-quality-agent.md +40 -0
  7. package/agents/code-standards-agent.md +93 -0
  8. package/agents/config-centralizer.md +686 -0
  9. package/agents/config-extraction-agent.md +225 -0
  10. package/agents/doc-orchestrator.md +47 -0
  11. package/agents/docs-agent.md +112 -0
  12. package/agents/docx-agent.md +211 -0
  13. package/agents/git-commit-crafter.md +100 -0
  14. package/agents/magic-value-eliminator-agent.md +72 -0
  15. package/agents/mandatory-agent-workflow-agent.md +88 -0
  16. package/agents/parallel-workflow-coordinator.md +779 -0
  17. package/agents/pdf-agent.md +302 -0
  18. package/agents/plan-executor.md +226 -0
  19. package/agents/pr-description-writer.md +87 -0
  20. package/agents/project-context-loader.md +238 -0
  21. package/agents/project-docs-analyzer.md +54 -0
  22. package/agents/project-structure-organizer-agent.md +72 -0
  23. package/agents/readability-review-agent.md +76 -0
  24. package/agents/refactoring-specialist.md +69 -0
  25. package/agents/right-sized-engineer.md +129 -0
  26. package/agents/session-continuity-manager.md +53 -0
  27. package/agents/skill-to-agent-converter.md +371 -0
  28. package/agents/skill-writer-agent.md +470 -0
  29. package/agents/stub-detector-agent.md +140 -0
  30. package/agents/tdd-test-writer.md +62 -0
  31. package/agents/test-data-builder.md +68 -0
  32. package/agents/tooling-builder.md +78 -0
  33. package/agents/user-docs-writer.md +67 -0
  34. package/agents/validation-expert.md +71 -0
  35. package/agents/workflow-visual-documenter.md +82 -0
  36. package/agents/xlsx-agent.md +169 -0
  37. package/bin/install.mjs +256 -0
  38. package/commands/commit.md +28 -0
  39. package/commands/docupdate.md +322 -0
  40. package/commands/implement.md +102 -0
  41. package/commands/initialize.md +91 -0
  42. package/commands/plan.md +63 -0
  43. package/commands/pr-comments.md +47 -0
  44. package/commands/readability-review.md +20 -0
  45. package/commands/review-plan.md +7 -0
  46. package/commands/right-size.md +15 -0
  47. package/commands/stubcheck.md +89 -0
  48. package/commands/sum.md +30 -0
  49. package/docs/CODE_RULES.md +186 -0
  50. package/docs/DJANGO_PATTERNS.md +80 -0
  51. package/docs/REACT_PATTERNS.md +185 -0
  52. package/docs/TEST_QUALITY.md +104 -0
  53. package/hooks/advisory/migration-safety-advisor.py +49 -0
  54. package/hooks/advisory/refactor-guard.py +205 -0
  55. package/hooks/blocking/block-main-commit.py +168 -0
  56. package/hooks/blocking/code-rules-enforcer.py +549 -0
  57. package/hooks/blocking/destructive-command-blocker.py +107 -0
  58. package/hooks/blocking/docker-settings-guard.py +44 -0
  59. package/hooks/blocking/hedging-language-blocker.py +130 -0
  60. package/hooks/blocking/parallel-task-blocker.py +69 -0
  61. package/hooks/blocking/pr-description-enforcer.py +87 -0
  62. package/hooks/blocking/pyautogui-scroll-blocker.py +74 -0
  63. package/hooks/blocking/sensitive-file-protector.py +70 -0
  64. package/hooks/blocking/tdd-enforcer.py +62 -0
  65. package/hooks/blocking/test-preflight-check.py +343 -0
  66. package/hooks/blocking/write-existing-file-blocker.py +63 -0
  67. package/hooks/git-hooks/post-commit.py +103 -0
  68. package/hooks/github-action/test_workflow.py +33 -0
  69. package/hooks/hooks.json +246 -0
  70. package/hooks/lifecycle/config-change-guard.py +84 -0
  71. package/hooks/lifecycle/session-end-cleanup.py +59 -0
  72. package/hooks/notification/attention-needed-notify.py +63 -0
  73. package/hooks/notification/claude-notification-handler.py +59 -0
  74. package/hooks/notification/notification_utils.py +206 -0
  75. package/hooks/rewrite-plugin-paths.py +116 -0
  76. package/hooks/session/bulk-edit-reminder.py +30 -0
  77. package/hooks/session/code-rules-reminder.py +97 -0
  78. package/hooks/session/compact-context-reinject.py +39 -0
  79. package/hooks/session/hook-structure-context.py +140 -0
  80. package/hooks/session/plugin-data-dir-cleanup.py +39 -0
  81. package/hooks/validation/code-style-validator.py +145 -0
  82. package/hooks/validation/e2e-test-validator.py +142 -0
  83. package/hooks/validation/hook-format-validator.py +66 -0
  84. package/hooks/validation/mypy_validator.py +180 -0
  85. package/hooks/validators/README.md +125 -0
  86. package/hooks/validators/VALIDATION_REPORT.md +287 -0
  87. package/hooks/validators/__init__.py +19 -0
  88. package/hooks/validators/abbreviation_checks.py +82 -0
  89. package/hooks/validators/code_quality_checks.py +133 -0
  90. package/hooks/validators/comment_checks.py +188 -0
  91. package/hooks/validators/file_structure_checks.py +182 -0
  92. package/hooks/validators/git_checks.py +107 -0
  93. package/hooks/validators/health_check.py +214 -0
  94. package/hooks/validators/magic_value_checks.py +81 -0
  95. package/hooks/validators/mypy_integration.py +52 -0
  96. package/hooks/validators/output_formatter.py +266 -0
  97. package/hooks/validators/pr_reference_checks.py +72 -0
  98. package/hooks/validators/python_antipattern_checks.py +110 -0
  99. package/hooks/validators/python_style_checks.py +364 -0
  100. package/hooks/validators/react_checks.py +90 -0
  101. package/hooks/validators/ruff_integration.py +80 -0
  102. package/hooks/validators/run_all_validators.py +772 -0
  103. package/hooks/validators/security_checks.py +135 -0
  104. package/hooks/validators/test_abbreviation_checks.py +76 -0
  105. package/hooks/validators/test_bad.tsx +7 -0
  106. package/hooks/validators/test_code_quality_checks.py +129 -0
  107. package/hooks/validators/test_file_structure_checks.py +307 -0
  108. package/hooks/validators/test_files/01_basic_component.tsx +10 -0
  109. package/hooks/validators/test_files/02_component_without_react.tsx +10 -0
  110. package/hooks/validators/test_files/03_pure_component.tsx +10 -0
  111. package/hooks/validators/test_files/04_pure_component_import.tsx +10 -0
  112. package/hooks/validators/test_files/05_typescript_generics.tsx +14 -0
  113. package/hooks/validators/test_files/06_typescript_two_generics.tsx +18 -0
  114. package/hooks/validators/test_files/07_multiline_declaration.tsx +11 -0
  115. package/hooks/validators/test_files/08_error_boundary_valid.tsx +14 -0
  116. package/hooks/validators/test_files/09_error_boundary_with_other_class.tsx +20 -0
  117. package/hooks/validators/test_files/10_inheritance_chain.tsx +16 -0
  118. package/hooks/validators/test_files/11_ts_file.ts +10 -0
  119. package/hooks/validators/test_files/12_non_react_class.tsx +14 -0
  120. package/hooks/validators/test_files/13_functional_component.tsx +8 -0
  121. package/hooks/validators/test_files/14_indented_class.tsx +13 -0
  122. package/hooks/validators/test_files/15_getDerivedStateFromError.tsx +14 -0
  123. package/hooks/validators/test_files/16_mixed_components.tsx +20 -0
  124. package/hooks/validators/test_files/EXECUTIVE_SUMMARY.md +175 -0
  125. package/hooks/validators/test_files/TEST_RESULTS_TABLE.txt +60 -0
  126. package/hooks/validators/test_files/VALIDATION_REPORT.md +201 -0
  127. package/hooks/validators/test_files/async_views.py +23 -0
  128. package/hooks/validators/test_files/async_with_imports.py +14 -0
  129. package/hooks/validators/test_files/bad_inline_imports.py +37 -0
  130. package/hooks/validators/test_files/management/commands/cmd_01_no_debug_check.py +10 -0
  131. package/hooks/validators/test_files/management/commands/cmd_02_proper_debug_check.py +14 -0
  132. package/hooks/validators/test_files/management/commands/cmd_03_debug_check_with_return.py +14 -0
  133. package/hooks/validators/test_files/management/commands/cmd_04_imported_DEBUG.py +14 -0
  134. package/hooks/validators/test_files/management/commands/cmd_05_debug_check_in_helper.py +16 -0
  135. package/hooks/validators/test_files/management/commands/cmd_06_debug_check_late.py +22 -0
  136. package/hooks/validators/test_files/management/commands/cmd_07_positive_debug_check.py +15 -0
  137. package/hooks/validators/test_files/management/commands/cmd_08_debug_with_and.py +14 -0
  138. package/hooks/validators/test_files/not_management_command.py +10 -0
  139. package/hooks/validators/test_files/skip_decorators/test_01_simple_skip.py +8 -0
  140. package/hooks/validators/test_files/skip_decorators/test_02_pytest_skipif.py +8 -0
  141. package/hooks/validators/test_files/skip_decorators/test_03_unittest_skipIf.py +8 -0
  142. package/hooks/validators/test_files/skip_decorators/test_04_skip_with_parens.py +8 -0
  143. package/hooks/validators/test_files/skip_decorators/test_05_xfail.py +7 -0
  144. package/hooks/validators/test_files/skip_decorators/test_06_custom_skip.py +11 -0
  145. package/hooks/validators/test_files/skip_decorators/test_07_capital_Skip.py +8 -0
  146. package/hooks/validators/test_files/skip_decorators/test_08_skipUnless.py +7 -0
  147. package/hooks/validators/test_files/skip_decorators/test_09_pytest_mark_skip_simple.py +7 -0
  148. package/hooks/validators/test_files/test_async_functions.py +45 -0
  149. package/hooks/validators/test_files/test_purecomponent/PureComponentExample.tsx +7 -0
  150. package/hooks/validators/test_files/test_purecomponent/ReactPureComponentExample.tsx +7 -0
  151. package/hooks/validators/test_git_checks.py +295 -0
  152. package/hooks/validators/test_good.tsx +5 -0
  153. package/hooks/validators/test_health_check.py +57 -0
  154. package/hooks/validators/test_magic_value_checks.py +63 -0
  155. package/hooks/validators/test_mypy_integration.py +27 -0
  156. package/hooks/validators/test_output_formatter.py +150 -0
  157. package/hooks/validators/test_pr_reference_checks.py +41 -0
  158. package/hooks/validators/test_python_antipattern_checks.py +113 -0
  159. package/hooks/validators/test_python_style_checks.py +439 -0
  160. package/hooks/validators/test_react_checks.py +213 -0
  161. package/hooks/validators/test_results.txt +25 -0
  162. package/hooks/validators/test_ruff_integration.py +27 -0
  163. package/hooks/validators/test_run_all_validators.py +228 -0
  164. package/hooks/validators/test_run_all_validators_integration.py +48 -0
  165. package/hooks/validators/test_safety_checks.py +243 -0
  166. package/hooks/validators/test_security_checks.py +105 -0
  167. package/hooks/validators/test_test_safety_checks.py +321 -0
  168. package/hooks/validators/test_todo_checks.py +39 -0
  169. package/hooks/validators/test_type_safety_checks.py +85 -0
  170. package/hooks/validators/test_useless_test_checks.py +55 -0
  171. package/hooks/validators/test_validator_base.py +26 -0
  172. package/hooks/validators/test_verify_paths.py +34 -0
  173. package/hooks/validators/todo_checks.py +59 -0
  174. package/hooks/validators/type_safety_checks.py +101 -0
  175. package/hooks/validators/useless_test_checks.py +92 -0
  176. package/hooks/validators/validator_base.py +19 -0
  177. package/hooks/validators/verify_paths.py +57 -0
  178. package/hooks/workflow/auto-formatter.py +114 -0
  179. package/hooks/workflow/investigation-tracker-reset.py +46 -0
  180. package/package.json +30 -0
  181. package/rules/agent-spawn-protocol.md +47 -0
  182. package/rules/cleanup-temp-files.md +27 -0
  183. package/rules/code-reviews.md +11 -0
  184. package/rules/code-standards.md +43 -0
  185. package/rules/conservative-action.md +20 -0
  186. package/rules/context7.md +12 -0
  187. package/rules/explore-thoroughly.md +27 -0
  188. package/rules/git-workflow.md +42 -0
  189. package/rules/parallel-tools.md +23 -0
  190. package/rules/research-mode.md +23 -0
  191. package/rules/right-sized-engineering.md +28 -0
  192. package/rules/tdd.md +7 -0
  193. package/rules/testing.md +12 -0
  194. package/skills/agent-prompt/SKILL.md +102 -0
  195. package/skills/anthropic-plan/SKILL.md +107 -0
  196. package/skills/everything-search/SKILL.md +144 -0
  197. package/skills/ingest/SKILL.md +40 -0
  198. package/skills/npm-creator/SKILL.md +183 -0
  199. package/skills/pr-review-responder/EXAMPLES.md +590 -0
  200. package/skills/pr-review-responder/PRINCIPLES.md +539 -0
  201. package/skills/pr-review-responder/README.md +209 -0
  202. package/skills/pr-review-responder/SKILL.md +202 -0
  203. package/skills/pr-review-responder/TESTING.md +407 -0
  204. package/skills/pr-review-responder/scripts/respond_to_reviews.py +376 -0
  205. package/skills/pr-review-responder/update_skill.py +297 -0
  206. package/skills/prompt-generator/REFERENCE.md +150 -0
  207. package/skills/prompt-generator/SKILL.md +154 -0
  208. package/skills/readability-review/SKILL.md +127 -0
  209. package/skills/recall/SKILL.md +27 -0
  210. package/skills/remember/SKILL.md +63 -0
  211. package/skills/rule-audit/SKILL.md +307 -0
  212. package/skills/rule-creator/SKILL.md +150 -0
  213. package/skills/skill-writer/REFERENCE.md +246 -0
  214. package/skills/skill-writer/SKILL.md +270 -0
  215. package/skills/tdd-team/SKILL.md +128 -0
@@ -0,0 +1,364 @@
1
+ """Python style checks using AST-based validation.
2
+
3
+ Implements four style checks:
4
+ 1. Imports at top of file
5
+ 2. No empty lines after decorators
6
+ 3. Single empty line between functions
7
+ 4. View functions end with _view suffix
8
+ """
9
+
10
+ import ast
11
+ import sys
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import List
15
+
16
+
17
+ # Constants
18
+ VIEW_SUFFIX = "_view"
19
+ REQUEST_PARAM = "request"
20
+ VIEWS_FILENAME = "views.py"
21
+
22
+
23
+ @dataclass
24
+ class Violation:
25
+ """Represents a style violation."""
26
+
27
+ file: str
28
+ line: int
29
+ message: str
30
+
31
+ def __str__(self) -> str:
32
+ """Format as file:line: message."""
33
+ return f"{self.file}:{self.line}: {self.message}"
34
+
35
+
36
+ def check_imports_at_top(tree: ast.AST, filename: str) -> List[Violation]:
37
+ """Check that all imports are at the top of the file.
38
+
39
+ Catches two violations:
40
+ 1. Module-level imports after non-import statements
41
+ 2. Imports inside functions/classes (inline imports)
42
+
43
+ Args:
44
+ tree: AST tree to check
45
+ filename: Name of file being checked
46
+
47
+ Returns:
48
+ List of violations found
49
+ """
50
+ violations: List[Violation] = []
51
+
52
+ # Check 1: Module-level imports must be at top
53
+ if isinstance(tree, ast.Module):
54
+ seen_non_import = False
55
+ for child in tree.body:
56
+ if isinstance(child, (ast.Import, ast.ImportFrom)):
57
+ if seen_non_import:
58
+ violations.append(
59
+ Violation(
60
+ filename,
61
+ child.lineno,
62
+ "Import statement must be at top of file",
63
+ )
64
+ )
65
+ elif isinstance(child, ast.Expr) and isinstance(child.value, ast.Constant):
66
+ # Allow docstrings at top
67
+ if isinstance(child.value.value, str):
68
+ continue
69
+ seen_non_import = True
70
+ else:
71
+ seen_non_import = True
72
+
73
+ # Check 2: No imports inside functions or methods
74
+ for node in ast.walk(tree):
75
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
76
+ for child in ast.walk(node):
77
+ if isinstance(child, (ast.Import, ast.ImportFrom)):
78
+ violations.append(
79
+ Violation(
80
+ filename,
81
+ child.lineno,
82
+ "Import inside function - move to top of file",
83
+ )
84
+ )
85
+
86
+ return violations
87
+
88
+
89
+ def check_no_empty_line_after_decorators(source: str, filename: str) -> List[Violation]:
90
+ """Check that decorators have no empty line before function.
91
+
92
+ Args:
93
+ source: Source code as string
94
+ filename: Name of file being checked
95
+
96
+ Returns:
97
+ List of violations found
98
+ """
99
+ violations: List[Violation] = []
100
+ lines = source.splitlines()
101
+
102
+ try:
103
+ tree = ast.parse(source)
104
+ except SyntaxError:
105
+ return violations
106
+
107
+ for node in ast.walk(tree):
108
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.decorator_list:
109
+ # Get last decorator line
110
+ last_decorator_line = max(d.lineno for d in node.decorator_list)
111
+ function_line = node.lineno
112
+
113
+ # Check if there's an empty line between decorator and function
114
+ if function_line - last_decorator_line > 1:
115
+ violations.append(
116
+ Violation(
117
+ filename,
118
+ last_decorator_line,
119
+ "No empty line allowed between decorator and function",
120
+ )
121
+ )
122
+
123
+ return violations
124
+
125
+
126
+ def check_single_empty_line_between_functions(
127
+ source: str, filename: str
128
+ ) -> List[Violation]:
129
+ """Check that functions have exactly one empty line between them.
130
+
131
+ Args:
132
+ source: Source code as string
133
+ filename: Name of file being checked
134
+
135
+ Returns:
136
+ List of violations found
137
+ """
138
+ violations: List[Violation] = []
139
+ lines = source.splitlines()
140
+
141
+ try:
142
+ tree = ast.parse(source)
143
+ except SyntaxError:
144
+ return violations
145
+
146
+ # Get all top-level function definitions
147
+ functions = [
148
+ node for node in ast.walk(tree) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
149
+ ]
150
+
151
+ # Filter to only top-level functions (not nested)
152
+ if isinstance(tree, ast.Module):
153
+ top_level_functions = [
154
+ node for node in tree.body if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
155
+ ]
156
+ else:
157
+ top_level_functions = []
158
+
159
+ # Sort by line number
160
+ top_level_functions.sort(key=lambda f: f.lineno)
161
+
162
+ # Check spacing between consecutive functions
163
+ for i in range(len(top_level_functions) - 1):
164
+ current_func = top_level_functions[i]
165
+ next_func = top_level_functions[i + 1]
166
+
167
+ # Find last line of current function
168
+ current_end = current_func.end_lineno
169
+ next_start = next_func.lineno
170
+
171
+ if current_end is not None:
172
+ # Calculate empty lines between functions
173
+ empty_lines = next_start - current_end - 1
174
+
175
+ if empty_lines != 1:
176
+ violations.append(
177
+ Violation(
178
+ filename,
179
+ current_end,
180
+ f"Expected 1 empty line between functions, found {empty_lines}",
181
+ )
182
+ )
183
+
184
+ return violations
185
+
186
+
187
+ def check_view_function_naming(tree: ast.AST, filename: str) -> List[Violation]:
188
+ """Check that view functions end with _view suffix.
189
+
190
+ Only applies to functions in views.py that have 'request' as first parameter.
191
+
192
+ Args:
193
+ tree: AST tree to check
194
+ filename: Name of file being checked
195
+
196
+ Returns:
197
+ List of violations found
198
+ """
199
+ violations: List[Violation] = []
200
+
201
+ # Only check files named views.py
202
+ if not filename.endswith(VIEWS_FILENAME):
203
+ return violations
204
+
205
+ for node in ast.walk(tree):
206
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
207
+ # Check if first parameter is 'request'
208
+ if node.args.args and node.args.args[0].arg == REQUEST_PARAM:
209
+ # Check if function name ends with _view
210
+ if not node.name.endswith(VIEW_SUFFIX):
211
+ violations.append(
212
+ Violation(
213
+ filename,
214
+ node.lineno,
215
+ f"View function '{node.name}' must end with '{VIEW_SUFFIX}'",
216
+ )
217
+ )
218
+
219
+ return violations
220
+
221
+
222
+ def fix_empty_lines_after_decorators(source: str) -> str:
223
+ """Remove empty lines between decorators and function definitions.
224
+
225
+ Args:
226
+ source: Source code as string
227
+
228
+ Returns:
229
+ Fixed source code
230
+ """
231
+ lines = source.splitlines(keepends=True)
232
+ result_lines: List[str] = []
233
+ skip_next_blank = False
234
+
235
+ for line in lines:
236
+ stripped = line.strip()
237
+
238
+ if stripped.startswith("@"):
239
+ skip_next_blank = True
240
+ result_lines.append(line)
241
+ elif skip_next_blank and stripped == "":
242
+ continue
243
+ else:
244
+ skip_next_blank = False
245
+ result_lines.append(line)
246
+
247
+ return "".join(result_lines)
248
+
249
+
250
+ def fix_multiple_blank_lines(source: str) -> str:
251
+ """Collapse multiple blank lines between functions to single blank line.
252
+
253
+ Args:
254
+ source: Source code as string
255
+
256
+ Returns:
257
+ Fixed source code
258
+ """
259
+ lines = source.splitlines(keepends=True)
260
+ result_lines: List[str] = []
261
+ blank_count = 0
262
+
263
+ for line in lines:
264
+ if line.strip() == "":
265
+ blank_count += 1
266
+ if blank_count <= 1:
267
+ result_lines.append(line)
268
+ else:
269
+ blank_count = 0
270
+ result_lines.append(line)
271
+
272
+ return "".join(result_lines)
273
+
274
+
275
+ def fix_file(file_path: Path) -> bool:
276
+ """Apply all safe fixes to a file.
277
+
278
+ Args:
279
+ file_path: Path to file to fix
280
+
281
+ Returns:
282
+ True if any fixes were applied, False otherwise
283
+ """
284
+ try:
285
+ original = file_path.read_text(encoding="utf-8")
286
+ except Exception:
287
+ return False
288
+
289
+ fixed = original
290
+ fixed = fix_empty_lines_after_decorators(fixed)
291
+ fixed = fix_multiple_blank_lines(fixed)
292
+
293
+ if fixed != original:
294
+ file_path.write_text(fixed, encoding="utf-8")
295
+ return True
296
+
297
+ return False
298
+
299
+
300
+ def validate_file(file_path: Path) -> List[Violation]:
301
+ """Validate a Python file with all style checks.
302
+
303
+ Args:
304
+ file_path: Path to Python file to validate
305
+
306
+ Returns:
307
+ List of all violations found
308
+ """
309
+ violations: List[Violation] = []
310
+ filename = str(file_path)
311
+
312
+ try:
313
+ source = file_path.read_text(encoding="utf-8")
314
+ except Exception as e:
315
+ violations.append(Violation(filename, 0, f"Error reading file: {e}"))
316
+ return violations
317
+
318
+ try:
319
+ tree = ast.parse(source)
320
+ except SyntaxError as e:
321
+ violations.append(
322
+ Violation(filename, e.lineno or 0, f"Syntax error: {e.msg}")
323
+ )
324
+ return violations
325
+
326
+ # Run all checks
327
+ violations.extend(check_imports_at_top(tree, filename))
328
+ violations.extend(check_no_empty_line_after_decorators(source, filename))
329
+ violations.extend(check_single_empty_line_between_functions(source, filename))
330
+ violations.extend(check_view_function_naming(tree, filename))
331
+
332
+ return violations
333
+
334
+
335
+ def main() -> int:
336
+ """Main entry point for command-line usage.
337
+
338
+ Returns:
339
+ Exit code: 0 if all files pass, 1 if violations found
340
+ """
341
+ if len(sys.argv) < 2:
342
+ print("Usage: python_style_checks.py <file1.py> [file2.py ...]", file=sys.stderr)
343
+ return 1
344
+
345
+ all_violations: List[Violation] = []
346
+
347
+ for file_arg in sys.argv[1:]:
348
+ file_path = Path(file_arg)
349
+ if not file_path.exists():
350
+ print(f"Error: File not found: {file_path}", file=sys.stderr)
351
+ return 1
352
+
353
+ violations = validate_file(file_path)
354
+ all_violations.extend(violations)
355
+
356
+ # Print all violations
357
+ for violation in all_violations:
358
+ print(violation)
359
+
360
+ return 1 if all_violations else 0
361
+
362
+
363
+ if __name__ == "__main__":
364
+ sys.exit(main())
@@ -0,0 +1,90 @@
1
+ """React code quality validators.
2
+
3
+ Validates React-specific code standards:
4
+ - No class components (use functional components with hooks)
5
+ - Exception: Error boundaries (until React adds hook-based error boundaries)
6
+ """
7
+
8
+ import re
9
+ import sys
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Violation:
16
+ """Represents a validation violation."""
17
+ file: str
18
+ line: int
19
+ message: str
20
+
21
+
22
+ CLASS_COMPONENT_PATTERN = re.compile(
23
+ r'^\s*class\s+\w+\s+extends\s+(Component|React\.Component|PureComponent|React\.PureComponent)\b',
24
+ re.MULTILINE
25
+ )
26
+
27
+ CLASS_KEYWORD_PATTERN = re.compile(r'\bclass\b')
28
+
29
+ ERROR_BOUNDARY_PATTERN = re.compile(
30
+ r'\b(componentDidCatch|getDerivedStateFromError)\b'
31
+ )
32
+
33
+
34
+ def check_no_class_components(file_paths: list[str]) -> list[Violation]:
35
+ """Check that no class components exist (except error boundaries).
36
+
37
+ Args:
38
+ file_paths: List of file paths to check
39
+
40
+ Returns:
41
+ List of violations found
42
+ """
43
+ violations: list[Violation] = []
44
+
45
+ for file_path_str in file_paths:
46
+ file_path = Path(file_path_str)
47
+
48
+ if file_path.suffix not in {'.tsx', '.jsx'}:
49
+ continue
50
+
51
+ content = file_path.read_text(encoding='utf-8')
52
+
53
+ if ERROR_BOUNDARY_PATTERN.search(content):
54
+ continue
55
+
56
+ for match in CLASS_COMPONENT_PATTERN.finditer(content):
57
+ class_match = CLASS_KEYWORD_PATTERN.search(match.group(0))
58
+ if class_match:
59
+ class_position = match.start() + class_match.start()
60
+ line_num = content[:class_position].count('\n') + 1
61
+ violations.append(Violation(
62
+ file=file_path_str,
63
+ line=line_num,
64
+ message="Use functional components with hooks instead of class components"
65
+ ))
66
+
67
+ return violations
68
+
69
+
70
+ def main() -> int:
71
+ """Main entry point for command-line usage.
72
+
73
+ Returns:
74
+ Exit code: 0 if all checks pass, 1 if violations found
75
+ """
76
+ if len(sys.argv) < 2:
77
+ print("Usage: python react_checks.py <file1> [file2] ...", file=sys.stderr)
78
+ return 1
79
+
80
+ file_paths = sys.argv[1:]
81
+ violations = check_no_class_components(file_paths)
82
+
83
+ for violation in violations:
84
+ print(f"{violation.file}:{violation.line}: {violation.message}")
85
+
86
+ return 1 if violations else 0
87
+
88
+
89
+ if __name__ == '__main__':
90
+ sys.exit(main())
@@ -0,0 +1,80 @@
1
+ """Ruff integration for fast Python linting."""
2
+
3
+ import subprocess
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+
8
+ @dataclass
9
+ class RuffResult:
10
+ passed: bool
11
+ output: str
12
+ fixed_count: int
13
+
14
+
15
+ def check_ruff_available() -> bool:
16
+ """Check if ruff is installed."""
17
+ try:
18
+ result = subprocess.run(
19
+ ["ruff", "--version"],
20
+ capture_output=True,
21
+ text=True,
22
+ )
23
+ return result.returncode == 0
24
+ except FileNotFoundError:
25
+ return False
26
+
27
+
28
+ def run_ruff_check(files: list[Path]) -> RuffResult:
29
+ """Run ruff check on files."""
30
+ if not files:
31
+ return RuffResult(passed=True, output="No files to check", fixed_count=0)
32
+
33
+ if not check_ruff_available():
34
+ return RuffResult(passed=True, output="Ruff not installed - skipping", fixed_count=0)
35
+
36
+ py_files = [str(f) for f in files if f.suffix == ".py"]
37
+ if not py_files:
38
+ return RuffResult(passed=True, output="No Python files", fixed_count=0)
39
+
40
+ result = subprocess.run(
41
+ ["ruff", "check"] + py_files,
42
+ capture_output=True,
43
+ text=True,
44
+ )
45
+
46
+ return RuffResult(
47
+ passed=result.returncode == 0,
48
+ output=result.stdout or result.stderr or "No issues found",
49
+ fixed_count=0,
50
+ )
51
+
52
+
53
+ def run_ruff_fix(files: list[Path]) -> RuffResult:
54
+ """Run ruff with --fix to auto-fix violations."""
55
+ if not check_ruff_available():
56
+ return RuffResult(passed=True, output="Ruff not installed", fixed_count=0)
57
+
58
+ py_files = [str(f) for f in files if f.suffix == ".py"]
59
+ if not py_files:
60
+ return RuffResult(passed=True, output="No Python files", fixed_count=0)
61
+
62
+ result = subprocess.run(
63
+ ["ruff", "check", "--fix"] + py_files,
64
+ capture_output=True,
65
+ text=True,
66
+ )
67
+
68
+ fixed_count = 0
69
+ for line in result.stdout.split("\n"):
70
+ if "Fixed" in line:
71
+ try:
72
+ fixed_count = int(line.split()[1])
73
+ except (IndexError, ValueError):
74
+ pass
75
+
76
+ return RuffResult(
77
+ passed=result.returncode == 0,
78
+ output=result.stdout or "No fixes applied",
79
+ fixed_count=fixed_count,
80
+ )