[FEATURE] Add /fork (conversation branching) support to VS Code extension

Status Open
Maintainer reply None cached
Activity 10 comments · opened Jun 18, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

The CLI supports conversation forking via --fork-session (and /fork slash command), allowing users to branch a conversation at any point and explore a different direction while preserving the original session. This capability is available by clicking on a previous message, but this creates an issue where the context is lost when the message is far back in the conversation.

Proposed Solution

/fork slash command available in the VS Code extension prompt box (alongside existing commands like /compact, /usage, etc.)

Forked session appears as a new tab, preserving full conversation history up to the fork point.

This will allow the user to fork the message at that current point instead of going far back in the history and losing valuable context.

Alternative Solutions

_No response_

Priority

Medium - Would be very helpful

Feature Category

CLI commands and flags

Use Case Example

_No response_

Additional Context

PLEASE add this! This would be very useful!

Often times I send a message and then Claude will send many, many messages and context back so it would be nice to "/fork" from that current point instead of clicking on that message I sent a long time ago to "fork conversation from here"

View original on GitHub ↗

6 Comments

ianwieds · 2 months ago

Want to give credit to @ahartzog for creating the original issue that was prematurely closed, which I copied some text from since it was well worded

ahartzog · 2 months ago

@ianwieds you _can_ sort of accomplish this with double-pressing escape but it's rather unintuitive and I think adding the /fork command would be very helpful

ianwieds · 2 months ago

@ahartzog yeah that method is okay, but it still doesn't have the latest context since it's applied to an older user message, unless I'm missing something?

In the meantime, I made a workaround that works well enough, although it's super hacky. It's a skill that runs a shell command with 2 methods:

  1. File-copy mode (default) — Copies the session's JSONL file with sed to swap in a new session ID, appends a title entry, and copies the session directory. Pure filesystem operation — faster, no CLI needed, no extra messages injected.
  1. CLI mode (--cli) — Calls claude --resume <id> --fork-session with --model haiku --effort low --safe-mode and a throwaway prompt to create the fork through the official mechanism. Slower and adds a small prompt/response to the new session's history.

In either case, it then opens a new VS Code panel with the forked chat session. Hacky, but it does effectively "fork from here".

SKILL.md

---
name: claude:fork
description: Fork the current conversation into a new Claude Code panel.
when_to_use: Use when the user wants to fork, branch, or split the current conversation into a new window/panel.
user-invocable: true
---

# Fork Conversation

Forks the current session into a new VS Code Claude Code panel with full conversation history.

## Steps

Run the fork script:

`bash ~/.claude/skills/claude:fork/fork.sh "$CLAUDE_CODE_SESSION_ID"`

The script handles everything: generates a UUID, auto-names the fork, forks via the CLI (haiku — cheap/fast), verifies the JSONL, and opens the new panel in VS Code.

Report the output to the user.

fork.sh

#!/usr/bin/env bash
# Fork the current Claude Code session into a new VS Code panel.
# Usage: fork.sh <session-id> [--cli]
#   Default: file-copy mode (instant, no CLI needed, no extra messages)
#   --cli:   use the Claude CLI (official fork, adds a small prompt/response)

set -euo pipefail

LOG_FILE="/tmp/claude-fork.log"

log() { printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*" | tee -a "$LOG_FILE"; }
die() { log "ERROR: $*" >&2; exit 1; }

log "=== Fork started ==="

SESSION_ID="${1:?Usage: fork.sh <session-id> [--cli]}"
MODE="copy"
[[ "${2:-}" == "--cli" ]] && MODE="cli"
log "Source session: $SESSION_ID (mode: $MODE)"

# Find the source JSONL
SESSION_JSONL=$(find ~/.claude/projects -name "${SESSION_ID}.jsonl" 2>/dev/null | head -1)
log "JSONL: ${SESSION_JSONL:-NOT FOUND}"
[[ -z "$SESSION_JSONL" ]] && die "No JSONL found for session $SESSION_ID"

# Extract current session name
CURRENT_NAME=""
CURRENT_NAME=$(grep -o '"customTitle":"[^"]*"' "$SESSION_JSONL" 2>/dev/null | tail -1 | sed 's/"customTitle":"//;s/"$//' || true)
if [[ -z "$CURRENT_NAME" ]]; then
  CURRENT_NAME=$(grep -o '"aiTitle":"[^"]*"' "$SESSION_JSONL" 2>/dev/null | tail -1 | sed 's/"aiTitle":"//;s/"$//' || true)
fi
log "Current name: ${CURRENT_NAME:-<unnamed>}"

# Build fork name: "X" → "X (fork)", "X (fork)" → "X (fork 2)", etc.
if [[ -z "$CURRENT_NAME" ]]; then
  FORK_NAME="fork"
elif [[ "$CURRENT_NAME" =~ \(fork\ ([0-9]+)\)$ ]]; then
  NEXT=$(( ${BASH_REMATCH[1]} + 1 ))
  FORK_NAME="${CURRENT_NAME% (fork *)} (fork ${NEXT})"
elif [[ "$CURRENT_NAME" =~ \(fork\)$ ]]; then
  FORK_NAME="${CURRENT_NAME% (fork)} (fork 2)"
else
  FORK_NAME="${CURRENT_NAME} (fork)"
fi
log "Fork name: $FORK_NAME"

NEW_SESSION=$(uuidgen | tr '[:upper:]' '[:lower:]')
log "New session: $NEW_SESSION"

PROJECT_DIR=$(dirname "$SESSION_JSONL")
NEW_JSONL="${PROJECT_DIR}/${NEW_SESSION}.jsonl"

if [[ "$MODE" == "copy" ]]; then
  # File-copy fork: replace session IDs, inject the new title
  log "Copying JSONL with replaced session IDs"
  sed "s/${SESSION_ID}/${NEW_SESSION}/g" "$SESSION_JSONL" > "$NEW_JSONL"

  # Inject a custom-title entry so the fork gets its name
  printf '{"type":"custom-title","sessionId":"%s","customTitle":"%s"}\n' \
    "$NEW_SESSION" "$FORK_NAME" >> "$NEW_JSONL"

  # Copy the session directory (subagents, tool-results) if it exists
  SRC_DIR="${PROJECT_DIR}/${SESSION_ID}"
  if [[ -d "$SRC_DIR" ]]; then
    cp -R "$SRC_DIR" "${PROJECT_DIR}/${NEW_SESSION}"
    log "Copied session directory"
  fi
else
  # CLI fork: official mechanism
  CLAUDE_BIN=$(command -v claude 2>/dev/null || echo "$HOME/.local/bin/claude")
  if [[ ! -x "$CLAUDE_BIN" ]]; then
    die "Claude CLI not found at $CLAUDE_BIN (install: curl -fsSL https://claude.ai/install.sh | bash)"
  fi
  log "CLI: $CLAUDE_BIN"

  log "Running: claude --resume ... --fork-session --print"
  if ! FORK_OUTPUT=$("$CLAUDE_BIN" --resume "$SESSION_ID" \
    --fork-session \
    --session-id "$NEW_SESSION" \
    --name "$FORK_NAME" \
    --model haiku \
    --effort low \
    --safe-mode \
    --system-prompt "Respond with exactly: Done!" \
    --print "Fork you!" 2>&1); then
    log "CLI output: $FORK_OUTPUT"
    die "Fork command failed (exit $?)"
  fi
  log "CLI completed"
fi

# Verify
if [[ ! -f "$NEW_JSONL" ]]; then
  die "No session file created at $NEW_JSONL"
fi
log "Verified: $NEW_JSONL"

open "vscode://anthropic.claude-code/open?session=$NEW_SESSION"
log "Opened in VS Code"

echo "SESSION_ID=$NEW_SESSION"
echo "NAME=$FORK_NAME"
echo "FILE=$NEW_JSONL"

log "=== Fork complete ==="
NTUWhitefox · 2 months ago

Isn't what you want the same as what /branch command in CLI version do? And yes, I do not find such functionality in VS Code extension version. Now sure why but I use /branch command often. It is appreciated to have this in VS code because I love GUI.

orcinus-evc · 2 months ago

This keeps getting dismissed as "just hit esc twice" when this is a completely different thing. The request is not for rewinding and forking from rewound point. The request is FOR FORKING FROM ACTIVE SESSION, WITHOUT THE REWIND. Also worth noting that claude-cli does not fork when rewinding, while claude for vscode does. Behaviors across these products are extremely inconsistent, with horrendous nomenclature.

ianwieds · 2 months ago

@orcinus-evc 100%! Didn't know all that about the inconsistent behavior, but I'm not surprised, lol.

Showing cached comments. Read the full discussion on GitHub ↗