ripperdoc 0.2.8__tar.gz → 0.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.
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/PKG-INFO +8 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/README.md +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/pyproject.toml +4 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/__init__.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/cli.py +257 -123
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/__init__.py +2 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/agents_cmd.py +138 -8
- ripperdoc-0.2.10/ripperdoc/cli/commands/clear_cmd.py +24 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/config_cmd.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/context_cmd.py +3 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/doctor_cmd.py +18 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/exit_cmd.py +1 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/hooks_cmd.py +27 -53
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/models_cmd.py +27 -10
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/permissions_cmd.py +27 -9
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/resume_cmd.py +9 -3
- ripperdoc-0.2.10/ripperdoc/cli/commands/stats_cmd.py +244 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/status_cmd.py +4 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/tasks_cmd.py +8 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/file_mention_completer.py +2 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/interrupt_handler.py +2 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/message_display.py +4 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/panels.py +1 -0
- ripperdoc-0.2.10/ripperdoc/cli/ui/provider_options.py +247 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/rich_ui.py +403 -81
- ripperdoc-0.2.10/ripperdoc/cli/ui/spinner.py +85 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/thinking_spinner.py +1 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/tool_renderers.py +8 -2
- ripperdoc-0.2.10/ripperdoc/cli/ui/wizard.py +213 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/agents.py +19 -6
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/config.py +51 -17
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/custom_commands.py +7 -6
- ripperdoc-0.2.10/ripperdoc/core/default_tools.py +177 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/config.py +1 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/events.py +27 -28
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/executor.py +4 -6
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/integration.py +12 -21
- ripperdoc-0.2.10/ripperdoc/core/hooks/llm_callback.py +59 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/manager.py +40 -15
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/permissions.py +118 -12
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/providers/anthropic.py +109 -36
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/providers/gemini.py +70 -5
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/providers/openai.py +89 -24
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/query.py +273 -68
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/query_utils.py +2 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/skills.py +9 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/system_prompt.py +4 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/tool.py +17 -8
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/sdk/client.py +79 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/ask_user_question_tool.py +5 -3
- ripperdoc-0.2.10/ripperdoc/tools/background_shell.py +561 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/bash_output_tool.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/bash_tool.py +63 -24
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/dynamic_mcp_tool.py +29 -8
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/enter_plan_mode_tool.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/exit_plan_mode_tool.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/file_edit_tool.py +167 -54
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/file_read_tool.py +28 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/file_write_tool.py +13 -10
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/glob_tool.py +3 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/grep_tool.py +3 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/kill_bash_tool.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/ls_tool.py +1 -1
- ripperdoc-0.2.10/ripperdoc/tools/lsp_tool.py +615 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/mcp_tools.py +13 -10
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/multi_edit_tool.py +8 -7
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/notebook_edit_tool.py +7 -4
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/skill_tool.py +1 -1
- ripperdoc-0.2.10/ripperdoc/tools/task_tool.py +829 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/todo_tool.py +2 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/tool_search_tool.py +3 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/conversation_compaction.py +9 -5
- ripperdoc-0.2.10/ripperdoc/utils/file_watch.py +344 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/json_utils.py +2 -1
- ripperdoc-0.2.10/ripperdoc/utils/lsp.py +806 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/mcp.py +11 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/memory.py +4 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/message_compaction.py +21 -7
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/message_formatting.py +14 -7
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/messages.py +126 -67
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/path_ignore.py +35 -8
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/permissions/path_validation_utils.py +2 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/permissions/shell_command_validation.py +427 -91
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/permissions/tool_permission_utils.py +174 -15
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/safe_get_cwd.py +2 -1
- ripperdoc-0.2.10/ripperdoc/utils/session_heatmap.py +244 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/session_history.py +13 -6
- ripperdoc-0.2.10/ripperdoc/utils/session_stats.py +293 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/todo.py +2 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/token_estimation.py +6 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/PKG-INFO +8 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/SOURCES.txt +11 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/requires.txt +9 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_background_shell_shutdown.py +7 -7
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_cli_commands.py +8 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_compact.py +2 -6
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_config.py +0 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_custom_commands.py +1 -5
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_file_mention_completer.py +7 -13
- ripperdoc-0.2.10/tests/test_hooks.py +2080 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_path_ignore.py +3 -0
- ripperdoc-0.2.10/tests/test_permissions.py +250 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_query_abort.py +3 -3
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_sdk.py +2 -2
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_shell_permissions.py +312 -20
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_tool_search.py +1 -1
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_tools.py +1 -1
- ripperdoc-0.2.10/tests/test_utils.py +1097 -0
- ripperdoc-0.2.8/ripperdoc/cli/commands/clear_cmd.py +0 -19
- ripperdoc-0.2.8/ripperdoc/cli/ui/spinner.py +0 -49
- ripperdoc-0.2.8/ripperdoc/core/default_tools.py +0 -88
- ripperdoc-0.2.8/ripperdoc/tools/background_shell.py +0 -389
- ripperdoc-0.2.8/ripperdoc/tools/task_tool.py +0 -379
- ripperdoc-0.2.8/ripperdoc/utils/file_watch.py +0 -135
- ripperdoc-0.2.8/tests/test_permissions.py +0 -150
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/LICENSE +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/__main__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/base.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/compact_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/cost_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/help_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/mcp_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/memory_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/todos_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/commands/tools_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/context_display.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/cli/ui/helpers.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/commands.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/hooks/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/providers/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/core/providers/base.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/sdk/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/tools/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/bash_constants.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/bash_output_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/coerce.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/context_length_errors.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/exit_code_handlers.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/git_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/log.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/output_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/path_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/permissions/__init__.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/prompt.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/sandbox_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/session_usage.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/shell_token_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc/utils/shell_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/dependency_links.txt +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/entry_points.txt +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/ripperdoc.egg-info/top_level.txt +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/setup.cfg +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/setup.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_context_length_errors.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_context_limits.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_hooks_cmd.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_mcp_config.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_messages.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_output_utils.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_skills.py +0 -0
- {ripperdoc-0.2.8 → ripperdoc-0.2.10}/tests/test_todo.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ripperdoc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: AI-powered terminal assistant for coding tasks
|
|
5
5
|
Author: Ripperdoc Team
|
|
6
6
|
License: Apache-2.0
|
|
@@ -33,6 +33,12 @@ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
|
33
33
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
34
34
|
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
35
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Provides-Extra: anthropic
|
|
37
|
+
Requires-Dist: anthropic>=0.39.0; extra == "anthropic"
|
|
38
|
+
Provides-Extra: openai
|
|
39
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
40
|
+
Provides-Extra: gemini
|
|
41
|
+
Requires-Dist: google-genai>=0.3.0; extra == "gemini"
|
|
36
42
|
Dynamic: license-file
|
|
37
43
|
|
|
38
44
|
<div align="center">
|
|
@@ -137,7 +143,7 @@ See the [examples/](examples/) directory for complete SDK usage examples.
|
|
|
137
143
|
|
|
138
144
|
### Safe Mode Permissions
|
|
139
145
|
|
|
140
|
-
Safe mode is the default. Use `--
|
|
146
|
+
Safe mode is the default. Use `--yolo` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
|
|
141
147
|
|
|
142
148
|
### Agent Skills
|
|
143
149
|
|
|
@@ -100,7 +100,7 @@ See the [examples/](examples/) directory for complete SDK usage examples.
|
|
|
100
100
|
|
|
101
101
|
### Safe Mode Permissions
|
|
102
102
|
|
|
103
|
-
Safe mode is the default. Use `--
|
|
103
|
+
Safe mode is the default. Use `--yolo` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
|
|
104
104
|
|
|
105
105
|
### Agent Skills
|
|
106
106
|
|
|
@@ -46,6 +46,9 @@ dev = [
|
|
|
46
46
|
"black>=23.0.0",
|
|
47
47
|
"ruff>=0.1.0",
|
|
48
48
|
]
|
|
49
|
+
anthropic = ["anthropic>=0.39.0"]
|
|
50
|
+
openai = ["openai>=1.0.0"]
|
|
51
|
+
gemini = ["google-genai>=0.3.0"]
|
|
49
52
|
|
|
50
53
|
[project.scripts]
|
|
51
54
|
ripperdoc = "ripperdoc.cli.cli:main"
|
|
@@ -68,7 +71,7 @@ warn_return_any = true
|
|
|
68
71
|
warn_unused_configs = true
|
|
69
72
|
disallow_untyped_defs = true
|
|
70
73
|
files = ["ripperdoc"]
|
|
71
|
-
exclude = ["^tests/"]
|
|
74
|
+
exclude = ["^tests/", "^setup\\.py$"]
|
|
72
75
|
|
|
73
76
|
[tool.ruff]
|
|
74
77
|
line-length = 100
|
|
@@ -6,6 +6,7 @@ This module provides the command-line interface for the Ripperdoc agent.
|
|
|
6
6
|
import asyncio
|
|
7
7
|
import click
|
|
8
8
|
import sys
|
|
9
|
+
import time
|
|
9
10
|
import uuid
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any, Dict, List, Optional
|
|
@@ -13,27 +14,33 @@ from typing import Any, Dict, List, Optional
|
|
|
13
14
|
from ripperdoc import __version__
|
|
14
15
|
from ripperdoc.core.config import (
|
|
15
16
|
get_global_config,
|
|
16
|
-
save_global_config,
|
|
17
17
|
get_project_config,
|
|
18
|
-
ModelProfile,
|
|
19
|
-
ProviderType,
|
|
20
18
|
)
|
|
21
|
-
from ripperdoc.
|
|
19
|
+
from ripperdoc.cli.ui.wizard import check_onboarding
|
|
20
|
+
from ripperdoc.core.default_tools import get_default_tools, BUILTIN_TOOL_NAMES
|
|
22
21
|
from ripperdoc.core.query import query, QueryContext
|
|
23
22
|
from ripperdoc.core.system_prompt import build_system_prompt
|
|
24
23
|
from ripperdoc.core.skills import build_skill_summary, load_all_skills
|
|
25
24
|
from ripperdoc.core.hooks.manager import hook_manager
|
|
25
|
+
from ripperdoc.core.hooks.llm_callback import build_hook_llm_callback
|
|
26
26
|
from ripperdoc.utils.messages import create_user_message
|
|
27
27
|
from ripperdoc.utils.memory import build_memory_instructions
|
|
28
28
|
from ripperdoc.core.permissions import make_permission_checker
|
|
29
|
+
from ripperdoc.utils.session_history import (
|
|
30
|
+
SessionHistory,
|
|
31
|
+
list_session_summaries,
|
|
32
|
+
load_session_messages,
|
|
33
|
+
)
|
|
29
34
|
from ripperdoc.utils.mcp import (
|
|
30
35
|
load_mcp_servers_async,
|
|
31
36
|
format_mcp_instructions,
|
|
32
37
|
shutdown_mcp_runtime,
|
|
33
38
|
)
|
|
39
|
+
from ripperdoc.utils.lsp import shutdown_lsp_manager
|
|
40
|
+
from ripperdoc.tools.background_shell import shutdown_background_shell
|
|
34
41
|
from ripperdoc.tools.mcp_tools import load_dynamic_mcp_tools_async, merge_tools_with_dynamic
|
|
35
42
|
from ripperdoc.utils.log import enable_session_file_logging, get_logger
|
|
36
|
-
|
|
43
|
+
|
|
37
44
|
|
|
38
45
|
from rich.console import Console
|
|
39
46
|
from rich.markdown import Markdown
|
|
@@ -44,22 +51,64 @@ console = Console()
|
|
|
44
51
|
logger = get_logger()
|
|
45
52
|
|
|
46
53
|
|
|
54
|
+
def parse_tools_option(tools_arg: Optional[str]) -> Optional[List[str]]:
|
|
55
|
+
"""Parse the --tools argument.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
tools_arg: The raw tools argument from CLI.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
None for default (all tools), empty list for "" (no tools),
|
|
62
|
+
or a list of tool names for filtering.
|
|
63
|
+
"""
|
|
64
|
+
if tools_arg is None:
|
|
65
|
+
return None # Use all default tools
|
|
66
|
+
|
|
67
|
+
tools_arg = tools_arg.strip()
|
|
68
|
+
|
|
69
|
+
if tools_arg == "":
|
|
70
|
+
return [] # Disable all tools
|
|
71
|
+
|
|
72
|
+
if tools_arg.lower() == "default":
|
|
73
|
+
return None # Use all default tools
|
|
74
|
+
|
|
75
|
+
# Parse comma-separated list
|
|
76
|
+
tool_names = [name.strip() for name in tools_arg.split(",") if name.strip()]
|
|
77
|
+
|
|
78
|
+
# Validate tool names
|
|
79
|
+
invalid_tools = [name for name in tool_names if name not in BUILTIN_TOOL_NAMES]
|
|
80
|
+
if invalid_tools:
|
|
81
|
+
logger.warning(
|
|
82
|
+
"[cli] Unknown tools specified: %s. Available tools: %s",
|
|
83
|
+
", ".join(invalid_tools),
|
|
84
|
+
", ".join(BUILTIN_TOOL_NAMES),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return tool_names if tool_names else None
|
|
88
|
+
|
|
89
|
+
|
|
47
90
|
async def run_query(
|
|
48
91
|
prompt: str,
|
|
49
92
|
tools: list,
|
|
50
|
-
|
|
93
|
+
yolo_mode: bool = False,
|
|
51
94
|
verbose: bool = False,
|
|
52
95
|
session_id: Optional[str] = None,
|
|
96
|
+
custom_system_prompt: Optional[str] = None,
|
|
97
|
+
append_system_prompt: Optional[str] = None,
|
|
98
|
+
model: Optional[str] = None,
|
|
53
99
|
) -> None:
|
|
54
100
|
"""Run a single query and print the response."""
|
|
55
101
|
|
|
56
102
|
logger.info(
|
|
57
103
|
"[cli] Running single prompt session",
|
|
58
104
|
extra={
|
|
59
|
-
"
|
|
105
|
+
"yolo_mode": yolo_mode,
|
|
60
106
|
"verbose": verbose,
|
|
61
107
|
"session_id": session_id,
|
|
62
108
|
"prompt_length": len(prompt),
|
|
109
|
+
"model": model,
|
|
110
|
+
"has_custom_system_prompt": custom_system_prompt is not None,
|
|
111
|
+
"has_append_system_prompt": append_system_prompt is not None,
|
|
63
112
|
},
|
|
64
113
|
)
|
|
65
114
|
if prompt:
|
|
@@ -69,20 +118,37 @@ async def run_query(
|
|
|
69
118
|
)
|
|
70
119
|
|
|
71
120
|
project_path = Path.cwd()
|
|
72
|
-
can_use_tool = make_permission_checker(project_path,
|
|
121
|
+
can_use_tool = None if yolo_mode else make_permission_checker(project_path, yolo_mode=False)
|
|
73
122
|
|
|
74
123
|
# Initialize hook manager
|
|
75
124
|
hook_manager.set_project_dir(project_path)
|
|
76
125
|
hook_manager.set_session_id(session_id)
|
|
126
|
+
hook_manager.set_llm_callback(build_hook_llm_callback())
|
|
127
|
+
session_history = SessionHistory(project_path, session_id or str(uuid.uuid4()))
|
|
128
|
+
hook_manager.set_transcript_path(str(session_history.path))
|
|
129
|
+
|
|
130
|
+
def _collect_hook_contexts(result: Any) -> List[str]:
|
|
131
|
+
contexts: List[str] = []
|
|
132
|
+
system_message = getattr(result, "system_message", None)
|
|
133
|
+
additional_context = getattr(result, "additional_context", None)
|
|
134
|
+
if system_message:
|
|
135
|
+
contexts.append(str(system_message))
|
|
136
|
+
if additional_context:
|
|
137
|
+
contexts.append(str(additional_context))
|
|
138
|
+
return contexts
|
|
77
139
|
|
|
78
140
|
# Create initial user message
|
|
79
141
|
from ripperdoc.utils.messages import UserMessage, AssistantMessage, ProgressMessage
|
|
80
142
|
|
|
81
143
|
messages: List[UserMessage | AssistantMessage | ProgressMessage] = [create_user_message(prompt)]
|
|
144
|
+
session_history.append(messages[0])
|
|
82
145
|
|
|
83
146
|
# Create query context
|
|
84
|
-
query_context = QueryContext(
|
|
147
|
+
query_context = QueryContext(
|
|
148
|
+
tools=tools, yolo_mode=yolo_mode, verbose=verbose, model=model or "main"
|
|
149
|
+
)
|
|
85
150
|
|
|
151
|
+
session_start_time = time.time()
|
|
86
152
|
try:
|
|
87
153
|
context: Dict[str, Any] = {}
|
|
88
154
|
# System prompt
|
|
@@ -105,13 +171,46 @@ async def run_query(
|
|
|
105
171
|
memory_instructions = build_memory_instructions()
|
|
106
172
|
if memory_instructions:
|
|
107
173
|
additional_instructions.append(memory_instructions)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
additional_instructions
|
|
113
|
-
|
|
114
|
-
)
|
|
174
|
+
|
|
175
|
+
session_start_result = await hook_manager.run_session_start_async("startup")
|
|
176
|
+
session_hook_contexts = _collect_hook_contexts(session_start_result)
|
|
177
|
+
if session_hook_contexts:
|
|
178
|
+
additional_instructions.extend(session_hook_contexts)
|
|
179
|
+
|
|
180
|
+
prompt_hook_result = await hook_manager.run_user_prompt_submit_async(prompt)
|
|
181
|
+
if prompt_hook_result.should_block or not prompt_hook_result.should_continue:
|
|
182
|
+
reason = (
|
|
183
|
+
prompt_hook_result.block_reason
|
|
184
|
+
or prompt_hook_result.stop_reason
|
|
185
|
+
or "Prompt blocked by hook."
|
|
186
|
+
)
|
|
187
|
+
console.print(f"[red]{escape(str(reason))}[/red]")
|
|
188
|
+
return
|
|
189
|
+
prompt_hook_contexts = _collect_hook_contexts(prompt_hook_result)
|
|
190
|
+
if prompt_hook_contexts:
|
|
191
|
+
additional_instructions.extend(prompt_hook_contexts)
|
|
192
|
+
|
|
193
|
+
# Build system prompt based on options:
|
|
194
|
+
# - custom_system_prompt: replaces the default entirely
|
|
195
|
+
# - append_system_prompt: appends to the default system prompt
|
|
196
|
+
if custom_system_prompt:
|
|
197
|
+
# Complete replacement
|
|
198
|
+
system_prompt = custom_system_prompt
|
|
199
|
+
# Still append if both are provided
|
|
200
|
+
if append_system_prompt:
|
|
201
|
+
system_prompt = f"{system_prompt}\n\n{append_system_prompt}"
|
|
202
|
+
else:
|
|
203
|
+
# Build default with optional append
|
|
204
|
+
all_instructions = list(additional_instructions) if additional_instructions else []
|
|
205
|
+
if append_system_prompt:
|
|
206
|
+
all_instructions.append(append_system_prompt)
|
|
207
|
+
system_prompt = build_system_prompt(
|
|
208
|
+
tools,
|
|
209
|
+
prompt,
|
|
210
|
+
context,
|
|
211
|
+
additional_instructions=all_instructions or None,
|
|
212
|
+
mcp_instructions=mcp_instructions,
|
|
213
|
+
)
|
|
115
214
|
|
|
116
215
|
# Run the query
|
|
117
216
|
try:
|
|
@@ -160,6 +259,7 @@ async def run_query(
|
|
|
160
259
|
|
|
161
260
|
# Add message to history
|
|
162
261
|
messages.append(message) # type: ignore[arg-type]
|
|
262
|
+
session_history.append(message) # type: ignore[arg-type]
|
|
163
263
|
|
|
164
264
|
except KeyboardInterrupt:
|
|
165
265
|
console.print("\n[yellow]Interrupted by user[/yellow]")
|
|
@@ -169,7 +269,8 @@ async def run_query(
|
|
|
169
269
|
console.print(f"[red]Error: {escape(str(e))}[/red]")
|
|
170
270
|
logger.warning(
|
|
171
271
|
"[cli] Unhandled error while running prompt: %s: %s",
|
|
172
|
-
type(e).__name__,
|
|
272
|
+
type(e).__name__,
|
|
273
|
+
e,
|
|
173
274
|
extra={"session_id": session_id},
|
|
174
275
|
)
|
|
175
276
|
if verbose:
|
|
@@ -181,107 +282,31 @@ async def run_query(
|
|
|
181
282
|
extra={"session_id": session_id, "message_count": len(messages)},
|
|
182
283
|
)
|
|
183
284
|
finally:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def check_onboarding() -> bool:
|
|
189
|
-
"""Check if onboarding is complete and run if needed."""
|
|
190
|
-
config = get_global_config()
|
|
191
|
-
|
|
192
|
-
if config.has_completed_onboarding:
|
|
193
|
-
return True
|
|
194
|
-
|
|
195
|
-
console.print("[bold cyan]Welcome to Ripperdoc![/bold cyan]\n")
|
|
196
|
-
console.print("Let's set up your AI model configuration.\n")
|
|
197
|
-
|
|
198
|
-
# Simple onboarding
|
|
199
|
-
provider_choices = [
|
|
200
|
-
*[p.value for p in ProviderType],
|
|
201
|
-
"openai",
|
|
202
|
-
"deepseek",
|
|
203
|
-
"mistral",
|
|
204
|
-
"kimi",
|
|
205
|
-
"qwen",
|
|
206
|
-
"glm",
|
|
207
|
-
"custom",
|
|
208
|
-
]
|
|
209
|
-
provider_choice = click.prompt(
|
|
210
|
-
"Choose your model protocol",
|
|
211
|
-
type=click.Choice(provider_choices),
|
|
212
|
-
default=ProviderType.ANTHROPIC.value,
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
api_base = None
|
|
216
|
-
if provider_choice == "custom":
|
|
217
|
-
provider_choice = click.prompt(
|
|
218
|
-
"Protocol family (for API compatibility)",
|
|
219
|
-
type=click.Choice([p.value for p in ProviderType]),
|
|
220
|
-
default=ProviderType.OPENAI_COMPATIBLE.value,
|
|
221
|
-
)
|
|
222
|
-
api_base = click.prompt("API Base URL")
|
|
223
|
-
|
|
224
|
-
api_key = ""
|
|
225
|
-
while not api_key:
|
|
226
|
-
api_key = prompt_secret("Enter your API key").strip()
|
|
227
|
-
if not api_key:
|
|
228
|
-
console.print("[red]API key is required.[/red]")
|
|
229
|
-
|
|
230
|
-
provider = ProviderType(provider_choice)
|
|
231
|
-
|
|
232
|
-
# Get model name
|
|
233
|
-
if provider == ProviderType.ANTHROPIC:
|
|
234
|
-
model = click.prompt("Model name", default="claude-3-5-sonnet-20241022")
|
|
235
|
-
elif provider == ProviderType.OPENAI_COMPATIBLE:
|
|
236
|
-
default_model = "gpt-4o-mini"
|
|
237
|
-
if provider_choice == "deepseek":
|
|
238
|
-
default_model = "deepseek-chat"
|
|
239
|
-
api_base = api_base or "https://api.deepseek.com"
|
|
240
|
-
model = click.prompt("Model name", default=default_model)
|
|
241
|
-
if api_base is None:
|
|
242
|
-
api_base = (
|
|
243
|
-
click.prompt("API base URL (optional)", default="", show_default=False) or None
|
|
285
|
+
duration = max(time.time() - session_start_time, 0.0)
|
|
286
|
+
try:
|
|
287
|
+
await hook_manager.run_session_end_async(
|
|
288
|
+
"other", duration_seconds=duration, message_count=len(messages)
|
|
244
289
|
)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if api_base is None:
|
|
252
|
-
api_base = (
|
|
253
|
-
click.prompt("API base URL (optional)", default="", show_default=False) or None
|
|
290
|
+
except (OSError, RuntimeError, ConnectionError, ValueError, TypeError) as exc:
|
|
291
|
+
logger.warning(
|
|
292
|
+
"[cli] SessionEnd hook failed: %s: %s",
|
|
293
|
+
type(exc).__name__,
|
|
294
|
+
exc,
|
|
295
|
+
extra={"session_id": session_id},
|
|
254
296
|
)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
context_window_input = click.prompt(
|
|
259
|
-
"Context window in tokens (optional, press Enter to skip)", default="", show_default=False
|
|
260
|
-
)
|
|
261
|
-
context_window = None
|
|
262
|
-
if context_window_input.strip():
|
|
297
|
+
await shutdown_mcp_runtime()
|
|
298
|
+
await shutdown_lsp_manager()
|
|
299
|
+
# Shutdown background shell manager
|
|
263
300
|
try:
|
|
264
|
-
|
|
265
|
-
except
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
api_key=api_key,
|
|
273
|
-
api_base=api_base,
|
|
274
|
-
context_window=context_window,
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
config.has_completed_onboarding = True
|
|
278
|
-
config.last_onboarding_version = __version__
|
|
279
|
-
|
|
280
|
-
save_global_config(config)
|
|
281
|
-
|
|
282
|
-
console.print("\n[green]✓ Configuration saved![/green]\n")
|
|
301
|
+
shutdown_background_shell(force=True)
|
|
302
|
+
except (OSError, RuntimeError) as exc:
|
|
303
|
+
logger.debug(
|
|
304
|
+
"[cli] Failed to shut down background shell: %s: %s",
|
|
305
|
+
type(exc).__name__,
|
|
306
|
+
exc,
|
|
307
|
+
)
|
|
308
|
+
logger.debug("[cli] Shutdown MCP runtime", extra={"session_id": session_id})
|
|
283
309
|
|
|
284
|
-
return True
|
|
285
310
|
|
|
286
311
|
|
|
287
312
|
@click.group(invoke_without_command=True)
|
|
@@ -293,10 +318,59 @@ def check_onboarding() -> bool:
|
|
|
293
318
|
help="YOLO mode: skip all permission prompts for tools",
|
|
294
319
|
)
|
|
295
320
|
@click.option("--verbose", is_flag=True, help="Verbose output")
|
|
321
|
+
@click.option(
|
|
322
|
+
"--show-full-thinking/--hide-full-thinking",
|
|
323
|
+
default=None,
|
|
324
|
+
help="Show full reasoning content instead of truncated preview",
|
|
325
|
+
)
|
|
296
326
|
@click.option("-p", "--prompt", type=str, help="Direct prompt (non-interactive)")
|
|
327
|
+
@click.option(
|
|
328
|
+
"--tools",
|
|
329
|
+
type=str,
|
|
330
|
+
default=None,
|
|
331
|
+
help=(
|
|
332
|
+
'Specify the list of available tools. Use "" to disable all tools, '
|
|
333
|
+
'"default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").'
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
@click.option(
|
|
337
|
+
"--system-prompt",
|
|
338
|
+
type=str,
|
|
339
|
+
default=None,
|
|
340
|
+
help="System prompt to use for the session (replaces default).",
|
|
341
|
+
)
|
|
342
|
+
@click.option(
|
|
343
|
+
"--append-system-prompt",
|
|
344
|
+
type=str,
|
|
345
|
+
default=None,
|
|
346
|
+
help="Additional instructions to append to the system prompt.",
|
|
347
|
+
)
|
|
348
|
+
@click.option(
|
|
349
|
+
"--model",
|
|
350
|
+
type=str,
|
|
351
|
+
default=None,
|
|
352
|
+
help="Model profile for the current session.",
|
|
353
|
+
)
|
|
354
|
+
@click.option(
|
|
355
|
+
"-c",
|
|
356
|
+
"--continue",
|
|
357
|
+
"continue_session",
|
|
358
|
+
is_flag=True,
|
|
359
|
+
help="Continue the most recent conversation in the current directory.",
|
|
360
|
+
)
|
|
297
361
|
@click.pass_context
|
|
298
362
|
def cli(
|
|
299
|
-
ctx: click.Context,
|
|
363
|
+
ctx: click.Context,
|
|
364
|
+
cwd: Optional[str],
|
|
365
|
+
yolo: bool,
|
|
366
|
+
verbose: bool,
|
|
367
|
+
show_full_thinking: Optional[bool],
|
|
368
|
+
prompt: Optional[str],
|
|
369
|
+
tools: Optional[str],
|
|
370
|
+
system_prompt: Optional[str],
|
|
371
|
+
append_system_prompt: Optional[str],
|
|
372
|
+
model: Optional[str],
|
|
373
|
+
continue_session: bool,
|
|
300
374
|
) -> None:
|
|
301
375
|
"""Ripperdoc - AI-powered coding agent"""
|
|
302
376
|
session_id = str(uuid.uuid4())
|
|
@@ -334,16 +408,60 @@ def cli(
|
|
|
334
408
|
# Initialize project configuration for the current working directory
|
|
335
409
|
get_project_config(project_path)
|
|
336
410
|
|
|
337
|
-
|
|
411
|
+
yolo_mode = yolo
|
|
412
|
+
# Parse --tools option
|
|
413
|
+
allowed_tools = parse_tools_option(tools)
|
|
414
|
+
|
|
415
|
+
# Handle --continue option: load the most recent session
|
|
416
|
+
resume_messages = None
|
|
417
|
+
if continue_session:
|
|
418
|
+
summaries = list_session_summaries(project_path)
|
|
419
|
+
if summaries:
|
|
420
|
+
most_recent = summaries[0]
|
|
421
|
+
session_id = most_recent.session_id
|
|
422
|
+
resume_messages = load_session_messages(project_path, session_id)
|
|
423
|
+
logger.info(
|
|
424
|
+
"[cli] Continuing session",
|
|
425
|
+
extra={
|
|
426
|
+
"session_id": session_id,
|
|
427
|
+
"message_count": len(resume_messages),
|
|
428
|
+
"last_prompt": most_recent.last_prompt,
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
console.print(f"[dim]Continuing session: {most_recent.last_prompt}[/dim]")
|
|
432
|
+
else:
|
|
433
|
+
logger.warning("[cli] No previous sessions found to continue")
|
|
434
|
+
console.print("[yellow]No previous sessions found in this directory.[/yellow]")
|
|
435
|
+
|
|
338
436
|
logger.debug(
|
|
339
437
|
"[cli] Configuration initialized",
|
|
340
|
-
extra={
|
|
438
|
+
extra={
|
|
439
|
+
"session_id": session_id,
|
|
440
|
+
"yolo_mode": yolo_mode,
|
|
441
|
+
"verbose": verbose,
|
|
442
|
+
"allowed_tools": allowed_tools,
|
|
443
|
+
"model": model,
|
|
444
|
+
"has_system_prompt": system_prompt is not None,
|
|
445
|
+
"has_append_system_prompt": append_system_prompt is not None,
|
|
446
|
+
"continue_session": continue_session,
|
|
447
|
+
},
|
|
341
448
|
)
|
|
342
449
|
|
|
343
450
|
# If prompt is provided, run directly
|
|
344
451
|
if prompt:
|
|
345
|
-
|
|
346
|
-
asyncio.run(
|
|
452
|
+
tool_list = get_default_tools(allowed_tools=allowed_tools)
|
|
453
|
+
asyncio.run(
|
|
454
|
+
run_query(
|
|
455
|
+
prompt,
|
|
456
|
+
tool_list,
|
|
457
|
+
yolo_mode,
|
|
458
|
+
verbose,
|
|
459
|
+
session_id=session_id,
|
|
460
|
+
custom_system_prompt=system_prompt,
|
|
461
|
+
append_system_prompt=append_system_prompt,
|
|
462
|
+
model=model,
|
|
463
|
+
)
|
|
464
|
+
)
|
|
347
465
|
return
|
|
348
466
|
|
|
349
467
|
# If no command specified, start interactive REPL with Rich interface
|
|
@@ -352,10 +470,16 @@ def cli(
|
|
|
352
470
|
from ripperdoc.cli.ui.rich_ui import main_rich
|
|
353
471
|
|
|
354
472
|
main_rich(
|
|
355
|
-
|
|
473
|
+
yolo_mode=yolo_mode,
|
|
356
474
|
verbose=verbose,
|
|
475
|
+
show_full_thinking=show_full_thinking,
|
|
357
476
|
session_id=session_id,
|
|
358
477
|
log_file_path=log_file,
|
|
478
|
+
allowed_tools=allowed_tools,
|
|
479
|
+
custom_system_prompt=system_prompt,
|
|
480
|
+
append_system_prompt=append_system_prompt,
|
|
481
|
+
model=model,
|
|
482
|
+
resume_messages=resume_messages,
|
|
359
483
|
)
|
|
360
484
|
return
|
|
361
485
|
|
|
@@ -370,7 +494,8 @@ def config_cmd() -> None:
|
|
|
370
494
|
console.print(f"Onboarding Complete: {config.has_completed_onboarding}")
|
|
371
495
|
console.print(f"Theme: {config.theme}")
|
|
372
496
|
console.print(f"Verbose: {config.verbose}")
|
|
373
|
-
console.print(f"
|
|
497
|
+
console.print(f"Yolo Mode: {config.yolo_mode}")
|
|
498
|
+
console.print(f"Show Full Thinking: {config.show_full_thinking}\n")
|
|
374
499
|
|
|
375
500
|
if config.model_profiles:
|
|
376
501
|
console.print("[bold]Model Profiles:[/bold]")
|
|
@@ -397,11 +522,20 @@ def main() -> None:
|
|
|
397
522
|
sys.exit(130)
|
|
398
523
|
except SystemExit:
|
|
399
524
|
raise
|
|
400
|
-
except (
|
|
525
|
+
except (
|
|
526
|
+
RuntimeError,
|
|
527
|
+
ValueError,
|
|
528
|
+
TypeError,
|
|
529
|
+
OSError,
|
|
530
|
+
IOError,
|
|
531
|
+
ConnectionError,
|
|
532
|
+
click.ClickException,
|
|
533
|
+
) as e:
|
|
401
534
|
console.print(f"[red]Fatal error: {escape(str(e))}[/red]")
|
|
402
535
|
logger.warning(
|
|
403
536
|
"[cli] Fatal error in main CLI entrypoint: %s: %s",
|
|
404
|
-
type(e).__name__,
|
|
537
|
+
type(e).__name__,
|
|
538
|
+
e,
|
|
405
539
|
)
|
|
406
540
|
sys.exit(1)
|
|
407
541
|
|
|
@@ -21,6 +21,7 @@ from .mcp_cmd import command as mcp_command
|
|
|
21
21
|
from .models_cmd import command as models_command
|
|
22
22
|
from .permissions_cmd import command as permissions_command
|
|
23
23
|
from .resume_cmd import command as resume_command
|
|
24
|
+
from .stats_cmd import command as stats_command
|
|
24
25
|
from .tasks_cmd import command as tasks_command
|
|
25
26
|
from .status_cmd import command as status_command
|
|
26
27
|
from .todos_cmd import command as todos_command
|
|
@@ -29,7 +30,6 @@ from .tools_cmd import command as tools_command
|
|
|
29
30
|
from ripperdoc.core.custom_commands import (
|
|
30
31
|
CustomCommandDefinition,
|
|
31
32
|
load_all_custom_commands,
|
|
32
|
-
find_custom_command,
|
|
33
33
|
expand_command_content,
|
|
34
34
|
)
|
|
35
35
|
|
|
@@ -52,6 +52,7 @@ ALL_COMMANDS: List[SlashCommand] = [
|
|
|
52
52
|
models_command,
|
|
53
53
|
exit_command,
|
|
54
54
|
status_command,
|
|
55
|
+
stats_command,
|
|
55
56
|
doctor_command,
|
|
56
57
|
memory_command,
|
|
57
58
|
permissions_command,
|