Generated, harness-agnostic workflow gates for spec-kit projects
Project description
speckit-gate
Harness-agnostic workflow gates for spec-kit projects.
Prevents out-of-order spec-kit command invocations by evaluating prerequisite artefacts at hook time. Supports hard-deny enforcement on Claude Code and Codex; advisory-only enforcement on all other spec-kit harnesses.
Install
Option 1 — uvx (one-shot, no install required)
# Scan and initialise gates in the current project
uvx speckit-gate init --defaults
uvx speckit-gate compile
# Install Claude Code hooks
uvx speckit-gate install --harness claude
Option 2 — Claude Code plugin (persistent, recommended for Claude Code)
# From within Claude Code, or via the CLI:
claude plugin install srobroek/speckit-gate
The .claude-plugin/plugin.json in adapters/claude/ wires
UserPromptExpansion, PreToolUse:Skill, and PreToolUse:Agent hooks.
Option 3 — spec-kit bundle (all harnesses, advisory enforcement)
# Add the community bundle catalog and install
specify bundle catalog add https://github.com/srobroek/speckit-gate/releases/latest/download/bundles.json --id speckit-gate
specify bundle install speckit-gate
See Bundle adapter rationale below.
Option 4 — APM external source
# In apm.yml:
dependencies:
apm:
- repo: srobroek/speckit-gate
ref: ">=0.1.0"
apm install
Quick start
# gates.yaml — 10-line example
config:
prefix: speckit.
feature_root: specs
resolve: [git-branch, newest-dir]
gates:
plan:
requires: [specify]
produces: [specs/<feat>/plan.md]
context: Decomposes spec into implementation plan.
tasks:
requires: [plan]
produces: [specs/<feat>/tasks.md]
context: Decomposes plan into discrete tasks.
Compile to nodes.json:
speckit-gate compile
# → Compiled 2 gates → .specify/gates/nodes.json
Agent-driven interview
Use the speckit-gate skill (in skills/speckit-gate/SKILL.md) with
Claude Code or any skill-capable harness. The skill:
- Scans the project for spec-kit commands.
- Shows a full proposal table (known prerequisites pre-filled).
- Asks ONE question per unknown command.
- Writes
gates.yamlviainit --answersand runscompile.
For no-agent setup run speckit-gate init --interactive or
speckit-gate init --defaults.
Harness enforcement matrix
A gate is only as strong as its blindest channel, and every harness differs by invocation channel: what happens when the user types a command vs. when the model (a workflow engine, a subagent) evokes one mid-run. A gate that covers only the user channel is silently bypassed by agent-driven runs — so the matrix is per-channel. All deny rows below are verified against harness source (mid-2026); adapters currently ship for Claude Code and Codex.
| Harness | User invocation | Model/agent evocation | Adapter |
|---|---|---|---|
| Claude Code | deny — UserPromptExpansion, sees command name |
deny — PreToolUse: Skill|Agent |
shipped |
| Qwen Code | deny — UserPromptExpansion, sees command_name (+ UserPromptSubmit post-expansion) |
deny — PreToolUse on skill/agent tools (full prompt); model-invoked commands re-fire UserPromptExpansion. SubagentStart cannot block |
planned |
| Codex CLI | deny — UserPromptSubmit (raw human text; text-match, no command structure; requires [features] hooks = true) |
none — skills inject as context with no event; SubagentStart is metadata-only (continue:false at best). Artifact gates are the backstop |
shipped |
| Gemini CLI | deny — BeforeAgent (expanded template text only — no command name, no slash-origin marker; content-match required) |
n/a — model cannot invoke .toml commands by design |
planned |
| Mistral Vibe | none — slash/skill input expands in the UI layer, invisible to hooks | deny — before_tool on the skill tool (sees skill name; flag: enable_experimental_hooks). Note: skill loads bypass Vibe's user-approval prompt, so this hook is the only programmatic gate |
planned |
| GitHub Copilot | hooks exist; deny semantics unverified | unverified | — |
| Cursor, Zed, Cline, Goose, Devin, Trae, Lingma, Kimi, ZCode, Firebender, Junie, Auggie, + ~14 more | static context only — no hook system | static context only | spec-kit bundle (advisory text) |
Where a channel shows none/static, enforcement falls back to artifact gates: a downstream gated command hard-requires the report file the skipped step should have produced, so out-of-order runs still fail at the next enforceable point.
deny = the hook emits permissionDecision: deny or decision: block,
preventing the tool from running.
advisory = the gate injects prerequisite context into the model's context
window (via spec-kit's before_* hook infrastructure) but does not block
execution.
Bundle adapter rationale
spec-kit supports 36+ harnesses. Of these, only Claude Code and Codex expose a real hook system (PreToolUse / UserPromptExpansion / UserPromptSubmit) that can deny tool calls before they run. All other harnesses receive spec-kit's advisory prompt injection as their only integration path — there is no hook event to intercept.
The spec-kit bundle is therefore the only distribution path that reaches
advisory harnesses. It ships an extension that registers /speckit.gate.init
and emits prerequisite context before each spec-kit command via spec-kit's
built-in before_* advisory hooks.
The bundle does not duplicate Claude Code or Codex enforcement — those
harnesses should use the plugin or install --harness path for real deny
gates.
CLI reference
| Verb | Description |
|---|---|
scan |
Scan project for spec-kit commands |
propose |
Show prerequisite proposal table |
init |
Write gates.yaml (--defaults / --answers / --interactive) |
compile |
Compile gates.yaml → .specify/gates/nodes.json |
compile --check |
Drift check only; exit 1 if stale |
install --harness |
Install hook adapters (claude/codex/speckit/all) |
explain <cmd> |
Show prerequisites for a command |
dry-run <cmd> |
Simulate a hook event |
| `dispatch pre | post` |
gates.yaml schema
config:
prefix: speckit. # command prefix; default "speckit."
feature_root: specs # feature artefact root; default "specs"
resolve: [git-branch, newest-dir]
messages:
no_feature: "..." # block reason when no feature resolved
gates:
<command>:
requires: [cmd-or-artefact, ...]
produces: [artefact, ...]
deprecated: true|false
spawn_agent: true|false # adds PreToolUse:Agent gate (Claude only)
context: "..." # advisory context injected into the model
compile derives edges by matching produces → requires and writes
.specify/gates/nodes.json in the flat dispatcher format the hook reads.
Presets
| Preset | Description |
|---|---|
presets/core.gates.yaml |
Spec-kit built-in commands only (analyze, checklist, clarify, constitution, converge, implement, plan, specify, tasks, taskstoissues). Safe to use on any spec-kit project. |
presets/extensions.example.gates.yaml |
Example file showing how to gate community and custom extensions. Not an installable preset — copy individual entries into your gates.yaml. |
Copy the core preset as your starting gates.yaml:
curl -sL https://raw.githubusercontent.com/srobroek/speckit-gate/main/presets/core.gates.yaml \
> gates.yaml
speckit-gate compile
Extending for custom extensions
If your project uses community extensions (e.g. speckit.verify-tasks,
speckit.agent-assign.*, speckit.refine.*), add gates for them on top of
the core preset. See presets/extensions.example.gates.yaml for documented
patterns covering:
- requires-chain — sequencing a community command after a built-in artefact
- spawn_agent — gating commands that spawn sub-agents via
PreToolUse:Agent - deprecated — emitting an upgrade hint instead of a prerequisite block
- produces-artefact — requiring a specific artefact path rather than a command name
# Copy entries you need from the example into your gates.yaml:
curl -sL https://raw.githubusercontent.com/srobroek/speckit-gate/main/presets/extensions.example.gates.yaml
Plain-python fallback
For exec-tax-sensitive environments (slow filesystem, EDR overhead) where
uvx startup latency matters even with caching, use the installed console
script directly:
pip install speckit-gate # or uv pip install speckit-gate
# then in hooks.json:
# "command": "speckit-gate dispatch pre"
The dispatcher (dispatch.py) is stdlib-only with no import overhead.
uvx caches after first run and is the default wiring; switch to the
installed path only when profiling shows hook latency is a problem.
Development
uv sync --dev
uv run pytest
uv run speckit-gate --help
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file speckit_gate-0.2.1.tar.gz.
File metadata
- Download URL: speckit_gate-0.2.1.tar.gz
- Upload date:
- Size: 50.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b72d8dc1558056057ade6c939c5efb135d6433efa43a74a1f827d1d5840a10b2
|
|
| MD5 |
74b53d2c0604aeb31384083e95a876ff
|
|
| BLAKE2b-256 |
8652cb5a20d146dcd6ee60b957fbaba5aca8c0a107fc4db11377c8eee72e9f1a
|
Provenance
The following attestation bundles were made for speckit_gate-0.2.1.tar.gz:
Publisher:
release-please.yml on srobroek/speckit-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
speckit_gate-0.2.1.tar.gz -
Subject digest:
b72d8dc1558056057ade6c939c5efb135d6433efa43a74a1f827d1d5840a10b2 - Sigstore transparency entry: 2100907394
- Sigstore integration time:
-
Permalink:
srobroek/speckit-gate@f6939c113b63d0594c4fb094c43076bbd0a370ef -
Branch / Tag:
refs/heads/main - Owner: https://github.com/srobroek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@f6939c113b63d0594c4fb094c43076bbd0a370ef -
Trigger Event:
push
-
Statement type:
File details
Details for the file speckit_gate-0.2.1-py3-none-any.whl.
File metadata
- Download URL: speckit_gate-0.2.1-py3-none-any.whl
- Upload date:
- Size: 30.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8c03fad924e6c4eadc8c88834800f5234224a526a081b5d6eeee26a8c03acac
|
|
| MD5 |
abf40e8b07de57264220e376e5715329
|
|
| BLAKE2b-256 |
03b730593815688dbcf1e81f7ea79f6b75947bcc0f21748d37d89c7b46865f8c
|
Provenance
The following attestation bundles were made for speckit_gate-0.2.1-py3-none-any.whl:
Publisher:
release-please.yml on srobroek/speckit-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
speckit_gate-0.2.1-py3-none-any.whl -
Subject digest:
b8c03fad924e6c4eadc8c88834800f5234224a526a081b5d6eeee26a8c03acac - Sigstore transparency entry: 2100907872
- Sigstore integration time:
-
Permalink:
srobroek/speckit-gate@f6939c113b63d0594c4fb094c43076bbd0a370ef -
Branch / Tag:
refs/heads/main - Owner: https://github.com/srobroek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@f6939c113b63d0594c4fb094c43076bbd0a370ef -
Trigger Event:
push
-
Statement type: