devflow-kit 1.0.0 → 1.2.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 -0
- package/README.md +35 -11
- package/dist/cli.js +5 -1
- package/dist/commands/ambient.d.ts +18 -0
- package/dist/commands/ambient.js +136 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +97 -10
- package/dist/commands/memory.d.ts +22 -0
- package/dist/commands/memory.js +175 -0
- package/dist/commands/uninstall.js +72 -5
- package/dist/plugins.js +74 -3
- package/dist/utils/post-install.d.ts +12 -0
- package/dist/utils/post-install.js +82 -1
- package/dist/utils/safe-delete-install.d.ts +7 -0
- package/dist/utils/safe-delete-install.js +40 -5
- package/package.json +2 -1
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +7 -0
- package/plugins/devflow-ambient/README.md +49 -0
- package/plugins/devflow-ambient/commands/ambient.md +110 -0
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +89 -0
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +68 -0
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
- 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-core-skills/.claude-plugin/plugin.json +3 -6
- package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +10 -6
- package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +139 -0
- package/plugins/devflow-core-skills/skills/test-driven-development/references/rationalization-prevention.md +111 -0
- package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +15 -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 +1 -3
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-java/.claude-plugin/plugin.json +15 -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 +15 -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 +15 -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 +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -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 +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/scripts/hooks/ambient-prompt.sh +48 -0
- package/scripts/hooks/background-memory-update.sh +49 -8
- package/scripts/hooks/ensure-memory-gitignore.sh +17 -0
- package/scripts/hooks/pre-compact-memory.sh +12 -6
- package/scripts/hooks/session-start-memory.sh +50 -8
- package/scripts/hooks/stop-update-memory.sh +10 -6
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +89 -0
- package/shared/skills/ambient-router/references/skill-catalog.md +68 -0
- package/shared/skills/docs-framework/SKILL.md +10 -6
- 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/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/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/test-driven-development/SKILL.md +139 -0
- package/shared/skills/test-driven-development/references/rationalization-prevention.md +111 -0
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/src/templates/managed-settings.json +14 -0
- 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/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,235 @@
|
|
|
1
|
+
# Extended Java Patterns
|
|
2
|
+
|
|
3
|
+
Correct patterns for Java development. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Builder Pattern (Type-Safe)
|
|
8
|
+
|
|
9
|
+
```java
|
|
10
|
+
// Compile-time enforcement of required fields
|
|
11
|
+
public record EmailMessage(String to, String subject, String body, List<String> cc) {
|
|
12
|
+
|
|
13
|
+
public static Builder builder() { return new Builder(); }
|
|
14
|
+
|
|
15
|
+
public static class Builder {
|
|
16
|
+
private String to;
|
|
17
|
+
private String subject;
|
|
18
|
+
private String body;
|
|
19
|
+
private List<String> cc = List.of();
|
|
20
|
+
|
|
21
|
+
public Builder to(String to) { this.to = Objects.requireNonNull(to); return this; }
|
|
22
|
+
public Builder subject(String subject) { this.subject = Objects.requireNonNull(subject); return this; }
|
|
23
|
+
public Builder body(String body) { this.body = Objects.requireNonNull(body); return this; }
|
|
24
|
+
public Builder cc(List<String> cc) { this.cc = List.copyOf(cc); return this; }
|
|
25
|
+
|
|
26
|
+
public EmailMessage build() {
|
|
27
|
+
Objects.requireNonNull(to, "to is required");
|
|
28
|
+
Objects.requireNonNull(subject, "subject is required");
|
|
29
|
+
Objects.requireNonNull(body, "body is required");
|
|
30
|
+
return new EmailMessage(to, subject, body, cc);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Usage
|
|
36
|
+
var email = EmailMessage.builder()
|
|
37
|
+
.to("user@example.com")
|
|
38
|
+
.subject("Welcome")
|
|
39
|
+
.body("Hello!")
|
|
40
|
+
.build();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Strategy via Interfaces
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
// Define strategy as functional interface
|
|
49
|
+
@FunctionalInterface
|
|
50
|
+
public interface PricingStrategy {
|
|
51
|
+
Money calculatePrice(Order order);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Implementations as lambdas or classes
|
|
55
|
+
PricingStrategy standard = order -> order.subtotal();
|
|
56
|
+
PricingStrategy discounted = order -> order.subtotal().multiply(0.9);
|
|
57
|
+
PricingStrategy tiered = order -> {
|
|
58
|
+
if (order.itemCount() > 10) return order.subtotal().multiply(0.8);
|
|
59
|
+
if (order.itemCount() > 5) return order.subtotal().multiply(0.9);
|
|
60
|
+
return order.subtotal();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Inject strategy
|
|
64
|
+
public class OrderService {
|
|
65
|
+
private final PricingStrategy pricing;
|
|
66
|
+
|
|
67
|
+
public OrderService(PricingStrategy pricing) {
|
|
68
|
+
this.pricing = pricing;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public Money calculateTotal(Order order) {
|
|
72
|
+
return pricing.calculatePrice(order);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Repository Pattern
|
|
80
|
+
|
|
81
|
+
```java
|
|
82
|
+
public interface UserRepository {
|
|
83
|
+
Optional<User> findById(String id);
|
|
84
|
+
List<User> findByEmail(String email);
|
|
85
|
+
User save(User user);
|
|
86
|
+
void deleteById(String id);
|
|
87
|
+
boolean existsById(String id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Implementation with constructor injection
|
|
91
|
+
public class JpaUserRepository implements UserRepository {
|
|
92
|
+
private final EntityManager em;
|
|
93
|
+
|
|
94
|
+
public JpaUserRepository(EntityManager em) {
|
|
95
|
+
this.em = em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
public Optional<User> findById(String id) {
|
|
100
|
+
return Optional.ofNullable(em.find(User.class, id));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Override
|
|
104
|
+
public User save(User user) {
|
|
105
|
+
return em.merge(user);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Value Objects
|
|
113
|
+
|
|
114
|
+
```java
|
|
115
|
+
// Immutable value object with validation
|
|
116
|
+
public record Money(BigDecimal amount, Currency currency) {
|
|
117
|
+
public Money {
|
|
118
|
+
Objects.requireNonNull(amount, "amount must not be null");
|
|
119
|
+
Objects.requireNonNull(currency, "currency must not be null");
|
|
120
|
+
if (amount.scale() > currency.getDefaultFractionDigits()) {
|
|
121
|
+
throw new IllegalArgumentException("Scale exceeds currency precision");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public Money add(Money other) {
|
|
126
|
+
requireSameCurrency(other);
|
|
127
|
+
return new Money(amount.add(other.amount), currency);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public Money multiply(double factor) {
|
|
131
|
+
return new Money(
|
|
132
|
+
amount.multiply(BigDecimal.valueOf(factor))
|
|
133
|
+
.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_UP),
|
|
134
|
+
currency
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private void requireSameCurrency(Money other) {
|
|
139
|
+
if (!currency.equals(other.currency)) {
|
|
140
|
+
throw new IllegalArgumentException(
|
|
141
|
+
"Cannot combine %s and %s".formatted(currency, other.currency)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Event-Driven Pattern
|
|
151
|
+
|
|
152
|
+
```java
|
|
153
|
+
// Domain event as sealed interface
|
|
154
|
+
public sealed interface OrderEvent permits OrderCreated, OrderShipped, OrderCancelled {
|
|
155
|
+
String orderId();
|
|
156
|
+
Instant occurredAt();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public record OrderCreated(String orderId, Instant occurredAt, List<LineItem> items)
|
|
160
|
+
implements OrderEvent {}
|
|
161
|
+
|
|
162
|
+
public record OrderShipped(String orderId, Instant occurredAt, String trackingNumber)
|
|
163
|
+
implements OrderEvent {}
|
|
164
|
+
|
|
165
|
+
public record OrderCancelled(String orderId, Instant occurredAt, String reason)
|
|
166
|
+
implements OrderEvent {}
|
|
167
|
+
|
|
168
|
+
// Event handler with exhaustive matching
|
|
169
|
+
public class OrderEventHandler {
|
|
170
|
+
public void handle(OrderEvent event) {
|
|
171
|
+
switch (event) {
|
|
172
|
+
case OrderCreated e -> notifyWarehouse(e.items());
|
|
173
|
+
case OrderShipped e -> sendTrackingEmail(e.trackingNumber());
|
|
174
|
+
case OrderCancelled e -> processRefund(e.orderId(), e.reason());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Stream Pipelines
|
|
183
|
+
|
|
184
|
+
```java
|
|
185
|
+
// Declarative collection processing
|
|
186
|
+
public List<String> getActiveUserEmails(List<User> users) {
|
|
187
|
+
return users.stream()
|
|
188
|
+
.filter(User::isActive)
|
|
189
|
+
.map(User::email)
|
|
190
|
+
.filter(email -> email.contains("@"))
|
|
191
|
+
.sorted()
|
|
192
|
+
.toList(); // Unmodifiable list (Java 16+)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Grouping and aggregation
|
|
196
|
+
public Map<Department, Long> countByDepartment(List<Employee> employees) {
|
|
197
|
+
return employees.stream()
|
|
198
|
+
.collect(Collectors.groupingBy(Employee::department, Collectors.counting()));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Reducing to summary
|
|
202
|
+
public record OrderSummary(int count, Money total) {}
|
|
203
|
+
|
|
204
|
+
public OrderSummary summarize(List<Order> orders) {
|
|
205
|
+
return orders.stream()
|
|
206
|
+
.reduce(
|
|
207
|
+
new OrderSummary(0, Money.ZERO),
|
|
208
|
+
(summary, order) -> new OrderSummary(
|
|
209
|
+
summary.count() + 1,
|
|
210
|
+
summary.total().add(order.total())
|
|
211
|
+
),
|
|
212
|
+
(a, b) -> new OrderSummary(a.count() + b.count(), a.total().add(b.total()))
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Dependency Injection (Manual)
|
|
220
|
+
|
|
221
|
+
```java
|
|
222
|
+
// Composition root wires everything together
|
|
223
|
+
public class Application {
|
|
224
|
+
public static void main(String[] args) {
|
|
225
|
+
var config = Config.load();
|
|
226
|
+
var dataSource = createDataSource(config);
|
|
227
|
+
var userRepo = new JpaUserRepository(dataSource);
|
|
228
|
+
var eventBus = new InMemoryEventBus();
|
|
229
|
+
var userService = new UserService(userRepo, eventBus);
|
|
230
|
+
var userController = new UserController(userService);
|
|
231
|
+
|
|
232
|
+
startServer(config.port(), userController);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
@@ -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
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "devflow-python",
|
|
3
|
+
"description": "Python language patterns - type hints, protocols, dataclasses, async programming",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "DevFlow Contributors",
|
|
6
|
+
"email": "dean@keren.dev"
|
|
7
|
+
},
|
|
8
|
+
"version": "1.2.0",
|
|
9
|
+
"homepage": "https://github.com/dean0x/devflow",
|
|
10
|
+
"repository": "https://github.com/dean0x/devflow",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": ["python", "type-hints", "dataclasses", "async"],
|
|
13
|
+
"agents": [],
|
|
14
|
+
"skills": ["python"]
|
|
15
|
+
}
|
|
@@ -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
|