Skip to content

CLI reference ​

Everything ships in one package:

bash
npm install --save-dev @suss/cli
suss init [<directory>] [--plain | --write [--overwrite]]
suss extract [-p <tsconfig> | --dir <directory>] [--lang typescript|python|ruby] [-f <pack>[=<config.json>] ...] [-o <output.json>] [--files <f1> <f2> ...] [--gaps strict|permissive|silent]
suss inspect [<summaries.json> | --dir <directory>]
suss inspect --diff <before.json | directory> <after.json | directory>
suss inspect --flow "<METHOD> <url>" [<summaries.json> | --dir <directory>] [--entry <name>] [--scope <document>] [--json]
suss check [--dir <directory>] [--intent <intent-dir>] [--all] [--json] [-o <output>]
suss check <provider.json> <consumer.json> [--all] [--json] [-o <output>]
suss check [--dir <directory>] --at <file[:line] | boundary | summary-id> [--json]
suss ask "<question>" [--dir <directory> | <summaries.json>] [--all] [--json]
suss map [<directory>] [--dir <summaries>] [-o <page.html>] [--links editor|github]
suss contract --from <source> <spec> [-o <output.json>]
suss corroborate --experimental [-p <tsconfig> | --dir <directory>] [-f <pack> ...] [-o <output.json>]
suss infer stub <package> [-p <tsconfig> | --dir <directory>] [-o <file | ->]
suss infer intent --from <summaries.json | directory> [-o <directory> | --into <directory>]
suss infer prd --from <intent-directory> [-o <directory> | --into <directory>]
suss intent outcomes --from <intent-directory> [--json]
suss intent check <change-list> --before <dir | file> --after <dir | file> [--prompts <file>] [--json]
suss intent keep <change-list> --dir <dir | file> --audience <text> [--into <directory>]
suss --version

In a synopsis, <...> marks a required value and [...] an optional one.

Commands ​

CommandReadsWrites
suss initpackage.json, schemas and deploy templates on diskThe commands to run, and suss.json if you accept it or pass --write
suss extractTypeScript, Python or Ruby source, through the packs you nameSummary JSON
suss contractOne of ten declared sources: an OpenAPI document, a deploy template, a database schema, and so onSummary JSON, the same structure extract writes
suss checkSummary files, and optionally a folder of intent docsFindings, as text or JSON
suss inspectA summary file, a folder of them, or two of themText for a person, or JSON under --diff and --flow
suss askSummary files, and the source for a why questionOne answer, as text or JSON
suss mapThe project, the way a bare check reads it, or a folder of summariesOne HTML page of the routes, fields, tables and env vars, and the summaries behind it
suss corroborate (experimental)Source, through the express or fastify packsSummaries annotated with what running the code showed
suss inferObserved calls, summaries, or curated intent docsA YAML draft for you to finish
suss intentIntent docs, or a change list and the summaries from before and after a changeOutcome ids, verdicts on each change, or intent docs kept from a change list

extract, inspect and check run with no arguments at all. Each one reads suss.json, or picks the packs init would pick when there is no file, and prints the commands it ran to stderr.

The summary JSON is the artifact every other command works from. inspect renders it and check compares two of them, so anything either one reports is also there in the JSON for you to read yourself. Two commands compute an answer that isn't in any file. inspect --flow walks the routing a set of summaries declares, and suss ask takes a question about one boundary and works out the answer from the summaries.

Top-level flags ​

FlagWhat it does
-h, --helpPrint the usage above and exit 0. Running suss with no command, or any command with --help, prints the same thing.
-v, --versionPrint the installed version and exit 0.

Environment variables ​

An interactive run ends with one line on stderr when a newer suss is on the registry. You never get that line when the output is piped, and two variables turn it off everywhere.

VariableEffect
NO_COLORSet it to anything and output is plain text with no ANSI colour.
TERM=dumbThe same. Colour is also off whenever stdout is not a TTY, so a piped or redirected run is plain without you asking.
CISet it to anything and the update notice is off. suss init detects CI separately and prints its commands rather than prompting, the same as --plain.
SUSS_NO_UPDATE_NOTICESet it to anything and the update notice is off.
SUSS_CACHE_DIRA directory to keep suss's own files in, instead of .suss in the project. See Where suss writes. The --cache-dir flag overrides it.
SUSS_CACHE_MAX_MBHow many megabytes the extraction cache keeps, 1024 when it is not set. See Where suss writes.
NODE_OPTIONSA heap size set here with --max-old-space-size is the one suss runs with. Without one, suss picks its own, as Memory describes.

Where suss writes ​

A command that extracts keeps a cache so the next run reads only what changed. It goes in .suss/cache under the folder being read, with a .gitignore inside it. In a git worktree, it goes in the same folder of the main checkout instead, so every worktree of a repository shares one cache, and a new worktree's first run reads again only the files that differ from what another checkout stored. In a checkout you may only read, that write fails or dirties the tree. Point the cache somewhere else with --cache-dir, or with SUSS_CACHE_DIR for every command in a shell:

bash
suss check --cache-dir /tmp/suss-cache
SUSS_CACHE_DIR=/tmp/suss-cache suss extract --dir . -o /tmp/summaries.json

init, extract, inspect, check, ask and map take the flag, and the flag wins over the variable. map writes its page and the summaries behind it to .suss in the project, or, with the cache moved, to a folder for the project under map in the cache directory. Several projects can share one directory, because each gets a subfolder named for a hash of its path. For the extraction cache in a git repository, the hash is of the repository and the folder's place in it, so the worktrees still share one cache there. The MCP server takes the same flag as an argument (suss-mcp <project> --cache-dir <path>) or reads the variable, and keeps its record for the plugin's hooks there too.

The extraction cache keeps 1 GB, or the number of megabytes SUSS_CACHE_MAX_MB says. When a write takes it past that, suss removes what the checkouts used longest ago stored, and a file two checkouts share stays as long as one of them does. The checkout that is writing keeps what it stored however large it is. What a removed worktree stored goes at the next write.

With the cache moved, a command writes into the project only where you asked it to: -o, --into, --out-dir, and the files init --write creates. Everything else goes to the system temp folder (the update-check stamp, the temporary summaries a bare check reads, and the MCP server's socket).

Memory ​

suss runs with a V8 heap limit of 8 GB. Node picks a smaller limit from the machine's memory, about 4 GB on a 16 GB machine, and extracting a large TypeScript service with every pack suss init suggests can use more than that. Under Node's limit such a run stops partway with "JavaScript heap out of memory".

The limit is a ceiling on growth, so a small project uses no more memory than it would under Node's default. A large one uses more, because V8 lets the heap grow before it collects garbage instead of collecting over and over near a lower limit. On a NestJS service of about 7,000 units with eleven packs, peak memory went from 4.7 GB to 6.2 GB, and the run got faster. If memory matters more than time on a large project, set a lower size as shown below. On a machine or container with less than about 11 GB, suss keeps the limit to three quarters of the memory there. A run that outgrows it then stops with V8's heap error, and the operating system does not have to kill it.

To choose the size yourself, put it in NODE_OPTIONS. suss leaves a size set there alone, whether it is larger or smaller than its own:

bash
NODE_OPTIONS=--max-old-space-size=12288 suss extract

suss raises the limit by starting itself again with --max-old-space-size, before it loads anything else. On Node 22.15 and later the new process replaces the first one and keeps its process id. On older versions of Node, and on Windows, the first process stays as a parent: it passes on SIGINT, SIGTERM and SIGHUP, and exits with the child's exit code. The MCP server, suss-mcp, does the same. With that parent in place, a debugger started with node --inspect attaches to the parent, which only waits for the child. To debug a run there, put a size in NODE_OPTIONS so suss does not start itself again.

Where output goes ​

StreamWhat arrives there
stdoutSummary JSON from extract and contract, the report from inspect, check and ask, and the JSON those write under --json
stderr"Wrote N summaries to PATH", extraction warnings, the commands a no-argument run chose, error messages

A run without -o puts nothing but JSON on stdout, so suss extract ... | jq works. -o PATH moves that JSON into the file; extract and contract then write one acknowledgement line to stderr.

Exit codes lists what every command returns to the shell.

Released under the Apache-2.0 License.