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.
Files changed (152) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/README.md +23 -6
  3. package/dist/cli.js +2 -0
  4. package/dist/commands/ambient.js +5 -4
  5. package/dist/commands/init.js +4 -2
  6. package/dist/commands/memory.js +4 -4
  7. package/dist/commands/skills.d.ts +11 -0
  8. package/dist/commands/skills.js +116 -0
  9. package/dist/commands/uninstall.js +11 -1
  10. package/dist/plugins.js +67 -3
  11. package/dist/utils/installer.js +20 -2
  12. package/package.json +4 -2
  13. package/plugins/devflow-accessibility/.claude-plugin/plugin.json +22 -0
  14. package/plugins/devflow-ambient/.claude-plugin/plugin.json +4 -2
  15. package/plugins/devflow-ambient/README.md +8 -8
  16. package/plugins/devflow-ambient/commands/ambient.md +14 -14
  17. package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +16 -9
  18. package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +6 -2
  19. package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
  20. package/plugins/devflow-code-review/.claude-plugin/plugin.json +13 -6
  21. package/plugins/devflow-code-review/agents/reviewer.md +8 -0
  22. package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
  23. package/plugins/devflow-code-review/commands/code-review.md +12 -2
  24. package/plugins/devflow-code-review/skills/architecture-patterns/SKILL.md +1 -1
  25. package/plugins/devflow-code-review/skills/complexity-patterns/SKILL.md +1 -1
  26. package/plugins/devflow-code-review/skills/consistency-patterns/SKILL.md +1 -1
  27. package/plugins/devflow-code-review/skills/database-patterns/SKILL.md +1 -1
  28. package/plugins/devflow-code-review/skills/dependencies-patterns/SKILL.md +1 -1
  29. package/plugins/devflow-code-review/skills/documentation-patterns/SKILL.md +1 -1
  30. package/plugins/devflow-code-review/skills/performance-patterns/SKILL.md +1 -1
  31. package/plugins/devflow-code-review/skills/regression-patterns/SKILL.md +1 -1
  32. package/plugins/devflow-code-review/skills/review-methodology/SKILL.md +1 -1
  33. package/plugins/devflow-code-review/skills/security-patterns/SKILL.md +1 -1
  34. package/plugins/devflow-core-skills/.claude-plugin/plugin.json +10 -7
  35. package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +5 -8
  36. package/plugins/devflow-debug/.claude-plugin/plugin.json +10 -3
  37. package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +22 -0
  38. package/plugins/devflow-go/.claude-plugin/plugin.json +22 -0
  39. package/plugins/devflow-go/skills/go/SKILL.md +187 -0
  40. package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
  41. package/plugins/devflow-go/skills/go/references/detection.md +129 -0
  42. package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
  43. package/plugins/devflow-go/skills/go/references/violations.md +205 -0
  44. package/plugins/devflow-implement/.claude-plugin/plugin.json +19 -5
  45. package/plugins/devflow-implement/agents/coder.md +11 -6
  46. package/plugins/devflow-implement/skills/self-review/SKILL.md +1 -1
  47. package/plugins/devflow-java/.claude-plugin/plugin.json +22 -0
  48. package/plugins/devflow-java/skills/java/SKILL.md +183 -0
  49. package/plugins/devflow-java/skills/java/references/detection.md +120 -0
  50. package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
  51. package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
  52. package/plugins/devflow-java/skills/java/references/violations.md +213 -0
  53. package/plugins/devflow-python/.claude-plugin/plugin.json +22 -0
  54. package/plugins/devflow-python/skills/python/SKILL.md +188 -0
  55. package/plugins/devflow-python/skills/python/references/async.md +220 -0
  56. package/plugins/devflow-python/skills/python/references/detection.md +128 -0
  57. package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
  58. package/plugins/devflow-python/skills/python/references/violations.md +204 -0
  59. package/plugins/devflow-react/.claude-plugin/plugin.json +22 -0
  60. package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
  61. package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
  62. package/plugins/devflow-resolve/.claude-plugin/plugin.json +13 -3
  63. package/plugins/devflow-resolve/skills/security-patterns/SKILL.md +1 -1
  64. package/plugins/devflow-rust/.claude-plugin/plugin.json +22 -0
  65. package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
  66. package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
  67. package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
  68. package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
  69. package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
  70. package/plugins/devflow-self-review/.claude-plugin/plugin.json +10 -3
  71. package/plugins/devflow-self-review/skills/self-review/SKILL.md +1 -1
  72. package/plugins/devflow-specify/.claude-plugin/plugin.json +15 -4
  73. package/plugins/devflow-typescript/.claude-plugin/plugin.json +22 -0
  74. package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
  75. package/scripts/hooks/{ambient-prompt.sh → ambient-prompt} +4 -4
  76. package/scripts/hooks/{background-memory-update.sh → background-memory-update} +3 -3
  77. package/scripts/hooks/{ensure-memory-gitignore.sh → ensure-memory-gitignore} +1 -1
  78. package/scripts/hooks/{pre-compact-memory.sh → pre-compact-memory} +2 -2
  79. package/scripts/hooks/run-hook +23 -0
  80. package/scripts/hooks/session-start-memory +151 -0
  81. package/scripts/hooks/{stop-update-memory.sh → stop-update-memory} +4 -4
  82. package/shared/agents/coder.md +11 -6
  83. package/shared/agents/reviewer.md +8 -0
  84. package/shared/skills/ambient-router/SKILL.md +16 -9
  85. package/shared/skills/ambient-router/references/skill-catalog.md +6 -2
  86. package/shared/skills/architecture-patterns/SKILL.md +1 -1
  87. package/shared/skills/complexity-patterns/SKILL.md +1 -1
  88. package/shared/skills/consistency-patterns/SKILL.md +1 -1
  89. package/shared/skills/database-patterns/SKILL.md +1 -1
  90. package/shared/skills/dependencies-patterns/SKILL.md +1 -1
  91. package/shared/skills/documentation-patterns/SKILL.md +1 -1
  92. package/shared/skills/go/SKILL.md +187 -0
  93. package/shared/skills/go/references/concurrency.md +312 -0
  94. package/shared/skills/go/references/detection.md +129 -0
  95. package/shared/skills/go/references/patterns.md +232 -0
  96. package/shared/skills/go/references/violations.md +205 -0
  97. package/shared/skills/java/SKILL.md +183 -0
  98. package/shared/skills/java/references/detection.md +120 -0
  99. package/shared/skills/java/references/modern-java.md +270 -0
  100. package/shared/skills/java/references/patterns.md +235 -0
  101. package/shared/skills/java/references/violations.md +213 -0
  102. package/shared/skills/performance-patterns/SKILL.md +1 -1
  103. package/shared/skills/python/SKILL.md +188 -0
  104. package/shared/skills/python/references/async.md +220 -0
  105. package/shared/skills/python/references/detection.md +128 -0
  106. package/shared/skills/python/references/patterns.md +226 -0
  107. package/shared/skills/python/references/violations.md +204 -0
  108. package/shared/skills/react/SKILL.md +1 -1
  109. package/shared/skills/react/references/patterns.md +3 -3
  110. package/shared/skills/regression-patterns/SKILL.md +1 -1
  111. package/shared/skills/review-methodology/SKILL.md +1 -1
  112. package/shared/skills/rust/SKILL.md +193 -0
  113. package/shared/skills/rust/references/detection.md +131 -0
  114. package/shared/skills/rust/references/ownership.md +242 -0
  115. package/shared/skills/rust/references/patterns.md +210 -0
  116. package/shared/skills/rust/references/violations.md +191 -0
  117. package/shared/skills/security-patterns/SKILL.md +1 -1
  118. package/shared/skills/self-review/SKILL.md +1 -1
  119. package/shared/skills/test-driven-development/SKILL.md +5 -8
  120. package/shared/skills/typescript/references/patterns.md +3 -3
  121. package/src/templates/settings.json +3 -3
  122. package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
  123. package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
  124. package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
  125. package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
  126. package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
  127. package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
  128. package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
  129. package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
  130. package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
  131. package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
  132. package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
  133. package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
  134. package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
  135. package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
  136. package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
  137. package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
  138. package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
  139. package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
  140. package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
  141. package/scripts/hooks/session-start-memory.sh +0 -126
  142. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
  143. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
  144. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
  145. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
  146. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
  147. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
  148. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
  149. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
  150. /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
  151. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
  152. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
@@ -0,0 +1,213 @@
1
+ # Common Java Violations
2
+
3
+ Extended violation patterns for Java reviews. Reference from main SKILL.md.
4
+
5
+ ---
6
+
7
+ ## Null Returns
8
+
9
+ ### Returning null Instead of Optional
10
+
11
+ ```java
12
+ // VIOLATION: Null return forces callers to null-check
13
+ public User findById(String id) {
14
+ return userMap.get(id); // Returns null if absent
15
+ }
16
+
17
+ // Callers must remember to check:
18
+ User user = findById(id);
19
+ user.getName(); // NullPointerException if not found
20
+ ```
21
+
22
+ ### Nullable Collections
23
+
24
+ ```java
25
+ // VIOLATION: Returning null instead of empty collection
26
+ public List<Order> getOrders(String userId) {
27
+ List<Order> orders = orderMap.get(userId);
28
+ return orders; // null if user has no orders
29
+ }
30
+
31
+ // Callers iterate without checking:
32
+ for (Order o : getOrders(userId)) { // NullPointerException
33
+ process(o);
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Raw Types
40
+
41
+ ### Unparameterized Generics
42
+
43
+ ```java
44
+ // VIOLATION: Raw List - no type safety
45
+ List users = new ArrayList();
46
+ users.add("not a user"); // No compile error
47
+ users.add(42); // No compile error
48
+ User first = (User) users.get(0); // ClassCastException at runtime
49
+
50
+ // VIOLATION: Raw Map
51
+ Map cache = new HashMap();
52
+ cache.put(123, "value");
53
+ String val = (String) cache.get(123); // Unsafe cast
54
+ ```
55
+
56
+ ### Raw Type in Method Signatures
57
+
58
+ ```java
59
+ // VIOLATION: Raw Comparable
60
+ public class Price implements Comparable {
61
+ public int compareTo(Object other) {
62
+ return Double.compare(this.amount, ((Price) other).amount); // Unsafe cast
63
+ }
64
+ }
65
+
66
+ // VIOLATION: Raw Iterator
67
+ Iterator it = collection.iterator();
68
+ while (it.hasNext()) {
69
+ String s = (String) it.next(); // Unsafe cast
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Checked Exception Abuse
76
+
77
+ ### Broad Throws Declarations
78
+
79
+ ```java
80
+ // VIOLATION: throws Exception - hides what can actually go wrong
81
+ public User createUser(String name) throws Exception {
82
+ // Callers must catch Exception, can't handle specific failures
83
+ }
84
+
85
+ // VIOLATION: Wrapping everything in checked exceptions
86
+ public void process(String data) throws ProcessingException {
87
+ try {
88
+ Integer.parseInt(data);
89
+ } catch (NumberFormatException e) {
90
+ throw new ProcessingException("Failed", e); // Unnecessary wrapping
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### Swallowed Exceptions
96
+
97
+ ```java
98
+ // VIOLATION: Empty catch block
99
+ try {
100
+ connection.close();
101
+ } catch (SQLException e) {
102
+ // silently ignored - resource leak hidden
103
+ }
104
+
105
+ // VIOLATION: Catching and logging only
106
+ try {
107
+ processPayment(order);
108
+ } catch (PaymentException e) {
109
+ logger.error("Payment failed", e);
110
+ // Continues as if nothing happened - order in inconsistent state
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Mutable Data Objects
117
+
118
+ ### JavaBean-Style Mutability
119
+
120
+ ```java
121
+ // VIOLATION: Mutable DTO with setters
122
+ public class UserDTO {
123
+ private String name;
124
+ private String email;
125
+
126
+ public void setName(String name) { this.name = name; }
127
+ public void setEmail(String email) { this.email = email; }
128
+ public String getName() { return name; }
129
+ public String getEmail() { return email; }
130
+ }
131
+
132
+ // Anyone can modify at any time:
133
+ dto.setName("changed"); // No control over when/where mutation happens
134
+ ```
135
+
136
+ ### Exposing Internal Mutable State
137
+
138
+ ```java
139
+ // VIOLATION: Getter returns mutable internal list
140
+ public class Team {
141
+ private List<String> members = new ArrayList<>();
142
+
143
+ public List<String> getMembers() {
144
+ return members; // Caller can modify internal state
145
+ }
146
+ }
147
+
148
+ // External code breaks encapsulation:
149
+ team.getMembers().clear(); // Empties the team's internal list
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Deep Inheritance
155
+
156
+ ### Fragile Base Class
157
+
158
+ ```java
159
+ // VIOLATION: Deep hierarchy creates tight coupling
160
+ public abstract class AbstractEntity { ... }
161
+ public abstract class AbstractAuditableEntity extends AbstractEntity { ... }
162
+ public abstract class AbstractVersionedEntity extends AbstractAuditableEntity { ... }
163
+ public class User extends AbstractVersionedEntity { ... }
164
+
165
+ // Changing any base class ripples through all descendants
166
+ // Testing requires understanding 4 levels of behavior
167
+ ```
168
+
169
+ ### Inheritance for Code Reuse
170
+
171
+ ```java
172
+ // VIOLATION: Extending just to reuse utility methods
173
+ public class OrderService extends BaseService {
174
+ // Only extends BaseService to get logAndAudit() method
175
+ public void processOrder(Order order) {
176
+ logAndAudit("processing", order.getId()); // Inherited utility
177
+ }
178
+ }
179
+ // Should be: inject a LogAuditService instead
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Concurrency Violations
185
+
186
+ ### Shared Mutable State Without Synchronization
187
+
188
+ ```java
189
+ // VIOLATION: HashMap accessed from multiple threads
190
+ private Map<String, Session> sessions = new HashMap<>();
191
+
192
+ public void addSession(String id, Session s) {
193
+ sessions.put(id, s); // Not thread-safe
194
+ }
195
+ ```
196
+
197
+ ### Double-Checked Locking Done Wrong
198
+
199
+ ```java
200
+ // VIOLATION: Missing volatile on lazily-initialized field
201
+ private ExpensiveObject instance;
202
+
203
+ public ExpensiveObject getInstance() {
204
+ if (instance == null) {
205
+ synchronized (this) {
206
+ if (instance == null) {
207
+ instance = new ExpensiveObject(); // Partially constructed object visible
208
+ }
209
+ }
210
+ }
211
+ return instance;
212
+ }
213
+ ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: performance-patterns
3
- description: Performance analysis patterns for code review. Detects N+1 queries, memory leaks, unbounded allocations, I/O bottlenecks, and missing caching opportunities. Loaded by Reviewer agent when focus=performance.
3
+ description: This skill should be used when reviewing code for N+1 queries, memory leaks, or I/O bottlenecks.
4
4
  user-invocable: false
5
5
  allowed-tools: Read, Grep, Glob
6
6
  ---
@@ -0,0 +1,188 @@
1
+ ---
2
+ name: python
3
+ description: This skill should be used when the user works with Python files (.py), asks about "type hints", "protocols", "dataclasses", "async/await", "decorators", or discusses Pythonic patterns and data modeling. Provides patterns for type safety, error handling, data modeling, and async programming.
4
+ user-invocable: false
5
+ allowed-tools: Read, Grep, Glob
6
+ activation:
7
+ file-patterns:
8
+ - "**/*.py"
9
+ exclude:
10
+ - "venv/**"
11
+ - ".venv/**"
12
+ - "**/__pycache__/**"
13
+ ---
14
+
15
+ # Python Patterns
16
+
17
+ Reference for Python-specific patterns, type safety, and idioms.
18
+
19
+ ## Iron Law
20
+
21
+ > **EXPLICIT IS BETTER THAN IMPLICIT**
22
+ >
23
+ > Type-hint every function signature. Name every exception. Use dataclasses over raw dicts.
24
+ > Python's flexibility is a strength only when boundaries are explicit. Implicit behavior
25
+ > causes debugging nightmares and makes codebases hostile to newcomers.
26
+
27
+ ## When This Skill Activates
28
+
29
+ - Working with Python codebases
30
+ - Designing typed APIs with type hints
31
+ - Modeling data with dataclasses or Pydantic
32
+ - Implementing async code
33
+ - Structuring Python packages
34
+
35
+ ---
36
+
37
+ ## Type Safety
38
+
39
+ ### Type Hint Everything
40
+
41
+ ```python
42
+ # BAD: def process(data, config): ...
43
+ # GOOD:
44
+ def process(data: list[dict[str, Any]], config: AppConfig) -> ProcessResult:
45
+ ...
46
+ ```
47
+
48
+ ### Use Protocols for Structural Typing
49
+
50
+ ```python
51
+ from typing import Protocol
52
+
53
+ class Repository(Protocol):
54
+ def find_by_id(self, id: str) -> User | None: ...
55
+ def save(self, entity: User) -> User: ...
56
+
57
+ # Any class with these methods satisfies Repository — no inheritance needed
58
+ ```
59
+
60
+ ### Strict Optional Handling
61
+
62
+ ```python
63
+ # BAD: def get_name(user): return user.name
64
+ # GOOD:
65
+ def get_name(user: User | None) -> str:
66
+ if user is None:
67
+ return "Anonymous"
68
+ return user.name
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Error Handling
74
+
75
+ ### Custom Exception Hierarchies
76
+
77
+ ```python
78
+ class AppError(Exception):
79
+ """Base application error."""
80
+
81
+ class NotFoundError(AppError):
82
+ def __init__(self, entity: str, id: str) -> None:
83
+ super().__init__(f"{entity} {id} not found")
84
+ self.entity = entity
85
+ self.id = id
86
+
87
+ class ValidationError(AppError):
88
+ def __init__(self, field: str, message: str) -> None:
89
+ super().__init__(f"Validation failed for {field}: {message}")
90
+ ```
91
+
92
+ ### Context Managers for Resources
93
+
94
+ ```python
95
+ from contextlib import contextmanager
96
+
97
+ @contextmanager
98
+ def database_transaction(conn: Connection):
99
+ try:
100
+ yield conn
101
+ conn.commit()
102
+ except Exception:
103
+ conn.rollback()
104
+ raise
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Data Modeling
110
+
111
+ ### Dataclasses Over Raw Dicts
112
+
113
+ ```python
114
+ # BAD: user = {"name": "Alice", "email": "alice@example.com"}
115
+ # GOOD:
116
+ @dataclass(frozen=True)
117
+ class User:
118
+ name: str
119
+ email: str
120
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
121
+ ```
122
+
123
+ ### Pydantic for Validation at Boundaries
124
+
125
+ ```python
126
+ from pydantic import BaseModel, EmailStr
127
+
128
+ class CreateUserRequest(BaseModel):
129
+ name: str
130
+ email: EmailStr
131
+ age: int = Field(ge=0, le=150)
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Pythonic Patterns
137
+
138
+ ```python
139
+ # Comprehensions over loops for transforms
140
+ names = [user.name for user in users if user.active]
141
+
142
+ # Enumerate over manual index tracking
143
+ for i, item in enumerate(items):
144
+ process(i, item)
145
+
146
+ # EAFP: Easier to Ask Forgiveness than Permission
147
+ try:
148
+ value = mapping[key]
149
+ except KeyError:
150
+ value = default
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Anti-Patterns
156
+
157
+ | Pattern | Bad | Good |
158
+ |---------|-----|------|
159
+ | Bare except | `except:` | `except (ValueError, KeyError):` |
160
+ | Mutable default | `def fn(items=[])` | `def fn(items: list | None = None)` |
161
+ | No type hints | `def process(data)` | `def process(data: DataFrame) -> Result` |
162
+ | String typing | `x: "MyClass"` (without reason) | `from __future__ import annotations` |
163
+ | God class | `class App` with 50 methods | Compose smaller focused classes |
164
+
165
+ ---
166
+
167
+ ## Extended References
168
+
169
+ For additional patterns and examples:
170
+ - `references/violations.md` - Common Python violations
171
+ - `references/patterns.md` - Extended Python patterns
172
+ - `references/detection.md` - Detection patterns for Python issues
173
+ - `references/async.md` - Async Python patterns
174
+
175
+ ---
176
+
177
+ ## Checklist
178
+
179
+ - [ ] All functions have type hints (params + return)
180
+ - [ ] Custom exceptions with meaningful messages
181
+ - [ ] Dataclasses or Pydantic for structured data
182
+ - [ ] No bare `except:` clauses
183
+ - [ ] No mutable default arguments
184
+ - [ ] Context managers for resource management
185
+ - [ ] `from __future__ import annotations` for forward refs
186
+ - [ ] Protocols for structural typing (not ABC unless needed)
187
+ - [ ] Comprehensions for simple transforms
188
+ - [ ] Tests use pytest with fixtures
@@ -0,0 +1,220 @@
1
+ # Async Python Patterns
2
+
3
+ Deep-dive on async Python programming. Reference from main SKILL.md.
4
+
5
+ ## Core asyncio Patterns
6
+
7
+ ### Basic Async Function
8
+
9
+ ```python
10
+ import asyncio
11
+ from typing import Any
12
+
13
+ async def fetch_user(user_id: str) -> User:
14
+ async with aiohttp.ClientSession() as session:
15
+ async with session.get(f"/api/users/{user_id}") as response:
16
+ data = await response.json()
17
+ return User.model_validate(data)
18
+ ```
19
+
20
+ ### Concurrent Execution with gather
21
+
22
+ ```python
23
+ async def load_dashboard(user_id: str) -> Dashboard:
24
+ # Independent fetches run concurrently
25
+ user, orders, preferences = await asyncio.gather(
26
+ fetch_user(user_id),
27
+ fetch_orders(user_id),
28
+ fetch_preferences(user_id),
29
+ )
30
+ return Dashboard(user=user, orders=orders, preferences=preferences)
31
+ ```
32
+
33
+ ## Structured Concurrency with TaskGroup
34
+
35
+ ### TaskGroup for Safe Concurrency (Python 3.11+)
36
+
37
+ ```python
38
+ async def process_batch(items: list[Item]) -> list[Result]:
39
+ results: list[Result] = []
40
+
41
+ async with asyncio.TaskGroup() as tg:
42
+ for item in items:
43
+ tg.create_task(process_and_collect(item, results))
44
+
45
+ return results
46
+
47
+ async def process_and_collect(item: Item, results: list[Result]) -> None:
48
+ result = await process_item(item)
49
+ results.append(result)
50
+ ```
51
+
52
+ ### Error Handling with TaskGroup
53
+
54
+ ```python
55
+ async def resilient_batch(items: list[Item]) -> tuple[list[Result], list[Error]]:
56
+ results: list[Result] = []
57
+ errors: list[Error] = []
58
+
59
+ # TaskGroup cancels all tasks if one raises — wrap individual tasks
60
+ async def safe_process(item: Item) -> None:
61
+ try:
62
+ result = await process_item(item)
63
+ results.append(result)
64
+ except ProcessingError as e:
65
+ errors.append(Error(item_id=item.id, message=str(e)))
66
+
67
+ async with asyncio.TaskGroup() as tg:
68
+ for item in items:
69
+ tg.create_task(safe_process(item))
70
+
71
+ return results, errors
72
+ ```
73
+
74
+ ## Async Generators
75
+
76
+ ### Streaming Results
77
+
78
+ ```python
79
+ from typing import AsyncGenerator
80
+
81
+ async def stream_results(query: str, params: tuple = ()) -> AsyncGenerator[Record, None]:
82
+ async with get_connection() as conn:
83
+ cursor = await conn.execute(query, params)
84
+ async for row in cursor:
85
+ yield Record.from_row(row)
86
+
87
+ # Usage
88
+ async for record in stream_results("SELECT * FROM events WHERE type = ?", ("click",)):
89
+ await process(record)
90
+ ```
91
+
92
+ ### Async Generator with Cleanup
93
+
94
+ ```python
95
+ async def paginated_fetch(url: str, page_size: int = 100) -> AsyncGenerator[Item, None]:
96
+ page = 0
97
+ while True:
98
+ response = await fetch_page(url, page=page, size=page_size)
99
+ if not response.items:
100
+ break
101
+ for item in response.items:
102
+ yield item
103
+ page += 1
104
+ ```
105
+
106
+ ## Semaphore for Rate Limiting
107
+
108
+ ### Bounded Concurrency
109
+
110
+ ```python
111
+ async def fetch_all(urls: list[str], max_concurrent: int = 10) -> list[Response]:
112
+ semaphore = asyncio.Semaphore(max_concurrent)
113
+
114
+ async def bounded_fetch(url: str) -> Response:
115
+ async with semaphore:
116
+ return await fetch(url)
117
+
118
+ return await asyncio.gather(*[bounded_fetch(url) for url in urls])
119
+ ```
120
+
121
+ ## aiohttp Patterns
122
+
123
+ ### Client Session Management
124
+
125
+ ```python
126
+ from contextlib import asynccontextmanager
127
+ from typing import AsyncGenerator
128
+
129
+ @asynccontextmanager
130
+ async def api_client(base_url: str) -> AsyncGenerator[aiohttp.ClientSession, None]:
131
+ timeout = aiohttp.ClientTimeout(total=30)
132
+ async with aiohttp.ClientSession(base_url, timeout=timeout) as session:
133
+ yield session
134
+
135
+ # Usage — session reused for multiple requests
136
+ async def sync_users(user_ids: list[str]) -> list[User]:
137
+ async with api_client("https://api.example.com") as client:
138
+ tasks = [fetch_user_with_session(client, uid) for uid in user_ids]
139
+ return await asyncio.gather(*tasks)
140
+
141
+ async def fetch_user_with_session(
142
+ client: aiohttp.ClientSession, user_id: str
143
+ ) -> User:
144
+ async with client.get(f"/users/{user_id}") as response:
145
+ response.raise_for_status()
146
+ data = await response.json()
147
+ return User.model_validate(data)
148
+ ```
149
+
150
+ ## Timeout Patterns
151
+
152
+ ### Per-Operation Timeout
153
+
154
+ ```python
155
+ async def fetch_with_timeout(url: str, timeout_seconds: float = 5.0) -> dict[str, Any]:
156
+ try:
157
+ async with asyncio.timeout(timeout_seconds):
158
+ return await fetch(url)
159
+ except TimeoutError:
160
+ raise OperationTimeout(f"Request to {url} timed out after {timeout_seconds}s")
161
+ ```
162
+
163
+ ## Anti-Patterns
164
+
165
+ ### Blocking Calls in Async Code
166
+
167
+ ```python
168
+ # VIOLATION: Blocks the event loop
169
+ async def bad_fetch(url: str) -> str:
170
+ import requests
171
+ return requests.get(url).text # Blocks entire event loop!
172
+
173
+ # CORRECT: Use async library or run in executor
174
+ async def good_fetch(url: str) -> str:
175
+ async with aiohttp.ClientSession() as session:
176
+ async with session.get(url) as response:
177
+ return await response.text()
178
+
179
+ # CORRECT: Offload blocking call to thread pool
180
+ async def run_blocking(func: Callable[..., R], *args: Any) -> R:
181
+ loop = asyncio.get_running_loop()
182
+ return await loop.run_in_executor(None, func, *args)
183
+ ```
184
+
185
+ ### Fire-and-Forget Without Error Handling
186
+
187
+ ```python
188
+ # VIOLATION: Exception silently lost
189
+ async def bad_handler(event: Event) -> None:
190
+ asyncio.create_task(send_notification(event)) # Error vanishes
191
+
192
+ # CORRECT: Track the task and handle errors
193
+ async def good_handler(event: Event) -> None:
194
+ task = asyncio.create_task(send_notification(event))
195
+ task.add_done_callback(handle_task_exception)
196
+
197
+ def handle_task_exception(task: asyncio.Task[Any]) -> None:
198
+ if not task.cancelled() and task.exception() is not None:
199
+ logger.error("Background task failed", exc_info=task.exception())
200
+ ```
201
+
202
+ ### Sequential When Parallel Is Safe
203
+
204
+ ```python
205
+ # VIOLATION: Unnecessarily sequential — 3x slower
206
+ async def slow_load(user_id: str) -> Dashboard:
207
+ user = await fetch_user(user_id)
208
+ orders = await fetch_orders(user_id)
209
+ prefs = await fetch_preferences(user_id)
210
+ return Dashboard(user=user, orders=orders, preferences=prefs)
211
+
212
+ # CORRECT: Concurrent — ~1x latency
213
+ async def fast_load(user_id: str) -> Dashboard:
214
+ user, orders, prefs = await asyncio.gather(
215
+ fetch_user(user_id),
216
+ fetch_orders(user_id),
217
+ fetch_preferences(user_id),
218
+ )
219
+ return Dashboard(user=user, orders=orders, preferences=prefs)
220
+ ```