devflow-kit 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -1
- package/README.md +23 -6
- package/dist/cli.js +2 -0
- package/dist/commands/ambient.js +5 -4
- package/dist/commands/init.js +4 -2
- package/dist/commands/memory.js +4 -4
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.js +116 -0
- package/dist/commands/uninstall.js +11 -1
- package/dist/plugins.js +67 -3
- package/dist/utils/installer.js +20 -2
- package/package.json +4 -2
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +4 -2
- package/plugins/devflow-ambient/README.md +8 -8
- package/plugins/devflow-ambient/commands/ambient.md +14 -14
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +16 -9
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +6 -2
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +13 -6
- package/plugins/devflow-code-review/agents/reviewer.md +8 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
- package/plugins/devflow-code-review/commands/code-review.md +12 -2
- package/plugins/devflow-code-review/skills/architecture-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/complexity-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/consistency-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/database-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/dependencies-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/documentation-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/performance-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/regression-patterns/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/review-methodology/SKILL.md +1 -1
- package/plugins/devflow-code-review/skills/security-patterns/SKILL.md +1 -1
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +10 -7
- package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +5 -8
- package/plugins/devflow-debug/.claude-plugin/plugin.json +10 -3
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-go/skills/go/SKILL.md +187 -0
- package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
- package/plugins/devflow-go/skills/go/references/detection.md +129 -0
- package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
- package/plugins/devflow-go/skills/go/references/violations.md +205 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +19 -5
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-implement/skills/self-review/SKILL.md +1 -1
- package/plugins/devflow-java/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-java/skills/java/SKILL.md +183 -0
- package/plugins/devflow-java/skills/java/references/detection.md +120 -0
- package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
- package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
- package/plugins/devflow-java/skills/java/references/violations.md +213 -0
- package/plugins/devflow-python/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-python/skills/python/SKILL.md +188 -0
- package/plugins/devflow-python/skills/python/references/async.md +220 -0
- package/plugins/devflow-python/skills/python/references/detection.md +128 -0
- package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
- package/plugins/devflow-python/skills/python/references/violations.md +204 -0
- package/plugins/devflow-react/.claude-plugin/plugin.json +22 -0
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +13 -3
- package/plugins/devflow-resolve/skills/security-patterns/SKILL.md +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +22 -0
- package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
- package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
- package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
- package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
- package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +10 -3
- package/plugins/devflow-self-review/skills/self-review/SKILL.md +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +15 -4
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +22 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/scripts/hooks/{ambient-prompt.sh → ambient-prompt} +4 -4
- package/scripts/hooks/{background-memory-update.sh → background-memory-update} +3 -3
- package/scripts/hooks/{ensure-memory-gitignore.sh → ensure-memory-gitignore} +1 -1
- package/scripts/hooks/{pre-compact-memory.sh → pre-compact-memory} +2 -2
- package/scripts/hooks/run-hook +23 -0
- package/scripts/hooks/session-start-memory +151 -0
- package/scripts/hooks/{stop-update-memory.sh → stop-update-memory} +4 -4
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +16 -9
- package/shared/skills/ambient-router/references/skill-catalog.md +6 -2
- package/shared/skills/architecture-patterns/SKILL.md +1 -1
- package/shared/skills/complexity-patterns/SKILL.md +1 -1
- package/shared/skills/consistency-patterns/SKILL.md +1 -1
- package/shared/skills/database-patterns/SKILL.md +1 -1
- package/shared/skills/dependencies-patterns/SKILL.md +1 -1
- package/shared/skills/documentation-patterns/SKILL.md +1 -1
- package/shared/skills/go/SKILL.md +187 -0
- package/shared/skills/go/references/concurrency.md +312 -0
- package/shared/skills/go/references/detection.md +129 -0
- package/shared/skills/go/references/patterns.md +232 -0
- package/shared/skills/go/references/violations.md +205 -0
- package/shared/skills/java/SKILL.md +183 -0
- package/shared/skills/java/references/detection.md +120 -0
- package/shared/skills/java/references/modern-java.md +270 -0
- package/shared/skills/java/references/patterns.md +235 -0
- package/shared/skills/java/references/violations.md +213 -0
- package/shared/skills/performance-patterns/SKILL.md +1 -1
- package/shared/skills/python/SKILL.md +188 -0
- package/shared/skills/python/references/async.md +220 -0
- package/shared/skills/python/references/detection.md +128 -0
- package/shared/skills/python/references/patterns.md +226 -0
- package/shared/skills/python/references/violations.md +204 -0
- package/shared/skills/react/SKILL.md +1 -1
- package/shared/skills/react/references/patterns.md +3 -3
- package/shared/skills/regression-patterns/SKILL.md +1 -1
- package/shared/skills/review-methodology/SKILL.md +1 -1
- package/shared/skills/rust/SKILL.md +193 -0
- package/shared/skills/rust/references/detection.md +131 -0
- package/shared/skills/rust/references/ownership.md +242 -0
- package/shared/skills/rust/references/patterns.md +210 -0
- package/shared/skills/rust/references/violations.md +191 -0
- package/shared/skills/security-patterns/SKILL.md +1 -1
- package/shared/skills/self-review/SKILL.md +1 -1
- package/shared/skills/test-driven-development/SKILL.md +5 -8
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/src/templates/settings.json +3 -3
- package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
- package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
- package/scripts/hooks/session-start-memory.sh +0 -126
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Go Correct Patterns
|
|
2
|
+
|
|
3
|
+
Extended correct patterns for Go development. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Table-Driven Tests
|
|
6
|
+
|
|
7
|
+
```go
|
|
8
|
+
func TestAdd(t *testing.T) {
|
|
9
|
+
tests := []struct {
|
|
10
|
+
name string
|
|
11
|
+
a, b int
|
|
12
|
+
expected int
|
|
13
|
+
}{
|
|
14
|
+
{"positive numbers", 2, 3, 5},
|
|
15
|
+
{"negative numbers", -1, -2, -3},
|
|
16
|
+
{"zero", 0, 0, 0},
|
|
17
|
+
{"mixed signs", -1, 3, 2},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for _, tt := range tests {
|
|
21
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
22
|
+
result := Add(tt.a, tt.b)
|
|
23
|
+
if result != tt.expected {
|
|
24
|
+
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Functional Options
|
|
34
|
+
|
|
35
|
+
```go
|
|
36
|
+
type Server struct {
|
|
37
|
+
addr string
|
|
38
|
+
timeout time.Duration
|
|
39
|
+
logger *slog.Logger
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type Option func(*Server)
|
|
43
|
+
|
|
44
|
+
func WithAddr(addr string) Option {
|
|
45
|
+
return func(s *Server) { s.addr = addr }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func WithTimeout(d time.Duration) Option {
|
|
49
|
+
return func(s *Server) { s.timeout = d }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func WithLogger(l *slog.Logger) Option {
|
|
53
|
+
return func(s *Server) { s.logger = l }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func NewServer(opts ...Option) *Server {
|
|
57
|
+
s := &Server{
|
|
58
|
+
addr: ":8080", // sensible default
|
|
59
|
+
timeout: 30 * time.Second, // sensible default
|
|
60
|
+
logger: slog.Default(),
|
|
61
|
+
}
|
|
62
|
+
for _, opt := range opts {
|
|
63
|
+
opt(s)
|
|
64
|
+
}
|
|
65
|
+
return s
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Usage
|
|
69
|
+
srv := NewServer(
|
|
70
|
+
WithAddr(":9090"),
|
|
71
|
+
WithTimeout(60 * time.Second),
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Custom Error Types
|
|
78
|
+
|
|
79
|
+
```go
|
|
80
|
+
type ValidationError struct {
|
|
81
|
+
Field string
|
|
82
|
+
Message string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func (e *ValidationError) Error() string {
|
|
86
|
+
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type NotFoundError struct {
|
|
90
|
+
Resource string
|
|
91
|
+
ID string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func (e *NotFoundError) Error() string {
|
|
95
|
+
return fmt.Sprintf("%s %s not found", e.Resource, e.ID)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Usage with errors.As
|
|
99
|
+
func handleErr(err error) {
|
|
100
|
+
var validErr *ValidationError
|
|
101
|
+
if errors.As(err, &validErr) {
|
|
102
|
+
log.Printf("bad input: field=%s msg=%s", validErr.Field, validErr.Message)
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
var notFound *NotFoundError
|
|
106
|
+
if errors.As(err, ¬Found) {
|
|
107
|
+
log.Printf("missing: %s/%s", notFound.Resource, notFound.ID)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
log.Printf("unexpected: %v", err)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Middleware Pattern
|
|
117
|
+
|
|
118
|
+
```go
|
|
119
|
+
type Middleware func(http.Handler) http.Handler
|
|
120
|
+
|
|
121
|
+
func Logging(logger *slog.Logger) Middleware {
|
|
122
|
+
return func(next http.Handler) http.Handler {
|
|
123
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
124
|
+
start := time.Now()
|
|
125
|
+
next.ServeHTTP(w, r)
|
|
126
|
+
logger.Info("request",
|
|
127
|
+
"method", r.Method,
|
|
128
|
+
"path", r.URL.Path,
|
|
129
|
+
"duration", time.Since(start),
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func Recovery() Middleware {
|
|
136
|
+
return func(next http.Handler) http.Handler {
|
|
137
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
138
|
+
defer func() {
|
|
139
|
+
if err := recover(); err != nil {
|
|
140
|
+
w.WriteHeader(http.StatusInternalServerError)
|
|
141
|
+
slog.Error("panic recovered", "error", err)
|
|
142
|
+
}
|
|
143
|
+
}()
|
|
144
|
+
next.ServeHTTP(w, r)
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Chain composes middleware in order
|
|
150
|
+
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
|
|
151
|
+
for i := len(middlewares) - 1; i >= 0; i-- {
|
|
152
|
+
handler = middlewares[i](handler)
|
|
153
|
+
}
|
|
154
|
+
return handler
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Usage
|
|
158
|
+
mux := http.NewServeMux()
|
|
159
|
+
mux.HandleFunc("/api/users", handleUsers)
|
|
160
|
+
handler := Chain(mux, Recovery(), Logging(logger))
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Graceful Shutdown
|
|
166
|
+
|
|
167
|
+
```go
|
|
168
|
+
func main() {
|
|
169
|
+
srv := &http.Server{Addr: ":8080", Handler: mux}
|
|
170
|
+
|
|
171
|
+
go func() {
|
|
172
|
+
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
|
173
|
+
slog.Error("server error", "error", err)
|
|
174
|
+
}
|
|
175
|
+
}()
|
|
176
|
+
|
|
177
|
+
quit := make(chan os.Signal, 1)
|
|
178
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
179
|
+
<-quit
|
|
180
|
+
|
|
181
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
182
|
+
defer cancel()
|
|
183
|
+
|
|
184
|
+
if err := srv.Shutdown(ctx); err != nil {
|
|
185
|
+
slog.Error("shutdown error", "error", err)
|
|
186
|
+
}
|
|
187
|
+
slog.Info("server stopped")
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Constructor Pattern
|
|
194
|
+
|
|
195
|
+
```go
|
|
196
|
+
// Validate at construction - enforce invariants
|
|
197
|
+
func NewUser(name, email string) (*User, error) {
|
|
198
|
+
if name == "" {
|
|
199
|
+
return nil, &ValidationError{Field: "name", Message: "required"}
|
|
200
|
+
}
|
|
201
|
+
if !strings.Contains(email, "@") {
|
|
202
|
+
return nil, &ValidationError{Field: "email", Message: "invalid format"}
|
|
203
|
+
}
|
|
204
|
+
return &User{
|
|
205
|
+
ID: uuid.New().String(),
|
|
206
|
+
Name: name,
|
|
207
|
+
Email: email,
|
|
208
|
+
CreatedAt: time.Now(),
|
|
209
|
+
}, nil
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Structured Logging with slog
|
|
216
|
+
|
|
217
|
+
```go
|
|
218
|
+
func ProcessOrder(ctx context.Context, orderID string) error {
|
|
219
|
+
logger := slog.With("order_id", orderID, "trace_id", traceID(ctx))
|
|
220
|
+
|
|
221
|
+
logger.Info("processing order")
|
|
222
|
+
|
|
223
|
+
items, err := fetchItems(ctx, orderID)
|
|
224
|
+
if err != nil {
|
|
225
|
+
logger.Error("failed to fetch items", "error", err)
|
|
226
|
+
return fmt.Errorf("fetching items for order %s: %w", orderID, err)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
logger.Info("items fetched", "count", len(items))
|
|
230
|
+
return nil
|
|
231
|
+
}
|
|
232
|
+
```
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Go Violation Examples
|
|
2
|
+
|
|
3
|
+
Extended violation patterns for Go reviews. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Error Handling Violations
|
|
6
|
+
|
|
7
|
+
### Ignored Errors
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
// VIOLATION: Silently discarding error
|
|
11
|
+
data, _ := json.Marshal(user)
|
|
12
|
+
w.Write(data)
|
|
13
|
+
|
|
14
|
+
// VIOLATION: Error ignored in deferred call
|
|
15
|
+
defer file.Close() // Close() returns error
|
|
16
|
+
|
|
17
|
+
// VIOLATION: Swallowing error with log
|
|
18
|
+
if err != nil {
|
|
19
|
+
log.Println("something failed") // Error details lost
|
|
20
|
+
return nil
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Unwrapped Errors
|
|
25
|
+
|
|
26
|
+
```go
|
|
27
|
+
// VIOLATION: No context on error
|
|
28
|
+
func LoadConfig(path string) (*Config, error) {
|
|
29
|
+
data, err := os.ReadFile(path)
|
|
30
|
+
if err != nil {
|
|
31
|
+
return nil, err // Caller has no idea what failed
|
|
32
|
+
}
|
|
33
|
+
var cfg Config
|
|
34
|
+
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
35
|
+
return nil, err // Which unmarshal? What file?
|
|
36
|
+
}
|
|
37
|
+
return &cfg, nil
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Goroutine Leak Violations
|
|
44
|
+
|
|
45
|
+
### Fire-and-Forget Goroutine
|
|
46
|
+
|
|
47
|
+
```go
|
|
48
|
+
// VIOLATION: No way to stop or wait for this goroutine
|
|
49
|
+
func StartPoller() {
|
|
50
|
+
go func() {
|
|
51
|
+
for {
|
|
52
|
+
poll()
|
|
53
|
+
time.Sleep(10 * time.Second)
|
|
54
|
+
}
|
|
55
|
+
}()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// VIOLATION: Goroutine blocks forever on channel nobody reads
|
|
59
|
+
func process(items []Item) {
|
|
60
|
+
ch := make(chan Result)
|
|
61
|
+
for _, item := range items {
|
|
62
|
+
go func(i Item) {
|
|
63
|
+
ch <- compute(i) // Blocks if nobody reads
|
|
64
|
+
}(item)
|
|
65
|
+
}
|
|
66
|
+
// Only reads first result - rest leak
|
|
67
|
+
result := <-ch
|
|
68
|
+
_ = result
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Missing Context Cancellation
|
|
73
|
+
|
|
74
|
+
```go
|
|
75
|
+
// VIOLATION: Goroutine ignores context
|
|
76
|
+
func Fetch(ctx context.Context, url string) ([]byte, error) {
|
|
77
|
+
ch := make(chan []byte, 1)
|
|
78
|
+
go func() {
|
|
79
|
+
resp, _ := http.Get(url) // Ignores ctx cancellation
|
|
80
|
+
body, _ := io.ReadAll(resp.Body)
|
|
81
|
+
ch <- body
|
|
82
|
+
}()
|
|
83
|
+
return <-ch, nil
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Interface Pollution Violations
|
|
90
|
+
|
|
91
|
+
### Premature Interface Definition
|
|
92
|
+
|
|
93
|
+
```go
|
|
94
|
+
// VIOLATION: Interface defined at producer, not consumer
|
|
95
|
+
package user
|
|
96
|
+
|
|
97
|
+
type UserStore interface { // Only one implementation exists
|
|
98
|
+
Get(id string) (*User, error)
|
|
99
|
+
Save(u *User) error
|
|
100
|
+
Delete(id string) error
|
|
101
|
+
List() ([]*User, error)
|
|
102
|
+
Count() (int, error)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type PostgresStore struct{ db *sql.DB }
|
|
106
|
+
|
|
107
|
+
func (s *PostgresStore) Get(id string) (*User, error) { ... }
|
|
108
|
+
// ... implements all 5 methods
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### God Interface
|
|
112
|
+
|
|
113
|
+
```go
|
|
114
|
+
// VIOLATION: Interface too large - impossible to mock cleanly
|
|
115
|
+
type Service interface {
|
|
116
|
+
CreateUser(ctx context.Context, u *User) error
|
|
117
|
+
GetUser(ctx context.Context, id string) (*User, error)
|
|
118
|
+
UpdateUser(ctx context.Context, u *User) error
|
|
119
|
+
DeleteUser(ctx context.Context, id string) error
|
|
120
|
+
ListUsers(ctx context.Context) ([]*User, error)
|
|
121
|
+
SendEmail(ctx context.Context, to string, body string) error
|
|
122
|
+
GenerateReport(ctx context.Context) ([]byte, error)
|
|
123
|
+
ProcessPayment(ctx context.Context, amt int) error
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Naked Return Violations
|
|
130
|
+
|
|
131
|
+
```go
|
|
132
|
+
// VIOLATION: Naked returns obscure what's being returned
|
|
133
|
+
func divide(a, b float64) (result float64, err error) {
|
|
134
|
+
if b == 0 {
|
|
135
|
+
err = errors.New("division by zero")
|
|
136
|
+
return // What is result here? Zero - but not obvious
|
|
137
|
+
}
|
|
138
|
+
result = a / b
|
|
139
|
+
return // Have to trace back to find return values
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## init() Abuse Violations
|
|
146
|
+
|
|
147
|
+
```go
|
|
148
|
+
// VIOLATION: Side effects in init - runs on import
|
|
149
|
+
func init() {
|
|
150
|
+
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
|
|
151
|
+
if err != nil {
|
|
152
|
+
log.Fatal(err) // Crashes on import
|
|
153
|
+
}
|
|
154
|
+
globalDB = db
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// VIOLATION: Registration magic in init
|
|
158
|
+
func init() {
|
|
159
|
+
http.HandleFunc("/health", healthCheck) // Hidden route registration
|
|
160
|
+
prometheus.MustRegister(requestCounter) // Panics if called twice
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Mutex Violations
|
|
167
|
+
|
|
168
|
+
```go
|
|
169
|
+
// VIOLATION: Copying mutex (passes by value)
|
|
170
|
+
type Cache struct {
|
|
171
|
+
mu sync.Mutex
|
|
172
|
+
data map[string]string
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
func process(c Cache) { // c is a COPY - mutex is copied too
|
|
176
|
+
c.mu.Lock()
|
|
177
|
+
defer c.mu.Unlock()
|
|
178
|
+
c.data["key"] = "val"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// VIOLATION: Forgetting to unlock
|
|
182
|
+
func (c *Cache) Get(key string) string {
|
|
183
|
+
c.mu.Lock()
|
|
184
|
+
if val, ok := c.data[key]; ok {
|
|
185
|
+
return val // Mutex never unlocked!
|
|
186
|
+
}
|
|
187
|
+
c.mu.Unlock()
|
|
188
|
+
return ""
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Slice and Map Violations
|
|
195
|
+
|
|
196
|
+
```go
|
|
197
|
+
// VIOLATION: Nil map write (runtime panic)
|
|
198
|
+
var m map[string]int
|
|
199
|
+
m["key"] = 1 // panic: assignment to entry in nil map
|
|
200
|
+
|
|
201
|
+
// VIOLATION: Sharing slice backing array
|
|
202
|
+
func getFirstThree(s []int) []int {
|
|
203
|
+
return s[:3] // Shares backing array - mutations leak
|
|
204
|
+
}
|
|
205
|
+
```
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: java
|
|
3
|
+
description: This skill should be used when the user works with Java files (.java), asks about "records", "sealed classes", "Optional", "streams", "composition over inheritance", or discusses modern Java patterns and API design. Provides patterns for type system usage, error handling, immutability, and concurrency.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
allowed-tools: Read, Grep, Glob
|
|
6
|
+
activation:
|
|
7
|
+
file-patterns:
|
|
8
|
+
- "**/*.java"
|
|
9
|
+
exclude:
|
|
10
|
+
- "**/build/**"
|
|
11
|
+
- "**/target/**"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Java Patterns
|
|
15
|
+
|
|
16
|
+
Reference for modern Java patterns, type system, and best practices.
|
|
17
|
+
|
|
18
|
+
## Iron Law
|
|
19
|
+
|
|
20
|
+
> **FAVOR COMPOSITION OVER INHERITANCE**
|
|
21
|
+
>
|
|
22
|
+
> Delegation and interfaces over class hierarchies. Inheritance creates tight coupling,
|
|
23
|
+
> breaks encapsulation, and makes refactoring dangerous. Use interfaces for polymorphism,
|
|
24
|
+
> records for data, and sealed classes for restricted hierarchies. Extend only when the
|
|
25
|
+
> "is-a" relationship is genuinely invariant.
|
|
26
|
+
|
|
27
|
+
## When This Skill Activates
|
|
28
|
+
|
|
29
|
+
- Working with Java codebases
|
|
30
|
+
- Designing APIs with modern Java features
|
|
31
|
+
- Using records, sealed classes, Optional
|
|
32
|
+
- Implementing concurrent code
|
|
33
|
+
- Structuring Java packages
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Type System (Modern Java)
|
|
38
|
+
|
|
39
|
+
### Records for Data
|
|
40
|
+
|
|
41
|
+
```java
|
|
42
|
+
// BAD: Mutable POJO with getters/setters
|
|
43
|
+
// GOOD: Immutable record
|
|
44
|
+
public record User(String name, String email, Instant createdAt) {
|
|
45
|
+
public User {
|
|
46
|
+
Objects.requireNonNull(name, "name must not be null");
|
|
47
|
+
Objects.requireNonNull(email, "email must not be null");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Sealed Classes for Restricted Hierarchies
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
public sealed interface Result<T> permits Success, Failure {
|
|
56
|
+
record Success<T>(T value) implements Result<T> {}
|
|
57
|
+
record Failure<T>(String error) implements Result<T> {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Exhaustive pattern matching (Java 21+)
|
|
61
|
+
switch (result) {
|
|
62
|
+
case Success<User> s -> handleSuccess(s.value());
|
|
63
|
+
case Failure<User> f -> handleError(f.error());
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Optional for Absent Values
|
|
68
|
+
|
|
69
|
+
```java
|
|
70
|
+
// BAD: return null;
|
|
71
|
+
// GOOD:
|
|
72
|
+
public Optional<User> findById(String id) {
|
|
73
|
+
return Optional.ofNullable(userMap.get(id));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// BAD: if (optional.isPresent()) optional.get()
|
|
77
|
+
// GOOD:
|
|
78
|
+
optional.map(User::name).orElse("Anonymous");
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Error Handling
|
|
84
|
+
|
|
85
|
+
### Custom Exceptions with Context
|
|
86
|
+
|
|
87
|
+
```java
|
|
88
|
+
public class EntityNotFoundException extends RuntimeException {
|
|
89
|
+
private final String entityType;
|
|
90
|
+
private final String entityId;
|
|
91
|
+
|
|
92
|
+
public EntityNotFoundException(String entityType, String entityId) {
|
|
93
|
+
super("%s with id %s not found".formatted(entityType, entityId));
|
|
94
|
+
this.entityType = entityType;
|
|
95
|
+
this.entityId = entityId;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Try-with-Resources
|
|
101
|
+
|
|
102
|
+
```java
|
|
103
|
+
// Always use try-with-resources for AutoCloseable
|
|
104
|
+
try (var conn = dataSource.getConnection();
|
|
105
|
+
var stmt = conn.prepareStatement(sql)) {
|
|
106
|
+
stmt.setString(1, id);
|
|
107
|
+
return mapResult(stmt.executeQuery());
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Immutability
|
|
114
|
+
|
|
115
|
+
```java
|
|
116
|
+
// Prefer unmodifiable collections
|
|
117
|
+
List<String> names = List.of("Alice", "Bob");
|
|
118
|
+
Map<String, Integer> scores = Map.of("Alice", 100, "Bob", 95);
|
|
119
|
+
|
|
120
|
+
// Defensive copies in constructors
|
|
121
|
+
public final class Team {
|
|
122
|
+
private final List<String> members;
|
|
123
|
+
public Team(List<String> members) {
|
|
124
|
+
this.members = List.copyOf(members);
|
|
125
|
+
}
|
|
126
|
+
public List<String> members() { return members; }
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Composition Over Inheritance
|
|
133
|
+
|
|
134
|
+
```java
|
|
135
|
+
// BAD: class UserService extends BaseService extends AbstractDAO
|
|
136
|
+
// GOOD: compose via constructor injection
|
|
137
|
+
public class UserService {
|
|
138
|
+
private final UserRepository repository;
|
|
139
|
+
private final EventPublisher events;
|
|
140
|
+
|
|
141
|
+
public UserService(UserRepository repository, EventPublisher events) {
|
|
142
|
+
this.repository = repository;
|
|
143
|
+
this.events = events;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Anti-Patterns
|
|
151
|
+
|
|
152
|
+
| Pattern | Bad | Good |
|
|
153
|
+
|---------|-----|------|
|
|
154
|
+
| Returning null | `return null` | `return Optional.empty()` |
|
|
155
|
+
| Checked exception abuse | `throws Exception` | Specific exceptions or unchecked |
|
|
156
|
+
| Raw types | `List list` | `List<User> list` |
|
|
157
|
+
| Deep inheritance | 4+ level hierarchy | Interfaces + composition |
|
|
158
|
+
| Mutable data objects | `setName()/getName()` | Records or immutable classes |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Extended References
|
|
163
|
+
|
|
164
|
+
For additional patterns and examples:
|
|
165
|
+
- `references/violations.md` - Common Java violations
|
|
166
|
+
- `references/patterns.md` - Extended Java patterns
|
|
167
|
+
- `references/detection.md` - Detection patterns for Java issues
|
|
168
|
+
- `references/modern-java.md` - Modern Java features (17-21+)
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Checklist
|
|
173
|
+
|
|
174
|
+
- [ ] Records for pure data types
|
|
175
|
+
- [ ] Sealed interfaces for type hierarchies
|
|
176
|
+
- [ ] Optional instead of null returns
|
|
177
|
+
- [ ] Composition over inheritance
|
|
178
|
+
- [ ] Try-with-resources for all AutoCloseable
|
|
179
|
+
- [ ] Immutable collections (List.of, Map.of)
|
|
180
|
+
- [ ] No raw generic types
|
|
181
|
+
- [ ] Custom exceptions with context
|
|
182
|
+
- [ ] Streams for collection transforms
|
|
183
|
+
- [ ] Constructor injection for dependencies
|