@paragraph-com/cli
v0.0.4
Published
CLI for Paragraph
Readme
@paragraph-com/cli
Command-line interface for Paragraph. Designed for both human and programmatic (agent) usage.
Install
npm install -g @paragraph-com/cliOr via Homebrew:
brew tap paragraph-xyz/tap && brew install paragraphQuick start
# Authenticate
paragraph login
# Create a post
paragraph post create --title "My Post" --file ./draft.md
# Publish it
paragraph post publish my-post
# List your posts
paragraph post listRun paragraph --help to see all commands, or paragraph <command> --help for details on any command.
Authentication
Get your API key from paragraph.com/settings -> Publication -> Developer.
# Interactive login (opens browser or prompts for key)
paragraph login
# Provide token directly
paragraph login --token <your-api-key>
# Pipe token from stdin (useful for CI/agents)
echo "<your-api-key>" | paragraph login --with-token
# Or skip login — pass the key per-command via env var
PARAGRAPH_API_KEY=<your-api-key> paragraph post list
# Verify
paragraph whoami
# Remove stored credentials
paragraph logoutCredentials are stored in ~/.paragraph/config.json (mode 0600). If an API key is revoked, the CLI automatically clears the stored credentials on the next 401 response.
Commands
Posts
# List your posts (requires auth)
paragraph post list
paragraph post list --status draft
paragraph post list --limit 50 --cursor <cursor>
# List posts from any publication (public)
paragraph post list --publication <id-or-slug>
# Get a post -- accepts ID, URL, or @publication/slug
paragraph post get <post-id>
paragraph post get @yearn/some-post-slug
paragraph post get https://paragraph.com/@yearn/some-post-slug
# Extract a single field (outputs raw value to stdout, pipeable)
paragraph post get <id> --field markdown > post.md
paragraph post get <id> --field title
# Browse posts
paragraph post by-tag defi --limit 20
paragraph post feed --limit 10
# Create a post (defaults to draft)
paragraph post create --title "My Post" --text "# Hello World"
paragraph post create --title "My Post" --file ./draft.md
cat draft.md | paragraph post create --title "From Stdin"
paragraph post create --title "Post" --text "Content" --subtitle "Summary" --tags "web3,defi"
# Update a post
paragraph post update <id-or-slug> --title "New Title"
paragraph post update <id-or-slug> --file ./updated.md --tags "new,tags"
# Lifecycle
paragraph post publish <id-or-slug> # publish a draft
paragraph post publish <id-or-slug> --newsletter # publish + send email to subscribers
paragraph post draft <id-or-slug> # revert to draft
paragraph post archive <id-or-slug> # archive
# Preview before acting
paragraph post publish <id-or-slug> --dry-run
paragraph post delete <id-or-slug> --dry-run
# Send test newsletter email (draft only)
paragraph post test-email <id>
# Delete a post
paragraph post delete <id-or-slug>
paragraph post delete <id-or-slug> --yes # skip confirmation (required for agents/CI)Top-level shortcuts:
paragraph create --title "Quick Post" --text "Content"
paragraph update my-post-slug --title "Updated"
paragraph delete my-post-slug --yesPublications
# Get publication -- accepts ID, slug, or custom domain
paragraph publication get @variantwriting
paragraph publication get blog.variant.fund
paragraph publication get <publication-id>Search
paragraph search post --query "ethereum"
paragraph search blog --query "web3"Subscribers
paragraph subscriber list --limit 100
paragraph subscriber count <publication-id>
paragraph subscriber add --email [email protected]
paragraph subscriber import --csv subscribers.csvCoins
paragraph coin get <id-or-address>
paragraph coin popular --limit 10
paragraph coin search --query "test"
paragraph coin holders <id-or-address> --limit 50
paragraph coin quote <id-or-address> --amount <wei>Users
paragraph user get <user-id>
paragraph user get 0x1234... # by wallet addressInteractive TUI
Running paragraph with no arguments launches an interactive terminal UI with menus, scrollable lists, and keyboard navigation.
The TUI is disabled automatically when:
--json,--help, or--versionflags are used- stdout is not a TTY (e.g., piped output)
CI=trueorPARAGRAPH_NON_INTERACTIVE=1is set
Agent / programmatic usage
The CLI is designed for use by AI agents and scripts.
JSON output
All commands support --json. Data goes to stdout, status messages to stderr, so you can pipe cleanly:
paragraph --json post list | jq '.data[0].title'
paragraph --json post get <id> | jq '.markdown'
paragraph --json search post --query "web3" | jq '.length'Paginated commands return:
{
"data": [{ "id": "...", "title": "..." }],
"pagination": { "cursor": "abc123", "hasMore": true }
}Single-item commands return the object directly:
{ "id": "...", "title": "...", "markdown": "..." }Structured errors
In --json mode, errors are structured JSON on stderr with a non-zero exit code:
{ "error": "Not found.", "code": "NOT_FOUND", "status": 404 }Error codes: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, RATE_LIMITED, SERVER_ERROR, REQUEST_FAILED, CLIENT_ERROR, UNKNOWN.
Flags and stdin
Every identifier accepts both a positional argument and an --id flag, so you can chain commands:
paragraph post get --id $(paragraph --json post list | jq -r '.data[0].id')Content can come from --text, --file, or stdin:
cat draft.md | paragraph post create --title "My Post"Non-interactive safety
deleterequires--yesin non-TTY environmentsloginsupports--with-tokenfor stdin piping and--tokenfor direct input- Destructive commands support
--dry-runto preview without acting - Set
PARAGRAPH_NON_INTERACTIVE=1orCI=trueto force CLI mode
Environment variables
| Variable | Purpose |
|----------|---------|
| PARAGRAPH_API_KEY | API key (alternative to login) |
| PARAGRAPH_API_URL | Custom API base URL |
| PARAGRAPH_NON_INTERACTIVE | Set to 1 to disable TUI |
| CI | Set to true to disable TUI |
Development
git clone https://github.com/paragraph-xyz/paragraph-cli.git
cd paragraph-cli
npm install
npm run build
npm run dev # watch mode
npm testRequires Node.js >= 18.
Releasing
npm run release patch # 0.1.0 → 0.1.1
npm run release minor # 0.1.0 → 0.2.0
npm run release major # 0.1.0 → 1.0.0Builds, tests, publishes to npm, creates a GitHub release, and updates the Homebrew tap. Requires gh CLI and npm publish access.
