---
name: slidefly
description: |
  Publish or share an existing local HTML deck (HTML PPT / slide file) to Slidefly
  to get a shareable short URL. Use ONLY when (1) a .html file already exists on
  disk AND (2) user mentions "publish / share / 分享 / 发布 / 传到网上 / 给我链接 /
  put online / 上线 / make this shareable", OR wants to manage existing Slidefly
  decks (list / delete / change visibility / claim / login).

  DO NOT use this skill to generate the HTML deck. If the user wants to create a
  new deck, use a slide-generation skill (e.g. anthropic/slides, codex/slides,
  marp, reveal.js) or write HTML inline FIRST, then invoke this skill to publish
  the resulting file.
version: 0.1.0
license: MIT
---

# Slidefly Skill

Publish a local HTML deck to [Slidefly](https://slidefly.app) and return a shareable link on `slidefly.io`.

## Trigger conditions

**Do trigger** when all of the following are true:

1. A local `.html` or `.htm` file already exists (or was just generated in this session).
2. The user wants to publish, share, or manage an existing Slidefly deck.

Examples that **should** trigger:

- "Publish this deck and give me a link."
- "把这个 HTML 演示稿发到网上。"
- "Share the slides with my team."
- "List my Slidefly decks."
- "Delete the deck we published yesterday."

Examples that **should not** trigger:

- "Make me a 10-slide deck about RAG." (generate HTML first, then publish)
- "Improve the typography on slide 3." (edit HTML first)
- "What is Slidefly?" (answer directly, no CLI)
- "Convert this PDF to HTML." (different task)
- "Deploy this Next.js app." (not a single HTML deck file)

## Prerequisites

1. **HTML file on disk** — single self-contained `.html` is best for v0.
2. **Node.js + npx** — preferred entrypoint (no global install required):

```bash
npx -y @slidefly/cli@latest publish ./deck.html --title "My Deck" --json
```

3. **Install this skill locally** (optional, for persistent agent guidance):

```bash
npx -y @slidefly/cli@latest install --target auto
# or: slidefly install --target cursor --scope project
```

Alias for repeated use in one shell session:

```bash
alias slidefly='npx -y @slidefly/cli@latest'
```

## Reader navigation (optional)

Decks can include this script so swipe and keyboard navigation from the Slidefly reader shell (`slidefly.io/d/...`) reaches in-iframe slide decks via `postMessage`:

```html
<script src="https://slidefly.io/reader-host.js"></script>
```

The shell sends `{ source: 'slidefly', action: 'next' | 'prev' | ... }`; the host script dispatches keyboard events and Reveal.js API calls when present.

## Anonymous-first workflow (default)

**No login is required for the first publish.** The CLI posts to the anonymous API, stores `claim_token` locally, and returns a URL on the content domain:

```bash
npx -y @slidefly/cli@latest publish ./deck.html --title "Q4 Plan" --json
```

Example response:

```json
{
  "ok": true,
  "data": {
    "deck_id": "v0c8Kf3sQ1MnEa7bYj9wHt",
    "url": "https://slidefly.io/d/v0c8Kf3sQ1MnEa7bYj9wHt",
    "title": "Q4 Plan",
    "visibility": "unlisted",
    "size_bytes": 184320,
    "anonymous": true
  },
  "warnings": [{
    "code": "ANONYMOUS_LOCAL_ONLY",
    "message": "This deck is managed only on this machine via ~/.slidefly/config.json. Run slidefly login to claim it to your account."
  }]
}
```

Agent obligations after publish:

1. Show the full `https://slidefly.io/d/...` URL to the user.
2. Explain visibility (`unlisted` = link-only access by default).
3. Repeat the `ANONYMOUS_LOCAL_ONLY` warning when present.
4. Never paste `claim_token` or raw `~/.slidefly/config.json` into chat.

## Authenticated publish (when logged in)

After `slidefly login`, the CLI stores an API key in `~/.slidefly/config.json`. Subsequent `publish` calls use the owned-deck API (no local `claim_token`, no `anon_decks` entry):

```bash
slidefly login --json
slidefly publish ./deck.html --title "Q4 Plan" --visibility public --json
slidefly publish ./deck.html --id v0c8Kf3sQ1MnEa7bYj9wHt --json   # update owned deck
slidefly delete v0c8Kf3sQ1MnEa7bYj9wHt --json
slidefly visibility v0c8Kf3sQ1MnEa7bYj9wHt public --json
```

Owned publish supports `--visibility public|unlisted|private` (plan limits apply). Use `--id <deck_id>` to upload a new version of an existing owned deck. `delete` and `visibility` work on owned decks by deck ID when logged in, even if the deck is not in local `anon_decks`.

## Command reference

| Command | Purpose |
|---|---|
| `slidefly publish <file.html> [--title T] [--visibility V] [--id DECK_ID] [--json]` | Publish (anonymous if logged out; owned if logged in). `--id` updates an owned deck |
| `slidefly list [--json]` | List decks stored in local config (no server call) |
| `slidefly open <deck_id> [--json]` | Open deck URL in browser |
| `slidefly delete <deck_id> [--json]` | Delete deck (anon: local claim_token; owned: API key) |
| `slidefly visibility <deck_id> <public\|unlisted\|private> [--json]` | Change visibility (anon: fails with ANONYMOUS_LIMITED; owned: API key) |
| `slidefly claim [deck_id] [--json]` | Claim anonymous deck(s) to logged-in account |
| `slidefly login [--no-claim] [--api-key KEY] [--code] [--json]` | Browser PKCE login (default); `--code` for headless/SSH; `--api-key` is fallback |
| `slidefly logout [--json]` | Clear stored API key (keeps anon_decks) |
| `slidefly status [--json]` | Show local config summary |
| `slidefly install [--target auto\|claude-code\|cursor\|all] [--scope user\|project] [--force] [--from-url URL] [--json]` | Install this skill for Claude Code / Cursor |
| `slidefly uninstall [--json]` | Remove installed skill files (does not delete config) |

Global flag: `--api-key <key>` on any command.

Always pass `--json` when parsing stdout programmatically. Non-TTY stdout auto-emits JSON.

## Common workflows

### Publish a new deck

```bash
npx -y @slidefly/cli@latest publish ./deck.html --title "RAG 101" --json
```

### Update title or republish

When logged in, update an owned deck in place:

```bash
slidefly publish ./deck.html --id v0c8Kf3sQ1MnEa7bYj9wHt --json
```

When logged out, republish creates a new anonymous deck. After `login` + `claim`, use authenticated publish/update instead.

### Manage local anonymous decks

```bash
slidefly list --json
slidefly delete v0c8Kf3sQ1MnEa7bYj9wHt --json
```

### Upgrade to account (claim)

When the user needs public visibility, analytics, or cross-machine management:

```bash
slidefly login --json          # opens browser; auto-claims local anon decks
slidefly login --no-claim --json   # login only, skip auto-claim
slidefly claim --json            # claim manually if needed
```

Fallback when browser/loopback login is blocked (SSH, remote devbox, no local browser):

```bash
slidefly login --code --json
# CLI prints /cli/code URL; user opens it in any browser, copies 8-char code, pastes in terminal
slidefly claim --json
```

Fallback when no browser access at all:

```bash
slidefly login --api-key "$KEY" --json
slidefly claim --json
```

## Error handling matrix

Parse `error.code` from JSON output. Follow retry rules strictly.

| Code | HTTP | Agent action | User message template |
|---|---|---|---|
| `INVALID_HTML` | 422 | Do not retry | "HTML 格式有问题：{details}。需要我重写吗？" |
| `MALICIOUS_CONTENT` | 451 | Do not retry | "Slidefly 拒绝了该 deck（{details}）。误判可去 /report 申诉" |
| `QUOTA_EXCEEDED` | 402 | Do not retry; suggest login | "匿名配额已用完。运行 `slidefly login` 注册后可发更多" |
| `RATE_LIMITED` | 429 | Wait 60s, retry once | 终失败："Slidefly 限频中，请稍后再试" |
| `AUTH_REQUIRED` | 401 | Do not retry; prompt login | "需要登录。运行 `slidefly login` 后我再试" |
| `AUTH_INVALID` | 401 | Do not retry; re-login | "Token 失效，运行 `slidefly login` 重新授权" |
| `FORBIDDEN` | 403 | Do not retry | "无权操作该 deck。匿名 deck 可先 `slidefly login` 再 claim" |
| `DECK_NOT_FOUND` | 404 | Do not retry | "Deck `{id}` 不存在或已删除" |
| `EXPIRED` | 410 | Do not retry | "Deck 已过期" |
| `PASSWORD_REQUIRED` | 401 | Ask user for password | "该 deck 有密码保护，请告诉我密码" |
| `ANONYMOUS_LIMITED` | 403 | Suggest login + claim | "匿名 deck 只能 unlisted。要登录认领后再改吗？" |
| `INTERNAL_ERROR` | 500 | Retry once | "Slidefly 后端错误，稍后重试" |
| `SERVICE_UNAVAILABLE` | 503 | Wait 30s, retry once | 终失败："Slidefly 上游不可用" |

**Network errors:** wait 5s, retry once, then stop.

**Global rules:**

- Never auto-rewrite HTML after `INVALID_HTML` or `MALICIOUS_CONTENT`.
- Do not retry other 4xx errors (except one 429 retry).
- Retry 5xx at most once.

## Output contract

Success:

```json
{ "ok": true, "data": { ... }, "warnings": [ ... ] }
```

Failure:

```json
{ "ok": false, "error": { "code": "...", "message": "...", "hint": "..." } }
```

Human TTY mode prints minimal text (usually the URL). Agents should always use `--json`.

## Sandbox notes

If `npx` is unavailable:

1. Check for a preinstalled `slidefly` binary.
2. Ask the user to run `npx @slidefly/cli publish ...` locally and paste the URL.
3. Worst case: `curl -F file=@deck.html` against `https://slidefly.app/api/decks/anonymous` (requires manual multipart assembly).

If loopback login is blocked, use `slidefly login --code` (headless device flow) or `slidefly login --api-key` with a key from the dashboard.

## Example dialogues

**Success:** User asks to publish an existing `deck.html` → run publish with `--json` → return `https://slidefly.io/d/...` + anonymous warning.

**Quota exceeded:** Surface `QUOTA_EXCEEDED`, suggest `slidefly login`, do not spam retries.

**Malicious content:** Surface `MALICIOUS_CONTENT` verbatim, do not modify HTML and retry.

**Cross-machine:** `slidefly list` empty → explain local-only anon decks; offer `slidefly login` + claim on original machine.

**Go public:** When logged out, `visibility public` on anon deck → `ANONYMOUS_LIMITED` → suggest `slidefly login` + claim, or publish with `--visibility public` after login.

## Privacy & security

- Do not expose `~/.slidefly/config.json`, API keys, or `claim_token`.
- Default visibility is `unlisted` unless the user explicitly asks for `public`.
- Share URLs live on `slidefly.io`; SaaS dashboard lives on `slidefly.app`.
