Skip to main content
Back to CLI Docs

Release history

Every shipped version of @solidnumber/cli — the first CLI built for AI agents. Newest at the top.

Authored by Adam Campbell · BSL-1.1 (converts to Apache 2.0 on 2030-04-14) · GitHub · npm

All releases

Quick scan. Each row is one published version. The deep-dives for the architecturally significant releases follow below.

VersionDateSummary
v2.24.122026-09solid update brings everything Solid# on your machine current and never leaves you stuck; solid app arrives for publishing a designer’s interactive app.
v2.24.112026-09solid inventory import reports every row, and solid update works in the first minutes after a release.
v2.24.102026-09solid docs points at the public booking routes that exist, and the published counts are regenerated.
v2.24.92026-09solid update keeps the MCP server current, not just the CLI.
v2.24.82026-09Flags reach the commands they belong to, a missing-scope error tells you the real fix, and solid mcp install writes a key that does not expire.
v2.24.72026-09solid nest <folder> imports the whole site a designer handed over, solid mcp doctor --fix leaves exactly one Solid# connection, and auth login --token no longer drifts back to the previous account.
v2.24.62026-09solid verbs list takes the Atlas addresses the backend now uses, and a retired address tells you where it went instead of returning an empty list.
v2.24.52026-09A filter you typed did nothing, in three different commands, and “solid audit --limit 3” refused to run at all — a query-parameter name the server does not read is dropped in silence, so the route answers with its own default and the response looks healthy.
v2.24.42026-09solid audit filtered nothing and printed blank columns, and audit export -o wrote the JSON envelope instead of the CSV.
v2.24.32026-09solid find could return the alias of a duplicate pair — the name the manifest had just marked not to use — and solid today printed ‘—’ for revenue and pipeline forever.
v2.24.22026-09solid setup reported success and wrote the server into a file the terminal never opens, so the machine had no Solid connection at all and an account-level connector bound to a different business answered instead.
v2.24.12026-09The “update available” notice never printed, so no client could learn that a fix existed.
v2.24.02026-09An AI tool can hold more than one Solid# connection at once — an account-level connector approved in a browser and a local server the CLI configures — and nothing in the protocol decides which it uses. Sign in to one business and the assistant may answer about the other; not a leak, which is exactly why it is hard to catch.
v2.23.02026-09Three discovery tools answered confidently when they had not understood the question — the failure mode that costs an agent a turn and its trust. find learned how owners actually phrase things, measured against a held-out set. The skills now also ship in the cross-vendor Agent Plugins format.
v2.22.02026-09A tenant pin could make a session announce one company while writing to another, with the warning it silenced being the only thing that would have said so. Pages can be seen before they go live. Thirty help examples that could not run were fixed.
v2.21.12026-09Publishing was broken for every tenant, and a correct write looked like a silent failure. Both fixed. find now understands how people actually type.
v2.21.02026-09Asking what this platform can do no longer costs more than doing it. ⛔ Behaviour change: verbs list no longer returns input_schema by default.
v2.20.02026-09A failed command no longer looks like a successful one. ⛔ Behaviour change: unknown arguments are rejected instead of discarded.
v2.19.02026-09solid update — one command to get current, whichever way you installed. It also finds the second copy of the CLI you did not know you had.
v2.18.02026-09A business becomes a file you can apply — plan, reconcile, lockfile, three-way drift, rollback, and export to adopt a company that already exists.
v2.17.02026-09Deals report their own progress, quizzes branch properly, and every advertised count is generated rather than typed.
v2.16.02026-08Forms, operable — solid forms rebuilt on the verb layer, with a public address for every live form. solid verbs invoke gains --confirm, so writes are invokable from the CLI at all.
v2.15.12026-08Fix — solid transaction start and append now send confirm, after the route that let them skip the gate was closed.
v2.152026-07The Starter Kit — solid init scaffolds a tenant-stamped starter project bound to one company, with the operating rules and a token-safe setup already in place.
v2.142026-07Telephony command center — lines, routing, spend and pricing in the terminal, plus solid call simulate to dry-run a caller through a phone tree before it answers a real one.
v2.132026-06The operator brief. solid today is the one-command daily read; solid how-to explains the platform in plain language to owners and agents alike.
v2.122026-06Site-Publish Substrate — apply reconciles pages, sites, domains and surveys, and solid embed emits paste-ready chat, form and payment-link snippets.
v2.112026-05/06Substrate layer, Sales Agent CLI and outbound voice — declarative reconcile, server-pushed events, 18 sales verbs, and real outbound calls and SMS.
v2.102026-05Multi-site operator commands. solid sites list shows every tenant the calling identity can access; solid sites switch <slug> rebinds .solid/manifest.json in-place — agents handling multiple tenants no longer need to track slugs by hand. The agent-attraction surface continues to ship declare-once across five transports.
v2.92026-05Universal verb invoker — AI-first by default. The agent-attraction surface is now declare-once on the backend (services/agent_verb_manifest.py) and auto-bridged to five transports (HTTP, MCP stdio, WebMCP, UCP, CLI). solid verbs list auto-discovers every verb from GET /api/v1/agent/verbs (public, no auth). solid verbs describe returns JSON Schema for agent validators. solid verbs invoke auto-routes GET vs POST. A new verb declared in the backend manifest lights up the CLI on the next session — no CLI release required. 13 parity assertions guarantee drift across the four transport registries cannot ship.
v2.82026-05Agent-attraction primitives in the CLI. solid transaction (start/append/commit/abort/get) wires the backend's atomic-batch substrate — single rollback point for an agent that needs to run N mutations as one unit. solid manifest prints the tenant JSON-LD manifest (@context + entity IRIs + capability graph) so agents can plan against a single canonical retrieval. solid audit log (paginated, cursor-based, JSON/JSONL/table) hits the new /api/v1/agent/audit endpoint. The terminal is now the operator-side of the agent-attraction surface; the same verbs are MCP-discoverable from Claude Code / Cursor and HTTP-callable from any agent runtime.
v2.72026-05Agent transports go cross-runtime. First-class solid webmcp + solid ucp verbs. The same JSON-LD verb registry is now drivable from stdio MCP (your AI editor), in-browser WebMCP (navigator.modelContext, 31 verbs across 5 surfaces), and UCP (signed buyer-agent wire format). One vocabulary, three transports.
v2.62026-05Predict-and-Act GA — TabPFN-v2 (Nature 2025) zero-shot tabular foundation model wired through solid predict. Day-1 predictions on tiny histories, no per-tenant training, no cold-start. Devon auto-discovers new predict targets nightly. Q-Chain auditor surface (solid qchain export/verify/audit-key) — external auditors validate the agent-action chain offline against /.well-known/qchain.json. Email capture on solid demo create with Sarah-drafted Layer 2 outreach. Magic-link install. brew + scoop distribution.
v2.52026-05solid tenant gate-debug — compute-budget activity gate returns its full decision with a machine-readable reason code (dormant_no_activity_ever, owner_allowlist_billing_exempt, …) so agents pattern-match instead of inferring.
v2.42026-05solid predict surface — deal-close, no-show, payment-late, targets. Tabular Foundation Model engine (TabPFN-v2) — zero-shot under tenant data.
v2.02026-04-30Agent-ready by default. Structured JSON error envelopes ship out of the box; SOLID_LEGACY_ERRORS=1 restores 1.x prose for legacy human-written scripts.
v1.262026-04-22Tenant graph diff + N-Quads/Turtle dump. Pipe into Jena, Neo4j, Blazegraph, GraphDB, Neptune.
v1.242026-04-18Offline mutation queue. Auto-detects connectivity loss. solid push --flush replays. Idempotency keys travel with each queued mutation.
v1.222026-04-14JSON-LD tenant graph + SPARQL 1.1 (offline BGP + online --server). Self-describing JSON-LD per @type.
v1.212026-04-10solid graph — typed walk of every Company/Service/Product/Agent/KB/Chain/Webhook.
v1.102026-03-28Opt-in JSON envelopes via SOLID_JSON_V2=1. AI-Driven CLI with 4-layer safety (mode caps, destructive gate, sandbox, backend enforcer).
v1.02026-03-15First public release. solid auth login, pull, push, kb, pages, ai.

Release notes

What changed, why it was broken, and what it means for anything already calling this CLI.

v2.24.12

2026-09

New: solid app — publishing opens once each company’s apps have a web address of their own; until then the command says so and nothing is published. When it opens, solid app publish <folder> puts an app built in Figma Make, Lovable, v0, Bolt or plain React live for this company. Point it at the BUILT folder (the one with index.html — dist/, build/ or docs/): it is zipped on your machine and uploaded to a private link, so the app’s files never travel inside the API call. solid app list, get, rollback and unpublish manage it; every version is kept, and rollback also brings an unpublished app back.

New: solid app github, run once inside the app’s repository, adds a workflow that builds the app in your own GitHub Actions and publishes it on every push. It saves a key that can only publish apps as a repository secret, so private repositories are never shared with us.

Fixed: sending and replying to email from the inbox commands was rejected on every call — the CLI posted fields the server does not take. It now sends the recipient, subject and text the server expects, and a reply goes to the other party of the original message.

solid update tries every way this machine can install the CLI — the one it detects first, then npm, then Homebrew or scoop — and counts the update done only when the solid your terminal runs reports the new version. The Homebrew tap is added for you. If Homebrew has not picked up the release yet, it says so and does not install a second copy. You only see a command to run yourself when every way has failed, with the list of what was tried.

Once the CLI is updated, the new version refreshes everything else it set up: the MCP server your AI tools launch, the Claude Code session hook, shell completion, the agent skills and plugin in every project where you ran solid agent setup, and the render browser. Only what you set up is refreshed; nothing new is installed.

A second copy of solid first on your PATH — an npm install under another Node version — is updated where it lives. solid update --check lists every part and what it would do; --json reports each part for an agent.

Coming from 2.24.11 or earlier: that version runs its own update code for this one step. If it stops, run npm install -g @solidnumber/cli@latest once — every update after that takes care of itself.

v2.24.11

2026-09

solid inventory import prints created, updated and unchanged counts, names every failed row with its row number, SKU and reason, and lists the columns it did not import. It exits 1 if any row failed. --dry-run says nothing was written and previews each row.

Server-side, shipped with it: the import now writes your real products and per-location stock — it used to fail for every company. Rows match by SKU in your company, a blank cell never overwrites, a new SKU needs a name, and a location column sets that location’s count. --dry-run is now truly a dry run: the server used to ignore the flag the CLI sends. New verb: inventory.import_csv.

solid update installs with --prefer-online, so updating to @latest no longer fails with ETARGET in the first minutes after a release. The MCP self-update uses the same flag.

v2.24.10

2026-09

solid docs listed GET /api/v1/cms/public/availability for open appointment slots. That route never existed (404). It now lists the host-resolved routes: GET /api/v1/cms/pages/public/booking?host=<site> (bookable services), GET /api/v1/cms/pages/public/booking/times?host=<site>&service_id=<id>&date=YYYY-MM-DD (open slots) and POST /api/v1/cms/pages/public/book?host=<site> (book a slot).

Counts re-measured by sync:counts: 172 top-level commands, 914 verbs.

Server-side, not part of this package: new verbs reachable through solid verbs invoke with no CLI update — appointment.confirm / unconfirm / update / book_series / series_update / series_end, staff.*, team.*, reminder.set / list / cancel, sales.motion_*, and appointment.public_times.

v2.24.9

2026-09

Configs written before 2.24.8 launch a bare @solidnumber/mcp, which npx serves from its cache forever. solid update now switches every Solid# launch in Claude Desktop, Cursor, Windsurf and Claude Code to @solidnumber/mcp@latest — only that argument changes; a deliberate version pin is left alone and reported; unparseable files are never written.

A global npm install of @solidnumber/mcp is upgraded when behind, and the MCP step runs even when the CLI is already current.

solid update --json performs the update and reports it, so an agent can run it; --check changes nothing, with or without --json. Exit 1 if any step fails.

v2.24.8

2026-09

solid inbox --limit 5 was read as a subcommand named 5. Fixed — and inbox list --limit / inbox email list --limit now reach the route instead of running with the default 20.

A missing scope is reported as SCOPE_MISSING with the scope it needs, not “check your tier”. solid keys rotate --add-scope <scope> now exists, and rotate creates the new key before revoking the old one.

solid verbs invoke uses each verb’s HTTP method from the manifest; solid ucp consent grant|revoke and solid ucp capabilities call routes that exist; solid subscriptions list sends no filters the route ignores.

solid mcp install writes an sk_ key for this company and @solidnumber/mcp@latest — never an expiring login token. solid mcp doctor names each connection’s company and the exact repair command.

Homebrew and scoop now pick up a release within minutes of npm instead of waiting for the next 4-hour bump.

v2.24.7

2026-09

solid nest <folder> used to send the folder’s path to the server as if it were the HTML. It now reads the folder (relative paths kept, images and fonts as binary, hidden, VCS and node_modules items skipped and listed), imports every page with the links between them kept, and builds each one. --entry names the home page; --single imports only it.

solid mcp doctor --fix removes the local duplicate Solid# connections, backing each config file up first. The claude.ai connector is never touched — it is named as a step for you. --dry-run shows the plan.

auth login --token sets the previous account’s refresh token and cached companies aside before the key is verified, and restores them only if the key is bad.

v2.24.6

2026-09

Class 5 is curated with three-digit addresses (530 taking payment, 531 refunds); verbs list accepted only one or two digits and refused them as malformed. It now accepts a class, a noun or a curated address, 1 to 3 digits.

An address the backend has retired used to answer with an empty list, which reads exactly like “nothing lives here”. It now returns the standard error envelope — 410 address_retired — with the replacement addresses in did_you_mean and a fix you can run. With several candidates the fix is solid map: the CLI does not pick one for you.

The examples in verbs, map and context use the three-digit addresses.

v2.24.5

2026-09

solid audit --limit 3 failed with “Unknown command: solid audit 3”. A guard re-read the raw command line and dropped anything starting with a dash — which drops the flag and keeps its value. Every option on that command takes a value, so the ordinary spaced form was broken for all of them and only --limit=3 worked, which made 2.24.4’s filter fix unreachable by the syntax anyone types. The guard is gone; the parser already knows what is an option.

crm contacts --limit=2 returned all 31 contacts. It sent page_size, a name that route does not declare. blog list sent page_size, page and status against a route reading limit, skip and published, so its --limit and --status did nothing either. Both now use the names the routes read, and --status draft|published is refused rather than dropped when it is neither.

Where a filter genuinely cannot run on the server — a contact’s status is worked out per row, not stored — it is applied to the rows and the output says so, with the count it no longer describes removed. A filter over one page is a different claim from a filter over the company.

solid today showed four identical “FOLLOW UP: Voice call with Customer” items. Calls whose caller could not be identified are now one item with a count, as the chat backlog already was; a follow-up that knows who to ring, or carries a number, keeps its own card.

When the server refuses a request, its reason now reaches you. Commands printed a bare “Request failed”; the server’s detail is in the error envelope, with echoed input and anything secret-shaped removed first, so a key you sent is never printed back.

--token works on every command, and progress spinners never write into --json output, so a machine reading the CLI gets clean JSON.

pages create --site attaches the page to the site you name and honours --json; publish reports the page’s real public URL. With more than one company site the CLI no longer guesses which one you meant.

blog delete now needs a person to approve it. The CLI says nothing changed yet and prints the approval link, instead of a generic failure.

The block schema documents direct-file video (autoplay starts muted and inline) and a hero background video, with working examples.

The context hook prints your pinned business notes once, and a server-side sandbox is driven through the sandbox.* verbs.

v2.24.4

2026-09

Filters are sent as user_id and event_type — the names the route reads. user_email / action_type were ignored, so every filter returned the whole log.

Rows show event, user, channel (web / CLI / MCP) and IP, with the scoped company in the header — the fields the server actually returns.

export -o writes the CSV from the response’s content and warns when the export is capped. --from/--to are not applied by the server yet; the command says so.

v2.24.3

2026-09

Pairs are collapsed to the half with no same_as. “Keep the dotted one” could not choose when both halves were dotted (contact.create / crm.contacts.create), so the higher-scoring alias won. The MCP connector 1.3.0 hides aliases by the same rule.

solid today renders GET /api/v1/dashboard/brief — the payload the web Daily Brief and the phone read — instead of its own aggregator, which read revenue and pipeline from keys that do not exist. A metric marked unavailable stays unavailable; it is never turned into a number.

Did-you-mean suggestions stay inside the namespace you typed.

v2.24.2

2026-09

⛔ The ‘claude’ binary is Claude Code, whose config is ~/.claude.json. setup mapped it to Claude DESKTOP’s claude_desktop_config.json — a file the terminal never reads. It detected the editor, printed “✓ Claude Code”, wrote the key somewhere nothing loads, and left the terminal with no Solid door; an account-level connector approved months earlier, bound to another company, answered every question instead — confidently, with write access. One word in one mapping.

Every existing setup test passed straight through that bug, because they assert what setup PRINTS. The new test asserts where the BYTES GO, and the editor table is exported so it can. The step name still follows the editor (“Claude Code”); only the config target changed.

On a machine that has never had a Solid MCP server — no checkout, no entry in ~/.claude.json — login, switch and solid ai all reported success and provisioned nothing. Syncing repaired a server that already existed; it can now write one when none does.

v2.24.0

2026-09

solid ai enumerates EVERY Solid# connection the tool can reach, including account-level connectors that live on the server and appear in no file on your machine, resolves the company behind each, and refuses to launch if two are active or if one disagrees with your session. Two connections is a refusal, not a preference.

solid mcp doctor reports the whole census: every connection, where it is configured, which company it serves, and whether the CLI can re-point it. It used to check one local file and call a keyless entry “wired”. --json was documented but never implemented; it works now and exits non-zero on a bad verdict.

The login screen no longer claims AI tools inherit your CLI token — they never did, and that one reassuring sentence was why nobody checked. auth login and switch now re-point the local credential to the company you chose.

A publish-time guard refuses to ship source under a version already on npm, so an installed build and a repo build can never claim the same number while differing.

v2.23.0

2026-09

⛔ how-to answered every phrasing with an unrelated topic. “switch company” returned “Where to start as an operator”; “how do I change which company I’m on” returned “What the CLI can do”. Two causes: keywords matched as SUBSTRINGS, so a topic keyed on “ai” fired on expl-ai-n, em-ai-l and f-ai-l; and “do” was itself a keyword, so it won anything nothing else matched. Word boundaries cannot save a keyword that common — it had to go. Worse than either: an unmatched question silently returned the “start” topic, which reads exactly like an answer, so an agent takes it and stops looking. It now returns empty, says so, lists what it does cover, and exits 1. The old test asserted the fallback — it was pinning the bug.

solid find cannot answer CLI-local questions and did not say so. It ranks the backend verb manifest, and switching company is session state, not a verb — so “switch to another company” returned a phone-product verb and “create a new company” returned company.create_field_schema at 0.95. Both are the best answers in the space that was searched, and neither is the answer. The ranker was not wrong; the corpus was. The envelope now names the corpus, its size, and where CLI commands live — unconditionally, because the dangerous case is the confident one.

find now understands the words owners actually use, scored against a HELD-OUT set rather than the prompts it was tuned on. A repeated idiom counted twice and plurals were treated as a different word; both fixed.

A question gets an answer, not an action. The ranker carried a verb’s read/write flag into its output and never consulted it while scoring, so “is anything failing to sync?” ranked a verb that RUNS a sync, and “what can staff see?” ranked one that creates a payment link. Questions now demote mutating verbs — demoted, never hidden, and an imperative still ranks the write first, because refusing “delete every contact” is the execution boundary’s job, not search’s.

solid verbs example handed back calls that cannot run. It seeded optional fields into the payload — workflow.list became {"status":"<status>","limit":0}, which the server rejects, while {} works. Three healthy verbs had been recorded as broken because of it. And --dry-run certified that payload as valid, because it checked shapes and never values while the schema itself declared an enum and a minimum. It now checks enum membership, bounds and unsubstituted placeholders, and names the field and the reason.

solid agent setup also writes an Agent Plugins 1.1.0 package — the cross-vendor standard published 2026-08-06 by Amazon, Cursor, Microsoft, OpenAI and Vercel. Same skills, a plugin.json manifest and an mcp.json, so any client implementing the standard can load them. The .claude/skills/ layout is still written too; dropping it to avoid duplicating two-kilobyte files would break the client most likely to be pointed at this directory.

Four dead command references in user-facing strings (solid auth companies, solid login, solid processor connect). The guard that checks examples scanned two formats and not the prose beside them — the same shape of gap it was written to close. It now scans backticked commands anywhere a user reads them.

The CLI’s own discovery copy still answered 116 agents and named two that do not exist.

v2.22.0

2026-09

⛔ SOLID_COMPANY_ID could not scope a call and behaved as though it did. The backend derives the tenant from the JWT; x-company-id is honoured by two controllers and neither is the general path. Setting the pin did two harmful things anyway — it SILENCED the implicit-tenant warning, and config reported the pinned id as the current company. A session could therefore announce “company 999” while every write landed on 61, with nothing saying otherwise, because the pin had suppressed the only warning that would have. A mismatch between the pin and the session now refuses at boot rather than writing a page, an invoice or a contact into the wrong business. If you set that variable, read this twice.

solid pages preview <id> — to see whether a change looked right, the only option before this was publishing to a live site and reading it back. The backend already had the whole mechanism: a signed, expiring, draft-aware preview link with no command in front of it. The command also says WHICH of the two it is showing you, because “no draft pending” means the link shows what is already live, and that is worth knowing before you review it.

Help documented 30 commands that reject what it shows. 413 examples lived in a table nothing verified, beside a prose form that a test already covered — two formats, one checked, which is how it stayed hidden. Flags shown as positionals (solid users invite a@b.com), commands that never existed (solid widget install). All corrected, and the table is now checked by the same test as the prose.

The doctor could not see prose-only output, so a command that printed a human sentence and no JSON passed a check designed to catch exactly that.

171 top-level commands, 911 verbs.

v2.21.1

2026-09

solid pages publish returned 422 on every page, for every tenant. It sent no body at all. The endpoint takes a model whose fields all have defaults, so {} is the right call and sending nothing read as a missing field. Publishing works again. Unpublish takes no body model, so its bodyless POST was already correct and is untouched.

solid apply said “done” after a page update. A page edit lands in a draft and the live site keeps serving the old content until you publish, so a correct write looked exactly like a silent failure. It cost a full debugging cycle before anyone spotted it. It now says the change is pending publish, lists what changed, and prints the command to run.

solid find was tuned on keyword queries and did well on them. Against the way people actually type, it scored 8 of 20. “Add a new customer” returned a phone-provisioning verb: you say customer, the verb says contact; you say add, the verb says create. The right verb matched neither word. A synonym bridge and rarity weighting take it to 13 of 20 first pick, 16 in the top three, measured against 20 real prompts rather than estimated.

The screen you see after logging in never mentioned find, map or update — a whole release of discovery work you could not discover from the one list most people read. It lists them now. solid context names map, find, where and verbs list <prefix> beside the verb count, for the same reason: an agent that does not know the surface is addressable dumps every verb to locate one.

⛔ solid where does not do what we told agents it did. We described it as “which verbs touch that noun”. It answers where something runs and what breaks with it. solid map is the noun index. A confident wrong description spends the call and answers a different question than the one you asked.

171 top-level commands, 909 verbs.

v2.21.0

2026-09

solid verbs list answered every question at full resolution: every verb with its complete schema, ~316,000 tokens, whether you wanted one or all of them. An agent that asked what it could do had nothing left to do it with. The list now returns an index — name, one line, read or write — at about 23,000 tokens, and three ways in sit in front of it: solid find “refund a payment” goes from plain language to a callable name in one call, solid map hands over the whole platform as ten classes, and every verb now carries a coordinate, so solid verbs list 4 is everything about money and truncating it widens the search. Session start dropped from 27,369 tokens to 177.

⛔ Read this one twice: verbs list no longer returns input_schema by default. Anything parsing schemas out of the list needs --full, or solid verbs describe <name> for one verb at a time. That is the behaviour change, and the reason this is a minor rather than a patch.

The playground stopped lying. --dry-run validates your payload against the verb’s own schema instead of returning success: true for a call production would refuse, it no longer demands --confirm to preview a write, and it never reports success for a rehearsal. Errors now carry one envelope: a 4xx is marked not retryable, an unknown command suggests the real one, and every failure names the literal next command to run.

Verb dispatch routes by transport. A third of the registry is reached over a different path than the rest, and the CLI used to assume one — so those verbs answered 405 no matter how correctly you called them. They work now.

Facets filter the index: --writes, --reads, --tier, --shape. solid verbs example hands back a filled-in call built from the verb’s own schema. --since <etag> makes a repeat fetch nearly free. Output is compact on a pipe and pretty on a terminal; indentation alone was 171,089 tokens.

The verb list comes from your platform, not from the package — the CLI fetches it on the call, so a verb added server-side is callable that day without upgrading. The CLI’s own commands and flags ship in the release and change only when you install one.

170 top-level commands, 908 verbs.

v2.20.0

2026-09

The CLI’s failure paths exited 0. For an agent — the only caller this surface really has — exit 0 with no payload is indistinguishable from “succeeded, found nothing”. Seven of eleven get commands reported success on a 404 and moved on.

Three causes, all closed. The fail(spinner, msg, err) helper had been copy-pasted into 23 command modules and 22 of those copies printed and then fell through; about 500 call sites now share one helper that always exits 1 and emits the JSON error envelope that already existed and was already on by default. HTTP 200 responses carrying an error-shaped body — forms get answering a miss with {status:"error"}, invoices get with {ok:true,result:{error:"invoice_not_found"}} — are now rejected by the response interceptor, deliberately narrowly, checked against live responses so health and invoices list do not trip it. And unknown positional arguments are rejected rather than silently discarded.

⛔ Read that last one twice. A script passing a stray argument that used to vanish will now fail. It is also why solid completion bash finally emits bash and solid completion fish emits fish, instead of both quietly emitting zsh as they had since v1.9.

168 top-level commands, 905 verbs.

v2.19.0

2026-09

The notifier told you a new version existed and then left you to remember the incantation. solid update works out how this copy got here and runs the right thing: npm install -g, brew upgrade, or scoop update. --check shows what it would do, --json is for an agent. Being offline is not an error — it prints the command instead of failing.

⛔ The bug it really exists for is the second copy. A machine can carry two solid binaries at once, an npm global under nvm and a Homebrew formula, and whichever comes first in PATH wins. Upgrade one and the other lies in wait: open a shell before nvm initialises, or switch Node versions, and you are silently running a CLI from weeks ago that disagrees with the backend about which verbs exist. We found exactly that in the wild — two installs, seven releases apart, with nothing said.

The notifier cannot see it, because it only ever looks at the copy that is running. So solid update scans PATH and names the others. Detection puts Homebrew ahead of npm on purpose: a brew formula’s payload also lives in a node_modules directory, and getting that backwards prints npm install -g to a Homebrew user, quietly installing a third copy instead of upgrading the one they have.

168 top-level commands, 905 verbs.

v2.18.0

2026-09

solid apply reads a manifest, shows you a plan, reconciles the tenant toward it, and remembers what it did: a lockfile, three-way drift (declared vs lock vs live, so a dashboard edit and a manifest edit are never confused), rollback from the pre-images it captured, and export to adopt a company that already exists without hand-authoring its manifest.

Sixteen resource kinds now: products, contacts, deals, webhooks, pages, sites, domains, surveys, knowledge base, services, brand, agents, phone lines, and new here — capabilities, commerce flows and the payment processor. A manifest can finally say what the business is allowed to do and who takes the money, not only what it looks like.

⛔ Apply configures; it does not conjure. It will not buy a phone number, will not create an agent (those come from the registry), will not activate a flow (that starts it running), and will not edit a connected processor in place. Rotating live payment credentials from a manifest is an outage waiting for a typo, so drift is reported and nothing is rewritten. Every refusal prints its reason instead of failing quietly, and the reason is specific: the processor tells you to reconnect, not to delete and recreate, because for a live payment connection that advice takes payments down.

164 top-level commands, 874 verbs.

v2.17.0

2026-09

solid deals reads the deal and reports where it stands, offer and order milestones, so an agent can answer “what is left on this?” without stitching three calls together. Quiz scoring walks the real branching path instead of a flattened list, so a quiz that forks on an answer is scored the way the respondent actually experienced it. The legacy REST seam closes wherever a verb exists: one vocabulary, one code path, and no second way to do the same thing that drifts from the first.

sync-counts generates every advertised count instead of trusting a human to walk twelve surfaces. It stamps the CLI’s own paired phrasing and refuses what it must not touch — a minimum version like @2.10+, a dated changelog line, WebMCP’s separate measurement — printing each refusal so a clean run can never hide one. prepublishOnly runs it in check mode, so a stale surface stops a publish instead of shipping a wrong number.

164 top-level commands, 870 verbs.

v2.16.0

2026-08

Forms, operable — solid forms is rebuilt on the verb layer and grows the whole product: moments (when a form goes out), reviews (where a happy customer is sent), build (a starter drafted in this business's own industry words, saving nothing without --save), walk (answer a form the way an agent does — next question, capture, submit), vocabulary, and the lifecycle: publish, pause, resume, link. Every live form now has a standing public address, and embed builds a paste-ready iframe around it instead of the old pre-lifecycle link. solid verbs invoke gains --confirm: write verbs were previously refused by the backend with a message naming a flag that did not exist, so nothing that writes was invokable from the CLI at all. No command sends company_id or an industry code — the tenant and its words are resolved server-side from the authenticated principal.

v2.15.1

2026-08

Fix — solid transaction start and solid transaction append now send confirm. Both verbs are declared side_effects=write; they previously reached the backend handler only because the explicit /api/v1/agent routes bypassed the confirm gate, and that bypass is now closed. Opening a transaction handle is the user's own action, so the CLI confirms on their behalf rather than requiring a flag. Clients below 2.15.1 receive a 400 on those two commands once the backend change is live. No other command is affected.

v2.15

2026-07

The Starter Kit — solid init <name> --company <id> scaffolds a tenant-stamped starter project bound to one Solid# company. The generated project carries a CLAUDE.md with the operating rules (ground-don't-guess, persist-to-company_id, preview→confirm, start_here/end_session), a .solid/config.json tenant stamp (company_id + connector URL), a token-safe .gitignore, a scoped-token .env.example, and it git init's the client's own repo — so a coding agent builds on the right tenant from line one, with tokens never committed. A companion connector verb, scaffold_project, hands the same starter pack over the wire for agents working through the hosted MCP connector rather than the CLI. Command and verb surface unchanged: 163 top-level commands, 748 verbs.

v2.14

2026-07

Telephony command center — the phone system becomes a first-class CLI surface. solid voice lines shows every line you can see (owner, routing mode, backing AI agent); lines routing <line_id> <mode> sets who fields a call (ring | ask | ai | off); lines context returns your default line + today's call counts in one call; solid voice spend breaks usage and charges down per line/user/agent; solid voice pricing prints number rentals, plans, and transcription rates. solid call simulate dry-runs a caller through a line's answer ladder + bound phone tree — key presses and intents via --inputs, nothing rings, --json exits 1 on flow errors so a routing change can be proven from CI before it ever answers a real customer (the same switchboard.simulate verb the Phone Tree builder's test button uses). solid insights report is the master communications report in the terminal — sentiment, call evaluation, and needs-attention rollups across every channel. The MCP connector surface (claude.ai, ChatGPT, and stdio via @solidnumber/mcp for Claude Desktop / Cursor / Windsurf) is hardened for broad client compatibility, so an agent loads the full verb surface reliably on connect. 163 top-level commands, 748 verbs.

v2.13

2026-06

The operator brief + discoverability. solid today is the one-command daily brief — net revenue, open pipeline, urgent/pending work, and ranked recommended next actions assembled from your live business in a single call (--json for agents). solid how-to answers in plain language how to connect Solid# to Claude or ChatGPT (the MCP connector flow + URL), where to start as an operator, and what the CLI can do — discoverability for non-technical owners and agents alike, no command-dump required. Pairs with the matching connector self-knowledge: the claude.ai / ChatGPT connector now ships a how_to tool so the assistant can explain itself in-session. solid whoami / solid auth status now show which company you are bound to by name, your role, and your tier — so you (and your agents, via --json) can see your scope and permission level at a glance, not just a company id.

v2.12

2026-06

Site-Publish Substrate — build a business from one manifest. solid apply now reconciles page, site, domain, and survey kinds alongside products, contacts, deals, and webhooks: declare the whole business surface in one YAML, apply, then solid publish --all takes it live on your domain (immutable kinds report drift instead of silently mutating). solid embed chat|form|paylink emits ready-to-paste snippets — HTML or --react — that wire any website, including AI-generated ones, to the live backend: the AI chat widget, lead forms that create CRM contacts with lead scoring on every submission, and hosted payment-link checkout. solid context --claude now installs the solid-commerce Claude Code skill into the tenant-bound directory so coding agents wire generated sites to real forms, chat, and payments by default.

v2.11

2026-05/06

Substrate layer + sales agent + outbound voice. solid apply <manifest> declarative desired-state reconcile (create / update / prune, --dry-run, content-derived idempotency) — GitOps for a tenant; solid signal stream server-pushed SSE events; @solidnumber/cli/client programmatic package export; Idempotency-Key on every mutation; broad Node 18+ runtime. Sales Agent CLI: 18 AI-powered solid sales verbs (pipeline/forecast reads, KB-grounded scoring + outreach suggestions, consent-gated qualify/advance/close, voice-handoff nurture) wired to 5 agents, plus editor-anywhere onboarding (preflight detects installed-but-can't-run editors; VS Code + Claude Code first-class; solid ai gains Gemini + Grok targets). Outbound voice + universal verb dispatch. solid voice call <phone> and solid voice text <phone> dispatch ADA to place real outbound calls and send SMS — same tenant rate limits as inbound. solid voice translate enable|disable|status <phone> toggles per-phone live speech-to-speech translation (13 output languages; opt-in, Pro+ tier). solid agent dispatch <verb> is the universal entry point — call any of the agent-attraction verbs across every business function through one command. New shortcut commands solid deal, solid calendar, solid lead-promote promote the highest-traffic CRM/scheduling flows to first-class. solid voice health is a per-tenant voice-readiness audit.

v2.0 — Agent-Ready by Default

Breaking

Structured JSON error envelopes — opt-in since 1.10 via SOLID_JSON_V2=1 — are now on by default. Every --json error response ships code, status, hint, docs_url, request_id, and tier/feature/upgrade context. AI agents (Claude Code, Cursor, Codex) work out of the box; humans see the same prose they always did.

2.0 default — structured envelope
$ solid kb get 99 --json
{
  "error": {
    "code": "NOT_FOUND",
    "status": 404,
    "message": "KB entry 99 not found",
    "hint": "Run \"solid kb list\" to see valid IDs",
    "docs_url": "https://solidnumber.com/docs/cli#kb",
    "request_id": "req_8f2a3c"
  }
}
Opt-out — restore 1.x prose
$ SOLID_LEGACY_ERRORS=1 solid kb get 99 --json
Error: KB entry 99 not found
  Hint: Run "solid kb list" to see valid IDs
# 1.x scripts that pattern-match prose still work.

Migration in 30 seconds

  • • You wrote scripts that parse JSON errors: already on the new shape if you set SOLID_JSON_V2=1 — nothing changes.
  • • You wrote scripts that grep prose error text: set SOLID_LEGACY_ERRORS=1 in your shell or CI to restore the 1.x shape, then migrate when you have time.
  • • You drive the CLI from an AI agent: do nothing. The new shape is what you wanted all along.

Tenant Graph + Offline Mutations (v1.21 → v1.26)

Stable

Your tenant's entire surface — companies, services, products, agents, KB, chains, webhooks — ships as a typed JSON-LD graph. Walk it, query it with SPARQL, diff snapshots, dump it into any RDF store, and write to it from a plane.

solid graph --querySPARQL

Offline BGP runs in-process against the bundled .claude/solid-context.jsonld. Add --server for full SPARQL 1.1 (OPTIONAL, FILTER, property paths, UNION, GROUP BY).

Terminal
solid graph --query \
  "SELECT ?s WHERE { ?s a schema:Service }"

solid graph --query "..." --server      # full SPARQL 1.1
solid push --queueoffline-first

Lose wifi mid-mutation, the CLI auto-queues. Regain wifi, the next call auto-replays the queue. No flag, no ceremony. Or arm it explicitly with --queue for plane mode.

Terminal
solid push --queue       # arm offline mode
solid push --flush       # replay queued mutations
# or just lose connectivity — it auto-queues + auto-replays
solid graph --diffCI gate

Structural diff between a baseline snapshot and the current graph. Exit code is the signal — gate CI on tenant stability.

Terminal
solid graph --diff baseline.jsonld --offline \
  || echo "tenant drifted"
solid graph --dumpN-Quads / Turtle

Pipe the tenant graph into Apache Jena Fuseki, Neo4j, Blazegraph, GraphDB, or Neptune. N-Quads is the canonical lossless format every RDF store accepts.

Terminal
solid graph --dump nquads --offline \
  | tdbloader2 --loc /var/jena/db -

AI-Driven CLI

Stable

The CLI isn't just for humans anymore. Claude Code, Cursor, and Codex can drive every command on your behalf — with four safety layers so a hallucinating AI can't issue refunds or delete customer data.

solid aistupid easy

Auto-detects Claude Code / Cursor / Codex, refreshes company context, launches your AI with everything it needs to reason about your business.

Terminal
solid auth login
solid ai                         # launches detected AI for your current company
solid ai --as cursor             # force Cursor
solid ai --no-context            # skip the context refresh
solid ai --sandbox               # safe-preview mode — every mutation intercepted

# Switching company (rare — agencies + multi-tenant operators):
solid auth me                    # shows your active company_id
solid auth companies             # lists every company your account belongs to
solid ai --company <id>          # override for this session
solid installonce, ever

Wires a Claude Code SessionStart hook so claude auto-refreshes .claude/CLAUDE.md every session. Idempotent. Undo with --uninstall.

Terminal
solid install
solid auth login
claude                           # context already fresh

Four layers of safety

LayerWhat it doesWhere
Mode capRefuses out-of-scope verbs before they parseCLI (client-side)
Destructive gaterefund / delete / cancel / revoke require --yesCLI (client-side)
SandboxEvery mutation intercepted; AI sees what would happenCLI dry-run
Backend enforcerServer-side mirror so scripted HTTP can’t bypassBackend middleware
Mode caps--mode

Scope what the AI can touch. Client and server both enforce, so a compromised HTTP client can't bypass.

customer — CRM, orders, voice, analytics
developer — pages, push, sandbox, vibe
agency — switch, company, users, train
full — no cap (default for humans)
Terminal
solid ai --mode customer
solid ai --mode developer --sandbox
Sandbox--sandbox

Every mutation the AI attempts is intercepted and mocked. The AI's reasoning still works because it sees a “would have worked” shape; nothing lands in real state.

Terminal
solid ai --sandbox
# → "Sandbox: ON — every mutation intercepted."
# Agent runs freely. Re-run without --sandbox to apply.
solid agent activityaudit trail

Every AI-driven request carries X-Solid-Agent, X-Solid-Agent-Mode, and X-Solid-Human-Initiator headers. The backend logs them so you always know what an AI did, in which mode, on behalf of whom.

Terminal
solid agent activity                # 24h summary card
solid agent activity --range 7d     # weekly
solid agent activity --events       # raw feed
solid agent activity --json         # programmatic
Release history — @solidnumber/cli changelog | Solid#