spex-init¶
spex init [targetDir] (default: cwd) bootstraps a fresh repo into SpexCode. Adoption is data, not
code: every prompt/contract the command plants is a real spec.md shipped as a template file and
copied — no prompt string is ever embedded in the CLI source. The seed is therefore edited the same
way any spec is: by editing the template files, not the code.
What it plants, both resolved from the CLI package's OWN location via import.meta.url (so init works
when the package is installed outside the dogfood repo — never a hardcoded repo path):
- The seed spec tree —
templates/spec/*copied into<dir>/.spec/: a rootprojectnode plus a default.configof dev-flow plugins, each a flat child carrying asurfacefield (thesystemcontractcore+forge-link, and thecommandpresets), a verbatim copy of the dogfood.confignode so a fresh adopt ships the current set. That default.configis the default preset; with--preset <name>the named non-default package undertemplates/presets/<name>/is copied in on top — cumulative, socarefulstacks its plugins over the default set. The spexcode-only plugins live only in the dogfood.config, never in the template, so they are never seeded. init-preset owns which sets exist; this command owns the copy. - The git hooks —
templates/hooks/*(the main-guard pre-commit and session-stamp prepare-commit-msg) copied into the target's resolved common hooks dir. This is the one canonical hook source:scripts/install-hooks.sh(the monorepo'snpm run hooks) installs the very same files, so the two paths can't drift (see main-guard). They ship inside the package so a relocated install still carries them. - A starter
spexcode.json—templates/spexcode.jsoncopied to<dir>/spexcode.json. Without it an adopter inherits SpexCode's own spec-lint defaults, whosegovernedRootsname this repo's dirs; absent in the adopter's tree, lint would silently govern nothing and read falsely-clean. The starter shipsgovernedRoots: ["."]— the zero-config safe default:.governs the whole project, but only git-tracked source (so node_modules/build/nested worktrees never count) minus tests, so a fresh repo just works and a mature one can still curate explicit roots.
An illegal harness-target set fails loud, up front. Before rendering, init validates the project's
harness-select harnesses set (from the just-planted/existing spexcode.json) and aborts with a stated
reason on an illegal one — a plugin paired with a native harness, or a plugin with no landing folder — rather
than letting the later materialize swallow it as a soft "skipped" warning. A fresh starter spexcode.json
omits the field (defaulting to every native harness), so this only bites a hand-edited or re-init'd config —
exactly where a clear error belongs.
A git work tree is a precondition, checked first. SpexCode is git-backed — git is the version
database and the hooks live in .git — so a non-git target would leave a half-state: specs on disk but
no history, no hooks, no sessions. init therefore rejects a non-git target before writing anything,
with one actionable error pointing at git init. It deliberately does not run git init itself:
creating a repo is a side effect beyond init's remit (a subdir, a dir not meant as a repo root), and the
repair is one command.
Adoption is additive, never destructive. No existing file is overwritten: an existing <dir>/.spec
aborts the spec phase with a warning, and an existing hook is left untouched. On success it prints the
next steps — install the packages, edit project/spec.md, run the backend, confirm spex lint is clean.