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,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 ""
|