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
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Shared spinner implementation for
|
|
2
|
+
Shared spinner implementation for CLI mode.
|
|
3
3
|
|
|
4
4
|
This module provides consistent spinner animations across different UI modes.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from .console_spinner import ConsoleSpinner
|
|
8
8
|
from .spinner_base import SpinnerBase
|
|
9
|
-
from .textual_spinner import TextualSpinner
|
|
10
9
|
|
|
11
10
|
# Keep track of all active spinners to manage them globally
|
|
12
11
|
_active_spinners = []
|
|
@@ -25,7 +24,16 @@ def unregister_spinner(spinner):
|
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
def pause_all_spinners():
|
|
28
|
-
"""Pause all active spinners.
|
|
27
|
+
"""Pause all active spinners.
|
|
28
|
+
|
|
29
|
+
No-op when called from a sub-agent context to prevent
|
|
30
|
+
parallel sub-agents from interfering with the main spinner.
|
|
31
|
+
"""
|
|
32
|
+
# Lazy import to avoid circular dependency
|
|
33
|
+
from code_puppy.tools.subagent_context import is_subagent
|
|
34
|
+
|
|
35
|
+
if is_subagent():
|
|
36
|
+
return # Sub-agents don't control the main spinner
|
|
29
37
|
for spinner in _active_spinners:
|
|
30
38
|
try:
|
|
31
39
|
spinner.pause()
|
|
@@ -35,7 +43,16 @@ def pause_all_spinners():
|
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
def resume_all_spinners():
|
|
38
|
-
"""Resume all active spinners.
|
|
46
|
+
"""Resume all active spinners.
|
|
47
|
+
|
|
48
|
+
No-op when called from a sub-agent context to prevent
|
|
49
|
+
parallel sub-agents from interfering with the main spinner.
|
|
50
|
+
"""
|
|
51
|
+
# Lazy import to avoid circular dependency
|
|
52
|
+
from code_puppy.tools.subagent_context import is_subagent
|
|
53
|
+
|
|
54
|
+
if is_subagent():
|
|
55
|
+
return # Sub-agents don't control the main spinner
|
|
39
56
|
for spinner in _active_spinners:
|
|
40
57
|
try:
|
|
41
58
|
spinner.resume()
|
|
@@ -44,12 +61,23 @@ def resume_all_spinners():
|
|
|
44
61
|
pass
|
|
45
62
|
|
|
46
63
|
|
|
64
|
+
def update_spinner_context(info: str) -> None:
|
|
65
|
+
"""Update the shared context information displayed beside active spinners."""
|
|
66
|
+
SpinnerBase.set_context_info(info)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def clear_spinner_context() -> None:
|
|
70
|
+
"""Clear any context information displayed beside active spinners."""
|
|
71
|
+
SpinnerBase.clear_context_info()
|
|
72
|
+
|
|
73
|
+
|
|
47
74
|
__all__ = [
|
|
48
75
|
"SpinnerBase",
|
|
49
|
-
"TextualSpinner",
|
|
50
76
|
"ConsoleSpinner",
|
|
51
77
|
"register_spinner",
|
|
52
78
|
"unregister_spinner",
|
|
53
79
|
"pause_all_spinners",
|
|
54
80
|
"resume_all_spinners",
|
|
81
|
+
"update_spinner_context",
|
|
82
|
+
"clear_spinner_context",
|
|
55
83
|
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Console spinner implementation for CLI mode using Rich's Live Display.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import platform
|
|
5
6
|
import threading
|
|
6
7
|
import time
|
|
7
8
|
|
|
@@ -43,12 +44,15 @@ class ConsoleSpinner(SpinnerBase):
|
|
|
43
44
|
if self._thread and self._thread.is_alive():
|
|
44
45
|
return
|
|
45
46
|
|
|
47
|
+
# Print blank line before spinner for visual separation from content
|
|
48
|
+
self.console.print()
|
|
49
|
+
|
|
46
50
|
# Create a Live display for the spinner
|
|
47
51
|
self._live = Live(
|
|
48
52
|
self._generate_spinner_panel(),
|
|
49
53
|
console=self.console,
|
|
50
|
-
refresh_per_second=
|
|
51
|
-
transient=True,
|
|
54
|
+
refresh_per_second=20,
|
|
55
|
+
transient=True, # Clear the spinner line when stopped (no puppy litter!)
|
|
52
56
|
auto_refresh=False, # Don't auto-refresh to avoid wiping out user input
|
|
53
57
|
)
|
|
54
58
|
self._live.start()
|
|
@@ -75,6 +79,33 @@ class ConsoleSpinner(SpinnerBase):
|
|
|
75
79
|
|
|
76
80
|
self._thread = None
|
|
77
81
|
|
|
82
|
+
# Windows-specific cleanup: Rich's Live display can leave terminal in corrupted state
|
|
83
|
+
if platform.system() == "Windows":
|
|
84
|
+
import sys
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
# Reset ANSI formatting for both stdout and stderr
|
|
88
|
+
sys.stdout.write("\x1b[0m") # Reset all attributes
|
|
89
|
+
sys.stdout.flush()
|
|
90
|
+
sys.stderr.write("\x1b[0m")
|
|
91
|
+
sys.stderr.flush()
|
|
92
|
+
|
|
93
|
+
# Clear the line and reposition cursor
|
|
94
|
+
sys.stdout.write("\r") # Return to start of line
|
|
95
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
96
|
+
sys.stdout.flush()
|
|
97
|
+
|
|
98
|
+
# Flush keyboard input buffer to clear any stuck keys
|
|
99
|
+
try:
|
|
100
|
+
import msvcrt
|
|
101
|
+
|
|
102
|
+
while msvcrt.kbhit():
|
|
103
|
+
msvcrt.getch()
|
|
104
|
+
except ImportError:
|
|
105
|
+
pass # msvcrt not available (not Windows or different Python impl)
|
|
106
|
+
except Exception:
|
|
107
|
+
pass # Fail silently if cleanup doesn't work
|
|
108
|
+
|
|
78
109
|
# Unregister this spinner from global management
|
|
79
110
|
from . import unregister_spinner
|
|
80
111
|
|
|
@@ -86,23 +117,23 @@ class ConsoleSpinner(SpinnerBase):
|
|
|
86
117
|
|
|
87
118
|
def _generate_spinner_panel(self):
|
|
88
119
|
"""Generate a Rich panel containing the spinner text."""
|
|
89
|
-
if
|
|
120
|
+
# Check if we're awaiting user input - show nothing during input prompts
|
|
121
|
+
from code_puppy.tools.command_runner import is_awaiting_user_input
|
|
122
|
+
|
|
123
|
+
if self._paused or is_awaiting_user_input():
|
|
90
124
|
return Text("")
|
|
91
125
|
|
|
92
126
|
text = Text()
|
|
93
127
|
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if is_awaiting_user_input():
|
|
98
|
-
# Show waiting message when waiting for user input
|
|
99
|
-
text.append(SpinnerBase.WAITING_MESSAGE, style="bold cyan")
|
|
100
|
-
else:
|
|
101
|
-
# Show thinking message during normal processing
|
|
102
|
-
text.append(SpinnerBase.THINKING_MESSAGE, style="bold cyan")
|
|
103
|
-
|
|
128
|
+
# Show thinking message during normal processing
|
|
129
|
+
text.append(SpinnerBase.THINKING_MESSAGE, style="bold cyan")
|
|
104
130
|
text.append(self.current_frame, style="bold cyan")
|
|
105
131
|
|
|
132
|
+
context_info = SpinnerBase.get_context_info()
|
|
133
|
+
if context_info:
|
|
134
|
+
text.append(" ")
|
|
135
|
+
text.append(context_info, style="bold white")
|
|
136
|
+
|
|
106
137
|
# Return a simple Text object instead of a Panel for a cleaner look
|
|
107
138
|
return text
|
|
108
139
|
|
|
@@ -127,39 +158,29 @@ class ConsoleSpinner(SpinnerBase):
|
|
|
127
158
|
# Short sleep to control animation speed
|
|
128
159
|
time.sleep(0.05)
|
|
129
160
|
except Exception as e:
|
|
130
|
-
|
|
161
|
+
# Note: Using sys.stderr - can't use messaging during spinner
|
|
162
|
+
import sys
|
|
163
|
+
|
|
164
|
+
sys.stderr.write(f"\nSpinner error: {e}\n")
|
|
131
165
|
self._is_spinning = False
|
|
132
166
|
|
|
133
167
|
def pause(self):
|
|
134
168
|
"""Pause the spinner animation."""
|
|
135
169
|
if self._is_spinning:
|
|
136
170
|
self._paused = True
|
|
137
|
-
#
|
|
171
|
+
# Stop the live display completely to restore terminal echo during input
|
|
138
172
|
if self._live:
|
|
139
173
|
try:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
self._live.update(text)
|
|
149
|
-
self._live.refresh()
|
|
150
|
-
# Allow a moment for the waiting message to be visible
|
|
151
|
-
import time
|
|
152
|
-
|
|
153
|
-
time.sleep(0.1)
|
|
154
|
-
|
|
155
|
-
# Then clear the display
|
|
156
|
-
self._live.update(Text(""))
|
|
174
|
+
self._live.stop()
|
|
175
|
+
self._live = None
|
|
176
|
+
# Clear the line to remove any artifacts
|
|
177
|
+
import sys
|
|
178
|
+
|
|
179
|
+
sys.stdout.write("\r") # Return to start of line
|
|
180
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
181
|
+
sys.stdout.flush()
|
|
157
182
|
except Exception:
|
|
158
|
-
|
|
159
|
-
try:
|
|
160
|
-
self._live.stop()
|
|
161
|
-
except Exception:
|
|
162
|
-
pass
|
|
183
|
+
pass
|
|
163
184
|
|
|
164
185
|
def resume(self):
|
|
165
186
|
"""Resume the spinner animation."""
|
|
@@ -171,24 +192,43 @@ class ConsoleSpinner(SpinnerBase):
|
|
|
171
192
|
|
|
172
193
|
if self._is_spinning and self._paused:
|
|
173
194
|
self._paused = False
|
|
174
|
-
#
|
|
175
|
-
if self._live:
|
|
195
|
+
# Restart the live display if it was stopped during pause
|
|
196
|
+
if not self._live:
|
|
197
|
+
try:
|
|
198
|
+
# Clear any leftover artifacts before starting
|
|
199
|
+
import sys
|
|
200
|
+
|
|
201
|
+
sys.stdout.write("\r") # Return to start of line
|
|
202
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
203
|
+
sys.stdout.flush()
|
|
204
|
+
|
|
205
|
+
# Print blank line before spinner for visual separation
|
|
206
|
+
self.console.print()
|
|
207
|
+
|
|
208
|
+
self._live = Live(
|
|
209
|
+
self._generate_spinner_panel(),
|
|
210
|
+
console=self.console,
|
|
211
|
+
refresh_per_second=20,
|
|
212
|
+
transient=True, # Clear spinner line when stopped
|
|
213
|
+
auto_refresh=False,
|
|
214
|
+
)
|
|
215
|
+
self._live.start()
|
|
216
|
+
except Exception:
|
|
217
|
+
pass
|
|
218
|
+
else:
|
|
219
|
+
# If live display still exists, clear console state first
|
|
176
220
|
try:
|
|
221
|
+
# Force Rich to reset any cached console state
|
|
222
|
+
if hasattr(self.console, "_buffer"):
|
|
223
|
+
# Clear Rich's internal buffer to prevent artifacts
|
|
224
|
+
self.console.file.write("\r") # Return to start
|
|
225
|
+
self.console.file.write("\x1b[K") # Clear line
|
|
226
|
+
self.console.file.flush()
|
|
227
|
+
|
|
177
228
|
self._live.update(self._generate_spinner_panel())
|
|
229
|
+
self._live.refresh()
|
|
178
230
|
except Exception:
|
|
179
|
-
|
|
180
|
-
# Try to restart it
|
|
181
|
-
try:
|
|
182
|
-
self._live = Live(
|
|
183
|
-
self._generate_spinner_panel(),
|
|
184
|
-
console=self.console,
|
|
185
|
-
refresh_per_second=10,
|
|
186
|
-
transient=True,
|
|
187
|
-
auto_refresh=False, # Don't auto-refresh to avoid wiping out user input
|
|
188
|
-
)
|
|
189
|
-
self._live.start()
|
|
190
|
-
except Exception:
|
|
191
|
-
pass
|
|
231
|
+
pass
|
|
192
232
|
|
|
193
233
|
def __enter__(self):
|
|
194
234
|
"""Support for context manager."""
|
|
@@ -3,6 +3,7 @@ Base spinner implementation to be extended for different UI modes.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
+
from threading import Lock
|
|
6
7
|
|
|
7
8
|
from code_puppy.config import get_puppy_name
|
|
8
9
|
|
|
@@ -33,6 +34,9 @@ class SpinnerBase(ABC):
|
|
|
33
34
|
# Current message - starts with thinking by default
|
|
34
35
|
MESSAGE = THINKING_MESSAGE
|
|
35
36
|
|
|
37
|
+
_context_info: str = ""
|
|
38
|
+
_context_lock: Lock = Lock()
|
|
39
|
+
|
|
36
40
|
def __init__(self):
|
|
37
41
|
"""Initialize the spinner."""
|
|
38
42
|
self._is_spinning = False
|
|
@@ -64,3 +68,28 @@ class SpinnerBase(ABC):
|
|
|
64
68
|
def is_spinning(self):
|
|
65
69
|
"""Check if the spinner is currently spinning."""
|
|
66
70
|
return self._is_spinning
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def set_context_info(cls, info: str) -> None:
|
|
74
|
+
"""Set shared context information displayed beside the spinner."""
|
|
75
|
+
with cls._context_lock:
|
|
76
|
+
cls._context_info = info
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def clear_context_info(cls) -> None:
|
|
80
|
+
"""Clear any context information displayed beside the spinner."""
|
|
81
|
+
cls.set_context_info("")
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def get_context_info(cls) -> str:
|
|
85
|
+
"""Return the current spinner context information."""
|
|
86
|
+
with cls._context_lock:
|
|
87
|
+
return cls._context_info
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def format_context_info(total_tokens: int, capacity: int, proportion: float) -> str:
|
|
91
|
+
"""Create a concise context summary for spinner display."""
|
|
92
|
+
if capacity <= 0:
|
|
93
|
+
return ""
|
|
94
|
+
proportion_pct = proportion * 100
|
|
95
|
+
return f"Tokens: {total_tokens:,}/{capacity:,} ({proportion_pct:.1f}% used)"
|