Skip to content

Instantly share code, notes, and snippets.

@ferraridavide
Created February 24, 2026 10:21
Show Gist options
  • Select an option

  • Save ferraridavide/07e1e20ae4a644814997af41d6596816 to your computer and use it in GitHub Desktop.

Select an option

Save ferraridavide/07e1e20ae4a644814997af41d6596816 to your computer and use it in GitHub Desktop.
Parse chat export from GitHub Copilot
import json
from typing import Dict, Any, List, Optional
def get_invocation_message(item: Dict[str, Any]) -> Optional[str]:
"""Safely extracts invocation message whether it's a string or dict."""
invocation_msg_data = item.get("invocationMessage")
if not invocation_msg_data:
return None
if isinstance(invocation_msg_data, str):
return invocation_msg_data
return invocation_msg_data.get("value")
def process_chat_data(filepath: str) -> None:
"""Loads and processes chat response data from a JSON file."""
try:
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error loading '{filepath}': {e}")
return
# Safely navigate the JSON tree
requests = data.get("requests", [])
if not requests:
return
response = requests[0].get("response", [])
for item in response:
kind = item.get("kind")
if not kind:
handle_raw_text(item)
continue
match kind:
case "thinking":
handle_thinking(item)
case "toolInvocationSerialized":
handle_tool_invocation(item, response)
case "mcpServersStarting" | "undoStop" | "progressTaskSerialized" | "codeblockUri" | "textEditGroup" | "inlineReference":
continue
case _:
print(item)
def handle_raw_text(item: Dict[str, Any]) -> None:
"""Handles items that do not have a specific 'kind' categorization."""
value = item.get("value")
if value and value.strip() not in ("", "```"):
print(value)
def handle_thinking(item: Dict[str, Any]) -> None:
"""Formats and prints the thinking block."""
value = item.get("value")
if value and value.strip():
print(f"<thinking>\n{value}\n</thinking>")
def handle_tool_invocation(item: Dict[str, Any], full_response: List[Dict[str, Any]]) -> None:
"""Routes different tool invocations to their specific handlers."""
tool_data = item.get("toolSpecificData")
if not tool_data:
invocation_msg = get_invocation_message(item)
if invocation_msg:
print(f"<toolCall>\n{invocation_msg}\n</toolCall>")
return
tool_kind = tool_data.get("kind")
match tool_kind:
case "todoList":
handle_todo_list(tool_data.get("todoList"))
case "subagent":
handle_subagent(item, full_response)
case "input":
if "subAgentInvocationId" not in item:
invocation_msg = get_invocation_message(item)
if invocation_msg:
print(f"<toolCall>\n{invocation_msg}\n</toolCall>")
case "terminal":
if "subAgentInvocationId" not in item:
cmd = tool_data.get("commandLine", {}).get("original")
if cmd:
print(f"<terminal>\n{cmd}\n</terminal>")
case _:
print(item)
def handle_todo_list(todo_list: Any) -> None:
"""Formats and prints the todo list."""
if not isinstance(todo_list, list):
print(json.dumps(todo_list, indent=2, ensure_ascii=False))
return
status_icon = {
"not-started": "[ ]",
"in-progress": "[~]",
"completed": "[x]",
}
print("<todoList>")
for todo in todo_list:
title = todo.get("title", "").strip()
status = todo.get("status", "unknown")
icon = status_icon.get(status, "•")
print(f"{icon} {title}")
print("</todoList>")
def handle_subagent(item: Dict[str, Any], full_response: List[Dict[str, Any]]) -> None:
"""Processes subagent operations and groups related tool calls."""
tool_call_id = item.get("toolCallId", "")
tool_data = item.get("toolSpecificData", {})
print("<subagent>")
prompt = tool_data.get("prompt", "")
if prompt:
print(f"<prompt>\n{prompt}\n</prompt>")
print("<items>")
for related in full_response:
# Only process items linked to this specific subagent
if related.get("subAgentInvocationId") != tool_call_id:
continue
invocation_msg = get_invocation_message(related)
if invocation_msg:
print(f"<toolCall>{invocation_msg}</toolCall>")
related_tool_data = related.get("toolSpecificData", {})
if related_tool_data.get("kind") == "terminal":
cmd = related_tool_data.get("commandLine", {}).get("original")
if cmd:
print(f"<terminal>\n{cmd}\n</terminal>")
print("</items>")
result = tool_data.get("result", "")
if result:
print(f"<result>\n{result}\n</result>")
print("</subagent>")
process_chat_data("./chat.json")
@shdwkl
Copy link
Copy Markdown

shdwkl commented Feb 26, 2026

copilot has new format for chatsession jsonl

try out this extension https://marketplace.visualstudio.com/manage/publishers/imperium-dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment