Specnaut home · Documentation

Specnaut

Specnaut is an enhanced fork of the specify CLI from GitHub Spec Kit, distributed as a single native binary (no Python prerequisites). It scaffolds the files your AI coding harness consumes — SpecKit slash-commands, spec / plan / tasks templates, a constitution, agents, and a backlog system — directly into an existing project, in one command.

Specnaut does not call any LLM and does not orchestrate any agent at runtime. Your AI harness (Claude Code, Cursor, Codex, GitHub Copilot CLI, Windsurf, OpenCode, Antigravity) is what reads the generated files and acts on them.

This page is the canonical documentation. The same content is available as raw Markdown at /llms.txt for LLM consumption — see llmstxt.org for the convention.

Install

The fastest path on macOS or Linux:

curl -fsSL https://raw.githubusercontent.com/specnaut/specnaut-cli/main/install.sh | bash

The installer downloads the platform binary, verifies the SHA256 checksum, and places it in /usr/local/bin (auto-elevating via sudo if needed). On non-writable prefixes with no terminal it falls back to ~/.local/bin.

Pin a specific version:

curl -fsSL https://.../install.sh | VERSION=v0.7.1 bash

Custom install dir:

curl -fsSL https://.../install.sh | PREFIX=$HOME/.local/bin bash

Or via Homebrew (macOS / Linux):

brew tap specnaut/tap
brew install specnaut

Manual download: pick the binary for your OS/arch from GitHub Releases, chmod +x, place it on your $PATH. On macOS clear the quarantine attribute with xattr -d com.apple.quarantine /path/to/specnaut.

Install as a plugin / extension (five harnesses)

If you want Specnaut's skills and sub-agents available across all your projects without running specnaut init, install Specnaut as a plugin / extension in your harness. Specnaut ships adapters for five harnesses with the same skill content across all of them — the bundled router skill, the phase docs, the bootstrap skill, the sub-agents, and the SessionStart hook (where supported).

Harness Install command
Claude Code /plugin install specnaut/specnaut-cli-plugin
Codex CLI / App /plugins → search "specnaut" → install (once the marketplace listing lands; see Notes)
Cursor /add-plugin specnaut/specnaut-cli
OpenCode Add "plugin": ["specnaut@git+https://github.com/specnaut/specnaut-cli.git"] to opencode.json
GitHub Copilot CLI copilot plugin marketplace add specnaut/specnaut-cli-marketplace
copilot plugin install specnaut@specnaut-marketplace

The skill content is identical across harnesses; only the surface conventions differ (slash-command prefix, auto-activation mechanism, tool naming). See the per-harness tool-mapping references at plugin/skills/using-specnaut/references/<harness>-tools.md for the equivalent of every Claude Code tool on each harness.

Claude Code — slash-command prefix

/specnaut-plugin:specnaut plan "<feature description>"
/specnaut-plugin:specnaut tasks

Slightly verbose, but unambiguous (the plugin's slash-commands are namespaced and the consolidated router itself is named specnaut). If you scaffold project-local with specnaut init instead, you get the shorter /specnaut plan "..." form.

To test a local checkout of the plugin without publishing:

claude --plugin-dir /path/to/specnaut/plugin

Auto-activation across harnesses

Specnaut ships a using-specnaut bootstrap skill loaded automatically at session start on every harness that supports it (via plugin/hooks/hooks.json on Claude Code, plugin/hooks/hooks-cursor.json on Cursor, the experimental.chat.messages.transform hook in .opencode/plugins/specnaut.js on OpenCode). The bootstrap skill teaches the agent Specnaut's skill registry, agent registry, and routing principles so you don't need to invoke /specnaut explicitly — typing "plan this issue" or "review my work" is enough for the right skill to fire.

Notes on Codex CLI and the shared marketplace

Codex CLI and Copilot CLI distribute plugins through marketplaces. Specnaut has two adapter targets that need a one-time human setup before the marketplace listings are live:

  • Codex CLI.codex-plugin/plugin.json ships in this repo; the scripts/sync-to-codex-plugin.sh script (fires on every release tag) mirrors the Specnaut plugin content into specnaut/plugins (a fork of openai/plugins). Until that fork is rebased into upstream and the CODEX_SYNC_TOKEN PAT is provisioned (see issues #298–#300), the sync emits a workflow warning and skips — same fail-safe pattern as the Homebrew tap bump.
  • Copilot CLI + shared marketplace.claude-plugin/marketplace.json lives in specnaut/specnaut-cli-marketplace (a separate repo). scripts/sync-to-marketplace.sh bumps the version on every release. Until the marketplace repo + MARKETPLACE_SYNC_TOKEN are provisioned (see issues #309–#310), the sync skips with a warning.

Plugin vs specnaut init — they complement each other:

Aspect Binary (specnaut init) Plugin (/plugin install)
Scope Project-local (.claude/) User-scope (all projects)
Slash-command style /specnaut plan (short) /specnaut-plugin:specnaut plan
Customizable per-project Yes No (user-scope, shared)
Board skill, hooks, .specnaut/ Yes No (project-stateful — binary-only)
Kept in sync specnaut upgrade /plugin update

Most teams use both: the plugin provides discoverability and keeps the agents up-to-date across all projects; specnaut init provides the short slash-commands and project-local customization.

Quickstart

Create a new project

specnaut init my-project
cd my-project

This scaffolds a tree configured for the Claude Code harness by default (.claude/, .specnaut/, AGENTS.md, .specnaut/backlog.md, …). Open the project in your harness — that's where you'll run the rest.

The three top-level skills

Specnaut gives your harness three skills, and the split between them is the model:

Skill Owns Example
/board the backlog and the dashboard /board add "Fix the login redirect"
/specnaut specification only — plan, tasks, implement, review, merge /specnaut plan "Add OAuth2 login"
/ship going to production — versioning and release notes /ship tag

Manage the backlog, write the spec, ship it.

/specnaut owns specification and no release concern. Tagging and release notes used to sit under it as the tag-version and release-version phases; they are now /ship tag and /ship release. Both the owner and the verb changed, so /ship tag-version is not a command — see Ship: versioning and release notes.

Step 1 after init: run /specnaut constitution

/specnaut constitution is the expected first action after specnaut init. It scaffolds your project's guiding principles (architecture, quality gates, ways of working) into .specnaut/memory/constitution.md so the rest of the pipeline (/specnaut plan, /specnaut tasks, /specnaut implement) has something to anchor on — the plan phase gives every principle an explicit verdict.

The generated constitution comes pre-populated with four opinionated baseline blocks (all user-tunable): Engineering methodology (TDD / DDD / SOLID-DRY-KISS-YAGNI / Boy-Scout-escalation), Architecture layers (hexagonal default: domain/ / application/ / infrastructure/ / presentation/), Back-end patterns (Repository, service objects, DI through constructors, thin controllers, errors as domain types, pure domain), and Front-end patterns (view/logic separation, no business rules in templates, smart vs dumb components, single source of truth for state, typed API client, accessibility mandatory). New projects via specnaut init inherit all four blocks automatically. specnaut upgrade delivers updated agents and skills but does not rewrite an existing constitution — to adopt the new baselines in an existing project, rebase your constitution manually.

Refine the generated constitution and the root AGENTS.md for your stack, then move on to /specnaut plan "<feature description>" for your first feature.

Add Specnaut to an existing project

cd my-existing-project
specnaut init --here

Specnaut merges its .gitignore block into your existing file (non-destructively, fenced with # --- Specnaut: gitignore --- markers). Other specnaut-managed files use upgrade-aware semantics: if you customize a generated file, specnaut upgrade will preserve it unless you pass --force.

Declaring a file preserved across a forced refresh. specnaut upgrade auto-preserves a file whose hash diverged, but specnaut init --force would otherwise overwrite every managed file. To keep a customized file (e.g. a tailored .claude/agents/product-owner.md) even through a forced refresh, list it in a version-controllable .specnaut/preserve.yml manifest:

preserved:
  - .claude/agents/product-owner.md

Both init --force and upgrade then leave that file untouched and print one notice per preserved path — never a silent skip. The file stays lock-tracked, so specnaut diff keeps showing how it has drifted from the evolving bundle. A project with no preserve.yml behaves exactly as before. To deliberately discard a customization and restore the bundled version for one run, add --reset-preserved (it overrides every declaration for that run and reports each override; it is never the default). A declared path that is not a managed bundle file is reported as an ineffective declaration (a warning) rather than silently honored.

Pass --dry-run to preview the plan without touching disk — combined with --force it shows which files would be overwritten and which would be merged, but writes nothing. --dry-run is the trump card: it wins over --force.

Inside a monorepo workspace? When the target sits inside an enclosing Specnaut workspace, specnaut init and specnaut upgrade provision .specnaut/ as usual but skip the agentic files (.claude/skills, .claude/agents, .claude/commands) — those are inherited from the parent, so no copy is scattered into the sub-repo.

A target counts as parent-managed when an ancestor has .specnaut/ and either:

  • that ancestor's deno.json workspace list declares the target as a member, or
  • the target carries an empty .specnaut/parent-managed.yml marker.

The second exists because the first is a Deno-specific signal. A sub-repo on a different toolchain is usually kept out of workspace deliberately — adding it there breaks its own build — and before the marker it had no way to say "my enclosing workspace manages me". The marker does not skip the ancestor check: without a providing ancestor, suppressing the agentic files would leave the sub-repo with none and nothing supplying them.

To force full provisioning anyway, drop an empty .specnaut/standalone.yml marker in the target. It wins over both signals above.

What's in .specnaut/installed.lock and should I commit it?

specnaut init writes a small YAML file at .specnaut/installed.lock. It records the harness you chose, the templates version installed, and a SHA-256 + install timestamp for every file Specnaut emitted. It contains no secrets — only file paths, content hashes, and version strings.

Commit it. specnaut upgrade reads this lock to know which harness to map templates to, to detect files you have customized (so it doesn't clobber them), and to drop orphaned files that are no longer part of the bundle. specnaut check --project also surfaces the harness, templates version, and backlog backend from this file (and warns when backlog-config.yml has empty required fields for the github / gitlab backends). Without the lock, both commands degrade gracefully but cannot do their real job — specnaut upgrade will refuse and ask you to re-run specnaut init --here --force to rebuild the lock from scratch.

Pick a different harness

specnaut init my-project --ai cursor
specnaut init my-project --ai antigravity
specnaut init my-project --ai codex
# … etc.

Seven harness targets are supported: claude (default), cursor, codex, windsurf, copilot, opencode, antigravity. Each emits files in the convention that harness expects.

Pick a backlog backend

specnaut init my-project --backlog github
specnaut init my-project --backlog gitlab
specnaut init my-project --backlog local      # default
specnaut init my-project --backlog cloud      # hosted Specnaut Cloud

Four backends are supported: local (default), github, gitlab, and cloud — the hosted Specnaut Cloud backend, which you connect to with specnaut login (see Connect the CLI to Specnaut Cloud below). See Backlog as product source of truth for what each one stores and how the PO agent talks to it.

Pre-fill the backlog config with --backlog-url

When the chosen backend is github or gitlab, specnaut init can take the project's Kanban URL up front and write a fully-populated .specnaut/backlog-config.yml — no manual edit needed before running /board. Pass the project URL via --backlog-url:

# GitHub org-owned project
specnaut init --here --ai claude --backlog github \
  --backlog-url https://github.com/orgs/myorg/projects/1

# GitHub user-owned project
specnaut init --here --ai claude --backlog github \
  --backlog-url https://github.com/users/alice/projects/12

# GitLab (gitlab.com or self-hosted)
specnaut init --here --ai claude --backlog gitlab \
  --backlog-url https://gitlab.com/mygroup/myproject

Three URL formats are supported:

  • GitHub org-owned: https://github.com/orgs/<org>/projects/<N>
  • GitHub user-owned: https://github.com/users/<user>/projects/<N>
  • GitLab project: https://<host>/<group>/<project>

For GitHub, the repo: field of the populated config is derived from git remote get-url origin (both HTTPS and SSH remote shapes are recognised). Pass --backlog-repo <owner>/<name> to override that derivation when the project lives across multiple repos or the local remote isn't origin.

Without --backlog-url on a TTY, specnaut init interactively prompts for the URL after the backend picker. In non-TTY mode (CI / scripted setup) --backlog-url is required when --backlog is github or gitlab — omitting it exits with code 2 and a clear error message. The non-clobber invariant still holds: re-running init against a project with an existing backlog-config.yml does NOT overwrite it.

Connect the CLI to Specnaut Cloud

Specnaut Cloud is the optional hosted backlog + remote-control backend. To drive a project's backlog from the CLI against your Cloud account, scaffold with the cloud backend and then authenticate the machine — specnaut login is the one command to run:

specnaut init my-project --backlog cloud   # scaffold against the cloud backend
specnaut login                             # authenticate this machine (alias for `specnaut cloud login`)

specnaut login runs a browser device-authorization flow (like gh auth login): it prints a one-time code + verification URL, opens your browser, waits for you to approve, then stores an access

  • refresh token securely and links the project to a Cloud project. Credentials go to the OS keychain when a keyring is reachable, otherwise a 0600 file at ~/.specnaut/credentials.json; they are keyed by deployment URL, so one machine can hold tokens for several deployments.

Before opening the browser, login prints the target server and where the URL came from, and asks for confirmation the first time you authenticate against a URL that came from a project's .specnaut/backlog-config.yml — a safeguard so a cloned repo cannot silently redirect your login:

Connecting to:  https://your-deployment.convex.site
Source:         project config (.specnaut/backlog-config.yml)

Once connected, the Cloud CLI commands are:

specnaut login                 # authenticate this machine (alias for `specnaut cloud login`)
specnaut cloud login           # authenticate + (re)select the linked project
specnaut cloud orgs            # list the organizations your account belongs to
specnaut cloud board           # show the linked project's board (tasks grouped by column)
specnaut cloud token           # print a fresh access token to stdout (for scripts)
specnaut cloud logout          # remove the stored credentials for the deployment

Add --api-url <url> to any of these to target a specific deployment. Headless / CI: set the SPECNAUT_CLOUD_TOKEN environment variable to a Cloud API token to skip the browser flow entirely — no keychain access is attempted, which is the supported path for CI and unattended VMs.

Run init non-interactively (CI / scripts)

When you pass both --ai and --backlog (and --backlog-url when the backend is remote), no interactive prompt is shown — specnaut init runs fully unattended, which is what you want in CI or scripted setup:

# Local backend — zero-config, just the two flags
specnaut init my-project --ai claude --backlog local

# GitHub backend — --backlog-url is required in non-TTY mode
specnaut init my-project --ai claude --backlog github \
  --backlog-url https://github.com/orgs/myorg/projects/1

# GitLab backend — same shape
specnaut init --here --no-git --ai cursor --backlog gitlab \
  --backlog-url https://gitlab.com/mygroup/myproject

Without those flags, specnaut init shows an arrow-key picker (↑/↓ to move, space/enter to select) when stdin is a TTY, and falls back to a numeric prompt — or the defaults — when stdin is piped.

Pick a versioning scheme

specnaut init asks which scheme to use for the bundled /ship tag and /ship release commands. Two options:

  • SemVer (v1.2.3) — recommended for libraries / SDKs whose consumers reason about breaking changes by version number.
  • Date-based (vYY.M.Da) — recommended for apps / SaaS / deployed products where the version number is just a release identifier. No major/minor/patch guesswork; the letter suffix handles same-day re-tags.

Specnaut pre-selects a sensible default by scanning the project for SemVer signals:

  • Library publishing markerspackage.json exports, pyproject.toml [project] / [tool.poetry], Cargo.toml [lib], composer.json type=library.
  • Semver-shaped git tags — any local tag matching v?MAJOR.MINOR.PATCH (with optional pre-release / build suffix), e.g. v1.2.3, 1.0.0-rc.1, v2.0.0+build.5. Date-shaped tags like v25.5.16a are explicitly excluded so brownfield repos already on date scheme don't get mis-suggested.
  • CHANGELOG.md — Keep-a-Changelog style headers (## [1.2.0], ## v1.2.0, ## 1.2.0).

Any one signal flips the suggestion to SemVer. When zero signals are found, Specnaut suggests date-based. The user can always override at the picker. The choice is persisted by rewriting the scaffolded skill itself (the unchosen scheme's blocks are stripped at scaffold time), so the on-disk .specnaut/scripts/release/tag.sh only contains the chosen scheme's logic. To switch schemes later, re-run specnaut init and pick the other option.

Pass --scheme semver|date to bypass the picker in non-TTY mode.

Other commands

specnaut check                    # diagnose your environment
specnaut check --project          # also diagnose the current specnaut project
                                  #   (warns if the plugin was uninstalled after migration)
specnaut upgrade                  # update templates to the binary's version
                                  #   (when specnaut-plugin is installed + harness=claude:
                                  #    vanilla agent/command files are auto-migrated to the plugin)
specnaut upgrade --dry-run        # preview the upgrade plan
specnaut upgrade --force          # apply destructive changes (backs up customizations)
specnaut upgrade --reset-preserved  # ignore .specnaut/preserve.yml for this run (reports each override)
specnaut diff                     # show how managed files diverge from the bundle (read-only)
specnaut diff --only-customised   # restrict the diff to files you actually changed
specnaut init --here --force --reset-preserved  # forced refresh that overrides preserve declarations
specnaut reconcile --status       # list files pending post-upgrade reconciliation (JSON)
specnaut reconcile <path> --accept-upstream  # take new template version (backs up local)
specnaut reconcile <path> --accept-current   # keep local version (re-stamps lock SHA)
specnaut login                    # connect the CLI to Specnaut Cloud (see "Connect the CLI to Specnaut Cloud")
specnaut cloud orgs               # list your Cloud organizations
specnaut cloud board              # show the linked project's board
specnaut cloud token              # print a fresh Cloud access token (for scripts)
specnaut cloud logout             # clear stored Cloud credentials
specnaut self-update              # upgrade the binary itself
specnaut self-update --check      # only report whether an update is available
specnaut --version                # print version
specnaut --help                   # full usage

Ship: versioning and release notes

Every scaffolded project ships /ship, the third top-level skill. It owns going to production, and it is the only place release lives:

  • /ship tag — creates an annotated git tag using the project's versioning scheme. Bumps automatically (latest tag → next). For SemVer, --bump major|minor|patch controls the direction (default patch); for date-based, the letter suffix increments. Pushes to origin if a remote is configured, else stays local. Pass --no-push to skip.
  • /ship release — generates categorized release notes for a tag (default: latest) covering every commit since the previous tag. The output is the release-body Markdown, one section per non-empty Conventional Commits bucket (Features / Bug Fixes / Performance / Refactors / Documentation / Tests / Build & CI / Chores / Style / Other). Pipe the output into gh release create / glab release create to publish.

If you are coming from an earlier version, these were /specnaut tag-version and /specnaut release-version. Both halves of the name changed — the owning skill and the verb — so swapping only the prefix gives you /ship tag-version, which nothing answers to. The new names are /ship tag and /ship release.

Projects scaffolded before the split get /ship by running specnaut upgrade; there is nothing to move by hand, and a customised phase document travels to its new address rather than being left behind at the old one. The scripts do not move: they stay at .specnaut/scripts/release/{tag,release}.sh, the same path across every harness, so anything of yours that calls them directly keeps working.

For GitHub-hosted projects, the bundled release-github.sh wrapper is the one-command path:

bash .specnaut/scripts/release/release-github.sh           # latest tag, auto-baseline, publish
bash .specnaut/scripts/release/release-github.sh --draft   # create as draft

For GitLab-hosted projects, release-gitlab.sh mirrors the same contract:

bash .specnaut/scripts/release/release-gitlab.sh           # latest tag
bash .specnaut/scripts/release/release-gitlab.sh v1.2.3    # specific tag

Both wrappers compute the baseline as the previous tag with a published release attached (not the previous tag by date) — tags pushed without a release are "subsumed" and their commits land in this release, with the subsumed tag names listed inline. They push the tag to origin if needed, then call gh release create / glab release create. Idempotent: a second run against an already-released tag exits 0 with an explanatory message.

For local-only projects (no remote, or you just want a Markdown artifact), the bundled release-local.sh wrapper writes the categorized body to a file:

bash .specnaut/scripts/release/release-local.sh             # latest tag → RELEASE_NOTES_<tag>.md
bash .specnaut/scripts/release/release-local.sh --out NOTES.md v1.2.3

No remote API calls, no auth — paste the output into any release UI, attach to a deploy email, or pipe to a custom publisher.

Available harnesses

Key Display name Output root
claude Claude Code .claude/
cursor Cursor .cursor/
codex Codex CLI .codex/, .agents/
windsurf Windsurf .windsurf/
copilot GitHub Copilot CLI .github/instructions/
opencode OpenCode .opencode/
antigravity Antigravity .agents/

All harnesses share the same source-of-truth content in templates/core/. The per-harness adapters in src/infrastructure/harness/ map that core bundle to each harness's directory layout and frontmatter conventions.

Some harnesses also ship harness-specific helper files alongside the core scaffold:

  • Claude.claude/CLAUDE.md (harness reference, including /goal, /loop, and claude agents usage notes) + .claude/loop.md (default prompt for /loop, Claude's recurring periodic-maintenance feature).
  • Codex.codex/AGENTS.md (harness reference) + .codex/goal.md (default prompt for /goal, Codex's experimental one-shot long-horizon feature; enable via goals = true under [features] in config.toml).

Interoperating with DeepSeek Harness

DeepSeek Harness (dsh) has no --ai key, and Specnaut ships no adapter for it. It does not need one: run specnaut init --ai codex, and a dsh session reads the result as it stands. Both surfaces dsh discovers inside a project are ones the Codex target already writes:

  • InstructionsAGENTS.md and CLAUDE.md, found by walking up from the session's working directory to the nearest .git ancestor. That is dsh's agent-instructions plugin, whose projectRootMarkers default to ['.git'].
  • Skills<projectRoot>/.agents/skills/<name>/SKILL.md. dsh's filesystem skill provider resolves <projectRoot>/.dsh/skills at rank 100 and <projectRoot>/.agents/skills at rank 200, parsing single-level <name>/SKILL.md bundles with a kebab-case name and YAML frontmatter carrying name and description — which is what the Codex target emits.

Where it stops. dsh does not discover .agents/agents/*.md or .agents/workflows/*.md. Its subagents are programmatic providers registered on ctx.subagents in the plugin tree, not markdown found on disk. Specnaut's agent fleet does not cross over; skills and instructions do.

Read this as interoperation by convention rather than as a supported harness. The compatibility is incidental — it follows from .agents/ having become a convention shared across vendors, not from an integration Specnaut maintains or tests against a real dsh install. dsh is itself in developer preview, and its README warns of compatibility-breaking changes, so this note can go stale without anything here changing.

Project-specific skill overlays

Specnaut's skill folders are plain markdown — anything you put under your harness's skills/ directory (e.g. .claude/skills/<name>/, .cursor/skills/<name>/) is a skill, full stop. To make the common "override an upstream skill" pattern discoverable, Specnaut recognises two optional fields in SKILL.md frontmatter:

Field Meaning
alias_of: <skill-name> This skill is a thin wrapper that delegates to the named upstream skill. Dotted notation (e.g. ship.tag) makes the distribution explicit.
overlays: A list of pre/post hooks. Each entry carries when: before | after and path: ./scripts/<file>.sh relative to the SKILL.md.

The Specnaut binary itself never resolves or dispatches aliases / overlays — the harness (Claude Code, Cursor, Codex, …) is responsible for honouring the frontmatter at invocation time. Specnaut's role is to standardise the contract.

The alias and overlay fields are declared in a skill's own frontmatter, so what is installed and what wraps what is readable directly from the files in your harness's skills directory. Skills without alias_of are plain skills; those with it name their target.

A reference example lives at templates/core/skills/alias-example/SKILL.md in the Specnaut source tree. It is not installed by specnaut init — copy it manually when you want to introduce your first alias.

What makes Specnaut different from upstream Spec Kit

Specnaut is a fork of the official specify CLI with the following additions:

1. Auto-chained pipeline

The generated /specnaut skill chains plan → tasks → implement → review → merge in a single session. Upstream stops at every step and asks the human to invoke the next one.

/specnaut plan "<feature description>"

That one command carries you to a reviewed branch. When the idea is still fuzzy and you cannot yet write that one-line description, say so anyway — plan opens with a short discovery dialogue (one question at a time, 2–3 genuinely different shapes) and then writes the plan in the same turn. There is no separate step to remember.

There are exactly two stops, and no third

  • STOP 1 — the end of plan. Always, not only when something is ambiguous. You are shown the architecture as a proposal with the alternatives that were rejected and why, both audits' findings separately, and the open questions one at a time. A single option presented as settled gets approved by default, which is the same as not asking.
  • STOP 2 — the review verdict, which is the merge request. There is no separate pre-merge prompt: the summary (files changed, tests, open risks, business outcome) and Ready to merge? are the same moment.

Every other boundary is crossed automatically, in the same turn. merge is never automatic — but if you already said to merge, that is your instruction and it is not re-collected.

Only a CRITICAL or HIGH finding buys another fix cycle; MEDIUM and LOW go to the backlog and the branch ships. Those cycles run inside STOP 2 without asking you again between each one.

One planning document, and what makes it binding

A feature produces exactly two files: plan.md and tasks.md. There is no spec.md, research.md, data-model.md, quickstart.md or contracts/ — eight documents per feature was eight chances for two of them to disagree, and the disagreement was always found by whoever was implementing.

plan.md has twelve mandatory sections, and the one the phase exists for is the decision table:

The decision Its single home What would duplicate it
the rule, in your words one file path — never a layer the shapes a second spelling takes

Every requirement that is a rule gets a row. The third column is the one a reviewer greps for, and writing it is what makes you notice that a schema constraint and an application check are two spellings of one rule. The table is binding on the implementer: a decision may not move out of its home without the plan being amended first, so a review finding that a decision has two homes is a plan violation rather than a style opinion.

The plan is audited before a line of code exists

plan dispatches the architecture-auditor and security-auditor agents on the plan itself, in the same message so they run concurrently. They judge different things: the architect asks whether a rule has one home, the security seat asks whether that home is reachable by someone who should not reach it.

Their findings go into plan.md — either the plan changes, or it records why the objection was accepted. A clean verdict is written down with its coverage, because a clean verdict is worth exactly what it covered.

This is what replaced the old cross-artefact consistency check. With one document there are no artefacts left to hold in agreement, and the trade is a good one: architecture found at review time is architecture rebuilt, whereas a plan-time finding costs an edit. Security findings are the most expensive class to fix late — a missing authorization gate is one line, but a data model that made the gate impossible is a migration, a backfill and every caller.

Linking a feature to a backlog issue

Pass --issue <id> to /specnaut plan (or to the bundled create-new-feature.sh) to record the originating backlog issue in .specnaut/feature.json:

/specnaut plan "Fix the off-by-one in pagination" --issue 42

After /specnaut merge fast-forwards the branch onto main and you push, the merge phase reads feature.json.linked_issue, runs cascade-check.sh (github / gitlab) to confirm no sub-issues block the close, asks Close issue #42 on the board now? (yes/no), and on yes flips the project column to Done via move.sh then dispatches the product-owner agent to post a close comment with the merged commit range and gh issue close --reason completed. The board stays in sync with main instead of drifting.

--issue is opt-in; existing feature trees without the field skip the auto-close silently.

Running a single phase

--manual is the only chain flag:

/specnaut plan --manual "<feature description>"

Mid-chain re-entry needs no flag

Any phase can enter the chain when invoked mid-flow — useful after a long session or a fresh shell. The behaviour is inferred from what is on disk: if the downstream artefacts under .specnaut/specs/<feature>/ are missing, the chain fires; if they exist, the invocation is a single-phase re-run, so regenerating plan.md does not accidentally cascade through the rest.

/specnaut implement N     # picks up the tail: → review → STOP 2

2. review phase post-implement

After implement, the generated workflow runs a dedicated review phase that checks structure (architecture boundaries, silent error swallowing, leaked internal IDs, cache layering, test coverage) and the quality gates (format, lint, typecheck, tests). If review flags something, the loop is implement → review → fix → re-review — also automatic.

3. Developer agent doctrine

Every scaffold ships a developer agent that implements tasks from tasks.md. The agent operates under a strict doctrine that applies to every task, regardless of project stack:

Domain Model gate (NON-NEGOTIABLE) — before writing a single line of code, the developer reads the domain model in plan.md's technical-context section (spec path) or in the Product Owner's /board brief output (direct-implementation path). If it is absent, empty, or still contains template placeholders, the agent halts and returns BLOCKED with reason awaiting:product-owner-domain-brief. The implement phase enforces the same gate and surfaces the same BLOCKED report, recommending a re-run of /specnaut plan to fill it. The plan phase is responsible for populating the full block (Bounded context, Vocabulary, Entities, Value objects, Invariants) rather than just listing key entities — and its stop is where you are asked about anything it could not settle.

Test-Driven Development (NON-NEGOTIABLE) — red → green → refactor on every implementation. No business logic ships untested. If the project has no test infrastructure, the developer bootstraps the language-idiomatic test runner (Vitest for TS/JS, Pytest for Python, JUnit for Java, go test for Go, cargo test for Rust, PHPUnit for PHP, RSpec for Ruby, etc.) as part of the task and records it explicitly in the Decisions block of the completion report.

Domain-Driven Design (NON-NEGOTIABLE) — every change respects the project's domain boundaries. Domain layer stays pure (no I/O, no framework). Application layer holds use cases and ports. Infrastructure layer holds adapters. Presentation talks only to use cases. Cross-bounded-context bleed-through is forbidden — split or use an anti-corruption layer.

Boy Scout Rule with escalation — small in-scope cleanups (≤ 1 file, ~15 lines of diff, no public API change) are done in the same PR and noted in Decisions. Larger out-of-scope cleanups are logged in a Tech debt surfaced block of the completion report rather than ballooning the PR. The Product Owner reads that block and opens a classified tech-debt ticket (tech-debt label, default Size XS/S, Priority P3, bumped to P2 on correctness/security risk) for each item.

SOLID / DRY / KISS / YAGNI — explicitly required. Framework-specific patterns (Repository, DI, React hooks, MVC controllers) come from the constitution's Back-end and Front-end pattern blocks. No silent catches — every catch either logs at ERROR/WARN or re-throws. In-code documentation — doc-comments on every function, method, or class encoding a business rule or non-obvious design decision, in the idiomatic format for the language.

4. Backlog as product source of truth

A Product Owner agent gates every mutation, and supports three backends:

  • Local Markdown (--backlog local, default) — index at .specnaut/backlog.md, task files at .specnaut/backlog/NNN-slug.md (typed frontmatter: id, title, category, priority, complexity, status, parent, depends_on, spec, tags, created). Sub-tasks reference their parent via parent: "#NNN".
  • GitHub Issues + Projects (--backlog github) — the agent talks directly to the backend via gh CLI; epics use the native sub-issues API. Read paths use gh issue list/view --json projectItems (REST-ish CLI projection of Project V2 fields, ~1–2 GraphQL points per call), and raw gh api graphql is reserved for the one operation with no CLI equivalent (gh project item-edit's underlying updateProjectV2ItemFieldValue mutation). Keeps backlog grooming under the shared 5,000-points-per-hour GitHub API quota. No local mirror, no sync command — the remote is the source of truth.
  • GitLab Issues (--backlog gitlab) — the agent talks to GitLab via glab CLI. Status is tracked via scoped Status::* labels rather than a native column field; sub-tasks use a parent::#NNN scoped label (Free-tier compatible — native GitLab Epics are Premium-only). Otherwise the model mirrors the GitHub backend (no local mirror, no sync command).

The user picks one backend per project. The chosen backend is recorded in .specnaut/installed.lock so the PO knows which one to use without auto-detection.

Semantic label bootstrap. For GitHub and GitLab backends, specnaut init scaffolds .specnaut/scripts/backlog/ensure-labels.sh. Run it once to seed seven canonical labels — security, refactor, docs, tech-debt, dx, performance, dependency — into the remote repo. Idempotent; never edits or deletes existing labels. The GitHub default bug label is verified but never re-created. The full reference lives in .specnaut/LABELS.md next to the install — including a guidance note for local backend users on tagging via task-file frontmatter.

Mandatory classification — every groomed item is sized, prioritised, typed, and labelled. The PO classifies every item it creates or clarifies along four axes — Size, Priority, Issue Type (Task / Bug / Feature), and at least one label — before the item is done; classification is a gate, not optional polish. On a GitHub project with native Priority / Size single-select fields and native Issue Types, the bundled .specnaut/scripts/backlog/set-field.sh <issue> <Priority|Size|IssueType> <value> writes each to its native field or type — and the PO never also applies a priority:* / size:* / type:* label on an item that already carries the native value. Labels are a strict fallback for projects or orgs without the native field/type; they are not a peer signal. set-field.sh exit codes tell the caller which path applies: 0 = set, 10 = field/type absent (fall back to label), 11 = value unrecognised, 12 = issue not on the project / not in the repo. detect-fields.sh (run once per groom) emits the field/option IDs into env vars for case-insensitive matching. On GitLab the four axes are scoped labels via glab; on the local Markdown backend they live in task-file frontmatter.

Bounded context (soft fifth axis) — a domain:<context> label (e.g. domain:checkout) is optional on mono-domain projects but the ## Domain Model block in every /board brief output is always mandatory. Items touching ≥ 2 bounded contexts automatically trigger the epic detection heuristic with reason "cross-bounded-context".

/board brief — Domain Model is mandatory. Every brief the PO generates for a developer MUST include a ## Domain Model block with: Bounded context, Vocabulary (ubiquitous language), Entities (with aggregate root flag), Value objects, Invariants, and Out of scope. A brief without this block is incomplete — the PO clarifies with the user before issuing it. If a plan.md is attached, the block is written into its technical-context section too (the plan template carries it).

Tech-debt intake protocol. When a developer's completion report carries a Tech debt surfaced block, the PO parses it, deduplicates against the current backlog, and opens one classified ticket per surfaced item: tech-debt label, default Size XS or S, Priority P3 (bumped to P2 when the item involves correctness or security risk). This is automatically triggered — no manual step required.

Epics & sub-tasks. Big work that needs decomposition lives as a parent epic with one or more sub-tasks. The link mechanism differs per backend, but the contract is the same: parents cannot close while any child is still open.

Backend Parent → child link
local parent: "#NNN" in the child's frontmatter, plus a ## Sub-tasks cross-link in the parent file
github Native sub-issues API — children render automatically under the parent's "Sub-issues progress" field on Project V2
gitlab Scoped label parent::#NNN on the child (Free-tier compatible)

Create a child on any backend with the bundled add.sh --parent <num> flag — the script writes the link, attaches to the project/board, and refuses (exit 3) when the named parent doesn't exist:

.specnaut/scripts/backlog/add.sh "Child title" "Child body" "" --parent 42

The bundled cascade-check.sh <num> (github + gitlab) is the close gate — exits 11 with the open children listed when close is unsafe, exits 12 (informational) when the parent is already closed so callers don't issue a redundant close and 422, exits 3 when the parent doesn't exist, exits 0 when all children are closed. The PO runs it before gh issue close / glab issue close. The local backend uses an inline grep equivalent.

A companion propagate-parent-status.sh keeps the parent's board column honest as children move: a child entering In progress or In review promotes a stalled parent (Backlog or Ready → In progress), and once every child reaches Done the parent rolls up to Done. A child moving to Ready is deliberately a no-op — Ready means groomed-and-waiting, not active work, so it must not promote the parent.

The Product Owner agent proactively proposes epic decomposition during /board add and during grooming whenever a request crosses ≥2 subsystems, has more than 5 acceptance-criteria bullets, or carries trigger phrases like "break down", "phased", "rewrite", "end-to-end". Obvious splits get auto-created; ambiguous ones get a concrete sub-task list back as a question. You don't have to ask for the breakdown — the PO surfaces it on its own.

5. Claude Code plugin distribution

Specnaut ships a first-class Claude Code plugin (specnaut-plugin) available via the Claude Code marketplace:

/plugin install specnaut/specnaut-cli-plugin

The plugin gives any Claude Code user instant access to the full Specnaut slash-command suite and sub-agents — no binary, no specnaut init required. The plugin assets (the consolidated specnaut router skill with its phase docs including the five audit-* axes — security / performance / accessibility from Epic #302 and architecture / dependencies from Epic #320, the specnaut-review auto-invoke alias, and 15 sub-agents including the manual-only performance-auditor, a11y-auditor, architecture-auditor, and dependency-auditor introduced with the audit family) are namespaced under /specnaut-plugin:* so they coexist with project-local copies without collision.

When both the plugin and the binary are in use, specnaut upgrade detects the plugin and auto-migrates vanilla on-disk agents and command files (backed up, then deleted — the plugin serves them going forward). specnaut check --project warns when covered files are missing and the plugin is not installed, with a recovery hint.

6. Bundled specnaut-expert agent

Every scaffold ships a specnaut-expert agent that knows Specnaut itself — its commands, harnesses, backlog backends, and what changed between releases. It auto-triggers on Specnaut-related questions ("how does specnaut X", "what is /specnaut Y", "quoi de neuf") so users on a Specnaut-scaffolded project can ask the harness about the tool without copy-pasting docs. It uses a vendored knowledge snapshot for offline / deterministic answers and WebFetch against https://specnaut.com/llms.txt + the GitHub Releases API for live "what's new" queries. Manual dispatch via /specnaut-expert <question> is also supported.

The agent also handles bug reports: ask "report this as a bug" (or hit a Specnaut failure) and it pre-fills a structured GitHub issue against specnaut/specnaut-cli with a 6-section template (Summary / Repro / Observed / Expected / Environment / Logs), auto-populating the environment block from .specnaut/installed.lock + specnaut --version + uname -srm, scrubbing common token shapes (GitHub PATs, GitLab PATs, Anthropic / OpenAI keys, AWS access keys), and handing you a pre-filled https://github.com/specnaut/specnaut-cli/issues/new?… URL to review and submit. The agent never auto-submits — you always see the body before clicking.

7. Bundled security-auditor agent — two modes

Every scaffold also ships a security-auditor agent with two dispatch shapes:

  1. PR review — spawned by the review-coordinator during /specnaut review. Audits the diff against eight rules (secrets in source, input validation, authz, injection, path traversal, SSRF, silent catches, internal-ID exposure) and emits a FINDING / VERDICT report.
  2. Alert triage — invoked by the maintainer's /release flow when the security-preflight workflow surfaces open GitHub-side alerts (secret-scanning, dependabot, code-scanning, private advisories). The agent decides per-alert: open a backlog ticket via the PO, dismiss via gh api -X PATCH with a documented resolution= reason, or escalate to the user.

The triage mode is release-time only and uses a tightly-constrained Bash grant — only the three gh api alert-dismissal endpoints are permitted. End users never trigger this mode; PR review remains the user-facing path.

8. Bundled ui-ux-designer agent

Every scaffold also ships a ui-ux-designer agent that owns a single source of truth — the project's DESIGN.md — that every other agent consults to keep generated UI on-brand. Three modes auto-select from DESIGN.md state:

  1. Discovery — when DESIGN.md is absent. The agent runs a 2-4 question interview (project + audience, visual mood, brand seed, optional stack hint) and writes a complete first DESIGN.md from a canonical template covering typography, palette (light + dark with WCAG-AA contrast rules), 4-point spacing scale, radius / shadow tokens, component primitives, and motion.
  2. Edit — when DESIGN.md is present and the dispatch is a refactor request. The agent edits the spec in place with a one-line rationale per change and a Decision-log append.
  3. Audit — when the dispatch contains the word audit. The agent scans **/*.{tsx,jsx,vue, svelte,html,css,scss} under src/ for literal hex colours, off-system fonts, and off-grid spacing values, reports drift in a | File | Line | Found | Expected token | Severity | table, and emits clean / drift_minor / drift_major.

The agent is manual-dispatch only (disable-model-invocation: true) — design decisions are intentional and the agent never auto-runs. It produces Markdown, never code; the developer agent is what translates DESIGN.md into a Tailwind theme, CSS vars, or component library. DESIGN.md is NOT scaffolded by specnaut init; it materialises on the agent's first invocation when the user actually wants a design system, so backend-only and CLI-only projects don't carry stub spec files they never read.

Design principles

  • Agnostic of the user project's language — Python, TypeScript, Go, PHP, Rust… your project, your stack.
  • Agnostic of the LLM — Claude, OpenAI, Gemini, local models, anything your harness supports.
  • Agnostic of the AI harness — eight first-class targets today, with the same core content for all.
  • Agnostic of the backlog source — pick local Markdown or your remote tracker (GitHub Issues + Projects, GitLab Issues; Bitbucket planned). The PO agent talks to whichever you chose.
  • Single binary — distributed via deno compile for macOS arm64/x64, Linux arm64/x64, and Windows x64. No Python, no pip, no extra runtimes on the user's machine.

Contributing

Agent adoption

Every feat: PR body must include an ## Agent adoption section with a ```prompt fenced block. The release pipeline extracts these into a structured ### Adoption guide block on the GitHub Release; specnaut-expert review-upgrade plays them back in the user's project after specnaut upgrade.

See the CONTRIBUTING guide for the convention and examples. The CI workflow pr_adoption_lint.yml in the CLI repo enforces presence.

Release notes shape

GitHub Releases bodies are auto-generated by scripts/gen-changelog.ts and follow this structure:

  1. ### Featuresfeat: commits, one bullet per commit.
  2. ### Bug fixesfix: commits.
  3. ### Adoption guide — one block per feat: PR that has a ## Agent adoption section in its PR body. Format:
**#NNN — Feature title**

<prose paragraph from the PR body>

```prompt
<ready-to-paste adoption prompt>
```

Consumed by specnaut-expert review-upgrade (Phase 4 of the upgrade adoption flow).

  1. ### Internal / choreschore: / refactor: / docs: / test: etc., collapsed under a <details> block.

If a feat: PR is merged without ## Agent adoption, gen-changelog.ts emits a stderr warning during the release workflow. The release still ships; the missing entry can be amended manually in the GitHub Release body.

Upgrades & adoption

specnaut upgrade updates templates in place and prints a handoff line inviting the user to review what changed via the specnaut-expert agent.

Upgrading from 1.x to 2.x

2.0.0 is a breaking release. The canonical migration guide is UPGRADING.md in the CLI repository — read it before running specnaut upgrade on a project scaffolded with 1.x. It is kept there rather than duplicated here so the two cannot drift apart.

What changes, at a glance:

Before After
9 chainable phases 5plan → tasks → implement → review → merge
5 chain flags 1--manual
up to 8 files per feature 2plan.md + tasks.md
stops whenever clarification is needed, then pre-merge exactly 2 — end of plan, and the review verdict

brainstorm, specify, clarify, analyze, checklist and list-skills no longer exist; an old phase name prints the phase index and stops. analyze was replaced, not dropped — with a single planning document there are no artefacts left to hold in agreement, so its successor is a binding decision table plus architecture and security audits run against the plan before any code exists.

Your existing spec directories are left on disk untouched. Nothing migrates them.

Files written

  • .specnaut/upgrade-pending.json — a marker recording the upgrade range:
{
  "from": "1.4.0",
  "to": "1.6.0",
  "at": "2026-05-16T14:33:00.000Z"
}

Written on every successful apply. On chained upgrades, the existing marker's from is preserved. Consumed by specnaut-expert review-upgrade and by specnaut reconcile. Deleted by the agent at the end of a successful review.

  • .specnaut/upgrade-staging/<path> — for every file the upgrade preserved (i.e., on-disk version was customized vs. lock SHA), the upstream (bundled-template) version is written here under the same relative path. The on-disk project file is untouched. The staging directory is the source for specnaut reconcile (see below).

Both are gitignored (templates/core/root/.gitignore ships the lines).

Handoff line

specnaut upgrade ends with:

✓ upgraded to templates 1.4.0 → 1.6.0

→ Walk through what's new with your AI:
`@specnaut-expert review-upgrade`

An AI agent that sees .specnaut/upgrade-pending.json in a project should proactively suggest running @specnaut-expert review-upgrade.

specnaut reconcile

Per-file post-upgrade reconciliation. Run after specnaut upgrade for each file that was preserved (customized locally — see the Upgrades & adoption section for context).

specnaut reconcile --status
Print JSON listing files currently pending reconciliation. Reads
`.specnaut/upgrade-staging/`. Output:
{
  "pending": [".claude/agents/developer.md", ...],
  "stagingDir": ".specnaut/upgrade-staging" | null
}

specnaut reconcile <path> --accept-upstream
Take the new template version for <path>. Backs up the local file to
`<path>.specnaut.bak`, copies upstream content from
`.specnaut/upgrade-staging/<path>` into place, and updates the lock
SHA. Removes the staging entry.

specnaut reconcile <path> --accept-current
Keep the local customized version. Re-stamps the lock SHA to match
on-disk content, so the next upgrade does not re-flag this file as
preserved. Removes the staging entry.

specnaut-expert review-upgrade is the recommended way to walk through reconciliation interactively — it surfaces a keep / take / merge / view / skip choice per file and dispatches the developer subagent for intelligent merges.

specnaut-expert review-upgrade

Dispatching @specnaut-expert review-upgrade triggers a 7-step guided workflow inside the specnaut-expert agent:

  1. Read marker — reads .specnaut/upgrade-pending.json; exits with instructions if absent.
  2. Fetch releases — fetches GitHub Release bodies for every tag in (from, to] and parses each ### Adoption guide section into structured adoption prompts. Falls back to the vendored snapshot if the GitHub API is unreachable.
  3. Present plan — shows releases in range, adoption prompts count, and files pending reconciliation (specnaut reconcile --status). Offers to create a branch specnaut-upgrade-v{to} for review-as-PR.
  4. Walk adoption prompts — presents each prompt one by one with four options: [a] run it (dispatches developer agent), [s] skip, [c] show raw prompt, [q] quit.
  5. Reconcile customized files — for each file pending reconciliation, shows a diff summary and offers [k] keep local, [t] take upstream, [m] intelligent merge (dispatches developer), [v] view full diff, [s] skip.
  6. Cleanup — when both walks complete with nothing skipped, deletes the marker and (if on the review branch) commits a summary. Skipped items are left on disk for the next review-upgrade run.

Trigger keyword: review-upgrade in the dispatch message.

Repository

Source, releases, and issue tracker: github.com/specnaut/specnaut-cli.

The AGENTS.md file at the repo root is the canonical context document for any future Claude Code, Codex, or other agent session contributing to the project itself.

Recent releases

  • v4.3.0Releases are signed now, and self-update verifies before it replaces the binary.
  • v4.2.6The epic close-gate now tells you which thing went wrong.
  • v4.2.5A shipped feature's spec directory is now deleted when its issue closes at merge.
  • v4.2.4An epic whose last child finishes now moves to In review, not Done.
  • v4.2.3Four gates that answered "fine" when they had not looked.