From f6c432d21d6a942f1928f3780fe59c42340e4d05 Mon Sep 17 00:00:00 2001 From: Octoturge Date: Sat, 15 Aug 2026 11:41:09 +0200 Subject: [PATCH] Install Agent Skills into all 3 AI CLIs, and use Bun instead of npm Adds scripts/install-skills.sh: pulls this repo's extensions/ skill bundles (SKILL.md-format) at workspace startup and installs them into Claude Code (~/.claude/skills), GitHub Copilot CLI (~/.copilot/skills), and Antigravity CLI (~/.gemini/config/skills). Every env gets the common awesome-skills-plugin bundle; COBOL/3D/TTRPG additionally get their matching skill(s) from custom-specialty-plugin via a per-template SPECIALTY_SKILLS value. Each template also installs Bun via a coder_script and hooks ~/.bun/bin onto PATH in .bashrc (the bun.sh installer doesn't reliably do this in a scripted shell). The CLI setup wizard now uses `bun install -g` instead of `npm install -g` for GitHub Copilot CLI and Claude Code CLI. All six templates re-validated with terraform init/validate against the real coder-server container on octo-winsrv. Co-Authored-By: Claude Sonnet 5 --- README.md | 55 ++++++++++++++++++++-------- scripts/cli-setup-wizard.sh | 19 +++++----- scripts/install-skills.sh | 68 +++++++++++++++++++++++++++++++++++ templates/3d-printing/main.tf | 45 +++++++++++++++++++++++ templates/cobol/main.tf | 45 +++++++++++++++++++++++ templates/default/main.tf | 46 ++++++++++++++++++++++++ templates/python/main.tf | 46 ++++++++++++++++++++++++ templates/ttrpg/main.tf | 46 ++++++++++++++++++++++++ templates/web/main.tf | 46 ++++++++++++++++++++++++ 9 files changed, 393 insertions(+), 23 deletions(-) create mode 100644 scripts/install-skills.sh diff --git a/README.md b/README.md index b77e88e..5f26074 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ templates/ web/main.tf # Web Applications scripts/ cli-setup-wizard.sh # shared first-run wizard, see below + install-skills.sh # shared Agent Skills installer, see below profile-templates/ # VS Code .code-profile exports, one per env -extensions/ # Claude Code plugin/skill bundles (see note below) +extensions/ # Agent Skills bundles, installed per env (see below) ``` Each `templates//main.tf` is a complete, independent Coder template @@ -57,30 +58,56 @@ the actual `.code-profile` file extension, so settings never applied. That bug (plus the single shared container) is why "one container, many envs" never really worked. +### Bun + +Every template installs [Bun](https://bun.sh) via a `coder_script` +(`curl -fsSL https://bun.sh/install | bash`) and hooks `~/.bun/bin` onto +`PATH` in `~/.bashrc` (the installer doesn't reliably do this itself in a +non-interactive/scripted shell). The CLI setup wizard below uses +`bun install -g ` instead of `npm install -g ` for everything it +installs. + ### CLI setup wizard `scripts/cli-setup-wizard.sh` is dropped onto every workspace and hooked into `~/.bashrc`. It runs in every new interactive terminal - until the user finishes it - and offers to install + log into: -- **GitHub Copilot CLI** (`npm install -g @github/copilot`, then `copilot login`) +- **GitHub Copilot CLI** (`bun install -g @github/copilot`, then `copilot login`) - **Google Antigravity CLI** (`curl -fsSL https://antigravity.google/cli/install.sh | bash`, binary `agy`) -- **Claude Code CLI** (`npm install -g @anthropic-ai/claude-code`, then `claude`) +- **Claude Code CLI** (`bun install -g @anthropic-ai/claude-code`, then `claude`) It does **not** ask about VS Code extensions, since those are handled by Terraform (see above). Once the user confirms completion it writes a sentinel file (`~/.cache/coder-cli-wizard/done`) and stops prompting. It can always be re-run manually: `bash /opt/coder/cli-setup-wizard.sh --force`. -### `extensions/` directory +### `extensions/` directory -> Agent Skills -Heads up: `extensions/awesome-skills-plugin` and `extensions/custom-specialty-plugin` -are **Claude Code plugin/skill bundles** (`plugin.json` + `SKILL.md` files), -not actual VS Code extension packages. The old root `main.tf` copied this -folder straight into code-server's extensions directory, which would never -have worked as VS Code extensions regardless of the container bug above. -This rewrite doesn't wire `extensions/` into anything - it's left as-is -pending a decision on whether/how to install these as Claude Code plugins -inside each workspace (e.g. `custom-specialty-plugin/skills/cobol-teacher` -for the COBOL env, `openscad-parametric` for 3D, `ttrpg-lore-weaver` / -`foundryvtt-modding` for TTRPG). +`extensions/awesome-skills-plugin` and `extensions/custom-specialty-plugin` +are **Agent Skills bundles** (`plugin.json` + `SKILL.md` files) - the old +root `main.tf` copied this folder straight into code-server's VS Code +extensions directory, which never worked since these aren't VS Code +extension packages. + +Each template now runs `scripts/install-skills.sh` (via a `coder_script`, +pulling a fresh zip of this repo from Gitea rather than embedding ~2.5MB +into Terraform state) to install skills into all three AI CLIs' personal +skills directories: + +| CLI | Skills directory | +| --- | --- | +| Claude Code | `~/.claude/skills//` | +| GitHub Copilot CLI | `~/.copilot/skills//` | +| Antigravity CLI | `~/.gemini/config/skills//` (per antigravity.google/docs/skills - some third-party docs disagree on this path, worth a spot-check on a live workspace) | + +Every environment gets the full `extensions/awesome-skills-plugin/skills/*` +bundle. On top of that, whichever env has a matching entry in +`extensions/custom-specialty-plugin/skills/` gets it installed too, set via +the `SPECIALTY_SKILLS` env var passed to the script from each template's +`install_skills` `coder_script`: + +- COBOL -> `cobol-teacher` +- 3D Printing -> `openscad-parametric` +- TTRPG -> `foundryvtt-modding` and `ttrpg-lore-weaver` +- Default / Python / Web -> none (no matching specialty skill exists yet) diff --git a/scripts/cli-setup-wizard.sh b/scripts/cli-setup-wizard.sh index 8a94d62..20fc6ef 100644 --- a/scripts/cli-setup-wizard.sh +++ b/scripts/cli-setup-wizard.sh @@ -19,7 +19,8 @@ WIZARD_DONE_FILE="${HOME}/.cache/coder-cli-wizard/done" FORCE=0 [ "${1:-}" = "--force" ] && FORCE=1 -export PATH="${HOME}/.local/bin:${PATH}" +export BUN_INSTALL="${HOME}/.bun" +export PATH="${BUN_INSTALL}/bin:${HOME}/.local/bin:${PATH}" # Only bother interactive shells with a real terminal attached, and only until # the user marks the wizard as done. @@ -53,16 +54,16 @@ echo "===================================================================" # --- GitHub Copilot CLI --- if command -v copilot >/dev/null 2>&1; then echo "GitHub Copilot CLI already installed, skipping." -elif command -v npm >/dev/null 2>&1; then +elif command -v bun >/dev/null 2>&1; then if ask_yes_no "Install GitHub Copilot CLI and log in?"; then - if npm install -g @github/copilot; then + if bun install -g @github/copilot; then copilot login || echo "Install succeeded but login didn't complete. Retry any time with: copilot login" else - echo "Copilot CLI install failed. Retry later with: npm install -g @github/copilot && copilot login" + echo "Copilot CLI install failed. Retry later with: bun install -g @github/copilot && copilot login" fi fi else - echo "Skipping GitHub Copilot CLI: npm not found on this workspace image." + echo "Skipping GitHub Copilot CLI: bun not found on this workspace image." fi # --- Google Antigravity CLI (agy) --- @@ -82,17 +83,17 @@ fi # --- Claude Code CLI --- if command -v claude >/dev/null 2>&1; then echo "Claude Code CLI already installed, skipping." -elif command -v npm >/dev/null 2>&1; then +elif command -v bun >/dev/null 2>&1; then if ask_yes_no "Install Claude Code CLI and log in?"; then - if npm install -g @anthropic-ai/claude-code; then + if bun install -g @anthropic-ai/claude-code; then echo "Launching 'claude' once to complete sign-in (use /login if not prompted; Ctrl+C to exit when done)..." claude || echo "Sign-in didn't complete. Retry any time by running: claude" else - echo "Claude Code CLI install failed. Retry later with: npm install -g @anthropic-ai/claude-code" + echo "Claude Code CLI install failed. Retry later with: bun install -g @anthropic-ai/claude-code" fi fi else - echo "Skipping Claude Code CLI: npm not found on this workspace image." + echo "Skipping Claude Code CLI: bun not found on this workspace image." fi echo "" diff --git a/scripts/install-skills.sh b/scripts/install-skills.sh new file mode 100644 index 0000000..f7d82cd --- /dev/null +++ b/scripts/install-skills.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Installs this repo's Agent Skills (extensions/{awesome-skills-plugin, +# custom-specialty-plugin}/skills/*, each a SKILL.md-based skill directory) +# into every AI CLI's personal skills directory: +# +# Claude Code CLI -> ~/.claude/skills// +# GitHub Copilot CLI -> ~/.copilot/skills// +# Antigravity CLI -> ~/.gemini/config/skills// (per antigravity.google/docs/skills; +# worth a spot-check if agy doesn't pick these up, some third-party +# docs disagree on the exact path) +# +# Run once at workspace startup via coder_script. Pulls this repo fresh from +# Gitea rather than embedding ~2.5MB of skill files into Terraform state. +# +# Env vars: +# SPECIALTY_SKILLS - optional space-separated skill names from +# extensions/custom-specialty-plugin/skills/ to install in addition to +# the common awesome-skills-plugin bundle (every env gets that one). + +set -e + +REPO_ZIP_URL="https://git.octoturge.com/octoturge/Profiles-for-Coder/archive/main.zip" +ZIP_PATH="/tmp/coder-skills-src.zip" +WORK_DIR="/tmp/coder-skills-src" + +rm -rf "$WORK_DIR" "$ZIP_PATH" +curl -fsSL "$REPO_ZIP_URL" -o "$ZIP_PATH" +mkdir -p "$WORK_DIR" +unzip -q -o "$ZIP_PATH" -d "$WORK_DIR" + +INNER_DIR=$(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -type d | head -n1) +if [ -z "$INNER_DIR" ]; then + echo "install-skills: couldn't find extracted repo contents, skipping." >&2 + rm -rf "$WORK_DIR" "$ZIP_PATH" + exit 0 +fi + +TARGET_DIRS=("$HOME/.claude/skills" "$HOME/.copilot/skills" "$HOME/.gemini/config/skills") +for dir in "${TARGET_DIRS[@]}"; do + mkdir -p "$dir" +done + +# Common skill bundle, installed for every environment. +COMMON_SKILLS_SRC="$INNER_DIR/extensions/awesome-skills-plugin/skills" +if [ -d "$COMMON_SKILLS_SRC" ]; then + for dir in "${TARGET_DIRS[@]}"; do + cp -r "$COMMON_SKILLS_SRC/." "$dir/" + done + echo "install-skills: installed common skill bundle into ${TARGET_DIRS[*]}" +else + echo "install-skills: common skill bundle not found at $COMMON_SKILLS_SRC, skipping." >&2 +fi + +# Environment-specific specialty skills, if any were requested. +for skill in ${SPECIALTY_SKILLS:-}; do + SRC="$INNER_DIR/extensions/custom-specialty-plugin/skills/$skill" + if [ -d "$SRC" ]; then + for dir in "${TARGET_DIRS[@]}"; do + cp -r "$SRC" "$dir/$skill" + done + echo "install-skills: installed specialty skill '$skill'" + else + echo "install-skills: specialty skill '$skill' not found at $SRC, skipping." >&2 + fi +done + +rm -rf "$WORK_DIR" "$ZIP_PATH" +echo "install-skills: done." diff --git a/templates/3d-printing/main.tf b/templates/3d-printing/main.tf index a447d34..ea74af2 100644 --- a/templates/3d-printing/main.tf +++ b/templates/3d-printing/main.tf @@ -267,3 +267,48 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories, plus the openscad-parametric skill +# from extensions/custom-specialty-plugin. See ../../scripts/install-skills.sh. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="openscad-parametric" /opt/coder/install-skills.sh + EOT +} diff --git a/templates/cobol/main.tf b/templates/cobol/main.tf index 5fc274d..f9ee846 100644 --- a/templates/cobol/main.tf +++ b/templates/cobol/main.tf @@ -267,3 +267,48 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories, plus the cobol-teacher skill from +# extensions/custom-specialty-plugin. See ../../scripts/install-skills.sh. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="cobol-teacher" /opt/coder/install-skills.sh + EOT +} diff --git a/templates/default/main.tf b/templates/default/main.tf index 88fda2e..5fe56ae 100644 --- a/templates/default/main.tf +++ b/templates/default/main.tf @@ -267,3 +267,49 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories. See ../../scripts/install-skills.sh. +# Default has no matching entry in extensions/custom-specialty-plugin/skills, +# so it only gets the common awesome-skills-plugin bundle. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="" /opt/coder/install-skills.sh + EOT +} diff --git a/templates/python/main.tf b/templates/python/main.tf index 880be95..6caf374 100644 --- a/templates/python/main.tf +++ b/templates/python/main.tf @@ -267,3 +267,49 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories. See ../../scripts/install-skills.sh. +# Python has no matching entry in extensions/custom-specialty-plugin/skills, +# so it only gets the common awesome-skills-plugin bundle. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="" /opt/coder/install-skills.sh + EOT +} diff --git a/templates/ttrpg/main.tf b/templates/ttrpg/main.tf index f8b8ad4..b8fac8b 100644 --- a/templates/ttrpg/main.tf +++ b/templates/ttrpg/main.tf @@ -267,3 +267,49 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories, plus the foundryvtt-modding and +# ttrpg-lore-weaver skills from extensions/custom-specialty-plugin. +# See ../../scripts/install-skills.sh. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="foundryvtt-modding ttrpg-lore-weaver" /opt/coder/install-skills.sh + EOT +} diff --git a/templates/web/main.tf b/templates/web/main.tf index d05f2ae..991cd99 100644 --- a/templates/web/main.tf +++ b/templates/web/main.tf @@ -267,3 +267,49 @@ resource "coder_script" "cli_setup_wizard" { fi EOT } + +# Installs Bun and uses it (instead of npm) for the CLI installs the wizard +# script runs. The installer doesn't reliably add ~/.bun/bin to PATH in +# non-interactive shells, so that's hooked into .bashrc explicitly here. +resource "coder_script" "install_bun" { + agent_id = coder_agent.main.id + display_name = "Install Bun" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + export BUN_INSTALL="$HOME/.bun" + if [ ! -x "$BUN_INSTALL/bin/bun" ]; then + curl -fsSL https://bun.sh/install | bash + fi + + MARKER="# >>> coder bun path >>>" + if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then + { + echo "" + echo "$MARKER" + echo 'export BUN_INSTALL="$HOME/.bun"' + echo 'export PATH="$BUN_INSTALL/bin:$PATH"' + echo "# <<< coder bun path <<<" + } >> "$HOME/.bashrc" + fi + EOT +} + +# Installs this repo's Agent Skills into Claude Code, GitHub Copilot CLI, and +# Antigravity CLI's skills directories. See ../../scripts/install-skills.sh. +# Web has no matching entry in extensions/custom-specialty-plugin/skills, +# so it only gets the common awesome-skills-plugin bundle. +resource "coder_script" "install_skills" { + agent_id = coder_agent.main.id + display_name = "Install Agent Skills" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -e + mkdir -p /opt/coder + echo '${base64encode(file("${path.module}/../../scripts/install-skills.sh"))}' | base64 -d > /opt/coder/install-skills.sh + chmod +x /opt/coder/install-skills.sh + SPECIALTY_SKILLS="" /opt/coder/install-skills.sh + EOT +}