aboutsummaryrefslogtreecommitdiffstats
path: root/SKILLS_PLAN.md
blob: ded2b382fd994ecb1b8b661b25659afeedec4388 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Ship Skills — Reimagining Ship as Claude Skills

## The Idea

Rather than a monolithic CLI that bakes in rigid assumptions, ship becomes a family of
narrow, composable Claude skills. Each skill knows how to do one thing well. Claude
provides the reasoning and orchestration. The server is the source of truth.

Skills are completely generic — no hostnames, app names, or passwords baked in. The
same skills work for anyone. Share them with a friend, point them at a different VPS,
they just work.

## Shared Configuration

A single static file at `~/.config/ship/config.json` holds the VPS host (and little
else). All skills read from this file. No vault dependency — works for anyone.

```json
{
  "host": "ubuntu@1.2.3.4",
  "domain": "example.com"
}
```

The server itself is the source of truth for everything else — what services are
running, what ports are allocated, what Caddy configs exist. No local state file that
can go stale.

## The Skills

### `ship-setup`
One-time setup. Asks for VPS host if not configured, saves to `~/.config/ship/config.json`,
SSHes in and installs server dependencies (Caddy, directory structure, etc).
All other skills depend on this having been run once.

### `ship-status`
Derives current state entirely from the server at runtime:
- Running apps → `systemctl list-units --type=service`
- Ports → `/etc/ship/ports/` or env files
- Domains → parse Caddy configs in `sites-enabled/`
- Static sites → list `/var/www/`

No state file needed. Always accurate. Replaces the need for any local tracking.

### `ship-env`
Read and write env vars with merge semantics. Never overwrites — reads existing file
first, merges new values on top, writes result. Old vars survive redeployments.

### `ship-caddy`
Manage per-app Caddyfile config. Knows Caddy syntax. Diffs before writing. Validates
before reloading. Never regenerates from scratch — only touches what needs changing.

### `ship-service`
Systemd management. Handles the difference between a new service (enable + start) and
an existing one (restart). Status, logs, restart, stop — all covered.

### `ship-binary`
Upload and install a pre-built binary. SCP to `/tmp`, move to `/usr/local/bin/`,
chmod +x, set up work directory and service user. Calls `ship-service` and `ship-env`
to complete the deployment.

### `ship-static`
Rsync a local dist folder to `/var/www/{name}` on the server. Calls `ship-caddy` to
configure serving.

### `ship-deploy`
A runbook skill that orchestrates the others in the right order for a full deployment.
Not imperative code — just a checklist of steps with enough context for Claude to
reason about what to do. Adapts based on what the user tells it (binary vs static,
what env vars are needed, etc).

## What the Server Knows

All persistent state lives on the server in conventional locations:

```
/etc/caddy/sites-enabled/{name}.caddy   # per-app Caddy config
/etc/ship/env/{name}.env                # environment variables
/etc/ship/ports/{name}                  # allocated port number
/etc/systemd/system/{name}.service      # systemd unit
/var/www/{name}/                        # static site files
/var/lib/{name}/                        # app work directory (binary, data)
/usr/local/bin/{name}                   # binary executable
```

## Why This Is Better Than the CLI

- **Transparent** — Claude tells you what it's about to do before doing it
- **Flexible** — no rigid assumptions, Claude reasons about edge cases
- **Mergeable** — env files, Caddy configs never blindly overwritten
- **Debuggable** — if something goes wrong, just ask Claude to fix it
- **Shareable** — no app-specific knowledge baked in, works for anyone
- **No stale state** — server is always the source of truth

## Per-App Notes (Optional)

The server can't know things like "this app needs FOODTRACKER_PASSWORD on redeploy"
or "this app has SQLite at /var/lib/foodtracker/data/". That's documentation, not
state. Users can keep these as plain notes in whatever system they prefer — a vault,
a README, a comment in a script. The skills don't depend on it.

## SQLite Backup

Before swapping a binary, `ship-binary` checks `/var/lib/{name}/` for any `.db` files
and backs them up to `/var/lib/{name}/backups/{timestamp}.db` before proceeding. Silent
and automatic — you never lose data from a bad deploy.

## Multi-Host Support

Config supports multiple named hosts. One is marked as default. All skills use the
default unless told otherwise.

```json
{
  "default": "prod",
  "hosts": {
    "prod": {
      "host": "ubuntu@1.2.3.4",
      "domain": "example.com"
    },
    "staging": {
      "host": "ubuntu@5.6.7.8",
      "domain": "staging.example.com"
    }
  }
}
```

Usage is natural — "deploy foodtracker to staging" and Claude picks the right host.
`ship-setup` can be run multiple times to add new hosts. The default can be changed
at any time.

## Out of Scope (For Now)

- Health checks — skipping initially, can add later if needed