* feat(workflows): expose workflow source directory to steps (#3467) Propagate WorkflowDefinition.source_path to steps via {{ context.workflow_dir }} in template expressions and SPECKIT_WORKFLOW_DIR env var for shell steps. The original source directory is persisted in state.json so resume restores the correct value instead of the run-directory copy path. Closes #3467 Assisted-By: 🤖 Claude Code * fix: apply bot review suggestions (#2) Applied fixes from bot review comments: - Comment #3563319058: prevent stale SPECKIT_WORKFLOW_DIR leak from parent env - Comment #3563319094: use cross-platform Python one-liner instead of printenv - Comment #3563319103: add monkeypatch.delenv for deterministic env var test - Comment #3563319116: same env leak fix as #3563319058 Assisted-By: 🤖 Claude Code * fix: use YAML single-quotes and forward-slash paths for Windows CI (#2) sys.executable on Windows returns backslash paths (D:\a\...) which YAML double-quoted strings interpret as escape sequences. Switch to single-quoted YAML strings and normalize paths with replace("\\", "/"). Assisted-By: 🤖 Claude Code * fix: resolve workflow_dir to absolute path and add installed-by-ID test (#3469) Applied fixes from bot review comments: - Comment #3563382853: resolve source_path before taking parent to ensure absolute paths - Comment #3563382864: add test for installed-by-ID workflow_dir semantics Assisted-By: 🤖 Claude Code * docs: document context.workflow_dir and SPECKIT_WORKFLOW_DIR Add reference documentation for the new workflow_dir runtime value in both workflows/README.md and docs/reference/workflows.md so workflow authors can discover the feature and its semantics. Assisted-By: 🤖 Claude Code * fix: clarify installed workflow_dir is an absolute path (#3469) The documentation for context.workflow_dir described the installed-by-ID case as ".specify/workflows/<id>/" which appears relative, contradicting the "resolved absolute path" semantics. Clarified that it is the absolute path to the installation directory. Assisted-By: 🤖 Claude Code * fix: apply bot review suggestions (#3469) Applied fixes from bot review comments: - Comment #3580005128: Quote sys.executable in shell step env var test - Comment #3580005174: Quote sys.executable in no-env-var test Assisted-By: 🤖 Claude Code * fix: apply bot review suggestions (#3469) Applied fixes from bot review comments: - Comment #3587146944: Quote interpolated workflow_dir path in example Assisted-By: 🤖 Claude Code
12 KiB
Workflows
Workflows automate multi-step Spec-Driven Development processes — chaining commands, prompts, shell steps, and human checkpoints into repeatable sequences. They support conditional logic, loops, fan-out/fan-in, and can be paused and resumed from the exact point of interruption.
Run a Workflow
specify workflow run <source>
| Option | Description |
|---|---|
-i / --input |
Pass input values as key=value (repeatable) |
--json |
Emit the run outcome as a single JSON object |
Runs a workflow from a catalog ID, URL, or local file path. Inputs declared by the workflow can be provided via --input or will be prompted interactively.
Example:
specify workflow run speckit -i spec="Build a kanban board with drag-and-drop task management" -i scope=full
With --json, a single machine-readable object is printed instead of formatted text (the default output is unchanged when the flag is omitted):
specify workflow run my-pipeline.yml --json
{
"run_id": "662bf791",
"workflow_id": "build-and-review",
"status": "paused",
"current_step_id": "review",
"current_step_index": 0
}
workflow_id is the workflow.id declared inside the YAML, not the file name. The object is printed exactly as shown — pretty-printed with two-space indentation, on plain stdout with no Rich markup — so it always parses. While the workflow runs under --json, any progress a step would print (for example a gate prompt, or output from a prompt step's CLI subprocess) is redirected to stderr, so stdout carries only the JSON object. Read the object from stdout; leave stderr attached to the terminal or capture it separately.
Note: Most workflow commands require a project already initialized with
specify init. The exception isspecify workflow run <local-file.{yml,yaml}>, which can run outside a project; in that case, run state is stored under the current directory's.specify/workflows/runs/<run_id>/.
Resume a Workflow
specify workflow resume <run_id>
| Option | Description |
|---|---|
-i / --input |
Updated input values as key=value (repeatable) |
--json |
Emit the resume outcome as a single JSON object |
Resumes a paused or failed workflow run from the exact step where it stopped. Useful after responding to a gate step or fixing an issue that caused a failure.
Supplied --input values are merged over the run's stored inputs and re-validated against the workflow's input types, then the blocked step is re-run with the updated values. This lets a run continue with information that only became available after it paused, or with a corrected value after a failure:
specify workflow resume <run_id> --input cmd="exit 0"
Workflow Status
specify workflow status [<run_id>]
| Option | Description |
|---|---|
--json |
Emit run status (or the runs list) as a JSON object |
Shows the status of a specific run, or lists all runs if no ID is given. Run states: created, running, completed, paused, failed, aborted.
List Installed Workflows
specify workflow list
Lists workflows installed in the current project.
Install a Workflow
specify workflow add <source>
| Option | Description |
|---|---|
--dev |
Install from a local workflow YAML file or directory |
--from <url> |
Install from a custom URL (<source> names the expected workflow ID) |
Installs a workflow from the catalog, a URL (HTTPS required), or a local file path.
Update Workflows
specify workflow update [workflow_id]
Updates one installed catalog workflow — or all of them when no ID is given — to the latest catalog version. Prompts for confirmation and keeps the installed copy if a download or validation fails.
Enable or Disable a Workflow
specify workflow enable <workflow_id>
specify workflow disable <workflow_id>
Disabled workflows stay installed and listed (marked [disabled]) but refuse to run until re-enabled.
Remove a Workflow
specify workflow remove <workflow_id>
Removes an installed workflow from the project.
Search Available Workflows
specify workflow search [query]
| Option | Description |
|---|---|
--tag |
Filter by tag |
--author |
Filter by author |
Searches all active catalogs for workflows matching the query.
Workflow Info
specify workflow info <workflow_id>
Shows detailed information about a workflow, including its steps, inputs, and requirements.
Catalog Management
Workflow catalogs control where search and add look for workflows. Catalogs are checked in priority order.
List Catalogs
specify workflow catalog list
Shows all active catalog sources.
Add a Catalog
specify workflow catalog add <url>
| Option | Description |
|---|---|
--name <name> |
Optional name for the catalog |
Adds a custom catalog URL to the project's .specify/workflow-catalogs.yml.
Remove a Catalog
specify workflow catalog remove <index>
Removes a catalog by its index in the catalog list.
Catalog Resolution Order
Catalogs are resolved in this order (first match wins):
- Environment variable —
SPECKIT_WORKFLOW_CATALOG_URLoverrides all catalogs - Project config —
.specify/workflow-catalogs.yml - User config —
~/.specify/workflow-catalogs.yml - Built-in defaults — official catalog + community catalog
Workflow Definition
Workflows are defined in YAML files. Here is the built-in Full SDD Cycle workflow that ships with Spec Kit:
schema_version: "1.0"
workflow:
id: "speckit"
name: "Full SDD Cycle"
version: "1.0.0"
author: "GitHub"
description: "Runs specify → plan → tasks → implement with review gates"
requires:
speckit_version: ">=0.7.2"
integrations:
any: ["copilot", "claude", "gemini"]
inputs:
spec:
type: string
required: true
prompt: "Describe what you want to build"
integration:
type: string
default: "copilot"
prompt: "Integration to use (e.g. claude, copilot, gemini)"
scope:
type: string
default: "full"
enum: ["full", "backend-only", "frontend-only"]
steps:
- id: specify
command: speckit.specify
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: review-spec
type: gate
message: "Review the generated spec before planning."
options: [approve, reject]
on_reject: abort
- id: plan
command: speckit.plan
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: review-plan
type: gate
message: "Review the plan before generating tasks."
options: [approve, reject]
on_reject: abort
- id: tasks
command: speckit.tasks
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: implement
command: speckit.implement
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
This produces the following execution flow:
flowchart TB
A["specify<br/>(command)"] --> B{"review-spec<br/>(gate)"}
B -- approve --> C["plan<br/>(command)"]
B -- reject --> X1["⏹ Abort"]
C --> D{"review-plan<br/>(gate)"}
D -- approve --> E["tasks<br/>(command)"]
D -- reject --> X2["⏹ Abort"]
E --> F["implement<br/>(command)"]
style A fill:#49a,color:#fff
style B fill:#a94,color:#fff
style C fill:#49a,color:#fff
style D fill:#a94,color:#fff
style E fill:#49a,color:#fff
style F fill:#49a,color:#fff
style X1 fill:#999,color:#fff
style X2 fill:#999,color:#fff
Run it with:
specify workflow run speckit -i spec="Build a kanban board with drag-and-drop task management"
Step Types
| Type | Purpose |
|---|---|
command |
Invoke a Spec Kit command (e.g., speckit.plan) |
prompt |
Send an arbitrary prompt to the AI coding agent |
shell |
Execute a shell command and capture output |
init |
Bootstrap a project (like specify init) |
gate |
Pause for human approval before continuing |
if |
Conditional branching (then/else) |
switch |
Multi-branch dispatch on an expression |
while |
Loop while a condition is true |
do-while |
Execute at least once, then loop on condition |
fan-out |
Dispatch a step for each item in a list |
fan-in |
Aggregate results from a fan-out step |
Security note: a
shellstep runs a local command with your privileges. There is no capability sandbox —requiresis an advisory pre-condition block (spec-kit version, integrations), not a runtime gate, so it does not restrict what a step can do. In particular there is norequires.permissionscapability gate: it is rejected by validation precisely because it would imply a sandbox that does not exist. Review any catalog or downloaded workflow before running it, and use agatestep to require explicit approval before sensitive or destructive shell commands.
Expressions
Steps can reference inputs and previous step outputs using {{ expression }} syntax:
| Namespace | Description |
|---|---|
inputs.spec |
Workflow input values |
steps.specify.output.file |
Output from a previous step |
item |
Current item in a fan-out iteration |
context.run_id |
Current workflow run ID |
context.workflow_dir |
Resolved absolute path to the workflow source directory. Empty string for string-loaded workflows. |
Available filters: default, join, contains, map, from_json.
Example:
condition: "{{ steps.test.output.exit_code == 0 }}"
args: "{{ inputs.spec }}"
message: "{{ status | default('pending') }}"
Shell Step Environment Variables
Shell steps automatically receive the following environment variables:
| Variable | Description |
|---|---|
SPECKIT_WORKFLOW_DIR |
Resolved absolute path to the workflow source directory (same value as {{ context.workflow_dir }}). Not set when the workflow has no source path. |
Input Types
| Type | Coercion |
|---|---|
string |
Pass-through |
number |
"42" → 42, "3.14" → 3.14 |
boolean |
"true" / "1" / "yes" → True |
State and Resume
Each workflow run persists its state at .specify/workflows/runs/<run_id>/:
state.json— current run state and step progressinputs.json— resolved input valueslog.jsonl— step-by-step execution log
This enables specify workflow resume to continue from the exact step where a run was paused (e.g., at a gate) or failed.
FAQ
What happens when a workflow hits a gate step?
The workflow pauses and waits for human input. Run specify workflow resume <run_id> after reviewing to continue.
Can I run the same workflow multiple times?
Yes. Each run gets a unique ID and its own state directory. Use specify workflow status to see all runs.
Who maintains workflows?
Most workflows are independently created and maintained by their respective authors. The Spec Kit maintainers do not review, audit, endorse, or support workflow code. Review a workflow's source before installing and use at your own discretion.