diff --git a/databricks-skills/databricks-app-python/1-authorization.md b/databricks-skills/databricks-apps-python/1-authorization.md similarity index 100% rename from databricks-skills/databricks-app-python/1-authorization.md rename to databricks-skills/databricks-apps-python/1-authorization.md diff --git a/databricks-skills/databricks-app-python/2-app-resources.md b/databricks-skills/databricks-apps-python/2-app-resources.md similarity index 100% rename from databricks-skills/databricks-app-python/2-app-resources.md rename to databricks-skills/databricks-apps-python/2-app-resources.md diff --git a/databricks-skills/databricks-app-python/3-frameworks.md b/databricks-skills/databricks-apps-python/3-frameworks.md similarity index 100% rename from databricks-skills/databricks-app-python/3-frameworks.md rename to databricks-skills/databricks-apps-python/3-frameworks.md diff --git a/databricks-skills/databricks-app-python/4-deployment.md b/databricks-skills/databricks-apps-python/4-deployment.md similarity index 100% rename from databricks-skills/databricks-app-python/4-deployment.md rename to databricks-skills/databricks-apps-python/4-deployment.md diff --git a/databricks-skills/databricks-app-python/5-lakebase.md b/databricks-skills/databricks-apps-python/5-lakebase.md similarity index 100% rename from databricks-skills/databricks-app-python/5-lakebase.md rename to databricks-skills/databricks-apps-python/5-lakebase.md diff --git a/databricks-skills/databricks-app-python/6-mcp-approach.md b/databricks-skills/databricks-apps-python/6-mcp-approach.md similarity index 100% rename from databricks-skills/databricks-app-python/6-mcp-approach.md rename to databricks-skills/databricks-apps-python/6-mcp-approach.md diff --git a/databricks-skills/databricks-app-python/SKILL.md b/databricks-skills/databricks-apps-python/SKILL.md similarity index 77% rename from databricks-skills/databricks-app-python/SKILL.md rename to databricks-skills/databricks-apps-python/SKILL.md index 777d3377..161dbd81 100644 --- a/databricks-skills/databricks-app-python/SKILL.md +++ b/databricks-skills/databricks-apps-python/SKILL.md @@ -1,24 +1,71 @@ --- -name: databricks-app-python -description: "Builds Python-based Databricks applications using Dash, Streamlit, Gradio, Flask, FastAPI, or Reflex. Handles OAuth authorization (app and user auth), app resources, SQL warehouse and Lakebase connectivity, model serving integration, foundation model APIs, LLM integration, and deployment. Use when building Python web apps, dashboards, ML demos, or REST APIs for Databricks, or when the user mentions Streamlit, Dash, Gradio, Flask, FastAPI, Reflex, or Databricks app." +name: databricks-apps-python +description: "Builds Databricks applications. Prefers AppKit (TypeScript + React SDK) for new apps; falls back to Python frameworks (Dash, Streamlit, Gradio, Flask, FastAPI, Reflex) when Python is required. Handles OAuth authorization, app resources, SQL warehouse and Lakebase connectivity, model serving, foundation model APIs, and deployment. Use when building web apps, dashboards, ML demos, or REST APIs for Databricks, or when the user mentions AppKit, Streamlit, Dash, Gradio, Flask, FastAPI, Reflex, or Databricks app." --- -# Databricks Python Application +# Databricks Applications Build Python-based Databricks applications. For full examples and recipes, see the **[Databricks Apps Cookbook](https://apps-cookbook.dev/)**. --- -## Critical Rules (always follow) +## AppKit (Preferred for New Apps) -- **MUST** confirm framework choice or use [Framework Selection](#framework-selection) below +**[AppKit](https://github.com/databricks/appkit)** is the recommended SDK for new Databricks apps. It is a TypeScript + React SDK with a plugin architecture, built-in caching, telemetry, and end-to-end type safety. + +### Requirements +- Node.js v22+ +- Databricks CLI v0.295.0+ + +### Scaffold a new app +```bash +databricks apps init +``` +This interactive command scaffolds the full project, installs dependencies, and optionally deploys. + +### Deploy +```bash +databricks apps deploy +``` + +### AppKit plugins +| Plugin | Purpose | +|--------|---------| +| **Analytics** | SQL queries against Databricks SQL Warehouses — file-based, typed, cached | +| **Genie** | Conversational AI/BI interface with natural language queries | +| **Files** | Browse/upload Unity Catalog Volumes | +| **Lakebase** | OLTP PostgreSQL via Lakebase with OAuth token management | + +### AI-assisted development +```bash +# Install agent skills for AI-powered scaffolding +databricks experimental aitools skills install + +# Query AppKit docs inline +npx @databricks/appkit docs "your question here" +``` + +### AppKit documentation +- **[AppKit Docs](https://databricks.github.io/appkit/docs/)** — getting started, plugins, API reference +- **[AI-assisted development](https://databricks.github.io/appkit/docs/development/ai-assisted-development)** — guidance for code assistants +- **[llms.txt](https://databricks.github.io/appkit/llms.txt)** — machine-readable docs for AI context + +--- + +## Python Apps (alternative) + +Use Python when: the team is Python-only, you need Streamlit/Dash/Gradio/Gradio, or you are extending an existing Python app. + +## Critical Rules for Python apps (always follow) + +- **MUST** confirm framework choice or use [Python Framework Selection](#python-framework-selection) below - **MUST** use SDK `Config()` for authentication (never hardcode tokens) - **MUST** use `app.yaml` `valueFrom` for resources (never hardcode resource IDs) - **MUST** use `dash-bootstrap-components` for Dash app layout and styling - **MUST** use `@st.cache_resource` for Streamlit database connections - **MUST** deploy Flask with Gunicorn, FastAPI with uvicorn (not dev servers) -## Required Steps +## Required Steps for Python apps Copy this checklist and verify each item: ``` @@ -31,7 +78,7 @@ Copy this checklist and verify each item: --- -## Framework Selection +## Python Framework Selection | Framework | Best For | app.yaml Command | |-----------|----------|------------------| @@ -82,7 +129,7 @@ Copy this checklist and verify each item: 1. Determine the task type: - **New app from scratch?** → Use [Framework Selection](#framework-selection), then read [3-frameworks.md](3-frameworks.md) + **New app from scratch?** → Use [AppKit](#appkit-preferred-for-new-apps) (`databricks apps init`). Fall back to [Python Framework Selection](#python-framework-selection) only if Python is required. **Setting up authorization?** → Read [1-authorization.md](1-authorization.md) **Connecting to data/resources?** → Read [2-app-resources.md](2-app-resources.md) **Using Lakebase (PostgreSQL)?** → Read [5-lakebase.md](5-lakebase.md) @@ -195,6 +242,7 @@ class EntityIn(BaseModel): ## Official Documentation +- **[AppKit](https://databricks.github.io/appkit/docs/)** — preferred SDK for new apps (TypeScript + React) - **[Databricks Apps Overview](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/)** — main docs hub - **[Apps Cookbook](https://apps-cookbook.dev/)** — ready-to-use code snippets (Streamlit, Dash, Reflex, FastAPI) - **[Authorization](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/auth)** — app auth and user auth diff --git a/databricks-skills/databricks-app-python/examples/fm-minimal-chat.py b/databricks-skills/databricks-apps-python/examples/fm-minimal-chat.py similarity index 100% rename from databricks-skills/databricks-app-python/examples/fm-minimal-chat.py rename to databricks-skills/databricks-apps-python/examples/fm-minimal-chat.py diff --git a/databricks-skills/databricks-app-python/examples/fm-parallel-calls.py b/databricks-skills/databricks-apps-python/examples/fm-parallel-calls.py similarity index 100% rename from databricks-skills/databricks-app-python/examples/fm-parallel-calls.py rename to databricks-skills/databricks-apps-python/examples/fm-parallel-calls.py diff --git a/databricks-skills/databricks-app-python/examples/fm-structured-outputs.py b/databricks-skills/databricks-apps-python/examples/fm-structured-outputs.py similarity index 100% rename from databricks-skills/databricks-app-python/examples/fm-structured-outputs.py rename to databricks-skills/databricks-apps-python/examples/fm-structured-outputs.py diff --git a/databricks-skills/databricks-app-python/examples/llm_config.py b/databricks-skills/databricks-apps-python/examples/llm_config.py similarity index 100% rename from databricks-skills/databricks-app-python/examples/llm_config.py rename to databricks-skills/databricks-apps-python/examples/llm_config.py diff --git a/install.ps1 b/install.ps1 index a22c8b16..4a4d8878 100644 --- a/install.ps1 +++ b/install.ps1 @@ -79,7 +79,7 @@ $script:ListSkills = $false # Databricks skills (bundled in repo) $script:Skills = @( - "databricks-agent-bricks", "databricks-aibi-dashboards", "databricks-app-python", + "databricks-agent-bricks", "databricks-aibi-dashboards", "databricks-apps-python", "databricks-bundles", "databricks-config", "databricks-dbsql", "databricks-docs", "databricks-genie", "databricks-iceberg", "databricks-jobs", "databricks-lakebase-autoscale", "databricks-lakebase-provisioned", "databricks-metric-views", "databricks-mlflow-evaluation", "databricks-model-serving", "databricks-ai-functions", @@ -100,6 +100,11 @@ $MlflowRawUrl = "https://raw.githubusercontent.com/mlflow/skills/main" $script:ApxSkills = @("databricks-app-apx") $ApxRawUrl = "https://raw.githubusercontent.com/databricks-solutions/apx/main/skills/apx" +# Agent skills (fetched from databricks/databricks-agent-skills repo) +$script:AgentSkills = @("databricks", "databricks-apps", "databricks-lakebase") +$AgentSkillsRawUrl = "https://raw.githubusercontent.com/databricks/databricks-agent-skills/main/skills" +$AgentSkillsApiUrl = "https://api.github.com/repos/databricks/databricks-agent-skills/git/trees/main?recursive=1" + # ─── Skill profiles ────────────────────────────────────────── $script:CoreSkills = @("databricks-config", "databricks-docs", "databricks-python-sdk", "databricks-unity-catalog") @@ -123,15 +128,17 @@ $script:ProfileAiMlMlflow = @( "retrieving-mlflow-traces", "searching-mlflow-docs" ) $script:ProfileAppDeveloper = @( - "databricks-app-python", "databricks-app-apx", "databricks-lakebase-autoscale", + "databricks-apps-python", "databricks-app-apx", "databricks-lakebase-autoscale", "databricks-lakebase-provisioned", "databricks-model-serving", "databricks-dbsql", "databricks-jobs", "databricks-bundles" ) +$script:ProfileAppDeveloperAgent = @("databricks", "databricks-apps", "databricks-lakebase") # Selected skills (populated during profile selection) $script:SelectedSkills = @() $script:SelectedMlflowSkills = @() $script:SelectedApxSkills = @() +$script:SelectedAgentSkills = @() # ─── --list-skills handler ──────────────────────────────────── if ($script:ListSkills) { @@ -175,6 +182,10 @@ if ($script:ListSkills) { Write-Host "--------------------------------" foreach ($s in $script:ApxSkills) { Write-Host " $s" } Write-Host "" + Write-Host "Agent Skills (from databricks/databricks-agent-skills repo)" -ForegroundColor White + Write-Host "--------------------------------" + foreach ($s in $script:AgentSkills) { Write-Host " $($s -replace '^.*:', '')" } + Write-Host "" Write-Host "Usage: .\install.ps1 --skills-profile data-engineer,ai-ml-engineer" -ForegroundColor DarkGray Write-Host " .\install.ps1 --skills databricks-jobs,databricks-dbsql" -ForegroundColor DarkGray Write-Host "" @@ -845,12 +856,15 @@ function Resolve-Skills { $dbSkills = @() + $script:CoreSkills $mlflowSkills = @() $apxSkills = @() + $agentSkills = @() foreach ($skill in $userList) { $skill = $skill.Trim() if ($script:MlflowSkills -contains $skill) { $mlflowSkills += $skill } elseif ($script:ApxSkills -contains $skill) { $apxSkills += $skill + } elseif ($script:AgentSkills | ForEach-Object { $_ -replace '^.*:', '' } | Where-Object { $_ -eq $skill }) { + $agentSkills += ($script:AgentSkills | Where-Object { ($_ -replace '^.*:', '') -eq $skill }) } else { $dbSkills += $skill } @@ -858,6 +872,7 @@ function Resolve-Skills { $script:SelectedSkills = $dbSkills | Select-Object -Unique $script:SelectedMlflowSkills = $mlflowSkills | Select-Object -Unique $script:SelectedApxSkills = $apxSkills | Select-Object -Unique + $script:SelectedAgentSkills = $agentSkills | Select-Object -Unique return } @@ -866,6 +881,7 @@ function Resolve-Skills { $script:SelectedSkills = $script:Skills $script:SelectedMlflowSkills = $script:MlflowSkills $script:SelectedApxSkills = $script:ApxSkills + $script:SelectedAgentSkills = $script:AgentSkills return } @@ -873,6 +889,7 @@ function Resolve-Skills { $dbSkills = @() + $script:CoreSkills $mlflowSkills = @() $apxSkills = @() + $agentSkills = @() foreach ($profile in ($script:SkillsProfile -split ',')) { $profile = $profile.Trim() @@ -881,6 +898,7 @@ function Resolve-Skills { $script:SelectedSkills = $script:Skills $script:SelectedMlflowSkills = $script:MlflowSkills $script:SelectedApxSkills = $script:ApxSkills + $script:SelectedAgentSkills = $script:AgentSkills return } "data-engineer" { $dbSkills += $script:ProfileDataEngineer } @@ -892,6 +910,7 @@ function Resolve-Skills { "app-developer" { $dbSkills += $script:ProfileAppDeveloper $apxSkills += $script:ApxSkills + $agentSkills += $script:ProfileAppDeveloperAgent } default { Write-Warn "Unknown skill profile: $profile (ignored)" } } @@ -900,6 +919,7 @@ function Resolve-Skills { $script:SelectedSkills = $dbSkills | Select-Object -Unique $script:SelectedMlflowSkills = $mlflowSkills | Select-Object -Unique $script:SelectedApxSkills = $apxSkills | Select-Object -Unique + $script:SelectedAgentSkills = $agentSkills | Select-Object -Unique } function Invoke-PromptSkillsProfile { @@ -1090,7 +1110,7 @@ function Invoke-PromptCustomSkills { "data-engineer" { $preselected += $script:ProfileDataEngineer } "analyst" { $preselected += $script:ProfileAnalyst } "ai-ml-engineer" { $preselected += $script:ProfileAiMlEngineer + $script:ProfileAiMlMlflow } - "app-developer" { $preselected += $script:ProfileAppDeveloper + $script:ApxSkills } + "app-developer" { $preselected += $script:ProfileAppDeveloper + $script:ApxSkills + $script:ProfileAppDeveloperAgent } } } @@ -1119,8 +1139,11 @@ function Invoke-PromptCustomSkills { @{ Label = "Synthetic Data"; Value = "databricks-synthetic-data-gen"; State = ($preselected -contains "databricks-synthetic-data-gen"); Hint = "Generate test data" } @{ Label = "Lakebase Autoscale"; Value = "databricks-lakebase-autoscale"; State = ($preselected -contains "databricks-lakebase-autoscale"); Hint = "Managed PostgreSQL" } @{ Label = "Lakebase Provisioned"; Value = "databricks-lakebase-provisioned"; State = ($preselected -contains "databricks-lakebase-provisioned"); Hint = "Provisioned PostgreSQL" } - @{ Label = "App Python"; Value = "databricks-app-python"; State = ($preselected -contains "databricks-app-python"); Hint = "Dash, Streamlit, Flask" } + @{ Label = "App (AppKit + Python)"; Value = "databricks-apps-python"; State = ($preselected -contains "databricks-apps-python"); Hint = "AppKit, Dash, Streamlit, Flask" } @{ Label = "App APX"; Value = "databricks-app-apx"; State = ($preselected -contains "databricks-app-apx"); Hint = "FastAPI + React" } + @{ Label = "Agent: Databricks"; Value = "databricks"; State = ($preselected -contains "databricks"); Hint = "CLI auth, data exploration" } + @{ Label = "Agent: Apps"; Value = "databricks-apps"; State = ($preselected -contains "databricks-apps"); Hint = "AppKit + all frameworks" } + @{ Label = "Agent: Lakebase"; Value = "databricks-lakebase"; State = ($preselected -contains "databricks-lakebase"); Hint = "Lakebase OLTP" } @{ Label = "MLflow Onboarding"; Value = "mlflow-onboarding"; State = ($preselected -contains "mlflow-onboarding"); Hint = "Getting started" } @{ Label = "Agent Evaluation"; Value = "agent-evaluation"; State = ($preselected -contains "agent-evaluation"); Hint = "Evaluate AI agents" } @{ Label = "MLflow Tracing"; Value = "instrumenting-with-mlflow-tracing"; State = ($preselected -contains "instrumenting-with-mlflow-tracing"); Hint = "Instrument with tracing" } @@ -1161,7 +1184,8 @@ function Install-Skills { $dbCount = $script:SelectedSkills.Count $mlflowCount = $script:SelectedMlflowSkills.Count $apxCount = $script:SelectedApxSkills.Count - $totalCount = $dbCount + $mlflowCount + $apxCount + $agentCount = $script:SelectedAgentSkills.Count + $totalCount = $dbCount + $mlflowCount + $apxCount + $agentCount Write-Msg "Installing $totalCount skills" # Build set of all skills being installed now @@ -1169,6 +1193,7 @@ function Install-Skills { $allNewSkills += $script:SelectedSkills $allNewSkills += $script:SelectedMlflowSkills $allNewSkills += $script:SelectedApxSkills + $allNewSkills += $script:SelectedAgentSkills | ForEach-Object { $_ -replace '^.*:', '' } # Clean up previously installed skills that are no longer selected # Check scope-local manifest first, fall back to global for upgrades from older versions @@ -1263,6 +1288,57 @@ function Install-Skills { $ErrorActionPreference = $prevEAP2 Write-Ok "APX skills ($apxCount) -> $shortDir" } + + # Install Agent skills from databricks/databricks-agent-skills repo + if ($script:SelectedAgentSkills.Count -gt 0) { + # Fetch the full repo tree once (single API call) for all skills + $agentTree = $null + try { + $agentTree = Invoke-WebRequest -Uri $AgentSkillsApiUrl -UseBasicParsing -ErrorAction Stop | Select-Object -ExpandProperty Content + } catch { + Write-Warn "Could not fetch agent skills tree from GitHub API" + } + if ($agentTree) { + $prevEAP3 = $ErrorActionPreference; $ErrorActionPreference = "Continue" + foreach ($entry in $script:SelectedAgentSkills) { + $srcName = ($entry -split ':')[0] + $installName = ($entry -replace '^.*:', '') + $destDir = Join-Path $dir $installName + if (-not (Test-Path $destDir)) { + New-Item -ItemType Directory -Path $destDir -Force | Out-Null + } + # Extract all file paths under skills// that contain a dot (i.e. are files not dirs) + $filePaths = [regex]::Matches($agentTree, '"path":"(skills/' + [regex]::Escape($srcName) + '/[^"]*\.[^"]*)"') | + ForEach-Object { $_.Groups[1].Value } + if (-not $filePaths) { + Remove-Item $destDir -ErrorAction SilentlyContinue + Write-Warn "Could not fetch agent skill '$srcName'" + continue + } + $okFlag = $false + foreach ($filePath in $filePaths) { + $rel = $filePath.Substring("skills/$srcName/".Length) + $dest = Join-Path $destDir ($rel -replace '/', '\') + $destParent = Split-Path $dest -Parent + if (-not (Test-Path $destParent)) { + New-Item -ItemType Directory -Path $destParent -Force | Out-Null + } + try { + Invoke-WebRequest -Uri "$AgentSkillsRawUrl/$srcName/$rel" -OutFile $dest -UseBasicParsing -ErrorAction Stop + $okFlag = $true + } catch {} + } + if ($okFlag) { + $manifestEntries += "$dir|$installName" + } else { + Remove-Item -Recurse -Force $destDir -ErrorAction SilentlyContinue + Write-Warn "Could not install agent skill '$srcName'" + } + } + $ErrorActionPreference = $prevEAP3 + } + Write-Ok "Agent skills ($agentCount) -> $shortDir" + } } # Save manifest and profile to scope-local state directory @@ -1822,7 +1898,7 @@ function Invoke-Main { Write-Host " MCP server: " -NoNewline; Write-Host $script:InstallDir -ForegroundColor Green } if ($script:InstallSkills) { - $skTotal = $script:SelectedSkills.Count + $script:SelectedMlflowSkills.Count + $script:SelectedApxSkills.Count + $skTotal = $script:SelectedSkills.Count + $script:SelectedMlflowSkills.Count + $script:SelectedApxSkills.Count + $script:SelectedAgentSkills.Count if (-not [string]::IsNullOrWhiteSpace($script:UserSkills)) { Write-Host " Skills: " -NoNewline; Write-Host "custom selection ($skTotal skills)" -ForegroundColor Green } else { diff --git a/install.sh b/install.sh index adf72b26..093dc00b 100755 --- a/install.sh +++ b/install.sh @@ -88,7 +88,7 @@ MIN_SDK_VERSION="0.85.0" G='\033[0;32m' Y='\033[1;33m' R='\033[0;31m' BL='\033[0;34m' B='\033[1m' D='\033[2m' N='\033[0m' # Databricks skills (bundled in repo) -SKILLS="databricks-agent-bricks databricks-ai-functions databricks-aibi-dashboards databricks-app-python databricks-bundles databricks-config databricks-dbsql databricks-docs databricks-genie databricks-iceberg databricks-jobs databricks-lakebase-autoscale databricks-lakebase-provisioned databricks-metric-views databricks-mlflow-evaluation databricks-model-serving databricks-python-sdk databricks-spark-declarative-pipelines databricks-spark-structured-streaming databricks-synthetic-data-gen databricks-unity-catalog databricks-unstructured-pdf-generation databricks-vector-search databricks-zerobus-ingest spark-python-data-source" +SKILLS="databricks-agent-bricks databricks-ai-functions databricks-aibi-dashboards databricks-apps-python databricks-bundles databricks-config databricks-dbsql databricks-docs databricks-genie databricks-iceberg databricks-jobs databricks-lakebase-autoscale databricks-lakebase-provisioned databricks-metric-views databricks-mlflow-evaluation databricks-model-serving databricks-python-sdk databricks-spark-declarative-pipelines databricks-spark-structured-streaming databricks-synthetic-data-gen databricks-unity-catalog databricks-unstructured-pdf-generation databricks-vector-search databricks-zerobus-ingest spark-python-data-source" # MLflow skills (fetched from mlflow/skills repo) MLFLOW_SKILLS="agent-evaluation analyze-mlflow-chat-session analyze-mlflow-trace instrumenting-with-mlflow-tracing mlflow-onboarding querying-mlflow-metrics retrieving-mlflow-traces searching-mlflow-docs" @@ -98,6 +98,11 @@ MLFLOW_RAW_URL="https://raw.githubusercontent.com/mlflow/skills/main" APX_SKILLS="databricks-app-apx" APX_RAW_URL="https://raw.githubusercontent.com/databricks-solutions/apx/main/skills/apx" +# Agent skills (fetched from databricks/databricks-agent-skills repo) +AGENT_SKILLS="databricks databricks-apps databricks-lakebase" +AGENT_SKILLS_RAW_URL="https://raw.githubusercontent.com/databricks/databricks-agent-skills/main/skills" +AGENT_SKILLS_API_URL="https://api.github.com/repos/databricks/databricks-agent-skills/git/trees/main?recursive=1" + # ─── Skill profiles ────────────────────────────────────────── # Core skills always installed regardless of profile selection CORE_SKILLS="databricks-config databricks-docs databricks-python-sdk databricks-unity-catalog" @@ -107,12 +112,14 @@ PROFILE_DATA_ENGINEER="databricks-spark-declarative-pipelines databricks-spark-s PROFILE_ANALYST="databricks-aibi-dashboards databricks-dbsql databricks-genie databricks-metric-views" PROFILE_AIML_ENGINEER="databricks-agent-bricks databricks-ai-functions databricks-vector-search databricks-model-serving databricks-genie databricks-unstructured-pdf-generation databricks-mlflow-evaluation databricks-synthetic-data-gen databricks-jobs" PROFILE_AIML_MLFLOW="agent-evaluation analyze-mlflow-chat-session analyze-mlflow-trace instrumenting-with-mlflow-tracing mlflow-onboarding querying-mlflow-metrics retrieving-mlflow-traces searching-mlflow-docs" -PROFILE_APP_DEVELOPER="databricks-app-python databricks-app-apx databricks-lakebase-autoscale databricks-lakebase-provisioned databricks-model-serving databricks-dbsql databricks-jobs databricks-bundles" +PROFILE_APP_DEVELOPER="databricks-apps-python databricks-app-apx databricks-lakebase-autoscale databricks-lakebase-provisioned databricks-model-serving databricks-dbsql databricks-jobs databricks-bundles" +PROFILE_APP_DEVELOPER_AGENT="databricks databricks-apps databricks-lakebase" # Selected skills (populated during profile selection) SELECTED_SKILLS="" SELECTED_MLFLOW_SKILLS="" SELECTED_APX_SKILLS="" +SELECTED_AGENT_SKILLS="" # Output helpers msg() { [ "$SILENT" = true ] || echo -e " $*"; } @@ -183,7 +190,7 @@ if [ "${LIST_SKILLS:-false}" = true ]; then echo -e "${B}Available Skill Profiles${N}" echo "────────────────────────────────" echo "" - echo -e " ${B}all${N} All 34 skills (default)" + echo -e " ${B}all${N} All 37 skills (default)" echo -e " ${B}data-engineer${N} Pipelines, Spark, Jobs, Streaming (14 skills)" echo -e " ${B}analyst${N} Dashboards, SQL, Genie, Metrics (8 skills)" echo -e " ${B}ai-ml-engineer${N} Agents, RAG, Vector Search, MLflow (17 skills)" @@ -235,6 +242,12 @@ if [ "${LIST_SKILLS:-false}" = true ]; then echo -e " $skill" done echo "" + echo -e "${B}Agent Skills${N} (from databricks/databricks-agent-skills repo)" + echo "────────────────────────────────" + for entry in $AGENT_SKILLS; do + echo -e " ${entry#*:}" + done + echo "" echo -e "${D}Usage: bash install.sh --skills-profile data-engineer,ai-ml-engineer${N}" echo -e "${D} bash install.sh --skills databricks-jobs,databricks-dbsql${N}" echo "" @@ -655,19 +668,21 @@ prompt_mcp_path() { # ─── Skill profile selection ────────────────────────────────── # Resolve selected skills from profile names or explicit skill list resolve_skills() { - local db_skills="" mlflow_skills="" apx_skills="" + local db_skills="" mlflow_skills="" apx_skills="" agent_skills="" # Priority 1: Explicit --skills flag (comma-separated skill names) if [ -n "$USER_SKILLS" ]; then local user_list user_list=$(echo "$USER_SKILLS" | tr ',' ' ') - # Separate into DB, MLflow, and APX buckets, always include core + # Separate into DB, MLflow, APX, and Agent buckets, always include core db_skills="$CORE_SKILLS" for skill in $user_list; do if echo "$MLFLOW_SKILLS" | grep -qw "$skill"; then mlflow_skills="${mlflow_skills:+$mlflow_skills }$skill" elif echo "$APX_SKILLS" | grep -qw "$skill"; then apx_skills="${apx_skills:+$apx_skills }$skill" + elif echo "$AGENT_SKILLS" | tr ' ' '\n' | sed 's/.*://' | grep -qw "$skill"; then + agent_skills="${agent_skills:+$agent_skills }$(echo "$AGENT_SKILLS" | tr ' ' '\n' | grep -w ".*:${skill}\|^${skill}$")" else db_skills="${db_skills:+$db_skills }$skill" fi @@ -676,6 +691,7 @@ resolve_skills() { SELECTED_SKILLS=$(echo "$db_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') SELECTED_MLFLOW_SKILLS=$(echo "$mlflow_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') SELECTED_APX_SKILLS=$(echo "$apx_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') + SELECTED_AGENT_SKILLS=$(echo "$agent_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') return fi @@ -684,6 +700,7 @@ resolve_skills() { SELECTED_SKILLS="$SKILLS" SELECTED_MLFLOW_SKILLS="$MLFLOW_SKILLS" SELECTED_APX_SKILLS="$APX_SKILLS" + SELECTED_AGENT_SKILLS="$AGENT_SKILLS" return fi @@ -691,6 +708,7 @@ resolve_skills() { db_skills="$CORE_SKILLS" mlflow_skills="" apx_skills="" + agent_skills="" local profiles profiles=$(echo "$SKILLS_PROFILE" | tr ',' ' ') @@ -700,6 +718,7 @@ resolve_skills() { SELECTED_SKILLS="$SKILLS" SELECTED_MLFLOW_SKILLS="$MLFLOW_SKILLS" SELECTED_APX_SKILLS="$APX_SKILLS" + SELECTED_AGENT_SKILLS="$AGENT_SKILLS" return ;; data-engineer) @@ -715,6 +734,7 @@ resolve_skills() { app-developer) db_skills="$db_skills $PROFILE_APP_DEVELOPER" apx_skills="$apx_skills $APX_SKILLS" + agent_skills="$agent_skills $PROFILE_APP_DEVELOPER_AGENT" ;; *) warn "Unknown skill profile: $profile (ignored)" @@ -726,6 +746,7 @@ resolve_skills() { SELECTED_SKILLS=$(echo "$db_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') SELECTED_MLFLOW_SKILLS=$(echo "$mlflow_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') SELECTED_APX_SKILLS=$(echo "$apx_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') + SELECTED_AGENT_SKILLS=$(echo "$agent_skills" | tr ' ' '\n' | sort -u | tr '\n' ' ') } # Interactive skill profile selection (multi-select) @@ -883,7 +904,7 @@ prompt_custom_skills() { data-engineer) preselected="$preselected $PROFILE_DATA_ENGINEER" ;; analyst) preselected="$preselected $PROFILE_ANALYST" ;; ai-ml-engineer) preselected="$preselected $PROFILE_AIML_ENGINEER $PROFILE_AIML_MLFLOW" ;; - app-developer) preselected="$preselected $PROFILE_APP_DEVELOPER $APX_SKILLS" ;; + app-developer) preselected="$preselected $PROFILE_APP_DEVELOPER $APX_SKILLS $PROFILE_APP_DEVELOPER_AGENT" ;; esac done @@ -917,8 +938,11 @@ prompt_custom_skills() { "Synthetic Data|databricks-synthetic-data-gen|$(_is_preselected databricks-synthetic-data-gen)|Generate test data" \ "Lakebase Autoscale|databricks-lakebase-autoscale|$(_is_preselected databricks-lakebase-autoscale)|Managed PostgreSQL" \ "Lakebase Provisioned|databricks-lakebase-provisioned|$(_is_preselected databricks-lakebase-provisioned)|Provisioned PostgreSQL" \ - "App Python|databricks-app-python|$(_is_preselected databricks-app-python)|Dash, Streamlit, Flask" \ + "App (AppKit + Python)|databricks-apps-python|$(_is_preselected databricks-apps-python)|AppKit, Dash, Streamlit, Flask" \ "App APX|databricks-app-apx|$(_is_preselected databricks-app-apx)|FastAPI + React" \ + "Agent: Databricks|databricks|$(_is_preselected databricks)|CLI auth, data exploration" \ + "Agent: Apps|databricks-apps|$(_is_preselected databricks-apps)|AppKit + all frameworks" \ + "Agent: Lakebase|databricks-lakebase|$(_is_preselected databricks-lakebase)|Lakebase OLTP" \ "MLflow Onboarding|mlflow-onboarding|$(_is_preselected mlflow-onboarding)|Getting started" \ "Agent Evaluation|agent-evaluation|$(_is_preselected agent-evaluation)|Evaluate AI agents" \ "MLflow Tracing|instrumenting-with-mlflow-tracing|$(_is_preselected instrumenting-with-mlflow-tracing)|Instrument with tracing" \ @@ -1102,15 +1126,18 @@ install_skills() { dirs=("${unique[@]}") # Count selected skills for display - local db_count=0 mlflow_count=0 apx_count=0 + local db_count=0 mlflow_count=0 apx_count=0 agent_count=0 for _ in $SELECTED_SKILLS; do db_count=$((db_count + 1)); done for _ in $SELECTED_MLFLOW_SKILLS; do mlflow_count=$((mlflow_count + 1)); done for _ in $SELECTED_APX_SKILLS; do apx_count=$((apx_count + 1)); done - local total_count=$((db_count + mlflow_count + apx_count)) + for _ in $SELECTED_AGENT_SKILLS; do agent_count=$((agent_count + 1)); done + local total_count=$((db_count + mlflow_count + apx_count + agent_count)) msg "Installing ${B}${total_count}${N} skills" # Build set of all skills being installed now - local all_new_skills="$SELECTED_SKILLS $SELECTED_MLFLOW_SKILLS $SELECTED_APX_SKILLS" + local agent_install_names + agent_install_names=$(echo "$SELECTED_AGENT_SKILLS" | tr ' ' '\n' | sed 's/.*://' | tr '\n' ' ') + local all_new_skills="$SELECTED_SKILLS $SELECTED_MLFLOW_SKILLS $SELECTED_APX_SKILLS $agent_install_names" # Clean up previously installed skills that are no longer selected # Check scope-local manifest first, fall back to global for upgrades from older versions @@ -1185,6 +1212,45 @@ install_skills() { done ok "APX skills ($apx_count) → ${dir#$HOME/}" fi + + # Install Agent skills from databricks/databricks-agent-skills repo + if [ -n "$SELECTED_AGENT_SKILLS" ]; then + # Fetch the full repo tree once (single API call) for all skills + local agent_tree + agent_tree=$(curl -fsSL "$AGENT_SKILLS_API_URL" 2>/dev/null) + for entry in $SELECTED_AGENT_SKILLS; do + local src_name="${entry%%:*}" + local install_name="${entry#*:}" + local dest_dir="$dir/$install_name" + mkdir -p "$dest_dir" + # Extract all file paths under skills// from the tree + local files + files=$(echo "$agent_tree" \ + | grep -o '"path":"skills/'"$src_name"'/[^"]*"' \ + | grep '\.' \ + | sed 's/"path":"//;s/"$//') + if [ -z "$files" ]; then + rmdir "$dest_dir" 2>/dev/null || true + warn "Could not fetch agent skill '$src_name'" + continue + fi + local ok_flag=0 + while IFS= read -r filepath; do + [ -z "$filepath" ] && continue + local rel="${filepath#skills/$src_name/}" + local dest="$dest_dir/$rel" + mkdir -p "$(dirname "$dest")" + curl -fsSL "$AGENT_SKILLS_RAW_URL/$src_name/${rel}" -o "$dest" 2>/dev/null && ok_flag=1 || true + done <<< "$files" + if [ "$ok_flag" -eq 1 ]; then + echo "$dir|$install_name" >> "$manifest.tmp" + else + rm -rf "$dest_dir" + warn "Could not install agent skill '$src_name'" + fi + done + ok "Agent skills ($agent_count) → ${dir#$HOME/}" + fi done # Save manifest of installed skills (for cleanup on profile change) @@ -1698,7 +1764,7 @@ main() { echo -e " Skills: ${G}custom selection${N}" else local sk_total=0 - for _ in $SELECTED_SKILLS $SELECTED_MLFLOW_SKILLS $SELECTED_APX_SKILLS; do sk_total=$((sk_total + 1)); done + for _ in $SELECTED_SKILLS $SELECTED_MLFLOW_SKILLS $SELECTED_APX_SKILLS $SELECTED_AGENT_SKILLS; do sk_total=$((sk_total + 1)); done echo -e " Skills: ${G}${SKILLS_PROFILE:-all} ($sk_total skills)${N}" fi fi