devflow-kit 1.1.0 → 1.3.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/CHANGELOG.md +69 -1
- package/README.md +23 -6
- package/dist/cli.js +2 -0
- package/dist/commands/ambient.js +5 -4
- package/dist/commands/init.js +4 -2
- package/dist/commands/memory.js +4 -4
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.js +116 -0
- package/dist/commands/uninstall.js +11 -1
- package/dist/plugins.js +67 -3
- package/dist/utils/installer.js +20 -2
- package/package.json +4 -2
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +4 -2
- package/plugins/devflow-ambient/README.md +8 -8
- package/plugins/devflow-ambient/commands/ambient.md +14 -14
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +16 -9
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +6 -2
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +13 -6
- package/plugins/devflow-code-review/agents/reviewer.md +8 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
- package/plugins/devflow-code-review/commands/code-review.md +12 -2
- package/plugins/devflow-code-review/skills/architecture-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/complexity-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/consistency-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/database-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/dependencies-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/documentation-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/performance-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/regression-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/review-methodology/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/security-patterns/SKILL.md +1 -1
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +10 -7
- package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +5 -8
- package/plugins/devflow-debug/.claude-plugin/plugin.json +10 -3
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-go/skills/go/SKILL.md +187 -0
- package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
- package/plugins/devflow-go/skills/go/references/detection.md +129 -0
- package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
- package/plugins/devflow-go/skills/go/references/violations.md +205 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +19 -5
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-implement/skills/self-review/SKILL.md +1 -1
- package/plugins/devflow-java/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-java/skills/java/SKILL.md +183 -0
- package/plugins/devflow-java/skills/java/references/detection.md +120 -0
- package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
- package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
- package/plugins/devflow-java/skills/java/references/violations.md +213 -0
- package/plugins/devflow-python/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-python/skills/python/SKILL.md +188 -0
- package/plugins/devflow-python/skills/python/references/async.md +220 -0
- package/plugins/devflow-python/skills/python/references/detection.md +128 -0
- package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
- package/plugins/devflow-python/skills/python/references/violations.md +204 -0
- package/plugins/devflow-react/.claude-plugin/plugin.json +22 -0
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +13 -3
- package/plugins/devflow-resolve/skills/security-patterns/SKILL.md +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
- package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
- package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
- package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
- package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +10 -3
- package/plugins/devflow-self-review/skills/self-review/SKILL.md +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +15 -4
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +22 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/scripts/hooks/{ambient-prompt.sh → ambient-prompt} +4 -4
- package/scripts/hooks/{background-memory-update.sh → background-memory-update} +3 -3
- package/scripts/hooks/{ensure-memory-gitignore.sh → ensure-memory-gitignore} +1 -1
- package/scripts/hooks/{pre-compact-memory.sh → pre-compact-memory} +2 -2
- package/scripts/hooks/run-hook +23 -0
- package/scripts/hooks/session-start-memory +151 -0
- package/scripts/hooks/{stop-update-memory.sh → stop-update-memory} +4 -4
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +16 -9
- package/shared/skills/ambient-router/references/skill-catalog.md +6 -2
- package/shared/skills/architecture-patterns/SKILL.md +1 -1
- package/shared/skills/complexity-patterns/SKILL.md +1 -1
- package/shared/skills/consistency-patterns/SKILL.md +1 -1
- package/shared/skills/database-patterns/SKILL.md +1 -1
- package/shared/skills/dependencies-patterns/SKILL.md +1 -1
- package/shared/skills/documentation-patterns/SKILL.md +1 -1
- package/shared/skills/go/SKILL.md +187 -0
- package/shared/skills/go/references/concurrency.md +312 -0
- package/shared/skills/go/references/detection.md +129 -0
- package/shared/skills/go/references/patterns.md +232 -0
- package/shared/skills/go/references/violations.md +205 -0
- package/shared/skills/java/SKILL.md +183 -0
- package/shared/skills/java/references/detection.md +120 -0
- package/shared/skills/java/references/modern-java.md +270 -0
- package/shared/skills/java/references/patterns.md +235 -0
- package/shared/skills/java/references/violations.md +213 -0
- package/shared/skills/performance-patterns/SKILL.md +1 -1
- package/shared/skills/python/SKILL.md +188 -0
- package/shared/skills/python/references/async.md +220 -0
- package/shared/skills/python/references/detection.md +128 -0
- package/shared/skills/python/references/patterns.md +226 -0
- package/shared/skills/python/references/violations.md +204 -0
- package/shared/skills/react/SKILL.md +1 -1
- package/shared/skills/react/references/patterns.md +3 -3
- package/shared/skills/regression-patterns/SKILL.md +1 -1
- package/shared/skills/review-methodology/SKILL.md +1 -1
- package/shared/skills/rust/SKILL.md +193 -0
- package/shared/skills/rust/references/detection.md +131 -0
- package/shared/skills/rust/references/ownership.md +242 -0
- package/shared/skills/rust/references/patterns.md +210 -0
- package/shared/skills/rust/references/violations.md +191 -0
- package/shared/skills/security-patterns/SKILL.md +1 -1
- package/shared/skills/self-review/SKILL.md +1 -1
- package/shared/skills/test-driven-development/SKILL.md +5 -8
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/src/templates/settings.json +3 -3
- package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
- package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
- package/scripts/hooks/session-start-memory.sh +0 -126
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Python Issue Detection
|
|
2
|
+
|
|
3
|
+
Grep patterns for finding common Python issues. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Type Safety Detection
|
|
6
|
+
|
|
7
|
+
### Missing Type Hints
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Functions without return type annotation
|
|
11
|
+
grep -rn "def .*):$" --include="*.py" | grep -v "->.*:"
|
|
12
|
+
|
|
13
|
+
# Functions without any annotations
|
|
14
|
+
grep -rn "def .*([a-z_][a-z_0-9]*\s*," --include="*.py" | grep -v ":"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Bare Except Clauses
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Bare except (catches everything)
|
|
21
|
+
grep -rn "except:" --include="*.py"
|
|
22
|
+
|
|
23
|
+
# Overly broad except Exception
|
|
24
|
+
grep -rn "except Exception:" --include="*.py" | grep -v "# noqa"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Data Modeling Detection
|
|
28
|
+
|
|
29
|
+
### Mutable Default Arguments
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# List defaults
|
|
33
|
+
grep -rn "def .*=\s*\[\]" --include="*.py"
|
|
34
|
+
|
|
35
|
+
# Dict defaults
|
|
36
|
+
grep -rn "def .*=\s*{}" --include="*.py"
|
|
37
|
+
|
|
38
|
+
# Set defaults
|
|
39
|
+
grep -rn "def .*=\s*set()" --include="*.py"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Raw Dict Usage Instead of Dataclasses
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Dict literals assigned to typed variables
|
|
46
|
+
grep -rn ': dict\[' --include="*.py" | grep "= {"
|
|
47
|
+
|
|
48
|
+
# Nested dict access patterns (fragile)
|
|
49
|
+
grep -rn '\["[a-z].*\]\["' --include="*.py"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Anti-Pattern Detection
|
|
53
|
+
|
|
54
|
+
### Assert in Production Code
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Assert statements outside test files
|
|
58
|
+
grep -rn "^ assert " --include="*.py" | grep -v "test_\|_test\.py\|tests/"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Global Mutable State
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Global variable assignment
|
|
65
|
+
grep -rn "^[a-z_].*= \[\]\|^[a-z_].*= {}\|^[a-z_].*= set()" --include="*.py"
|
|
66
|
+
|
|
67
|
+
# Global keyword usage
|
|
68
|
+
grep -rn "global " --include="*.py"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Old-Style String Formatting
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Percent formatting
|
|
75
|
+
grep -rn '"%.*" %' --include="*.py"
|
|
76
|
+
grep -rn "'%.*' %" --include="*.py"
|
|
77
|
+
|
|
78
|
+
# .format() where f-strings would be cleaner
|
|
79
|
+
grep -rn '\.format(' --include="*.py"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Wildcard Imports
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Star imports
|
|
86
|
+
grep -rn "from .* import \*" --include="*.py"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Async Detection
|
|
90
|
+
|
|
91
|
+
### Missing Await
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Coroutine called without await (common mistake)
|
|
95
|
+
grep -rn "async def" --include="*.py" -l | xargs grep -n "[^await ]fetch\|[^await ]save\|[^await ]send"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Blocking Calls in Async Code
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# time.sleep in async context
|
|
102
|
+
grep -rn "time\.sleep" --include="*.py"
|
|
103
|
+
|
|
104
|
+
# requests library in async files (should use aiohttp/httpx)
|
|
105
|
+
grep -rn "import requests\|from requests" --include="*.py"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Security Detection
|
|
109
|
+
|
|
110
|
+
### Unsafe Deserialization
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# pickle usage (arbitrary code execution risk)
|
|
114
|
+
grep -rn "pickle\.loads\|pickle\.load(" --include="*.py"
|
|
115
|
+
|
|
116
|
+
# yaml.load without SafeLoader
|
|
117
|
+
grep -rn "yaml\.load(" --include="*.py" | grep -v "SafeLoader\|safe_load"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Shell Injection
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# subprocess with shell=True
|
|
124
|
+
grep -rn "shell=True" --include="*.py"
|
|
125
|
+
|
|
126
|
+
# os.system calls
|
|
127
|
+
grep -rn "os\.system(" --include="*.py"
|
|
128
|
+
```
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Python Correct Patterns
|
|
2
|
+
|
|
3
|
+
Extended correct patterns for Python development. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Dependency Injection
|
|
6
|
+
|
|
7
|
+
### Constructor Injection
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from typing import Protocol
|
|
11
|
+
|
|
12
|
+
class EmailSender(Protocol):
|
|
13
|
+
def send(self, to: str, subject: str, body: str) -> None: ...
|
|
14
|
+
|
|
15
|
+
class UserService:
|
|
16
|
+
def __init__(self, repo: UserRepository, emailer: EmailSender) -> None:
|
|
17
|
+
self._repo = repo
|
|
18
|
+
self._emailer = emailer
|
|
19
|
+
|
|
20
|
+
def create_user(self, request: CreateUserRequest) -> User:
|
|
21
|
+
user = User(name=request.name, email=request.email)
|
|
22
|
+
saved = self._repo.save(user)
|
|
23
|
+
self._emailer.send(user.email, "Welcome", f"Hello {user.name}")
|
|
24
|
+
return saved
|
|
25
|
+
|
|
26
|
+
# Easy to test — inject fakes
|
|
27
|
+
service = UserService(repo=FakeUserRepo(), emailer=FakeEmailSender())
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Factory Functions
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
def create_app(config: AppConfig) -> Flask:
|
|
34
|
+
app = Flask(__name__)
|
|
35
|
+
db = Database(config.database_url)
|
|
36
|
+
cache = RedisCache(config.redis_url)
|
|
37
|
+
user_service = UserService(repo=SqlUserRepo(db), emailer=SmtpSender(config.smtp))
|
|
38
|
+
register_routes(app, user_service)
|
|
39
|
+
return app
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Decorator Patterns
|
|
43
|
+
|
|
44
|
+
### Retry Decorator
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import functools
|
|
48
|
+
import time
|
|
49
|
+
from typing import TypeVar, Callable, ParamSpec
|
|
50
|
+
|
|
51
|
+
P = ParamSpec("P")
|
|
52
|
+
R = TypeVar("R")
|
|
53
|
+
|
|
54
|
+
def retry(
|
|
55
|
+
max_attempts: int = 3,
|
|
56
|
+
delay: float = 1.0,
|
|
57
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
58
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
59
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
60
|
+
@functools.wraps(func)
|
|
61
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
62
|
+
last_error: Exception | None = None
|
|
63
|
+
for attempt in range(max_attempts):
|
|
64
|
+
try:
|
|
65
|
+
return func(*args, **kwargs)
|
|
66
|
+
except exceptions as e:
|
|
67
|
+
last_error = e
|
|
68
|
+
if attempt < max_attempts - 1:
|
|
69
|
+
time.sleep(delay * (2 ** attempt))
|
|
70
|
+
raise last_error # type: ignore[misc]
|
|
71
|
+
return wrapper
|
|
72
|
+
return decorator
|
|
73
|
+
|
|
74
|
+
@retry(max_attempts=3, delay=0.5, exceptions=(ConnectionError, TimeoutError))
|
|
75
|
+
def fetch_data(url: str) -> dict[str, Any]:
|
|
76
|
+
...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Validation Decorator
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def validate_input(schema: type[BaseModel]):
|
|
83
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
84
|
+
@functools.wraps(func)
|
|
85
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
86
|
+
# Validate first positional arg after self/cls
|
|
87
|
+
data = args[1] if len(args) > 1 else args[0]
|
|
88
|
+
schema.model_validate(data)
|
|
89
|
+
return func(*args, **kwargs)
|
|
90
|
+
return wrapper
|
|
91
|
+
return decorator
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Context Manager Patterns
|
|
95
|
+
|
|
96
|
+
### Database Transaction
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from contextlib import contextmanager
|
|
100
|
+
from typing import Generator
|
|
101
|
+
|
|
102
|
+
@contextmanager
|
|
103
|
+
def transaction(session: Session) -> Generator[Session, None, None]:
|
|
104
|
+
try:
|
|
105
|
+
yield session
|
|
106
|
+
session.commit()
|
|
107
|
+
except Exception:
|
|
108
|
+
session.rollback()
|
|
109
|
+
raise
|
|
110
|
+
finally:
|
|
111
|
+
session.close()
|
|
112
|
+
|
|
113
|
+
# Usage
|
|
114
|
+
with transaction(db.session()) as session:
|
|
115
|
+
session.add(user)
|
|
116
|
+
session.add(audit_log)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Temporary Directory
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from contextlib import contextmanager
|
|
123
|
+
from pathlib import Path
|
|
124
|
+
import tempfile
|
|
125
|
+
import shutil
|
|
126
|
+
|
|
127
|
+
@contextmanager
|
|
128
|
+
def temp_workspace() -> Generator[Path, None, None]:
|
|
129
|
+
path = Path(tempfile.mkdtemp())
|
|
130
|
+
try:
|
|
131
|
+
yield path
|
|
132
|
+
finally:
|
|
133
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Pytest Fixture Patterns
|
|
137
|
+
|
|
138
|
+
### Service Fixtures with DI
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import pytest
|
|
142
|
+
|
|
143
|
+
@pytest.fixture
|
|
144
|
+
def fake_repo() -> FakeUserRepo:
|
|
145
|
+
return FakeUserRepo()
|
|
146
|
+
|
|
147
|
+
@pytest.fixture
|
|
148
|
+
def fake_emailer() -> FakeEmailSender:
|
|
149
|
+
return FakeEmailSender()
|
|
150
|
+
|
|
151
|
+
@pytest.fixture
|
|
152
|
+
def user_service(fake_repo: FakeUserRepo, fake_emailer: FakeEmailSender) -> UserService:
|
|
153
|
+
return UserService(repo=fake_repo, emailer=fake_emailer)
|
|
154
|
+
|
|
155
|
+
def test_create_user_sends_welcome_email(
|
|
156
|
+
user_service: UserService,
|
|
157
|
+
fake_emailer: FakeEmailSender,
|
|
158
|
+
) -> None:
|
|
159
|
+
user_service.create_user(CreateUserRequest(name="Alice", email="a@b.com"))
|
|
160
|
+
assert fake_emailer.sent_count == 1
|
|
161
|
+
assert fake_emailer.last_to == "a@b.com"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Parametrized Tests
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
@pytest.mark.parametrize(
|
|
168
|
+
"input_age, expected_valid",
|
|
169
|
+
[
|
|
170
|
+
(25, True),
|
|
171
|
+
(0, True),
|
|
172
|
+
(150, True),
|
|
173
|
+
(-1, False),
|
|
174
|
+
(151, False),
|
|
175
|
+
(None, False),
|
|
176
|
+
],
|
|
177
|
+
)
|
|
178
|
+
def test_age_validation(input_age: int | None, expected_valid: bool) -> None:
|
|
179
|
+
result = validate_age(input_age)
|
|
180
|
+
assert result.is_valid == expected_valid
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Structured Logging
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
import structlog
|
|
187
|
+
|
|
188
|
+
logger = structlog.get_logger()
|
|
189
|
+
|
|
190
|
+
def process_order(order: Order) -> OrderResult:
|
|
191
|
+
log = logger.bind(order_id=order.id, user_id=order.user_id)
|
|
192
|
+
log.info("processing_order", item_count=len(order.items))
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
result = fulfill(order)
|
|
196
|
+
log.info("order_fulfilled", total=result.total)
|
|
197
|
+
return result
|
|
198
|
+
except InsufficientStockError as e:
|
|
199
|
+
log.warning("order_failed_stock", item_id=e.item_id)
|
|
200
|
+
raise
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Enum Patterns
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from enum import Enum, auto
|
|
207
|
+
|
|
208
|
+
class OrderStatus(Enum):
|
|
209
|
+
PENDING = auto()
|
|
210
|
+
PROCESSING = auto()
|
|
211
|
+
SHIPPED = auto()
|
|
212
|
+
DELIVERED = auto()
|
|
213
|
+
CANCELLED = auto()
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def is_terminal(self) -> bool:
|
|
217
|
+
return self in (OrderStatus.DELIVERED, OrderStatus.CANCELLED)
|
|
218
|
+
|
|
219
|
+
def can_transition_to(self, target: "OrderStatus") -> bool:
|
|
220
|
+
valid = {
|
|
221
|
+
OrderStatus.PENDING: {OrderStatus.PROCESSING, OrderStatus.CANCELLED},
|
|
222
|
+
OrderStatus.PROCESSING: {OrderStatus.SHIPPED, OrderStatus.CANCELLED},
|
|
223
|
+
OrderStatus.SHIPPED: {OrderStatus.DELIVERED},
|
|
224
|
+
}
|
|
225
|
+
return target in valid.get(self, set())
|
|
226
|
+
```
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Python Violation Examples
|
|
2
|
+
|
|
3
|
+
Extended violation patterns for Python reviews. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Type Safety Violations
|
|
6
|
+
|
|
7
|
+
### Missing Type Hints
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
# VIOLATION: No type annotations
|
|
11
|
+
def process(data, config):
|
|
12
|
+
result = transform(data)
|
|
13
|
+
return result
|
|
14
|
+
|
|
15
|
+
# VIOLATION: Partial annotations (inconsistent)
|
|
16
|
+
def save(user: User, db): # db missing type
|
|
17
|
+
return db.insert(user)
|
|
18
|
+
|
|
19
|
+
# VIOLATION: Missing return type
|
|
20
|
+
def calculate_total(items: list[Item]):
|
|
21
|
+
return sum(item.price for item in items)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Bare Except Clauses
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# VIOLATION: Catches everything including SystemExit and KeyboardInterrupt
|
|
28
|
+
try:
|
|
29
|
+
process_data()
|
|
30
|
+
except:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
# VIOLATION: Catches too broadly and silences
|
|
34
|
+
try:
|
|
35
|
+
user = fetch_user(user_id)
|
|
36
|
+
except Exception:
|
|
37
|
+
user = None # Hides real errors (network, auth, parsing)
|
|
38
|
+
|
|
39
|
+
# VIOLATION: Bare except with logging but no re-raise
|
|
40
|
+
try:
|
|
41
|
+
critical_operation()
|
|
42
|
+
except:
|
|
43
|
+
logger.error("Something failed")
|
|
44
|
+
# Swallows the error — caller never knows
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Data Modeling Violations
|
|
48
|
+
|
|
49
|
+
### Raw Dicts Instead of Dataclasses
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# VIOLATION: Untyped dict — no IDE support, no validation
|
|
53
|
+
user = {
|
|
54
|
+
"name": "Alice",
|
|
55
|
+
"email": "alice@example.com",
|
|
56
|
+
"age": 30,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# VIOLATION: Accessing dict keys without safety
|
|
60
|
+
def get_display_name(user: dict) -> str:
|
|
61
|
+
return f"{user['first_name']} {user['last_name']}" # KeyError risk
|
|
62
|
+
|
|
63
|
+
# VIOLATION: Nested untyped dicts
|
|
64
|
+
config = {
|
|
65
|
+
"database": {
|
|
66
|
+
"host": "localhost",
|
|
67
|
+
"port": 5432,
|
|
68
|
+
},
|
|
69
|
+
"cache": {
|
|
70
|
+
"ttl": 300,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Mutable Default Arguments
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# VIOLATION: Mutable default shared across calls
|
|
79
|
+
def add_item(item: str, items: list[str] = []) -> list[str]:
|
|
80
|
+
items.append(item)
|
|
81
|
+
return items
|
|
82
|
+
|
|
83
|
+
# VIOLATION: Dict default mutated in place
|
|
84
|
+
def register(name: str, registry: dict[str, bool] = {}) -> None:
|
|
85
|
+
registry[name] = True
|
|
86
|
+
|
|
87
|
+
# VIOLATION: Set default
|
|
88
|
+
def collect(value: int, seen: set[int] = set()) -> set[int]:
|
|
89
|
+
seen.add(value)
|
|
90
|
+
return seen
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## String Formatting Violations
|
|
94
|
+
|
|
95
|
+
### Percent Formatting
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# VIOLATION: Old-style % formatting
|
|
99
|
+
message = "Hello %s, you have %d messages" % (name, count)
|
|
100
|
+
|
|
101
|
+
# VIOLATION: % formatting with dict — hard to read, error-prone
|
|
102
|
+
log = "%(timestamp)s - %(level)s - %(message)s" % log_data
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### String Concatenation in Loops
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# VIOLATION: O(n^2) string building
|
|
109
|
+
result = ""
|
|
110
|
+
for line in lines:
|
|
111
|
+
result += line + "\n"
|
|
112
|
+
|
|
113
|
+
# CORRECT: Use join
|
|
114
|
+
result = "\n".join(lines)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Global State Violations
|
|
118
|
+
|
|
119
|
+
### Module-Level Mutable State
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# VIOLATION: Global mutable state
|
|
123
|
+
_cache = {}
|
|
124
|
+
_connections = []
|
|
125
|
+
|
|
126
|
+
def get_cached(key: str) -> Any:
|
|
127
|
+
return _cache.get(key)
|
|
128
|
+
|
|
129
|
+
def add_connection(conn: Connection) -> None:
|
|
130
|
+
_connections.append(conn)
|
|
131
|
+
|
|
132
|
+
# VIOLATION: Global variable modified by functions
|
|
133
|
+
current_user = None
|
|
134
|
+
|
|
135
|
+
def login(username: str) -> None:
|
|
136
|
+
global current_user
|
|
137
|
+
current_user = authenticate(username)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Singleton via Module Import
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# VIOLATION: Hard-to-test singleton
|
|
144
|
+
# db.py
|
|
145
|
+
connection = create_connection(os.environ["DATABASE_URL"])
|
|
146
|
+
|
|
147
|
+
# service.py
|
|
148
|
+
from db import connection # Cannot mock or swap for tests
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Import Violations
|
|
152
|
+
|
|
153
|
+
### Wildcard Imports
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# VIOLATION: Pollutes namespace, hides dependencies
|
|
157
|
+
from os.path import *
|
|
158
|
+
from utils import *
|
|
159
|
+
|
|
160
|
+
# VIOLATION: Star import in __init__.py
|
|
161
|
+
# mypackage/__init__.py
|
|
162
|
+
from .models import *
|
|
163
|
+
from .services import *
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Circular Imports
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# VIOLATION: Circular dependency
|
|
170
|
+
# models.py
|
|
171
|
+
from services import UserService # services imports models too
|
|
172
|
+
|
|
173
|
+
# services.py
|
|
174
|
+
from models import User # Circular!
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Testing Violations
|
|
178
|
+
|
|
179
|
+
### Assert in Production Code
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# VIOLATION: assert is stripped with -O flag
|
|
183
|
+
def withdraw(account: Account, amount: float) -> None:
|
|
184
|
+
assert amount > 0, "Amount must be positive" # Disabled in production!
|
|
185
|
+
assert account.balance >= amount, "Insufficient funds"
|
|
186
|
+
account.balance -= amount
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### No pytest Fixtures
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# VIOLATION: Repeated setup in every test
|
|
193
|
+
def test_create_user():
|
|
194
|
+
db = Database(":memory:")
|
|
195
|
+
db.create_tables()
|
|
196
|
+
service = UserService(db)
|
|
197
|
+
# ... test logic
|
|
198
|
+
|
|
199
|
+
def test_update_user():
|
|
200
|
+
db = Database(":memory:") # Duplicated setup
|
|
201
|
+
db.create_tables()
|
|
202
|
+
service = UserService(db)
|
|
203
|
+
# ... test logic
|
|
204
|
+
```
|
|
@@ -95,7 +95,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
|
95
95
|
const [user, setUser] = useState<User | null>(null);
|
|
96
96
|
const login = async (creds: Credentials) => setUser(await authApi.login(creds));
|
|
97
97
|
const logout = () => { authApi.logout(); setUser(null); };
|
|
98
|
-
return <AuthContext
|
|
98
|
+
return <AuthContext value={{ user, login, logout }}>{children}</AuthContext>;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export function useAuth() {
|
|
@@ -429,9 +429,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
|
429
429
|
};
|
|
430
430
|
|
|
431
431
|
return (
|
|
432
|
-
<AuthContext
|
|
432
|
+
<AuthContext value={{ user, login, logout, isLoading }}>
|
|
433
433
|
{children}
|
|
434
|
-
</AuthContext
|
|
434
|
+
</AuthContext>
|
|
435
435
|
);
|
|
436
436
|
}
|
|
437
437
|
|
|
@@ -590,7 +590,7 @@ function SearchInput() {
|
|
|
590
590
|
```tsx
|
|
591
591
|
// CORRECT: Track previous value for comparisons
|
|
592
592
|
function usePrevious<T>(value: T): T | undefined {
|
|
593
|
-
const ref = useRef<T>();
|
|
593
|
+
const ref = useRef<T | undefined>(undefined);
|
|
594
594
|
|
|
595
595
|
useEffect(() => {
|
|
596
596
|
ref.current = value;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: regression-patterns
|
|
3
|
-
description:
|
|
3
|
+
description: This skill should be used when reviewing changes that may remove exports, change signatures, or alter behavior.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
allowed-tools: Read, Grep, Glob
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-methodology
|
|
3
|
-
description:
|
|
3
|
+
description: This skill should be used when performing a code review to apply the standard 6-step review process.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
allowed-tools: Read, Grep, Glob, Bash
|
|
6
6
|
---
|