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,319 @@
1
+ <#
2
+ .SYNOPSIS
3
+ jdi-install-playwright (Windows): installs Playwright dev dep + chromium browser
4
+ + injects Playwright MCP config in detected runtime configs.
5
+
6
+ .DESCRIPTION
7
+ Optional install. Adds:
8
+ - @playwright/test as devDependency (via detected pkg manager: pnpm > yarn > npm)
9
+ - chromium browser via `npx playwright install chromium`
10
+ - MCP server config in:
11
+ * .claude/settings.local.json (Claude Code)
12
+ * .opencode/opencode.jsonc (OpenCode)
13
+ Copilot has no MCP; skip with warn.
14
+ Antigravity MCP support unverified; skip with warn.
15
+
16
+ Idempotent: if @playwright/test already present, skips dep install.
17
+ If MCP config already has playwright entry, skips injection.
18
+
19
+ .PARAMETER SkipBrowser
20
+ Skip `npx playwright install chromium`. Use if browser already installed.
21
+
22
+ .PARAMETER SkipMcp
23
+ Skip MCP config injection. Only install dep + browser.
24
+
25
+ .PARAMETER Runtime
26
+ Force MCP injection only into this runtime. Default: detect all present.
27
+ Values: claude | opencode | copilot | antigravity | all
28
+
29
+ .PARAMETER AntigravityScope
30
+ Antigravity MCP scope: user (~/.gemini/settings.json) or project (.gemini/settings.json).
31
+ Default: user
32
+
33
+ .EXAMPLE
34
+ .\bin\jdi-install-playwright.ps1
35
+ .\bin\jdi-install-playwright.ps1 -SkipBrowser
36
+ .\bin\jdi-install-playwright.ps1 -Runtime claude
37
+ .\bin\jdi-install-playwright.ps1 -Runtime all -AntigravityScope project
38
+ #>
39
+ [CmdletBinding()]
40
+ param(
41
+ [switch]$SkipBrowser,
42
+ [switch]$SkipMcp,
43
+ [ValidateSet('claude','opencode','copilot','antigravity','all')]
44
+ [string]$Runtime = 'all',
45
+ [ValidateSet('user','project')]
46
+ [string]$AntigravityScope = 'user'
47
+ )
48
+
49
+ $ErrorActionPreference = 'Stop'
50
+ $ProjectDir = (Get-Location).Path
51
+
52
+ function Detect-PackageManager {
53
+ if (Test-Path (Join-Path $ProjectDir 'pnpm-lock.yaml')) { return 'pnpm' }
54
+ if (Test-Path (Join-Path $ProjectDir 'yarn.lock')) { return 'yarn' }
55
+ if (Test-Path (Join-Path $ProjectDir 'bun.lockb')) { return 'bun' }
56
+ return 'npm'
57
+ }
58
+
59
+ function Has-PlaywrightDep {
60
+ $pkg = Join-Path $ProjectDir 'package.json'
61
+ if (-not (Test-Path $pkg)) { return $false }
62
+ $json = Get-Content $pkg -Raw | ConvertFrom-Json
63
+ $deps = @{}
64
+ if ($json.dependencies) { $json.dependencies.PSObject.Properties | ForEach-Object { $deps[$_.Name] = $_.Value } }
65
+ if ($json.devDependencies) { $json.devDependencies.PSObject.Properties | ForEach-Object { $deps[$_.Name] = $_.Value } }
66
+ return $deps.ContainsKey('@playwright/test')
67
+ }
68
+
69
+ function Install-PlaywrightDep {
70
+ if (Has-PlaywrightDep) {
71
+ Write-Host " [skip] @playwright/test already in package.json"
72
+ return $true
73
+ }
74
+
75
+ if (-not (Test-Path (Join-Path $ProjectDir 'package.json'))) {
76
+ Write-Warning "package.json not found. Run 'npm init -y' first or this command outside a JS/TS project."
77
+ return $false
78
+ }
79
+
80
+ $pm = Detect-PackageManager
81
+ Write-Host " Installing @playwright/test via $pm..."
82
+
83
+ switch ($pm) {
84
+ 'pnpm' { & pnpm add -D '@playwright/test' }
85
+ 'yarn' { & yarn add -D '@playwright/test' }
86
+ 'bun' { & bun add -d '@playwright/test' }
87
+ default { & npm install --save-dev '@playwright/test' }
88
+ }
89
+
90
+ return ($LASTEXITCODE -eq 0)
91
+ }
92
+
93
+ function Install-ChromiumBrowser {
94
+ if ($SkipBrowser) {
95
+ Write-Host " [skip] -SkipBrowser flag set"
96
+ return $true
97
+ }
98
+ Write-Host " Installing chromium browser (~170MB, may take a minute)..."
99
+ & npx --yes playwright install chromium
100
+ return ($LASTEXITCODE -eq 0)
101
+ }
102
+
103
+ function Inject-ClaudeMcp {
104
+ $claudeDir = Join-Path $ProjectDir '.claude'
105
+ if (-not (Test-Path $claudeDir)) {
106
+ Write-Host " [skip] .claude/ not present (Claude Code not installed)"
107
+ return
108
+ }
109
+
110
+ $settingsPath = Join-Path $claudeDir 'settings.local.json'
111
+ $settings = @{}
112
+
113
+ if (Test-Path $settingsPath) {
114
+ try {
115
+ $raw = Get-Content $settingsPath -Raw
116
+ $parsed = $raw | ConvertFrom-Json
117
+ $parsed.PSObject.Properties | ForEach-Object { $settings[$_.Name] = $_.Value }
118
+ } catch {
119
+ Write-Warning " Existing $settingsPath is not valid JSON; skipping injection."
120
+ return
121
+ }
122
+ }
123
+
124
+ if (-not $settings.ContainsKey('mcpServers')) { $settings['mcpServers'] = @{} }
125
+ $mcp = @{}
126
+ if ($settings['mcpServers'] -is [psobject]) {
127
+ $settings['mcpServers'].PSObject.Properties | ForEach-Object { $mcp[$_.Name] = $_.Value }
128
+ } elseif ($settings['mcpServers'] -is [hashtable]) {
129
+ $mcp = $settings['mcpServers']
130
+ }
131
+
132
+ if ($mcp.ContainsKey('playwright')) {
133
+ Write-Host " [skip] mcpServers.playwright already present in settings.local.json"
134
+ return
135
+ }
136
+
137
+ $mcp['playwright'] = @{
138
+ command = 'npx'
139
+ args = @('-y', '@playwright/mcp@latest')
140
+ }
141
+ $settings['mcpServers'] = $mcp
142
+
143
+ $json = $settings | ConvertTo-Json -Depth 8
144
+ $json | Out-File -FilePath $settingsPath -Encoding UTF8 -NoNewline
145
+ Write-Host " -> wrote .claude/settings.local.json (mcpServers.playwright)"
146
+ }
147
+
148
+ function Inject-OpencodeMcp {
149
+ $ocDir = Join-Path $ProjectDir '.opencode'
150
+ if (-not (Test-Path $ocDir)) {
151
+ Write-Host " [skip] .opencode/ not present (OpenCode not installed)"
152
+ return
153
+ }
154
+
155
+ $configPath = Join-Path $ocDir 'opencode.jsonc'
156
+ $raw = ''
157
+ if (Test-Path $configPath) {
158
+ $raw = Get-Content $configPath -Raw
159
+ if ($raw -match '"playwright"\s*:') {
160
+ Write-Host " [skip] mcp.playwright already present in opencode.jsonc"
161
+ return
162
+ }
163
+ } else {
164
+ $raw = "// OpenCode config - JDI managed `n{`n `"`$schema`": `"https://opencode.ai/config.json`"`n}`n"
165
+ }
166
+
167
+ # Strip comments for parse, keep header for re-emit
168
+ $stripped = ($raw -replace '(?m)^\s*//.*$','') -replace '/\*[\s\S]*?\*/',''
169
+ try {
170
+ $parsed = $stripped | ConvertFrom-Json
171
+ } catch {
172
+ Write-Warning " Existing opencode.jsonc is not parseable; skipping injection."
173
+ return
174
+ }
175
+
176
+ $obj = @{}
177
+ $parsed.PSObject.Properties | ForEach-Object { $obj[$_.Name] = $_.Value }
178
+
179
+ if (-not $obj.ContainsKey('mcp')) { $obj['mcp'] = @{} }
180
+ $mcp = @{}
181
+ if ($obj['mcp'] -is [psobject]) {
182
+ $obj['mcp'].PSObject.Properties | ForEach-Object { $mcp[$_.Name] = $_.Value }
183
+ } elseif ($obj['mcp'] -is [hashtable]) {
184
+ $mcp = $obj['mcp']
185
+ }
186
+
187
+ $mcp['playwright'] = @{
188
+ type = 'local'
189
+ command = @('npx', '-y', '@playwright/mcp@latest')
190
+ enabled = $true
191
+ }
192
+ $obj['mcp'] = $mcp
193
+
194
+ $newJson = $obj | ConvertTo-Json -Depth 8
195
+ $header = "// OpenCode config - JDI managed (mcp.playwright managed; rest is yours)`n"
196
+ $output = $header + $newJson + "`n"
197
+ $output | Out-File -FilePath $configPath -Encoding UTF8 -NoNewline
198
+ Write-Host " -> wrote .opencode/opencode.jsonc (mcp.playwright)"
199
+ }
200
+
201
+ function Inject-CopilotMcp {
202
+ # Copilot MCP via VS Code workspace: .vscode/mcp.json
203
+ $vscodeDir = Join-Path $ProjectDir '.vscode'
204
+ $f = Join-Path $vscodeDir 'mcp.json'
205
+
206
+ if ((Test-Path $f) -and ((Get-Content $f -Raw) -match '"playwright"\s*:')) {
207
+ Write-Host " [skip] servers.playwright already present in .vscode/mcp.json"
208
+ return
209
+ }
210
+
211
+ New-Item -ItemType Directory -Force -Path $vscodeDir | Out-Null
212
+
213
+ $settings = @{}
214
+ if (Test-Path $f) {
215
+ try {
216
+ $parsed = Get-Content $f -Raw | ConvertFrom-Json
217
+ $parsed.PSObject.Properties | ForEach-Object { $settings[$_.Name] = $_.Value }
218
+ } catch {
219
+ Write-Warning " Existing .vscode/mcp.json not valid JSON; skipping."
220
+ return
221
+ }
222
+ }
223
+
224
+ if (-not $settings.ContainsKey('servers')) { $settings['servers'] = @{} }
225
+ $servers = @{}
226
+ if ($settings['servers'] -is [psobject]) {
227
+ $settings['servers'].PSObject.Properties | ForEach-Object { $servers[$_.Name] = $_.Value }
228
+ } elseif ($settings['servers'] -is [hashtable]) {
229
+ $servers = $settings['servers']
230
+ }
231
+
232
+ $servers['playwright'] = @{
233
+ command = 'npx'
234
+ args = @('-y', '@playwright/mcp@latest')
235
+ }
236
+ $settings['servers'] = $servers
237
+
238
+ ($settings | ConvertTo-Json -Depth 8) | Out-File -FilePath $f -Encoding UTF8 -NoNewline
239
+ Write-Host " -> wrote .vscode/mcp.json (servers.playwright) for Copilot"
240
+ }
241
+
242
+ function Inject-AntigravityMcp {
243
+ $base = if ($AntigravityScope -eq 'user') { Join-Path $UserHome '.gemini' } else { Join-Path $ProjectDir '.gemini' }
244
+ $f = Join-Path $base 'settings.json'
245
+
246
+ if ((Test-Path $f) -and ((Get-Content $f -Raw) -match '"playwright"\s*:')) {
247
+ Write-Host " [skip] mcpServers.playwright already present in $f"
248
+ return
249
+ }
250
+
251
+ New-Item -ItemType Directory -Force -Path $base | Out-Null
252
+
253
+ $settings = @{}
254
+ if (Test-Path $f) {
255
+ try {
256
+ $parsed = Get-Content $f -Raw | ConvertFrom-Json
257
+ $parsed.PSObject.Properties | ForEach-Object { $settings[$_.Name] = $_.Value }
258
+ } catch {
259
+ Write-Warning " Existing $f not valid JSON; skipping."
260
+ return
261
+ }
262
+ }
263
+
264
+ if (-not $settings.ContainsKey('mcpServers')) { $settings['mcpServers'] = @{} }
265
+ $mcp = @{}
266
+ if ($settings['mcpServers'] -is [psobject]) {
267
+ $settings['mcpServers'].PSObject.Properties | ForEach-Object { $mcp[$_.Name] = $_.Value }
268
+ } elseif ($settings['mcpServers'] -is [hashtable]) {
269
+ $mcp = $settings['mcpServers']
270
+ }
271
+
272
+ $mcp['playwright'] = @{
273
+ command = 'npx'
274
+ args = @('-y', '@playwright/mcp@latest')
275
+ }
276
+ $settings['mcpServers'] = $mcp
277
+
278
+ ($settings | ConvertTo-Json -Depth 8) | Out-File -FilePath $f -Encoding UTF8 -NoNewline
279
+ Write-Host " -> wrote $f (mcpServers.playwright) for Antigravity"
280
+ }
281
+
282
+ # =====================================================================
283
+ # Main
284
+ # =====================================================================
285
+
286
+ Write-Host ''
287
+ Write-Host '=== JDI: Install Playwright + MCP ==='
288
+ Write-Host ''
289
+
290
+ Write-Host 'Step 1: Install dep'
291
+ $depOk = Install-PlaywrightDep
292
+ if (-not $depOk) {
293
+ Write-Error "Dep install failed. Aborting."
294
+ exit 1
295
+ }
296
+
297
+ Write-Host ''
298
+ Write-Host 'Step 2: Install browser'
299
+ $bOk = Install-ChromiumBrowser
300
+ if (-not $bOk) {
301
+ Write-Warning "Browser install failed. You can rerun later: 'npx playwright install chromium'"
302
+ }
303
+
304
+ if (-not $SkipMcp) {
305
+ Write-Host ''
306
+ Write-Host 'Step 3: Inject MCP config in detected runtimes'
307
+ if ($Runtime -in 'claude','all') { Inject-ClaudeMcp }
308
+ if ($Runtime -in 'opencode','all') { Inject-OpencodeMcp }
309
+ if ($Runtime -in 'copilot','all') { Inject-CopilotMcp }
310
+ if ($Runtime -in 'antigravity','all') { Inject-AntigravityMcp }
311
+ }
312
+
313
+ Write-Host ''
314
+ Write-Host 'Done. Restart your runtime to pick up MCP changes.'
315
+ Write-Host ' - Claude Code: close + reopen, OR run /mcp to verify'
316
+ Write-Host ' - OpenCode: opencode reload'
317
+ Write-Host ' - Copilot (VS Code): reload window, palette `MCP: List Servers`'
318
+ Write-Host ' - Antigravity: restart antigravity'
319
+ Write-Host ''
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env bash
2
+ # jdi-install-playwright (POSIX): installs Playwright dev dep + chromium browser
3
+ # + injects Playwright MCP config in detected runtime configs.
4
+ #
5
+ # Optional install. Idempotent.
6
+ #
7
+ # Usage:
8
+ # ./bin/jdi-install-playwright.sh
9
+ # ./bin/jdi-install-playwright.sh --skip-browser
10
+ # ./bin/jdi-install-playwright.sh --runtime claude
11
+ #
12
+ # Flags:
13
+ # --skip-browser Skip `npx playwright install chromium`
14
+ # --skip-mcp Skip MCP config injection (only dep + browser)
15
+ # --runtime <r> claude | opencode | copilot | antigravity | all (default: all)
16
+ # --antigravity-scope <s> user (default) | project
17
+
18
+ set -euo pipefail
19
+
20
+ PROJECT_DIR="$(pwd)"
21
+ USER_HOME="${HOME:-$USERPROFILE}"
22
+ SKIP_BROWSER=0
23
+ SKIP_MCP=0
24
+ RUNTIME=all
25
+ AG_SCOPE=user
26
+
27
+ while [ $# -gt 0 ]; do
28
+ case "$1" in
29
+ --skip-browser) SKIP_BROWSER=1; shift ;;
30
+ --skip-mcp) SKIP_MCP=1; shift ;;
31
+ --runtime) RUNTIME="$2"; shift 2 ;;
32
+ --antigravity-scope) AG_SCOPE="$2"; shift 2 ;;
33
+ *) echo "Unknown flag: $1"; exit 1 ;;
34
+ esac
35
+ done
36
+
37
+ case "$RUNTIME" in
38
+ claude|opencode|copilot|antigravity|all) ;;
39
+ *) echo "Invalid --runtime. Use: claude | opencode | copilot | antigravity | all"; exit 1 ;;
40
+ esac
41
+
42
+ case "$AG_SCOPE" in
43
+ user|project) ;;
44
+ *) echo "Invalid --antigravity-scope. Use: user | project"; exit 1 ;;
45
+ esac
46
+
47
+ detect_pm() {
48
+ if [ -f "$PROJECT_DIR/pnpm-lock.yaml" ]; then echo pnpm
49
+ elif [ -f "$PROJECT_DIR/yarn.lock" ]; then echo yarn
50
+ elif [ -f "$PROJECT_DIR/bun.lockb" ]; then echo bun
51
+ else echo npm
52
+ fi
53
+ }
54
+
55
+ has_pw_dep() {
56
+ local pkg="$PROJECT_DIR/package.json"
57
+ [ -f "$pkg" ] || { echo 0; return; }
58
+ if grep -qE '"@playwright/test"' "$pkg"; then echo 1; else echo 0; fi
59
+ }
60
+
61
+ install_pw_dep() {
62
+ if [ "$(has_pw_dep)" = "1" ]; then
63
+ echo " [skip] @playwright/test already in package.json"
64
+ return 0
65
+ fi
66
+ if [ ! -f "$PROJECT_DIR/package.json" ]; then
67
+ echo " [warn] package.json not found. Run 'npm init -y' first."
68
+ return 1
69
+ fi
70
+
71
+ local pm
72
+ pm="$(detect_pm)"
73
+ echo " Installing @playwright/test via $pm..."
74
+
75
+ case "$pm" in
76
+ pnpm) pnpm add -D @playwright/test ;;
77
+ yarn) yarn add -D @playwright/test ;;
78
+ bun) bun add -d @playwright/test ;;
79
+ *) npm install --save-dev @playwright/test ;;
80
+ esac
81
+ }
82
+
83
+ install_chromium() {
84
+ if [ "$SKIP_BROWSER" = "1" ]; then
85
+ echo " [skip] --skip-browser flag set"
86
+ return 0
87
+ fi
88
+ echo " Installing chromium browser (~170MB, may take a minute)..."
89
+ npx --yes playwright install chromium || {
90
+ echo " [warn] chromium install failed. Rerun later: npx playwright install chromium"
91
+ return 0
92
+ }
93
+ }
94
+
95
+ inject_claude_mcp() {
96
+ local dir="$PROJECT_DIR/.claude"
97
+ if [ ! -d "$dir" ]; then
98
+ echo " [skip] .claude/ not present (Claude Code not installed)"
99
+ return
100
+ fi
101
+
102
+ local f="$dir/settings.local.json"
103
+ if [ -f "$f" ] && grep -qE '"playwright"\s*:' "$f"; then
104
+ echo " [skip] mcpServers.playwright already present in settings.local.json"
105
+ return
106
+ fi
107
+
108
+ if ! command -v node >/dev/null 2>&1; then
109
+ echo " [warn] node not in PATH; cannot edit JSON safely"
110
+ return
111
+ fi
112
+
113
+ node - <<'NODE_EOF'
114
+ const fs = require('fs');
115
+ const path = require('path');
116
+ const f = path.join(process.cwd(), '.claude', 'settings.local.json');
117
+ let s = {};
118
+ if (fs.existsSync(f)) {
119
+ try { s = JSON.parse(fs.readFileSync(f, 'utf8')); }
120
+ catch { console.log(' [warn] settings.local.json invalid JSON; skip'); process.exit(0); }
121
+ }
122
+ s.mcpServers = s.mcpServers || {};
123
+ if (s.mcpServers.playwright) {
124
+ console.log(' [skip] mcpServers.playwright already present');
125
+ process.exit(0);
126
+ }
127
+ s.mcpServers.playwright = {
128
+ command: 'npx',
129
+ args: ['-y', '@playwright/mcp@latest']
130
+ };
131
+ fs.writeFileSync(f, JSON.stringify(s, null, 2) + '\n');
132
+ console.log(' -> wrote .claude/settings.local.json (mcpServers.playwright)');
133
+ NODE_EOF
134
+ }
135
+
136
+ inject_opencode_mcp() {
137
+ local dir="$PROJECT_DIR/.opencode"
138
+ if [ ! -d "$dir" ]; then
139
+ echo " [skip] .opencode/ not present (OpenCode not installed)"
140
+ return
141
+ fi
142
+
143
+ if ! command -v node >/dev/null 2>&1; then
144
+ echo " [warn] node not in PATH; cannot edit JSONC safely"
145
+ return
146
+ fi
147
+
148
+ node - <<'NODE_EOF'
149
+ const fs = require('fs');
150
+ const path = require('path');
151
+ const f = path.join(process.cwd(), '.opencode', 'opencode.jsonc');
152
+ let raw = '';
153
+ if (fs.existsSync(f)) {
154
+ raw = fs.readFileSync(f, 'utf8');
155
+ if (/"playwright"\s*:/.test(raw)) {
156
+ console.log(' [skip] mcp.playwright already present in opencode.jsonc');
157
+ process.exit(0);
158
+ }
159
+ } else {
160
+ raw = '// OpenCode config - JDI managed\n{\n "$schema": "https://opencode.ai/config.json"\n}\n';
161
+ }
162
+ const stripped = raw.replace(/^\s*\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
163
+ let obj;
164
+ try { obj = JSON.parse(stripped); }
165
+ catch { console.log(' [warn] opencode.jsonc not parseable; skip'); process.exit(0); }
166
+ obj.mcp = obj.mcp || {};
167
+ obj.mcp.playwright = {
168
+ type: 'local',
169
+ command: ['npx', '-y', '@playwright/mcp@latest'],
170
+ enabled: true
171
+ };
172
+ const header = '// OpenCode config - JDI managed (mcp.playwright managed; rest is yours)\n';
173
+ fs.writeFileSync(f, header + JSON.stringify(obj, null, 2) + '\n');
174
+ console.log(' -> wrote .opencode/opencode.jsonc (mcp.playwright)');
175
+ NODE_EOF
176
+ }
177
+
178
+ inject_copilot_mcp() {
179
+ local vscode_dir="$PROJECT_DIR/.vscode"
180
+ local f="$vscode_dir/mcp.json"
181
+
182
+ if [ -f "$f" ] && grep -q '"playwright"' "$f" 2>/dev/null; then
183
+ echo " [skip] servers.playwright already present in .vscode/mcp.json"
184
+ return
185
+ fi
186
+
187
+ mkdir -p "$vscode_dir"
188
+
189
+ if ! command -v node >/dev/null 2>&1; then
190
+ echo " [warn] node not in PATH; cannot edit JSON safely"
191
+ return
192
+ fi
193
+
194
+ node - <<'NODE_EOF'
195
+ const fs = require('fs');
196
+ const path = require('path');
197
+ const f = path.join(process.cwd(), '.vscode', 'mcp.json');
198
+ let s = {};
199
+ if (fs.existsSync(f)) {
200
+ try { s = JSON.parse(fs.readFileSync(f, 'utf8')); }
201
+ catch { console.log(' [warn] mcp.json invalid JSON; skip'); process.exit(0); }
202
+ }
203
+ s.servers = s.servers || {};
204
+ if (s.servers.playwright) {
205
+ console.log(' [skip] servers.playwright already present');
206
+ process.exit(0);
207
+ }
208
+ s.servers.playwright = { command: 'npx', args: ['-y', '@playwright/mcp@latest'] };
209
+ fs.writeFileSync(f, JSON.stringify(s, null, 2) + '\n');
210
+ console.log(' -> wrote .vscode/mcp.json (servers.playwright) for Copilot');
211
+ NODE_EOF
212
+ }
213
+
214
+ inject_antigravity_mcp() {
215
+ local base
216
+ if [ "$AG_SCOPE" = "user" ]; then
217
+ base="$USER_HOME/.gemini"
218
+ else
219
+ base="$PROJECT_DIR/.gemini"
220
+ fi
221
+ local f="$base/settings.json"
222
+
223
+ if [ -f "$f" ] && grep -q '"playwright"' "$f" 2>/dev/null; then
224
+ echo " [skip] mcpServers.playwright already present in $f"
225
+ return
226
+ fi
227
+
228
+ mkdir -p "$base"
229
+
230
+ if ! command -v node >/dev/null 2>&1; then
231
+ echo " [warn] node not in PATH; cannot edit JSON safely"
232
+ return
233
+ fi
234
+
235
+ AG_FILE="$f" node - <<'NODE_EOF'
236
+ const fs = require('fs');
237
+ const f = process.env.AG_FILE;
238
+ let s = {};
239
+ if (fs.existsSync(f)) {
240
+ try { s = JSON.parse(fs.readFileSync(f, 'utf8')); }
241
+ catch { console.log(' [warn] settings.json invalid JSON; skip'); process.exit(0); }
242
+ }
243
+ s.mcpServers = s.mcpServers || {};
244
+ if (s.mcpServers.playwright) {
245
+ console.log(' [skip] mcpServers.playwright already present');
246
+ process.exit(0);
247
+ }
248
+ s.mcpServers.playwright = { command: 'npx', args: ['-y', '@playwright/mcp@latest'] };
249
+ fs.writeFileSync(f, JSON.stringify(s, null, 2) + '\n');
250
+ console.log(' -> wrote ' + f + ' (mcpServers.playwright) for Antigravity');
251
+ NODE_EOF
252
+ }
253
+
254
+ # =====================================================================
255
+ # Main
256
+ # =====================================================================
257
+
258
+ echo ""
259
+ echo "=== JDI: Install Playwright + MCP ==="
260
+ echo ""
261
+
262
+ echo "Step 1: Install dep"
263
+ install_pw_dep || { echo "Dep install failed. Aborting."; exit 1; }
264
+
265
+ echo ""
266
+ echo "Step 2: Install browser"
267
+ install_chromium
268
+
269
+ if [ "$SKIP_MCP" != "1" ]; then
270
+ echo ""
271
+ echo "Step 3: Inject MCP config in detected runtimes"
272
+ if [ "$RUNTIME" = "claude" ] || [ "$RUNTIME" = "all" ]; then inject_claude_mcp; fi
273
+ if [ "$RUNTIME" = "opencode" ] || [ "$RUNTIME" = "all" ]; then inject_opencode_mcp; fi
274
+ if [ "$RUNTIME" = "copilot" ] || [ "$RUNTIME" = "all" ]; then inject_copilot_mcp; fi
275
+ if [ "$RUNTIME" = "antigravity" ] || [ "$RUNTIME" = "all" ]; then inject_antigravity_mcp; fi
276
+ fi
277
+
278
+ echo ""
279
+ echo "Done. Restart your runtime to pick up MCP changes."
280
+ echo " - Claude Code: close + reopen, OR run /mcp to verify"
281
+ echo " - OpenCode: opencode reload"
282
+ echo " - Copilot (VS Code): reload window, palette 'MCP: List Servers'"
283
+ echo " - Antigravity: restart antigravity"
284
+ echo ""