Claude
Skills
Sign in
Back

transcript-viewer

Included with Lifetime
$97 forever

Convert and browse session transcripts as HTML or Markdown. Supports Claude Code JSONL logs (auto-saved to ~/.claude/projects/) and GitHub Copilot CLI JSONL logs (auto-saved to ~/.copilot/session-state/*/events.jsonl). Auto-detects log source based on available directories and file format. Supports viewing the current session, a specific session by ID, agent background task output files, or all project sessions with optional date-range filtering.

Web Dev

What this skill does


# 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:

1. **Current session** — View the active session's transcript
2. **Specific session** — View a session by its ID
3. **Agent output** — View background task output files produced by subagents
4. **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:

```bash
# 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:

```bash
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:

```bash
# 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:

```bash
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"`):

1. Find the most recently modified JSONL file under `~/.claude/projects/`:
   ```bash
   ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -1
   ```
2. Run:
   ```bash
   $CCL <path-to-jsonl> --format markdown
   ```
3. Display the Markdown output inline.

For **GitHub Copilot CLI** (`TOOL_CONTEXT="copilot"`):

1. Find the most recently modified session directory under `~/.copilot/session-state/`:
   ```bash
   ls -dt ~/.copilot/session-state/*/ 2>/dev/null | head -1
   ```
2. Read its `events.jsonl`:
   ```bash
   LATEST_SESSION=$(ls -dt ~/.copilot/session-state/*/ 2>/dev/null | head -1)
   $CCL "${LATEST_SESSION}events.jsonl" --format markdown
   ```
3. Display the Markdown output inline.

**Example output**:

```markdown
# 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 <ID>", "show transcript <ID>", "open log <ID>"

**What to do**:

For **Claude Code** (`TOOL_CONTEXT="claude-code"`):

1. Search for the JSONL file matching the session ID:
   ```bash
   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:
   ```bash
   ls ~/.claude/projects/*/ | grep "<SESSION_ID>"
   ```
2. Run:
   ```bash
   $CCL <path-to-jsonl> --format markdown
   ```
3. 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"`):

1. The session ID is the directory name under `~/.copilot/session-state/`. Check directly:
   ```bash
   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:
   ```bash
   ls -d ~/.copilot/session-state/*/ 2>/dev/null | grep "<SESSION_ID>"
   ```
2. Run:
   ```bash
   $CCL "$EVENTS_FILE" --format markdown
   ```
3. 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**:

1. 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:
   ```bash
   ls -t .agent-step-*.log 2>/dev/null
   ls -t /tmp/*.agent*.log 2>/dev/null
   ```
2. For each file found, run `detect_log_format <file>` to classify it:
   - `jsonl` → run `$CCL <file> --fo

Related in Web Dev