diff --git a/README.md b/README.md index 52f8b42..92dff4a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ A command line interface for the CloudAMQP API that provides complete management - **User-Friendly**: Clear help messages, examples, and safety confirmations - **Error Handling**: Proper API error extraction and display +## Claude Code + +Install skills to let Claude Code manage CloudAMQP instances on your behalf: + +```bash +cloudamqp install skills +``` + +This copies skills to `~/.claude/skills/cloudamqp-cli/`. Claude Code discovers them automatically. + ## Installation ### Pre-built binaries diff --git a/cmd/install.go b/cmd/install.go new file mode 100644 index 0000000..0ed5ce9 --- /dev/null +++ b/cmd/install.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +//go:embed all:skills +var skillsFS embed.FS + +var installCmd = &cobra.Command{ + Use: "install", + Short: "Install integrations", +} + +var installSkillsCmd = &cobra.Command{ + Use: "skills", + Short: "Install Claude Code skills to ~/.claude/skills/", + Args: cobra.NoArgs, + Long: `Install the CloudAMQP CLI skills for Claude Code. + +Skills teach Claude how to use the cloudamqp CLI. After installation, +Claude Code will automatically discover and use them. + +Skills are installed to: ~/.claude/skills/cloudamqp-cli/`, + RunE: func(cmd *cobra.Command, args []string) error { + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("could not determine home directory: %w", err) + } + dest := filepath.Join(home, ".claude", "skills", "cloudamqp-cli") + + const embedPrefix = "skills/cloudamqp-cli" + err = fs.WalkDir(skillsFS, embedPrefix, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + // Embedded paths always use forward slashes. Use strings.TrimPrefix + // then filepath.FromSlash so this works correctly on Windows too. + rel := strings.TrimPrefix(strings.TrimPrefix(path, embedPrefix), "/") + target := filepath.Join(dest, filepath.FromSlash(rel)) + if d.IsDir() { + return os.MkdirAll(target, 0755) + } + data, err := skillsFS.ReadFile(path) + if err != nil { + return err + } + return os.WriteFile(target, data, 0644) + }) + if err != nil { + return fmt.Errorf("failed to install skills: %w", err) + } + fmt.Printf("Skills installed to %s\n", dest) + return nil + }, +} + +func init() { + installCmd.AddCommand(installSkillsCmd) + rootCmd.AddCommand(installCmd) +} diff --git a/cmd/install_test.go b/cmd/install_test.go new file mode 100644 index 0000000..116c466 --- /dev/null +++ b/cmd/install_test.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInstallSkillsCmd(t *testing.T) { + home := t.TempDir() + t.Setenv("HOME", home) + + err := installSkillsCmd.RunE(installSkillsCmd, []string{}) + require.NoError(t, err) + + base := filepath.Join(home, ".claude", "skills", "cloudamqp-cli") + assert.FileExists(t, filepath.Join(base, "SKILL.md")) + assert.FileExists(t, filepath.Join(base, "references", "scripting.md")) + assert.FileExists(t, filepath.Join(base, "references", "upgrades.md")) + assert.FileExists(t, filepath.Join(base, "references", "vpc-setup.md")) +} + +func TestInstallSkillsCmd_NoArgs(t *testing.T) { + err := installSkillsCmd.Args(installSkillsCmd, []string{"unexpected"}) + assert.Error(t, err) +} diff --git a/cmd/skills/cloudamqp-cli/SKILL.md b/cmd/skills/cloudamqp-cli/SKILL.md new file mode 100644 index 0000000..1e56fd8 --- /dev/null +++ b/cmd/skills/cloudamqp-cli/SKILL.md @@ -0,0 +1,139 @@ +--- +name: cloudamqp-cli +description: Manage CloudAMQP instances, VPCs, teams, and RabbitMQ/LavinMQ configuration using the cloudamqp CLI. Use this skill whenever the user wants to create, list, inspect, update, delete, upgrade, restart, or troubleshoot CloudAMQP instances — even if they just say "spin up a RabbitMQ", "check my instances", or "upgrade my broker". Also use it for VPC setup, team management, and RabbitMQ config changes. +allowed-tools: Bash(cloudamqp:*), Bash(jq:*), Bash(cat:*), Bash(echo:*), Bash(chmod:*), Bash(grep:*), Bash(sleep:*) +--- + +# CloudAMQP CLI + +## Quick start + +```bash +cloudamqp instance list +cloudamqp instance get --id +cloudamqp instance create --name= --plan= --region= --wait +cloudamqp instance restart-rabbitmq --id +cloudamqp instance delete --id --force +``` + +## Authentication + +Check auth before running anything — interactive prompts don't work in agent context: + +```bash +cat ~/.cloudamqprc 2>/dev/null || echo "not configured" +``` + +If not configured, ask the user for their API key (from https://customer.cloudamqp.com/apikeys), then: + +```bash +echo "" > ~/.cloudamqprc +chmod 600 ~/.cloudamqprc +``` + +Alternatively, set `CLOUDAMQP_APIKEY` in the environment. If neither is set, all commands will fail. + +## Output + +Read commands (`list`, `get`) support `-o json` for machine-readable output and `-o table` (default) for display. Use `--fields` to select columns. Write commands (`create`, `update`, `invite`, etc.) print plain text — they don't support `-o json`. + +## Commands + +### Instance lifecycle + +```bash +cloudamqp instance create --name= --plan= --region= [--tags=...] [--vpc-id=] [--wait] [--wait-timeout=20m] +cloudamqp instance list [--details] +cloudamqp instance get --id +cloudamqp instance update --id [--name=] [--plan=] +cloudamqp instance delete --id [--force] +cloudamqp instance resize-disk --id --disk-size= [--allow-downtime] +``` + +### Copy settings between instances (dedicated only) + +```bash +cloudamqp instance create --name=staging --plan= --region= \ + --copy-from-id= --copy-settings=metrics,firewall,config,alarms,logs,definitions,plugins --wait +``` + +### Node and plugin management + +```bash +cloudamqp instance nodes list --id +cloudamqp instance nodes versions --id +cloudamqp instance plugins list --id +``` + +### RabbitMQ configuration + +```bash +cloudamqp instance config list --id +cloudamqp instance config get --id --key +cloudamqp instance config set --id --key --value +``` + +### Instance actions + +```bash +# restarts (rolling for HA clusters) +cloudamqp instance restart-rabbitmq --id [--nodes=node1,node2] +cloudamqp instance restart-cluster --id # full restart, causes downtime +cloudamqp instance restart-management --id + +# start/stop +cloudamqp instance start --id +cloudamqp instance stop --id +cloudamqp instance reboot --id +cloudamqp instance start-cluster --id +cloudamqp instance stop-cluster --id + +# upgrades — async, return immediately, poll until ready +cloudamqp instance upgrade-erlang --id +cloudamqp instance upgrade-rabbitmq --id --version= +cloudamqp instance upgrade-all --id +cloudamqp instance upgrade-versions --id +``` + +### VPC management + +```bash +cloudamqp vpc create --name= --region= --subnet= +cloudamqp vpc list +cloudamqp vpc get --id +cloudamqp vpc update --id --name= +cloudamqp vpc delete --id +``` + +### Team management + +```bash +cloudamqp team list +cloudamqp team invite --email= [--role=] [--tags=] +cloudamqp team update --user-id= --role= +cloudamqp team remove --email= +``` + +### Plans, regions, audit + +```bash +cloudamqp plans [--backend=rabbitmq|lavinmq] # always fetch, never guess +cloudamqp regions [--provider=] # always fetch, never guess +cloudamqp audit [--timestamp=2024-01] +cloudamqp rotate-key +``` + +## Key behaviors + +- **Async**: creation, resizes, upgrades return immediately. Use `--wait` on create, or poll `instance get --id -o json | jq -r '.ready'` until `"Yes"`. +- **Destructive commands** prompt for confirmation — use `--force` to skip. +- **Multiple tags**: repeat the flag: `--tags=prod --tags=web`. +- **Plan/region names**: always run `cloudamqp plans` / `cloudamqp regions` first — never hardcode them. + +## Reference guides + +Read these before tackling the relevant task: + +- **Scripting, JSON parsing, batch ops** → [references/scripting.md](references/scripting.md) +- **Upgrades, restarts, maintenance workflows** → [references/upgrades.md](references/upgrades.md) +- **VPC creation and network setup** → [references/vpc-setup.md](references/vpc-setup.md) diff --git a/cmd/skills/cloudamqp-cli/references/scripting.md b/cmd/skills/cloudamqp-cli/references/scripting.md new file mode 100644 index 0000000..3b4f5a9 --- /dev/null +++ b/cmd/skills/cloudamqp-cli/references/scripting.md @@ -0,0 +1,77 @@ +# Scripting and automation + +## JSON output for parsing + +Read commands (`list`, `get`, `plans`, `regions`) support `-o json`. All values come out as strings. + +```bash +# get connection URL for an instance (masked; add --show-url for full URL) +cloudamqp instance get --id -o json | jq -r '.url' + +# find instances that aren't ready (requires --details; ready is "Yes"/"No" string) +cloudamqp instance list --details -o json | jq -r '.[] | select(.ready == "No") | "\(.id) \(.name)"' + +# get IDs matching a tag (requires --details; tags is a comma-joined string) +cloudamqp instance list --details -o json | jq -r '.[] | select(.tags | split(",") | map(ltrimstr(" ")) | contains(["staging"])) | .id' +``` + +## Create and capture instance ID + +`instance create` prints a human-readable prefix before the JSON, so pipe through `tail -n +2`: + +```bash +# fetch a valid plan and region first +PLAN=$(cloudamqp plans --backend=rabbitmq -o json | jq -r '.[0].name') +REGION=$(cloudamqp regions -o json | jq -r '.[0].id') + +RESULT=$(cloudamqp instance create --name=temp --plan="$PLAN" --region="$REGION" | tail -n +2) +INSTANCE_ID=$(echo "$RESULT" | jq -r '.id') +``` + +## Wait for instance readiness + +Prefer the built-in flag: + +```bash +cloudamqp instance create --name=my-instance --plan= --region= --wait --wait-timeout=20m +``` + +Or poll manually: + +```bash +while true; do + STATUS=$(cloudamqp instance get --id "$INSTANCE_ID" -o json | jq -r '.ready') + [ "$STATUS" = "Yes" ] && break + sleep 30 +done +``` + +## Skip confirmations in scripts + +```bash +cloudamqp instance delete --id --force +cloudamqp vpc delete --id --force +``` + +## Batch operations + +```bash +# restart all instances tagged "staging" (--details required for tags field) +for ID in $(cloudamqp instance list --details -o json | jq -r '.[] | select(.tags | split(",") | map(ltrimstr(" ")) | contains(["staging"])) | .id'); do + cloudamqp instance restart-rabbitmq --id "$ID" +done +``` + +## Clone an instance with full config + +```bash +cloudamqp instance create \ + --name=staging-copy \ + --plan= \ + --region= \ + --copy-from-id= \ + --copy-settings=alarms,metrics,logs,firewall,config,definitions,plugins \ + --wait +``` + +Only works between dedicated instances (not shared plans). diff --git a/cmd/skills/cloudamqp-cli/references/upgrades.md b/cmd/skills/cloudamqp-cli/references/upgrades.md new file mode 100644 index 0000000..8e387a2 --- /dev/null +++ b/cmd/skills/cloudamqp-cli/references/upgrades.md @@ -0,0 +1,91 @@ +# Instance upgrades and maintenance + +## Check available upgrade versions + +```bash +cloudamqp instance upgrade-versions --id 1234 +cloudamqp instance nodes versions --id 1234 +``` + +## Upgrade paths + +### Upgrade everything (recommended) + +Upgrades both Erlang and RabbitMQ/LavinMQ to the latest compatible versions: + +```bash +cloudamqp instance upgrade-all --id 1234 +``` + +### Upgrade individually + +```bash +# erlang first, then RabbitMQ +cloudamqp instance upgrade-erlang --id 1234 +cloudamqp instance upgrade-rabbitmq --id 1234 --version=3.13.0 +``` + +Always upgrade Erlang before RabbitMQ when doing both separately. + +## Restart operations + +```bash +# restart RabbitMQ process (rolling for HA clusters) +cloudamqp instance restart-rabbitmq --id 1234 + +# restart specific nodes only +cloudamqp instance restart-rabbitmq --id 1234 --nodes=node1,node2 + +# full cluster restart +cloudamqp instance restart-cluster --id 1234 + +# restart management interface only +cloudamqp instance restart-management --id 1234 +``` + +## Start, stop, and reboot + +```bash +cloudamqp instance stop --id 1234 +cloudamqp instance start --id 1234 +cloudamqp instance reboot --id 1234 + +# cluster-level +cloudamqp instance stop-cluster --id 1234 +cloudamqp instance start-cluster --id 1234 +``` + +## Disk resize + +```bash +# resize disk (may require downtime) +cloudamqp instance resize-disk --id 1234 --disk-size=100 --allow-downtime +``` + +## Maintenance workflow + +A typical maintenance sequence: + +```bash +# 1. check current state +cloudamqp instance get --id 1234 +cloudamqp instance nodes list --id 1234 + +# 2. check available upgrades +cloudamqp instance upgrade-versions --id 1234 + +# 3. perform upgrade +cloudamqp instance upgrade-all --id 1234 + +# 4. verify after upgrade +cloudamqp instance nodes list --id 1234 +cloudamqp instance nodes versions --id 1234 +``` + +## Important notes + +- Upgrade operations are **async**: they return immediately but run in the background. +- Poll `instance get --id ` to check when the instance is ready again. +- Plan changes via `instance update --id --plan=` may cause brief downtime. +- `restart-rabbitmq` does a rolling restart on HA clusters (minimal disruption). +- `restart-cluster` restarts all nodes simultaneously (causes downtime). diff --git a/cmd/skills/cloudamqp-cli/references/vpc-setup.md b/cmd/skills/cloudamqp-cli/references/vpc-setup.md new file mode 100644 index 0000000..504e4ec --- /dev/null +++ b/cmd/skills/cloudamqp-cli/references/vpc-setup.md @@ -0,0 +1,63 @@ +# VPC and network setup + +## Create a VPC + +```bash +cloudamqp vpc create --name=prod-vpc --region=amazon-web-services::us-east-1 --subnet=10.56.72.0/24 +``` + +The region must match the region of any instances you want to place in the VPC. + +## Create an instance in a VPC + +```bash +cloudamqp instance create \ + --name=prod-broker \ + --plan=rabbit-1 \ + --region=amazon-web-services::us-east-1 \ + --vpc-id=5678 \ + --wait +``` + +## List and inspect VPCs + +```bash +cloudamqp vpc list +cloudamqp vpc get --id 5678 +``` + +## Update VPC name + +```bash +cloudamqp vpc update --id 5678 --name=new-vpc-name +``` + +## Delete a VPC + +```bash +cloudamqp vpc delete --id 5678 +``` + +Remove all instances from the VPC before deleting it. + +## Typical setup workflow + +```bash +# 1. pick a region +cloudamqp regions --provider=amazon-web-services + +# 2. create VPC +cloudamqp vpc create --name=prod-vpc --region=amazon-web-services::us-east-1 --subnet=10.0.0.0/24 + +# 3. create instance in the VPC +cloudamqp instance create \ + --name=prod-broker \ + --plan=rabbit-1 \ + --region=amazon-web-services::us-east-1 \ + --vpc-id= \ + --wait + +# 4. verify +cloudamqp instance get --id +cloudamqp vpc get --id +```