klaude-code 1.2.28__tar.gz → 1.2.30__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.
- {klaude_code-1.2.28 → klaude_code-1.2.30}/PKG-INFO +1 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/pyproject.toml +1 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/config_cmd.py +13 -6
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/list_model.py +1 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/__init__.py +3 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/clear_cmd.py +4 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/command_abc.py +4 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/fork_session_cmd.py +2 -2
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/model_cmd.py +42 -2
- klaude_code-1.2.30/src/klaude_code/command/resume_cmd.py +51 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/status_cmd.py +1 -1
- klaude_code-1.2.30/src/klaude_code/config/__init__.py +19 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/config/assets/builtin_config.yaml +11 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/config/config.py +21 -17
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/executor.py +42 -8
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/task.py +7 -6
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/shell/bash_tool.py +5 -2
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/mermaid_tool.py +2 -2
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/commands.py +1 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/model.py +1 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/op.py +12 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/op_handler.py +5 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/export.py +12 -13
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/session.py +2 -2
- klaude_code-1.2.30/src/klaude_code/session/templates/mermaid_viewer.html +926 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/event_handler.py +3 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/assistant.py +4 -2
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/common.py +2 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/developer.py +9 -7
- klaude_code-1.2.30/src/klaude_code/ui/renderers/mermaid_viewer.py +57 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/metadata.py +1 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/thinking.py +1 -1
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/tools.py +214 -26
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/theme.py +21 -0
- klaude_code-1.2.28/src/klaude_code/config/__init__.py +0 -9
- {klaude_code-1.2.28 → klaude_code-1.2.30}/README.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/codex/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/codex/exceptions.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/codex/oauth.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/auth/codex/token_manager.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/auth_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/debug.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/main.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/runtime.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/self_update.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/cli/session_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/debug_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/export_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/export_online_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/help_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/prompt-init.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/prompt-jj-describe.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/prompt_command.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/refresh_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/registry.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/release_notes_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/command/thinking_cmd.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/config/assets/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/config/builtin_config.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/config/select_model.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/const.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/agent.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/manager/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/manager/llm_clients.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompt.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/reminders.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/_utils.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/read_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/read_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/write_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/file/write_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/report_back_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/shell/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/skill/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/skill/skill_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/skill/skill_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/tool_abc.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/tool_context.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/tool_registry.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/tool_runner.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/truncation.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/core/turn.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/anthropic/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/anthropic/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/anthropic/input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/codex/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/codex/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/input_common.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openai_compatible/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openai_compatible/input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openai_compatible/stream.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openrouter/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openrouter/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openrouter/input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/openrouter/reasoning.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/registry.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/responses/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/responses/client.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/responses/input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/llm/usage.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/events.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/llm_param.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/sub_agent/oracle.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/sub_agent/task.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/sub_agent/web.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/protocol/tools.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/codec.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/selector.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/store.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/session/templates/export_session.html +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/assets/dev-docs/SKILL.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/assets/jj-workspace/SKILL.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/loader.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/manager.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/skill/system_skills.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/trace/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/trace/log.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/core/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/core/display.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/core/input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/core/stage_manager.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/debug/display.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/exec/display.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/completers.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/display.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/modes/repl/renderer.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/diffs.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/errors.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/sub_agent.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/renderers/user_input.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/code_panel.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/live.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/markdown.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/quote.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/searchable_text.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/rich/status.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/terminal/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/terminal/color.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/terminal/control.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/terminal/notifier.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/utils/__init__.py +0 -0
- {klaude_code-1.2.28 → klaude_code-1.2.30}/src/klaude_code/ui/utils/common.py +0 -0
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
-
from klaude_code.config import config_path, load_config
|
|
9
|
+
from klaude_code.config import config_path, create_example_config, example_config_path, load_config
|
|
10
10
|
from klaude_code.trace import log
|
|
11
11
|
|
|
12
12
|
|
|
@@ -57,17 +57,24 @@ def edit_config() -> None:
|
|
|
57
57
|
else: # Linux and other Unix systems
|
|
58
58
|
editor = "xdg-open"
|
|
59
59
|
|
|
60
|
-
# Ensure config
|
|
61
|
-
|
|
60
|
+
# Ensure config directory exists and create example config if needed
|
|
61
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
if create_example_config():
|
|
63
|
+
log(f"Created example config: {example_config_path}", style="dim")
|
|
64
|
+
|
|
65
|
+
# Decide which file to open
|
|
66
|
+
target_path = config_path if config_path.exists() else example_config_path
|
|
67
|
+
if target_path == example_config_path:
|
|
68
|
+
log(f"Opening example config (copy to {config_path.name} to use)", style="yellow")
|
|
62
69
|
|
|
63
70
|
try:
|
|
64
71
|
if editor == "open -a TextEdit":
|
|
65
|
-
subprocess.run(["open", "-a", "TextEdit", str(
|
|
72
|
+
subprocess.run(["open", "-a", "TextEdit", str(target_path)], check=True)
|
|
66
73
|
elif editor in ["open", "xdg-open"]:
|
|
67
74
|
# For open/xdg-open, we need to pass the file directly
|
|
68
|
-
subprocess.run([editor, str(
|
|
75
|
+
subprocess.run([editor, str(target_path)], check=True)
|
|
69
76
|
else:
|
|
70
|
-
subprocess.run([editor, str(
|
|
77
|
+
subprocess.run([editor, str(target_path)], check=True)
|
|
71
78
|
except subprocess.CalledProcessError as e:
|
|
72
79
|
log((f"Error: Failed to open editor: {e}", "red"))
|
|
73
80
|
raise typer.Exit(1) from None
|
|
@@ -218,7 +218,7 @@ def display_models_and_providers(config: Config):
|
|
|
218
218
|
|
|
219
219
|
panel = Panel(
|
|
220
220
|
panel_content,
|
|
221
|
-
title=Text(f"Provider: {provider.provider_name}", style=
|
|
221
|
+
title=Text(f"Provider: {provider.provider_name}", style=ThemeKey.CONFIG_PROVIDER),
|
|
222
222
|
border_style=ThemeKey.CONFIG_PANEL_BORDER,
|
|
223
223
|
padding=(0, 1),
|
|
224
224
|
title_align="left",
|
|
@@ -36,6 +36,7 @@ def ensure_commands_loaded() -> None:
|
|
|
36
36
|
from .model_cmd import ModelCommand
|
|
37
37
|
from .refresh_cmd import RefreshTerminalCommand
|
|
38
38
|
from .release_notes_cmd import ReleaseNotesCommand
|
|
39
|
+
from .resume_cmd import ResumeCommand
|
|
39
40
|
from .status_cmd import StatusCommand
|
|
40
41
|
from .terminal_setup_cmd import TerminalSetupCommand
|
|
41
42
|
from .thinking_cmd import ThinkingCommand
|
|
@@ -47,6 +48,7 @@ def ensure_commands_loaded() -> None:
|
|
|
47
48
|
register(ThinkingCommand())
|
|
48
49
|
register(ModelCommand())
|
|
49
50
|
register(ForkSessionCommand())
|
|
51
|
+
register(ResumeCommand())
|
|
50
52
|
load_prompt_commands()
|
|
51
53
|
register(StatusCommand())
|
|
52
54
|
register(HelpCommand())
|
|
@@ -70,6 +72,7 @@ def __getattr__(name: str) -> object:
|
|
|
70
72
|
"ModelCommand": "model_cmd",
|
|
71
73
|
"RefreshTerminalCommand": "refresh_cmd",
|
|
72
74
|
"ReleaseNotesCommand": "release_notes_cmd",
|
|
75
|
+
"ResumeCommand": "resume_cmd",
|
|
73
76
|
"StatusCommand": "status_cmd",
|
|
74
77
|
"TerminalSetupCommand": "terminal_setup_cmd",
|
|
75
78
|
"ThinkingCommand": "thinking_cmd",
|
|
@@ -15,4 +15,7 @@ class ClearCommand(CommandABC):
|
|
|
15
15
|
|
|
16
16
|
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
17
17
|
del user_input # unused
|
|
18
|
-
return CommandResult(
|
|
18
|
+
return CommandResult(
|
|
19
|
+
operations=[op.ClearSessionOperation(session_id=agent.session.id)],
|
|
20
|
+
persist_user_input=False,
|
|
21
|
+
)
|
|
@@ -42,6 +42,10 @@ class CommandResult(BaseModel):
|
|
|
42
42
|
) = None # List of UI events to display immediately
|
|
43
43
|
operations: list[op.Operation] | None = None
|
|
44
44
|
|
|
45
|
+
# Persistence controls: some slash commands are UI/control actions and should not be written to session history.
|
|
46
|
+
persist_user_input: bool = True
|
|
47
|
+
persist_events: bool = True
|
|
48
|
+
|
|
45
49
|
|
|
46
50
|
class CommandABC(ABC):
|
|
47
51
|
"""Abstract base class for slash commands."""
|
|
@@ -24,7 +24,7 @@ class ForkSessionCommand(CommandABC):
|
|
|
24
24
|
command_output=model.CommandOutput(command_name=self.name),
|
|
25
25
|
),
|
|
26
26
|
)
|
|
27
|
-
return CommandResult(events=[event])
|
|
27
|
+
return CommandResult(events=[event], persist_user_input=False, persist_events=False)
|
|
28
28
|
|
|
29
29
|
new_session = agent.session.fork()
|
|
30
30
|
await new_session.wait_for_flush()
|
|
@@ -39,4 +39,4 @@ class ForkSessionCommand(CommandABC):
|
|
|
39
39
|
),
|
|
40
40
|
),
|
|
41
41
|
)
|
|
42
|
-
return CommandResult(events=[event])
|
|
42
|
+
return CommandResult(events=[event], persist_user_input=False, persist_events=False)
|
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
|
|
3
|
+
import questionary
|
|
4
|
+
|
|
3
5
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
4
6
|
from klaude_code.config.select_model import select_model_from_config
|
|
5
7
|
from klaude_code.protocol import commands, events, model, op
|
|
6
8
|
|
|
9
|
+
SELECT_STYLE = questionary.Style(
|
|
10
|
+
[
|
|
11
|
+
("instruction", "ansibrightblack"),
|
|
12
|
+
("pointer", "ansicyan"),
|
|
13
|
+
("highlighted", "ansicyan"),
|
|
14
|
+
("text", "ansibrightblack"),
|
|
15
|
+
]
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _confirm_change_default_model_sync(selected_model: str) -> bool:
|
|
20
|
+
choices: list[questionary.Choice] = [
|
|
21
|
+
questionary.Choice(title="No (session only)", value=False),
|
|
22
|
+
questionary.Choice(title="Yes (save as default)", value=True),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Add a blank line between the model selector and this confirmation prompt.
|
|
27
|
+
questionary.print("")
|
|
28
|
+
result = questionary.select(
|
|
29
|
+
message=f"Save '{selected_model}' as default model (main_model)?",
|
|
30
|
+
choices=choices,
|
|
31
|
+
pointer="→",
|
|
32
|
+
instruction="Use arrow keys to move, Enter to select",
|
|
33
|
+
use_jk_keys=False,
|
|
34
|
+
style=SELECT_STYLE,
|
|
35
|
+
).ask()
|
|
36
|
+
except KeyboardInterrupt:
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
return bool(result)
|
|
40
|
+
|
|
7
41
|
|
|
8
42
|
class ModelCommand(CommandABC):
|
|
9
43
|
"""Display or change the model configuration."""
|
|
@@ -44,7 +78,13 @@ class ModelCommand(CommandABC):
|
|
|
44
78
|
)
|
|
45
79
|
]
|
|
46
80
|
)
|
|
47
|
-
|
|
81
|
+
save_as_default = await asyncio.to_thread(_confirm_change_default_model_sync, selected_model)
|
|
48
82
|
return CommandResult(
|
|
49
|
-
operations=[
|
|
83
|
+
operations=[
|
|
84
|
+
op.ChangeModelOperation(
|
|
85
|
+
session_id=agent.session.id,
|
|
86
|
+
model_name=selected_model,
|
|
87
|
+
save_as_default=save_as_default,
|
|
88
|
+
)
|
|
89
|
+
]
|
|
50
90
|
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
4
|
+
from klaude_code.protocol import commands, events, model, op
|
|
5
|
+
from klaude_code.session.selector import resume_select_session
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ResumeCommand(CommandABC):
|
|
9
|
+
"""Resume a previous session."""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def name(self) -> commands.CommandName:
|
|
13
|
+
return commands.CommandName.RESUME
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def summary(self) -> str:
|
|
17
|
+
return "Resume a previous session"
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def is_interactive(self) -> bool:
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
24
|
+
del user_input # unused
|
|
25
|
+
|
|
26
|
+
if agent.session.messages_count > 0:
|
|
27
|
+
event = events.DeveloperMessageEvent(
|
|
28
|
+
session_id=agent.session.id,
|
|
29
|
+
item=model.DeveloperMessageItem(
|
|
30
|
+
content="Cannot resume: current session already has messages. Use `klaude -r` to start a new instance with session selection.",
|
|
31
|
+
command_output=model.CommandOutput(command_name=self.name, is_error=True),
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
return CommandResult(events=[event], persist_user_input=False, persist_events=False)
|
|
35
|
+
|
|
36
|
+
selected_session_id = await asyncio.to_thread(resume_select_session)
|
|
37
|
+
if selected_session_id is None:
|
|
38
|
+
event = events.DeveloperMessageEvent(
|
|
39
|
+
session_id=agent.session.id,
|
|
40
|
+
item=model.DeveloperMessageItem(
|
|
41
|
+
content="(no session selected)",
|
|
42
|
+
command_output=model.CommandOutput(command_name=self.name),
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
return CommandResult(events=[event], persist_user_input=False, persist_events=False)
|
|
46
|
+
|
|
47
|
+
return CommandResult(
|
|
48
|
+
operations=[op.ResumeSessionOperation(target_session_id=selected_session_id)],
|
|
49
|
+
persist_user_input=False,
|
|
50
|
+
persist_events=False,
|
|
51
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .config import (
|
|
2
|
+
Config,
|
|
3
|
+
UserConfig,
|
|
4
|
+
config_path,
|
|
5
|
+
create_example_config,
|
|
6
|
+
example_config_path,
|
|
7
|
+
load_config,
|
|
8
|
+
print_no_available_models_hint,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Config",
|
|
13
|
+
"UserConfig",
|
|
14
|
+
"config_path",
|
|
15
|
+
"create_example_config",
|
|
16
|
+
"example_config_path",
|
|
17
|
+
"load_config",
|
|
18
|
+
"print_no_available_models_hint",
|
|
19
|
+
]
|
|
@@ -53,6 +53,17 @@ provider_list:
|
|
|
53
53
|
protocol: openrouter
|
|
54
54
|
api_key: ${OPENROUTER_API_KEY}
|
|
55
55
|
model_list:
|
|
56
|
+
- model_name: gpt-5-mini
|
|
57
|
+
model_params:
|
|
58
|
+
model: openai/gpt-5-mini
|
|
59
|
+
max_tokens: 128000
|
|
60
|
+
context_limit: 400000
|
|
61
|
+
thinking:
|
|
62
|
+
reasoning_effort: high
|
|
63
|
+
cost:
|
|
64
|
+
input: 0.25
|
|
65
|
+
output: 2.0
|
|
66
|
+
cache_read: 0.03
|
|
56
67
|
- model_name: gpt-5.1-codex-max
|
|
57
68
|
model_params:
|
|
58
69
|
model: openai/gpt-5.1-codex-max
|
|
@@ -51,6 +51,7 @@ def resolve_api_key(value: str | None) -> str | None:
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
config_path = Path.home() / ".klaude" / "klaude-config.yaml"
|
|
54
|
+
example_config_path = Path.home() / ".klaude" / "klaude-config.example.yaml"
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
class ModelConfig(BaseModel):
|
|
@@ -227,8 +228,8 @@ class Config(BaseModel):
|
|
|
227
228
|
def get_example_config() -> UserConfig:
|
|
228
229
|
"""Generate example config for user reference (will be commented out)."""
|
|
229
230
|
return UserConfig(
|
|
230
|
-
main_model="
|
|
231
|
-
sub_agent_models={"explore": "
|
|
231
|
+
main_model="opus",
|
|
232
|
+
sub_agent_models={"explore": "haiku", "oracle": "gpt-5.2", "webagent": "sonnet", "task": "sonnet"},
|
|
232
233
|
provider_list=[
|
|
233
234
|
UserProviderConfig(
|
|
234
235
|
provider_name="my-provider",
|
|
@@ -357,30 +358,32 @@ def _load_user_config() -> UserConfig | None:
|
|
|
357
358
|
raise ValueError(f"Invalid config file: {config_path}") from e
|
|
358
359
|
|
|
359
360
|
|
|
360
|
-
def
|
|
361
|
-
"""
|
|
362
|
-
|
|
363
|
-
|
|
361
|
+
def create_example_config() -> bool:
|
|
362
|
+
"""Create example config file if it doesn't exist.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
True if file was created, False if it already exists.
|
|
366
|
+
"""
|
|
367
|
+
if example_config_path.exists():
|
|
368
|
+
return False
|
|
364
369
|
|
|
365
370
|
example_config = get_example_config()
|
|
366
|
-
|
|
371
|
+
example_config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
367
372
|
config_dict = example_config.model_dump(mode="json", exclude_none=True)
|
|
368
373
|
|
|
369
|
-
# Comment out all example config lines
|
|
370
374
|
yaml_str = yaml.dump(config_dict, default_flow_style=False, sort_keys=False) or ""
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
_ =
|
|
375
|
+
header = "# Example configuration for klaude-code\n"
|
|
376
|
+
header += "# Copy this file to klaude-config.yaml and modify as needed.\n"
|
|
377
|
+
header += "# Run `klaude list` to see available models.\n"
|
|
378
|
+
header += "#\n"
|
|
379
|
+
header += "# Built-in providers (anthropic, openai, openrouter, deepseek) are available automatically.\n"
|
|
380
|
+
header += "# Just set the corresponding API key environment variable to use them.\n\n"
|
|
381
|
+
_ = example_config_path.write_text(header + yaml_str)
|
|
382
|
+
return True
|
|
378
383
|
|
|
379
384
|
|
|
380
385
|
def _load_config_uncached() -> Config:
|
|
381
386
|
"""Load and merge builtin + user config. Always returns a valid Config."""
|
|
382
|
-
_ensure_config_file_exists()
|
|
383
|
-
|
|
384
387
|
builtin_config = _get_builtin_config()
|
|
385
388
|
user_config = _load_user_config()
|
|
386
389
|
|
|
@@ -417,6 +420,7 @@ def print_no_available_models_hint() -> None:
|
|
|
417
420
|
log(f" export {env_var}=<your-api-key>", style="dim")
|
|
418
421
|
log("")
|
|
419
422
|
log(f"Or add custom providers in: {config_path}", style="dim")
|
|
423
|
+
log(f"See example config: {example_config_path}", style="dim")
|
|
420
424
|
|
|
421
425
|
|
|
422
426
|
# Expose cache control for tests and callers that need to invalidate the cache.
|
|
@@ -203,13 +203,15 @@ class ExecutorContext:
|
|
|
203
203
|
raise ValueError("Multiple RunAgentOperation results are not supported")
|
|
204
204
|
|
|
205
205
|
persisted_user_input = run_ops[0].input if run_ops else user_input
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
|
|
207
|
+
if result.persist_user_input:
|
|
208
|
+
agent.session.append_history(
|
|
209
|
+
[model.UserMessageItem(content=persisted_user_input.text, images=persisted_user_input.images)]
|
|
210
|
+
)
|
|
209
211
|
|
|
210
212
|
if result.events:
|
|
211
213
|
for evt in result.events:
|
|
212
|
-
if isinstance(evt, events.DeveloperMessageEvent):
|
|
214
|
+
if result.persist_events and isinstance(evt, events.DeveloperMessageEvent):
|
|
213
215
|
agent.session.append_history([evt.item])
|
|
214
216
|
await self.emit_event(evt)
|
|
215
217
|
|
|
@@ -237,12 +239,13 @@ class ExecutorContext:
|
|
|
237
239
|
agent.session.model_config_name = operation.model_name
|
|
238
240
|
agent.session.model_thinking = llm_config.thinking
|
|
239
241
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
if operation.save_as_default:
|
|
243
|
+
config.main_model = operation.model_name
|
|
244
|
+
await config.save()
|
|
243
245
|
|
|
246
|
+
default_note = " (saved as default)" if operation.save_as_default else ""
|
|
244
247
|
developer_item = model.DeveloperMessageItem(
|
|
245
|
-
content=f"Switched to: {llm_config.model}",
|
|
248
|
+
content=f"Switched to: {llm_config.model}{default_note}",
|
|
246
249
|
command_output=model.CommandOutput(command_name=commands.CommandName.MODEL),
|
|
247
250
|
)
|
|
248
251
|
agent.session.append_history([developer_item])
|
|
@@ -305,6 +308,37 @@ class ExecutorContext:
|
|
|
305
308
|
)
|
|
306
309
|
await self.emit_event(events.DeveloperMessageEvent(session_id=agent.session.id, item=developer_item))
|
|
307
310
|
|
|
311
|
+
async def handle_resume_session(self, operation: op.ResumeSessionOperation) -> None:
|
|
312
|
+
target_session = Session.load(operation.target_session_id)
|
|
313
|
+
if (
|
|
314
|
+
target_session.model_thinking is not None
|
|
315
|
+
and target_session.model_name
|
|
316
|
+
and target_session.model_name == self.llm_clients.main.model_name
|
|
317
|
+
):
|
|
318
|
+
self.llm_clients.main.get_llm_config().thinking = target_session.model_thinking
|
|
319
|
+
|
|
320
|
+
profile = self.model_profile_provider.build_profile(self.llm_clients.main)
|
|
321
|
+
from klaude_code.core.agent import Agent
|
|
322
|
+
|
|
323
|
+
agent = Agent(session=target_session, profile=profile)
|
|
324
|
+
|
|
325
|
+
async for evt in agent.replay_history():
|
|
326
|
+
await self.emit_event(evt)
|
|
327
|
+
|
|
328
|
+
await self.emit_event(
|
|
329
|
+
events.WelcomeEvent(
|
|
330
|
+
work_dir=str(target_session.work_dir),
|
|
331
|
+
llm_config=self.llm_clients.main.get_llm_config(),
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
self._agent = agent
|
|
336
|
+
log_debug(
|
|
337
|
+
f"Resumed session: {target_session.id}",
|
|
338
|
+
style="cyan",
|
|
339
|
+
debug_type=DebugType.EXECUTION,
|
|
340
|
+
)
|
|
341
|
+
|
|
308
342
|
async def handle_export_session(self, operation: op.ExportSessionOperation) -> None:
|
|
309
343
|
agent = await self._ensure_agent(operation.session_id)
|
|
310
344
|
try:
|
|
@@ -238,12 +238,13 @@ class TaskExecutor:
|
|
|
238
238
|
return
|
|
239
239
|
|
|
240
240
|
if turn is None or turn.task_finished:
|
|
241
|
-
#
|
|
242
|
-
if
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
241
|
+
# Empty result should retry instead of finishing
|
|
242
|
+
if turn is not None and not turn.task_result.strip():
|
|
243
|
+
if ctx.sub_agent_state is not None:
|
|
244
|
+
error_msg = "Sub-agent returned empty result, retrying..."
|
|
245
|
+
else:
|
|
246
|
+
error_msg = "Agent returned empty result, retrying..."
|
|
247
|
+
yield events.ErrorEvent(error_message=error_msg, can_retry=True)
|
|
247
248
|
continue
|
|
248
249
|
break
|
|
249
250
|
|
|
@@ -334,7 +334,9 @@ class BashTool(ToolABC):
|
|
|
334
334
|
_best_effort_update_file_tracker(args.command)
|
|
335
335
|
return model.ToolResultItem(
|
|
336
336
|
status="success",
|
|
337
|
-
|
|
337
|
+
# Preserve leading whitespace for tools like `nl -ba`.
|
|
338
|
+
# Only trim trailing newlines to avoid adding an extra blank line in the UI.
|
|
339
|
+
output=output.rstrip("\n"),
|
|
338
340
|
)
|
|
339
341
|
else:
|
|
340
342
|
combined = ""
|
|
@@ -346,7 +348,8 @@ class BashTool(ToolABC):
|
|
|
346
348
|
combined = f"Command exited with code {rc}"
|
|
347
349
|
return model.ToolResultItem(
|
|
348
350
|
status="error",
|
|
349
|
-
|
|
351
|
+
# Preserve leading whitespace; only trim trailing newlines.
|
|
352
|
+
output=combined.rstrip("\n"),
|
|
350
353
|
)
|
|
351
354
|
except FileNotFoundError:
|
|
352
355
|
return model.ToolResultItem(
|
|
@@ -11,7 +11,7 @@ from klaude_code.core.tool.tool_abc import ToolABC, load_desc
|
|
|
11
11
|
from klaude_code.core.tool.tool_registry import register
|
|
12
12
|
from klaude_code.protocol import llm_param, model, tools
|
|
13
13
|
|
|
14
|
-
_MERMAID_LIVE_PREFIX = "https://mermaid.live/
|
|
14
|
+
_MERMAID_LIVE_PREFIX = "https://mermaid.live/view#pako:"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@register(tools.MERMAID)
|
|
@@ -49,7 +49,7 @@ class MermaidTool(ToolABC):
|
|
|
49
49
|
|
|
50
50
|
link = cls._build_link(args.code)
|
|
51
51
|
line_count = cls._count_lines(args.code)
|
|
52
|
-
ui_extra = model.MermaidLinkUIExtra(link=link, line_count=line_count)
|
|
52
|
+
ui_extra = model.MermaidLinkUIExtra(code=args.code, link=link, line_count=line_count)
|
|
53
53
|
output = f"Mermaid diagram rendered successfully ({line_count} lines)."
|
|
54
54
|
return model.ToolResultItem(status="success", output=output, ui_extra=ui_extra)
|
|
55
55
|
|
|
@@ -16,6 +16,7 @@ class CommandName(str, Enum):
|
|
|
16
16
|
RELEASE_NOTES = "release-notes"
|
|
17
17
|
THINKING = "thinking"
|
|
18
18
|
FORK_SESSION = "fork-session"
|
|
19
|
+
RESUME = "resume"
|
|
19
20
|
# PLAN and DOC are dynamically registered now, but kept here if needed for reference
|
|
20
21
|
# or we can remove them if no code explicitly imports them.
|
|
21
22
|
# PLAN = "plan"
|
|
@@ -27,6 +27,7 @@ class OperationType(Enum):
|
|
|
27
27
|
CHANGE_MODEL = "change_model"
|
|
28
28
|
CHANGE_THINKING = "change_thinking"
|
|
29
29
|
CLEAR_SESSION = "clear_session"
|
|
30
|
+
RESUME_SESSION = "resume_session"
|
|
30
31
|
EXPORT_SESSION = "export_session"
|
|
31
32
|
INTERRUPT = "interrupt"
|
|
32
33
|
INIT_AGENT = "init_agent"
|
|
@@ -73,6 +74,7 @@ class ChangeModelOperation(Operation):
|
|
|
73
74
|
type: OperationType = OperationType.CHANGE_MODEL
|
|
74
75
|
session_id: str
|
|
75
76
|
model_name: str
|
|
77
|
+
save_as_default: bool = False
|
|
76
78
|
|
|
77
79
|
async def execute(self, handler: OperationHandler) -> None:
|
|
78
80
|
await handler.handle_change_model(self)
|
|
@@ -98,6 +100,16 @@ class ClearSessionOperation(Operation):
|
|
|
98
100
|
await handler.handle_clear_session(self)
|
|
99
101
|
|
|
100
102
|
|
|
103
|
+
class ResumeSessionOperation(Operation):
|
|
104
|
+
"""Operation for resuming a different session."""
|
|
105
|
+
|
|
106
|
+
type: OperationType = OperationType.RESUME_SESSION
|
|
107
|
+
target_session_id: str
|
|
108
|
+
|
|
109
|
+
async def execute(self, handler: OperationHandler) -> None:
|
|
110
|
+
await handler.handle_resume_session(self)
|
|
111
|
+
|
|
112
|
+
|
|
101
113
|
class ExportSessionOperation(Operation):
|
|
102
114
|
"""Operation for exporting a session transcript to HTML."""
|
|
103
115
|
|
|
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
ExportSessionOperation,
|
|
17
17
|
InitAgentOperation,
|
|
18
18
|
InterruptOperation,
|
|
19
|
+
ResumeSessionOperation,
|
|
19
20
|
RunAgentOperation,
|
|
20
21
|
UserInputOperation,
|
|
21
22
|
)
|
|
@@ -44,6 +45,10 @@ class OperationHandler(Protocol):
|
|
|
44
45
|
"""Handle a clear session operation."""
|
|
45
46
|
...
|
|
46
47
|
|
|
48
|
+
async def handle_resume_session(self, operation: ResumeSessionOperation) -> None:
|
|
49
|
+
"""Handle a resume session operation."""
|
|
50
|
+
...
|
|
51
|
+
|
|
47
52
|
async def handle_export_session(self, operation: ExportSessionOperation) -> None:
|
|
48
53
|
"""Handle an export session operation."""
|
|
49
54
|
...
|
|
@@ -421,12 +421,6 @@ def _render_diff_span(span: model.DiffSpan, line_kind: str) -> str:
|
|
|
421
421
|
return f'<span class="diff-span">{text}</span>'
|
|
422
422
|
|
|
423
423
|
|
|
424
|
-
def _get_diff_ui_extra(ui_extra: model.ToolResultUIExtra | None) -> model.DiffUIExtra | None:
|
|
425
|
-
if isinstance(ui_extra, model.DiffUIExtra):
|
|
426
|
-
return ui_extra
|
|
427
|
-
return None
|
|
428
|
-
|
|
429
|
-
|
|
430
424
|
def _render_markdown_doc(doc: model.MarkdownDocUIExtra) -> str:
|
|
431
425
|
encoded = _escape_html(doc.content)
|
|
432
426
|
file_path = _escape_html(doc.file_path)
|
|
@@ -481,21 +475,28 @@ def _build_add_only_diff(text: str, file_path: str) -> model.DiffUIExtra:
|
|
|
481
475
|
def _get_mermaid_link_html(
|
|
482
476
|
ui_extra: model.ToolResultUIExtra | None, tool_call: model.ToolCallItem | None = None
|
|
483
477
|
) -> str | None:
|
|
484
|
-
|
|
478
|
+
code = ""
|
|
479
|
+
link: str | None = None
|
|
480
|
+
line_count = 0
|
|
481
|
+
|
|
482
|
+
if isinstance(ui_extra, model.MermaidLinkUIExtra):
|
|
483
|
+
code = ui_extra.code
|
|
484
|
+
link = ui_extra.link
|
|
485
|
+
line_count = ui_extra.line_count
|
|
486
|
+
|
|
487
|
+
if not code and tool_call and tool_call.name == "Mermaid":
|
|
485
488
|
try:
|
|
486
489
|
args = json.loads(tool_call.arguments)
|
|
487
490
|
code = args.get("code", "")
|
|
488
491
|
except (json.JSONDecodeError, TypeError):
|
|
489
492
|
code = ""
|
|
490
|
-
|
|
491
|
-
code = ""
|
|
493
|
+
line_count = code.count("\n") + 1 if code else 0
|
|
492
494
|
|
|
493
|
-
if not code and not
|
|
495
|
+
if not code and not link:
|
|
494
496
|
return None
|
|
495
497
|
|
|
496
498
|
# Prepare code for rendering and copy
|
|
497
499
|
escaped_code = _escape_html(code) if code else ""
|
|
498
|
-
line_count = code.count("\n") + 1 if code else 0
|
|
499
500
|
|
|
500
501
|
# Build Toolbar
|
|
501
502
|
toolbar_items: list[str] = []
|
|
@@ -512,8 +513,6 @@ def _get_mermaid_link_html(
|
|
|
512
513
|
'<button type="button" class="fullscreen-mermaid-btn" title="View Fullscreen">Fullscreen</button>'
|
|
513
514
|
)
|
|
514
515
|
|
|
515
|
-
link = ui_extra.link if isinstance(ui_extra, model.MermaidLinkUIExtra) else None
|
|
516
|
-
|
|
517
516
|
if link:
|
|
518
517
|
link_url = _escape_html(link)
|
|
519
518
|
buttons_html.append(
|
|
@@ -213,7 +213,7 @@ class Session(BaseModel):
|
|
|
213
213
|
forked.file_tracker = {k: v.model_copy(deep=True) for k, v in self.file_tracker.items()}
|
|
214
214
|
forked.todos = [todo.model_copy(deep=True) for todo in self.todos]
|
|
215
215
|
|
|
216
|
-
items = [
|
|
216
|
+
items = [it.model_copy(deep=True) for it in self.conversation_history]
|
|
217
217
|
if items:
|
|
218
218
|
forked.append_history(items)
|
|
219
219
|
|
|
@@ -246,7 +246,7 @@ class Session(BaseModel):
|
|
|
246
246
|
def need_turn_start(self, prev_item: model.ConversationItem | None, item: model.ConversationItem) -> bool:
|
|
247
247
|
if not isinstance(
|
|
248
248
|
item,
|
|
249
|
-
model.
|
|
249
|
+
model.ReasoningTextItem | model.AssistantMessageItem | model.ToolCallItem,
|
|
250
250
|
):
|
|
251
251
|
return False
|
|
252
252
|
if prev_item is None:
|