klaude-code 1.2.22__tar.gz → 1.2.24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {klaude_code-1.2.22 → klaude_code-1.2.24}/PKG-INFO +18 -13
- {klaude_code-1.2.22 → klaude_code-1.2.24}/README.md +17 -12
- {klaude_code-1.2.22 → klaude_code-1.2.24}/pyproject.toml +2 -1
- klaude_code-1.2.24/src/klaude_code/command/prompt-jj-describe.md +32 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/status_cmd.py +1 -1
- klaude_code-1.2.22/src/klaude_code/const/__init__.py → klaude_code-1.2.24/src/klaude_code/const.py +11 -2
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/executor.py +1 -1
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/manager/sub_agent_manager.py +1 -1
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/reminders.py +51 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/task.py +37 -18
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/__init__.py +1 -4
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/read_tool.py +23 -1
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/write_tool.py +7 -3
- {klaude_code-1.2.22/src/klaude_code/core/tool/memory → klaude_code-1.2.24/src/klaude_code/core/tool/skill}/skill_tool.py +16 -39
- klaude_code-1.2.24/src/klaude_code/llm/openai_compatible/client.py +119 -0
- klaude_code-1.2.24/src/klaude_code/llm/openai_compatible/stream.py +272 -0
- klaude_code-1.2.24/src/klaude_code/llm/openrouter/client.py +129 -0
- klaude_code-1.2.22/src/klaude_code/llm/openrouter/reasoning_handler.py → klaude_code-1.2.24/src/klaude_code/llm/openrouter/reasoning.py +24 -2
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/model.py +15 -2
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/export.py +1 -1
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/store.py +4 -2
- klaude_code-1.2.24/src/klaude_code/skill/__init__.py +27 -0
- klaude_code-1.2.24/src/klaude_code/skill/assets/deslop/SKILL.md +17 -0
- klaude_code-1.2.24/src/klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
- klaude_code-1.2.24/src/klaude_code/skill/assets/handoff/SKILL.md +39 -0
- klaude_code-1.2.24/src/klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
- klaude_code-1.2.24/src/klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
- klaude_code-1.2.22/src/klaude_code/core/tool/memory/skill_loader.py → klaude_code-1.2.24/src/klaude_code/skill/loader.py +60 -24
- klaude_code-1.2.24/src/klaude_code/skill/manager.py +70 -0
- klaude_code-1.2.24/src/klaude_code/skill/system_skills.py +192 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/core/stage_manager.py +0 -3
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/completers.py +103 -3
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/event_handler.py +101 -49
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +55 -6
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/renderer.py +24 -17
- klaude_code-1.2.24/src/klaude_code/ui/renderers/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/assistant.py +7 -2
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/developer.py +12 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/diffs.py +1 -1
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/metadata.py +6 -8
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/sub_agent.py +28 -5
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/thinking.py +16 -10
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/tools.py +83 -34
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/user_input.py +32 -2
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/markdown.py +40 -20
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/status.py +15 -19
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/theme.py +70 -17
- klaude_code-1.2.22/src/klaude_code/command/prompt-deslop.md +0 -14
- klaude_code-1.2.22/src/klaude_code/command/prompt-dev-docs-update.md +0 -56
- klaude_code-1.2.22/src/klaude_code/command/prompt-dev-docs.md +0 -46
- klaude_code-1.2.22/src/klaude_code/command/prompt-handoff.md +0 -33
- klaude_code-1.2.22/src/klaude_code/command/prompt-jj-workspace.md +0 -18
- klaude_code-1.2.22/src/klaude_code/core/tool/memory/__init__.py +0 -5
- klaude_code-1.2.22/src/klaude_code/llm/openai_compatible/client.py +0 -192
- klaude_code-1.2.22/src/klaude_code/llm/openai_compatible/stream_processor.py +0 -83
- klaude_code-1.2.22/src/klaude_code/llm/openrouter/client.py +0 -209
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/codex/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/codex/exceptions.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/codex/oauth.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/auth/codex/token_manager.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/auth_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/config_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/debug.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/list_model.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/main.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/runtime.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/self_update.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/cli/session_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/clear_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/command_abc.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/debug_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/export_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/export_online_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/help_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/model_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/prompt-init.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/prompt_command.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/refresh_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/registry.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/release_notes_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/command/thinking_cmd.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/config/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/config/config.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/config/select_model.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/agent.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/manager/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/manager/llm_clients.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompt.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/_utils.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/read_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/file/write_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/report_back_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/shell/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
- {klaude_code-1.2.22/src/klaude_code/core/tool/todo → klaude_code-1.2.24/src/klaude_code/core/tool/skill}/__init__.py +0 -0
- {klaude_code-1.2.22/src/klaude_code/core/tool/memory → klaude_code-1.2.24/src/klaude_code/core/tool/skill}/skill_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
- {klaude_code-1.2.22/src/klaude_code/core/tool/web → klaude_code-1.2.24/src/klaude_code/core/tool/todo}/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/tool_abc.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/tool_context.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/tool_registry.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/tool_runner.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/truncation.py +0 -0
- {klaude_code-1.2.22/src/klaude_code/ui/renderers → klaude_code-1.2.24/src/klaude_code/core/tool/web}/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/core/turn.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/anthropic/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/anthropic/client.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/anthropic/input.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/client.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/codex/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/codex/client.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/input_common.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/openai_compatible/input.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/openrouter/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/openrouter/input.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/registry.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/responses/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/responses/client.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/responses/input.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/llm/usage.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/commands.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/events.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/llm_param.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/op.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/op_handler.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/sub_agent/oracle.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/sub_agent/task.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/sub_agent/web.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/protocol/tools.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/codec.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/selector.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/session.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/session/templates/export_session.html +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/trace/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/trace/log.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/core/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/core/display.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/core/input.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/debug/display.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/exec/display.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/display.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/common.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/renderers/errors.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/code_panel.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/live.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/quote.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/rich/searchable_text.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/terminal/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/terminal/color.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/terminal/control.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/terminal/notifier.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/utils/__init__.py +0 -0
- {klaude_code-1.2.22 → klaude_code-1.2.24}/src/klaude_code/ui/utils/common.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.24
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: chardet>=5.2.0
|
|
@@ -18,18 +18,23 @@ Requires-Dist: typer>=0.17.3
|
|
|
18
18
|
Requires-Python: >=3.13
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
##
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
21
|
+
# Klaude Code
|
|
22
|
+
|
|
23
|
+
Multi-model code agent CLI.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
- **Multi-provider**: Anthropic, OpenAI Responses API, OpenRouter
|
|
27
|
+
- **Keep reasoning item in context**: Interleaved thinking support
|
|
28
|
+
- **Model-aware tools**: Claude Code tools for Sonnet, `apply_patch` for GPT-5/Codex
|
|
29
|
+
- **Structured sub-agent output**: Define JSON schema, get schema-compliant responses via constrained decoding
|
|
30
|
+
- **Recursive `@file` mentions**: Circular dependency protection, relative path resolution
|
|
31
|
+
- **Reminders**: Cooldown-based todo tracking and instruction reinforcement
|
|
32
|
+
- **External file sync**: Monitoring for external edits (linter, manual)
|
|
33
|
+
- **Interrupt handling**: Ctrl+C preserves partial responses and synthesizes tool cancellation results
|
|
34
|
+
- **Output truncation**: Large outputs saved to file system with snapshot links
|
|
35
|
+
- **Skills**: Built-in + user + project Agent Skills (with implicit invocation by Skill tool or explicit invocation by typing `$`)
|
|
36
|
+
- **Sessions**: Resumable with `--continue`
|
|
37
|
+
- **Extras**: Slash commands, sub-agents, image paste, terminal notifications, auto-theming
|
|
33
38
|
|
|
34
39
|
## Installation
|
|
35
40
|
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
- **
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
1
|
+
# Klaude Code
|
|
2
|
+
|
|
3
|
+
Multi-model code agent CLI.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **Multi-provider**: Anthropic, OpenAI Responses API, OpenRouter
|
|
7
|
+
- **Keep reasoning item in context**: Interleaved thinking support
|
|
8
|
+
- **Model-aware tools**: Claude Code tools for Sonnet, `apply_patch` for GPT-5/Codex
|
|
9
|
+
- **Structured sub-agent output**: Define JSON schema, get schema-compliant responses via constrained decoding
|
|
10
|
+
- **Recursive `@file` mentions**: Circular dependency protection, relative path resolution
|
|
11
|
+
- **Reminders**: Cooldown-based todo tracking and instruction reinforcement
|
|
12
|
+
- **External file sync**: Monitoring for external edits (linter, manual)
|
|
13
|
+
- **Interrupt handling**: Ctrl+C preserves partial responses and synthesizes tool cancellation results
|
|
14
|
+
- **Output truncation**: Large outputs saved to file system with snapshot links
|
|
15
|
+
- **Skills**: Built-in + user + project Agent Skills (with implicit invocation by Skill tool or explicit invocation by typing `$`)
|
|
16
|
+
- **Sessions**: Resumable with `--continue`
|
|
17
|
+
- **Extras**: Slash commands, sub-agents, image paste, terminal notifications, auto-theming
|
|
13
18
|
|
|
14
19
|
## Installation
|
|
15
20
|
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "klaude-code"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.24"
|
|
8
8
|
description = "Add your description here"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -81,6 +81,7 @@ layers = [
|
|
|
81
81
|
"klaude_code.cli",
|
|
82
82
|
"klaude_code.ui",
|
|
83
83
|
"klaude_code.core",
|
|
84
|
+
"klaude_code.skill",
|
|
84
85
|
"klaude_code.command",
|
|
85
86
|
"klaude_code.session",
|
|
86
87
|
"klaude_code.config",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Add description for current jj change
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run `jj status` and `jj diff --git` to see the current changes and add a description for the it.
|
|
6
|
+
|
|
7
|
+
In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:<example>
|
|
8
|
+
jj describe -m "$(cat <<'EOF'
|
|
9
|
+
Commit message here.
|
|
10
|
+
EOF
|
|
11
|
+
)"
|
|
12
|
+
</example>
|
|
13
|
+
|
|
14
|
+
Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
|
15
|
+
```
|
|
16
|
+
<type>(<scope>): <description>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Types:
|
|
20
|
+
- `feat`: New feature
|
|
21
|
+
- `fix`: Bug fix
|
|
22
|
+
- `docs`: Documentation changes
|
|
23
|
+
- `style`: Code style changes (formatting, no logic change)
|
|
24
|
+
- `refactor`: Code refactoring (no feature or fix)
|
|
25
|
+
- `test`: Adding or updating tests
|
|
26
|
+
- `chore`: Build process, dependencies, or tooling changes
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
- `feat(cli): add --verbose flag for debug output`
|
|
30
|
+
- `fix(llm): handle API timeout errors gracefully`
|
|
31
|
+
- `docs(readme): update installation instructions`
|
|
32
|
+
- `refactor(core): simplify session state management`
|
|
@@ -22,7 +22,7 @@ def accumulate_session_usage(session: Session) -> AggregatedUsage:
|
|
|
22
22
|
for item in session.conversation_history:
|
|
23
23
|
if isinstance(item, model.TaskMetadataItem):
|
|
24
24
|
task_count += 1
|
|
25
|
-
all_metadata.append(item.
|
|
25
|
+
all_metadata.append(item.main_agent)
|
|
26
26
|
all_metadata.extend(item.sub_agent_task_metadata)
|
|
27
27
|
|
|
28
28
|
# Aggregate by model+provider
|
klaude_code-1.2.22/src/klaude_code/const/__init__.py → klaude_code-1.2.24/src/klaude_code/const.py
RENAMED
|
@@ -90,10 +90,10 @@ INVALID_TOOL_CALL_MAX_LENGTH = 500
|
|
|
90
90
|
TRUNCATE_DISPLAY_MAX_LINE_LENGTH = 1000
|
|
91
91
|
|
|
92
92
|
# Maximum lines for truncated display output
|
|
93
|
-
TRUNCATE_DISPLAY_MAX_LINES =
|
|
93
|
+
TRUNCATE_DISPLAY_MAX_LINES = 8
|
|
94
94
|
|
|
95
95
|
# Maximum lines for sub-agent result display
|
|
96
|
-
SUB_AGENT_RESULT_MAX_LINES =
|
|
96
|
+
SUB_AGENT_RESULT_MAX_LINES = 50
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
# UI refresh rate (frames per second) for debounced content streaming
|
|
@@ -102,9 +102,18 @@ UI_REFRESH_RATE_FPS = 20
|
|
|
102
102
|
# Number of lines to keep visible at bottom of markdown streaming window
|
|
103
103
|
MARKDOWN_STREAM_LIVE_WINDOW = 6
|
|
104
104
|
|
|
105
|
+
# Left margin (columns) to reserve when rendering markdown
|
|
106
|
+
MARKDOWN_LEFT_MARGIN = 2
|
|
107
|
+
|
|
108
|
+
# Right margin (columns) to reserve when rendering markdown
|
|
109
|
+
MARKDOWN_RIGHT_MARGIN = 2
|
|
110
|
+
|
|
105
111
|
# Status hint text shown after spinner status
|
|
106
112
|
STATUS_HINT_TEXT = " (esc to interrupt)"
|
|
107
113
|
|
|
114
|
+
# Default spinner status text when idle/thinking
|
|
115
|
+
STATUS_DEFAULT_TEXT = "Thinking …"
|
|
116
|
+
|
|
108
117
|
# Status shimmer animation
|
|
109
118
|
# Horizontal padding used when computing shimmer band position
|
|
110
119
|
STATUS_SHIMMER_PADDING = 10
|
|
@@ -327,7 +327,7 @@ class ExecutorContext:
|
|
|
327
327
|
log_debug(traceback.format_exc(), style="red", debug_type=DebugType.EXECUTION)
|
|
328
328
|
await self.emit_event(
|
|
329
329
|
events.ErrorEvent(
|
|
330
|
-
error_message=f"Agent task failed: [{e.__class__.__name__}] {e!s}",
|
|
330
|
+
error_message=f"Agent task failed: [{e.__class__.__name__}] {e!s} {traceback.format_exc()}",
|
|
331
331
|
can_retry=False,
|
|
332
332
|
)
|
|
333
333
|
)
|
|
@@ -89,7 +89,7 @@ Only the content passed to `report_back` will be returned to user.\
|
|
|
89
89
|
result = event.task_result
|
|
90
90
|
# Capture TaskMetadataEvent for metadata propagation
|
|
91
91
|
elif isinstance(event, events.TaskMetadataEvent):
|
|
92
|
-
task_metadata = event.metadata.
|
|
92
|
+
task_metadata = event.metadata.main_agent
|
|
93
93
|
await self.emit_event(event)
|
|
94
94
|
return SubAgentResult(
|
|
95
95
|
task_result=result,
|
|
@@ -12,6 +12,7 @@ from klaude_code.core.tool import BashTool, ReadTool, reset_tool_context, set_to
|
|
|
12
12
|
from klaude_code.core.tool.file._utils import hash_text_sha256
|
|
13
13
|
from klaude_code.protocol import model, tools
|
|
14
14
|
from klaude_code.session import Session
|
|
15
|
+
from klaude_code.skill import get_skill
|
|
15
16
|
|
|
16
17
|
type Reminder = Callable[[Session], Awaitable[model.DeveloperMessageItem | None]]
|
|
17
18
|
|
|
@@ -19,6 +20,9 @@ type Reminder = Callable[[Session], Awaitable[model.DeveloperMessageItem | None]
|
|
|
19
20
|
# Match @ preceded by whitespace, start of line, or → (ReadTool line number arrow)
|
|
20
21
|
AT_FILE_PATTERN = re.compile(r'(?:(?<!\S)|(?<=\u2192))@("(?P<quoted>[^\"]+)"|(?P<plain>\S+))')
|
|
21
22
|
|
|
23
|
+
# Match $skill or ¥skill at the beginning of the first line
|
|
24
|
+
SKILL_PATTERN = re.compile(r"^[$¥](?P<skill>\S+)")
|
|
25
|
+
|
|
22
26
|
|
|
23
27
|
def get_last_new_user_input(session: Session) -> str | None:
|
|
24
28
|
"""Get last user input & developer message (CLAUDE.md) from conversation history. if there's a tool result after user input, return None"""
|
|
@@ -73,6 +77,21 @@ def get_at_patterns_with_source(session: Session) -> list[AtPatternSource]:
|
|
|
73
77
|
return patterns
|
|
74
78
|
|
|
75
79
|
|
|
80
|
+
def get_skill_from_user_input(session: Session) -> str | None:
|
|
81
|
+
"""Get $skill reference from the first line of last user input."""
|
|
82
|
+
for item in reversed(session.conversation_history):
|
|
83
|
+
if isinstance(item, model.ToolResultItem):
|
|
84
|
+
return None
|
|
85
|
+
if isinstance(item, model.UserMessageItem):
|
|
86
|
+
content = item.content or ""
|
|
87
|
+
first_line = content.split("\n", 1)[0]
|
|
88
|
+
m = SKILL_PATTERN.match(first_line)
|
|
89
|
+
if m:
|
|
90
|
+
return m.group("skill")
|
|
91
|
+
return None
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
76
95
|
async def _load_at_file_recursive(
|
|
77
96
|
session: Session,
|
|
78
97
|
pattern: str,
|
|
@@ -373,6 +392,36 @@ async def image_reminder(session: Session) -> model.DeveloperMessageItem | None:
|
|
|
373
392
|
)
|
|
374
393
|
|
|
375
394
|
|
|
395
|
+
async def skill_reminder(session: Session) -> model.DeveloperMessageItem | None:
|
|
396
|
+
"""Load skill content when user references a skill with $skill syntax."""
|
|
397
|
+
skill_name = get_skill_from_user_input(session)
|
|
398
|
+
if not skill_name:
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
# Get the skill from skill module
|
|
402
|
+
skill = get_skill(skill_name)
|
|
403
|
+
if not skill:
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
# Get base directory from skill_path
|
|
407
|
+
base_dir = str(skill.skill_path.parent) if skill.skill_path else "unknown"
|
|
408
|
+
|
|
409
|
+
content = f"""<system-reminder>The user activated the "{skill.name}" skill. Here is the skill content:
|
|
410
|
+
|
|
411
|
+
<skill>
|
|
412
|
+
<name>{skill.name}</name>
|
|
413
|
+
<base_dir>{base_dir}</base_dir>
|
|
414
|
+
|
|
415
|
+
{skill.to_prompt()}
|
|
416
|
+
</skill>
|
|
417
|
+
</system-reminder>"""
|
|
418
|
+
|
|
419
|
+
return model.DeveloperMessageItem(
|
|
420
|
+
content=content,
|
|
421
|
+
skill_name=skill.name,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
376
425
|
def _is_memory_loaded(session: Session, path: str) -> bool:
|
|
377
426
|
"""Check if a memory file has already been loaded (tracked with is_memory=True)."""
|
|
378
427
|
status = session.file_tracker.get(path)
|
|
@@ -510,6 +559,7 @@ ALL_REMINDERS = [
|
|
|
510
559
|
last_path_memory_reminder,
|
|
511
560
|
at_file_reader_reminder,
|
|
512
561
|
image_reminder,
|
|
562
|
+
skill_reminder,
|
|
513
563
|
]
|
|
514
564
|
|
|
515
565
|
|
|
@@ -540,6 +590,7 @@ def load_agent_reminders(
|
|
|
540
590
|
last_path_memory_reminder,
|
|
541
591
|
file_changed_externally_reminder,
|
|
542
592
|
image_reminder,
|
|
593
|
+
skill_reminder,
|
|
543
594
|
]
|
|
544
595
|
)
|
|
545
596
|
|
|
@@ -25,7 +25,7 @@ class MetadataAccumulator:
|
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
def __init__(self, model_name: str) -> None:
|
|
28
|
-
self.
|
|
28
|
+
self._main_agent = model.TaskMetadata(model_name=model_name) # Main agent metadata
|
|
29
29
|
self._sub_agent_metadata: list[model.TaskMetadata] = []
|
|
30
30
|
self._throughput_weighted_sum: float = 0.0
|
|
31
31
|
self._throughput_tracked_tokens: int = 0
|
|
@@ -36,13 +36,12 @@ class MetadataAccumulator:
|
|
|
36
36
|
def add(self, turn_metadata: model.ResponseMetadataItem) -> None:
|
|
37
37
|
"""Merge a turn's metadata into the accumulated state."""
|
|
38
38
|
self._turn_count += 1
|
|
39
|
-
main = self._main
|
|
40
39
|
usage = turn_metadata.usage
|
|
41
40
|
|
|
42
41
|
if usage is not None:
|
|
43
|
-
if
|
|
44
|
-
|
|
45
|
-
acc_usage =
|
|
42
|
+
if self._main_agent.usage is None:
|
|
43
|
+
self._main_agent.usage = model.Usage()
|
|
44
|
+
acc_usage = self._main_agent.usage
|
|
46
45
|
|
|
47
46
|
model.TaskMetadata.merge_usage(acc_usage, usage)
|
|
48
47
|
acc_usage.currency = usage.currency
|
|
@@ -63,9 +62,9 @@ class MetadataAccumulator:
|
|
|
63
62
|
self._throughput_tracked_tokens += current_output
|
|
64
63
|
|
|
65
64
|
if turn_metadata.provider is not None:
|
|
66
|
-
|
|
65
|
+
self._main_agent.provider = turn_metadata.provider
|
|
67
66
|
if turn_metadata.model_name:
|
|
68
|
-
|
|
67
|
+
self._main_agent.model_name = turn_metadata.model_name
|
|
69
68
|
|
|
70
69
|
def add_sub_agent_metadata(self, sub_agent_metadata: model.TaskMetadata) -> None:
|
|
71
70
|
"""Add sub-agent task metadata to the accumulated state."""
|
|
@@ -73,21 +72,22 @@ class MetadataAccumulator:
|
|
|
73
72
|
|
|
74
73
|
def finalize(self, task_duration_s: float) -> model.TaskMetadataItem:
|
|
75
74
|
"""Return the final accumulated metadata with computed throughput and duration."""
|
|
76
|
-
|
|
77
|
-
if main.usage is not None:
|
|
75
|
+
if self._main_agent.usage is not None:
|
|
78
76
|
if self._throughput_tracked_tokens > 0:
|
|
79
|
-
|
|
77
|
+
self._main_agent.usage.throughput_tps = self._throughput_weighted_sum / self._throughput_tracked_tokens
|
|
80
78
|
else:
|
|
81
|
-
|
|
79
|
+
self._main_agent.usage.throughput_tps = None
|
|
82
80
|
|
|
83
81
|
if self._first_token_latency_count > 0:
|
|
84
|
-
|
|
82
|
+
self._main_agent.usage.first_token_latency_ms = (
|
|
83
|
+
self._first_token_latency_sum / self._first_token_latency_count
|
|
84
|
+
)
|
|
85
85
|
else:
|
|
86
|
-
|
|
86
|
+
self._main_agent.usage.first_token_latency_ms = None
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return model.TaskMetadataItem(
|
|
88
|
+
self._main_agent.task_duration_s = task_duration_s
|
|
89
|
+
self._main_agent.turn_count = self._turn_count
|
|
90
|
+
return model.TaskMetadataItem(main_agent=self._main_agent, sub_agent_task_metadata=self._sub_agent_metadata)
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
@dataclass
|
|
@@ -126,17 +126,28 @@ class TaskExecutor:
|
|
|
126
126
|
self._context = context
|
|
127
127
|
self._current_turn: TurnExecutor | None = None
|
|
128
128
|
self._started_at: float = 0.0
|
|
129
|
+
self._metadata_accumulator: MetadataAccumulator | None = None
|
|
129
130
|
|
|
130
131
|
@property
|
|
131
132
|
def current_turn(self) -> TurnExecutor | None:
|
|
132
133
|
return self._current_turn
|
|
133
134
|
|
|
134
135
|
def cancel(self) -> list[events.Event]:
|
|
135
|
-
"""Cancel the current turn and return any resulting events."""
|
|
136
|
+
"""Cancel the current turn and return any resulting events including metadata."""
|
|
136
137
|
ui_events: list[events.Event] = []
|
|
137
138
|
if self._current_turn is not None:
|
|
138
139
|
ui_events.extend(self._current_turn.cancel())
|
|
139
140
|
self._current_turn = None
|
|
141
|
+
|
|
142
|
+
# Emit partial metadata on cancellation
|
|
143
|
+
if self._metadata_accumulator is not None and self._started_at > 0:
|
|
144
|
+
task_duration_s = time.perf_counter() - self._started_at
|
|
145
|
+
accumulated = self._metadata_accumulator.finalize(task_duration_s)
|
|
146
|
+
if accumulated.main_agent.usage is not None:
|
|
147
|
+
session_id = self._context.session_ctx.session_id
|
|
148
|
+
ui_events.append(events.TaskMetadataEvent(metadata=accumulated, session_id=session_id))
|
|
149
|
+
self._context.session_ctx.append_history([accumulated])
|
|
150
|
+
|
|
140
151
|
return ui_events
|
|
141
152
|
|
|
142
153
|
async def run(self, user_input: model.UserInputPayload) -> AsyncGenerator[events.Event]:
|
|
@@ -152,7 +163,8 @@ class TaskExecutor:
|
|
|
152
163
|
del user_input # Persisted by the operation handler before launching the task.
|
|
153
164
|
|
|
154
165
|
profile = ctx.profile
|
|
155
|
-
|
|
166
|
+
self._metadata_accumulator = MetadataAccumulator(model_name=profile.llm_client.model_name)
|
|
167
|
+
metadata_accumulator = self._metadata_accumulator
|
|
156
168
|
|
|
157
169
|
while True:
|
|
158
170
|
# Process reminders at the start of each turn
|
|
@@ -226,6 +238,13 @@ class TaskExecutor:
|
|
|
226
238
|
return
|
|
227
239
|
|
|
228
240
|
if turn is None or turn.task_finished:
|
|
241
|
+
# Sub-agent with empty result should retry instead of finishing
|
|
242
|
+
if ctx.sub_agent_state is not None and turn is not None and not turn.task_result.strip():
|
|
243
|
+
yield events.ErrorEvent(
|
|
244
|
+
error_message="Sub-agent returned empty result, retrying...",
|
|
245
|
+
can_retry=True,
|
|
246
|
+
)
|
|
247
|
+
continue
|
|
229
248
|
break
|
|
230
249
|
|
|
231
250
|
# Finalize metadata
|
|
@@ -3,11 +3,10 @@ from .file.apply_patch_tool import ApplyPatchTool
|
|
|
3
3
|
from .file.edit_tool import EditTool
|
|
4
4
|
from .file.read_tool import ReadTool
|
|
5
5
|
from .file.write_tool import WriteTool
|
|
6
|
-
from .memory.skill_loader import Skill, SkillLoader
|
|
7
|
-
from .memory.skill_tool import SkillTool
|
|
8
6
|
from .report_back_tool import ReportBackTool
|
|
9
7
|
from .shell.bash_tool import BashTool
|
|
10
8
|
from .shell.command_safety import SafetyCheckResult, is_safe_command
|
|
9
|
+
from .skill.skill_tool import SkillTool
|
|
11
10
|
from .sub_agent_tool import SubAgentTool
|
|
12
11
|
from .todo.todo_write_tool import TodoWriteTool
|
|
13
12
|
from .todo.update_plan_tool import UpdatePlanTool
|
|
@@ -40,8 +39,6 @@ __all__ = [
|
|
|
40
39
|
"ReportBackTool",
|
|
41
40
|
"SafetyCheckResult",
|
|
42
41
|
"SimpleTruncationStrategy",
|
|
43
|
-
"Skill",
|
|
44
|
-
"SkillLoader",
|
|
45
42
|
"SkillTool",
|
|
46
43
|
"SubAgentTool",
|
|
47
44
|
"TodoContext",
|
|
@@ -25,6 +25,18 @@ _IMAGE_MIME_TYPES: dict[str, str] = {
|
|
|
25
25
|
".webp": "image/webp",
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
_BINARY_CHECK_SIZE = 8192
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _is_binary_file(file_path: str) -> bool:
|
|
32
|
+
"""Check if a file is binary by looking for null bytes in the first chunk."""
|
|
33
|
+
try:
|
|
34
|
+
with open(file_path, "rb") as f:
|
|
35
|
+
chunk = f.read(_BINARY_CHECK_SIZE)
|
|
36
|
+
return b"\x00" in chunk
|
|
37
|
+
except OSError:
|
|
38
|
+
return False
|
|
39
|
+
|
|
28
40
|
|
|
29
41
|
def _format_numbered_line(line_no: int, content: str) -> str:
|
|
30
42
|
# 6-width right-aligned line number followed by a right arrow
|
|
@@ -218,12 +230,22 @@ class ReadTool(ToolABC):
|
|
|
218
230
|
),
|
|
219
231
|
)
|
|
220
232
|
|
|
233
|
+
is_image_file = _is_supported_image_file(file_path)
|
|
234
|
+
# Check for binary files (skip for images which are handled separately)
|
|
235
|
+
if not is_image_file and _is_binary_file(file_path):
|
|
236
|
+
return model.ToolResultItem(
|
|
237
|
+
status="error",
|
|
238
|
+
output=(
|
|
239
|
+
"<tool_use_error>This appears to be a binary file and cannot be read as text. "
|
|
240
|
+
"Use appropriate tools or libraries to handle binary files.</tool_use_error>"
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
|
|
221
244
|
try:
|
|
222
245
|
size_bytes = Path(file_path).stat().st_size
|
|
223
246
|
except OSError:
|
|
224
247
|
size_bytes = 0
|
|
225
248
|
|
|
226
|
-
is_image_file = _is_supported_image_file(file_path)
|
|
227
249
|
if is_image_file:
|
|
228
250
|
if size_bytes > const.READ_MAX_IMAGE_BYTES:
|
|
229
251
|
size_mb = size_bytes / (1024 * 1024)
|
|
@@ -124,9 +124,13 @@ class WriteTool(ToolABC):
|
|
|
124
124
|
is_memory=is_mem,
|
|
125
125
|
)
|
|
126
126
|
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
ui_extra
|
|
127
|
+
# For markdown files, use MarkdownDocUIExtra to render content as markdown
|
|
128
|
+
# Otherwise, build diff between previous and new content
|
|
129
|
+
ui_extra: model.ToolResultUIExtra | None
|
|
130
|
+
if file_path.endswith(".md"):
|
|
131
|
+
ui_extra = model.MarkdownDocUIExtra(file_path=file_path, content=args.content)
|
|
132
|
+
else:
|
|
133
|
+
ui_extra = build_structured_diff(before, args.content, file_path=file_path)
|
|
130
134
|
|
|
131
135
|
message = f"File {'overwritten' if exists else 'created'} successfully at: {file_path}"
|
|
132
136
|
return model.ToolResultItem(status="success", output=message, ui_extra=ui_extra)
|
|
@@ -1,38 +1,22 @@
|
|
|
1
|
+
"""SkillTool - Tool for agent to activate and load skills."""
|
|
2
|
+
|
|
1
3
|
from pathlib import Path
|
|
2
4
|
|
|
3
5
|
from pydantic import BaseModel
|
|
4
6
|
|
|
5
|
-
from klaude_code.core.tool.memory.skill_loader import SkillLoader
|
|
6
7
|
from klaude_code.core.tool.tool_abc import ToolABC, load_desc
|
|
7
8
|
from klaude_code.core.tool.tool_registry import register
|
|
8
9
|
from klaude_code.protocol import llm_param, model, tools
|
|
10
|
+
from klaude_code.skill import get_available_skills, get_skill, list_skill_names
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
@register(tools.SKILL)
|
|
12
14
|
class SkillTool(ToolABC):
|
|
13
|
-
"""Tool to execute/load a skill within the main conversation"""
|
|
14
|
-
|
|
15
|
-
_skill_loader: SkillLoader | None = None
|
|
16
|
-
_discovery_done: bool = False
|
|
17
|
-
|
|
18
|
-
@classmethod
|
|
19
|
-
def set_skill_loader(cls, loader: SkillLoader) -> None:
|
|
20
|
-
"""Set the skill loader instance"""
|
|
21
|
-
cls._skill_loader = loader
|
|
22
|
-
cls._discovery_done = False
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def _ensure_skills_discovered(cls) -> None:
|
|
26
|
-
if cls._discovery_done:
|
|
27
|
-
return
|
|
28
|
-
if cls._skill_loader is not None:
|
|
29
|
-
cls._skill_loader.discover_skills()
|
|
30
|
-
cls._discovery_done = True
|
|
15
|
+
"""Tool to execute/load a skill within the main conversation."""
|
|
31
16
|
|
|
32
17
|
@classmethod
|
|
33
18
|
def schema(cls) -> llm_param.ToolSchema:
|
|
34
|
-
"""Generate schema with embedded available skills metadata"""
|
|
35
|
-
cls._ensure_skills_discovered()
|
|
19
|
+
"""Generate schema with embedded available skills metadata."""
|
|
36
20
|
skills_xml = cls._generate_skills_xml()
|
|
37
21
|
|
|
38
22
|
return llm_param.ToolSchema(
|
|
@@ -53,16 +37,17 @@ class SkillTool(ToolABC):
|
|
|
53
37
|
|
|
54
38
|
@classmethod
|
|
55
39
|
def _generate_skills_xml(cls) -> str:
|
|
56
|
-
"""Generate XML format skills metadata"""
|
|
57
|
-
|
|
40
|
+
"""Generate XML format skills metadata."""
|
|
41
|
+
skills = get_available_skills()
|
|
42
|
+
if not skills:
|
|
58
43
|
return ""
|
|
59
44
|
|
|
60
45
|
xml_parts: list[str] = []
|
|
61
|
-
for
|
|
46
|
+
for name, description, location in skills:
|
|
62
47
|
xml_parts.append(f"""<skill>
|
|
63
|
-
<name>{
|
|
64
|
-
<description>{
|
|
65
|
-
<location>{
|
|
48
|
+
<name>{name}</name>
|
|
49
|
+
<description>{description}</description>
|
|
50
|
+
<location>{location}</location>
|
|
66
51
|
</skill>""")
|
|
67
52
|
return "\n".join(xml_parts)
|
|
68
53
|
|
|
@@ -71,7 +56,7 @@ class SkillTool(ToolABC):
|
|
|
71
56
|
|
|
72
57
|
@classmethod
|
|
73
58
|
async def call(cls, arguments: str) -> model.ToolResultItem:
|
|
74
|
-
"""Load and return full skill content"""
|
|
59
|
+
"""Load and return full skill content."""
|
|
75
60
|
try:
|
|
76
61
|
args = cls.SkillArguments.model_validate_json(arguments)
|
|
77
62
|
except ValueError as e:
|
|
@@ -80,18 +65,10 @@ class SkillTool(ToolABC):
|
|
|
80
65
|
output=f"Invalid arguments: {e}",
|
|
81
66
|
)
|
|
82
67
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if not cls._skill_loader:
|
|
86
|
-
return model.ToolResultItem(
|
|
87
|
-
status="error",
|
|
88
|
-
output="Skill loader not initialized",
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
skill = cls._skill_loader.get_skill(args.command)
|
|
68
|
+
skill = get_skill(args.command)
|
|
92
69
|
|
|
93
70
|
if not skill:
|
|
94
|
-
available = ", ".join(
|
|
71
|
+
available = ", ".join(list_skill_names())
|
|
95
72
|
return model.ToolResultItem(
|
|
96
73
|
status="error",
|
|
97
74
|
output=f"Skill '{args.command}' does not exist. Available skills: {available}",
|
|
@@ -101,7 +78,7 @@ class SkillTool(ToolABC):
|
|
|
101
78
|
base_dir = str(skill.skill_path.parent) if skill.skill_path else "unknown"
|
|
102
79
|
|
|
103
80
|
# Return with loading message format
|
|
104
|
-
result = f"""<command-message>The "{skill.name}" skill is
|
|
81
|
+
result = f"""<command-message>The "{skill.name}" skill is activated</command-message>
|
|
105
82
|
<command-name>{skill.name}</command-name>
|
|
106
83
|
|
|
107
84
|
Base directory for this skill: {base_dir}
|