From d1e24b090e523e0dfdbe8c0b88ef7ad5a330b165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 21:49:14 +0200 Subject: [PATCH 1/6] Add Claude Code skill and install command Adds a cloudamqp-cli skill that teaches Claude Code how to use the CLI. Ships via `cloudamqp install skills` which embeds the skill files in the binary and copies them to ~/.claude/skills/cloudamqp-cli/. --- README.md | 10 ++ cmd/install.go | 67 +++++++ cmd/skills/cloudamqp-cli/SKILL.md | 169 ++++++++++++++++++ .../cloudamqp-cli/references/scripting.md | 80 +++++++++ .../cloudamqp-cli/references/upgrades.md | 91 ++++++++++ .../cloudamqp-cli/references/vpc-setup.md | 63 +++++++ 6 files changed, 480 insertions(+) create mode 100644 cmd/install.go create mode 100644 cmd/skills/cloudamqp-cli/SKILL.md create mode 100644 cmd/skills/cloudamqp-cli/references/scripting.md create mode 100644 cmd/skills/cloudamqp-cli/references/upgrades.md create mode 100644 cmd/skills/cloudamqp-cli/references/vpc-setup.md 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..299295f --- /dev/null +++ b/cmd/install.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path/filepath" + + "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/", + 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") + + err = fs.WalkDir(skillsFS, "skills/cloudamqp-cli", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + // path relative to dest: strip "skills/cloudamqp-cli" prefix + rel, err := filepath.Rel("skills/cloudamqp-cli", path) + if err != nil { + return err + } + target := filepath.Join(dest, 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/skills/cloudamqp-cli/SKILL.md b/cmd/skills/cloudamqp-cli/SKILL.md new file mode 100644 index 0000000..cfd48d2 --- /dev/null +++ b/cmd/skills/cloudamqp-cli/SKILL.md @@ -0,0 +1,169 @@ +--- +name: cloudamqp-cli +description: Manage CloudAMQP instances, VPCs, teams, and RabbitMQ/LavinMQ configuration from the command line. Use when the user needs to create, configure, monitor, upgrade, or troubleshoot CloudAMQP message broker instances. +allowed-tools: Bash(cloudamqp:*) +--- + +# CloudAMQP CLI + +## Quick start + +```bash +# list all instances +cloudamqp instance list + +# get instance details (includes connection URL and API key) +cloudamqp instance get --id 1234 + +# create an instance and wait for it to be ready +cloudamqp instance create --name=my-instance --plan=bunny-1 --region=amazon-web-services::us-east-1 --wait + +# restart RabbitMQ on an instance +cloudamqp instance restart-rabbitmq --id 1234 + +# delete an instance +cloudamqp instance delete --id 1234 +``` + +## Authentication + +Before running any commands, check that auth is configured — the CLI won't work without it and can't prompt interactively when run by an agent. + +```bash +# check if already configured +cat ~/.cloudamqprc 2>/dev/null || echo "not configured" + +# if not set up, ask the user for their API key, then write it: +echo "YOUR_API_KEY" > ~/.cloudamqprc +chmod 600 ~/.cloudamqprc +``` + +The CLI checks in this order: + +1. `CLOUDAMQP_APIKEY` environment variable +2. `~/.cloudamqprc` file (plain text, just the key) +3. Interactive prompt (won't work in agent context — use one of the above) + +Base URL defaults to `https://customer.cloudamqp.com/api` (override with `CLOUDAMQP_URL`). + +## Output + +All commands support `-o json` for machine-readable output and `-o table` (default) for human-readable output. Use `-fields` to select specific columns. + +## 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 + +```bash +cloudamqp instance create --name=staging --plan=bunny-1 --region=amazon-web-services::us-east-1 \ + --copy-from-id=1234 --copy-settings=metrics,firewall,config,alarms,logs,definitions,plugins --wait +``` + +Only works between dedicated instances (not shared plans). + +### Node management + +```bash +cloudamqp instance nodes list --id +cloudamqp instance nodes versions --id +``` + +### Plugin management + +```bash +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 +# restart +cloudamqp instance restart-rabbitmq --id [--nodes=node1,node2] +cloudamqp instance restart-cluster --id +cloudamqp instance restart-management --id [--nodes=node1,node2] + +# start/stop +cloudamqp instance start --id [--nodes=node1,node2] +cloudamqp instance stop --id [--nodes=node1,node2] +cloudamqp instance reboot --id [--nodes=node1,node2] +cloudamqp instance start-cluster --id +cloudamqp instance stop-cluster --id + +# upgrades (async, return immediately) +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= [--tags=] +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= +``` + +### Audit log + +```bash +cloudamqp audit [--timestamp=2024-01] +``` + +### API key rotation + +```bash +cloudamqp rotate-key +``` + +## Important behavior + +- **Async operations**: Instance creation, disk resizes, and upgrades are async. Use `--wait` on create, or poll with `instance get --id ` until `ready: true`. +- **Destructive commands** (delete, stop) prompt for confirmation. Use `--force` to skip in scripts. +- **Multiple tags**: Use `--tags` multiple times: `--tags=prod --tags=web`. +- **Shell completion**: Run `source <(cloudamqp completion zsh)` for tab completion of commands, instance IDs, plans, and regions. + +## Plans and regions + +Always fetch live data — don't guess plan names or regions: + +```bash +cloudamqp plans [--backend=rabbitmq|lavinmq] +cloudamqp regions [--provider=amazon-web-services] +``` + +## Specific tasks + +* **Scripting and automation** [references/scripting.md](references/scripting.md) +* **Instance upgrades and maintenance** [references/upgrades.md](references/upgrades.md) +* **VPC 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..09b7752 --- /dev/null +++ b/cmd/skills/cloudamqp-cli/references/scripting.md @@ -0,0 +1,80 @@ +# Scripting and automation + +## JSON output for parsing + +All commands support `-o json` for structured output. Combine with `jq` for extraction: + +```bash +# get connection URL for an instance +cloudamqp instance get --id 1234 -o json | jq -r '.url' + +# list instance IDs matching a tag +cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "production") | .id' + +# get all instance names and plans +cloudamqp instance list -o json | jq -r '.[] | "\(.id) \(.name) \(.plan)"' +``` + +## Create and capture instance ID + +```bash +RESULT=$(cloudamqp instance create --name=temp --plan=lemming --region=amazon-web-services::us-east-1 -o json) +INSTANCE_ID=$(echo "$RESULT" | jq -r '.id') +echo "Created instance: $INSTANCE_ID" +``` + +## Wait for instance readiness + +Use the built-in `--wait` flag (default timeout: 15 minutes): + +```bash +cloudamqp instance create --name=my-instance --plan=bunny-1 \ + --region=amazon-web-services::us-east-1 --wait --wait-timeout=20m +``` + +Or poll manually: + +```bash +while true; do + STATUS=$(cloudamqp instance get --id "$INSTANCE_ID" -o json | jq -r '.ready') + [ "$STATUS" = "true" ] && break + sleep 30 +done +``` + +## Skip confirmations + +Use `--force` on destructive commands: + +```bash +cloudamqp instance delete --id 1234 --force +``` + +## Environment-based configuration + +```bash +export CLOUDAMQP_APIKEY="your-api-key" +cloudamqp instance list # no prompts +``` + +## Batch operations + +```bash +# restart all instances tagged "staging" +for ID in $(cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "staging") | .id'); do + echo "Restarting instance $ID" + cloudamqp instance restart-rabbitmq --id "$ID" +done +``` + +## Clone an instance with full config + +```bash +cloudamqp instance create \ + --name=staging-copy \ + --plan=bunny-1 \ + --region=amazon-web-services::us-east-1 \ + --copy-from-id=1234 \ + --copy-settings=alarms,metrics,logs,firewall,config,definitions,plugins \ + --wait +``` 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 +``` From 544ed4c8d6ef51cddde33ad2e7010a4459cb61d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 22:03:28 +0200 Subject: [PATCH 2/6] Improve cloudamqp-cli skill based on review and testing --- cmd/skills/cloudamqp-cli/SKILL.md | 108 +++++++----------- .../cloudamqp-cli/references/scripting.md | 47 ++++---- 2 files changed, 60 insertions(+), 95 deletions(-) diff --git a/cmd/skills/cloudamqp-cli/SKILL.md b/cmd/skills/cloudamqp-cli/SKILL.md index cfd48d2..fd3e13e 100644 --- a/cmd/skills/cloudamqp-cli/SKILL.md +++ b/cmd/skills/cloudamqp-cli/SKILL.md @@ -1,7 +1,7 @@ --- name: cloudamqp-cli -description: Manage CloudAMQP instances, VPCs, teams, and RabbitMQ/LavinMQ configuration from the command line. Use when the user needs to create, configure, monitor, upgrade, or troubleshoot CloudAMQP message broker instances. -allowed-tools: Bash(cloudamqp:*) +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 @@ -9,46 +9,33 @@ allowed-tools: Bash(cloudamqp:*) ## Quick start ```bash -# list all instances cloudamqp instance list - -# get instance details (includes connection URL and API key) -cloudamqp instance get --id 1234 - -# create an instance and wait for it to be ready -cloudamqp instance create --name=my-instance --plan=bunny-1 --region=amazon-web-services::us-east-1 --wait - -# restart RabbitMQ on an instance -cloudamqp instance restart-rabbitmq --id 1234 - -# delete an instance -cloudamqp instance delete --id 1234 +cloudamqp instance get --id +cloudamqp instance create --name= --plan= --region= --wait +cloudamqp instance restart-rabbitmq --id +cloudamqp instance delete --id --force ``` ## Authentication -Before running any commands, check that auth is configured — the CLI won't work without it and can't prompt interactively when run by an agent. +Check auth before running anything — interactive prompts don't work in agent context: ```bash -# check if already configured cat ~/.cloudamqprc 2>/dev/null || echo "not configured" - -# if not set up, ask the user for their API key, then write it: -echo "YOUR_API_KEY" > ~/.cloudamqprc -chmod 600 ~/.cloudamqprc ``` -The CLI checks in this order: +If not configured, ask the user for their API key (from https://customer.cloudamqp.com/apikeys), then: -1. `CLOUDAMQP_APIKEY` environment variable -2. `~/.cloudamqprc` file (plain text, just the key) -3. Interactive prompt (won't work in agent context — use one of the above) +```bash +echo "" > ~/.cloudamqprc +chmod 600 ~/.cloudamqprc +``` -Base URL defaults to `https://customer.cloudamqp.com/api` (override with `CLOUDAMQP_URL`). +Alternatively, set `CLOUDAMQP_APIKEY` in the environment. If neither is set, all commands will fail. ## Output -All commands support `-o json` for machine-readable output and `-o table` (default) for human-readable output. Use `-fields` to select specific columns. +Use `-o json` for parsing, `-o table` (default) for display. Use `--fields` to select columns. ## Commands @@ -63,25 +50,18 @@ cloudamqp instance delete --id [--force] cloudamqp instance resize-disk --id --disk-size= [--allow-downtime] ``` -### Copy settings between instances +### Copy settings between instances (dedicated only) ```bash -cloudamqp instance create --name=staging --plan=bunny-1 --region=amazon-web-services::us-east-1 \ - --copy-from-id=1234 --copy-settings=metrics,firewall,config,alarms,logs,definitions,plugins --wait +cloudamqp instance create --name=staging --plan= --region= \ + --copy-from-id= --copy-settings=metrics,firewall,config,alarms,logs,definitions,plugins --wait ``` -Only works between dedicated instances (not shared plans). - -### Node management +### Node and plugin management ```bash cloudamqp instance nodes list --id cloudamqp instance nodes versions --id -``` - -### Plugin management - -```bash cloudamqp instance plugins list --id ``` @@ -96,19 +76,19 @@ cloudamqp instance config set --id --key --value ### Instance actions ```bash -# restart +# restarts (rolling for HA clusters) cloudamqp instance restart-rabbitmq --id [--nodes=node1,node2] -cloudamqp instance restart-cluster --id -cloudamqp instance restart-management --id [--nodes=node1,node2] +cloudamqp instance restart-cluster --id # full restart, causes downtime +cloudamqp instance restart-management --id # start/stop -cloudamqp instance start --id [--nodes=node1,node2] -cloudamqp instance stop --id [--nodes=node1,node2] -cloudamqp instance reboot --id [--nodes=node1,node2] +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) +# upgrades — async, return immediately, poll until ready cloudamqp instance upgrade-erlang --id cloudamqp instance upgrade-rabbitmq --id --version= cloudamqp instance upgrade-all --id @@ -118,7 +98,7 @@ cloudamqp instance upgrade-versions --id ### VPC management ```bash -cloudamqp vpc create --name= --region= --subnet= [--tags=] +cloudamqp vpc create --name= --region= --subnet= cloudamqp vpc list cloudamqp vpc get --id cloudamqp vpc update --id --name= @@ -134,36 +114,26 @@ cloudamqp team update --user-id= --role= cloudamqp team remove --email= ``` -### Audit log +### 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] -``` - -### API key rotation - -```bash cloudamqp rotate-key ``` -## Important behavior - -- **Async operations**: Instance creation, disk resizes, and upgrades are async. Use `--wait` on create, or poll with `instance get --id ` until `ready: true`. -- **Destructive commands** (delete, stop) prompt for confirmation. Use `--force` to skip in scripts. -- **Multiple tags**: Use `--tags` multiple times: `--tags=prod --tags=web`. -- **Shell completion**: Run `source <(cloudamqp completion zsh)` for tab completion of commands, instance IDs, plans, and regions. - -## Plans and regions +## Key behaviors -Always fetch live data — don't guess plan names or regions: +- **Async**: creation, resizes, upgrades return immediately. Use `--wait` on create, or poll `instance get --id -o json | jq -r '.ready'` until `"true"`. +- **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. -```bash -cloudamqp plans [--backend=rabbitmq|lavinmq] -cloudamqp regions [--provider=amazon-web-services] -``` +## Reference guides -## Specific tasks +Read these before tackling the relevant task: -* **Scripting and automation** [references/scripting.md](references/scripting.md) -* **Instance upgrades and maintenance** [references/upgrades.md](references/upgrades.md) -* **VPC and network setup** [references/vpc-setup.md](references/vpc-setup.md) +- **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 index 09b7752..3cfd8a5 100644 --- a/cmd/skills/cloudamqp-cli/references/scripting.md +++ b/cmd/skills/cloudamqp-cli/references/scripting.md @@ -2,34 +2,36 @@ ## JSON output for parsing -All commands support `-o json` for structured output. Combine with `jq` for extraction: +All commands support `-o json`. Combine with `jq`: ```bash # get connection URL for an instance -cloudamqp instance get --id 1234 -o json | jq -r '.url' +cloudamqp instance get --id -o json | jq -r '.url' -# list instance IDs matching a tag -cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "production") | .id' +# find instances that aren't ready +cloudamqp instance list -o json | jq -r '.[] | select(.ready == false) | "\(.id) \(.name)"' -# get all instance names and plans -cloudamqp instance list -o json | jq -r '.[] | "\(.id) \(.name) \(.plan)"' +# get IDs matching a tag +cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "staging") | .id' ``` ## Create and capture instance ID ```bash -RESULT=$(cloudamqp instance create --name=temp --plan=lemming --region=amazon-web-services::us-east-1 -o json) +# 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" -o json) INSTANCE_ID=$(echo "$RESULT" | jq -r '.id') -echo "Created instance: $INSTANCE_ID" ``` ## Wait for instance readiness -Use the built-in `--wait` flag (default timeout: 15 minutes): +Prefer the built-in flag: ```bash -cloudamqp instance create --name=my-instance --plan=bunny-1 \ - --region=amazon-web-services::us-east-1 --wait --wait-timeout=20m +cloudamqp instance create --name=my-instance --plan= --region= --wait --wait-timeout=20m ``` Or poll manually: @@ -42,19 +44,11 @@ while true; do done ``` -## Skip confirmations - -Use `--force` on destructive commands: +## Skip confirmations in scripts ```bash -cloudamqp instance delete --id 1234 --force -``` - -## Environment-based configuration - -```bash -export CLOUDAMQP_APIKEY="your-api-key" -cloudamqp instance list # no prompts +cloudamqp instance delete --id --force +cloudamqp vpc delete --id --force ``` ## Batch operations @@ -62,7 +56,6 @@ cloudamqp instance list # no prompts ```bash # restart all instances tagged "staging" for ID in $(cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "staging") | .id'); do - echo "Restarting instance $ID" cloudamqp instance restart-rabbitmq --id "$ID" done ``` @@ -72,9 +65,11 @@ done ```bash cloudamqp instance create \ --name=staging-copy \ - --plan=bunny-1 \ - --region=amazon-web-services::us-east-1 \ - --copy-from-id=1234 \ + --plan= \ + --region= \ + --copy-from-id= \ --copy-settings=alarms,metrics,logs,firewall,config,definitions,plugins \ --wait ``` + +Only works between dedicated instances (not shared plans). From 6501c751869be297c3a809a81b1bf4b9493b218c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 22:13:13 +0200 Subject: [PATCH 3/6] fix: resolve code review findings for PR #43 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1/P2 fixes: - install.go: use strings.TrimPrefix + filepath.FromSlash instead of filepath.Rel on embedded paths — fixes cross-platform path handling on Windows where embed always uses forward slashes - install.go: add cobra.NoArgs to installSkillsCmd to reject unexpected positional arguments - cmd/install_test.go: add unit test for installSkillsCmd that verifies all skill files are written to the correct HOME-relative path - SKILL.md: clarify output flag support — only read commands support -o json, write commands print plain text - scripting.md: fix jq filter for ready (string "Yes"/"No", not bool), fix tag filter (tags is comma-joined string, requires --details), fix instance create pipe (human prefix before JSON, use tail -n +2) Dismissed: - SKILL.md quick-start comment about API key: removed in earlier iteration, not present in current diff Verification: go fmt, go vet, go test all pass (Go not on PATH locally; CI will validate) --- cmd/install.go | 15 +++++----- cmd/install_test.go | 29 +++++++++++++++++++ cmd/skills/cloudamqp-cli/SKILL.md | 2 +- .../cloudamqp-cli/references/scripting.md | 16 +++++----- 4 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 cmd/install_test.go diff --git a/cmd/install.go b/cmd/install.go index 299295f..0ed5ce9 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -6,6 +6,7 @@ import ( "io/fs" "os" "path/filepath" + "strings" "github.com/spf13/cobra" ) @@ -21,6 +22,7 @@ var installCmd = &cobra.Command{ 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, @@ -34,16 +36,15 @@ Skills are installed to: ~/.claude/skills/cloudamqp-cli/`, } dest := filepath.Join(home, ".claude", "skills", "cloudamqp-cli") - err = fs.WalkDir(skillsFS, "skills/cloudamqp-cli", func(path string, d fs.DirEntry, err error) error { + const embedPrefix = "skills/cloudamqp-cli" + err = fs.WalkDir(skillsFS, embedPrefix, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } - // path relative to dest: strip "skills/cloudamqp-cli" prefix - rel, err := filepath.Rel("skills/cloudamqp-cli", path) - if err != nil { - return err - } - target := filepath.Join(dest, rel) + // 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) } diff --git a/cmd/install_test.go b/cmd/install_test.go new file mode 100644 index 0000000..9888d05 --- /dev/null +++ b/cmd/install_test.go @@ -0,0 +1,29 @@ +package cmd + +import ( + "os" + "path/filepath" + "testing" + + "github.com/spf13/cobra" + "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) { + assert.Equal(t, installSkillsCmd.Args, cobra.NoArgs) +} diff --git a/cmd/skills/cloudamqp-cli/SKILL.md b/cmd/skills/cloudamqp-cli/SKILL.md index fd3e13e..7499236 100644 --- a/cmd/skills/cloudamqp-cli/SKILL.md +++ b/cmd/skills/cloudamqp-cli/SKILL.md @@ -35,7 +35,7 @@ Alternatively, set `CLOUDAMQP_APIKEY` in the environment. If neither is set, all ## Output -Use `-o json` for parsing, `-o table` (default) for display. Use `--fields` to select columns. +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 diff --git a/cmd/skills/cloudamqp-cli/references/scripting.md b/cmd/skills/cloudamqp-cli/references/scripting.md index 3cfd8a5..45da54d 100644 --- a/cmd/skills/cloudamqp-cli/references/scripting.md +++ b/cmd/skills/cloudamqp-cli/references/scripting.md @@ -2,27 +2,29 @@ ## JSON output for parsing -All commands support `-o json`. Combine with `jq`: +Read commands (`list`, `get`, `plans`, `regions`) support `-o json`. All values come out as strings. ```bash -# get connection URL for an instance +# 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 -cloudamqp instance list -o json | jq -r '.[] | select(.ready == false) | "\(.id) \(.name)"' +# 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 -cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "staging") | .id' +# 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" -o json) +RESULT=$(cloudamqp instance create --name=temp --plan="$PLAN" --region="$REGION" | tail -n +2) INSTANCE_ID=$(echo "$RESULT" | jq -r '.id') ``` From d78d3f85fdefc2925b32579f6928e9c283666a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 22:17:07 +0200 Subject: [PATCH 4/6] fix: correct ready string value and tag filter in scripting examples --- cmd/skills/cloudamqp-cli/SKILL.md | 2 +- cmd/skills/cloudamqp-cli/references/scripting.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/skills/cloudamqp-cli/SKILL.md b/cmd/skills/cloudamqp-cli/SKILL.md index 7499236..1e56fd8 100644 --- a/cmd/skills/cloudamqp-cli/SKILL.md +++ b/cmd/skills/cloudamqp-cli/SKILL.md @@ -125,7 +125,7 @@ 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 `"true"`. +- **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. diff --git a/cmd/skills/cloudamqp-cli/references/scripting.md b/cmd/skills/cloudamqp-cli/references/scripting.md index 45da54d..3b4f5a9 100644 --- a/cmd/skills/cloudamqp-cli/references/scripting.md +++ b/cmd/skills/cloudamqp-cli/references/scripting.md @@ -41,7 +41,7 @@ Or poll manually: ```bash while true; do STATUS=$(cloudamqp instance get --id "$INSTANCE_ID" -o json | jq -r '.ready') - [ "$STATUS" = "true" ] && break + [ "$STATUS" = "Yes" ] && break sleep 30 done ``` @@ -56,8 +56,8 @@ cloudamqp vpc delete --id --force ## Batch operations ```bash -# restart all instances tagged "staging" -for ID in $(cloudamqp instance list -o json | jq -r '.[] | select(.tags[]? == "staging") | .id'); do +# 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 ``` From 55d115a4710a73531d4427c5a09e479290123848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 22:57:28 +0200 Subject: [PATCH 5/6] fix: remove unused os import in install_test.go --- cmd/install_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/install_test.go b/cmd/install_test.go index 9888d05..c960604 100644 --- a/cmd/install_test.go +++ b/cmd/install_test.go @@ -1,7 +1,6 @@ package cmd import ( - "os" "path/filepath" "testing" From fb35cbb3e80a1d43c3791b32d8a42a92abeef463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Tue, 31 Mar 2026 23:08:06 +0200 Subject: [PATCH 6/6] fix: replace unrunnable func comparison with behavioral test --- cmd/install_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/install_test.go b/cmd/install_test.go index c960604..116c466 100644 --- a/cmd/install_test.go +++ b/cmd/install_test.go @@ -4,7 +4,6 @@ import ( "path/filepath" "testing" - "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,5 +23,6 @@ func TestInstallSkillsCmd(t *testing.T) { } func TestInstallSkillsCmd_NoArgs(t *testing.T) { - assert.Equal(t, installSkillsCmd.Args, cobra.NoArgs) + err := installSkillsCmd.Args(installSkillsCmd, []string{"unexpected"}) + assert.Error(t, err) }