docs/guides/monorepo.md
A Spec Kit project is directory-scoped: the project is whichever directory
contains .specify/. A monorepo can hold several independent Spec Kit projects
under one repository root, each with its own .specify/, specs/, constitution,
and feature numbering.
Root resolution already prefers the nearest .specify/ over the Git
toplevel, so commands run from inside a member project resolve to that project,
not the repo root.
my-monorepo/
├── .git/ # one Git repository at the root
├── apps/
│ ├── web/
│ │ └── .specify/ # Spec Kit project "web"
│ │ └── memory/constitution.md
│ └── api/
│ └── .specify/ # Spec Kit project "api"
│ └── memory/constitution.md
└── packages/
└── ui/
└── .specify/ # Spec Kit project "ui"
Initialize each member project independently:
specify init apps/web --integration claude
specify init apps/api --integration claude
Each project keeps its own specs/ directory and numbers features
independently (apps/web/specs/001-…, apps/api/specs/001-…).
The default workflow is unchanged: change into the project directory and run the
slash commands. Root resolution finds the nearest .specify/.
cd apps/web
# then run /speckit.specify, /speckit.plan, … in your agent
For non-interactive or CI runs where you do not want to cd, set
SPECIFY_INIT_DIR to the member project root (the directory containing
.specify/). Relative paths resolve against the current directory.
# operate on apps/web from the monorepo root (no cd required)
export SPECIFY_INIT_DIR=apps/web
The path must exist and contain .specify/. If it does not, the command
errors and does not fall back to the current directory or the Git toplevel.
This is deliberate: a typo never writes specs into the wrong project. A
nonexistent path is reported as you typed it; a path that exists but is not a
Spec Kit project is reported as its resolved absolute path:
# SPECIFY_INIT_DIR=apps/wbe (typo: no such directory)
ERROR: SPECIFY_INIT_DIR does not point to an existing directory: apps/wbe
# SPECIFY_INIT_DIR=apps (exists, but has no .specify/ of its own)
ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): /home/you/my-monorepo/apps
SPECIFY_INIT_DIR selects the project; SPECIFY_FEATURE_DIRECTORY selects
the feature within it. They compose: set both to pick a project and a
feature non-interactively. See the
SPECIFY_INIT_DIR reference for
the full contract and the two-axes model.
SPECIFY_INIT_DIR reaches your agentSPECIFY_INIT_DIR is read by the shell scripts that the slash commands invoke
(get_repo_root in Bash, Get-RepoRoot in PowerShell). It takes effect only
when it is present in the environment of the shell that runs those scripts.
SPECIFY_INIT_DIR before launching the
agent, and verify once (e.g. run /speckit.specify and confirm the new feature
landed under the intended project's specs/).[!NOTE] Spec Kit project files are scoped to the resolved project root, but Git operations still run in the containing Git work tree. In a monorepo with a single Git repository at the root and projects in subdirectories, feature branch creation creates or switches branches in the shared root repository. Spec directories still live under the selected member project, while the Git branch namespace is shared by the whole monorepo. Manage branches and commits at the repository root, or initialize Git per member project if you want isolated per-project branch namespaces.
Each member project has its own .specify/memory/constitution.md and
/speckit.constitution edits the local project's file. Spec Kit does not provide
a built-in base/inheritance mechanism; if you want one constitution to reference
shared rules elsewhere in the monorepo, you need to maintain that wiring yourself.
Otherwise, duplicate or sync shared engineering rules per project.