diff --git a/install.ps1 b/install.ps1 index e4b4ab67..530be3f2 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,7 +1,7 @@ # # Databricks AI Dev Kit - Unified Installer (Windows) # -# Installs skills, MCP server, and configuration for Claude Code, Cursor, OpenAI Codex, GitHub Copilot, Gemini CLI, and Antigravity. +# Installs skills, MCP server, and configuration for Claude Code, Cursor, OpenAI Codex, GitHub Copilot, Gemini CLI, Antigravity and OpenCode. # # Usage: irm https://raw.githubusercontent.com/databricks-solutions/ai-dev-kit/main/install.ps1 -OutFile install.ps1 # .\install.ps1 [OPTIONS] @@ -233,7 +233,7 @@ while ($i -lt $args.Count) { Write-Host " --mcp-only Skip skills installation" Write-Host " --mcp-path PATH Path to MCP server installation" Write-Host " --silent Silent mode (no output except errors)" - Write-Host " --tools LIST Comma-separated: claude,cursor,copilot,codex,gemini,antigravity" + Write-Host " --tools LIST Comma-separated: claude,cursor,copilot,codex,gemini,antigravity,opencode" Write-Host " --skills-profile LIST Comma-separated profiles: all,data-engineer,analyst,ai-ml-engineer,app-developer" Write-Host " --skills LIST Comma-separated skill names to install (overrides profile)" Write-Host " --list-skills List available skills and profiles, then exit" @@ -569,6 +569,7 @@ function Invoke-DetectTools { $hasGemini = $null -ne (Get-Command gemini -ErrorAction SilentlyContinue) $hasAntigravity = ($null -ne (Get-Command antigravity -ErrorAction SilentlyContinue)) -or (Test-Path "$env:LOCALAPPDATA\Programs\Antigravity\Antigravity.exe") + $hasOpencode = $null -ne (Get-Command opencode -ErrorAction SilentlyContinue) $claudeState = $hasClaude; $claudeHint = if ($hasClaude) { "detected" } else { "not found" } $cursorState = $hasCursor; $cursorHint = if ($hasCursor) { "detected" } else { "not found" } @@ -576,9 +577,10 @@ function Invoke-DetectTools { $copilotState = $hasCopilot; $copilotHint = if ($hasCopilot) { "detected" } else { "not found" } $geminiState = $hasGemini; $geminiHint = if ($hasGemini) { "detected" } else { "not found" } $antigravityState = $hasAntigravity; $antigravityHint = if ($hasAntigravity) { "detected" } else { "not found" } + $opencodeState = $hasOpencode; $opencodeHint = if ($hasOpencode) { "detected" } else { "not found" } # If nothing detected, default to claude - if (-not $hasClaude -and -not $hasCursor -and -not $hasCodex -and -not $hasCopilot -and -not $hasGemini -and -not $hasAntigravity) { + if (-not $hasClaude -and -not $hasCursor -and -not $hasCodex -and -not $hasCopilot -and -not $hasGemini -and -not $hasAntigravity -and -not $hasOpencode) { $claudeState = $true $claudeHint = "default" } @@ -595,6 +597,7 @@ function Invoke-DetectTools { @{ Label = "OpenAI Codex"; Value = "codex"; State = $codexState; Hint = $codexHint } @{ Label = "Gemini CLI"; Value = "gemini"; State = $geminiState; Hint = $geminiHint } @{ Label = "Antigravity"; Value = "antigravity"; State = $antigravityState; Hint = $antigravityHint } + @{ Label = "OpenCode"; Value = "opencode"; State = $opencodeState; Hint = $opencodeHint } ) $result = Select-Checkbox -Items $items @@ -1163,6 +1166,13 @@ function Install-Skills { } else { $dirs += Join-Path $BaseDir ".agents\skills" } + } + "opencode" { + if ($script:Scope -eq "global") { + $dirs += Join-Path $env:USERPROFILE ".config\opencode\skills" + } else { + $dirs += Join-Path $BaseDir ".opencode\skills" + } } } } @@ -1517,6 +1527,60 @@ Try asking: "List my SQL warehouses" or "Show my Unity Catalog schemas" Write-Ok "GEMINI.md" } +function Write-OpenCodeMcpJson { + param([string]$Path) + + $dir = Split-Path $Path -Parent + if (-not (Test-Path $dir)) { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + } + + # Backup existing + if (Test-Path $Path) { + Copy-Item $Path "$Path.bak" -Force + Write-Msg "Backed up $(Split-Path $Path -Leaf) -> $(Split-Path $Path -Leaf).bak" + } + + # Try to merge with existing config + if ((Test-Path $Path) -and (Test-Path $script:VenvPython)) { + try { + $existing = Get-Content $Path -Raw | ConvertFrom-Json + } catch { + $existing = $null + } + } + + if ($existing) { + if (-not $existing.mcp) { + $existing | Add-Member -NotePropertyName "mcp" -NotePropertyValue ([PSCustomObject]@{}) -Force + } + $dbEntry = [PSCustomObject]@{ + type = "local" + command = @($script:VenvPython -replace '\\', '/') + @($script:McpEntry -replace '\\', '/') + environment = [PSCustomObject]@{ DATABRICKS_CONFIG_PROFILE = $script:Profile_ } + enabled = $true + } + $existing.mcp | Add-Member -NotePropertyName "databricks" -NotePropertyValue $dbEntry -Force + $existing = $existing | ConvertTo-Json -Depth 10 + $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False + [System.IO.File]::WriteAllLines($Path, $existing, $Utf8NoBomEncoding) + } else { + $config = [PSCustomObject]@{ + '$schema' = 'https://opencode.ai/config.json' + mcp = @{ + databricks = @{ + type = 'local' + command = @($script:VenvPython -replace '\\', '/') + @($script:McpEntry -replace '\\', '/') + environment = [PSCustomObject]@{ DATABRICKS_CONFIG_PROFILE = $script:Profile_ } + enabled = $true + } + } + } | ConvertTo-Json -Depth 3 + $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False + [System.IO.File]::WriteAllLines($Path, $config, $Utf8NoBomEncoding) + } +} + function Write-McpConfigs { param([string]$BaseDir) @@ -1589,6 +1653,15 @@ function Write-McpConfigs { Write-GeminiMcpJson (Join-Path $env:USERPROFILE ".gemini\antigravity\mcp_config.json") Write-Ok "Antigravity MCP config" } + "opencode" { + if ($script:Scope -eq "global") { + Write-OpenCodeMcpJson (Join-Path $env:USERPROFILE ".config\opencode\opencode.json") + } else { + Write-OpenCodeMcpJson (Join-Path $BaseDir "opencode.json") + + } + Write-Ok "OpenCode CLI MCP config" + } } } } @@ -1643,6 +1716,10 @@ function Show-Summary { if ($script:Tools -match 'antigravity') { Write-Msg "$step. Open your project in Antigravity to use Databricks skills and MCP tools" $step++ + } + if ($script:Tools -match 'opencode') { + Write-Msg "$step. Open your project in Opencode to use Databricks skills and MCP tools" + $step++ } Write-Msg "$step. Open your project in your tool of choice" $step++ diff --git a/install.sh b/install.sh index 114cb2c4..b5798939 100644 --- a/install.sh +++ b/install.sh @@ -503,6 +503,7 @@ detect_tools() { local has_copilot=false local has_gemini=false local has_antigravity=false + local has_opencode=false command -v claude >/dev/null 2>&1 && has_claude=true { [ -d "/Applications/Cursor.app" ] || command -v cursor >/dev/null 2>&1; } && has_cursor=true @@ -510,19 +511,21 @@ detect_tools() { { [ -d "/Applications/Visual Studio Code.app" ] || command -v code >/dev/null 2>&1; } && has_copilot=true { command -v gemini >/dev/null 2>&1 || [ -f "$HOME/.gemini/local/gemini" ]; } && has_gemini=true { [ -d "/Applications/Antigravity.app" ] || command -v antigravity >/dev/null 2>&1; } && has_antigravity=true + command -v opencode >/dev/null 2>&1 && has_opencode=true # Build checkbox items: "Label|value|on_or_off|hint" - local claude_state="off" cursor_state="off" codex_state="off" copilot_state="off" gemini_state="off" antigravity_state="off" - local claude_hint="not found" cursor_hint="not found" codex_hint="not found" copilot_hint="not found" gemini_hint="not found" antigravity_hint="not found" + local claude_state="off" cursor_state="off" codex_state="off" copilot_state="off" gemini_state="off" antigravity_state="off" opencode_state="off" + local claude_hint="not found" cursor_hint="not found" codex_hint="not found" copilot_hint="not found" gemini_hint="not found" antigravity_hint="not found" opencode_hint="not found" [ "$has_claude" = true ] && claude_state="on" && claude_hint="detected" [ "$has_cursor" = true ] && cursor_state="on" && cursor_hint="detected" [ "$has_codex" = true ] && codex_state="on" && codex_hint="detected" [ "$has_copilot" = true ] && copilot_state="on" && copilot_hint="detected" [ "$has_gemini" = true ] && gemini_state="on" && gemini_hint="detected" [ "$has_antigravity" = true ] && antigravity_state="on" && antigravity_hint="detected" + [ "$has_opencode" = true ] && opencode_state="on" && opencode_hint="detected" # If nothing detected, pre-select claude as default - if [ "$has_claude" = false ] && [ "$has_cursor" = false ] && [ "$has_codex" = false ] && [ "$has_copilot" = false ] && [ "$has_gemini" = false ] && [ "$has_antigravity" = false ]; then + if [ "$has_claude" = false ] && [ "$has_cursor" = false ] && [ "$has_codex" = false ] && [ "$has_copilot" = false ] && [ "$has_gemini" = false ] && [ "$has_antigravity" = false ] && [ "$has_opencode" = false ]; then claude_state="on" claude_hint="default" fi @@ -539,6 +542,7 @@ detect_tools() { "OpenAI Codex|codex|${codex_state}|${codex_hint}" \ "Gemini CLI|gemini|${gemini_state}|${gemini_hint}" \ "Antigravity|antigravity|${antigravity_state}|${antigravity_hint}" \ + "Opencode|opencode|${opencode_state}|${opencode_hint}" \ ) else # Silent: use detected defaults @@ -549,6 +553,7 @@ detect_tools() { [ "$has_codex" = true ] && tools="${tools:+$tools }codex" [ "$has_gemini" = true ] && tools="${tools:+$tools }gemini" [ "$has_antigravity" = true ] && tools="${tools:+$tools }antigravity" + [ "$has_opencode" = true ] && tools="${tools:+$tools }opencode" [ -z "$tools" ] && tools="claude" TOOLS="$tools" fi @@ -1095,6 +1100,13 @@ install_skills() { else dirs+=("$base_dir/.agents/skills") fi + ;; + opencode) + if [ "$SCOPE" = "global" ]; then + dirs+=("$HOME/.config/opencode/skills") + else + dirs+=("$base_dir/.opencode/skills") + fi ;; esac done @@ -1280,6 +1292,42 @@ with open('$path', 'w') as f: json.dump(cfg, f, indent=2); f.write('\n') EOF } +write_opencode_mcp_json() { + local path=$1 + mkdir -p "$(dirname "$path")" + + # Backup existing file before any modifications + if [ -f "$path" ]; then + cp "$path" "${path}.bak" + msg "${D}Backed up ${path##*/} → ${path##*/}.bak${N}" + fi + + if [ -f "$path" ] && [ -f "$VENV_PYTHON" ]; then + "$VENV_PYTHON" -c " +import json, sys +try: + with open('$path') as f: cfg = json.load(f) +except: cfg = {'\$schema' : 'https://opencode.ai/config.json'} +cfg.setdefault('mcp', {})['databricks'] = {'type':'local', 'command': ['$VENV_PYTHON','$MCP_ENTRY'], 'enabled': True, 'environment': {'DATABRICKS_CONFIG_PROFILE': '$PROFILE'}} +with open('$path', 'w') as f: json.dump(cfg, f, indent=2); f.write('\n') +" 2>/dev/null && return + fi + + cat > "$path" << EOF +{ + "\$schema" : "https://opencode.ai/config.json", + "mcp": { + "databricks": { + "type": "local", + "command": ["$VENV_PYTHON","$MCP_ENTRY"], + "enabled": true, + "environment": {"DATABRICKS_CONFIG_PROFILE": "$PROFILE"} + } + } +} +EOF +} + write_mcp_toml() { local path=$1 mkdir -p "$(dirname "$path")" @@ -1485,6 +1533,14 @@ write_mcp_configs() { fi write_gemini_mcp_json "$HOME/.gemini/antigravity/mcp_config.json" ok "Antigravity MCP config" + ;; + opencode) + if [ "$SCOPE" = "global" ]; then + write_opencode_mcp_json "$HOME/.config/opencode/opencode.json" + else + write_opencode_mcp_json "$base_dir/opencode.json" + fi + ok "Opencode CLI MCP config" ;; esac done @@ -1532,6 +1588,10 @@ summary() { if echo "$TOOLS" | grep -q antigravity; then msg "${step}. Open your project in Antigravity to use Databricks skills and MCP tools" step=$((step + 1)) + fi + if echo "$TOOLS" | grep -q onpencode; then + msg "${step}. Open your project in Opencode to use Databricks skills and MCP tools" + step=$((step + 1)) fi msg "${step}. Open your project in your tool of choice" step=$((step + 1))