klaude-code 1.2.2__tar.gz → 1.2.4__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 (163) hide show
  1. {klaude_code-1.2.2 → klaude_code-1.2.4}/PKG-INFO +1 -1
  2. {klaude_code-1.2.2 → klaude_code-1.2.4}/pyproject.toml +19 -1
  3. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/cli/main.py +7 -0
  4. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/cli/runtime.py +6 -6
  5. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/__init__.py +9 -5
  6. klaude_code-1.2.4/src/klaude_code/command/clear_cmd.py +20 -0
  7. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/command_abc.py +36 -1
  8. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/export_cmd.py +16 -20
  9. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/help_cmd.py +1 -0
  10. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/model_cmd.py +3 -30
  11. klaude_code-1.2.2/src/klaude_code/command/prompt-update-dev-doc.md → klaude_code-1.2.4/src/klaude_code/command/prompt-dev-docs-update.md +3 -2
  12. klaude_code-1.2.2/src/klaude_code/command/prompt-dev-doc.md → klaude_code-1.2.4/src/klaude_code/command/prompt-dev-docs.md +3 -2
  13. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/prompt-init.md +2 -5
  14. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/prompt_command.py +3 -3
  15. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/registry.py +6 -7
  16. klaude_code-1.2.4/src/klaude_code/command/status_cmd.py +111 -0
  17. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/config/config.py +1 -1
  18. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/config/list_model.py +1 -1
  19. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/const/__init__.py +1 -1
  20. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/agent.py +2 -11
  21. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/executor.py +155 -14
  22. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-gemini.md +1 -1
  23. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/reminders.py +24 -0
  24. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/task.py +10 -0
  25. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/shell/bash_tool.py +6 -2
  26. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/sub_agent_tool.py +1 -1
  27. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/tool_context.py +1 -1
  28. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/tool_registry.py +1 -1
  29. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/tool_runner.py +1 -1
  30. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/web/mermaid_tool.py +1 -1
  31. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/__init__.py +3 -4
  32. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/anthropic/client.py +12 -9
  33. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openai_compatible/client.py +2 -18
  34. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +2 -2
  35. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openrouter/client.py +2 -18
  36. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openrouter/input.py +6 -2
  37. klaude_code-1.2.4/src/klaude_code/llm/registry.py +22 -0
  38. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/responses/client.py +2 -0
  39. klaude_code-1.2.2/src/klaude_code/llm/metadata_tracker.py → klaude_code-1.2.4/src/klaude_code/llm/usage.py +49 -2
  40. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/commands.py +1 -0
  41. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/llm_param.py +12 -0
  42. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/model.py +30 -3
  43. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/op.py +14 -14
  44. klaude_code-1.2.4/src/klaude_code/protocol/op_handler.py +28 -0
  45. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/tools.py +0 -2
  46. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/session/export.py +124 -35
  47. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/session/session.py +1 -1
  48. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/session/templates/export_session.html +383 -39
  49. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/__init__.py +6 -2
  50. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/exec/display.py +26 -0
  51. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/event_handler.py +5 -1
  52. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/developer.py +62 -11
  53. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/metadata.py +33 -24
  54. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/sub_agent.py +1 -1
  55. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/tools.py +2 -2
  56. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/user_input.py +18 -22
  57. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/status.py +13 -2
  58. klaude_code-1.2.2/src/klaude_code/command/clear_cmd.py +0 -41
  59. klaude_code-1.2.2/src/klaude_code/llm/registry.py +0 -91
  60. {klaude_code-1.2.2 → klaude_code-1.2.4}/README.md +0 -0
  61. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/__init__.py +0 -0
  62. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/cli/__init__.py +0 -0
  63. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/cli/session_cmd.py +0 -0
  64. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/diff_cmd.py +0 -0
  65. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/refresh_cmd.py +0 -0
  66. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
  67. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/config/__init__.py +0 -0
  68. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/config/select_model.py +0 -0
  69. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/__init__.py +0 -0
  70. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompt.py +0 -0
  71. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  72. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  73. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-subagent-explore.md +0 -0
  74. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-subagent-oracle.md +0 -0
  75. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -0
  76. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/prompts/prompt-subagent.md +0 -0
  77. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/__init__.py +0 -0
  78. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/__init__.py +0 -0
  79. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  80. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  81. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  82. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  83. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
  84. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/multi_edit_tool.md +0 -0
  85. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/multi_edit_tool.py +0 -0
  86. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  87. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/read_tool.py +0 -0
  88. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  89. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/file/write_tool.py +0 -0
  90. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/__init__.py +0 -0
  91. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/memory_tool.md +0 -0
  92. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/memory_tool.py +0 -0
  93. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/skill_loader.py +0 -0
  94. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/skill_tool.md +0 -0
  95. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/memory/skill_tool.py +0 -0
  96. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  97. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  98. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  99. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  100. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  101. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  102. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  103. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  104. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/tool_abc.py +0 -0
  105. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/truncation.py +0 -0
  106. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/web/__init__.py +0 -0
  107. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  108. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  109. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  110. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/core/turn.py +0 -0
  111. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  112. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/anthropic/input.py +0 -0
  113. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/client.py +0 -0
  114. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/input_common.py +0 -0
  115. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  116. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  117. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  118. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/openrouter/reasoning_handler.py +0 -0
  119. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/responses/__init__.py +0 -0
  120. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/llm/responses/input.py +0 -0
  121. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/__init__.py +0 -0
  122. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/protocol/events.py +0 -0
  123. {klaude_code-1.2.2/src/klaude_code/core → klaude_code-1.2.4/src/klaude_code/protocol}/sub_agent.py +0 -0
  124. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/session/__init__.py +0 -0
  125. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/session/selector.py +0 -0
  126. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/trace/__init__.py +0 -0
  127. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/trace/log.py +0 -0
  128. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/core/__init__.py +0 -0
  129. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/core/display.py +0 -0
  130. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/core/input.py +0 -0
  131. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/core/stage_manager.py +0 -0
  132. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/__init__.py +0 -0
  133. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
  134. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/debug/display.py +0 -0
  135. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
  136. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
  137. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
  138. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/completers.py +0 -0
  139. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/display.py +0 -0
  140. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +0 -0
  141. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
  142. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/modes/repl/renderer.py +0 -0
  143. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/__init__.py +0 -0
  144. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/assistant.py +0 -0
  145. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/common.py +0 -0
  146. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/diffs.py +0 -0
  147. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/errors.py +0 -0
  148. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/renderers/thinking.py +0 -0
  149. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/__init__.py +0 -0
  150. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/live.py +0 -0
  151. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/markdown.py +0 -0
  152. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/quote.py +0 -0
  153. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/searchable_text.py +0 -0
  154. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/rich/theme.py +0 -0
  155. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/terminal/__init__.py +0 -0
  156. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/terminal/color.py +0 -0
  157. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/terminal/control.py +0 -0
  158. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/terminal/notifier.py +0 -0
  159. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
  160. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/utils/__init__.py +0 -0
  161. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/utils/common.py +0 -0
  162. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/ui/utils/debouncer.py +0 -0
  163. {klaude_code-1.2.2 → klaude_code-1.2.4}/src/klaude_code/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.2.2
3
+ Version: 1.2.4
4
4
  Summary: Add your description here
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: openai>=1.102.0
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "1.2.2"
7
+ version = "1.2.4"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -29,6 +29,7 @@ module-name = "klaude_code"
29
29
 
30
30
  [dependency-groups]
31
31
  dev = [
32
+ "import-linter>=2.6",
32
33
  "isort>=6.0.1",
33
34
  "pyright>=1.1.407",
34
35
  "pytest>=8.4.1",
@@ -57,3 +58,20 @@ exclude = [".venv/"]
57
58
  [[tool.pyright.executionEnvironments]]
58
59
  root = "."
59
60
  extraPaths = ["src"]
61
+
62
+ [tool.importlinter]
63
+ root_packages = ["klaude_code"]
64
+ include_external_packages = false
65
+
66
+ [[tool.importlinter.contracts]]
67
+ name = "Layered architecture"
68
+ type = "layers"
69
+ layers = [
70
+ "klaude_code.cli",
71
+ "klaude_code.core",
72
+ "klaude_code.session",
73
+ "klaude_code.config",
74
+ "klaude_code.llm",
75
+ "klaude_code.protocol",
76
+ "klaude_code.const",
77
+ ]
@@ -62,6 +62,7 @@ def list_models() -> None:
62
62
 
63
63
 
64
64
  @app.command("config")
65
+ @app.command("conf", hidden=True)
65
66
  def edit_config() -> None:
66
67
  """Open the configuration file in $EDITOR or default system editor"""
67
68
  editor = os.environ.get("EDITOR")
@@ -148,6 +149,11 @@ def exec_command(
148
149
  "--vanilla",
149
150
  help="Vanilla mode exposes the model's raw API behavior: it provides only minimal tools (Bash, Read & Edit) and omits system prompts and reminders.",
150
151
  ),
152
+ stream_json: bool = typer.Option(
153
+ False,
154
+ "--stream-json",
155
+ help="Stream all events as JSON lines to stdout.",
156
+ ),
151
157
  ) -> None:
152
158
  """Execute non-interactively with provided input."""
153
159
 
@@ -193,6 +199,7 @@ def exec_command(
193
199
  vanilla=vanilla,
194
200
  is_exec_mode=True,
195
201
  debug_filters=debug_filters,
202
+ stream_json=stream_json,
196
203
  )
197
204
 
198
205
  asyncio.run(
@@ -8,15 +8,14 @@ import typer
8
8
  from rich.text import Text
9
9
 
10
10
  from klaude_code import ui
11
- from klaude_code.command import is_interactive_command
11
+ from klaude_code.command import has_interactive_command
12
12
  from klaude_code.config import Config, load_config
13
13
  from klaude_code.core.agent import DefaultModelProfileProvider, VanillaModelProfileProvider
14
- from klaude_code.core.executor import Executor
15
- from klaude_code.core.sub_agent import iter_sub_agent_profiles
14
+ from klaude_code.core.executor import Executor, LLMClients
16
15
  from klaude_code.core.tool import SkillLoader, SkillTool
17
- from klaude_code.llm import LLMClients
18
16
  from klaude_code.protocol import events, op
19
17
  from klaude_code.protocol.model import UserInputPayload
18
+ from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
20
19
  from klaude_code.trace import DebugType, log, set_debug_logging
21
20
  from klaude_code.ui.modes.repl import build_repl_status_snapshot
22
21
  from klaude_code.ui.modes.repl.input_prompt_toolkit import REPLStatusSnapshot
@@ -72,6 +71,7 @@ class AppInitConfig:
72
71
  vanilla: bool
73
72
  is_exec_mode: bool = False
74
73
  debug_filters: set[DebugType] | None = None
74
+ stream_json: bool = False
75
75
 
76
76
 
77
77
  @dataclass
@@ -148,7 +148,7 @@ async def initialize_app_components(init_config: AppInitConfig) -> AppComponents
148
148
  # Set up UI components using factory functions
149
149
  display: ui.DisplayABC
150
150
  if init_config.is_exec_mode:
151
- display = ui.create_exec_display(debug=init_config.debug)
151
+ display = ui.create_exec_display(debug=init_config.debug, stream_json=init_config.stream_json)
152
152
  else:
153
153
  display = ui.create_default_display(debug=init_config.debug, theme=theme)
154
154
 
@@ -300,7 +300,7 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
300
300
  )
301
301
  # If it's an interactive command (e.g., /model), avoid starting the ESC monitor
302
302
  # to prevent TTY conflicts with interactive prompts (questionary/prompt_toolkit).
303
- if is_interactive_command(user_input.text):
303
+ if has_interactive_command(user_input.text):
304
304
  await components.executor.wait_for(submission_id)
305
305
  else:
306
306
  # Esc monitor for long-running, interruptible operations
@@ -1,5 +1,5 @@
1
1
  from .clear_cmd import ClearCommand
2
- from .command_abc import CommandABC, CommandResult
2
+ from .command_abc import CommandABC, CommandResult, InputAction, InputActionType
3
3
  from .diff_cmd import DiffCommand
4
4
  from .export_cmd import ExportCommand
5
5
  from .help_cmd import HelpCommand
@@ -10,12 +10,13 @@ from .model_cmd import ModelCommand
10
10
  from .refresh_cmd import RefreshTerminalCommand
11
11
  from .registry import (
12
12
  dispatch_command,
13
- get_command_names,
14
13
  get_commands,
15
- is_interactive_command,
14
+ has_interactive_command,
15
+ is_slash_command_name,
16
16
  load_prompt_commands,
17
17
  register_command,
18
18
  )
19
+ from .status_cmd import StatusCommand
19
20
  from .terminal_setup_cmd import TerminalSetupCommand
20
21
 
21
22
  # Dynamically load prompt commands
@@ -28,12 +29,15 @@ __all__ = [
28
29
  "ModelCommand",
29
30
  "ExportCommand",
30
31
  "RefreshTerminalCommand",
32
+ "StatusCommand",
31
33
  "TerminalSetupCommand",
32
34
  "register_command",
33
35
  "CommandABC",
34
36
  "CommandResult",
37
+ "InputAction",
38
+ "InputActionType",
35
39
  "dispatch_command",
36
40
  "get_commands",
37
- "get_command_names",
38
- "is_interactive_command",
41
+ "is_slash_command_name",
42
+ "has_interactive_command",
39
43
  ]
@@ -0,0 +1,20 @@
1
+ from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
2
+ from klaude_code.command.registry import register_command
3
+ from klaude_code.core.agent import Agent
4
+ from klaude_code.protocol import commands
5
+
6
+
7
+ @register_command
8
+ class ClearCommand(CommandABC):
9
+ """Clear current session and start a new conversation"""
10
+
11
+ @property
12
+ def name(self) -> commands.CommandName:
13
+ return commands.CommandName.CLEAR
14
+
15
+ @property
16
+ def summary(self) -> str:
17
+ return "Clear conversation history and free up context"
18
+
19
+ async def run(self, raw: str, agent: Agent) -> CommandResult:
20
+ return CommandResult(actions=[InputAction.clear()])
@@ -1,4 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
+ from enum import Enum
2
3
 
3
4
  from pydantic import BaseModel
4
5
 
@@ -7,14 +8,48 @@ from klaude_code.protocol import commands
7
8
  from klaude_code.protocol import events as protocol_events
8
9
 
9
10
 
11
+ class InputActionType(str, Enum):
12
+ """Supported input action kinds."""
13
+
14
+ RUN_AGENT = "run_agent"
15
+ CHANGE_MODEL = "change_model"
16
+ CLEAR = "clear"
17
+
18
+
19
+ class InputAction(BaseModel):
20
+ """Structured executor action derived from a user input."""
21
+
22
+ type: InputActionType
23
+ text: str = ""
24
+ model_name: str | None = None
25
+
26
+ @classmethod
27
+ def run_agent(cls, text: str) -> "InputAction":
28
+ """Create a RunAgent action preserving the provided text."""
29
+
30
+ return cls(type=InputActionType.RUN_AGENT, text=text)
31
+
32
+ @classmethod
33
+ def change_model(cls, model_name: str) -> "InputAction":
34
+ """Create a ChangeModel action for the provided model name."""
35
+
36
+ return cls(type=InputActionType.CHANGE_MODEL, model_name=model_name)
37
+
38
+ @classmethod
39
+ def clear(cls) -> "InputAction":
40
+ """Create a Clear action to reset the session."""
41
+
42
+ return cls(type=InputActionType.CLEAR)
43
+
44
+
10
45
  class CommandResult(BaseModel):
11
46
  """Result of a command execution."""
12
47
 
13
- agent_input: str | None = None # Input to be submitted to agent, or None if no input needed
14
48
  events: (
15
49
  list[protocol_events.DeveloperMessageEvent | protocol_events.WelcomeEvent | protocol_events.ReplayHistoryEvent]
16
50
  | None
17
51
  ) = None # List of UI events to display immediately
52
+ actions: list[InputAction] | None = None
18
53
 
19
54
 
20
55
  class CommandABC(ABC):
@@ -37,29 +37,25 @@ class ExportCommand(CommandABC):
37
37
  output_path.parent.mkdir(parents=True, exist_ok=True)
38
38
  output_path.write_text(html_doc, encoding="utf-8")
39
39
  self._open_file(output_path)
40
- return CommandResult(
41
- events=[
42
- events.DeveloperMessageEvent(
43
- session_id=agent.session.id,
44
- item=model.DeveloperMessageItem(
45
- content=f"Session exported and opened: {output_path}",
46
- command_output=model.CommandOutput(command_name=self.name),
47
- ),
48
- )
49
- ]
40
+ event = events.DeveloperMessageEvent(
41
+ session_id=agent.session.id,
42
+ item=model.DeveloperMessageItem(
43
+ content=f"Session exported and opened: {output_path}",
44
+ command_output=model.CommandOutput(command_name=self.name),
45
+ ),
50
46
  )
47
+ return CommandResult(events=[event])
51
48
  except Exception as exc: # pragma: no cover - safeguard for unexpected errors
52
- return CommandResult(
53
- events=[
54
- events.DeveloperMessageEvent(
55
- session_id=agent.session.id,
56
- item=model.DeveloperMessageItem(
57
- content=f"Failed to export session: {exc}",
58
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
59
- ),
60
- )
61
- ]
49
+ import traceback
50
+
51
+ event = events.DeveloperMessageEvent(
52
+ session_id=agent.session.id,
53
+ item=model.DeveloperMessageItem(
54
+ content=f"Failed to export session: {exc}\n{traceback.format_exc()}",
55
+ command_output=model.CommandOutput(command_name=self.name, is_error=True),
56
+ ),
62
57
  )
58
+ return CommandResult(events=[event])
63
59
 
64
60
  def _resolve_output_path(self, raw: str, agent: Agent) -> Path:
65
61
  trimmed = raw.strip()
@@ -23,6 +23,7 @@ Usage:
23
23
  [b]@[/b] to mention file
24
24
  [b]esc[/b] to interrupt agent task
25
25
  [b]shift-enter[/b] or [b]ctrl-j[/b] for new line
26
+ [b]ctrl-v[/b] for pasting image
26
27
  [b]--continue[/b] or [b]--resume[/b] to continue an old session
27
28
  [b]--select-model[/b] to switch model
28
29
 
@@ -1,12 +1,10 @@
1
1
  import asyncio
2
2
 
3
- from klaude_code.command.command_abc import CommandABC, CommandResult
3
+ from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
4
4
  from klaude_code.command.registry import register_command
5
- from klaude_code.config import load_config, select_model_from_config
5
+ from klaude_code.config import select_model_from_config
6
6
  from klaude_code.core.agent import Agent
7
- from klaude_code.llm import create_llm_client
8
7
  from klaude_code.protocol import commands, events, model
9
- from klaude_code.trace import DebugType, log_debug
10
8
 
11
9
 
12
10
  @register_command
@@ -42,29 +40,4 @@ class ModelCommand(CommandABC):
42
40
  ]
43
41
  )
44
42
 
45
- config = load_config()
46
- assert config is not None
47
- llm_config = config.get_model_config(selected_model)
48
-
49
- log_debug(
50
- "Updated LLM config",
51
- llm_config.model_dump_json(exclude_none=True),
52
- style="yellow",
53
- debug_type=DebugType.LLM_CONFIG,
54
- )
55
-
56
- llm_client = create_llm_client(llm_config)
57
- agent.set_model_profile(agent.build_model_profile(llm_client))
58
-
59
- return CommandResult(
60
- events=[
61
- events.DeveloperMessageEvent(
62
- session_id=agent.session.id,
63
- item=model.DeveloperMessageItem(
64
- content=f"switched to model: {selected_model}",
65
- command_output=model.CommandOutput(command_name=self.name),
66
- ),
67
- ),
68
- events.WelcomeEvent(llm_config=llm_config, work_dir=str(agent.session.work_dir)),
69
- ]
70
- )
43
+ return CommandResult(actions=[InputAction.change_model(selected_model)])
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Update dev documentation before context compaction
3
- argument-hint: Optional - specific context or tasks to focus on (leave empty for comprehensive update)
3
+ from: https://github.com/diet103/claude-code-infrastructure-showcase/blob/main/.claude/commands/dev-docs-update.md
4
4
  ---
5
5
 
6
6
  We're approaching context limits. Please update the development documentation to ensure seamless continuation after context reset.
@@ -50,6 +50,7 @@ If switching to a new conversation:
50
50
  - Any uncommitted changes that need attention
51
51
  - Test commands to verify work
52
52
 
53
- ## Additional Context: $ARGUMENTS
53
+ ## Additional Context:
54
+ $ARGUMENTS
54
55
 
55
56
  **Priority**: Focus on capturing information that would be hard to rediscover or reconstruct from code alone.
@@ -1,9 +1,10 @@
1
1
  ---
2
2
  description: Create a comprehensive strategic plan with structured task breakdown
3
- argument-hint: Describe what you need planned (e.g., "refactor authentication system", "implement microservices")
3
+ from: https://github.com/diet103/claude-code-infrastructure-showcase/blob/main/.claude/commands/dev-docs.md
4
4
  ---
5
5
 
6
- You are an elite strategic planning specialist. Create a comprehensive, actionable plan for: $ARGUMENTS
6
+ Create a comprehensive, actionable plan for:
7
+ $ARGUMENTS
7
8
 
8
9
  ## Instructions
9
10
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Create an AGENTS.md file with instructions for agent
3
- argument-hint: Additional user instructions
3
+ from: https://github.com/openai/codex/blob/main/codex-rs/tui/prompt_for_init_command.md
4
4
  ---
5
5
 
6
6
  Generate/Update a file named AGENTS.md that serves as a contributor guide for this repository.
@@ -42,7 +42,4 @@ Commit & Pull Request Guidelines
42
42
  - Summarize commit message conventions found in the project’s Git history.
43
43
  - Outline pull request requirements (descriptions, linked issues, screenshots, etc.).
44
44
 
45
- (Optional) Add other sections if relevant, such as Security & Configuration Tips, Architecture Overview, or Agent-Specific Instructions.
46
-
47
- Additional Instructions:
48
- $ARGUMENTS
45
+ (Optional) Add other sections if relevant, such as Security & Configuration Tips, Architecture Overview, or Agent-Specific Instructions.
@@ -2,7 +2,7 @@ from importlib.resources import files
2
2
 
3
3
  import yaml
4
4
 
5
- from klaude_code.command.command_abc import CommandABC, CommandResult
5
+ from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
6
6
  from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands
8
8
 
@@ -57,7 +57,7 @@ class PromptCommand(CommandABC):
57
57
  async def run(self, raw: str, agent: Agent) -> CommandResult:
58
58
  self._ensure_loaded()
59
59
  template_content = self._content or ""
60
- user_input = raw.strip()
60
+ user_input = raw.strip() or "<none>"
61
61
 
62
62
  if "$ARGUMENTS" in template_content:
63
63
  final_prompt = template_content.replace("$ARGUMENTS", user_input)
@@ -66,4 +66,4 @@ class PromptCommand(CommandABC):
66
66
  if user_input:
67
67
  final_prompt += f"\n\nAdditional Instructions:\n{user_input}"
68
68
 
69
- return CommandResult(agent_input=final_prompt)
69
+ return CommandResult(actions=[InputAction.run_agent(final_prompt)])
@@ -1,7 +1,7 @@
1
1
  from importlib.resources import files
2
2
  from typing import TYPE_CHECKING, TypeVar
3
3
 
4
- from klaude_code.command.command_abc import CommandResult
4
+ from klaude_code.command.command_abc import CommandResult, InputAction
5
5
  from klaude_code.command.prompt_command import PromptCommand
6
6
  from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands, events, model
@@ -40,15 +40,14 @@ def get_commands() -> dict[commands.CommandName | str, "CommandABC"]:
40
40
  return _COMMANDS.copy()
41
41
 
42
42
 
43
- def get_command_names() -> list[str]:
44
- """Get all registered command names for completion."""
45
- return [str(k) for k in _COMMANDS.keys()]
43
+ def is_slash_command_name(name: str) -> bool:
44
+ return name in _COMMANDS
46
45
 
47
46
 
48
47
  async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
49
48
  # Detect command name
50
49
  if not raw.startswith("/"):
51
- return CommandResult(agent_input=raw)
50
+ return CommandResult(actions=[InputAction.run_agent(raw)])
52
51
 
53
52
  splits = raw.split(" ", maxsplit=1)
54
53
  command_name_raw = splits[0][1:]
@@ -70,7 +69,7 @@ async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
70
69
  pass
71
70
 
72
71
  if command_key is None:
73
- return CommandResult(agent_input=raw)
72
+ return CommandResult(actions=[InputAction.run_agent(raw)])
74
73
 
75
74
  command = _COMMANDS[command_key]
76
75
  command_identifier: commands.CommandName | str = command.name
@@ -96,7 +95,7 @@ async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
96
95
  )
97
96
 
98
97
 
99
- def is_interactive_command(raw: str) -> bool:
98
+ def has_interactive_command(raw: str) -> bool:
100
99
  if not raw.startswith("/"):
101
100
  return False
102
101
  splits = raw.split(" ", maxsplit=1)
@@ -0,0 +1,111 @@
1
+ from klaude_code.command.command_abc import CommandABC, CommandResult
2
+ from klaude_code.command.registry import register_command
3
+ from klaude_code.core.agent import Agent
4
+ from klaude_code.protocol import commands, events, model
5
+ from klaude_code.session.session import Session
6
+
7
+
8
+ def accumulate_session_usage(session: Session) -> tuple[model.Usage, int]:
9
+ """Accumulate usage statistics from all ResponseMetadataItems in session history.
10
+
11
+ Returns:
12
+ A tuple of (accumulated_usage, task_count)
13
+ """
14
+ total = model.Usage()
15
+ task_count = 0
16
+
17
+ for item in session.conversation_history:
18
+ if isinstance(item, model.ResponseMetadataItem) and item.usage:
19
+ task_count += 1
20
+ usage = item.usage
21
+ total.input_tokens += usage.input_tokens
22
+ total.cached_tokens += usage.cached_tokens
23
+ total.reasoning_tokens += usage.reasoning_tokens
24
+ total.output_tokens += usage.output_tokens
25
+ total.total_tokens += usage.total_tokens
26
+
27
+ # Accumulate costs
28
+ if usage.input_cost is not None:
29
+ total.input_cost = (total.input_cost or 0.0) + usage.input_cost
30
+ if usage.output_cost is not None:
31
+ total.output_cost = (total.output_cost or 0.0) + usage.output_cost
32
+ if usage.cache_read_cost is not None:
33
+ total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
34
+ if usage.total_cost is not None:
35
+ total.total_cost = (total.total_cost or 0.0) + usage.total_cost
36
+
37
+ # Keep the latest context_usage_percent
38
+ if usage.context_usage_percent is not None:
39
+ total.context_usage_percent = usage.context_usage_percent
40
+
41
+ return total, task_count
42
+
43
+
44
+ def _format_tokens(tokens: int) -> str:
45
+ """Format token count with K/M suffix for readability."""
46
+ if tokens >= 1_000_000:
47
+ return f"{tokens / 1_000_000:.2f}M"
48
+ if tokens >= 1_000:
49
+ return f"{tokens / 1_000:.1f}K"
50
+ return str(tokens)
51
+
52
+
53
+ def _format_cost(cost: float | None) -> str:
54
+ """Format cost in USD."""
55
+ if cost is None:
56
+ return "-"
57
+ if cost < 0.01:
58
+ return f"${cost:.4f}"
59
+ return f"${cost:.2f}"
60
+
61
+
62
+ def format_status_content(usage: model.Usage) -> str:
63
+ """Format session status as comma-separated text."""
64
+ parts: list[str] = []
65
+
66
+ parts.append(f"Input: {_format_tokens(usage.input_tokens)}")
67
+ if usage.cached_tokens > 0:
68
+ parts.append(f"Cached: {_format_tokens(usage.cached_tokens)}")
69
+ parts.append(f"Output: {_format_tokens(usage.output_tokens)}")
70
+ parts.append(f"Total: {_format_tokens(usage.total_tokens)}")
71
+
72
+ if usage.total_cost is not None:
73
+ parts.append(f"Cost: {_format_cost(usage.total_cost)}")
74
+
75
+ return ", ".join(parts)
76
+
77
+
78
+ @register_command
79
+ class StatusCommand(CommandABC):
80
+ """Display session usage statistics."""
81
+
82
+ @property
83
+ def name(self) -> commands.CommandName:
84
+ return commands.CommandName.STATUS
85
+
86
+ @property
87
+ def summary(self) -> str:
88
+ return "Show session usage statistics"
89
+
90
+ async def run(self, raw: str, agent: Agent) -> CommandResult:
91
+ session = agent.session
92
+ usage, task_count = accumulate_session_usage(session)
93
+
94
+ event = events.DeveloperMessageEvent(
95
+ session_id=session.id,
96
+ item=model.DeveloperMessageItem(
97
+ content=format_status_content(usage),
98
+ command_output=model.CommandOutput(
99
+ command_name=self.name,
100
+ ui_extra=model.ToolResultUIExtra(
101
+ type=model.ToolResultUIExtraType.SESSION_STATUS,
102
+ session_status=model.SessionStatusUIExtra(
103
+ usage=usage,
104
+ task_count=task_count,
105
+ ),
106
+ ),
107
+ ),
108
+ ),
109
+ )
110
+
111
+ return CommandResult(events=[event])
@@ -6,8 +6,8 @@ from typing import Any, cast
6
6
  import yaml
7
7
  from pydantic import BaseModel, Field, ValidationError, model_validator
8
8
 
9
- from klaude_code.core.sub_agent import iter_sub_agent_profiles
10
9
  from klaude_code.protocol import llm_param
10
+ from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
11
11
  from klaude_code.trace import log
12
12
 
13
13
  config_path = Path.home() / ".klaude" / "klaude-config.yaml"
@@ -4,7 +4,7 @@ from rich.table import Table
4
4
  from rich.text import Text
5
5
 
6
6
  from klaude_code.config import Config
7
- from klaude_code.core.sub_agent import iter_sub_agent_profiles
7
+ from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
8
8
  from klaude_code.ui.rich.theme import ThemeKey, get_theme
9
9
 
10
10
 
@@ -109,7 +109,7 @@ STATUS_SHIMMER_PADDING = 10
109
109
  # Duration in seconds for one full shimmer sweep across the text
110
110
  STATUS_SHIMMER_SWEEP_SECONDS = 2
111
111
  # Half-width of the shimmer band in characters
112
- STATUS_SHIMMER_BAND_HALF_WIDTH = 3.0
112
+ STATUS_SHIMMER_BAND_HALF_WIDTH = 5.0
113
113
  # Scale factor applied to shimmer intensity when blending colors
114
114
  STATUS_SHIMMER_ALPHA_SCALE = 0.7
115
115
 
@@ -74,11 +74,8 @@ class Agent:
74
74
  self,
75
75
  session: Session,
76
76
  profile: AgentProfile,
77
- *,
78
- model_profile_provider: ModelProfileProvider | None = None,
79
77
  ):
80
78
  self.session: Session = session
81
- self.model_profile_provider: ModelProfileProvider = model_profile_provider or DefaultModelProfileProvider()
82
79
  self.profile: AgentProfile | None = None
83
80
  # Active task executor, if any
84
81
  self._current_task: TaskExecutor | None = None
@@ -156,14 +153,8 @@ class Agent:
156
153
  """Apply a fully constructed profile to the agent."""
157
154
 
158
155
  self.profile = profile
159
- self.session.model_name = profile.llm_client.model_name
160
-
161
- def build_model_profile(
162
- self,
163
- llm_client: LLMClientABC,
164
- sub_agent_type: tools.SubAgentType | None = None,
165
- ) -> AgentProfile:
166
- return self.model_profile_provider.build_profile(llm_client, sub_agent_type)
156
+ if not self.session.model_name:
157
+ self.session.model_name = profile.llm_client.model_name
167
158
 
168
159
  def get_llm_client(self) -> LLMClientABC:
169
160
  return self._require_profile().llm_client