# /shiplock — Build and deploy a personal app with ShipLock

You are helping the user build and deploy a personal or small-team web app using ShipLock. ShipLock gives every app a live URL, a built-in JSON data store, and access control.

## Step 1 — Understand what they want

If the user described what they want in the `/shiplock` command itself, use that directly — don't ask again.

If they just typed `/shiplock` with nothing else, ask ONE question: **"What do you want to build, and who needs access?"**

Examples to prompt their thinking:
- A shared grocery list for two people
- A habit tracker just for you
- A shared expense log for a trip
- A form that collects RSVPs from others
- A private note-taking app

**While reading their description, infer the access policy:**
- "just for me" / "private" / "only I need it" → `private`
- "me and [someone]" / "share with" / "my partner/team" → `link-only` (default for sharing)
- "collect responses" / "anyone can" / "public form" → `public`
- Ambiguous → default to `link-only` and mention it at the end

Do NOT ask a separate question about access unless the description gives you nothing to go on.

## Step 2 — Build the HTML app

Build a complete, well-designed single-file HTML app. Requirements:

**Data persistence** — use ShipLock's built-in data API via `window.SL`:
```javascript
const API = window.SL?.api || 'https://api.shiplock.app'
const APP_ID = window.SL?.appId || 'local'

async function loadData() {
  if (APP_ID === 'local') return JSON.parse(localStorage.getItem('data') || '[]')
  const r = await fetch(`${API}/data/${APP_ID}`, {
    headers: window.SL?.visitorToken ? { 'X-SL-Token': window.SL.visitorToken } : {}
  })
  return r.ok ? r.json() : []
}

async function saveData(data) {
  if (APP_ID === 'local') { localStorage.setItem('data', JSON.stringify(data)); return }
  await fetch(`${API}/data/${APP_ID}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      ...(window.SL?.visitorToken ? { 'X-SL-Token': window.SL.visitorToken } : {})
    },
    body: JSON.stringify(data)
  })
}
```

**Data shape** — always store records as a **flat array of objects** so they show as clean rows in the ShipLock dashboard:
```javascript
// ✅ Good — renders as a table with columns
[{ item: 'Milk', done: false }, { item: 'Eggs', done: true }]

// ❌ Avoid — nested objects break dashboard display
{ items: { id1: { item: 'Milk' } } }
```
Use `arr.find(r => r.id === id)` for keyed lookups instead of nested maps.

**Design standards:**
- Mobile-first, looks great on a phone home screen
- Dark or clean light theme — not generic/Bootstrap-looking
- Font: system-ui or a single Google Font
- Touch-friendly tap targets (min 44px)
- No external dependencies except one Google Font if needed

**Do not** include backend setup, server code, or instructions to run anything locally. Single self-contained HTML file only.

## Step 3 — Auth and deploy via ShipLock MCP

If ShipLock MCP tools are not available, skip to the connector setup section at the bottom.

### Check auth

Call `list_apps` with no arguments.

- **If it returns a list (even empty):** Auth is working. Skip to Deploy.
- **If it returns "Authentication required":** Ask the user ONE question:

  > "Are you new to ShipLock, or do you already have an account?"

  **New user:**
  1. Ask for their email address
  2. Call `start_trial` with that email
  3. It returns a `token` — share it with the user and say:

     > "Your ShipLock token is: `TOKEN`
     >
     > To skip this step next time, save it permanently:
     >
     > **Claude Code** — update `~/.claude.json` to pass it as a header (more secure than a URL param):
     > ```json
     > { "mcpServers": { "shiplock": { "type": "http", "url": "https://api.shiplock.app/mcp", "headers": { "Authorization": "Bearer TOKEN" } } } }
     > ```
     >
     > **Claude.ai** — edit your ShipLock connector URL to `https://api.shiplock.app/mcp?token=TOKEN`
     >
     > Your dashboard: https://shiplock.app/dashboard"

  **Existing user:**
  > "Grab your token from https://shiplock.app/dashboard → Settings, then update your MCP URL to `https://api.shiplock.app/mcp?token=YOUR_TOKEN` and restart Claude. Or paste the token here and I'll use it for this session."

  If they paste a token, pass it as `session_token` in tool arguments.

### Deploy

**Fast path — shell available (Claude Code):** NEVER pass HTML inline through the MCP `deploy` tool when you have shell access — re-typing a whole file into a tool call is slow and wastes tokens. Upload the file directly instead:

1. Write the HTML to a file (or use the existing file if the user already has one on disk — deploy it as-is, do not re-read and re-emit it)
2. Get the session token: check `~/.claude.json` → `.mcpServers.shiplock.headers.Authorization` (strip the `Bearer ` prefix) or the `?token=` param in the URL; otherwise use the token the user pasted
3. Upload:

```bash
curl -s -X POST https://api.shiplock.app/deploy \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@/path/to/app.html" \
  -F "name=App Name" \
  -F "policy=public"
```

`policy` is one of `public` / `link-only` / `private` / `group`. To redeploy a new version of an existing app, add `-F "appId=APP_ID"`. The response is JSON: `{ appId, url, shareUrl, version, policy }` — `shareUrl` includes the link token for link-only apps.

**Fallback — no shell (claude.ai):** call the MCP `deploy` tool with:
- `html`: the complete HTML you built
- `name`: a short descriptive name
- `policy`: the access policy you inferred in Step 1

## Step 4 — Tell them what they have

After deploying, give them:

1. **The live URL** — it works immediately
2. **Add to home screen** — visit in Safari/Chrome → Share → Add to Home Screen
3. **Access note** — one line based on policy:
   - `link-only`: "The URL itself is the key — only people with this link can access it. Share it however you like."
   - `private`: "Only you can access this — you'll need to sign in with your ShipLock account."
   - `public`: "Anyone with the URL can access it."
4. **Manage at** https://shiplock.app/dashboard — change access, view data, redeploy

---

## If ShipLock MCP connector is not set up

Tell the user:

> To use `/shiplock`, add ShipLock as a connector first:
>
> **Claude Code (terminal):** Add to `~/.claude.json`:
> ```json
> {
>   "mcpServers": {
>     "shiplock": {
>       "type": "http",
>       "url": "https://api.shiplock.app/mcp"
>     }
>   }
> }
> ```
> Then restart Claude Code and run `/shiplock` again. You'll be walked through account setup on first use.
>
> **Claude.ai (web):** Customize → Connectors → + → Name: ShipLock → URL: `https://api.shiplock.app/mcp` → Add

---

## How to install this skill

**Claude Code:** One-liner install:
```bash
curl -s https://shiplock.app/skill.md > ~/.claude/commands/shiplock.md
```

**Claude.ai:** Customize → Skills → Create skill → paste the contents of this file
