claude-code-log 1.3.0__tar.gz → 1.4.0__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.
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/.github/workflows/ci.yml +6 -0
- claude_code_log-1.4.0/.github/workflows/docs.yml +68 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/.gitignore +4 -3
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/CHANGELOG.md +48 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/CLAUDE.md +7 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/CONTRIBUTING.md +41 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/PKG-INFO +18 -3
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/README.md +16 -1
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/cli.py +247 -5
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/converter.py +515 -550
- claude_code_log-1.4.0/claude_code_log/factories/priorities.py +46 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/tool_factory.py +303 -46
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/user_factory.py +25 -0
- claude_code_log-1.4.0/claude_code_log/git_remote.py +343 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/renderer.py +409 -22
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/edit_diff_styles.css +1 -1
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/global_styles.css +1 -2
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/message_styles.css +93 -10
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/project_card_styles.css +32 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/session_nav.html +6 -1
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/teammate_styles.css +6 -7
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/todo_styles.css +19 -0
- claude_code_log-1.4.0/claude_code_log/html/templates/index.html +157 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/tool_formatters.py +143 -43
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/utils.py +47 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/json/renderer.py +4 -1
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/markdown/renderer.py +334 -23
- claude_code_log-1.4.0/claude_code_log/markdown_plugins.py +440 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/models.py +232 -4
- claude_code_log-1.4.0/claude_code_log/plugins.py +371 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/renderer.py +727 -355
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/utils.py +272 -3
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/application_model.md +22 -12
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/dag.md +2 -3
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/implementing-a-tool-renderer.md +37 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages.md +2 -0
- claude_code_log-1.4.0/dev-docs/plugins.md +741 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/rendering-architecture.md +50 -10
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/justfile +22 -11
- claude_code_log-1.4.0/mkdocs.yml +87 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/pyproject.toml +16 -2
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/uv.lock +330 -2
- claude_code_log-1.4.0/work/obsidian-friendly-output.md +81 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/work/refactor-reindex-with-ghosting.md +39 -6
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/work/rendering-next.md +1 -34
- claude_code_log-1.4.0/work/simplify-converter-renderer.md +312 -0
- claude_code_log-1.4.0/work/tool-renderer-plugins.md +29 -0
- claude_code_log-1.3.0/claude_code_log/html/templates/index.html +0 -118
- claude_code_log-1.3.0/work/async-agents.md +0 -215
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/.claude/settings.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/.claude/skills/tool-renderer/SKILL.md +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/.vscode/settings.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/LICENSE +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/cache.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/dag.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/agent_metadata_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/assistant_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/attachment_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/meta_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/system_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/task_notification_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/teammate_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/factories/transcript_factory.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/ansi_colors.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/assistant_formatters.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/async_formatter.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/renderer_code.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/system_formatters.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/teammate_formatter.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/filter_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/page_nav_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/pygments_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/search.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/search_inline.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/search_inline_script.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/search_results_panel.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/search_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/session_nav_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/timeline.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/timeline_styles.css +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/components/timezone_converter.js +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/templates/transcript.html +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/html/user_formatters.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/image_export.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/json/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/markdown/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/001_initial_schema.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/002_html_cache.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/003_html_pagination.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/004_html_pagination_variant.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/005_session_team_name.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/006_session_ai_title.sql +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/__init__.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/migrations/runner.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/parser.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/py.typed +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/renderer_timings.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/claude_code_log/tui.py +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/agents.md +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/css-classes.md +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/message-hierarchy.md +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/assistant.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/assistant.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/assistant_sidechain.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/assistant_sidechain.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/thinking.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/assistant/thinking.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/file_history_snapshot.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/file_history_snapshot.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/queue_operation.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/queue_operation.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/summary.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/summary.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/system_info.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/system/system_info.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/AskUserQuestion-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Bash-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/BashOutput-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/BashOutput-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/BashOutput-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/BashOutput-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Edit-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/ExitPlanMode-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Glob-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Glob-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Glob-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Glob-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Grep-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Grep-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Grep-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Grep-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/KillShell-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/LS-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/LS-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/LS-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/LS-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/MultiEdit-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Read-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Task-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Task-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Task-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Task-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/TodoWrite-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/TodoWrite-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/TodoWrite-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/TodoWrite-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebFetch-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebFetch-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebFetch-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebFetch-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebSearch-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebSearch-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebSearch-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/WebSearch-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_result_error.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_result_error.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/Write-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/exit_plan_mode-tool_result.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/exit_plan_mode-tool_result.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/exit_plan_mode-tool_use.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/tools/exit_plan_mode-tool_use.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/bash_input.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/bash_input.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/bash_output.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/bash_output.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/command_output.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/command_output.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/image.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/image.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_command.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_command.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_sidechain.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_sidechain.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_slash_command.json +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/messages/user/user_slash_command.jsonl +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/dev-docs/teammates.md +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/mise.toml +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/__init__.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/formatter.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/formatters/__init__.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/lexer.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/lexers/__init__.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/stubs/pygments/util.pyi +0 -0
- {claude_code_log-1.3.0 → claude_code_log-1.4.0}/work/session-state-propagation.md +0 -0
|
@@ -20,6 +20,12 @@ jobs:
|
|
|
20
20
|
|
|
21
21
|
steps:
|
|
22
22
|
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
# Full history so test/test_commit_linkifier.py's
|
|
25
|
+
# TestIntegrationLocalRepo can resolve SHAs older than the
|
|
26
|
+
# current tip (`git branch -r --contains` reads local refs
|
|
27
|
+
# only and needs the commits in the object DB).
|
|
28
|
+
fetch-depth: 0
|
|
23
29
|
|
|
24
30
|
- name: Install uv
|
|
25
31
|
uses: astral-sh/setup-uv@v4
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
# Allow the deploy job to publish to GitHub Pages.
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
# Avoid overlapping deploys; let an in-progress run finish.
|
|
16
|
+
concurrency:
|
|
17
|
+
group: pages
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
# Silence the Material-for-MkDocs vendor banner about a future MkDocs 2.0.
|
|
22
|
+
DISABLE_MKDOCS_2_WARNING: "true"
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v5
|
|
29
|
+
|
|
30
|
+
- name: Install uv
|
|
31
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
32
|
+
with:
|
|
33
|
+
enable-cache: true
|
|
34
|
+
|
|
35
|
+
- name: Set up Python
|
|
36
|
+
run: uv python install 3.12
|
|
37
|
+
|
|
38
|
+
- name: Install docs dependencies
|
|
39
|
+
run: uv sync --group docs
|
|
40
|
+
|
|
41
|
+
- name: Build site (strict)
|
|
42
|
+
run: uv run mkdocs build --strict
|
|
43
|
+
|
|
44
|
+
- name: Upload site as artifact (downloadable preview)
|
|
45
|
+
uses: actions/upload-artifact@v7
|
|
46
|
+
with:
|
|
47
|
+
name: site-preview
|
|
48
|
+
path: site
|
|
49
|
+
retention-days: 14
|
|
50
|
+
|
|
51
|
+
- name: Upload Pages artifact
|
|
52
|
+
if: github.ref == 'refs/heads/main'
|
|
53
|
+
uses: actions/upload-pages-artifact@v3
|
|
54
|
+
with:
|
|
55
|
+
path: site
|
|
56
|
+
|
|
57
|
+
deploy:
|
|
58
|
+
# Only publish from main; PRs just run the strict build above.
|
|
59
|
+
if: github.ref == 'refs/heads/main'
|
|
60
|
+
needs: build
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
environment:
|
|
63
|
+
name: github-pages
|
|
64
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
65
|
+
steps:
|
|
66
|
+
- name: Deploy to GitHub Pages
|
|
67
|
+
id: deployment
|
|
68
|
+
uses: actions/deploy-pages@v4
|
|
@@ -147,6 +147,8 @@ venv.bak/
|
|
|
147
147
|
|
|
148
148
|
# mkdocs documentation
|
|
149
149
|
/site
|
|
150
|
+
# Locally-generated TUI screenshots (regenerated at build time by gen-files)
|
|
151
|
+
docs/assets/tui/
|
|
150
152
|
|
|
151
153
|
# mypy
|
|
152
154
|
.mypy_cache/
|
|
@@ -187,11 +189,10 @@ test/test_data/*.html
|
|
|
187
189
|
.local.code-workspace
|
|
188
190
|
local.ps1
|
|
189
191
|
|
|
190
|
-
# Generated documentation examples (uploaded to GitHub releases instead)
|
|
191
|
-
docs/claude-code-log-transcript.html
|
|
192
|
-
docs/cache/
|
|
193
192
|
|
|
194
193
|
# Integration test data - exclude generated cache/HTML, keep JSONL files
|
|
195
194
|
test/test_data/real_projects/*/cache/
|
|
196
195
|
test/test_data/real_projects/*/*.html
|
|
197
196
|
test/test_data/real_projects/index.html
|
|
197
|
+
# SQLite cache the CLI drops into a projects dir when run against test data
|
|
198
|
+
claude-code-log-cache.db
|
|
@@ -6,6 +6,54 @@ 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.4.0] - 2026-06-03
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Fix sed**
|
|
14
|
+
- **Add MkDocs documentation site with live TUI reference (#197)**
|
|
15
|
+
- **Relax Textual constraint from `==` to `>=` (#196)**
|
|
16
|
+
- **Add `--version` flag to the CLI (#195)**
|
|
17
|
+
- **Fix AskUserQuestion result rendering + highlight chosen options (#180) (#189)**
|
|
18
|
+
- **Derive render_session_id from the SessionTree, not a loop variable (#190)**
|
|
19
|
+
- **Fix collapsible body overlapping preceding content in tool cards (#153) (#187)**
|
|
20
|
+
- **Extract compute_session_data + compute_project_aggregates (C9b) (#188)**
|
|
21
|
+
- **Add C9a characterization tests for session-scan call sites (#186)**
|
|
22
|
+
- **Route converter summary + ai-title extraction through shared helpers (#185)**
|
|
23
|
+
- **status: Wave B fully merged; Wave C kickoff (C8/C9a/C9b stacked, C10 dropped, decisions locked)**
|
|
24
|
+
- **Compute branch preview once from the DAG-line (#184)**
|
|
25
|
+
- **status: #184 fully validated (CI 11/11, CodeRabbit clean) — ready to merge**
|
|
26
|
+
- **docs: keep simplification status note self-contained to project scope**
|
|
27
|
+
- **status: correct #184 state; move GitHub CI/CodeRabbit ops to github guideline**
|
|
28
|
+
- **status: reverse-order stacked-PR lift recipe CONFIRMED on #184**
|
|
29
|
+
- **Factor session-header construction out of _render_messages (#183)**
|
|
30
|
+
- **status: #183/#184 rebased + CodeRabbit forced (#183 clean, #184 2 doc fixes); record @coderabbitai + reverse-order workarounds**
|
|
31
|
+
- **Dedup requestId tokens in pagination cache-miss fallback (#182)**
|
|
32
|
+
- **status: Wave B track complete — opp 7 PR #184 up, all monk-approved; add merge sequence**
|
|
33
|
+
- **status: note stacked-PR CI/CodeRabbit defers to merge-time**
|
|
34
|
+
- **status: opp 1 green (#182, awaiting merge); opp 6 #183 in review; opp 7 in progress**
|
|
35
|
+
- **status: opp 1 -> PR #182 (in review); opp 6 in progress**
|
|
36
|
+
- **Add live-status section to simplification plan**
|
|
37
|
+
- **Add converter/renderer simplification plan**
|
|
38
|
+
- **Move detail-visibility predicate onto MessageContent (#181)**
|
|
39
|
+
- **Sync rendering-architecture.md §5 with the current pipeline (#178)**
|
|
40
|
+
- **Extract inline junction-forward-link block into a named pass (#177)**
|
|
41
|
+
- **Co-locate the away-summary detail rule on AwaySummaryMessage (#176)**
|
|
42
|
+
- **Remove vestigial progress-chain parent repair (#175)**
|
|
43
|
+
- **plugins: dev-docs gaps + public helper API + ToolResult example (Phase 2) (#173)**
|
|
44
|
+
- **Implement unified plugin system from RFC #166 (#169)**
|
|
45
|
+
- **Render Read tool results with pygments via structured payload (closes #170) (#172)**
|
|
46
|
+
- **work/: triage against shipped main (#171)**
|
|
47
|
+
- **Always regenerate projects index so variant-flag toggles refresh links (#168)**
|
|
48
|
+
- **RFC: plugin system (unified message-transformer mechanism) (#166)**
|
|
49
|
+
- **Per-message timestamps in Markdown output (#160) (#165)**
|
|
50
|
+
- **Support non-GitHub forges via static map + `--git-link` fallback (#156) (#164)**
|
|
51
|
+
- **Obsidian-friendly output: --output dir + --expand-paths + --filter-path (#151) (#155)**
|
|
52
|
+
- **Linkify commit SHAs in rendered Markdown + HTML — closes #156 (#161)**
|
|
53
|
+
- **CSS clean-ups (issue #153) (#163)**
|
|
54
|
+
- **Cross-link TaskOutput / TaskUpdate headers back to their spawn (#154) (#158)**
|
|
55
|
+
|
|
56
|
+
|
|
9
57
|
## [1.3.0] - 2026-05-14
|
|
10
58
|
|
|
11
59
|
### Changed
|
|
@@ -107,9 +107,16 @@ with pointers to the deep-dive docs:
|
|
|
107
107
|
- [dev-docs/teammates.md](dev-docs/teammates.md) - Teammates feature deep-dive
|
|
108
108
|
- [dev-docs/message-hierarchy.md](dev-docs/message-hierarchy.md) - Fold/unfold state machine
|
|
109
109
|
- [dev-docs/implementing-a-tool-renderer.md](dev-docs/implementing-a-tool-renderer.md) - How-to: add a new tool
|
|
110
|
+
- [dev-docs/plugins.md](dev-docs/plugins.md) - Plugin system reference + author guide
|
|
110
111
|
|
|
111
112
|
User-facing docs live in [docs/](docs/); plans and TODOs live in [work/](work/).
|
|
112
113
|
|
|
114
|
+
A documentation site (MkDocs + Material) is published to GitHub Pages from
|
|
115
|
+
`docs/` + `mkdocs.yml`. The CLI reference is rendered live from Click, and the
|
|
116
|
+
TUI reference (keybindings + screenshots) is auto-generated at build time. See
|
|
117
|
+
the "Documentation Site" section of @CONTRIBUTING.md. Preview with
|
|
118
|
+
`just docs-serve`; strict build with `just docs-build`.
|
|
119
|
+
|
|
113
120
|
### Keeping dev-docs/ in sync
|
|
114
121
|
|
|
115
122
|
`dev-docs/` is **as-built reference** — the code is the authoritative
|
|
@@ -206,6 +206,47 @@ kill -USR1 $(pgrep -f claude-code-log | head -1)
|
|
|
206
206
|
|
|
207
207
|
The handler is installed in `cli.py` via `faulthandler.register(SIGUSR1)`. POSIX-only; no-op on Windows. Unlike `py-spy`, it needs no root and no extra install.
|
|
208
208
|
|
|
209
|
+
## Documentation Site
|
|
210
|
+
|
|
211
|
+
The project publishes a documentation site to GitHub Pages, built with
|
|
212
|
+
[MkDocs](https://www.mkdocs.org/) and the
|
|
213
|
+
[Material](https://squidfunk.github.io/mkdocs-material/) theme. The site
|
|
214
|
+
configuration is `mkdocs.yml`; pages live under `docs/`.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Install docs dependencies
|
|
218
|
+
uv sync --group docs
|
|
219
|
+
|
|
220
|
+
# Live-reload preview at http://127.0.0.1:8000
|
|
221
|
+
just docs-serve
|
|
222
|
+
|
|
223
|
+
# Strict build (fails on broken links/nav — same as CI)
|
|
224
|
+
just docs-build
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Key points:
|
|
228
|
+
|
|
229
|
+
- **CLI reference** (`docs/reference/cli.md`) is rendered live from the Click
|
|
230
|
+
command via the `mkdocs-click` plugin — no manual upkeep.
|
|
231
|
+
- **TUI reference** (`reference/tui.md`) is generated at build time by
|
|
232
|
+
`docs/gen_pages.py` (a `mkdocs-gen-files` script): it introspects the Textual
|
|
233
|
+
`BINDINGS` for the keybindings tables (`scripts/generate_tui_docs.py`) and
|
|
234
|
+
captures SVG screenshots of the running TUI
|
|
235
|
+
(`scripts/generate_tui_screenshots.py`). Both scripts are runnable standalone.
|
|
236
|
+
- **Example output** (`example.md` + `examples/transcript.html`) is rendered at
|
|
237
|
+
build time from a bundled sample project
|
|
238
|
+
(`scripts/generate_example_output.py`, also `just example`) — no private data
|
|
239
|
+
or release asset involved. Generation is fault-tolerant so a render hiccup
|
|
240
|
+
can't block the build.
|
|
241
|
+
- **Development** section surfaces `dev-docs/` (symlinked as `docs/development`).
|
|
242
|
+
`CONTRIBUTING.md` and `CHANGELOG.md` are symlinked in as `docs/contributing.md`
|
|
243
|
+
and `docs/changelog.md`. A build hook (`docs/hooks.py`) rewrites links to repo
|
|
244
|
+
source files (e.g. `../claude_code_log/cli.py`) into GitHub URLs so the strict
|
|
245
|
+
build stays green.
|
|
246
|
+
- Deployment is automated by `.github/workflows/docs.yml`: PRs run a strict
|
|
247
|
+
build; pushes to `main` deploy to Pages. The repo's **Settings → Pages →
|
|
248
|
+
Source** must be set to **GitHub Actions** (one-time).
|
|
249
|
+
|
|
209
250
|
## Architecture
|
|
210
251
|
|
|
211
252
|
Start with [dev-docs/application_model.md](dev-docs/application_model.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-log
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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
|
|
@@ -18,7 +18,7 @@ Requires-Dist: mistune>=3.1.4
|
|
|
18
18
|
Requires-Dist: packaging>=25.0
|
|
19
19
|
Requires-Dist: pydantic>=2.12.0
|
|
20
20
|
Requires-Dist: pygments>=2.19.2
|
|
21
|
-
Requires-Dist: textual
|
|
21
|
+
Requires-Dist: textual>=6.5.0
|
|
22
22
|
Requires-Dist: toml>=0.10.2
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
@@ -40,7 +40,7 @@ TUI demo:
|
|
|
40
40
|
|
|
41
41
|
This tool generates clean, minimalist HTML pages showing user prompts and assistant responses chronologically. It's designed to create a readable log of your Claude Code interactions with support for both individual files and entire project hierarchies.
|
|
42
42
|
|
|
43
|
-
📄 **[View Example HTML Output](https://github.
|
|
43
|
+
📄 **[View Example HTML Output](https://daaain.github.io/claude-code-log/example/)** - A real example generated from a sample of this project's development, regenerated on every docs build
|
|
44
44
|
|
|
45
45
|
## Quickstart
|
|
46
46
|
|
|
@@ -188,6 +188,21 @@ claude-code-log /path/to/project --detail low --format md --compact
|
|
|
188
188
|
|
|
189
189
|
`--compact` merges consecutive same-type sections in Markdown so runs of assistant responses share one heading instead of repeating `### 🤖 Assistant:` for each.
|
|
190
190
|
|
|
191
|
+
### Linking Commit SHAs
|
|
192
|
+
|
|
193
|
+
Plain `7c2e6f6`-shaped tokens in transcript prose get turned into clickable commit links when the SHA is reachable from a local remote-tracking branch. **github.com**, **gitlab.com**, and **bitbucket.org** work out of the box. For self-hosted forges (in-house GitLab, Gitea, Forgejo, …), supply a URL template via `--git-link`:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Self-hosted GitLab
|
|
197
|
+
claude-code-log /path/to/transcript --git-link 'https://{host}/{path}/-/commit/{sha}'
|
|
198
|
+
|
|
199
|
+
# Same thing via env var (useful for TUI / repeated invocations)
|
|
200
|
+
export CLAUDE_CODE_LOG_GIT_LINK='https://{host}/{path}/-/commit/{sha}'
|
|
201
|
+
claude-code-log --tui
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Placeholders: `{host}`, `{path}`, `{sha}`. The template fires only when the static map doesn't already know the host, so a mix of GitHub repos + self-hosted GitLab gets correct links from both. SHAs not reachable from any local remote-tracking ref render as plain text — local-only work-in-progress commits never produce broken links.
|
|
205
|
+
|
|
191
206
|
## Project Hierarchy Output
|
|
192
207
|
|
|
193
208
|
When processing all projects, the tool generates:
|
|
@@ -16,7 +16,7 @@ TUI demo:
|
|
|
16
16
|
|
|
17
17
|
This tool generates clean, minimalist HTML pages showing user prompts and assistant responses chronologically. It's designed to create a readable log of your Claude Code interactions with support for both individual files and entire project hierarchies.
|
|
18
18
|
|
|
19
|
-
📄 **[View Example HTML Output](https://github.
|
|
19
|
+
📄 **[View Example HTML Output](https://daaain.github.io/claude-code-log/example/)** - A real example generated from a sample of this project's development, regenerated on every docs build
|
|
20
20
|
|
|
21
21
|
## Quickstart
|
|
22
22
|
|
|
@@ -164,6 +164,21 @@ claude-code-log /path/to/project --detail low --format md --compact
|
|
|
164
164
|
|
|
165
165
|
`--compact` merges consecutive same-type sections in Markdown so runs of assistant responses share one heading instead of repeating `### 🤖 Assistant:` for each.
|
|
166
166
|
|
|
167
|
+
### Linking Commit SHAs
|
|
168
|
+
|
|
169
|
+
Plain `7c2e6f6`-shaped tokens in transcript prose get turned into clickable commit links when the SHA is reachable from a local remote-tracking branch. **github.com**, **gitlab.com**, and **bitbucket.org** work out of the box. For self-hosted forges (in-house GitLab, Gitea, Forgejo, …), supply a URL template via `--git-link`:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Self-hosted GitLab
|
|
173
|
+
claude-code-log /path/to/transcript --git-link 'https://{host}/{path}/-/commit/{sha}'
|
|
174
|
+
|
|
175
|
+
# Same thing via env var (useful for TUI / repeated invocations)
|
|
176
|
+
export CLAUDE_CODE_LOG_GIT_LINK='https://{host}/{path}/-/commit/{sha}'
|
|
177
|
+
claude-code-log --tui
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Placeholders: `{host}`, `{path}`, `{sha}`. The template fires only when the static map doesn't already know the host, so a mix of GitHub repos + self-hosted GitLab gets correct links from both. SHAs not reachable from any local remote-tracking ref render as plain text — local-only work-in-progress commits never produce broken links.
|
|
181
|
+
|
|
167
182
|
## Project Hierarchy Output
|
|
168
183
|
|
|
169
184
|
When processing all projects, the tool generates:
|
|
@@ -468,13 +468,99 @@ def _clear_output_files(
|
|
|
468
468
|
click.echo(f"Warning: Failed to clear {ext_upper} files: {e}")
|
|
469
469
|
|
|
470
470
|
|
|
471
|
+
# Placeholders accepted by ``--git-link`` templates. Mirrors
|
|
472
|
+
# ``resolve_sha`` in claude_code_log.git_remote — keep in sync.
|
|
473
|
+
_GIT_LINK_ALLOWED_PLACEHOLDERS = frozenset({"host", "path", "sha"})
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _validate_git_link_template(template: str) -> None:
|
|
477
|
+
"""Validate a ``--git-link`` template eagerly; raise ``click.UsageError`` on issues.
|
|
478
|
+
|
|
479
|
+
Two checks:
|
|
480
|
+
|
|
481
|
+
1. ``{sha}`` must be present (it's the only mandatory field —
|
|
482
|
+
the resolver's whole job is to substitute the commit SHA).
|
|
483
|
+
2. All placeholders must be in ``_GIT_LINK_ALLOWED_PLACEHOLDERS``.
|
|
484
|
+
Catches typos like ``{hsot}`` before they reach
|
|
485
|
+
``template.format()`` (which would raise ``KeyError`` at
|
|
486
|
+
render time). The resolver has a try/except guarding the
|
|
487
|
+
env-var-only path; this validator is the loud-error path for
|
|
488
|
+
CLI users.
|
|
489
|
+
|
|
490
|
+
Uses ``string.Formatter().parse()`` rather than regex so the
|
|
491
|
+
same parser Python uses for ``str.format`` decides what counts
|
|
492
|
+
as a placeholder.
|
|
493
|
+
"""
|
|
494
|
+
import string
|
|
495
|
+
|
|
496
|
+
parsed_fields = [
|
|
497
|
+
field
|
|
498
|
+
for _, field, _, _ in string.Formatter().parse(template)
|
|
499
|
+
if field is not None
|
|
500
|
+
]
|
|
501
|
+
if "" in parsed_fields:
|
|
502
|
+
raise click.UsageError(
|
|
503
|
+
"--git-link template uses an anonymous positional placeholder ({}). "
|
|
504
|
+
"Use a named placeholder ({host}, {path}, or {sha}) instead "
|
|
505
|
+
f"(got: {template!r})."
|
|
506
|
+
)
|
|
507
|
+
fields = set(parsed_fields)
|
|
508
|
+
unknown = fields - _GIT_LINK_ALLOWED_PLACEHOLDERS
|
|
509
|
+
if unknown:
|
|
510
|
+
raise click.UsageError(
|
|
511
|
+
f"--git-link template uses unknown placeholder(s): "
|
|
512
|
+
f"{', '.join('{' + f + '}' for f in sorted(unknown))}. "
|
|
513
|
+
f"Allowed: {{host}}, {{path}}, {{sha}}."
|
|
514
|
+
)
|
|
515
|
+
if "sha" not in fields:
|
|
516
|
+
raise click.UsageError(
|
|
517
|
+
"--git-link template must contain a {sha} placeholder "
|
|
518
|
+
f"(got: {template!r}). Example: "
|
|
519
|
+
"'https://{host}/{path}/-/commit/{sha}'."
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
471
523
|
@click.command()
|
|
524
|
+
@click.version_option(version=get_library_version(), prog_name="claude-code-log")
|
|
472
525
|
@click.argument("input_path", type=click.Path(path_type=Path), required=False)
|
|
473
526
|
@click.option(
|
|
474
527
|
"-o",
|
|
475
528
|
"--output",
|
|
476
529
|
type=click.Path(path_type=Path),
|
|
477
|
-
help=
|
|
530
|
+
help=(
|
|
531
|
+
"Output destination. With a recognised file suffix "
|
|
532
|
+
"(.html/.md/.markdown/.json) treated as a single output file; "
|
|
533
|
+
"otherwise treated as a directory root (and now also honoured "
|
|
534
|
+
"for --all-projects, where outputs land at "
|
|
535
|
+
"<output>/<project>/...). Pair with --expand-paths to project "
|
|
536
|
+
"back to the real on-disk tree."
|
|
537
|
+
),
|
|
538
|
+
)
|
|
539
|
+
@click.option(
|
|
540
|
+
"--expand-paths",
|
|
541
|
+
is_flag=True,
|
|
542
|
+
help=(
|
|
543
|
+
"When set with --output and --all-projects, expand each "
|
|
544
|
+
"project's flat encoded dir name (e.g. '-home-joe-project-A') "
|
|
545
|
+
"back to its real path under <output>/. Resolves the encoded "
|
|
546
|
+
"name via the cache's recorded `cwd`, falling back to a peek "
|
|
547
|
+
"of the first JSONL when the cache is empty. Useful for "
|
|
548
|
+
"projecting transcripts into Obsidian-style Markdown vaults."
|
|
549
|
+
),
|
|
550
|
+
)
|
|
551
|
+
@click.option(
|
|
552
|
+
"--filter-path",
|
|
553
|
+
type=str,
|
|
554
|
+
default=None,
|
|
555
|
+
help=(
|
|
556
|
+
"Restrict --all-projects to projects matching a path prefix. "
|
|
557
|
+
"With --expand-paths, the prefix is matched against the "
|
|
558
|
+
"expanded real path AND truncated from the destination "
|
|
559
|
+
"(`/home/joe/project/A` with --filter-path /home/joe lands at "
|
|
560
|
+
"<output>/project/A/). Without --expand-paths, matches the "
|
|
561
|
+
"flat encoded dir name (e.g. '-home-joe' selects projects "
|
|
562
|
+
"starting with '-home-joe-')."
|
|
563
|
+
),
|
|
478
564
|
)
|
|
479
565
|
@click.option(
|
|
480
566
|
"--open-browser",
|
|
@@ -499,7 +585,25 @@ def _clear_output_files(
|
|
|
499
585
|
@click.option(
|
|
500
586
|
"--no-individual-sessions",
|
|
501
587
|
is_flag=True,
|
|
502
|
-
help=
|
|
588
|
+
help=(
|
|
589
|
+
"Skip generating individual session files (combined transcript only). "
|
|
590
|
+
"Back-compat alias for --combined only."
|
|
591
|
+
),
|
|
592
|
+
)
|
|
593
|
+
@click.option(
|
|
594
|
+
"--combined",
|
|
595
|
+
"combined",
|
|
596
|
+
type=click.Choice(["yes", "no", "only"], case_sensitive=False),
|
|
597
|
+
default=None,
|
|
598
|
+
help=(
|
|
599
|
+
"Control combined-vs-individual transcript generation: "
|
|
600
|
+
"'yes' = both combined and per-session files (default for --all-projects); "
|
|
601
|
+
"'no' = only per-session files (recommended for Obsidian / vault use — "
|
|
602
|
+
"combined is dead weight); "
|
|
603
|
+
"'only' = only the combined file (= --no-individual-sessions). "
|
|
604
|
+
"When unset, defaults to 'no' under --expand-paths (Obsidian mode), "
|
|
605
|
+
"else 'yes'."
|
|
606
|
+
),
|
|
503
607
|
)
|
|
504
608
|
@click.option(
|
|
505
609
|
"--no-cache",
|
|
@@ -578,6 +682,29 @@ def _clear_output_files(
|
|
|
578
682
|
"Markdown-only — a no-op for HTML."
|
|
579
683
|
),
|
|
580
684
|
)
|
|
685
|
+
@click.option(
|
|
686
|
+
"--git-link",
|
|
687
|
+
"git_link",
|
|
688
|
+
default=None,
|
|
689
|
+
envvar="CLAUDE_CODE_LOG_GIT_LINK",
|
|
690
|
+
metavar="TEMPLATE",
|
|
691
|
+
help=(
|
|
692
|
+
"URL template for resolving commit SHAs on forges not in the built-in "
|
|
693
|
+
"map (github.com, gitlab.com, bitbucket.org). Placeholders: {host}, "
|
|
694
|
+
"{path}, {sha}. Example for self-hosted GitLab: "
|
|
695
|
+
"--git-link 'https://{host}/{path}/-/commit/{sha}'. Can also be set "
|
|
696
|
+
"via the CLAUDE_CODE_LOG_GIT_LINK env var."
|
|
697
|
+
),
|
|
698
|
+
)
|
|
699
|
+
@click.option(
|
|
700
|
+
"--no-timestamps",
|
|
701
|
+
is_flag=True,
|
|
702
|
+
help=(
|
|
703
|
+
"Suppress per-message timestamp lines in Markdown output "
|
|
704
|
+
"(#160). Markdown-only — a warning is emitted (but not an "
|
|
705
|
+
"error) if combined with --format html / --format json."
|
|
706
|
+
),
|
|
707
|
+
)
|
|
581
708
|
@click.option(
|
|
582
709
|
"--debug",
|
|
583
710
|
is_flag=True,
|
|
@@ -587,6 +714,9 @@ def _clear_output_files(
|
|
|
587
714
|
def main(
|
|
588
715
|
input_path: Optional[Path],
|
|
589
716
|
output: Optional[Path],
|
|
717
|
+
expand_paths: bool,
|
|
718
|
+
filter_path: Optional[str],
|
|
719
|
+
combined: Optional[str],
|
|
590
720
|
open_browser: bool,
|
|
591
721
|
from_date: Optional[str],
|
|
592
722
|
to_date: Optional[str],
|
|
@@ -603,6 +733,8 @@ def main(
|
|
|
603
733
|
session_id: Optional[str],
|
|
604
734
|
detail: str,
|
|
605
735
|
compact: bool,
|
|
736
|
+
git_link: Optional[str],
|
|
737
|
+
no_timestamps: bool,
|
|
606
738
|
debug: bool,
|
|
607
739
|
) -> None:
|
|
608
740
|
"""Convert Claude transcript JSONL files to HTML or Markdown.
|
|
@@ -613,9 +745,101 @@ def main(
|
|
|
613
745
|
# can be diagnosed with `kill -USR1 <pid>` without root or restart.
|
|
614
746
|
_install_stack_dump_signal()
|
|
615
747
|
|
|
748
|
+
# Custom-forge URL template: validate eagerly with a loud error,
|
|
749
|
+
# then pin to the env var so the resolver (which reads the env at
|
|
750
|
+
# render time) picks it up. Doing this at env-var level keeps the
|
|
751
|
+
# resolver decoupled from Click; the env var is the underlying
|
|
752
|
+
# contract, the CLI flag is a convenience that sets it.
|
|
753
|
+
if git_link is not None:
|
|
754
|
+
_validate_git_link_template(git_link)
|
|
755
|
+
os.environ["CLAUDE_CODE_LOG_GIT_LINK"] = git_link
|
|
756
|
+
|
|
616
757
|
# Configure logging to show warnings and above
|
|
617
758
|
logging.basicConfig(level=logging.WARNING, format="%(levelname)s: %(message)s")
|
|
618
759
|
|
|
760
|
+
# Resolve --combined default and back-compat with --no-individual-sessions.
|
|
761
|
+
# `--combined` semantics:
|
|
762
|
+
# yes → write combined transcript AND per-session files
|
|
763
|
+
# no → write per-session files only (Obsidian-friendly)
|
|
764
|
+
# only → write combined transcript only (= --no-individual-sessions)
|
|
765
|
+
# Default: yes, except when --expand-paths is set (Obsidian mode → no).
|
|
766
|
+
if combined is None:
|
|
767
|
+
combined = "no" if expand_paths else "yes"
|
|
768
|
+
else:
|
|
769
|
+
combined = combined.lower()
|
|
770
|
+
if no_individual_sessions:
|
|
771
|
+
if combined == "no":
|
|
772
|
+
raise click.BadParameter(
|
|
773
|
+
"--no-individual-sessions conflicts with --combined no "
|
|
774
|
+
"(both attempt to skip per-session files but --no-individual-sessions "
|
|
775
|
+
"implies combined-only). Pick one.",
|
|
776
|
+
param_hint="--no-individual-sessions",
|
|
777
|
+
)
|
|
778
|
+
# `--no-individual-sessions` is a strict alias for `--combined only`;
|
|
779
|
+
# honour it for back-compat (and prefer this over an unset --combined).
|
|
780
|
+
combined = "only"
|
|
781
|
+
# Derived flags actually consumed downstream.
|
|
782
|
+
write_combined = combined in ("yes", "only")
|
|
783
|
+
write_individual = combined in ("yes", "no")
|
|
784
|
+
|
|
785
|
+
# Loud rejection of relative `--filter-path` when paired with
|
|
786
|
+
# `--expand-paths` (#151). Without this, a user typing
|
|
787
|
+
# `--filter-path home/joe` (forgetting the leading `/`) would
|
|
788
|
+
# match against an absolute resolved path via `Path.relative_to`,
|
|
789
|
+
# which raises ValueError for *any* mismatch including
|
|
790
|
+
# "argument is relative" — so the silent failure mode is "every
|
|
791
|
+
# project skipped". Reject up-front instead.
|
|
792
|
+
#
|
|
793
|
+
# `path_looks_absolute` is host-OS-agnostic (accepts POSIX `/`
|
|
794
|
+
# OR Windows `C:\` form), so a Linux-recorded `/home/joe`
|
|
795
|
+
# processed on Windows still passes the guard.
|
|
796
|
+
from .utils import path_looks_absolute as _path_looks_absolute
|
|
797
|
+
|
|
798
|
+
if filter_path and expand_paths and not _path_looks_absolute(filter_path):
|
|
799
|
+
raise click.BadParameter(
|
|
800
|
+
f"--filter-path must be an absolute path when --expand-paths is set; "
|
|
801
|
+
f"got {filter_path!r}",
|
|
802
|
+
param_hint="--filter-path",
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
# Warn early if Obsidian-friendly flags (#151) were passed in a
|
|
806
|
+
# context where they're no-ops. `--all-projects` (explicit or
|
|
807
|
+
# implicit via no input_path) is the only mode that consumes them;
|
|
808
|
+
# `--output` must be a directory (file-suffixed output goes
|
|
809
|
+
# through the single-file path which doesn't honour these flags).
|
|
810
|
+
from .utils import output_path_is_file as _output_path_is_file
|
|
811
|
+
|
|
812
|
+
will_run_all_projects = all_projects or input_path is None
|
|
813
|
+
if (expand_paths or filter_path) and tui:
|
|
814
|
+
click.echo(
|
|
815
|
+
"Warning: --expand-paths / --filter-path are ignored in --tui mode.",
|
|
816
|
+
err=True,
|
|
817
|
+
)
|
|
818
|
+
elif (expand_paths or filter_path) and not will_run_all_projects:
|
|
819
|
+
click.echo(
|
|
820
|
+
"Warning: --expand-paths / --filter-path require --all-projects "
|
|
821
|
+
"(or omitting INPUT_PATH); ignoring.",
|
|
822
|
+
err=True,
|
|
823
|
+
)
|
|
824
|
+
elif (expand_paths or filter_path) and (
|
|
825
|
+
output is None or _output_path_is_file(output)
|
|
826
|
+
):
|
|
827
|
+
click.echo(
|
|
828
|
+
"Warning: --expand-paths / --filter-path require --output to be a "
|
|
829
|
+
"directory (no recognised file suffix); ignoring.",
|
|
830
|
+
err=True,
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
# `--no-timestamps` is Markdown-only (#160). Warn (not error) when
|
|
834
|
+
# paired with HTML/JSON so the flag is benignly ignored rather than
|
|
835
|
+
# silently misapplied.
|
|
836
|
+
if no_timestamps and output_format not in ("md", "markdown"):
|
|
837
|
+
click.echo(
|
|
838
|
+
f"Warning: --no-timestamps is Markdown-only; ignoring under "
|
|
839
|
+
f"--format {output_format}.",
|
|
840
|
+
err=True,
|
|
841
|
+
)
|
|
842
|
+
|
|
619
843
|
from .models import DetailLevel
|
|
620
844
|
|
|
621
845
|
detail_level = DetailLevel(detail.lower())
|
|
@@ -779,6 +1003,7 @@ def main(
|
|
|
779
1003
|
image_export_mode,
|
|
780
1004
|
detail=detail_level,
|
|
781
1005
|
compact=compact,
|
|
1006
|
+
no_timestamps=no_timestamps,
|
|
782
1007
|
)
|
|
783
1008
|
click.echo(f"Successfully exported session to {output_path}")
|
|
784
1009
|
if open_browser:
|
|
@@ -813,17 +1038,32 @@ def main(
|
|
|
813
1038
|
raise FileNotFoundError(f"Projects directory not found: {input_path}")
|
|
814
1039
|
|
|
815
1040
|
click.echo(f"Processing all projects in {input_path}...")
|
|
1041
|
+
# `--output` for `--all-projects` (#151): pass a *directory*
|
|
1042
|
+
# to project per-project outputs into. File-suffixed values
|
|
1043
|
+
# are routed to the single-file path elsewhere; here we
|
|
1044
|
+
# only honour directory-shaped `--output`.
|
|
1045
|
+
from .utils import output_path_is_file
|
|
1046
|
+
|
|
1047
|
+
output_dir_for_projects: Optional[Path] = None
|
|
1048
|
+
if output is not None and not output_path_is_file(output):
|
|
1049
|
+
output_dir_for_projects = output
|
|
1050
|
+
|
|
816
1051
|
output_path = process_projects_hierarchy(
|
|
817
1052
|
input_path,
|
|
818
1053
|
from_date,
|
|
819
1054
|
to_date,
|
|
820
1055
|
not no_cache,
|
|
821
|
-
|
|
1056
|
+
write_individual,
|
|
822
1057
|
output_format,
|
|
823
1058
|
image_export_mode,
|
|
824
1059
|
page_size=page_size,
|
|
825
1060
|
detail=detail_level,
|
|
826
1061
|
compact=compact,
|
|
1062
|
+
output_dir=output_dir_for_projects,
|
|
1063
|
+
expand_paths=expand_paths,
|
|
1064
|
+
filter_path=filter_path,
|
|
1065
|
+
write_combined=write_combined,
|
|
1066
|
+
no_timestamps=no_timestamps,
|
|
827
1067
|
)
|
|
828
1068
|
|
|
829
1069
|
# Count processed projects
|
|
@@ -872,7 +1112,7 @@ def main(
|
|
|
872
1112
|
output,
|
|
873
1113
|
from_date,
|
|
874
1114
|
to_date,
|
|
875
|
-
|
|
1115
|
+
write_individual,
|
|
876
1116
|
not no_cache,
|
|
877
1117
|
image_export_mode=image_export_mode,
|
|
878
1118
|
page_size=page_size,
|
|
@@ -881,12 +1121,14 @@ def main(
|
|
|
881
1121
|
# User's `-o` path is a one-off export, not a cached artifact:
|
|
882
1122
|
# don't occupy a cache slot keyed by an arbitrary destination.
|
|
883
1123
|
update_cache=output is None,
|
|
1124
|
+
write_combined=write_combined,
|
|
1125
|
+
no_timestamps=no_timestamps,
|
|
884
1126
|
)
|
|
885
1127
|
if input_path.is_file():
|
|
886
1128
|
click.echo(f"Successfully converted {input_path} to {output_path}")
|
|
887
1129
|
else:
|
|
888
1130
|
jsonl_count = len(list(input_path.glob("*.jsonl")))
|
|
889
|
-
if
|
|
1131
|
+
if write_individual:
|
|
890
1132
|
ext = get_file_extension(output_format)
|
|
891
1133
|
session_files = list(input_path.glob(f"session-*.{ext}"))
|
|
892
1134
|
click.echo(
|