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,120 @@
|
|
|
1
|
+
# Detection Patterns for Java Issues
|
|
2
|
+
|
|
3
|
+
Grep and regex patterns for automated detection of Java violations. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Null Returns
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Methods returning null instead of Optional
|
|
11
|
+
rg 'return\s+null\s*;' --type java
|
|
12
|
+
|
|
13
|
+
# Nullable method parameters without validation
|
|
14
|
+
rg 'public\s+\w+\s+\w+\((?!.*@NonNull).*\)' --type java
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Raw Types
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Raw List, Map, Set, Collection usage
|
|
23
|
+
rg '\b(List|Map|Set|Collection|Iterator|Iterable)\b(?!\s*<)' --type java
|
|
24
|
+
|
|
25
|
+
# Raw Comparable implementation
|
|
26
|
+
rg 'implements\s+Comparable\b(?!\s*<)' --type java
|
|
27
|
+
|
|
28
|
+
# Unsafe casts from Object
|
|
29
|
+
rg '\(\s*(String|Integer|Long|Double)\s*\)\s*\w+\.get' --type java
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Deep Inheritance
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Classes extending non-Object/non-interface classes (potential deep hierarchy)
|
|
38
|
+
rg 'class\s+\w+\s+extends\s+(?!Object\b)\w+' --type java
|
|
39
|
+
|
|
40
|
+
# Abstract class chains (look for multiple levels)
|
|
41
|
+
rg 'abstract\s+class\s+\w+\s+extends\s+Abstract' --type java
|
|
42
|
+
|
|
43
|
+
# Count inheritance depth per file
|
|
44
|
+
rg 'extends\s+\w+' --type java --count
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Missing Try-with-Resources
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Manual close() calls (should use try-with-resources)
|
|
53
|
+
rg '\.close\(\)\s*;' --type java
|
|
54
|
+
|
|
55
|
+
# InputStream/OutputStream/Connection created outside try-with-resources
|
|
56
|
+
rg 'new\s+(FileInputStream|FileOutputStream|BufferedReader|BufferedWriter|Socket)\(' --type java
|
|
57
|
+
|
|
58
|
+
# getConnection() without try-with-resources context
|
|
59
|
+
rg '\.getConnection\(\)' --type java
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Checked Exception Abuse
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Broad throws declarations
|
|
68
|
+
rg 'throws\s+Exception\b' --type java
|
|
69
|
+
|
|
70
|
+
# Empty catch blocks
|
|
71
|
+
rg -U 'catch\s*\([^)]+\)\s*\{\s*\}' --type java --multiline
|
|
72
|
+
|
|
73
|
+
# Catch-and-ignore (catch with only a comment or log)
|
|
74
|
+
rg -U 'catch\s*\([^)]+\)\s*\{\s*(//|/\*|logger\.(debug|trace))' --type java --multiline
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Mutable Data Objects
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Setter methods (JavaBean anti-pattern for data objects)
|
|
83
|
+
rg 'public\s+void\s+set[A-Z]\w*\(' --type java
|
|
84
|
+
|
|
85
|
+
# Mutable collections returned from getters
|
|
86
|
+
rg 'return\s+(this\.)?(list|map|set|collection)\s*;' --type java -i
|
|
87
|
+
|
|
88
|
+
# Missing defensive copies in constructors
|
|
89
|
+
rg 'this\.\w+\s*=\s*\w+\s*;' --type java
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Concurrency Issues
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# HashMap in concurrent context (should be ConcurrentHashMap)
|
|
98
|
+
rg 'new\s+HashMap\b' --type java
|
|
99
|
+
|
|
100
|
+
# Missing volatile on shared fields
|
|
101
|
+
rg 'private\s+(?!volatile\s)(?!final\s)\w+\s+\w+\s*=' --type java
|
|
102
|
+
|
|
103
|
+
# Synchronized on non-private lock object
|
|
104
|
+
rg 'synchronized\s*\(\s*this\s*\)' --type java
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Streams Anti-Patterns
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Stream.forEach with side effects (should use for-loop or collect)
|
|
113
|
+
rg '\.stream\(\).*\.forEach\(' --type java
|
|
114
|
+
|
|
115
|
+
# Nested streams (performance concern)
|
|
116
|
+
rg '\.stream\(\).*\.stream\(\)' --type java
|
|
117
|
+
|
|
118
|
+
# collect(Collectors.toList()) instead of .toList() (Java 16+)
|
|
119
|
+
rg 'collect\(Collectors\.toList\(\)\)' --type java
|
|
120
|
+
```
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Modern Java Features (17-21+)
|
|
2
|
+
|
|
3
|
+
Deep-dive on modern Java language features. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Records (Java 16+)
|
|
8
|
+
|
|
9
|
+
### Basic Record
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
// Immutable data carrier with auto-generated equals, hashCode, toString
|
|
13
|
+
public record Point(double x, double y) {}
|
|
14
|
+
|
|
15
|
+
// Usage
|
|
16
|
+
var p = new Point(3.0, 4.0);
|
|
17
|
+
double x = p.x(); // Accessor method, not getX()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Compact Constructor (Validation)
|
|
21
|
+
|
|
22
|
+
```java
|
|
23
|
+
public record Email(String value) {
|
|
24
|
+
public Email {
|
|
25
|
+
Objects.requireNonNull(value, "email must not be null");
|
|
26
|
+
if (!value.contains("@")) {
|
|
27
|
+
throw new IllegalArgumentException("Invalid email: " + value);
|
|
28
|
+
}
|
|
29
|
+
value = value.toLowerCase().strip(); // Reassign before final assignment
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Record with Custom Methods
|
|
35
|
+
|
|
36
|
+
```java
|
|
37
|
+
public record Range(int start, int end) {
|
|
38
|
+
public Range {
|
|
39
|
+
if (start > end) throw new IllegalArgumentException("start must be <= end");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public int length() { return end - start; }
|
|
43
|
+
public boolean contains(int value) { return value >= start && value <= end; }
|
|
44
|
+
public Range overlap(Range other) {
|
|
45
|
+
int newStart = Math.max(start, other.start);
|
|
46
|
+
int newEnd = Math.min(end, other.end);
|
|
47
|
+
return newStart <= newEnd ? new Range(newStart, newEnd) : null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Records as Local Classes
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
public List<String> processOrders(List<Order> orders) {
|
|
56
|
+
// Local record for intermediate computation
|
|
57
|
+
record OrderTotal(String orderId, Money total) {}
|
|
58
|
+
|
|
59
|
+
return orders.stream()
|
|
60
|
+
.map(o -> new OrderTotal(o.id(), o.calculateTotal()))
|
|
61
|
+
.filter(ot -> ot.total().isGreaterThan(Money.of(100)))
|
|
62
|
+
.map(OrderTotal::orderId)
|
|
63
|
+
.toList();
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Sealed Classes (Java 17+)
|
|
70
|
+
|
|
71
|
+
### Sealed Interface
|
|
72
|
+
|
|
73
|
+
```java
|
|
74
|
+
public sealed interface Shape permits Circle, Rectangle, Triangle {
|
|
75
|
+
double area();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public record Circle(double radius) implements Shape {
|
|
79
|
+
public double area() { return Math.PI * radius * radius; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public record Rectangle(double width, double height) implements Shape {
|
|
83
|
+
public double area() { return width * height; }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public record Triangle(double base, double height) implements Shape {
|
|
87
|
+
public double area() { return 0.5 * base * height; }
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Sealed Class with Abstract Methods
|
|
92
|
+
|
|
93
|
+
```java
|
|
94
|
+
public sealed abstract class Payment permits CreditCard, BankTransfer, Crypto {
|
|
95
|
+
abstract Money amount();
|
|
96
|
+
abstract String reference();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public final class CreditCard extends Payment {
|
|
100
|
+
private final String cardLast4;
|
|
101
|
+
private final Money amount;
|
|
102
|
+
// ...
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public final class BankTransfer extends Payment { /* ... */ }
|
|
106
|
+
public final class Crypto extends Payment { /* ... */ }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Pattern Matching (Java 21+)
|
|
112
|
+
|
|
113
|
+
### Switch with Patterns
|
|
114
|
+
|
|
115
|
+
```java
|
|
116
|
+
// Exhaustive pattern matching on sealed types
|
|
117
|
+
public String describe(Shape shape) {
|
|
118
|
+
return switch (shape) {
|
|
119
|
+
case Circle c -> "Circle with radius %.2f".formatted(c.radius());
|
|
120
|
+
case Rectangle r -> "Rectangle %s x %s".formatted(r.width(), r.height());
|
|
121
|
+
case Triangle t -> "Triangle with base %.2f".formatted(t.base());
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Guarded Patterns
|
|
127
|
+
|
|
128
|
+
```java
|
|
129
|
+
public String classifyTemperature(Object obj) {
|
|
130
|
+
return switch (obj) {
|
|
131
|
+
case Integer i when i < 0 -> "Freezing";
|
|
132
|
+
case Integer i when i < 15 -> "Cold";
|
|
133
|
+
case Integer i when i < 25 -> "Comfortable";
|
|
134
|
+
case Integer i -> "Hot";
|
|
135
|
+
case Double d when d < 0.0 -> "Freezing";
|
|
136
|
+
case Double d -> "Warm-ish (%.1f)".formatted(d);
|
|
137
|
+
case String s -> "Not a temperature: " + s;
|
|
138
|
+
case null -> "No reading";
|
|
139
|
+
default -> "Unknown type";
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Record Patterns (Destructuring)
|
|
145
|
+
|
|
146
|
+
```java
|
|
147
|
+
// Nested destructuring
|
|
148
|
+
record Address(String city, String country) {}
|
|
149
|
+
record Person(String name, Address address) {}
|
|
150
|
+
|
|
151
|
+
public String greet(Object obj) {
|
|
152
|
+
return switch (obj) {
|
|
153
|
+
case Person(var name, Address(var city, _)) ->
|
|
154
|
+
"Hello %s from %s".formatted(name, city);
|
|
155
|
+
default -> "Hello stranger";
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Text Blocks (Java 15+)
|
|
163
|
+
|
|
164
|
+
```java
|
|
165
|
+
// Multi-line strings with proper indentation
|
|
166
|
+
String json = """
|
|
167
|
+
{
|
|
168
|
+
"name": "%s",
|
|
169
|
+
"email": "%s",
|
|
170
|
+
"active": true
|
|
171
|
+
}
|
|
172
|
+
""".formatted(user.name(), user.email());
|
|
173
|
+
|
|
174
|
+
String sql = """
|
|
175
|
+
SELECT u.id, u.name, u.email
|
|
176
|
+
FROM users u
|
|
177
|
+
JOIN orders o ON o.user_id = u.id
|
|
178
|
+
WHERE u.active = true
|
|
179
|
+
AND o.created_at > ?
|
|
180
|
+
ORDER BY u.name
|
|
181
|
+
""";
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Virtual Threads (Java 21+)
|
|
187
|
+
|
|
188
|
+
### Basic Virtual Thread
|
|
189
|
+
|
|
190
|
+
```java
|
|
191
|
+
// Lightweight thread - does not pin platform thread during I/O
|
|
192
|
+
Thread.startVirtualThread(() -> {
|
|
193
|
+
var result = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
194
|
+
process(result.body());
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Structured Concurrency (Preview)
|
|
199
|
+
|
|
200
|
+
```java
|
|
201
|
+
// All subtasks complete or cancel together
|
|
202
|
+
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
|
203
|
+
Subtask<User> userTask = scope.fork(() -> fetchUser(userId));
|
|
204
|
+
Subtask<List<Order>> ordersTask = scope.fork(() -> fetchOrders(userId));
|
|
205
|
+
|
|
206
|
+
scope.join().throwIfFailed();
|
|
207
|
+
|
|
208
|
+
return new UserDashboard(userTask.get(), ordersTask.get());
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### ExecutorService with Virtual Threads
|
|
213
|
+
|
|
214
|
+
```java
|
|
215
|
+
// Process thousands of concurrent I/O tasks
|
|
216
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
217
|
+
List<Future<Response>> futures = urls.stream()
|
|
218
|
+
.map(url -> executor.submit(() -> httpClient.send(
|
|
219
|
+
HttpRequest.newBuilder(URI.create(url)).build(),
|
|
220
|
+
HttpResponse.BodyHandlers.ofString()
|
|
221
|
+
)))
|
|
222
|
+
.toList();
|
|
223
|
+
|
|
224
|
+
List<Response> responses = futures.stream()
|
|
225
|
+
.map(f -> {
|
|
226
|
+
try { return f.get(); }
|
|
227
|
+
catch (Exception e) { throw new RuntimeException(e); }
|
|
228
|
+
})
|
|
229
|
+
.toList();
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Other Modern Features
|
|
236
|
+
|
|
237
|
+
### Enhanced instanceof (Java 16+)
|
|
238
|
+
|
|
239
|
+
```java
|
|
240
|
+
// Pattern variable binding eliminates explicit cast
|
|
241
|
+
if (obj instanceof String s && s.length() > 5) {
|
|
242
|
+
System.out.println(s.toUpperCase());
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Works with negation
|
|
246
|
+
if (!(obj instanceof String s)) {
|
|
247
|
+
throw new IllegalArgumentException("Expected String");
|
|
248
|
+
}
|
|
249
|
+
// s is in scope here
|
|
250
|
+
process(s);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Helpful NullPointerExceptions (Java 14+)
|
|
254
|
+
|
|
255
|
+
```java
|
|
256
|
+
// JVM now tells you exactly which reference was null:
|
|
257
|
+
// java.lang.NullPointerException: Cannot invoke "String.length()"
|
|
258
|
+
// because the return value of "User.name()" is null
|
|
259
|
+
// Enable with: -XX:+ShowCodeDetailsInExceptionMessages (default since Java 17)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Stream Gatherers (Java 22+ Preview)
|
|
263
|
+
|
|
264
|
+
```java
|
|
265
|
+
// Custom intermediate stream operations
|
|
266
|
+
var windowedAverages = temperatures.stream()
|
|
267
|
+
.gather(Gatherers.windowSliding(5))
|
|
268
|
+
.map(window -> window.stream().mapToDouble(d -> d).average().orElse(0))
|
|
269
|
+
.toList();
|
|
270
|
+
```
|
|
@@ -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
|
+
```
|