From 1c654a8e66fea5dff17e0620b09c693dde2da69f Mon Sep 17 00:00:00 2001 From: FND Date: Fri, 4 Sep 2026 12:27:55 +0200 Subject: [PATCH 1/2] feat: add agent skill for zero-install publishing Resolves #2. Agents no longer need the npm-led install: a drop-in agent skill (skills/tot) teaches the full publish/update/remove lifecycle via curl against https://api.tot.page, and prefers the tot CLI when it is on PATH (asset scanning, checkpoint polling, ~/.tot registry). - SKILL.md: model-invoked skill, core model + CLI-or-curl route + markdown publish flow with the checkpoint gate - references/html-assets.md: workspace-first asset flow and scan rules - references/api.md: full endpoint reference, exact response shapes - README 'For AI agents' section; REPO_LAYOUT records skills/ All curl flows verified live end-to-end against api.tot.page (publish, checkpoint poll, living+frozen URLs, update, asset serve, delete), test pages removed afterwards. --- README.md | 14 +++++ docs/REPO_LAYOUT.md | 2 + skills/tot/SKILL.md | 80 ++++++++++++++++++++++++++ skills/tot/references/api.md | 85 ++++++++++++++++++++++++++++ skills/tot/references/html-assets.md | 59 +++++++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 skills/tot/SKILL.md create mode 100644 skills/tot/references/api.md create mode 100644 skills/tot/references/html-assets.md diff --git a/README.md b/README.md index d179968..b3ffddb 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,20 @@ tot notes.md | `tot remove ` | Remove the living page from its share link. | | `tot login --key ` | Optional. Publish as an owned account instead of anonymous. | +## For AI agents + +Agents can publish with zero install: the CLI is a thin wrapper over the HTTP API at +`https://api.tot.page`, and a drop-in [agent skill](skills/tot/SKILL.md) teaches the full +curl flow — publish, update, remove, HTML assets, ownership. Copy it into your agent's +skills directory: + +```bash +cp -r skills/tot ~/.claude/skills/tot # or .cursor/skills/, ~/.agents/skills/, … +``` + +Plain API reference: [site/agents.md](site/agents.md). With the CLI installed the skill +uses `tot`; otherwise it falls back to curl. + ## How it works Files are served byte for byte. Markdown comes back as raw markdown. HTML comes back as raw HTML. diff --git a/docs/REPO_LAYOUT.md b/docs/REPO_LAYOUT.md index b24a271..9252de8 100644 --- a/docs/REPO_LAYOUT.md +++ b/docs/REPO_LAYOUT.md @@ -14,6 +14,8 @@ Root is for the npm package and top-level project files: - `src/` - CLI implementation. - `test/` - CLI tests. +- `skills/tot` - distributable agent skill (`SKILL.md` + `references/`) that teaches + agents to publish via the `tot` CLI or the HTTP API directly, no install required. - `package.json`, `pnpm-lock.yaml`, `tsconfig*.json`, `vitest.config.ts` - package tooling. - `README.md`, `LICENSE` - public package metadata. diff --git a/skills/tot/SKILL.md b/skills/tot/SKILL.md new file mode 100644 index 0000000..00b55da --- /dev/null +++ b/skills/tot/SKILL.md @@ -0,0 +1,80 @@ +--- +name: tot +description: >- + Publish a markdown or HTML file to a live public tot.page URL — a living link + plus immutable @hash snapshots — then update or remove it, with curl alone or + via the tot CLI. Use when asked to publish, share, host, or put online a + document, report, or page, to update or delete an existing tot.page link, or + whenever tot or tot.page comes up. +--- + +# tot — publish a file to a live link + +One local file becomes one public URL. No signup, no install — `curl` is enough +(the `tot` CLI, when present, makes the same calls for you). Files are served +raw: markdown comes back as raw `.md`, HTML as raw `.html`, byte for byte. No +config, no build step. + +Every page has two URLs: + +- **living** — `https://tot.page/{slug}`, always shows the latest version. +- **frozen** — `…/{path}@{hash}`, one immutable snapshot per publish, for + pointing at "exactly this version". + +## The rules that bite + +- **Checkpoint gate.** A fresh page answers `version: null` until its first git + checkpoint lands (~2–10s). Poll the read endpoint until `version` is + non-null — the link 404s until then. A publish is done when the poll passes, + not when the POST returns. +- **Open pages.** Without an API key every page is open: anyone holding the link + can read, update, or delete it. Publish content you mean to be public, and + treat each link as the credential it is. +- **1.5 MB body limit** (UTF-8) — over it the API answers `422`. Errors are + JSON: `{"error":{"code":"…","message":"…"}}`. + +## Route: CLI or curl + +```bash +command -v tot +``` + +- **found** → use the CLI; it scans HTML assets, waits out the checkpoint, and + tracks published pages in `~/.tot` across sessions: + `tot ` publish · `tot update ` · `tot remove ` · `tot list` +- **not found** → the curl flows below; identical capability, zero install. + +## Publish (markdown, or HTML with no local refs) + +```bash +BODY=$(jq -Rs '{kind: "markdown", body: .}' notes.md) # HTML → kind: "html" +RESP=$(curl -sS -X POST https://api.tot.page/v1/documents \ + -H 'content-type: application/json' -d "$BODY") +WS=$(jq -r .workspace.id <<<"$RESP") # the page's identity — keep it +DOC=$(jq -r .document.id <<<"$RESP") # for update/remove — keep it +``` + +`jq -Rs` JSON-encodes the file safely; hand-writing `body` breaks on newlines +and quotes. HTML with local file references takes the asset flow instead — see +Branches below. + +Pass the checkpoint gate, then report both URLs: + +```bash +until PAGE=$(curl -sS "https://api.tot.page/v1/workspaces/$WS/documents/$DOC") \ + && [ "$(jq -r .version <<<"$PAGE")" != "null" ]; do sleep 2; done +jq -r '"living: \(.share_url)\nfrozen: \(.file_url)"' <<<"$PAGE" +``` + +Done when both URLs are in hand and given to the user. `$WS` and `$DOC` are the +page's identity for every later update or remove — keep them for the session; +across sessions, note them next to the source file, or switch to the CLI, which +records them in `~/.tot`. + +## Branches + +- **HTML referencing local files** (images, stylesheets, scripts, video) — + upload assets before the document: + [references/html-assets.md](references/html-assets.md) +- **Update · remove · key auth · full endpoint reference** — + [references/api.md](references/api.md) diff --git a/skills/tot/references/api.md b/skills/tot/references/api.md new file mode 100644 index 0000000..d7ffa8a --- /dev/null +++ b/skills/tot/references/api.md @@ -0,0 +1,85 @@ +# tot.page API reference + +Base URL `https://api.tot.page`; pages are served at `https://tot.page/{slug}`. +Auth is optional: send `Authorization: Bearer wsk_live_…` to publish as an owned +account. Without it pages are open — the link is the key (see SKILL.md). + +Body limit 1.5 MB (UTF-8) → `422`. Errors are JSON: +`{"error":{"code":"…","message":"…"}}`. + +## POST /v1/documents — publish (markdown, or HTML with no local refs) + +Request: `{"kind": "markdown" | "html", "body": "", "title"?: ""}` + +Response `201` — note the nesting; the document lives under `.document`: + +```json +{ + "document": { + "id": "doc_…", + "workspace_id": "ws_…", + "doc_path": "index.md", + "kind": "markdown", + "title": null, + "version": null, + "share_url": "https://tot.page/{slug}", + "file_url": null, + "created_at": "…", + "updated_at": "…" + }, + "workspace": { "id": "ws_…", "slug": "…", "share_url": "…", "visibility": "open" } +} +``` + +`version`/`file_url` are `null` until the first checkpoint lands (~2–10s); poll +the read endpoint below until `version` is non-null. + +## GET /v1/workspaces/{wsId}/documents/{docId} — read + +Returns the document object **bare** (no `.document` wrapper): + +`id, workspace_id, doc_path, kind, title, version, share_url, file_url, body, +created_at, updated_at` + +- `Accept: application/json` (default) → the object above. +- `Accept: text/markdown` → the raw body. + +This is also the checkpoint poll. + +## PUT /v1/workspaces/{wsId}/documents/{docId} — update, same living link + +**Raw body, not JSON.** + +```bash +curl -sS -X PUT "https://api.tot.page/v1/workspaces/$WS/documents/$DOC" \ + -H 'content-type: text/markdown' --data-binary @notes.md +``` + +- Content-Type: `text/markdown` or `text/html`. +- Optional `If-Match: ` → `412` when the page changed under you. +- Returns the bare document with the new `version` and `file_url`. The living + `share_url` reflects the new version within ~60s. +- HTML with local refs: upload new/changed assets first — see + [html-assets.md](html-assets.md). + +## DELETE /v1/workspaces/{wsId}/documents/{docId} — remove + +`204`, hard delete, no undo. The living link 404s immediately; frozen `@hash` +snapshots stay up for the workspace's lifetime. Open pages: anyone with the +link can delete. + +## Workspace endpoints — for HTML with assets + +- `POST /v1/workspaces` → `201` with the workspace **nested**: + `{"workspace": {"id": "ws_…", "slug": "…", "share_url": "…", "visibility": "open"}}` +- `PUT /v1/workspaces/{wsId}/assets/{assetPath}` — raw asset bytes; 200. +- `POST /v1/workspaces/{wsId}/documents` — request + `{"doc_path": "index.html", "kind": "html", "body": ""}`; `201` with the + bare document object. + +Full asset flow and scanning rules: [html-assets.md](html-assets.md). + +## GET /v1/me — identify a key + +`Authorization: Bearer wsk_live_…` → `{ "user_id": "…", "email": "…", +"active_org_id": "…" }`. Use to verify a key before relying on it. diff --git a/skills/tot/references/html-assets.md b/skills/tot/references/html-assets.md new file mode 100644 index 0000000..eace8c8 --- /dev/null +++ b/skills/tot/references/html-assets.md @@ -0,0 +1,59 @@ +# HTML with local assets + +For HTML that directly references local files. Bare HTML (only external URLs or +inline content) takes the simple flow in SKILL.md instead. + +## Scan the HTML first + +Direct refs that count as assets: + +- ``, `` entries, `