From 71b5e661f92e07dd9025380075448126a671b794 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Mon, 5 Jan 2026 11:27:45 -0800 Subject: [PATCH 1/5] feat(features): add devcontainer feature for AI coding agents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current behavior: N/A - new feature New behavior: Adds a devcontainer feature that installs Claude Code, Gemini CLI, and Codex CLI via npm. The feature supports: - Enable/disable each agent independently - Pin specific versions or use "latest" - Soft dependency on Node.js feature via installsAfter The feature is published to ghcr.io/gilmanlab/features/agents and is dogfooded in the project's own devcontainer configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .devcontainer/devcontainer.json | 3 +- .github/workflows/features.yml | 91 +++++++++++++++++ images/features/agents/README.md | 84 ++++++++++++++++ .../features/agents/devcontainer-feature.json | 52 ++++++++++ images/features/agents/install.sh | 98 +++++++++++++++++++ images/features/agents/test/scenarios.json | 19 ++++ .../features/agents/test/test-claude-only.sh | 28 ++++++ images/features/agents/test/test.sh | 28 ++++++ 8 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/features.yml create mode 100644 images/features/agents/README.md create mode 100644 images/features/agents/devcontainer-feature.json create mode 100755 images/features/agents/install.sh create mode 100644 images/features/agents/test/scenarios.json create mode 100755 images/features/agents/test/test-claude-only.sh create mode 100755 images/features/agents/test/test.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9b1000b..c153a22 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,8 @@ "dockerDashComposeVersion": "v2" }, "ghcr.io/dhoeric/features/hadolint:1": {}, - "ghcr.io/guiyomh/features/just:0": {} + "ghcr.io/guiyomh/features/just:0": {}, + "ghcr.io/gilmanlab/features/agents:1": {} }, "postCreateCommand": ".devcontainer/post-create.sh", "customizations": { diff --git a/.github/workflows/features.yml b/.github/workflows/features.yml new file mode 100644 index 0000000..2894c11 --- /dev/null +++ b/.github/workflows/features.yml @@ -0,0 +1,91 @@ +name: Features + +on: + pull_request: + paths: + - 'images/features/**' + - '.github/workflows/features.yml' + push: + branches: + - master + paths: + - 'images/features/**' + - '.github/workflows/features.yml' + tags: + - 'features/*/v*' + +env: + REGISTRY: ghcr.io + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Validate devcontainer-feature.json + run: | + for feature_dir in images/features/*/; do + if [ -f "${feature_dir}devcontainer-feature.json" ]; then + echo "Validating ${feature_dir}devcontainer-feature.json" + jq empty "${feature_dir}devcontainer-feature.json" + fi + done + + - name: Lint install.sh scripts + uses: koalaman/shellcheck-action@3b8df25f1665b9e4d42bab8a72ef426e6c6e3d63 # v1.1 + with: + scandir: 'images/features' + + test: + needs: lint + runs-on: ubuntu-latest + strategy: + matrix: + feature: [agents] + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '22' + + - name: Install devcontainer CLI + run: npm install -g @devcontainers/cli + + - name: Test feature + run: | + devcontainer features test \ + --features ./images/features/${{ matrix.feature }} \ + --base-image mcr.microsoft.com/devcontainers/base:ubuntu \ + --project-folder ./images/features + + publish: + needs: [lint, test] + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/features/')) + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Log in to Container Registry + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish Features + uses: devcontainers/action@v1 + with: + publish-features: true + base-path-to-features: ./images/features + generate-docs: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/images/features/agents/README.md b/images/features/agents/README.md new file mode 100644 index 0000000..7ac7b2e --- /dev/null +++ b/images/features/agents/README.md @@ -0,0 +1,84 @@ +# AI Coding Agents (agents) + +Installs AI coding agent CLIs: Claude Code, Gemini CLI, and Codex CLI. + +## Usage + +Add this feature to your `devcontainer.json`: + +```json +{ + "features": { + "ghcr.io/gilmanlab/features/agents:1": {} + } +} +``` + +**Note:** This feature requires Node.js/npm. Add the Node.js feature if not already present: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/node:1": {}, + "ghcr.io/gilmanlab/features/agents:1": {} + } +} +``` + +## Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `claudeCode` | boolean | `true` | Install Claude Code CLI | +| `claudeCodeVersion` | string | `latest` | Version of Claude Code | +| `geminiCli` | boolean | `true` | Install Gemini CLI | +| `geminiCliVersion` | string | `latest` | Version of Gemini CLI | +| `codexCli` | boolean | `true` | Install Codex CLI | +| `codexCliVersion` | string | `latest` | Version of Codex CLI | + +## Examples + +### Install all agents (default) + +```json +{ + "features": { + "ghcr.io/gilmanlab/features/agents:1": {} + } +} +``` + +### Install only Claude Code + +```json +{ + "features": { + "ghcr.io/gilmanlab/features/agents:1": { + "geminiCli": false, + "codexCli": false + } + } +} +``` + +### Install specific versions + +```json +{ + "features": { + "ghcr.io/gilmanlab/features/agents:1": { + "claudeCodeVersion": "2.0.76", + "geminiCliVersion": "0.22.5", + "codexCliVersion": "0.77.0" + } + } +} +``` + +## Installed CLIs + +| CLI | Package | Binary | +|-----|---------|--------| +| Claude Code | `@anthropic-ai/claude-code` | `claude` | +| Gemini CLI | `@google/gemini-cli` | `gemini` | +| Codex CLI | `@openai/codex` | `codex` | diff --git a/images/features/agents/devcontainer-feature.json b/images/features/agents/devcontainer-feature.json new file mode 100644 index 0000000..df08e2a --- /dev/null +++ b/images/features/agents/devcontainer-feature.json @@ -0,0 +1,52 @@ +{ + "id": "agents", + "version": "1.0.0", + "name": "AI Coding Agents", + "description": "Installs AI coding agent CLIs: Claude Code, Gemini CLI, and Codex CLI", + "documentationURL": "https://github.com/GilmanLab/headjack/tree/master/images/features/agents", + "keywords": [ + "ai", + "agents", + "claude", + "gemini", + "codex", + "anthropic", + "google", + "openai" + ], + "options": { + "claudeCode": { + "type": "boolean", + "default": true, + "description": "Install Claude Code CLI (@anthropic-ai/claude-code)" + }, + "claudeCodeVersion": { + "type": "string", + "default": "latest", + "description": "Version of Claude Code to install" + }, + "geminiCli": { + "type": "boolean", + "default": true, + "description": "Install Gemini CLI (@google/gemini-cli)" + }, + "geminiCliVersion": { + "type": "string", + "default": "latest", + "description": "Version of Gemini CLI to install" + }, + "codexCli": { + "type": "boolean", + "default": true, + "description": "Install Codex CLI (@openai/codex)" + }, + "codexCliVersion": { + "type": "string", + "default": "latest", + "description": "Version of Codex CLI to install" + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/node" + ] +} diff --git a/images/features/agents/install.sh b/images/features/agents/install.sh new file mode 100755 index 0000000..543c151 --- /dev/null +++ b/images/features/agents/install.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# AI Coding Agents Feature - install.sh +# Installs Claude Code, Gemini CLI, and Codex CLI via npm +set -e + +# Feature options (devcontainer converts option names to uppercase) +CLAUDE_CODE="${CLAUDECODE:-true}" +CLAUDE_CODE_VERSION="${CLAUDECODEVERSION:-latest}" +GEMINI_CLI="${GEMINICLI:-true}" +GEMINI_CLI_VERSION="${GEMINICLIVERSION:-latest}" +CODEX_CLI="${CODEXCLI:-true}" +CODEX_CLI_VERSION="${CODEXCLIVERSION:-latest}" + +echo "Installing AI Coding Agent CLIs..." + +# Check if npm is available +if ! command -v npm > /dev/null 2>&1; then + echo "Error: npm is not available. Please ensure Node.js is installed." + echo "Add the Node.js feature before this feature:" + echo ' "ghcr.io/devcontainers/features/node:1": {}' + exit 1 +fi + +echo "Using npm version: $(npm --version)" +echo "Using Node.js version: $(node --version)" + +# Build list of packages to install +PACKAGES="" + +if [ "${CLAUDE_CODE}" = "true" ]; then + if [ "${CLAUDE_CODE_VERSION}" = "latest" ]; then + PACKAGES="${PACKAGES} @anthropic-ai/claude-code" + else + PACKAGES="${PACKAGES} @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" + fi + echo "Will install Claude Code: ${CLAUDE_CODE_VERSION}" +fi + +if [ "${GEMINI_CLI}" = "true" ]; then + if [ "${GEMINI_CLI_VERSION}" = "latest" ]; then + PACKAGES="${PACKAGES} @google/gemini-cli" + else + PACKAGES="${PACKAGES} @google/gemini-cli@${GEMINI_CLI_VERSION}" + fi + echo "Will install Gemini CLI: ${GEMINI_CLI_VERSION}" +fi + +if [ "${CODEX_CLI}" = "true" ]; then + if [ "${CODEX_CLI_VERSION}" = "latest" ]; then + PACKAGES="${PACKAGES} @openai/codex" + else + PACKAGES="${PACKAGES} @openai/codex@${CODEX_CLI_VERSION}" + fi + echo "Will install Codex CLI: ${CODEX_CLI_VERSION}" +fi + +# Install packages if any are enabled +if [ -n "${PACKAGES}" ]; then + echo "Installing:${PACKAGES}" + # shellcheck disable=SC2086 + npm install -g ${PACKAGES} + echo "Installation complete." +else + echo "No agents selected for installation." +fi + +# Verify installations +echo "" +echo "Verification:" +if [ "${CLAUDE_CODE}" = "true" ]; then + if command -v claude > /dev/null 2>&1; then + echo " Claude Code: installed" + else + echo " Claude Code: FAILED" + exit 1 + fi +fi + +if [ "${GEMINI_CLI}" = "true" ]; then + if command -v gemini > /dev/null 2>&1; then + echo " Gemini CLI: installed" + else + echo " Gemini CLI: FAILED" + exit 1 + fi +fi + +if [ "${CODEX_CLI}" = "true" ]; then + if command -v codex > /dev/null 2>&1; then + echo " Codex CLI: installed" + else + echo " Codex CLI: FAILED" + exit 1 + fi +fi + +echo "" +echo "AI Coding Agent CLIs installation finished." diff --git a/images/features/agents/test/scenarios.json b/images/features/agents/test/scenarios.json new file mode 100644 index 0000000..761ef18 --- /dev/null +++ b/images/features/agents/test/scenarios.json @@ -0,0 +1,19 @@ +{ + "test-default": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/node:1": {}, + "agents": {} + } + }, + "test-claude-only": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/node:1": {}, + "agents": { + "geminiCli": false, + "codexCli": false + } + } + } +} diff --git a/images/features/agents/test/test-claude-only.sh b/images/features/agents/test/test-claude-only.sh new file mode 100755 index 0000000..b4cc425 --- /dev/null +++ b/images/features/agents/test/test-claude-only.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Test script for claude-only scenario +set -e + +echo "Testing Claude-only configuration..." + +# Verify Claude Code is installed +if ! command -v claude > /dev/null 2>&1; then + echo "FAIL: claude command not found" + exit 1 +fi +echo "PASS: claude command found" + +# Verify Gemini CLI is NOT installed +if command -v gemini > /dev/null 2>&1; then + echo "FAIL: gemini command should not be installed" + exit 1 +fi +echo "PASS: gemini command correctly not installed" + +# Verify Codex CLI is NOT installed +if command -v codex > /dev/null 2>&1; then + echo "FAIL: codex command should not be installed" + exit 1 +fi +echo "PASS: codex command correctly not installed" + +echo "All tests passed!" diff --git a/images/features/agents/test/test.sh b/images/features/agents/test/test.sh new file mode 100755 index 0000000..0a3443b --- /dev/null +++ b/images/features/agents/test/test.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Test script for default scenario (all agents installed) +set -e + +echo "Testing AI Coding Agents feature..." + +# Verify Claude Code +if ! command -v claude > /dev/null 2>&1; then + echo "FAIL: claude command not found" + exit 1 +fi +echo "PASS: claude command found" + +# Verify Gemini CLI +if ! command -v gemini > /dev/null 2>&1; then + echo "FAIL: gemini command not found" + exit 1 +fi +echo "PASS: gemini command found" + +# Verify Codex CLI +if ! command -v codex > /dev/null 2>&1; then + echo "FAIL: codex command not found" + exit 1 +fi +echo "PASS: codex command found" + +echo "All tests passed!" From 826d6925ee947cdd08309483ec07d60daed26e37 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Mon, 5 Jan 2026 11:29:59 -0800 Subject: [PATCH 2/5] fix(ci): use shellcheck directly instead of action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current behavior: The koalaman/shellcheck-action reference was not resolvable, causing the lint job to fail. New behavior: Use shellcheck directly via find command. Ubuntu runners have shellcheck pre-installed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/features.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/features.yml b/.github/workflows/features.yml index 2894c11..cab2909 100644 --- a/.github/workflows/features.yml +++ b/.github/workflows/features.yml @@ -34,9 +34,8 @@ jobs: done - name: Lint install.sh scripts - uses: koalaman/shellcheck-action@3b8df25f1665b9e4d42bab8a72ef426e6c6e3d63 # v1.1 - with: - scandir: 'images/features' + run: | + find images/features -name "*.sh" -type f -exec shellcheck {} + test: needs: lint From af8a61eab96dce70e99efdc5483fd370ffafe842 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Mon, 5 Jan 2026 11:32:45 -0800 Subject: [PATCH 3/5] fix(features): restructure to match devcontainer CLI expectations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current behavior: The devcontainer features test command failed because it expected src/ and test/ directories at the project root. New behavior: Restructured to use the expected layout: - images/features/src/agents/ - feature source files - images/features/test/agents/ - test scenarios and scripts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/features.yml | 8 ++++---- images/features/{ => src}/agents/README.md | 0 .../features/{ => src}/agents/devcontainer-feature.json | 0 images/features/{ => src}/agents/install.sh | 0 .../features/{agents/test => test/agents}/scenarios.json | 0 .../{agents/test => test/agents}/test-claude-only.sh | 0 images/features/{agents/test => test/agents}/test.sh | 0 7 files changed, 4 insertions(+), 4 deletions(-) rename images/features/{ => src}/agents/README.md (100%) rename images/features/{ => src}/agents/devcontainer-feature.json (100%) rename images/features/{ => src}/agents/install.sh (100%) rename images/features/{agents/test => test/agents}/scenarios.json (100%) rename images/features/{agents/test => test/agents}/test-claude-only.sh (100%) rename images/features/{agents/test => test/agents}/test.sh (100%) diff --git a/.github/workflows/features.yml b/.github/workflows/features.yml index cab2909..2471c37 100644 --- a/.github/workflows/features.yml +++ b/.github/workflows/features.yml @@ -26,7 +26,7 @@ jobs: - name: Validate devcontainer-feature.json run: | - for feature_dir in images/features/*/; do + for feature_dir in images/features/src/*/; do if [ -f "${feature_dir}devcontainer-feature.json" ]; then echo "Validating ${feature_dir}devcontainer-feature.json" jq empty "${feature_dir}devcontainer-feature.json" @@ -35,7 +35,7 @@ jobs: - name: Lint install.sh scripts run: | - find images/features -name "*.sh" -type f -exec shellcheck {} + + find images/features/src -name "*.sh" -type f -exec shellcheck {} + test: needs: lint @@ -58,7 +58,7 @@ jobs: - name: Test feature run: | devcontainer features test \ - --features ./images/features/${{ matrix.feature }} \ + --features ./images/features/src/${{ matrix.feature }} \ --base-image mcr.microsoft.com/devcontainers/base:ubuntu \ --project-folder ./images/features @@ -84,7 +84,7 @@ jobs: uses: devcontainers/action@v1 with: publish-features: true - base-path-to-features: ./images/features + base-path-to-features: ./images/features/src generate-docs: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/images/features/agents/README.md b/images/features/src/agents/README.md similarity index 100% rename from images/features/agents/README.md rename to images/features/src/agents/README.md diff --git a/images/features/agents/devcontainer-feature.json b/images/features/src/agents/devcontainer-feature.json similarity index 100% rename from images/features/agents/devcontainer-feature.json rename to images/features/src/agents/devcontainer-feature.json diff --git a/images/features/agents/install.sh b/images/features/src/agents/install.sh similarity index 100% rename from images/features/agents/install.sh rename to images/features/src/agents/install.sh diff --git a/images/features/agents/test/scenarios.json b/images/features/test/agents/scenarios.json similarity index 100% rename from images/features/agents/test/scenarios.json rename to images/features/test/agents/scenarios.json diff --git a/images/features/agents/test/test-claude-only.sh b/images/features/test/agents/test-claude-only.sh similarity index 100% rename from images/features/agents/test/test-claude-only.sh rename to images/features/test/agents/test-claude-only.sh diff --git a/images/features/agents/test/test.sh b/images/features/test/agents/test.sh similarity index 100% rename from images/features/agents/test/test.sh rename to images/features/test/agents/test.sh From 2a05dcefb7cb834e9e08139c8c9347b09ece7a54 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Mon, 5 Jan 2026 11:34:31 -0800 Subject: [PATCH 4/5] fix(ci): correct feature path in test command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current behavior: The --features path was incorrectly appended to --project-folder, resulting in a non-existent path. New behavior: Use just the feature name since --project-folder handles the base path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/features.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/features.yml b/.github/workflows/features.yml index 2471c37..340c90f 100644 --- a/.github/workflows/features.yml +++ b/.github/workflows/features.yml @@ -58,7 +58,7 @@ jobs: - name: Test feature run: | devcontainer features test \ - --features ./images/features/src/${{ matrix.feature }} \ + --features ${{ matrix.feature }} \ --base-image mcr.microsoft.com/devcontainers/base:ubuntu \ --project-folder ./images/features From 4f8b2aa5f0778fd1def4641773a5c645f77ca1b0 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Mon, 5 Jan 2026 11:39:50 -0800 Subject: [PATCH 5/5] fix(features): auto-install Node.js and add missing test script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current behavior: Feature failed on base images without Node.js, and test-default scenario was missing a test script. New behavior: - install.sh now auto-installs Node.js via apt/apk/dnf/yum if npm is not available, making the feature self-contained - Added test-default.sh script for the test-default scenario 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- images/features/src/agents/install.sh | 33 +++++++++++++++++---- images/features/test/agents/test-default.sh | 28 +++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) create mode 100755 images/features/test/agents/test-default.sh diff --git a/images/features/src/agents/install.sh b/images/features/src/agents/install.sh index 543c151..298f122 100755 --- a/images/features/src/agents/install.sh +++ b/images/features/src/agents/install.sh @@ -13,12 +13,35 @@ CODEX_CLI_VERSION="${CODEXCLIVERSION:-latest}" echo "Installing AI Coding Agent CLIs..." -# Check if npm is available +# Install Node.js if npm is not available if ! command -v npm > /dev/null 2>&1; then - echo "Error: npm is not available. Please ensure Node.js is installed." - echo "Add the Node.js feature before this feature:" - echo ' "ghcr.io/devcontainers/features/node:1": {}' - exit 1 + echo "Node.js not found. Installing..." + + # Detect package manager and install Node.js + if command -v apt-get > /dev/null 2>&1; then + apt-get update + apt-get install -y curl ca-certificates gnupg + mkdir -p /etc/apt/keyrings + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list + apt-get update + apt-get install -y nodejs + rm -rf /var/lib/apt/lists/* + elif command -v apk > /dev/null 2>&1; then + apk add --no-cache nodejs npm + elif command -v dnf > /dev/null 2>&1; then + dnf install -y nodejs npm + elif command -v yum > /dev/null 2>&1; then + curl -fsSL https://rpm.nodesource.com/setup_22.x | bash - + yum install -y nodejs + else + echo "Error: Could not install Node.js - unsupported package manager" + echo "Please add the Node.js feature manually:" + echo ' "ghcr.io/devcontainers/features/node:1": {}' + exit 1 + fi + + echo "Node.js installed successfully" fi echo "Using npm version: $(npm --version)" diff --git a/images/features/test/agents/test-default.sh b/images/features/test/agents/test-default.sh new file mode 100755 index 0000000..ecbc4ef --- /dev/null +++ b/images/features/test/agents/test-default.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Test script for test-default scenario (all agents installed) +set -e + +echo "Testing default configuration (all agents)..." + +# Verify Claude Code +if ! command -v claude > /dev/null 2>&1; then + echo "FAIL: claude command not found" + exit 1 +fi +echo "PASS: claude command found" + +# Verify Gemini CLI +if ! command -v gemini > /dev/null 2>&1; then + echo "FAIL: gemini command not found" + exit 1 +fi +echo "PASS: gemini command found" + +# Verify Codex CLI +if ! command -v codex > /dev/null 2>&1; then + echo "FAIL: codex command not found" + exit 1 +fi +echo "PASS: codex command found" + +echo "All tests passed!"