@descryy/cli
v0.20.1
Published
Descry's command-line surface. First command is `init`, which writes the declarations a multi-repo run needs — the workspace id in .descry/config.json, and a package/pairing declaration when the repo is a monorepo. Neither is ever inferred.
Readme
@descryy/cli
The descry command-line tool. It sets a repository up for Descry, checks that the install works, and runs Descry's checks without an agent.
Descry reads a repository's source into a graph and tells a coding agent what a change reaches, what is tested and what breaks when the app runs, along with an explicit list of what it could not check. The graph is built and served by @descryy/mcp; this package is its setup and command-line side. Everything runs on your machine.
Requirements
- Node.js 22.5 or newer.
- A git repository with at least one commit.
- Linux for the sandboxed runs the server offers (
observe,generate_test). Descry's own tests run on Linux; macOS and Windows have not been verified.
Quickstart
In the repository:
npx -y @descryy/cli initIt writes
.descry/config.jsonwith an explicit repository identity and the language adapters that recognise the repository, records that this machine has confirmed those adapters, and prints what to do next. The last lines of its output are the exact registration command, with the server version of this install filled in.Register the server with your agent. For Claude Code:
claude mcp add descry --scope local -- npx -y @descryy/mcp@<version> "$(pwd)"Any other MCP client takes the same thing as a stdio command:
npxwith the arguments-y,@descryy/mcp@<version>and the absolute path of the repository.<version>is the oneinitprinted: the server version this CLI was released with.In the agent, call the
workspacetool with verbanalyzeto build the graph.
Commit .descry/config.json and .descry/.gitignore. The databases and everything else in .descry/ stay on this machine and are ignored by git.
If init could not find an adapter for the repository, it says so and prints the install command for one (npm install @descryy/adapter-<name>); run init again afterwards. If the repository's config already lists adapters that this machine has not confirmed, init lists them and asks you to confirm them at the terminal (--yes confirms without asking). With no terminal and no --yes it confirms nothing and prints descry trust; from the agent, workspace with verb trust_sources lists exactly what would load and asks once.
Commands
descry init [--workspace <name>] [--repo <name>] [--yes] [--json] [repository-path]
descry trust [--yes] [repository-path]
descry setup [--workspace <name>] [--repo <name>] [--yes] [--hook <agent>] [repository-path]
descry doctor [--json] [--fix] [repository-path]
descry check [--base <ref>] [--require-ready] [--json] [--scenarios] [repository-path]
descry qa gaps [--json] [--limit <number>] [--fail-on P0|P1|P2|none] [repository-path]
descry qa analyze [--json] [--limit <number>] [--fail-on P0|P1|P2|none] [repository-path]
descry qa validate-tests (--test <id>... | --file <path>) [--timeout-ms <number>] [--yes] [--json] [repository-path]
descry browse explore|autopilot|layout|prove-reach --url <address> [--safety <level>] [--yes] [--json] [repository-path]
descry analyze [--no-history] [--max-commits <number>] [--json] [repository-path]
descry approve browser --origin <address>... [--write] [--ttl <8h>] [repository-path]
descry approve tests --command <command> [--ttl <8h>] [repository-path]
descry approve sandbox | store [--ttl <8h>] [repository-path]
descry approve --list | --revoke <id> | --revoke --all
descry approvals list [--json]
descry approvals approve <id> | deny <id>
descry observe tests [--command <command>] [--cwd <folder>] [--no-coverage] [--yes] [--json] [repository-path]
descry scenario record <name> --url <address> [--yes] [--json] [repository-path]
descry feedback [--json] [--hook <agent>] [--refresh] [repository-path]
descry slop [--scope change|repo] [--base <ref>] [--paths <path>...] [--detectors fast|all|S1,S4] [--include introduced|all] [--limit <number>] [--detail minimal|standard] [--format json|text] [--refresh] [--fail-on-findings] [repository-path]
descry slop suppressions [--format json|text] [--limit <number>] [repository-path]
descry slop --hook <agent> [repository-path]
descry hook install|uninstall|restore|test --agent <agent> [repository-path]
descry viz [--port <number>] [repository-path]
descry credential set <name>initwrites the repository's identity, its adapters, its.descry/.gitignore, and, for a monorepo with no packages declaration, a packages declaration after showing you the exact diff.--workspacesets a workspace id to share across repositories; leave it out for a single repository.--jsonprints one JSON object with the result and the next steps and never prompts, so a packages declaration is written only if you also pass--yes.trustlists the adapters this repository's config names that this machine has not confirmed, asks you at the terminal, and records the confirmation per repository and per adapter.--yesskips the question for a script; with no terminal and no--yesit confirms nothing and prints the command. It is the prompt-free alternative toDESCRY_TRUST_CONFIGURED_SOURCES=1, which trusts every repository the server opens.setupdoes what a repository needs in one step or nothing:init, build the graph, install Chromium for the browser tools, then a self-test of each. A failed step rolls the whole run back.doctorchecks that Descry can run here. It looks at the Node version and its built-in SQLite, that one copy of the engine is installed, that this CLI and the server beside it are the versions released together, npx's cache for a half-extracted package, the standing approvals and the approvals waiting for you, and, in a repository, its git state (a repository, with a commit), config, adapters, whether this machine has confirmed them, the graph and the.gitignore. Each finding names its fix. Exit code 0 is all passed, 2 is warnings only, 1 is a failure.--fixremoves only broken Descry entries from npx's cache, after listing them.checkruns the change-scope, contract, validation and cross-PR checks over your changed files and exits non-zero when they find defects. It is meant for a git hook or a CI step. Exit 0 is clean, 1 is blocking findings, 2 is that the check could not run. Pass--require-readyin CI so a missing graph is a failure rather than a silent pass.qa gapsandqa analyzereport what the repository's tests prove and where that proof is missing, each section labelled complete, partial, not run or failed.browse,observe tests,qa validate-testsandscenario recordare the verbs the server only offers behind a confirmation, for CI and for a script. Each prints the same consent text the server would, asks you at the terminal, and runs only on a yes; with no terminal it refuses unless you pass--yes, which is for a person typing it (an agent that adds it runs the command under whatever shell permission its host gave it, the same astrust --yes).browseopens a browser session, runs one verb (explore,autopilot,layoutorprove-reach) and closes the session;exploreandautopilotclick and type, so they need--safety write.observe testsshows the exact command line before running it.scenario recordopens a visible browser, records what you do in it until you press Enter, and saves it asdescry/scenarios/<name>.scenario.json; a password you type is never stored, the step keeps a${credential:NAME}reference and the name is printed so you can store the value withcredential set.analyzebuilds the code graph for the repository, the same build theworkspacetool'sanalyzeverb runs, socheck,qaandobserve testscan be preceded by it with no MCP client. It loads only adapters this machine has confirmed; with unconfirmed ones it builds what it can (git history), says plainly that no source was read, and exits 2.--no-historyskips git history and--max-commitscaps it.approveis for an agent whose client cannot show you an approval prompt. It shows what is approved, needs a terminal, and has you type it back; it has no--yes. A grant is a file under~/.descry/grantsbound to this repository, private to you, and it expires.approve browser --originallows read-only sessions on the exact origins you named (8 hours by default, at most 7 days); with--writeit allows write-level sessions, on loopback addresses only (localhost, 127.0.0.1, [::1]).approve tests --commandallows one test command, typed exactly, throughobserve tests.approve sandboxallowsqa validate_testsandgenerate_test verifyin this repository, which run in a sandbox with the network denied.approve storeallows writes to Descry's own store (incidents, answers, pull-request scopes, unlinking a shared graph). Everything but read-only browsing lasts at most 24 hours. None of them covers saved logins or headers.--listshows grants and--revokeremoves one or all. A file in a repository cannot create or widen one.approvalshandles one call at a time. The first call of a gated verb queues an approval under~/.descry/approvals(15 minutes, at most 50, never the arguments themselves);approvals listshows what is waiting with the exact sentence,approvals approve <id>has you type the id back and lets the agent's next call run once, andapprovals deny <id>refuses. It works in any client and in headless runs. It has no--yes.- Every prompt here reads from the controlling terminal, discards anything that was pasted before the prompt appeared and takes only what you type after it, so pasting several commands at once cannot answer an earlier question. Run each command alone and wait for its prompt. With no terminal and no
--yesa command refuses. feedbacklists the few contract and configuration defects the uncommitted change introduced.hookturns that into an end-of-turn hook in Claude Code, Codex, Copilot, Gemini or Cursor, and can remove it again.slopfinds low-quality code in a change or, with--scope repo, the whole repository: references to things that do not exist, stubs and placeholders, swallowed errors, additions nothing uses, and tests that assert nothing. It reports properties of the code, never who or what wrote it, and edits nothing. It needs a built code graph. Output is text for reading or--format json; a partial run says so on its last line and is never reported as clean. Exit 0 is a completed run, 1 is--fail-on-findingsmet by a strongly supported fast-tier finding the change introduced, 2 is a bad argument, 3 is that Descry refused to run (no graph, not a git repository), 4 is an unexpected failure.slop suppressionslists every inlinedescry-slop-ignoremarker with its reason.slop --hook <agent>answers as an end-of-turn hook: at most five one-line findings, and nothing at all when the change is clean or anything goes wrong; add it to the agent's settings by hand.vizserves a local inspector over an already-built graph.credential setreads a secret from stdin into the local credential store that browser scenarios use.
Troubleshooting
Run npx -y @descryy/cli doctor in the repository. The messages a new repository meets most:
- "has no .descry/config.json" or "has no sources": run
npx -y @descryy/cli init. - "not confirmed on this machine": see "Analyze says no source was read" below.
- "is not a git repository" or "has no commits yet": run
git initand make a first commit. - "descry-mcp: not found" or
ENOTEMPTYfrom npx: rundoctor --fix.
Analyze says no source was read / adapter not confirmed
The graph then holds only git history, no code. Fix it in this order:
- Run
npx -y @descryy/cli init --yesin the repository, which records this machine's confirmation of the adapters it writes. Or rundescry trust(see Commands). - If the config already listed adapters, call
workspacewith verbtrust_sourcesfrom the agent and approve the prompt it raises. Tell the person to expect that prompt; if the client cannot show one, step 1 is the way. - Call
workspacewith verbanalyzeagain.
Do not set DESCRY_TRUST_CONFIGURED_SOURCES=1 in a global MCP server entry. It makes the server trust the adapter list of every repository it opens, and a repository can change that list by editing a committed file. Set it only for a single, trusted repository, in that repository's own server entry.
Claude Code in auto mode
Claude Code decides each tool call before Descry sees it, by an allow rule, an ask rule or, in auto mode, a classifier. Rules are named mcp__<server-name>__<tool>, where <server-name> is the name you gave claude mcp add. The read tools can be allowed freely:
{
"permissions": {
"allow": [
"mcp__descry__code_context",
"mcp__descry__git_context",
"mcp__descry__disclosure",
"mcp__descry__check",
"mcp__descry__reach",
"mcp__descry__similar_incidents",
"mcp__descry__draw_conclusion"
]
}
}in .claude/settings.local.json. The tools that run code, drive a browser or write can be allowed by rule too: their first call changes nothing, and a person approves the exact sentence in the permission prompt of descry_confirm, which an allow rule cannot skip. Where that tool is not available (not Claude Code, or a headless run), use descry approvals, descry approve or the command-line twins described above. Do not redeem tokens through a script or a hook that approves whatever Descry asks.
Known limits
- Playwright and other end-to-end tests are linked to code only through executed coverage (
observeverbtests). A static link from a test'spage.gotois limited to exact literal paths, so until coverage has run, such a suite shows as registered but not covering anything. - The reliability class of a change-scope answer is the minimum over the edges that answer touched. A broad diff can be class C because of a handful of R0 edges.
- A frontend-only repository has no API routes in its graph, so
checkverbcontractsis not analysable until the backend repository is linked withworkspaceverblink.
Privacy
Nothing here talks to a Descry service. The files this tool writes are in the repository's .descry/ folder, plus confirmations and stored credentials under ~/.descry/ on this machine.
License
MIT
