---
name: drop
description: Deploy, update, download, search, and share single-page HTML apps or Markdown documents (Droplets) on Hi Marley's internal hosting platform. Use when the user wants to create, deploy, update, or share a web app, canvas, dashboard, interactive tool, or document.
user-invocable: true
---

# Drop — Hi Marley Internal App Hosting

Drop is Hi Marley's internal Netlify Drop-style service for deploying single-page HTML apps or Markdown documents ("Droplets") with shareable internal URLs.

**Server:** `https://drop.himarley.ai`

## Setup

Use the CLI for Drop operations. For sign-in, run `drop login` in the terminal and let it open the system browser. See [login troubleshooting](https://drop.himarley.ai/docs/cli#sign-in) if sign-in is blocked.

```bash
# Install (one-time, platform auto-detected)
curl -fsSL https://drop.himarley.ai/install-drop.sh | bash

# Login with browser-based SSO
drop login

# Headless or remote shell
drop login --no-browser
```

If `drop` is not found, run the install command above.
The installer places the managed standalone binary at `~/.drop/bin/drop` and does not require Node.js, npm, or Homebrew.

## CLI Commands

| Command | Description |
|---------|-------------|
| `drop create <file> --name "Name" --slug slug` | Deploy or update a Droplet (.html or .md) |
| `drop update <slug> <file>` | Update an existing Droplet (preserves name/description) |
| `drop patch <slug> --find "X" --replace "Y"` | Small targeted text edits without downloading |
| `drop edit <slug>` | Open in $EDITOR, auto-deploys on save |
| `drop download <slug>` | Download the current source (HTML or Markdown) to stdout |
| `drop get <slug>` | Show Droplet details (name, size, access, etc.) |
| `drop list` | List all your Droplets |
| `drop search <query>` | Full-text search across all Droplets |
| `drop search-content <slug> "text"` | Search within a Droplet's content |
| `drop versions <slug>` | Show version history |
| `drop rollback <slug> --version N` | Roll back to a previous version |
| `drop open <slug>` | Open Droplet in browser |
| `drop create <file> --slug slug --assets ./logo.png ./demo.mp4` | Deploy a Droplet and attach assets in one command |
| `drop assets upload <file> --app slug` | Upload or replace a file asset (image, video, CSV) |
| `drop assets list --app slug` | List attached assets |
| `drop assets delete <filename> --app slug` | Delete an asset |
| `drop comments list <slug>` | List comments (`--resolved false` for open only) |
| `drop comments create <slug> --body "..."` | Post a comment — add `--target-selector` + `--target-text` to pin it |
| `drop comments resolve <id>` | Mark a comment addressed — the close verb, preserves history |
| `drop comments delete <id>` | Permanently remove a comment (spam / mistakes only) |
| `drop upgrade` | Check for and install CLI updates |
| `drop login --server <url>` | Authenticate with browser-based SSO |
| `drop skill create <file>` | Create a Skill from a .md or .html file |
| `drop create <file> --type skill` | Same as above, using --type flag |
| `drop create <file> --slug slug --draft` | Save as a draft instead of publishing (#296) |
| `drop drafts <slug>` | List your drafts on a Droplet |
| `drop publish <slug> [shortId]` | Publish one of your drafts as a new version |
| `drop unpublish <slug>` | Take a published Droplet offline (preserves history) |
| `drop republish <slug>` | Restore a previously-unpublished Droplet |
| `drop discard <slug> [shortId]` / `drop discard <slug> --all` | Discard one or all of your drafts |
| `drop skills` | List available Claude Code skills (My Skills / Featured / Shared) |
| `drop skills install <slug>` | Install a skill to ~/.claude/skills/ |
| `drop skills install <slug>@v3` | Install and pin to a specific version |
| `drop skills update` | Update all installed skills to latest |
| `drop skills outdated` | Show which installed skills have updates |
| `drop skills pin <slug>` | Pin a skill to its current version |
| `drop skills unpin <slug>` | Unpin to track latest on next update |
| `drop skills uninstall <slug>` | Remove a skill |
| `drop skills info <slug>` | Show skill details and install status |
| `drop create <file> --slug slug --tag foo --tag bar` | Deploy a Droplet with tags (repeatable) |

## The Edit Workflow

This is the primary use case — downloading, editing, and redeploying:

```bash
# 1. Download the current version
drop download my-dashboard -o my-dashboard.html

# 2. Edit the file (make your changes)

# 3. Redeploy
drop create my-dashboard.html --slug my-dashboard --notes "Updated chart colors"
```

## The Create Workflow

1. **User describes what they want** — build it as a single self-contained HTML file or Markdown document
2. **Deploy to Drop** — `drop create app.html --name "App Name" --slug app-name` (or `doc.md`)
3. **Share the URL** — the app is live at `https://drop.himarley.ai/view/app-name`
4. **User gets feedback** — update the HTML and redeploy with the same slug
5. **Repeat** — the URL never changes, viewers always see the latest version

## Drafts and Publishing

Use drafts when iterating on a Droplet whose URL has already been shared, or when staging new content that shouldn't be live yet. Each editor has a personal draft workspace per Droplet — drafts are strictly invisible to anyone other than their creator.

```bash
# First-time draft on a new slug (creates the Droplet in never-published state)
drop create app.html --slug my-app --name "My App" --draft

# Iterate on an existing Droplet — use update for subsequent edits
drop update my-app v2.html --draft
drop update my-app v3.html --draft  # checkpoint, drafts coexist

# Patch existing content as a draft (find/replace without re-uploading)
drop patch my-app --find "TODO" --replace "DONE" --draft

# See what's in your workspace
drop drafts my-app

# Publish your latest draft (squashes the others)
drop publish my-app

# Or publish a specific draft by its short id
drop publish my-app 1

# Take a published Droplet offline; restore later with drop republish
drop unpublish my-app
drop republish my-app

# Clear drafts you don't want
drop discard my-app 2
drop discard my-app --all
```

**When to use drafts:**
- The Droplet's URL is already shared in Slack/email and you want to revise without breaking the link
- You're staging a brand-new Droplet and want the URL placeholder ("not yet published") until you're ready
- You want to checkpoint mid-iteration ("v2 was actually better than v3" — keep both as drafts, publish whichever wins)

**Which command for which step:**
- `drop create <file> --slug X --draft` — first time touching slug X (creates the Droplet)
- `drop update <slug> <file> --draft` — slug exists; adds a new draft to your workspace
- `drop patch <slug> --find … --replace … --draft` — targeted text edits as a draft

**When NOT to use drafts:** for one-shot deploys that should go live immediately, plain `drop create` / `drop update` / `drop patch` (no flag) are correct and simpler.

**Privacy note for agents:** drafts are personal scratch — even other editors of the same Droplet cannot see your drafts. If multiple agents are working on the same Droplet, each agent's drafts are private to its own session. Coordinate via the user, not via shared draft state.

## Assets

Preferred workflow:

```bash
# Deploy HTML with attached assets in one command
drop create app.html --slug my-app --assets ./logo.png ./demo.mp4

# Deploy Markdown with attached assets in one command
drop create notes.md --slug meeting-notes --assets ./diagram.png
```

Low-level asset management:

```bash
drop assets upload ./logo.png --app my-app
drop assets list --app my-app
drop assets delete logo.png --app my-app
```

Reference assets with droplet-relative paths:

- HTML image: `<img src="assets/logo.png" alt="Logo">`
- HTML video: `<video src="assets/demo.mp4" controls></video>`
- Markdown image: `![Diagram](assets/diagram.png)`
- Markdown download: `[CSV export](assets/data.csv)`

Assets are attached to the droplet, not to each version. Redeploying or rolling back the HTML/Markdown updates the primary document only; attached assets stay in place unless explicitly replaced or deleted.

## Comments — reading and pinning feedback

Comments survive new published versions and can be resolved once addressed, which is what makes them the right store for review feedback instead of a scratch note in the Droplet body.

```bash
# Read what's still open
drop comments list my-deck --resolved false

# Page-level comment
drop comments create my-deck --body "Overall this reads well"

# Reply into a thread
drop comments create my-deck --body "Agreed, fixing" --parent-id <comment-id>

# Close the loop — resolve, don't delete (delete is for spam and mistakes)
drop comments resolve <comment-id>
```

### Pinning a comment to an element

A pinned comment needs **both** `--target-selector` and `--target-text`. The viewer resolves a pin by trying the selector first, then falling back to finding whichever element's text matches `--target-text`.

**A selector-only pin has no fallback.** The next re-deploy that shifts the DOM kills it silently — the comment survives but stops pointing at anything. Always pass both.

Read both values off the live HTML:

```bash
# 1. Get the current source
drop download my-deck -o /tmp/deck.html

# 2. Find a stable target — prefer an element that already has an id
grep -n 'id="' /tmp/deck.html

# 3. Pin, passing the element's literal text
drop comments create my-deck \
  --body "Needs a source for the 40% figure" \
  --target-selector "#d2-07-inquiry-assist" \
  --target-text "Inquiry Assist"
```

`--target-text` must be the element's actual `textContent` — trimmed, first 100 characters — **not a description of it**. An exact match resolves the pin immediately; anything else falls through to fuzzy text matching, which can land on the wrong element or fail outright.

Note that `drop search-content <slug> "text"` is line-scoped, so it will not show you an `id` that sits on an ancestor element — a common shape in slide decks, where `<section id="...">` wraps the heading you searched for. For picking pin targets, `drop download` is usually one call instead of several.

## Best Practices for Building Droplets

### Structure
- **Self-contained**: inline all CSS and JS in a single `.html` file, or write a `.md` (Markdown) file — Markdown is rendered with a branded stylesheet at deploy time
- **CDN dependencies**: use CDN links for libraries (Tailwind Play CDN, Chart.js, D3.js, Alpine.js, etc.)
- **Responsive**: include a viewport meta tag and responsive design
- **Title**: set a meaningful `<title>` tag

### Hi Marley Brand Colors (use when appropriate)

| Color  | Hex       | Usage                      |
|--------|-----------|----------------------------|
| Coral  | `#F9685D` | Primary actions, CTAs      |
| Navy   | `#263456` | Headings, dark backgrounds |
| Teal   | `#65C9C9` | Accents, highlights        |
| Slate  | `#415271` | Body text                  |
| Orange | `#FCA463` | Warnings, attention        |

### Performance
- Keep the primary `.html` or `.md` file under 10MB
- Optimize images (use SVG when possible, or base64-encode small images)
- For large datasets, consider lazy loading or pagination

### Common CDN Libraries

```html
<!-- Tailwind CSS (Play CDN) -->
<script src="https://cdn.tailwindcss.com"></script>

<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<!-- D3.js -->
<script src="https://d3js.org/d3.v7.min.js"></script>

<!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>

<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
```

## Droplet State SDK — making Droplets remember things

Every Droplet auto-loads a small `window.Drop` SDK. Use it to persist checkboxes, count poll votes, collect surveys, and read who has access — without writing a backend. No install, no import, no `<script>` tag to add: the SDK is injected at deploy time.

`await Drop.ready` once at the top of any inline script before reading `Drop.viewer.*` or calling primitives.

### The five surfaces

```js
await Drop.ready;

// Per-viewer (private) state — only this viewer sees the value.
// Use for: checkbox state, "I read this" markers, personal notes, UI prefs.
await Drop.state.set('action-3', true);
await Drop.state.get('action-3');                  // null if unset

// Shared state — everyone who can read the Droplet sees the same value.
// expectedVersion enables optimistic concurrency; omit for last-write-wins.
const { value, version } = await Drop.shared.get('chosen-date');
await Drop.shared.set('chosen-date', '2026-06-12', { expectedVersion: version });

// Polls — one vote per viewer, aggregated counts visible to all.
await Drop.poll.vote('offsite', 'option-a');
await Drop.poll.results('offsite');                // { counts, myVote, totalVotes }

// Surveys — structured submissions; owner sees responses + CSV export on dashboard.
await Drop.survey.submit('feedback', { rating: 4, notes: 'good' });

// Access list — who can read this Droplet (owner + named editors/viewers).
// Use for: assignment dropdowns, @-mention autocomplete, personalised greetings.
const team = await Drop.access.list();             // [{ email, name }, ...]
```

### Viewer info

```js
await Drop.ready;
Drop.viewer.email      // 'jane@himarley.com' | null  (null for external/share-link viewers)
Drop.viewer.isInternal // true | false              (false = writes are no-ops)
```

When `isInternal === false` (an external share-link viewer), writes silently no-op. Hide interactive UI for those viewers if it doesn't make sense without state:

```js
if (!Drop.viewer.isInternal) document.getElementById('checklist').remove();
```

### Limits

- Each value: 256 KB max (JSON-serialized)
- 1,000 distinct keys per Droplet (private + shared combined)
- 60 writes / minute per (viewer, Droplet)
- `onChange` handlers poll every ~5s — fine for "the team is editing this together," not for chat-like real-time
- Reserved key prefixes — `_drop.`, `_poll.`, `_survey.` — author code can't write these directly, use the helpers

### Owner experience

On the Droplet's dashboard page (`/apps/{slug}`), there's a **State** tab that shows polls (with voter expand), survey responses (with per-survey CSV export), and shared state values. Private state — per-viewer values like checkbox ticks — is not shown there by design.

Full SDK reference + a live demo at `https://drop.himarley.ai/docs#section-state-sdk`.

### Quickstart pattern: persistent checklist

```html
<!doctype html>
<html><body>
  <h1>Sprint runbook</h1>
  <ul>
    <li><label><input id="step1" type="checkbox"> Restart the worker</label></li>
    <li><label><input id="step2" type="checkbox"> Verify dashboards</label></li>
  </ul>
  <script>
    (async () => {
      await Drop.ready;
      for (const id of ['step1', 'step2']) {
        const el = document.getElementById(id);
        el.checked = (await Drop.state.get(id)) === true;
        el.addEventListener('change', () => Drop.state.set(id, el.checked));
      }
    })();
  </script>
</body></html>
```

## Tags

Tags are short freeform labels (e.g. `payment-runs`, `q3-review`, `hr/onboarding`) attached to Droplets to group related work. A Droplet can carry up to 20 tags; each tag is up to 50 chars, normalized to lowercase + trimmed on the server.

### Adding tags

```bash
# Repeat --tag for each tag at deploy time
drop create app.html --slug my-app --tag payments --tag q3-review

# Or update an existing Droplet's tags via the web UI (per-Droplet chip editor
# on the Droplet detail page) or the multi-select bulk picker on /dashboard.
```

API: `PATCH /api/apps/{slug}` with body `{ tags: [...] }` replaces the set. Bulk-add (UNION-friendly) via `POST /api/apps/bulk/tags` with body `{ slugs: [...], add: [...] }`.

### Searching by tag

The dashboard search bar and `drop search` both understand `tag:` as an inline operator. The shortcut `#foo` resolves to `tag:foo` in the dashboard header search:

```bash
# All Droplets tagged "payments"
drop search "tag:payments"

# Compose with other operators
drop search "tag:payments from:jane after:2026-04-01"

# Person-aware shared: — find what you shared with someone specific
drop search "from:me shared:dylan@himarley.com"
drop search "shared:@engineering type:droplet"
```

`from:` and `shared:` both accept a person email, a `@group-slug`, or `me`. The dashboard palette suggests matches as you type after the colon.

### Space-private tag prefixes

A Space can register prefixes (e.g. `hr/`, `legal/`) that hide matching tags from non-members. The tags still exist on the Droplet; non-members just don't see them in tag lists, autocomplete, or search results. The Droplet's owner is exempt from the filter on their own Droplets — owners can apply any prefix to their own work regardless of Space membership.

### Permissions

Tag editing requires droplet edit access — any authenticated user with edit permission on a Droplet can add or remove tags via the chip editor, bulk picker, or `--tag` CLI flag. Read access (searching by tag, viewing tags on a Droplet) is open to everyone.

## Skills

Drop hosts shared Claude Code skills — reusable recipes for creating branded droplets (presentations, meeting summaries, recommendation docs, blueprints, and more).

### Discovering and Using Skills

```bash
# See what's available
drop skills

# Install a skill (downloads to ~/.claude/skills/<name>/SKILL.md)
drop skills install pres

# Now use it in Claude Code — type /pres and follow the instructions
```

Once installed, skills work offline — they're local markdown files that Claude Code picks up automatically.

### Creating Skills

Any authenticated user can create a skill using the dedicated command or the `--type` flag:

```bash
# Dedicated command (recommended)
drop skill create my-skill.md --name "My Skill"

# Or using the --type flag on drop create
drop create my-skill.md --type skill --name "My Skill"
```

Markdown files with YAML frontmatter (`name:` and `description:` fields) are also auto-detected as skills when deployed without `--type`.

Skills appear in the owner's dashboard with a "Skill" badge. Admins can feature skills to make them visible on the `/skills` showcase page.

### Browsing Skills

Visit the [Skills page](https://drop.himarley.ai/skills) to browse featured skills, see examples, and get install commands.

## Troubleshooting

**Any CLI error (auth or otherwise): run `drop upgrade` first, then retry.** An outdated CLI is the most common cause of errors that appear after a Drop server change. If the error output says `Update available`, upgrade before trying anything else. Don't work around it with `--server` flags or by editing `~/.drop/` files.

- **"Login completed but Drop rejected the token"** — Run `drop upgrade`, then `drop login`. Old CLIs still point at Drop's retired domain
- **"Not logged in"** — Run `drop login`
- **"Authentication failed"** — Run `drop upgrade`, then re-run `drop login` to refresh your CLI session
- **"File is too large"** — Droplets must be under 10MB
- **"Only .html and .md files can be deployed"** — Bundle everything into a single HTML file, or write a Markdown file
- **"command not found: drop"** — Run `curl -fsSL https://drop.himarley.ai/install-drop.sh | bash`
