From d0ae31c24c3c98ae89eebd67227c0c0d01606ed5 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 11 Apr 2026 20:43:41 -0700 Subject: 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 --- skills/ship-status/SKILL.md | 85 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 skills/ship-status/SKILL.md (limited to 'skills/ship-status/SKILL.md') diff --git a/skills/ship-status/SKILL.md b/skills/ship-status/SKILL.md new file mode 100644 index 0000000..f47e081 --- /dev/null +++ b/skills/ship-status/SKILL.md @@ -0,0 +1,85 @@ +--- +name: ship-status +description: Show what's running on a ship VPS. Derives state from the server — no local state file. Use when you want to know what apps are deployed, what ports they use, and whether they're running. +argument-hint: "[host-nickname]" +--- + +# ship-status + +Show the current state of all deployments on a ship VPS by reading directly from the server. + +## Read Config + +Load the host from `~/.config/ship/config.json`. Use the default host unless a nickname was specified: + +```bash +python3 -c " +import json, os, sys +cfg = json.load(open(os.path.expanduser('~/.config/ship/config.json'))) +nick = sys.argv[1] if len(sys.argv) > 1 else cfg['default'] +h = cfg['hosts'][nick] +print(h['host']) +print(h['domain']) +" +``` + +## Gather Server State + +Run all of these in a single SSH session to minimize round trips: + +**Systemd services (binary/docker apps):** +```bash +ssh "systemctl list-units --type=service --state=active --no-pager --no-legend | grep -v '@' | awk '{print \$1}' | xargs -I{} sh -c 'name=\$(echo {} | sed s/.service//); port=\$(cat /etc/ship/ports/\$name 2>/dev/null); env=\$(cat /etc/ship/env/\$name.env 2>/dev/null | grep SHIP_URL | cut -d= -f2); echo \"\$name|\$port|\$env\"' 2>/dev/null | grep '|'" +``` + +**Static sites:** +```bash +ssh "ls /var/www/ 2>/dev/null" +``` + +**Caddy configs (domains):** +```bash +ssh "ls /etc/caddy/sites-enabled/ 2>/dev/null | grep -v '^$'" +``` + +**Caddy status:** +```bash +ssh "systemctl is-active caddy" +``` + +**Disk usage for app data dirs:** +```bash +ssh "du -sh /var/lib/*/ 2>/dev/null" +``` + +## Present Results + +Format as a clean summary, for example: + +``` +HOST: prod (ubuntu@1.2.3.4) + +SERVICES + foodtracker running :9013 https://foodtracker.example.com + myapi running :9014 https://api.example.com + +STATIC SITES + mysite https://mysite.example.com + +CADDY: running + +DATA + /var/lib/foodtracker/ 48M + /var/lib/myapi/ 2M +``` + +If a service appears in `/etc/ship/ports/` but is not active in systemd, flag it as **stopped**. + +If a Caddy config exists for a name but no service or static site matches, flag it as **orphaned config**. + +## Notes + +- No local state file is consulted — everything comes from the server +- If `~/.config/ship/config.json` doesn't exist, tell the user to run `/ship-setup` first +- If a nickname is given that doesn't exist in config, list available nicknames +- Use default host if no argument given -- cgit v1.2.3