Skip to content

[Feature Request] Add --include-history flag to output historical messages when resuming sessions #5135

Closed as not planned

Description

@Lewik

Problem

When resuming a session with claude --resume <session-id>, the CLI creates a new JSONL file and starts streaming only new messages. There's no built-in way to get historical messages from the previous session through the CLI's stdout stream.

This forces developers integrating Claude Code to implement complex workarounds:

  1. Manually parse JSONL files from ~/.claude/projects/
  2. Load historical messages before starting the CLI process
  3. Merge historical and streaming data with different parsing logic
  4. Handle deduplication and synchronization issues

Current Workaround

Currently, integrations must:

  • Read and parse .jsonl files directly from the filesystem
  • Implement custom JSONL parsers to extract historical messages
  • Load history separately before starting the CLI process
  • Maintain two different data pipelines (file-based for history, stream-based for new messages)
  • Handle potential format inconsistencies between parsed files and streamed output

This approach has several issues:

  • Duplicate parsing logic for files vs stream
  • Risk of format inconsistencies
  • Complex state management
  • No guarantee that file format won't change
  • Requires filesystem access permissions

Proposed Solution

Add an --include-history flag that streams historical messages before entering interactive mode:

claude --resume <session-id> --output-format json --include-history

Expected behavior:

  1. First, output all historical messages from the resumed session in chronological order
  2. Clearly mark the boundary between historical and new messages using one of these approaches:
    • Add an "isHistorical": true field to historical message entries
    • Output a special delimiter entry like {"type": "history_delimiter"} after all historical messages
    • Include metadata in each entry indicating its source (e.g., "source": "history" vs "source": "live")
  3. Then seamlessly transition to streaming new messages in real-time
  4. Use the same JSON format structure for both historical and new messages

Benefits

  • Unified data source: All messages come from stdout, no file parsing needed
  • Consistent format: Same parsing logic for all messages
  • Clear boundaries: Developers can easily distinguish historical from new messages
  • Simplified integration: Remove need for filesystem access and custom parsers
  • Future-proof: CLI handles any format changes internally
  • Better performance: No need to read potentially large JSONL files

Related Issue

This request is related to anthropics/claude-code-sdk-python#109, where we initially requested similar functionality for the Python SDK. However, this feature would be better implemented at the CLI level, making it available to all SDKs and integrations.

Use Cases

  • Chat UI applications that need to display conversation history
  • Session analysis tools
  • Debugging tools that need full conversation context

🤖 Generated with Claude Code

Activity

  1. coygeek commented on Aug 24, 2025

    @coygeek

    Hey, this is a really well-thought-out feature request. I've been building some tooling around the SDK myself and ran into the exact same issue. Having to build a separate file parser just to load the history is definitely a pain point, especially since the local file format could change in the future. A unified stream from stdout would be a huge improvement for developers.

    While we wait for the team to hopefully implement something like your --include-history flag, I've been using a workaround that combines locating the transcript file with launching the process. It's not perfect, and it still has the filesystem dependency you mentioned, but it might be a bit more structured than what you're doing now.

    Based on the documentation, we know a few key things:

    • The SessionStart hook receives a transcript_path in its JSON input
    • Conversations are stored locally, and the path includes the session ID

    This means the path is predictable if you have the session ID. The integration script that's launching claude can construct the path to the transcript file itself, read it, stream the history, and then immediately start streaming the live output from the claude subprocess.

    Here’s a rough sketch of how that might look in a Python script:

    import os
    import json
    import subprocess
    import glob
    
    def find_transcript_path(session_id: str) -> str | None:
        """Finds the full path to the transcript file for a given session ID."""
        base_path = os.path.expanduser("~/.claude/projects/*")
        # Search through all project directories for the session file
        for project_dir in glob.glob(base_path):
            transcript_file = os.path.join(project_dir, f"{session_id}.jsonl")
            if os.path.exists(transcript_file):
                return transcript_file
        return None
    
    def stream_claude_session_with_history(session_id: str, prompt: str):
        """
        A workaround to stream historical messages before live messages.
        """
        print(f"--- Resuming session {session_id} with history ---")
    
        # 1. Find and stream historical messages
        transcript_path = find_transcript_path(session_id)
        if transcript_path:
            print("--- Start of Historical Messages ---")
            try:
                with open(transcript_path, 'r') as f:
                    for line in f:
                        # You can add your "isHistorical: true" flag here
                        historical_message = json.loads(line)
                        historical_message['isHistorical'] = True
                        print(json.dumps(historical_message))
            except (IOError, json.JSONDecodeError) as e:
                print(f"Error reading history: {e}")
            print("--- End of Historical Messages ---")
        else:
            print(f"Warning: Could not find history file for session {session_id}")
    
        # 2. Launch the claude process to get live messages
        command = [
            "claude",
            "--resume", session_id,
            "-p", prompt,
            "--output-format", "stream-json"
        ]
        
        print("--- Start of Live Messages ---")
        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    
        # 3. Stream live messages from the subprocess stdout
        if process.stdout:
            for line in iter(process.stdout.readline, ''):
                # The output is already a JSON string per line
                print(line, end='')
    
        process.wait()
        
        # Optional: print any errors
        if process.stderr:
            errors = process.stderr.read()
            if errors:
                print(f"\n--- Errors ---\n{errors}")
    
    # --- Example Usage ---
    # You would get this session ID from a previous run
    # For example, by parsing the 'session_id' from the init message.
    SESSION_TO_RESUME = "your-session-id-here" 
    CONTINUATION_PROMPT = "Okay, now based on our previous conversation, please continue."
    
    # stream_claude_session_with_history(SESSION_TO_RESUME, CONTINUATION_PROMPT)

    This still has the downsides of relying on the file format, but it centralizes the logic in one place before you kick off the live stream. Your main application logic can just consume this unified stream.

    Anyway, just wanted to share my approach in case it helps. I'm definitely giving a +1 to your feature request. It would make building robust integrations so much easier. Cheers

  2. github-actions commented on Dec 7, 2025

    @github-actions

    This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

  3. github-actions commented on Jan 7, 2026

    @github-actions

    This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.

  4. github-actions commented on Jan 14, 2026

    @github-actions

    This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.

  5. locked as resolved and limited conversation to collaborators on Jan 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions