diff --git a/images/Dockerfile b/images/Dockerfile index 3e2613e..c65a838 100644 --- a/images/Dockerfile +++ b/images/Dockerfile @@ -233,6 +233,52 @@ ARG AGENT_INSTALL_CACHEBUST= # Claude Code official installer. RUN curl -fsSL https://claude.ai/install.sh | bash +# Claude Code LSP plugins (C/C++, Python, TypeScript, Go) baked in so +# in-VM Claude has code intelligence on first launch. Ports the four +# language servers the original `setup` installed (claude-vm.sh:348-362): +# clangd-lsp / pyright-lsp / typescript-lsp / gopls-lsp from the +# claude-plugins-official marketplace. +# +# Marketplace registration first: `claude plugin marketplace add` clones +# https://github.com/anthropics/claude-plugins-official.git into Claude's +# plugin state, then each `claude plugin install @` +# resolves against it. The bare repo URL is what the original git-cloned +# by hand; `marketplace add` lets the CLI manage the clone + index for us. +# +# Best-effort per the chrome-mcp pre-warm style above: a transient +# registry/clone blip during the hourly build must not fail the whole +# image. Each step is guarded with `|| echo skipped`; a missing plugin +# costs in-VM code intelligence for that language, not a broken image — +# Claude still boots and the user can re-run `claude plugin install`. +# +# Must come AFTER the Claude install above — `claude` didn't exist before +# it. node/npm (from the nodejs RUN) and git (from the base apt RUN) are +# both already present, which the marketplace clone + plugin resolution +# need. +# +# Invoke claude by ABSOLUTE PATH (/root/.local/bin/claude): the installer +# drops the binary there but the only ENV PATH in this file is +# /usr/local/cargo/bin:$PATH, and Docker RUN uses a non-login /bin/sh that +# never sources the shell profile the installer edits. A bare `claude` +# would hit command-not-found (rc 127), every `|| echo skipped` guard would +# fire, and the RUN would still exit 0 — baking a GREEN image with ZERO LSP +# plugins. The absolute path matches the `--version` sanity check below and +# the OpenCode block's /root/.opencode/bin/opencode. +# +# A `claude plugin list` runs after the loop so the build log shows which +# plugins actually landed — without it the best-effort guards would hide a +# total no-op behind a clean build. If `marketplace add` ever rejects the +# `owner/repo` shorthand, swap in the explicit URL +# https://github.com/anthropics/claude-plugins-official.git. +RUN { /root/.local/bin/claude plugin marketplace add anthropics/claude-plugins-official \ + || echo "==> LSP marketplace add skipped (will lazy-add at first plugin use)"; } \ + && for lsp in clangd-lsp pyright-lsp typescript-lsp gopls-lsp; do \ + /root/.local/bin/claude plugin install "${lsp}@claude-plugins-official" \ + || echo "==> LSP plugin ${lsp} skipped (install at runtime if needed)"; \ + done \ + && echo "==> LSP plugins installed at build time:" \ + && { /root/.local/bin/claude plugin list || true; } + # OpenCode official installer. Installs into ~/.opencode/bin; PATH already # includes it. Symlink into /usr/local/bin so non-login shells find it too. RUN curl -fsSL https://opencode.ai/install | bash \