diff options
| author | Clawd <ai@clawd.bot> | 2026-04-11 20:43:41 -0700 |
|---|---|---|
| committer | Clawd <ai@clawd.bot> | 2026-04-11 20:43:41 -0700 |
| commit | d0ae31c24c3c98ae89eebd67227c0c0d01606ed5 (patch) | |
| tree | c684469e0f7d3b65477cfc631ecdaafa3c6a218a /SKILLS_PLAN.md | |
| parent | 5548b36e0953c17dbe30f6b63c892b7c83196b20 (diff) | |
Add ship-* Claude skills and plan
Introduces a skills/ directory with 8 Claude skills that reimagine ship
as a set of composable, human-driven deployment tools backed by Claude's
reasoning rather than a rigid CLI.
Skills:
- ship-setup: one-time VPS config, saves host to ~/.config/ship/config.json
- ship-status: derives live state from server, no local state file
- ship-env: read/write env vars with merge semantics, never overwrites
- ship-binary: deploy Go binaries with SQLite backup, correct restart behavior
- ship-caddy: manage per-app Caddyfile with validate-before-reload
- ship-service: systemd management and log inspection
- ship-static: rsync static sites with SPA routing support
- ship-deploy: orchestration runbook tying the others together
Also adds SKILLS_PLAN.md documenting the architecture and rationale.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'SKILLS_PLAN.md')
| -rw-r--r-- | SKILLS_PLAN.md | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/SKILLS_PLAN.md b/SKILLS_PLAN.md new file mode 100644 index 0000000..ded2b38 --- /dev/null +++ b/SKILLS_PLAN.md | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | # Ship Skills — Reimagining Ship as Claude Skills | ||
| 2 | |||
| 3 | ## The Idea | ||
| 4 | |||
| 5 | Rather than a monolithic CLI that bakes in rigid assumptions, ship becomes a family of | ||
| 6 | narrow, composable Claude skills. Each skill knows how to do one thing well. Claude | ||
| 7 | provides the reasoning and orchestration. The server is the source of truth. | ||
| 8 | |||
| 9 | Skills are completely generic — no hostnames, app names, or passwords baked in. The | ||
| 10 | same skills work for anyone. Share them with a friend, point them at a different VPS, | ||
| 11 | they just work. | ||
| 12 | |||
| 13 | ## Shared Configuration | ||
| 14 | |||
| 15 | A single static file at `~/.config/ship/config.json` holds the VPS host (and little | ||
| 16 | else). All skills read from this file. No vault dependency — works for anyone. | ||
| 17 | |||
| 18 | ```json | ||
| 19 | { | ||
| 20 | "host": "ubuntu@1.2.3.4", | ||
| 21 | "domain": "example.com" | ||
| 22 | } | ||
| 23 | ``` | ||
| 24 | |||
| 25 | The server itself is the source of truth for everything else — what services are | ||
| 26 | running, what ports are allocated, what Caddy configs exist. No local state file that | ||
| 27 | can go stale. | ||
| 28 | |||
| 29 | ## The Skills | ||
| 30 | |||
| 31 | ### `ship-setup` | ||
| 32 | One-time setup. Asks for VPS host if not configured, saves to `~/.config/ship/config.json`, | ||
| 33 | SSHes in and installs server dependencies (Caddy, directory structure, etc). | ||
| 34 | All other skills depend on this having been run once. | ||
| 35 | |||
| 36 | ### `ship-status` | ||
| 37 | Derives current state entirely from the server at runtime: | ||
| 38 | - Running apps → `systemctl list-units --type=service` | ||
| 39 | - Ports → `/etc/ship/ports/` or env files | ||
| 40 | - Domains → parse Caddy configs in `sites-enabled/` | ||
| 41 | - Static sites → list `/var/www/` | ||
| 42 | |||
| 43 | No state file needed. Always accurate. Replaces the need for any local tracking. | ||
| 44 | |||
| 45 | ### `ship-env` | ||
| 46 | Read and write env vars with merge semantics. Never overwrites — reads existing file | ||
| 47 | first, merges new values on top, writes result. Old vars survive redeployments. | ||
| 48 | |||
| 49 | ### `ship-caddy` | ||
| 50 | Manage per-app Caddyfile config. Knows Caddy syntax. Diffs before writing. Validates | ||
| 51 | before reloading. Never regenerates from scratch — only touches what needs changing. | ||
| 52 | |||
| 53 | ### `ship-service` | ||
| 54 | Systemd management. Handles the difference between a new service (enable + start) and | ||
| 55 | an existing one (restart). Status, logs, restart, stop — all covered. | ||
| 56 | |||
| 57 | ### `ship-binary` | ||
| 58 | Upload and install a pre-built binary. SCP to `/tmp`, move to `/usr/local/bin/`, | ||
| 59 | chmod +x, set up work directory and service user. Calls `ship-service` and `ship-env` | ||
| 60 | to complete the deployment. | ||
| 61 | |||
| 62 | ### `ship-static` | ||
| 63 | Rsync a local dist folder to `/var/www/{name}` on the server. Calls `ship-caddy` to | ||
| 64 | configure serving. | ||
| 65 | |||
| 66 | ### `ship-deploy` | ||
| 67 | A runbook skill that orchestrates the others in the right order for a full deployment. | ||
| 68 | Not imperative code — just a checklist of steps with enough context for Claude to | ||
| 69 | reason about what to do. Adapts based on what the user tells it (binary vs static, | ||
| 70 | what env vars are needed, etc). | ||
| 71 | |||
| 72 | ## What the Server Knows | ||
| 73 | |||
| 74 | All persistent state lives on the server in conventional locations: | ||
| 75 | |||
| 76 | ``` | ||
| 77 | /etc/caddy/sites-enabled/{name}.caddy # per-app Caddy config | ||
| 78 | /etc/ship/env/{name}.env # environment variables | ||
| 79 | /etc/ship/ports/{name} # allocated port number | ||
| 80 | /etc/systemd/system/{name}.service # systemd unit | ||
| 81 | /var/www/{name}/ # static site files | ||
| 82 | /var/lib/{name}/ # app work directory (binary, data) | ||
| 83 | /usr/local/bin/{name} # binary executable | ||
| 84 | ``` | ||
| 85 | |||
| 86 | ## Why This Is Better Than the CLI | ||
| 87 | |||
| 88 | - **Transparent** — Claude tells you what it's about to do before doing it | ||
| 89 | - **Flexible** — no rigid assumptions, Claude reasons about edge cases | ||
| 90 | - **Mergeable** — env files, Caddy configs never blindly overwritten | ||
| 91 | - **Debuggable** — if something goes wrong, just ask Claude to fix it | ||
| 92 | - **Shareable** — no app-specific knowledge baked in, works for anyone | ||
| 93 | - **No stale state** — server is always the source of truth | ||
| 94 | |||
| 95 | ## Per-App Notes (Optional) | ||
| 96 | |||
| 97 | The server can't know things like "this app needs FOODTRACKER_PASSWORD on redeploy" | ||
| 98 | or "this app has SQLite at /var/lib/foodtracker/data/". That's documentation, not | ||
| 99 | state. Users can keep these as plain notes in whatever system they prefer — a vault, | ||
| 100 | a README, a comment in a script. The skills don't depend on it. | ||
| 101 | |||
| 102 | ## SQLite Backup | ||
| 103 | |||
| 104 | Before swapping a binary, `ship-binary` checks `/var/lib/{name}/` for any `.db` files | ||
| 105 | and backs them up to `/var/lib/{name}/backups/{timestamp}.db` before proceeding. Silent | ||
| 106 | and automatic — you never lose data from a bad deploy. | ||
| 107 | |||
| 108 | ## Multi-Host Support | ||
| 109 | |||
| 110 | Config supports multiple named hosts. One is marked as default. All skills use the | ||
| 111 | default unless told otherwise. | ||
| 112 | |||
| 113 | ```json | ||
| 114 | { | ||
| 115 | "default": "prod", | ||
| 116 | "hosts": { | ||
| 117 | "prod": { | ||
| 118 | "host": "ubuntu@1.2.3.4", | ||
| 119 | "domain": "example.com" | ||
| 120 | }, | ||
| 121 | "staging": { | ||
| 122 | "host": "ubuntu@5.6.7.8", | ||
| 123 | "domain": "staging.example.com" | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | ``` | ||
| 128 | |||
| 129 | Usage is natural — "deploy foodtracker to staging" and Claude picks the right host. | ||
| 130 | `ship-setup` can be run multiple times to add new hosts. The default can be changed | ||
| 131 | at any time. | ||
| 132 | |||
| 133 | ## Out of Scope (For Now) | ||
| 134 | |||
| 135 | - Health checks — skipping initially, can add later if needed | ||
