Skip to content
stacks
Docs menu

CLI commands

Every stacks command and subcommand, with its flags, defaults, JSON result schema, exit codes and examples.

View as MarkdownOpen in ClaudeOpen in ChatGPT

Every stacks command, its flags and what it does. stacks <command> --help prints the same flags with examples and exit codes, and stacks schema <name> prints the JSON Schema of any --json result named on this page.

Command What it does --json schema
stacks search Find stacks and fragments for what you want to build search
stacks list List the stacks, fragments and apps you can build from list
stacks show Review one stack or fragment show
stacks new Start a project from a stack new
stacks add Apply fragments to an existing repository add
stacks verify Run the project's verify steps verify, verify-event
stacks runs Show a project's verify history runs
stacks validate Check entries, or preview a composed brief validate, validate-brief
stacks create Create or copy a stack or fragment for editing create
stacks submit Open a draft pull request adding your entries to the public registry submit
stacks update Check for a newer release, or upgrade a global npm install update
stacks skill install Install an agent skill skill
stacks guide Print the guide for coding agents guide
stacks guide author Print the guide to writing and submitting a stack or fragment guide-author
stacks doctor Inspect the runtime, paths and configuration doctor
stacks schema Print the JSON Schema of a --json result none
stacks telemetry Inspect consent, turn collection off, rotate identity or send pending events telemetry

A failed --json command writes one error envelope as its last stderr line; its schema is error. The envelope, the three output audiences and the full exit code table are in Output, JSON and exit codes.

Global flags

Every command accepts these flags.

Flag What it does
--json Write one JSON result to stdout. A failure writes one JSON error envelope as the last stderr line instead
--offline Use cached and bundled entries without network requests. STACKS_OFFLINE=1 does the same. No registry, search, stats or telemetry request is made
--no-telemetry Skip telemetry for this invocation. Queued events stay queued
--help, -h Show the command's help: flags, exit codes and examples
--version, -v Print the version. With --json, prints { "version": "<version>" }
--completions <shell> Print a shell completion script. See Shell completions
--log-level <level> Minimum log level: all, trace, debug, info, warn, warning, error, fatal or none
--wizard Start wizard mode for a command

Running stacks with no arguments prints the agent guide, the same as stacks guide. stacks --help prints the full help and ends with a short "Start here (agents)" footer.

On the first invocation with telemetry enabled, stacks prints a one-time privacy notice on stderr (JSON results gain an optional notice field instead), and that invocation sends nothing. See Telemetry for the off switches.

Interactive pickers

Run new without a stack, add without fragments, or create without an entry kind or id, and stacks asks for what's missing with numbered choices. It asks only when output is for a human: no --json, both stdin and stdout on a terminal, no coding agent detected and CI unset. Everywhere else new, add and create stack|fragment exit 2 (Usage), and a bare create prints its help.

Type one number to pick a row. Fragment lists take several numbers separated by commas; for new's optional fragments, Enter picks none. Ctrl+C quits before anything is written. After the last answer stacks prints a Running: line with the equivalent command, so you can rerun it without the prompts.

Ranks stacks and fragments against a request in plain words and explains each hit.

stacks search "an iOS health app" --json
stacks search go cli --kind stack --limit 3
stacks search "a static blog" --local     # rank on this machine only
Terminal
Argument or flag What it does
<request...> What you want to build, in your own words. 1 to 500 characters; several words don't need quotes
--kind stack|fragment Search only stacks or only fragments
--limit <n> Maximum hits per list, from 1 to 20. Default 5
--local Rank locally without sending the request to the API

search sends the request text to the hosted API at https://stacks.steamfield.io and ranks locally at the same time. When the API is unavailable it falls back to the local ranking. --local, --offline, STACKS_OFFLINE=1 and DO_NOT_TRACK keep the request on your machine.

stacks search go cli --kind stack --limit 3 --local
Terminal
stack go-cli (strong)
A Go command-line tool with subcommands, configurable settings, actionable errors and human or JSON output.
Why: cli (platforms); go (languages, description, summary, requests); go cli (id, name, tags, body)
...

stack go-api (partial)
A Go HTTP API on the standard library with JSON resources, consistent errors, request logs and graceful shutdown.
Why: go (languages, id, name, description, tags, body, summary, requests)
Penalties: platform (cli; entry: server)
Caveat: Race-enabled tests need cgo and a C compiler on every machine and CI runner.
...

Next: stacks show go-cli, then stacks new go-cli <dir>.
Output

Each hit has a fit (strong or partial), the fields that matched (Why), anything that counted against it (Penalties), and caveats. How search ranks entries covers the ranking, the result fields and the API's backoff.

Exit codes: 0 even when nothing fits strongly (the JSON result says noStrongMatch), 2 for invalid input (Usage, BadInput), 6 for an unreadable registry (PermissionDenied), 130 when interrupted.

stacks list

Lists the stacks, fragments and apps you can build from, where each one comes from, and whether it's trusted.

stacks list                 # addable fragments in a project; otherwise all groups
stacks list stacks          # one group: stacks, fragments (apps excluded) or apps
stacks list --json          # full registry, plus project context when a manifest decodes
stacks list --dir ../app    # list from another project directory
Terminal
Argument or flag What it does
[group] Show one group: stacks, fragments (apps excluded) or apps. Omit it to show all three
--dir <path> Project directory to list from. Defaults to the current directory

Inside a project (a directory with .stacks/manifest.json), the default text view shows only the fragments that fit and aren't applied yet, each with its stacks add command. An explicit group keeps the full view. --json always returns the full registry and adds project: { stack, applied, addable } when the manifest decodes. A corrupt, newer or unreadable manifest prints a warning and falls back to the full view.

stacks list stacks
Terminal
Stacks
  astro-blog               historical pass 2026-10-27
    A static Astro blog with MDX posts in a zod-validated content collection (draft/published/unlisted status), React islands, Tailwind with typography, sitemap and RSS, optional generated OG images, and vitest plus astro check.
  astro-fullstack          unproven
    A Bun workspace with a static Astro site (React islands, Tailwind), a Node API on Effect's HttpApi and a shared contract package, so islands call the API through a typed client. The API can also serve the built site.
  ...
  go-cli                   historical pass 2026-10-27
    A Go command-line tool with cobra commands, viper config (file, env, flags), structured errors with actionable suggestions, human and JSON output, testify tests with race detection and a coverage floor, and golangci-lint.
  ...

Next: stacks new astro-blog my-project --sandbox
Next: stacks list --json
Output

The label beside each stack is its last recorded run: historical pass <date> for a pass, <result> <date> for anything else, or unproven when there is no record. Bundled and index stacks carry it as lastRun: { date, result, agent, preset, fingerprint } | null in JSON. It records a past run of the default composition; list doesn't compare it with the current content, and stacks show <id> does. Fragments and local entries have no run record.

Origins and shadowing

Entries come from four origins, highest precedence first. The same id in a higher origin wins, and list --json marks the winner's shadows with the origins it hides.

Origin Where Trusted
project ./.stacks/ in the working directory No: a cloned repository can ship these
user $XDG_CONFIG_HOME/stacks/ (default ~/.config/stacks/) Yes
index Verified bundles cached from the signed registry index Yes for the default index; no for a custom STACKS_INDEX_URL
bundled The registry shipped with the CLI Yes, unless STACKS_REGISTRY_DIR replaced it

An entry that fails to load is listed under rejected with a one-line error, and a lower copy never silently replaces it. An entry whose schema_version is newer than this CLI reads is skipped with a warning, and the next origin's copy is used. Registries and trust explains what trust changes, and Entry file format describes the directory layout inside each root.

Published stats

For bundled and index entries, list and show can add published project stats from the hosted API. list --json carries a flat stats summary per entry; show --json carries the full detail, including agent breakdowns and a weekly series. Text output prints one Stats: line.

Field Meaning
window, through, population The trailing 7 or 28 finalized UTC days, ending on through. population is real stacks projects or seeded development data, which text labels seeded
projects Distinct projects in the window, each counted once however many events it sent
greenRate, greenRateLower, greenRateUpper, sample Green projects over attempted projects, its 95% Wilson interval, and the attempted sample behind it. Green requires a full, unchanged verify
noVerifyShare Share of projects with no verify event, which can include projects whose telemetry was off
medianMinutesToGreen Median time from scaffold to first green verify

A suppressed cell (too few projects to publish) is null in JSON, and text prints unavailable. Stats are optional: a missing or unreachable response adds no warning and doesn't change the exit code. They're cached under $XDG_CACHE_HOME/stacks/stats/ for 24 hours, --offline makes no request, and a custom STACKS_INDEX_URL turns them off because they describe a different registry.

Exit codes: 0, 2 on an unknown group, 6 when a registry root can't be read, 130 when interrupted.

stacks show

Reviews one stack or fragment without starting any process: metadata, every guidance branch, commands, verify steps and shipped files, including adapter files for every agent.

stacks show typescript-cli          # metadata, guidance, commands, steps, shipped files
stacks show postgres --json         # one ShowReport object
stacks show effect --kind fragment  # look among fragments only
Terminal
Argument or flag What it does
<id> The stack or fragment id to review. Apps are fragments
--kind stack|fragment Select a kind when a stack and a fragment share an id
stacks show go-cli
Terminal
Go CLI (go-cli), stack, origin bundled, trusted
A starter for a terminal program written in the Go language, with settings and clear error messages.
Good at: Commands that read settings from files, environment variables and flags
Good at: Output for people to read, or in a structured form scripts can parse
Good at: Error messages that say what went wrong and how to fix it
...
Tags: go, cli, make
Dependencies (cli): github.com/spf13/cobra, github.com/spf13/viper
Dependencies (test): github.com/stretchr/testify
Tools: go, make, git, golangci-lint
Decisions:
  - Module path: github.com/<owner>/<project-slug> (default), ...
  - Binary name: the project slug (default; the Makefile's BIN).
  ...
Fragments: required none; default agent-ops, git-hooks, ci-github; optional goreleaser, rr, ce-plugin
Verify:
  install: make install (phase setup, layer install)
  build: make build (layer build)
  vet: make vet (layer typecheck)
  lint: make lint (layer lint)
  test: make test (layer test)
  ...
Files:
  .claude/settings.json (claude) <- adapters/claude/.claude/settings.json
  .editorconfig <- files/.editorconfig
  .gitignore <- files/.gitignore
  Makefile <- files/Makefile.tmpl
  ...
Output

When the entry's search card has plain-language copy, show prints its definition, strengths, first project example and limits before the technical description. Bundled and index stacks also get their recorded run, and sameContent in JSON says whether the current composition's fingerprint matches the one that run used. Other origins get lastRun: null. Published stats appear as described under Published stats.

Exit codes: 0; 2 on an unknown or rejected id, an id that needs --kind, or a stack whose default fragments conflict (ComposeConflict); 3 on a broken config.json; 6 when the entry can't be read; 130 when interrupted.

stacks new

Starts a project from a stack: composes it with its fragments, writes the starter files and .stacks/BRIEF.md, then hands the project to a coding agent. It never runs a command from a stack or fragment.

stacks new typescript-cli my-cli                     # write the project, then hand it to your agent
stacks new astro-blog my-blog --sandbox --dry-run    # preview without writing anything
stacks new typescript-cli my-cli --sandbox --json    # one NewResult object, as an agent would
Terminal
Argument or flag What it does
[stack] The stack to start from (see stacks list stacks). Omitted, a person at a terminal gets a picker
[dir] The project directory: missing, empty, or holding only .git and OS files such as .DS_Store. Default: the slug of --name, else ./<stack>. . is the current directory
--with <ids> Add optional fragments or an app. Repeatable or comma-separated
--without <ids> Leave out default fragments. Repeatable or comma-separated
--sandbox Local only: every default, no deploy, release or app fragments unless named with --with, and steps tagged prod skipped. Implies --yes. Alias for --preset sandbox
--preset default|sandbox The composition preset. Default: the preset setting, else default. Sandbox implies --yes
--yes Take every decision's default. The brief tells the agent not to ask you
--agents <list> Agents to write adapter files for: claude, codex, opencode, cursor. Repeatable or comma-separated. Default: the agents setting, else claude
--name <name> Project name for templates. Default: the directory's base name
--author <name> Author for templates. Default: the author setting, then git config user.name, else empty. An explicit empty value suppresses the fallback
--scope <scope> npm scope for the package_scope template variable, with or without @. Default: the package_scope setting, else none
--dry-run Compose and render the plan without writing anything or starting an agent
--launch <harness> The harness to hand the project to: claude, codex, opencode, cursor, copilot or codex-app (macOS). Never with --json, --no-launch or --dry-run
--no-launch Don't start an agent; print the prompt to give one
--source <where> How you reached stacks: web, docs, skill, cli or other. Optional telemetry metadata
stacks new go-cli my-cli --sandbox --dry-run
Terminal
Planned my-cli from go-cli (sandbox).
Fragments: agent-ops, ci-github, git-hooks
Agents: claude
Git: would create a new repository
Would write 20 files:
  .claude/ (10 files)
  .editorconfig
  .gitignore
  .stacks/ (4 files)
  AGENTS.md
  CLAUDE.md
  Makefile
  README.md
Brief: .stacks/BRIEF.md (3272 words)
Dry run: nothing was written.
Output

A real run writes the same files plus .stacks/ (BRIEF.md, NOTES.md, manifest.json, an empty state/, and a .gitignore that ignores everything there except the manifest), writes a state record outside the project, and runs git init when the target isn't already inside a repository and git is on PATH. The brief and the manifest explains what each file is for.

Presets

The preset comes from the flags, then the preset setting in config.json, then default. --sandbox and --preset sandbox are the same thing, and sandbox implies --yes however it was selected. --sandbox --preset default is a Usage error (exit 2).

Dry run

--dry-run checks the target, composes and renders every file in memory (the manifest included), then stops. It creates no directory, state record or git repository and starts no agent. Its JSON result has dryRun: true, stateRecord: false, and written lists the planned files. --dry-run --launch is a Usage error.

Names and template values

--name sets project_name. When you don't give a directory it also picks one through its slug; an explicit directory always stays the target. Slugs strip npm scopes, fold accents, split camelCase and digit boundaries, then join lowercase ASCII words with dashes. A name with no ASCII words becomes project.

Files ending in .tmpl are rendered and lose the suffix. Every other file is copied byte for byte, keeping its executable bit. The template variables are listed in Entry file format.

Handing the project to an agent

After writing, new hands the project off by the first of these rules that matches, and records which one in the manifest's handoff field.

  1. Inside an agent session (a marker such as CLAUDECODE or AI_AGENT is set): stdout ends with NEXT STEP FOR THE AGENT: read .stacks/BRIEF.md now and carry it out, ..., telling that agent to loop on stacks verify. With an untrusted entry it says STOP instead, and tells the agent to show you the entries and the brief's first section and continue only if you confirm.
  2. --json, --no-launch, stdin or stdout not a terminal, or any untrusted entry: the prompt is printed and nothing is launched. An untrusted entry never launches an agent.
  3. --launch picks a harness explicitly. Without it, the first available agent in the agents setting launches. With none configured, a single found harness launches, and several show a numbered picker. The search order is claude, codex, opencode, cursor-agent, copilot, then the Codex app on macOS. With none found, the prompt is printed.

A terminal harness gets your environment and terminal in the project directory, and stacks exits with the agent's own status. The Codex app is opened with the project and prompt, and stacks exits 0. Claude Code and Codex are the proven adapters; OpenCode and Cursor adapters are written but unproved.

Refused targets and conflicts

Condition Error
The target exists and holds anything besides .git and OS litter (.DS_Store, Thumbs.db, desktop.ini, ._*) TargetNotEmpty (2)
A fragment with needs_own_repo inside another repository ComposeConflict (2), with the --without flags that avoid it
An entry writing under .git/ or .stacks/, in any letter case ComposeConflict, reason reserved_path
Two entry files whose paths differ only in case ComposeConflict, reason file_owner
A write that would land outside the project's real path PermissionDenied (6), reason escape
An unknown template variable in a .tmpl file EntryInvalid (2)

A failure or Ctrl+C removes everything new wrote, the state record included. Missing tools never fail new: they're listed in the result and the brief, with the --without flag that would drop the fragment needing one. If the state directory isn't writable, new still succeeds with stateRecord: false, and verify's tamper check falls back to the manifest alone.

Exit codes: 0; 2 on bad input, an unknown entry, a non-empty target or conflicting fragments; 3 on bad configuration or a manifest schema newer than this CLI; 6 when a write is refused; 130 when interrupted. After launching a terminal agent, the exit is the agent's own status; an app launch exits 0.

stacks add

Applies fragments to an existing repository without overwriting live files. Conflicting contributions are parked under .stacks/incoming/ for your agent to merge.

stacks add ci-github                                   # the current repository
stacks add ci-github git-hooks --dir ../my-project --json
stacks add agent-ops --agents claude,codex --no-launch
Terminal
Argument or flag What it does
[fragment...] One or more fragments to apply (see stacks list fragments). Omitted, a person at a terminal gets a picker
--dir <path> Existing project directory. Defaults to the current directory
--sandbox Use the sandbox preset when creating a manifest. Existing projects keep their preset. Implies --yes
--yes Take every decision's default
--agents <list> Agents to write adapter files for. Default: the agents setting, else claude
--launch <harness> The harness to hand the project to, as for new. Never with --json or --no-launch
--no-launch Don't start an agent; print the prompt
--source <where> Optional telemetry attribution, as for new

add applies only the requested fragments and what they require. Existing files keep their bytes. A contribution identical to the live file is skipped; a conflicting one is parked whole at .stacks/incoming/<path>. Later parked JSON contributions deep-merge into the parked copy, and other repeated contributions get numbered copies. The result and the add brief list every pending incoming file, including ones from earlier runs.

An existing manifest keeps its stack, preset, agents, accepted verify steps and existing env and command values. Without one, add creates a manifest with mode: "add" and stack: null, and infers the project name from package.json, pyproject.toml, go.mod, Cargo.toml, then the directory name. --sandbox and --agents apply only to a newly created manifest; values that contradict an existing one are refused.

Before planning, add runs the same tamper check as verify, and changed steps refuse with VerifyChanged (5). When an untrusted fragment is added under an agent or without a terminal, add writes the new steps but keeps the previous digest (digestRecorded: false), so a person has to review them and run stacks verify --accept-changes. add runs no verify steps and no tools. A failed or interrupted write restores parked copies and state and removes new files.

add --json returns AddResult: added, alreadyApplied, newly written files, skipped, mergeNeeded with incoming paths, missingRequired with exact add commands, commandsKept, warnings, missingTools, the inferred name, manifest and digest status, and the brief and handoff. Add to an existing repository walks through a full run.

Exit codes: 0; 2 on bad input or an unknown fragment; 3 on a bad manifest; 5 when the project's verify steps changed (VerifyChanged); 6 on permissions; 130 when interrupted. After launching a terminal agent, the agent's own status.

stacks verify

Runs the project's verify steps from .stacks/manifest.json and reports whether the project is done. It finds the nearest manifest walking up from the current directory and runs only the steps listed there, printing each command before running it.

stacks verify                    # every step, from the project root or any directory below it
stacks verify --only lint,test   # rerun two checks while fixing them (outcome partial)
stacks verify --all              # also run steps skipped by default
stacks verify --json             # one VerifyResult object
Terminal
Flag What it does
--only <names> Run only these check and serve steps, by name. Repeatable or comma-separated. Setup and teardown still run. The outcome is partial, never green
--skip-tag <tag> Skip steps carrying this tag. Repeatable or comma-separated. The outcome is partial
--all Also run steps skipped by default (steps tagged prod in a sandbox project)
--accept-changes For a person who changed the verify steps on purpose: record them as they are now, then run them. Refused under a detected agent or without a terminal on stdin

Phases run in this order:

Phase How its steps run
setup Stops at the first failure and skips everything after it
check Every step runs, so one run reports every failing check
serve Each step is started, probed over HTTP, then stopped
teardown Always runs, after a failure or Ctrl+C too

Projects with an app run the app's steps as a second stage, only after the base project passes.

Steps run through /bin/sh -c in the project root (or the step's cwd) with stdin closed. They inherit your environment minus agent session markers, and the manifest's env fills any key you haven't set, so PORT_WEB=5187 stacks verify moves a port. Each step has a timeout (default 10m). Exit status 127 is reported as a missing command.

Outcome Exit Meaning
green 0 A full run where every step that ran passed
partial 0 A passing run narrowed by --only or --skip-tag. Never counts as done
failed 5 (VerifyFailed) A step failed. The full result is still on stdout, with each failed step's failureKind (step, env, timeout) and output tail
changed 5 (VerifyChanged) The steps differ from the copy recorded when the project was made. Nothing runs, not even teardown

--json also writes live events to stderr while it runs, one JSON object per line (schema verify-event): step-started as a step starts and step-finished with its status and duration as it ends. Read the last stderr line for the error envelope and skip lines with an event key.

Every run that gets past the manifest appends one record to .stacks/state/runs.jsonl. Records hold step names, phases, statuses and timings, with no commands, output, paths or environment values. Verify: the definition of done covers outcomes, app stages, serve probes and tamper evidence in full.

Important

--accept-changes is for a person. Under an agent (reason: "agent") or without a terminal on stdin (reason: "no-tty") it exits 6 and changes nothing. An agent that sees VerifyChanged should stop and ask you.

Exit codes: 0 when green or partial; 2 outside a project (NotFound) or on an --only name that isn't a check or serve step (BadInput); 3 on a manifest that doesn't decode or a state record from a newer stacks; 5 when a step fails or the steps changed; 6 when the manifest can't be read or --accept-changes is refused; 130 when interrupted, after teardown.

stacks verify --json
Terminal
{"error":{"tag":"NotFound","message":"Not found.","retryable":false,"next":"run this command again from inside a project stacks new created, or create one with stacks new <stack> <dir>.","details":{"suggestions":[],"kind":"project","looked":"..."}}}
stderr

stacks runs

Summarizes the nearest project's verify history, timings and interventions.

stacks runs          # summary for the nearest project
stacks runs --json   # one ProjectRuns object
Terminal

runs has no flags beyond the global ones. It reads .stacks/state/runs.jsonl, .stacks/NOTES.md and the project's state record outside the project. Interventions are the nonempty items under ## Asked in NOTES.md. Timings come only from the state record, so a fresh clone reports them as unknown. Invalid lines, or lines written by a newer stacks, are counted and skipped.

stacks runs
Terminal
Stack: go-cli
Fingerprint: sha256:c1c76ffed7d10d3815c1bfa762899875465cb766f353796fd671ab6561f9f3d3
Created at: 2026-10-30T05:00:57.858Z
Handoff: inside-agent
Brief words: 3272
Verify runs: 0
Last outcome: unknown
Minutes to green: unknown
Minutes to first successful test command (run completion): unknown
Interventions: 0
State record: true
Skipped lines: 0
Output

Exit codes: 0; 2 outside a project (NotFound); 3 on a manifest that doesn't decode, or a manifest or state record from a newer stacks; 6 when the manifest can't be read; 130 when interrupted.

stacks validate

Checks stacks and fragments before an agent builds from them, or previews the brief a composition renders to. It reads files only and runs nothing from an entry.

stacks validate                                         # the bundled registry
stacks validate ~/.config/stacks                        # a registry root (holds stacks/ or fragments/)
stacks validate ~/.config/stacks/stacks/my-api --json   # one entry, as a ValidateReport
stacks validate --brief typescript-cli --sandbox        # the brief a composition renders to
stacks validate ~/.config/stacks/stacks/my-api --submission   # ready for the public registry?
Terminal
Argument or flag What it does
[path] A registry root (holding stacks/ or fragments/) or one entry directory inside one. Relative paths resolve against the working directory. Omit it to check the bundled registry
--brief <stack> Print the brief this stack composes to, with its word counts and findings. Writes nothing
--with <ids> With --brief: add an optional fragment or app. Repeatable or comma-separated
--without <ids> With --brief: leave out a default fragment. Repeatable or comma-separated
--sandbox With --brief: render the sandbox preset
--submission Check one entry directory against the bar for the public registry (below)

--json returns ValidateReport (schema validate), or BriefPreview (schema validate-brief) with --brief. Validation checks lists every check, the word budgets and how the brief preview works.

--submission adds these checks to the report, each with a file and line:

Check Severity Finds
fill-marker error A stacks:fill marker left in the frontmatter, the body, the search card, or a file under files/ or adapters/. A shipped marker is allowed when a verify step whose run mentions stacks:fill names that file's project path (with or without .tmpl), as the skeleton's readme step names README.md, because the project's agent fills it
secret error A credential-shaped string: cloud, GitHub, Slack and AI provider keys, private keys, and long literals assigned to names such as password or token. A value that reads as a placeholder or a reference (your-..., {{project_slug}}, ${...}, secrets., process.env) is allowed. The report names the kind of secret, never the value
env-file error A .env file other than .env.example, .env.sample or .env.template (each also as .tmpl)
absolute-path warning A home directory path (/Users/<name>/ other than /Users/Shared/, or /home/<name>/) or a drive-letter path such as C:\work in shipped text. The homes of container, CI and dev container accounts (node, runner, vscode, ubuntu, codespace, gitpod, coder, circleci, app, user, ec2-user, linuxbrew, bun, pwuser, nonroot, jovyan, vagrant, jenkins, gitlab-runner) are allowed
missing-card warning No search card

The scan matches patterns, so it misses things: read the files yourself before submitting.

Exit codes: 0 when there are no errors (warnings and notes don't fail); 2 on errors (ValidationFailed, with the report still on stdout), a path that is neither a registry nor an entry, --with, --without or --sandbox without --brief, a --brief composition that is unknown or conflicts, or --submission with --brief or with a path that isn't one entry; 3 on a broken config.json with --brief; 6 when the root can't be read; 130 when interrupted.

stacks create

Writes a new stack or fragment skeleton into your user registry, or copies an existing entry there for editing. It never runs anything from an entry.

stacks create stack my-api --json
stacks create fragment my-ci --category ci
stacks create fragment my-app --category app
stacks create stack go-cli --from bundled
stacks create stack team-api --from project --yes
Terminal

create has two subcommands, stacks create stack [id] and stacks create fragment [id]. The id is lowercase kebab-case. Without a subcommand or id, a person at a terminal gets a picker.

Flag Applies to What it does
--from bundled|index|project|user both Copy the entry with this id from that origin instead of writing a skeleton. user is refused, because the user registry is the destination
--category <category> fragment Category for a new fragment: agent-ops, app, ci, deploy, release, service, vendor or tooling. Default tooling. An app needs applies_to filled before it validates
--yes both Skip the trust question after the full report. Still requires a person at a terminal for an untrusted copy

Entries land in $XDG_CONFIG_HOME/stacks/stacks/<id>/ or .../fragments/<id>/ (default ~/.config/stacks/). A copy keeps the same id and every file, adapter and search card, so the user copy then shadows the original. Copying from project or any other untrusted source prints a full trust report on stderr and asks a person at a terminal to confirm. An agent or a non-terminal stdin can't confer that trust (PermissionDenied, 6).

An occupied target is refused and left as it was, and so is a skeleton whose id another registry root already has: copy that entry with --from instead. --json returns { kind, id, path, guide } with absolute paths. Write your own stack or fragment covers filling in the new entry and validating it.

Exit codes: 0; 2 on a bad id, an id another registry already has, an occupied target, an unknown source or a declined confirmation; 6 when the user registry can't be written or an untrusted copy can't be confirmed by a person at a terminal; 130 when interrupted.

stacks submit

Opens a draft pull request that adds your stacks and fragments to the public registry, steamfield/stacks. It runs the GitHub CLI (gh) and git with fixed arguments and holds no credentials of its own, so gh must be on PATH and signed in (gh auth login).

stacks submit ~/.config/stacks/stacks/my-api --dry-run    # check and print, open nothing
stacks submit my-api my-lint --query "a REST API in Go"    # one pull request for both
stacks submit my-api --repo my-org/stacks --json           # another upstream; a SubmitResult
Terminal
Argument or flag What it does
<entry...> Entry directories, or ids of entries in your user registry. An argument with a /, or one that starts with . or ~, is a path; anything else is an id (./my-api for a folder in the current directory). An id that isn't kebab-case is refused. Give a stack together with the fragments it needs and they go in one pull request
--dry-run Clone and check as usual, then print the files and the pull request body. Pushes nothing and opens nothing
--query <text> A search the entry should rank first for, proposed to maintainers in the pull request. Repeatable
--repo <owner/name> The upstream repository. Default steamfield/stacks

submit stops at the first step that fails:

  1. It refuses offline mode, then reads your GitHub login with gh api user.
  2. It makes a partial, shallow clone of the upstream main in a temporary directory, which is removed however the command ends.
  3. It copies each entry to packages/cli/registry/stacks/<id>/ or fragments/<id>/, its search card to search/, and maps <kind>/<id> to your login in authors.json. An update without a search card keeps the upstream card. An unreviewed author card gets its digest stamped from the entry as submitted. A reviewed card whose digest no longer matches is restamped and becomes an unreviewed author card again.
  4. It checks the entries against the clone's registry, not your installed one, so every reference must resolve upstream or among the submitted entries. validate --submission must report no errors and no warnings except the ones maintainers clear after the pull request: search-unreviewed, search-missing, missing-card, deps-missing and deps-unreviewed. A removed id is always refused. An existing id is refused unless authors.json gives it to your login, which makes the pull request an update.
  5. Unless --dry-run, it forks the repository (reusing an existing fork), commits exactly the submitted entries, cards and authors.json on branch entry/<kind>-<id>, pushes to your fork and opens a draft pull request. The body lists the entries, the validation summary and the warnings left, your proposed queries, the CLI version and the maintainer checklist.

A rerun pushes to the same branch, rewrites the open pull request's title and body, and reports it instead of opening another. It replaces the branch only when its tip is the single commit an earlier submit made. A branch with commits submit didn't make is refused (branch_taken): update that pull request by hand, or delete the branch on your fork to start over.

An agent may run submit. The authoring skill tells it to show you the --dry-run output and get your yes first. --json returns SubmitResult (schema submit): { repo, entries, author, branch, prUrl, reused, dryRun, findings, files }, plus body with --dry-run.

Exit codes: 0 when submitted or dry-run; 2 for offline mode, a missing or signed-out gh, a bad --repo, an id that isn't kebab-case, an entry that fails the checks (each blocking finding in details.blockers), an id collision, a branch with commits submit didn't make, or nothing to change; 4 when GitHub or git can't be reached (UpstreamUnavailable, retry later); 6 when GitHub refuses the fork or push for your login; 130 when interrupted.

stacks update

Checks the latest published version of @steamfield/stacks and upgrades a global npm install.

stacks update                  # upgrade a global npm install
stacks update --check          # report versions and the command without installing
stacks update --check --json   # one UpdateResult object
Terminal
Flag What it does
--check Report versions and the update command without installing. Agents use this

Each invocation makes one request to the npm registry with a 10 second timeout. Nothing checks in the background and no version is cached, so under --offline or STACKS_OFFLINE=1 both forms fail with BadInput (exit 2) before any request.

Status Meaning
up-to-date The running version is the latest
ahead The running version is newer than the published one
available A newer version exists. The result carries the install method and command to run
updated A global npm install was upgraded

When a newer version exists, update works out how stacks was installed. A global npm install upgrades with npm install -g @steamfield/stacks@<latest>, pinned to the version it reported, with a 5 minute timeout. npm runs from your home directory, so a project's .npmrc can't change it. An npx install gets npx -y @steamfield/stacks@<latest> as its command; other installs get the npm command without installing. The running process keeps its old code after updating.

An agent may run --check. An agent that tries to install an available upgrade without --check gets PermissionDenied (6).

Exit codes: 0 when up to date, ahead, available or updated; 2 in offline mode; 4 when the registry is unavailable (UpstreamUnavailable, retry later); 6 when an agent tries to install, or npm fails (UpdateFailed: run the supplied command yourself to see npm's output); 130 when interrupted.

stacks skill install

Installs a bundled skill for Claude Code. stacks, the default, teaches your agent the search, show, new, verify loop. stacks-author teaches it to write a stack or fragment and submit it, and ships a references/ folder beside its SKILL.md.

stacks skill install                  # $HOME/.claude/skills/stacks/SKILL.md
stacks skill install stacks-author    # $HOME/.claude/skills/stacks-author/, every file
stacks skill install --project        # .claude/skills/ under the git toplevel
stacks skill install --force          # replace edited copies
stacks skill install --print          # print SKILL.md to copy somewhere else
stacks skill install --print --json   # { name, path: null, action: "printed", files, text }
Terminal
Argument or flag What it does
[name] stacks (default) or stacks-author
--project Install under the git toplevel, or the working directory outside git
--force Replace installed files whose content differs
--print Print the skill's SKILL.md without looking up or writing an install target. For stacks-author, its links to references become stacks guide author <topic> commands

Every file of the skill goes under .claude/skills/<name>/, and --json lists each one's path and action in files. Identical installed files are left alone (action: "unchanged"). Different ones are preserved with TargetNotEmpty, and nothing is written, unless you pass --force; a directory sitting at a skill file's path is always preserved. Writes refuse symlinked skill files and symlinks that escape the skills root, though a dotfiles symlink above the skills root works. Installing under an agent is allowed. For agents other than Claude Code, take the text from --print or stacks guide author. Use stacks with your agent covers the other agents.

Exit codes: 0 when written, unchanged or printed; 2 on an unknown name, when a different file is already there (TargetNotEmpty), or when the bundled skill is missing; 6 when HOME is unset or the target can't be written; 130 when interrupted.

stacks guide

Prints a short guide written for coding agents: the search, show, new, verify loop and the rules an agent must keep. stacks with no arguments prints the same thing, and --json returns { guide, commands }.

stacks guide
Terminal
stacks starts projects for coding agents. It writes starter files and .stacks/BRIEF.md; verify runs the manifest's checks.

The loop:
1. stacks search "<their words>" --json; compare strong matches using why and evidence.
If noStrongMatch, say nothing fits well; run stacks list --json and offer the closest entries.
2. stacks show <id> --json; explain your pick and one tradeoff.
...
3. stacks new <stack> <dir> --sandbox --json; follow <dir>/.stacks/BRIEF.md. Drop --sandbox to make decisions or deploy.
4. In <dir>, stacks verify until green; run stacks verify --all before calling it done.

Existing repo: stacks add <fragment...> --json; follow the brief, merge incoming files and verify.
Writing or submitting a stack or fragment: stacks guide author.

Rules:
- Never pick an entry list --json marks untrusted unless the user named it.
- Never edit .stacks/manifest.json.
- Weakening, skipping or deleting a verify step counts as failure.
- If verify reports VerifyChanged, stop and ask the user: only they can accept the change.
- Ask the user only what the brief says to ask.
- Relay telemetry notices once, including stacks telemetry disable. Never enable collection.
...
Output

At a human terminal, the guide starts with one extra line saying it's written for agents and how to start a project yourself.

Exit codes: 0, 2 on a usage error, 130 when interrupted.

stacks guide author

Prints the stacks-author skill without installing it, so any agent can follow it from any project. With no topic it prints the skill's SKILL.md. A topic prints one of its references.

stacks guide author              # how to write and submit a stack or fragment
stacks guide author from-repo    # one reference: turning an existing repository into a stack
stacks guide author --json       # { topic, text, topics }
Terminal
Topic Covers
anatomy The entry format in full
from-repo Turning a repository you already have into a stack
interview Designing a new stack with the user, one round of questions at a time
fragment Fragments and apps
search-card Writing the search card
submit The submission bar, the dry run and what maintainers do next

Links between the files become the stacks guide author <topic> command that prints the target. --json returns schema guide-author.

Exit codes: 0; 2 on an unknown topic (with the closest topic suggested) or a missing bundled file; 6 when a bundled file can't be read; 130 when interrupted.

stacks doctor

Reports what stacks sees on this machine: version, platform, config, data, cache and working paths, the user entry directory, the bundled registry and skill paths and whether each exists, configuration errors, and the remote index state.

stacks doctor
stacks doctor --json   # one DoctorReport object
Terminal

remoteIndex in the JSON report carries the index URL, its trust mode, the offline flag, the cache timestamp, the last attempt and outcome, and any backoff deadline. doctor never refreshes the index. A missing config.json is fine; a malformed one is listed in configErrors and doctor still exits 0, which makes it the command to run when other commands fail with InvalidConfig.

Exit codes: 0, 2 on a usage error, 130 when interrupted.

stacks schema

Prints the JSON Schema (draft 2020-12, objects closed) of a command's --json result, so a caller can validate what it parses.

stacks schema                 # list the names and what each describes
stacks schema verify          # the schema of verify --json's result
stacks schema error --json    # the error envelope's schema, as compact JSON
Terminal
stacks schema
Terminal
add             add --json
create          create --json
doctor          doctor --json
error           the last stderr line of any failed --json command
guide           guide --json, and stacks --json alone
guide-author    guide author [topic] --json
list            list --json
new             new --json
runs            runs --json
search          search --json
show            show --json
skill           skill install --json
submit          submit --json
telemetry       telemetry status|enable|disable|reset|flush --json
update          update --json
validate        validate --json
validate-brief  validate --brief <stack> --json
verify          verify --json
verify-event    each live stderr line verify --json writes before the result
Output

stacks schema --json with no name returns { schemas: [{ name, describes }] }. An unknown name is a Usage error with a did-you-mean suggestion.

Exit codes: 0, 2 on a usage error or an unknown name, 130 when interrupted.

stacks telemetry

Controls the CLI's anonymous usage telemetry. Telemetry is on by default after a one-time notice; the privacy page says what is collected and how long it's kept.

stacks telemetry status            # consent, notice, identity and pending events
stacks telemetry disable           # turn collection off and delete pending data
stacks telemetry reset --json      # rotate identity, keep consent
stacks telemetry flush --json      # send pending events now
stacks telemetry enable            # turn it back on, from your own terminal
Terminal
Subcommand What it does Exit codes
status Report consent and why, the notice time, local identity when present, and the pending event count. Sends nothing 0, 2, 130
enable Turn collection on. Requires a person with a terminal on stdin; agents can't enable it 0, 2, 3 (InvalidConfig), 6 (no person, or a write failed), 130
disable Save "telemetry": false and delete pending events, install identity and project links. Works in scripts and under agents 0, 2, 3, 6, 130
reset Rotate the install identity and delete pending events and project links, keeping consent 0, 2, 130
flush Send up to 50 queued events now, subject to consent and backoff, with a 5 second deadline 0 even when delivery fails, 2, 130

All five take --json and share the telemetry schema. disable saves the opt-out even if cleanup fails, and reports cleared: false in that case. For one invocation, --no-telemetry or --offline skips collection; DO_NOT_TRACK, STACKS_TELEMETRY_DISABLED and CI turn it off for every run. Telemetry covers the consent states, and the opt-out list has every switch.

Shell completions

--completions <shell> prints a static completion script covering every command and flag. It takes bash, zsh, fish or powershell, with sh and pwsh as aliases. Regenerate the script after upgrading stacks.

stacks --completions bash >> ~/.bashrc
stacks --completions zsh > "${fpath[1]}/_stacks"
stacks --completions fish > ~/.config/fish/completions/stacks.fish
Terminal