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,80 @@
1
+ # Django Patterns Reference
2
+
3
+ > Load this file when working with Django models, views, templates, or migrations.
4
+
5
+ ## Model Patterns
6
+
7
+ **NEVER:**
8
+
9
+ - **Create separate user models** - Extend Django's User with OneToOneField
10
+ - Example: `UserProfile(user=OneToOneField(User, related_name='profile'))`
11
+ - See: https://docs.djangoproject.com/en/5.2/topics/auth/customizing/#extending-the-existing-user-model
12
+
13
+ - **Use confusing model/field names** - Match semantics & avoid conflicts
14
+ - "Settings" = user preferences (preferences, settings, display options), "UserProfile" = user metadata
15
+ - `custom_name` avoids conflict with User.username
16
+
17
+ - **Break model fields across multiple lines** - Match the style of other fields in the model
18
+ - Bad: Multi-line ForeignKey definition
19
+ - Good: `evolves_from = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='evolutions')`
20
+ - Consistency within the model class matters more than arbitrary line length rules
21
+
22
+ - **Circumvent Django ORM for database operations** - Always use Django models for DB access
23
+ - Django ORM handles migrations, relationships, and abstractions under the hood
24
+ - Bypassing it creates inconsistencies with migrations and model state
25
+ - For DB operations: Use Django models, views, management commands
26
+ - For non-DB tools: Use pure Python scripts (file processing, API calls, asset uploads)
27
+
28
+ ## Template Patterns
29
+
30
+ **NEVER:**
31
+
32
+ - **Put view-specific template code in base.html** - Only in the template that uses it
33
+ - Example: `{% if layout %}` in base.html when only home view defines layout
34
+ - If variable is always present in specific template, no conditional needed
35
+
36
+ ## Production Migrations (Post-Launch ONLY)
37
+
38
+ **CRITICAL:** After launch, model changes MUST be backwards-compatible.
39
+
40
+ **Why:** Manual migration takes 2+ minutes. New code runs against old schema during this window.
41
+
42
+ ### Safe vs Unsafe Changes
43
+
44
+ | Safe | Unsafe |
45
+ |------|--------|
46
+ | Nullable fields | Removing fields |
47
+ | Fields with defaults | Renaming fields |
48
+ | New models | Non-null without defaults |
49
+ | | Type changes |
50
+
51
+ ### Migration Strategy
52
+
53
+ 1. Deploy code working with both schemas
54
+ 2. Run migration (2+ min)
55
+ 3. Verify production
56
+ 4. Deploy cleanup if needed
57
+
58
+ *During development: manual migration is fine, no big deal.*
59
+
60
+ ## API Contract Changes (Post-Launch)
61
+
62
+ **CRITICAL:** After launch, API contract changes MUST be backwards-compatible.
63
+
64
+ **Why:** Users may have old client code cached. Old clients must work with new API.
65
+
66
+ ### Safe vs Unsafe Changes
67
+
68
+ | Safe | Unsafe |
69
+ |------|--------|
70
+ | New optional fields | Removing fields |
71
+ | New endpoints | Renaming fields |
72
+ | Additive changes | Changing field types |
73
+ | | Removing endpoints |
74
+
75
+ ### API Evolution Strategy
76
+
77
+ 1. Add new fields/endpoints alongside old ones
78
+ 2. Deploy frontend that handles both old and new
79
+ 3. Deprecate old fields (but keep working)
80
+ 4. Remove old fields only after all clients updated
@@ -0,0 +1,185 @@
1
+ # React Patterns Reference
2
+
3
+ > Load this file when working with React components, state management, hooks, or testing.
4
+
5
+ ## Component Patterns
6
+
7
+ **NEVER:**
8
+
9
+ - **Create wrapper components for single use** - Use the component directly
10
+ - Example: `<ButtonWrapper><Button /></ButtonWrapper>` when Button is only used once
11
+ - Ask: "Is this wrapper adding any value?"
12
+
13
+ - **Put page-specific logic in shared components** - Keep shared components pure
14
+ - Example: `if (pathname === '/home')` inside a shared Header component
15
+ - Shared components should be context-agnostic
16
+
17
+ - **Use class components for new code** - Always use functional components with hooks
18
+ - Exception: Error boundaries still require class components (React limitation)
19
+
20
+ - **Create HOCs when hooks work** - Prefer custom hooks over HOCs
21
+ - Example: `withAuth(Component)` -> `useAuth()` hook
22
+ - HOCs add wrapper layers; hooks are composable
23
+
24
+ - **Prop drill more than 2 levels** - Use Context or state management
25
+ - Example: `<Parent data={x}><Child data={x}><GrandChild data={x} /></Child></Parent>`
26
+ - Solution: Create context or lift state appropriately
27
+
28
+ ## State Management Patterns
29
+
30
+ **NEVER:**
31
+
32
+ - **Store derived state** - Calculate on render
33
+ - Bad: `const [fullName, setFullName] = useState(first + ' ' + last)`
34
+ - Good: `const fullName = first + ' ' + last`
35
+
36
+ - **Mutate state directly** - Always create new references
37
+ - Bad: `items.push(newItem); setItems(items)`
38
+ - Good: `setItems([...items, newItem])`
39
+
40
+ - **Use useState for complex state logic** - Use useReducer
41
+ - When: Multiple sub-values, next state depends on previous, complex update logic
42
+ - Example: Form with many fields, shopping cart, multi-step wizard
43
+
44
+ - **Create global state for local concerns** - Colocate state
45
+ - If only one component uses the state, keep it in that component
46
+ - Lift state only when siblings need to share
47
+
48
+ - **Forget to memoize expensive calculations** - Use useMemo
49
+ - Bad: `const sorted = items.sort((a, b) => ...)` on every render
50
+ - Good: `const sorted = useMemo(() => items.sort(...), [items])`
51
+
52
+ ## Hooks Patterns
53
+
54
+ **NEVER:**
55
+
56
+ - **Call hooks conditionally** - Hooks must be at top level
57
+ - Bad: `if (condition) { const [x, setX] = useState() }`
58
+ - Good: Always call hooks, use condition inside
59
+
60
+ - **Create effects without cleanup** - Return cleanup function when needed
61
+ - Subscriptions, timers, event listeners MUST be cleaned up
62
+ - Bad: `useEffect(() => { window.addEventListener(...) }, [])`
63
+ - Good: `useEffect(() => { window.addEventListener(...); return () => window.removeEventListener(...) }, [])`
64
+
65
+ - **Use empty dependency arrays incorrectly** - Include all dependencies
66
+ - Bad: `useEffect(() => { fetchData(userId) }, [])` (missing userId)
67
+ - Good: `useEffect(() => { fetchData(userId) }, [userId])`
68
+ - Use ESLint exhaustive-deps rule
69
+
70
+ - **Create new functions in render without useCallback** - Memoize callbacks passed to children
71
+ - Bad: `<Child onClick={() => handleClick(id)} />`
72
+ - Good: `const handleChildClick = useCallback(() => handleClick(id), [id])`
73
+
74
+ - **Overuse useMemo/useCallback** - Only optimize when needed
75
+ - Memoization has cost; don't wrap everything
76
+ - Profile first, then optimize specific bottlenecks
77
+
78
+ ## Component Organization
79
+
80
+ **File structure:**
81
+ ```
82
+ components/
83
+ ComponentName/
84
+ ComponentName.tsx # Component logic
85
+ ComponentName.test.tsx # Tests (colocated)
86
+ index.ts # Public export
87
+ ```
88
+
89
+ **NEVER:**
90
+
91
+ - **Mix business logic with UI** - Separate concerns
92
+ - Custom hooks for business logic: `useTaskManager()`
93
+ - Components for UI: `<TaskList tasks={tasks} />`
94
+
95
+ - **Create god components** - Split into focused components
96
+ - If component > 200 lines, likely doing too much
97
+ - Extract sub-components or custom hooks
98
+
99
+ - **Export internal components** - Only export what's needed
100
+ - Internal helpers stay private to the component folder
101
+ - Only `index.ts` defines the public API
102
+
103
+ ## Code Standards
104
+
105
+ - **Complete type annotations** - All props, state, and return types defined
106
+ - **No `any` types** - Use generics, union types, or `unknown` with type guards
107
+ - **No `@ts-ignore` or `@ts-expect-error`** without clear justification
108
+ - **Functional components only** - No class components (except error boundaries)
109
+ - **Custom hooks for logic** - Separate business logic from UI
110
+ - **Props interfaces** - Define explicit Props type for every component
111
+ - **Immutable updates** - Never mutate state or props
112
+ - **Small components** - Target 50-100 lines, max 200 lines
113
+ - **Max 2 nesting levels** - Extract sub-components
114
+ - **No inline styles** - Use CSS modules, styled-components, or Tailwind
115
+
116
+ ## Testing Patterns
117
+
118
+ - **Use React Testing Library** - Not Enzyme
119
+ - **Query by accessibility** - Role, label, text (not test-id as first choice)
120
+ - **Test user flows** - Render, interact, assert on visible changes
121
+ - **Mock API boundaries** - Not internal functions
122
+ - **Colocate tests** - `Component.test.tsx` next to `Component.tsx`
123
+
124
+ **Testing anti-patterns:**
125
+
126
+ - **Snapshot test everything** - Only for stable, visual components
127
+ - **Mock too much** - Prefer integration tests
128
+ - **Test implementation details** - Test what user experiences
129
+ - **Forget async handling** - Use waitFor, findBy queries
130
+
131
+ ## Example: God Component Refactoring
132
+
133
+ **Bad:**
134
+ ```tsx
135
+ function TaskDashboard() {
136
+ const [tasks, setTasks] = useState([]);
137
+ const [filter, setFilter] = useState('all');
138
+ const [sortBy, setSortBy] = useState('date');
139
+ // ... 200 more lines of logic
140
+ return (
141
+ <div>{/* 150 lines of JSX */}</div>
142
+ );
143
+ }
144
+ ```
145
+
146
+ **Good:**
147
+ ```tsx
148
+ function TaskDashboard() {
149
+ const { tasks, filter, sortBy, actions } = useTaskManager();
150
+ return (
151
+ <div>
152
+ <TaskFilters filter={filter} onFilterChange={actions.setFilter} />
153
+ <TaskList tasks={tasks} sortBy={sortBy} />
154
+ </div>
155
+ );
156
+ }
157
+
158
+ function useTaskManager() {
159
+ const [tasks, setTasks] = useState<Task[]>([]);
160
+ const [filter, setFilter] = useState<Filter>('all');
161
+ // ... focused logic
162
+ return { tasks, filter, sortBy, actions };
163
+ }
164
+ ```
165
+
166
+ ## Example: Derived State
167
+
168
+ **Bad:**
169
+ ```tsx
170
+ const [items, setItems] = useState<Item[]>([]);
171
+ const [filteredItems, setFilteredItems] = useState<Item[]>([]);
172
+
173
+ useEffect(() => {
174
+ setFilteredItems(items.filter(i => i.active));
175
+ }, [items]);
176
+ ```
177
+
178
+ **Good:**
179
+ ```tsx
180
+ const [items, setItems] = useState<Item[]>([]);
181
+ const filteredItems = useMemo(
182
+ () => items.filter(i => i.active),
183
+ [items]
184
+ );
185
+ ```
@@ -0,0 +1,104 @@
1
+ # Test Quality Reference
2
+
3
+ > Load this file when writing tests, reviewing test code, or setting up test infrastructure.
4
+
5
+ ## Test Infrastructure Anti-Patterns
6
+
7
+ **CRITICAL: Test helpers get over-engineered MORE than any other code.**
8
+
9
+ **ALWAYS:**
10
+ - Single file
11
+ - Simple functions
12
+ - Minimal abstractions
13
+ - Pragmatic approach
14
+
15
+ **NEVER:**
16
+ - Multi-file packages
17
+ - Cache classes
18
+ - Abstractions "for future"
19
+ - Rigid constraints
20
+
21
+ **Pre-check (MANDATORY):**
22
+ - [ ] ONE file?
23
+ - [ ] Functions not classes?
24
+ - [ ] Solving problem not building infrastructure?
25
+ - [ ] Junior-dev-understandable?
26
+
27
+ ## Delete Useless Tests
28
+
29
+ **Tests must add value.** Delete these types of tests:
30
+
31
+ ### NEVER test:
32
+
33
+ **Function existence** - "Testing that the function exists doesn't add value. If the function doesn't exist, the code will not run."
34
+ - Bad: `test_public_api_exports_download_function()` only verified `callable(func)`
35
+ - Action: DELETE
36
+
37
+ **Constant values** - "It's silly to test that constant values have not changed."
38
+ - Bad: `assert CACHE_DIR == "cache"`
39
+ - Action: DELETE
40
+
41
+ **Duplicate coverage** - "Isn't this test case the same as test_X?"
42
+ - Action: DELETE redundant tests
43
+
44
+ ## Test Dependencies MUST FAIL
45
+
46
+ "The tests should fail if they can't run. Missing system dependencies should make the test fail."
47
+
48
+ - **NEVER** use `@skip_if_missing_dependency` or similar skip decorators
49
+ - Tests FAIL with clear error -> forces installation
50
+
51
+ ## Core Testing Principles
52
+
53
+ - **Behavior-driven** - Verify expected behavior, not implementation
54
+ - **Test through public API** - Never examine internals
55
+ - **100% coverage via business behavior** - Not implementation coverage
56
+ - **No 1:1 mapping** - Test files test behavior, not individual implementation files
57
+ - **Tests document behavior** - Tests are the spec
58
+
59
+ ## React Testing Patterns
60
+
61
+ ### ALWAYS:
62
+
63
+ **Test behavior, not implementation** - What user sees/does, not internal state
64
+ - Bad: `expect(component.state.isOpen).toBe(true)`
65
+ - Good: `expect(screen.getByRole('dialog')).toBeVisible()`
66
+
67
+ **Use Testing Library queries correctly** - Priority order:
68
+ 1. `getByRole` (best)
69
+ 2. `getByLabelText`
70
+ 3. `getByText`
71
+ 4. `getByTestId` (last resort)
72
+
73
+ - Bad: `getByTestId('submit-button')` as first choice
74
+ - Good: `getByRole('button', { name: /submit/i })`
75
+
76
+ **Test user interactions**
77
+ - Use `userEvent` over `fireEvent` (more realistic)
78
+ - `await userEvent.click(button)` then assert result
79
+
80
+ ### NEVER:
81
+
82
+ **Snapshot test everything** - Only for stable, visual components
83
+ - Snapshots break on any change, creating noise
84
+ - Reserve for design system components, icons
85
+
86
+ **Mock too much** - Prefer integration tests
87
+ - Bad: Mock every hook and function
88
+ - Good: Render with real hooks, mock only API calls
89
+
90
+ **Test implementation details** - Test what user experiences
91
+ - Bad: Assert on state values, hook return values
92
+ - Good: Assert on rendered output, user-visible behavior
93
+
94
+ **Forget async handling** - Use waitFor, findBy queries
95
+ - Bad: Expect immediately after async action
96
+ - Good: `await waitFor(() => expect(...))` or `await screen.findByText(...)`
97
+
98
+ ## Test File Organization
99
+
100
+ - **Use React Testing Library** - Not Enzyme
101
+ - **Query by accessibility** - Role, label, text (not test-id as first choice)
102
+ - **Test user flows** - Render, interact, assert on visible changes
103
+ - **Mock API boundaries** - Not internal functions
104
+ - **Colocate tests** - `Component.test.tsx` next to `Component.tsx`
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ """Advisory hook: warn when Django migrations contain unsafe operations."""
3
+
4
+ import json
5
+ import re
6
+ import sys
7
+
8
+ MIGRATION_PATH_PATTERN = re.compile(r"[/\\]migrations[/\\]\d{4}_\w+\.py$")
9
+ UNSAFE_OPERATIONS = ["RemoveField", "RenameField", "DeleteModel", "RenameModel"]
10
+
11
+
12
+ def main() -> None:
13
+ try:
14
+ hook_input = json.load(sys.stdin)
15
+ except json.JSONDecodeError:
16
+ sys.exit(0)
17
+
18
+ tool_input = hook_input.get("tool_input", {})
19
+ file_path = tool_input.get("file_path", "")
20
+
21
+ if not MIGRATION_PATH_PATTERN.search(file_path):
22
+ sys.exit(0)
23
+
24
+ content = tool_input.get("content", "") or tool_input.get("new_string", "")
25
+ found_unsafe = [op for op in UNSAFE_OPERATIONS if op in content]
26
+
27
+ if found_unsafe:
28
+ operations = ", ".join(found_unsafe)
29
+ print(
30
+ json.dumps(
31
+ {
32
+ "hookSpecificOutput": {
33
+ "hookEventName": "PreToolUse",
34
+ "permissionDecision": "ask",
35
+ "permissionDecisionReason": (
36
+ f"MIGRATION SAFETY: Contains {operations}. "
37
+ "Post-launch, model changes MUST be backwards-compatible. "
38
+ "Verify this won't break running instances during deployment."
39
+ ),
40
+ }
41
+ }
42
+ )
43
+ )
44
+
45
+ sys.exit(0)
46
+
47
+
48
+ if __name__ == "__main__":
49
+ main()
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Refactor guard - blocks edits that rename/restructure existing code not in the git diff.
4
+
5
+ Detects when an Edit tool call is modifying existing code (renaming variables,
6
+ functions, restructuring) rather than writing new code or replacing wholesale.
7
+
8
+ Only fires for Edit operations (not Write, which creates/replaces entire files).
9
+ """
10
+ import json
11
+ import os
12
+ import re
13
+ import subprocess
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ REFACTOR_BYPASS_TOKEN_PATH = Path.home() / ".claude" / ".refactor-bypass-token"
19
+
20
+
21
+ def get_git_diff_added_lines(file_path: str) -> set[str]:
22
+ """Get the set of added lines (stripped) from git diff for a file."""
23
+ added_lines: set[str] = set()
24
+ try:
25
+ result = subprocess.run(
26
+ ["git", "diff", "HEAD", "--", file_path],
27
+ capture_output=True,
28
+ text=True,
29
+ timeout=5,
30
+ )
31
+ for line in result.stdout.split("\n"):
32
+ if line.startswith("+") and not line.startswith("+++"):
33
+ added_lines.add(line[1:].strip())
34
+
35
+ staged_result = subprocess.run(
36
+ ["git", "diff", "--staged", "--", file_path],
37
+ capture_output=True,
38
+ text=True,
39
+ timeout=5,
40
+ )
41
+ for line in staged_result.stdout.split("\n"):
42
+ if line.startswith("+") and not line.startswith("+++"):
43
+ added_lines.add(line[1:].strip())
44
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
45
+ return set()
46
+ return added_lines
47
+
48
+
49
+ def is_new_file(file_path: str) -> bool:
50
+ """Check if file is untracked (entirely new)."""
51
+ try:
52
+ result = subprocess.run(
53
+ ["git", "ls-files", "--others", "--exclude-standard", "--", file_path],
54
+ capture_output=True,
55
+ text=True,
56
+ timeout=5,
57
+ )
58
+ return bool(result.stdout.strip())
59
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
60
+ return False
61
+
62
+
63
+ def is_hook_infrastructure(file_path: str) -> bool:
64
+ """Check if file is a Claude Code hook."""
65
+ path_lower = file_path.lower().replace("\\", "/")
66
+ return "/.claude/" in path_lower
67
+
68
+
69
+ def extract_identifiers(code: str) -> set[str]:
70
+ """Extract meaningful identifiers (variable/function/class names) from code."""
71
+ identifier_pattern = re.compile(r'\b([a-zA-Z_][a-zA-Z0-9_]{2,})\b')
72
+ identifiers = set(identifier_pattern.findall(code))
73
+ python_keywords = {
74
+ "def", "class", "return", "import", "from", "if", "elif", "else",
75
+ "for", "while", "try", "except", "finally", "with", "as", "yield",
76
+ "raise", "pass", "break", "continue", "and", "or", "not", "in",
77
+ "is", "lambda", "None", "True", "False", "self", "cls", "async",
78
+ "await", "global", "nonlocal", "assert", "del", "print", "len",
79
+ "range", "list", "dict", "set", "str", "int", "float", "bool",
80
+ "type", "isinstance", "hasattr", "getattr", "setattr", "super",
81
+ "property", "staticmethod", "classmethod", "abstractmethod",
82
+ "Optional", "Union", "List", "Dict", "Set", "Tuple", "Any",
83
+ }
84
+ return identifiers - python_keywords
85
+
86
+
87
+ def is_refactor_edit(old_string: str, new_string: str) -> Optional[str]:
88
+ """Detect if an edit is a rename/refactor rather than a functional change.
89
+
90
+ Returns a description of the refactor if detected, None otherwise.
91
+ """
92
+ old_lines = [line.strip() for line in old_string.strip().split("\n") if line.strip()]
93
+ new_lines = [line.strip() for line in new_string.strip().split("\n") if line.strip()]
94
+
95
+ if not old_lines or not new_lines:
96
+ return None
97
+
98
+ if abs(len(old_lines) - len(new_lines)) > max(len(old_lines) // 2, 3):
99
+ return None
100
+
101
+ old_identifiers = extract_identifiers(old_string)
102
+ new_identifiers = extract_identifiers(new_string)
103
+
104
+ removed_identifiers = old_identifiers - new_identifiers
105
+ added_identifiers = new_identifiers - old_identifiers
106
+
107
+ if not removed_identifiers or not added_identifiers:
108
+ return None
109
+
110
+ old_no_ids = old_string
111
+ new_no_ids = new_string
112
+ for identifier in old_identifiers | new_identifiers:
113
+ old_no_ids = old_no_ids.replace(identifier, "ID")
114
+ new_no_ids = new_no_ids.replace(identifier, "ID")
115
+
116
+ old_structure = re.sub(r'\s+', ' ', old_no_ids.strip())
117
+ new_structure = re.sub(r'\s+', ' ', new_no_ids.strip())
118
+
119
+ if old_structure == new_structure:
120
+ renamed = []
121
+ for old_id in sorted(removed_identifiers):
122
+ for new_id in sorted(added_identifiers):
123
+ if old_id.lower().replace("_", "") == new_id.lower().replace("_", ""):
124
+ renamed.append(f"{old_id} -> {new_id}")
125
+ break
126
+ old_words = set(re.findall(r'[a-z]+|[A-Z][a-z]*', old_id))
127
+ new_words = set(re.findall(r'[a-z]+|[A-Z][a-z]*', new_id))
128
+ if old_words and new_words and len(old_words & new_words) >= len(old_words) * 0.5:
129
+ renamed.append(f"{old_id} -> {new_id}")
130
+ break
131
+
132
+ if renamed:
133
+ return f"Renaming detected: {', '.join(renamed[:3])}"
134
+
135
+ if len(removed_identifiers) >= 2 and len(added_identifiers) >= 2:
136
+ return f"Multiple identifiers changed with same structure: removed {sorted(removed_identifiers)[:3]}, added {sorted(added_identifiers)[:3]}"
137
+
138
+ return None
139
+
140
+
141
+ def is_bypass_approved() -> bool:
142
+ """Check if user explicitly approved a refactor bypass via one-time token file.
143
+
144
+ The token file is deleted after a single use, so each refactor
145
+ requires fresh explicit approval from the user.
146
+ """
147
+ if REFACTOR_BYPASS_TOKEN_PATH.exists():
148
+ REFACTOR_BYPASS_TOKEN_PATH.unlink()
149
+ return True
150
+ return False
151
+
152
+
153
+ def main() -> None:
154
+ try:
155
+ input_data = json.load(sys.stdin)
156
+ except json.JSONDecodeError:
157
+ sys.exit(0)
158
+
159
+ tool_name = input_data.get("tool_name", "")
160
+ if tool_name != "Edit":
161
+ sys.exit(0)
162
+
163
+ if is_bypass_approved():
164
+ sys.exit(0)
165
+
166
+ tool_input = input_data.get("tool_input", {})
167
+ file_path = tool_input.get("file_path", "")
168
+ old_string = tool_input.get("old_string", "")
169
+ new_string = tool_input.get("new_string", "")
170
+
171
+ if not file_path or not old_string or not new_string:
172
+ sys.exit(0)
173
+
174
+ if is_hook_infrastructure(file_path):
175
+ sys.exit(0)
176
+
177
+ if is_new_file(file_path):
178
+ sys.exit(0)
179
+
180
+ refactor_description = is_refactor_edit(old_string, new_string)
181
+ if not refactor_description:
182
+ sys.exit(0)
183
+
184
+ diff_added_lines = get_git_diff_added_lines(file_path)
185
+
186
+ old_lines_stripped = {line.strip() for line in old_string.split("\n") if line.strip()}
187
+ old_lines_in_diff = old_lines_stripped & diff_added_lines
188
+
189
+ if old_lines_in_diff and len(old_lines_in_diff) >= len(old_lines_stripped) * 0.5:
190
+ sys.exit(0)
191
+
192
+ result = {
193
+ "hookSpecificOutput": {
194
+ "hookEventName": "PreToolUse",
195
+ "permissionDecision": "allow",
196
+ "additionalContext": f"[HOOK ADVISORY] Refactor guard — {refactor_description} in {file_path}. Only modify lines already changed in the current git diff. Ask the user for explicit approval first. If the user approves, create the bypass token then retry.",
197
+ }
198
+ }
199
+ print(json.dumps(result))
200
+ sys.stdout.flush()
201
+ sys.exit(0)
202
+
203
+
204
+ if __name__ == "__main__":
205
+ main()