# How Claudian Plan Mode Works: Shift+Tab Toggling and Approval Workflows Explained

> Understand Claudian plan mode. Learn how Shift+Tab toggling and approval workflows streamline your planning process for enhanced safety and control.

- Repository: [YishenTu/claudian](https://github.com/YishenTu/claudian)
- Tags: how-to-guide
- Published: 2026-03-17

---

**Claudian plan mode** is a special permission state that disables the Safe/Yolo toggle, captures plan files written to `~/.claude/plans/`, and offers three approval options when planning completes.

The **YishenTu/claudian** repository implements a sophisticated planning workflow for AI-assisted coding sessions. Plan mode allows Claude to generate structured plans without immediately executing file changes, giving users control over when and how to proceed with generated recommendations.

## Entering and Exiting Plan Mode via Shift+Tab

### View-Level Key Handler in ClaudianView.ts

The global **Shift+Tab** shortcut toggles plan mode at the view level. In [`src/features/chat/ClaudianView.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/ClaudianView.ts) (lines 483-498), the keyboard handler intercepts the combination and switches between active planning and normal operation:

```typescript
// ClaudianView.ts – view-level key handler
if (e.key === 'Tab' && e.shiftKey && !e.isComposing) {
    e.preventDefault();
    const activeTab = this.tabManager?.getActiveTab();
    if (!activeTab) return;
    const current = this.plugin.settings.permissionMode;
    if (current === 'plan') {
        // leave plan mode → restore the mode that was saved before we entered it
        const restoreMode = activeTab.state.prePlanPermissionMode ?? 'normal';
        activeTab.state.prePlanPermissionMode = null;
        updatePlanModeUI(activeTab, this.plugin, restoreMode);
    } else {
        // enter plan mode → remember the current mode so we can go back to it later
        activeTab.state.prePlanPermissionMode = current;
        updatePlanModeUI(activeTab, this.plugin, 'plan');
    }
}

```

### State Preservation and Restoration

When entering plan mode, the current permission mode (`normal`, `yolo`, etc.) is stored in `tab.state.prePlanPermissionMode`. When exiting via **Shift+Tab** again, that saved value restores the previous state. This ensures users return to their exact original configuration after planning completes.

## UI Updates and Visual Indicators

### Toolbar Changes in InputToolbar.ts

Plan mode modifies the interface to indicate the special state. In [`src/features/chat/ui/InputToolbar.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/ui/InputToolbar.ts) (lines 66-73), the toolbar hides the standard permission toggle and displays a **PLAN** label:

```typescript
// InputToolbar.ts – permission label handling
if (mode === 'plan') {
    this.toggleEl.style.display = 'none';
    this.labelEl.setText('PLAN');
    this.labelEl.addClass('plan-active');
}

```

### CSS Classes and Permission Toggle

The `updatePlanModeUI()` function in [`src/features/chat/tabs/Tab.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/tabs/Tab.ts) (lines 1190-1195) persists the mode change and updates visual elements:

```typescript
// Tab.ts – UI helper
export function updatePlanModeUI(tab: TabData, plugin: ClaudianPlugin, mode: PermissionMode): void {
    plugin.settings.permissionMode = mode;
    void plugin.saveSettings();
    tab.ui.permissionToggle?.updateDisplay();
    tab.dom.inputWrapper.toggleClass('claudian-input-plan-mode', mode === 'plan');
}

```

The `claudian-input-plan-mode` CSS class applies to the input wrapper, enabling custom styling during planning sessions.

## Capturing Plan Files During Chat Streams

### StreamController.ts File Detection

During active chat streams, Claudian monitors **Write** tool calls to detect plan generation. The [`StreamController.ts`](https://github.com/YishenTu/claudian/blob/main/StreamController.ts) file (lines 69-88) captures any write operation targeting the `~/.claude/plans/` directory:

```typescript
// StreamController.ts – capture plan file path
if (chunk.name === TOOL_WRITE) {
    this.capturePlanFilePath(chunk.input);
}
...
private capturePlanFilePath(input: Record<string, unknown>): void {
    const filePath = input.file_path as string | undefined;
    if (filePath && filePath.replace(/\\/g, '/').includes('/.claude/plans/')) {
        this.deps.state.planFilePath = filePath;
    }
}

```

The captured path populates `ChatState.planFilePath`, making the generated plan available for UI components and approval workflows.

## The Approval Workflow and Exit Plan Mode

### InlineExitPlanMode Component

When planning completes, the **InlineExitPlanMode** component (defined in [`src/features/chat/rendering/InlineExitPlanMode.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rendering/InlineExitPlanMode.ts)) presents three distinct actions:

1. **Approve (new session)** – Creates a fresh chat session pre-loaded with the plan text
2. **Approve (current session)** – Continues in the existing session, discarding the plan file
3. **Feedback** – Allows free-form input to refine the current plan

The approve-new-session handler (lines 74-86) extracts plan content and signals the resolution:

```typescript
// InlineExitPlanMode.ts – approve-new-session handling
newSessionRow.addEventListener('click', () => {
    this.focusedIndex = 0;
    this.updateFocus();
    this.handleResolve({
        type: 'approve-new-session',
        planContent: this.extractPlanContent(),
    });
});

```

### Session Management and Plan Continuation

When **approve-new-session** is selected, the plan content is stored in `tab.state.pendingNewSessionPlan`. After the current stream terminates (`tab.state.cancelRequested = true`), the `ConversationController` creates a new session and auto-sends the approved plan text.

### Callback Implementation in Tab.ts

The exit-plan-mode callback in [`src/features/chat/tabs/Tab.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/tabs/Tab.ts) (lines 1164-1180) manages the transition logic:

```typescript
// Tab.ts – exit-plan-mode callback
tab.service.setExitPlanModeCallback(
    async (input, signal) => {
        const decision = await tab.controllers.inputController?.handleExitPlanMode(input, signal) ?? null;
        // Revert only on approve; feedback and cancel keep plan mode active.
        if (decision !== null && decision.type !== 'feedback') {
            // Restore permission mode only if we are still in plan mode (user may have toggled out via Shift+Tab)
            if (plugin.settings.permissionMode === 'plan') {
                const restoreMode = tab.state.prePlanPermissionMode ?? 'normal';
                tab.state.prePlanPermissionMode = null;
                updatePlanModeUI(tab, plugin, restoreMode);
            }
            if (decision.type === 'approve-new-session') {
                // Store plan content so the next session can auto-send it
                tab.state.pendingNewSessionPlan = decision.planContent;
                tab.state.cancelRequested = true;   // ends the current stream
            }
        }
        return decision;
    }
);

```

This callback respects manual **Shift+Tab** toggles—if the user already exited plan mode via keyboard before approving, the UI state remains unchanged.

## State Management and Persistence

### ChatState Type Definitions

The planning workflow relies on three critical fields defined in [`src/features/chat/state/types.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/state/types.ts) (lines 100-108):

```typescript
// state/types.ts – plan-related fields
pendingNewSessionPlan: string | null;   // plan to send in the next session
planFilePath: string | null;            // path of the written plan file
prePlanPermissionMode: PermissionMode | null; // mode saved before entering plan mode

```

These values persist across stream events and clear automatically when exiting plan mode or initializing new sessions.

## Summary

- **Shift+Tab** toggles plan mode globally in [`ClaudianView.ts`](https://github.com/YishenTu/claudian/blob/main/ClaudianView.ts), preserving the previous permission state in `prePlanPermissionMode` for later restoration.
- Plan mode hides the standard Safe/Yolo toggle and displays a **PLAN** label via [`InputToolbar.ts`](https://github.com/YishenTu/claudian/blob/main/InputToolbar.ts), applying the `claudian-input-plan-mode` CSS class.
- The system captures plan files written to `~/.claude/plans/` during chat streams through [`StreamController.ts`](https://github.com/YishenTu/claudian/blob/main/StreamController.ts) and stores the path in `ChatState`.
- The approval workflow offers three choices: approve for new session (pre-loads plan content), approve for current session, or provide feedback for iteration.
- Exiting plan mode restores the original permission settings only if the user hasn't already manually toggled out using **Shift+Tab**.

## Frequently Asked Questions

### What is plan mode in Claudian?

Plan mode is a special permission state that disables automatic execution toggles and allows Claude to generate structured planning documents without immediately modifying project files. When active, it displays a **PLAN** label in the toolbar and captures any files written to the `~/.claude/plans/` directory for review before implementation.

### How do I toggle plan mode using the keyboard?

Press **Shift+Tab** to enter or exit plan mode. This shortcut is handled in [`src/features/chat/ClaudianView.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/ClaudianView.ts) and works across all active tabs. When entering, your current permission mode (normal, yolo, etc.) is saved; when exiting, that mode is automatically restored unless you've already approved a plan through the UI workflow.

### What happens when I approve a plan for a new session?

Selecting **Approve (new session)** stores the plan content in `tab.state.pendingNewSessionPlan`, terminates the current chat stream, and creates a fresh session that automatically sends the plan text as the opening message. This isolates the planning phase from implementation while preserving the generated strategy for execution.

### Where does Claudian store the plan file path?

The plan file path is stored in `ChatState.planFilePath`, defined in [`src/features/chat/state/types.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/state/types.ts). The [`StreamController.ts`](https://github.com/YishenTu/claudian/blob/main/StreamController.ts) populates this field when it detects **Write** tool calls targeting the `~/.claude/plans/` directory, making the path available to UI components like `InlineExitPlanMode` for preview and approval actions.