Skip to content

feat: Full REPL Mode - native CLI integration for Chat tab with session sync #545

@ivnnv

Description

@ivnnv

Problem

The Chat tab and Shell tab in Claude Code UI operate as completely separate systems:

  • Chat tab uses the @anthropic-ai/claude-agent-sdk to call query(). Separate process, separate sessions, separate conversation history.
  • Shell tab spawns the native claude CLI as a PTY process via node-pty. Full native behavior.

This means:

  • Conversations started in Chat cannot be resumed in Shell (or vice versa)
  • Permissions granted in the native CLI (~/.claude/settings.json) are not honored by the Chat tab
  • MCP servers, plugins, hooks, and CLAUDE.md are loaded differently between the two
  • Session history lives in two places (localStorage for Chat, ~/.claude/projects/ for Shell/CLI)
  • Features added to the native CLI require reimplementation in the SDK integration layer

Proposed Solution

Add a Full REPL Mode that, when enabled, makes the Chat tab spawn the native claude CLI (with --output-format stream-json --print) instead of using the SDK. The JSON events are mapped to the same WebSocket message format the Chat UI already expects, so the existing React components render them without changes.

Key features:

  1. Settings panel toggle under Claude > Permissions to enable/disable Full REPL Mode (persisted in localStorage)
  2. Native CLI spawning for Chat queries with --output-format stream-json --verbose --print
  3. Bidirectional session sync between Chat and Shell tabs via a server-side session registry. Sessions started in Chat can be resumed in Shell and vice versa, using --resume
  4. Permission parity with the native CLI (reads ~/.claude/settings.json)
  5. "Remember" persistence writes approved tools back to ~/.claude/settings.json
  6. MCP server info banner in Chat tab indicating MCP tools are available in Shell (since --print mode can't efficiently load MCP servers without blocking)

Architecture:

  • Chat tab spawns bash -c 'claude --output-format stream-json ...' via node-pty
  • Server parses JSONL output and maps CLI events (system/init, assistant, user, result) to existing Chat UI WebSocket message types (session-created, claude-response, token-budget, claude-complete)
  • Session registry (projectPath -> sessionId) enables seamless handoff: when switching tabs, the active CLI process is killed and the new tab resumes with --resume
  • MCP servers are skipped in --print mode (adds 30s+ of timeout for failing servers) but fully available in the Shell tab's interactive REPL

Scope & limitations:

  • This implementation covers Claude provider only. Other providers (Cursor, Codex, Gemini) continue using their existing integration and could adopt a similar pattern in the future
  • MCP server tools are not available in Chat's --print mode due to CLI startup overhead. The Shell tab provides full MCP access
  • The --print flag means each Chat message spawns a fresh CLI process. A future optimization could use a persistent process with --input-format stream-json if the CLI adds support for that mode outside of VS Code's internal SDK

Files Changed

File Type Description
server/utils/settings-reader.js New Reads/caches/writes ~/.claude/settings.json with mtime-based caching
server/claude-cli-query.js New CLI-based query runner, JSONL parser, session registry, event mapping
server/claude-sdk.js Modified Full REPL Mode permission overrides in mapCliOptionsToSDK, persist approvals to disk
server/routes/settings.js Modified /api/settings/repl-mode endpoint for UI config status
server/index.js Modified Route claude-command to CLI or SDK, session handoff between tabs
src/components/chat/utils/chatStorage.ts Modified isFullReplModeActive() / setFullReplMode() helpers
src/components/chat/hooks/useChatComposerState.ts Modified Pass fullReplMode flag in WebSocket messages
src/components/chat/view/ChatInterface.tsx Modified MCP tools banner when Full REPL Mode active
src/components/settings/.../PermissionsContent.tsx Modified Full REPL Mode toggle UI with settings.json status display

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions