code-puppy 0.0.196__tar.gz → 0.0.198__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.
- {code_puppy-0.0.196 → code_puppy-0.0.198}/PKG-INFO +13 -2
- {code_puppy-0.0.196 → code_puppy-0.0.198}/README.md +11 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/base_agent.py +3 -5
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/json_agent.py +8 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/command_handler.py +108 -113
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/prompt_toolkit_completion.py +34 -10
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/config.py +6 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/main.py +6 -6
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/app.py +153 -7
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/input_area.py +1 -1
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/status_bar.py +4 -1
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/screens/__init__.py +2 -0
- code_puppy-0.0.198/code_puppy/tui/screens/autosave_picker.py +166 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/screens/settings.py +4 -2
- {code_puppy-0.0.196 → code_puppy-0.0.198}/pyproject.toml +2 -2
- {code_puppy-0.0.196 → code_puppy-0.0.198}/.gitignore +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/LICENSE +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_c_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_code_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_golang_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_python_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_qa_expert.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_security_auditor.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/add_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/test_command.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/motd.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/messaging/spinner/textual_spinner.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/models.json +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/summarization_agent.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/camoufox_manager.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/browser/vqa_agent.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/chat_view.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/command_history_modal.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/copy_button.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/custom_widgets.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/human_input_modal.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/components/sidebar.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/messages.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/models/__init__.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/models/chat_message.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/models/command_history.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/models/enums.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/screens/help.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/screens/mcp_install_wizard.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui/screens/tools.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/tui_state.py +0 -0
- {code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/version_checker.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: code-puppy
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.198
|
4
4
|
Summary: Code generation agent
|
5
5
|
Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
|
6
6
|
Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
|
@@ -25,7 +25,7 @@ Requires-Dist: logfire>=0.7.1
|
|
25
25
|
Requires-Dist: openai>=1.99.1
|
26
26
|
Requires-Dist: pathspec>=0.11.0
|
27
27
|
Requires-Dist: playwright>=1.40.0
|
28
|
-
Requires-Dist: prompt-toolkit>=3.0.
|
28
|
+
Requires-Dist: prompt-toolkit>=3.0.52
|
29
29
|
Requires-Dist: pydantic-ai==1.0.6
|
30
30
|
Requires-Dist: pydantic>=2.4.0
|
31
31
|
Requires-Dist: pyjwt>=2.8.0
|
@@ -69,6 +69,17 @@ Code Puppy is an AI-powered code generation agent, designed to understand progra
|
|
69
69
|
|
70
70
|
## Features
|
71
71
|
|
72
|
+
### Session Autosave & Contexts
|
73
|
+
- Autosaves live in `~/.code_puppy/autosaves` and include a `.pkl` and `_meta.json` per session.
|
74
|
+
- On startup, you’ll be prompted to optionally load a recent autosave (with message counts and timestamps).
|
75
|
+
- Autosaves use a stable session ID per interactive run so subsequent prompts overwrite the same session (not N new files). Rotate via `/session new` when you want a fresh session.
|
76
|
+
- Loading an autosave makes it the active autosave target (future autosaves overwrite that loaded session).
|
77
|
+
- Loading a manual context with `/load_context <name>` automatically rotates the autosave ID to avoid overwriting anything.
|
78
|
+
- Helpers:
|
79
|
+
- `/session id` shows the current autosave ID and file prefix
|
80
|
+
- `/session new` rotates the autosave ID
|
81
|
+
|
82
|
+
|
72
83
|
- **Multi-language support**: Capable of generating code in various programming languages.
|
73
84
|
- **Interactive CLI**: A command-line interface for interactive use.
|
74
85
|
- **Detailed explanations**: Provides insights into generated code to understand its logic and structure.
|
@@ -25,6 +25,17 @@ Code Puppy is an AI-powered code generation agent, designed to understand progra
|
|
25
25
|
|
26
26
|
## Features
|
27
27
|
|
28
|
+
### Session Autosave & Contexts
|
29
|
+
- Autosaves live in `~/.code_puppy/autosaves` and include a `.pkl` and `_meta.json` per session.
|
30
|
+
- On startup, you’ll be prompted to optionally load a recent autosave (with message counts and timestamps).
|
31
|
+
- Autosaves use a stable session ID per interactive run so subsequent prompts overwrite the same session (not N new files). Rotate via `/session new` when you want a fresh session.
|
32
|
+
- Loading an autosave makes it the active autosave target (future autosaves overwrite that loaded session).
|
33
|
+
- Loading a manual context with `/load_context <name>` automatically rotates the autosave ID to avoid overwriting anything.
|
34
|
+
- Helpers:
|
35
|
+
- `/session id` shows the current autosave ID and file prefix
|
36
|
+
- `/session new` rotates the autosave ID
|
37
|
+
|
38
|
+
|
28
39
|
- **Multi-language support**: Capable of generating code in various programming languages.
|
29
40
|
- **Interactive CLI**: A command-line interface for interactive use.
|
30
41
|
- **Detailed explanations**: Provides insights into generated code to understand its logic and structure.
|
@@ -720,10 +720,7 @@ class BaseAgent(ABC):
|
|
720
720
|
emit_system_message(
|
721
721
|
f"[green]Successfully loaded {len(servers)} MCP server(s)[/green]"
|
722
722
|
)
|
723
|
-
|
724
|
-
emit_system_message(
|
725
|
-
"[yellow]No MCP servers available (check if servers are enabled)[/yellow]"
|
726
|
-
)
|
723
|
+
# Stay silent when there are no servers configured/available
|
727
724
|
return servers
|
728
725
|
|
729
726
|
def reload_mcp_servers(self):
|
@@ -891,7 +888,8 @@ class BaseAgent(ABC):
|
|
891
888
|
asyncio.CancelledError: When execution is cancelled by user
|
892
889
|
"""
|
893
890
|
group_id = str(uuid.uuid4())
|
894
|
-
|
891
|
+
# Avoid double-loading: reuse existing agent if already built
|
892
|
+
pydantic_agent = self._code_generation_agent or self.reload_code_generation_agent()
|
895
893
|
|
896
894
|
async def run_agent_task():
|
897
895
|
try:
|
@@ -101,6 +101,14 @@ class JSONAgent(BaseAgent):
|
|
101
101
|
"""Get tool configuration from JSON config."""
|
102
102
|
return self._config.get("tools_config")
|
103
103
|
|
104
|
+
def refresh_config(self) -> None:
|
105
|
+
"""Reload the agent configuration from disk.
|
106
|
+
|
107
|
+
This keeps long-lived agent instances in sync after external edits.
|
108
|
+
"""
|
109
|
+
self._config = self._load_config()
|
110
|
+
self._validate_config()
|
111
|
+
|
104
112
|
def get_model_name(self) -> Optional[str]:
|
105
113
|
"""Get pinned model name from JSON config, if specified.
|
106
114
|
|
@@ -5,131 +5,103 @@ from pathlib import Path
|
|
5
5
|
from code_puppy.command_line.model_picker_completion import update_model_in_input
|
6
6
|
from code_puppy.command_line.motd import print_motd
|
7
7
|
from code_puppy.command_line.utils import make_directory_table
|
8
|
-
from code_puppy.config import
|
8
|
+
from code_puppy.config import (
|
9
|
+
CONTEXTS_DIR,
|
10
|
+
finalize_autosave_session,
|
11
|
+
get_config_keys,
|
12
|
+
)
|
9
13
|
from code_puppy.session_storage import list_sessions, load_session, save_session
|
10
14
|
from code_puppy.tools.tools_content import tools_content
|
11
15
|
|
12
16
|
|
13
17
|
def get_commands_help():
|
14
|
-
"""Generate commands help using Rich Text
|
18
|
+
"""Generate aligned commands help using Rich Text for safe markup."""
|
15
19
|
from rich.text import Text
|
16
20
|
|
17
21
|
# Ensure plugins are loaded so custom help can register
|
18
22
|
_ensure_plugins_loaded()
|
19
23
|
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
help_lines.append(
|
69
|
-
Text("/compact", style="cyan")
|
70
|
-
+ Text(
|
71
|
-
" Summarize and compact current chat history (uses compaction_strategy config)"
|
72
|
-
)
|
73
|
-
)
|
74
|
-
help_lines.append(
|
75
|
-
Text("/dump_context", style="cyan")
|
76
|
-
+ Text(" <name> Save current message history to file")
|
77
|
-
)
|
78
|
-
help_lines.append(
|
79
|
-
Text("/load_context", style="cyan")
|
80
|
-
+ Text(" <name> Load message history from file")
|
81
|
-
)
|
82
|
-
help_lines.append(
|
83
|
-
Text("/set", style="cyan")
|
84
|
-
+ Text(
|
85
|
-
" Set puppy config key-values (e.g., /set yolo_mode true, /set auto_save_session true)"
|
86
|
-
)
|
87
|
-
)
|
88
|
-
help_lines.append(
|
89
|
-
Text("/tools", style="cyan")
|
90
|
-
+ Text(" Show available tools and capabilities")
|
91
|
-
)
|
92
|
-
help_lines.append(
|
93
|
-
Text("/truncate", style="cyan")
|
94
|
-
+ Text(
|
95
|
-
" <N> Truncate message history to N most recent messages (keeping system message)"
|
96
|
-
)
|
97
|
-
)
|
98
|
-
help_lines.append(
|
99
|
-
Text("/<unknown>", style="cyan")
|
100
|
-
+ Text(" Show unknown command warning")
|
101
|
-
)
|
24
|
+
# Collect core commands with their syntax parts and descriptions
|
25
|
+
# (cmd_syntax, description)
|
26
|
+
core_cmds = [
|
27
|
+
("/help, /h", "Show this help message"),
|
28
|
+
("/cd <dir>", "Change directory or show directories"),
|
29
|
+
(
|
30
|
+
"/agent <name>",
|
31
|
+
"Switch to a different agent or show available agents",
|
32
|
+
),
|
33
|
+
("/exit, /quit", "Exit interactive mode"),
|
34
|
+
("/generate-pr-description [@dir]", "Generate comprehensive PR description"),
|
35
|
+
("/model, /m <model>", "Set active model"),
|
36
|
+
("/reasoning <low|medium|high>", "Set OpenAI reasoning effort for GPT-5 models"),
|
37
|
+
("/pin_model <agent> <model>", "Pin a specific model to an agent"),
|
38
|
+
("/mcp", "Manage MCP servers (list, start, stop, status, etc.)"),
|
39
|
+
("/motd", "Show the latest message of the day (MOTD)"),
|
40
|
+
("/show", "Show puppy config key-values"),
|
41
|
+
(
|
42
|
+
"/compact",
|
43
|
+
"Summarize and compact current chat history (uses compaction_strategy config)",
|
44
|
+
),
|
45
|
+
("/dump_context <name>", "Save current message history to file"),
|
46
|
+
("/load_context <name>", "Load message history from file"),
|
47
|
+
(
|
48
|
+
"/set",
|
49
|
+
"Set puppy config (e.g., /set yolo_mode true, /set auto_save_session true)",
|
50
|
+
),
|
51
|
+
("/tools", "Show available tools and capabilities"),
|
52
|
+
(
|
53
|
+
"/truncate <N>",
|
54
|
+
"Truncate history to N most recent messages (keeping system message)",
|
55
|
+
),
|
56
|
+
("/<unknown>", "Show unknown command warning"),
|
57
|
+
]
|
58
|
+
|
59
|
+
# Determine padding width for the left column
|
60
|
+
left_width = max(len(cmd) for cmd, _ in core_cmds) + 2 # add spacing
|
61
|
+
|
62
|
+
lines: list[Text] = []
|
63
|
+
lines.append(Text("Commands Help", style="bold magenta"))
|
64
|
+
|
65
|
+
for cmd, desc in core_cmds:
|
66
|
+
left = Text(cmd.ljust(left_width), style="cyan")
|
67
|
+
right = Text(desc)
|
68
|
+
line = Text()
|
69
|
+
line.append_text(left)
|
70
|
+
line.append_text(right)
|
71
|
+
lines.append(line)
|
102
72
|
|
103
73
|
# Add custom commands from plugins (if any)
|
104
74
|
try:
|
105
75
|
from code_puppy import callbacks
|
106
76
|
|
107
77
|
custom_help_results = callbacks.on_custom_command_help()
|
108
|
-
|
109
|
-
custom_entries = []
|
78
|
+
custom_entries: list[tuple[str, str]] = []
|
110
79
|
for res in custom_help_results:
|
111
80
|
if not res:
|
112
81
|
continue
|
113
82
|
if isinstance(res, tuple) and len(res) == 2:
|
114
|
-
custom_entries.append(res)
|
83
|
+
custom_entries.append((str(res[0]), str(res[1])))
|
115
84
|
elif isinstance(res, list):
|
116
85
|
for item in res:
|
117
86
|
if isinstance(item, tuple) and len(item) == 2:
|
118
|
-
custom_entries.append(item)
|
87
|
+
custom_entries.append((str(item[0]), str(item[1])))
|
119
88
|
if custom_entries:
|
120
|
-
|
121
|
-
|
89
|
+
lines.append(Text("", style="dim"))
|
90
|
+
lines.append(Text("Custom Commands", style="bold magenta"))
|
91
|
+
# Compute padding for custom commands as well
|
92
|
+
custom_left_width = max(len(name) for name, _ in custom_entries) + 3
|
122
93
|
for name, desc in custom_entries:
|
123
|
-
|
124
|
-
|
125
|
-
)
|
94
|
+
left = Text(f"/{name}".ljust(custom_left_width), style="cyan")
|
95
|
+
right = Text(desc)
|
96
|
+
line = Text()
|
97
|
+
line.append_text(left)
|
98
|
+
line.append_text(right)
|
99
|
+
lines.append(line)
|
126
100
|
except Exception:
|
127
|
-
# If callbacks fail, skip custom help silently
|
128
101
|
pass
|
129
102
|
|
130
|
-
# Combine all lines
|
131
103
|
final_text = Text()
|
132
|
-
for i, line in enumerate(
|
104
|
+
for i, line in enumerate(lines):
|
133
105
|
if i > 0:
|
134
106
|
final_text.append("\n")
|
135
107
|
final_text.append_text(line)
|
@@ -461,31 +433,44 @@ def handle_command(command: str):
|
|
461
433
|
import uuid
|
462
434
|
|
463
435
|
group_id = str(uuid.uuid4())
|
436
|
+
available_agents = get_available_agents()
|
464
437
|
|
465
|
-
if
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
new_agent = get_current_agent()
|
470
|
-
emit_success(
|
471
|
-
f"Switched to agent: {new_agent.display_name}",
|
438
|
+
if agent_name not in available_agents:
|
439
|
+
emit_error(f"Agent '{agent_name}' not found", message_group=group_id)
|
440
|
+
emit_warning(
|
441
|
+
f"Available agents: {', '.join(available_agents.keys())}",
|
472
442
|
message_group=group_id,
|
473
443
|
)
|
474
|
-
emit_info(f"[dim]{new_agent.description}[/dim]", message_group=group_id)
|
475
444
|
return True
|
476
|
-
else:
|
477
|
-
# Generate a group ID for all messages in this command
|
478
|
-
import uuid
|
479
445
|
|
480
|
-
|
446
|
+
current_agent = get_current_agent()
|
447
|
+
if current_agent.name == agent_name:
|
448
|
+
emit_info(
|
449
|
+
f"Already using agent: {current_agent.display_name}",
|
450
|
+
message_group=group_id,
|
451
|
+
)
|
452
|
+
return True
|
481
453
|
|
482
|
-
|
483
|
-
|
454
|
+
new_session_id = finalize_autosave_session()
|
455
|
+
if not set_current_agent(agent_name):
|
484
456
|
emit_warning(
|
485
|
-
|
457
|
+
"Agent switch failed after autosave rotation. Your context was preserved.",
|
486
458
|
message_group=group_id,
|
487
459
|
)
|
488
460
|
return True
|
461
|
+
|
462
|
+
new_agent = get_current_agent()
|
463
|
+
new_agent.reload_code_generation_agent()
|
464
|
+
emit_success(
|
465
|
+
f"Switched to agent: {new_agent.display_name}",
|
466
|
+
message_group=group_id,
|
467
|
+
)
|
468
|
+
emit_info(f"[dim]{new_agent.description}[/dim]", message_group=group_id)
|
469
|
+
emit_info(
|
470
|
+
f"[dim]Auto-save session rotated to: {new_session_id}[/dim]",
|
471
|
+
message_group=group_id,
|
472
|
+
)
|
473
|
+
return True
|
489
474
|
else:
|
490
475
|
emit_warning("Usage: /agent [agent-name]")
|
491
476
|
return True
|
@@ -625,12 +610,22 @@ def handle_command(command: str):
|
|
625
610
|
|
626
611
|
emit_success(f"Model '{model_name}' pinned to agent '{agent_name}'")
|
627
612
|
|
628
|
-
# If this is the current agent,
|
613
|
+
# If this is the current agent, refresh it so the prompt updates immediately
|
629
614
|
from code_puppy.agents import get_current_agent
|
630
615
|
|
631
616
|
current_agent = get_current_agent()
|
632
617
|
if current_agent.name == agent_name:
|
633
|
-
|
618
|
+
try:
|
619
|
+
if is_json_agent and hasattr(current_agent, "refresh_config"):
|
620
|
+
current_agent.refresh_config()
|
621
|
+
current_agent.reload_code_generation_agent()
|
622
|
+
emit_info(
|
623
|
+
f"Active agent reloaded with pinned model '{model_name}'"
|
624
|
+
)
|
625
|
+
except Exception as reload_error:
|
626
|
+
emit_warning(
|
627
|
+
f"Pinned model applied but reload failed: {reload_error}"
|
628
|
+
)
|
634
629
|
|
635
630
|
return True
|
636
631
|
|
{code_puppy-0.0.196 → code_puppy-0.0.198}/code_puppy/command_line/prompt_toolkit_completion.py
RENAMED
@@ -194,24 +194,48 @@ async def get_input_with_combined_completion(
|
|
194
194
|
LoadContextCompleter(trigger="/load_context"),
|
195
195
|
]
|
196
196
|
)
|
197
|
-
# Add custom key bindings
|
197
|
+
# Add custom key bindings and multiline toggle
|
198
198
|
bindings = KeyBindings()
|
199
199
|
|
200
|
-
|
200
|
+
# Multiline mode state
|
201
|
+
multiline = {"enabled": False}
|
202
|
+
|
203
|
+
# Toggle multiline with Alt+M
|
204
|
+
@bindings.add(Keys.Escape, "m")
|
201
205
|
def _(event):
|
202
|
-
|
206
|
+
multiline["enabled"] = not multiline["enabled"]
|
207
|
+
status = "ON" if multiline["enabled"] else "OFF"
|
208
|
+
# Print status for user feedback (version-agnostic)
|
209
|
+
print(f"[multiline] {status}", flush=True)
|
210
|
+
|
211
|
+
# Also toggle multiline with F2 (more reliable across platforms)
|
212
|
+
@bindings.add("f2")
|
213
|
+
def _(event):
|
214
|
+
multiline["enabled"] = not multiline["enabled"]
|
215
|
+
status = "ON" if multiline["enabled"] else "OFF"
|
216
|
+
print(f"[multiline] {status}", flush=True)
|
203
217
|
|
204
|
-
#
|
205
|
-
|
218
|
+
# Newline insert bindings — robust and explicit
|
219
|
+
# Ctrl+J (line feed) works in virtually all terminals; mark eager so it wins
|
220
|
+
@bindings.add("c-j", eager=True)
|
206
221
|
def _(event):
|
207
|
-
"""Pressing alt+enter (meta+enter) inserts a newline."""
|
208
222
|
event.app.current_buffer.insert_text("\n")
|
209
223
|
|
210
|
-
#
|
211
|
-
|
224
|
+
# Also allow Ctrl+Enter for newline (terminal-dependent)
|
225
|
+
try:
|
226
|
+
@bindings.add("c-enter", eager=True)
|
227
|
+
def _(event):
|
228
|
+
event.app.current_buffer.insert_text("\n")
|
229
|
+
except Exception:
|
230
|
+
pass
|
231
|
+
|
232
|
+
# Enter behavior depends on multiline mode
|
233
|
+
@bindings.add("enter", filter=~is_searching, eager=True)
|
212
234
|
def _(event):
|
213
|
-
|
214
|
-
|
235
|
+
if multiline["enabled"]:
|
236
|
+
event.app.current_buffer.insert_text("\n")
|
237
|
+
else:
|
238
|
+
event.current_buffer.validate_and_handle()
|
215
239
|
|
216
240
|
@bindings.add(Keys.Escape)
|
217
241
|
def _(event):
|
@@ -784,3 +784,9 @@ def auto_save_session_if_enabled() -> bool:
|
|
784
784
|
|
785
785
|
Console().print(f"[dim]❌ Failed to auto-save session: {exc}[/dim]")
|
786
786
|
return False
|
787
|
+
|
788
|
+
|
789
|
+
def finalize_autosave_session() -> str:
|
790
|
+
"""Persist the current autosave snapshot and rotate to a fresh session."""
|
791
|
+
auto_save_session_if_enabled()
|
792
|
+
return rotate_autosave_id()
|
@@ -24,6 +24,7 @@ from code_puppy.config import (
|
|
24
24
|
AUTOSAVE_DIR,
|
25
25
|
COMMAND_HISTORY_FILE,
|
26
26
|
ensure_config_exists,
|
27
|
+
finalize_autosave_session,
|
27
28
|
initialize_command_history_file,
|
28
29
|
save_command_to_history,
|
29
30
|
)
|
@@ -272,16 +273,13 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
272
273
|
emit_info("[bold green]Code Puppy[/bold green] - Interactive Mode")
|
273
274
|
emit_system_message("Type '/exit' or '/quit' to exit the interactive mode.")
|
274
275
|
emit_system_message("Type 'clear' to reset the conversation history.")
|
276
|
+
emit_system_message("[dim]Type /help to view all commands[/dim]")
|
275
277
|
emit_system_message(
|
276
|
-
"Type [bold blue]@[/bold blue] for path completion, or [bold blue]/m[/bold blue] to pick a model.
|
278
|
+
"Type [bold blue]@[/bold blue] for path completion, or [bold blue]/m[/bold blue] to pick a model. Toggle multiline with [bold blue]Alt+M[/bold blue] or [bold blue]F2[/bold blue]; newline: [bold blue]Ctrl+J[/bold blue]."
|
277
279
|
)
|
278
280
|
emit_system_message(
|
279
281
|
"Press [bold red]Ctrl+C[/bold red] during processing to cancel the current task or inference."
|
280
282
|
)
|
281
|
-
from code_puppy.command_line.command_handler import get_commands_help
|
282
|
-
|
283
|
-
help_text = get_commands_help()
|
284
|
-
emit_system_message(help_text)
|
285
283
|
try:
|
286
284
|
from code_puppy.command_line.motd import print_motd
|
287
285
|
|
@@ -417,12 +415,14 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
417
415
|
|
418
416
|
# Check for clear command (supports both `clear` and `/clear`)
|
419
417
|
if task.strip().lower() in ("clear", "/clear"):
|
420
|
-
from code_puppy.messaging import emit_system_message, emit_warning
|
418
|
+
from code_puppy.messaging import emit_info, emit_system_message, emit_warning
|
421
419
|
|
422
420
|
agent = get_current_agent()
|
421
|
+
new_session_id = finalize_autosave_session()
|
423
422
|
agent.clear_message_history()
|
424
423
|
emit_warning("Conversation history cleared!")
|
425
424
|
emit_system_message("The agent will not remember previous interactions.\n")
|
425
|
+
emit_info(f"[dim]Auto-save session rotated to: {new_session_id}[/dim]")
|
426
426
|
continue
|
427
427
|
|
428
428
|
# Handle / commands before anything else
|