jdi-cli 0.1.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/AGENTS.md +209 -0
- package/ARCHITECTURE.md +210 -0
- package/COMMANDS.md +241 -0
- package/CREATE-EXAMPLE.md +385 -0
- package/CREATE.md +315 -0
- package/EXTENSION.md +141 -0
- package/LICENSE +21 -0
- package/MEMORY.md +471 -0
- package/PORTABILITY.md +438 -0
- package/README.md +789 -0
- package/bin/git-hooks/post-commit +16 -0
- package/bin/git-hooks/pre-commit +21 -0
- package/bin/jdi-build.ps1 +381 -0
- package/bin/jdi-build.sh +332 -0
- package/bin/jdi-doctor.ps1 +403 -0
- package/bin/jdi-doctor.sh +400 -0
- package/bin/jdi-install-caveman.ps1 +97 -0
- package/bin/jdi-install-caveman.sh +99 -0
- package/bin/jdi-install-playwright.ps1 +319 -0
- package/bin/jdi-install-playwright.sh +284 -0
- package/bin/jdi-install.ps1 +154 -0
- package/bin/jdi-install.sh +132 -0
- package/bin/jdi-uninstall.ps1 +309 -0
- package/bin/jdi-uninstall.sh +264 -0
- package/bin/jdi-update.ps1 +215 -0
- package/bin/jdi-update.sh +209 -0
- package/bin/jdi.js +460 -0
- package/bin/lib/jdi-monitor.ps1 +66 -0
- package/bin/lib/jdi-monitor.sh +74 -0
- package/bin/lib/jdi-truncate.ps1 +96 -0
- package/bin/lib/jdi-truncate.sh +99 -0
- package/bin/lib/ui.js +197 -0
- package/core/agents/jdi-adopter.md +465 -0
- package/core/agents/jdi-architect.md +894 -0
- package/core/agents/jdi-asker.md +153 -0
- package/core/agents/jdi-bootstrap.md +247 -0
- package/core/agents/jdi-planner.md +254 -0
- package/core/agents/jdi-researcher.md +303 -0
- package/core/commands/jdi-adopt.md +155 -0
- package/core/commands/jdi-bootstrap.md +81 -0
- package/core/commands/jdi-create.md +80 -0
- package/core/commands/jdi-discuss.md +80 -0
- package/core/commands/jdi-do.md +200 -0
- package/core/commands/jdi-loop.md +315 -0
- package/core/commands/jdi-new.md +131 -0
- package/core/commands/jdi-plan.md +73 -0
- package/core/commands/jdi-ship.md +146 -0
- package/core/commands/jdi-verify.md +159 -0
- package/core/skills/clean-code/SKILL.md +261 -0
- package/core/skills/dry/SKILL.md +150 -0
- package/core/skills/frontend-rules/SKILL.md +386 -0
- package/core/skills/frontend-validator/SKILL.md +567 -0
- package/core/skills/kiss/SKILL.md +178 -0
- package/core/skills/solid/SKILL.md +281 -0
- package/core/skills/yagni/SKILL.md +207 -0
- package/core/templates/agent.md +72 -0
- package/core/templates/doer-specialist.md +216 -0
- package/core/templates/reviewer-specialist.md +405 -0
- package/core/templates/skill.md +66 -0
- package/package.json +70 -0
- package/runtimes/antigravity/agents.md +74 -0
- package/runtimes/antigravity/skills/clean-code/SKILL.md +252 -0
- package/runtimes/antigravity/skills/dry/SKILL.md +141 -0
- package/runtimes/antigravity/skills/frontend-rules/SKILL.md +376 -0
- package/runtimes/antigravity/skills/frontend-validator/SKILL.md +559 -0
- package/runtimes/antigravity/skills/jdi-adopt/SKILL.md +155 -0
- package/runtimes/antigravity/skills/jdi-adopter/SKILL.md +436 -0
- package/runtimes/antigravity/skills/jdi-architect/SKILL.md +872 -0
- package/runtimes/antigravity/skills/jdi-asker/SKILL.md +125 -0
- package/runtimes/antigravity/skills/jdi-asker/references/context-template.md +34 -0
- package/runtimes/antigravity/skills/jdi-asker/references/decision-format.md +19 -0
- package/runtimes/antigravity/skills/jdi-asker/scripts/find_phase_dir.sh +25 -0
- package/runtimes/antigravity/skills/jdi-bootstrap/SKILL.md +81 -0
- package/runtimes/antigravity/skills/jdi-create/SKILL.md +80 -0
- package/runtimes/antigravity/skills/jdi-discuss/SKILL.md +80 -0
- package/runtimes/antigravity/skills/jdi-discuss/scripts/run_command.sh +62 -0
- package/runtimes/antigravity/skills/jdi-do/SKILL.md +200 -0
- package/runtimes/antigravity/skills/jdi-loop/SKILL.md +315 -0
- package/runtimes/antigravity/skills/jdi-new/SKILL.md +131 -0
- package/runtimes/antigravity/skills/jdi-plan/SKILL.md +73 -0
- package/runtimes/antigravity/skills/jdi-planner/SKILL.md +225 -0
- package/runtimes/antigravity/skills/jdi-researcher/SKILL.md +274 -0
- package/runtimes/antigravity/skills/jdi-ship/SKILL.md +146 -0
- package/runtimes/antigravity/skills/jdi-verify/SKILL.md +159 -0
- package/runtimes/antigravity/skills/kiss/SKILL.md +169 -0
- package/runtimes/antigravity/skills/solid/SKILL.md +272 -0
- package/runtimes/antigravity/skills/yagni/SKILL.md +198 -0
- package/runtimes/claude/CLAUDE.md +91 -0
- package/runtimes/claude/agents/jdi-adopter.md +430 -0
- package/runtimes/claude/agents/jdi-architect.md +864 -0
- package/runtimes/claude/agents/jdi-asker.md +119 -0
- package/runtimes/claude/agents/jdi-bootstrap.md +213 -0
- package/runtimes/claude/agents/jdi-planner.md +221 -0
- package/runtimes/claude/agents/jdi-researcher.md +269 -0
- package/runtimes/claude/commands/jdi-adopt.md +155 -0
- package/runtimes/claude/commands/jdi-bootstrap.md +81 -0
- package/runtimes/claude/commands/jdi-create.md +80 -0
- package/runtimes/claude/commands/jdi-discuss.md +80 -0
- package/runtimes/claude/commands/jdi-do.md +200 -0
- package/runtimes/claude/commands/jdi-loop.md +315 -0
- package/runtimes/claude/commands/jdi-new.md +131 -0
- package/runtimes/claude/commands/jdi-plan.md +73 -0
- package/runtimes/claude/commands/jdi-ship.md +146 -0
- package/runtimes/claude/commands/jdi-verify.md +159 -0
- package/runtimes/claude/settings.example.json +132 -0
- package/runtimes/claude/skills/clean-code/SKILL.md +247 -0
- package/runtimes/claude/skills/dry/SKILL.md +136 -0
- package/runtimes/claude/skills/frontend-rules/SKILL.md +369 -0
- package/runtimes/claude/skills/frontend-validator/SKILL.md +553 -0
- package/runtimes/claude/skills/kiss/SKILL.md +164 -0
- package/runtimes/claude/skills/solid/SKILL.md +267 -0
- package/runtimes/claude/skills/yagni/SKILL.md +193 -0
- package/runtimes/copilot/agents/jdi-adopter.agent.md +430 -0
- package/runtimes/copilot/agents/jdi-architect.agent.md +864 -0
- package/runtimes/copilot/agents/jdi-asker.agent.md +119 -0
- package/runtimes/copilot/agents/jdi-bootstrap.agent.md +213 -0
- package/runtimes/copilot/agents/jdi-planner.agent.md +221 -0
- package/runtimes/copilot/agents/jdi-researcher.agent.md +269 -0
- package/runtimes/copilot/copilot-instructions.md +80 -0
- package/runtimes/copilot/prompts/jdi-adopt.prompt.md +155 -0
- package/runtimes/copilot/prompts/jdi-bootstrap.prompt.md +81 -0
- package/runtimes/copilot/prompts/jdi-create.prompt.md +80 -0
- package/runtimes/copilot/prompts/jdi-discuss.prompt.md +80 -0
- package/runtimes/copilot/prompts/jdi-do.prompt.md +200 -0
- package/runtimes/copilot/prompts/jdi-loop.prompt.md +315 -0
- package/runtimes/copilot/prompts/jdi-new.prompt.md +131 -0
- package/runtimes/copilot/prompts/jdi-plan.prompt.md +73 -0
- package/runtimes/copilot/prompts/jdi-ship.prompt.md +146 -0
- package/runtimes/copilot/prompts/jdi-verify.prompt.md +159 -0
- package/runtimes/opencode/AGENTS.md +87 -0
- package/runtimes/opencode/agents/jdi-adopter.md +434 -0
- package/runtimes/opencode/agents/jdi-architect.md +861 -0
- package/runtimes/opencode/agents/jdi-asker.md +123 -0
- package/runtimes/opencode/agents/jdi-bootstrap.md +217 -0
- package/runtimes/opencode/agents/jdi-planner.md +225 -0
- package/runtimes/opencode/agents/jdi-researcher.md +273 -0
- package/runtimes/opencode/commands/jdi-adopt.md +155 -0
- package/runtimes/opencode/commands/jdi-bootstrap.md +81 -0
- package/runtimes/opencode/commands/jdi-create.md +80 -0
- package/runtimes/opencode/commands/jdi-discuss.md +80 -0
- package/runtimes/opencode/commands/jdi-do.md +200 -0
- package/runtimes/opencode/commands/jdi-loop.md +315 -0
- package/runtimes/opencode/commands/jdi-new.md +131 -0
- package/runtimes/opencode/commands/jdi-plan.md +73 -0
- package/runtimes/opencode/commands/jdi-ship.md +146 -0
- package/runtimes/opencode/commands/jdi-verify.md +159 -0
- package/runtimes/opencode/opencode.example.jsonc +169 -0
- package/runtimes/opencode/skills/clean-code/SKILL.md +247 -0
- package/runtimes/opencode/skills/dry/SKILL.md +136 -0
- package/runtimes/opencode/skills/frontend-rules/SKILL.md +369 -0
- package/runtimes/opencode/skills/frontend-validator/SKILL.md +553 -0
- package/runtimes/opencode/skills/kiss/SKILL.md +164 -0
- package/runtimes/opencode/skills/solid/SKILL.md +267 -0
- package/runtimes/opencode/skills/yagni/SKILL.md +193 -0
- package/templates-jdi-folder/config.json +18 -0
- package/templates-jdi-folder/registry.md +31 -0
- package/templates-jdi-folder/reviewers.md +33 -0
- package/templates-jdi-folder/skills-registry.md +32 -0
- package/templates-jdi-folder/specialists.md +39 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: solid
|
|
3
|
+
description: SOLID. Robert C. Martin's 5 OO design principles - SRP, OCP, LSP, ISP, DIP. Applicable in any language with types/classes/interfaces (C#, Java, TS, Python, Go, Rust, Kotlin, Swift, etc). Direct summary + anti-patterns + detection heuristics.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: SOLID
|
|
7
|
+
|
|
8
|
+
5 design principles — **S**ingle Responsibility, **O**pen/Closed, **L**iskov Substitution, **I**nterface Segregation, **D**ependency Inversion.
|
|
9
|
+
|
|
10
|
+
Applicable in any language with classes or interfaces. In FP/procedural, principles map to modules and functions (SRP: 1 function 1 responsibility; ISP: minimal parameters; DIP: dependency injection via parameter).
|
|
11
|
+
|
|
12
|
+
## S - Single Responsibility Principle (SRP)
|
|
13
|
+
|
|
14
|
+
> A class/module must have **1 reason to change**.
|
|
15
|
+
|
|
16
|
+
**1 reason = 1 stakeholder or 1 axis of change**, not "1 action".
|
|
17
|
+
|
|
18
|
+
`UserService` that does auth + persistence + sends email violates — there are 3 reasons to change (security team changes auth, DBA changes persistence, marketing changes email).
|
|
19
|
+
|
|
20
|
+
### Violation symptoms
|
|
21
|
+
- Class with generic name (`Manager`, `Helper`, `Service`, `Util`)
|
|
22
|
+
- Methods without thematic coherence
|
|
23
|
+
- Multiple imports of unrelated libraries (DB + email + crypto in the same class)
|
|
24
|
+
- Diff of one class affects separate domains in different sprints
|
|
25
|
+
|
|
26
|
+
### Fix
|
|
27
|
+
Extract responsibilities into separate classes:
|
|
28
|
+
- `UserAuthenticator` (auth)
|
|
29
|
+
- `UserRepository` (persistence)
|
|
30
|
+
- `UserNotifier` (email)
|
|
31
|
+
|
|
32
|
+
Compose in a coordinator if needed, but each piece has 1 reason.
|
|
33
|
+
|
|
34
|
+
## O - Open/Closed Principle (OCP)
|
|
35
|
+
|
|
36
|
+
> Open for **extension**, closed for **modification**.
|
|
37
|
+
|
|
38
|
+
Adding new behavior shouldn't require editing tested code. Use polymorphism, strategy, plugins, or config — not infinite if/else.
|
|
39
|
+
|
|
40
|
+
### Violation symptoms
|
|
41
|
+
- `switch (type)` that grows with every new feature
|
|
42
|
+
- `if (provider === "x") ... else if ... else if ...`
|
|
43
|
+
- Each new feature edits N existing classes instead of adding 1 new one
|
|
44
|
+
|
|
45
|
+
### Fix
|
|
46
|
+
- **Strategy pattern**: each case becomes an impl of an interface
|
|
47
|
+
- **Polymorphism**: subclass override instead of external switch
|
|
48
|
+
- **Registry**: `registry.register("x", handler)` — new feature just adds
|
|
49
|
+
- **Visitor**: for closed type hierarchies
|
|
50
|
+
|
|
51
|
+
### When to ignore
|
|
52
|
+
OCP is aspirational, not absolute. Apply at points of **known** variation (payment strategies are multiple), don't speculate (KISS + YAGNI).
|
|
53
|
+
|
|
54
|
+
## L - Liskov Substitution Principle (LSP)
|
|
55
|
+
|
|
56
|
+
> Subtype must be **substitutable** for the supertype without breaking behavior.
|
|
57
|
+
|
|
58
|
+
If `Bird` has method `fly()`, `Penguin extends Bird` violates LSP — penguin doesn't fly. Caller receiving `Bird` breaks when it receives `Penguin`.
|
|
59
|
+
|
|
60
|
+
### Violation symptoms
|
|
61
|
+
- Subclass that **throws exception** on inherited method ("not supported")
|
|
62
|
+
- Subclass that **strengthens precondition** (`base accepts >= 0`, sub accepts `> 0`)
|
|
63
|
+
- Subclass that **weakens postcondition** (base guarantees "sorted", sub doesn't guarantee)
|
|
64
|
+
- Caller needs `if (instanceof Subtype)` to handle a special case
|
|
65
|
+
|
|
66
|
+
### Fix
|
|
67
|
+
- Rethink hierarchy: `Penguin` isn't a flying `Bird`, it's a `Bird` that walks. Create `FlyingBird : Bird` and `Penguin : Bird` without fly.
|
|
68
|
+
- Composition over inheritance: prefer composed interfaces over deep hierarchies.
|
|
69
|
+
- Refactor to capabilities: `Flyable`, `Swimmable`, `Walkable` (overlap with ISP).
|
|
70
|
+
|
|
71
|
+
## I - Interface Segregation Principle (ISP)
|
|
72
|
+
|
|
73
|
+
> Clients should not be forced to depend on interfaces they **do not use**.
|
|
74
|
+
|
|
75
|
+
Big interfaces ("fat interfaces") force impls to stub meaningless methods (throwing NotImplemented), and callers to import broad dependencies.
|
|
76
|
+
|
|
77
|
+
### Violation symptoms
|
|
78
|
+
- Interface with 15 methods, each caller uses 2-3
|
|
79
|
+
- Impl with several methods throwing `throw new NotImplementedException()`
|
|
80
|
+
- Huge test mock to use 1 method of the interface
|
|
81
|
+
|
|
82
|
+
### Fix
|
|
83
|
+
Split into smaller, cohesive interfaces:
|
|
84
|
+
```
|
|
85
|
+
IFileReader { read() }
|
|
86
|
+
IFileWriter { write() }
|
|
87
|
+
// callers pick the subset they need
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
In FP/Go, same principle: minimal parameters, structural typing doesn't force implementing everything.
|
|
91
|
+
|
|
92
|
+
## D - Dependency Inversion Principle (DIP)
|
|
93
|
+
|
|
94
|
+
> High-level modules **do not** depend on low-level. Both depend on **abstractions**.
|
|
95
|
+
|
|
96
|
+
Business logic doesn't care about "PostgreSQL", "AWS S3", "SendGrid". It cares about abstractions (`UserRepository`, `BlobStorage`, `EmailSender`). Concrete implementations live in outer layers.
|
|
97
|
+
|
|
98
|
+
### Violation symptoms
|
|
99
|
+
- Domain class imports `pg`, `aws-sdk`, `sendgrid`, `axios`
|
|
100
|
+
- Business logic testable only with real infra (DB up, S3 mocked, etc)
|
|
101
|
+
- Swapping provider requires rewriting business logic
|
|
102
|
+
|
|
103
|
+
### Fix
|
|
104
|
+
- **Dependency injection** (constructor / property / parameter)
|
|
105
|
+
- Define interfaces in the domain module; impls live in infra/adapter layer (overlap with Hexagonal/Clean Architecture)
|
|
106
|
+
- Composition root injects the right impl
|
|
107
|
+
|
|
108
|
+
### DIP != IoC container
|
|
109
|
+
DIP is a principle. IoC container is **one** tool. You can apply DIP with manual constructor, simple factory, or parameter injection — no framework.
|
|
110
|
+
|
|
111
|
+
## Summary of the 5
|
|
112
|
+
|
|
113
|
+
| Letter | Focus | Key question |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| **S** | Unit cohesion | How many reasons to change this class/module? (>1 = violates) |
|
|
116
|
+
| **O** | Stability against extension | Does adding a new feature edit tested code or add new code? |
|
|
117
|
+
| **L** | Inheritance contract | Does replacing parent with child break any caller? |
|
|
118
|
+
| **I** | Interface size | Does every impl/caller use all methods? |
|
|
119
|
+
| **D** | Direction of dependency | Does domain import infra or does infra import domain? |
|
|
120
|
+
|
|
121
|
+
## General anti-patterns
|
|
122
|
+
|
|
123
|
+
| Anti-pattern | Principle violated |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `God class` with 30+ heterogeneous methods | SRP |
|
|
126
|
+
| `switch (kind)` in N callers, grows with each feature | OCP |
|
|
127
|
+
| Subclass with `throw NotSupportedException()` | LSP |
|
|
128
|
+
| `IRepository` with 20 generic methods | ISP |
|
|
129
|
+
| Domain service importing concrete ORM | DIP |
|
|
130
|
+
| Inheritance > 3 levels | LSP + SRP (usually) |
|
|
131
|
+
| Constructor with 8+ parameters | SRP (too many responsibilities) |
|
|
132
|
+
| Util class with 50 unrelated functions | SRP |
|
|
133
|
+
|
|
134
|
+
## Procedure
|
|
135
|
+
|
|
136
|
+
### Doer
|
|
137
|
+
|
|
138
|
+
Before creating class/module/interface:
|
|
139
|
+
- **SRP**: describe in 1 sentence. If you need "and", split.
|
|
140
|
+
- **ISP**: list expected callers. Does each one need **all** methods? If not, split.
|
|
141
|
+
- **DIP**: what does this depend on? Concretions (DB, HTTP, FS) -> inject as abstraction.
|
|
142
|
+
|
|
143
|
+
Before subclassing:
|
|
144
|
+
- **LSP**: does substitution by parent work in all callers? If not, wrong hierarchy.
|
|
145
|
+
|
|
146
|
+
Before adding `if/switch` at a growing point:
|
|
147
|
+
- **OCP**: is strategy/registry worth it?
|
|
148
|
+
|
|
149
|
+
### Reviewer (gate 5)
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# SRP — very large classes
|
|
153
|
+
find src/ -name '*.cs' -o -name '*.ts' -o -name '*.py' | while read f; do
|
|
154
|
+
loc=$(wc -l < "$f")
|
|
155
|
+
[[ $loc -gt 400 ]] && echo "WARN SRP: $f has $loc lines, possible god class"
|
|
156
|
+
done
|
|
157
|
+
|
|
158
|
+
# OCP — big switches on hot paths
|
|
159
|
+
grep -RnE 'switch\s*\(' src/ | head
|
|
160
|
+
# (manually: evaluate if it grows with each feature)
|
|
161
|
+
|
|
162
|
+
# LSP — NotImplemented in subclass
|
|
163
|
+
grep -RnE 'NotImplemented|UnsupportedOperation|throw new.*not (implemented|supported)' src/
|
|
164
|
+
|
|
165
|
+
# ISP — giant interfaces
|
|
166
|
+
grep -RnA50 '^(public |export )?interface' src/ | grep -cE '^\s+\w+\s*\(' | sort -rn
|
|
167
|
+
# count > 10 methods -> WARN
|
|
168
|
+
|
|
169
|
+
# DIP — domain module importing concretions
|
|
170
|
+
grep -RnE 'from.*pg|from.*aws-sdk|using Npgsql|using AWSSDK' src/domain/ src/core/
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Relevant match -> WARN with principle cited.
|
|
174
|
+
|
|
175
|
+
## Inputs
|
|
176
|
+
|
|
177
|
+
- Diff/content of the file
|
|
178
|
+
- Project structure (to detect domain vs infra)
|
|
179
|
+
|
|
180
|
+
## Outputs
|
|
181
|
+
|
|
182
|
+
Does NOT produce a file. Modifies judgement.
|
|
183
|
+
|
|
184
|
+
## Examples
|
|
185
|
+
|
|
186
|
+
### Example 1: SRP violated
|
|
187
|
+
|
|
188
|
+
Wrong:
|
|
189
|
+
```csharp
|
|
190
|
+
public class OrderService {
|
|
191
|
+
public Order Create(...) { /* validate + save + email + log + audit */ }
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
5 reasons to change.
|
|
196
|
+
|
|
197
|
+
Right:
|
|
198
|
+
```csharp
|
|
199
|
+
public class OrderValidator { }
|
|
200
|
+
public class OrderRepository { }
|
|
201
|
+
public class OrderNotifier { }
|
|
202
|
+
public class OrderService {
|
|
203
|
+
public Order Create(...) {
|
|
204
|
+
_validator.Validate(...)
|
|
205
|
+
var order = _repo.Save(...)
|
|
206
|
+
_notifier.Notify(order)
|
|
207
|
+
return order;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Example 2: OCP violated
|
|
213
|
+
|
|
214
|
+
Wrong:
|
|
215
|
+
```typescript
|
|
216
|
+
function calcDiscount(type: string, amount: number) {
|
|
217
|
+
if (type === "vip") return amount * 0.2
|
|
218
|
+
else if (type === "newcomer") return amount * 0.1
|
|
219
|
+
else if (type === "blackfriday") return amount * 0.5
|
|
220
|
+
return 0
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Each new type edits this function.
|
|
225
|
+
|
|
226
|
+
Right:
|
|
227
|
+
```typescript
|
|
228
|
+
const strategies: Record<string, (amount: number) => number> = {
|
|
229
|
+
vip: a => a * 0.2,
|
|
230
|
+
newcomer: a => a * 0.1,
|
|
231
|
+
blackfriday: a => a * 0.5,
|
|
232
|
+
}
|
|
233
|
+
function calcDiscount(type: string, amount: number) {
|
|
234
|
+
return strategies[type]?.(amount) ?? 0
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
New type: register in `strategies`, don't touch `calcDiscount`.
|
|
239
|
+
|
|
240
|
+
### Example 3: DIP violated
|
|
241
|
+
|
|
242
|
+
Wrong:
|
|
243
|
+
```python
|
|
244
|
+
# domain/order.py
|
|
245
|
+
import psycopg2
|
|
246
|
+
class OrderService:
|
|
247
|
+
def get(self, id):
|
|
248
|
+
conn = psycopg2.connect(...)
|
|
249
|
+
...
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Domain coupled to Postgres.
|
|
253
|
+
|
|
254
|
+
Right:
|
|
255
|
+
```python
|
|
256
|
+
# domain/order.py
|
|
257
|
+
class OrderService:
|
|
258
|
+
def __init__(self, repo: OrderRepository): # abstraction
|
|
259
|
+
self._repo = repo
|
|
260
|
+
def get(self, id): return self._repo.get(id)
|
|
261
|
+
|
|
262
|
+
# infra/postgres_order_repo.py
|
|
263
|
+
class PostgresOrderRepository(OrderRepository):
|
|
264
|
+
def get(self, id): ... # concrete impl
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Domain doesn't know Postgres exists.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yagni
|
|
3
|
+
description: YAGNI (You Aren't Gonna Need It). Build only what the current requirement asks for. Generalize after the 3rd real case, never before. Code not written is code with no bug, no maintenance cost, no pending test. Applies in any language.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: YAGNI
|
|
7
|
+
|
|
8
|
+
> You aren't gonna need it.
|
|
9
|
+
|
|
10
|
+
YAGNI is discipline against **speculative code**: features, abstractions, parameters, hooks, layers, configs that exist "in case it's needed". In 90% of cases, never needed — and when needed, the requirement is different from what you imagined.
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
### 1. Build only what the current requirement asks
|
|
15
|
+
|
|
16
|
+
Ask of every new line of code:
|
|
17
|
+
- **Does this functionality have a requirement today?**
|
|
18
|
+
- **Who is the caller that needs this NOW?**
|
|
19
|
+
|
|
20
|
+
If there's no real caller, don't write it. Dead code is **net negative**: latent bug, maintenance cost, distraction in review, hinders refactor.
|
|
21
|
+
|
|
22
|
+
### 2. Generalize after the 3rd real case
|
|
23
|
+
|
|
24
|
+
Sandi Metz: "Duplication is far cheaper than the wrong abstraction."
|
|
25
|
+
|
|
26
|
+
- 1 case: implement specific
|
|
27
|
+
- 2 cases: copy or minimally parameterize
|
|
28
|
+
- 3 cases: now extract real pattern (one you **saw** happen, not imagined)
|
|
29
|
+
|
|
30
|
+
Generalizing earlier couples callers to the wrong interface. Refactoring later to the right interface is cheap; breaking callers to swap a wrong generic interface is expensive.
|
|
31
|
+
|
|
32
|
+
### 3. Costs of speculative code
|
|
33
|
+
|
|
34
|
+
Every "in case it's needed" line costs:
|
|
35
|
+
|
|
36
|
+
- **Maintenance**: someone will touch it when refactoring the neighborhood
|
|
37
|
+
- **Confusion**: reader thinks "this is being used, must be important"
|
|
38
|
+
- **Tests**: untested code becomes a bomb; tested, wasted time
|
|
39
|
+
- **Coupling**: callers will couple to the speculative interface, making it hard to remove
|
|
40
|
+
- **Scope creep**: simple feature becomes complex feature
|
|
41
|
+
- **Bug surface**: a line that doesn't exist has no bug
|
|
42
|
+
|
|
43
|
+
### 4. What YAGNI is NOT
|
|
44
|
+
|
|
45
|
+
YAGNI is not an excuse to:
|
|
46
|
+
- **Hardcoded everywhere**: some extension points are real requirements (i18n, logging, auth)
|
|
47
|
+
- **Cut real requirement**: if ticket asks for X, deliver X complete, not half
|
|
48
|
+
- **Skip security/error handling**: these are universal requirements, not speculative
|
|
49
|
+
- **Raw illegible code**: clarity is a requirement, not speculation
|
|
50
|
+
- **Skip tests**: coverage is a contract
|
|
51
|
+
|
|
52
|
+
### 5. Symptoms of violation
|
|
53
|
+
|
|
54
|
+
Code smells of broken YAGNI if:
|
|
55
|
+
|
|
56
|
+
- Optional parameters never passed (`fn(a, b, opts?: {...})` with opts always `undefined`)
|
|
57
|
+
- Hooks/events without subscribers
|
|
58
|
+
- Plugin system without plugins
|
|
59
|
+
- Config "in case we want to change" that nobody ever changed
|
|
60
|
+
- Interface with 1 impl (overlap with KISS)
|
|
61
|
+
- Generic `<T>` used with only 1 type
|
|
62
|
+
- "Future-proof" architecture written to scale 100x before validating current requirement
|
|
63
|
+
- Branches in code for scenarios nobody can describe
|
|
64
|
+
|
|
65
|
+
### 6. How to remove
|
|
66
|
+
|
|
67
|
+
After discovering speculative code:
|
|
68
|
+
1. Confirm nobody calls (`grep` callers)
|
|
69
|
+
2. Delete. Yes, delete directly. Git keeps history.
|
|
70
|
+
3. Don't leave "// removed on XX/YY" — more trash.
|
|
71
|
+
4. If you find out later you need it, add it when you need it (safe bet: later you know the real requirement, not imagined).
|
|
72
|
+
|
|
73
|
+
## Anti-patterns
|
|
74
|
+
|
|
75
|
+
| Anti-pattern | Why it violates |
|
|
76
|
+
|---|---|
|
|
77
|
+
| Optional parameter never used | Adds surface area without benefit |
|
|
78
|
+
| "Generic" function used by 1 caller | Generalized too early |
|
|
79
|
+
| Plugin/extension point without extenders | Dead code carries maintenance |
|
|
80
|
+
| "Configurable" config nobody changes | False flexibility — becomes hardcode later |
|
|
81
|
+
| Try/catch for impossible exception | Indicates fear, not requirement |
|
|
82
|
+
| Defensive validation for value coming from a safe type | TypeScript/C#/Python types already guarantee |
|
|
83
|
+
| `for/while` instead of direct return for "future looping" | Invents speculative repetition |
|
|
84
|
+
| Layer "to make it generic" without 2nd impl | Speculation with pass-through cost |
|
|
85
|
+
| Comment "TODO: extend to X later" without ticket | Message to nobody |
|
|
86
|
+
| Abstraction with 1 concrete implementation | Generic abstraction without second case |
|
|
87
|
+
| `enum` with 1 value "will grow" | Add value when it appears |
|
|
88
|
+
|
|
89
|
+
## Procedure
|
|
90
|
+
|
|
91
|
+
### Doer (before/during implementation)
|
|
92
|
+
|
|
93
|
+
Before adding:
|
|
94
|
+
|
|
95
|
+
1. **Is there a current requirement?** (ticket, conversation, explicit business rule) Otherwise, don't add.
|
|
96
|
+
2. **Who calls this today?** If nobody, don't add.
|
|
97
|
+
3. **When will I use that flexibility?** If "don't know", don't add.
|
|
98
|
+
|
|
99
|
+
After writing, ask:
|
|
100
|
+
- Is there a parameter/config/branch that could disappear without losing requirement?
|
|
101
|
+
|
|
102
|
+
### Reviewer (gate 5)
|
|
103
|
+
|
|
104
|
+
Heuristics:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Optional parameters never passed
|
|
108
|
+
# (depends on stack — examples)
|
|
109
|
+
grep -RnE 'function \w+\([^)]*opts\?:' src/ # TS
|
|
110
|
+
grep -RnE '\([^)]*=\s*null\)' src/ # optional default null
|
|
111
|
+
|
|
112
|
+
# "TODO: extend" code
|
|
113
|
+
grep -RnE 'TODO.*(extend|future|reserved|placeholder|in case)' src/
|
|
114
|
+
|
|
115
|
+
# Try/catch without clear reason
|
|
116
|
+
grep -RnA3 'try\s*{' src/ | grep -B1 'catch.*:.*ignore'
|
|
117
|
+
|
|
118
|
+
# Declared and unused variables
|
|
119
|
+
# (linter already catches — confirm in review)
|
|
120
|
+
|
|
121
|
+
# Plugin/extension points
|
|
122
|
+
grep -RnE 'register|registerPlugin|EventEmitter|hook(' src/
|
|
123
|
+
# Cross-check: are there actually callers?
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
3+ matches without real caller -> WARN.
|
|
127
|
+
|
|
128
|
+
## Inputs
|
|
129
|
+
|
|
130
|
+
- File diff (focus on additions)
|
|
131
|
+
- List of callers if any
|
|
132
|
+
|
|
133
|
+
## Outputs
|
|
134
|
+
|
|
135
|
+
Does NOT produce a file. Modifies judgement — doer avoids writing, reviewer marks WARN.
|
|
136
|
+
|
|
137
|
+
## Examples
|
|
138
|
+
|
|
139
|
+
### Example 1: Speculative optional param
|
|
140
|
+
|
|
141
|
+
Wrong:
|
|
142
|
+
```python
|
|
143
|
+
def send_email(to: str, subject: str, body: str,
|
|
144
|
+
cc: list[str] = None,
|
|
145
|
+
bcc: list[str] = None,
|
|
146
|
+
attachments: list[Path] = None,
|
|
147
|
+
priority: str = "normal",
|
|
148
|
+
retry_count: int = 3,
|
|
149
|
+
on_failure: Callable = None):
|
|
150
|
+
...
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Current requirement is to send simple email (`to, subject, body`). The other 5 params are speculative.
|
|
154
|
+
|
|
155
|
+
Right:
|
|
156
|
+
```python
|
|
157
|
+
def send_email(to: str, subject: str, body: str):
|
|
158
|
+
...
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Add `cc`, `bcc` etc **when** real requirement arrives, not before.
|
|
162
|
+
|
|
163
|
+
### Example 2: Plugin system without plugins
|
|
164
|
+
|
|
165
|
+
Wrong:
|
|
166
|
+
```typescript
|
|
167
|
+
class PaymentProcessor {
|
|
168
|
+
private plugins: Plugin[] = []
|
|
169
|
+
registerPlugin(p: Plugin) { this.plugins.push(p) }
|
|
170
|
+
process(...) {
|
|
171
|
+
this.plugins.forEach(p => p.beforeProcess())
|
|
172
|
+
// logic
|
|
173
|
+
this.plugins.forEach(p => p.afterProcess())
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Has 0 plugins registered. Whole plugin system is dead code.
|
|
179
|
+
|
|
180
|
+
Right:
|
|
181
|
+
```typescript
|
|
182
|
+
class PaymentProcessor {
|
|
183
|
+
process(...) { /* logic */ }
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
When the 1st real plugin appears, then yes. Not before.
|
|
188
|
+
|
|
189
|
+
### Example 3: Unused config string
|
|
190
|
+
|
|
191
|
+
Wrong: `config.json -> "DEFAULT_LANGUAGE": "pt-BR"` but nobody reads it. Code uses `"pt-BR"` directly.
|
|
192
|
+
|
|
193
|
+
Right: delete the config. Add it when the multi-language feature is actually implemented.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema_version": "1.1",
|
|
3
|
+
"context_window": 200000,
|
|
4
|
+
"thresholds": {
|
|
5
|
+
"warn_pct": 60,
|
|
6
|
+
"critical_pct": 70
|
|
7
|
+
},
|
|
8
|
+
"budgets": {
|
|
9
|
+
"max_context_chars": 6000,
|
|
10
|
+
"max_plan_chars": 12000,
|
|
11
|
+
"max_summary_chars": 8192
|
|
12
|
+
},
|
|
13
|
+
"compaction": {
|
|
14
|
+
"keep_phases": 2,
|
|
15
|
+
"archive_after": 5
|
|
16
|
+
},
|
|
17
|
+
"coverage_min": 80
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# JDI — Registry
|
|
2
|
+
|
|
3
|
+
Audit trail de tudo que o `jdi-architect` cria. Append-only.
|
|
4
|
+
|
|
5
|
+
2 fontes:
|
|
6
|
+
- `/jdi-create` (modo create) — agent/skill generico em `core/`
|
|
7
|
+
- `/jdi-bootstrap` (modo specialist) — doer + reviewer per-project em `.jdi/agents/`
|
|
8
|
+
|
|
9
|
+
Cada entrada documenta: o que, por que, quando, como invocar.
|
|
10
|
+
|
|
11
|
+
## Format
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
## R-{N} ({date ISO})
|
|
15
|
+
**Tipo:** {agent | skill | composite | specialist}
|
|
16
|
+
**Nome ou slug:** {jdi-{nome}} ou {todo-app}
|
|
17
|
+
**Criado por:** /jdi-create | /jdi-bootstrap | manual
|
|
18
|
+
**Por que:** {razao em 1-2 linhas}
|
|
19
|
+
**Substitui:** {agent/skill anterior, se refactor}
|
|
20
|
+
**Files:**
|
|
21
|
+
- {path 1}
|
|
22
|
+
- {path 2}
|
|
23
|
+
**Integration:**
|
|
24
|
+
- {arquivo atualizado}
|
|
25
|
+
- {arquivo atualizado}
|
|
26
|
+
**Removido em:** {data + razao, se foi removido depois}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Entries
|
|
30
|
+
|
|
31
|
+
<!-- Append abaixo. Mais recente no fim. -->
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Reviewers
|
|
2
|
+
|
|
3
|
+
Per-project reviewer specialists. `/jdi-verify <N>` le esse arquivo.
|
|
4
|
+
|
|
5
|
+
Veredicto: APPROVED / APPROVED_WITH_WARNINGS / BLOCKED. BLOCKED bloqueia o `/jdi-ship`.
|
|
6
|
+
|
|
7
|
+
## Format
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
| Agent | Trigger | Bloqueia ship? |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| jdi-reviewer-{slug} | /jdi-verify | sim, se BLOCKED |
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Reviewers ficam em `.jdi/agents/jdi-reviewer-{slug}.md` (per-project).
|
|
16
|
+
|
|
17
|
+
## Como sao criados
|
|
18
|
+
|
|
19
|
+
Junto com o doer, via `/jdi-bootstrap` -> `jdi-architect` modo specialist. Template base em `core/templates/reviewer-specialist.md`.
|
|
20
|
+
|
|
21
|
+
Gates default (cada reviewer customiza):
|
|
22
|
+
- Build
|
|
23
|
+
- Tests
|
|
24
|
+
- Coverage (threshold do PROJECT.md, default 80%)
|
|
25
|
+
- Lint/Format
|
|
26
|
+
- Security/Perf rules da stack
|
|
27
|
+
- Plan consistency
|
|
28
|
+
|
|
29
|
+
## Entries
|
|
30
|
+
|
|
31
|
+
| Agent | Trigger | Bloqueia ship? |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
<!-- Append abaixo via /jdi-bootstrap -->
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Skills Registry
|
|
2
|
+
|
|
3
|
+
Skills disponiveis pros agents JDI carregarem on-demand.
|
|
4
|
+
|
|
5
|
+
Skill = procedimento reusavel. Sem decision loop proprio. Carregado inline pelo agent pai.
|
|
6
|
+
|
|
7
|
+
## Format
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
| Skill | Path | Quando aplicar | Loaded by |
|
|
11
|
+
|---|---|---|---|
|
|
12
|
+
| {nome} | core/skills/{nome}/ | {trigger} | {lista de agents} |
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Como agents carregam
|
|
16
|
+
|
|
17
|
+
Cada agent tem uma secao `<skills_to_load>` no prompt:
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
<skills_to_load>
|
|
21
|
+
- {skill-nome}: aplica quando {condicao}
|
|
22
|
+
- {outra-skill}: aplica quando {condicao}
|
|
23
|
+
</skills_to_load>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Agent le essa secao, carrega skill via Read no path correspondente, segue o procedure.
|
|
27
|
+
|
|
28
|
+
## Entries
|
|
29
|
+
|
|
30
|
+
| Skill | Path | Quando aplicar | Loaded by |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
<!-- Append abaixo via /jdi-create -->
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Specialists
|
|
2
|
+
|
|
3
|
+
Per-project doer specialists. Cada projeto tem 1 doer (ou mais, se multi-stack).
|
|
4
|
+
|
|
5
|
+
`/jdi-do <N>` le esse arquivo. Match -> spawn specialist registrado.
|
|
6
|
+
|
|
7
|
+
## Format
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
| Stack | Agent | Trigger |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| {stack} | jdi-doer-{slug} | {quando aplicar} |
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Specialists ficam em `.jdi/agents/jdi-doer-{slug}.md` (per-project, NAO no `core/`).
|
|
16
|
+
|
|
17
|
+
## Como sao criados
|
|
18
|
+
|
|
19
|
+
`/jdi-bootstrap` invoca o `jdi-architect` em modo specialist:
|
|
20
|
+
|
|
21
|
+
1. Le `.jdi/PROJECT.md`
|
|
22
|
+
2. 6 perguntas focadas (test framework, build/test commands, coverage min, lint, conventions)
|
|
23
|
+
3. Substitui placeholders em `core/templates/doer-specialist.md`
|
|
24
|
+
4. Write em `.jdi/agents/jdi-doer-{slug}.md`
|
|
25
|
+
5. Append linha aqui
|
|
26
|
+
|
|
27
|
+
## Multi-stack (futuro)
|
|
28
|
+
|
|
29
|
+
Projeto com backend + frontend pode ter 2 doers:
|
|
30
|
+
- `jdi-doer-{slug}-backend` (ex: .NET)
|
|
31
|
+
- `jdi-doer-{slug}-frontend` (ex: React)
|
|
32
|
+
|
|
33
|
+
`/jdi-do <N>` decide qual chamar baseado em `files_modified` do PLAN.md. Hoje `/jdi-bootstrap` cria 1 doer agregado — multi-doer eh feature pendente.
|
|
34
|
+
|
|
35
|
+
## Entries
|
|
36
|
+
|
|
37
|
+
| Stack | Agent | Trigger |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
<!-- Append abaixo via /jdi-bootstrap -->
|