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,559 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-validator
|
|
3
|
+
description: Validates live UI via Playwright + axe-core. Detects Playwright; installs if missing (with user consent). Spawns dev server, navigates critical routes on mobile+desktop, captures console errors, network failures, a11y violations, screenshots, layout shifts. Structured JSON output for the reviewer to parse.
|
|
4
|
+
triggers:
|
|
5
|
+
- "validate UI"
|
|
6
|
+
- "run playwright"
|
|
7
|
+
- "check accessibility"
|
|
8
|
+
- "smoke test frontend"
|
|
9
|
+
- "validate live interface"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Skill: jdi-frontend-validator
|
|
13
|
+
|
|
14
|
+
Runs UI validation in a real browser. Without Playwright installed, installs with consent. Output always JSON in `.jdi/cache/ui-findings.json` for the parent reviewer to consume.
|
|
15
|
+
|
|
16
|
+
## When to apply
|
|
17
|
+
|
|
18
|
+
Reviewer calls at gate 7. Preconditions in PROJECT.md:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
frontend:
|
|
22
|
+
has_frontend: true
|
|
23
|
+
frontend_url: http://localhost:5173
|
|
24
|
+
dev_command: pnpm dev
|
|
25
|
+
critical_paths:
|
|
26
|
+
- /
|
|
27
|
+
- /login
|
|
28
|
+
- /dashboard
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Missing any key -> abort with descriptive error.
|
|
32
|
+
|
|
33
|
+
## Procedure
|
|
34
|
+
|
|
35
|
+
### Step 1: Pre-flight
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# bash
|
|
39
|
+
test -d .jdi/ || { echo "No .jdi/. Run /jdi-new"; exit 1; }
|
|
40
|
+
test -f .jdi/PROJECT.md || { echo "PROJECT.md missing"; exit 1; }
|
|
41
|
+
|
|
42
|
+
# Read frontend.has_frontend, frontend_url, dev_command, critical_paths
|
|
43
|
+
# (simple YAML parse - assume well-defined format)
|
|
44
|
+
|
|
45
|
+
# Create cache
|
|
46
|
+
mkdir -p .jdi/cache/screenshots
|
|
47
|
+
|
|
48
|
+
# .gitignore guarantee
|
|
49
|
+
grep -q '^\.jdi/cache/' .gitignore 2>/dev/null || echo '.jdi/cache/' >> .gitignore
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
# PowerShell
|
|
54
|
+
if (-not (Test-Path .jdi)) { Write-Error "No .jdi/. Run /jdi-new"; exit 1 }
|
|
55
|
+
if (-not (Test-Path .jdi/PROJECT.md)) { Write-Error "PROJECT.md missing"; exit 1 }
|
|
56
|
+
|
|
57
|
+
New-Item -ItemType Directory -Force -Path .jdi/cache/screenshots | Out-Null
|
|
58
|
+
|
|
59
|
+
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\.jdi/cache/' -Quiet)) {
|
|
60
|
+
Add-Content .gitignore '.jdi/cache/'
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 2: Detect package manager
|
|
65
|
+
|
|
66
|
+
Lockfile detection (more reliable than `which`):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# bash
|
|
70
|
+
if [ -f pnpm-lock.yaml ]; then PKG_MGR=pnpm
|
|
71
|
+
elif [ -f yarn.lock ]; then PKG_MGR=yarn
|
|
72
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then PKG_MGR=bun
|
|
73
|
+
elif [ -f package-lock.json ]; then PKG_MGR=npm
|
|
74
|
+
else PKG_MGR=npm # fallback
|
|
75
|
+
fi
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
# PowerShell
|
|
80
|
+
if (Test-Path pnpm-lock.yaml) { $PKG_MGR = "pnpm" }
|
|
81
|
+
elseif (Test-Path yarn.lock) { $PKG_MGR = "yarn" }
|
|
82
|
+
elseif ((Test-Path bun.lockb) -or (Test-Path bun.lock)) { $PKG_MGR = "bun" }
|
|
83
|
+
elseif (Test-Path package-lock.json) { $PKG_MGR = "npm" }
|
|
84
|
+
else { $PKG_MGR = "npm" }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Corresponding install command:
|
|
88
|
+
|
|
89
|
+
| Pkg mgr | Install dev dep | Run binary |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| npm | `npm install --save-dev <pkg>` | `npx <bin>` |
|
|
92
|
+
| pnpm | `pnpm add -D <pkg>` | `pnpm exec <bin>` or `pnpm dlx <bin>` |
|
|
93
|
+
| yarn | `yarn add -D <pkg>` | `yarn <bin>` |
|
|
94
|
+
| bun | `bun add -d <pkg>` | `bunx <bin>` |
|
|
95
|
+
|
|
96
|
+
### Step 3: Detect Playwright
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# bash
|
|
100
|
+
PW_BIN="npx --no-install playwright"
|
|
101
|
+
[ "$PKG_MGR" = "pnpm" ] && PW_BIN="pnpm exec playwright"
|
|
102
|
+
[ "$PKG_MGR" = "yarn" ] && PW_BIN="yarn playwright"
|
|
103
|
+
[ "$PKG_MGR" = "bun" ] && PW_BIN="bunx playwright"
|
|
104
|
+
|
|
105
|
+
if ! $PW_BIN --version >/dev/null 2>&1; then
|
|
106
|
+
PLAYWRIGHT_MISSING=1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Check axe-core/playwright separately
|
|
110
|
+
if [ -f package.json ] && ! grep -q '@axe-core/playwright' package.json; then
|
|
111
|
+
AXE_MISSING=1
|
|
112
|
+
fi
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```powershell
|
|
116
|
+
# PowerShell - simplified
|
|
117
|
+
$pwExists = $false
|
|
118
|
+
try {
|
|
119
|
+
$null = & npx --no-install playwright --version 2>$null
|
|
120
|
+
if ($LASTEXITCODE -eq 0) { $pwExists = $true }
|
|
121
|
+
} catch {}
|
|
122
|
+
if (-not $pwExists) { $env:PLAYWRIGHT_MISSING = "1" }
|
|
123
|
+
|
|
124
|
+
$axeExists = $false
|
|
125
|
+
if (Test-Path package.json) {
|
|
126
|
+
if (Select-String -Path package.json -Pattern '@axe-core/playwright' -Quiet) { $axeExists = $true }
|
|
127
|
+
}
|
|
128
|
+
if (-not $axeExists) { $env:AXE_MISSING = "1" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 4: If missing, ask consent + install
|
|
132
|
+
|
|
133
|
+
Use AskUserQuestion (or prompt fallback if runtime doesn't support it):
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
Playwright not installed in this project.
|
|
137
|
+
|
|
138
|
+
Install now?
|
|
139
|
+
- [Yes, install with Chromium] (~150MB, 2-5min, recommended)
|
|
140
|
+
- [Yes, install with all browsers] (~500MB, 5-10min)
|
|
141
|
+
- [No, skip gate 7 this time] (gate returns SKIPPED)
|
|
142
|
+
- [Cancel entire review]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Choice mapping:
|
|
146
|
+
|
|
147
|
+
**Yes, Chromium:**
|
|
148
|
+
```bash
|
|
149
|
+
$INSTALL_CMD @playwright/test @axe-core/playwright
|
|
150
|
+
$PLAYWRIGHT_INSTALL chromium
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Where:
|
|
154
|
+
- `$INSTALL_CMD` = `pnpm add -D` / `npm install --save-dev` / etc based on PKG_MGR
|
|
155
|
+
- `$PLAYWRIGHT_INSTALL` = `$PW_BIN install`
|
|
156
|
+
|
|
157
|
+
**Yes, all browsers:**
|
|
158
|
+
```bash
|
|
159
|
+
$INSTALL_CMD @playwright/test @axe-core/playwright
|
|
160
|
+
$PLAYWRIGHT_INSTALL
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**No, skip:**
|
|
164
|
+
Returns `{ "status": "SKIPPED", "reason": "user declined Playwright install" }` - reviewer marks gate 7 as SKIPPED (not BLOCK).
|
|
165
|
+
|
|
166
|
+
**Cancel review:**
|
|
167
|
+
Returns error code, reviewer aborts.
|
|
168
|
+
|
|
169
|
+
### Step 5: Generate temporary Playwright spec
|
|
170
|
+
|
|
171
|
+
Creates `.jdi/cache/playwright-check.spec.js` (gitignored). Content:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// Auto-generated by jdi-frontend-validator. DO NOT edit manually.
|
|
175
|
+
// @ts-check
|
|
176
|
+
const { test, expect } = require('@playwright/test');
|
|
177
|
+
const AxeBuilder = require('@axe-core/playwright').default;
|
|
178
|
+
const fs = require('fs');
|
|
179
|
+
const path = require('path');
|
|
180
|
+
|
|
181
|
+
const URL = process.env.JDI_FRONTEND_URL;
|
|
182
|
+
const ROUTES = (process.env.JDI_ROUTES || '/').split(',').map(r => r.trim()).filter(Boolean);
|
|
183
|
+
const OUT = process.env.JDI_OUT || '.jdi/cache/ui-findings.json';
|
|
184
|
+
const SCREENSHOT_DIR = process.env.JDI_SCREENSHOT_DIR || '.jdi/cache/screenshots';
|
|
185
|
+
|
|
186
|
+
const VIEWPORTS = [
|
|
187
|
+
{ name: 'mobile', width: 375, height: 667 },
|
|
188
|
+
{ name: 'desktop', width: 1280, height: 720 }
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
const findings = {
|
|
192
|
+
metadata: { url: URL, routes: ROUTES, timestamp: new Date().toISOString() },
|
|
193
|
+
console: [],
|
|
194
|
+
network: [],
|
|
195
|
+
a11y: [],
|
|
196
|
+
layout: [],
|
|
197
|
+
screenshots: [],
|
|
198
|
+
navigationFailures: []
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
for (const route of ROUTES) {
|
|
202
|
+
for (const viewport of VIEWPORTS) {
|
|
203
|
+
test(`${route} @ ${viewport.name}`, async ({ page }) => {
|
|
204
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
205
|
+
|
|
206
|
+
page.on('console', msg => {
|
|
207
|
+
if (msg.type() === 'error') {
|
|
208
|
+
findings.console.push({
|
|
209
|
+
route,
|
|
210
|
+
viewport: viewport.name,
|
|
211
|
+
text: msg.text(),
|
|
212
|
+
location: msg.location()
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
page.on('requestfailed', req => {
|
|
218
|
+
findings.network.push({
|
|
219
|
+
route,
|
|
220
|
+
viewport: viewport.name,
|
|
221
|
+
url: req.url(),
|
|
222
|
+
method: req.method(),
|
|
223
|
+
failure: req.failure()?.errorText,
|
|
224
|
+
severity: 'requestfailed'
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
page.on('response', res => {
|
|
229
|
+
if (res.status() >= 500) {
|
|
230
|
+
findings.network.push({
|
|
231
|
+
route,
|
|
232
|
+
viewport: viewport.name,
|
|
233
|
+
url: res.url(),
|
|
234
|
+
status: res.status(),
|
|
235
|
+
severity: '5xx'
|
|
236
|
+
});
|
|
237
|
+
} else if (res.status() >= 400 && res.status() !== 404) {
|
|
238
|
+
findings.network.push({
|
|
239
|
+
route,
|
|
240
|
+
viewport: viewport.name,
|
|
241
|
+
url: res.url(),
|
|
242
|
+
status: res.status(),
|
|
243
|
+
severity: '4xx'
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const targetUrl = `${URL}${route}`;
|
|
249
|
+
let response;
|
|
250
|
+
try {
|
|
251
|
+
response = await page.goto(targetUrl, { waitUntil: 'networkidle', timeout: 30000 });
|
|
252
|
+
} catch (err) {
|
|
253
|
+
findings.navigationFailures.push({
|
|
254
|
+
route,
|
|
255
|
+
viewport: viewport.name,
|
|
256
|
+
error: err.message
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!response || !response.ok()) {
|
|
262
|
+
findings.navigationFailures.push({
|
|
263
|
+
route,
|
|
264
|
+
viewport: viewport.name,
|
|
265
|
+
status: response?.status() ?? 'no-response',
|
|
266
|
+
url: targetUrl
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Detect horizontal scroll
|
|
272
|
+
const hasHScroll = await page.evaluate(() => {
|
|
273
|
+
return document.documentElement.scrollWidth > document.documentElement.clientWidth + 1;
|
|
274
|
+
});
|
|
275
|
+
if (hasHScroll) {
|
|
276
|
+
findings.layout.push({
|
|
277
|
+
route,
|
|
278
|
+
viewport: viewport.name,
|
|
279
|
+
issue: 'horizontal_scroll'
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// axe-core a11y scan
|
|
284
|
+
try {
|
|
285
|
+
const axeResults = await new AxeBuilder({ page })
|
|
286
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa', 'best-practice'])
|
|
287
|
+
.analyze();
|
|
288
|
+
|
|
289
|
+
for (const v of axeResults.violations) {
|
|
290
|
+
findings.a11y.push({
|
|
291
|
+
route,
|
|
292
|
+
viewport: viewport.name,
|
|
293
|
+
id: v.id,
|
|
294
|
+
impact: v.impact,
|
|
295
|
+
help: v.help,
|
|
296
|
+
helpUrl: v.helpUrl,
|
|
297
|
+
nodes: v.nodes.length,
|
|
298
|
+
sample: v.nodes.slice(0, 3).map(n => ({
|
|
299
|
+
target: n.target,
|
|
300
|
+
html: n.html.slice(0, 200)
|
|
301
|
+
}))
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
// axe-core failure doesn't block run
|
|
306
|
+
findings.a11y.push({
|
|
307
|
+
route,
|
|
308
|
+
viewport: viewport.name,
|
|
309
|
+
error: `axe-core failed: ${err.message}`
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Screenshot
|
|
314
|
+
const safeName = (route === '/' ? 'root' : route.replace(/^\//, '').replace(/\//g, '_'));
|
|
315
|
+
const screenshotPath = path.join(SCREENSHOT_DIR, `${safeName}_${viewport.name}.png`);
|
|
316
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
317
|
+
findings.screenshots.push({ route, viewport: viewport.name, path: screenshotPath });
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
test.afterAll(() => {
|
|
323
|
+
fs.writeFileSync(OUT, JSON.stringify(findings, null, 2));
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
And inline config `.jdi/cache/playwright.config.js`:
|
|
328
|
+
|
|
329
|
+
```javascript
|
|
330
|
+
module.exports = {
|
|
331
|
+
testDir: '.jdi/cache',
|
|
332
|
+
testMatch: 'playwright-check.spec.js',
|
|
333
|
+
timeout: 60000,
|
|
334
|
+
retries: 0,
|
|
335
|
+
workers: 1,
|
|
336
|
+
reporter: [['line']],
|
|
337
|
+
use: {
|
|
338
|
+
headless: true,
|
|
339
|
+
ignoreHTTPSErrors: true
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Step 6: Spawn dev server
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
# bash
|
|
348
|
+
DEV_LOG=.jdi/cache/dev-server.log
|
|
349
|
+
DEV_PID_FILE=.jdi/cache/dev-server.pid
|
|
350
|
+
|
|
351
|
+
# Spawn in background, redirecting log
|
|
352
|
+
nohup $DEV_COMMAND > $DEV_LOG 2>&1 &
|
|
353
|
+
echo $! > $DEV_PID_FILE
|
|
354
|
+
|
|
355
|
+
# Wait ready (poll URL, timeout 60s)
|
|
356
|
+
READY=0
|
|
357
|
+
for i in $(seq 1 60); do
|
|
358
|
+
if curl -sSf -o /dev/null --max-time 2 "$FRONTEND_URL"; then
|
|
359
|
+
READY=1
|
|
360
|
+
break
|
|
361
|
+
fi
|
|
362
|
+
sleep 1
|
|
363
|
+
done
|
|
364
|
+
|
|
365
|
+
if [ $READY -eq 0 ]; then
|
|
366
|
+
# Cleanup
|
|
367
|
+
kill $(cat $DEV_PID_FILE) 2>/dev/null
|
|
368
|
+
echo '{"status":"INCONCLUSIVE","reason":"dev server failed to start in 60s","logs":"'$DEV_LOG'"}' > .jdi/cache/ui-findings.json
|
|
369
|
+
exit 0 # doesn't fail the reviewer - INCONCLUSIVE is WARN
|
|
370
|
+
fi
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```powershell
|
|
374
|
+
# PowerShell
|
|
375
|
+
$DEV_LOG = ".jdi/cache/dev-server.log"
|
|
376
|
+
$DEV_PID_FILE = ".jdi/cache/dev-server.pid"
|
|
377
|
+
|
|
378
|
+
$proc = Start-Process -FilePath pwsh -ArgumentList "-NoProfile", "-Command", $DEV_COMMAND `
|
|
379
|
+
-RedirectStandardOutput $DEV_LOG -RedirectStandardError $DEV_LOG `
|
|
380
|
+
-PassThru -WindowStyle Hidden
|
|
381
|
+
$proc.Id | Out-File -FilePath $DEV_PID_FILE
|
|
382
|
+
|
|
383
|
+
$ready = $false
|
|
384
|
+
for ($i = 0; $i -lt 60; $i++) {
|
|
385
|
+
try {
|
|
386
|
+
$r = Invoke-WebRequest -Uri $FRONTEND_URL -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
|
|
387
|
+
if ($r.StatusCode -eq 200) { $ready = $true; break }
|
|
388
|
+
} catch {}
|
|
389
|
+
Start-Sleep -Seconds 1
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (-not $ready) {
|
|
393
|
+
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
|
394
|
+
$err = @{ status="INCONCLUSIVE"; reason="dev server failed to start in 60s"; logs=$DEV_LOG } | ConvertTo-Json
|
|
395
|
+
$err | Out-File .jdi/cache/ui-findings.json
|
|
396
|
+
exit 0
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Step 7: Run Playwright
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
# bash
|
|
404
|
+
JDI_FRONTEND_URL="$FRONTEND_URL" \
|
|
405
|
+
JDI_ROUTES="$(echo $CRITICAL_PATHS | tr '\n' ',')" \
|
|
406
|
+
JDI_OUT=".jdi/cache/ui-findings.json" \
|
|
407
|
+
JDI_SCREENSHOT_DIR=".jdi/cache/screenshots" \
|
|
408
|
+
$PW_BIN test --config=.jdi/cache/playwright.config.js 2>&1 | tee .jdi/cache/playwright.log
|
|
409
|
+
|
|
410
|
+
# Playwright exit code doesn't matter - findings already written by afterAll
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```powershell
|
|
414
|
+
# PowerShell
|
|
415
|
+
$env:JDI_FRONTEND_URL = $FRONTEND_URL
|
|
416
|
+
$env:JDI_ROUTES = ($CRITICAL_PATHS -join ',')
|
|
417
|
+
$env:JDI_OUT = ".jdi/cache/ui-findings.json"
|
|
418
|
+
$env:JDI_SCREENSHOT_DIR = ".jdi/cache/screenshots"
|
|
419
|
+
|
|
420
|
+
& npx playwright test --config=.jdi/cache/playwright.config.js 2>&1 | Tee-Object -FilePath .jdi/cache/playwright.log
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Step 8: Kill dev server (always, even on failure)
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
# bash
|
|
427
|
+
if [ -f $DEV_PID_FILE ]; then
|
|
428
|
+
PID=$(cat $DEV_PID_FILE)
|
|
429
|
+
# Kill process + children (dev server usually has children: node, esbuild, vite, etc)
|
|
430
|
+
pkill -P $PID 2>/dev/null
|
|
431
|
+
kill $PID 2>/dev/null
|
|
432
|
+
# Safeguard on common ports (in case wrong PID)
|
|
433
|
+
# Vite: 5173, Next: 3000, etc - skip aggressive cleanup
|
|
434
|
+
rm $DEV_PID_FILE
|
|
435
|
+
fi
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
```powershell
|
|
439
|
+
# PowerShell
|
|
440
|
+
if (Test-Path $DEV_PID_FILE) {
|
|
441
|
+
$pid = Get-Content $DEV_PID_FILE
|
|
442
|
+
# Kill children first
|
|
443
|
+
Get-CimInstance Win32_Process -Filter "ParentProcessId=$pid" -ErrorAction SilentlyContinue |
|
|
444
|
+
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
445
|
+
# Kill the parent
|
|
446
|
+
Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue
|
|
447
|
+
Remove-Item $DEV_PID_FILE
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Step 9: Return to parent reviewer
|
|
452
|
+
|
|
453
|
+
Skill doesn't write REVIEW.md. Only writes `.jdi/cache/ui-findings.json` + screenshots.
|
|
454
|
+
|
|
455
|
+
Reviewer reads the JSON, classifies severities, and writes "UI Validation" section in REVIEW.md.
|
|
456
|
+
|
|
457
|
+
## Finding classification (reference for the reviewer)
|
|
458
|
+
|
|
459
|
+
| Finding | Severity |
|
|
460
|
+
|---|---|
|
|
461
|
+
| `console.error` on any route | BLOCK |
|
|
462
|
+
| `network.5xx` on critical_path | BLOCK |
|
|
463
|
+
| `network.4xx` on critical_path | WARN |
|
|
464
|
+
| `network.requestfailed` (CORS/abort/etc) | WARN |
|
|
465
|
+
| `navigationFailures` (404/timeout/etc on critical_path) | BLOCK |
|
|
466
|
+
| `a11y.impact=critical` | BLOCK |
|
|
467
|
+
| `a11y.impact=serious` | BLOCK |
|
|
468
|
+
| `a11y.impact=moderate` | WARN |
|
|
469
|
+
| `a11y.impact=minor` | INFO |
|
|
470
|
+
| `layout.horizontal_scroll` on mobile | BLOCK |
|
|
471
|
+
| `layout.horizontal_scroll` on desktop | INFO |
|
|
472
|
+
| `axe-core failed` (technical error) | WARN |
|
|
473
|
+
| `INCONCLUSIVE` (dev server timeout) | WARN |
|
|
474
|
+
| `SKIPPED` (user declined install) | WARN |
|
|
475
|
+
|
|
476
|
+
## Expected inputs
|
|
477
|
+
|
|
478
|
+
From PROJECT.md (passed as environment variables by the reviewer):
|
|
479
|
+
- `frontend.frontend_url` -> `FRONTEND_URL`
|
|
480
|
+
- `frontend.dev_command` -> `DEV_COMMAND`
|
|
481
|
+
- `frontend.critical_paths` -> `CRITICAL_PATHS` (list)
|
|
482
|
+
|
|
483
|
+
## Outputs
|
|
484
|
+
|
|
485
|
+
Files created in `.jdi/cache/` (gitignored):
|
|
486
|
+
- `ui-findings.json` - structured findings
|
|
487
|
+
- `screenshots/*.png` - 1 per route x viewport
|
|
488
|
+
- `dev-server.log` - dev server log
|
|
489
|
+
- `playwright.log` - Playwright run log
|
|
490
|
+
- `playwright-check.spec.js` - generated spec
|
|
491
|
+
- `playwright.config.js` - generated config
|
|
492
|
+
|
|
493
|
+
NEVER commit `.jdi/cache/`.
|
|
494
|
+
|
|
495
|
+
## Anti-patterns
|
|
496
|
+
|
|
497
|
+
- Running against prod URL - dev local only. Prod is out of scope for this gate
|
|
498
|
+
- Testing flows that require login - MVP doesn't support auth setup. Critical paths must be public OR pre-authenticated manually (cookie/session passed via PROJECT.md in follow-up)
|
|
499
|
+
- Blocking review if Playwright install fails - degrade to SKIPPED
|
|
500
|
+
- Leaving dev server alive after gate - always kill, even on error
|
|
501
|
+
- Committing screenshots - .gitignore guaranteed in pre-flight
|
|
502
|
+
- Running parallel (workers > 1) - local dev server doesn't scale, and race conditions confuse findings
|
|
503
|
+
- Using `--headed` in CI - always headless
|
|
504
|
+
- Trusting Playwright exit code - findings come from afterAll, even with test failure
|
|
505
|
+
|
|
506
|
+
## References
|
|
507
|
+
|
|
508
|
+
- `references/playwright-setup.md` - Detailed install per package manager + troubleshoot
|
|
509
|
+
- `references/dev-server-detection.md` - Heuristics for detecting ready (curl, wait-on, polling)
|
|
510
|
+
- `references/axe-rules.md` - Mapping of axe rule IDs -> WCAG -> severity
|
|
511
|
+
- `references/auth-flows.md` - Roadmap for authenticated flows (future)
|
|
512
|
+
|
|
513
|
+
## Examples
|
|
514
|
+
|
|
515
|
+
### Example 1: Vite + React, Playwright missing, user accepts install
|
|
516
|
+
|
|
517
|
+
```
|
|
518
|
+
1. Reviewer triggers gate 7
|
|
519
|
+
2. Skill detects `npx playwright --version` -> exit 1
|
|
520
|
+
3. Lockfile = pnpm-lock.yaml -> PKG_MGR=pnpm
|
|
521
|
+
4. AskUserQuestion -> user picks "Yes, Chromium"
|
|
522
|
+
5. pnpm add -D @playwright/test @axe-core/playwright
|
|
523
|
+
6. pnpm exec playwright install chromium
|
|
524
|
+
7. Spawns `pnpm dev` in bg, PID 12345
|
|
525
|
+
8. Waits http://localhost:5173 -> ready in 4s
|
|
526
|
+
9. Runs Playwright on /, /login, /dashboard x mobile + desktop = 6 navigations
|
|
527
|
+
10. Findings:
|
|
528
|
+
- 1 console error (uncaught promise) on /dashboard mobile + desktop
|
|
529
|
+
- 0 network errors
|
|
530
|
+
- 2 a11y serious on /login (missing label + contrast)
|
|
531
|
+
- 1 horizontal scroll on /dashboard mobile
|
|
532
|
+
11. Kill PID 12345 + children
|
|
533
|
+
12. Writes .jdi/cache/ui-findings.json
|
|
534
|
+
13. Reviewer reads JSON, marks gate 7 = BLOCK (3 issues), writes REVIEW.md
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Example 2: API-only, has_frontend=false
|
|
538
|
+
|
|
539
|
+
Skill is not even loaded. Reviewer skips gate 7 with SKIPPED.
|
|
540
|
+
|
|
541
|
+
### Example 3: Dev server fails to start
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
1. Spawns `pnpm dev` -> process dies after 2s (port 5173 occupied)
|
|
545
|
+
2. Poll of 60s expires without 200 OK
|
|
546
|
+
3. Cleanup of PID
|
|
547
|
+
4. Writes {"status":"INCONCLUSIVE","reason":"dev server failed to start in 60s"}
|
|
548
|
+
5. Reviewer marks gate 7 = WARN with link to dev-server.log
|
|
549
|
+
6. Review not blocked, but user alerted
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Example 4: User declines to install Playwright
|
|
553
|
+
|
|
554
|
+
```
|
|
555
|
+
1. AskUserQuestion -> "No, skip gate 7"
|
|
556
|
+
2. Writes {"status":"SKIPPED","reason":"user declined Playwright install"}
|
|
557
|
+
3. Reviewer marks gate 7 = SKIPPED (warn not block)
|
|
558
|
+
4. REVIEW.md notes "UI Validation: SKIPPED - run /jdi-verify again when you accept installing"
|
|
559
|
+
```
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jdi-adopt
|
|
3
|
+
description: Entry point for brownfield project (code already exists). Runs jdi-adopter — scan + analysis + confirmation + generates .jdi/ with adopted=true flag. Replaces /jdi-new for existing projects.
|
|
4
|
+
argument_hint: "<optional short description>"
|
|
5
|
+
runtime_intent:
|
|
6
|
+
invokes_agent: jdi-adopter
|
|
7
|
+
runtime_overrides:
|
|
8
|
+
claude:
|
|
9
|
+
allowed-tools: [Read, Write, Bash, Grep, Glob, AskUserQuestion, WebSearch, WebFetch, Agent]
|
|
10
|
+
copilot:
|
|
11
|
+
tools: [read, write, grep, glob, terminal]
|
|
12
|
+
opencode:
|
|
13
|
+
agent: jdi-adopter
|
|
14
|
+
subtask: true
|
|
15
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
16
|
+
antigravity:
|
|
17
|
+
triggers:
|
|
18
|
+
- "/jdi-adopt"
|
|
19
|
+
- "adopt project"
|
|
20
|
+
- "existing project"
|
|
21
|
+
- "brownfield"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<objective>
|
|
25
|
+
Add JDI to project that ALREADY HAS CODE. Scan repo, infer stack/code-design, confirm with user (code-design ALWAYS confirmed), generate PROJECT.md + ROADMAP.md + STATE.md + DECISIONS.md with adopted=true flag.
|
|
26
|
+
</objective>
|
|
27
|
+
|
|
28
|
+
<arguments>
|
|
29
|
+
- `description` (optional): short text override of what the project does. If omitted, adopter extracts from README.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
- `/jdi-adopt`
|
|
33
|
+
- `/jdi-adopt "Orders REST API, legacy, want to add reporting"`
|
|
34
|
+
</arguments>
|
|
35
|
+
|
|
36
|
+
<process>
|
|
37
|
+
|
|
38
|
+
### Step 1: Validation
|
|
39
|
+
```bash
|
|
40
|
+
test -d .jdi/ && {
|
|
41
|
+
echo ".jdi/ already exists. Use /jdi-bootstrap if no specialists, OR edit manually."
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# directory must have code (otherwise use /jdi-new)
|
|
46
|
+
file_count=$(find . -maxdepth 3 -type f \
|
|
47
|
+
-not -path './.git/*' -not -path './node_modules/*' \
|
|
48
|
+
-not -path './.venv/*' -not -path './venv/*' \
|
|
49
|
+
-not -path './target/*' -not -path './dist/*' -not -path './build/*' \
|
|
50
|
+
-not -path './bin/*' -not -path './obj/*' \
|
|
51
|
+
2>/dev/null | wc -l)
|
|
52
|
+
|
|
53
|
+
if [ "$file_count" -lt 3 ]; then
|
|
54
|
+
echo "Directory nearly empty ($file_count files). Use /jdi-new for greenfield."
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
PowerShell:
|
|
60
|
+
```powershell
|
|
61
|
+
if (Test-Path .jdi) {
|
|
62
|
+
Write-Error ".jdi/ already exists. Use /jdi-bootstrap if no specialists, OR edit manually."
|
|
63
|
+
exit 1
|
|
64
|
+
}
|
|
65
|
+
$files = Get-ChildItem -Recurse -File -Depth 3 -ErrorAction SilentlyContinue |
|
|
66
|
+
Where-Object { $_.FullName -notmatch '\\(\.git|node_modules|\.venv|venv|target|dist|build|bin|obj)\\' }
|
|
67
|
+
if ($files.Count -lt 3) {
|
|
68
|
+
Write-Error "Directory nearly empty ($($files.Count) files). Use /jdi-new for greenfield."
|
|
69
|
+
exit 1
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Step 2: Spawn jdi-adopter
|
|
74
|
+
Invoke `jdi-adopter` passing description (if any). Wait.
|
|
75
|
+
|
|
76
|
+
Adopter conducts:
|
|
77
|
+
- Automatic scan (manifests, layout, git log, README)
|
|
78
|
+
- 5 questions (stack, code-design, vision, new-features, LLM)
|
|
79
|
+
- Optional web research (max 2 lookups)
|
|
80
|
+
- Generation of `.jdi/` files
|
|
81
|
+
- Initial commit
|
|
82
|
+
|
|
83
|
+
### Step 3: Verify outputs
|
|
84
|
+
```bash
|
|
85
|
+
test -f .jdi/PROJECT.md || { echo "PROJECT.md not created"; exit 1; }
|
|
86
|
+
test -f .jdi/ROADMAP.md || { echo "ROADMAP.md not created"; exit 1; }
|
|
87
|
+
test -f .jdi/STATE.md || { echo "STATE.md not created"; exit 1; }
|
|
88
|
+
test -f .jdi/DECISIONS.md|| { echo "DECISIONS.md not created"; exit 1; }
|
|
89
|
+
|
|
90
|
+
grep -q '^adopted: true' .jdi/STATE.md || echo "warn: adopted flag missing in STATE.md"
|
|
91
|
+
grep -q '^D-2 ' .jdi/DECISIONS.md || echo "warn: D-2 (boundary) missing in DECISIONS.md"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
PowerShell:
|
|
95
|
+
```powershell
|
|
96
|
+
foreach ($f in @('PROJECT.md','ROADMAP.md','STATE.md','DECISIONS.md')) {
|
|
97
|
+
if (-not (Test-Path ".jdi/$f")) { Write-Error "$f not created"; exit 1 }
|
|
98
|
+
}
|
|
99
|
+
if (-not (Select-String -Path .jdi/STATE.md -Pattern '^adopted:\s*true' -Quiet)) {
|
|
100
|
+
Write-Warning "adopted flag missing in STATE.md"
|
|
101
|
+
}
|
|
102
|
+
if (-not (Select-String -Path .jdi/DECISIONS.md -Pattern '^D-2 ' -Quiet)) {
|
|
103
|
+
Write-Warning "D-2 (boundary) missing in DECISIONS.md"
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Step 4: Create config.json (token/context budget)
|
|
108
|
+
|
|
109
|
+
If `.jdi/config.json` does not yet exist, write default identical to `/jdi-new`:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"$schema_version": "1.1",
|
|
114
|
+
"context_window": 200000,
|
|
115
|
+
"thresholds": {
|
|
116
|
+
"warn_pct": 60,
|
|
117
|
+
"critical_pct": 70
|
|
118
|
+
},
|
|
119
|
+
"budgets": {
|
|
120
|
+
"max_context_chars": 6000,
|
|
121
|
+
"max_plan_chars": 12000,
|
|
122
|
+
"max_summary_chars": 8192
|
|
123
|
+
},
|
|
124
|
+
"compaction": {
|
|
125
|
+
"keep_phases": 2,
|
|
126
|
+
"archive_after": 5
|
|
127
|
+
},
|
|
128
|
+
"coverage_min": 80
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Step 5: Confirm
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
{project_name} adopted. {N} new phases planned.
|
|
136
|
+
Existing assets captured in .jdi/PROJECT.md (context, NOT TODO).
|
|
137
|
+
Code design: {design} (LOCKED after confirm).
|
|
138
|
+
Boundary: legacy code does not enforce 80% coverage (D-2 in DECISIONS.md).
|
|
139
|
+
Next: /jdi-bootstrap
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
</process>
|
|
143
|
+
|
|
144
|
+
<gates>
|
|
145
|
+
- pre: `.jdi/` missing + directory with >= 3 code files (excluding ignored)
|
|
146
|
+
- post: PROJECT.md (with `## Existing assets`) + ROADMAP.md (adopted=true) + STATE.md (adopted: true) + DECISIONS.md (D-1 code-design, D-2 boundary) + config.json created, commit made
|
|
147
|
+
</gates>
|
|
148
|
+
|
|
149
|
+
<errors>
|
|
150
|
+
- `.jdi/` already exists -> exit with instruction
|
|
151
|
+
- Directory nearly empty -> suggest `/jdi-new` instead of adopt
|
|
152
|
+
- Adopter cancelled -> exit clean, no commit
|
|
153
|
+
- Adopter failed -> show error, no commit, suggest manual retry
|
|
154
|
+
- Code design not confirmed by user -> abort (rule: ALWAYS confirm)
|
|
155
|
+
</errors>
|