diff --git a/.claude/skills/brev-cli/INSTALLATION.md b/.claude/skills/brev-cli/INSTALLATION.md new file mode 100644 index 00000000..55331b10 --- /dev/null +++ b/.claude/skills/brev-cli/INSTALLATION.md @@ -0,0 +1,91 @@ +# Installing the Brev CLI Claude Code Skill + +## One-Liner Install + +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash +``` + +## What Gets Installed + +``` +~/.claude/skills/brev-cli/ +├── SKILL.md # Main skill definition +├── prompts/ +│ ├── quick-session.md # Quick GPU session workflow +│ ├── ml-training.md # ML training setup workflow +│ └── cleanup.md # Instance cleanup workflow +├── reference/ +│ ├── commands.md # Full command reference +│ └── search-filters.md # GPU search options +└── examples/ + └── common-patterns.md # Common command patterns +``` + +## Options + +### Install from a specific branch + +**Using the standalone script:** +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash -s -- --branch my-branch +``` + +**Using the CLI command:** +```bash +BREV_SKILL_BRANCH=my-branch brev claude-skill +``` + +### Uninstall + +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash -s -- --uninstall +``` + +### Manual Installation + +If you prefer to install manually: + +```bash +# Clone the repo +git clone https://github.com/brevdev/brev-cli.git +cd brev-cli + +# Copy skill to Claude directory +mkdir -p ~/.claude/skills/ +cp -r .claude/skills/brev-cli ~/.claude/skills/ +``` + +## After Installation + +1. **Restart Claude Code** or start a new conversation +2. **Verify installation:** + ```bash + ls ~/.claude/skills/brev-cli/ + ``` +3. **Test the skill:** + - Say "search for A100 GPUs" or + - Use `/brev-cli` + +## Troubleshooting + +### Skill not appearing + +- Make sure you restarted Claude Code +- Check the file exists: `cat ~/.claude/skills/brev-cli/SKILL.md` +- Verify YAML frontmatter is valid (no tabs, proper formatting) + +### Permission denied + +```bash +# Fix permissions +chmod -R 755 ~/.claude/skills/brev-cli/ +``` + +### Update to latest version + +Just run the installer again - it will overwrite existing files: + +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash +``` diff --git a/.claude/skills/brev-cli/SKILL.md b/.claude/skills/brev-cli/SKILL.md new file mode 100644 index 00000000..30fbbc57 --- /dev/null +++ b/.claude/skills/brev-cli/SKILL.md @@ -0,0 +1,220 @@ +--- +name: brev-cli +description: Manage GPU cloud instances with the Brev CLI. Use when users want to create GPU instances, search for GPUs, SSH into instances, open editors, copy files, port forward, manage organizations, or work with cloud compute. Trigger keywords - brev, gpu, instance, create instance, ssh, vram, A100, H100, cloud gpu, remote machine. +allowed-tools: Bash, Read, AskUserQuestion +argument-hint: [create|search|shell|open|ls|delete] [instance-name] +--- + + +# Brev CLI + +Manage GPU cloud instances from the command line. Create, search, connect, and manage remote GPU machines. + +## When to Use + +Use this skill when users want to: +- Create GPU instances (with smart defaults or specific types) +- Search for available GPU types (A100, H100, L40S, etc.) +- SSH into instances or run commands remotely +- Open editors (VS Code, Cursor, Windsurf) on remote instances +- Copy files to/from instances +- Port forward from remote to local +- Manage organizations and instances + +**Trigger Keywords:** brev, gpu, instance, create instance, ssh, vram, A100, H100, cloud gpu, remote machine, shell + +## Quick Start + +```bash +# Search for GPUs (sorted by price) +brev search + +# Create an instance with smart defaults +brev create my-instance + +# Create with specific GPU +brev create my-instance --type g5.xlarge + +# List your instances +brev ls + +# SSH into an instance +brev shell my-instance + +# Open in VS Code/Cursor +brev open my-instance code +brev open my-instance cursor +``` + +## Core Commands + +### Search GPUs +```bash +# All available GPUs +brev search + +# Filter by GPU name +brev search --gpu-name A100 +brev search --gpu-name H100 + +# Filter by VRAM, sort by price +brev search --min-vram 40 --sort price + +# Filter by boot time +brev search --max-boot-time 5 --sort price +``` + +### Create Instances +```bash +# Smart defaults (cheapest matching GPU) +brev create my-instance + +# Specific type +brev create my-instance --type g5.xlarge + +# Multiple types (fallback chain) +brev create my-instance --type g5.xlarge,g5.2xlarge + +# Pipe from search +brev search --gpu-name A100 | brev create my-instance + +# Multiple instances +brev create my-cluster --count 3 + +# With startup script +brev create my-instance --startup-script @setup.sh +brev create my-instance --startup-script 'pip install torch' +``` + +### Instance Access +```bash +# SSH into instance +brev shell my-instance + +# Run command remotely +brev shell my-instance -c "nvidia-smi" +brev shell my-instance -c "python train.py" + +# Run a local script on the instance (use @filepath) +brev shell my-instance -c @setup.sh +brev shell my-instance -c @/path/to/script.sh + +# Open in editor +brev open my-instance # default editor +brev open my-instance code # VS Code +brev open my-instance cursor # Cursor +brev open my-instance windsurf # Windsurf +brev open my-instance terminal # Terminal window +brev open my-instance tmux # Terminal + tmux + +# Copy files +brev copy ./local-file my-instance:/remote/path/ +brev copy my-instance:/remote/file ./local-path/ + +# Port forward +brev port-forward my-instance -p 8080:8080 +``` + +### Instance Management +```bash +# List instances +brev ls + +# Delete instance +brev delete my-instance + +# Stop/start (if supported) +brev stop my-instance +brev start my-instance + +# Reset (recover from errors) +brev reset my-instance +``` + +### Pipeable Workflows +```bash +# Stop all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev stop + +# Delete all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev delete + +# Start all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev start + +# Stop instances matching pattern +brev ls | grep "test-" | awk '{print $1}' | brev stop + +# Run command on all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev shell -c "nvidia-smi" + +# Create and open in one command +brev search --gpu-name A100 | brev create my-box | brev open cursor +``` + +### Organizations +```bash +# List orgs +brev org ls + +# Set active org +brev org set my-org +brev set my-org # alias + +# Generate invite link +brev invite +``` + +## Common Workflows + +1. **Quick GPU Session** ([prompts/quick-session.md](prompts/quick-session.md)) + - Search → Create → Open editor + +2. **ML Training Setup** ([prompts/ml-training.md](prompts/ml-training.md)) + - Find high-VRAM GPU → Create with startup script → Copy data → Run training + +3. **Instance Cleanup** ([prompts/cleanup.md](prompts/cleanup.md)) + - List instances → Identify unused → Delete + +## Safety Rules - CRITICAL + +**NEVER do these without explicit user confirmation:** +- Delete instances (`brev delete`) +- Stop running instances (`brev stop`) +- Create multiple instances (`--count > 1`) +- Create expensive instances (H100, multi-GPU) + +**ALWAYS do these:** +- Show instance cost/type before creating +- Confirm instance name before deletion +- Check `brev ls` before assuming instance exists + +## Troubleshooting + +**"Instance not found":** +- Run `brev ls` to see available instances +- Check if you're in the correct org: `brev org ls` + +**"Failed to create instance":** +- Try a different instance type: `brev search --sort price` +- Check quota/credits with org admin + +**SSH connection fails:** +- Run `brev refresh` to update SSH config +- Ensure instance is running: `brev ls` + +**Editor won't open:** +- Verify editor is in PATH: `which code` / `which cursor` +- Set default: `brev open --set-default code` + +## References + +- **[reference/commands.md](reference/commands.md)** - Full command reference +- **[reference/search-filters.md](reference/search-filters.md)** - GPU search options +- **[prompts/](prompts/)** - Workflow guides +- **[examples/](examples/)** - Common patterns diff --git a/.claude/skills/brev-cli/examples/common-patterns.md b/.claude/skills/brev-cli/examples/common-patterns.md new file mode 100644 index 00000000..771bfa66 --- /dev/null +++ b/.claude/skills/brev-cli/examples/common-patterns.md @@ -0,0 +1,331 @@ +# Common Brev CLI Patterns + +Frequently used command combinations and workflows. + +## Piping Patterns + +Brev CLI is fully pipeable. Commands read from stdin and output instance names to stdout. + +### The Pipe Architecture + +``` +brev ls → awk/grep → brev stop/start/delete/shell/open +brev search → brev create → brev open/shell +``` + +### Bulk Operations from `brev ls` + +```bash +# Stop all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev stop + +# Delete all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev delete + +# Start all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev start + +# Open all running instances in editor +brev ls | awk '/RUNNING/ {print $1}' | brev open cursor + +# Run command on all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev shell -c "nvidia-smi" +``` + +### Pattern Matching with grep + +```bash +# Stop all "test-*" instances +brev ls | grep "test-" | awk '{print $1}' | brev stop + +# Delete all "experiment-*" instances +brev ls | grep "experiment-" | awk '{print $1}' | brev delete + +# Open all "dev-*" instances +brev ls | grep "^dev-" | awk '{print $1}' | brev open + +# Stop instances with specific GPU type +brev ls | grep "A100" | awk '{print $1}' | brev stop +``` + +### Preview Before Destructive Operations + +```bash +# Preview: see what would be deleted +brev ls | awk '/STOPPED/ {print $1}' + +# Execute: actually delete them +brev ls | awk '/STOPPED/ {print $1}' | brev delete + +# Preview: see what would be stopped +brev ls | grep "test-" | awk '/RUNNING/ {print $1}' + +# Execute: stop them +brev ls | grep "test-" | awk '/RUNNING/ {print $1}' | brev stop +``` + +### Create → Access Chains + +```bash +# Create and open in editor +brev search --gpu-name A100 | brev create my-box | brev open cursor + +# Create and SSH immediately +brev create my-box | brev shell + +# Create and run setup command +brev create my-box | brev shell -c "pip install torch" + +# Create cluster and verify all nodes +brev create my-cluster --count 3 | brev shell -c "hostname && nvidia-smi" +``` + +## Running Scripts with @filepath + +Use `@` prefix to run local scripts on remote instances without copying files first. + +### Basic Script Execution +```bash +# Run a local script on remote instance +brev shell my-instance -c @setup.sh + +# Absolute path +brev shell my-instance -c @/home/user/scripts/install.sh + +# Relative path +brev shell my-instance -c @./scripts/deploy.sh +``` + +### Create + Setup in One Command +```bash +# Create instance and run setup script +brev create my-instance | brev shell -c @setup.sh + +# With specific GPU +brev search --gpu-name A100 | brev create ml-box | brev shell -c @ml-setup.sh +``` + +### Run Script on Multiple Instances +```bash +# Same script on all cluster nodes +brev shell node-1 node-2 node-3 -c @setup.sh + +# Or pipe from create +brev create my-cluster --count 3 | brev shell -c @setup.sh +``` + +### Example Setup Scripts + +**ml-setup.sh** - ML environment: +```bash +#!/bin/bash +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 +pip install transformers accelerate datasets wandb +nvidia-smi +python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')" +``` + +**dev-setup.sh** - Development environment: +```bash +#!/bin/bash +apt-get update && apt-get install -y vim tmux htop +pip install ipython jupyter black ruff +git config --global user.name "Your Name" +git config --global user.email "you@example.com" +``` + +## Quick Start Patterns + +### Create and Connect (One-Liner) +```bash +# Create and open in VS Code +brev search --gpu-name A100 | brev create my-box | brev open code + +# Create and SSH in +brev shell $(brev create my-box) + +# Create and run a command +brev create my-box | brev shell -c "nvidia-smi" +``` + +### Find Cheapest GPU +```bash +# Cheapest with minimum specs +brev search --min-vram 24 --sort price | head -5 + +# Cheapest A100 +brev search --gpu-name A100 --sort price | head -1 +``` + +## Training Workflows + +### PyTorch Training Setup +```bash +# Create with PyTorch pre-installed +brev search --min-vram 40 | brev create ml-train -s 'pip install torch transformers' + +# Copy training code +brev copy ./train.py ml-train:/home/ubuntu/ + +# Copy dataset +brev copy ./data/ ml-train:/home/ubuntu/data/ + +# Start training +brev shell ml-train -c "cd /home/ubuntu && python train.py" +``` + +### Background Training +```bash +# Start training in background with logs +brev shell ml-train -c "nohup python train.py > training.log 2>&1 &" + +# Monitor logs +brev shell ml-train -c "tail -f training.log" + +# Check GPU usage +brev shell ml-train -c "nvidia-smi" +``` + +### Multi-GPU Training +```bash +# Find multi-GPU instances +brev search --min-total-vram 160 --sort price + +# Create 8-GPU cluster +brev search --gpu-name A100 | brev create training-cluster --count 8 +``` + +## Development Workflows + +### Quick Development Box +```bash +# Fast-booting cheap GPU +brev search --max-boot-time 3 --sort price | brev create dev-box + +# Open in preferred editor +brev open dev-box cursor +``` + +### Remote Development Session +```bash +# Start tmux session +brev open my-box tmux + +# Later: reconnect to same session +brev shell my-box -c "tmux attach" +``` + +### Port Forwarding for Jupyter +```bash +# Start Jupyter on instance +brev shell my-box -c "jupyter notebook --no-browser --port 8888 &" + +# Forward port locally +brev port-forward my-box -p 8888:8888 + +# Access at http://localhost:8888 +``` + +## Cluster Patterns + +### Create Named Cluster +```bash +# Creates: my-cluster-1, my-cluster-2, my-cluster-3 +brev create my-cluster --count 3 --type g5.xlarge +``` + +### Run Command on All Cluster Nodes +```bash +# Run nvidia-smi on all nodes +brev shell my-cluster-1 my-cluster-2 my-cluster-3 -c "nvidia-smi" + +# Or pipe from create +brev create my-cluster --count 3 | brev shell -c "nvidia-smi" +``` + +### Open All Cluster Nodes +```bash +brev open my-cluster-1 my-cluster-2 my-cluster-3 cursor +``` + +## File Transfer Patterns + +### Sync Project Directory +```bash +# Upload project +brev copy ./myproject/ my-box:/home/ubuntu/myproject/ + +# Download results +brev copy my-box:/home/ubuntu/results/ ./results/ +``` + +### Backup Before Delete +```bash +# Save checkpoints +brev copy my-box:/home/ubuntu/checkpoints/ ./backups/ + +# Save logs +brev copy my-box:/home/ubuntu/*.log ./logs/ + +# Now safe to delete +brev delete my-box +``` + +## Organization Patterns + +### Switch Orgs for Different Projects +```bash +# List orgs +brev org ls + +# Switch to work org +brev set work-org + +# Create work instance +brev create work-project + +# Switch back to personal +brev set personal-org +``` + +### Check All Instances Across Orgs +```bash +# Check current org +brev ls + +# Check specific org +brev ls --org other-org +``` + +## Troubleshooting Patterns + +### Debug Connection Issues +```bash +# Refresh SSH config +brev refresh + +# Check instance status +brev ls + +# Try host connection +brev shell my-box --host +``` + +### Recover Stuck Instance +```bash +# Reset instance +brev reset my-box + +# Wait and reconnect +sleep 30 && brev shell my-box +``` + +### Clean Up All Instances +```bash +# List all +brev ls + +# Delete each (confirm each one) +brev delete instance-1 +brev delete instance-2 +``` diff --git a/.claude/skills/brev-cli/prompts/cleanup.md b/.claude/skills/brev-cli/prompts/cleanup.md new file mode 100644 index 00000000..1eae1e6c --- /dev/null +++ b/.claude/skills/brev-cli/prompts/cleanup.md @@ -0,0 +1,78 @@ +# Instance Cleanup Workflow + +Review and clean up unused GPU instances. + +## Step 1: List All Instances + +```bash +brev ls +``` + +Parse output to identify: +- Instance name +- Status (running/stopped) +- Instance type +- Creation time (if available) + +## Step 2: Identify Candidates for Cleanup + +Look for: +- Stopped instances (not in use) +- Old instances (name suggests temporary use) +- Test/dev instances + +Present list to user: +``` +Found X instances: + +Running: + - prod-training (H100, running) - Keep? + - ml-experiment (A100, running) - Keep? + +Stopped: + - old-test (g5.xlarge, stopped) - Delete? + - temp-dev (g4dn.xlarge, stopped) - Delete? +``` + +## Step 3: Confirm Deletions + +Use AskUserQuestion with multiSelect: +``` +question: "Which instances should we delete?" +header: "Cleanup" +multiSelect: true +options: + - label: "" + description: ", " + - label: "" + description: ", " +``` + +## Step 4: Delete Selected Instances + +For each confirmed instance: +```bash +brev delete +``` + +Report success/failure for each. + +## Step 5: Verify Cleanup + +```bash +brev ls +``` + +Confirm deleted instances are gone. + +## Safety Checks + +**Before deleting, ALWAYS:** +- Confirm instance name with user +- Warn if instance is running +- Suggest copying important data first: + ```bash + brev copy :/home/ubuntu/important/ ./backup/ + ``` + +**NEVER delete without explicit confirmation for each instance.** diff --git a/.claude/skills/brev-cli/prompts/ml-training.md b/.claude/skills/brev-cli/prompts/ml-training.md new file mode 100644 index 00000000..7aa38524 --- /dev/null +++ b/.claude/skills/brev-cli/prompts/ml-training.md @@ -0,0 +1,118 @@ +# ML Training Setup Workflow + +Set up a GPU instance for machine learning training. + +## Step 1: Determine GPU Requirements + +Use AskUserQuestion: +``` +question: "What size model are you training?" +header: "Model Size" +options: + - label: "Small (< 1B params)" + description: "16-24GB VRAM sufficient" + - label: "Medium (1-7B params)" + description: "40-48GB VRAM recommended" + - label: "Large (7B+ params)" + description: "80GB+ VRAM, multi-GPU" + - label: "Not sure" + description: "I'll describe the task" +``` + +## Step 2: Search for Suitable GPUs + +**Small models:** +```bash +brev search --min-vram 16 --sort price +``` + +**Medium models:** +```bash +brev search --min-vram 40 --sort price +``` + +**Large models:** +```bash +brev search --min-total-vram 80 --sort price +``` + +Show top 3-5 options with prices. + +## Step 3: Setup Script + +Ask: "Do you have a setup script, or should we use defaults?" + +**Default setup script:** +```bash +cat > /tmp/ml-setup.sh << 'EOF' +#!/bin/bash +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 +pip install transformers accelerate datasets +pip install wandb tensorboard +nvidia-smi +EOF +``` + +**User-provided:** +- Accept path to script file +- Or inline commands + +## Step 4: Create Instance + +```bash +# With setup script +brev search | brev create --startup-script @/tmp/ml-setup.sh + +# Or inline +brev search | brev create -s 'pip install torch transformers' +``` + +## Step 5: Copy Training Data (Optional) + +Ask: "Do you need to copy training data to the instance?" + +```bash +# Copy dataset +brev copy ./data/ :/home/ubuntu/data/ + +# Copy training script +brev copy ./train.py :/home/ubuntu/ +``` + +## Step 6: Verify Setup + +```bash +# Check GPU is available +brev shell -c "nvidia-smi" + +# Check PyTorch sees GPU +brev shell -c "python -c 'import torch; print(torch.cuda.is_available())'" +``` + +## Step 7: Start Training + +Options: +1. **Interactive:** `brev shell ` then run manually +2. **Background:** `brev shell -c "nohup python train.py &"` +3. **Editor:** `brev open cursor` + +## Step 8: Monitor + +```bash +# Check GPU usage +brev shell -c "nvidia-smi" + +# Check training logs +brev shell -c "tail -f training.log" +``` + +## Cleanup Reminder + +When training completes: +```bash +# Copy results back +brev copy :/home/ubuntu/checkpoints/ ./checkpoints/ + +# Delete instance to stop billing +brev delete +``` diff --git a/.claude/skills/brev-cli/prompts/quick-session.md b/.claude/skills/brev-cli/prompts/quick-session.md new file mode 100644 index 00000000..4069bdb9 --- /dev/null +++ b/.claude/skills/brev-cli/prompts/quick-session.md @@ -0,0 +1,87 @@ +# Quick GPU Session Workflow + +Get a GPU instance up and running quickly. + +## Step 1: Determine Requirements + +Use AskUserQuestion: +``` +question: "What do you need the GPU for?" +header: "Use Case" +options: + - label: "ML Training" + description: "Need high VRAM (40GB+), longer session" + - label: "Inference/Testing" + description: "Moderate VRAM (16-24GB), quick access" + - label: "Development" + description: "Any GPU, focus on fast boot time" + - label: "Specific GPU" + description: "I know exactly what I need" +``` + +## Step 2: Search for GPUs + +Based on use case: + +**ML Training:** +```bash +brev search --min-vram 40 --sort price +``` + +**Inference/Testing:** +```bash +brev search --min-vram 16 --max-boot-time 5 --sort price +``` + +**Development:** +```bash +brev search --max-boot-time 3 --sort price +``` + +**Specific GPU (ask user):** +```bash +brev search --gpu-name +``` + +## Step 3: Get Instance Name + +Ask: "What should we name this instance?" + +Suggest: Based on use case (e.g., `ml-training`, `dev-box`, `inference-test`) + +## Step 4: Create Instance + +```bash +# Show what we're creating +echo "Creating instance with: " + +# Create and capture name +brev search | brev create +``` + +Wait for instance to be ready. + +## Step 5: Open Editor + +Ask which editor: +``` +question: "How do you want to connect?" +header: "Editor" +options: + - label: "VS Code" + - label: "Cursor" + - label: "Terminal/SSH" + - label: "Just create, I'll connect later" +``` + +```bash +brev open +``` + +## Step 6: Confirm + +Report: +- Instance name +- Instance type +- GPU specs +- How to reconnect later: `brev shell ` or `brev open ` diff --git a/.claude/skills/brev-cli/reference/commands.md b/.claude/skills/brev-cli/reference/commands.md new file mode 100644 index 00000000..db4c640a --- /dev/null +++ b/.claude/skills/brev-cli/reference/commands.md @@ -0,0 +1,390 @@ +# Brev CLI Command Reference + +Complete reference for all brev commands. + +## Pipeable Architecture + +Brev CLI commands are designed to pipe together. Commands read instance names from stdin and output instance names to stdout, enabling powerful command chains. + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ brev search │ ──▶ │ brev create │ ──▶ │ brev open │ +│ brev ls │ │ brev stop │ │ brev shell │ +│ grep/awk │ │ brev start │ │ brev delete │ +└─────────────┘ │ brev delete │ └─────────────┘ + └─────────────┘ +``` + +### Piping from `brev ls` + +Extract instance names using `awk` and pipe to other commands: + +```bash +# Stop all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev stop + +# Delete all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev delete + +# Start all stopped instances +brev ls | awk '/STOPPED/ {print $1}' | brev start + +# Open all running instances in Cursor +brev ls | awk '/RUNNING/ {print $1}' | brev open cursor + +# Run nvidia-smi on all running instances +brev ls | awk '/RUNNING/ {print $1}' | brev shell -c "nvidia-smi" +``` + +### Filtering with grep + +```bash +# Stop instances matching a pattern +brev ls | grep "test-" | awk '{print $1}' | brev stop + +# Delete old experiment instances +brev ls | grep "experiment" | awk '{print $1}' | brev delete + +# Open all "dev-" instances +brev ls | grep "^dev-" | awk '{print $1}' | brev open +``` + +### Chaining create → access + +```bash +# Create and immediately open +brev search --gpu-name A100 | brev create my-box | brev open cursor + +# Create and run command +brev create my-box | brev shell -c "nvidia-smi" + +# Create cluster and run command on all +brev create my-cluster --count 3 | brev shell -c "hostname" +``` + +### Safety: Preview before bulk operations + +```bash +# Preview what will be stopped +brev ls | awk '/RUNNING/ {print $1}' + +# Then actually stop (add | brev stop) +brev ls | awk '/RUNNING/ {print $1}' | brev stop +``` + +## Instance Commands + +### brev create +Create GPU instances with automatic retry and fallback. + +```bash +brev create [name] [flags] +``` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--name` | `-n` | Instance name | +| `--type` | `-t` | Comma-separated instance types to try | +| `--count` | `-c` | Number of instances (default: 1) | +| `--parallel` | `-p` | Parallel creation attempts (default: 1) | +| `--startup-script` | `-s` | Script to run on boot (string or @filepath) | +| `--timeout` | | Seconds to wait for ready (default: 300) | +| `--detached` | `-d` | Don't wait for instance to be ready | + +**Examples:** +```bash +brev create my-instance +brev create my-instance --type g5.xlarge +brev create my-cluster --count 3 --type g5.xlarge +brev search --gpu-name A100 | brev create my-instance +brev create my-instance -s @setup.sh +brev create my-instance -s 'pip install torch' +``` + +### brev search +Search and filter available GPU instance types. + +```bash +brev search [flags] +``` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--gpu-name` | `-g` | Filter by GPU name (partial match) | +| `--provider` | `-p` | Filter by cloud provider | +| `--min-vram` | `-v` | Minimum VRAM per GPU (GB) | +| `--min-total-vram` | `-t` | Minimum total VRAM (GB) | +| `--min-capability` | `-c` | Minimum GPU compute capability | +| `--min-disk` | | Minimum disk size (GB) | +| `--max-boot-time` | | Maximum boot time (minutes) | +| `--sort` | `-s` | Sort by: price, gpu-count, vram, total-vram, vcpu, disk, boot-time | +| `--desc` | `-d` | Sort descending | +| `--json` | | Output as JSON | + +**Examples:** +```bash +brev search +brev search --gpu-name A100 +brev search --min-vram 40 --sort price +brev search --gpu-name H100 --max-boot-time 3 +``` + +### brev ls +List instances in active org. + +```bash +brev ls [flags] +``` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--org` | `-o` | Override active org | +| `--all` | | Show all instances in org | + +### brev delete +Delete instances. Supports multiple names and stdin piping. + +```bash +brev delete ... +echo "instance-name" | brev delete +``` + +**Examples:** +```bash +# Delete single instance +brev delete my-instance + +# Delete multiple instances +brev delete instance1 instance2 instance3 + +# Pipe from ls (delete all stopped) +brev ls | awk '/STOPPED/ {print $1}' | brev delete + +# Delete matching pattern +brev ls | grep "test-" | awk '{print $1}' | brev delete +``` + +### brev stop +Stop running instances. Supports multiple names and stdin piping. + +```bash +brev stop ... +brev stop --all +echo "instance-name" | brev stop +``` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--all` | `-a` | Stop all running instances | + +**Examples:** +```bash +# Stop single instance +brev stop my-instance + +# Stop multiple instances +brev stop instance1 instance2 + +# Stop all running instances +brev stop --all + +# Pipe from ls (stop matching pattern) +brev ls | grep "dev-" | awk '/RUNNING/ {print $1}' | brev stop +``` + +### brev start +Start stopped instances. Supports multiple names and stdin piping. + +```bash +brev start ... +echo "instance-name" | brev start +``` + +**Examples:** +```bash +# Start single instance +brev start my-instance + +# Start multiple instances +brev start instance1 instance2 + +# Pipe from ls (start all stopped) +brev ls | awk '/STOPPED/ {print $1}' | brev start +``` + +### brev reset +Reset an instance to recover from errors. + +```bash +brev reset +``` + +## Instance Access Commands + +### brev shell +SSH into an instance or run commands. + +```bash +brev shell [instance...] [flags] +``` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--command` | `-c` | Command to run (use @filename for script) | +| `--host` | | SSH to host machine instead of container | + +**The `@filepath` syntax:** + +Prefix a file path with `@` to run a local script on the remote instance: +- `@setup.sh` - relative path from current directory +- `@/absolute/path/script.sh` - absolute path +- The script is read locally and executed remotely +- Works with any shell script + +**Examples:** +```bash +# Interactive SSH +brev shell my-instance + +# Run inline command +brev shell my-instance -c "nvidia-smi" +brev shell my-instance -c "pip install torch && python train.py" + +# Run local script on remote instance +brev shell my-instance -c @setup.sh +brev shell my-instance -c @./scripts/install-deps.sh +brev shell my-instance -c @/home/user/my-script.sh + +# Run on multiple instances +brev shell instance1 instance2 -c "nvidia-smi" +brev shell instance1 instance2 -c @setup.sh + +# Chain with create +brev create my-instance | brev shell -c "nvidia-smi" +brev create my-instance | brev shell -c @setup.sh +``` + +### brev open +Open an editor connected to the instance. + +```bash +brev open [instance...] [editor] [flags] +``` + +**Editors:** `code`, `cursor`, `windsurf`, `terminal`, `tmux` + +**Flags:** +| Flag | Short | Description | +|------|-------|-------------| +| `--editor` | `-e` | Editor to use | +| `--dir` | `-d` | Directory to open | +| `--wait` | `-w` | Wait for setup to finish | +| `--set-default` | | Set default editor | +| `--host` | | Connect to host instead of container | + +**Examples:** +```bash +brev open my-instance +brev open my-instance cursor +brev open --set-default cursor +brev create my-instance | brev open code +``` + +### brev copy +Copy files to/from instances. + +```bash +brev copy +``` + +**Flags:** +| Flag | Description | +|------|-------------| +| `--host` | Copy to/from host instead of container | + +**Examples:** +```bash +brev copy ./local-file my-instance:/remote/path/ +brev copy my-instance:/remote/file ./local-path/ +brev copy ./data/ my-instance:/home/ubuntu/data/ +``` + +### brev port-forward +Forward remote port to local. + +```bash +brev port-forward -p : +``` + +**Examples:** +```bash +brev port-forward my-instance -p 8080:8080 +brev port-forward my-instance -p 3000:3000 +``` + +## Organization Commands + +### brev org ls +List organizations. + +```bash +brev org ls +``` + +### brev org set / brev set +Set active organization. + +```bash +brev org set +brev set +``` + +### brev org create +Create a new organization. + +```bash +brev org create +``` + +### brev invite +Generate an invite link. + +```bash +brev invite +``` + +## Configuration Commands + +### brev login / brev logout +Authenticate with Brev. + +```bash +brev login +brev logout +``` + +### brev refresh +Refresh SSH config. + +```bash +brev refresh +``` + +### brev healthcheck +Check backend health. + +```bash +brev healthcheck +``` + +### brev ssh-key +Get your public SSH key. + +```bash +brev ssh-key +``` diff --git a/.claude/skills/brev-cli/reference/search-filters.md b/.claude/skills/brev-cli/reference/search-filters.md new file mode 100644 index 00000000..a77b5a1c --- /dev/null +++ b/.claude/skills/brev-cli/reference/search-filters.md @@ -0,0 +1,166 @@ +# GPU Search Filters Reference + +Detailed guide to filtering and sorting GPU instance types. + +## Filter Options + +### GPU Name (`--gpu-name`, `-g`) +Filter by GPU model name. Case-insensitive, partial match. + +```bash +# Exact matches +brev search --gpu-name A100 +brev search --gpu-name H100 +brev search --gpu-name L40S + +# Partial matches +brev search --gpu-name A10 # Matches A100, A10G, etc. +brev search --gpu-name RTX # Matches RTX 4090, RTX 3090, etc. +``` + +### Provider (`--provider`, `-p`) +Filter by cloud provider. Case-insensitive, partial match. + +```bash +brev search --provider aws +brev search --provider gcp +brev search --provider azure +``` + +### VRAM (`--min-vram`, `-v`) +Minimum VRAM per GPU in GB. + +```bash +brev search --min-vram 16 # 16GB+ per GPU +brev search --min-vram 24 # 24GB+ per GPU +brev search --min-vram 40 # 40GB+ per GPU (A100 40GB, etc.) +brev search --min-vram 80 # 80GB+ per GPU (A100 80GB, H100) +``` + +### Total VRAM (`--min-total-vram`, `-t`) +Minimum total VRAM across all GPUs in GB. + +```bash +brev search --min-total-vram 48 # 48GB total (e.g., 2x 24GB) +brev search --min-total-vram 80 # 80GB total (e.g., 1x 80GB or 2x 40GB) +brev search --min-total-vram 160 # 160GB total (e.g., 2x 80GB) +``` + +### Compute Capability (`--min-capability`, `-c`) +Minimum GPU compute capability (architecture). + +| Capability | Architecture | GPUs | +|------------|--------------|------| +| 7.0 | Volta | V100 | +| 7.5 | Turing | T4, RTX 20xx | +| 8.0 | Ampere | A100, A10, A30 | +| 8.6 | Ampere | RTX 30xx, A40 | +| 8.9 | Ada Lovelace | L40, RTX 40xx | +| 9.0 | Hopper | H100 | + +```bash +brev search --min-capability 8.0 # Ampere or newer +brev search --min-capability 9.0 # Hopper only +``` + +### Disk Size (`--min-disk`) +Minimum disk size in GB. + +```bash +brev search --min-disk 500 # 500GB+ disk +brev search --min-disk 1000 # 1TB+ disk +``` + +### Boot Time (`--max-boot-time`) +Maximum boot time in minutes. + +```bash +brev search --max-boot-time 3 # Fast boot (< 3 min) +brev search --max-boot-time 5 # Moderate boot (< 5 min) +brev search --max-boot-time 10 # Any boot time +``` + +## Sort Options + +### Sort Column (`--sort`, `-s`) + +| Column | Description | +|--------|-------------| +| `price` | Hourly cost (default) | +| `gpu-count` | Number of GPUs | +| `vram` | VRAM per GPU | +| `total-vram` | Total VRAM | +| `vcpu` | Number of vCPUs | +| `type` | Instance type name | +| `provider` | Cloud provider | +| `disk` | Disk size | +| `boot-time` | Boot time | + +```bash +brev search --sort price # Cheapest first (default) +brev search --sort vram # Highest VRAM first +brev search --sort boot-time # Fastest boot first +``` + +### Sort Direction (`--desc`, `-d`) +Sort in descending order. + +```bash +brev search --sort price --desc # Most expensive first +brev search --sort vram --desc # Highest VRAM first +``` + +## Common Filter Combinations + +### Development (fast, cheap) +```bash +brev search --max-boot-time 3 --sort price +``` + +### ML Training (high VRAM) +```bash +brev search --min-vram 40 --sort price +``` + +### Large Model Training +```bash +brev search --min-total-vram 80 --sort price +``` + +### Production (fast + reliable) +```bash +brev search --gpu-name A100 --max-boot-time 5 --sort price +``` + +### Budget Conscious +```bash +brev search --min-vram 16 --sort price | head -5 +``` + +### Latest Architecture +```bash +brev search --min-capability 9.0 --sort price +``` + +## Output Format + +### Table Output (default) +``` +TYPE GPU COUNT VRAM TOTAL $/HR BOOT FEATURES +g5.xlarge A10G 1 24GB 24GB $1.00 2m S R P +g5.2xlarge A10G 1 24GB 24GB $1.20 2m S R P +p4d.24xlarge A100 8 40GB 320GB $32.77 5m S R +``` + +### JSON Output (`--json`) +```bash +brev search --json | jq '.[] | {type, gpu_name, price}' +``` + +## Features Column + +| Code | Meaning | +|------|---------| +| S | Stoppable - can stop/restart without data loss | +| R | Rebootable - can reboot the instance | +| P | Flex Ports - can modify firewall rules | diff --git a/README.md b/README.md index ff03010a..e2e18842 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,43 @@ brev login https://brev.nvidia.com/ +## Claude Code Integration + +Use Brev with [Claude Code](https://claude.ai/code) for AI-assisted GPU instance management. + +### Install the Claude Code Skill + +**Option 1: Via Brev CLI (recommended)** + +If you have Brev installed, the skill is offered automatically during `brev login`. Or install manually: + +```bash +brev claude-skill +``` + +**Option 2: Standalone installer** + +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash +``` + +### Usage + +After installing, restart Claude Code and use natural language: +- "Create an A100 instance for ML training" +- "Search for GPUs with 40GB VRAM" +- "Stop all my running instances" + +Or invoke directly with `/brev-cli`. + +### Uninstall + +```bash +brev claude-skill --uninstall +``` + +See [.claude/skills/brev-cli/INSTALLATION.md](.claude/skills/brev-cli/INSTALLATION.md) for more options. + ## Docs https://docs.nvidia.com/brev/latest/ diff --git a/pkg/cmd/claudeskill/claudeskill.go b/pkg/cmd/claudeskill/claudeskill.go new file mode 100644 index 00000000..cc3368d5 --- /dev/null +++ b/pkg/cmd/claudeskill/claudeskill.go @@ -0,0 +1,279 @@ +// Package claudeskill handles installation of the Brev CLI Claude Code skill +package claudeskill + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + breverrors "github.com/brevdev/brev-cli/pkg/errors" + "github.com/brevdev/brev-cli/pkg/terminal" + "github.com/fatih/color" + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" +) + +const ( + // GitHub raw content base URL template + baseURLTemplate = "https://raw.githubusercontent.com/brevdev/brev-cli/%s/.claude/skills/brev-cli" + + // Default branch + defaultBranch = "main" + + // Environment variable to override branch (for testing) + branchEnvVar = "BREV_SKILL_BRANCH" + + // Skill name + skillName = "brev-cli" +) + +// getBaseURL returns the base URL for downloading skill files +// Uses BREV_SKILL_BRANCH env var if set, otherwise defaults to main +func getBaseURL() string { + branch := os.Getenv(branchEnvVar) + if branch == "" { + branch = defaultBranch + } + return fmt.Sprintf(baseURLTemplate, branch) +} + +// Files to download (relative to skill directory) +var skillFiles = []string{ + "SKILL.md", + "INSTALLATION.md", + "prompts/quick-session.md", + "prompts/ml-training.md", + "prompts/cleanup.md", + "reference/commands.md", + "reference/search-filters.md", + "examples/common-patterns.md", +} + +// ClaudeSkillStore interface for any store dependencies +type ClaudeSkillStore interface { + UserHomeDir() (string, error) +} + +// NewCmdClaudeSkill creates the claude-skill command +func NewCmdClaudeSkill(t *terminal.Terminal, store ClaudeSkillStore) *cobra.Command { + var uninstall bool + + cmd := &cobra.Command{ + Annotations: map[string]string{"configuration": ""}, + Use: "claude-skill", + DisableFlagsInUseLine: true, + Short: "Install the Brev CLI skill for Claude Code", + Long: "Install or uninstall the Brev CLI skill for Claude Code AI assistant", + Example: "brev claude-skill\nbrev claude-skill --uninstall", + RunE: func(cmd *cobra.Command, args []string) error { + homeDir, err := store.UserHomeDir() + if err != nil { + return breverrors.WrapAndTrace(err) + } + + if uninstall { + return UninstallSkill(t, homeDir) + } + return InstallSkill(t, homeDir, false) + }, + } + + cmd.Flags().BoolVarP(&uninstall, "uninstall", "u", false, "Uninstall the Claude Code skill") + + return cmd +} + +// GetSkillDir returns the path to the Claude skill directory +func GetSkillDir(homeDir string) string { + return filepath.Join(homeDir, ".claude", "skills", skillName) +} + +// IsClaudeInstalled checks if Claude Code appears to be installed +func IsClaudeInstalled(homeDir string) bool { + claudeDir := filepath.Join(homeDir, ".claude") + _, err := os.Stat(claudeDir) + return err == nil +} + +// IsSkillInstalled checks if the brev-cli skill is already installed +func IsSkillInstalled(homeDir string) bool { + skillFile := filepath.Join(GetSkillDir(homeDir), "SKILL.md") + _, err := os.Stat(skillFile) + return err == nil +} + +// PromptInstallSkill asks the user if they want to install the Claude skill +// Returns true if they want to install, false otherwise +func PromptInstallSkill(t *terminal.Terminal, homeDir string) bool { + // Skip if skill is already installed + if IsSkillInstalled(homeDir) { + return false + } + + // Check if Claude Code appears to be installed + if !IsClaudeInstalled(homeDir) { + return false + } + + fmt.Println() + caretType := color.New(color.FgCyan, color.Bold).SprintFunc() + fmt.Println(" ", caretType("▸"), " Claude Code Integration") + fmt.Println() + fmt.Println(" We detected Claude Code on your system.") + fmt.Println(" Would you like to install the Brev CLI skill?") + fmt.Println() + fmt.Println(" This enables natural language commands like:") + fmt.Println(t.Yellow(" \"Create an A100 instance for ML training\"")) + fmt.Println(t.Yellow(" \"Search for GPUs with 40GB VRAM\"")) + fmt.Println(t.Yellow(" \"Stop all my running instances\"")) + fmt.Println() + + prompt := promptui.Select{ + Label: "Install Claude Code skill", + Items: []string{"Yes, install it", "No, skip for now"}, + } + + idx, _, err := prompt.Run() + if err != nil { + return false + } + + return idx == 0 +} + +// InstallSkill downloads and installs the Claude skill +func InstallSkill(t *terminal.Terminal, homeDir string, quiet bool) error { + skillDir := GetSkillDir(homeDir) + baseURL := getBaseURL() + + if !quiet { + fmt.Println() + fmt.Printf(" Installing brev-cli skill to %s\n", t.Yellow(skillDir)) + // Show branch if not default + if branch := os.Getenv(branchEnvVar); branch != "" { + fmt.Printf(" Using branch: %s\n", t.Yellow(branch)) + } + fmt.Println() + } + + // Create directory structure + dirs := []string{ + skillDir, + filepath.Join(skillDir, "prompts"), + filepath.Join(skillDir, "reference"), + filepath.Join(skillDir, "examples"), + } + + for _, dir := range dirs { + if err := os.MkdirAll(dir, 0o755); err != nil { + return breverrors.WrapAndTrace(err) + } + } + + // Download files + client := &http.Client{Timeout: 30 * time.Second} + failed := 0 + + for _, file := range skillFiles { + url := fmt.Sprintf("%s/%s", baseURL, file) + destPath := filepath.Join(skillDir, file) + + if err := downloadFile(client, url, destPath); err != nil { + if !quiet { + fmt.Printf(" %s %s\n", t.Red("✗"), file) + } + failed++ + } else if !quiet { + fmt.Printf(" %s %s\n", t.Green("✓"), file) + } + } + + fmt.Println() + + if failed > 0 { + fmt.Printf(" %s %d file(s) failed to download\n", t.Yellow("Warning:"), failed) + } else { + fmt.Printf(" %s Skill installed successfully!\n", t.Green("✓")) + } + + fmt.Println() + fmt.Println(" " + t.Green("Next steps:")) + fmt.Println(" 1. Restart Claude Code (or start a new conversation)") + fmt.Println(" 2. Say " + t.Yellow("\"create a gpu instance\"") + " or use " + t.Yellow("/brev-cli")) + fmt.Println() + + return nil +} + +// UninstallSkill removes the Claude skill +func UninstallSkill(t *terminal.Terminal, homeDir string) error { + skillDir := GetSkillDir(homeDir) + + if !IsSkillInstalled(homeDir) { + fmt.Printf(" Skill not installed at %s\n", skillDir) + return nil + } + + fmt.Printf(" Uninstalling skill from %s\n", skillDir) + + if err := os.RemoveAll(skillDir); err != nil { + return breverrors.WrapAndTrace(err) + } + + fmt.Printf(" %s Skill uninstalled\n", t.Green("✓")) + fmt.Println(" Restart Claude Code to apply changes.") + + return nil +} + +// RunInstallSkillIfWanted prompts and installs if user wants it +// This is called from the login flow +func RunInstallSkillIfWanted(t *terminal.Terminal, homeDir string) { + if PromptInstallSkill(t, homeDir) { + err := InstallSkill(t, homeDir, false) + if err != nil { + // Don't fail login for skill install errors + fmt.Printf(" %s Failed to install skill: %v\n", t.Yellow("Warning:"), err) + } + } +} + +// downloadFile downloads a file from URL to destPath +func downloadFile(client *http.Client, url, destPath string) error { + resp, err := client.Get(url) //nolint:gosec,noctx // URL is hardcoded + if err != nil { + return breverrors.WrapAndTrace(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return breverrors.NewValidationError(fmt.Sprintf("failed to download %s: %s", url, resp.Status)) + } + + // Read body + body, err := io.ReadAll(resp.Body) + if err != nil { + return breverrors.WrapAndTrace(err) + } + + // Write file + if err := os.WriteFile(destPath, body, 0o644); err != nil { //nolint:gosec // skill files are not sensitive + return breverrors.WrapAndTrace(err) + } + + return nil +} + +// PromptInstallSkillSimple is a simpler yes/no prompt for the login flow +func PromptInstallSkillSimple() bool { + reader := bufio.NewReader(os.Stdin) + fmt.Print("Install Claude Code skill? [y/N]: ") + response, _ := reader.ReadString('\n') + response = strings.ToLower(strings.TrimSpace(response)) + return response == "y" || response == "yes" +} diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 2980b0ce..7641a986 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -6,6 +6,7 @@ import ( "github.com/brevdev/brev-cli/pkg/auth" "github.com/brevdev/brev-cli/pkg/cmd/background" + "github.com/brevdev/brev-cli/pkg/cmd/claudeskill" "github.com/brevdev/brev-cli/pkg/cmd/clipboard" "github.com/brevdev/brev-cli/pkg/cmd/configureenvvars" "github.com/brevdev/brev-cli/pkg/cmd/connect" @@ -291,6 +292,7 @@ func createCmdTree(cmd *cobra.Command, t *terminal.Terminal, loginCmdStore *stor cmd.AddCommand(runtasks.NewCmdRunTasks(t, noLoginCmdStore)) cmd.AddCommand(proxy.NewCmdProxy(t, noLoginCmdStore)) cmd.AddCommand(healthcheck.NewCmdHealthcheck(t, noLoginCmdStore)) + cmd.AddCommand(claudeskill.NewCmdClaudeSkill(t, noLoginCmdStore)) cmd.AddCommand(setupworkspace.NewCmdSetupWorkspace(noLoginCmdStore)) cmd.AddCommand(recreate.NewCmdRecreate(t, loginCmdStore)) diff --git a/pkg/cmd/login/login.go b/pkg/cmd/login/login.go index 4b19044f..27312d7f 100644 --- a/pkg/cmd/login/login.go +++ b/pkg/cmd/login/login.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/brevdev/brev-cli/pkg/auth" + "github.com/brevdev/brev-cli/pkg/cmd/claudeskill" "github.com/brevdev/brev-cli/pkg/cmd/cmderrors" "github.com/brevdev/brev-cli/pkg/cmd/hello" @@ -190,6 +191,12 @@ func (o LoginOptions) RunLogin(t *terminal.Terminal, loginToken string, skipBrow return breverrors.WrapAndTrace(err) } + // Offer to install Claude Code skill if Claude is detected + homeDir, err := o.LoginStore.UserHomeDir() + if err == nil { + claudeskill.RunInstallSkillIfWanted(t, homeDir) + } + return nil } diff --git a/scripts/install-claude-skill.sh b/scripts/install-claude-skill.sh new file mode 100755 index 00000000..acbdc7c7 --- /dev/null +++ b/scripts/install-claude-skill.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# +# Brev CLI Claude Code Skill Installer +# +# Installs the brev-cli skill to ~/.claude/skills/brev-cli/ +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash +# +# Or with a specific branch: +# curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/scripts/install-claude-skill.sh | bash -s -- --branch my-branch +# + +set -e + +# Configuration +REPO="brevdev/brev-cli" +BRANCH="main" +SKILL_NAME="brev-cli" +INSTALL_DIR="$HOME/.claude/skills/$SKILL_NAME" +BASE_URL="https://raw.githubusercontent.com/$REPO" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --branch|-b) + BRANCH="$2" + shift 2 + ;; + --uninstall|-u) + UNINSTALL=true + shift + ;; + --help|-h) + echo "Brev CLI Claude Code Skill Installer" + echo "" + echo "Usage:" + echo " install-claude-skill.sh [options]" + echo "" + echo "Options:" + echo " --branch, -b Install from specific branch (default: main)" + echo " --uninstall, -u Uninstall the skill" + echo " --help, -h Show this help message" + exit 0 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + exit 1 + ;; + esac +done + +# Uninstall +if [[ "$UNINSTALL" == "true" ]]; then + if [[ -d "$INSTALL_DIR" ]]; then + echo -e "${YELLOW}Uninstalling brev-cli skill...${NC}" + rm -rf "$INSTALL_DIR" + echo -e "${GREEN}✓ Skill uninstalled from $INSTALL_DIR${NC}" + echo -e "${BLUE}Restart Claude Code to apply changes.${NC}" + else + echo -e "${YELLOW}Skill not installed at $INSTALL_DIR${NC}" + fi + exit 0 +fi + +echo -e "${BLUE}Installing brev-cli Claude Code skill...${NC}" +echo -e " Branch: ${YELLOW}$BRANCH${NC}" +echo -e " Target: ${YELLOW}$INSTALL_DIR${NC}" +echo "" + +# Files to download (relative to .claude/skills/brev-cli/) +FILES=( + "SKILL.md" + "prompts/quick-session.md" + "prompts/ml-training.md" + "prompts/cleanup.md" + "reference/commands.md" + "reference/search-filters.md" + "examples/common-patterns.md" +) + +# Create directory structure +echo -e "${BLUE}Creating directory structure...${NC}" +mkdir -p "$INSTALL_DIR"/{prompts,reference,examples} + +# Download files +echo -e "${BLUE}Downloading skill files...${NC}" +FAILED=0 +for file in "${FILES[@]}"; do + url="$BASE_URL/$BRANCH/.claude/skills/$SKILL_NAME/$file" + dest="$INSTALL_DIR/$file" + + if curl -fsSL "$url" -o "$dest" 2>/dev/null; then + echo -e " ${GREEN}✓${NC} $file" + else + echo -e " ${RED}✗${NC} $file (failed to download)" + FAILED=$((FAILED + 1)) + fi +done + +echo "" + +if [[ $FAILED -gt 0 ]]; then + echo -e "${YELLOW}Warning: $FAILED file(s) failed to download.${NC}" + echo -e "${YELLOW}The skill may not work correctly.${NC}" +else + echo -e "${GREEN}✓ Skill installed successfully!${NC}" +fi + +echo "" +echo -e "${BLUE}Next steps:${NC}" +echo -e " 1. Restart Claude Code (or start a new conversation)" +echo -e " 2. Use ${YELLOW}/brev-cli${NC} or say ${YELLOW}\"create a gpu instance\"${NC}" +echo "" +echo -e "${BLUE}Quick commands:${NC}" +echo -e " ${YELLOW}brev search${NC} # Find available GPUs" +echo -e " ${YELLOW}brev create my-instance${NC} # Create an instance" +echo -e " ${YELLOW}brev ls${NC} # List instances" +echo -e " ${YELLOW}brev shell my-instance${NC} # SSH into instance" +echo ""