| name | dry-run |
| description | Preview command effects without making changes. Simulates file writes, git operations, agent spawns, and state changes. All reads execute normally for accurate preview. Use --dry-run flag on any command. |
Problem: Commands make real changes - creating files, spawning agents, modifying state, executing git operations. Testing commands on production workflows risks unintended modifications.
Solution: Add --dry-run flag that:
- Executes all read operations (for accurate analysis)
- Simulates all write operations (shows what would change)
- Previews agent spawns (shows what Task() calls would occur)
- Reports state changes (shows YAML mutations)
- Outputs standardized summary (consistent format across commands)
The result: Safe command testing with full visibility into intended effects.
# In command process section
DRY_RUN=false
if echo "$ARGUMENTS" | grep -q -- "--dry-run"; then
DRY_RUN=true
# Remove flag from arguments for processing
ARGUMENTS=$(echo "$ARGUMENTS" | sed 's/--dry-run//g' | xargs)
fi
Checking in command logic:
If DRY_RUN is true:
- Collect intended operations in simulation log
- Skip actual Write/Edit tool calls
- Skip actual Task() spawns (log them instead)
- Skip actual Bash commands with side effects
- Execute Read/Grep/Glob normally
- Output dry-run summary at end
════════════════════════════════════════════════════════════════════════════════
DRY-RUN MODE: No changes will be made
════════════════════════════════════════════════════════════════════════════════
📁 FILES THAT WOULD BE CREATED:
✚ specs/004-auth/spec.md (estimated ~150 lines)
✚ specs/004-auth/state.yaml (workflow state)
✚ specs/004-auth/NOTES.md (session notes)
📝 FILES THAT WOULD BE MODIFIED:
✎ specs/004-auth/state.yaml
- phase: init → spec
- status: pending → in_progress
🤖 AGENTS THAT WOULD BE SPAWNED:
1. spec-phase-agent: "Execute spec phase for user authentication"
2. clarify-phase-agent: "Clarify requirements"
3. plan-phase-agent: "Generate implementation plan"
🔀 GIT OPERATIONS THAT WOULD OCCUR:
• git checkout -b feature/004-auth
• git add specs/004-auth/
• git commit -m "feat: initialize auth feature workspace"
📊 STATE CHANGES:
state.yaml:
phase: spec → plan → tasks → implement
status: pending → in_progress → completed
════════════════════════════════════════════════════════════════════════════════
DRY-RUN COMPLETE: 0 actual changes made
Run without --dry-run to execute these operations
════════════════════════════════════════════════════════════════════════════════
| Scenario | Without --dry-run | With --dry-run |
|---|---|---|
| Testing new feature | Creates real workspace | Shows what would be created |
| Debugging workflow | May corrupt state | Safe preview of operations |
| Learning commands | Trial and error cleanup | Zero-risk exploration |
| CI/CD validation | Real side effects | Validates without changes |
| Training/demos | Need fresh environment | Repeatable demonstrations |
At command start, check for --dry-run in arguments:
### Step 0: Dry-Run Detection
Check for --dry-run flag:
```bash
DRY_RUN="false"
if [[ "$ARGUMENTS" == *"--dry-run"* ]]; then
DRY_RUN="true"
echo "DRY-RUN MODE ENABLED"
fi
If DRY_RUN is true:
- Initialize simulation log array
- Proceed with analysis but skip execution
- Collect all intended operations
**Important**: Remove `--dry-run` from arguments before parsing other flags to avoid conflicts.
</step>
<step number="2">
**Execute reads normally**
Read operations are safe and necessary for accurate simulation:
```markdown
**Safe operations (execute normally in dry-run):**
- Read tool: `Read file_path=...`
- Grep tool: `Grep pattern=...`
- Glob tool: `Glob pattern=...`
- Bash (read-only): `ls`, `cat`, `git status`, `git log`, `test -f`
- WebFetch: Documentation/API lookups
- WebSearch: Research queries
**Why**: Accurate simulation requires understanding current state.
Intercept and log write operations without executing:
**Write operations to simulate:**
**File writes** (Write tool):
Instead of: Write file_path="specs/001/spec.md" content="..." Log: "WOULD CREATE: specs/001/spec.md (~150 lines)"
**File edits** (Edit tool):
Instead of: Edit file_path="state.yaml" old_string="..." new_string="..." Log: "WOULD MODIFY: state.yaml (phase: init → spec)"
**Bash writes** (Bash tool with side effects):
Instead of: Bash command="mkdir -p specs/001" Log: "WOULD EXECUTE: mkdir -p specs/001"
Instead of: Bash command="git commit -m '...'" Log: "WOULD EXECUTE: git commit -m '...'"
Log Task() calls without spawning:
**Agent spawn simulation:**
Instead of:
Task tool call: subagent_type: "spec-phase-agent" description: "Execute spec phase" prompt: "..."
Log:
WOULD SPAWN AGENT: Type: spec-phase-agent Description: Execute spec phase Expected outputs: spec.md, updated state.yaml
**Why not spawn**: Agents perform real work. Dry-run must prevent cascading side effects.
Analyze YAML state mutations:
**State change tracking:**
Read current state.yaml, then log intended mutations:
```yaml
# Current state
phase: init
status: pending
# After command (simulated)
phase: spec
status: in_progress
phases:
spec: completed
Log as:
STATE CHANGES (state.yaml):
- phase: init → spec
- status: pending → in_progress
- phases.spec: (new) → completed
</step>
<step number="6">
**Output standardized summary**
Format all collected operations:
```markdown
**Summary template:**
At command end, output:
1. **Header** with dry-run banner
2. **Files created** (new files that would be written)
3. **Files modified** (existing files that would change)
4. **Agents spawned** (Task() calls that would execute)
5. **Git operations** (commits, branches, pushes)
6. **State changes** (YAML mutations)
7. **Footer** with execution instructions
Use consistent emoji prefixes:
- 📁 Files created
- 📝 Files modified
- 🤖 Agents spawned
- 🔀 Git operations
- 📊 State changes
| Operation | Tool | Why Safe |
|---|---|---|
| Read file | Read | No side effects |
| Search code | Grep | No side effects |
| Find files | Glob | No side effects |
| Check status | Bash (git status) | Read-only |
| View logs | Bash (git log) | Read-only |
| Test existence | Bash (test -f) | Read-only |
| List files | Bash (ls) | Read-only |
| Fetch docs | WebFetch | Read-only |
| Search web | WebSearch | Read-only |
All reads provide accurate context for simulation.
| Operation | Tool | Simulation Output |
|---|---|---|
| Create file | Write | "WOULD CREATE: path (~N lines)" |
| Edit file | Edit | "WOULD MODIFY: path (old → new)" |
| Make directory | Bash (mkdir) | "WOULD EXECUTE: mkdir ..." |
| Git commit | Bash (git commit) | "WOULD EXECUTE: git commit ..." |
| Git branch | Bash (git checkout -b) | "WOULD EXECUTE: git checkout -b ..." |
| Git push | Bash (git push) | "WOULD EXECUTE: git push ..." |
| Run script | Bash (python/node) | "WOULD EXECUTE: python ..." |
| Spawn agent | Task | "WOULD SPAWN: agent-type" |
| Ask user | AskUserQuestion | "WOULD ASK: question text" |
All writes are logged with expected effects.
| Operation | Approach |
|---|---|
| State validation | Run validation, report errors |
| Prerequisite checks | Execute checks, report status |
| File existence checks | Execute, use result for simulation |
| Git status queries | Execute, use for accurate preview |
Some operations need execution for accurate simulation while being side-effect-free.
Add to command frontmatter:
---
argument-hint: "[args] [--dry-run]"
---
Add to command process section:
### Step 0: Mode Detection
**Detect dry-run mode:**
```bash
DRY_RUN="false"
CLEAN_ARGS="$ARGUMENTS"
if [[ "$ARGUMENTS" == *"--dry-run"* ]]; then
DRY_RUN="true"
CLEAN_ARGS=$(echo "$ARGUMENTS" | sed 's/--dry-run//g' | xargs)
echo "═══════════════════════════════════════════════════════════════════"
echo "DRY-RUN MODE: No changes will be made"
echo "═══════════════════════════════════════════════════════════════════"
fi
Initialize simulation log (if dry-run): If DRY_RUN is true, initialize arrays:
- FILES_CREATED=()
- FILES_MODIFIED=()
- AGENTS_SPAWNED=()
- GIT_OPERATIONS=()
- STATE_CHANGES=()
Add before each operation:
```markdown
If DRY_RUN is true:
Log operation to appropriate array
Skip execution
Else:
Execute operation normally
Add at command end:
### Final Step: Dry-Run Summary (if applicable)
If DRY_RUN is true:
Output dry-run summary using format from skill
Exit without actual changes
### Step 0: Mode Detection
**Detect flags:**
```bash
DRY_RUN="false"
DEEP_MODE="false"
CLEAN_ARGS="$ARGUMENTS"
if [[ "$ARGUMENTS" == *"--dry-run"* ]]; then
DRY_RUN="true"
CLEAN_ARGS=$(echo "$CLEAN_ARGS" | sed 's/--dry-run//g')
fi
if [[ "$ARGUMENTS" == *"--deep"* ]]; then
DEEP_MODE="true"
CLEAN_ARGS=$(echo "$CLEAN_ARGS" | sed 's/--deep//g')
fi
CLEAN_ARGS=$(echo "$CLEAN_ARGS" | xargs) # Trim whitespace
Step 1.1: Initialize Feature Workspace
If DRY_RUN is true:
Log: "WOULD CREATE: specs/NNN-feature-slug/state.yaml"
Log: "WOULD CREATE: specs/NNN-feature-slug/NOTES.md"
Log: "WOULD EXECUTE: python .spec-flow/scripts/spec-cli.py feature '...'"
Skip actual execution Else: Execute normally
</example_integration>
</command_integration>
<examples>
<example name="feature-dry-run">
**Input**: `/feature "add user dashboard" --dry-run`
**Output**:
════════════════════════════════════════════════════════════════════════════════ DRY-RUN MODE: No changes will be made ════════════════════════════════════════════════════════════════════════════════
📁 FILES THAT WOULD BE CREATED: ✚ specs/005-add-user-dashboard/state.yaml ✚ specs/005-add-user-dashboard/NOTES.md ✚ specs/005-add-user-dashboard/interaction-state.yaml ✚ specs/005-add-user-dashboard/domain-memory.yaml ✚ specs/005-add-user-dashboard/spec.md (after spec-phase-agent) ✚ specs/005-add-user-dashboard/plan.md (after plan-phase-agent) ✚ specs/005-add-user-dashboard/tasks.md (after tasks-phase-agent)
🤖 AGENTS THAT WOULD BE SPAWNED:
- initializer → Initialize domain memory for feature
- spec-phase-agent → Generate specification
- clarify-phase-agent → Resolve ambiguities
- plan-phase-agent → Create implementation plan
- tasks-phase-agent → Break down into TDD tasks
- worker (N times) → Implement each feature atomically
- optimize-phase-agent → Run quality gates
- ship-staging-phase-agent → Deploy to staging
- finalize-phase-agent → Archive and document
🔀 GIT OPERATIONS THAT WOULD OCCUR: • git checkout -b feature/005-add-user-dashboard • git add specs/005-add-user-dashboard/ • git commit (multiple, after each phase) • git push origin feature/005-add-user-dashboard
📊 WORKFLOW PHASES: init → spec → clarify → plan → tasks → implement → optimize → ship → finalize
════════════════════════════════════════════════════════════════════════════════
DRY-RUN COMPLETE: 0 actual changes made
Run /feature "add user dashboard" to execute these operations
════════════════════════════════════════════════════════════════════════════════
</example>
<example name="implement-dry-run">
**Input**: `/implement --dry-run`
**Output**:
════════════════════════════════════════════════════════════════════════════════ DRY-RUN MODE: No changes will be made ════════════════════════════════════════════════════════════════════════════════
📋 CURRENT STATE: Feature: specs/004-auth-system/ Phase: implement Tasks remaining: 8 of 12
📝 FILES THAT WOULD BE MODIFIED: ✎ specs/004-auth-system/domain-memory.yaml - features[F001].status: pending → completed - features[F002].status: pending → in_progress ✎ specs/004-auth-system/state.yaml - phase: implement (unchanged during execution) ✎ src/services/auth.ts (implementation) ✎ src/services/auth.test.ts (tests) ✎ src/routes/login.ts (implementation) ✎ src/routes/login.test.ts (tests)
🤖 AGENTS THAT WOULD BE SPAWNED:
- worker → Implement F001: User login endpoint
- worker → Implement F002: Session management
- worker → Implement F003: Password validation
- worker → Implement F004: Token refresh (... 4 more workers for remaining features)
🔄 TDD CYCLE PER TASK: For each worker: 1. Write failing tests 2. Implement feature 3. Verify tests pass 4. Update domain-memory.yaml 5. Commit changes
════════════════════════════════════════════════════════════════════════════════
DRY-RUN COMPLETE: 0 actual changes made
Run /implement to execute 8 remaining tasks
════════════════════════════════════════════════════════════════════════════════
</example>
<example name="ship-dry-run">
**Input**: `/ship --staging --dry-run`
**Output**:
════════════════════════════════════════════════════════════════════════════════ DRY-RUN MODE: No changes will be made ════════════════════════════════════════════════════════════════════════════════
📋 PRE-FLIGHT CHECKS (would execute): ✓ Build verification ✓ Test suite (127 tests) ✓ Type checking ✓ Security scan ✓ Environment variables
🔀 GIT OPERATIONS THAT WOULD OCCUR: • git push origin feature/004-auth-system • gh pr create --base staging --head feature/004-auth-system • Wait for CI to pass • gh pr merge --auto
📦 DEPLOYMENT THAT WOULD OCCUR: • Merge to staging branch triggers deploy-staging.yml • Vercel/Railway builds from staging • Health check endpoints verified • Deployment URL: https://staging.example.com (estimated)
📊 STATE CHANGES: state.yaml: - phases.ship: pending → completed - deployment.staging.status: pending → deployed - deployment.staging.pr_number: (new) → #42
🤖 AGENTS THAT WOULD BE SPAWNED:
- ship-staging-phase-agent → Execute staging deployment
════════════════════════════════════════════════════════════════════════════════
DRY-RUN COMPLETE: 0 actual changes made
Run /ship --staging to deploy to staging environment
════════════════════════════════════════════════════════════════════════════════
</example>
<example name="quick-dry-run">
**Input**: `/quick "fix typo in README" --dry-run`
**Output**:
════════════════════════════════════════════════════════════════════════════════ DRY-RUN MODE: No changes will be made ════════════════════════════════════════════════════════════════════════════════
📋 TASK ANALYSIS: Type: Documentation fix Scope: Single file Estimated changes: <10 lines
📝 FILES THAT WOULD BE MODIFIED: ✎ README.md (typo fix)
🔀 GIT OPERATIONS THAT WOULD OCCUR: • git add README.md • git commit -m "docs: fix typo in README"
🤖 AGENTS THAT WOULD BE SPAWNED:
- quick-worker → Execute atomic change with tests
════════════════════════════════════════════════════════════════════════════════
DRY-RUN COMPLETE: 0 actual changes made
Run /quick "fix typo in README" to execute
════════════════════════════════════════════════════════════════════════════════
</example>
</examples>
<anti_patterns>
<anti_pattern name="executing-writes-in-dry-run">
**Problem**: Accidentally executing write operations in dry-run mode
**Bad approach**:
```markdown
If DRY_RUN:
echo "Would write file"
Write file... # WRONG: Executes regardless of dry-run!
Correct approach:
If DRY_RUN:
Log: "WOULD CREATE: file.md"
Else:
Write file_path="file.md" content="..."
Rule: Always wrap write operations in dry-run conditional.
Bad approach:
If DRY_RUN:
echo "Would spawn agent"
Task(spec-phase-agent) # WRONG: Agent runs and makes changes!
Correct approach:
If DRY_RUN:
Log: "WOULD SPAWN: spec-phase-agent"
Log: "Expected outputs: spec.md"
Else:
Task(spec-phase-agent)
Rule: Never spawn agents in dry-run mode.
Bad approach:
DRY-RUN OUTPUT:
Would create spec.md
# Missing: state.yaml changes, git operations, other files
Correct approach:
DRY-RUN OUTPUT:
FILES CREATED: spec.md, state.yaml, NOTES.md
FILES MODIFIED: state.yaml (phase: init → spec)
GIT OPERATIONS: checkout -b, add, commit
AGENTS: spec-phase-agent, clarify-phase-agent
Rule: Log ALL operations that would occur.
Bad approach:
If DRY_RUN:
Skip reading state.yaml # WRONG: Can't accurately simulate!
Guess at current state
Correct approach:
# Always read current state (safe operation)
STATE=$(cat specs/NNN/state.yaml)
CURRENT_PHASE=$(yq eval '.phase' ...)
If DRY_RUN:
Log: "Current phase: $CURRENT_PHASE → would transition to: spec"
Rule: Execute reads for accurate simulation context.
- Zero side effects: No files created/modified, no git operations, no agents spawned
- Accurate preview: All intended operations listed in output
- Consistent format: Standard output format across all commands
- Readable output: Clear emoji prefixes, organized sections
- Actionable: Output tells user how to execute for real
- Complete: Shows files, agents, git ops, state changes
- --dry-run flag detected and removed from arguments
- All Write tool calls wrapped in dry-run conditional
- All Edit tool calls wrapped in dry-run conditional
- All Task() calls wrapped in dry-run conditional
- All Bash writes (mkdir, git commit, etc.) wrapped
- Read operations execute normally for context
- Output uses standard format with emoji prefixes
- Output includes all operation categories
- Output shows how to run without dry-run
- No actual changes occur in dry-run mode
| Command | Dry-run shows |
|---|---|
/feature |
Workspace creation, all phases, agents |
/epic |
Epic workspace, sprint planning, parallel execution |
/quick |
Quick fix changes, commit |
/plan |
Plan.md creation, research |
/tasks |
Tasks.md generation |
/implement |
Worker spawns, file changes, tests |
/optimize |
Quality gate execution, reports |
/ship |
Deployment operations, PR creation |
/finalize |
Archival, cleanup, documentation |
Usage: Add --dry-run to any command:
/feature "add auth" --dry-run
/implement --dry-run
/ship --staging --dry-run
- Flag detection:
--dry-runrecognized by command - Zero execution: No files created, modified, or deleted
- No agents spawned: Task() calls logged but not executed
- No git changes: No commits, branches, or pushes
- Accurate preview: All operations that WOULD occur are listed
- Standard format: Consistent output format with clear sections
- Actionable output: User knows how to run command for real
- Safe exploration: Users can test any command without risk
- CI/CD ready: Can validate commands in automation pipelines
- Repeatable: Same dry-run produces same output (deterministic)