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.
Files changed (159) hide show
  1. package/AGENTS.md +209 -0
  2. package/ARCHITECTURE.md +210 -0
  3. package/COMMANDS.md +241 -0
  4. package/CREATE-EXAMPLE.md +385 -0
  5. package/CREATE.md +315 -0
  6. package/EXTENSION.md +141 -0
  7. package/LICENSE +21 -0
  8. package/MEMORY.md +471 -0
  9. package/PORTABILITY.md +438 -0
  10. package/README.md +789 -0
  11. package/bin/git-hooks/post-commit +16 -0
  12. package/bin/git-hooks/pre-commit +21 -0
  13. package/bin/jdi-build.ps1 +381 -0
  14. package/bin/jdi-build.sh +332 -0
  15. package/bin/jdi-doctor.ps1 +403 -0
  16. package/bin/jdi-doctor.sh +400 -0
  17. package/bin/jdi-install-caveman.ps1 +97 -0
  18. package/bin/jdi-install-caveman.sh +99 -0
  19. package/bin/jdi-install-playwright.ps1 +319 -0
  20. package/bin/jdi-install-playwright.sh +284 -0
  21. package/bin/jdi-install.ps1 +154 -0
  22. package/bin/jdi-install.sh +132 -0
  23. package/bin/jdi-uninstall.ps1 +309 -0
  24. package/bin/jdi-uninstall.sh +264 -0
  25. package/bin/jdi-update.ps1 +215 -0
  26. package/bin/jdi-update.sh +209 -0
  27. package/bin/jdi.js +460 -0
  28. package/bin/lib/jdi-monitor.ps1 +66 -0
  29. package/bin/lib/jdi-monitor.sh +74 -0
  30. package/bin/lib/jdi-truncate.ps1 +96 -0
  31. package/bin/lib/jdi-truncate.sh +99 -0
  32. package/bin/lib/ui.js +197 -0
  33. package/core/agents/jdi-adopter.md +465 -0
  34. package/core/agents/jdi-architect.md +894 -0
  35. package/core/agents/jdi-asker.md +153 -0
  36. package/core/agents/jdi-bootstrap.md +247 -0
  37. package/core/agents/jdi-planner.md +254 -0
  38. package/core/agents/jdi-researcher.md +303 -0
  39. package/core/commands/jdi-adopt.md +155 -0
  40. package/core/commands/jdi-bootstrap.md +81 -0
  41. package/core/commands/jdi-create.md +80 -0
  42. package/core/commands/jdi-discuss.md +80 -0
  43. package/core/commands/jdi-do.md +200 -0
  44. package/core/commands/jdi-loop.md +315 -0
  45. package/core/commands/jdi-new.md +131 -0
  46. package/core/commands/jdi-plan.md +73 -0
  47. package/core/commands/jdi-ship.md +146 -0
  48. package/core/commands/jdi-verify.md +159 -0
  49. package/core/skills/clean-code/SKILL.md +261 -0
  50. package/core/skills/dry/SKILL.md +150 -0
  51. package/core/skills/frontend-rules/SKILL.md +386 -0
  52. package/core/skills/frontend-validator/SKILL.md +567 -0
  53. package/core/skills/kiss/SKILL.md +178 -0
  54. package/core/skills/solid/SKILL.md +281 -0
  55. package/core/skills/yagni/SKILL.md +207 -0
  56. package/core/templates/agent.md +72 -0
  57. package/core/templates/doer-specialist.md +216 -0
  58. package/core/templates/reviewer-specialist.md +405 -0
  59. package/core/templates/skill.md +66 -0
  60. package/package.json +70 -0
  61. package/runtimes/antigravity/agents.md +74 -0
  62. package/runtimes/antigravity/skills/clean-code/SKILL.md +252 -0
  63. package/runtimes/antigravity/skills/dry/SKILL.md +141 -0
  64. package/runtimes/antigravity/skills/frontend-rules/SKILL.md +376 -0
  65. package/runtimes/antigravity/skills/frontend-validator/SKILL.md +559 -0
  66. package/runtimes/antigravity/skills/jdi-adopt/SKILL.md +155 -0
  67. package/runtimes/antigravity/skills/jdi-adopter/SKILL.md +436 -0
  68. package/runtimes/antigravity/skills/jdi-architect/SKILL.md +872 -0
  69. package/runtimes/antigravity/skills/jdi-asker/SKILL.md +125 -0
  70. package/runtimes/antigravity/skills/jdi-asker/references/context-template.md +34 -0
  71. package/runtimes/antigravity/skills/jdi-asker/references/decision-format.md +19 -0
  72. package/runtimes/antigravity/skills/jdi-asker/scripts/find_phase_dir.sh +25 -0
  73. package/runtimes/antigravity/skills/jdi-bootstrap/SKILL.md +81 -0
  74. package/runtimes/antigravity/skills/jdi-create/SKILL.md +80 -0
  75. package/runtimes/antigravity/skills/jdi-discuss/SKILL.md +80 -0
  76. package/runtimes/antigravity/skills/jdi-discuss/scripts/run_command.sh +62 -0
  77. package/runtimes/antigravity/skills/jdi-do/SKILL.md +200 -0
  78. package/runtimes/antigravity/skills/jdi-loop/SKILL.md +315 -0
  79. package/runtimes/antigravity/skills/jdi-new/SKILL.md +131 -0
  80. package/runtimes/antigravity/skills/jdi-plan/SKILL.md +73 -0
  81. package/runtimes/antigravity/skills/jdi-planner/SKILL.md +225 -0
  82. package/runtimes/antigravity/skills/jdi-researcher/SKILL.md +274 -0
  83. package/runtimes/antigravity/skills/jdi-ship/SKILL.md +146 -0
  84. package/runtimes/antigravity/skills/jdi-verify/SKILL.md +159 -0
  85. package/runtimes/antigravity/skills/kiss/SKILL.md +169 -0
  86. package/runtimes/antigravity/skills/solid/SKILL.md +272 -0
  87. package/runtimes/antigravity/skills/yagni/SKILL.md +198 -0
  88. package/runtimes/claude/CLAUDE.md +91 -0
  89. package/runtimes/claude/agents/jdi-adopter.md +430 -0
  90. package/runtimes/claude/agents/jdi-architect.md +864 -0
  91. package/runtimes/claude/agents/jdi-asker.md +119 -0
  92. package/runtimes/claude/agents/jdi-bootstrap.md +213 -0
  93. package/runtimes/claude/agents/jdi-planner.md +221 -0
  94. package/runtimes/claude/agents/jdi-researcher.md +269 -0
  95. package/runtimes/claude/commands/jdi-adopt.md +155 -0
  96. package/runtimes/claude/commands/jdi-bootstrap.md +81 -0
  97. package/runtimes/claude/commands/jdi-create.md +80 -0
  98. package/runtimes/claude/commands/jdi-discuss.md +80 -0
  99. package/runtimes/claude/commands/jdi-do.md +200 -0
  100. package/runtimes/claude/commands/jdi-loop.md +315 -0
  101. package/runtimes/claude/commands/jdi-new.md +131 -0
  102. package/runtimes/claude/commands/jdi-plan.md +73 -0
  103. package/runtimes/claude/commands/jdi-ship.md +146 -0
  104. package/runtimes/claude/commands/jdi-verify.md +159 -0
  105. package/runtimes/claude/settings.example.json +132 -0
  106. package/runtimes/claude/skills/clean-code/SKILL.md +247 -0
  107. package/runtimes/claude/skills/dry/SKILL.md +136 -0
  108. package/runtimes/claude/skills/frontend-rules/SKILL.md +369 -0
  109. package/runtimes/claude/skills/frontend-validator/SKILL.md +553 -0
  110. package/runtimes/claude/skills/kiss/SKILL.md +164 -0
  111. package/runtimes/claude/skills/solid/SKILL.md +267 -0
  112. package/runtimes/claude/skills/yagni/SKILL.md +193 -0
  113. package/runtimes/copilot/agents/jdi-adopter.agent.md +430 -0
  114. package/runtimes/copilot/agents/jdi-architect.agent.md +864 -0
  115. package/runtimes/copilot/agents/jdi-asker.agent.md +119 -0
  116. package/runtimes/copilot/agents/jdi-bootstrap.agent.md +213 -0
  117. package/runtimes/copilot/agents/jdi-planner.agent.md +221 -0
  118. package/runtimes/copilot/agents/jdi-researcher.agent.md +269 -0
  119. package/runtimes/copilot/copilot-instructions.md +80 -0
  120. package/runtimes/copilot/prompts/jdi-adopt.prompt.md +155 -0
  121. package/runtimes/copilot/prompts/jdi-bootstrap.prompt.md +81 -0
  122. package/runtimes/copilot/prompts/jdi-create.prompt.md +80 -0
  123. package/runtimes/copilot/prompts/jdi-discuss.prompt.md +80 -0
  124. package/runtimes/copilot/prompts/jdi-do.prompt.md +200 -0
  125. package/runtimes/copilot/prompts/jdi-loop.prompt.md +315 -0
  126. package/runtimes/copilot/prompts/jdi-new.prompt.md +131 -0
  127. package/runtimes/copilot/prompts/jdi-plan.prompt.md +73 -0
  128. package/runtimes/copilot/prompts/jdi-ship.prompt.md +146 -0
  129. package/runtimes/copilot/prompts/jdi-verify.prompt.md +159 -0
  130. package/runtimes/opencode/AGENTS.md +87 -0
  131. package/runtimes/opencode/agents/jdi-adopter.md +434 -0
  132. package/runtimes/opencode/agents/jdi-architect.md +861 -0
  133. package/runtimes/opencode/agents/jdi-asker.md +123 -0
  134. package/runtimes/opencode/agents/jdi-bootstrap.md +217 -0
  135. package/runtimes/opencode/agents/jdi-planner.md +225 -0
  136. package/runtimes/opencode/agents/jdi-researcher.md +273 -0
  137. package/runtimes/opencode/commands/jdi-adopt.md +155 -0
  138. package/runtimes/opencode/commands/jdi-bootstrap.md +81 -0
  139. package/runtimes/opencode/commands/jdi-create.md +80 -0
  140. package/runtimes/opencode/commands/jdi-discuss.md +80 -0
  141. package/runtimes/opencode/commands/jdi-do.md +200 -0
  142. package/runtimes/opencode/commands/jdi-loop.md +315 -0
  143. package/runtimes/opencode/commands/jdi-new.md +131 -0
  144. package/runtimes/opencode/commands/jdi-plan.md +73 -0
  145. package/runtimes/opencode/commands/jdi-ship.md +146 -0
  146. package/runtimes/opencode/commands/jdi-verify.md +159 -0
  147. package/runtimes/opencode/opencode.example.jsonc +169 -0
  148. package/runtimes/opencode/skills/clean-code/SKILL.md +247 -0
  149. package/runtimes/opencode/skills/dry/SKILL.md +136 -0
  150. package/runtimes/opencode/skills/frontend-rules/SKILL.md +369 -0
  151. package/runtimes/opencode/skills/frontend-validator/SKILL.md +553 -0
  152. package/runtimes/opencode/skills/kiss/SKILL.md +164 -0
  153. package/runtimes/opencode/skills/solid/SKILL.md +267 -0
  154. package/runtimes/opencode/skills/yagni/SKILL.md +193 -0
  155. package/templates-jdi-folder/config.json +18 -0
  156. package/templates-jdi-folder/registry.md +31 -0
  157. package/templates-jdi-folder/reviewers.md +33 -0
  158. package/templates-jdi-folder/skills-registry.md +32 -0
  159. 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>