jdi-cli 0.1.1 → 0.1.3
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/README.md +6 -0
- package/core/agents/jdi-architect.md +19 -0
- package/core/agents/jdi-researcher.md +2 -1
- package/core/commands/jdi-add-phase.md +171 -0
- package/core/commands/jdi-remove-phase.md +228 -0
- package/core/skills/clean-architecture/SKILL.md +134 -0
- package/core/skills/ddd/SKILL.md +140 -0
- package/core/skills/hexagonal/SKILL.md +127 -0
- package/core/skills/onion/SKILL.md +133 -0
- package/core/skills/the-method/SKILL.md +139 -0
- package/core/skills/vertical-slice/SKILL.md +127 -0
- package/package.json +1 -1
- package/runtimes/antigravity/agents.md +4 -0
- package/runtimes/antigravity/skills/clean-architecture/SKILL.md +125 -0
- package/runtimes/antigravity/skills/ddd/SKILL.md +131 -0
- package/runtimes/antigravity/skills/hexagonal/SKILL.md +118 -0
- package/runtimes/antigravity/skills/jdi-add-phase/SKILL.md +171 -0
- package/runtimes/antigravity/skills/jdi-architect/SKILL.md +19 -0
- package/runtimes/antigravity/skills/jdi-remove-phase/SKILL.md +228 -0
- package/runtimes/antigravity/skills/jdi-researcher/SKILL.md +2 -1
- package/runtimes/antigravity/skills/onion/SKILL.md +124 -0
- package/runtimes/antigravity/skills/the-method/SKILL.md +130 -0
- package/runtimes/antigravity/skills/vertical-slice/SKILL.md +118 -0
- package/runtimes/claude/CLAUDE.md +4 -0
- package/runtimes/claude/agents/jdi-architect.md +19 -0
- package/runtimes/claude/agents/jdi-researcher.md +2 -1
- package/runtimes/claude/commands/jdi-add-phase.md +171 -0
- package/runtimes/claude/commands/jdi-remove-phase.md +228 -0
- package/runtimes/claude/skills/clean-architecture/SKILL.md +120 -0
- package/runtimes/claude/skills/ddd/SKILL.md +125 -0
- package/runtimes/claude/skills/hexagonal/SKILL.md +112 -0
- package/runtimes/claude/skills/onion/SKILL.md +119 -0
- package/runtimes/claude/skills/the-method/SKILL.md +124 -0
- package/runtimes/claude/skills/vertical-slice/SKILL.md +113 -0
- package/runtimes/copilot/agents/jdi-architect.agent.md +19 -0
- package/runtimes/copilot/agents/jdi-researcher.agent.md +2 -1
- package/runtimes/copilot/copilot-instructions.md +4 -0
- package/runtimes/copilot/prompts/jdi-add-phase.prompt.md +171 -0
- package/runtimes/copilot/prompts/jdi-remove-phase.prompt.md +228 -0
- package/runtimes/opencode/AGENTS.md +4 -0
- package/runtimes/opencode/agents/jdi-architect.md +19 -0
- package/runtimes/opencode/agents/jdi-researcher.md +2 -1
- package/runtimes/opencode/commands/jdi-add-phase.md +171 -0
- package/runtimes/opencode/commands/jdi-remove-phase.md +228 -0
- package/runtimes/opencode/skills/clean-architecture/SKILL.md +120 -0
- package/runtimes/opencode/skills/ddd/SKILL.md +125 -0
- package/runtimes/opencode/skills/hexagonal/SKILL.md +112 -0
- package/runtimes/opencode/skills/onion/SKILL.md +119 -0
- package/runtimes/opencode/skills/the-method/SKILL.md +124 -0
- package/runtimes/opencode/skills/vertical-slice/SKILL.md +113 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jdi-add-phase
|
|
3
|
+
description: Adds a new phase to ROADMAP.md. Append at end (default) or insert at a specific position. Bumps total_phases. Atomic commit.
|
|
4
|
+
argument_hint: "\"<phase name>\" [--goal \"<goal>\"] [--at <position>]"
|
|
5
|
+
runtime_intent:
|
|
6
|
+
invokes_agent: none
|
|
7
|
+
runtime_overrides:
|
|
8
|
+
claude:
|
|
9
|
+
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion]
|
|
10
|
+
copilot:
|
|
11
|
+
tools: [read, write, edit, grep, glob, terminal]
|
|
12
|
+
opencode:
|
|
13
|
+
subtask: true
|
|
14
|
+
antigravity:
|
|
15
|
+
triggers:
|
|
16
|
+
- "/jdi-add-phase"
|
|
17
|
+
- "add new phase"
|
|
18
|
+
- "create phase"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<objective>
|
|
22
|
+
Adds a new phase to the project's roadmap. Edits `.jdi/ROADMAP.md`. Does not start the phase — only registers it. The user advances via `/jdi-discuss <N>` when ready.
|
|
23
|
+
</objective>
|
|
24
|
+
|
|
25
|
+
<arguments>
|
|
26
|
+
- `name` (required) — short phase name. Quote if it contains spaces.
|
|
27
|
+
- `--goal "<text>"` (optional) — 1-line description of what the phase delivers. If missing, AskUserQuestion will prompt.
|
|
28
|
+
- `--at <position>` (optional) — insert at this position instead of appending. Existing phases at and after `<position>` shift down by 1 (renumbered in ROADMAP only — slugs stay the same in any existing `.jdi/phases/` folders). Default: append.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
- `/jdi-add-phase "User authentication" --goal "Login + signup + JWT"`
|
|
32
|
+
- `/jdi-add-phase "Performance pass" --at 3`
|
|
33
|
+
- `/jdi-add-phase "Hotfix N+1 query"`
|
|
34
|
+
</arguments>
|
|
35
|
+
|
|
36
|
+
<process>
|
|
37
|
+
|
|
38
|
+
### Step 1: Validation
|
|
39
|
+
```bash
|
|
40
|
+
test -d .jdi/ || { echo "Not a JDI project. /jdi-new first."; exit 1; }
|
|
41
|
+
test -f .jdi/ROADMAP.md || { echo "ROADMAP.md missing."; exit 1; }
|
|
42
|
+
test -f .jdi/STATE.md || { echo "STATE.md missing."; exit 1; }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If `<name>` argument missing: AskUserQuestion "Phase name?" (free text). Required.
|
|
46
|
+
|
|
47
|
+
If `--goal` flag missing: AskUserQuestion "Phase goal (1 line)?" (free text). Required.
|
|
48
|
+
|
|
49
|
+
### Step 2: Compute phase number + slug
|
|
50
|
+
|
|
51
|
+
Read `total_phases` from `.jdi/ROADMAP.md`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
TOTAL=$(grep -oE 'total_phases:\s*[0-9]+' .jdi/ROADMAP.md | grep -oE '[0-9]+')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
PowerShell:
|
|
58
|
+
```powershell
|
|
59
|
+
$total = (Select-String -Path .jdi/ROADMAP.md -Pattern 'total_phases:\s*([0-9]+)').Matches[0].Groups[1].Value -as [int]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If `--at <pos>` provided, validate `1 <= pos <= total + 1`. Otherwise `pos = total + 1` (append).
|
|
63
|
+
|
|
64
|
+
If `pos <= current_phase` (read from STATE.md), abort with error: "Cannot insert before current phase {current}. Use --at {current+1} or higher."
|
|
65
|
+
|
|
66
|
+
Generate `slug` from `name`:
|
|
67
|
+
- Lowercase
|
|
68
|
+
- Replace accents (à → a, é → e, ç → c, ñ → n)
|
|
69
|
+
- Non-alphanumeric → `-`
|
|
70
|
+
- Collapse repeated `-`, strip leading/trailing `-`
|
|
71
|
+
- Truncate to 40 chars
|
|
72
|
+
|
|
73
|
+
Phase identifier prefix is the 2-digit position (`NN`): `01`, `02`, ..., zero-padded.
|
|
74
|
+
|
|
75
|
+
### Step 3: Renumber prefix on shift (only if --at)
|
|
76
|
+
|
|
77
|
+
If `--at <pos>` shifts existing phases:
|
|
78
|
+
|
|
79
|
+
Edit `.jdi/ROADMAP.md` — for every existing `### Phase K:` where `K >= pos`, change to `### Phase K+1:` and bump its `**Slug:**` prefix from `KK-...` to `(K+1)(K+1)-...`.
|
|
80
|
+
|
|
81
|
+
**WARNING (printed):**
|
|
82
|
+
> "Renumbering ROADMAP phases. Any existing `.jdi/phases/<NN-slug>/` folders are NOT renamed — they keep their original slugs and become "history". New work in renumbered phases creates new folders. Commits referencing old `phase {K}` remain valid as history."
|
|
83
|
+
|
|
84
|
+
This is intentional. Do not rename `.jdi/phases/` folders — would break commit history and cross-references in DECISIONS.md.
|
|
85
|
+
|
|
86
|
+
If `--at` not provided (append), skip Step 3.
|
|
87
|
+
|
|
88
|
+
### Step 4: Append phase section to ROADMAP.md
|
|
89
|
+
|
|
90
|
+
If appending (`pos == total + 1`), append at the bottom of `## Phases`:
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
|
|
94
|
+
### Phase {pos}: {name}
|
|
95
|
+
- **Slug:** {NN}-{slug}
|
|
96
|
+
- **Status:** pending
|
|
97
|
+
- **Goal:** {goal}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
If inserting (`pos <= total`), insert the new section BEFORE the section that previously had number `pos` (now renumbered to `pos+1`).
|
|
101
|
+
|
|
102
|
+
### Step 5: Bump total_phases
|
|
103
|
+
|
|
104
|
+
Edit `.jdi/ROADMAP.md`:
|
|
105
|
+
```
|
|
106
|
+
total_phases: {TOTAL + 1}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Step 6: Audit trail in DECISIONS.md (optional, only if user passes --reason)
|
|
110
|
+
|
|
111
|
+
Not required. If user wants to record why this phase was added mid-project, they pass `--reason "<text>"`. When present, append to `.jdi/DECISIONS.md`:
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+
D-{N+1} ({date}, phase {pos}): Phase added mid-project. Reason: {reason}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
(N = current max D-X.)
|
|
118
|
+
|
|
119
|
+
### Step 7: Commit
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git add .jdi/ROADMAP.md .jdi/DECISIONS.md 2>/dev/null
|
|
123
|
+
git commit -m "chore(jdi): add phase {pos} {NN-slug}"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
PowerShell:
|
|
127
|
+
```powershell
|
|
128
|
+
git add .jdi/ROADMAP.md .jdi/DECISIONS.md 2>$null
|
|
129
|
+
git commit -m "chore(jdi): add phase $pos $NN-$slug"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Step 8: Confirm
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Phase {pos}: {name} added.
|
|
136
|
+
Slug: {NN}-{slug}
|
|
137
|
+
Goal: {goal}
|
|
138
|
+
Status: pending
|
|
139
|
+
total_phases: {TOTAL + 1}
|
|
140
|
+
|
|
141
|
+
Next: /jdi-discuss {pos} (when ready to start this phase)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
</process>
|
|
145
|
+
|
|
146
|
+
<gates>
|
|
147
|
+
- pre: `.jdi/ROADMAP.md` + `.jdi/STATE.md` exist
|
|
148
|
+
- pre: `--at <pos>` (if used) is greater than `current_phase` in STATE.md
|
|
149
|
+
- post: ROADMAP.md has new phase section + bumped total_phases + atomic commit
|
|
150
|
+
</gates>
|
|
151
|
+
|
|
152
|
+
<errors>
|
|
153
|
+
- `.jdi/` missing -> "Run /jdi-new first"
|
|
154
|
+
- `--at <pos>` <= current_phase -> "Cannot insert before current phase. Use --at {current+1} or higher."
|
|
155
|
+
- `--at <pos>` < 1 or > total+1 -> "Position out of range. Valid: 1..{total+1}"
|
|
156
|
+
- Phase name empty -> AskUserQuestion to fill
|
|
157
|
+
- Goal empty -> AskUserQuestion to fill
|
|
158
|
+
</errors>
|
|
159
|
+
|
|
160
|
+
<runtime_notes>
|
|
161
|
+
|
|
162
|
+
**Claude Code:**
|
|
163
|
+
- AskUserQuestion handles missing args interactively.
|
|
164
|
+
|
|
165
|
+
**Copilot:**
|
|
166
|
+
- AskUserQuestion not always available — read missing args from prompt body or fail with clear error.
|
|
167
|
+
|
|
168
|
+
**OpenCode/Antigravity:**
|
|
169
|
+
- Same interactive flow as Claude when supported. Otherwise fail with clear error message asking the user to re-invoke with all args.
|
|
170
|
+
|
|
171
|
+
</runtime_notes>
|
|
@@ -389,13 +389,29 @@ Write to `.jdi/agents/jdi-reviewer-{slug}.md`.
|
|
|
389
389
|
|
|
390
390
|
After writing doer/reviewer, inject `<skills_to_load>` block after `</role>` via Edit.
|
|
391
391
|
|
|
392
|
+
**Code-design skill (mandatory) — resolve from `PROJECT.md.Code Design` (LOCKED value) using this mapping:**
|
|
393
|
+
|
|
394
|
+
| Code Design (PROJECT.md) | Skill to load |
|
|
395
|
+
|---|---|
|
|
396
|
+
| The Method | `the-method` |
|
|
397
|
+
| DDD | `ddd` |
|
|
398
|
+
| Clean Architecture | `clean-architecture` |
|
|
399
|
+
| Hexagonal | `hexagonal` |
|
|
400
|
+
| Onion | `onion` |
|
|
401
|
+
| Vertical Slice | `vertical-slice` |
|
|
402
|
+
|
|
403
|
+
The resolved code-design skill is loaded by **both doer and reviewer**. Exactly one code-design skill is loaded. Never load two code-design skills simultaneously — the project uses exactly one design. If the mapping cannot resolve, abort with an error and ask the user to fix `PROJECT.md.Code Design`.
|
|
404
|
+
|
|
392
405
|
**Doer — always:**
|
|
393
406
|
```markdown
|
|
394
407
|
<skills_to_load>
|
|
395
408
|
- solid — before creating classes/modules/interfaces. Detects god class, large switches, deep inheritance, dep on concretes.
|
|
409
|
+
- {CODE_DESIGN_SKILL} — INVIOLABLE structural rules for the project's locked code design. Apply on every file created.
|
|
396
410
|
</skills_to_load>
|
|
397
411
|
```
|
|
398
412
|
|
|
413
|
+
Replace `{CODE_DESIGN_SKILL}` with the resolved entry from the mapping above (e.g. `the-method`, `ddd`, `clean-architecture`, `hexagonal`, `onion`, `vertical-slice`).
|
|
414
|
+
|
|
399
415
|
If `has_frontend=true`, append:
|
|
400
416
|
```markdown
|
|
401
417
|
- frontend-rules — when task touches .tsx/.vue/.svelte/.razor/.cshtml/.html/.twig/.erb/.blade.php. WCAG 2.2 AA + UX.
|
|
@@ -408,9 +424,12 @@ If `has_frontend=true`, append:
|
|
|
408
424
|
- kiss — gate 5: over-engineering — interface with 1 impl, factory for new(), pass-through, deep inheritance.
|
|
409
425
|
- yagni — gate 5: speculative code — optional params never passed, TODO without ticket, generic with 1 type.
|
|
410
426
|
- clean-code — bad names, long functions, magic numbers, silent catch, boolean params, redundant comments.
|
|
427
|
+
- {CODE_DESIGN_SKILL} — gate 5: enforce INVIOLABLE structural rules for the project's locked code design. BLOCKED on violations defined by the skill.
|
|
411
428
|
</skills_to_load>
|
|
412
429
|
```
|
|
413
430
|
|
|
431
|
+
Replace `{CODE_DESIGN_SKILL}` with the same resolved entry — both doer and reviewer load the SAME code-design skill.
|
|
432
|
+
|
|
414
433
|
If `has_frontend=true`, append:
|
|
415
434
|
```markdown
|
|
416
435
|
- frontend-rules — gate 5 frontend: <input> without label, button without aria-label, localStorage with token, outline removed.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jdi-remove-phase
|
|
3
|
+
description: Removes a phase from ROADMAP.md. Refuses to remove done phases or the current phase. Archives any existing phase artifacts. Atomic commit.
|
|
4
|
+
argument_hint: "<phase_number> [--force]"
|
|
5
|
+
runtime_intent:
|
|
6
|
+
invokes_agent: none
|
|
7
|
+
runtime_overrides:
|
|
8
|
+
claude:
|
|
9
|
+
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion]
|
|
10
|
+
copilot:
|
|
11
|
+
tools: [read, write, edit, grep, glob, terminal]
|
|
12
|
+
opencode:
|
|
13
|
+
subtask: true
|
|
14
|
+
antigravity:
|
|
15
|
+
triggers:
|
|
16
|
+
- "/jdi-remove-phase"
|
|
17
|
+
- "remove phase"
|
|
18
|
+
- "delete phase"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<objective>
|
|
22
|
+
Removes a phase from `.jdi/ROADMAP.md`. Refuses to remove the current phase or any phase already shipped. If the phase has artifacts under `.jdi/phases/<NN-slug>/`, moves them to `.jdi/archive/removed-<NN-slug>/` instead of deleting (preserves history).
|
|
23
|
+
</objective>
|
|
24
|
+
|
|
25
|
+
<arguments>
|
|
26
|
+
- `phase_number` (required) — number of the phase to remove (as listed in ROADMAP.md).
|
|
27
|
+
- `--force` (optional) — required when removing a phase that has any artifacts in `.jdi/phases/`. Without `--force`, the command refuses and explains.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
- `/jdi-remove-phase 4`
|
|
31
|
+
- `/jdi-remove-phase 5 --force`
|
|
32
|
+
</arguments>
|
|
33
|
+
|
|
34
|
+
<process>
|
|
35
|
+
|
|
36
|
+
### Step 1: Validation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
test -d .jdi/ || { echo "Not a JDI project."; exit 1; }
|
|
40
|
+
test -f .jdi/ROADMAP.md || { echo "ROADMAP.md missing."; exit 1; }
|
|
41
|
+
test -f .jdi/STATE.md || { echo "STATE.md missing."; exit 1; }
|
|
42
|
+
|
|
43
|
+
# Argument required
|
|
44
|
+
[ -n "$1" ] || { echo "Phase number required. Usage: /jdi-remove-phase <N> [--force]"; exit 1; }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Parse `phase_number` (positional). Parse `--force` flag.
|
|
48
|
+
|
|
49
|
+
### Step 2: Read state
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
CURRENT=$(grep -oE 'current_phase:\s*[0-9]+' .jdi/STATE.md | grep -oE '[0-9]+')
|
|
53
|
+
TOTAL=$(grep -oE 'total_phases:\s*[0-9]+' .jdi/ROADMAP.md | grep -oE '[0-9]+')
|
|
54
|
+
PHASE_NUMBER=$1
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
PowerShell:
|
|
58
|
+
```powershell
|
|
59
|
+
$current = (Select-String -Path .jdi/STATE.md -Pattern 'current_phase:\s*([0-9]+)').Matches[0].Groups[1].Value -as [int]
|
|
60
|
+
$total = (Select-String -Path .jdi/ROADMAP.md -Pattern 'total_phases:\s*([0-9]+)').Matches[0].Groups[1].Value -as [int]
|
|
61
|
+
$phaseNumber = [int]$args[0]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: Hard refuses (no override)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Must be in range
|
|
68
|
+
if [ "$PHASE_NUMBER" -lt 1 ] || [ "$PHASE_NUMBER" -gt "$TOTAL" ]; then
|
|
69
|
+
echo "Phase $PHASE_NUMBER does not exist. Valid: 1..$TOTAL"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Cannot remove past phases (preserves history)
|
|
74
|
+
if [ "$PHASE_NUMBER" -lt "$CURRENT" ]; then
|
|
75
|
+
echo "Cannot remove phase $PHASE_NUMBER — already past. current_phase is $CURRENT. Past phases are immutable history."
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Cannot remove the current phase
|
|
80
|
+
if [ "$PHASE_NUMBER" -eq "$CURRENT" ]; then
|
|
81
|
+
echo "Cannot remove the current phase ($CURRENT). Ship or abandon it first, then advance current_phase."
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Read the phase's `Status:` from ROADMAP:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
STATUS=$(awk "/### Phase $PHASE_NUMBER:/,/^### Phase /" .jdi/ROADMAP.md | grep -oE 'Status:\*\* (pending|ready|done|partial|blocked|in-progress)' | head -1 | awk '{print $2}')
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If `STATUS == done`: refuse hard.
|
|
93
|
+
```
|
|
94
|
+
Phase $PHASE_NUMBER is `done`. Cannot remove. Shipped phases are immutable history.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Step 4: Detect artifacts
|
|
98
|
+
|
|
99
|
+
Find the phase folder under `.jdi/phases/`:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
NN=$(printf '%02d' "$PHASE_NUMBER")
|
|
103
|
+
PHASE_DIR=$(ls -d .jdi/phases/${NN}-*/ 2>/dev/null | head -1 || true)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
PowerShell:
|
|
107
|
+
```powershell
|
|
108
|
+
$nn = '{0:D2}' -f $phaseNumber
|
|
109
|
+
$phaseDir = Get-ChildItem .jdi/phases/ -Directory -Filter "$nn-*" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If `PHASE_DIR` exists AND `--force` NOT passed: refuse.
|
|
113
|
+
```
|
|
114
|
+
Phase $PHASE_NUMBER has artifacts in $PHASE_DIR (CONTEXT/PLAN/SUMMARY/REVIEW).
|
|
115
|
+
Re-run with --force to archive these artifacts to .jdi/archive/removed-<NN-slug>/ and proceed.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Step 5: Confirm with user (irreversible-ish action)
|
|
119
|
+
|
|
120
|
+
AskUserQuestion (always run, even with --force):
|
|
121
|
+
|
|
122
|
+
> "Remove phase $PHASE_NUMBER: '$PHASE_NAME'?
|
|
123
|
+
>
|
|
124
|
+
> Status: $STATUS
|
|
125
|
+
> Artifacts: ${PHASE_DIR:-none}
|
|
126
|
+
> Action: ROADMAP section removed, total_phases decremented, artifacts (if any) moved to .jdi/archive/removed-<NN-slug>/."
|
|
127
|
+
>
|
|
128
|
+
> Options:
|
|
129
|
+
> - [Yes, remove]
|
|
130
|
+
> - [Cancel]
|
|
131
|
+
|
|
132
|
+
If "Cancel" -> exit clean.
|
|
133
|
+
|
|
134
|
+
### Step 6: Archive artifacts (if any)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
if [ -n "$PHASE_DIR" ]; then
|
|
138
|
+
mkdir -p .jdi/archive
|
|
139
|
+
test -f .jdi/archive/index.md || echo "# Archive index" > .jdi/archive/index.md
|
|
140
|
+
|
|
141
|
+
BASENAME=$(basename "$PHASE_DIR")
|
|
142
|
+
TARGET=".jdi/archive/removed-$BASENAME"
|
|
143
|
+
mv "$PHASE_DIR" "$TARGET"
|
|
144
|
+
echo "- removed-$BASENAME (removed $(date -u +%F) via /jdi-remove-phase)" >> .jdi/archive/index.md
|
|
145
|
+
fi
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
PowerShell:
|
|
149
|
+
```powershell
|
|
150
|
+
if ($phaseDir) {
|
|
151
|
+
if (-not (Test-Path .jdi/archive)) { New-Item -ItemType Directory .jdi/archive | Out-Null }
|
|
152
|
+
if (-not (Test-Path .jdi/archive/index.md)) { Set-Content .jdi/archive/index.md "# Archive index" }
|
|
153
|
+
$basename = $phaseDir.Name
|
|
154
|
+
$target = ".jdi/archive/removed-$basename"
|
|
155
|
+
Move-Item $phaseDir.FullName $target
|
|
156
|
+
Add-Content .jdi/archive/index.md "- removed-$basename (removed $(Get-Date -Format 'yyyy-MM-dd') via /jdi-remove-phase)"
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Step 7: Edit ROADMAP.md
|
|
161
|
+
|
|
162
|
+
Remove the entire `### Phase $PHASE_NUMBER: ...` block (header + bullets) up to (but not including) the next `### Phase ` line or end of file.
|
|
163
|
+
|
|
164
|
+
Decrement `total_phases`:
|
|
165
|
+
```
|
|
166
|
+
total_phases: {TOTAL - 1}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Do NOT renumber** subsequent phases. Phases after the removed one keep their original numbers. This preserves all references in commit history, `DECISIONS.md`, and archived phase folders. The roadmap simply has a gap (e.g., 1, 2, 3, 5, 6 — 4 was removed).
|
|
170
|
+
|
|
171
|
+
### Step 8: Audit trail in DECISIONS.md
|
|
172
|
+
|
|
173
|
+
Append:
|
|
174
|
+
```markdown
|
|
175
|
+
D-{N+1} ({date}): Phase $PHASE_NUMBER removed via /jdi-remove-phase. Artifacts: ${PHASE_DIR:+archived to .jdi/archive/removed-<NN-slug>/}{none if empty}.
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Step 9: Commit
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
git add .jdi/ROADMAP.md .jdi/DECISIONS.md .jdi/archive/ 2>/dev/null
|
|
182
|
+
git commit -m "chore(jdi): remove phase {PHASE_NUMBER}"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Step 10: Confirm
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
Phase {PHASE_NUMBER} removed.
|
|
189
|
+
{if artifacts:} Artifacts archived: .jdi/archive/removed-{NN-slug}/
|
|
190
|
+
total_phases: {TOTAL - 1}
|
|
191
|
+
|
|
192
|
+
Note: phase numbers are not renumbered — references in commits, DECISIONS, and archive remain valid.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
</process>
|
|
196
|
+
|
|
197
|
+
<gates>
|
|
198
|
+
- pre: `.jdi/ROADMAP.md` + `.jdi/STATE.md` exist
|
|
199
|
+
- pre: `phase_number` is in range `1..total_phases`
|
|
200
|
+
- pre: `phase_number > current_phase`
|
|
201
|
+
- pre: phase status != `done`
|
|
202
|
+
- pre: `--force` provided when phase has artifacts
|
|
203
|
+
- post: ROADMAP.md section removed + total_phases decremented + artifacts archived (if any) + DECISIONS.md appended + atomic commit
|
|
204
|
+
</gates>
|
|
205
|
+
|
|
206
|
+
<errors>
|
|
207
|
+
- `.jdi/` missing -> "Run /jdi-new first"
|
|
208
|
+
- `phase_number` missing -> usage hint
|
|
209
|
+
- `phase_number` out of range -> "Valid: 1..{total}"
|
|
210
|
+
- `phase_number < current_phase` -> refuse (past = history)
|
|
211
|
+
- `phase_number == current_phase` -> refuse (ship or abandon first)
|
|
212
|
+
- phase status `done` -> refuse (shipped = history)
|
|
213
|
+
- artifacts exist + no `--force` -> refuse + suggest `--force`
|
|
214
|
+
- user cancels at AskUserQuestion -> exit clean
|
|
215
|
+
</errors>
|
|
216
|
+
|
|
217
|
+
<runtime_notes>
|
|
218
|
+
|
|
219
|
+
**Claude Code:**
|
|
220
|
+
- Confirms via AskUserQuestion.
|
|
221
|
+
|
|
222
|
+
**Copilot:**
|
|
223
|
+
- AskUserQuestion not always available — require explicit `--yes` flag as fallback.
|
|
224
|
+
|
|
225
|
+
**OpenCode/Antigravity:**
|
|
226
|
+
- Same interactive confirm as Claude. Fallback to `--yes` when prompts unsupported.
|
|
227
|
+
|
|
228
|
+
</runtime_notes>
|
|
@@ -61,10 +61,11 @@ Options:
|
|
|
61
61
|
- Vertical Slice
|
|
62
62
|
- Clean Architecture
|
|
63
63
|
- Hexagonal (Ports & Adapters)
|
|
64
|
+
- Onion Architecture
|
|
64
65
|
- The Method (Juval Löwy)
|
|
65
66
|
- "Don't know, suggest" (-> recommend based on type + stack)
|
|
66
67
|
|
|
67
|
-
Locked for the life of the project (global rule).
|
|
68
|
+
Locked for the life of the project (global rule). Mutually exclusive — the project uses exactly ONE code design. The choice is enforced by a JDI skill loaded into doer + reviewer (one of: `ddd`, `vertical-slice`, `clean-architecture`, `hexagonal`, `onion`, `the-method`).
|
|
68
69
|
|
|
69
70
|
**Q4 — MVP scope**
|
|
70
71
|
"Which minimum features for the MVP? (comma-separated)"
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: onion
|
|
3
|
+
description: Onion Architecture (Jeffrey Palermo). Concentric shells with the Domain Model at the absolute center. Dependencies invert across shells - outer depends on inner, inner knows nothing of outer. Language-agnostic rigid rules. Mutually exclusive with The Method, DDD, Clean Architecture, Hexagonal, Vertical Slice.
|
|
4
|
+
triggers:
|
|
5
|
+
- "Onion Architecture"
|
|
6
|
+
- "Jeffrey Palermo"
|
|
7
|
+
- "domain at the center"
|
|
8
|
+
- "concentric shells"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Skill: Onion Architecture
|
|
12
|
+
|
|
13
|
+
Rigid, inviolable rules from Jeffrey Palermo's Onion Architecture. The system is a series of **concentric shells** with the **Domain Model at the absolute center**. Outer shells depend on inner shells; the inverse is forbidden.
|
|
14
|
+
|
|
15
|
+
Onion is the ONLY allowed design when PROJECT.md `Code Design: LOCKED: Onion`. Do not use The Method, DDD as primary structure, Clean Architecture's 4-named layers, Hexagonal port/adapter terminology as primary structure, or Vertical Slice feature folders as primary structure.
|
|
16
|
+
|
|
17
|
+
## The shells (mandatory order from center outward)
|
|
18
|
+
|
|
19
|
+
1. **Domain Model** — entities and value objects expressing the business state. No behavior dependent on infrastructure. The absolute center.
|
|
20
|
+
2. **Domain Services** — domain operations that do not belong to a single entity/value object. Pure domain logic. Stateless.
|
|
21
|
+
3. **Application Services** — orchestrate use cases. Coordinate Domain Services and Domain Model. Define interfaces for infrastructure dependencies (repository contracts, external system contracts).
|
|
22
|
+
4. **Infrastructure** — persistence implementations, external system clients, UI, tests, framework bindings. The outermost shell.
|
|
23
|
+
|
|
24
|
+
Every code unit belongs to exactly one of these 4 shells. A unit that does not fit must be redesigned.
|
|
25
|
+
|
|
26
|
+
## The Dependency Direction (inviolable)
|
|
27
|
+
|
|
28
|
+
**Dependencies point inward.** Every shell may depend on shells closer to the center; no shell may depend on shells farther from the center.
|
|
29
|
+
|
|
30
|
+
1. Domain Model depends on nothing else in the system.
|
|
31
|
+
2. Domain Services depend on Domain Model only.
|
|
32
|
+
3. Application Services depend on Domain Model and Domain Services.
|
|
33
|
+
4. Infrastructure depends on Application Services, Domain Services, and Domain Model.
|
|
34
|
+
5. Names defined in Infrastructure (ORM types, framework types, vendor SDK types) never appear in any inner shell.
|
|
35
|
+
|
|
36
|
+
## Inversion across infrastructure (inviolable)
|
|
37
|
+
|
|
38
|
+
1. **Application Services define the interfaces** (repository contracts, external system contracts) they require. These interfaces live in Application Services or Domain Services — never in Infrastructure.
|
|
39
|
+
2. **Infrastructure implements those interfaces.** The implementation class lives in Infrastructure. The interface lives inward.
|
|
40
|
+
3. **The Composition Root wires interfaces to implementations.** The Composition Root is part of Infrastructure (or a separate startup module that depends on Infrastructure). Inner shells never construct Infrastructure types.
|
|
41
|
+
|
|
42
|
+
## Domain Model rules
|
|
43
|
+
|
|
44
|
+
1. The Domain Model contains only entities and value objects in domain language.
|
|
45
|
+
2. The Domain Model has no annotations, no inheritance from framework base classes, no persistence concerns, no serialization concerns.
|
|
46
|
+
3. The Domain Model is not anemic. Behavior that belongs to an entity lives on that entity. Behavior that belongs to no single entity lives in Domain Services.
|
|
47
|
+
4. Value objects are immutable. Replacing is the only mutation pattern.
|
|
48
|
+
5. The Domain Model does not know how it is persisted, displayed, or transmitted.
|
|
49
|
+
|
|
50
|
+
## Domain Services rules
|
|
51
|
+
|
|
52
|
+
6. Domain Services are stateless. They contain pure domain logic that does not naturally belong to a single Entity or Value Object.
|
|
53
|
+
7. Domain Services depend on the Domain Model only. They never call repositories, infrastructure, or Application Services.
|
|
54
|
+
8. Domain Services do not perform I/O.
|
|
55
|
+
9. Names of Domain Services are domain operations, not technical operations.
|
|
56
|
+
|
|
57
|
+
## Application Services rules
|
|
58
|
+
|
|
59
|
+
10. Application Services orchestrate use cases. One Application Service method = one application-level operation.
|
|
60
|
+
11. Application Services declare the interfaces (repository, gateway, sender, publisher) they require. These interfaces live with Application Services (or in Domain Services when their abstraction is purely domain-driven).
|
|
61
|
+
12. Application Services do not contain business rules — they coordinate. Business rules live in Domain Model and Domain Services.
|
|
62
|
+
13. Application Services do not import any Infrastructure class. They depend on the interfaces they themselves declare.
|
|
63
|
+
14. Application Services receive input DTOs and return output DTOs (or void). They do not return Domain Model entities to outer shells when those would leak persistence concerns.
|
|
64
|
+
|
|
65
|
+
## Infrastructure rules
|
|
66
|
+
|
|
67
|
+
15. Infrastructure implements interfaces declared by inner shells. It does not introduce interfaces of its own that inner shells consume.
|
|
68
|
+
16. Infrastructure contains: persistence (ORM mappings, repositories), HTTP/UI controllers, message bus clients, file system access, vendor SDK calls, scheduled job handlers, and the Composition Root.
|
|
69
|
+
17. Infrastructure does not contain business rules.
|
|
70
|
+
18. Infrastructure classes are named with their technology when the technology is the encapsulated concern (e.g., `SqlOrderRepository`, `HttpPaymentGateway`).
|
|
71
|
+
19. Two infrastructure classes do not call each other to fulfill a use case. Coordination happens via Application Services.
|
|
72
|
+
|
|
73
|
+
## Forbidden patterns (inviolable)
|
|
74
|
+
|
|
75
|
+
- **Inner shell referencing outer shell.** Domain Model referencing Application Services or Infrastructure is forbidden. Domain Services referencing Application Services or Infrastructure is forbidden. Application Services referencing Infrastructure (concrete classes) is forbidden.
|
|
76
|
+
- **Persistence annotations or framework types in the Domain Model.**
|
|
77
|
+
- **Repository interface placed in Infrastructure.** The interface must live inward (Application Services or Domain Services).
|
|
78
|
+
- **Domain Services performing I/O.**
|
|
79
|
+
- **Application Services containing business rules.**
|
|
80
|
+
- **Infrastructure-defined interfaces consumed by inner shells.**
|
|
81
|
+
- **Skipping a shell** (Infrastructure code calling Domain Model methods directly when an Application Service exists for that use case; or controller calling repository directly bypassing Application Services).
|
|
82
|
+
- **Anemic Domain Model.**
|
|
83
|
+
- **CRUD-only Application Services** with no application semantics.
|
|
84
|
+
- **Cross-shell reach-around** (Infrastructure A calling Infrastructure B to fulfill a use case without going through an Application Service).
|
|
85
|
+
|
|
86
|
+
## Naming conventions
|
|
87
|
+
|
|
88
|
+
- Domain Model classes: domain nouns (`Customer`, `Order`, `Subscription`).
|
|
89
|
+
- Domain Services: domain verb-noun (`OrderPricingPolicy`, `SubscriptionRenewalRules`).
|
|
90
|
+
- Application Services: application-level operations (`RegisterCustomerService`, `RenewSubscriptionService`).
|
|
91
|
+
- Interfaces consumed by Application Services: domain-meaningful (`IOrderRepository`, `IPaymentGateway`, `INotificationSender`).
|
|
92
|
+
- Infrastructure classes: technology-qualified (`SqlOrderRepository`, `StripePaymentGateway`, `SmtpNotificationSender`).
|
|
93
|
+
|
|
94
|
+
## Reviewer enforcement (gate 5)
|
|
95
|
+
|
|
96
|
+
Reviewer rejects (BLOCKED) when:
|
|
97
|
+
- Domain Model, Domain Services, or Application Services import a framework / ORM / HTTP / vendor SDK type.
|
|
98
|
+
- A repository or external-system interface is declared in Infrastructure and consumed inward.
|
|
99
|
+
- An inner shell file imports an outer shell file.
|
|
100
|
+
- Application Services contain business rules that belong on Domain Model or Domain Services.
|
|
101
|
+
- Domain Services perform I/O or call a repository.
|
|
102
|
+
- Infrastructure classes coordinate use cases instead of being driven by Application Services.
|
|
103
|
+
- Infrastructure introduces an interface that inner shells depend on.
|
|
104
|
+
|
|
105
|
+
Reviewer warns (APPROVED_WITH_WARNINGS) when:
|
|
106
|
+
- Domain Model is anemic.
|
|
107
|
+
- Application Services are named after CRUD verbs without semantic meaning.
|
|
108
|
+
- Composition Root is fragmented across multiple infrastructure modules.
|
|
109
|
+
- An entity is mutable in places where a value object would be correct.
|
|
110
|
+
|
|
111
|
+
## Anti-patterns
|
|
112
|
+
|
|
113
|
+
- "Service" layer mixing Domain Services with Application Services indistinguishably
|
|
114
|
+
- Generic repository (`IRepository<T>`) declared at the Infrastructure level
|
|
115
|
+
- Domain Model classes inheriting from an ORM base
|
|
116
|
+
- Domain Services calling repositories
|
|
117
|
+
- Application Services holding state across calls
|
|
118
|
+
- Direct controller-to-repository wiring
|
|
119
|
+
- Infrastructure types appearing as parameters to inner-shell methods
|
|
120
|
+
- "Onion" framing used to disguise Clean Architecture, Hexagonal, or DDD — the rule is the inward dependency direction and the Domain Model at the absolute center
|
|
121
|
+
|
|
122
|
+
## Outputs
|
|
123
|
+
|
|
124
|
+
Does NOT produce own files. Modifies parent agent's structural decisions during code authoring and review.
|