commands/session/ — Session Management Commands

Remote session display, conversation persistence, and cross-session navigation

The /session command displays a QR code and URL for the active remote session. The broader session ecosystem includes /resume, /export, and /branch commands for listing, resuming, exporting, and forking conversation sessions stored as JSONL transcripts.

2
Files
4
Related Commands
JSONL
Storage Format
3
Session ID Formats

📚 What is this & why does it exist?

The session module is the persistence backbone of Claude Code conversations. Every conversation is a "session" identified by a UUID and stored as a JSONL file. The /session command itself focuses on remote access—displaying a QR code so users can connect to a headless Claude Code instance from another device. The broader session ecosystem (/resume, /export, /branch) enables users to pick up where they left off, share transcripts, and fork conversations into new exploration paths.

💡 Real-world analogy

Sessions work like a journal with numbered notebooks. Each conversation is a notebook (JSONL file) stored on a shelf organized by project. /session is like showing a QR code on the notebook cover so someone else can scan it and read along remotely. /resume is pulling a specific notebook off the shelf and reopening it to the last page. /export is photocopying the notebook into a clean plaintext printout. /branch is duplicating the notebook up to a certain page and starting fresh from there—the new copy tracks which original it was forked from.

Contents

Architecture

flowchart TD
  SESSION["/session command"] -->|"load()"| STSX["session.tsx"]
  STSX -->|"useAppState()"| RURL["remoteSessionUrl"]
  STSX -->|"qrToString()"| QR["QR Code Display"]
  STSX -->|"useKeybinding(ESC)"| DONE["onDone()"]

  RESUME["/resume command"] -->|"loadAllProjectsMessageLogs()"| LOGS["Session JSONL Files"]
  RESUME -->|"loadFullLog()"| FULL["Full Transcript"]
  RESUME -->|"restoreSessionMetadata()"| RESTORE["Session Restore"]

  EXPORT["/export command"] -->|"renderMessagesToPlainText()"| TXT["Plaintext File"]

  BRANCH["/branch command"] -->|"createFork()"| FORK["Forked JSONL"]
  FORK -->|"forkedFrom metadata"| LINEAGE["Fork Lineage"]

  LOGS -->|"~/.claude/sessions/"| DISK["Filesystem"]
  LOGS -->|".index/ lite logs"| LITE["Lite Log Index"]

Session Lifecycle

sequenceDiagram
  participant U as User
  participant B as Bootstrap
  participant S as SessionStorage
  participant A as AppState

  U->>B: claude --remote
  B->>A: Set remoteSessionUrl
  B->>S: Create session JSONL
  Note over S: ~/.claude/sessions/{projectHash}/{id}.jsonl

  U->>U: /session
  A-->>U: Display QR + URL

  U->>U: /resume
  S-->>U: List sessions (lite logs)
  U->>S: Select session
  S->>S: loadFullLog() if lite
  S->>A: restoreSessionMetadata()

  U->>U: /export
  S-->>U: renderMessagesToPlainText()

  U->>U: /branch
  S->>S: createFork() with new UUID
  Note over S: Copies messages up to branch point

Key Classes & Functions

Class / FunctionFilePurpose
call()session.tsxEntry point: renders <SessionInfo> component showing QR and URL
SessionInfosession.tsxReact component: reads remoteSessionUrl from AppState, generates QR
getIsRemoteMode()bootstrap/state.tsReturns whether Claude Code was launched with --remote flag
getSessionId()bootstrap/state.tsReturns the current session UUID
switchSession()bootstrap/state.tsSwitches the active session to a different session ID
loadAllProjectsMessageLogs()sessionStorage.tsLoads session metadata from all projects for the resume picker
loadSameRepoMessageLogs()sessionStorage.tsLoads sessions matching current repository worktree paths
loadFullLog()sessionStorage.tsExpands a lite log into the full transcript with all messages
isLiteLog()sessionStorage.tsChecks if a log entry is a compact index vs full transcript
saveCustomTitle()sessionStorage.tsPersists a user-chosen title for a session
restoreSessionMetadata()sessionStorage.tsRestores attribution, file history, and state for a resumed session
renderMessagesToPlainText()exportRenderer.tsConverts session messages to a human-readable plaintext format
createFork()branch.tsDuplicates a session up to a branch point with new UUID and forkedFrom
parseSessionIdentifier()sessionUrl.tsParses session input as JSONL path, UUID, or URL
qrToString()qrcode (lib)Converts a URL to UTF-8 ASCII art QR code

Data Structures

TypeNameDescription
typeLogOptionSession metadata: sessionId, sessionTitle, lastUpdated, cwd, branch, isLiteLog
typeParsedSessionUrlParsed identifier: sessionId, ingressUrl, isUrl, jsonlFile, isJsonlFile
typeTranscriptMessageSingle message: type (user/assistant/system), message, uuid, timestamp, cwd
typeLocalJSXCommandCallCommand entry signature: (onDone, context?, args?) => Promise<ReactNode>
formatJSONLJSON Lines format—each line is one entry (message, snapshot, metadata)
stateremoteSessionUrlAppState field: URL for remote session access (undefined if local)
stateremoteConnectionStatusAppState field: 'connecting' | 'connected' | 'reconnecting' | 'disconnected'
path~/.claude/sessions/{hash}/{id}.jsonlOn-disk session storage path, organized by project hash
path.index/Lite log index folder for fast session search without full load

Interactions

DirectionPartnerInterfaceDescription
→state/AppStateuseAppState(s => s.remoteSessionUrl)Reads the remote session URL for QR code generation
→bootstrap/stategetIsRemoteMode()Gates command visibility—only shown when --remote is active
→bootstrap/stategetSessionId() / switchSession()Get current session ID or switch to a resumed session
→sessionStorageloadAllProjectsMessageLogs()Discovers all sessions across projects for the resume picker
→sessionStorageloadFullLog()Expands a lite log into full transcript on demand
→exportRendererrenderMessagesToPlainText()Serializes messages for /export output
→qrcodeqrToString(url, opts)Generates ASCII QR code with error correction level L
→hooks/useKeybindinguseKeybinding('escape', onDone)Dismisses the session info panel on Escape
←commands.ts registrydefault exportCommand metadata with aliases: ['remote'], conditional isEnabled
↔getWorktreePathsgetWorktreePaths()Resolves project paths for cross-project resume validation

Configuration

Knob / ParameterDefaultDescription
--remotefalseCLI flag that enables remote session mode and makes /session visible
type: 'utf8'utf8QR code output type (UTF-8 ASCII art for terminal display)
errorCorrectionLevel: 'L'LQR error correction level (Low = smaller code, faster render)
~/.claude/sessions/—Base directory for all session JSONL files, organized by project hash
.index/—Lite log index subfolder for fast session discovery
forkedFrom—Metadata field in branched sessions tracking parent session ID
sessionTitleauto-derivedCustom or auto-derived title from first user prompt

Directory Map

Source tree for session
session/
├── index.ts              ← Command registration (type: 'local-jsx', aliases: ['remote'])
└── session.tsx            ← SessionInfo component: QR code + URL display (~140 lines)

Related commands (separate directories):
├── resume/               ← /resume: session listing, search, and restore
├── export/               ← /export: plaintext transcript export
└── branch/               ← /branch: fork conversation with lineage tracking

Deep-Dive Highlights

Core mechanisms from the session ecosystem have been extracted into dedicated focus areas for in-depth exploration.

JSONL Persistence Engine Focus

Append-only JSONL storage, lite log indexing for fast search, on-demand full log expansion, and per-project hash-based directory organization.

sessionStorage.ts

Session Resume Flow Focus

Cross-project discovery, worktree validation, metadata restoration (attribution, file history, todo lists), and session switching via bootstrap state.

resume/ + restoreSessionMetadata()

Fork & Branch Tracking Focus

Session duplication up to a branch point, forkedFrom lineage metadata, new UUID generation, and first-prompt derivation for branch titles.

branch/ + createFork()

Remote Session & QR Access Focus

Remote mode detection, AppState URL propagation, QR code generation with UTF-8 encoding, and connection status tracking.

session.tsx + bootstrap/state.ts

Code Examples

Example 1: Command Registration (Conditional Visibility)

// index.ts — Session command with conditional enablement
import type { Command } from '../../commands.js'
import { getIsRemoteMode } from '../../bootstrap/state.js'

const session = {
  type: 'local-jsx',
  name: 'session',
  aliases: ['remote'],
  description: 'Show remote session URL and QR code',
  isEnabled: () => getIsRemoteMode(),
  isHidden: !getIsRemoteMode(),
  load: () => import('./session.js'),
} satisfies Command

export default session

Example 2: SessionInfo Component with QR Generation

// session.tsx — SessionInfo component (simplified)
import React, { useState, useEffect } from 'react'
import { Box, Text } from 'ink'
import { useAppState } from '../../state/AppState.js'
import { useKeybinding } from '../../hooks/useKeybinding.js'
import { qrToString } from 'qrcode'
import { Pane } from '../../components/Pane.js'

function SessionInfo({ onDone }: { onDone: () => void }) {
  const remoteSessionUrl = useAppState(s => s.remoteSessionUrl)
  const [qrCode, setQrCode] = useState<string | null>(null)

  // Generate QR code when URL becomes available
  useEffect(() => {
    if (!remoteSessionUrl) return
    qrToString(remoteSessionUrl, {
      type: 'utf8',
      errorCorrectionLevel: 'L',
    }).then(setQrCode)
  }, [remoteSessionUrl])

  // Dismiss on Escape
  useKeybinding('escape', onDone)

  if (!remoteSessionUrl) {
    return <Text color="yellow">No remote session active</Text>
  }

  return (
    <Pane title="Remote Session">
      <Box flexDirection="column" alignItems="center">
        {qrCode && <Text>{qrCode}</Text>}
        <Text color="cyan">{remoteSessionUrl}</Text>
        <Text dimColor>Press ESC to dismiss</Text>
      </Box>
    </Pane>
  )
}

export const call: LocalJSXCommandCall = async (onDone) => {
  return <SessionInfo onDone={onDone} />
}

Example 3: Session Identifier Parsing

// sessionUrl.ts — Accepts 3 formats for session identification
import { parseSessionIdentifier } from '../utils/sessionUrl.js'

// Format 1: JSONL file path
const fromFile = parseSessionIdentifier('/path/to/export.jsonl')
// => { jsonlFile: '/path/to/export.jsonl', isJsonlFile: true, ... }

// Format 2: UUID
const fromUuid = parseSessionIdentifier('550e8400-e29b-41d4-a716-446655440000')
// => { sessionId: '550e8400-...', isUrl: false, isJsonlFile: false }

// Format 3: Remote URL
const fromUrl = parseSessionIdentifier('https://api.example.com/v1/session/abc')
// => { sessionId: 'abc', ingressUrl: 'https://api.example.com', isUrl: true }