The CLIs

The running jobjob_7Hq2 · delivered · document.fr-CA.pdf · $49.00 charged

Two command lines, rendered from one contract. services.dev is yours, to write, check, serve and test a brand. npx <brand> is your buyers', their agents' and your members'.

Your brand has two command lines, for two different people.

  • services.dev is yours. It writes a Service, checks it, serves it on localhost, and drives every branch with magic values and test clocks.
  • npx <brand> is your buyers'. It's published under your own package name, in your brand's words, and never names services.dev. Your buyers, their agents and the members of your Pool run it. Each deploy publishes it.

Both render one column of the contract. Every command a buyer runs is a row of the operations registry, the same row that gives the API its route, MCP its tool and the SDK method its name, so a command can never do something the other surfaces can't.

In an empty folder, one command writes a Service and serves it. The banner prints the CLI's line beside every other surface's:

Terminal
$ mkdir lingua && cd lingua
$ npx services.dev
  wrote   package.json · services/translation.ts · services/translation.md · AGENTS.md
lingua · AI delivers every job.
  site    http://localhost:8787/translation
  api     POST http://localhost:8787/jobs {"service":"translation"}
  key     sk_test_7Hq2 · a call with no key uses it, on localhost only
  mcp     http://localhost:8787/mcp · stdio: npx services.dev mcp
  cli     npx services.dev translation k=v …
  sdk     connect({ origin: 'http://localhost:8787' }).translation.order(input)
  docs    http://localhost:8787/llms.txt
  buyers  http://localhost:8787/portal
  you     http://localhost:8787/console
  AI and money are simulated until you wire them. $0.42 a call is an example.

Your first paid job runs it to an accepted job.

help groups every command by who runs it: you, a buyer or their agent, the person at the line, and you again, testing in the sandbox.

Output
magic values: none

$ npx services.dev help
services.dev

You
  npx services.dev                  write a Service if this folder has none, then serve it
  init [name]                       write services/<id>.ts, its brief and AGENTS.md
  dev [--port 8787] [--members N --market tight|normal|flush]  serve every Service on localhost; an agent orders a first job
  check [--json]                    what each Service sells and leaves you, and anything to fix
  sdk [--out sdk]                   write the brand's typed client: index.js, index.d.ts and package.json
  diff <before> <after>             what a change does on the API, CLI, MCP, SDK, Site, Docs and App
  verify [--url <url>] [--surface http|mcp|all] [--suite <file|url>]  run a brand's conformance suite against it, or any suite@1 document: pass, fail, skipped
  mcp [--url <url>]                 serve this folder's MCP server on stdio, or relay to a running brand's
  tasks offer <task> <member>       name the member a Task goes to
  clocks assign <clock> <member>    assign the member who answers for a clock
  members login <member>            mint a member's own session on localhost
  asks get <ask>                    read an ask
  jobs export <job>                 one job's evidence bundle
  jobs ledger <job>                 one job, as the owner reads it

Seven commands read the folder: init, dev, check, sdk, diff, verify and mcp. Every other command is generated from the operations registry: add a row and its command appears, with no CLI code to write. A Service is ordered by its own id, so translation is a command the moment the file exists.

A command's own help prints the row it runs, as every other surface renders it:

Terminal
$ npx services.dev jobs send-back --help
npx services.dev jobs send-back <job> <note>
  send a job back with a note, and the AI redoes it
  row     job.send_back · POST /jobs/:id/send_back · send_back_job · jobs.sendBack()
  note    What to fix.
  key     Any string you choose: the same key replays the first answer and never acts twice. The Idempotency-Key header does the same.
  exit    0 ok · 2 usage · 3 invalid request · 4 authentication · 5 blocked · 6 conflict · 10 retry

Every command that acts is safe to retry: it is idempotent by nature, like jobs accept, or it takes a key, like this one. The join is checked, not hoped for: check fails surface_drift when a command has no row, or a live row has no command. In CI shows it failing on a Service named like a command.

On localhost, your brand's CLI is services.dev itself. A buyer's command runs against dev exactly as your buyers' will, and gets the job every other surface returns:

$ npx services.dev translation document=https://example.com/lease.pdf to=fr-CA --max 49 --key lease
job_7Hq2 · working · $49.00 held
job_7Hq2 · delivered · lease.fr-CA.pdf · $49.00 charged
  record  draft · ai | review · ai · pass
  ledger  +$49.00 · AI $0.84 · you $48.16
  next    npx services.dev accept job_7Hq2
$ curl -X POST http://localhost:8787/jobs -H 'content-type: application/json' -d '{"service":"translation","document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","max_usd":49,"key":"lease"}'
← 202 · job_7Hq2 · working · $49.00 held
  next    get_job {"id":"job_7Hq2"}
→ tools/call order_translation {"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","max_usd":49,"key":"lease"}
← job_7Hq2 · working · $49.00 held
  next    get_job {"id":"job_7Hq2"}
await lingua.translation.order({"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","max_usd":49,"key":"lease"})
← job_7Hq2 · working · $49.00 held
  next    get_job {"id":"job_7Hq2"}

The capture fails unless all four return the same body, request ids aside. The CLI also waits for delivery and prints the record and the ledger, because on localhost you're the owner. Set a key and it reads the job as that key does, with the buyer's view only: Install and log in shows both.

npx <brand>

Each deploy publishes npx <brand> under your package name, beside your generated SDK. It renders the same column in your brand's own words, from Brand.words (the contract), and adds login for buyers. The members of your Pool get their own commands under a member's session, for the acts the delivery portal offers: attest, send back, decline and release. No key and no agent can run those. Its pages live in your brand's docs, one per command, so your buyers never land on services.dev.

Every command takes --json, or reads SERVICES_DEV_JSON=1 from an agent's environment. It prints the answer as the API sent it, and every failure as the one error shape: type, category, code, reason, next and doc_url. The exit code is the failure's category, so an agent or a CI job branches without reading the words. Build with your coding agent puts it to work.