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,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {NAME}
|
|
3
|
+
description: {ONE_LINE_DESCRIPTION}
|
|
4
|
+
type: skill
|
|
5
|
+
applies_to:
|
|
6
|
+
{WHEN_TO_APPLY}
|
|
7
|
+
loaded_by:
|
|
8
|
+
{AGENTS_THAT_LOAD}
|
|
9
|
+
runtime_overrides:
|
|
10
|
+
antigravity:
|
|
11
|
+
triggers:
|
|
12
|
+
{DISCOVERY_TRIGGERS}
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Skill: {NAME}
|
|
16
|
+
|
|
17
|
+
{DETAILED_DESCRIPTION}
|
|
18
|
+
|
|
19
|
+
## When to apply
|
|
20
|
+
|
|
21
|
+
{USAGE_CONDITIONS}
|
|
22
|
+
|
|
23
|
+
## Procedure
|
|
24
|
+
|
|
25
|
+
### Step 1: {NAME}
|
|
26
|
+
{DESCRIPTION}
|
|
27
|
+
|
|
28
|
+
### Step 2: {NAME}
|
|
29
|
+
{DESCRIPTION}
|
|
30
|
+
|
|
31
|
+
### Step 3: {NAME}
|
|
32
|
+
{DESCRIPTION}
|
|
33
|
+
|
|
34
|
+
## Expected inputs
|
|
35
|
+
|
|
36
|
+
{WHAT_THE_PARENT_AGENT_PROVIDES}
|
|
37
|
+
|
|
38
|
+
## Outputs
|
|
39
|
+
|
|
40
|
+
{WHAT_GOES_BACK_TO_PARENT_AGENT}
|
|
41
|
+
|
|
42
|
+
Does NOT produce own files. Modifies parent agent's work.
|
|
43
|
+
|
|
44
|
+
## References
|
|
45
|
+
|
|
46
|
+
- `references/{X}.md` — {DESCRIPTION}
|
|
47
|
+
- `references/{Y}.md` — {DESCRIPTION}
|
|
48
|
+
|
|
49
|
+
## Anti-patterns
|
|
50
|
+
|
|
51
|
+
- {THING_NOT_TO_DO}
|
|
52
|
+
- {THING_NOT_TO_DO}
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
### Example 1: {SCENARIO}
|
|
57
|
+
|
|
58
|
+
Input:
|
|
59
|
+
```
|
|
60
|
+
{EXAMPLE_INPUT}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Output (in parent agent context):
|
|
64
|
+
```
|
|
65
|
+
{EXAMPLE_OUTPUT}
|
|
66
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jdi-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JDI (Just Do It) — lean workflow toolkit for Claude Code, GitHub Copilot, Antigravity, and OpenCode. 10 commands (7 loop + ralph + adopt + meta), 6 core agents + N per-project specialists with file-glob routing. Optional Playwright MCP + Caveman plugin install.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"jdi": "bin/jdi.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/jdi.js",
|
|
10
|
+
"bin/lib",
|
|
11
|
+
"bin/jdi-build.sh",
|
|
12
|
+
"bin/jdi-build.ps1",
|
|
13
|
+
"bin/jdi-install.sh",
|
|
14
|
+
"bin/jdi-install.ps1",
|
|
15
|
+
"bin/jdi-update.sh",
|
|
16
|
+
"bin/jdi-update.ps1",
|
|
17
|
+
"bin/jdi-uninstall.sh",
|
|
18
|
+
"bin/jdi-uninstall.ps1",
|
|
19
|
+
"bin/jdi-doctor.sh",
|
|
20
|
+
"bin/jdi-doctor.ps1",
|
|
21
|
+
"bin/jdi-install-playwright.sh",
|
|
22
|
+
"bin/jdi-install-playwright.ps1",
|
|
23
|
+
"bin/jdi-install-caveman.sh",
|
|
24
|
+
"bin/jdi-install-caveman.ps1",
|
|
25
|
+
"bin/git-hooks",
|
|
26
|
+
"core",
|
|
27
|
+
"runtimes",
|
|
28
|
+
"templates-jdi-folder",
|
|
29
|
+
"README.md",
|
|
30
|
+
"ARCHITECTURE.md",
|
|
31
|
+
"AGENTS.md",
|
|
32
|
+
"COMMANDS.md",
|
|
33
|
+
"EXTENSION.md",
|
|
34
|
+
"CREATE.md",
|
|
35
|
+
"CREATE-EXAMPLE.md",
|
|
36
|
+
"PORTABILITY.md",
|
|
37
|
+
"MEMORY.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/slipalison/jdi-cli.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/slipalison/jdi-cli#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/slipalison/jdi-cli/issues"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"claude",
|
|
50
|
+
"claude-code",
|
|
51
|
+
"ai",
|
|
52
|
+
"agent",
|
|
53
|
+
"workflow",
|
|
54
|
+
"spec-driven-development",
|
|
55
|
+
"jdi",
|
|
56
|
+
"opencode",
|
|
57
|
+
"copilot",
|
|
58
|
+
"antigravity"
|
|
59
|
+
],
|
|
60
|
+
"author": "JDI contributors",
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=18.0.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "node bin/jdi.js build",
|
|
67
|
+
"doctor": "node bin/jdi.js doctor",
|
|
68
|
+
"test:local": "npm pack && echo 'Package generated. Test with: npx ./jdi-cli-1.4.0.tgz install <runtime>'"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# agents.md — JDI workflow (Antigravity)
|
|
2
|
+
|
|
3
|
+
Este projeto usa **JDI (Just Do It)** como workflow de desenvolvimento. JDI eh um workflow enxuto.
|
|
4
|
+
|
|
5
|
+
## Loop canonico
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/jdi-new "<descricao>" -> research + PROJECT.md + ROADMAP.md
|
|
9
|
+
/jdi-bootstrap -> cria specialists per-project
|
|
10
|
+
/jdi-discuss <N> -> captura decisoes locked
|
|
11
|
+
/jdi-plan <N> -> decompoe em tasks com waves
|
|
12
|
+
/jdi-do <N> -> executa via doer specialist
|
|
13
|
+
/jdi-verify <N> -> gates via reviewer specialist
|
|
14
|
+
/jdi-ship <N> -> finaliza phase
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`/jdi-create [desc]` cria agents/skills no `core/` (so dentro do repo JDI).
|
|
18
|
+
|
|
19
|
+
## Skills disponiveis
|
|
20
|
+
|
|
21
|
+
Em `.gemini/antigravity/skills/`. Discovery via triggers.
|
|
22
|
+
|
|
23
|
+
| Skill | Funcao |
|
|
24
|
+
|---|---|
|
|
25
|
+
| jdi-researcher | Research pre-roadmap (PROJECT.md + ROADMAP.md) |
|
|
26
|
+
| jdi-bootstrap | Cria specialists per-project (doer + reviewer) |
|
|
27
|
+
| jdi-asker | Loop de perguntas pra CONTEXT.md |
|
|
28
|
+
| jdi-planner | Gera PLAN.md com waves |
|
|
29
|
+
| jdi-architect | Meta-skill: cria agents/skills/specialists |
|
|
30
|
+
|
|
31
|
+
## Specialists per-project
|
|
32
|
+
|
|
33
|
+
Apos `/jdi-bootstrap`, criados em `.jdi/agents/jdi-doer-{slug}.md` e `.jdi/agents/jdi-reviewer-{slug}.md`.
|
|
34
|
+
|
|
35
|
+
Pra Antigravity reconhecer, copie pra `.gemini/antigravity/skills/` (ou rode `jdi-install.sh antigravity` apos bootstrap).
|
|
36
|
+
|
|
37
|
+
## Triggers
|
|
38
|
+
|
|
39
|
+
Digite `/jdi-discuss N` ou peca em natural — ex: "discutir phase 2", "iniciar phase 1", "executar phase 3", "verificar entrega da phase". Skills tem triggers prefixados `jdi-` pra evitar falsos positivos.
|
|
40
|
+
|
|
41
|
+
## Memoria — files em `.jdi/`
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
.jdi/
|
|
45
|
+
PROJECT.md, ROADMAP.md, DECISIONS.md, STATE.md
|
|
46
|
+
specialists.md, reviewers.md, registry.md
|
|
47
|
+
agents/ <- per-project specialists
|
|
48
|
+
phases/{NN-slug}/{CONTEXT,PLAN,SUMMARY,REVIEW}.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Limitacoes Antigravity
|
|
52
|
+
|
|
53
|
+
- **Sem restricao formal de tools**: cada SKILL.md documenta privilegios via prosa. Confianca via review.
|
|
54
|
+
- **Sem hooks runtime**: pre-commit/post-commit ficam em `.githooks/`. Ativar com `git config core.hooksPath .githooks`.
|
|
55
|
+
- **Discovery por trigger**: prefixo `jdi-` evita falsos positivos.
|
|
56
|
+
- **Subagent spawn limitado**: paralelizacao por default sequential. Use prompts explicitos pra paralelo.
|
|
57
|
+
|
|
58
|
+
## Convencoes
|
|
59
|
+
|
|
60
|
+
- Conventional Commits — scope = phase slug
|
|
61
|
+
- Atomic commits — 1 task = 1 commit
|
|
62
|
+
- 80% cobertura minima
|
|
63
|
+
- Code design locked no `/jdi-new`
|
|
64
|
+
|
|
65
|
+
## Idioma
|
|
66
|
+
|
|
67
|
+
- Codigo/commits/PRs: ingles
|
|
68
|
+
- Discussao/docs em `.jdi/`: pt-BR
|
|
69
|
+
|
|
70
|
+
## Prioridade quando conflita
|
|
71
|
+
|
|
72
|
+
1. Seguranca
|
|
73
|
+
2. Performance
|
|
74
|
+
3. Boas praticas
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clean-code
|
|
3
|
+
description: Clean Code. Human-readable code, optimized for reading (read 10x more than written). Names reveal intent, small functions, no redundant comments, explicit error handling, no magic numbers. Applies in any language.
|
|
4
|
+
triggers:
|
|
5
|
+
- "Clean Code"
|
|
6
|
+
- "clean code"
|
|
7
|
+
- "code smells"
|
|
8
|
+
- "code readability"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Skill: Clean Code
|
|
12
|
+
|
|
13
|
+
> Code is read 10x more than written. Optimize for reading.
|
|
14
|
+
|
|
15
|
+
Not about pretty style. About **making reader understand in 1 pass**, without mentally simulating execution.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
### 1. Names reveal intent
|
|
20
|
+
|
|
21
|
+
- **Variable**: what it **is**, not how it's stored (`elapsedSeconds` > `t` > `time`)
|
|
22
|
+
- **Function**: what it **does**, verb + object (`calculateTax(order)`, not `tax(order)`)
|
|
23
|
+
- **Boolean**: yes/no question (`isActive`, `hasPermission`, `canSubmit`)
|
|
24
|
+
- **Class/module**: noun (`OrderRepository`, `EmailValidator`)
|
|
25
|
+
- **Interface**: role/capability (`Repository`, `Cacheable`) — no `I`/`Abstract` prefix if language doesn't require
|
|
26
|
+
|
|
27
|
+
### Anti-names
|
|
28
|
+
|
|
29
|
+
| Wrong | Right |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `data`, `info`, `value`, `temp`, `result` | something descriptive of the context |
|
|
32
|
+
| `processData()` | `parseUserPayload()`, `applyDiscount()`, etc |
|
|
33
|
+
| `Manager`, `Helper`, `Util` | name of the real responsibility |
|
|
34
|
+
| `flag`, `status` | `isComplete`, `paymentStatus` |
|
|
35
|
+
| `obj`, `item`, `thing` | actual type |
|
|
36
|
+
| Abbreviations: `usr`, `ctx`, `mgr`, `cfg` | `user`, `context` (exception: well-established domain convention) |
|
|
37
|
+
| Variables with `_2`, `_new`, `_old` | refactor until 1 remains |
|
|
38
|
+
|
|
39
|
+
### 2. Small functions
|
|
40
|
+
|
|
41
|
+
- **Size**: ideally < 20 lines. If over 50, almost certainly doing too much.
|
|
42
|
+
- **1 abstraction level**: inside function, all operations at same level. Mixing "open connection" + "calculate tax" = no.
|
|
43
|
+
- **3-4 params max**: more than that signals either missing object or too much responsibility.
|
|
44
|
+
- **1 logical exit**: early return is OK; multiple returns mid-complex-logic is bad.
|
|
45
|
+
|
|
46
|
+
### 3. Functions do **one** thing
|
|
47
|
+
|
|
48
|
+
If you describe function as "does X **and** Y", split into two. Function name must be precise.
|
|
49
|
+
|
|
50
|
+
Exception: orchestrators (controllers, command handlers) coordinate — OK to describe as "validates, saves, notifies" if each step is a call to another function.
|
|
51
|
+
|
|
52
|
+
### 4. Comments
|
|
53
|
+
|
|
54
|
+
**Default: DON'T write**.
|
|
55
|
+
|
|
56
|
+
Well-named code doesn't need to explain **what** it does. If you feel like writing a comment, first attempt: rename function/variable.
|
|
57
|
+
|
|
58
|
+
### Comments OK when:
|
|
59
|
+
|
|
60
|
+
- **Why** non-obvious: workaround for specific bug, surprising design decision, external constraint
|
|
61
|
+
- **Critical invariant**: "this array MUST be sorted for binary search to work"
|
|
62
|
+
- **TODO/FIXME marker** with ticket link: `// TODO(#1234): handle UTF-16 surrogate`
|
|
63
|
+
- **Pitfall warning**: "// don't call this in a loop, O(n^2)"
|
|
64
|
+
- **Public API doc**: contract for caller (params, returns, exceptions)
|
|
65
|
+
|
|
66
|
+
### Comments bad when:
|
|
67
|
+
|
|
68
|
+
- Explain **what** (code already says)
|
|
69
|
+
- Repeat the function name in English
|
|
70
|
+
- Comment goes stale relative to code
|
|
71
|
+
- "// removed on XX/YY" left in the tree
|
|
72
|
+
- "// hack" without explanation
|
|
73
|
+
- "// not sure why this works" — investigate, don't guess
|
|
74
|
+
|
|
75
|
+
### 5. Explicit error handling
|
|
76
|
+
|
|
77
|
+
- **Never silence exception**: `try { ... } catch {}` is **latent bug**
|
|
78
|
+
- **Explicit handling**: structured log + rethrow OR return Result/Either
|
|
79
|
+
- **Errors are part of the contract**: document what can fail
|
|
80
|
+
- **Boundary**: handle error at the boundary (controller, top-level handler), not at every internal call
|
|
81
|
+
- **Validation**: at the entry (boundary), not scattered
|
|
82
|
+
|
|
83
|
+
### 6. No magic numbers/strings
|
|
84
|
+
|
|
85
|
+
- Number or string with meaning becomes a **named constant**
|
|
86
|
+
- `if (status === 3)` becomes `if (status === OrderStatus.Shipped)`
|
|
87
|
+
- `setTimeout(fn, 86400000)` becomes `setTimeout(fn, MS_PER_DAY)`
|
|
88
|
+
- Exception: 0, 1, -1, universally clear cases
|
|
89
|
+
|
|
90
|
+
### 7. Command-Query Separation (CQS)
|
|
91
|
+
|
|
92
|
+
- **Query**: returns info, does **not** mutate state
|
|
93
|
+
- **Command**: mutates state, does **not** return (or returns void/minimal ack)
|
|
94
|
+
|
|
95
|
+
`function getUser(id)` that **also** updates last_access violates CQS — caller doesn't expect side-effect. Split.
|
|
96
|
+
|
|
97
|
+
### 8. Boy Scout Rule
|
|
98
|
+
|
|
99
|
+
> Leave the campground cleaner than you found it.
|
|
100
|
+
|
|
101
|
+
Touched a file? Small cleanness improvement is OK in the same commit:
|
|
102
|
+
- Rename obscure variable
|
|
103
|
+
- Split giant function you already had to read
|
|
104
|
+
- Remove stale comment
|
|
105
|
+
- Delete dead code
|
|
106
|
+
|
|
107
|
+
No heavy unrelated refactor — atomic commit still rules.
|
|
108
|
+
|
|
109
|
+
### 9. Formatting
|
|
110
|
+
|
|
111
|
+
- **Auto-format**: linter/formatter on the project (prettier, dotnet format, ruff format, gofmt) — no human decisions
|
|
112
|
+
- **Member order**: consistent convention (publics before privates, or group by feature)
|
|
113
|
+
- **Blank line**: separates logical blocks. Function all glued together is hard to scan.
|
|
114
|
+
- **Consistent indentation**: respect project convention
|
|
115
|
+
|
|
116
|
+
### 10. Symmetry and consistency
|
|
117
|
+
|
|
118
|
+
- Functions at same "level" have similar signature
|
|
119
|
+
- `getUserById, getUserByEmail` — consistent param order
|
|
120
|
+
- Exceptions "as is" vs "throws" vs Result — pick **one** style in the project
|
|
121
|
+
- `null` vs `undefined` vs `Option` — pick **one**
|
|
122
|
+
- Consistent naming convention (camelCase, PascalCase, snake_case) following the language
|
|
123
|
+
|
|
124
|
+
## Classic code smells
|
|
125
|
+
|
|
126
|
+
| Smell | Symptom |
|
|
127
|
+
|---|---|
|
|
128
|
+
| **Long function** | > 50 lines |
|
|
129
|
+
| **Long parameter list** | > 4 params |
|
|
130
|
+
| **God class** | 1 class does everything |
|
|
131
|
+
| **Feature envy** | method uses more data from **another** class than its own |
|
|
132
|
+
| **Data clump** | same 3-4 params bundled in multiple places -> object |
|
|
133
|
+
| **Primitive obsession** | everything is string/int, no value objects |
|
|
134
|
+
| **Scattered switch statements** | OCP violated |
|
|
135
|
+
| **Shotgun surgery** | simple change touches N files |
|
|
136
|
+
| **Divergent change** | 1 class changes for 5 different reasons (SRP) |
|
|
137
|
+
| **Dead code** | function/parameter/var never used |
|
|
138
|
+
| **Speculative generality** | flexibility without caller (YAGNI) |
|
|
139
|
+
| **Comments compensating bad code** | refactor code, delete comment |
|
|
140
|
+
| **Magic numbers** | 86400, 1024 with no name |
|
|
141
|
+
| **Silenced exceptions** | `catch {}`, `catch (Exception _) { }` |
|
|
142
|
+
| **Long if/else chains** | use polymorphism/strategy |
|
|
143
|
+
| **Inconsistent naming** | `getUser` here, `fetchAccount` there, `loadOrder` over there |
|
|
144
|
+
| **Boolean parameter** | `fn(true)` at caller — nobody knows what `true` means |
|
|
145
|
+
|
|
146
|
+
## Procedure
|
|
147
|
+
|
|
148
|
+
### Doer
|
|
149
|
+
|
|
150
|
+
After writing:
|
|
151
|
+
1. **Name review**: does each variable/function have a name that stands without a comment? Rename if not.
|
|
152
|
+
2. **Size**: any function > 30 lines? Split.
|
|
153
|
+
3. **Magic**: any number/string without obvious meaning? Constant.
|
|
154
|
+
4. **Redundant comments**: any comment that just repeats the code? Delete.
|
|
155
|
+
5. **Error handling**: any silent `catch {}`? Log or rethrow.
|
|
156
|
+
|
|
157
|
+
### Reviewer (gate 5)
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Long functions
|
|
161
|
+
awk '/^(function|def|public |private |protected |async )/ { start=NR; name=$0 }
|
|
162
|
+
/^}$|^\s{0,2}}\s*$/ { if (NR-start > 50) print FILENAME":"start": function with "(NR-start)" lines: "name }' src/**/*.{ts,cs,py,go,java}
|
|
163
|
+
|
|
164
|
+
# Magic numbers (typical suspects)
|
|
165
|
+
grep -RnE '\b(86400|3600|1024|65535|1000000)\b' src/
|
|
166
|
+
|
|
167
|
+
# Silent catch
|
|
168
|
+
grep -RnE 'catch\s*(\([^)]*\))?\s*\{\s*\}' src/
|
|
169
|
+
grep -RnE 'except.*:\s*pass\s*$' src/
|
|
170
|
+
grep -RnE 'catch.*:.*ignore' src/
|
|
171
|
+
|
|
172
|
+
# TODO without ticket
|
|
173
|
+
grep -RnE 'TODO(?!.*#\d+)|FIXME(?!.*#\d+)' src/
|
|
174
|
+
|
|
175
|
+
# Suspect boolean params
|
|
176
|
+
grep -RnE 'function \w+\([^)]*: bool|: boolean' src/
|
|
177
|
+
|
|
178
|
+
# Generic name
|
|
179
|
+
grep -RnE '\b(data|info|value|temp|tmp|result|obj|item|thing)\b\s*[:=]' src/ | head -20
|
|
180
|
+
|
|
181
|
+
# Dead code (linter catches — confirm)
|
|
182
|
+
|
|
183
|
+
# Obvious comments
|
|
184
|
+
grep -RnE '^\s*//\s*(get|set|return|increment|decrement|loop|iterate)\b' src/
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Relevant match -> WARN or BLOCK depending on severity.
|
|
188
|
+
|
|
189
|
+
## Inputs
|
|
190
|
+
|
|
191
|
+
- Diff/content of the file
|
|
192
|
+
- Project convention (linter config, naming convention from PROJECT.md)
|
|
193
|
+
|
|
194
|
+
## Outputs
|
|
195
|
+
|
|
196
|
+
Does NOT produce a file. Modifies judgement.
|
|
197
|
+
|
|
198
|
+
## Examples
|
|
199
|
+
|
|
200
|
+
### Example 1: bad names
|
|
201
|
+
|
|
202
|
+
Wrong:
|
|
203
|
+
```python
|
|
204
|
+
def calc(d, t):
|
|
205
|
+
r = d * t * 0.18
|
|
206
|
+
return r
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Right:
|
|
210
|
+
```python
|
|
211
|
+
VAT_RATE = 0.18
|
|
212
|
+
|
|
213
|
+
def calculate_vat(amount: Decimal, qty: int) -> Decimal:
|
|
214
|
+
return amount * qty * VAT_RATE
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Example 2: giant function
|
|
218
|
+
|
|
219
|
+
Wrong: 1 function of 120 lines validating, saving, sending email, logging.
|
|
220
|
+
|
|
221
|
+
Right: 4 functions of 20 lines each + 1 orchestrator of 15 lines that coordinates.
|
|
222
|
+
|
|
223
|
+
### Example 3: silenced exception
|
|
224
|
+
|
|
225
|
+
Wrong:
|
|
226
|
+
```typescript
|
|
227
|
+
try {
|
|
228
|
+
await sendNotification(user)
|
|
229
|
+
} catch {}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Latent bug — notification failure disappears.
|
|
233
|
+
|
|
234
|
+
Right:
|
|
235
|
+
```typescript
|
|
236
|
+
try {
|
|
237
|
+
await sendNotification(user)
|
|
238
|
+
} catch (err) {
|
|
239
|
+
logger.error("notification failed", { userId: user.id, err })
|
|
240
|
+
// intentional: notification is best-effort, doesn't block flow
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Comment here justifies the "why" of the no-rethrow.
|
|
245
|
+
|
|
246
|
+
### Example 4: boolean param
|
|
247
|
+
|
|
248
|
+
Wrong: `createUser("alice", "alice@x.com", true, false)`
|
|
249
|
+
|
|
250
|
+
Right: `createUser({ name: "alice", email: "alice@x.com", admin: true, sendWelcome: false })`
|
|
251
|
+
|
|
252
|
+
Caller becomes self-documented.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dry
|
|
3
|
+
description: DRY (Don't Repeat Yourself). 1 source of truth per piece of knowledge. Detects real duplication (same decision in 2+ places) and separates from apparent duplication (same code, different reasons). Applies in any language.
|
|
4
|
+
triggers:
|
|
5
|
+
- "DRY"
|
|
6
|
+
- "code duplication"
|
|
7
|
+
- "Don't Repeat Yourself"
|
|
8
|
+
- "duplicate refactor"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Skill: DRY
|
|
12
|
+
|
|
13
|
+
> Every piece of knowledge has **one** authoritative, unambiguous representation in the system.
|
|
14
|
+
|
|
15
|
+
DRY is not about copying code. It's about **duplicated knowledge** — business rule, formula, format, decision, present in 2+ places and that change **together** when the requirement changes.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
### 1. Knowledge duplication != code coincidence
|
|
20
|
+
|
|
21
|
+
**Knowledge duplication (violates DRY):**
|
|
22
|
+
- Tax calculation in 2 places
|
|
23
|
+
- CPF validation in 3 services
|
|
24
|
+
- Date format schema scattered across the app
|
|
25
|
+
- Same timeout constant in 4 files
|
|
26
|
+
|
|
27
|
+
When the rule changes, you change in N places — miss one, system becomes inconsistent.
|
|
28
|
+
|
|
29
|
+
**Code coincidence (does NOT violate DRY):**
|
|
30
|
+
- 2 functions with 5 identical lines but different domains
|
|
31
|
+
- Framework boilerplate repeated (every controller has `[Authorize]`)
|
|
32
|
+
- Similar iteration loop in 2 unrelated contexts
|
|
33
|
+
|
|
34
|
+
Forcing abstraction here couples things that shouldn't be coupled.
|
|
35
|
+
|
|
36
|
+
### 2. Rule of 3
|
|
37
|
+
|
|
38
|
+
- 1 occurrence: leave it
|
|
39
|
+
- 2 occurrences: pay attention, but don't abstract yet
|
|
40
|
+
- 3 occurrences: abstract (probably real knowledge duplication)
|
|
41
|
+
|
|
42
|
+
Skipping this rule produces **premature abstraction** — worse than duplication because callers are already coupled to the wrong interface.
|
|
43
|
+
|
|
44
|
+
### 3. Types of DRY
|
|
45
|
+
|
|
46
|
+
**Code DRY:** reusable functions/classes for repeated logic
|
|
47
|
+
**Data DRY:** 1 source schema (generates DTO + validator + DB + docs)
|
|
48
|
+
**Process DRY:** 1 build script that serves dev + CI + prod
|
|
49
|
+
**Documentation DRY:** docs generated from code, not written in parallel
|
|
50
|
+
|
|
51
|
+
### 4. Single Source of Truth (SSoT)
|
|
52
|
+
|
|
53
|
+
For each piece of knowledge, identify:
|
|
54
|
+
- **Where the truth lives** (DB schema, env config, business rule)
|
|
55
|
+
- **Who derives from it** (DTOs, types, docs, UI)
|
|
56
|
+
- **Derivation tool** (codegen, schema migration, type inference)
|
|
57
|
+
|
|
58
|
+
Never: manually edit both the source and the derived.
|
|
59
|
+
|
|
60
|
+
### 5. When NOT to apply DRY
|
|
61
|
+
|
|
62
|
+
- **Premature abstraction**: 2 similar callers but with diverging future evolutions -> leave duplicated
|
|
63
|
+
- **Cross-boundary**: duplicating across microservices > coupling via shared lib
|
|
64
|
+
- **Test setup**: readable redundant tests > shared magical helpers
|
|
65
|
+
- **Wrong abstraction**: better duplicate than extract wrong (Sandi Metz: "duplication is far cheaper than the wrong abstraction")
|
|
66
|
+
|
|
67
|
+
## Anti-patterns
|
|
68
|
+
|
|
69
|
+
| Anti-pattern | Why it violates |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Copy-paste without extraction after 3rd occurrence | Duplicated knowledge, each caller silently diverges |
|
|
72
|
+
| Utility helper with 15 unrelated functions | "DRY" became ball of mud — bundles unrelated things |
|
|
73
|
+
| Abstraction after 2nd duplication with extension hooks "just in case" | Premature abstraction + YAGNI violated together |
|
|
74
|
+
| Same constant hardcoded in 4 files | Single source of truth absent — extract to config |
|
|
75
|
+
| Validation logic duplicated client + server | Code OK, but should share schema (Zod, Pydantic, JSON Schema) |
|
|
76
|
+
| Comment explaining what code does | Doc duplicates code, will go out of sync |
|
|
77
|
+
|
|
78
|
+
## Procedure
|
|
79
|
+
|
|
80
|
+
### Doer (before writing)
|
|
81
|
+
|
|
82
|
+
1. Is there an identical rule/calculation/constant elsewhere in the codebase? If so, refer/import. Don't duplicate.
|
|
83
|
+
2. Going to create the 2nd occurrence? OK, but mentally mark it.
|
|
84
|
+
3. Going to create the 3rd? Stop. Refactor first into abstraction, then use.
|
|
85
|
+
|
|
86
|
+
### Reviewer (gate 5)
|
|
87
|
+
|
|
88
|
+
Per-language specific greps (examples):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Duplicated magic constants
|
|
92
|
+
grep -RnE '\b86400\b|\b3600\b|\b1024\b' src/
|
|
93
|
+
|
|
94
|
+
# Same string in 3+ places
|
|
95
|
+
sort src/ | uniq -c | sort -rn | head -20 # coarse heuristic
|
|
96
|
+
|
|
97
|
+
# Duplicated email/CPF/etc validation
|
|
98
|
+
grep -RnE 'regex.*@|EmailRegex|CpfValidator|cpf_pattern' src/
|
|
99
|
+
|
|
100
|
+
# Hardcoded URLs/endpoints
|
|
101
|
+
grep -RnE 'https?://[a-z0-9.-]+\.[a-z]{2,}' src/ --include='*.ts' --include='*.cs' --include='*.py'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
3+ matches of the same pattern in different files -> WARN.
|
|
105
|
+
|
|
106
|
+
## Inputs
|
|
107
|
+
|
|
108
|
+
- Diff or content of the modified file
|
|
109
|
+
- Context: project stack for adapted greps
|
|
110
|
+
|
|
111
|
+
## Outputs
|
|
112
|
+
|
|
113
|
+
Does NOT produce a file. Modifies judgement:
|
|
114
|
+
- Doer chooses to reuse/extract
|
|
115
|
+
- Reviewer marks WARN with pointer to refactor
|
|
116
|
+
|
|
117
|
+
## Examples
|
|
118
|
+
|
|
119
|
+
### Example 1: 3 services calculating tax
|
|
120
|
+
|
|
121
|
+
Wrong:
|
|
122
|
+
```
|
|
123
|
+
service A: total * 0.18
|
|
124
|
+
service B: amount * 0.18
|
|
125
|
+
service C: value * 0.18
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Right: extract `TaxCalculator.applyVat(amount)` or `const VAT_RATE = 0.18` in shared config.
|
|
129
|
+
|
|
130
|
+
Reviewer marks WARN: "tax 0.18 hardcoded in 3 services. Extract to `config/tax.{ts,cs,py}`."
|
|
131
|
+
|
|
132
|
+
### Example 2: Code coincidence (does NOT violate DRY)
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
function findUser(id) { return db.query(...) }
|
|
136
|
+
function findOrder(id) { return db.query(...) }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Same structure, different domains. **Don't** extract `findEntity(id, table)` — will force abstraction that will diverge (user has soft-delete, order has cache, etc).
|
|
140
|
+
|
|
141
|
+
Reviewer ignores — code coincidence is OK.
|