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/ship-service | |
| 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/ship-service')
| -rw-r--r-- | skills/ship-service/SKILL.md | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/skills/ship-service/SKILL.md b/skills/ship-service/SKILL.md new file mode 100644 index 0000000..e4d7510 --- /dev/null +++ b/skills/ship-service/SKILL.md | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | --- | ||
| 2 | name: ship-service | ||
| 3 | description: Manage systemd services for deployed apps. Start, stop, restart, view status and logs. Use when you need to control a running service or diagnose problems. | ||
| 4 | argument-hint: "<app-name> [start|stop|restart|status|logs] [host-nickname]" | ||
| 5 | --- | ||
| 6 | |||
| 7 | # ship-service | ||
| 8 | |||
| 9 | Manage systemd services for apps deployed on a ship VPS. | ||
| 10 | |||
| 11 | ## Read Config | ||
| 12 | |||
| 13 | ```bash | ||
| 14 | python3 -c " | ||
| 15 | import json, os | ||
| 16 | cfg = json.load(open(os.path.expanduser('~/.config/ship/config.json'))) | ||
| 17 | nick = '<nickname-or-default>' | ||
| 18 | h = cfg['hosts'].get(nick, cfg['hosts'][cfg['default']]) | ||
| 19 | print(h['host']) | ||
| 20 | " | ||
| 21 | ``` | ||
| 22 | |||
| 23 | ## Usage Patterns | ||
| 24 | |||
| 25 | ### Status | ||
| 26 | |||
| 27 | ```bash | ||
| 28 | ssh <host> "sudo systemctl status <app-name> --no-pager" | ||
| 29 | ``` | ||
| 30 | |||
| 31 | Shows whether the service is running, its PID, memory usage, and recent log lines. | ||
| 32 | |||
| 33 | ### Restart | ||
| 34 | |||
| 35 | ```bash | ||
| 36 | ssh <host> "sudo systemctl restart <app-name>" | ||
| 37 | ``` | ||
| 38 | |||
| 39 | Use after changing env vars or replacing the binary. | ||
| 40 | |||
| 41 | ### Stop | ||
| 42 | |||
| 43 | ```bash | ||
| 44 | ssh <host> "sudo systemctl stop <app-name>" | ||
| 45 | ``` | ||
| 46 | |||
| 47 | ### Start | ||
| 48 | |||
| 49 | ```bash | ||
| 50 | ssh <host> "sudo systemctl start <app-name>" | ||
| 51 | ``` | ||
| 52 | |||
| 53 | ### View logs | ||
| 54 | |||
| 55 | Recent logs (last 50 lines): | ||
| 56 | ```bash | ||
| 57 | ssh <host> "sudo journalctl -u <app-name> -n 50 --no-pager" | ||
| 58 | ``` | ||
| 59 | |||
| 60 | Follow live logs: | ||
| 61 | ```bash | ||
| 62 | ssh <host> "sudo journalctl -u <app-name> -f" | ||
| 63 | ``` | ||
| 64 | |||
| 65 | Logs since last boot: | ||
| 66 | ```bash | ||
| 67 | ssh <host> "sudo journalctl -u <app-name> -b --no-pager" | ||
| 68 | ``` | ||
| 69 | |||
| 70 | ### Enable (start on boot) | ||
| 71 | |||
| 72 | ```bash | ||
| 73 | ssh <host> "sudo systemctl enable <app-name>" | ||
| 74 | ``` | ||
| 75 | |||
| 76 | ### Disable (don't start on boot) | ||
| 77 | |||
| 78 | ```bash | ||
| 79 | ssh <host> "sudo systemctl disable <app-name>" | ||
| 80 | ``` | ||
| 81 | |||
| 82 | ### View the systemd unit file | ||
| 83 | |||
| 84 | ```bash | ||
| 85 | ssh <host> "sudo cat /etc/systemd/system/<app-name>.service" | ||
| 86 | ``` | ||
| 87 | |||
| 88 | ### Remove a service entirely | ||
| 89 | |||
| 90 | Only do this if the user explicitly asks to remove/uninstall an app: | ||
| 91 | |||
| 92 | ```bash | ||
| 93 | ssh <host> "sudo systemctl stop <app-name> && sudo systemctl disable <app-name> && sudo rm /etc/systemd/system/<app-name>.service && sudo systemctl daemon-reload" | ||
| 94 | ``` | ||
| 95 | |||
| 96 | Confirm with the user before removing. Note that this does not delete the binary, | ||
| 97 | data directory, env file, or Caddy config — those are managed separately. | ||
| 98 | |||
| 99 | ## Diagnosing Problems | ||
| 100 | |||
| 101 | If a service is failing, check: | ||
| 102 | |||
| 103 | 1. Service status for the error: | ||
| 104 | ```bash | ||
| 105 | ssh <host> "sudo systemctl status <app-name> --no-pager" | ||
| 106 | ``` | ||
| 107 | |||
| 108 | 2. Full logs for context: | ||
| 109 | ```bash | ||
| 110 | ssh <host> "sudo journalctl -u <app-name> -n 100 --no-pager" | ||
| 111 | ``` | ||
| 112 | |||
| 113 | 3. Whether the binary exists and is executable: | ||
| 114 | ```bash | ||
| 115 | ssh <host> "ls -la /usr/local/bin/<app-name>" | ||
| 116 | ``` | ||
| 117 | |||
| 118 | 4. Whether the env file exists and looks correct: | ||
| 119 | ```bash | ||
| 120 | ssh <host> "sudo cat /etc/ship/env/<app-name>.env" | ||
| 121 | ``` | ||
| 122 | |||
| 123 | 5. Whether the port is already in use by something else: | ||
| 124 | ```bash | ||
| 125 | ssh <host> "sudo ss -tlnp | grep <port>" | ||
| 126 | ``` | ||
| 127 | |||
| 128 | ## Notes | ||
| 129 | |||
| 130 | - If the user just says "restart foodtracker" or "check the logs for myapp", infer the action | ||
| 131 | - After a restart, give it a moment then check status to confirm it came up | ||
| 132 | - If a service repeatedly crashes, look at the logs before suggesting a fix | ||
| 133 | - Use default host unless another is specified | ||
