| name | description |
|---|---|
setup-cmux |
Configure cmux terminal multiplexer workspaces for a project by generating .vscode/terminals.json with AI Workflow tabs, App workspace, and development terminals. Use when the user says "setup cmux", "configure cmux", "add cmux workspaces", "setup terminals", "setup ai workflow", "add workflow tabs", "configure make cmux", or wants to set up their terminal workspace layout for development with Claude agents, or when starting a new project that needs terminal multiplexer configuration. Also use when the user wants to add or modify cmux workspaces, tabs, or browser panes. |
Configure cmux terminal multiplexer workspaces for any project by generating or updating .vscode/terminals.json. This creates a complete development environment with AI workflow tabs, application servers, and development terminals — all launchable via make cmux.
Announce at start: "I'm using the setup-cmux skill to configure your cmux terminal workspace."
cmux reads .vscode/terminals.json to create terminal workspaces with tabs. Each workspace appears as a sidebar entry, and tabs within it can be terminals or browser surfaces. A Python script (scripts/cmux.py) processes the config and creates everything via the cmux CLI.
The config has two sections:
terminals— legacy flat list (iTerm2 fallback)cmux— structured workspaces with tabs, browser panes, health checks, and auto-input
The cmux section is what we configure. The terminals section is optional but useful as a fallback for environments without cmux.
Before configuring anything, understand the project:
# Project name and directory
basename $(pwd)
# Current git branch
git rev-parse --abbrev-ref HEAD 2>/dev/null
# Check for existing terminals.json
cat .vscode/terminals.json 2>/dev/null | head -5
# Check for Makefile targets
grep -E '^(dev|serve|start|test|cmux|tree|terms):' Makefile 2>/dev/null
# Check for package.json scripts
cat package.json 2>/dev/null | jq '.scripts | keys' 2>/dev/null
# Check for .env with URLs and ports
grep -E '^(PUBLIC_URL|PORT|BACKEND_PORT|VITE_PORT|DATABASE_URL)=' .env 2>/dev/null
# Detect tech stack
ls Cargo.toml package.json pyproject.toml go.mod 2>/dev/nullStore:
- PROJECT_NAME — directory name or repo name
- PREFIX — short uppercase prefix for tab names (e.g., "AR" for atomic-reactor, "GS" for grade-sync). Derive from project name or ask user.
- TECH_STACK — rust, node, python, go, etc.
- HAS_FRONTEND — does the project have a separate frontend dev server?
- HAS_BACKEND — does the project have a backend server?
- PUBLIC_URL — from .env if available
- HAS_DOCKER — uses docker-compose for services?
Present the detected context and ask which workspace configurations they want. Offer these workspace templates:
## Project: {PROJECT_NAME} ({TECH_STACK})
I can set up these cmux workspaces for you:
### 1. AI Workflow (recommended)
8 numbered step tabs named after the skill/command they run, plus console, reminder, and plans tabs:
plan-stories → plan-sweep → review-open-prs → fix-pr-issues → merge-approved-prs → cleanup-merged → docs-maintain → status + Console + Reminder + Plans
### 2. App
Application servers, database, tests, and optionally a browser pane.
Configured based on your tech stack ({TECH_STACK}).
### 3. Development
Multiple Claude sessions + a console tab for ad-hoc work.
Which would you like? (e.g., "all", "1 and 2", "just the AI workflow")
Adapt workspace suggestions based on detected stack. For example:
- No Makefile targets for backend? Skip the App workspace backend tab.
- No frontend? Skip the frontend tab and browser pane.
- Simple project? Maybe just Development + AI Workflow.
The prefix keeps tab names scannable across multiple projects in cmux. Derive it from the project name:
| Project Name | Prefix |
|---|---|
| atomic-reactor | AR |
| grade-sync | GS |
| my-cool-app | MCA |
| api-server | AS |
Rules:
- Take first letter of each hyphen/underscore-separated word
- Uppercase
- 2-3 characters max
- Ask user to confirm: "I'll use {PREFIX} as your tab prefix. OK?"
AI Workflow step tabs use: {N} {skill-name} (number + the actual skill/command name)
Other tabs use: {PREFIX} {Purpose}
Examples with prefix "GS":
1 plan-stories,2 plan-sweep,3 review-open-prs, ...,8 statusGS Backend,GS Frontend,GS ConsoleGS Claude 1,GS Claude 2
This is the development lifecycle pipeline. Each tab is user-controlled — the prompt is pre-typed into Claude's input but NOT submitted. The user decides when to kick off each step by pressing Enter.
Each tab has two layers:
/rename— sent viainput.linesafter Claude initializes (submitted with Enter automatically).- Task prompt — sent via
input.linesas an object with"submit": false, so it appears in Claude's input buffer but waits for the user to press Enter.
Step descriptions live in the "notes" field (documentation-only) and are available via make remind in the Reminder tab. Do NOT add echo banners to tab commands — they clutter the terminal.
IMPORTANT: Claude CLI does NOT have a --prompt flag. The prompt is a positional argument (claude 'text'), but for the AI Workflow we don't use it — we want user-controlled execution via input.lines with "submit": false.
The "notes" field provides human-readable context (what step, when to run, dependencies). It is documentation-only and not processed by cmux.
{
"workspace": "AI Workflow",
"tabs": [
{
"name": "1 plan-stories",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Intake [BRANCH]",
{"text": "/plan-stories work", "delay": 2, "submit": false}
]
},
"notes": "STEP 1: Story Intake. Pulls stories from the GitHub Projects board and generates implementation plans in docs/plans/. Run /plan-stories work to start. Re-run anytime new stories are assigned."
},
{
"name": "2 plan-sweep",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Execute [BRANCH]",
{"text": "/plan-sweep", "delay": 2, "submit": false}
]
},
"notes": "STEP 2: Plan Execution. Discovers plans in docs/plans/, analyzes dependencies, then executes them in parallel tiers using worktrees. Run /plan-sweep to start. Wait for step 1 to produce plans first."
},
{
"name": "3 review-open-prs",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Review [BRANCH]",
{"text": "Review all open PRs on this repo. Use the review-open-prs skill.", "delay": 2, "submit": false}
]
},
"notes": "STEP 3: PR Review. Reviews all open PRs for code quality, test coverage, and adherence to project standards. Run after step 2 creates PRs. Re-run after fix-pr-issues pushes fixes."
},
{
"name": "4 fix-pr-issues",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Fix [BRANCH]",
{"text": "Fix issues identified in PR reviews. Use the fix-pr-issues skill.", "delay": 2, "submit": false}
]
},
"notes": "STEP 4: Fix PR Issues. Reads review comments from open PRs and dispatches agents to fix them in worktrees. Run after step 3 leaves review comments. May need to cycle back to step 3."
},
{
"name": "5 merge-approved-prs",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Merge [BRANCH]",
{"text": "Merge all approved GitHub PRs in optimal order. Use the merge-approved-prs skill.", "delay": 2, "submit": false}
]
},
"notes": "STEP 5: Merge PRs. Merges all approved PRs in dependency order with automatic conflict resolution. Run after PRs are approved (step 3/4 cycle complete)."
},
{
"name": "6 cleanup-merged",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Cleanup [BRANCH]",
{"text": "Clean up git worktrees and branches that have been merged into main. Use the cleanup-merged skill.", "delay": 2, "submit": false}
]
},
"notes": "STEP 6: Cleanup. Removes worktrees and branches that have been merged into main. Safe — verifies each branch is fully merged before deleting. Run after step 5 merges PRs."
},
{
"name": "7 docs-maintain",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Docs [BRANCH]",
{"text": "Update project documentation to reflect recent code changes. Use the docs-maintain skill.", "delay": 2, "submit": false}
]
},
"notes": "STEP 7: Documentation. Updates project docs to reflect recent code changes, audits for broken links, and archives completed plans. Run after merges land on main."
},
{
"name": "8 status",
"commands": [
"cd [workspaceFolder]",
"claude --dangerously-skip-permissions"
],
"input": {
"delay": 8,
"lines": [
"/rename {PREFIX} Status [BRANCH]",
{"text": "Give me a development lifecycle status overview: 1) Plans in docs/plans/ (count and status), 2) Open PRs (count, review status), 3) Approved PRs ready to merge, 4) Merged branches needing cleanup, 5) Active worktrees. Present this as a dashboard.", "delay": 2, "submit": false}
]
},
"notes": "STATUS: Dashboard. Shows the full lifecycle overview — plans, PRs, merges, worktrees. Run anytime to see where things stand and which step to run next."
},
{
"name": "console",
"focus": true,
"commands": ["cd [workspaceFolder]"]
},
{
"name": "{PREFIX} Reminder",
"commands": ["cd [workspaceFolder]", "make remind"]
},
{
"name": "{PREFIX} Plans",
"commands": ["cd [workspaceFolder]/docs/plans"]
},
{
"name": "{PREFIX} Claude Plans",
"commands": ["cd [workspaceFolder]/docs/plans", "claude --dangerously-skip-permissions"],
"input": { "delay": 5, "lines": ["/rename {PREFIX} Claude Plans [BRANCH]"] }
}
]
}Adapt based on tech stack. Here are templates for common stacks:
{
"workspace": "App",
"tabs": [
{
"name": "{PREFIX} Backend",
"commands": ["cd [workspaceFolder]", "{backend_cmd}"],
"healthCheck": { "wait": 5, "expect": "listening" }
},
{
"name": "{PREFIX} Frontend",
"commands": ["cd [workspaceFolder]", "{frontend_cmd}"]
},
{
"name": "{PREFIX} Database",
"commands": ["cd [workspaceFolder]", "{db_cmd}"]
},
{
"name": "{PREFIX} Backend Tests",
"commands": ["cd [workspaceFolder]", "{backend_test_cmd}"]
},
{
"name": "{PREFIX} Frontend Tests",
"commands": ["cd [workspaceFolder]", "{frontend_test_cmd}"]
},
{
"name": "{PREFIX} Browser",
"type": "browser",
"urls": [
{ "name": "{PROJECT_NAME}", "url": "{PUBLIC_URL}" }
]
}
]
}{
"workspace": "App",
"tabs": [
{
"name": "{PREFIX} Dev",
"commands": ["cd [workspaceFolder]", "npm run dev"],
"healthCheck": { "wait": 3, "expect": "ready" }
},
{
"name": "{PREFIX} Tests",
"commands": ["cd [workspaceFolder]", "npm test"]
},
{
"name": "{PREFIX} Browser",
"type": "browser",
"urls": [
{ "name": "{PROJECT_NAME}", "url": "http://localhost:3000" }
]
}
]
}{
"workspace": "App",
"tabs": [
{
"name": "{PREFIX} Server",
"commands": ["cd [workspaceFolder]", "{server_cmd}"],
"healthCheck": { "wait": 3, "expect": "Uvicorn running" }
},
{
"name": "{PREFIX} Tests",
"commands": ["cd [workspaceFolder]", "{test_cmd}"]
}
]
}Detect the right commands from the Makefile or package.json. Common patterns:
| What | Makefile | package.json | Fallback |
|---|---|---|---|
| Backend | make dev-backend |
npm run dev |
cargo run / python manage.py runserver |
| Frontend | make dev-frontend |
npm run dev:frontend |
npm run dev |
| Backend tests | make test |
npm test |
cargo test / pytest |
| Frontend tests | npm run test |
npm test |
npx jest / npx vitest |
| Database | make db-console |
— | psql / docker-compose up -d postgres |
Multiple Claude sessions for general-purpose work:
{
"workspace": "{PROJECT_NAME}",
"tabs": [
{
"name": "{PREFIX} Claude 1",
"commands": ["cd [workspaceFolder]", "claude --dangerously-skip-permissions"],
"input": { "delay": 5, "lines": ["/rename {PREFIX} Claude [BRANCH] 1"] }
},
{
"name": "{PREFIX} Claude 2",
"commands": ["cd [workspaceFolder]", "claude --dangerously-skip-permissions"],
"input": { "delay": 5, "lines": ["/rename {PREFIX} Claude [BRANCH] 2"] }
},
{
"name": "{PREFIX} Claude 3",
"commands": ["cd [workspaceFolder]", "claude --dangerously-skip-permissions"],
"input": { "delay": 5, "lines": ["/rename {PREFIX} Claude [BRANCH] 3"] }
},
{
"name": "{PREFIX} Claude 4",
"commands": ["cd [workspaceFolder]", "claude --dangerously-skip-permissions"],
"input": { "delay": 5, "lines": ["/rename {PREFIX} Claude [BRANCH] 4"] }
},
{
"name": "{PREFIX} Console",
"focus": true,
"commands": ["cd [workspaceFolder]"]
}
]
}Read the existing file, preserve the terminals array (legacy section), and merge the new cmux.workspaces entries. If a workspace with the same name already exists, ask whether to replace or skip it.
Create the full file with both sections:
{
"autorun": true,
"autokill": true,
"terminals": [],
"cmux": {
"notify": true,
"workspaces": [
// ... generated workspaces
]
}
}mkdir -p .vscodeCheck the Makefile for a cmux target. If missing, offer to add one:
cmux: ## Open cmux workspaces from .vscode/terminals.json
@./scripts/cmux.pyAlso check for scripts/cmux.py. If missing, search for it in sibling projects and offer to copy it:
# Search common locations for cmux.py
find ~/projects -maxdepth 3 -name "cmux.py" -path "*/scripts/*" 2>/dev/null | head -5If found, offer to copy it:
mkdir -p scripts
cp <found_path> scripts/cmux.py
chmod +x scripts/cmux.pyIf not found anywhere, tell the user: "Your project needs scripts/cmux.py to process the terminals.json config. You can copy it from a project that has it, or install it from the cmux documentation."
Check the Makefile for a remind target. If missing, offer to add one that prints all AI Workflow step names and prompts:
remind: ## Show AI Workflow steps and prompts
@echo ""
@echo "AI Workflow Steps"
@echo "================="
@echo ""
@echo "Step 1: plan-stories"
@echo " Prompt: /plan-stories work"
@echo " Pulls stories from GitHub Projects board, generates plans in docs/plans/"
@echo ""
# ... one block per step ...
@echo "All prompts are pre-typed but not submitted — press Enter to run each step."
@echo ""This target is displayed in the Reminder tab so users can quickly reference which step does what without switching tabs.
Check the Makefile for a close target. If missing, offer to add one. This is the counterpart to make cmux — it closes all cmux workspaces except the one you're in:
close: ## Close all cmux workspaces except the current one
@if ! command -v cmux &> /dev/null; then \
echo "$(RED)✗ cmux is not installed$(NC)"; \
exit 1; \
fi
@echo "$(BLUE)Closing all cmux workspaces except current...$(NC)"; \
cmux list-workspaces 2>/dev/null | while read -r line; do \
if echo "$$line" | grep -q '\[selected\]'; then continue; fi; \
WS=$$(echo "$$line" | grep -o 'workspace:[0-9]*'); \
if [ -n "$$WS" ]; then \
echo " Closing $$WS..."; \
cmux close-workspace --workspace "$$WS" 2>/dev/null; \
fi; \
done; \
echo "$(GREEN)✓ All other workspaces closed$(NC)"Also add close to the .PHONY list.
Note: The cmux list-workspaces output format is:
workspace:18 Skills
* workspace:1 MyProject [selected]
workspace:52 AI Workflow
The * prefix and [selected] suffix mark the current workspace. The grep -o 'workspace:[0-9]*' pattern reliably extracts the workspace ID regardless of field position.
After writing .vscode/terminals.json, generate (or update) a ## cmux Terminal Interaction section in the project's CLAUDE.md. This teaches agents how to find and interact with the App workspace surfaces — restarting services, running tests, checking health, etc.
Agents running in cmux need to know:
- What terminal surfaces exist and what they're named
- How to find a surface by name (IDs are dynamic)
- How to restart a service (Ctrl+C + re-send command)
- How to read output to verify status
- Best practices to avoid breaking things
Read the App workspace tabs from the config you just wrote. For each tab, extract:
- Surface Name — the
namefield (e.g.,{PREFIX} Backend) - Command — the shell command(s) from
commands(e.g.,make dev-backend) - Purpose — infer from the command and tab name
Then generate the following section and insert/replace it in CLAUDE.md:
## cmux Terminal Interaction
When running inside cmux, agents can interact with other terminals to manage the development environment. Always check for cmux availability first: `command -v cmux &>/dev/null`.
**IMPORTANT:** All cmux commands must be run from the **original terminal window** where `make cmux` (or `cmux`) was launched. cmux commands will not work from terminals spawned by cmux itself (e.g., Claude sessions, app servers). If you need to run cmux commands, use the console tab or a shell that is in the original cmux window.
### App Workspace Surfaces
The **App** workspace contains these surfaces (tab names from `.vscode/terminals.json`):
| Surface Name | Command | Purpose |
|---|---|---|
| `{PREFIX} Backend` | `make dev-backend` | Rust backend via `cargo watch -x run` |
| `{PREFIX} Frontend` | `make dev-frontend` | Vite dev server for React SPA |
| ... (one row per App workspace tab) |
### Finding a Surface
```bash
# List all panels to find surface IDs by name
cmux list-panels --json 2>/dev/null | grep "{PREFIX} Backend"
# Example output: surface:325 terminal "{PREFIX} Backend"
# Or search by content visible on screen
cmux find-window --content "listening" --selectTo restart a service, send Ctrl+C to stop it, then re-send the start command:
# Find the surface ID
SURFACE=$(cmux list-panels --json 2>/dev/null | grep '"{PREFIX} Backend"' | grep -o 'surface:[0-9]*')
# Restart: Ctrl+C then re-run
cmux send --surface "$SURFACE" "\x03" # Ctrl+C to stop
sleep 1
cmux send --surface "$SURFACE" "{backend_command}\n"
# Verify it restarted
sleep 3
cmux read-screen --surface "$SURFACE" --scrollback --lines 20(Include a "Common restart scenarios" list with one entry per long-running service tab — Backend, Frontend, Tunnel, etc. — showing what triggers a restart and the exact command.)
(One example per test tab, showing how to find the surface and re-trigger tests.)
# Read the last 20 lines of a surface to check if a service is running
SURFACE=$(cmux list-panels --json 2>/dev/null | grep '"{PREFIX} Backend"' | grep -o 'surface:[0-9]*')
cmux read-screen --surface "$SURFACE" --scrollback --lines 20
# Look for "{healthCheck.expect}" to confirm it's up- Run cmux commands from the original window only — cmux commands must be executed from the terminal window where
make cmuxwas launched, not from terminals spawned by cmux (Claude sessions, servers, etc.) - Find surfaces by name — Surface IDs are dynamic; always look them up by name before interacting
- Read before acting — Use
cmux read-screento check current state before restarting - Wait after restart — Services take time to compile/start; read screen to confirm readiness
- Use sidebar for progress — For long operations, use
cmux set-progressandcmux log - Notify on completion — Use
cmux notifyfor long ops - Don't create duplicate surfaces — Check if a surface already exists before creating new ones
### How to Insert/Update
1. Read the project's `CLAUDE.md`
2. If a `## cmux Terminal Interaction` section already exists, replace it entirely (from `## cmux Terminal Interaction` up to the next `##` heading or end of file)
3. If it doesn't exist, append it at the end of `CLAUDE.md`
4. Adapt the content to the actual tabs configured — don't include tabs that weren't set up (e.g., if no Tunnel tab, skip the tunnel restart example)
5. Use the actual `{PREFIX}` and command values from the generated config
### Adaptation Rules
- **Only document App workspace tabs** — AI Workflow and Development workspace tabs don't need terminal interaction instructions (agents don't restart Claude sessions)
- **Include browser tabs only if relevant** — mention the browser surface name so agents know it exists, but don't include restart instructions for it
- **Match healthCheck.expect values** — if a tab has a healthCheck, use its `expect` string in the "Checking Service Health" example
- **Include tunnel/infrastructure tabs** — these are common restart targets, always include them with explicit restart instructions
- **Tech-stack-specific health signals** — use appropriate health check strings (e.g., "listening" for Rust/Node servers, "Uvicorn running" for Python, "ready" for Vite)
## Step 8: Present Summary
After writing the configuration, show a summary table per workspace:
| Tab | Name | Purpose |
|---|---|---|
| 1 | 1 plan-stories | /plan-stories work |
| 2 | 2 plan-sweep | /plan-sweep |
| 3 | 3 review-open-prs | review-open-prs |
| 4 | 4 fix-pr-issues | fix-pr-issues |
| 5 | 5 merge-approved-prs | merge-approved-prs |
| 6 | 6 cleanup-merged | cleanup-merged |
| 7 | 7 docs-maintain | docs-maintain |
| 8 | 8 status | status dashboard |
| 9 | console | General-purpose shell (focused) |
| 10 | GS Reminder | make remind — step reference |
| 11 | GS Plans | Shell in docs/plans/ |
| 12 | GS Claude Plans | Claude session for plan work |
| Tab | Name | Command |
|---|---|---|
| 1 | GS Backend | make dev-backend |
| 2 | GS Frontend | make dev-frontend |
| ... |
- Added
## cmux Terminal Interactionsection with surface names, restart commands, and health checks - Agents can now find and interact with App workspace terminals
- Run
make cmuxto launch all workspaces - Or run
./scripts/cmux.pydirectly
## Updating Existing Configuration
If the user asks to modify an existing setup (add tabs, change commands, rename prefix):
1. Read the current `.vscode/terminals.json`
2. Identify the workspace/tab to modify
3. Make the change using the Edit tool (prefer surgical edits over full rewrites)
4. Show the before/after for the affected section
## Configuration Reference
### Tab Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | yes | Tab title displayed in cmux |
| `commands` | string[] | yes | Shell commands to execute |
| `type` | string | no | `"terminal"` (default) or `"browser"` |
| `execute` | bool | no | Auto-run commands on creation (default true) |
| `focus` | bool | no | Set as focused tab after creation |
| `notes` | string | no | Human-readable description of what this tab does, when to run it, and dependencies. Documentation-only field — not processed by cmux. |
| `input.delay` | number | no | Seconds to wait before sending first input line |
| `input.lines` | (string\|object)[] | no | Lines to send to the running process. Strings are submitted with Enter. Objects support: `{"text": "...", "submit": false, "delay": 2}` — `submit` (default true) controls whether Enter is pressed, `delay` (default 0) adds a pause in seconds before sending that line. |
| `healthCheck.wait` | number | no | Seconds to wait before checking |
| `healthCheck.expect` | string | no | Text pattern to match in output |
| `split.type` | string | no | `"browser"` or `"terminal"` for split pane |
| `split.direction` | string | no | `"right"` or `"down"` |
| `urls` | array | no | For browser tabs: `[{name, url}]` |
### AI Workflow Tab Startup Sequence
Each AI Workflow step tab uses two mechanisms in order:
| Step | Mechanism | Purpose |
|------|-----------|---------|
| 1 | `claude --dangerously-skip-permissions` in `commands` | Start Claude (bare, no positional prompt) |
| 2 | String in `input.lines` | `/rename` — sent with Enter after `input.delay` seconds |
| 3 | Object in `input.lines` with `"submit": false` | Task prompt — typed into input but NOT submitted. User presses Enter when ready. |
**Why user-controlled?** The workflow is sequential (intake → execute → review → fix → merge → cleanup → docs). Each step should only run when the previous step is done. By pre-typing the prompt without submitting, the user sees what will run and decides when to trigger it.
**Why no echo banners?** Step descriptions live in the `"notes"` field and are available via the Reminder tab (`make remind`). Echo banners clutter the terminal and are redundant.
**IMPORTANT:** Claude CLI does NOT have a `--prompt` flag. Do not use `--prompt`. The prompt is a positional argument (`claude 'text'`), but for the AI Workflow we use `input.lines` with `"submit": false` instead so the user controls execution.
### AI Workflow Utility Tabs
In addition to the 8 step tabs, the AI Workflow workspace includes:
| Tab | Purpose |
|-----|---------|
| `console` | Focused by default. General-purpose shell for ad-hoc commands. |
| `{PREFIX} Reminder` | Runs `make remind` to display all step names, prompts, and descriptions. |
| `{PREFIX} Plans` | Shell opened in `docs/plans/` for browsing implementation plans. |
| `{PREFIX} Claude Plans` | Claude session in `docs/plans/` for plan-related work. |
### Placeholders
| Placeholder | Replaced With |
|------------|---------------|
| `[workspaceFolder]` | Project root directory |
| `[workspaceFolderBasename]` | Directory name only |
| `[BRANCH]` | Current git branch |
| `[ENV_VAR]` | Value from `.env` file |