klaude-code 1.2.8__tar.gz → 1.2.10__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 (185) hide show
  1. {klaude_code-1.2.8 → klaude_code-1.2.10}/PKG-INFO +1 -1
  2. {klaude_code-1.2.8 → klaude_code-1.2.10}/pyproject.toml +2 -1
  3. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/codex/__init__.py +1 -1
  4. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/cli/main.py +12 -1
  5. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/cli/runtime.py +7 -11
  6. klaude_code-1.2.10/src/klaude_code/command/__init__.py +90 -0
  7. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/clear_cmd.py +6 -2
  8. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/command_abc.py +5 -2
  9. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/diff_cmd.py +5 -2
  10. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/export_cmd.py +7 -4
  11. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/help_cmd.py +6 -2
  12. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/model_cmd.py +5 -2
  13. klaude_code-1.2.10/src/klaude_code/command/prompt-deslop.md +14 -0
  14. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/prompt_command.py +8 -3
  15. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/refresh_cmd.py +6 -2
  16. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/registry.py +17 -5
  17. klaude_code-1.2.10/src/klaude_code/command/release_notes_cmd.py +89 -0
  18. klaude_code-1.2.10/src/klaude_code/command/status_cmd.py +161 -0
  19. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/terminal_setup_cmd.py +7 -4
  20. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/const/__init__.py +1 -1
  21. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/agent.py +66 -26
  22. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/executor.py +2 -2
  23. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/manager/agent_manager.py +6 -7
  24. klaude_code-1.2.10/src/klaude_code/core/manager/llm_clients.py +67 -0
  25. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/manager/llm_clients_builder.py +19 -7
  26. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/manager/sub_agent_manager.py +6 -2
  27. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompt.py +38 -28
  28. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/reminders.py +4 -7
  29. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/task.py +59 -40
  30. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/__init__.py +2 -0
  31. klaude_code-1.2.10/src/klaude_code/core/tool/file/_utils.py +30 -0
  32. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/apply_patch_tool.py +1 -1
  33. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/edit_tool.py +6 -31
  34. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/multi_edit_tool.py +7 -32
  35. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/read_tool.py +6 -18
  36. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/write_tool.py +6 -31
  37. klaude_code-1.2.10/src/klaude_code/core/tool/memory/__init__.py +5 -0
  38. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/memory/memory_tool.py +2 -2
  39. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/memory/skill_loader.py +2 -1
  40. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/memory/skill_tool.py +13 -0
  41. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/sub_agent_tool.py +2 -1
  42. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/todo/todo_write_tool.py +1 -1
  43. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/todo/update_plan_tool.py +1 -1
  44. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/tool_context.py +21 -4
  45. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/tool_runner.py +5 -8
  46. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/web/mermaid_tool.py +1 -4
  47. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/turn.py +40 -37
  48. klaude_code-1.2.10/src/klaude_code/llm/__init__.py +13 -0
  49. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/anthropic/client.py +14 -44
  50. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/client.py +2 -2
  51. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/codex/client.py +4 -3
  52. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/input_common.py +0 -6
  53. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openai_compatible/client.py +31 -74
  54. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openai_compatible/input.py +6 -4
  55. klaude_code-1.2.10/src/klaude_code/llm/openai_compatible/stream_processor.py +82 -0
  56. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openrouter/client.py +32 -62
  57. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openrouter/input.py +4 -27
  58. klaude_code-1.2.10/src/klaude_code/llm/registry.py +48 -0
  59. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/responses/client.py +16 -48
  60. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/responses/input.py +1 -1
  61. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/usage.py +61 -11
  62. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/commands.py +1 -0
  63. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/events.py +11 -2
  64. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/model.py +147 -24
  65. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/op.py +1 -0
  66. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/sub_agent.py +5 -1
  67. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/session/export.py +56 -32
  68. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/session/session.py +43 -21
  69. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/session/templates/export_session.html +4 -1
  70. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/core/input.py +1 -1
  71. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/__init__.py +1 -5
  72. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/clipboard.py +5 -5
  73. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/event_handler.py +153 -54
  74. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/renderer.py +4 -4
  75. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/developer.py +35 -25
  76. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/metadata.py +68 -30
  77. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/tools.py +53 -87
  78. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/markdown.py +5 -5
  79. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/terminal/control.py +2 -2
  80. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/version.py +3 -3
  81. klaude_code-1.2.8/src/klaude_code/command/__init__.py +0 -43
  82. klaude_code-1.2.8/src/klaude_code/command/status_cmd.py +0 -119
  83. klaude_code-1.2.8/src/klaude_code/core/manager/llm_clients.py +0 -42
  84. klaude_code-1.2.8/src/klaude_code/llm/__init__.py +0 -23
  85. klaude_code-1.2.8/src/klaude_code/llm/registry.py +0 -22
  86. klaude_code-1.2.8/src/klaude_code/ui/renderers/__init__.py +0 -0
  87. {klaude_code-1.2.8 → klaude_code-1.2.10}/README.md +0 -0
  88. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/__init__.py +0 -0
  89. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/__init__.py +0 -0
  90. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/codex/exceptions.py +0 -0
  91. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  92. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/codex/oauth.py +0 -0
  93. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/auth/codex/token_manager.py +0 -0
  94. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/cli/__init__.py +0 -0
  95. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/cli/session_cmd.py +0 -0
  96. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/prompt-dev-docs-update.md +0 -0
  97. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/prompt-dev-docs.md +0 -0
  98. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/command/prompt-init.md +0 -0
  99. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/config/__init__.py +0 -0
  100. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/config/config.py +0 -0
  101. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/config/list_model.py +0 -0
  102. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/config/select_model.py +0 -0
  103. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/__init__.py +0 -0
  104. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/manager/__init__.py +0 -0
  105. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  106. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
  107. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1.md +0 -0
  108. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  109. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-subagent-explore.md +0 -0
  110. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-subagent-oracle.md +0 -0
  111. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -0
  112. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/prompts/prompt-subagent.md +0 -0
  113. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/__init__.py +0 -0
  114. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  115. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  116. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  117. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/multi_edit_tool.md +0 -0
  118. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  119. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  120. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/memory/memory_tool.md +0 -0
  121. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/memory/skill_tool.md +0 -0
  122. {klaude_code-1.2.8/src/klaude_code/core/tool/memory → klaude_code-1.2.10/src/klaude_code/core/tool/shell}/__init__.py +0 -0
  123. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  124. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  125. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  126. {klaude_code-1.2.8/src/klaude_code/core/tool/shell → klaude_code-1.2.10/src/klaude_code/core/tool/todo}/__init__.py +0 -0
  127. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  128. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  129. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/tool_abc.py +0 -0
  130. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/tool_registry.py +0 -0
  131. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/truncation.py +0 -0
  132. {klaude_code-1.2.8/src/klaude_code/core/tool/todo → klaude_code-1.2.10/src/klaude_code/core/tool/web}/__init__.py +0 -0
  133. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  134. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  135. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  136. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  137. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/anthropic/input.py +0 -0
  138. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/codex/__init__.py +0 -0
  139. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  140. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
  141. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  142. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/openrouter/reasoning_handler.py +0 -0
  143. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/llm/responses/__init__.py +0 -0
  144. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/__init__.py +0 -0
  145. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/llm_param.py +0 -0
  146. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/op_handler.py +0 -0
  147. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/protocol/tools.py +0 -0
  148. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/session/__init__.py +0 -0
  149. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/session/selector.py +0 -0
  150. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/trace/__init__.py +0 -0
  151. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/trace/log.py +0 -0
  152. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/__init__.py +0 -0
  153. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/core/__init__.py +0 -0
  154. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/core/display.py +0 -0
  155. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/core/stage_manager.py +0 -0
  156. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/__init__.py +0 -0
  157. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
  158. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/debug/display.py +0 -0
  159. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
  160. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/exec/display.py +0 -0
  161. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/completers.py +0 -0
  162. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/display.py +0 -0
  163. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +0 -0
  164. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
  165. {klaude_code-1.2.8/src/klaude_code/core/tool/web → klaude_code-1.2.10/src/klaude_code/ui/renderers}/__init__.py +0 -0
  166. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/assistant.py +0 -0
  167. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/common.py +0 -0
  168. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/diffs.py +0 -0
  169. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/errors.py +0 -0
  170. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/sub_agent.py +0 -0
  171. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/thinking.py +0 -0
  172. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/renderers/user_input.py +0 -0
  173. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/__init__.py +0 -0
  174. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/live.py +0 -0
  175. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/quote.py +0 -0
  176. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/searchable_text.py +0 -0
  177. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/status.py +0 -0
  178. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/rich/theme.py +0 -0
  179. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/terminal/__init__.py +0 -0
  180. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/terminal/color.py +0 -0
  181. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/terminal/notifier.py +0 -0
  182. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
  183. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/utils/__init__.py +0 -0
  184. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/utils/common.py +0 -0
  185. {klaude_code-1.2.8 → klaude_code-1.2.10}/src/klaude_code/ui/utils/debouncer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.2.8
3
+ Version: 1.2.10
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.8"
7
+ version = "1.2.10"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -33,6 +33,7 @@ dev = [
33
33
  "isort>=6.0.1",
34
34
  "pyright>=1.1.407",
35
35
  "pytest>=8.4.1",
36
+ "pytest-cov>=7.0.0",
36
37
  ]
37
38
 
38
39
 
@@ -17,4 +17,4 @@ __all__ = [
17
17
  "CodexOAuthError",
18
18
  "CodexTokenExpiredError",
19
19
  "CodexTokenManager",
20
- ]
20
+ ]
@@ -4,6 +4,7 @@ import os
4
4
  import subprocess
5
5
  import sys
6
6
  import uuid
7
+ from importlib.metadata import PackageNotFoundError
7
8
  from importlib.metadata import version as pkg_version
8
9
 
9
10
  import typer
@@ -27,6 +28,9 @@ def _version_callback(value: bool) -> None:
27
28
  if value:
28
29
  try:
29
30
  ver = pkg_version("klaude-code")
31
+ except PackageNotFoundError:
32
+ # Package is not installed or has no metadata; show a generic version string.
33
+ ver = "unknown"
30
34
  except Exception:
31
35
  ver = "unknown"
32
36
  print(f"klaude-code {ver}")
@@ -232,8 +236,12 @@ def exec_command(
232
236
  stdin = sys.stdin.read().rstrip("\n")
233
237
  if stdin:
234
238
  parts.append(stdin)
235
- except Exception as e:
239
+ except (OSError, ValueError) as e:
240
+ # Expected I/O-related errors when reading from stdin (e.g. broken pipe, closed stream).
236
241
  log((f"Error reading from stdin: {e}", "red"))
242
+ except Exception as e:
243
+ # Unexpected errors are still reported but kept from crashing the CLI.
244
+ log((f"Unexpected error reading from stdin: {e}", "red"))
237
245
 
238
246
  if input_content:
239
247
  parts.append(input_content)
@@ -333,6 +341,7 @@ def main_callback(
333
341
 
334
342
  # Resolve session id before entering asyncio loop
335
343
  session_id: str | None = None
344
+ is_new_session = False
336
345
  if resume:
337
346
  session_id = resume_select_session()
338
347
  if session_id is None:
@@ -343,6 +352,7 @@ def main_callback(
343
352
  # If still no session_id, generate a new one for a new session
344
353
  if session_id is None:
345
354
  session_id = uuid.uuid4().hex
355
+ is_new_session = True
346
356
 
347
357
  debug_enabled, debug_filters = resolve_debug_settings(debug, debug_filter)
348
358
 
@@ -357,5 +367,6 @@ def main_callback(
357
367
  run_interactive(
358
368
  init_config=init_config,
359
369
  session_id=session_id,
370
+ is_new_session=is_new_session,
360
371
  )
361
372
  )
@@ -13,7 +13,6 @@ from klaude_code.config import Config, load_config
13
13
  from klaude_code.core.agent import Agent, DefaultModelProfileProvider, VanillaModelProfileProvider
14
14
  from klaude_code.core.executor import Executor
15
15
  from klaude_code.core.manager import build_llm_clients
16
- from klaude_code.core.tool import SkillLoader, SkillTool
17
16
  from klaude_code.protocol import events, op
18
17
  from klaude_code.protocol.model import UserInputPayload
19
18
  from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
@@ -96,11 +95,6 @@ async def initialize_app_components(init_config: AppInitConfig) -> AppComponents
96
95
  if config is None:
97
96
  raise typer.Exit(1)
98
97
 
99
- # Initialize skills
100
- skill_loader = SkillLoader()
101
- skill_loader.discover_skills()
102
- SkillTool.set_skill_loader(skill_loader)
103
-
104
98
  # Initialize LLM clients
105
99
  try:
106
100
  enabled_sub_agents = [p.name for p in iter_sub_agent_profiles()]
@@ -216,8 +210,7 @@ async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
216
210
  # Generate a new session ID for exec mode
217
211
  session_id = uuid.uuid4().hex
218
212
 
219
- # Init Agent
220
- await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id))
213
+ await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id, is_new_session=True))
221
214
  await components.event_queue.join()
222
215
 
223
216
  # Submit the input content directly
@@ -231,7 +224,9 @@ async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
231
224
  await cleanup_app_components(components)
232
225
 
233
226
 
234
- async def run_interactive(init_config: AppInitConfig, session_id: str | None = None) -> None:
227
+ async def run_interactive(
228
+ init_config: AppInitConfig, session_id: str | None = None, *, is_new_session: bool = False
229
+ ) -> None:
235
230
  """Run the interactive REPL using the provided configuration."""
236
231
 
237
232
  components = await initialize_app_components(init_config)
@@ -284,8 +279,9 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
284
279
  restore_sigint = install_sigint_double_press_exit(_show_toast_once, _hide_progress)
285
280
 
286
281
  try:
287
- # Init Agent
288
- await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id))
282
+ await components.executor.submit_and_wait(
283
+ op.InitAgentOperation(session_id=session_id, is_new_session=is_new_session)
284
+ )
289
285
  await components.event_queue.join()
290
286
  # Input
291
287
  await input_provider.start()
@@ -0,0 +1,90 @@
1
+ from .command_abc import CommandABC, CommandResult, InputAction, InputActionType
2
+ from .registry import (
3
+ dispatch_command,
4
+ get_commands,
5
+ has_interactive_command,
6
+ is_slash_command_name,
7
+ load_prompt_commands,
8
+ register_command,
9
+ )
10
+
11
+ # Lazy load commands to avoid heavy imports at module load time
12
+ _commands_loaded = False
13
+
14
+
15
+ def ensure_commands_loaded() -> None:
16
+ """Ensure all commands are loaded (lazy initialization).
17
+
18
+ This function is called internally by registry functions like get_commands(),
19
+ dispatch_command(), etc. It can also be called explicitly if early loading is desired.
20
+ """
21
+ global _commands_loaded
22
+ if _commands_loaded:
23
+ return
24
+ _commands_loaded = True
25
+
26
+ # Import command modules to trigger @register_command decorators
27
+ from . import clear_cmd as _clear_cmd # noqa: F401
28
+ from . import diff_cmd as _diff_cmd # noqa: F401
29
+ from . import export_cmd as _export_cmd # noqa: F401
30
+ from . import help_cmd as _help_cmd # noqa: F401
31
+ from . import model_cmd as _model_cmd # noqa: F401
32
+ from . import refresh_cmd as _refresh_cmd # noqa: F401
33
+ from . import release_notes_cmd as _release_notes_cmd # noqa: F401
34
+ from . import status_cmd as _status_cmd # noqa: F401
35
+ from . import terminal_setup_cmd as _terminal_setup_cmd # noqa: F401
36
+
37
+ # Suppress unused variable warnings
38
+ _ = (
39
+ _clear_cmd,
40
+ _diff_cmd,
41
+ _export_cmd,
42
+ _help_cmd,
43
+ _model_cmd,
44
+ _refresh_cmd,
45
+ _release_notes_cmd,
46
+ _status_cmd,
47
+ _terminal_setup_cmd,
48
+ )
49
+
50
+ # Load prompt-based commands
51
+ load_prompt_commands()
52
+
53
+
54
+ # Lazy accessors for command classes
55
+ def __getattr__(name: str) -> object:
56
+ _commands_map = {
57
+ "ClearCommand": "clear_cmd",
58
+ "DiffCommand": "diff_cmd",
59
+ "ExportCommand": "export_cmd",
60
+ "HelpCommand": "help_cmd",
61
+ "ModelCommand": "model_cmd",
62
+ "RefreshTerminalCommand": "refresh_cmd",
63
+ "ReleaseNotesCommand": "release_notes_cmd",
64
+ "StatusCommand": "status_cmd",
65
+ "TerminalSetupCommand": "terminal_setup_cmd",
66
+ }
67
+ if name in _commands_map:
68
+ import importlib
69
+
70
+ module = importlib.import_module(f".{_commands_map[name]}", __package__)
71
+ return getattr(module, name)
72
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
73
+
74
+
75
+ __all__ = [
76
+ # Command classes are lazily loaded via __getattr__
77
+ # "ClearCommand", "DiffCommand", "HelpCommand", "ModelCommand",
78
+ # "ExportCommand", "RefreshTerminalCommand", "ReleaseNotesCommand",
79
+ # "StatusCommand", "TerminalSetupCommand",
80
+ "register_command",
81
+ "CommandABC",
82
+ "CommandResult",
83
+ "InputAction",
84
+ "InputActionType",
85
+ "dispatch_command",
86
+ "get_commands",
87
+ "is_slash_command_name",
88
+ "has_interactive_command",
89
+ "ensure_commands_loaded",
90
+ ]
@@ -1,8 +1,12 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
2
4
  from klaude_code.command.registry import register_command
3
- from klaude_code.core.agent import Agent
4
5
  from klaude_code.protocol import commands
5
6
 
7
+ if TYPE_CHECKING:
8
+ from klaude_code.core.agent import Agent
9
+
6
10
 
7
11
  @register_command
8
12
  class ClearCommand(CommandABC):
@@ -16,5 +20,5 @@ class ClearCommand(CommandABC):
16
20
  def summary(self) -> str:
17
21
  return "Clear conversation history and free up context"
18
22
 
19
- async def run(self, raw: str, agent: Agent) -> CommandResult:
23
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
20
24
  return CommandResult(actions=[InputAction.clear()])
@@ -1,12 +1,15 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from enum import Enum
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from pydantic import BaseModel
5
6
 
6
- from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands
8
8
  from klaude_code.protocol import events as protocol_events
9
9
 
10
+ if TYPE_CHECKING:
11
+ from klaude_code.core.agent import Agent
12
+
10
13
 
11
14
  class InputActionType(str, Enum):
12
15
  """Supported input action kinds."""
@@ -78,7 +81,7 @@ class CommandABC(ABC):
78
81
  return False
79
82
 
80
83
  @abstractmethod
81
- async def run(self, raw: str, agent: Agent) -> CommandResult:
84
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
82
85
  """
83
86
  Execute the command.
84
87
 
@@ -1,11 +1,14 @@
1
1
  import subprocess
2
2
  from pathlib import Path
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from klaude_code.command.command_abc import CommandABC, CommandResult
5
6
  from klaude_code.command.registry import register_command
6
- from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands, events, model
8
8
 
9
+ if TYPE_CHECKING:
10
+ from klaude_code.core.agent import Agent
11
+
9
12
 
10
13
  @register_command
11
14
  class DiffCommand(CommandABC):
@@ -19,7 +22,7 @@ class DiffCommand(CommandABC):
19
22
  def summary(self) -> str:
20
23
  return "Show git diff"
21
24
 
22
- async def run(self, raw: str, agent: Agent) -> CommandResult:
25
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
23
26
  try:
24
27
  # Check if current directory is in a git repository
25
28
  git_check = subprocess.run(
@@ -2,13 +2,16 @@ from __future__ import annotations
2
2
 
3
3
  import subprocess
4
4
  from pathlib import Path
5
+ from typing import TYPE_CHECKING
5
6
 
6
7
  from klaude_code.command.command_abc import CommandABC, CommandResult
7
8
  from klaude_code.command.registry import register_command
8
- from klaude_code.core.agent import Agent
9
9
  from klaude_code.protocol import commands, events, model
10
10
  from klaude_code.session.export import build_export_html, get_default_export_path
11
11
 
12
+ if TYPE_CHECKING:
13
+ from klaude_code.core.agent import Agent
14
+
12
15
 
13
16
  @register_command
14
17
  class ExportCommand(CommandABC):
@@ -30,7 +33,7 @@ class ExportCommand(CommandABC):
30
33
  def is_interactive(self) -> bool:
31
34
  return False
32
35
 
33
- async def run(self, raw: str, agent: Agent) -> CommandResult:
36
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
34
37
  try:
35
38
  output_path = self._resolve_output_path(raw, agent)
36
39
  html_doc = self._build_html(agent)
@@ -57,7 +60,7 @@ class ExportCommand(CommandABC):
57
60
  )
58
61
  return CommandResult(events=[event])
59
62
 
60
- def _resolve_output_path(self, raw: str, agent: Agent) -> Path:
63
+ def _resolve_output_path(self, raw: str, agent: "Agent") -> Path:
61
64
  trimmed = raw.strip()
62
65
  if trimmed:
63
66
  candidate = Path(trimmed).expanduser()
@@ -78,7 +81,7 @@ class ExportCommand(CommandABC):
78
81
  msg = f"Failed to open HTML with `open`: {exc}"
79
82
  raise RuntimeError(msg) from exc
80
83
 
81
- def _build_html(self, agent: Agent) -> str:
84
+ def _build_html(self, agent: "Agent") -> str:
82
85
  profile = agent.profile
83
86
  system_prompt = (profile.system_prompt if profile else "") or ""
84
87
  tools = profile.tools if profile else []
@@ -1,8 +1,12 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from klaude_code.command.command_abc import CommandABC, CommandResult
2
4
  from klaude_code.command.registry import register_command
3
- from klaude_code.core.agent import Agent
4
5
  from klaude_code.protocol import commands, events, model
5
6
 
7
+ if TYPE_CHECKING:
8
+ from klaude_code.core.agent import Agent
9
+
6
10
 
7
11
  @register_command
8
12
  class HelpCommand(CommandABC):
@@ -16,7 +20,7 @@ class HelpCommand(CommandABC):
16
20
  def summary(self) -> str:
17
21
  return "Show help and available commands"
18
22
 
19
- async def run(self, raw: str, agent: Agent) -> CommandResult:
23
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
20
24
  lines: list[str] = [
21
25
  """
22
26
  Usage:
@@ -1,11 +1,14 @@
1
1
  import asyncio
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
4
5
  from klaude_code.command.registry import register_command
5
6
  from klaude_code.config import select_model_from_config
6
- from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands, events, model
8
8
 
9
+ if TYPE_CHECKING:
10
+ from klaude_code.core.agent import Agent
11
+
9
12
 
10
13
  @register_command
11
14
  class ModelCommand(CommandABC):
@@ -23,7 +26,7 @@ class ModelCommand(CommandABC):
23
26
  def is_interactive(self) -> bool:
24
27
  return True
25
28
 
26
- async def run(self, raw: str, agent: Agent) -> CommandResult:
29
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
27
30
  selected_model = await asyncio.to_thread(select_model_from_config, preferred=raw)
28
31
 
29
32
  current_model = agent.profile.llm_client.model_name if agent.profile else None
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Remove AI code slop
3
+ from: https://cursor.com/cn/link/command?name=deslop&text=%23%20Remove%20AI%20code%20slop%0A%0ACheck%20the%20diff%20against%20main%2C%20and%20remove%20all%20AI%20generated%20slop%20introduced%20in%20this%20branch.%0A%0AThis%20includes%3A%0A-%20Extra%20comments%20that%20a%20human%20wouldn%27t%20add%20or%20is%20inconsistent%20with%20the%20rest%20of%20the%20file%0A-%20Extra%20defensive%20checks%20or%20try%2Fcatch%20blocks%20that%20are%20abnormal%20for%20that%20area%20of%20the%20codebase%20(especially%20if%20called%20by%20trusted%20%2F%20validated%20codepaths)%0A-%20Casts%20to%20any%20to%20get%20around%20type%20issues%0A-%20Any%20other%20style%20that%20is%20inconsistent%20with%20the%20file%0A%0AReport%20at%20the%20end%20with%20only%20a%201-3%20sentence%20summary%20of%20what%20you%20changed
4
+ ---
5
+
6
+ Check the diff against main, and remove all AI generated slop introduced in this branch.
7
+
8
+ This includes:
9
+ - Extra comments that a human wouldn't add or is inconsistent with the rest of the file
10
+ - Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths)
11
+ - Casts to any to get around type issues
12
+ - Any other style that is inconsistent with the file
13
+
14
+ Report at the end with only a 1-3 sentence summary of what you changed
@@ -1,10 +1,14 @@
1
1
  from importlib.resources import files
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  import yaml
4
5
 
5
6
  from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
6
- from klaude_code.core.agent import Agent
7
7
  from klaude_code.protocol import commands
8
+ from klaude_code.trace import log_debug
9
+
10
+ if TYPE_CHECKING:
11
+ from klaude_code.core.agent import Agent
8
12
 
9
13
 
10
14
  class PromptCommand(CommandABC):
@@ -41,7 +45,8 @@ class PromptCommand(CommandABC):
41
45
 
42
46
  self._metadata = {}
43
47
  self._content = raw_text
44
- except Exception:
48
+ except (OSError, yaml.YAMLError) as e:
49
+ log_debug(f"Failed to load prompt template {self.template_name}: {e}")
45
50
  self._metadata = {"description": "Error loading template"}
46
51
  self._content = f"Error loading template: {self.template_name}"
47
52
 
@@ -54,7 +59,7 @@ class PromptCommand(CommandABC):
54
59
  def support_addition_params(self) -> bool:
55
60
  return True
56
61
 
57
- async def run(self, raw: str, agent: Agent) -> CommandResult:
62
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
58
63
  self._ensure_loaded()
59
64
  template_content = self._content or ""
60
65
  user_input = raw.strip() or "<none>"
@@ -1,8 +1,12 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from klaude_code.command.command_abc import CommandABC, CommandResult
2
4
  from klaude_code.command.registry import register_command
3
- from klaude_code.core.agent import Agent
4
5
  from klaude_code.protocol import commands, events
5
6
 
7
+ if TYPE_CHECKING:
8
+ from klaude_code.core.agent import Agent
9
+
6
10
 
7
11
  @register_command
8
12
  class RefreshTerminalCommand(CommandABC):
@@ -20,7 +24,7 @@ class RefreshTerminalCommand(CommandABC):
20
24
  def is_interactive(self) -> bool:
21
25
  return True
22
26
 
23
- async def run(self, raw: str, agent: Agent) -> CommandResult:
27
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
24
28
  import os
25
29
 
26
30
  os.system("cls" if os.name == "nt" else "clear")
@@ -3,10 +3,12 @@ from typing import TYPE_CHECKING, TypeVar
3
3
 
4
4
  from klaude_code.command.command_abc import CommandResult, InputAction
5
5
  from klaude_code.command.prompt_command import PromptCommand
6
- from klaude_code.core.agent import Agent
7
6
  from klaude_code.protocol import commands, events, model
7
+ from klaude_code.trace import log_debug
8
8
 
9
9
  if TYPE_CHECKING:
10
+ from klaude_code.core.agent import Agent
11
+
10
12
  from .command_abc import CommandABC
11
13
 
12
14
  _COMMANDS: dict[commands.CommandName | str, "CommandABC"] = {}
@@ -30,21 +32,30 @@ def load_prompt_commands():
30
32
  if (name.startswith("prompt_") or name.startswith("prompt-")) and name.endswith(".md"):
31
33
  cmd = PromptCommand(name)
32
34
  _COMMANDS[cmd.name] = cmd
33
- except Exception:
34
- # If resource loading fails, just ignore
35
- pass
35
+ except OSError as e:
36
+ log_debug(f"Failed to load prompt commands: {e}")
37
+
38
+
39
+ def _ensure_commands_loaded() -> None:
40
+ """Ensure all commands are loaded (lazy initialization)."""
41
+ from klaude_code.command import ensure_commands_loaded
42
+
43
+ ensure_commands_loaded()
36
44
 
37
45
 
38
46
  def get_commands() -> dict[commands.CommandName | str, "CommandABC"]:
39
47
  """Get all registered commands."""
48
+ _ensure_commands_loaded()
40
49
  return _COMMANDS.copy()
41
50
 
42
51
 
43
52
  def is_slash_command_name(name: str) -> bool:
53
+ _ensure_commands_loaded()
44
54
  return name in _COMMANDS
45
55
 
46
56
 
47
- async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
57
+ async def dispatch_command(raw: str, agent: "Agent") -> CommandResult:
58
+ _ensure_commands_loaded()
48
59
  # Detect command name
49
60
  if not raw.startswith("/"):
50
61
  return CommandResult(actions=[InputAction.run_agent(raw)])
@@ -96,6 +107,7 @@ async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
96
107
 
97
108
 
98
109
  def has_interactive_command(raw: str) -> bool:
110
+ _ensure_commands_loaded()
99
111
  if not raw.startswith("/"):
100
112
  return False
101
113
  splits = raw.split(" ", maxsplit=1)
@@ -0,0 +1,89 @@
1
+ from pathlib import Path
2
+ from typing import TYPE_CHECKING
3
+
4
+ from klaude_code.command.command_abc import CommandABC, CommandResult
5
+ from klaude_code.command.registry import register_command
6
+ from klaude_code.protocol import commands, events, model
7
+
8
+ if TYPE_CHECKING:
9
+ from klaude_code.core.agent import Agent
10
+
11
+
12
+ def _read_changelog() -> str:
13
+ """Read CHANGELOG.md from project root."""
14
+ changelog_path = Path(__file__).parent.parent.parent.parent / "CHANGELOG.md"
15
+ if not changelog_path.exists():
16
+ return "CHANGELOG.md not found"
17
+ return changelog_path.read_text(encoding="utf-8")
18
+
19
+
20
+ def _extract_releases(changelog: str, count: int = 1) -> str:
21
+ """Extract release sections from changelog in reverse order (oldest first).
22
+
23
+ Args:
24
+ changelog: The full changelog content.
25
+ count: Number of releases to extract (default 1).
26
+
27
+ Returns:
28
+ The content of the specified number of releases, with newest at bottom.
29
+ """
30
+ lines = changelog.split("\n")
31
+ releases: list[list[str]] = []
32
+ current_release: list[str] = []
33
+ version_count = 0
34
+
35
+ for line in lines:
36
+ # Skip [Unreleased] section header
37
+ if line.startswith("## [Unreleased]"):
38
+ continue
39
+
40
+ # Check for version header (e.g., ## [1.2.8] - 2025-12-01)
41
+ if line.startswith("## [") and "]" in line:
42
+ if current_release:
43
+ releases.append(current_release)
44
+ version_count += 1
45
+ if version_count > count:
46
+ break
47
+ current_release = [line]
48
+ continue
49
+
50
+ if version_count > 0:
51
+ current_release.append(line)
52
+
53
+ # Append the last release if exists
54
+ if current_release and version_count <= count:
55
+ releases.append(current_release)
56
+
57
+ if not releases:
58
+ return "No release notes found"
59
+
60
+ # Reverse to show oldest first, newest last
61
+ releases.reverse()
62
+ return "\n".join("\n".join(release) for release in releases).strip()
63
+
64
+
65
+ @register_command
66
+ class ReleaseNotesCommand(CommandABC):
67
+ """Display the latest release notes from CHANGELOG.md."""
68
+
69
+ @property
70
+ def name(self) -> commands.CommandName:
71
+ return commands.CommandName.RELEASE_NOTES
72
+
73
+ @property
74
+ def summary(self) -> str:
75
+ return "Show the latest release notes"
76
+
77
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
78
+ changelog = _read_changelog()
79
+ content = _extract_releases(changelog, count=10)
80
+
81
+ event = events.DeveloperMessageEvent(
82
+ session_id=agent.session.id,
83
+ item=model.DeveloperMessageItem(
84
+ content=content,
85
+ command_output=model.CommandOutput(command_name=self.name),
86
+ ),
87
+ )
88
+
89
+ return CommandResult(events=[event])