@critlab/cli
v0.1.0
Published
Critlab CLI + MCP server — Share. Crit. Fix.
Maintainers
Readme
@critlab/cli
Developer preview (0.1.x). Talks to a self-hosted or local Critlab instance via
CRITLAB_API; the hosted service is not live yet.
Critlab CLI + MCP server — Share. Crit. Fix.
Critlab lets designers upload AI-built prototypes and collect element-anchored
comments. This package (crit) publishes prototypes, reads back the feedback
threads reviewers left, and exposes both as MCP tools so a coding agent can
pull feedback and close the loop directly.
Status: verified. The full Epic 1.4 round-trip —
crit publish→ share link → reviewer comment (anchor + env + DOM snapshot) →crit/MCP pulls open threads and the full bundle →crit resolve→ thread drops off the open list — has been exercised end-to-end against a real Critlab web app + sandbox. IfCRITLAB_APIisn't reachable, requests fail with a clearnetwork_errormessage instead of hanging or throwing something opaque.
Install
npm install -g @critlab/cliNote: Earlier
@critlab/clireleases (0.0.x) on npm were a placeholder published from the legacycli/folder at the repo root. This package (packages/cli) replaces it as of0.1.0; the rootcli/folder will be removed in a future cleanup. As noted above, this is a developer preview — there is no hosted Critlab API yet, so you'll need to run your own Critlab web app and pointCRITLAB_APIat it.
Environment variables
| Variable | Default | Purpose |
| --------------- | ------------------------ | --------------------------------------------- |
| CRITLAB_API | http://localhost:3000 | Base URL of the Critlab web app API |
| CRITLAB_TOKEN | (unset) | Optional bearer token, sent as Authorization: Bearer <token> |
CLI usage
# Publish a directory (must contain index.html) as a new prototype
crit publish ./dist --name "Checkout redesign"
# Publish a new version of an existing target
crit publish ./dist --target checkout-redesign
# List open feedback threads for a target
crit threads checkout-redesign
# Print the full bundle for one thread (comments, anchor, attachments) as JSON
crit thread thr_abc123
# Mark a thread resolved after shipping the fix
crit resolve thr_abc123
# Start the MCP server over stdio
crit mcpRunning crit with no arguments prints the tagline and help.
MCP usage
Claude Code
claude mcp add critlab -- crit mcpOther MCP clients (JSON config)
{
"mcpServers": {
"critlab": {
"command": "crit",
"args": ["mcp"],
"env": {
"CRITLAB_API": "http://localhost:3000",
"CRITLAB_TOKEN": ""
}
}
}
}Tools exposed
critlab_publish—{ dir, name?, targetSlug? }→ zips and publishes a directory, returns share + sandbox URLs.critlab_list_open_threads—{ targetSlug }→ compact list of open threads.critlab_get_thread_bundle—{ threadId }→ full thread (comments, anchor, env) plus a screenshot image block when one is fetchable.critlab_resolve_thread—{ threadId, note? }→ marks the thread resolved.
API errors surface as tool results with isError: true and an agent-readable
[code] message ... Hint: ... string.
API contract this package codes against
POST {CRITLAB_API}/api/packages multipart/form-data: file (zip), name, targetSlug?
201 -> { target: { id, slug, name }, version: { id, number, fileCount, sizeBytes }, shareUrl, sandboxUrl }
413 -> { error: { code: 'package_too_large', message, limitBytes, actualBytes, hint } }
400 -> { error: { code, message, hint } }
GET {CRITLAB_API}/api/targets/{slug-or-uuid}/threads?status=open
-> { threads: [{ id, status, anchor, anchorState, route, comments: [...] }] }
GET {CRITLAB_API}/api/threads/{id}
-> { thread }
PATCH {CRITLAB_API}/api/threads/{id} { status: 'resolved' | 'open' }
-> { thread }A few shape details worth calling out because they're easy to get wrong:
anchor.elementTextcarries the anchored element's text — notanchor.text.anchorStateis a bare string,"needs-repin" | "unknown"— not an object, and it does not carry viewport/browser info despite the name.- Env info (browser, OS, viewport, DPR, language) lives per-comment at
comments[].attachments.env, alongsidedomSnapshotandversionRef.viewportScreenshotandelementCropare alwaysnullin v1 — screenshot capture isn't implemented yet. routecan benull.- The
{slug}path segment on/api/targets/{...}/threadsalso accepts the target's uuid.
Claude Code skill
skill/SKILL.md teaches an agent the Critlab workflow (build → publish →
send link → later pull open threads → fix → republish → resolve), not just
the raw tool signatures. Install it alongside the MCP server, or point an
agent at this package's skill/ directory directly.
Round-trip in 60 seconds
With the web app + sandbox running locally (pnpm --filter @critlab/web dev,
pnpm --filter @critlab/sandbox dev, pnpm infra:up) and the CLI built:
$ node dist/index.js publish ./dist --name "CLI test"
Published "CLI test" (svmQDcSmnz) — version 1
Share URL: http://localhost:3000/p/svmQDcSmnz
Sandbox URL: http://localhost:4000/v/ff222981-669d-4ee7-9d2d-fdc223fa8dd2/
# ... send the share URL to a reviewer, they leave a comment on #cta ...
$ node dist/index.js threads svmQDcSmnz
d2fad084-6da3-41f4-a9d7-8d2326a36ecf /index.html "Get started" — Jane Reviewer: The CTA button text is too small on mobile, please increase and add more padding.
$ node dist/index.js thread d2fad084-6da3-41f4-a9d7-8d2326a36ecf
{
"id": "d2fad084-6da3-41f4-a9d7-8d2326a36ecf",
"status": "open",
"anchor": { "cssSelector": "#cta", "elementText": "Get started", ... },
...
}
# ... fix the button, rebuild ...
$ node dist/index.js publish ./dist --target svmQDcSmnz
Published "CLI test" (svmQDcSmnz) — version 2
...
$ node dist/index.js resolve d2fad084-6da3-41f4-a9d7-8d2326a36ecf
Thread d2fad084-6da3-41f4-a9d7-8d2326a36ecf marked resolved.
$ node dist/index.js threads svmQDcSmnz
No open threads for target "svmQDcSmnz".The same loop is available as MCP tools (critlab_publish,
critlab_list_open_threads, critlab_get_thread_bundle,
critlab_resolve_thread) — see src/mcp/server.test.ts for a live,
spawned-process integration test that exercises exactly this sequence.
Development
pnpm --filter @critlab/cli build # tsup -> dist/
pnpm --filter @critlab/cli dev # tsup --watch
pnpm --filter @critlab/cli typecheck
pnpm --filter @critlab/cli test # vitest run (unit always; live MCP
# integration test runs too when
# CRITLAB_API is reachable, otherwise
# it's skipped so CI stays green)