klaude-code 1.2.6__py3-none-any.whl → 1.8.0__py3-none-any.whl
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/auth/__init__.py +24 -0
- klaude_code/auth/codex/__init__.py +20 -0
- klaude_code/auth/codex/exceptions.py +17 -0
- klaude_code/auth/codex/jwt_utils.py +45 -0
- klaude_code/auth/codex/oauth.py +229 -0
- klaude_code/auth/codex/token_manager.py +84 -0
- klaude_code/cli/auth_cmd.py +73 -0
- klaude_code/cli/config_cmd.py +91 -0
- klaude_code/cli/cost_cmd.py +338 -0
- klaude_code/cli/debug.py +78 -0
- klaude_code/cli/list_model.py +307 -0
- klaude_code/cli/main.py +233 -134
- klaude_code/cli/runtime.py +309 -117
- klaude_code/{version.py → cli/self_update.py} +114 -5
- klaude_code/cli/session_cmd.py +37 -21
- klaude_code/command/__init__.py +88 -27
- klaude_code/command/clear_cmd.py +8 -7
- klaude_code/command/command_abc.py +31 -31
- klaude_code/command/debug_cmd.py +79 -0
- klaude_code/command/export_cmd.py +19 -53
- klaude_code/command/export_online_cmd.py +154 -0
- klaude_code/command/fork_session_cmd.py +267 -0
- klaude_code/command/help_cmd.py +7 -8
- klaude_code/command/model_cmd.py +60 -10
- klaude_code/command/model_select.py +84 -0
- klaude_code/command/prompt-jj-describe.md +32 -0
- klaude_code/command/prompt_command.py +19 -11
- klaude_code/command/refresh_cmd.py +8 -10
- klaude_code/command/registry.py +139 -40
- klaude_code/command/release_notes_cmd.py +84 -0
- klaude_code/command/resume_cmd.py +111 -0
- klaude_code/command/status_cmd.py +104 -60
- klaude_code/command/terminal_setup_cmd.py +7 -9
- klaude_code/command/thinking_cmd.py +98 -0
- klaude_code/config/__init__.py +14 -6
- klaude_code/config/assets/__init__.py +1 -0
- klaude_code/config/assets/builtin_config.yaml +303 -0
- klaude_code/config/builtin_config.py +38 -0
- klaude_code/config/config.py +378 -109
- klaude_code/config/select_model.py +117 -53
- klaude_code/config/thinking.py +269 -0
- klaude_code/{const/__init__.py → const.py} +50 -19
- klaude_code/core/agent.py +20 -28
- klaude_code/core/executor.py +327 -112
- klaude_code/core/manager/__init__.py +2 -4
- klaude_code/core/manager/llm_clients.py +1 -15
- klaude_code/core/manager/llm_clients_builder.py +10 -11
- klaude_code/core/manager/sub_agent_manager.py +37 -6
- klaude_code/core/prompt.py +63 -44
- klaude_code/core/prompts/prompt-claude-code.md +2 -13
- klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +117 -0
- klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
- klaude_code/core/prompts/prompt-codex.md +9 -42
- klaude_code/core/prompts/prompt-minimal.md +12 -0
- klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +16 -3
- klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -2
- klaude_code/core/prompts/prompt-sub-agent-web.md +51 -0
- klaude_code/core/reminders.py +283 -95
- klaude_code/core/task.py +113 -75
- klaude_code/core/tool/__init__.py +24 -31
- klaude_code/core/tool/file/_utils.py +36 -0
- klaude_code/core/tool/file/apply_patch.py +17 -25
- klaude_code/core/tool/file/apply_patch_tool.py +57 -77
- klaude_code/core/tool/file/diff_builder.py +151 -0
- klaude_code/core/tool/file/edit_tool.py +50 -63
- klaude_code/core/tool/file/move_tool.md +41 -0
- klaude_code/core/tool/file/move_tool.py +435 -0
- klaude_code/core/tool/file/read_tool.md +1 -1
- klaude_code/core/tool/file/read_tool.py +86 -86
- klaude_code/core/tool/file/write_tool.py +59 -69
- klaude_code/core/tool/report_back_tool.py +84 -0
- klaude_code/core/tool/shell/bash_tool.py +265 -22
- klaude_code/core/tool/shell/command_safety.py +3 -6
- klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -26
- klaude_code/core/tool/sub_agent_tool.py +13 -2
- klaude_code/core/tool/todo/todo_write_tool.md +0 -157
- klaude_code/core/tool/todo/todo_write_tool.py +1 -1
- klaude_code/core/tool/todo/todo_write_tool_raw.md +182 -0
- klaude_code/core/tool/todo/update_plan_tool.py +1 -1
- klaude_code/core/tool/tool_abc.py +18 -0
- klaude_code/core/tool/tool_context.py +27 -12
- klaude_code/core/tool/tool_registry.py +7 -7
- klaude_code/core/tool/tool_runner.py +44 -36
- klaude_code/core/tool/truncation.py +29 -14
- klaude_code/core/tool/web/mermaid_tool.md +43 -0
- klaude_code/core/tool/web/mermaid_tool.py +2 -5
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +112 -22
- klaude_code/core/tool/web/web_search_tool.md +23 -0
- klaude_code/core/tool/web/web_search_tool.py +130 -0
- klaude_code/core/turn.py +168 -66
- klaude_code/llm/__init__.py +2 -10
- klaude_code/llm/anthropic/client.py +190 -178
- klaude_code/llm/anthropic/input.py +39 -15
- klaude_code/llm/bedrock/__init__.py +3 -0
- klaude_code/llm/bedrock/client.py +60 -0
- klaude_code/llm/client.py +7 -21
- klaude_code/llm/codex/__init__.py +5 -0
- klaude_code/llm/codex/client.py +149 -0
- klaude_code/llm/google/__init__.py +3 -0
- klaude_code/llm/google/client.py +309 -0
- klaude_code/llm/google/input.py +215 -0
- klaude_code/llm/input_common.py +3 -9
- klaude_code/llm/openai_compatible/client.py +72 -164
- klaude_code/llm/openai_compatible/input.py +6 -4
- klaude_code/llm/openai_compatible/stream.py +273 -0
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +17 -1
- klaude_code/llm/openrouter/client.py +89 -160
- klaude_code/llm/openrouter/input.py +18 -30
- klaude_code/llm/openrouter/reasoning.py +118 -0
- klaude_code/llm/registry.py +39 -7
- klaude_code/llm/responses/client.py +184 -171
- klaude_code/llm/responses/input.py +20 -1
- klaude_code/llm/usage.py +17 -12
- klaude_code/protocol/commands.py +17 -1
- klaude_code/protocol/events.py +31 -4
- klaude_code/protocol/llm_param.py +13 -10
- klaude_code/protocol/model.py +232 -29
- klaude_code/protocol/op.py +90 -1
- klaude_code/protocol/op_handler.py +35 -1
- klaude_code/protocol/sub_agent/__init__.py +117 -0
- klaude_code/protocol/sub_agent/explore.py +63 -0
- klaude_code/protocol/sub_agent/oracle.py +91 -0
- klaude_code/protocol/sub_agent/task.py +61 -0
- klaude_code/protocol/sub_agent/web.py +79 -0
- klaude_code/protocol/tools.py +4 -2
- klaude_code/session/__init__.py +2 -2
- klaude_code/session/codec.py +71 -0
- klaude_code/session/export.py +293 -86
- klaude_code/session/selector.py +89 -67
- klaude_code/session/session.py +320 -309
- klaude_code/session/store.py +220 -0
- klaude_code/session/templates/export_session.html +595 -83
- klaude_code/session/templates/mermaid_viewer.html +926 -0
- klaude_code/skill/__init__.py +27 -0
- klaude_code/skill/assets/deslop/SKILL.md +17 -0
- klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
- klaude_code/skill/assets/handoff/SKILL.md +39 -0
- klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
- klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
- klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +55 -15
- klaude_code/skill/manager.py +70 -0
- klaude_code/skill/system_skills.py +192 -0
- klaude_code/trace/__init__.py +20 -2
- klaude_code/trace/log.py +150 -5
- klaude_code/ui/__init__.py +4 -9
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +7 -7
- klaude_code/ui/modes/debug/display.py +2 -1
- klaude_code/ui/modes/repl/__init__.py +3 -48
- klaude_code/ui/modes/repl/clipboard.py +5 -5
- klaude_code/ui/modes/repl/completers.py +487 -123
- klaude_code/ui/modes/repl/display.py +5 -4
- klaude_code/ui/modes/repl/event_handler.py +370 -117
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +552 -105
- klaude_code/ui/modes/repl/key_bindings.py +146 -23
- klaude_code/ui/modes/repl/renderer.py +189 -99
- klaude_code/ui/renderers/assistant.py +9 -2
- klaude_code/ui/renderers/bash_syntax.py +178 -0
- klaude_code/ui/renderers/common.py +78 -0
- klaude_code/ui/renderers/developer.py +104 -48
- klaude_code/ui/renderers/diffs.py +87 -6
- klaude_code/ui/renderers/errors.py +11 -6
- klaude_code/ui/renderers/mermaid_viewer.py +57 -0
- klaude_code/ui/renderers/metadata.py +112 -76
- klaude_code/ui/renderers/sub_agent.py +92 -7
- klaude_code/ui/renderers/thinking.py +40 -18
- klaude_code/ui/renderers/tools.py +405 -227
- klaude_code/ui/renderers/user_input.py +73 -13
- klaude_code/ui/rich/__init__.py +10 -1
- klaude_code/ui/rich/cjk_wrap.py +228 -0
- klaude_code/ui/rich/code_panel.py +131 -0
- klaude_code/ui/rich/live.py +17 -0
- klaude_code/ui/rich/markdown.py +305 -170
- klaude_code/ui/rich/searchable_text.py +10 -13
- klaude_code/ui/rich/status.py +190 -49
- klaude_code/ui/rich/theme.py +135 -39
- klaude_code/ui/terminal/__init__.py +55 -0
- klaude_code/ui/terminal/color.py +1 -1
- klaude_code/ui/terminal/control.py +13 -22
- klaude_code/ui/terminal/notifier.py +44 -4
- klaude_code/ui/terminal/selector.py +658 -0
- klaude_code/ui/utils/common.py +0 -18
- klaude_code-1.8.0.dist-info/METADATA +377 -0
- klaude_code-1.8.0.dist-info/RECORD +219 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/entry_points.txt +1 -0
- klaude_code/command/diff_cmd.py +0 -138
- klaude_code/command/prompt-dev-docs-update.md +0 -56
- klaude_code/command/prompt-dev-docs.md +0 -46
- klaude_code/config/list_model.py +0 -162
- klaude_code/core/manager/agent_manager.py +0 -127
- klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -46
- klaude_code/core/tool/file/multi_edit_tool.md +0 -42
- klaude_code/core/tool/file/multi_edit_tool.py +0 -199
- klaude_code/core/tool/memory/memory_tool.md +0 -16
- klaude_code/core/tool/memory/memory_tool.py +0 -462
- klaude_code/llm/openrouter/reasoning_handler.py +0 -209
- klaude_code/protocol/sub_agent.py +0 -348
- klaude_code/ui/utils/debouncer.py +0 -42
- klaude_code-1.2.6.dist-info/METADATA +0 -178
- klaude_code-1.2.6.dist-info/RECORD +0 -167
- /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
- /klaude_code/core/tool/{memory → skill}/__init__.py +0 -0
- /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/WHEEL +0 -0
klaude_code/cli/main.py
CHANGED
|
@@ -1,117 +1,124 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import os
|
|
3
|
-
import subprocess
|
|
4
4
|
import sys
|
|
5
|
-
import
|
|
6
|
-
from importlib.metadata import version as pkg_version
|
|
5
|
+
from pathlib import Path
|
|
7
6
|
|
|
8
7
|
import typer
|
|
9
8
|
|
|
10
|
-
from klaude_code.cli.
|
|
9
|
+
from klaude_code.cli.auth_cmd import register_auth_commands
|
|
10
|
+
from klaude_code.cli.config_cmd import register_config_commands
|
|
11
|
+
from klaude_code.cli.cost_cmd import register_cost_commands
|
|
12
|
+
from klaude_code.cli.debug import DEBUG_FILTER_HELP, open_log_file_in_editor, resolve_debug_settings
|
|
13
|
+
from klaude_code.cli.self_update import register_self_update_commands, version_option_callback
|
|
11
14
|
from klaude_code.cli.session_cmd import register_session_commands
|
|
12
|
-
from klaude_code.
|
|
13
|
-
from klaude_code.session import Session
|
|
14
|
-
from klaude_code.trace import
|
|
15
|
-
from klaude_code.ui.terminal.color import is_light_terminal_background
|
|
15
|
+
from klaude_code.command.resume_cmd import select_session_sync
|
|
16
|
+
from klaude_code.session import Session
|
|
17
|
+
from klaude_code.trace import DebugType, prepare_debug_log_file
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
def set_terminal_title(title: str) -> None:
|
|
19
21
|
"""Set terminal window title using ANSI escape sequence."""
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
# Never write terminal control sequences when stdout is not a TTY (pipes/CI/redirects).
|
|
23
|
+
# This avoids corrupting machine-readable output (e.g., JSON streaming) and log captures.
|
|
24
|
+
#
|
|
25
|
+
# Use the original stdout to bypass prompt_toolkit's `patch_stdout()`. Writing OSC
|
|
26
|
+
# sequences to the patched stdout can cause them to appear as visible text.
|
|
27
|
+
stream = getattr(sys, "__stdout__", None) or sys.stdout
|
|
28
|
+
try:
|
|
29
|
+
if not stream.isatty():
|
|
30
|
+
return
|
|
31
|
+
except Exception:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
stream.write(f"\033]0;{title}\007")
|
|
35
|
+
with contextlib.suppress(Exception):
|
|
36
|
+
stream.flush()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def update_terminal_title(model_name: str | None = None) -> None:
|
|
40
|
+
"""Update terminal title with folder name and optional model name."""
|
|
41
|
+
folder_name = os.path.basename(os.getcwd())
|
|
42
|
+
if model_name:
|
|
43
|
+
set_terminal_title(f"{folder_name}: klaude ✳ {model_name}")
|
|
44
|
+
else:
|
|
45
|
+
set_terminal_title(f"{folder_name}: klaude")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def prepare_debug_logging(debug: bool, debug_filter: str | None) -> tuple[bool, set[DebugType] | None, Path | None]:
|
|
49
|
+
"""Resolve debug settings and prepare log file if debugging is enabled.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
A tuple of (debug_enabled, debug_filters, log_path).
|
|
53
|
+
log_path is None if debugging is disabled.
|
|
54
|
+
"""
|
|
55
|
+
debug_enabled, debug_filters = resolve_debug_settings(debug, debug_filter)
|
|
56
|
+
log_path: Path | None = None
|
|
57
|
+
if debug_enabled:
|
|
58
|
+
log_path = prepare_debug_log_file()
|
|
59
|
+
return debug_enabled, debug_filters, log_path
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def read_input_content(cli_argument: str) -> str | None:
|
|
63
|
+
"""Read and merge input from stdin and CLI argument.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
cli_argument: The input content passed as CLI argument.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The merged input content, or None if no input was provided.
|
|
70
|
+
"""
|
|
71
|
+
from klaude_code.trace import log
|
|
22
72
|
|
|
73
|
+
parts: list[str] = []
|
|
23
74
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if value:
|
|
75
|
+
# Handle stdin input
|
|
76
|
+
if not sys.stdin.isatty():
|
|
27
77
|
try:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
78
|
+
stdin = sys.stdin.read().rstrip("\n")
|
|
79
|
+
if stdin:
|
|
80
|
+
parts.append(stdin)
|
|
81
|
+
except (OSError, ValueError) as e:
|
|
82
|
+
# Expected I/O-related errors when reading from stdin (e.g. broken pipe, closed stream).
|
|
83
|
+
log((f"Error reading from stdin: {e}", "red"))
|
|
84
|
+
except Exception as e:
|
|
85
|
+
# Unexpected errors are still reported but kept from crashing the CLI.
|
|
86
|
+
log((f"Unexpected error reading from stdin: {e}", "red"))
|
|
87
|
+
|
|
88
|
+
if cli_argument:
|
|
89
|
+
parts.append(cli_argument)
|
|
90
|
+
|
|
91
|
+
content = "\n".join(parts)
|
|
92
|
+
if len(content) == 0:
|
|
93
|
+
log(("Error: No input content provided", "red"))
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
return content
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
ENV_HELP = """\
|
|
100
|
+
Environment Variables:
|
|
101
|
+
|
|
102
|
+
KLAUDE_READ_GLOBAL_LINE_CAP Max lines to read (default: 2000)
|
|
33
103
|
|
|
104
|
+
KLAUDE_READ_MAX_CHARS Max total chars to read (default: 50000)
|
|
105
|
+
"""
|
|
34
106
|
|
|
35
107
|
app = typer.Typer(
|
|
36
108
|
add_completion=False,
|
|
37
109
|
pretty_exceptions_enable=False,
|
|
38
110
|
no_args_is_help=False,
|
|
111
|
+
rich_markup_mode="rich",
|
|
112
|
+
epilog=ENV_HELP,
|
|
39
113
|
)
|
|
40
114
|
|
|
41
|
-
|
|
42
|
-
register_session_commands(
|
|
43
|
-
app
|
|
115
|
+
# Register subcommands from modules
|
|
116
|
+
register_session_commands(app)
|
|
117
|
+
register_auth_commands(app)
|
|
118
|
+
register_config_commands(app)
|
|
119
|
+
register_cost_commands(app)
|
|
44
120
|
|
|
45
|
-
|
|
46
|
-
@app.command("list")
|
|
47
|
-
def list_models() -> None:
|
|
48
|
-
"""List all models and providers configuration"""
|
|
49
|
-
config = load_config()
|
|
50
|
-
if config is None:
|
|
51
|
-
raise typer.Exit(1)
|
|
52
|
-
|
|
53
|
-
# Auto-detect theme when not explicitly set in config, to match other CLI entrypoints.
|
|
54
|
-
if config.theme is None:
|
|
55
|
-
detected = is_light_terminal_background()
|
|
56
|
-
if detected is True:
|
|
57
|
-
config.theme = "light"
|
|
58
|
-
elif detected is False:
|
|
59
|
-
config.theme = "dark"
|
|
60
|
-
|
|
61
|
-
display_models_and_providers(config)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@app.command("config")
|
|
65
|
-
@app.command("conf", hidden=True)
|
|
66
|
-
def edit_config() -> None:
|
|
67
|
-
"""Open the configuration file in $EDITOR or default system editor"""
|
|
68
|
-
editor = os.environ.get("EDITOR")
|
|
69
|
-
|
|
70
|
-
# If no EDITOR is set, prioritize TextEdit on macOS
|
|
71
|
-
if not editor:
|
|
72
|
-
# Try common editors in order of preference on other platforms
|
|
73
|
-
for cmd in [
|
|
74
|
-
"code",
|
|
75
|
-
"nvim",
|
|
76
|
-
"vim",
|
|
77
|
-
"nano",
|
|
78
|
-
]:
|
|
79
|
-
try:
|
|
80
|
-
subprocess.run(["which", cmd], check=True, capture_output=True)
|
|
81
|
-
editor = cmd
|
|
82
|
-
break
|
|
83
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
# If no editor found, try platform-specific defaults
|
|
87
|
-
if not editor:
|
|
88
|
-
if sys.platform == "darwin": # macOS
|
|
89
|
-
editor = "open"
|
|
90
|
-
elif sys.platform == "win32": # Windows
|
|
91
|
-
editor = "notepad"
|
|
92
|
-
else: # Linux and other Unix systems
|
|
93
|
-
editor = "xdg-open"
|
|
94
|
-
|
|
95
|
-
# Ensure config file exists
|
|
96
|
-
config = load_config()
|
|
97
|
-
if config is None:
|
|
98
|
-
raise typer.Exit(1)
|
|
99
|
-
|
|
100
|
-
try:
|
|
101
|
-
if editor == "open -a TextEdit":
|
|
102
|
-
subprocess.run(["open", "-a", "TextEdit", str(config_path)], check=True)
|
|
103
|
-
elif editor in ["open", "xdg-open"]:
|
|
104
|
-
# For open/xdg-open, we need to pass the file directly
|
|
105
|
-
subprocess.run([editor, str(config_path)], check=True)
|
|
106
|
-
else:
|
|
107
|
-
subprocess.run([editor, str(config_path)], check=True)
|
|
108
|
-
except subprocess.CalledProcessError as e:
|
|
109
|
-
log((f"Error: Failed to open editor: {e}", "red"))
|
|
110
|
-
raise typer.Exit(1)
|
|
111
|
-
except FileNotFoundError:
|
|
112
|
-
log((f"Error: Editor '{editor}' not found", "red"))
|
|
113
|
-
log("Please install a text editor or set your $EDITOR environment variable")
|
|
114
|
-
raise typer.Exit(1)
|
|
121
|
+
register_self_update_commands(app)
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
@app.command("exec")
|
|
@@ -156,42 +163,37 @@ def exec_command(
|
|
|
156
163
|
),
|
|
157
164
|
) -> None:
|
|
158
165
|
"""Execute non-interactively with provided input."""
|
|
166
|
+
update_terminal_title()
|
|
159
167
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
set_terminal_title(f"{folder_name}: klaude")
|
|
163
|
-
|
|
164
|
-
parts: list[str] = []
|
|
165
|
-
|
|
166
|
-
# Handle stdin input
|
|
167
|
-
if not sys.stdin.isatty():
|
|
168
|
-
try:
|
|
169
|
-
stdin = sys.stdin.read().rstrip("\n")
|
|
170
|
-
if stdin:
|
|
171
|
-
parts.append(stdin)
|
|
172
|
-
except Exception as e:
|
|
173
|
-
log((f"Error reading from stdin: {e}", "red"))
|
|
174
|
-
|
|
175
|
-
if input_content:
|
|
176
|
-
parts.append(input_content)
|
|
177
|
-
|
|
178
|
-
input_content = "\n".join(parts)
|
|
179
|
-
if len(input_content) == 0:
|
|
180
|
-
log(("Error: No input content provided", "red"))
|
|
168
|
+
merged_input = read_input_content(input_content)
|
|
169
|
+
if merged_input is None:
|
|
181
170
|
raise typer.Exit(1)
|
|
182
171
|
|
|
172
|
+
from klaude_code.cli.runtime import AppInitConfig, run_exec
|
|
173
|
+
from klaude_code.command.model_select import select_model_interactive
|
|
174
|
+
from klaude_code.config import load_config
|
|
175
|
+
|
|
183
176
|
chosen_model = model
|
|
184
|
-
if select_model:
|
|
185
|
-
|
|
186
|
-
config = load_config()
|
|
187
|
-
if config is None:
|
|
188
|
-
raise typer.Exit(1)
|
|
189
|
-
default_name = model or config.main_model
|
|
190
|
-
chosen_model = select_model_from_config(preferred=default_name)
|
|
177
|
+
if model or select_model:
|
|
178
|
+
chosen_model = select_model_interactive(preferred=model)
|
|
191
179
|
if chosen_model is None:
|
|
192
|
-
|
|
180
|
+
raise typer.Exit(1)
|
|
181
|
+
else:
|
|
182
|
+
# Check if main_model is configured; if not, trigger interactive selection
|
|
183
|
+
config = load_config()
|
|
184
|
+
if config.main_model is None:
|
|
185
|
+
chosen_model = select_model_interactive()
|
|
186
|
+
if chosen_model is None:
|
|
187
|
+
raise typer.Exit(1)
|
|
188
|
+
# Save the selection as default
|
|
189
|
+
config.main_model = chosen_model
|
|
190
|
+
from klaude_code.config.config import config_path
|
|
191
|
+
from klaude_code.trace import log
|
|
193
192
|
|
|
194
|
-
|
|
193
|
+
asyncio.run(config.save())
|
|
194
|
+
log(f"Saved main_model={chosen_model} to {config_path}", style="cyan")
|
|
195
|
+
|
|
196
|
+
debug_enabled, debug_filters, log_path = prepare_debug_logging(debug, debug_filter)
|
|
195
197
|
|
|
196
198
|
init_config = AppInitConfig(
|
|
197
199
|
model=chosen_model,
|
|
@@ -202,10 +204,13 @@ def exec_command(
|
|
|
202
204
|
stream_json=stream_json,
|
|
203
205
|
)
|
|
204
206
|
|
|
207
|
+
if log_path:
|
|
208
|
+
open_log_file_in_editor(log_path)
|
|
209
|
+
|
|
205
210
|
asyncio.run(
|
|
206
211
|
run_exec(
|
|
207
212
|
init_config=init_config,
|
|
208
|
-
input_content=
|
|
213
|
+
input_content=merged_input,
|
|
209
214
|
)
|
|
210
215
|
)
|
|
211
216
|
|
|
@@ -217,8 +222,9 @@ def main_callback(
|
|
|
217
222
|
False,
|
|
218
223
|
"--version",
|
|
219
224
|
"-V",
|
|
225
|
+
"-v",
|
|
220
226
|
help="Show version and exit",
|
|
221
|
-
callback=
|
|
227
|
+
callback=version_option_callback,
|
|
222
228
|
is_eager=True,
|
|
223
229
|
),
|
|
224
230
|
model: str | None = typer.Option(
|
|
@@ -230,6 +236,11 @@ def main_callback(
|
|
|
230
236
|
),
|
|
231
237
|
continue_: bool = typer.Option(False, "--continue", "-c", help="Continue from latest session"),
|
|
232
238
|
resume: bool = typer.Option(False, "--resume", "-r", help="Select a session to resume for this project"),
|
|
239
|
+
resume_by_id: str | None = typer.Option(
|
|
240
|
+
None,
|
|
241
|
+
"--resume-by-id",
|
|
242
|
+
help="Resume a session by its ID (must exist)",
|
|
243
|
+
),
|
|
233
244
|
select_model: bool = typer.Option(
|
|
234
245
|
False,
|
|
235
246
|
"--select-model",
|
|
@@ -258,30 +269,115 @@ def main_callback(
|
|
|
258
269
|
) -> None:
|
|
259
270
|
# Only run interactive mode when no subcommand is invoked
|
|
260
271
|
if ctx.invoked_subcommand is None:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
272
|
+
from klaude_code.trace import log
|
|
273
|
+
|
|
274
|
+
resume_by_id_value = resume_by_id.strip() if resume_by_id is not None else None
|
|
275
|
+
if resume_by_id_value == "":
|
|
276
|
+
log(("Error: --resume-by-id cannot be empty", "red"))
|
|
277
|
+
raise typer.Exit(2)
|
|
278
|
+
|
|
279
|
+
if resume_by_id_value is not None and (resume or continue_):
|
|
280
|
+
log(("Error: --resume-by-id cannot be combined with --resume/--continue", "red"))
|
|
281
|
+
raise typer.Exit(2)
|
|
282
|
+
|
|
283
|
+
if resume_by_id_value is not None and not Session.exists(resume_by_id_value):
|
|
284
|
+
log((f"Error: session id '{resume_by_id_value}' not found for this project", "red"))
|
|
285
|
+
log(("Hint: run `klaude --resume` to select an existing session", "yellow"))
|
|
286
|
+
raise typer.Exit(2)
|
|
287
|
+
|
|
288
|
+
# In non-interactive environments, default to exec-mode behavior.
|
|
289
|
+
# This allows: echo "..." | klaude
|
|
290
|
+
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
291
|
+
if continue_ or resume or resume_by_id is not None:
|
|
292
|
+
log(("Error: --continue/--resume options require a TTY", "red"))
|
|
293
|
+
log(("Hint: use `klaude exec` for non-interactive usage", "yellow"))
|
|
294
|
+
raise typer.Exit(2)
|
|
295
|
+
|
|
296
|
+
exec_command(
|
|
297
|
+
input_content="",
|
|
298
|
+
model=model,
|
|
299
|
+
select_model=select_model,
|
|
300
|
+
debug=debug,
|
|
301
|
+
debug_filter=debug_filter,
|
|
302
|
+
vanilla=vanilla,
|
|
303
|
+
stream_json=False,
|
|
304
|
+
)
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
from klaude_code.cli.runtime import AppInitConfig, run_interactive
|
|
308
|
+
from klaude_code.command.model_select import select_model_interactive
|
|
309
|
+
|
|
310
|
+
update_terminal_title()
|
|
311
|
+
|
|
265
312
|
chosen_model = model
|
|
266
|
-
if select_model:
|
|
267
|
-
chosen_model =
|
|
313
|
+
if model or select_model:
|
|
314
|
+
chosen_model = select_model_interactive(preferred=model)
|
|
268
315
|
if chosen_model is None:
|
|
269
316
|
return
|
|
270
317
|
|
|
271
318
|
# Resolve session id before entering asyncio loop
|
|
319
|
+
# session_id=None means create a new session
|
|
272
320
|
session_id: str | None = None
|
|
321
|
+
|
|
273
322
|
if resume:
|
|
274
|
-
session_id =
|
|
323
|
+
session_id = select_session_sync()
|
|
275
324
|
if session_id is None:
|
|
276
325
|
return
|
|
277
326
|
# If user didn't pick, allow fallback to --continue
|
|
278
327
|
if session_id is None and continue_:
|
|
279
328
|
session_id = Session.most_recent_session_id()
|
|
280
|
-
# If still no session_id, generate a new one for a new session
|
|
281
|
-
if session_id is None:
|
|
282
|
-
session_id = uuid.uuid4().hex
|
|
283
329
|
|
|
284
|
-
|
|
330
|
+
if resume_by_id_value is not None:
|
|
331
|
+
session_id = resume_by_id_value
|
|
332
|
+
# If still no session_id, leave as None to create a new session
|
|
333
|
+
|
|
334
|
+
if session_id is not None and chosen_model is None:
|
|
335
|
+
from klaude_code.config import load_config
|
|
336
|
+
from klaude_code.trace import log
|
|
337
|
+
|
|
338
|
+
session_meta = Session.load_meta(session_id)
|
|
339
|
+
cfg = load_config()
|
|
340
|
+
|
|
341
|
+
if session_meta.model_config_name:
|
|
342
|
+
if any(m.model_name == session_meta.model_config_name for m in cfg.iter_model_entries()):
|
|
343
|
+
chosen_model = session_meta.model_config_name
|
|
344
|
+
else:
|
|
345
|
+
log(
|
|
346
|
+
(
|
|
347
|
+
f"Warning: session model '{session_meta.model_config_name}' is not defined in config; falling back to default",
|
|
348
|
+
"yellow",
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
if chosen_model is None and session_meta.model_name:
|
|
353
|
+
raw_model = session_meta.model_name.strip()
|
|
354
|
+
if raw_model:
|
|
355
|
+
matches = [
|
|
356
|
+
m.model_name
|
|
357
|
+
for m in cfg.iter_model_entries()
|
|
358
|
+
if (m.model_params.model or "").strip().lower() == raw_model.lower()
|
|
359
|
+
]
|
|
360
|
+
if len(matches) == 1:
|
|
361
|
+
chosen_model = matches[0]
|
|
362
|
+
|
|
363
|
+
# If still no model, check main_model; if not configured, trigger interactive selection
|
|
364
|
+
if chosen_model is None:
|
|
365
|
+
from klaude_code.config import load_config
|
|
366
|
+
|
|
367
|
+
cfg = load_config()
|
|
368
|
+
if cfg.main_model is None:
|
|
369
|
+
chosen_model = select_model_interactive()
|
|
370
|
+
if chosen_model is None:
|
|
371
|
+
raise typer.Exit(1)
|
|
372
|
+
# Save the selection as default
|
|
373
|
+
cfg.main_model = chosen_model
|
|
374
|
+
from klaude_code.config.config import config_path
|
|
375
|
+
from klaude_code.trace import log
|
|
376
|
+
|
|
377
|
+
asyncio.run(cfg.save())
|
|
378
|
+
log(f"Saved main_model={chosen_model} to {config_path}", style="dim")
|
|
379
|
+
|
|
380
|
+
debug_enabled, debug_filters, log_path = prepare_debug_logging(debug, debug_filter)
|
|
285
381
|
|
|
286
382
|
init_config = AppInitConfig(
|
|
287
383
|
model=chosen_model,
|
|
@@ -290,6 +386,9 @@ def main_callback(
|
|
|
290
386
|
debug_filters=debug_filters,
|
|
291
387
|
)
|
|
292
388
|
|
|
389
|
+
if log_path:
|
|
390
|
+
open_log_file_in_editor(log_path)
|
|
391
|
+
|
|
293
392
|
asyncio.run(
|
|
294
393
|
run_interactive(
|
|
295
394
|
init_config=init_config,
|