@displaydev/cli
v0.42.0
Published
CLI for [display.dev](https://display.dev) — publish HTML and Markdown with public, company, private, or invited-person access.
Readme
@displaydev/cli
CLI for display.dev — publish HTML and Markdown with public, company, private, or invited-person access.
Install
npm install -g @displaydev/cliAuthenticate
# Interactive login via email OTP
dsp login
# Or set an API key (from the dashboard)
export DISPLAYDEV_API_KEY=sk_live_...Agent-native login
dsp login is agent-initiated and human-approved. An agent may ask for the
user's email, start the login, and ask the human to read and provide the
six-digit code. The agent must not search the user's mailbox or treat the code
as a reusable credential.
The command adapts to its environment:
| Invocation | Behavior |
|---|---|
| dsp login --email [email protected] (TTY) | Sends OTP, prompts for the code in the terminal. |
| dsp login --email [email protected] (non-TTY) | Sends OTP and exits 0. Caller asks the human for the code and re-invokes. |
| dsp login --email [email protected] --code 123456 | Verifies the code, persists the token, exits. |
For structured output that doesn't rely on prose parsing:
dsp login --email [email protected] --json
# → {"status":"otp_sent","email":"[email protected]",
# "next":{"command":"dsp login","args":["--email","[email protected]","--code","<code>"]}}
dsp login --email [email protected] --code 123456 --json
# → {"status":"authenticated","method":"otp","email":"[email protected]",
# "suggestedActions":[{"type":"install_skill","command":"npx",
# "args":["skills","add","display-dev/skill","--skill","display-dev"],
# "requiresApproval":true}]}
dsp login --email [email protected] --json
# → {"status":"authenticated","method":"sso",
# "suggestedActions":[{"type":"install_skill","command":"npx",
# "args":["skills","add","display-dev/skill","--skill","display-dev"],
# "requiresApproval":true}]}SSO is selected automatically when the organization for the submitted email requires it.
Login failures use {"status":"error","error":"<code>"}. Stable error
codes are email_required, api_key_required, input_failed,
invalid_api_key, sso_required, send_failed,
invalid_or_expired_code, access_denied, device_code_expired,
authentication_failed, and configuration_failed.
The human-provided code is single-use and expires after ten minutes. The agent
sees it, and the compatible --code form places it briefly in process
arguments. The resulting session token is persisted and used only by the CLI.
After OTP, SSO, or API-key authentication, the CLI suggests the optional display.dev skill installation:
npx skills add display-dev/skill --skill display-devInstalling the skill changes agent-owned configuration. An agent must get the user's approval before running the command. Login never runs the installer.
Usage
# Publish a file. User-scoped credentials create a private artifact;
# org-scoped service keys create a company artifact. Publishes without a
# credential create a public artifact you can claim.
# The display name defaults to the filename without the extension.
dsp publish ./report.html
# Share a report with the organization
dsp publish ./report.html --company
# Override the display name
dsp publish ./report.html --name "Q1 Report"
# Publish as public (no login required to view)
dsp publish ./report.html --name "Q1 Report" --public
# Share with external recipients (repeatable)
dsp publish ./proposal.html --name "Q1 Proposal" \
--share [email protected] --share [email protected]
# Update an existing artifact from the version you edited
dsp publish ./report.html --id abc123 --base-version 5
# Change an existing artifact back to company auth
dsp publish ./report.html --id abc123 --base-version 5 --company
# Remove all guest shares from an existing artifact
dsp publish ./report.html --id abc123 --base-version 5 --clear-shares
# Browse and search artifacts
dsp list --author [email protected]
dsp search "quarterly"
dsp search "invoice total" --in abc123@5
# Read a bounded source range, pinned to a version
dsp read abc123@5 --offset 0 --limit 65536
# Replace one exact source span
dsp edit abc123 --base-version 5 --old "Q1" --new "Q2"
# Get artifact metadata, versions, outline, and open threads
dsp get-metadata abc123
# Make an independent copy from the current version
dsp make-copy abc123
# Make a copy from a retained version, then share it with a reviewer
dsp make-copy abc123@3 --name "Q1 plan" --visibility private --share [email protected]
# Delete an artifact
dsp delete abc123 --confirm
# Start MCP server (for Claude Desktop / Claude Code)
dsp mcpPublish flags
| Flag | Effect |
|---|---|
| --name <name> | Display name override. Optional — defaults to the filename without the extension on create. On update, renames the artifact (re-derives the slug). |
| --id <shortId> | Update an existing artifact (publishes a new version). |
| --base-version <n> | Required with --id; the current version you edited from. |
| --public | Set visibility to public (no auth required to view). |
| --company | Set visibility to company auth. This is the default for org-scoped service keys. |
| --visibility <level> | Set visibility to public, company, or private. |
| --share <email> | Grant guest access to an external email (repeatable). |
| --share-with <emails> | Grant guest access to a comma-separated list of emails. |
| --clear-shares | Remove all guest shares. Only valid with --id. |
| --theme <theme> | Markdown rendering theme. |
| --reload | Refresh open viewer tabs when the new version is ready. |
Visibility and share flags default to "keep current" on update: omitting --public, --company, and --visibility leaves visibility unchanged; omitting --share, --share-with, and --clear-shares leaves the guest list unchanged. Use only one of --public, --company, or --visibility. Do not combine --clear-shares with either --share or --share-with.
On create, omitting visibility uses the credential context: user sessions and user-owned keys create Private artifacts, while organization-scoped service keys create Company artifacts. CI jobs using user-scoped credentials should pass --company when the result is meant for the organization.
For update mode, fetch the current version first with dsp get-metadata <shortId>. Use dsp search <text> --in <shortId> and dsp read <shortId> for bounded context, then dsp edit for one exact replacement. Use dsp export when you intentionally need the complete local file, and dsp publish --id for whole-source replacement. If the artifact changed on the server, reconcile against the current version and retry with that baseline.
Updates keep the format established for the artifact. Use a .md file for a Markdown
artifact and a .html file for an HTML artifact. Publish a new artifact when
converting between Markdown and HTML.
Testing
The CLI suite uses three distinct fetch-faking strategies, each pinned to a layer of the stack. New contributors pick the one that matches the layer they're testing — adding a fourth is almost always wrong.
- Unit-level fetch fake (
stubFetchJson(...)) — for exercisingApiClientand anything that composes it purely in-process. Low ceremony, full control over responses, no cross-process boundary. Use thestubFetchJson(body, init?)helper fromcli/src/test-helpers.ts— it stubsglobal.fetchwith a fresh JSONResponseper call and returns thevi.fn()so tests can still inspectfetchMock.mock.calls[...]. Fall back to rawvi.stubGlobal('fetch', vi.fn().mockRejectedValue(...))only for network- failure cases. Representative example:cli/src/api-client.spec.tsand the integration wire-up assertion incli/src/mcp-mode-selection.spec.ts. - Hand-rolled
createMockApiClient()— for MCP tool specs that verify argument-routing from an MCP tool handler to the rightApiClientmethod. No HTTP in the loop at all; the fake is a plain object ofvi.fn()s. Representative example:cli/src/mcp-server.spec.tsandcli/src/mcp-publish-file-read.spec.ts. - Real localhost HTTP fixture (
createHttpFixture+spawnCli) — forprogram.actionE2E specs that need to exercise commander argv parsing, the stdout/stderr split, exit codes, and the full HTTP round-trip. Drives the CLI as a real child process. Representative examples:cli/src/cli-mode-selection.spec.ts,cli/src/cli-subcommands.spec.ts,cli/src/login-otp.spec.ts,cli/src/login-otp-send.spec.ts.
MCP
The CLI doubles as an MCP server over stdio. Add to your MCP client config:
{
"mcpServers": {
"display": {
"command": "dsp",
"args": ["mcp"],
"env": {
"DISPLAYDEV_API_KEY": "sk_live_..."
}
}
}
}Inspecting an existing artifact
Use dsp, not an anonymous web fetch, when inspecting an artifact URL. Gated
artifacts return 403 without display.dev credentials.
The short ID is the path segment before the first hyphen. For
https://display.dsp.so/YQyXggrM-product-video-script, it is YQyXggrM.
| Need | Command |
|---|---|
| Uploaded bytes | dsp export <shortId> |
| HTML as Markdown | dsp export <shortId> --format markdown |
| Specific version | dsp export <shortId>@<n> |
| Bounded source context | dsp read <shortId>[@<n>] [--offset <bytes>] [--limit <bytes>] |
| Metadata, versions, outline, and open threads | dsp get-metadata <shortId> |
| Browse artifacts | dsp list |
| Search artifact names | dsp search <query> |
| Search within one source | dsp search <query> --in <shortId>[@<n>] |
| Exact partial edit | dsp edit <shortId> --base-version <n> --old <text> --new <text>; use --old-file / --new-file for multiline UTF-8 inputs |
| Deprecated compatibility | dsp find [query] (writes a migration notice to stderr) |
Read-only commands are safe for inspection. Commands such as publish, make-copy,
edit, share, rename, delete, logo mutations, comment mutations, thread
mutations, and watch mutations change published state; run them only when the
user requests the change. delete requires --confirm.
The CLI defaults to https://api.display.dev. Set
DISPLAYDEV_API_URL=http://api.display.test:3331 explicitly for local work.
Publishing this package
Production Delivery evaluates every master push and calls
publish-cli.yml as a reusable child only when its planner admits the CLI. The
child publishes only when cli/package.json contains a version newer than npm.
Whenever user-visible CLI behavior changes, update the version in the same change. While the package is pre-1.0:
- Increase the minor version for a new feature or breaking change.
- Increase the patch version for a backward-compatible fix.
- Do not use
1.0.0merely to signal a breaking change; that version is the separate stability commitment.
Publishing uses npm OIDC Trusted Publishing. Do not add an NPM_TOKEN or
--provenance; sigstore provenance is unavailable for this private repository.
The npm trusted-publisher entry must bind display-dev/display-dev to the
calling workflow filename production-delivery.yml, not the reusable child
filename. Update that package setting before changing the production caller.
