klaude-code 2.10.2__tar.gz → 2.10.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. {klaude_code-2.10.2 → klaude_code-2.10.3}/PKG-INFO +1 -1
  2. {klaude_code-2.10.2 → klaude_code-2.10.3}/pyproject.toml +1 -1
  3. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/assets/builtin_config.yaml +7 -7
  4. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/anthropic/input.py +28 -4
  5. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/developer.py +1 -1
  6. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/errors.py +2 -4
  7. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/metadata.py +5 -10
  8. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/markdown.py +5 -1
  9. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/theme.py +2 -2
  10. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/user_input.py +1 -1
  11. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/completers.py +51 -17
  12. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/images.py +127 -0
  13. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/prompt_toolkit.py +1 -1
  14. {klaude_code-2.10.2 → klaude_code-2.10.3}/README.md +0 -0
  15. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/.DS_Store +0 -0
  16. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/__init__.py +0 -0
  17. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/app/__init__.py +0 -0
  18. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/app/runtime.py +0 -0
  19. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/AGENTS.md +0 -0
  20. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/__init__.py +0 -0
  21. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/antigravity/__init__.py +0 -0
  22. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/antigravity/exceptions.py +0 -0
  23. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/antigravity/oauth.py +0 -0
  24. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/antigravity/pkce.py +0 -0
  25. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/antigravity/token_manager.py +0 -0
  26. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/base.py +0 -0
  27. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/claude/__init__.py +0 -0
  28. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/claude/exceptions.py +0 -0
  29. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/claude/oauth.py +0 -0
  30. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/claude/token_manager.py +0 -0
  31. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/codex/__init__.py +0 -0
  32. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/codex/exceptions.py +0 -0
  33. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  34. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/codex/oauth.py +0 -0
  35. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/codex/token_manager.py +0 -0
  36. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/auth/env.py +0 -0
  37. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/__init__.py +0 -0
  38. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/auth_cmd.py +0 -0
  39. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/config_cmd.py +0 -0
  40. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/cost_cmd.py +0 -0
  41. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/debug.py +0 -0
  42. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/list_model.py +0 -0
  43. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/main.py +0 -0
  44. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/cli/self_update.py +0 -0
  45. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/__init__.py +0 -0
  46. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/assets/__init__.py +0 -0
  47. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/builtin_config.py +0 -0
  48. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/config.py +0 -0
  49. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/model_matcher.py +0 -0
  50. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/sub_agent_model_helper.py +0 -0
  51. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/config/thinking.py +0 -0
  52. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/const.py +0 -0
  53. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/__init__.py +0 -0
  54. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/agent.py +0 -0
  55. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/agent_profile.py +0 -0
  56. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/bash_mode.py +0 -0
  57. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/compaction/AGENTS.md +0 -0
  58. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/compaction/__init__.py +0 -0
  59. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/compaction/compaction.py +0 -0
  60. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/compaction/overflow.py +0 -0
  61. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/compaction/prompts.py +0 -0
  62. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/executor.py +0 -0
  63. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/loaded_skills.py +0 -0
  64. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/manager/__init__.py +0 -0
  65. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/manager/llm_clients.py +0 -0
  66. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  67. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
  68. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/memory.py +0 -0
  69. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-antigravity.md +0 -0
  70. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  71. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
  72. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2.md +0 -0
  73. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  74. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  75. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
  76. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
  77. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-sub-agent-image-gen.md +0 -0
  78. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
  79. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
  80. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/reminders.py +0 -0
  81. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/task.py +0 -0
  82. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/__init__.py +0 -0
  83. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/context.py +0 -0
  84. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/__init__.py +0 -0
  85. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/_utils.py +0 -0
  86. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  87. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  88. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  89. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
  90. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  91. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
  92. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  93. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/read_tool.py +0 -0
  94. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  95. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/file/write_tool.py +0 -0
  96. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/offload.py +0 -0
  97. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/report_back_tool.py +0 -0
  98. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  99. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  100. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  101. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  102. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/sub_agent/__init__.py +0 -0
  103. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/sub_agent/image_gen.md +0 -0
  104. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/sub_agent/image_gen.py +0 -0
  105. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/sub_agent/task.md +0 -0
  106. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/sub_agent/task.py +0 -0
  107. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  108. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  109. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  110. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
  111. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  112. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  113. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/tool_abc.py +0 -0
  114. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/tool_registry.py +0 -0
  115. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/tool_runner.py +0 -0
  116. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/__init__.py +0 -0
  117. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  118. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
  119. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  120. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  121. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
  122. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
  123. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/core/turn.py +0 -0
  124. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/__init__.py +0 -0
  125. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  126. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/anthropic/client.py +0 -0
  127. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/antigravity/__init__.py +0 -0
  128. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/antigravity/client.py +0 -0
  129. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/antigravity/input.py +0 -0
  130. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/bedrock_anthropic/__init__.py +0 -0
  131. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/bedrock_anthropic/client.py +0 -0
  132. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/claude/__init__.py +0 -0
  133. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/claude/client.py +0 -0
  134. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/client.py +0 -0
  135. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/google/__init__.py +0 -0
  136. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/google/client.py +0 -0
  137. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/google/input.py +0 -0
  138. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/image.py +0 -0
  139. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/input_common.py +0 -0
  140. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/json_stable.py +0 -0
  141. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_codex/__init__.py +0 -0
  142. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_codex/client.py +0 -0
  143. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  144. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_compatible/client.py +0 -0
  145. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  146. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_compatible/stream.py +0 -0
  147. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_responses/__init__.py +0 -0
  148. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_responses/client.py +0 -0
  149. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openai_responses/input.py +0 -0
  150. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  151. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openrouter/client.py +0 -0
  152. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openrouter/input.py +0 -0
  153. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/openrouter/reasoning.py +0 -0
  154. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/partial_message.py +0 -0
  155. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/registry.py +0 -0
  156. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/stream_parts.py +0 -0
  157. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/llm/usage.py +0 -0
  158. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/log.py +0 -0
  159. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/__init__.py +0 -0
  160. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/commands.py +0 -0
  161. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/events.py +0 -0
  162. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/llm_param.py +0 -0
  163. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/message.py +0 -0
  164. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/model.py +0 -0
  165. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/op.py +0 -0
  166. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/op_handler.py +0 -0
  167. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/AGENTS.md +0 -0
  168. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
  169. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
  170. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/image_gen.py +0 -0
  171. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/task.py +0 -0
  172. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/sub_agent/web.py +0 -0
  173. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/protocol/tools.py +0 -0
  174. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/__init__.py +0 -0
  175. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/codec.py +0 -0
  176. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/export.py +0 -0
  177. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/selector.py +0 -0
  178. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/session.py +0 -0
  179. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/store.py +0 -0
  180. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/templates/export_session.html +0 -0
  181. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/session/templates/mermaid_viewer.html +0 -0
  182. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/.DS_Store +0 -0
  183. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/__init__.py +0 -0
  184. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/assets/.DS_Store +0 -0
  185. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/assets/create-plan/SKILL.md +0 -0
  186. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
  187. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
  188. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
  189. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/loader.py +0 -0
  190. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/manager.py +0 -0
  191. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/skill/system_skills.py +0 -0
  192. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/__init__.py +0 -0
  193. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/__init__.py +0 -0
  194. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/clear_cmd.py +0 -0
  195. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/command_abc.py +0 -0
  196. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/compact_cmd.py +0 -0
  197. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/continue_cmd.py +0 -0
  198. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/copy_cmd.py +0 -0
  199. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/debug_cmd.py +0 -0
  200. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/export_cmd.py +0 -0
  201. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/export_online_cmd.py +0 -0
  202. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/fork_session_cmd.py +0 -0
  203. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/model_cmd.py +0 -0
  204. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/model_picker.py +0 -0
  205. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/prompt-init.md +0 -0
  206. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/prompt_command.py +0 -0
  207. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/refresh_cmd.py +0 -0
  208. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/registry.py +0 -0
  209. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/resume_cmd.py +0 -0
  210. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/status_cmd.py +0 -0
  211. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/sub_agent_model_cmd.py +0 -0
  212. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/command/thinking_cmd.py +0 -0
  213. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/commands.py +0 -0
  214. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/__init__.py +0 -0
  215. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/bash_syntax.py +0 -0
  216. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/command_output.py +0 -0
  217. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/common.py +0 -0
  218. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/diffs.py +0 -0
  219. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/mermaid_viewer.py +0 -0
  220. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/__init__.py +0 -0
  221. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/cjk_wrap.py +0 -0
  222. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/code_panel.py +0 -0
  223. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/live.py +0 -0
  224. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/quote.py +0 -0
  225. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/rich/status.py +0 -0
  226. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/sub_agent.py +0 -0
  227. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/thinking.py +0 -0
  228. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/tools.py +0 -0
  229. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/components/welcome.py +0 -0
  230. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/display.py +0 -0
  231. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/AGENTS.md +0 -0
  232. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/__init__.py +0 -0
  233. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/drag_drop.py +0 -0
  234. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/key_bindings.py +0 -0
  235. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/input/paste.py +0 -0
  236. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/machine.py +0 -0
  237. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/renderer.py +0 -0
  238. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/runner.py +0 -0
  239. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/__init__.py +0 -0
  240. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/color.py +0 -0
  241. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/control.py +0 -0
  242. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/image.py +0 -0
  243. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/notifier.py +0 -0
  244. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/progress_bar.py +0 -0
  245. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/tui/terminal/selector.py +0 -0
  246. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/__init__.py +0 -0
  247. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/common.py +0 -0
  248. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/core/__init__.py +0 -0
  249. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/core/display.py +0 -0
  250. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/core/input.py +0 -0
  251. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/debug_mode.py +0 -0
  252. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/terminal/__init__.py +0 -0
  253. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/ui/terminal/title.py +0 -0
  254. {klaude_code-2.10.2 → klaude_code-2.10.3}/src/klaude_code/update.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 2.10.2
3
+ Version: 2.10.3
4
4
  Summary: Minimal code agent CLI
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: chardet>=5.2.0
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "2.10.2"
7
+ version = "2.10.3"
8
8
  description = "Minimal code agent CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -102,12 +102,12 @@ provider_list:
102
102
  cost: {input: 1.75, output: 14, cache_read: 0.17}
103
103
 
104
104
  - model_name: kimi
105
- model_id: moonshotai/kimi-k2-thinking
105
+ model_id: moonshotai/kimi-k2.5
106
106
  context_limit: 262144
107
- provider_routing:
108
- only:
109
- - moonshotai/turbo
110
- cost: {input: 0.6, output: 2.5, cache_read: 0.15}
107
+ # provider_routing:
108
+ # only:
109
+ # - moonshotai/turbo
110
+ cost: {input: 0.6, output: 3, cache_read: 0.1}
111
111
 
112
112
  - model_name: haiku
113
113
  model_id: anthropic/claude-haiku-4.5
@@ -272,12 +272,12 @@ provider_list:
272
272
  model_list:
273
273
 
274
274
  - model_name: kimi
275
- model_id: kimi-k2-thinking
275
+ model_id: kimi-k2.5
276
276
  context_limit: 262144
277
277
  thinking:
278
278
  type: enabled
279
279
  budget_tokens: 8192
280
- cost: {input: 4, output: 16, cache_read: 1, currency: CNY}
280
+ cost: {input: 4, output: 21, cache_read: 0.7, currency: CNY}
281
281
 
282
282
 
283
283
  - provider_name: cerebras
@@ -107,11 +107,20 @@ def _tool_blocks_to_message(blocks: list[BetaToolResultBlockParam]) -> BetaMessa
107
107
  }
108
108
 
109
109
 
110
+ def _model_supports_unsigned_thinking(model_name: str | None) -> bool:
111
+ """Check if the model supports thinking blocks without signature (e.g., kimi, deepseek)."""
112
+ if not model_name:
113
+ return False
114
+ model_lower = model_name.lower()
115
+ return "kimi" in model_lower or "deepseek" in model_lower
116
+
117
+
110
118
  def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str | None) -> BetaMessageParam:
111
119
  content: list[BetaContentBlockParam] = []
112
120
  current_thinking_content: str | None = None
113
121
  native_thinking_parts, _ = split_thinking_parts(msg, model_name)
114
122
  native_thinking_ids = {id(part) for part in native_thinking_parts}
123
+ supports_unsigned = _model_supports_unsigned_thinking(model_name)
115
124
 
116
125
  def _degraded_thinking_block(text: str) -> BetaTextBlockParam | None:
117
126
  stripped = text.strip()
@@ -125,11 +134,18 @@ def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str
125
134
  },
126
135
  )
127
136
 
128
- def _flush_thinking_as_text_block() -> None:
137
+ def _flush_thinking() -> None:
129
138
  nonlocal current_thinking_content
130
139
  if current_thinking_content is None:
131
140
  return
132
- if block := _degraded_thinking_block(current_thinking_content):
141
+ if supports_unsigned:
142
+ content.append(
143
+ cast(
144
+ BetaContentBlockParam,
145
+ {"type": "thinking", "thinking": current_thinking_content},
146
+ )
147
+ )
148
+ elif block := _degraded_thinking_block(current_thinking_content):
133
149
  content.append(block)
134
150
  current_thinking_content = None
135
151
 
@@ -156,9 +172,17 @@ def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str
156
172
  )
157
173
  )
158
174
  current_thinking_content = None
175
+ elif supports_unsigned:
176
+ content.append(
177
+ cast(
178
+ BetaContentBlockParam,
179
+ {"type": "thinking", "thinking": current_thinking_content or ""},
180
+ )
181
+ )
182
+ current_thinking_content = None
159
183
  continue
160
184
 
161
- _flush_thinking_as_text_block()
185
+ _flush_thinking()
162
186
  if isinstance(part, message.TextPart):
163
187
  content.append(cast(BetaTextBlockParam, {"type": "text", "text": part.text}))
164
188
  elif isinstance(part, message.ToolCallPart):
@@ -182,7 +206,7 @@ def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str
182
206
  )
183
207
  )
184
208
 
185
- _flush_thinking_as_text_block()
209
+ _flush_thinking()
186
210
 
187
211
  return {"role": "assistant", "content": content}
188
212
 
@@ -6,7 +6,7 @@ from klaude_code.tui.components.common import create_grid
6
6
  from klaude_code.tui.components.rich.theme import ThemeKey
7
7
  from klaude_code.tui.components.tools import render_path
8
8
 
9
- REMINDER_BULLET = ""
9
+ REMINDER_BULLET = " +"
10
10
 
11
11
 
12
12
  def need_render_developer_message(e: events.DeveloperMessageEvent) -> bool:
@@ -9,8 +9,7 @@ def render_error(error_msg: Text) -> RenderableType:
9
9
  """Render error with X mark for error events."""
10
10
  grid = create_grid()
11
11
  error_msg.style = ThemeKey.ERROR
12
- error_msg.overflow = "ellipsis"
13
- error_msg.no_wrap = True
12
+ error_msg.overflow = "fold"
14
13
  grid.add_row(Text("✘", style=ThemeKey.ERROR_BOLD), error_msg)
15
14
  return grid
16
15
 
@@ -19,7 +18,6 @@ def render_tool_error(error_msg: Text) -> RenderableType:
19
18
  """Render error with indent for tool results."""
20
19
  grid = create_grid()
21
20
  error_msg.style = ThemeKey.ERROR
22
- error_msg.overflow = "ellipsis"
23
- error_msg.no_wrap = True
21
+ error_msg.overflow = "fold"
24
22
  grid.add_row(Text(" "), error_msg)
25
23
  return grid
@@ -32,9 +32,6 @@ def _render_task_metadata_block(
32
32
 
33
33
  # Second column: provider/model description / tokens / cost / …
34
34
  content = Text()
35
- if metadata.provider is not None:
36
- content.append_text(Text(metadata.provider.lower().replace(" ", "-"), style=ThemeKey.METADATA))
37
- content.append_text(Text("/", style=ThemeKey.METADATA))
38
35
  content.append_text(Text(metadata.model_name, style=ThemeKey.METADATA))
39
36
  if metadata.description:
40
37
  content.append_text(Text(" ", style=ThemeKey.METADATA)).append_text(
@@ -129,7 +126,7 @@ def _render_task_metadata_block(
129
126
 
130
127
  if parts:
131
128
  content.append_text(Text(" ", style=ThemeKey.METADATA))
132
- content.append_text(Text(" ", style=ThemeKey.METADATA).join(parts))
129
+ content.append_text(Text(" ", style=ThemeKey.METADATA_DIM).join(parts))
133
130
 
134
131
  grid.add_row(mark, content)
135
132
  return grid
@@ -140,15 +137,14 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
140
137
  renderables: list[RenderableType] = []
141
138
 
142
139
  has_sub_agents = len(e.metadata.sub_agent_task_metadata) > 0
143
- # Use an extra space for the main agent mark to align with two-character marks (├─, └─)
144
- main_mark_text = "●"
140
+ main_mark_text = "•"
145
141
  main_mark = Text(main_mark_text, style=ThemeKey.METADATA)
146
142
 
147
143
  renderables.append(_render_task_metadata_block(e.metadata.main_agent, mark=main_mark, show_context_and_time=True))
148
144
 
149
145
  # Render each sub-agent metadata block
150
146
  for meta in e.metadata.sub_agent_task_metadata:
151
- sub_mark = Text(" ", style=ThemeKey.METADATA)
147
+ sub_mark = Text(" ", style=ThemeKey.METADATA)
152
148
  renderables.append(_render_task_metadata_block(meta, mark=sub_mark, show_context_and_time=True))
153
149
 
154
150
  # Add total cost line when there are sub-agents
@@ -165,9 +161,8 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
165
161
 
166
162
  currency_symbol = "¥" if currency == "CNY" else "$"
167
163
  total_line = Text.assemble(
168
- (" ", ThemeKey.METADATA),
169
- (" Σ ", ThemeKey.METADATA),
170
- ("total ", ThemeKey.METADATA),
164
+ (" ", ThemeKey.METADATA),
165
+ (" total ", ThemeKey.METADATA),
171
166
  (currency_symbol, ThemeKey.METADATA),
172
167
  (f"{total_cost:.4f}", ThemeKey.METADATA),
173
168
  )
@@ -609,7 +609,11 @@ class MarkdownStream:
609
609
 
610
610
  live_text_to_set: Text | None = None
611
611
  if not final and MARKDOWN_STREAM_LIVE_REPAINT_ENABLED and self._live_sink is not None:
612
- # Only update live area after we have rendered at least one stable block
612
+ # Only update the live area after we have rendered at least one stable block.
613
+ #
614
+ # This keeps the bottom "live" region anchored to stable scrollback, and
615
+ # avoids showing a live frame that would later need to be retroactively
616
+ # re-rendered once stable content exists.
613
617
  if not self._stable_rendered_lines:
614
618
  return
615
619
  # When nothing is stable yet, we still want to show incremental output.
@@ -262,8 +262,8 @@ def get_theme(theme: str | None = None) -> Themes:
262
262
  ThemeKey.ERROR_DIM.value: "dim " + palette.red,
263
263
  ThemeKey.INTERRUPT.value: palette.red,
264
264
  # USER_INPUT
265
- ThemeKey.USER_INPUT.value: f"{palette.magenta} on {palette.user_message_background}",
266
- ThemeKey.USER_INPUT_PROMPT.value: f"bold {palette.magenta} on {palette.user_message_background}",
265
+ ThemeKey.USER_INPUT.value: f"{palette.cyan} on {palette.user_message_background}",
266
+ ThemeKey.USER_INPUT_PROMPT.value: f"bold {palette.cyan} on {palette.user_message_background}",
267
267
  ThemeKey.USER_INPUT_AT_PATTERN.value: f"{palette.purple} on {palette.user_message_background}",
268
268
  ThemeKey.USER_INPUT_SLASH_COMMAND.value: f"bold {palette.blue} on {palette.user_message_background}",
269
269
  ThemeKey.USER_INPUT_SKILL.value: f"bold {palette.green} on {palette.user_message_background}",
@@ -89,7 +89,7 @@ def render_user_input(content: str) -> RenderableType:
89
89
 
90
90
  return Padding(
91
91
  Group(*renderables),
92
- pad=(0, 1),
92
+ pad=(1, 1),
93
93
  style=ThemeKey.USER_INPUT,
94
94
  expand=False,
95
95
  )
@@ -673,34 +673,68 @@ class _AtFilesCompleter(Completer):
673
673
  all_files_lower = self._git_file_list_lower or []
674
674
  kn = keyword_norm
675
675
 
676
- # Bound per-keystroke work: stop scanning once enough matches are found.
676
+ # Bound per-keystroke work.
677
+ #
678
+ # Important: When the keyword is common (e.g. "tools"), truncating the
679
+ # scan purely by number of matching *files* can accidentally hide valid
680
+ # directory completions that appear later in the git path order.
681
+ #
682
+ # Example: multiple */tools/ directories under different parents.
683
+ file_quota = max_results
684
+ dir_quota = max_results
685
+ scan_cap = max(2000, max_results * 200)
686
+
687
+ keyword_stripped = keyword_norm.strip("/")
688
+ keyword_basename = os.path.basename(keyword_stripped)
689
+ explicit_parent = "/" in keyword_stripped
690
+
691
+ def dir_matches_keyword(dir_path: str) -> bool:
692
+ if not keyword_basename:
693
+ return False
694
+ if explicit_parent:
695
+ # When user typed an explicit parent segment, match against the
696
+ # whole directory path (not just basename).
697
+ return kn in f"{dir_path}/".lower()
698
+ # Otherwise prioritize directories by basename match.
699
+ return keyword_basename in os.path.basename(dir_path).lower()
700
+
677
701
  matching_files: list[str] = []
702
+ dir_list: list[str] = []
703
+ dir_seen: set[str] = set()
678
704
  scan_truncated = False
705
+ scanned = 0
706
+
679
707
  for p, pl in zip(all_files, all_files_lower, strict=False):
680
- if kn in pl:
681
- matching_files.append(p)
682
- if len(matching_files) >= max_results:
708
+ scanned += 1
709
+ if kn not in pl:
710
+ if scanned >= scan_cap and (matching_files or dir_list):
683
711
  scan_truncated = True
684
712
  break
713
+ continue
714
+
715
+ if len(matching_files) < file_quota:
716
+ matching_files.append(p)
685
717
 
686
- # Also include parent directories of matching files so users can
687
- # complete into a folder, similar to fd's directory results.
688
- dir_candidates: set[str] = set()
689
- for p in matching_files[: max_results * 3]:
718
+ # Collect matching parent directories, walking upwards until repo root.
719
+ # This allows completing into directories like "image/tools/" even
720
+ # when the matching file is nested deeper.
690
721
  parent = os.path.dirname(p)
691
722
  while parent and parent != ".":
692
- dir_candidates.add(f"{parent}/")
723
+ if dir_matches_keyword(parent):
724
+ cand = f"{parent}/"
725
+ if cand not in dir_seen:
726
+ dir_seen.add(cand)
727
+ dir_list.append(cand)
728
+ if len(dir_list) >= dir_quota:
729
+ break
693
730
  parent = os.path.dirname(parent)
694
731
 
695
- dir_list = sorted(dir_candidates)
696
- dir_truncated = False
697
- if len(dir_list) > max_results:
698
- dir_list = dir_list[:max_results]
699
- dir_truncated = True
732
+ if len(matching_files) >= file_quota and len(dir_list) >= dir_quota:
733
+ scan_truncated = True
734
+ break
700
735
 
701
- candidates = matching_files + dir_list
702
- truncated = scan_truncated or dir_truncated
703
- return candidates, truncated
736
+ candidates = dir_list + matching_files
737
+ return candidates, scan_truncated
704
738
 
705
739
  def _get_git_repo_root(self, cwd: Path) -> Path | None:
706
740
  if not self._has_cmd("git"):
@@ -28,6 +28,9 @@ from klaude_code.protocol.message import ImageFilePart
28
28
 
29
29
  IMAGE_SUFFIXES = frozenset({".png", ".jpg", ".jpeg", ".gif", ".webp"})
30
30
 
31
+ # Claude API limit is 5MB, we use 4.5MB to have some margin
32
+ MAX_IMAGE_SIZE_BYTES = 4_500_000
33
+
31
34
  IMAGE_MARKER_RE = re.compile(r'\[image (?P<path>"[^"]+"|[^\]]+)\]')
32
35
 
33
36
 
@@ -159,6 +162,127 @@ def _grab_clipboard_image(dest_path: Path) -> bool:
159
162
  return _grab_clipboard_image_linux(dest_path)
160
163
 
161
164
 
165
+ # ---------------------------------------------------------------------------
166
+ # Image resizing for size limits
167
+ # ---------------------------------------------------------------------------
168
+
169
+
170
+ def _get_image_dimensions_macos(path: Path) -> tuple[int, int] | None:
171
+ """Get image dimensions using sips on macOS."""
172
+ try:
173
+ result = subprocess.run(
174
+ ["sips", "-g", "pixelWidth", "-g", "pixelHeight", str(path)],
175
+ capture_output=True,
176
+ text=True,
177
+ )
178
+ if result.returncode != 0:
179
+ return None
180
+ width = height = 0
181
+ for line in result.stdout.splitlines():
182
+ if "pixelWidth" in line:
183
+ width = int(line.split(":")[-1].strip())
184
+ elif "pixelHeight" in line:
185
+ height = int(line.split(":")[-1].strip())
186
+ if width > 0 and height > 0:
187
+ return (width, height)
188
+ except (OSError, ValueError):
189
+ pass
190
+ return None
191
+
192
+
193
+ def _resize_image_macos(path: Path, scale: float) -> bool:
194
+ """Resize image using sips on macOS. Modifies file in place."""
195
+ dims = _get_image_dimensions_macos(path)
196
+ if dims is None:
197
+ return False
198
+ new_width = max(1, int(dims[0] * scale))
199
+ try:
200
+ result = subprocess.run(
201
+ ["sips", "--resampleWidth", str(new_width), str(path)],
202
+ capture_output=True,
203
+ )
204
+ return result.returncode == 0
205
+ except OSError:
206
+ return False
207
+
208
+
209
+ def _resize_image_linux(path: Path, scale: float) -> bool:
210
+ """Resize image using ImageMagick convert on Linux."""
211
+ if not shutil.which("convert"):
212
+ return False
213
+ percent = int(scale * 100)
214
+ try:
215
+ result = subprocess.run(
216
+ ["convert", str(path), "-resize", f"{percent}%", str(path)],
217
+ capture_output=True,
218
+ )
219
+ return result.returncode == 0
220
+ except OSError:
221
+ return False
222
+
223
+
224
+ def _resize_image_windows(path: Path, scale: float) -> bool:
225
+ """Resize image using PowerShell on Windows."""
226
+ script = f'''
227
+ Add-Type -AssemblyName System.Drawing
228
+ $img = [System.Drawing.Image]::FromFile("{path}")
229
+ $newWidth = [int]($img.Width * {scale})
230
+ $newHeight = [int]($img.Height * {scale})
231
+ $bmp = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
232
+ $graphics = [System.Drawing.Graphics]::FromImage($bmp)
233
+ $graphics.DrawImage($img, 0, 0, $newWidth, $newHeight)
234
+ $img.Dispose()
235
+ $bmp.Save("{path}", [System.Drawing.Imaging.ImageFormat]::Png)
236
+ $bmp.Dispose()
237
+ $graphics.Dispose()
238
+ Write-Output "ok"
239
+ '''
240
+ try:
241
+ result = subprocess.run(
242
+ ["powershell", "-Command", script],
243
+ capture_output=True,
244
+ text=True,
245
+ )
246
+ return result.returncode == 0 and "ok" in result.stdout
247
+ except OSError:
248
+ return False
249
+
250
+
251
+ def _resize_image(path: Path, scale: float) -> bool:
252
+ """Resize image by scale factor. Modifies file in place."""
253
+ if sys.platform == "darwin":
254
+ return _resize_image_macos(path, scale)
255
+ elif sys.platform == "win32":
256
+ return _resize_image_windows(path, scale)
257
+ else:
258
+ return _resize_image_linux(path, scale)
259
+
260
+
261
+ def _ensure_image_size_limit(path: Path, max_bytes: int = MAX_IMAGE_SIZE_BYTES) -> None:
262
+ """Resize image if it exceeds the size limit. Modifies file in place."""
263
+ try:
264
+ current_size = path.stat().st_size
265
+ if current_size <= max_bytes:
266
+ return
267
+
268
+ # Calculate scale factor based on size ratio
269
+ # We use sqrt because area scales with square of linear dimensions
270
+ scale = (max_bytes / current_size) ** 0.5
271
+ # Be a bit more aggressive to ensure we get under the limit
272
+ scale *= 0.9
273
+
274
+ for _ in range(5): # Max 5 resize attempts
275
+ if not _resize_image(path, scale):
276
+ return
277
+ new_size = path.stat().st_size
278
+ if new_size <= max_bytes:
279
+ return
280
+ # Still too large, reduce more
281
+ scale = 0.8
282
+ except OSError:
283
+ pass
284
+
285
+
162
286
  def capture_clipboard_tag() -> str | None:
163
287
  """Capture an image from clipboard and return an [image ...] marker."""
164
288
 
@@ -174,6 +298,9 @@ def capture_clipboard_tag() -> str | None:
174
298
  if not _grab_clipboard_image(path):
175
299
  return None
176
300
 
301
+ # Resize if image exceeds size limit
302
+ _ensure_image_size_limit(path)
303
+
177
304
  return format_image_marker(str(path))
178
305
 
179
306
 
@@ -61,7 +61,7 @@ COMPLETION_SELECTED_DARK_BG = "ansigreen"
61
61
  COMPLETION_SELECTED_LIGHT_BG = "ansigreen"
62
62
  COMPLETION_SELECTED_UNKNOWN_BG = "ansigreen"
63
63
  COMPLETION_MENU = "ansibrightblack"
64
- INPUT_PROMPT_STYLE = "ansimagenta bold"
64
+ INPUT_PROMPT_STYLE = "ansicyan bold"
65
65
  INPUT_PROMPT_BASH_STYLE = "ansigreen bold"
66
66
  PLACEHOLDER_TEXT_STYLE_DARK_BG = "fg:#5a5a5a"
67
67
  PLACEHOLDER_TEXT_STYLE_LIGHT_BG = "fg:#7a7a7a"
File without changes