Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 68 additions & 0 deletions cmd/install.go
Original file line number Diff line number Diff line change
@@ -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/`,
Comment on lines +22 to +31
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider setting Args: cobra.NoArgs on installSkillsCmd (and possibly installCmd) to match the rest of the CLI commands and to avoid silently accepting unexpected positional arguments (e.g. cloudamqp install skills extra).

Copilot uses AI. Check for mistakes.
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
},
Comment on lines +22 to +62
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing command-level tests in cmd/command_test.go, but the new install skills behavior isn’t covered. Please add a unit test that runs installSkillsCmd with a temporary HOME/USERPROFILE and asserts that SKILL.md and the references/ files are created under ~/.claude/skills/cloudamqp-cli/.

Copilot uses AI. Check for mistakes.
}

func init() {
installCmd.AddCommand(installSkillsCmd)
rootCmd.AddCommand(installCmd)
}
28 changes: 28 additions & 0 deletions cmd/install_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
139 changes: 139 additions & 0 deletions cmd/skills/cloudamqp-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -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 <id>
cloudamqp instance create --name=<name> --plan=<plan> --region=<region> --wait
cloudamqp instance restart-rabbitmq --id <id>
cloudamqp instance delete --id <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 "<api-key>" > ~/.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=<name> --plan=<plan> --region=<region> [--tags=<tag>...] [--vpc-id=<id>] [--wait] [--wait-timeout=20m]
cloudamqp instance list [--details]
cloudamqp instance get --id <id>
cloudamqp instance update --id <id> [--name=<name>] [--plan=<plan>]
cloudamqp instance delete --id <id> [--force]
cloudamqp instance resize-disk --id <id> --disk-size=<gb> [--allow-downtime]
```

### Copy settings between instances (dedicated only)

```bash
cloudamqp instance create --name=staging --plan=<plan> --region=<region> \
--copy-from-id=<id> --copy-settings=metrics,firewall,config,alarms,logs,definitions,plugins --wait
```

### Node and plugin management

```bash
cloudamqp instance nodes list --id <id>
cloudamqp instance nodes versions --id <id>
cloudamqp instance plugins list --id <id>
```

### RabbitMQ configuration

```bash
cloudamqp instance config list --id <id>
cloudamqp instance config get --id <id> --key <key>
cloudamqp instance config set --id <id> --key <key> --value <value>
```

### Instance actions

```bash
# restarts (rolling for HA clusters)
cloudamqp instance restart-rabbitmq --id <id> [--nodes=node1,node2]
cloudamqp instance restart-cluster --id <id> # full restart, causes downtime
cloudamqp instance restart-management --id <id>

# start/stop
cloudamqp instance start --id <id>
cloudamqp instance stop --id <id>
cloudamqp instance reboot --id <id>
cloudamqp instance start-cluster --id <id>
cloudamqp instance stop-cluster --id <id>

# upgrades — async, return immediately, poll until ready
cloudamqp instance upgrade-erlang --id <id>
cloudamqp instance upgrade-rabbitmq --id <id> --version=<version>
cloudamqp instance upgrade-all --id <id>
cloudamqp instance upgrade-versions --id <id>
```

### VPC management

```bash
cloudamqp vpc create --name=<name> --region=<region> --subnet=<cidr>
cloudamqp vpc list
cloudamqp vpc get --id <id>
cloudamqp vpc update --id <id> --name=<name>
cloudamqp vpc delete --id <id>
```

### Team management

```bash
cloudamqp team list
cloudamqp team invite --email=<email> [--role=<role>] [--tags=<tag>]
cloudamqp team update --user-id=<id> --role=<role>
cloudamqp team remove --email=<email>
```

### Plans, regions, audit

```bash
cloudamqp plans [--backend=rabbitmq|lavinmq] # always fetch, never guess
cloudamqp regions [--provider=<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 <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)
77 changes: 77 additions & 0 deletions cmd/skills/cloudamqp-cli/references/scripting.md
Original file line number Diff line number Diff line change
@@ -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 <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)"'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--details and -o json isn't valid flags for the CLI


# 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=<plan> --region=<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 <id> --force
cloudamqp vpc delete --id <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=<plan> \
--region=<region> \
--copy-from-id=<source-id> \
--copy-settings=alarms,metrics,logs,firewall,config,definitions,plugins \
--wait
```

Only works between dedicated instances (not shared plans).
Loading
Loading