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,266 @@
1
+ """Output formatting for validators.
2
+
3
+ Provides:
4
+ - Colored terminal output
5
+ - Contextual diff display
6
+ - Progress indicators
7
+ - JSON output for CI
8
+ """
9
+
10
+ import json
11
+ import sys
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+ from typing import Dict, List, TypedDict
15
+
16
+
17
+ class ViolationDict(TypedDict):
18
+ file: str
19
+ line: int
20
+ message: str
21
+
22
+
23
+ class ValidatorResultDict(TypedDict):
24
+ name: str
25
+ checks: str
26
+ passed: bool
27
+ output: str
28
+
29
+
30
+ class OutputMode(Enum):
31
+ TEXT = "text"
32
+ JSON = "json"
33
+ COMPACT = "compact"
34
+
35
+
36
+ def colorize(text: str, color: str, enabled: bool = True) -> str:
37
+ """Add ANSI color codes to text.
38
+
39
+ Args:
40
+ text: Text to colorize
41
+ color: Color name (red, green, yellow, blue)
42
+ enabled: Whether to apply colors
43
+
44
+ Returns:
45
+ Colorized text or original if disabled
46
+ """
47
+ if not enabled:
48
+ return text
49
+
50
+ colors = {
51
+ "red": "\033[91m",
52
+ "green": "\033[92m",
53
+ "yellow": "\033[93m",
54
+ "blue": "\033[94m",
55
+ "bold": "\033[1m",
56
+ "reset": "\033[0m",
57
+ }
58
+
59
+ color_code = colors.get(color, "")
60
+ reset = colors["reset"]
61
+
62
+ return f"{color_code}{text}{reset}"
63
+
64
+
65
+ def format_violation_with_context(
66
+ source: str,
67
+ line_num: int,
68
+ message: str,
69
+ context_lines: int = 2,
70
+ use_colors: bool = True,
71
+ ) -> str:
72
+ """Format a violation with surrounding code context.
73
+
74
+ Args:
75
+ source: Full source code
76
+ line_num: Line number of violation (1-indexed)
77
+ message: Violation message
78
+ context_lines: Number of context lines before/after
79
+ use_colors: Whether to use ANSI colors
80
+
81
+ Returns:
82
+ Formatted violation with context
83
+ """
84
+ lines = source.splitlines()
85
+ start = max(0, line_num - context_lines - 1)
86
+ end = min(len(lines), line_num + context_lines)
87
+
88
+ output_lines: List[str] = []
89
+ output_lines.append(colorize(f" {message}", "red", use_colors))
90
+ output_lines.append("")
91
+
92
+ for i in range(start, end):
93
+ line_number = i + 1
94
+ line_content = lines[i]
95
+ prefix = ">" if line_number == line_num else " "
96
+
97
+ if line_number == line_num:
98
+ formatted = colorize(
99
+ f" {prefix} {line_number:4d} | {line_content}",
100
+ "red",
101
+ use_colors,
102
+ )
103
+ else:
104
+ formatted = f" {line_number:4d} | {line_content}"
105
+
106
+ output_lines.append(formatted)
107
+
108
+ output_lines.append("")
109
+ return "\n".join(output_lines)
110
+
111
+
112
+ @dataclass(frozen=True)
113
+ class OutputFormatter:
114
+ """Formats validator output for display."""
115
+
116
+ mode: OutputMode = OutputMode.TEXT
117
+ use_colors: bool = True
118
+ show_context: bool = True
119
+ context_lines: int = 2
120
+
121
+ @property
122
+ def effective_use_colors(self) -> bool:
123
+ """Return whether colors should be used, accounting for TTY detection."""
124
+ return self.use_colors and sys.stdout.isatty()
125
+
126
+ def format_header(self, title: str) -> str:
127
+ """Format a section header."""
128
+ if self.mode == OutputMode.JSON:
129
+ return ""
130
+
131
+ separator = "=" * 60
132
+ return f"\n{separator}\n{title}\n{separator}\n"
133
+
134
+ def format_progress(self, current: int, total: int, name: str) -> str:
135
+ """Format a progress indicator."""
136
+ if self.mode == OutputMode.JSON:
137
+ return ""
138
+
139
+ bar_width = 20
140
+ filled = int(bar_width * current / total)
141
+ bar = "#" * filled + "-" * (bar_width - filled)
142
+
143
+ return f"[{bar}] {current}/{total} {name}"
144
+
145
+ def format_result(
146
+ self,
147
+ name: str,
148
+ checks: str,
149
+ passed: bool,
150
+ output: str,
151
+ ) -> str:
152
+ """Format a single validator result."""
153
+ if self.mode == OutputMode.JSON:
154
+ return ""
155
+
156
+ use_colors = self.effective_use_colors
157
+ status = colorize("[PASS]", "green", use_colors) if passed else colorize("[FAIL]", "red", use_colors)
158
+
159
+ lines = [f"{status} {name} (checks {checks})"]
160
+
161
+ if not passed and output:
162
+ for line in output.strip().split("\n"):
163
+ if line:
164
+ lines.append(f" {line}")
165
+
166
+ return "\n".join(lines)
167
+
168
+ def format_results(self, results: List[ValidatorResultDict]) -> str:
169
+ """Format all validator results."""
170
+ if self.mode == OutputMode.JSON:
171
+ return json.dumps({"results": results}, indent=2)
172
+
173
+ output_lines: List[str] = []
174
+
175
+ for result in results:
176
+ output_lines.append(
177
+ self.format_result(
178
+ name=result["name"],
179
+ checks=result.get("checks", ""),
180
+ passed=result["passed"],
181
+ output=result.get("output", ""),
182
+ )
183
+ )
184
+
185
+ return "\n".join(output_lines)
186
+
187
+ def format_summary(self, passed: int, failed: int) -> str:
188
+ """Format the final summary."""
189
+ if self.mode == OutputMode.JSON:
190
+ return ""
191
+
192
+ use_colors = self.effective_use_colors
193
+ separator = "=" * 60
194
+
195
+ if failed == 0:
196
+ verdict = colorize("READY TO PUSH", "green", use_colors)
197
+ detail = "All automated checks passed"
198
+ else:
199
+ verdict = colorize("VIOLATIONS FOUND", "red", use_colors)
200
+ detail = f"{failed} check(s) failed"
201
+
202
+ return f"\n{separator}\nVERDICT: {verdict} - {detail}\n{separator}\n"
203
+
204
+ def format_stats(
205
+ self,
206
+ files_checked: int,
207
+ violations_found: int,
208
+ time_elapsed: float,
209
+ ) -> str:
210
+ """Format statistics."""
211
+ if self.mode == OutputMode.JSON:
212
+ return ""
213
+
214
+ return (
215
+ f"\nStats: {files_checked} files checked, "
216
+ f"{violations_found} violations found, "
217
+ f"{time_elapsed:.2f}s elapsed\n"
218
+ )
219
+
220
+
221
+ def group_violations_by_file(violations: List[ViolationDict]) -> Dict[str, List[ViolationDict]]:
222
+ """Group violations by file path.
223
+
224
+ Args:
225
+ violations: List of violation dicts with 'file' key
226
+
227
+ Returns:
228
+ Dict mapping file paths to their violations
229
+ """
230
+ grouped: Dict[str, List[ViolationDict]] = {}
231
+
232
+ for violation in violations:
233
+ file_path = violation.get("file", "unknown")
234
+ if file_path not in grouped:
235
+ grouped[file_path] = []
236
+ grouped[file_path].append(violation)
237
+
238
+ return grouped
239
+
240
+
241
+ def format_grouped_violations(
242
+ grouped: Dict[str, List[ViolationDict]],
243
+ use_colors: bool = True,
244
+ ) -> str:
245
+ """Format violations grouped by file.
246
+
247
+ Args:
248
+ grouped: Dict from group_violations_by_file
249
+ use_colors: Whether to use ANSI colors
250
+
251
+ Returns:
252
+ Formatted string with file headers
253
+ """
254
+ output_lines: List[str] = []
255
+
256
+ for file_path, violations in sorted(grouped.items()):
257
+ output_lines.append("")
258
+ output_lines.append(colorize(f" {file_path}", "bold", use_colors))
259
+ output_lines.append(" " + "-" * (len(file_path) + 2))
260
+
261
+ for violation in sorted(violations, key=lambda v: v.get("line", 0)):
262
+ line = violation.get("line", "?")
263
+ message = violation.get("message", "Unknown violation")
264
+ output_lines.append(f" Line {line}: {message}")
265
+
266
+ return "\n".join(output_lines)
@@ -0,0 +1,72 @@
1
+ """PR and commit reference detection validator.
2
+
3
+ Implements check 6: No PR/commit references in comments.
4
+ Code comments must be timeless - no historical context.
5
+ """
6
+
7
+ import re
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import List
11
+
12
+ from validator_base import Violation
13
+
14
+
15
+ PR_PATTERN = re.compile(r"#.*\bPR\s*#?\d+", re.IGNORECASE)
16
+ COMMIT_PATTERN = re.compile(r"#.*\bcommit\b", re.IGNORECASE)
17
+ ISSUE_PATTERN = re.compile(r"#.*(?:addresses|fixes|closes|resolves)\s*#\d+", re.IGNORECASE)
18
+ HASH_REF_PATTERN = re.compile(r"#\s*#\d+")
19
+
20
+
21
+ def check_pr_references(source: str, filename: str) -> List[Violation]:
22
+ violations: List[Violation] = []
23
+ lines = source.splitlines()
24
+
25
+ for line_num, line in enumerate(lines, start=1):
26
+ if PR_PATTERN.search(line):
27
+ violations.append(
28
+ Violation(filename, line_num, "PR reference in comment - comments should be timeless")
29
+ )
30
+ elif COMMIT_PATTERN.search(line):
31
+ violations.append(
32
+ Violation(filename, line_num, "Commit reference in comment - comments should be timeless")
33
+ )
34
+ elif ISSUE_PATTERN.search(line) or HASH_REF_PATTERN.search(line):
35
+ violations.append(
36
+ Violation(filename, line_num, "Issue reference in comment - comments should be timeless")
37
+ )
38
+
39
+ return violations
40
+
41
+
42
+ def validate_file(file_path: Path) -> List[Violation]:
43
+ filename = str(file_path)
44
+ try:
45
+ source = file_path.read_text(encoding="utf-8")
46
+ except Exception as error:
47
+ return [Violation(filename, 0, f"Error reading file: {error}")]
48
+
49
+ return check_pr_references(source, filename)
50
+
51
+
52
+ def main() -> int:
53
+ if len(sys.argv) < 2:
54
+ print("Usage: pr_reference_checks.py <file1> [file2 ...]", file=sys.stderr)
55
+ return 1
56
+
57
+ all_violations: List[Violation] = []
58
+ for file_arg in sys.argv[1:]:
59
+ file_path = Path(file_arg)
60
+ if not file_path.exists():
61
+ print(f"Error: File not found: {file_path}", file=sys.stderr)
62
+ return 1
63
+ all_violations.extend(validate_file(file_path))
64
+
65
+ for violation in all_violations:
66
+ print(violation)
67
+
68
+ return 1 if all_violations else 0
69
+
70
+
71
+ if __name__ == "__main__":
72
+ sys.exit(main())
@@ -0,0 +1,110 @@
1
+ """Python anti-pattern detection validator.
2
+
3
+ Implements:
4
+ - Check 33: Mutable default arguments
5
+ - Check 34: Bare except clauses
6
+ - Check 35: Print in production code
7
+ """
8
+
9
+ import ast
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import List
13
+
14
+ from validator_base import Violation
15
+
16
+
17
+ def check_mutable_default_args(tree: ast.AST, filename: str) -> List[Violation]:
18
+ violations: List[Violation] = []
19
+
20
+ for node in ast.walk(tree):
21
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
22
+ for default in node.args.defaults:
23
+ if isinstance(default, (ast.List, ast.Dict, ast.Set)):
24
+ violations.append(
25
+ Violation(
26
+ filename,
27
+ node.lineno,
28
+ f"Mutable default argument in '{node.name}' - use None and initialize inside",
29
+ )
30
+ )
31
+
32
+ return violations
33
+
34
+
35
+ def check_bare_except(tree: ast.AST, filename: str) -> List[Violation]:
36
+ violations: List[Violation] = []
37
+
38
+ for node in ast.walk(tree):
39
+ if isinstance(node, ast.ExceptHandler):
40
+ if node.type is None:
41
+ violations.append(
42
+ Violation(
43
+ filename,
44
+ node.lineno,
45
+ "Bare except clause - specify exception type",
46
+ )
47
+ )
48
+
49
+ return violations
50
+
51
+
52
+ def check_print_in_production(tree: ast.AST, filename: str) -> List[Violation]:
53
+ violations: List[Violation] = []
54
+
55
+ if "test" in filename.lower():
56
+ return violations
57
+
58
+ for node in ast.walk(tree):
59
+ if isinstance(node, ast.Call):
60
+ if isinstance(node.func, ast.Name) and node.func.id == "print":
61
+ violations.append(
62
+ Violation(
63
+ filename,
64
+ node.lineno,
65
+ "print() in production code - use logging instead",
66
+ )
67
+ )
68
+
69
+ return violations
70
+
71
+
72
+ def validate_file(file_path: Path) -> List[Violation]:
73
+ violations: List[Violation] = []
74
+ filename = str(file_path)
75
+
76
+ try:
77
+ source = file_path.read_text(encoding="utf-8")
78
+ tree = ast.parse(source)
79
+ except Exception as error:
80
+ return [Violation(filename, 0, f"Error: {error}")]
81
+
82
+ violations.extend(check_mutable_default_args(tree, filename))
83
+ violations.extend(check_bare_except(tree, filename))
84
+ violations.extend(check_print_in_production(tree, filename))
85
+
86
+ return violations
87
+
88
+
89
+ def main() -> int:
90
+ if len(sys.argv) < 2:
91
+ print("Usage: python_antipattern_checks.py <file1.py> [file2.py ...]", file=sys.stderr)
92
+ return 1
93
+
94
+ all_violations: List[Violation] = []
95
+
96
+ for file_arg in sys.argv[1:]:
97
+ file_path = Path(file_arg)
98
+ if not file_path.exists():
99
+ print(f"Error: File not found: {file_path}", file=sys.stderr)
100
+ return 1
101
+ all_violations.extend(validate_file(file_path))
102
+
103
+ for violation in all_violations:
104
+ print(violation)
105
+
106
+ return 1 if all_violations else 0
107
+
108
+
109
+ if __name__ == "__main__":
110
+ sys.exit(main())