code-puppy 0.0.169__py3-none-any.whl → 0.0.366__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.
- code_puppy/__init__.py +7 -1
- code_puppy/agents/__init__.py +8 -8
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +9 -2
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +48 -9
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +146 -199
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +1713 -1
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/json_agent.py +12 -1
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +174 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +233 -627
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +1 -4
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +16 -27
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +3 -3
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +17 -11
- code_puppy/command_line/mcp/start_all_command.py +22 -13
- code_puppy/command_line/mcp/start_command.py +50 -31
- code_puppy/command_line/mcp/status_command.py +6 -7
- code_puppy/command_line/mcp/stop_all_command.py +11 -8
- code_puppy/command_line/mcp/stop_command.py +11 -10
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/utils.py +1 -1
- code_puppy/command_line/mcp/wizard_utils.py +22 -18
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +89 -30
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +626 -75
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +1181 -51
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +220 -104
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -594
- code_puppy/{mcp → mcp_}/__init__.py +17 -0
- code_puppy/{mcp → mcp_}/async_lifecycle.py +35 -4
- code_puppy/{mcp → mcp_}/blocking_startup.py +70 -43
- code_puppy/{mcp → mcp_}/captured_stdio_server.py +2 -2
- code_puppy/{mcp → mcp_}/config_wizard.py +5 -5
- code_puppy/{mcp → mcp_}/dashboard.py +15 -6
- code_puppy/{mcp → mcp_}/examples/retry_example.py +4 -1
- code_puppy/{mcp → mcp_}/managed_server.py +66 -39
- code_puppy/{mcp → mcp_}/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/{mcp → mcp_}/registry.py +6 -6
- code_puppy/{mcp → mcp_}/server_registry_catalog.py +25 -8
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +33 -5
- code_puppy/messaging/spinner/console_spinner.py +92 -52
- code_puppy/messaging/spinner/spinner_base.py +29 -0
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +686 -80
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +86 -104
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +10 -15
- code_puppy/session_storage.py +294 -0
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +52 -14
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +139 -6
- code_puppy/tools/agent_tools.py +548 -49
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +941 -153
- code_puppy/tools/common.py +1146 -6
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +352 -266
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/METADATA +184 -67
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +1 -0
- code_puppy/agent.py +0 -231
- code_puppy/agents/agent_orchestrator.json +0 -26
- code_puppy/agents/runtime_manager.py +0 -272
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/command_line/meta_command_handler.py +0 -153
- code_puppy/message_history_processor.py +0 -490
- code_puppy/messaging/spinner/textual_spinner.py +0 -101
- code_puppy/state_management.py +0 -200
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -986
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -550
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -182
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -15
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -290
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy-0.0.169.data/data/code_puppy/models.json +0 -128
- code_puppy-0.0.169.dist-info/RECORD +0 -112
- /code_puppy/{mcp → mcp_}/circuit_breaker.py +0 -0
- /code_puppy/{mcp → mcp_}/error_isolation.py +0 -0
- /code_puppy/{mcp → mcp_}/health_monitor.py +0 -0
- /code_puppy/{mcp → mcp_}/retry_manager.py +0 -0
- /code_puppy/{mcp → mcp_}/status_tracker.py +0 -0
- /code_puppy/{mcp → mcp_}/system_tools.py +0 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
code_puppy/main.py
CHANGED
|
@@ -1,599 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import asyncio
|
|
3
|
-
import os
|
|
4
|
-
import subprocess
|
|
5
|
-
import sys
|
|
6
|
-
import time
|
|
7
|
-
import webbrowser
|
|
1
|
+
"""Main entry point for Code Puppy CLI.
|
|
8
2
|
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from rich.text import Text
|
|
13
|
-
|
|
14
|
-
from code_puppy import __version__, callbacks, plugins
|
|
15
|
-
from code_puppy.agent import get_custom_usage_limits
|
|
16
|
-
from code_puppy.agents.runtime_manager import get_runtime_agent_manager
|
|
17
|
-
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
18
|
-
get_input_with_combined_completion,
|
|
19
|
-
get_prompt_with_active_model,
|
|
20
|
-
)
|
|
21
|
-
from code_puppy.config import (
|
|
22
|
-
COMMAND_HISTORY_FILE,
|
|
23
|
-
ensure_config_exists,
|
|
24
|
-
initialize_command_history_file,
|
|
25
|
-
save_command_to_history,
|
|
26
|
-
)
|
|
27
|
-
from code_puppy.http_utils import find_available_port
|
|
28
|
-
from code_puppy.message_history_processor import (
|
|
29
|
-
message_history_accumulator,
|
|
30
|
-
prune_interrupted_tool_calls,
|
|
31
|
-
)
|
|
32
|
-
from code_puppy.state_management import is_tui_mode, set_message_history, set_tui_mode
|
|
33
|
-
from code_puppy.tools.common import console
|
|
34
|
-
from code_puppy.version_checker import default_version_mismatch_behavior
|
|
35
|
-
|
|
36
|
-
plugins.load_plugin_callbacks()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async def main():
|
|
40
|
-
parser = argparse.ArgumentParser(description="Code Puppy - A code generation agent")
|
|
41
|
-
parser.add_argument(
|
|
42
|
-
"--version",
|
|
43
|
-
"-v",
|
|
44
|
-
action="version",
|
|
45
|
-
version=f"{__version__}",
|
|
46
|
-
help="Show version and exit",
|
|
47
|
-
)
|
|
48
|
-
parser.add_argument(
|
|
49
|
-
"--interactive",
|
|
50
|
-
"-i",
|
|
51
|
-
action="store_true",
|
|
52
|
-
help="Run in interactive mode",
|
|
53
|
-
)
|
|
54
|
-
parser.add_argument("--tui", "-t", action="store_true", help="Run in TUI mode")
|
|
55
|
-
parser.add_argument(
|
|
56
|
-
"--web",
|
|
57
|
-
"-w",
|
|
58
|
-
action="store_true",
|
|
59
|
-
help="Run in web mode (serves TUI in browser)",
|
|
60
|
-
)
|
|
61
|
-
parser.add_argument(
|
|
62
|
-
"--prompt",
|
|
63
|
-
"-p",
|
|
64
|
-
type=str,
|
|
65
|
-
help="Execute a single prompt and exit (no interactive mode)",
|
|
66
|
-
)
|
|
67
|
-
parser.add_argument(
|
|
68
|
-
"command", nargs="*", help="Run a single command (deprecated, use -p instead)"
|
|
69
|
-
)
|
|
70
|
-
args = parser.parse_args()
|
|
71
|
-
|
|
72
|
-
if args.tui or args.web:
|
|
73
|
-
set_tui_mode(True)
|
|
74
|
-
elif args.interactive or args.command or args.prompt:
|
|
75
|
-
set_tui_mode(False)
|
|
76
|
-
|
|
77
|
-
message_renderer = None
|
|
78
|
-
if not is_tui_mode():
|
|
79
|
-
from rich.console import Console
|
|
80
|
-
|
|
81
|
-
from code_puppy.messaging import (
|
|
82
|
-
SynchronousInteractiveRenderer,
|
|
83
|
-
get_global_queue,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
message_queue = get_global_queue()
|
|
87
|
-
display_console = Console() # Separate console for rendering messages
|
|
88
|
-
message_renderer = SynchronousInteractiveRenderer(
|
|
89
|
-
message_queue, display_console
|
|
90
|
-
)
|
|
91
|
-
message_renderer.start()
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
not args.tui
|
|
95
|
-
and not args.interactive
|
|
96
|
-
and not args.web
|
|
97
|
-
and not args.command
|
|
98
|
-
and not args.prompt
|
|
99
|
-
):
|
|
100
|
-
pass
|
|
101
|
-
|
|
102
|
-
initialize_command_history_file()
|
|
103
|
-
if args.web:
|
|
104
|
-
from rich.console import Console
|
|
105
|
-
|
|
106
|
-
direct_console = Console()
|
|
107
|
-
try:
|
|
108
|
-
# Find an available port for the web server
|
|
109
|
-
available_port = find_available_port()
|
|
110
|
-
if available_port is None:
|
|
111
|
-
direct_console.print(
|
|
112
|
-
"[bold red]Error:[/bold red] No available ports in range 8090-9010!"
|
|
113
|
-
)
|
|
114
|
-
sys.exit(1)
|
|
115
|
-
python_executable = sys.executable
|
|
116
|
-
serve_command = f"{python_executable} -m code_puppy --tui"
|
|
117
|
-
textual_serve_cmd = [
|
|
118
|
-
"textual",
|
|
119
|
-
"serve",
|
|
120
|
-
"-c",
|
|
121
|
-
serve_command,
|
|
122
|
-
"--port",
|
|
123
|
-
str(available_port),
|
|
124
|
-
]
|
|
125
|
-
direct_console.print(
|
|
126
|
-
"[bold blue]🌐 Starting Code Puppy web interface...[/bold blue]"
|
|
127
|
-
)
|
|
128
|
-
direct_console.print(f"[dim]Running: {' '.join(textual_serve_cmd)}[/dim]")
|
|
129
|
-
web_url = f"http://localhost:{available_port}"
|
|
130
|
-
direct_console.print(
|
|
131
|
-
f"[green]Web interface will be available at: {web_url}[/green]"
|
|
132
|
-
)
|
|
133
|
-
direct_console.print("[yellow]Press Ctrl+C to stop the server.[/yellow]\n")
|
|
134
|
-
process = subprocess.Popen(textual_serve_cmd)
|
|
135
|
-
time.sleep(0.3)
|
|
136
|
-
try:
|
|
137
|
-
direct_console.print(
|
|
138
|
-
"[cyan]🚀 Opening web interface in your default browser...[/cyan]"
|
|
139
|
-
)
|
|
140
|
-
webbrowser.open(web_url)
|
|
141
|
-
direct_console.print("[green]✅ Browser opened successfully![/green]\n")
|
|
142
|
-
except Exception as e:
|
|
143
|
-
direct_console.print(
|
|
144
|
-
f"[yellow]⚠️ Could not automatically open browser: {e}[/yellow]"
|
|
145
|
-
)
|
|
146
|
-
direct_console.print(
|
|
147
|
-
f"[yellow]Please manually open: {web_url}[/yellow]\n"
|
|
148
|
-
)
|
|
149
|
-
result = process.wait()
|
|
150
|
-
sys.exit(result)
|
|
151
|
-
except Exception as e:
|
|
152
|
-
direct_console.print(
|
|
153
|
-
f"[bold red]Error starting web interface:[/bold red] {str(e)}"
|
|
154
|
-
)
|
|
155
|
-
sys.exit(1)
|
|
156
|
-
from code_puppy.messaging import emit_system_message
|
|
157
|
-
|
|
158
|
-
emit_system_message("🐶 Code Puppy is Loading...")
|
|
159
|
-
|
|
160
|
-
available_port = find_available_port()
|
|
161
|
-
if available_port is None:
|
|
162
|
-
error_msg = "Error: No available ports in range 8090-9010!"
|
|
163
|
-
emit_system_message(f"[bold red]{error_msg}[/bold red]")
|
|
164
|
-
return
|
|
165
|
-
|
|
166
|
-
ensure_config_exists()
|
|
167
|
-
current_version = __version__
|
|
168
|
-
|
|
169
|
-
no_version_update = os.getenv("NO_VERSION_UPDATE", "").lower() in (
|
|
170
|
-
"1",
|
|
171
|
-
"true",
|
|
172
|
-
"yes",
|
|
173
|
-
"on",
|
|
174
|
-
)
|
|
175
|
-
if no_version_update:
|
|
176
|
-
version_msg = f"Current version: {current_version}"
|
|
177
|
-
update_disabled_msg = (
|
|
178
|
-
"Update phase disabled because NO_VERSION_UPDATE is set to 1 or true"
|
|
179
|
-
)
|
|
180
|
-
emit_system_message(version_msg)
|
|
181
|
-
emit_system_message(f"[dim]{update_disabled_msg}[/dim]")
|
|
182
|
-
else:
|
|
183
|
-
if len(callbacks.get_callbacks("version_check")):
|
|
184
|
-
await callbacks.on_version_check(current_version)
|
|
185
|
-
else:
|
|
186
|
-
default_version_mismatch_behavior(current_version)
|
|
187
|
-
|
|
188
|
-
await callbacks.on_startup()
|
|
189
|
-
|
|
190
|
-
global shutdown_flag
|
|
191
|
-
shutdown_flag = False
|
|
192
|
-
try:
|
|
193
|
-
initial_command = None
|
|
194
|
-
prompt_only_mode = False
|
|
195
|
-
|
|
196
|
-
if args.prompt:
|
|
197
|
-
initial_command = args.prompt
|
|
198
|
-
prompt_only_mode = True
|
|
199
|
-
elif args.command:
|
|
200
|
-
initial_command = " ".join(args.command)
|
|
201
|
-
prompt_only_mode = False
|
|
202
|
-
|
|
203
|
-
if prompt_only_mode:
|
|
204
|
-
await execute_single_prompt(initial_command, message_renderer)
|
|
205
|
-
elif is_tui_mode():
|
|
206
|
-
try:
|
|
207
|
-
from code_puppy.tui import run_textual_ui
|
|
208
|
-
|
|
209
|
-
await run_textual_ui(initial_command=initial_command)
|
|
210
|
-
except ImportError:
|
|
211
|
-
from code_puppy.messaging import emit_error, emit_warning
|
|
212
|
-
|
|
213
|
-
emit_error(
|
|
214
|
-
"Error: Textual UI not available. Install with: pip install textual"
|
|
215
|
-
)
|
|
216
|
-
emit_warning("Falling back to interactive mode...")
|
|
217
|
-
await interactive_mode(message_renderer)
|
|
218
|
-
except Exception as e:
|
|
219
|
-
from code_puppy.messaging import emit_error, emit_warning
|
|
220
|
-
|
|
221
|
-
emit_error(f"TUI Error: {str(e)}")
|
|
222
|
-
emit_warning("Falling back to interactive mode...")
|
|
223
|
-
await interactive_mode(message_renderer)
|
|
224
|
-
elif args.interactive or initial_command:
|
|
225
|
-
await interactive_mode(message_renderer, initial_command=initial_command)
|
|
226
|
-
else:
|
|
227
|
-
await prompt_then_interactive_mode(message_renderer)
|
|
228
|
-
finally:
|
|
229
|
-
if message_renderer:
|
|
230
|
-
message_renderer.stop()
|
|
231
|
-
await callbacks.on_shutdown()
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# Add the file handling functionality for interactive mode
|
|
235
|
-
async def interactive_mode(message_renderer, initial_command: str = None) -> None:
|
|
236
|
-
from code_puppy.command_line.command_handler import handle_command
|
|
237
|
-
|
|
238
|
-
"""Run the agent in interactive mode."""
|
|
239
|
-
from code_puppy.state_management import clear_message_history, get_message_history
|
|
240
|
-
|
|
241
|
-
clear_message_history()
|
|
242
|
-
display_console = message_renderer.console
|
|
243
|
-
from code_puppy.messaging import emit_info, emit_system_message
|
|
244
|
-
|
|
245
|
-
emit_info("[bold green]Code Puppy[/bold green] - Interactive Mode")
|
|
246
|
-
emit_system_message("Type '/exit' or '/quit' to exit the interactive mode.")
|
|
247
|
-
emit_system_message("Type 'clear' to reset the conversation history.")
|
|
248
|
-
emit_system_message(
|
|
249
|
-
"Type [bold blue]@[/bold blue] for path completion, or [bold blue]/m[/bold blue] to pick a model. Use [bold blue]Esc+Enter[/bold blue] for multi-line input."
|
|
250
|
-
)
|
|
251
|
-
emit_system_message(
|
|
252
|
-
"Press [bold red]Ctrl+C[/bold red] during processing to cancel the current task or inference."
|
|
253
|
-
)
|
|
254
|
-
from code_puppy.command_line.command_handler import get_commands_help
|
|
255
|
-
|
|
256
|
-
help_text = get_commands_help()
|
|
257
|
-
emit_system_message(help_text)
|
|
258
|
-
try:
|
|
259
|
-
from code_puppy.command_line.motd import print_motd
|
|
260
|
-
|
|
261
|
-
print_motd(console, force=False)
|
|
262
|
-
except Exception as e:
|
|
263
|
-
from code_puppy.messaging import emit_warning
|
|
264
|
-
|
|
265
|
-
emit_warning(f"MOTD error: {e}")
|
|
266
|
-
from code_puppy.agents.runtime_manager import get_runtime_agent_manager
|
|
267
|
-
from code_puppy.messaging import emit_info
|
|
268
|
-
|
|
269
|
-
emit_info("[bold cyan]Initializing agent...[/bold cyan]")
|
|
270
|
-
# Initialize the runtime agent manager
|
|
271
|
-
agent_manager = get_runtime_agent_manager()
|
|
272
|
-
agent_manager.get_agent()
|
|
273
|
-
if initial_command:
|
|
274
|
-
from code_puppy.messaging import emit_info, emit_system_message
|
|
275
|
-
|
|
276
|
-
emit_info(
|
|
277
|
-
f"[bold blue]Processing initial command:[/bold blue] {initial_command}"
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
try:
|
|
281
|
-
# Check if any tool is waiting for user input before showing spinner
|
|
282
|
-
try:
|
|
283
|
-
from code_puppy.tools.command_runner import is_awaiting_user_input
|
|
284
|
-
|
|
285
|
-
awaiting_input = is_awaiting_user_input()
|
|
286
|
-
except ImportError:
|
|
287
|
-
awaiting_input = False
|
|
288
|
-
|
|
289
|
-
# Run with or without spinner based on whether we're awaiting input
|
|
290
|
-
if awaiting_input:
|
|
291
|
-
# No spinner - use agent_manager's run_with_mcp method
|
|
292
|
-
response = await agent_manager.run_with_mcp(
|
|
293
|
-
initial_command,
|
|
294
|
-
message_history=get_message_history(),
|
|
295
|
-
usage_limits=get_custom_usage_limits(),
|
|
296
|
-
)
|
|
297
|
-
else:
|
|
298
|
-
# Use our custom spinner for better compatibility with user input
|
|
299
|
-
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
300
|
-
|
|
301
|
-
with ConsoleSpinner(console=display_console):
|
|
302
|
-
# Use agent_manager's run_with_mcp method
|
|
303
|
-
response = await agent_manager.run_with_mcp(
|
|
304
|
-
initial_command,
|
|
305
|
-
message_history=prune_interrupted_tool_calls(
|
|
306
|
-
get_message_history()
|
|
307
|
-
),
|
|
308
|
-
usage_limits=get_custom_usage_limits(),
|
|
309
|
-
)
|
|
310
|
-
set_message_history(
|
|
311
|
-
prune_interrupted_tool_calls(get_message_history())
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
agent_response = response.output
|
|
315
|
-
|
|
316
|
-
emit_system_message(
|
|
317
|
-
f"\n[bold purple]AGENT RESPONSE: [/bold purple]\n{agent_response}"
|
|
318
|
-
)
|
|
319
|
-
new_msgs = response.all_messages()
|
|
320
|
-
message_history_accumulator(new_msgs)
|
|
321
|
-
set_message_history(prune_interrupted_tool_calls(get_message_history()))
|
|
322
|
-
emit_system_message("\n" + "=" * 50)
|
|
323
|
-
emit_info("[bold green]🐶 Continuing in Interactive Mode[/bold green]")
|
|
324
|
-
emit_system_message(
|
|
325
|
-
"Your command and response are preserved in the conversation history."
|
|
326
|
-
)
|
|
327
|
-
emit_system_message("=" * 50 + "\n")
|
|
328
|
-
|
|
329
|
-
except Exception as e:
|
|
330
|
-
from code_puppy.messaging import emit_error
|
|
331
|
-
|
|
332
|
-
emit_error(f"Error processing initial command: {str(e)}")
|
|
333
|
-
|
|
334
|
-
# Check if prompt_toolkit is installed
|
|
335
|
-
try:
|
|
336
|
-
from code_puppy.messaging import emit_system_message
|
|
337
|
-
|
|
338
|
-
emit_system_message(
|
|
339
|
-
"[dim]Using prompt_toolkit for enhanced tab completion[/dim]"
|
|
340
|
-
)
|
|
341
|
-
except ImportError:
|
|
342
|
-
from code_puppy.messaging import emit_warning
|
|
343
|
-
|
|
344
|
-
emit_warning("Warning: prompt_toolkit not installed. Installing now...")
|
|
345
|
-
try:
|
|
346
|
-
import subprocess
|
|
347
|
-
|
|
348
|
-
subprocess.check_call(
|
|
349
|
-
[sys.executable, "-m", "pip", "install", "prompt_toolkit"]
|
|
350
|
-
)
|
|
351
|
-
from code_puppy.messaging import emit_success
|
|
352
|
-
|
|
353
|
-
emit_success("Successfully installed prompt_toolkit")
|
|
354
|
-
except Exception as e:
|
|
355
|
-
from code_puppy.messaging import emit_error, emit_warning
|
|
356
|
-
|
|
357
|
-
emit_error(f"Error installing prompt_toolkit: {e}")
|
|
358
|
-
emit_warning("Falling back to basic input without tab completion")
|
|
359
|
-
|
|
360
|
-
while True:
|
|
361
|
-
from code_puppy.agents.agent_manager import get_current_agent_config
|
|
362
|
-
from code_puppy.messaging import emit_info
|
|
363
|
-
|
|
364
|
-
# Get the custom prompt from the current agent, or use default
|
|
365
|
-
current_agent = get_current_agent_config()
|
|
366
|
-
user_prompt = current_agent.get_user_prompt() or "Enter your coding task:"
|
|
367
|
-
|
|
368
|
-
emit_info(f"[bold blue]{user_prompt}[/bold blue]")
|
|
369
|
-
|
|
370
|
-
try:
|
|
371
|
-
# Use prompt_toolkit for enhanced input with path completion
|
|
372
|
-
try:
|
|
373
|
-
# Use the async version of get_input_with_combined_completion
|
|
374
|
-
task = await get_input_with_combined_completion(
|
|
375
|
-
get_prompt_with_active_model(), history_file=COMMAND_HISTORY_FILE
|
|
376
|
-
)
|
|
377
|
-
except ImportError:
|
|
378
|
-
# Fall back to basic input if prompt_toolkit is not available
|
|
379
|
-
task = input(">>> ")
|
|
380
|
-
|
|
381
|
-
except (KeyboardInterrupt, EOFError):
|
|
382
|
-
# Handle Ctrl+C or Ctrl+D
|
|
383
|
-
from code_puppy.messaging import emit_warning
|
|
384
|
-
|
|
385
|
-
emit_warning("\nInput cancelled")
|
|
386
|
-
continue
|
|
387
|
-
|
|
388
|
-
# Check for exit commands (plain text or command form)
|
|
389
|
-
if task.strip().lower() in ["exit", "quit"] or task.strip().lower() in [
|
|
390
|
-
"/exit",
|
|
391
|
-
"/quit",
|
|
392
|
-
]:
|
|
393
|
-
from code_puppy.messaging import emit_success
|
|
394
|
-
|
|
395
|
-
emit_success("Goodbye!")
|
|
396
|
-
# The renderer is stopped in the finally block of main().
|
|
397
|
-
break
|
|
398
|
-
|
|
399
|
-
# Check for clear command (supports both `clear` and `/clear`)
|
|
400
|
-
if task.strip().lower() in ("clear", "/clear"):
|
|
401
|
-
clear_message_history()
|
|
402
|
-
from code_puppy.messaging import emit_system_message, emit_warning
|
|
403
|
-
|
|
404
|
-
emit_warning("Conversation history cleared!")
|
|
405
|
-
emit_system_message("The agent will not remember previous interactions.\n")
|
|
406
|
-
continue
|
|
407
|
-
|
|
408
|
-
# Handle / commands before anything else
|
|
409
|
-
if task.strip().startswith("/"):
|
|
410
|
-
command_result = handle_command(task.strip())
|
|
411
|
-
if command_result is True:
|
|
412
|
-
continue
|
|
413
|
-
elif isinstance(command_result, str):
|
|
414
|
-
# Command returned a prompt to execute
|
|
415
|
-
task = command_result
|
|
416
|
-
elif command_result is False:
|
|
417
|
-
# Command not recognized, continue with normal processing
|
|
418
|
-
pass
|
|
419
|
-
|
|
420
|
-
if task.strip():
|
|
421
|
-
# Write to the secret file for permanent history with timestamp
|
|
422
|
-
save_command_to_history(task)
|
|
423
|
-
|
|
424
|
-
try:
|
|
425
|
-
prettier_code_blocks()
|
|
426
|
-
|
|
427
|
-
# No need to get agent directly - use manager's run methods
|
|
428
|
-
|
|
429
|
-
# Use our custom spinner for better compatibility with user input
|
|
430
|
-
from code_puppy.messaging import emit_warning
|
|
431
|
-
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
432
|
-
|
|
433
|
-
runtime_manager = get_runtime_agent_manager()
|
|
434
|
-
with ConsoleSpinner(console=message_renderer.console):
|
|
435
|
-
result = await runtime_manager.run_with_mcp(
|
|
436
|
-
task,
|
|
437
|
-
get_custom_usage_limits(),
|
|
438
|
-
message_history=prune_interrupted_tool_calls(
|
|
439
|
-
get_message_history()
|
|
440
|
-
),
|
|
441
|
-
)
|
|
442
|
-
# Check if the task was cancelled (but don't show message if we just killed processes)
|
|
443
|
-
if result is None:
|
|
444
|
-
continue
|
|
445
|
-
# Get the structured response
|
|
446
|
-
agent_response = result.output
|
|
447
|
-
from code_puppy.messaging import emit_info
|
|
448
|
-
|
|
449
|
-
emit_system_message(
|
|
450
|
-
f"\n[bold purple]AGENT RESPONSE: [/bold purple]\n{agent_response}"
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
# Update message history - the agent's history processor will handle truncation
|
|
454
|
-
new_msgs = result.all_messages()
|
|
455
|
-
message_history_accumulator(new_msgs)
|
|
456
|
-
|
|
457
|
-
emit_system_message(
|
|
458
|
-
f"Context: {len(get_message_history())} messages in history\n"
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
# Ensure console output is flushed before next prompt
|
|
462
|
-
# This fixes the issue where prompt doesn't appear after agent response
|
|
463
|
-
display_console.file.flush() if hasattr(
|
|
464
|
-
display_console.file, "flush"
|
|
465
|
-
) else None
|
|
466
|
-
import time
|
|
467
|
-
|
|
468
|
-
time.sleep(0.1) # Brief pause to ensure all messages are rendered
|
|
469
|
-
|
|
470
|
-
except Exception:
|
|
471
|
-
from code_puppy.messaging.queue_console import get_queue_console
|
|
472
|
-
|
|
473
|
-
get_queue_console().print_exception()
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
def prettier_code_blocks():
|
|
477
|
-
class SimpleCodeBlock(CodeBlock):
|
|
478
|
-
def __rich_console__(
|
|
479
|
-
self, console: Console, options: ConsoleOptions
|
|
480
|
-
) -> RenderResult:
|
|
481
|
-
code = str(self.text).rstrip()
|
|
482
|
-
yield Text(self.lexer_name, style="dim")
|
|
483
|
-
syntax = Syntax(
|
|
484
|
-
code,
|
|
485
|
-
self.lexer_name,
|
|
486
|
-
theme=self.theme,
|
|
487
|
-
background_color="default",
|
|
488
|
-
line_numbers=True,
|
|
489
|
-
)
|
|
490
|
-
yield syntax
|
|
491
|
-
yield Text(f"/{self.lexer_name}", style="dim")
|
|
492
|
-
|
|
493
|
-
Markdown.elements["fence"] = SimpleCodeBlock
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
async def execute_single_prompt(prompt: str, message_renderer) -> None:
|
|
497
|
-
"""Execute a single prompt and exit (for -p flag)."""
|
|
498
|
-
from code_puppy.messaging import emit_info, emit_system_message
|
|
499
|
-
|
|
500
|
-
emit_info(f"[bold blue]Executing prompt:[/bold blue] {prompt}")
|
|
501
|
-
|
|
502
|
-
try:
|
|
503
|
-
# Get agent through runtime manager and use its run_with_mcp method
|
|
504
|
-
agent_manager = get_runtime_agent_manager()
|
|
505
|
-
|
|
506
|
-
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
507
|
-
|
|
508
|
-
with ConsoleSpinner(console=message_renderer.console):
|
|
509
|
-
response = await agent_manager.run_with_mcp(
|
|
510
|
-
prompt,
|
|
511
|
-
usage_limits=get_custom_usage_limits(),
|
|
512
|
-
)
|
|
513
|
-
|
|
514
|
-
agent_response = response.output
|
|
515
|
-
emit_system_message(
|
|
516
|
-
f"\n[bold purple]AGENT RESPONSE: [/bold purple]\n{agent_response}"
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
except asyncio.CancelledError:
|
|
520
|
-
from code_puppy.messaging import emit_warning
|
|
521
|
-
|
|
522
|
-
emit_warning("Execution cancelled by user")
|
|
523
|
-
except Exception as e:
|
|
524
|
-
from code_puppy.messaging import emit_error
|
|
525
|
-
|
|
526
|
-
emit_error(f"Error executing prompt: {str(e)}")
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
async def prompt_then_interactive_mode(message_renderer) -> None:
|
|
530
|
-
"""Prompt user for input, execute it, then continue in interactive mode."""
|
|
531
|
-
from code_puppy.messaging import emit_info, emit_system_message
|
|
532
|
-
|
|
533
|
-
emit_info("[bold green]🐶 Code Puppy[/bold green] - Enter your request")
|
|
534
|
-
emit_system_message(
|
|
535
|
-
"After processing your request, you'll continue in interactive mode."
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
try:
|
|
539
|
-
# Get user input
|
|
540
|
-
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
541
|
-
get_input_with_combined_completion,
|
|
542
|
-
get_prompt_with_active_model,
|
|
543
|
-
)
|
|
544
|
-
from code_puppy.config import COMMAND_HISTORY_FILE
|
|
545
|
-
|
|
546
|
-
emit_info("[bold blue]What would you like me to help you with?[/bold blue]")
|
|
547
|
-
|
|
548
|
-
try:
|
|
549
|
-
# Use prompt_toolkit for enhanced input with path completion
|
|
550
|
-
user_prompt = await get_input_with_combined_completion(
|
|
551
|
-
get_prompt_with_active_model(), history_file=COMMAND_HISTORY_FILE
|
|
552
|
-
)
|
|
553
|
-
except ImportError:
|
|
554
|
-
# Fall back to basic input if prompt_toolkit is not available
|
|
555
|
-
user_prompt = input(">>> ")
|
|
556
|
-
|
|
557
|
-
if user_prompt.strip():
|
|
558
|
-
# Execute the prompt
|
|
559
|
-
await execute_single_prompt(user_prompt, message_renderer)
|
|
560
|
-
|
|
561
|
-
# Transition to interactive mode
|
|
562
|
-
emit_system_message("\n" + "=" * 50)
|
|
563
|
-
emit_info("[bold green]🐶 Continuing in Interactive Mode[/bold green]")
|
|
564
|
-
emit_system_message(
|
|
565
|
-
"Your request and response are preserved in the conversation history."
|
|
566
|
-
)
|
|
567
|
-
emit_system_message("=" * 50 + "\n")
|
|
568
|
-
|
|
569
|
-
# Continue in interactive mode with the initial command as history
|
|
570
|
-
await interactive_mode(message_renderer, initial_command=user_prompt)
|
|
571
|
-
else:
|
|
572
|
-
# No input provided, just go to interactive mode
|
|
573
|
-
await interactive_mode(message_renderer)
|
|
574
|
-
|
|
575
|
-
except (KeyboardInterrupt, EOFError):
|
|
576
|
-
from code_puppy.messaging import emit_warning
|
|
577
|
-
|
|
578
|
-
emit_warning("\nInput cancelled. Starting interactive mode...")
|
|
579
|
-
await interactive_mode(message_renderer)
|
|
580
|
-
except Exception as e:
|
|
581
|
-
from code_puppy.messaging import emit_error
|
|
582
|
-
|
|
583
|
-
emit_error(f"Error in prompt mode: {str(e)}")
|
|
584
|
-
emit_info("Falling back to interactive mode...")
|
|
585
|
-
await interactive_mode(message_renderer)
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
def main_entry():
|
|
589
|
-
"""Entry point for the installed CLI tool."""
|
|
590
|
-
try:
|
|
591
|
-
asyncio.run(main())
|
|
592
|
-
except KeyboardInterrupt:
|
|
593
|
-
# Just exit gracefully with no error message
|
|
594
|
-
callbacks.on_shutdown()
|
|
595
|
-
return 0
|
|
3
|
+
This module re-exports the main_entry function from cli_runner for backwards compatibility.
|
|
4
|
+
All the actual logic lives in cli_runner.py.
|
|
5
|
+
"""
|
|
596
6
|
|
|
7
|
+
from code_puppy.cli_runner import main_entry
|
|
597
8
|
|
|
598
9
|
if __name__ == "__main__":
|
|
599
10
|
main_entry()
|
|
@@ -17,6 +17,15 @@ from .error_isolation import (
|
|
|
17
17
|
)
|
|
18
18
|
from .managed_server import ManagedMCPServer, ServerConfig, ServerState
|
|
19
19
|
from .manager import MCPManager, ServerInfo, get_mcp_manager
|
|
20
|
+
from .mcp_logs import (
|
|
21
|
+
clear_logs,
|
|
22
|
+
get_log_file_path,
|
|
23
|
+
get_log_stats,
|
|
24
|
+
get_mcp_logs_dir,
|
|
25
|
+
list_servers_with_logs,
|
|
26
|
+
read_logs,
|
|
27
|
+
write_log,
|
|
28
|
+
)
|
|
20
29
|
from .registry import ServerRegistry
|
|
21
30
|
from .retry_manager import RetryManager, RetryStats, get_retry_manager, retry_mcp_call
|
|
22
31
|
from .status_tracker import Event, ServerStatusTracker
|
|
@@ -46,4 +55,12 @@ __all__ = [
|
|
|
46
55
|
"MCPDashboard",
|
|
47
56
|
"MCPConfigWizard",
|
|
48
57
|
"run_add_wizard",
|
|
58
|
+
# Log management
|
|
59
|
+
"get_mcp_logs_dir",
|
|
60
|
+
"get_log_file_path",
|
|
61
|
+
"read_logs",
|
|
62
|
+
"write_log",
|
|
63
|
+
"clear_logs",
|
|
64
|
+
"list_servers_with_logs",
|
|
65
|
+
"get_log_stats",
|
|
49
66
|
]
|