Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
Animated diagrams your AI agent can write.
</p>

<p align="center">
<b>Local-first. Your code never leaves your machine. No telemetry.</b>
</p>

<p align="center">
<a href="https://github.com/naorsabag/openhop/actions/workflows/ci.yml"><img src="https://github.com/naorsabag/openhop/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
<a href="https://www.npmjs.com/package/openhop"><img src="https://img.shields.io/npm/v/openhop.svg?color=cb3837&label=npm" alt="npm version" /></a>
Expand All @@ -30,7 +34,9 @@
<a href="#install-options">Install</a> ·
<a href="#use-cases">Use cases</a> ·
<a href="#how-it-works">How it works</a> ·
<a href="#examples">Examples</a>
<a href="#examples">Examples</a> ·
<a href="docs/">Docs</a> ·
<a href="https://discord.gg/openhop">Discord</a>
</p>

---
Expand Down Expand Up @@ -88,6 +94,14 @@ npx openhop init
npx openskills install naorsabag/openhop
```

**Path C — plugin install**

```text
/plugin install naorsabag/openhop
```

…or from your agent GUI.

**Want to start the server yourself?**

```bash
Expand All @@ -107,6 +121,13 @@ git clone https://github.com/naorsabag/openhop.git
cd openhop && npm install && npm run dev
```

> **OpenHop v0.1 is local-first.**
> The CLI runs entirely on your machine. There's no hosted backend, no flow
> storage, no servers we keep running. Sharing today = sharing the YAML file
> (or the URL-fragment share link from the [hosted playground](https://naorsabag.github.io/openhop/),
> which compresses the flow into the URL hash — still no server-side
> storage).

## Use cases

Once the skill is installed, point your agent at a codebase and ask it things like:
Expand Down
28 changes: 28 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# OpenHop docs

Reference material for the people who already know what OpenHop is. If you're
new, start with the [project README](../README.md) and `npx openhop demo`.

## Contents

| Doc | What's in it |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| [install.md](install.md) | Every install path — `npx openhop init`, OpenSkills, the Claude Code `/plugin` command, and the manual file-drop layout for each supported client. |
| [yaml.md](yaml.md) | YAML schema reference. Every step kind, every data shape, every keyword the skill is allowed to emit. |
| [cli.md](cli.md) | Full CLI reference — every command, every flag, every exit code. |
| [architecture.md](architecture.md) | What runs where, why it's local-first, and the wire shape between the agent, the CLI, the API, and the web renderer. |

The animated playground lives at <https://naorsabag.github.io/openhop/>. It
loads the same renderer used locally — same sprites, same animation, same
inspector — but stores nothing server-side (the flow is encoded into the URL
hash). Good for sharing a snapshot without anyone installing anything.

## Conventions

- Commands prefixed with `$` are run by you. Output below is what you'd see.
- `npx openhop …` and `openhop …` are interchangeable once the package is
installed globally or as a dev dependency. The docs use `npx` because it
works without any prior install.
- Anything labeled **agent-facing** is what your AI coding agent emits or
consumes — you generally don't write it by hand. The source of truth for
the agent's contract is [`skills/openhop/SKILL.md`](../skills/openhop/SKILL.md).
90 changes: 90 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Architecture

What runs where, and why "local-first" is the load-bearing word in the README.

## The three packages

| Package | Where it runs | What it does |
| ----------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `openhop` (CLI) | your laptop | Validates YAML, talks HTTP to the API. Entry point for `init` / `demo` / `serve` / `push` / `patch` / `list` / `remove`. |
| `@openhop/server` | your laptop | Fastify API on `:8787`. Owns flow storage (in-memory plus a JSON-on-disk fallback at `~/.openhop/`). Exposes REST routes + Swagger at `/docs`. |
| `@openhop/web` | your browser | PIXI-rendered web UI on `:8788`. Subscribes to flow updates, runs the pixel-art animation. |

## Wire shape

```
┌─────────┐ prompt ┌───────┐ YAML ┌────────┐
│ user │ ────────────▶ │ agent │ ────────────▶ │ CLI │
└─────────┘ └───────┘ └────┬───┘
▲ │ HTTP POST /flows
│ animated URL ▼
│ return-trip ┌────────┐
└───────────────────── │ API │
└────┬───┘
│ broadcast
┌────────┐
│ web UI │
└────────┘
│ open in browser
┌────────┐
│browser │
└────────┘
```

The skill at `skills/openhop/SKILL.md` is the agent's contract. It tells the
agent how to recognize the trigger phrases, how to draft the YAML, and what
the CLI's surface looks like.

## Local-first, on purpose

- **No hosted backend.** There is no openhop.dev API server you can point at.
Even the [Pages playground](https://naorsabag.github.io/openhop/) runs the
same web bundle locally in your browser — the flow lives in the URL hash,
not on a server we keep running.
- **No telemetry, no analytics, no phone-home.** The CLI never makes a
network request beyond your configured server URL (`--server`, defaults
`http://localhost:8787`).
- **No account.** No login, no signup, no API key.
- **No flow storage outside your machine.** Flows go to `~/.openhop/` and
the in-memory store of your local API. `openhop remove` actually deletes.

## Sharing without a server

Two ways to share a flow without anyone hosting one:

1. **YAML file.** Open a PR, paste into a chat, attach to an issue. Anyone
with `openhop push <file>` (or the demo) can render it.
2. **URL-fragment share link** from the Pages playground. The YAML is
lz-compressed into the URL's hash (after `#…`). The hash never hits a
server — Pages just serves the static bundle, and the bundle decodes the
hash in the browser. The URL is the entire payload.

If we ever ship a hosted, shareable backend, it'll be **opt-in** and never
the default.

## Component map

```
packages/
├── shared/ # zod schema, parseFlowYaml(), exit codes
├── server/ # Fastify app, in-memory + disk store
├── web/ # PIXI renderer, React Flow canvas, two app shells
│ ├── App.tsx # API-backed mode (npx openhop demo / serve)
│ └── AppFragment.tsx # hash-only mode (GitHub Pages deploy)
└── cli/ # commander.js entry, talks to the API
skills/
└── openhop/
└── SKILL.md # agent-facing contract
```

## What "local-first" doesn't mean

- It's not E2E-encrypted — anything you give the API is sent in plaintext to
`http://localhost:…`. If you `--server` to a remote host, that host sees
everything.
- It's not offline-first — the agent still calls its LLM provider over the
internet. We just don't add a round-trip on top.
- It's not sandboxed — the CLI runs with your user permissions. Don't pipe
untrusted YAML through `push` if you don't trust the source.
153 changes: 153 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# CLI reference

Every command, every flag. The source of truth is `packages/cli/src/index.ts`.

All commands share the same data plane: read from stdin or a path, post to a
local Fastify server, get a flow id back. Output discipline: **data on stdout,
logs on stderr**, `--json` for machine-readable summaries.

## `openhop serve`

Start the API server (`:8787`) and the web UI (`:8788`).

```bash
openhop serve # both API + web
openhop serve --no-web # API only (headless agents, CI)
openhop serve -p 9000 # override API port
openhop serve --web-port 9001 # override web port
```

| Flag | Default | Description |
| ------------------- | ------- | ------------------------------------------------------------------------ |
| `-p, --port <port>` | `8787` | API port. |
| `--web-port <port>` | `8788` | Web UI port. |
| `--no-web` | — | Start API only; skip the web UI. |
| `--no-wait-ready` | — | Don't print the machine-parseable `openhop: ready api=…` line on stdout. |

When both come up, the ready line is:

```
openhop: ready api=http://0.0.0.0:8787 web=http://0.0.0.0:8788 elapsed=0s
```

Agents block on that line to know when to start pushing.

## `openhop demo`

Zero-config bootstrap. Boots API + web in-process, posts a starter
authentication flow, opens your browser at the rendered URL, stays running
until Ctrl-C.

```bash
openhop demo # API on :8787, web on :8788, browser opens
openhop demo --no-open # print the URL only
openhop demo -p 9000 --web-port 9001 # custom ports
```

Substitute for the hosted playground when you'd rather see it run locally.

## `openhop init`

Install the OpenHop skill into every detected AI client on this machine. See
[install.md](install.md) for the per-client paths.

```bash
openhop init # all detected clients
openhop init --dry-run # plan only, write nothing
openhop init --force # overwrite existing skills
openhop init --client claude-code # one client only
openhop init --json # JSON summary on stdout
```

| Flag | Description |
| --------------- | ---------------------------------------------------------------- |
| `--dry-run` | Print what would be written; don't touch disk. |
| `--force` | Overwrite an existing skill instead of skipping. |
| `--client <id>` | One of `claude-code`, `cursor`, `windsurf`, `cline`, `continue`. |
| `--json` | Machine-readable summary. |

Exit codes:

| Code | Meaning |
| ---- | -------------------------------------------------------------------------------------- |
| 0 | At least one client was processed (installed, skipped already-installed, or advisory). |
| 4 | No clients detected — nothing to do. |
| 5 | At least one install failed. |

## `openhop push <file>`

Push a YAML flow to the server. Returns the flow id + render URL.

```bash
openhop push examples/order-flow.yaml
openhop push - < ./flow.yaml # stdin
openhop push --json examples/order-flow.yaml
```

| Flag | Default | Description |
| -------------------- | ----------------------- | -------------------- |
| `-s, --server <url>` | `http://localhost:8787` | Server URL. |
| `--json` | — | Emit JSON on stdout. |

Stdout format (text mode): `https://localhost:8788/flow/<id>`. Stderr carries
validation errors / fuzzy-typo hints when the YAML is invalid (exit 2).

## `openhop patch <flow-id> <file>`

Apply patch operations to an existing flow. Same input shape as the
agent-facing patch contract (`add-nodes`, `remove-nodes`, `add-steps`, etc.) —
see [yaml.md](yaml.md) and the SKILL for the full list.

```bash
openhop patch f_a8b3 ./patch.yaml
openhop patch f_a8b3 --json ./patch.yaml
```

| Flag | Default | Description |
| -------------------- | ----------------------- | -------------------- |
| `-s, --server <url>` | `http://localhost:8787` | Server URL. |
| `--json` | — | Emit JSON on stdout. |

## `openhop list`

List flows on the server.

```bash
openhop list # flat table
openhop list --tree # path-based hierarchy
openhop list --search auth # substring + fuzzy search
openhop list --search auth --limit 10 # cap results
openhop list --json
```

| Flag | Default | Description |
| -------------------- | ----------------------- | ------------------------------------------------------------ |
| `-s, --server <url>` | `http://localhost:8787` | Server URL. |
| `--tree` | — | Render as a directory tree instead of a flat table. |
| `--search <query>` | — | Substring + fuzzy match across title, path, description, id. |
| `--limit <n>` | `50` | Max search results. |
| `--json` | — | Machine-readable summary. |

## `openhop remove <flow-id>`

Delete a flow.

```bash
openhop remove f_a8b3
openhop remove f_a8b3 --json
```

| Flag | Default | Description |
| -------------------- | ----------------------- | -------------------- |
| `-s, --server <url>` | `http://localhost:8787` | Server URL. |
| `--json` | — | Emit JSON on stdout. |

## `openhop --version` / `openhop --api-version`

```bash
openhop --version # CLI semver
openhop --api-version # OpenHop YAML schema version (independent of CLI semver)
```

Agents read `--api-version` to decide whether their generated YAML targets a
schema this CLI supports.
72 changes: 72 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Install

Three install paths, picked by which AI client you use.

## Path A — `npx openhop init` (auto-detect)

The shortest happy path. Auto-detects every Tier-1 AI client on your machine
and drops the `SKILL.md` into the right place.

```bash
npx openhop init
```

Supported by `init`:

| Client | Skills directory | Notes |
| ------------------ | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Claude Code | `~/.claude/skills/openhop/` | Native. |
| Cursor (v2.4+) | `~/.cursor/skills/openhop/` | Native. Also auto-discovers `~/.agents/skills/`. |
| Windsurf (Cascade) | `~/.codeium/windsurf/skills/openhop/` | Native. |
| Cline (3.48+) | `~/.cline/skills/openhop/` | Requires one-time toggle: **VS Code → Settings → Cline → Features → Enable Skills (experimental)**. |
| Continue.dev | (advisory) | No native skills surface; the rules system at `~/.continue/rules/` is too small for a full `SKILL.md`. Tracked for a condensed-rule translator. |

Flags: see [cli.md#openhop-init](cli.md#openhop-init).

## Path B — OpenSkills (cross-vendor universal installer)

For any client `npx openhop init` doesn't know about.

```bash
npx openskills install naorsabag/openhop
```

[OpenSkills](https://github.com/numman-ali/openskills) knows every client's
skills directory and drops the file in the right place. Covers Codex CLI,
Gemini CLI, Junie, GitHub Copilot, OpenCode, Goose, Antigravity, and more.

After OpenSkills places the skill, also run `npx openhop init` so the CLI
machinery the skill calls into is installed locally. `init` skips the skill
copy if OpenSkills already wrote it.

## Path C — plugin install

```text
/plugin install naorsabag/openhop
```

…or from your agent GUI.

## Manual / advisory clients

For clients with no published skills directory (or where the convention is
still in flux), the install is "drop `SKILL.md` somewhere the agent reads
from and re-launch". The directories above are the conventions we ship to.

If you've installed OpenHop somewhere not listed here and it works, open a
PR adding the client to [`packages/cli/src/init.ts:buildClients`](../packages/cli/src/init.ts).

## What gets installed

Just `skills/openhop/SKILL.md` (plus its small assets). The CLI + server +
web renderer ship as the same `openhop` npm package and the agent boots them
on demand the first time you ask for a flow — they don't need to be
pre-installed.

To pre-install everything:

```bash
npm install -g openhop
```

…but `npx` works without it.
Loading
Loading