Documentation: cli.specs.dev
A general-purpose developer CLI for scaffolding projects from templates. Define variables, write template files, run hooks — specs handles the rest.
Homebrew (macOS):
brew install specsnl/tap/specsFrom source:
go install github.com/specsnl/specs-cli@latestDownload a binary from the releases page.
Use a template directly without registering it first:
specs use github:specsnl/my-template ./my-projectOr register a template and reuse it later:
specs template download github:specsnl/my-template my-template
specs template use my-template ./my-projectYou can also register a local directory as a template:
specs template save ./my-template my-templatespecs template list shows an update Status for each registered template:
- Remote templates (from
download) are checked against their git remote. - Local templates (from
save) are checked against their source directory on disk —update availablemeans the source path has moved ahead of what was saved (source missingif that path is gone). Uncommitted changes on the saved commit (a "dirty" working tree) are not treated as an update, so a dirty source is not reported as perpetually out of date.
specs template upgrade [name] applies available updates: remote templates are re-cloned, local
templates are re-copied from their source path. Cached statuses refresh automatically once older
than 24 hours or when written by a different specs version.
A template's project.yml declares its variables, defaults, computed values, and hooks. A few __-prefixed keys are reserved by specs and never exposed as template variables:
| Key | Purpose |
|---|---|
__delimiters |
Override the default {{ }} template delimiters with a custom pair (e.g. [[ ]]). |
__specs__version |
Declare a semver constraint on the specs CLI version required to use the template, e.g. __specs__version: ^0.1.0. specs use and specs template use refuse to run the template unless the running binary satisfies the constraint (development builds are exempt; specs template save skips the check). |
See the documentation for the full project-file reference.
The repo ships a Dockerfile and a compose.yml that together define a self-contained build and test environment. Contributors don't need a local Go installation — all builds and tests run inside a Docker container that pins the exact Go version and tooling.
| File | Role |
|---|---|
Dockerfile |
Defines the build image — Go 1.26 + tooling, used by task build and task test |
compose.yml |
Wires the Dockerfile stages into named services consumed by the Taskfile |
Taskfile.dist.yml |
Orchestrates all developer workflows; wraps Docker Compose so you never call it directly |
Requirements: Task and Docker.
Build the images once before running any task:
task dc:buildThen use the standard tasks:
task build # Build the binary for the current platform
task test # Run unit testsList all available tasks:
task --listtask dc:build builds all Docker Compose services in the build profile. The key service is go-builder, built from the builder-download stage of the Dockerfile. It mounts the repository root and two Docker volumes — one for the Go module cache and one for the build cache — so subsequent runs are fast.
task test and task build spin up a one-off go-builder container (docker compose run --rm), run the Go command inside it, then discard the container. The service doesn't need to be started in advance — it is ephemeral by design.
task build also invokes docker buildx bake using the go-binary service to produce a statically linked binary and copy it out of the image into the project root.
If you already have Go 1.26+ installed locally, you can bypass the container entirely:
go build ./...
go test ./...CI always runs through Docker and the Taskfile. The container is the source of truth for reproducible builds.
All CI and agent workflows follow the same rule: use task commands, never call docker compose directly. See .github/instructions/executing-commands.md for the authoritative execution rules.
MIT — see LICENSE.