|
| 1 | +# Architecture Diagrams |
| 2 | + |
| 3 | +## Current System Architecture |
| 4 | + |
| 5 | +``` |
| 6 | +┌─────────────────────────────────────────────────────────────────┐ |
| 7 | +│ CLI Entry Point │ |
| 8 | +│ (local, web, json, all commands) │ |
| 9 | +└───────────────────┬─────────────────────────────────────────────┘ |
| 10 | + │ |
| 11 | + ▼ |
| 12 | +┌─────────────────────────────────────────────────────────────────┐ |
| 13 | +│ __init__.py (2994 lines) │ |
| 14 | +│ │ |
| 15 | +│ ┌────────────────────────────────────────────────────────┐ │ |
| 16 | +│ │ Session Discovery │ │ |
| 17 | +│ │ • find_local_sessions() │ │ |
| 18 | +│ │ • get_session_summary() │ │ |
| 19 | +│ │ • get_project_display_name() │ │ |
| 20 | +│ └────────────────────────────────────────────────────────┘ │ |
| 21 | +│ │ │ |
| 22 | +│ ▼ │ |
| 23 | +│ ┌────────────────────────────────────────────────────────┐ │ |
| 24 | +│ │ Session Parsing │ │ |
| 25 | +│ │ • parse_session_file() │ │ |
| 26 | +│ │ • _parse_jsonl_file() │ │ |
| 27 | +│ └────────────────────────────────────────────────────────┘ │ |
| 28 | +│ │ │ |
| 29 | +│ ▼ │ |
| 30 | +│ ┌────────────────────────────────────────────────────────┐ │ |
| 31 | +│ │ Message Processing │ │ |
| 32 | +│ │ • Group by conversations │ │ |
| 33 | +│ │ • Build tool_result_lookup │ │ |
| 34 | +│ │ • Track paired_tool_ids │ │ |
| 35 | +│ │ • analyze_conversation() │ │ |
| 36 | +│ └────────────────────────────────────────────────────────┘ │ |
| 37 | +│ │ │ |
| 38 | +│ ▼ │ |
| 39 | +│ ┌────────────────────────────────────────────────────────┐ │ |
| 40 | +│ │ Rendering │ │ |
| 41 | +│ │ • render_message_with_tool_pairs() │ │ |
| 42 | +│ │ • render_content_block() │ │ |
| 43 | +│ │ • Tool-specific renderers │ │ |
| 44 | +│ │ • highlight_code() │ │ |
| 45 | +│ │ • render_markdown_text() │ │ |
| 46 | +│ └────────────────────────────────────────────────────────┘ │ |
| 47 | +│ │ │ |
| 48 | +│ ▼ │ |
| 49 | +│ ┌────────────────────────────────────────────────────────┐ │ |
| 50 | +│ │ Output Generation │ │ |
| 51 | +│ │ • generate_html() │ │ |
| 52 | +│ │ • Pagination (5 per page) │ │ |
| 53 | +│ │ • create_gist() │ │ |
| 54 | +│ └────────────────────────────────────────────────────────┘ │ |
| 55 | +│ │ |
| 56 | +│ Constants: CSS (330 lines), JS (150 lines) │ |
| 57 | +└─────────────────────────────────────────────────────────────────┘ |
| 58 | + │ |
| 59 | + ▼ |
| 60 | +┌─────────────────────────────────────────────────────────────────┐ |
| 61 | +│ Jinja2 Templates │ |
| 62 | +│ • macros.html • page.html • index.html • base.html │ |
| 63 | +└─────────────────────────────────────────────────────────────────┘ |
| 64 | + │ |
| 65 | + ▼ |
| 66 | +┌─────────────────────────────────────────────────────────────────┐ |
| 67 | +│ HTML Output Files │ |
| 68 | +│ • index.html • page-001.html • page-002.html • ... │ |
| 69 | +└─────────────────────────────────────────────────────────────────┘ |
| 70 | +``` |
| 71 | + |
| 72 | +## Data Flow: Session to HTML |
| 73 | + |
| 74 | +``` |
| 75 | +Session File (.json/.jsonl) |
| 76 | + │ |
| 77 | + ├─ JSON: Direct load |
| 78 | + │ {"loglines": [...]} |
| 79 | + │ |
| 80 | + └─ JSONL: Parse line-by-line |
| 81 | + {"type": "user", ...} |
| 82 | + {"type": "assistant", ...} |
| 83 | + {"type": "user", ...} |
| 84 | + │ |
| 85 | + ▼ |
| 86 | +Normalized Format |
| 87 | + {"loglines": [ |
| 88 | + {"type": "user", "timestamp": "...", "message": {...}}, |
| 89 | + {"type": "assistant", "timestamp": "...", "message": {...}}, |
| 90 | + ... |
| 91 | + ]} |
| 92 | + │ |
| 93 | + ▼ |
| 94 | +Conversation Grouping |
| 95 | + [ |
| 96 | + { |
| 97 | + "user_text": "Prompt 1", |
| 98 | + "messages": [(type, json, ts), ...], |
| 99 | + "is_continuation": false |
| 100 | + }, |
| 101 | + ... |
| 102 | + ] |
| 103 | + │ |
| 104 | + ▼ |
| 105 | +Tool Pairing |
| 106 | + tool_result_lookup = { |
| 107 | + "toolu_001": {...}, |
| 108 | + "toolu_002": {...} |
| 109 | + } |
| 110 | + paired_tool_ids = {"toolu_001", "toolu_002"} |
| 111 | + │ |
| 112 | + ▼ |
| 113 | +Message Rendering |
| 114 | + For each message: |
| 115 | + ├─ Parse JSON |
| 116 | + ├─ Lookup tool results |
| 117 | + ├─ Render content blocks |
| 118 | + │ ├─ text → Markdown |
| 119 | + │ ├─ thinking → Styled block |
| 120 | + │ ├─ tool_use → Tool renderer |
| 121 | + │ └─ tool_result → Paired display |
| 122 | + └─ Generate HTML |
| 123 | + │ |
| 124 | + ▼ |
| 125 | +Pagination |
| 126 | + Split into pages (5 conversations each) |
| 127 | + │ |
| 128 | + ▼ |
| 129 | +HTML Generation |
| 130 | + • page-001.html (Conversations 1-5) |
| 131 | + • page-002.html (Conversations 6-10) |
| 132 | + • ... |
| 133 | + • index.html (Timeline + stats) |
| 134 | +``` |
| 135 | + |
| 136 | +## Tool Pairing Mechanism |
| 137 | + |
| 138 | +``` |
| 139 | +Assistant Message User Message |
| 140 | +┌──────────────────────┐ ┌──────────────────────┐ |
| 141 | +│ content: [ │ │ content: [ │ |
| 142 | +│ { │ │ { │ |
| 143 | +│ type: "tool_use",│────────────│ type: "tool_result", |
| 144 | +│ id: "toolu_001", │ Links │ tool_use_id: "toolu_001", |
| 145 | +│ name: "Write", │ via │ content: "Success" |
| 146 | +│ input: {...} │ ID ref │ } │ |
| 147 | +│ } │ │ ] │ |
| 148 | +│ ] │ │ │ |
| 149 | +└──────────────────────┘ └──────────────────────┘ |
| 150 | + │ │ |
| 151 | + └──────────────┬───────────────────────┘ |
| 152 | + │ |
| 153 | + ▼ |
| 154 | + Paired Rendering |
| 155 | + ┌──────────────────────────┐ |
| 156 | + │ ┌──────────────────────┐ │ |
| 157 | + │ │ Tool Call: Write │ │ |
| 158 | + │ │ file_path: foo.py │ │ |
| 159 | + │ └──────────────────────┘ │ |
| 160 | + │ ┌──────────────────────┐ │ |
| 161 | + │ │ Tool Result │ │ |
| 162 | + │ │ Success │ │ |
| 163 | + │ └──────────────────────┘ │ |
| 164 | + └──────────────────────────┘ |
| 165 | +``` |
| 166 | + |
| 167 | +## Proposed Module Structure |
| 168 | + |
| 169 | +``` |
| 170 | +claude-code-transcripts/ |
| 171 | +├── src/claude_code_transcripts/ |
| 172 | +│ ├── __init__.py # Public API exports only |
| 173 | +│ ├── __main__.py # CLI entry point |
| 174 | +│ │ |
| 175 | +│ ├── discovery/ # Session finding |
| 176 | +│ │ ├── __init__.py |
| 177 | +│ │ ├── local.py # find_local_sessions() |
| 178 | +│ │ ├── web.py # API-based fetching |
| 179 | +│ │ └── filters.py # Session filtering |
| 180 | +│ │ |
| 181 | +│ ├── parsing/ # File reading |
| 182 | +│ │ ├── __init__.py |
| 183 | +│ │ ├── session.py # parse_session_file() |
| 184 | +│ │ ├── normalizer.py # Format conversion |
| 185 | +│ │ └── schemas.py # Optional: Pydantic/dataclass |
| 186 | +│ │ |
| 187 | +│ ├── processing/ # Data transformation |
| 188 | +│ │ ├── __init__.py |
| 189 | +│ │ ├── grouping.py # Conversation grouping |
| 190 | +│ │ ├── tool_pairing.py # Tool call/result linking |
| 191 | +│ │ └── analysis.py # Stats extraction |
| 192 | +│ │ |
| 193 | +│ ├── rendering/ # HTML generation |
| 194 | +│ │ ├── __init__.py |
| 195 | +│ │ ├── message.py # Message rendering |
| 196 | +│ │ ├── content_blocks.py # Block renderers |
| 197 | +│ │ ├── tools.py # Tool-specific renderers |
| 198 | +│ │ └── formatters.py # Code/Markdown formatting |
| 199 | +│ │ |
| 200 | +│ ├── output/ # File writing |
| 201 | +│ │ ├── __init__.py |
| 202 | +│ │ ├── html_generator.py # Main orchestrator |
| 203 | +│ │ ├── pagination.py # Page splitting |
| 204 | +│ │ └── gist.py # GitHub Gist upload |
| 205 | +│ │ |
| 206 | +│ ├── cli/ # CLI commands |
| 207 | +│ │ ├── __init__.py |
| 208 | +│ │ ├── commands.py # CLI group definition |
| 209 | +│ │ ├── local_cmd.py # Local command |
| 210 | +│ │ ├── web_cmd.py # Web command |
| 211 | +│ │ ├── json_cmd.py # JSON command |
| 212 | +│ │ └── all_cmd.py # All command |
| 213 | +│ │ |
| 214 | +│ ├── utils/ # Shared utilities |
| 215 | +│ │ ├── __init__.py |
| 216 | +│ │ ├── text.py # ANSI stripping, text extraction |
| 217 | +│ │ ├── git.py # Repo detection |
| 218 | +│ │ └── credentials.py # API credentials |
| 219 | +│ │ |
| 220 | +│ ├── assets/ # Static resources |
| 221 | +│ │ ├── __init__.py |
| 222 | +│ │ ├── styles.py # CSS constants |
| 223 | +│ │ └── scripts.py # JS constants |
| 224 | +│ │ |
| 225 | +│ └── templates/ # Jinja2 templates (existing) |
| 226 | +│ ├── macros.html |
| 227 | +│ ├── page.html |
| 228 | +│ ├── index.html |
| 229 | +│ └── base.html |
| 230 | +│ |
| 231 | +└── tests/ # Test suite |
| 232 | + ├── test_discovery/ |
| 233 | + ├── test_parsing/ |
| 234 | + ├── test_processing/ |
| 235 | + ├── test_rendering/ |
| 236 | + └── ... |
| 237 | +``` |
| 238 | + |
| 239 | +## Content Block Rendering Pipeline |
| 240 | + |
| 241 | +``` |
| 242 | +ContentBlock |
| 243 | + │ |
| 244 | + ├─ type: "text" |
| 245 | + │ └─> render_markdown_text() |
| 246 | + │ └─> Markdown → HTML |
| 247 | + │ |
| 248 | + ├─ type: "thinking" |
| 249 | + │ └─> render_markdown_text() |
| 250 | + │ └─> _macros.thinking() |
| 251 | + │ └─> Styled yellow block |
| 252 | + │ |
| 253 | + ├─ type: "tool_use" |
| 254 | + │ ├─ name: "Write" |
| 255 | + │ │ └─> render_write_tool() |
| 256 | + │ │ └─> highlight_code() |
| 257 | + │ │ └─> _macros.write_tool() |
| 258 | + │ │ |
| 259 | + │ ├─ name: "Edit" |
| 260 | + │ │ └─> render_edit_tool() |
| 261 | + │ │ └─> highlight_code() (old & new) |
| 262 | + │ │ └─> _macros.edit_tool() |
| 263 | + │ │ |
| 264 | + │ ├─ name: "Bash" |
| 265 | + │ │ └─> render_bash_tool() |
| 266 | + │ │ └─> _macros.bash_tool() |
| 267 | + │ │ |
| 268 | + │ ├─ name: "TodoWrite" |
| 269 | + │ │ └─> render_todo_write() |
| 270 | + │ │ └─> _macros.todo_list() |
| 271 | + │ │ |
| 272 | + │ └─ name: [Other] |
| 273 | + │ └─> render_json_with_markdown() |
| 274 | + │ └─> _macros.tool_use() |
| 275 | + │ |
| 276 | + ├─ type: "tool_result" |
| 277 | + │ ├─> strip_ansi() |
| 278 | + │ ├─> Detect commits |
| 279 | + │ ├─> format_json() (JSON view) |
| 280 | + │ ├─> render_markdown_text() (Markdown view) |
| 281 | + │ └─> _macros.tool_result() |
| 282 | + │ |
| 283 | + └─ type: "image" |
| 284 | + └─> _macros.image_block() |
| 285 | + └─> <img data:base64,...> |
| 286 | +``` |
| 287 | + |
| 288 | +## Session Discovery Flow Chart |
| 289 | + |
| 290 | +``` |
| 291 | +CLI: claude-code-transcripts local |
| 292 | + │ |
| 293 | + ▼ |
| 294 | +find_local_sessions(~/.claude/projects, limit=10) |
| 295 | + │ |
| 296 | + ├─> Glob: **/*.jsonl |
| 297 | + │ └─> [file1.jsonl, file2.jsonl, ...] |
| 298 | + │ |
| 299 | + ├─> Filter: Skip if name.startswith("agent-") |
| 300 | + │ └─> [file1.jsonl, file2.jsonl] |
| 301 | + │ |
| 302 | + ├─> For each file: |
| 303 | + │ ├─> get_session_summary(file) |
| 304 | + │ │ ├─> JSONL: _get_jsonl_summary() |
| 305 | + │ │ │ ├─> Priority 1: type="summary" entry |
| 306 | + │ │ │ └─> Priority 2: First user message |
| 307 | + │ │ └─> JSON: First user message |
| 308 | + │ │ |
| 309 | + │ ├─> Skip if summary == "warmup" |
| 310 | + │ └─> Skip if summary == "(no summary)" |
| 311 | + │ |
| 312 | + ├─> Sort: By mtime (newest first) |
| 313 | + │ └─> [(file1, mtime1), (file2, mtime2), ...] |
| 314 | + │ |
| 315 | + └─> Return: Top N results |
| 316 | + └─> [(file1, summary1), (file2, summary2), ...] |
| 317 | + │ |
| 318 | + ▼ |
| 319 | + questionary.select() |
| 320 | + │ |
| 321 | + ▼ |
| 322 | + Selected session file |
| 323 | + │ |
| 324 | + ▼ |
| 325 | + generate_html() |
| 326 | +``` |
| 327 | + |
| 328 | +--- |
| 329 | + |
| 330 | +**Full Documentation:** [ARCHITECTURE_ANALYSIS.md](./ARCHITECTURE_ANALYSIS.md) |
0 commit comments