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,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: go
|
|
3
|
+
description: This skill should be used when the user works with Go files (.go), asks about "error handling", "interfaces", "goroutines", "channels", "packages", or discusses Go idioms and concurrency. Provides patterns for error handling, interface design, concurrency, and package organization.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
allowed-tools: Read, Grep, Glob
|
|
6
|
+
activation:
|
|
7
|
+
file-patterns:
|
|
8
|
+
- "**/*.go"
|
|
9
|
+
exclude:
|
|
10
|
+
- "vendor/**"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Go Patterns
|
|
14
|
+
|
|
15
|
+
Reference for Go-specific patterns, idioms, and best practices.
|
|
16
|
+
|
|
17
|
+
## Iron Law
|
|
18
|
+
|
|
19
|
+
> **ERRORS ARE VALUES**
|
|
20
|
+
>
|
|
21
|
+
> Never ignore errors. `if err != nil` is correctness, not boilerplate. Every error
|
|
22
|
+
> return must be checked, wrapped with context, or explicitly documented as intentionally
|
|
23
|
+
> ignored with `_ = fn()`. Silent error swallowing causes cascading failures.
|
|
24
|
+
|
|
25
|
+
## When This Skill Activates
|
|
26
|
+
|
|
27
|
+
- Working with Go codebases
|
|
28
|
+
- Designing interfaces and packages
|
|
29
|
+
- Implementing concurrent code
|
|
30
|
+
- Handling errors
|
|
31
|
+
- Structuring Go projects
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Error Handling
|
|
36
|
+
|
|
37
|
+
### Wrap Errors with Context
|
|
38
|
+
|
|
39
|
+
```go
|
|
40
|
+
// BAD: return err
|
|
41
|
+
// GOOD: return fmt.Errorf("reading config %s: %w", path, err)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Sentinel Errors for Expected Conditions
|
|
45
|
+
|
|
46
|
+
```go
|
|
47
|
+
var ErrNotFound = errors.New("not found")
|
|
48
|
+
|
|
49
|
+
func FindUser(id string) (*User, error) {
|
|
50
|
+
u, err := db.Get(id)
|
|
51
|
+
if err != nil {
|
|
52
|
+
return nil, fmt.Errorf("finding user %s: %w", id, err)
|
|
53
|
+
}
|
|
54
|
+
if u == nil {
|
|
55
|
+
return nil, ErrNotFound
|
|
56
|
+
}
|
|
57
|
+
return u, nil
|
|
58
|
+
}
|
|
59
|
+
// Caller: if errors.Is(err, ErrNotFound) { ... }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Interface Design
|
|
65
|
+
|
|
66
|
+
### Accept Interfaces, Return Structs
|
|
67
|
+
|
|
68
|
+
```go
|
|
69
|
+
// BAD: func NewService(repo *PostgresRepo) *Service
|
|
70
|
+
// GOOD: func NewService(repo Repository) *Service
|
|
71
|
+
|
|
72
|
+
type Repository interface {
|
|
73
|
+
FindByID(ctx context.Context, id string) (*Entity, error)
|
|
74
|
+
Save(ctx context.Context, entity *Entity) error
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Keep Interfaces Small
|
|
79
|
+
|
|
80
|
+
```go
|
|
81
|
+
// BAD: 10-method interface
|
|
82
|
+
// GOOD: single-method interfaces composed as needed
|
|
83
|
+
type Reader interface { Read(p []byte) (n int, err error) }
|
|
84
|
+
type Writer interface { Write(p []byte) (n int, err error) }
|
|
85
|
+
type ReadWriter interface { Reader; Writer }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Concurrency
|
|
91
|
+
|
|
92
|
+
### Use Context for Cancellation
|
|
93
|
+
|
|
94
|
+
```go
|
|
95
|
+
func Process(ctx context.Context, items []Item) error {
|
|
96
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
97
|
+
for _, item := range items {
|
|
98
|
+
g.Go(func() error {
|
|
99
|
+
return processItem(ctx, item)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
return g.Wait()
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Channel Direction
|
|
107
|
+
|
|
108
|
+
```go
|
|
109
|
+
// Declare direction in function signatures
|
|
110
|
+
func producer(ch chan<- int) { ch <- 42 }
|
|
111
|
+
func consumer(ch <-chan int) { v := <-ch; _ = v }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Package Design
|
|
117
|
+
|
|
118
|
+
### Organize by Domain, Not by Type
|
|
119
|
+
|
|
120
|
+
```go
|
|
121
|
+
// BAD: models/, controllers/, services/
|
|
122
|
+
// GOOD: user/, order/, payment/
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Export Only What's Needed
|
|
126
|
+
|
|
127
|
+
```go
|
|
128
|
+
// Internal helpers stay unexported (lowercase)
|
|
129
|
+
func (s *Service) validate(u *User) error { ... }
|
|
130
|
+
|
|
131
|
+
// Public API is exported (uppercase)
|
|
132
|
+
func (s *Service) CreateUser(ctx context.Context, req CreateUserReq) (*User, error) { ... }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Zero Values
|
|
138
|
+
|
|
139
|
+
```go
|
|
140
|
+
// Use zero values as valid defaults
|
|
141
|
+
var mu sync.Mutex // Ready to use
|
|
142
|
+
var buf bytes.Buffer // Ready to use
|
|
143
|
+
var wg sync.WaitGroup // Ready to use
|
|
144
|
+
|
|
145
|
+
// Design types with useful zero values
|
|
146
|
+
type Config struct {
|
|
147
|
+
Timeout time.Duration // zero = no timeout
|
|
148
|
+
Retries int // zero = no retries
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Anti-Patterns
|
|
155
|
+
|
|
156
|
+
| Pattern | Bad | Good |
|
|
157
|
+
|---------|-----|------|
|
|
158
|
+
| Ignoring error | `val, _ := fn()` | `val, err := fn(); if err != nil { ... }` |
|
|
159
|
+
| Naked return | `return` in named returns | Explicit `return val, err` |
|
|
160
|
+
| init() abuse | Complex `init()` functions | Explicit initialization in `main()` or constructors |
|
|
161
|
+
| Interface pollution | Defining interfaces before use | Define interfaces at the consumer site |
|
|
162
|
+
| Goroutine leak | `go fn()` without lifecycle | Use context, errgroup, or done channels |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Extended References
|
|
167
|
+
|
|
168
|
+
For additional patterns and examples:
|
|
169
|
+
- `references/violations.md` - Common Go violations
|
|
170
|
+
- `references/patterns.md` - Extended Go patterns
|
|
171
|
+
- `references/detection.md` - Detection patterns for Go issues
|
|
172
|
+
- `references/concurrency.md` - Advanced concurrency patterns
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Checklist
|
|
177
|
+
|
|
178
|
+
- [ ] All errors checked or explicitly ignored with `_ =`
|
|
179
|
+
- [ ] Errors wrapped with `fmt.Errorf("context: %w", err)`
|
|
180
|
+
- [ ] Interfaces defined at consumer, not producer
|
|
181
|
+
- [ ] Interfaces kept small (1-3 methods)
|
|
182
|
+
- [ ] Context passed as first parameter
|
|
183
|
+
- [ ] Goroutines have clear lifecycle/cancellation
|
|
184
|
+
- [ ] Channel direction specified in signatures
|
|
185
|
+
- [ ] Zero values are useful defaults
|
|
186
|
+
- [ ] Packages organized by domain
|
|
187
|
+
- [ ] No `init()` with side effects
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Go Concurrency Patterns
|
|
2
|
+
|
|
3
|
+
Advanced concurrency patterns for Go. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## errgroup for Structured Concurrency
|
|
6
|
+
|
|
7
|
+
```go
|
|
8
|
+
import "golang.org/x/sync/errgroup"
|
|
9
|
+
|
|
10
|
+
func FetchAll(ctx context.Context, urls []string) ([]Response, error) {
|
|
11
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
12
|
+
responses := make([]Response, len(urls))
|
|
13
|
+
|
|
14
|
+
for i, url := range urls {
|
|
15
|
+
g.Go(func() error {
|
|
16
|
+
resp, err := fetch(ctx, url)
|
|
17
|
+
if err != nil {
|
|
18
|
+
return fmt.Errorf("fetching %s: %w", url, err)
|
|
19
|
+
}
|
|
20
|
+
responses[i] = resp // Safe: each goroutine writes to unique index
|
|
21
|
+
return nil
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if err := g.Wait(); err != nil {
|
|
26
|
+
return nil, err
|
|
27
|
+
}
|
|
28
|
+
return responses, nil
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### errgroup with Concurrency Limit
|
|
33
|
+
|
|
34
|
+
```go
|
|
35
|
+
func ProcessItems(ctx context.Context, items []Item) error {
|
|
36
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
37
|
+
g.SetLimit(10) // Maximum 10 concurrent goroutines
|
|
38
|
+
|
|
39
|
+
for _, item := range items {
|
|
40
|
+
g.Go(func() error {
|
|
41
|
+
return processItem(ctx, item)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return g.Wait()
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Worker Pool
|
|
52
|
+
|
|
53
|
+
```go
|
|
54
|
+
func WorkerPool(ctx context.Context, jobs <-chan Job, workers int) <-chan Result {
|
|
55
|
+
results := make(chan Result, workers)
|
|
56
|
+
|
|
57
|
+
var wg sync.WaitGroup
|
|
58
|
+
for range workers {
|
|
59
|
+
wg.Add(1)
|
|
60
|
+
go func() {
|
|
61
|
+
defer wg.Done()
|
|
62
|
+
for {
|
|
63
|
+
select {
|
|
64
|
+
case job, ok := <-jobs:
|
|
65
|
+
if !ok {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
results <- process(ctx, job)
|
|
69
|
+
case <-ctx.Done():
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
go func() {
|
|
77
|
+
wg.Wait()
|
|
78
|
+
close(results)
|
|
79
|
+
}()
|
|
80
|
+
|
|
81
|
+
return results
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Usage
|
|
85
|
+
jobs := make(chan Job, 100)
|
|
86
|
+
results := WorkerPool(ctx, jobs, 5)
|
|
87
|
+
|
|
88
|
+
// Send jobs
|
|
89
|
+
go func() {
|
|
90
|
+
defer close(jobs)
|
|
91
|
+
for _, j := range allJobs {
|
|
92
|
+
jobs <- j
|
|
93
|
+
}
|
|
94
|
+
}()
|
|
95
|
+
|
|
96
|
+
// Collect results
|
|
97
|
+
for r := range results {
|
|
98
|
+
fmt.Println(r)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Fan-Out / Fan-In
|
|
105
|
+
|
|
106
|
+
```go
|
|
107
|
+
// Fan-out: one source, multiple workers
|
|
108
|
+
func fanOut(ctx context.Context, input <-chan int, workers int) []<-chan int {
|
|
109
|
+
channels := make([]<-chan int, workers)
|
|
110
|
+
for i := range workers {
|
|
111
|
+
channels[i] = worker(ctx, input)
|
|
112
|
+
}
|
|
113
|
+
return channels
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func worker(ctx context.Context, input <-chan int) <-chan int {
|
|
117
|
+
out := make(chan int)
|
|
118
|
+
go func() {
|
|
119
|
+
defer close(out)
|
|
120
|
+
for n := range input {
|
|
121
|
+
select {
|
|
122
|
+
case out <- n * n:
|
|
123
|
+
case <-ctx.Done():
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}()
|
|
128
|
+
return out
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Fan-in: multiple sources, one destination
|
|
132
|
+
func fanIn(ctx context.Context, channels ...<-chan int) <-chan int {
|
|
133
|
+
merged := make(chan int)
|
|
134
|
+
var wg sync.WaitGroup
|
|
135
|
+
|
|
136
|
+
for _, ch := range channels {
|
|
137
|
+
wg.Add(1)
|
|
138
|
+
go func() {
|
|
139
|
+
defer wg.Done()
|
|
140
|
+
for val := range ch {
|
|
141
|
+
select {
|
|
142
|
+
case merged <- val:
|
|
143
|
+
case <-ctx.Done():
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
go func() {
|
|
151
|
+
wg.Wait()
|
|
152
|
+
close(merged)
|
|
153
|
+
}()
|
|
154
|
+
|
|
155
|
+
return merged
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Select with Timeout
|
|
162
|
+
|
|
163
|
+
```go
|
|
164
|
+
func fetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
|
|
165
|
+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
166
|
+
defer cancel()
|
|
167
|
+
|
|
168
|
+
ch := make(chan result, 1)
|
|
169
|
+
go func() {
|
|
170
|
+
data, err := doFetch(ctx, url)
|
|
171
|
+
ch <- result{data, err}
|
|
172
|
+
}()
|
|
173
|
+
|
|
174
|
+
select {
|
|
175
|
+
case r := <-ch:
|
|
176
|
+
return r.data, r.err
|
|
177
|
+
case <-ctx.Done():
|
|
178
|
+
return nil, fmt.Errorf("fetch %s: %w", url, ctx.Err())
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
type result struct {
|
|
183
|
+
data []byte
|
|
184
|
+
err error
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Select for Multiple Sources
|
|
189
|
+
|
|
190
|
+
```go
|
|
191
|
+
func merge(ctx context.Context, primary, fallback <-chan Event) <-chan Event {
|
|
192
|
+
out := make(chan Event)
|
|
193
|
+
go func() {
|
|
194
|
+
defer close(out)
|
|
195
|
+
for {
|
|
196
|
+
select {
|
|
197
|
+
case e, ok := <-primary:
|
|
198
|
+
if !ok {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
out <- e
|
|
202
|
+
case e, ok := <-fallback:
|
|
203
|
+
if !ok {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
out <- e
|
|
207
|
+
case <-ctx.Done():
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}()
|
|
212
|
+
return out
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Mutex vs Channels
|
|
219
|
+
|
|
220
|
+
### Use Mutex When
|
|
221
|
+
|
|
222
|
+
```go
|
|
223
|
+
// Protecting shared state with simple read/write
|
|
224
|
+
type SafeCounter struct {
|
|
225
|
+
mu sync.RWMutex
|
|
226
|
+
v map[string]int
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
func (c *SafeCounter) Inc(key string) {
|
|
230
|
+
c.mu.Lock()
|
|
231
|
+
defer c.mu.Unlock()
|
|
232
|
+
c.v[key]++
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
func (c *SafeCounter) Get(key string) int {
|
|
236
|
+
c.mu.RLock()
|
|
237
|
+
defer c.mu.RUnlock()
|
|
238
|
+
return c.v[key]
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Use Channels When
|
|
243
|
+
|
|
244
|
+
```go
|
|
245
|
+
// Communicating between goroutines / coordinating work
|
|
246
|
+
func pipeline(ctx context.Context, input []int) <-chan int {
|
|
247
|
+
out := make(chan int)
|
|
248
|
+
go func() {
|
|
249
|
+
defer close(out)
|
|
250
|
+
for _, n := range input {
|
|
251
|
+
select {
|
|
252
|
+
case out <- transform(n):
|
|
253
|
+
case <-ctx.Done():
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}()
|
|
258
|
+
return out
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Decision Guide
|
|
263
|
+
|
|
264
|
+
| Scenario | Use |
|
|
265
|
+
|----------|-----|
|
|
266
|
+
| Guarding shared state | `sync.Mutex` or `sync.RWMutex` |
|
|
267
|
+
| Passing ownership of data | Channels |
|
|
268
|
+
| Coordinating goroutine lifecycle | `context.Context` + channels |
|
|
269
|
+
| Waiting for N goroutines | `sync.WaitGroup` or `errgroup` |
|
|
270
|
+
| One-time initialization | `sync.Once` |
|
|
271
|
+
| Concurrent map access | `sync.Map` (high read, low write) |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## sync.Once for Initialization
|
|
276
|
+
|
|
277
|
+
```go
|
|
278
|
+
type Client struct {
|
|
279
|
+
once sync.Once
|
|
280
|
+
conn *grpc.ClientConn
|
|
281
|
+
err error
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
func (c *Client) connection() (*grpc.ClientConn, error) {
|
|
285
|
+
c.once.Do(func() {
|
|
286
|
+
// requires: "google.golang.org/grpc/credentials/insecure"
|
|
287
|
+
c.conn, c.err = grpc.Dial("localhost:50051",
|
|
288
|
+
grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
289
|
+
})
|
|
290
|
+
return c.conn, c.err
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Rate Limiting
|
|
297
|
+
|
|
298
|
+
```go
|
|
299
|
+
func rateLimited(ctx context.Context, items []Item, rps int) error {
|
|
300
|
+
limiter := rate.NewLimiter(rate.Limit(rps), 1)
|
|
301
|
+
|
|
302
|
+
for _, item := range items {
|
|
303
|
+
if err := limiter.Wait(ctx); err != nil {
|
|
304
|
+
return fmt.Errorf("rate limiter: %w", err)
|
|
305
|
+
}
|
|
306
|
+
if err := process(ctx, item); err != nil {
|
|
307
|
+
return fmt.Errorf("processing item %s: %w", item.ID, err)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return nil
|
|
311
|
+
}
|
|
312
|
+
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Go Issue Detection
|
|
2
|
+
|
|
3
|
+
Grep and regex patterns for finding common Go issues. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Error Handling Detection
|
|
6
|
+
|
|
7
|
+
### Unchecked Errors
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Find ignored error returns (blank identifier)
|
|
11
|
+
grep -rn ', _ := ' --include='*.go' .
|
|
12
|
+
grep -rn ', _ = ' --include='*.go' .
|
|
13
|
+
|
|
14
|
+
# Find bare error returns without wrapping
|
|
15
|
+
grep -rn 'return.*err$' --include='*.go' . | grep -v 'fmt.Errorf' | grep -v ':= err'
|
|
16
|
+
|
|
17
|
+
# Find deferred calls that return errors (Close, Flush, Sync)
|
|
18
|
+
grep -rn 'defer.*\.Close()' --include='*.go' .
|
|
19
|
+
grep -rn 'defer.*\.Flush()' --include='*.go' .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Missing Error Context
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Find raw error returns (no wrapping)
|
|
26
|
+
grep -rn 'return nil, err$' --include='*.go' .
|
|
27
|
+
grep -rn 'return err$' --include='*.go' .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Concurrency Detection
|
|
33
|
+
|
|
34
|
+
### Goroutine Without Context
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Find goroutines that don't reference ctx
|
|
38
|
+
grep -rn 'go func()' --include='*.go' .
|
|
39
|
+
|
|
40
|
+
# Find goroutines without done/cancel/ctx
|
|
41
|
+
grep -B5 -A10 'go func' --include='*.go' . | grep -L 'ctx\|done\|cancel\|errgroup'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Potential Goroutine Leaks
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Find unbuffered channel creation followed by goroutine
|
|
48
|
+
grep -rn 'make(chan ' --include='*.go' . | grep -v ', [0-9]'
|
|
49
|
+
|
|
50
|
+
# Find goroutines with infinite loops
|
|
51
|
+
grep -A5 'go func' --include='*.go' . | grep 'for {'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Interface Detection
|
|
57
|
+
|
|
58
|
+
### Empty Interface Usage
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Find empty interface (pre-1.18 style)
|
|
62
|
+
grep -rn 'interface{}' --include='*.go' .
|
|
63
|
+
|
|
64
|
+
# Find any type (1.18+ style) - may be intentional, review context
|
|
65
|
+
grep -rn '\bany\b' --include='*.go' . | grep -v '_test.go' | grep -v 'vendor/'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Large Interfaces
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Find interface declarations and count methods (interfaces > 5 methods)
|
|
72
|
+
grep -B1 -A20 'type.*interface {' --include='*.go' .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Panic Detection
|
|
78
|
+
|
|
79
|
+
### Panic in Library Code
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Find panic calls outside main/test
|
|
83
|
+
grep -rn 'panic(' --include='*.go' . | grep -v '_test.go' | grep -v 'main.go'
|
|
84
|
+
|
|
85
|
+
# Find log.Fatal (calls os.Exit) in library code
|
|
86
|
+
grep -rn 'log.Fatal' --include='*.go' . | grep -v 'main.go' | grep -v '_test.go'
|
|
87
|
+
|
|
88
|
+
# Find os.Exit in library code
|
|
89
|
+
grep -rn 'os.Exit' --include='*.go' . | grep -v 'main.go' | grep -v '_test.go'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## init() Detection
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Find all init functions
|
|
98
|
+
grep -rn '^func init()' --include='*.go' .
|
|
99
|
+
|
|
100
|
+
# Find init functions with side effects (network, file, global state)
|
|
101
|
+
grep -A10 '^func init()' --include='*.go' . | grep -E 'http\.|sql\.|os\.|Open|Dial|Connect'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Mutex and Race Detection
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Find mutex passed by value (function params with Mutex, not pointer)
|
|
110
|
+
grep -rn 'func.*sync\.Mutex[^*]' --include='*.go' .
|
|
111
|
+
|
|
112
|
+
# Find Lock without corresponding Unlock
|
|
113
|
+
grep -rn '\.Lock()' --include='*.go' . | grep -v 'defer.*Unlock'
|
|
114
|
+
|
|
115
|
+
# Find RLock without RUnlock
|
|
116
|
+
grep -rn '\.RLock()' --include='*.go' . | grep -v 'defer.*RUnlock'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Map and Slice Safety
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Find uninitialized map declarations (potential nil map panic)
|
|
125
|
+
grep -rn 'var.*map\[' --include='*.go' . | grep -v 'make\|:='
|
|
126
|
+
|
|
127
|
+
# Find slice append without pre-allocation hint
|
|
128
|
+
grep -rn 'append(' --include='*.go' . | head -20
|
|
129
|
+
```
|