Transcript Viewer Skill
Purpose
This skill converts and browses session transcripts from two supported tools:
-
Claude Code — JSONL logs auto-saved to ~/.claude/projects/
-
GitHub Copilot CLI — JSONL logs auto-saved to ~/.copilot/session-state/*/events.jsonl
It provides four browsing modes:
-
Current session — View the active session's transcript
-
Specific session — View a session by its ID
-
Agent output — View background task output files produced by subagents
-
All sessions — Browse all project sessions, with optional date-range filtering
Tool Context Auto-Detection
Before browsing, detect which tool is active to set default log paths. Prefer directory-based detection (more reliable than env vars); fall back to env vars from src/amplihack/hooks/launcher_detector.py when directories don't exist:
Primary: directory-based detection (most reliable)
if [[ -d "$HOME/.copilot/session-state" ]]; then
Check if there are any sessions present
COPILOT_SESSIONS=$(ls -d "$HOME/.copilot/session-state/"/ 2>/dev/null | wc -l) CLAUDE_SESSIONS=$(ls "$HOME/.claude/projects/"/*.jsonl 2>/dev/null | wc -l) if [[ "$COPILOT_SESSIONS" -gt 0 && "$CLAUDE_SESSIONS" -eq 0 ]]; then TOOL_CONTEXT="copilot" DEFAULT_LOG_DIR="$HOME/.copilot/session-state" elif [[ "$CLAUDE_SESSIONS" -gt 0 ]]; then TOOL_CONTEXT="claude-code" DEFAULT_LOG_DIR="$HOME/.claude/projects" else # Both dirs exist but empty — use env vars to decide TOOL_CONTEXT="claude-code" DEFAULT_LOG_DIR="$HOME/.claude/projects" fi elif [[ -d "$HOME/.claude/projects" ]]; then TOOL_CONTEXT="claude-code" DEFAULT_LOG_DIR="$HOME/.claude/projects"
Fallback: env var detection (same vars as launcher_detector.py)
elif [[ -n "${CLAUDE_CODE_SESSION:-}${CLAUDE_SESSION_ID:-}${ANTHROPIC_API_KEY:-}" ]]; then TOOL_CONTEXT="claude-code" DEFAULT_LOG_DIR="$HOME/.claude/projects" elif [[ -n "${GITHUB_COPILOT_TOKEN:-}${COPILOT_SESSION:-}" ]]; then
Note: GITHUB_TOKEN is intentionally excluded — it's too generic and
appears in non-Copilot CI contexts, causing false positives.
TOOL_CONTEXT="copilot" DEFAULT_LOG_DIR="$HOME/.copilot/session-state" else
Default to claude-code — safe fallback (most users)
TOOL_CONTEXT="claude-code" DEFAULT_LOG_DIR="$HOME/.claude/projects" fi
When both ~/.copilot/session-state/ and ~/.claude/projects/ exist with sessions, offer the user a choice: "Found sessions for both Claude Code and GitHub Copilot CLI. Which would you like to browse? [1] Claude Code [2] GitHub Copilot CLI"
The user can always override with an explicit path.
Log Format Auto-Detection
When given a file path, detect its format before processing:
detect_log_format() { local file="$1" if [[ "$file" == *.jsonl ]]; then echo "jsonl" elif [[ "$file" == *.md ]]; then # Check for Copilot /share export signature — specific header only # Note: do NOT match on "/share" alone (too generic — appears in docs, READMEs) if grep -q "Copilot Session Export|copilot-session" "$file" 2>/dev/null; then echo "copilot-markdown" else echo "markdown" fi elif [[ "$file" == *.log ]]; then # .log files may be plain text or agent JSONL; check content local first_char first_char=$(head -c 1 "$file" 2>/dev/null) if [[ "$first_char" == "{" ]]; then echo "jsonl" else echo "plain-log" fi else # Inspect first byte for other extensions local first_char first_char=$(head -c 1 "$file" 2>/dev/null) if [[ "$first_char" == "{" ]]; then echo "jsonl" else echo "unknown" fi fi }
Format Handler
jsonl
Pass to claude-code-log
copilot-markdown
Display inline (already readable Markdown)
markdown
Display inline
unknown
Warn and display raw
Tool Detection
Before running any command, check whether claude-code-log is available:
Step 1: direct install check
which claude-code-log 2>/dev/null
Step 2: npx fallback
npx --yes claude-code-log --version 2>/dev/null
Set CCL to the resolved command:
if which claude-code-log &>/dev/null; then CCL="claude-code-log" elif npx --yes claude-code-log --version &>/dev/null 2>&1; then CCL="npx claude-code-log" else CCL="" fi
Missing Tool — Graceful Error
If CCL is empty, display this message and stop:
claude-code-log is not installed.
To install it globally: npm install -g claude-code-log
Or run without installing (requires npx): npx claude-code-log --help
After installing, retry your request.
Do not attempt to install it automatically.
Modes
Mode 1: Current Session
Trigger phrases: "view current transcript", "show my current session", "current log"
What to do:
For Claude Code (TOOL_CONTEXT="claude-code" ):
-
Find the most recently modified JSONL file under ~/.claude/projects/ : ls -t ~/.claude/projects//.jsonl 2>/dev/null | head -1
-
Run: $CCL <path-to-jsonl> --format markdown
-
Display the Markdown output inline.
For GitHub Copilot CLI (TOOL_CONTEXT="copilot" ):
-
Find the most recently modified session directory under ~/.copilot/session-state/ : ls -dt ~/.copilot/session-state/*/ 2>/dev/null | head -1
-
Read its events.jsonl : LATEST_SESSION=$(ls -dt ~/.copilot/session-state/*/ 2>/dev/null | head -1) $CCL "${LATEST_SESSION}events.jsonl" --format markdown
-
Display the Markdown output inline.
Example output:
Session: 2025-11-23 19:32
Model: claude-sonnet-4-6 Messages: 42
User: Fix the authentication bug in login.py Assistant: I'll examine the file... ...
Mode 2: Specific Session by ID
Trigger phrases: "view session ", "show transcript ", "open log "
What to do:
For Claude Code (TOOL_CONTEXT="claude-code" ):
- Search for the JSONL file matching the session ID: find ~/.claude/projects -name "*.jsonl" | xargs grep -l "<SESSION_ID>" 2>/dev/null | head -1
Or, if the ID looks like a filename fragment, use: ls ~/.claude/projects/*/ | grep "<SESSION_ID>"
-
Run: $CCL <path-to-jsonl> --format markdown
-
Display the output. If no file matches, report: No session found with ID: <SESSION_ID> Available sessions: run "view all sessions" to list them.
For GitHub Copilot CLI (TOOL_CONTEXT="copilot" ):
- The session ID is the directory name under ~/.copilot/session-state/ . Check directly: SESSION_DIR="$HOME/.copilot/session-state/<SESSION_ID>" if [[ -d "$SESSION_DIR" ]]; then EVENTS_FILE="$SESSION_DIR/events.jsonl" fi
If the full ID is not known, search for a partial match: ls -d ~/.copilot/session-state/*/ 2>/dev/null | grep "<SESSION_ID>"
-
Run: $CCL "$EVENTS_FILE" --format markdown
-
Display the output. If no directory matches, report: No Copilot session found with ID: <SESSION_ID> Available sessions: run "browse all sessions" to list them.
Mode 3: Agent Background Task Output
Trigger phrases: "view agent output", "show background task output", "agent log"
What to do:
-
Find .log or .jsonl files created by background agent tasks. These are typically written to the current working directory or a temp path with a name matching .agent-step-.log or similar: ls -t .agent-step-.log 2>/dev/null ls -t /tmp/.agent.log 2>/dev/null
-
For each file found, run detect_log_format <file> to classify it:
-
jsonl → run $CCL <file> --format markdown
-
plain-log → display directly with cat
-
This ensures JSONL-formatted .log files (rare but possible) are rendered properly.
-
If no agent output files are found: No agent background task output files found in the current directory. Background agents write their output to files named .agent-step-<ID>.log.
Mode 4: All Sessions (Browse)
Trigger phrases: "browse all sessions", "list transcripts", "view all sessions", "show session history"
What to do:
For Claude Code (TOOL_CONTEXT="claude-code" ):
-
List all JSONL files under ~/.claude/projects/ : find ~/.claude/projects -name "*.jsonl" -printf "%T@ %p\n" 2>/dev/null
| sort -rn | awk '{print $2}' -
For each file, extract the session date and first user message: $CCL <file> --format markdown --summary
If --summary flag is not supported, just show filename and date
head -1 <file> | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('timestamp',''))"
- Print a summary table: Available Sessions (Claude Code) =================================
Date Session ID / File
1 2025-11-23 19:32:36 ~/.claude/projects/foo/abc123.jsonl 2 2025-11-22 14:10:05 ~/.claude/projects/foo/def456.jsonl ...
- Offer to open a specific session: "Enter a number to view that session."
For GitHub Copilot CLI (TOOL_CONTEXT="copilot" ):
-
List all session directories under ~/.copilot/session-state/ sorted by modification time: ls -dt ~/.copilot/session-state/*/ 2>/dev/null
-
For each session directory, read its events.jsonl to extract the timestamp: for session_dir in $(ls -dt ~/.copilot/session-state/*/); do session_id=$(basename "$session_dir") events_file="$session_dir/events.jsonl" if [[ -f "$events_file" ]]; then timestamp=$(head -1 "$events_file" | python3 -c
"import sys,json; d=json.loads(sys.stdin.read()); print(d.get('timestamp',''))" 2>/dev/null) echo "$timestamp $session_id" fi done -
Print a summary table: Available Sessions (GitHub Copilot CLI) ========================================
Date Session ID
1 2025-11-23 19:32:36 abc1234567890abcdef1234567890abcd 2 2025-11-22 14:10:05 def4567890abcdef1234567890abcdef ...
- Offer to open a specific session: "Enter a number to view that session."
Date-Range Filtering
When the user specifies a date range (e.g., "last 7 days", "between 2025-11-01 and 2025-11-30"):
Filter by modification time (last N days)
find ~/.claude/projects -name "*.jsonl" -mtime -7
Filter by date range using find -newer
find ~/.claude/projects -name "*.jsonl"
-newer /tmp/start_date_ref
! -newer /tmp/end_date_ref
Create the reference files with touch -d :
touch -d "2025-11-01" /tmp/start_date_ref touch -d "2025-11-30" /tmp/end_date_ref
Output Formats
Markdown (default)
Pass --format markdown to claude-code-log . The output is printed inline in the conversation. Best for quick reading in the terminal or Claude Code.
HTML
Pass --format html to claude-code-log . Write the output to a file and open it:
$CCL <file> --format html > /tmp/transcript-view.html
open /tmp/transcript-view.html 2>/dev/null
|| xdg-open /tmp/transcript-view.html 2>/dev/null
|| echo "HTML saved to /tmp/transcript-view.html — open it in your browser."
The user can request HTML explicitly: "view transcript as HTML" or "export to HTML".
GitHub Copilot CLI Support
Automatic Log Persistence
GitHub Copilot CLI automatically saves session data to disk at:
~/.copilot/session-state/{session-id}/
Each session directory contains:
-
events.jsonl — JSONL session history (similar format to Claude Code logs)
-
workspace.yaml — session metadata (working directory, session ID, etc.)
-
plan.md — implementation plan for the session
-
checkpoints/ — compaction history (older event snapshots)
Legacy sessions may also exist at ~/.copilot/history-session-state/ .
JSONL Format
Copilot events.jsonl contains one JSON object per line, similar to Claude Code format:
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Fix the bug"}]},"timestamp":"2025-11-23T19:32:36Z","sessionId":"abc1234567890"} {"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"I'll examine the file..."}]},"timestamp":"2025-11-23T19:32:40Z","sessionId":"abc1234567890"}
claude-code-log handles parsing these files since the format is compatible.
Viewing a Copilot Session
When the user wants to view a Copilot session (by directory detection or explicit path):
- Locate the events.jsonl for the session:
Current session (most recent)
LATEST_SESSION=$(ls -dt ~/.copilot/session-state/*/ 2>/dev/null | head -1) EVENTS_FILE="${LATEST_SESSION}events.jsonl"
Specific session by ID
EVENTS_FILE="$HOME/.copilot/session-state/<SESSION_ID>/events.jsonl"
-
Run: $CCL "$EVENTS_FILE" --format markdown
-
Display the output inline.
Detecting Available Sessions
To check if Copilot sessions exist:
if [[ -d "$HOME/.copilot/session-state" ]]; then SESSION_COUNT=$(ls -d "$HOME/.copilot/session-state/"*/ 2>/dev/null | wc -l) if [[ "$SESSION_COUNT" -gt 0 ]]; then echo "Found $SESSION_COUNT Copilot session(s)" fi fi
Full Workflow
When the user invokes this skill, follow this decision tree:
-
Detect tool context (directory-based first, then env var fallback):
- ~/.copilot/session-state/ exists with sessions → copilot
- ~/.claude/projects/ exists with sessions → claude-code
- Both exist with sessions → offer user a choice
- Directory detection fails, use env vars:
- CLAUDE_CODE_SESSION / CLAUDE_SESSION_ID / ANTHROPIC_API_KEY set → claude-code
- GITHUB_COPILOT_TOKEN / COPILOT_SESSION set → copilot
- Neither → default to claude-code (safe fallback)
-
Detect CCL (which claude-code-log / npx fallback) → If missing: show install instructions and STOP → Required for ALL modes (both Claude Code and Copilot use JSONL format)
-
If user provides an explicit file path: a. Detect its format (detect_log_format function above) b. jsonl or events.jsonl → pass to $CCL c. unknown → warn and display raw
-
Determine mode from user message:
- mentions "current" or no session specified → Mode 1 (Current Session)
- mentions a session ID or hash → Mode 2 (Specific Session)
- mentions "agent" or "background" → Mode 3 (Agent Output)
- mentions "all", "browse", "list" → Mode 4 (All Sessions)
-
Determine output format:
- "as HTML" or "export HTML" → html
- default → markdown
-
Execute the appropriate mode (using TOOL_CONTEXT to choose correct paths) and display results.
Error Handling
Situation Response
claude-code-log not installed Show install instructions, stop
JSONL file not found "No session file found at "
Session ID not found "No session with ID . Run 'browse all sessions' to list available ones."
No agent output files "No agent background task output found in current directory."
Empty JSONL file "Session file is empty — no messages to display."
Date range produces no results "No sessions found between and ."
claude-code-log returns non-zero exit Display stderr and suggest --help
~/.copilot/session-state/ exists but empty "No Copilot sessions found. Start a session with GitHub Copilot CLI to create logs."
Copilot session dir exists but no events.jsonl "Session directory found but events.jsonl is missing at "
Unknown file format Warn user and display raw content
Implementation Notes
Detecting Session IDs
Claude Code session IDs are UUID-like strings. If the user writes something like "view session abc123" or "show log def456", treat the last word as the session ID and search for matching JSONL filenames.
JSONL Structure (Claude Code)
Claude Code session JSONL files contain one JSON object per line:
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"..."}]},"timestamp":"...","sessionId":"..."} {"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"..."}]},"timestamp":"..."}
claude-code-log handles parsing; this skill does not re-implement it.
Copilot JSONL Structure (GitHub Copilot CLI)
Copilot CLI events.jsonl files contain one JSON object per line, stored at ~/.copilot/session-state/{session-id}/events.jsonl :
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"..."}]},"timestamp":"...","sessionId":"..."} {"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"..."}]},"timestamp":"...","sessionId":"..."}
The format is compatible with claude-code-log . Additional per-session files:
-
workspace.yaml — session metadata
-
plan.md — implementation plan
-
checkpoints/ — compaction history (older snapshots)
Legacy sessions may be stored at ~/.copilot/history-session-state/ with the same structure.
Philosophy Alignment
-
Thin wrapper: Delegates to claude-code-log for all JSONL (both Claude Code and Copilot)
-
Graceful degradation: Clear error messages when tool is missing or no sessions found
-
Single responsibility: Only views/converts transcripts, never modifies them
-
No hidden state: All file paths are shown to the user
Limitations
-
Requires claude-code-log (npm) or npx to convert JSONL (both Claude Code and Copilot) to HTML/Markdown
-
Cannot view transcripts from remote machines
-
Date filtering relies on filesystem modification times, not session timestamps
-
--summary flag availability depends on claude-code-log version
-
Legacy Copilot sessions in ~/.copilot/history-session-state/ are not auto-discovered (must use explicit path)
-
When both Claude Code and Copilot sessions exist, the skill prompts the user to choose rather than merging them
Quick Reference
User says Tool Mode Command
"view current transcript" Claude Code Current session $CCL <latest.jsonl> --format markdown
"show session abc123" Claude Code Specific session $CCL ~/.claude/projects/**/abc123.jsonl --format markdown
"view agent output" Claude Code Agent output cat .agent-step-*.log or $CCL *.jsonl
"browse all sessions" Claude Code All sessions list + summarize all ~/.claude/projects/**/*.jsonl
"view transcript as HTML" Claude Code Any + HTML $CCL <file> --format html > /tmp/view.html
"last 7 days" (with browse) Claude Code Date filter find ... -mtime -7
"view current copilot session" Copilot Current session $CCL ~/.copilot/session-state/<latest>/events.jsonl --format markdown
"show copilot session abc123" Copilot Specific session $CCL ~/.copilot/session-state/abc123/events.jsonl --format markdown
"browse copilot sessions" Copilot All sessions list dirs in ~/.copilot/session-state/ , show session IDs
"view copilot session as HTML" Copilot Any + HTML $CCL <events.jsonl> --format html > /tmp/view.html