Seed CLI Skill
SkillSearchRecommended starting point, no local setup needed. Create, update, and read Seed Hypermedia documents and comments using the Seed CLI. Use when the user wants to write, read, search, or manage Seed documents. Connects to remote servers (hyper.media by default).
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Seed CLI Skill skill
What this skill tells your AI
The instructions your AI receives, as published by seed-hypermedia/seed in .agents/skills/seed-cli/SKILL.md and read by ahel’s review.
Scope: Document operations on Seed Hypermedia via the Seed CLI — creating, updating, reading documents and comments, searching, and managing drafts. For low-level gRPC access to a local Seed daemon, use the seed-grpc skill. For LLM-powered PDF import see the seed-pdf-import skill.
Prerequisites
1. Ensure CLI is Available and Up-to-Date
The Seed CLI is distributed as the npm package @seed-hypermedia/cli (binary: seed-cli).
Use npx to run it — this avoids global install permission issues and always fetches the latest version:
npx -y @seed-hypermedia/cli@latest --help
Throughout this skill, replace any seed-cli <command> with npx -y @seed-hypermedia/cli@latest <command>. The -y
flag auto-confirms the install prompt. npx caches the package locally after first run, so subsequent calls are fast.
If the user already has seed-cli installed globally (check with command -v seed-cli), you may use seed-cli
directly instead of the npx form.
2. Check Available Keys
The CLI resolves signing keys from two sources (vault support requires @seed-hypermedia/cli >= 0.2.0 — the
npx ...@latest form always qualifies):
- The vault (
vault.json) — the encrypted identity store used by the daemon and desktop app. Identities created in the desktop app (the user's real accounts) are available for signing. Read-only: the CLI never modifies the vault. Discovery order:--vault <path>flag →SEED_VAULT_PATHenv var →vaultPathin~/.seed/config.json(seed-cli config --vault-path <path>) → well-known locations (Linux desktop app:~/.config/Seed/daemon/vault.json,Seed-devwith--dev; macOS:~/Library/Application Support/Seed/daemon/vault.json; bare daemon:~/.mtt/vault.json). The vault's decryption secret (KEK) comes from the OS keychain (serviceseed-hypermedia-vault-secret-v2); on headless machines passSEED_VAULT_KEK(base64, 32 bytes) directly — treat it like a private key. - The OS keyring — legacy store, still used for keys the CLI creates itself (
key generate/key import). Serviceseed-daemon-main(mainnet) orseed-daemon-dev(devnet).
When a name/account exists in both, the vault wins. key list shows a source field (vault or keyring) per key. No
daemon needs to be running. Mainnet identities typically live in the desktop app's vault, not the keyring — an empty
or missing seed-daemon-main keyring does NOT mean there are no mainnet keys; always check key list (vault included).
If key list output has no source field, the installed CLI predates vault support — use the npx ...@latest form
instead. Full reference: docs/KEYS.md shipped with the npm package
(https://unpkg.com/@seed-hypermedia/cli/docs/KEYS.md).
IMPORTANT: Mainnet and devnet use separate keyrings and separate vaults. A key from one network does not exist
in the other. Always list keys with the same environment flag you will use for publishing — use key list --dev
when you intend to publish with --dev, and key list (no flag) when targeting mainnet.
# List keys (mainnet — production; vault + keyring, with source field)
seed-cli key list
# List keys (devnet — development)
seed-cli key list --dev
# Read a specific vault file explicitly
seed-cli --vault /path/to/vault.json key list
If no keys exist, the user must import or generate one:
# Import from mnemonic (recovers existing account)
seed-cli key import -n mykey "word1 word2 ... word12"
# Generate a new key
seed-cli key generate -n mykey --show-mnemonic
3. Determine Server & Environment
Seed Hypermedia operates on completely separate, isolated networks. Each network is effectively a different protocol — keys, documents, accounts, and all data belong to one network only. Nothing is shared or visible across networks.
| Environment | Flag | Keyring | Default server |
|---|---|---|---|
| Mainnet (production) | (none) | seed-daemon-main | https://hyper.media |
| Devnet (development) | --dev | seed-daemon-dev | (local or configured) |
| Custom server (testnet) | --server | depends on --dev | user-specified URL |
What "isolated" means in practice:
- A key registered on mainnet does not exist on devnet. You cannot use it there.
- A document published on devnet cannot be seen, fetched, or referenced from mainnet.
- Searching on one network returns zero results from the other.
hm://IDs are network-scoped — the same account ID on mainnet and devnet are unrelated identities.
IMPORTANT: The --dev flag (or lack of it) must be used consistently on every command in a session — including
key list, search, document get, document create, etc. Mixing flags (e.g., listing keys without --dev but
publishing with --dev) will cause "Key not found" errors because you are crossing network boundaries.
Always ask the user which environment to target if unclear. Use --dev when testing. Determine the server context early
and keep it consistent across the entire session.
# Mainnet (default — no flags needed)
seed-cli key list
seed-cli search "topic" --type hybrid --limit 40
seed-cli document create -f content.md --key mykey
# Devnet — --dev on EVERY command
seed-cli key list --dev
seed-cli search "topic" --type hybrid --limit 40 --dev
seed-cli document create -f content.md --key mykey --dev
# Custom server — --server on EVERY command (combine with --dev if targeting a dev server)
seed-cli search "topic" --type hybrid --limit 40 --server http://localhost:4000
seed-cli document create -f content.md --key mykey --server http://localhost:4000
Draft Management
The CLI has a draft subcommand for managing local document drafts before publishing. Drafts are stored as markdown
files in the platform-specific Seed app data directory (shared with the desktop app):
- Linux:
~/.config/Seed/drafts/ - macOS:
~/Library/Application Support/Seed/drafts/ - Windows:
%APPDATA%\Seed\drafts\
With --dev, "Seed" becomes "Seed-local". Override with SEED_CLI_DRAFTS_DIR env var.
Draft filenames follow the format <slug>_<nanoid>.md (e.g., my-document_aBcDeFgHiJ.md). The slug is auto-generated
from the document title (lowercase, hyphens, max 60 chars) and the nanoid is a unique 10-character ID. The CLI can also
read .json drafts created by older versions of the desktop app.
Drafts are also registered in index.json which stores routing metadata (publish target, visibility, dependencies) that
cannot be expressed in markdown frontmatter. The desktop app reads both the .md files and index.json to display
drafts — CLI-created drafts appear in the desktop app without restarting it.
Draft Commands
# Save a draft (validates content, saves to <drafts-dir>/<slug>_<nanoid>.md)
seed-cli draft create -f content.md
# Save a draft with a publish target (edits an existing document)
seed-cli draft create -f content.md --edit hm://z6Mk.../docs/intro
# Save a draft as a new child document under a parent
seed-cli draft create -f content.md --location hm://z6Mk.../docs
# Save a draft with explicit visibility
seed-cli draft create -f content.md --visibility PRIVATE
# Save to a custom path (bypasses drafts directory and index.json)
seed-cli draft create -f content.md -o /path/to/custom-name.md
# Review a draft (raw markdown) — works with both .md and .json drafts
seed-cli draft get <slug-or-id>
# Review with terminal-rendered pretty output
seed-cli draft get <slug-or-id> --pretty
# List all drafts (both .md and .json)
seed-cli draft list
# Remove a specific draft
seed-cli draft rm <slug-or-id> --force
# Remove all .md drafts
seed-cli draft rm --all --force
Routing flags for draft create:
| Flag | Description |
|---|---|
--edit <hm-url> | HM URL of the document to edit (sets editUid/editPath in index) |
--location <hm-url> | HM URL of the parent to create a child under (sets locationUid/locationPath) |
--visibility <value> | Document visibility: PUBLIC or PRIVATE (default: PUBLIC) |
When --edit or --location is provided, the CLI writes the routing metadata to index.json so the desktop app knows
the draft's publish target. Without these flags, the draft appears in the desktop as a "no target" draft and the user is
prompted to choose a publish location.
Content Input
The CLI accepts content via the -f flag or stdin. Format is auto-detected:
| Source | Format detection |
|---|---|
-f file.md or -f file.txt | Markdown |
-f file.json | JSON blocks (HMBlockNode[]) |
-f file.pdf | PDF (extracted via pdfjs/GROBID) |
| Piped stdin | [ or { first char → JSON; %PDF magic → PDF; else markdown |
Markdown with Frontmatter (Preferred)
The simplest way to create rich documents is markdown with YAML frontmatter. Frontmatter keys map 1:1 to HMMetadata
field names:
---
name: My Document Title
summary: A brief description of the document
displayAuthor: Jane Doe, John Smith
displayPublishTime: 2025-03-01
cover: file://./cover.png
icon: ipfs://bafkrei...
showOutline: true
---
# Introduction
This is a **bold** paragraph with a [link](https://example.com).
## Section One
- First item
- Second item

Supported frontmatter keys: name, summary, displayAuthor, displayPublishTime, icon, cover, siteUrl,
layout, showOutline, showActivity, contentWidth (S/M/L), seedExperimentalLogo, seedExperimentalHomeOrder
(UpdatedFirst/CreatedFirst), importCategories, importTags, theme (object with headerLayout).
CLI flags override frontmatter values. Frontmatter values override PDF-extracted metadata.
JSON Blocks
For precise control over the block tree structure, use JSON. See ./references/seed-document-format.md for the complete block format reference.
seed-cli document create -f blocks.json --name "Title" --key mykey
Resolving Web URLs
Any CLI command that takes an hm:// ID also accepts a plain https:// URL (e.g., seed-cli document get,
document update, comment list, comment create, query). Prefer the URL form when the user pastes one — there is
no need to resolve the domain to an account UID first.
- The server is inferred from the URL origin;
--serveris not required when passing a URL. - Fragments are preserved through URL resolution:
#<blockId>and#<blockId>[start:end](character ranges). - Anti-pattern: do not wrap a domain in
hm://(e.g.,hm://seedteamtalks.hyper.media).hm://authorities are account UIDs, not hostnames. Site domains are strictly for HTTP routing — pass the fullhttps://URL instead.
# ✓ Works — auto-detects server from URL origin, no --server needed
seed-cli document get "https://seedteamtalks.hyper.media/discussions/how-to-bring-legalize-es-to-seed"
seed-cli comment list "https://seedteamtalks.hyper.media/discussions/how-to-bring-legalize-es-to-seed"
seed-cli document get "https://hyper.media" # bare domain → root doc
# ✓ Works — URL with block fragment
seed-cli document get "https://seedteamtalks.hyper.media/discussions/how-to-bring-legalize-es-to-seed#vHwg_kR0"
# ✗ Fails — do NOT construct hm:// from a domain name
seed-cli document get "hm://seedteamtalks.hyper.media" --server "https://seedteamtalks.hyper.media"
# → invalid_argument: failed to parse account 'seedteamtalks.hyper.media': selected encoding not supported
Fallback: HTTP Identity Headers
When the CLI is not available (shell scripts, bespoke tooling), every Seed site page exposes identity headers on GET/HEAD/OPTIONS. They are CORS-exposed and URL-encoded.
X-Hypermedia-Id— canonicalhm://<account>/<path>(URL-encoded)X-Hypermedia-Version— current version CIDX-Hypermedia-Type—DocumentorCommentX-Hypermedia-Title,X-Hypermedia-AuthorsX-Hypermedia-Target— (comment pages only)hm://of the target document being commented on
Values are URL-encoded; decode before use. Example:
curl -sI "https://site.hyper.media/my-doc" | awk '/x-hypermedia-id/ {print}' | python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().split(": ", 1)[1]))'
Write Operations
Create a New Document
The account ID is derived automatically from the signing key — no positional argument needed.
Space publishing: By default, the document is published under the signing key's own account. To publish under a
shared space/site account (e.g. a team site where you have WRITER capability), use the --account flag. The CLI
automatically resolves the required capability from the target account.
# From a markdown file
seed-cli document create -f content.md --key mykey
# Publish under a shared space/site account (requires WRITER or AGENT capability)
seed-cli document create -f content.md --key mykey --account z6MkSpaceAccountId
# From markdown with explicit metadata flags (override frontmatter)
seed-cli document create -f content.md --name "My Document" --summary "Description" --key mykey
# From JSON blocks
seed-cli document create -f blocks.json --name "Title" --key mykey
# From stdin (piped markdown)
echo "# Hello World" | seed-cli document create --name "Hello" --key mykey
# PDF import (built-in extraction)
seed-cli document create -f paper.pdf --key mykey
# With GROBID for better PDF extraction
seed-cli document create -f paper.pdf --grobid-url http://localhost:8070 --key mykey
# Preview PDF extraction without publishing
seed-cli document create -f paper.pdf --dry-run
# Custom path and development mode
seed-cli document create -f content.md -p my-document --key mykey --dev
Parameters:
-f, --file <path>: Input file (format detected by extension:.md,.json,.pdf)-p, --path <path>: Document path (e.g. "my-document"). Auto-generated from name if omitted.-k, --key <name>: Signing key name or account ID-a, --account <uid>: Target space/account UID — publish under a different account using a delegated capability. The CLI automatically resolves a WRITER or AGENT capability for the signing key on the target account.--dry-run: Preview extracted content without publishing--grobid-url <url>: GROBID server URL for enhanced PDF extraction--dev: Use development environment
Metadata flags (override frontmatter and PDF-extracted values):
| Flag | Metadata key | Description |
|---|---|---|
--name <value> | name | Document title |
--summary <value> | summary | Document summary |
--display-author <value> | displayAuthor | Author display name |
--display-publish-time <value> | displayPublishTime | Publish date (YYYY-MM-DD) |
--icon <value> | icon | Icon (ipfs:// or file://) |
--cover <value> | cover | Cover image (ipfs:// or file://) |
--site-url <value> | siteUrl | Site URL |
--layout <value> | layout | Layout style |
--show-outline / --no-show-outline | showOutline | Show/hide outline |
--show-activity / --no-show-activity | showActivity | Show/hide activity |
--content-width <value> | contentWidth | Content width (S, M, L) |
--seed-experimental-logo <value> | seedExperimentalLogo | Logo (ipfs:// or file://) |
--seed-experimental-home-order <value> | seedExperimentalHomeOrder | Ordering (UpdatedFirst, CreatedFirst) |
--import-categories <value> | importCategories | Categories (comma-separated) |
--import-tags <value> | importTags | Tags (comma-separated) |
Fields that accept file:// paths (--icon, --cover, --seed-experimental-logo) are automatically resolved to
ipfs:// at publish time.
What happens internally:
- Parses input content (markdown → block tree, JSON → block nodes, PDF → extracted blocks)
- Merges metadata: defaults < frontmatter/PDF metadata < CLI flags
- Resolves
file://links in blocks and metadata toipfs://CID(chunks files with UnixFS) - Creates three signed blobs: genesis change, document change, and version ref
- Publishes all blobs (document + file/image data) atomically to the server
Update Document Metadata or Content
# Update title
seed-cli document update <hm-id> --name "New Title" --key mykey
# Update metadata fields
seed-cli document update <hm-id> --summary "New summary" --display-author "New Author" --key mykey
# Update content from file (smart diff — only changed blocks are submitted)
seed-cli document update <hm-id> -f updated-content.md --key mykey
# Delete specific blocks
seed-cli document update <hm-id> --delete-blocks "blockId1,blockId2" --key mykey
# Combine metadata and content update
seed-cli document update <hm-id> -f content.md --name "New Title" --key mykey
# Development mode
seed-cli document update <hm-id> --name "Title" --key mykey --dev
Parameters:
<hm-id>: Hypermedia ID of the document (e.g.,hm://z6Mk.../path)-f, --file <path>: Input file (format detected by extension: .md, .json). Diffs against existing content — only changed blocks are submitted.--parent <blockId>: Parent block ID for new content (default: document root)--delete-blocks <ids>: Comma-separated block IDs to delete- All metadata flags from the create command (see table above)
-k, --key: Signing key name or account ID--dev: Use development environment
When using -f, the CLI performs a per-block diff against the existing document:
- Blocks with IDs matching the existing document are compared for content changes — only modified blocks are submitted.
- Blocks with unknown IDs are treated as new.
- Old blocks absent from the input are deleted.
- Metadata from frontmatter is applied as defaults — CLI flags take priority.
Create a Comment
<target> accepts either an hm:// ID or a plain https:// URL (see Resolving Web URLs). A
#<blockId> fragment on the target triggers block-level quoting (see subsection b below). Block-level quoting and
--reply threading are independent axes — they compose.
(a) Plain comment
# Inline text
seed-cli comment create <target> --body "My comment" --key mykey
# From a file
seed-cli comment create <target> --file comment.md --key mykey
# Development mode
seed-cli comment create <target> --body "Comment" --key mykey --dev
(b) Block-level quoted reply
To produce the "quote + reply" format used in Seed discussions, pass the target with a #<blockId> fragment. The CLI
automatically wraps the comment body in an Embed block pointing at the quoted block. For precise character-range
quotes, use #<blockId>[start:end].
# Quote a whole block
seed-cli comment create "hm://<account>/<path>#<blockId>" --body "My reply" --key mykey
# Quote a character range within the block
seed-cli comment create "hm://<account>/<path>#<blockId>[start:end]" --body "My reply" --key mykey
# Same, using a web URL (server inferred from origin — no --server needed)
seed-cli comment create "https://<site>/<path>#<blockId>" --body "My reply" --key mykey
Use the blockRef field from search --type hybrid results to identify blocks to quote. Character ranges come from
HMBlockRange objects emitted during rich-text selection; in markdown workflows, prefer whole-block quoting
(#<blockId> without a range).
(c) Threaded reply (--reply)
# Reply to an existing comment (threading)
seed-cli comment create <target> --body "Reply text" --reply <comment-id> --key mykey
--reply <commentId> threads the new comment under another comment (reply parent). For the comment-ID shape expected
here (and by comment get), see Finding Document and Comment IDs.
(d) Combined — threaded reply that also quotes a block
seed-cli comment create "https://<site>/<path>#<blockId>" --reply <parentCommentId> --body "..." --key mykey
Parameters:
<target>:hm://ID orhttps://URL of the document to comment on. A#<blockId>or#<blockId>[start:end]fragment auto-wraps the body in anEmbedblock (block-level quoted reply).--body: Comment text (inline)--file: Read comment text from a file--reply: Reply to an existing comment by its ID — independent of block-level quoting; combine freely-k, --key: Signing key name or account ID--dev: Use development environment
Workflow
Draft Mode (Default)
When the user asks to "draft", "write", "create a document", or any request that does not explicitly say "publish", use the draft-first workflow. Never publish directly unless the user explicitly asks.
-
Read first — If updating an existing document, use the seed-grpc skill to fetch it.
-
Identify the key — List available keys from the target environment and confirm which one to use. The environment flag on
key listMUST match the flag you will use for publishing — networks are isolated and keys do not cross over.# If targeting devnet: seed-cli key list --dev # If targeting mainnet: seed-cli key listHint for batched prompts: If you need to ask the user about the environment and the signing key in the same interaction round (e.g., when using a question tool that batches multiple questions), run both
key listandkey list --devupfront. Then present all discovered keys grouped and labeled by network (e.g., "mykey (mainnet)", "devkey (devnet)") so the user can pick any key regardless of which environment they choose. Additionally, include a free-text option for the user to paste a raw key:- Public key (starts with
z6Mk): look it up in the matching keyring. - Private key or mnemonic: derive the account ID using
seed-cli key derive.
Resolving unnamed keys: Some keys have no human-friendly name — their
namefield equals theiraccountId(both start withz6Mk). For these, resolve the author's display name from their profile:# For mainnet keys: seed-cli account get <accountId> # For devnet keys: seed-cli account get <accountId> --dev # For a custom domain (e.g., gabo.es): seed-cli account get <accountId> --server https://gabo.es - Public key (starts with
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 56
- Forks
- 12
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Key
seed-cli- Source
- github.com/seed-hypermedia/seed