@caddis/cli 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -1
- package/bundles/antigravity-plugin/agents/code-reviewer.md +47 -0
- package/bundles/antigravity-plugin/agents/preflight.md +53 -0
- package/bundles/antigravity-plugin/guard_agy.py +338 -0
- package/bundles/antigravity-plugin/hooks.json +36 -0
- package/bundles/antigravity-plugin/mcp_config.json +8 -0
- package/bundles/antigravity-plugin/mcp_ping_server.py +55 -0
- package/bundles/antigravity-plugin/plugin.json +5 -0
- package/bundles/antigravity-plugin/session_end_agy.py +57 -0
- package/bundles/antigravity-plugin/skills/_registry.md +115 -0
- package/bundles/antigravity-plugin/skills/add-rules/SKILL.md +45 -0
- package/bundles/antigravity-plugin/skills/api-design/SKILL.md +249 -0
- package/bundles/antigravity-plugin/skills/backend-development/SKILL.md +305 -0
- package/bundles/antigravity-plugin/skills/best-practices/SKILL.md +500 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +263 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +326 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +245 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/anti-patterns.md +571 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/before-after-examples.md +1114 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/best-practices-guide.md +513 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/common-workflows.md +692 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/prompt-patterns.md +547 -0
- package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +57 -0
- package/bundles/antigravity-plugin/skills/ci-cd-pipeline/SKILL.md +315 -0
- package/bundles/antigravity-plugin/skills/code-documentation/SKILL.md +271 -0
- package/bundles/antigravity-plugin/skills/code-review/SKILL.md +122 -0
- package/bundles/antigravity-plugin/skills/codebase-audit/SKILL.md +204 -0
- package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +157 -0
- package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +40 -0
- package/bundles/antigravity-plugin/skills/css-architecture/SKILL.md +305 -0
- package/bundles/antigravity-plugin/skills/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
- package/bundles/antigravity-plugin/skills/database-design/SKILL.md +177 -0
- package/bundles/antigravity-plugin/skills/db-diagram/SKILL.md +148 -0
- package/bundles/antigravity-plugin/skills/db-diagram/scripts/sql_to_graph.py +1212 -0
- package/bundles/antigravity-plugin/skills/digress/SKILL.md +61 -0
- package/bundles/antigravity-plugin/skills/draw-io/SKILL.md +162 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/aws-icons.md +677 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/layout-guidelines.md +142 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/troubleshooting.md +118 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/workflows.md +103 -0
- package/bundles/antigravity-plugin/skills/draw-io/scripts/convert-drawio-to-png.sh +25 -0
- package/bundles/antigravity-plugin/skills/draw-io/scripts/find_aws_icon.py +79 -0
- package/bundles/antigravity-plugin/skills/error-handling/SKILL.md +260 -0
- package/bundles/antigravity-plugin/skills/excalidraw-db/SKILL.md +38 -0
- package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +300 -0
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +198 -0
- package/bundles/antigravity-plugin/skills/frontend-design/SKILL.md +193 -0
- package/bundles/antigravity-plugin/skills/frontend-design/references/my-tech-stack.md +127 -0
- package/bundles/antigravity-plugin/skills/gh-cli/SKILL.md +195 -0
- package/bundles/antigravity-plugin/skills/git-commit/SKILL.md +285 -0
- package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +577 -0
- package/bundles/antigravity-plugin/skills/handoff/SKILL.md +100 -0
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +114 -0
- package/bundles/antigravity-plugin/skills/javascript-typescript/SKILL.md +142 -0
- package/bundles/antigravity-plugin/skills/kb/SKILL.md +60 -0
- package/bundles/antigravity-plugin/skills/mermaid-db/SKILL.md +33 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +236 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/advanced-features.md +556 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/architecture-diagrams.md +192 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/c4-diagrams.md +410 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/class-diagrams.md +361 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/erd-diagrams.md +510 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/flowcharts.md +450 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/sequence-diagrams.md +394 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/troubleshooting.md +335 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/workflows.md +418 -0
- package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +68 -0
- package/bundles/antigravity-plugin/skills/mockup/SKILL.md +242 -0
- package/bundles/antigravity-plugin/skills/particle-art/SKILL.md +243 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/canvas-utils.ts +171 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/dot-field.template.tsx +203 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/flow-field.template.tsx +263 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/node-shape.template.tsx +261 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/shape-sampler.ts +281 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/stipple-morph.template.tsx +167 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/stipple.template.tsx +175 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/trail-ghost.template.tsx +266 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/usage-examples.md +320 -0
- package/bundles/antigravity-plugin/skills/playwright/API_REFERENCE.md +653 -0
- package/bundles/antigravity-plugin/skills/playwright/SKILL.md +454 -0
- package/bundles/antigravity-plugin/skills/playwright/lib/helpers.js +441 -0
- package/bundles/antigravity-plugin/skills/playwright/package.json +26 -0
- package/bundles/antigravity-plugin/skills/playwright/run.js +228 -0
- package/bundles/antigravity-plugin/skills/prd/SKILL.md +107 -0
- package/bundles/antigravity-plugin/skills/preflight/SKILL.md +435 -0
- package/bundles/antigravity-plugin/skills/python/SKILL.md +388 -0
- package/bundles/antigravity-plugin/skills/react-best-practices/SKILL.md +269 -0
- package/bundles/antigravity-plugin/skills/react-dev/README.md +404 -0
- package/bundles/antigravity-plugin/skills/react-dev/SKILL.md +459 -0
- package/bundles/antigravity-plugin/skills/react-dev/examples/generic-components.md +579 -0
- package/bundles/antigravity-plugin/skills/react-dev/examples/server-components.md +579 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/event-handlers.md +574 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/hooks.md +456 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/react-19-patterns.md +638 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/react-router.md +1002 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/tanstack-router.md +587 -0
- package/bundles/antigravity-plugin/skills/refactoring/SKILL.md +486 -0
- package/bundles/antigravity-plugin/skills/resume/SKILL.md +36 -0
- package/bundles/antigravity-plugin/skills/security-review/SKILL.md +196 -0
- package/bundles/antigravity-plugin/skills/setup-project-ai/SKILL.md +61 -0
- package/bundles/antigravity-plugin/skills/ship/SKILL.md +107 -0
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +103 -0
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +102 -0
- package/bundles/antigravity-plugin/skills/skill-creator/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin/skills/skill-creator/SKILL.md +491 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/analyzer.md +274 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/comparator.md +202 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/grader.md +223 -0
- package/bundles/antigravity-plugin/skills/skill-creator/assets/eval_review.html +146 -0
- package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/bundles/antigravity-plugin/skills/skill-creator/references/schemas.md +430 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/__init__.py +0 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/generate_report.py +326 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/improve_description.py +247 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/package_skill.py +136 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_eval.py +310 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_loop.py +328 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/utils.py +47 -0
- package/bundles/antigravity-plugin/skills/sql/SKILL.md +321 -0
- package/bundles/antigravity-plugin/skills/tdd/SKILL.md +37 -0
- package/bundles/antigravity-plugin/skills/tdd-workflow/SKILL.md +188 -0
- package/bundles/antigravity-plugin/skills/technical-writing/SKILL.md +286 -0
- package/bundles/antigravity-plugin/skills/test-strategy/SKILL.md +155 -0
- package/bundles/antigravity-plugin/skills/ui-brief/SKILL.md +84 -0
- package/bundles/antigravity-plugin/skills/ui-review/SKILL.md +176 -0
- package/bundles/antigravity-plugin/skills/ui-review/references/framework-fixes.md +471 -0
- package/bundles/antigravity-plugin/skills/ui-review/references/visual-checklist.md +236 -0
- package/bundles/antigravity-plugin/skills/usage-review/SKILL.md +77 -0
- package/bundles/antigravity-plugin/skills/use-model/SKILL.md +64 -0
- package/bundles/antigravity-plugin/skills/using-git-worktrees/SKILL.md +217 -0
- package/bundles/antigravity-plugin/skills/version/SKILL.md +18 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/SKILL.md +732 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/SKILL.md +97 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/console_logging.py +35 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/scripts/with_server.py +106 -0
- package/bundles/antigravity-plugin/skills/windows-deployment/SKILL.md +880 -0
- package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +384 -0
- package/bundles/antigravity-plugin/statusline-command-agy.sh +91 -0
- package/bundles/antigravity-plugin/warm_start_agy.py +149 -0
- package/bundles/manifest.json +6 -0
- package/dist/cli.js +5363 -0
- package/package.json +61 -4
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refactoring
|
|
3
|
+
context: fork
|
|
4
|
+
description: Safely refactor code while maintaining behavior. Use when improving code structure, reducing duplication, extracting functions, or modernizing legacy code.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Refactoring Skill
|
|
8
|
+
|
|
9
|
+
Systematically refactor code to improve quality while preserving functionality.
|
|
10
|
+
|
|
11
|
+
## Trigger
|
|
12
|
+
|
|
13
|
+
Activate when:
|
|
14
|
+
- User asks to "refactor" or "clean up" code
|
|
15
|
+
- Code review identifies structural issues
|
|
16
|
+
- User wants to improve maintainability
|
|
17
|
+
- Technical debt needs to be addressed
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Refactoring Principles
|
|
22
|
+
|
|
23
|
+
1. **Never change behavior** during refactoring
|
|
24
|
+
2. **Small steps** - one change at a time
|
|
25
|
+
3. **Tests first** - ensure coverage before changing
|
|
26
|
+
4. **Commit often** - after each successful refactoring
|
|
27
|
+
5. **One thing at a time** - don't mix refactoring with feature changes
|
|
28
|
+
|
|
29
|
+
### When NOT to Refactor
|
|
30
|
+
|
|
31
|
+
- Code that works and won't change again (if it ain't broke...)
|
|
32
|
+
- Critical production code without tests (add tests first)
|
|
33
|
+
- When under a tight deadline (schedule it for later)
|
|
34
|
+
- "Just because" - always have a clear purpose
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Phase 1: Assessment
|
|
39
|
+
|
|
40
|
+
### Objectives
|
|
41
|
+
- Understand current code structure
|
|
42
|
+
- Identify refactoring opportunities
|
|
43
|
+
- Assess test coverage
|
|
44
|
+
|
|
45
|
+
### Actions
|
|
46
|
+
|
|
47
|
+
1. **Read and understand the code**
|
|
48
|
+
- What does this code do?
|
|
49
|
+
- What are the inputs/outputs?
|
|
50
|
+
- Where are the dependencies?
|
|
51
|
+
|
|
52
|
+
2. **Identify code smells**
|
|
53
|
+
|
|
54
|
+
| Smell | Signs | Refactoring |
|
|
55
|
+
|-------|-------|-------------|
|
|
56
|
+
| Long Method | >20 lines | Extract Method |
|
|
57
|
+
| Large Class | >200 lines, multiple concerns | Extract Class |
|
|
58
|
+
| Duplicated Code | Same logic in multiple places | Extract Method/Class |
|
|
59
|
+
| Long Parameter List | >3-4 parameters | Parameter Object |
|
|
60
|
+
| Deep Nesting | >3 levels of indentation | Guard Clauses, Extract Method |
|
|
61
|
+
| Magic Numbers | Hardcoded values | Named Constants |
|
|
62
|
+
| Dead Code | Unused functions/variables | Delete |
|
|
63
|
+
| Feature Envy | Method uses another class's data more | Move Method |
|
|
64
|
+
|
|
65
|
+
3. **Check test coverage**
|
|
66
|
+
```bash
|
|
67
|
+
pytest --cov=module_name --cov-report=term-missing
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Output
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
## Refactoring Assessment
|
|
74
|
+
|
|
75
|
+
### Current State
|
|
76
|
+
- File: [filename]
|
|
77
|
+
- Lines: [count]
|
|
78
|
+
- Test Coverage: [percentage]
|
|
79
|
+
|
|
80
|
+
### Code Smells Identified
|
|
81
|
+
1. [Smell]: [Location] - [Description]
|
|
82
|
+
2. [Smell]: [Location] - [Description]
|
|
83
|
+
|
|
84
|
+
### Recommended Refactorings
|
|
85
|
+
1. [Refactoring]: [Expected improvement]
|
|
86
|
+
2. [Refactoring]: [Expected improvement]
|
|
87
|
+
|
|
88
|
+
### Risk Assessment
|
|
89
|
+
- Test coverage: [adequate/needs improvement]
|
|
90
|
+
- Complexity: [low/medium/high]
|
|
91
|
+
- Dependencies: [list affected areas]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Phase 2: Preparation
|
|
97
|
+
|
|
98
|
+
### Objectives
|
|
99
|
+
- Ensure adequate test coverage
|
|
100
|
+
- Set up for safe refactoring
|
|
101
|
+
|
|
102
|
+
### Actions
|
|
103
|
+
|
|
104
|
+
1. **Write missing tests** (if coverage < 80%)
|
|
105
|
+
```python
|
|
106
|
+
def test_existing_behavior():
|
|
107
|
+
"""Capture current behavior before refactoring."""
|
|
108
|
+
result = function_to_refactor(input_data)
|
|
109
|
+
assert result == expected_output
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
2. **Create characterization tests** (for legacy code)
|
|
113
|
+
```python
|
|
114
|
+
def test_characterization():
|
|
115
|
+
"""Document current behavior, even if unexpected."""
|
|
116
|
+
# Run function and record actual output
|
|
117
|
+
result = legacy_function(test_input)
|
|
118
|
+
# Assert against actual output (not expected)
|
|
119
|
+
assert result == actual_observed_output
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
3. **Run all tests** and ensure they pass
|
|
123
|
+
```bash
|
|
124
|
+
pytest -v
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
4. **Commit current state**
|
|
128
|
+
```bash
|
|
129
|
+
git add .
|
|
130
|
+
git commit -m "chore: add tests before refactoring"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Phase 3: Execute Refactoring
|
|
136
|
+
|
|
137
|
+
### Common Refactoring Patterns
|
|
138
|
+
|
|
139
|
+
#### Extract Method
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# Before: Long method with mixed concerns
|
|
143
|
+
def process_complaint(complaint):
|
|
144
|
+
# Validation (10 lines)
|
|
145
|
+
if not complaint.customer_id:
|
|
146
|
+
raise ValueError("Customer ID required")
|
|
147
|
+
if not complaint.description:
|
|
148
|
+
raise ValueError("Description required")
|
|
149
|
+
# ... more validation
|
|
150
|
+
|
|
151
|
+
# Processing (15 lines)
|
|
152
|
+
complaint.status = "processing"
|
|
153
|
+
# ... processing logic
|
|
154
|
+
|
|
155
|
+
# Notification (10 lines)
|
|
156
|
+
send_email(complaint.customer_email, "...")
|
|
157
|
+
# ... notification logic
|
|
158
|
+
|
|
159
|
+
# After: Extracted methods
|
|
160
|
+
def process_complaint(complaint):
|
|
161
|
+
validate_complaint(complaint)
|
|
162
|
+
process_complaint_logic(complaint)
|
|
163
|
+
send_complaint_notification(complaint)
|
|
164
|
+
|
|
165
|
+
def validate_complaint(complaint):
|
|
166
|
+
if not complaint.customer_id:
|
|
167
|
+
raise ValueError("Customer ID required")
|
|
168
|
+
if not complaint.description:
|
|
169
|
+
raise ValueError("Description required")
|
|
170
|
+
|
|
171
|
+
def process_complaint_logic(complaint):
|
|
172
|
+
complaint.status = "processing"
|
|
173
|
+
# ... processing logic
|
|
174
|
+
|
|
175
|
+
def send_complaint_notification(complaint):
|
|
176
|
+
send_email(complaint.customer_email, "...")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Extract Class
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# Before: Class doing too much
|
|
183
|
+
class ComplaintHandler:
|
|
184
|
+
def __init__(self):
|
|
185
|
+
self.db_connection = create_connection()
|
|
186
|
+
|
|
187
|
+
def create_complaint(self, data):
|
|
188
|
+
# 20 lines of complaint logic
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
def send_email(self, to, subject, body):
|
|
192
|
+
# 15 lines of email logic
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
def generate_report(self, date_range):
|
|
196
|
+
# 25 lines of reporting logic
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
# After: Separated concerns
|
|
200
|
+
class ComplaintHandler:
|
|
201
|
+
def __init__(self, notifier, reporter):
|
|
202
|
+
self.notifier = notifier
|
|
203
|
+
self.reporter = reporter
|
|
204
|
+
|
|
205
|
+
def create_complaint(self, data):
|
|
206
|
+
# Complaint logic only
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
class EmailNotifier:
|
|
210
|
+
def send_email(self, to, subject, body):
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
class ComplaintReporter:
|
|
214
|
+
def generate_report(self, date_range):
|
|
215
|
+
pass
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Replace Conditionals with Polymorphism
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
# Before: Switch-like conditionals
|
|
222
|
+
def calculate_priority_score(complaint):
|
|
223
|
+
if complaint.type == "billing":
|
|
224
|
+
return complaint.amount * 0.1
|
|
225
|
+
elif complaint.type == "technical":
|
|
226
|
+
return 50 + complaint.severity * 10
|
|
227
|
+
elif complaint.type == "service":
|
|
228
|
+
return 30 + complaint.wait_time * 2
|
|
229
|
+
else:
|
|
230
|
+
return 10
|
|
231
|
+
|
|
232
|
+
# After: Strategy pattern
|
|
233
|
+
class PriorityCalculator(Protocol):
|
|
234
|
+
def calculate(self, complaint) -> float: ...
|
|
235
|
+
|
|
236
|
+
class BillingPriorityCalculator:
|
|
237
|
+
def calculate(self, complaint) -> float:
|
|
238
|
+
return complaint.amount * 0.1
|
|
239
|
+
|
|
240
|
+
class TechnicalPriorityCalculator:
|
|
241
|
+
def calculate(self, complaint) -> float:
|
|
242
|
+
return 50 + complaint.severity * 10
|
|
243
|
+
|
|
244
|
+
CALCULATORS = {
|
|
245
|
+
"billing": BillingPriorityCalculator(),
|
|
246
|
+
"technical": TechnicalPriorityCalculator(),
|
|
247
|
+
"service": ServicePriorityCalculator(),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def calculate_priority_score(complaint):
|
|
251
|
+
calculator = CALCULATORS.get(complaint.type, DefaultCalculator())
|
|
252
|
+
return calculator.calculate(complaint)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Guard Clauses
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
# Before: Deep nesting
|
|
259
|
+
def process(data):
|
|
260
|
+
if data is not None:
|
|
261
|
+
if data.is_valid:
|
|
262
|
+
if data.has_permission:
|
|
263
|
+
# Actual logic here
|
|
264
|
+
result = do_something(data)
|
|
265
|
+
return result
|
|
266
|
+
else:
|
|
267
|
+
raise PermissionError()
|
|
268
|
+
else:
|
|
269
|
+
raise ValueError("Invalid data")
|
|
270
|
+
else:
|
|
271
|
+
raise ValueError("Data required")
|
|
272
|
+
|
|
273
|
+
# After: Guard clauses
|
|
274
|
+
def process(data):
|
|
275
|
+
if data is None:
|
|
276
|
+
raise ValueError("Data required")
|
|
277
|
+
if not data.is_valid:
|
|
278
|
+
raise ValueError("Invalid data")
|
|
279
|
+
if not data.has_permission:
|
|
280
|
+
raise PermissionError()
|
|
281
|
+
|
|
282
|
+
# Actual logic at main level
|
|
283
|
+
return do_something(data)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Duplicated Code
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
# Before: Same logic in multiple places
|
|
290
|
+
def get_user_discount(user):
|
|
291
|
+
if user.membership == "gold": return user.total * 0.2
|
|
292
|
+
if user.membership == "silver": return user.total * 0.1
|
|
293
|
+
return 0
|
|
294
|
+
|
|
295
|
+
def get_order_discount(order):
|
|
296
|
+
if order.user.membership == "gold": return order.total * 0.2
|
|
297
|
+
if order.user.membership == "silver": return order.total * 0.1
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
# After: Extract common logic
|
|
301
|
+
DISCOUNT_RATES = {"gold": 0.2, "silver": 0.1}
|
|
302
|
+
|
|
303
|
+
def get_membership_rate(membership: str) -> float:
|
|
304
|
+
return DISCOUNT_RATES.get(membership, 0)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Long Parameter List → Parameter Object
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# Before: Too many parameters
|
|
311
|
+
def create_user(email, password, name, age, address, city, country, phone):
|
|
312
|
+
...
|
|
313
|
+
|
|
314
|
+
# After: Group into a dataclass
|
|
315
|
+
@dataclass
|
|
316
|
+
class UserData:
|
|
317
|
+
email: str
|
|
318
|
+
password: str
|
|
319
|
+
name: str
|
|
320
|
+
age: int | None = None
|
|
321
|
+
address: str | None = None
|
|
322
|
+
phone: str | None = None
|
|
323
|
+
|
|
324
|
+
def create_user(data: UserData):
|
|
325
|
+
...
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### Feature Envy → Move Method
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
# Before: Method uses another object's data more than its own
|
|
332
|
+
class Order:
|
|
333
|
+
def calculate_discount(self, user):
|
|
334
|
+
if user.membership_level == "gold":
|
|
335
|
+
return self.total * 0.2
|
|
336
|
+
if user.account_age > 365:
|
|
337
|
+
return self.total * 0.1
|
|
338
|
+
return 0
|
|
339
|
+
|
|
340
|
+
# After: Move logic to the object that owns the data
|
|
341
|
+
class User:
|
|
342
|
+
def get_discount_rate(self) -> float:
|
|
343
|
+
if self.membership_level == "gold": return 0.2
|
|
344
|
+
if self.account_age > 365: return 0.1
|
|
345
|
+
return 0
|
|
346
|
+
|
|
347
|
+
class Order:
|
|
348
|
+
def calculate_discount(self, user):
|
|
349
|
+
return self.total * user.get_discount_rate()
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### Primitive Obsession → Value Objects
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
# Before: Using raw strings for domain concepts
|
|
356
|
+
def send_email(to: str, subject: str, body: str): ...
|
|
357
|
+
send_email("user@example.com", "Hello", "...")
|
|
358
|
+
|
|
359
|
+
# After: Domain types with validation
|
|
360
|
+
class Email:
|
|
361
|
+
def __init__(self, value: str):
|
|
362
|
+
if "@" not in value:
|
|
363
|
+
raise ValueError(f"Invalid email: {value}")
|
|
364
|
+
self.value = value
|
|
365
|
+
|
|
366
|
+
def send_email(to: Email, subject: str, body: str): ...
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### Dead Code → Delete It
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
# Before: Unused code lingers
|
|
373
|
+
def old_implementation(): ... # Nobody calls this
|
|
374
|
+
DEPRECATED_VALUE = 5 # Not referenced
|
|
375
|
+
# def commented_out_code(): ... # Noise
|
|
376
|
+
|
|
377
|
+
# After: Remove it — git has history if you need it back
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### After Each Refactoring
|
|
381
|
+
|
|
382
|
+
1. **Run tests**
|
|
383
|
+
```bash
|
|
384
|
+
pytest -v
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
2. **If tests pass, commit**
|
|
388
|
+
```bash
|
|
389
|
+
git commit -m "refactor: extract validation to separate method"
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
3. **If tests fail, revert**
|
|
393
|
+
```bash
|
|
394
|
+
git checkout -- .
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Phase 4: Validation
|
|
400
|
+
|
|
401
|
+
### Objectives
|
|
402
|
+
- Verify behavior unchanged
|
|
403
|
+
- Confirm quality improved
|
|
404
|
+
- Document changes
|
|
405
|
+
|
|
406
|
+
### Actions
|
|
407
|
+
|
|
408
|
+
1. **Run full test suite**
|
|
409
|
+
```bash
|
|
410
|
+
pytest --cov=module_name
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
2. **Compare before/after metrics**
|
|
414
|
+
- Lines of code
|
|
415
|
+
- Cyclomatic complexity
|
|
416
|
+
- Test coverage
|
|
417
|
+
- Method/class sizes
|
|
418
|
+
|
|
419
|
+
3. **Manual smoke test**
|
|
420
|
+
- Test key user flows
|
|
421
|
+
- Verify edge cases
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Phase 5: Documentation
|
|
426
|
+
|
|
427
|
+
### Output
|
|
428
|
+
|
|
429
|
+
```markdown
|
|
430
|
+
## Refactoring Summary
|
|
431
|
+
|
|
432
|
+
### Changes Made
|
|
433
|
+
1. **[Refactoring 1]**: [What was changed and why]
|
|
434
|
+
2. **[Refactoring 2]**: [What was changed and why]
|
|
435
|
+
|
|
436
|
+
### Files Modified
|
|
437
|
+
- `path/to/file.py`: [Summary of changes]
|
|
438
|
+
|
|
439
|
+
### Metrics Comparison
|
|
440
|
+
| Metric | Before | After |
|
|
441
|
+
|--------|--------|-------|
|
|
442
|
+
| Lines of Code | [x] | [y] |
|
|
443
|
+
| Methods >20 lines | [x] | [y] |
|
|
444
|
+
| Test Coverage | [x]% | [y]% |
|
|
445
|
+
|
|
446
|
+
### Behavioral Changes
|
|
447
|
+
None (refactoring only)
|
|
448
|
+
|
|
449
|
+
### Tests Added
|
|
450
|
+
- `test_function_name`: [What it tests]
|
|
451
|
+
|
|
452
|
+
### Follow-up Recommendations
|
|
453
|
+
- [ ] [Future improvement 1]
|
|
454
|
+
- [ ] [Future improvement 2]
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Common Refactoring Operations Reference
|
|
460
|
+
|
|
461
|
+
| Operation | Description |
|
|
462
|
+
|-----------|-------------|
|
|
463
|
+
| Extract Method | Turn code fragment into a method |
|
|
464
|
+
| Extract Class | Move behavior to a new class |
|
|
465
|
+
| Inline Method | Move method body back to caller |
|
|
466
|
+
| Introduce Parameter Object | Group related parameters |
|
|
467
|
+
| Replace Conditional with Polymorphism | Use strategy/polymorphism instead of switch/if |
|
|
468
|
+
| Replace Magic Number with Constant | Named constants for clarity |
|
|
469
|
+
| Decompose Conditional | Break complex conditions into named functions |
|
|
470
|
+
| Replace Nested Conditional with Guard Clauses | Early returns |
|
|
471
|
+
| Introduce Null Object | Eliminate null checks |
|
|
472
|
+
| Replace Type Code with Enum | Strong typing for categories |
|
|
473
|
+
| Replace Inheritance with Delegation | Composition over inheritance |
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Safety Checklist
|
|
478
|
+
|
|
479
|
+
- [ ] Tests exist for code being refactored
|
|
480
|
+
- [ ] All tests pass before starting
|
|
481
|
+
- [ ] Refactoring in small, atomic steps
|
|
482
|
+
- [ ] Tests run after each change
|
|
483
|
+
- [ ] Commits after each successful refactoring
|
|
484
|
+
- [ ] No behavior changes introduced
|
|
485
|
+
- [ ] All tests pass after completion
|
|
486
|
+
- [ ] Changes documented
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resume
|
|
3
|
+
description: Pop the most recently parked workstream off the stack and resume it at its exact resume point
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /caddis:resume — pop the parked task and pick it back up
|
|
7
|
+
|
|
8
|
+
A digression is finished; return to the task you parked with `/digress`. This pops the top frame off the
|
|
9
|
+
workstream stack, restates where you were, realigns `relay.md`, and immediately continues the work — the
|
|
10
|
+
user should not have to remember or re-state anything.
|
|
11
|
+
|
|
12
|
+
## Step 1 — read the stack
|
|
13
|
+
Read `.caddis/workstreams.json`. If it is **absent, unparseable, `version != 1`, or `stack` is empty**,
|
|
14
|
+
say exactly `Nothing is parked.` and stop. Do nothing else.
|
|
15
|
+
|
|
16
|
+
## Step 2 — pop the top frame
|
|
17
|
+
The top of stack is the **last** element of `stack` (LIFO — most recently parked). Remove it and write the
|
|
18
|
+
rest of the file back (preserve `version` and any other frames + unknown fields). This is the one write
|
|
19
|
+
this command makes to the state file.
|
|
20
|
+
|
|
21
|
+
## Step 3 — restate + realign relay.md
|
|
22
|
+
Restate the popped frame to the user: its `plan`, `phase`, and `resumePointer` (and `repo` if set — the
|
|
23
|
+
parked task lives in another repo, so say which). Then edit `.caddis/relay.md`'s `## Next step` section
|
|
24
|
+
**in place** so it matches the popped frame's `resumePointer` — preserve every other section of relay.md
|
|
25
|
+
untouched. (If relay.md or that section is absent, skip this edit silently; the restatement above is enough.)
|
|
26
|
+
|
|
27
|
+
## Step 4 — resume the work
|
|
28
|
+
Begin executing the `resumePointer` immediately. Ask nothing — the frame already carries the next action.
|
|
29
|
+
If the parked plan lives in another `repo`, note that the user may need to open that repo first, then
|
|
30
|
+
proceed there.
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
- **Never** run a destructive or history-rewriting git action (no `git checkout`, `git reset`, `git stash`,
|
|
34
|
+
branch switches). Resuming is metadata-only.
|
|
35
|
+
- Pop exactly one frame per invocation (the LIFO top). Run `/resume` again to pop the next.
|
|
36
|
+
- Only real paths and verified facts — restate the frame as written; do not embellish the resume point.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-review
|
|
3
|
+
context: fork
|
|
4
|
+
description: "Security review workflow — OWASP, code scanning, cloud infrastructure"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Security Review
|
|
8
|
+
|
|
9
|
+
Security review workflow covering OWASP Top 10, code scanning, and cloud infrastructure.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- Implementing authentication or authorization
|
|
14
|
+
- Handling user input or file uploads
|
|
15
|
+
- Creating or modifying API endpoints
|
|
16
|
+
- Working with secrets, deploying to cloud, or configuring CI/CD
|
|
17
|
+
|
|
18
|
+
## Phase 1: OWASP Top 10 Checks
|
|
19
|
+
|
|
20
|
+
### A01 — Broken Access Control
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
# BAD — no auth check
|
|
24
|
+
@app.route("/admin/users")
|
|
25
|
+
def list_users():
|
|
26
|
+
return db.query(User).all()
|
|
27
|
+
|
|
28
|
+
# GOOD
|
|
29
|
+
@app.route("/admin/users")
|
|
30
|
+
@require_role("admin")
|
|
31
|
+
def list_users():
|
|
32
|
+
return db.query(User).all()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- [ ] Every endpoint checks authentication
|
|
36
|
+
- [ ] Role-based access control enforced
|
|
37
|
+
- [ ] Users cannot access other users' data (IDOR)
|
|
38
|
+
|
|
39
|
+
### A02 — Cryptographic Failures
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# BAD: password_hash = hashlib.md5(password.encode()).hexdigest()
|
|
43
|
+
# GOOD:
|
|
44
|
+
from passlib.hash import argon2
|
|
45
|
+
password_hash = argon2.hash(password)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- [ ] Passwords hashed with bcrypt/argon2
|
|
49
|
+
- [ ] Sensitive data encrypted at rest and in transit
|
|
50
|
+
- [ ] No secrets in source code or logs
|
|
51
|
+
|
|
52
|
+
### A03 — Injection
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
# BAD: cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
|
|
56
|
+
# GOOD:
|
|
57
|
+
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- [ ] All queries parameterized or use ORM
|
|
61
|
+
- [ ] No `eval()`, `exec()`, or `os.system()` with user input
|
|
62
|
+
|
|
63
|
+
### A05 — Security Misconfiguration
|
|
64
|
+
|
|
65
|
+
- [ ] Debug mode disabled in production
|
|
66
|
+
- [ ] Default credentials changed
|
|
67
|
+
- [ ] Unnecessary endpoints disabled
|
|
68
|
+
|
|
69
|
+
### A06 — Vulnerable Components
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip audit # Python
|
|
73
|
+
npm audit # JavaScript
|
|
74
|
+
trivy image app # Container
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- [ ] No known vulnerabilities; lock files committed
|
|
78
|
+
|
|
79
|
+
### A07 — Authentication Failures
|
|
80
|
+
|
|
81
|
+
- [ ] Session tokens in httpOnly cookies (not localStorage)
|
|
82
|
+
- [ ] Account lockout after failed attempts
|
|
83
|
+
- [ ] MFA for privileged accounts
|
|
84
|
+
|
|
85
|
+
### A09 — Logging Failures
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# BAD: logger.info(f"Login: user={email}, password={password}")
|
|
89
|
+
# GOOD: logger.info(f"Login: user={email}, success={is_valid}")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- [ ] Auth events logged; no PII or secrets in logs
|
|
93
|
+
|
|
94
|
+
### A10 — SSRF
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
ALLOWED_HOSTS = {"api.example.com"}
|
|
98
|
+
parsed = urlparse(user_url)
|
|
99
|
+
if parsed.hostname not in ALLOWED_HOSTS:
|
|
100
|
+
raise ValueError("URL not allowed")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Phase 2: Input Validation
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from pydantic import BaseModel, EmailStr, constr, conint
|
|
107
|
+
|
|
108
|
+
class CreateUserRequest(BaseModel):
|
|
109
|
+
email: EmailStr
|
|
110
|
+
name: constr(min_length=1, max_length=100)
|
|
111
|
+
age: conint(ge=0, le=150)
|
|
112
|
+
|
|
113
|
+
# Validate
|
|
114
|
+
try:
|
|
115
|
+
data = CreateUserRequest(**request_json)
|
|
116
|
+
except ValidationError as e:
|
|
117
|
+
return {"error": e.errors()}, 400
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
File uploads: check size, extension, and magic bytes.
|
|
121
|
+
|
|
122
|
+
## Phase 3: Security Headers
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
@app.after_request
|
|
126
|
+
def add_security_headers(response):
|
|
127
|
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
128
|
+
response.headers["X-Frame-Options"] = "DENY"
|
|
129
|
+
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
|
130
|
+
response.headers["Content-Security-Policy"] = (
|
|
131
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
|
|
132
|
+
)
|
|
133
|
+
return response
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Phase 4: Cloud Infrastructure
|
|
137
|
+
|
|
138
|
+
### IAM — Least Privilege
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
# GOOD: specific permissions on specific resources
|
|
142
|
+
permissions: [s3:GetObject, s3:ListBucket]
|
|
143
|
+
resources: [arn:aws:s3:::my-bucket/*]
|
|
144
|
+
|
|
145
|
+
# BAD: permissions: [s3:*], resources: ["*"]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- [ ] No root account in production; MFA on privileged accounts
|
|
149
|
+
- [ ] Service accounts use roles, not long-lived keys
|
|
150
|
+
|
|
151
|
+
### Secrets Management
|
|
152
|
+
|
|
153
|
+
- [ ] Secrets in a manager (AWS SM, Vault) with rotation
|
|
154
|
+
- [ ] No secrets in env vars without rotation policy
|
|
155
|
+
|
|
156
|
+
### CI/CD Security
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
jobs:
|
|
160
|
+
deploy:
|
|
161
|
+
permissions: { contents: read }
|
|
162
|
+
steps:
|
|
163
|
+
- uses: actions/checkout@v4
|
|
164
|
+
- uses: trufflesecurity/trufflehog@main # Secret scanning
|
|
165
|
+
- run: pip audit # Dep audit
|
|
166
|
+
- run: pytest --cov=myapp --cov-fail-under=80
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- [ ] OIDC instead of long-lived credentials
|
|
170
|
+
- [ ] Branch protection and code review required
|
|
171
|
+
|
|
172
|
+
### Network
|
|
173
|
+
|
|
174
|
+
- [ ] Database not publicly accessible
|
|
175
|
+
- [ ] Security groups follow least privilege
|
|
176
|
+
- [ ] WAF enabled
|
|
177
|
+
|
|
178
|
+
## Pre-Deployment Checklist
|
|
179
|
+
|
|
180
|
+
- [ ] No hardcoded secrets
|
|
181
|
+
- [ ] All user inputs validated server-side
|
|
182
|
+
- [ ] All queries parameterized
|
|
183
|
+
- [ ] Auth + authz on every endpoint
|
|
184
|
+
- [ ] Rate limiting enabled
|
|
185
|
+
- [ ] HTTPS enforced
|
|
186
|
+
- [ ] Security headers configured
|
|
187
|
+
- [ ] No sensitive data in error responses or logs
|
|
188
|
+
- [ ] Dependencies audited
|
|
189
|
+
- [ ] Cloud IAM least-privilege applied
|
|
190
|
+
- [ ] Backups automated with tested recovery
|
|
191
|
+
|
|
192
|
+
## Resources
|
|
193
|
+
|
|
194
|
+
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
|
195
|
+
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
|
|
196
|
+
- [CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks)
|