klaude-code 1.2.30__tar.gz → 1.3.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.
- {klaude_code-1.2.30 → klaude_code-1.3.0}/PKG-INFO +1 -1
- {klaude_code-1.2.30 → klaude_code-1.3.0}/pyproject.toml +1 -1
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/model_cmd.py +3 -3
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/__init__.py +2 -0
- klaude_code-1.3.0/src/klaude_code/core/tool/file/move_tool.md +41 -0
- klaude_code-1.3.0/src/klaude_code/core/tool/file/move_tool.py +435 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/tool_registry.py +3 -3
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/sub_agent/task.py +1 -1
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/tools.py +1 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/event_handler.py +1 -1
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +20 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/key_bindings.py +72 -9
- klaude_code-1.3.0/src/klaude_code/ui/renderers/bash_syntax.py +178 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/tools.py +60 -148
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/theme.py +3 -1
- {klaude_code-1.2.30 → klaude_code-1.3.0}/README.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/codex/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/codex/exceptions.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/codex/oauth.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/auth/codex/token_manager.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/auth_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/config_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/debug.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/list_model.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/main.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/runtime.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/self_update.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/cli/session_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/clear_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/command_abc.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/debug_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/export_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/export_online_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/fork_session_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/help_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/prompt-init.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/prompt-jj-describe.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/prompt_command.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/refresh_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/registry.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/release_notes_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/resume_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/status_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/command/thinking_cmd.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/assets/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/assets/builtin_config.yaml +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/builtin_config.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/config.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/config/select_model.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/const.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/agent.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/executor.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/manager/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/manager/llm_clients.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompt.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/reminders.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/task.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/_utils.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/read_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/read_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/write_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/file/write_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/report_back_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/shell/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/skill/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/skill/skill_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/skill/skill_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/tool_abc.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/tool_context.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/tool_runner.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/truncation.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/core/turn.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/anthropic/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/anthropic/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/anthropic/input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/codex/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/codex/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/input_common.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openai_compatible/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openai_compatible/input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openai_compatible/stream.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openrouter/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openrouter/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openrouter/input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/openrouter/reasoning.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/registry.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/responses/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/responses/client.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/responses/input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/llm/usage.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/commands.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/events.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/llm_param.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/model.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/op.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/op_handler.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/sub_agent/oracle.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/protocol/sub_agent/web.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/codec.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/export.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/selector.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/session.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/store.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/templates/export_session.html +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/session/templates/mermaid_viewer.html +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/assets/dev-docs/SKILL.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/assets/jj-workspace/SKILL.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/loader.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/manager.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/skill/system_skills.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/trace/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/trace/log.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/core/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/core/display.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/core/input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/core/stage_manager.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/debug/display.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/exec/display.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/completers.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/display.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/renderer.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/assistant.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/common.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/developer.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/diffs.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/errors.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/mermaid_viewer.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/metadata.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/sub_agent.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/thinking.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/renderers/user_input.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/code_panel.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/live.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/markdown.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/quote.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/searchable_text.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/rich/status.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/terminal/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/terminal/color.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/terminal/control.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/terminal/notifier.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/utils/__init__.py +0 -0
- {klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/utils/common.py +0 -0
|
@@ -18,15 +18,15 @@ SELECT_STYLE = questionary.Style(
|
|
|
18
18
|
|
|
19
19
|
def _confirm_change_default_model_sync(selected_model: str) -> bool:
|
|
20
20
|
choices: list[questionary.Choice] = [
|
|
21
|
-
questionary.Choice(title="No
|
|
22
|
-
questionary.Choice(title="Yes (save as default)", value=True),
|
|
21
|
+
questionary.Choice(title="No (session only)", value=False),
|
|
22
|
+
questionary.Choice(title="Yes (save as default main_model in ~/.klaude/klaude-config.yaml)", value=True),
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
try:
|
|
26
26
|
# Add a blank line between the model selector and this confirmation prompt.
|
|
27
27
|
questionary.print("")
|
|
28
28
|
result = questionary.select(
|
|
29
|
-
message=f"Save '{selected_model}' as default model
|
|
29
|
+
message=f"Save '{selected_model}' as default model?",
|
|
30
30
|
choices=choices,
|
|
31
31
|
pointer="→",
|
|
32
32
|
instruction="Use arrow keys to move, Enter to select",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .file.apply_patch import DiffError, process_patch
|
|
2
2
|
from .file.apply_patch_tool import ApplyPatchTool
|
|
3
|
+
from .file.move_tool import MoveTool
|
|
3
4
|
from .file.edit_tool import EditTool
|
|
4
5
|
from .file.read_tool import ReadTool
|
|
5
6
|
from .file.write_tool import WriteTool
|
|
@@ -31,6 +32,7 @@ from .web.web_search_tool import WebSearchTool
|
|
|
31
32
|
__all__ = [
|
|
32
33
|
"ApplyPatchTool",
|
|
33
34
|
"BashTool",
|
|
35
|
+
"MoveTool",
|
|
34
36
|
"DiffError",
|
|
35
37
|
"EditTool",
|
|
36
38
|
"FileTracker",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Moves a range of lines from one file to another.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
- Cuts lines from `start_line` to `end_line` (inclusive, 1-indexed) from the source file
|
|
5
|
+
- Pastes them into the target file at `insert_line` (inserted before that line)
|
|
6
|
+
- Both files must have been read first using the Read tool
|
|
7
|
+
- To create a new target file, set `insert_line` to 1 and ensure target file does not exist
|
|
8
|
+
- For same-file moves, line numbers refer to the original file state before any changes
|
|
9
|
+
- Use this tool when refactoring code into separate modules to avoid passing large code blocks twice
|
|
10
|
+
- To move files or directories, use the Bash tool with `mv` command instead
|
|
11
|
+
|
|
12
|
+
Return format:
|
|
13
|
+
The tool returns context snippets showing the state after the operation:
|
|
14
|
+
|
|
15
|
+
1. Source file context (after cut): Shows 3 lines before and after the cut location
|
|
16
|
+
2. Target file context (after insert): Shows 3 lines before the inserted content, the inserted content itself, and 3 lines after
|
|
17
|
+
|
|
18
|
+
Example output:
|
|
19
|
+
```
|
|
20
|
+
Cut 8 lines from /path/source.py (lines 9-16) and pasted into /path/target.py (updated) at line 10.
|
|
21
|
+
|
|
22
|
+
Source file context (after cut):
|
|
23
|
+
6 return value
|
|
24
|
+
7
|
|
25
|
+
8
|
|
26
|
+
-------- cut here --------
|
|
27
|
+
9 class NextClass:
|
|
28
|
+
10 pass
|
|
29
|
+
11
|
|
30
|
+
|
|
31
|
+
Target file context (after insert):
|
|
32
|
+
7 return {}
|
|
33
|
+
8
|
|
34
|
+
9
|
|
35
|
+
-------- inserted --------
|
|
36
|
+
10 class MovedClass:
|
|
37
|
+
...
|
|
38
|
+
17 return result
|
|
39
|
+
-------- end --------
|
|
40
|
+
18 # Next section
|
|
41
|
+
```
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from klaude_code.core.tool.file._utils import file_exists, hash_text_sha256, is_directory, read_text, write_text
|
|
11
|
+
from klaude_code.core.tool.file.diff_builder import build_structured_diff
|
|
12
|
+
from klaude_code.core.tool.tool_abc import ToolABC, load_desc
|
|
13
|
+
from klaude_code.core.tool.tool_context import get_current_file_tracker
|
|
14
|
+
from klaude_code.core.tool.tool_registry import register
|
|
15
|
+
from klaude_code.protocol import llm_param, model, tools
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MoveArguments(BaseModel):
|
|
19
|
+
source_file_path: str
|
|
20
|
+
start_line: int = Field(ge=1, description="Start line number (1-indexed, inclusive)")
|
|
21
|
+
end_line: int = Field(ge=1, description="End line number (1-indexed, inclusive)")
|
|
22
|
+
target_file_path: str
|
|
23
|
+
insert_line: int = Field(ge=1, description="Line number to insert before (1-indexed)")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _build_context_snippet(
|
|
27
|
+
all_lines: list[str],
|
|
28
|
+
start_line: int,
|
|
29
|
+
end_line: int,
|
|
30
|
+
context_lines: int = 3,
|
|
31
|
+
marker: str = "cut here",
|
|
32
|
+
) -> str:
|
|
33
|
+
"""Build a snippet showing context around a cut/insert point.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
all_lines: All lines of the file (after modification).
|
|
37
|
+
start_line: 1-indexed start line of the context focus area.
|
|
38
|
+
end_line: 1-indexed end line of the context focus area.
|
|
39
|
+
context_lines: Number of context lines before and after.
|
|
40
|
+
marker: Text to show in the separator line.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Formatted snippet with context and separator.
|
|
44
|
+
"""
|
|
45
|
+
result: list[str] = []
|
|
46
|
+
|
|
47
|
+
# Context before
|
|
48
|
+
ctx_start = max(1, start_line - context_lines)
|
|
49
|
+
for line_no in range(ctx_start, start_line):
|
|
50
|
+
idx = line_no - 1
|
|
51
|
+
if idx < len(all_lines):
|
|
52
|
+
content = all_lines[idx].rstrip("\n")
|
|
53
|
+
result.append(f"{line_no:>6}\t{content}")
|
|
54
|
+
|
|
55
|
+
# Separator
|
|
56
|
+
result.append(f" -------- {marker} --------")
|
|
57
|
+
|
|
58
|
+
# Context after
|
|
59
|
+
ctx_end = min(len(all_lines), end_line + context_lines)
|
|
60
|
+
for line_no in range(end_line, ctx_end + 1):
|
|
61
|
+
idx = line_no - 1
|
|
62
|
+
if idx < len(all_lines):
|
|
63
|
+
content = all_lines[idx].rstrip("\n")
|
|
64
|
+
result.append(f"{line_no:>6}\t{content}")
|
|
65
|
+
|
|
66
|
+
return "\n".join(result)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _build_insert_context_snippet(
|
|
70
|
+
all_lines: list[str],
|
|
71
|
+
insert_line: int,
|
|
72
|
+
inserted_count: int,
|
|
73
|
+
context_lines: int = 3,
|
|
74
|
+
) -> str:
|
|
75
|
+
"""Build a snippet showing context around inserted content.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
all_lines: All lines of the file (after insertion).
|
|
79
|
+
insert_line: 1-indexed line where content was inserted.
|
|
80
|
+
inserted_count: Number of lines that were inserted.
|
|
81
|
+
context_lines: Number of context lines before and after.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Formatted snippet with context and inserted content highlighted.
|
|
85
|
+
"""
|
|
86
|
+
result: list[str] = []
|
|
87
|
+
insert_end = insert_line + inserted_count - 1
|
|
88
|
+
|
|
89
|
+
# Context before
|
|
90
|
+
ctx_start = max(1, insert_line - context_lines)
|
|
91
|
+
for line_no in range(ctx_start, insert_line):
|
|
92
|
+
idx = line_no - 1
|
|
93
|
+
if idx < len(all_lines):
|
|
94
|
+
content = all_lines[idx].rstrip("\n")
|
|
95
|
+
result.append(f"{line_no:>6}\t{content}")
|
|
96
|
+
|
|
97
|
+
# Start separator
|
|
98
|
+
result.append(" -------- inserted --------")
|
|
99
|
+
|
|
100
|
+
# Inserted content
|
|
101
|
+
for line_no in range(insert_line, insert_end + 1):
|
|
102
|
+
idx = line_no - 1
|
|
103
|
+
if idx < len(all_lines):
|
|
104
|
+
content = all_lines[idx].rstrip("\n")
|
|
105
|
+
result.append(f"{line_no:>6}\t{content}")
|
|
106
|
+
|
|
107
|
+
# End separator
|
|
108
|
+
result.append(" -------- end --------")
|
|
109
|
+
|
|
110
|
+
# Context after
|
|
111
|
+
ctx_end = min(len(all_lines), insert_end + context_lines)
|
|
112
|
+
for line_no in range(insert_end + 1, ctx_end + 1):
|
|
113
|
+
idx = line_no - 1
|
|
114
|
+
if idx < len(all_lines):
|
|
115
|
+
content = all_lines[idx].rstrip("\n")
|
|
116
|
+
result.append(f"{line_no:>6}\t{content}")
|
|
117
|
+
|
|
118
|
+
return "\n".join(result)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@register(tools.MOVE)
|
|
122
|
+
class MoveTool(ToolABC):
|
|
123
|
+
@classmethod
|
|
124
|
+
def schema(cls) -> llm_param.ToolSchema:
|
|
125
|
+
return llm_param.ToolSchema(
|
|
126
|
+
name=tools.MOVE,
|
|
127
|
+
type="function",
|
|
128
|
+
description=load_desc(Path(__file__).parent / "move_tool.md"),
|
|
129
|
+
parameters={
|
|
130
|
+
"type": "object",
|
|
131
|
+
"properties": {
|
|
132
|
+
"source_file_path": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"description": "The absolute path to the source file to cut from",
|
|
135
|
+
},
|
|
136
|
+
"start_line": {
|
|
137
|
+
"type": "integer",
|
|
138
|
+
"description": "Start line number (1-indexed, inclusive)",
|
|
139
|
+
},
|
|
140
|
+
"end_line": {
|
|
141
|
+
"type": "integer",
|
|
142
|
+
"description": "End line number (1-indexed, inclusive)",
|
|
143
|
+
},
|
|
144
|
+
"target_file_path": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "The absolute path to the target file to paste into",
|
|
147
|
+
},
|
|
148
|
+
"insert_line": {
|
|
149
|
+
"type": "integer",
|
|
150
|
+
"description": "Line number to insert before (1-indexed)",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"required": ["source_file_path", "start_line", "end_line", "target_file_path", "insert_line"],
|
|
154
|
+
"additionalProperties": False,
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
async def call(cls, arguments: str) -> model.ToolResultItem:
|
|
160
|
+
try:
|
|
161
|
+
args = MoveArguments.model_validate_json(arguments)
|
|
162
|
+
except ValueError as e:
|
|
163
|
+
return model.ToolResultItem(status="error", output=f"Invalid arguments: {e}")
|
|
164
|
+
|
|
165
|
+
source_path = os.path.abspath(args.source_file_path)
|
|
166
|
+
target_path = os.path.abspath(args.target_file_path)
|
|
167
|
+
same_file = source_path == target_path
|
|
168
|
+
|
|
169
|
+
# Validate paths
|
|
170
|
+
if is_directory(source_path):
|
|
171
|
+
return model.ToolResultItem(
|
|
172
|
+
status="error",
|
|
173
|
+
output="<tool_use_error>Source path is a directory, not a file.</tool_use_error>",
|
|
174
|
+
)
|
|
175
|
+
if is_directory(target_path):
|
|
176
|
+
return model.ToolResultItem(
|
|
177
|
+
status="error",
|
|
178
|
+
output="<tool_use_error>Target path is a directory, not a file.</tool_use_error>",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Validate line range
|
|
182
|
+
if args.start_line > args.end_line:
|
|
183
|
+
return model.ToolResultItem(
|
|
184
|
+
status="error",
|
|
185
|
+
output="<tool_use_error>start_line must be <= end_line.</tool_use_error>",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Check file tracker
|
|
189
|
+
file_tracker = get_current_file_tracker()
|
|
190
|
+
source_exists = file_exists(source_path)
|
|
191
|
+
target_exists = file_exists(target_path)
|
|
192
|
+
|
|
193
|
+
if not source_exists:
|
|
194
|
+
return model.ToolResultItem(
|
|
195
|
+
status="error",
|
|
196
|
+
output="<tool_use_error>Source file does not exist.</tool_use_error>",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
source_status: model.FileStatus | None = None
|
|
200
|
+
target_status: model.FileStatus | None = None
|
|
201
|
+
|
|
202
|
+
if file_tracker is not None:
|
|
203
|
+
source_status = file_tracker.get(source_path)
|
|
204
|
+
if source_status is None:
|
|
205
|
+
return model.ToolResultItem(
|
|
206
|
+
status="error",
|
|
207
|
+
output="Source file has not been read yet. Read it first.",
|
|
208
|
+
)
|
|
209
|
+
if target_exists:
|
|
210
|
+
target_status = file_tracker.get(target_path)
|
|
211
|
+
if target_status is None:
|
|
212
|
+
return model.ToolResultItem(
|
|
213
|
+
status="error",
|
|
214
|
+
output="Target file has not been read yet. Read it first before writing to it.",
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Read source file
|
|
218
|
+
try:
|
|
219
|
+
source_content = await asyncio.to_thread(read_text, source_path)
|
|
220
|
+
except OSError as e:
|
|
221
|
+
return model.ToolResultItem(
|
|
222
|
+
status="error", output=f"<tool_use_error>Failed to read source: {e}</tool_use_error>"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Verify source hasn't been modified externally
|
|
226
|
+
if source_status is not None and source_status.content_sha256 is not None:
|
|
227
|
+
current_sha256 = hash_text_sha256(source_content)
|
|
228
|
+
if current_sha256 != source_status.content_sha256:
|
|
229
|
+
return model.ToolResultItem(
|
|
230
|
+
status="error",
|
|
231
|
+
output="Source file has been modified externally. Read it first before editing.",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
source_lines = source_content.splitlines(keepends=True)
|
|
235
|
+
|
|
236
|
+
# Validate line numbers against actual file
|
|
237
|
+
if args.start_line > len(source_lines):
|
|
238
|
+
return model.ToolResultItem(
|
|
239
|
+
status="error",
|
|
240
|
+
output=f"<tool_use_error>start_line {args.start_line} exceeds file length {len(source_lines)}.</tool_use_error>",
|
|
241
|
+
)
|
|
242
|
+
if args.end_line > len(source_lines):
|
|
243
|
+
return model.ToolResultItem(
|
|
244
|
+
status="error",
|
|
245
|
+
output=f"<tool_use_error>end_line {args.end_line} exceeds file length {len(source_lines)}.</tool_use_error>",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Extract the lines to move (convert to 0-indexed)
|
|
249
|
+
cut_lines = source_lines[args.start_line - 1 : args.end_line]
|
|
250
|
+
|
|
251
|
+
# Read target file content (if exists)
|
|
252
|
+
target_before = ""
|
|
253
|
+
if target_exists:
|
|
254
|
+
try:
|
|
255
|
+
target_before = await asyncio.to_thread(read_text, target_path)
|
|
256
|
+
except OSError as e:
|
|
257
|
+
return model.ToolResultItem(
|
|
258
|
+
status="error", output=f"<tool_use_error>Failed to read target: {e}</tool_use_error>"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Verify target hasn't been modified externally
|
|
262
|
+
if target_status is not None and target_status.content_sha256 is not None:
|
|
263
|
+
current_sha256 = hash_text_sha256(target_before)
|
|
264
|
+
if current_sha256 != target_status.content_sha256:
|
|
265
|
+
return model.ToolResultItem(
|
|
266
|
+
status="error",
|
|
267
|
+
output="Target file has been modified externally. Read it first before writing to it.",
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# For new target file, only allow insert_line = 1
|
|
271
|
+
if not target_exists and args.insert_line != 1:
|
|
272
|
+
return model.ToolResultItem(
|
|
273
|
+
status="error",
|
|
274
|
+
output="<tool_use_error>Target file does not exist. Use insert_line=1 to create new file.</tool_use_error>",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Build new content for both files
|
|
278
|
+
source_before = source_content
|
|
279
|
+
|
|
280
|
+
if same_file:
|
|
281
|
+
# Same file move: more complex logic
|
|
282
|
+
# First remove the cut lines, then insert at adjusted position
|
|
283
|
+
new_lines = source_lines[: args.start_line - 1] + source_lines[args.end_line :]
|
|
284
|
+
|
|
285
|
+
# Adjust insert position if it was after the cut region
|
|
286
|
+
adjusted_insert = args.insert_line
|
|
287
|
+
if args.insert_line > args.end_line:
|
|
288
|
+
adjusted_insert -= args.end_line - args.start_line + 1
|
|
289
|
+
elif args.insert_line > args.start_line:
|
|
290
|
+
# Insert position is within the cut region - error
|
|
291
|
+
return model.ToolResultItem(
|
|
292
|
+
status="error",
|
|
293
|
+
output="<tool_use_error>insert_line cannot be within the cut range for same-file move.</tool_use_error>",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Validate adjusted insert line
|
|
297
|
+
if adjusted_insert > len(new_lines) + 1:
|
|
298
|
+
return model.ToolResultItem(
|
|
299
|
+
status="error",
|
|
300
|
+
output=f"<tool_use_error>insert_line {args.insert_line} is out of bounds after cut.</tool_use_error>",
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Insert at adjusted position
|
|
304
|
+
final_lines = new_lines[: adjusted_insert - 1] + cut_lines + new_lines[adjusted_insert - 1 :]
|
|
305
|
+
source_after = "".join(final_lines)
|
|
306
|
+
target_after = source_after # Same file
|
|
307
|
+
|
|
308
|
+
# Write the file once
|
|
309
|
+
try:
|
|
310
|
+
await asyncio.to_thread(write_text, source_path, source_after)
|
|
311
|
+
except OSError as e:
|
|
312
|
+
return model.ToolResultItem(
|
|
313
|
+
status="error", output=f"<tool_use_error>Failed to write: {e}</tool_use_error>"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Update tracker
|
|
317
|
+
if file_tracker is not None:
|
|
318
|
+
with contextlib.suppress(Exception):
|
|
319
|
+
existing = file_tracker.get(source_path)
|
|
320
|
+
is_mem = existing.is_memory if existing else False
|
|
321
|
+
file_tracker[source_path] = model.FileStatus(
|
|
322
|
+
mtime=Path(source_path).stat().st_mtime,
|
|
323
|
+
content_sha256=hash_text_sha256(source_after),
|
|
324
|
+
is_memory=is_mem,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
ui_extra = build_structured_diff(source_before, source_after, file_path=source_path)
|
|
328
|
+
cut_count = args.end_line - args.start_line + 1
|
|
329
|
+
|
|
330
|
+
# Build context snippets for same-file move
|
|
331
|
+
final_lines = source_after.splitlines(keepends=True)
|
|
332
|
+
# Show context around cut location (now joined)
|
|
333
|
+
cut_context = _build_context_snippet(final_lines, args.start_line, args.start_line, marker="cut here")
|
|
334
|
+
# Show context around insert location
|
|
335
|
+
insert_context = _build_insert_context_snippet(final_lines, adjusted_insert, cut_count)
|
|
336
|
+
|
|
337
|
+
output = (
|
|
338
|
+
f"Moved {cut_count} lines within {source_path} "
|
|
339
|
+
f"(from lines {args.start_line}-{args.end_line} to line {args.insert_line}).\n\n"
|
|
340
|
+
f"Source context (after cut):\n{cut_context}\n\n"
|
|
341
|
+
f"Insert context:\n{insert_context}"
|
|
342
|
+
)
|
|
343
|
+
return model.ToolResultItem(
|
|
344
|
+
status="success",
|
|
345
|
+
output=output,
|
|
346
|
+
ui_extra=ui_extra,
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
# Different files
|
|
350
|
+
# Remove lines from source
|
|
351
|
+
new_source_lines = source_lines[: args.start_line - 1] + source_lines[args.end_line :]
|
|
352
|
+
source_after = "".join(new_source_lines)
|
|
353
|
+
|
|
354
|
+
# Insert into target
|
|
355
|
+
target_lines = target_before.splitlines(keepends=True) if target_before else []
|
|
356
|
+
|
|
357
|
+
# Validate insert_line for existing target
|
|
358
|
+
if target_exists and args.insert_line > len(target_lines) + 1:
|
|
359
|
+
return model.ToolResultItem(
|
|
360
|
+
status="error",
|
|
361
|
+
output=f"<tool_use_error>insert_line {args.insert_line} exceeds target file length + 1.</tool_use_error>",
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
new_target_lines = target_lines[: args.insert_line - 1] + cut_lines + target_lines[args.insert_line - 1 :]
|
|
365
|
+
target_after = "".join(new_target_lines)
|
|
366
|
+
|
|
367
|
+
# Ensure target ends with newline if source content did
|
|
368
|
+
if cut_lines and not target_after.endswith("\n"):
|
|
369
|
+
target_after += "\n"
|
|
370
|
+
|
|
371
|
+
# Write both files
|
|
372
|
+
try:
|
|
373
|
+
await asyncio.to_thread(write_text, source_path, source_after)
|
|
374
|
+
await asyncio.to_thread(write_text, target_path, target_after)
|
|
375
|
+
except OSError as e:
|
|
376
|
+
return model.ToolResultItem(
|
|
377
|
+
status="error", output=f"<tool_use_error>Failed to write: {e}</tool_use_error>"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Update tracker for both files
|
|
381
|
+
if file_tracker is not None:
|
|
382
|
+
with contextlib.suppress(Exception):
|
|
383
|
+
existing = file_tracker.get(source_path)
|
|
384
|
+
is_mem = existing.is_memory if existing else False
|
|
385
|
+
file_tracker[source_path] = model.FileStatus(
|
|
386
|
+
mtime=Path(source_path).stat().st_mtime,
|
|
387
|
+
content_sha256=hash_text_sha256(source_after),
|
|
388
|
+
is_memory=is_mem,
|
|
389
|
+
)
|
|
390
|
+
with contextlib.suppress(Exception):
|
|
391
|
+
existing = file_tracker.get(target_path)
|
|
392
|
+
is_mem = existing.is_memory if existing else False
|
|
393
|
+
file_tracker[target_path] = model.FileStatus(
|
|
394
|
+
mtime=Path(target_path).stat().st_mtime,
|
|
395
|
+
content_sha256=hash_text_sha256(target_after),
|
|
396
|
+
is_memory=is_mem,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Build UI extra with diffs for both files
|
|
400
|
+
source_diff = build_structured_diff(source_before, source_after, file_path=source_path)
|
|
401
|
+
target_diff = build_structured_diff(target_before, target_after, file_path=target_path)
|
|
402
|
+
|
|
403
|
+
ui_extra: model.ToolResultUIExtra | None = None
|
|
404
|
+
if source_diff and target_diff:
|
|
405
|
+
ui_extra = model.MultiUIExtra(items=[source_diff, target_diff])
|
|
406
|
+
elif source_diff:
|
|
407
|
+
ui_extra = source_diff
|
|
408
|
+
elif target_diff:
|
|
409
|
+
ui_extra = target_diff
|
|
410
|
+
|
|
411
|
+
cut_count = args.end_line - args.start_line + 1
|
|
412
|
+
action = "created" if not target_exists else "updated"
|
|
413
|
+
|
|
414
|
+
# Build context snippets for different-file move
|
|
415
|
+
source_after_lines = source_after.splitlines(keepends=True)
|
|
416
|
+
target_after_lines = target_after.splitlines(keepends=True)
|
|
417
|
+
|
|
418
|
+
# Show context around cut location in source file
|
|
419
|
+
source_context = _build_context_snippet(
|
|
420
|
+
source_after_lines, args.start_line, args.start_line, marker="cut here"
|
|
421
|
+
)
|
|
422
|
+
# Show context around insert location in target file
|
|
423
|
+
target_context = _build_insert_context_snippet(target_after_lines, args.insert_line, cut_count)
|
|
424
|
+
|
|
425
|
+
output = (
|
|
426
|
+
f"Moved {cut_count} lines from {source_path} (lines {args.start_line}-{args.end_line}) "
|
|
427
|
+
f"to {target_path} ({action}) at line {args.insert_line}.\n\n"
|
|
428
|
+
f"Source file context (after move):\n{source_context}\n\n"
|
|
429
|
+
f"Target file context (after insert):\n{target_context}"
|
|
430
|
+
)
|
|
431
|
+
return model.ToolResultItem(
|
|
432
|
+
status="success",
|
|
433
|
+
output=output,
|
|
434
|
+
ui_extra=ui_extra,
|
|
435
|
+
)
|
|
@@ -66,11 +66,11 @@ def load_agent_tools(
|
|
|
66
66
|
|
|
67
67
|
# Main agent tools
|
|
68
68
|
if "gpt-5" in model_name:
|
|
69
|
-
tool_names = [tools.BASH, tools.READ, tools.APPLY_PATCH, tools.UPDATE_PLAN]
|
|
69
|
+
tool_names = [tools.BASH, tools.READ, tools.APPLY_PATCH, tools.MOVE, tools.UPDATE_PLAN]
|
|
70
70
|
elif "gemini-3" in model_name:
|
|
71
|
-
tool_names = [tools.BASH, tools.READ, tools.EDIT, tools.WRITE]
|
|
71
|
+
tool_names = [tools.BASH, tools.READ, tools.EDIT, tools.WRITE, tools.MOVE]
|
|
72
72
|
else:
|
|
73
|
-
tool_names = [tools.BASH, tools.READ, tools.EDIT, tools.WRITE, tools.TODO_WRITE]
|
|
73
|
+
tool_names = [tools.BASH, tools.READ, tools.EDIT, tools.WRITE, tools.MOVE, tools.TODO_WRITE]
|
|
74
74
|
|
|
75
75
|
tool_names.extend(sub_agent_tool_names(enabled_only=True, model_name=model_name))
|
|
76
76
|
tool_names.extend([tools.SKILL, tools.MERMAID])
|
|
@@ -54,7 +54,7 @@ register_sub_agent(
|
|
|
54
54
|
description=TASK_DESCRIPTION,
|
|
55
55
|
parameters=TASK_PARAMETERS,
|
|
56
56
|
prompt_file="prompts/prompt-sub-agent.md",
|
|
57
|
-
tool_set=(tools.BASH, tools.READ, tools.EDIT, tools.WRITE),
|
|
57
|
+
tool_set=(tools.BASH, tools.READ, tools.EDIT, tools.WRITE, tools.MOVE),
|
|
58
58
|
active_form="Tasking",
|
|
59
59
|
output_schema_arg="output_format",
|
|
60
60
|
)
|
|
@@ -504,7 +504,7 @@ class DisplayEventHandler:
|
|
|
504
504
|
emit_osc94(OSC94States.HIDDEN)
|
|
505
505
|
self.spinner_status.reset()
|
|
506
506
|
self.renderer.spinner_stop()
|
|
507
|
-
self.renderer.console.print(Rule(characters="
|
|
507
|
+
self.renderer.console.print(Rule(characters="─", style=ThemeKey.LINES))
|
|
508
508
|
await self.stage_manager.transition_to(Stage.WAITING)
|
|
509
509
|
self._maybe_notify_task_finish(event)
|
|
510
510
|
|
{klaude_code-1.2.30 → klaude_code-1.3.0}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py
RENAMED
|
@@ -106,6 +106,26 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
106
106
|
),
|
|
107
107
|
)
|
|
108
108
|
|
|
109
|
+
def _select_first_completion_on_open(buf) -> None: # type: ignore[no-untyped-def]
|
|
110
|
+
"""Default to selecting the first completion without inserting it."""
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
state = buf.complete_state # type: ignore[reportUnknownMemberType]
|
|
114
|
+
if state is None:
|
|
115
|
+
return
|
|
116
|
+
if not state.completions: # type: ignore[reportUnknownMemberType]
|
|
117
|
+
return
|
|
118
|
+
if state.complete_index is None: # type: ignore[reportUnknownMemberType]
|
|
119
|
+
state.complete_index = 0 # type: ignore[reportUnknownMemberType]
|
|
120
|
+
with contextlib.suppress(Exception):
|
|
121
|
+
self._session.app.invalidate()
|
|
122
|
+
except Exception:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Ensure the completion menu always has a default selection (first item),
|
|
126
|
+
# so Enter/Tab can accept immediately.
|
|
127
|
+
self._session.default_buffer.on_completions_changed += _select_first_completion_on_open
|
|
128
|
+
|
|
109
129
|
def _get_bottom_toolbar(self) -> FormattedText | None:
|
|
110
130
|
"""Return bottom toolbar content only when there's an update message available."""
|
|
111
131
|
if not self._status_provider:
|