claude-code-log 1.0.0__tar.gz → 1.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. claude_code_log-1.1.1/.claude/skills/tool-renderer/SKILL.md +414 -0
  2. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/.vscode/settings.json +4 -1
  3. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/CHANGELOG.md +30 -0
  4. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/CONTRIBUTING.md +3 -3
  5. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/PKG-INFO +1 -2
  6. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/README.md +0 -1
  7. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/converter.py +14 -0
  8. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/tool_factory.py +228 -9
  9. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/__init__.py +4 -0
  10. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/renderer.py +38 -0
  11. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/global_styles.css +7 -1
  12. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/message_styles.css +88 -4
  13. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/tool_formatters.py +126 -0
  14. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/markdown/renderer.py +73 -0
  15. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/models.py +55 -0
  16. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/renderer.py +11 -2
  17. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/tui.py +1 -0
  18. claude_code_log-1.1.1/dev-docs/implementing-a-tool-renderer.md +273 -0
  19. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/restoring-archived-sessions.md +25 -20
  20. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/justfile +56 -56
  21. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/pyproject.toml +3 -2
  22. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/uv.lock +5 -5
  23. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/.claude/settings.json +0 -0
  24. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/.github/workflows/ci.yml +0 -0
  25. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/.github/workflows/claude.yml +0 -0
  26. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/.gitignore +0 -0
  27. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/CLAUDE.md +0 -0
  28. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/LICENSE +0 -0
  29. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/__init__.py +0 -0
  30. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/cache.py +0 -0
  31. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/cli.py +0 -0
  32. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/__init__.py +0 -0
  33. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/assistant_factory.py +0 -0
  34. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/meta_factory.py +0 -0
  35. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/system_factory.py +0 -0
  36. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/transcript_factory.py +0 -0
  37. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/factories/user_factory.py +0 -0
  38. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/ansi_colors.py +0 -0
  39. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/assistant_formatters.py +0 -0
  40. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/renderer_code.py +0 -0
  41. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/system_formatters.py +0 -0
  42. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/edit_diff_styles.css +0 -0
  43. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/filter_styles.css +0 -0
  44. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/page_nav_styles.css +0 -0
  45. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/project_card_styles.css +0 -0
  46. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/pygments_styles.css +0 -0
  47. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/search.html +0 -0
  48. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/search_inline.html +0 -0
  49. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/search_inline_script.html +0 -0
  50. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/search_results_panel.html +0 -0
  51. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/search_styles.css +0 -0
  52. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/session_nav.html +0 -0
  53. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/session_nav_styles.css +0 -0
  54. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/timeline.html +0 -0
  55. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/timeline_styles.css +0 -0
  56. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/timezone_converter.js +0 -0
  57. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/components/todo_styles.css +0 -0
  58. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/index.html +0 -0
  59. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/templates/transcript.html +0 -0
  60. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/user_formatters.py +0 -0
  61. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/html/utils.py +0 -0
  62. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/image_export.py +0 -0
  63. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/markdown/__init__.py +0 -0
  64. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/migrations/001_initial_schema.sql +0 -0
  65. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/migrations/002_html_cache.sql +0 -0
  66. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/migrations/003_html_pagination.sql +0 -0
  67. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/migrations/__init__.py +0 -0
  68. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/migrations/runner.py +0 -0
  69. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/parser.py +0 -0
  70. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/py.typed +0 -0
  71. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/renderer_timings.py +0 -0
  72. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/claude_code_log/utils.py +0 -0
  73. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/FOLD_STATE_DIAGRAM.md +0 -0
  74. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/css-classes.md +0 -0
  75. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/assistant.json +0 -0
  76. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/assistant.jsonl +0 -0
  77. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/assistant_sidechain.json +0 -0
  78. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/assistant_sidechain.jsonl +0 -0
  79. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/thinking.json +0 -0
  80. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/assistant/thinking.jsonl +0 -0
  81. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/file_history_snapshot.json +0 -0
  82. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/file_history_snapshot.jsonl +0 -0
  83. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/queue_operation.json +0 -0
  84. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/queue_operation.jsonl +0 -0
  85. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/summary.json +0 -0
  86. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/summary.jsonl +0 -0
  87. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/system_info.json +0 -0
  88. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/system/system_info.jsonl +0 -0
  89. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_result.json +0 -0
  90. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_result.jsonl +0 -0
  91. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_result_error.json +0 -0
  92. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_result_error.jsonl +0 -0
  93. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_use.json +0 -0
  94. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/AskUserQuestion-tool_use.jsonl +0 -0
  95. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_result.json +0 -0
  96. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_result.jsonl +0 -0
  97. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_result_error.json +0 -0
  98. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_result_error.jsonl +0 -0
  99. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_use.json +0 -0
  100. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Bash-tool_use.jsonl +0 -0
  101. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/BashOutput-tool_result.json +0 -0
  102. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/BashOutput-tool_result.jsonl +0 -0
  103. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/BashOutput-tool_use.json +0 -0
  104. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/BashOutput-tool_use.jsonl +0 -0
  105. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_result.json +0 -0
  106. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_result.jsonl +0 -0
  107. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_result_error.json +0 -0
  108. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_result_error.jsonl +0 -0
  109. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_use.json +0 -0
  110. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Edit-tool_use.jsonl +0 -0
  111. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_result.json +0 -0
  112. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_result.jsonl +0 -0
  113. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_result_error.json +0 -0
  114. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_result_error.jsonl +0 -0
  115. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_use.json +0 -0
  116. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/ExitPlanMode-tool_use.jsonl +0 -0
  117. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Glob-tool_result.json +0 -0
  118. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Glob-tool_result.jsonl +0 -0
  119. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Glob-tool_use.json +0 -0
  120. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Glob-tool_use.jsonl +0 -0
  121. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Grep-tool_result.json +0 -0
  122. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Grep-tool_result.jsonl +0 -0
  123. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Grep-tool_use.json +0 -0
  124. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Grep-tool_use.jsonl +0 -0
  125. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_result.json +0 -0
  126. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_result.jsonl +0 -0
  127. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_result_error.json +0 -0
  128. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_result_error.jsonl +0 -0
  129. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_use.json +0 -0
  130. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/KillShell-tool_use.jsonl +0 -0
  131. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/LS-tool_result.json +0 -0
  132. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/LS-tool_result.jsonl +0 -0
  133. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/LS-tool_use.json +0 -0
  134. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/LS-tool_use.jsonl +0 -0
  135. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_result.json +0 -0
  136. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_result.jsonl +0 -0
  137. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_result_error.json +0 -0
  138. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_result_error.jsonl +0 -0
  139. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_use.json +0 -0
  140. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/MultiEdit-tool_use.jsonl +0 -0
  141. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_result.json +0 -0
  142. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_result.jsonl +0 -0
  143. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_result_error.json +0 -0
  144. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_result_error.jsonl +0 -0
  145. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_use.json +0 -0
  146. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Read-tool_use.jsonl +0 -0
  147. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Task-tool_result.json +0 -0
  148. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Task-tool_result.jsonl +0 -0
  149. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Task-tool_use.json +0 -0
  150. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Task-tool_use.jsonl +0 -0
  151. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/TodoWrite-tool_result.json +0 -0
  152. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/TodoWrite-tool_result.jsonl +0 -0
  153. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/TodoWrite-tool_use.json +0 -0
  154. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/TodoWrite-tool_use.jsonl +0 -0
  155. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebFetch-tool_result.json +0 -0
  156. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebFetch-tool_result.jsonl +0 -0
  157. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebFetch-tool_use.json +0 -0
  158. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebFetch-tool_use.jsonl +0 -0
  159. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebSearch-tool_result.json +0 -0
  160. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebSearch-tool_result.jsonl +0 -0
  161. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebSearch-tool_use.json +0 -0
  162. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/WebSearch-tool_use.jsonl +0 -0
  163. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_result.json +0 -0
  164. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_result.jsonl +0 -0
  165. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_result_error.json +0 -0
  166. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_result_error.jsonl +0 -0
  167. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_use.json +0 -0
  168. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/Write-tool_use.jsonl +0 -0
  169. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/exit_plan_mode-tool_result.json +0 -0
  170. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/exit_plan_mode-tool_result.jsonl +0 -0
  171. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/exit_plan_mode-tool_use.json +0 -0
  172. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/tools/exit_plan_mode-tool_use.jsonl +0 -0
  173. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/bash_input.json +0 -0
  174. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/bash_input.jsonl +0 -0
  175. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/bash_output.json +0 -0
  176. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/bash_output.jsonl +0 -0
  177. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/command_output.json +0 -0
  178. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/command_output.jsonl +0 -0
  179. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/image.json +0 -0
  180. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/image.jsonl +0 -0
  181. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user.json +0 -0
  182. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user.jsonl +0 -0
  183. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_command.json +0 -0
  184. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_command.jsonl +0 -0
  185. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_sidechain.json +0 -0
  186. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_sidechain.jsonl +0 -0
  187. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_slash_command.json +0 -0
  188. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages/user/user_slash_command.jsonl +0 -0
  189. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/messages.md +0 -0
  190. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/rendering-architecture.md +0 -0
  191. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/dev-docs/rendering-next.md +0 -0
  192. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/mise.toml +0 -0
  193. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/__init__.pyi +0 -0
  194. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/formatter.pyi +0 -0
  195. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/formatters/__init__.pyi +0 -0
  196. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/lexer.pyi +0 -0
  197. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/lexers/__init__.pyi +0 -0
  198. {claude_code_log-1.0.0 → claude_code_log-1.1.1}/stubs/pygments/util.pyi +0 -0
@@ -0,0 +1,414 @@
1
+ ---
2
+ name: tool-renderer
3
+ description: Implement specialized rendering for Claude Code tools. Use when adding a new tool type (WebSearch, WebFetch, etc.) to the transcript viewer, or when asked to implement tool rendering.
4
+ ---
5
+
6
+ # Implementing a Tool Renderer
7
+
8
+ This guide walks through adding rendering support for a new Claude Code tool, using WebSearch as an example.
9
+
10
+ ## Before You Start
11
+
12
+ **Examine existing test data** to understand the tool's actual JSON structure:
13
+
14
+ ```bash
15
+ # Find test files containing the tool
16
+ rg -l "ToolName" test/test_data/
17
+
18
+ # Look at actual JSONL entries
19
+ rg '"name":\s*"ToolName"' test/test_data/ -A 2 -B 2
20
+ ```
21
+
22
+ Key fields to identify:
23
+ - **Input parameters**: What's in `tool_use.input`?
24
+ - **toolUseResult structure**: What metadata does the structured result contain?
25
+ - **tool_result.content**: What does the raw text output look like?
26
+
27
+ The `toolUseResult` field on transcript entries often contains richer structured data than `tool_result.content`. **Always prefer parsing from `toolUseResult` when available.**
28
+
29
+ ## Overview
30
+
31
+ Tool rendering involves several components working together:
32
+
33
+ 1. **Models** (`models.py`) - Type definitions for tool inputs and outputs
34
+ 2. **Factory** (`factories/tool_factory.py`) - Parsing raw JSON into typed models
35
+ 3. **HTML Formatters** (`html/tool_formatters.py`) - HTML rendering functions
36
+ 4. **Renderers** - Integration with HTML and Markdown renderers
37
+
38
+ ## Step 1: Define Models
39
+
40
+ ### Tool Input Model
41
+
42
+ Add a Pydantic model for the tool's input parameters in `models.py`:
43
+
44
+ ```python
45
+ class WebSearchInput(BaseModel):
46
+ """Input parameters for the WebSearch tool."""
47
+ query: str
48
+ ```
49
+
50
+ ### Tool Output Model
51
+
52
+ Add a dataclass for the parsed output. Output models are dataclasses (not Pydantic) since they're created by our parsers, not from JSON:
53
+
54
+ ```python
55
+ @dataclass
56
+ class WebSearchLink:
57
+ """Single search result link."""
58
+ title: str
59
+ url: str
60
+
61
+ @dataclass
62
+ class WebSearchOutput:
63
+ """Parsed WebSearch tool output."""
64
+ query: str
65
+ links: list[WebSearchLink]
66
+ preamble: Optional[str] = None # Text before the Links
67
+ summary: Optional[str] = None # Markdown analysis after the Links
68
+ ```
69
+
70
+ **Note:** Some tools have structured output with multiple sections. WebSearch is parsed as **preamble/links/summary** - text before Links, the Links JSON array, and markdown analysis after. This allows flexible rendering while preserving all content.
71
+
72
+ ### Update Type Unions
73
+
74
+ Add the new types to the `ToolInput` and `ToolOutput` unions:
75
+
76
+ ```python
77
+ ToolInput = Union[
78
+ # ... existing types ...
79
+ WebSearchInput,
80
+ ToolUseContent, # Generic fallback - keep last
81
+ ]
82
+
83
+ ToolOutput = Union[
84
+ # ... existing types ...
85
+ WebSearchOutput,
86
+ ToolResultContent, # Generic fallback - keep last
87
+ ]
88
+ ```
89
+
90
+ ## Step 2: Implement Factory Functions
91
+
92
+ In `factories/tool_factory.py`:
93
+
94
+ ### Register Input Model
95
+
96
+ Add the input model to `TOOL_INPUT_MODELS`:
97
+
98
+ ```python
99
+ TOOL_INPUT_MODELS: dict[str, type[BaseModel]] = {
100
+ # ... existing entries ...
101
+ "WebSearch": WebSearchInput,
102
+ }
103
+ ```
104
+
105
+ ### Implement Output Parser
106
+
107
+ **Important**: Always check if the tool has structured `toolUseResult` data available. This is the preferred approach because:
108
+ - It's more reliable than regex parsing of text content
109
+ - It often contains metadata (timing, byte counts, status codes) not in the text
110
+ - The structure is well-defined and type-safe
111
+
112
+ Example `toolUseResult` structures in test data:
113
+ ```json
114
+ // WebSearch
115
+ {"query": "...", "results": [...], "durationSeconds": 15.7}
116
+
117
+ // WebFetch
118
+ {"url": "...", "result": "...", "code": 200, "codeText": "OK", "bytes": 12345, "durationMs": 1500}
119
+ ```
120
+
121
+ Create a parser function that extracts from `toolUseResult`:
122
+
123
+ ```python
124
+ def _parse_websearch_from_structured(
125
+ tool_use_result: ToolUseResult,
126
+ ) -> Optional[WebSearchOutput]:
127
+ """Parse WebSearch from structured toolUseResult data.
128
+
129
+ The toolUseResult for WebSearch has the format:
130
+ {
131
+ "query": "search query",
132
+ "results": [
133
+ {"tool_use_id": "...", "content": [{"title": "...", "url": "..."}]},
134
+ "Analysis text..."
135
+ ]
136
+ }
137
+ """
138
+ if not isinstance(tool_use_result, dict):
139
+ return None
140
+ query = tool_use_result.get("query")
141
+ results = tool_use_result.get("results")
142
+ # ... extract links from results[0].content, summary from results[1] ...
143
+ return WebSearchOutput(query=query, links=links, preamble=None, summary=summary)
144
+
145
+
146
+ def parse_websearch_output(
147
+ tool_result: ToolResultContent,
148
+ file_path: Optional[str],
149
+ tool_use_result: Optional[ToolUseResult] = None, # Extended signature
150
+ ) -> Optional[WebSearchOutput]:
151
+ """Parse WebSearch tool result from structured toolUseResult."""
152
+ del tool_result, file_path # Unused
153
+ if tool_use_result is None:
154
+ return None
155
+ return _parse_websearch_from_structured(tool_use_result)
156
+ ```
157
+
158
+ ### Register Output Parser
159
+
160
+ Add to `TOOL_OUTPUT_PARSERS` and **register in `PARSERS_WITH_TOOL_USE_RESULT`** if using the extended signature:
161
+
162
+ ```python
163
+ TOOL_OUTPUT_PARSERS: dict[str, ToolOutputParser] = {
164
+ # ... existing entries ...
165
+ "WebSearch": parse_websearch_output,
166
+ }
167
+
168
+ # REQUIRED for parsers that use toolUseResult - without this, the structured
169
+ # data won't be passed to your parser!
170
+ PARSERS_WITH_TOOL_USE_RESULT: set[str] = {"WebSearch", "WebFetch"}
171
+ ```
172
+
173
+ **Note**: If your parser has the 3-argument signature `(tool_result, file_path, tool_use_result)`, you MUST add it to `PARSERS_WITH_TOOL_USE_RESULT`. Otherwise `create_tool_output()` won't pass the structured data.
174
+
175
+ ## Step 3: Implement HTML Formatters
176
+
177
+ In `html/tool_formatters.py`:
178
+
179
+ ### Input Formatter
180
+
181
+ **Design consideration**: The title already shows key info (tool name + primary parameter). Only show content in the body if it adds value or is too long for the title.
182
+
183
+ ```python
184
+ def format_websearch_input(search_input: WebSearchInput) -> str:
185
+ """Format WebSearch tool use content."""
186
+ # If query is short enough to fit in title, return empty
187
+ if len(search_input.query) <= 100:
188
+ return "" # Full query shown in title
189
+ escaped_query = escape_html(search_input.query)
190
+ return f'<div class="websearch-query">{escaped_query}</div>'
191
+ ```
192
+
193
+ This avoids redundancy when the title already shows everything important.
194
+
195
+ ### Output Formatter
196
+
197
+ For tools with structured content like WebSearch, combine all parts into markdown then render:
198
+
199
+ ```python
200
+ def _websearch_as_markdown(output: WebSearchOutput) -> str:
201
+ """Convert WebSearch output to markdown: preamble + links list + summary."""
202
+ parts = []
203
+ if output.preamble:
204
+ parts.extend([output.preamble, ""])
205
+ for link in output.links:
206
+ parts.append(f"- [{link.title}]({link.url})")
207
+ if output.summary:
208
+ parts.extend(["", output.summary])
209
+ return "\n".join(parts)
210
+
211
+
212
+ def format_websearch_output(output: WebSearchOutput) -> str:
213
+ """Format WebSearch as single collapsible markdown block."""
214
+ markdown_content = _websearch_as_markdown(output)
215
+ return render_markdown_collapsible(markdown_content, "websearch-results")
216
+ ```
217
+
218
+ ### Update Exports
219
+
220
+ Add functions to `__all__`:
221
+
222
+ ```python
223
+ __all__ = [
224
+ # ... existing exports ...
225
+ "format_websearch_input",
226
+ "format_websearch_output",
227
+ ]
228
+ ```
229
+
230
+ ## Step 4: Wire Up HTML Renderer
231
+
232
+ In `html/renderer.py`:
233
+
234
+ ### Import Formatters
235
+
236
+ ```python
237
+ from .tool_formatters import (
238
+ # ... existing imports ...
239
+ format_websearch_input,
240
+ format_websearch_output,
241
+ )
242
+ ```
243
+
244
+ ### Add Format Methods
245
+
246
+ ```python
247
+ def format_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
248
+ return format_websearch_input(input)
249
+
250
+ def format_WebSearchOutput(self, output: WebSearchOutput, _: TemplateMessage) -> str:
251
+ return format_websearch_output(output)
252
+ ```
253
+
254
+ ### Add Title Method (Optional)
255
+
256
+ For a custom title in the message header:
257
+
258
+ ```python
259
+ def title_WebSearchInput(self, input: WebSearchInput, message: TemplateMessage) -> str:
260
+ return self._tool_title(message, "🔎", f'"{input.query}"')
261
+ ```
262
+
263
+ ## Step 5: Implement Markdown Renderer
264
+
265
+ In `markdown/renderer.py`:
266
+
267
+ ### Import Models
268
+
269
+ ```python
270
+ from ..models import (
271
+ # ... existing imports ...
272
+ WebSearchInput,
273
+ WebSearchOutput,
274
+ )
275
+ ```
276
+
277
+ ### Add Format Methods
278
+
279
+ ```python
280
+ def format_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
281
+ """Format -> empty (query shown in title)."""
282
+ return ""
283
+
284
+ def format_WebSearchOutput(self, output: WebSearchOutput, _: TemplateMessage) -> str:
285
+ """Format -> markdown list of links."""
286
+ parts = [f"Query: *{output.query}*", ""]
287
+ for link in output.links:
288
+ parts.append(f"- [{link.title}]({link.url})")
289
+ return "\n".join(parts)
290
+
291
+ def title_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
292
+ """Title -> '🔎 WebSearch `query`'."""
293
+ return f'🔎 WebSearch `{input.query}`'
294
+ ```
295
+
296
+ ## Step 6: Add Tests
297
+
298
+ Create a dedicated test file `test/test_{toolname}_rendering.py`. Tests are **required** - they catch regressions and document expected behavior.
299
+
300
+ ### Test Structure
301
+
302
+ ```python
303
+ """Test cases for {ToolName} tool rendering."""
304
+
305
+ from claude_code_log.factories.tool_factory import parse_{toolname}_output
306
+ from claude_code_log.html.tool_formatters import (
307
+ format_{toolname}_input,
308
+ format_{toolname}_output,
309
+ )
310
+ from claude_code_log.models import (
311
+ ToolResultContent,
312
+ {ToolName}Input,
313
+ {ToolName}Output,
314
+ )
315
+
316
+
317
+ class Test{ToolName}Input:
318
+ """Test input model and formatting."""
319
+
320
+ def test_input_basic(self):
321
+ """Test input model creation."""
322
+ ...
323
+
324
+ def test_format_input_short(self):
325
+ """Test formatting when content fits in title."""
326
+ ...
327
+
328
+ def test_format_input_long(self):
329
+ """Test formatting when content is too long for title."""
330
+ ...
331
+
332
+
333
+ class Test{ToolName}Parser:
334
+ """Test output parsing."""
335
+
336
+ def test_parse_structured_output(self):
337
+ """Test parsing from structured toolUseResult."""
338
+ ...
339
+
340
+ def test_parse_minimal_output(self):
341
+ """Test parsing with only required fields."""
342
+ ...
343
+
344
+ def test_parse_missing_field(self):
345
+ """Test graceful failure with missing required field."""
346
+ ...
347
+
348
+ def test_parse_no_tool_use_result(self):
349
+ """Test returns None when no toolUseResult."""
350
+ ...
351
+
352
+
353
+ class Test{ToolName}OutputFormatting:
354
+ """Test output HTML formatting."""
355
+
356
+ def test_format_output_full(self):
357
+ """Test formatting with all metadata."""
358
+ ...
359
+
360
+ def test_format_output_minimal(self):
361
+ """Test formatting with minimal data."""
362
+ ...
363
+ ```
364
+
365
+ ### Running Tests
366
+
367
+ ```bash
368
+ # Run just your new tests
369
+ uv run pytest test/test_{toolname}_rendering.py -v
370
+
371
+ # Run full test suite to check for regressions
372
+ uv run pytest -n auto -m "not (tui or browser)" -v
373
+ ```
374
+
375
+ ## Checklist
376
+
377
+ ### Models (`models.py`)
378
+ - [ ] Add input model (Pydantic `BaseModel`)
379
+ - [ ] Add output model (dataclass with all fields from `toolUseResult`)
380
+ - [ ] Update `ToolInput` union
381
+ - [ ] Update `ToolOutput` union
382
+
383
+ ### Factory (`factories/tool_factory.py`)
384
+ - [ ] Add to `TOOL_INPUT_MODELS`
385
+ - [ ] Import output model
386
+ - [ ] Implement output parser with 3-arg signature if using `toolUseResult`
387
+ - [ ] Add to `TOOL_OUTPUT_PARSERS`
388
+ - [ ] Add to `PARSERS_WITH_TOOL_USE_RESULT` (required if parser uses `toolUseResult`)
389
+
390
+ ### HTML (`html/tool_formatters.py`, `html/renderer.py`)
391
+ - [ ] Import models
392
+ - [ ] Add input formatter function
393
+ - [ ] Add output formatter function
394
+ - [ ] Update `__all__` exports
395
+ - [ ] Wire up `format_{Input}` method in renderer
396
+ - [ ] Wire up `format_{Output}` method in renderer
397
+ - [ ] Add `title_{Input}` method in renderer
398
+
399
+ ### Markdown (`markdown/renderer.py`)
400
+ - [ ] Import models
401
+ - [ ] Add `format_{Input}` method
402
+ - [ ] Add `format_{Output}` method
403
+ - [ ] Add `title_{Input}` method
404
+
405
+ ### Tests (`test/test_{toolname}_rendering.py`)
406
+ - [ ] Create test file
407
+ - [ ] Test input model creation
408
+ - [ ] Test input formatting (short/long content)
409
+ - [ ] Test parser with full structured data
410
+ - [ ] Test parser with minimal data
411
+ - [ ] Test parser with missing fields (graceful failure)
412
+ - [ ] Test parser with no `toolUseResult`
413
+ - [ ] Test output formatting
414
+ - [ ] Run full test suite to verify no regressions
@@ -5,4 +5,7 @@
5
5
  "docs/claude-code-log-transcript.html": true,
6
6
  "test/test_data/cache/**": true,
7
7
  },
8
- }
8
+ "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
9
+ "python-envs.defaultEnvManager": "ms-python.python:venv",
10
+ "python-envs.pythonProjects": []
11
+ }
@@ -6,6 +6,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [1.1.1] - 2026-03-10
10
+
11
+ ### Changed
12
+
13
+ - **Fix build cold start + format justfile**
14
+ - **fix: handle None level in SystemMessage title (#100)**
15
+
16
+
17
+ ## [1.1.0] - 2026-03-06
18
+
19
+ ### Changed
20
+
21
+ - **Fix WebSearch and WebFetch rendering in agent transcripts (#98)**
22
+ - **Fix fold-bar colors and System Hook alignment (#89)**
23
+ - **Add WebFetch tool renderer (#87)**
24
+ - **Merge pull request #83 from daaain/dev/websearch-tool-renderer**
25
+ - **Update some outdated docs + VS Code insists on these settings (#86)**
26
+ - **Fix double tab opening when clicking links in TUI MarkdownViewer**
27
+ - **Simplify WebSearch parser and improve rendering**
28
+ - **Use structured toolUseResult for WebSearch parsing**
29
+ - **Add analysis content support to WebSearch output**
30
+ - **Add documentation for implementing tool renderers**
31
+ - **Add WebSearch HTML and Markdown formatters**
32
+ - **Add WebSearch tool models and factory parser**
33
+ - **Fix snapshot + make sure snapshot order is stable**
34
+ - **Improve CSS layout to be responsive for mobile small screens (#77)**
35
+ - **Update pyright to 1.1.408 (#82)**
36
+ - **Support subagents directory structure (Claude Code 2.1.2+) (#80)**
37
+
38
+
9
39
  ## [1.0.0] - 2026-01-22
10
40
 
11
41
  BREAKING CHANGE: cache is now using a SQLite database instead of JSON files!
@@ -226,14 +226,14 @@ The interactive timeline is implemented in JavaScript within `claude_code_log/te
226
226
 
227
227
  ## Cache System
228
228
 
229
- The tool implements a caching system for performance:
229
+ The tool implements a SQLite-based caching system for performance:
230
230
 
231
- - **Location**: `.cache/` directory within each project folder
231
+ - **Location**: `claude-code-log-cache.db` in the projects directory (or set `CLAUDE_CODE_LOG_CACHE_PATH` env var)
232
232
  - **Contents**: Pre-parsed session metadata (IDs, summaries, timestamps, token usage)
233
233
  - **Invalidation**: Automatic detection based on file modification times
234
234
  - **Performance**: 10-100x faster loading for large projects
235
235
 
236
- The cache automatically rebuilds when source files change or cache version changes.
236
+ The cache automatically rebuilds when source files change or cache schema version changes.
237
237
 
238
238
  ## Release Process
239
239
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-log
3
- Version: 1.0.0
3
+ Version: 1.1.1
4
4
  Summary: Convert Claude Code transcript JSONL files to HTML
5
5
  Project-URL: Homepage, https://github.com/daaain/claude-code-log
6
6
  Project-URL: Issues, https://github.com/daaain/claude-code-log/issues
@@ -271,5 +271,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and archi
271
271
  - add minimalist theme and make it light + dark; animate gradient background in fancy theme
272
272
  - do we need special handling for hooks?
273
273
  - make processing parallel, currently we only use 1 CPU (core) and it's slow
274
- - migrate cache from JSON files to SQLite to make it faster and more versatile for downstream tasks and analytics
275
274
  - merge git worktree directories
@@ -247,5 +247,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and archi
247
247
  - add minimalist theme and make it light + dark; animate gradient background in fancy theme
248
248
  - do we need special handling for hooks?
249
249
  - make processing parallel, currently we only use 1 CPU (core) and it's slow
250
- - migrate cache from JSON files to SQLite to make it faster and more versatile for downstream tasks and analytics
251
250
  - merge git worktree directories
@@ -252,11 +252,25 @@ def load_transcript(
252
252
  agent_messages_map: dict[str, list[TranscriptEntry]] = {}
253
253
  if agent_ids:
254
254
  parent_dir = jsonl_path.parent
255
+ session_basename = (
256
+ jsonl_path.stem
257
+ ) # e.g., "29ccd257-68b1-427f-ae5f-6524b7cb6f20"
255
258
  for agent_id in agent_ids:
259
+ # Try legacy location first (same directory as session file)
256
260
  agent_file = parent_dir / f"agent-{agent_id}.jsonl"
257
261
  # Skip if the agent file is the same as the current file (self-reference)
258
262
  if agent_file == jsonl_path:
259
263
  continue
264
+ # Try new subagents directory structure (Claude Code 2.1.2+)
265
+ if not agent_file.exists():
266
+ subagent_file = (
267
+ parent_dir
268
+ / session_basename
269
+ / "subagents"
270
+ / f"agent-{agent_id}.jsonl"
271
+ )
272
+ if subagent_file.exists():
273
+ agent_file = subagent_file
260
274
  if agent_file.exists():
261
275
  if not silent:
262
276
  print(f"Loading agent file {agent_file}...")