diff --git a/docs/guides/monorepo.md b/docs/guides/monorepo.md new file mode 100644 index 0000000000..507d2dc3fe --- /dev/null +++ b/docs/guides/monorepo.md @@ -0,0 +1,110 @@ +# Using Spec Kit in a Monorepo + +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. + +## Layout + +```text +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: + +```bash +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-…`). + +## Working inside a member project + +The default workflow is unchanged: change into the project directory and run the +slash commands. Root resolution finds the nearest `.specify/`. + +```bash +cd apps/web +# then run /speckit.specify, /speckit.plan, … in your agent +``` + +## Targeting a member project from the repo root + +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. + +```bash +# from the monorepo root, operate on apps/web without cd-ing in +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: + +```text +# 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](../reference/core.md#environment-variables) for +the full contract and the two-axes model. + +## How `SPECIFY_INIT_DIR` reaches your agent + +`SPECIFY_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. + +- **Scripted / CI runs:** export it in the same shell that drives the commands; + it is reliable there. +- **Interactive agents:** whether an exported variable reaches the shell tool an + agent uses is agent-specific. Export `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/`). + +## Git in a monorepo + +> [!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. + +## Constitutions + +Each member project has its own `.specify/memory/constitution.md` and +`/speckit.constitution` edits the local project's file. There is no base/inherit +mechanism today, so shared engineering rules must be duplicated per project or kept +out of the constitution. diff --git a/docs/toc.yml b/docs/toc.yml index aba93ab432..711abb3375 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -53,6 +53,8 @@ href: local-development.md - name: Evolving Specs href: guides/evolving-specs.md + - name: Monorepos + href: guides/monorepo.md # Community - name: Community