Developing presets
A preset packages custom document templates, agent instructions, and scripts
in a directory with a preset.yml manifest. Prefer a template contribution
when only the output structure changes; override a command when the process
itself needs different instructions. For a one-off project change, a
project-local override may be simpler than a distributable preset.
Start from the scaffold
Copy the repository's preset scaffold to a directory you control:
cp -r presets/scaffold my-preset
Edit its ID, name, version, description, required Spec Kit version, and contributions. Remove unused examples, including example extension command overrides, rather than shipping them accidentally.
A typical package contains:
my-preset/
preset.yml
README.md
LICENSE
CHANGELOG.md
.presetignore
templates/
spec-addendum.md
commands/
speckit.specify.md
scripts/
my-helper.sh
Only include directories used by your preset. The manifest's
provides.templates list contains contributions of all three types, not
just document templates. The file field can point to a non-conventional
path; use name to identify the logical artifact being customized.
Declare contributions
For example, append a traceability section to the effective specification template:
schema_version: "1.0"
preset:
id: "team-traceability"
name: "Team Traceability"
version: "1.0.0"
description: "Adds team traceability requirements to specifications"
author: "Example Team"
repository: "https://github.com/example/team-traceability"
license: "MIT"
requires:
speckit_version: ">=0.9.0"
provides:
templates:
- type: "template"
name: "spec-template"
file: "templates/spec-addendum.md"
description: "Traceability section"
strategy: "append"
tags:
- "traceability"
- "governance"
Keep headings clear and placeholders explicit. If a command references a template section, ensure the effective template actually contains it. Declare only contributions you need so the preset composes with others.
For wrap, the content file must contain {CORE_TEMPLATE} for templates
and commands, or $CORE_SCRIPT for scripts. These placeholders stand for the
resolved lower-priority content, not necessarily unmodified core content.
An append, prepend, or wrap contribution needs a lower replace base;
a new self-contained command normally uses replace.
Declare extension dependencies in preset.yml when the preset relies on
extension capabilities. A namespace alone does not establish a dependency
or suppress registration. See the API reference for warning behavior and
optional/version-constrained dependencies.
Exclude development files with .presetignore
Place a UTF-8 .presetignore, with or without a byte-order mark, beside
preset.yml in the source root:
.git/
.idea/
tests/
Filtering applies to copied local directories and extracted archives,
including replacements installed by preset update. Without this file,
all source files are copied as before. .gitignore is not used for
installation filtering, and there are no implicit development-file
exclusions.
Patterns use Git-style matching and normalization, as with
.extensionignore:
- Use one pattern per line; blank lines and
#comments are ignored. *and?do not cross directory boundaries;**can cross them.- A trailing
/matches directories only. A leading/, or a/elsewhere except at the end, anchors a pattern to the source root. Patterns without such a slash match names at any depth. - A later
!pattern re-includes a match. Excluded directories are not traversed, so a child cannot be re-included while its parent is excluded. - Whitespace is trimmed and backslashes are normalized to
/. Use UTF-8 for non-ASCII names.
To keep only docs/api.md inside docs/, exclude the contents rather than
the directory:
docs/*
!docs/api.md
Using docs/, then !docs/ and !docs/api.md, re-includes all files under
docs/ unless another rule excludes them.
Only the source root's .presetignore adds rules; nested ignore files are
not evaluated. If a root ignore file exists, files named .presetignore at
any depth are excluded even when negated. Without a root ignore file,
nested .presetignore files are copied unchanged.
Manifest and declared asset files are not exempt. Keep preset.yml and all
required contribution files included; filtering them out can produce an
incomplete or unusable installation.
The directory installer validates ignore rules before removing an existing
destination, including forced replacement through the Python manager API.
Invalid UTF-8 and malformed patterns raise a preset validation error.
The CLI's separate update command still removes first, then invokes add;
it does not preflight the replacement ignore file.
Test locally
Use an initialized test project with the integration and command/skill mode your preset supports. From that project:
specify preset add --dev /path/to/my-preset --priority 5
specify preset info team-traceability
specify preset list
specify preset resolve spec-template
specify artifact info template:spec-template --json
Confirm the contribution is present in generated artifacts, not just in the installed directory. For command overrides, inspect the active integration's delivered command or skill and invoke it with that agent. Test relevant formats and modes rather than assuming every integration uses the same file layout.
Test overlapping presets at different priorities and equal priorities, project-local overrides, removal/restoration, and missing dependencies or composition bases. If you ship executable scripts, test their intended behavior and failures; PowerShell scripts must target Windows PowerShell 5.1. Do not expect enable, disable, or priority changes alone to rewrite already delivered command or skill files.
After editing the development source, install a fresh copy. --dev does not
watch or link it:
specify preset remove team-traceability
specify preset add --dev /path/to/my-preset --priority 5
The update --dev alternative has deliberate remove-then-add semantics and
can leave the preset removed if the new source is invalid. Test archive
installation too before publishing, especially if packaging changes the
directory layout.