| 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. |
The dry-run skill enables safe command testing by simulating all side effects without execution.
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.
<quick_start>
<flag_detection>
Detect --dry-run in command arguments:
DRY_RUN=false
if echo "$ARGUMENTS" | grep -q -- "--dry-run"; then
DRY_RUN=true
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
</flag_detection>
<simulation_output_format>
Standard dry-run output format:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
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
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
</simulation_output_format>
<immediate_value>
Why use dry-run:
| 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 |
| </immediate_value> | | |
| </quick_start> | | |
**Detect dry-run flag**
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.
**Simulate write operations**
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 '...'"
**Simulate agent spawns**
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.
**Track state changes**
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_categories>
<safe_operations>
Execute normally in dry-run mode:
| 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.
</safe_operations>
<simulated_operations>
Simulate (log but don't execute) in dry-run mode:
| 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.
</simulated_operations>
<hybrid_operations>
Execute carefully in dry-run mode:
| 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.
</hybrid_operations>
</operation_categories>
<command_integration>
Standard pattern for command integration:
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
<example_integration>
Example: /feature command integration:
### 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.
</anti_pattern>
<anti_pattern name="spawning-agents-in-dry-run">
Problem: Spawning Task() agents defeats dry-run purpose
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.
</anti_pattern>
<anti_pattern name="incomplete-simulation">
Problem: Missing operations from dry-run output
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.
</anti_pattern>
<anti_pattern name="skipping-necessary-reads">
Problem: Skipping reads makes simulation inaccurate
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.
</anti_pattern>
</anti_patterns>
Dry-run implementation is correct when:
- 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
</success_indicators>
<validation_checklist>
Before considering dry-run implementation complete:
<commands_supporting_dry_run>
Commands that support --dry-run flag:
| 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
</commands_supporting_dry_run>
<success_criteria>
The dry-run skill is successfully applied when:
- Flag detection:
--dry-run recognized 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)
</success_criteria>