codepp 0.0.437__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 +10 -0
- code_puppy/__main__.py +10 -0
- code_puppy/agents/__init__.py +31 -0
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +117 -0
- 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 +638 -0
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_helios.py +124 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +742 -0
- code_puppy/agents/agent_pack_leader.py +385 -0
- code_puppy/agents/agent_planning.py +165 -0
- code_puppy/agents/agent_python_programmer.py +169 -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_scheduler.py +121 -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 +2156 -0
- code_puppy/agents/event_stream_handler.py +348 -0
- code_puppy/agents/json_agent.py +202 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +327 -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 +453 -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 +75 -0
- code_puppy/api/routers/sessions.py +234 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +692 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +672 -0
- code_puppy/cli_runner.py +1073 -0
- code_puppy/command_line/__init__.py +1 -0
- code_puppy/command_line/add_model_menu.py +1092 -0
- code_puppy/command_line/agent_menu.py +662 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +704 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +532 -0
- code_puppy/command_line/command_handler.py +293 -0
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +719 -0
- code_puppy/command_line/core_commands.py +867 -0
- code_puppy/command_line/diff_menu.py +865 -0
- code_puppy/command_line/file_path_completion.py +73 -0
- code_puppy/command_line/load_context_completion.py +52 -0
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/base.py +32 -0
- 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 +138 -0
- code_puppy/command_line/mcp/help_command.py +147 -0
- code_puppy/command_line/mcp/install_command.py +214 -0
- code_puppy/command_line/mcp/install_menu.py +705 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +235 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +100 -0
- code_puppy/command_line/mcp/search_command.py +123 -0
- code_puppy/command_line/mcp/start_all_command.py +135 -0
- code_puppy/command_line/mcp/start_command.py +117 -0
- code_puppy/command_line/mcp/status_command.py +184 -0
- code_puppy/command_line/mcp/stop_all_command.py +112 -0
- code_puppy/command_line/mcp/stop_command.py +80 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +334 -0
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +197 -0
- code_puppy/command_line/model_settings_menu.py +932 -0
- code_puppy/command_line/motd.py +96 -0
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +342 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +846 -0
- code_puppy/command_line/session_commands.py +302 -0
- code_puppy/command_line/shell_passthrough.py +145 -0
- code_puppy/command_line/skills_completion.py +160 -0
- code_puppy/command_line/uc_menu.py +893 -0
- code_puppy/command_line/utils.py +93 -0
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/config.py +1770 -0
- code_puppy/error_logging.py +134 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +754 -0
- code_puppy/hook_engine/README.md +105 -0
- code_puppy/hook_engine/__init__.py +21 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +221 -0
- code_puppy/hook_engine/executor.py +296 -0
- code_puppy/hook_engine/matcher.py +156 -0
- code_puppy/hook_engine/models.py +240 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +144 -0
- code_puppy/http_utils.py +361 -0
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +10 -0
- code_puppy/mcp_/__init__.py +66 -0
- code_puppy/mcp_/async_lifecycle.py +286 -0
- code_puppy/mcp_/blocking_startup.py +469 -0
- code_puppy/mcp_/captured_stdio_server.py +275 -0
- code_puppy/mcp_/circuit_breaker.py +290 -0
- code_puppy/mcp_/config_wizard.py +507 -0
- code_puppy/mcp_/dashboard.py +308 -0
- code_puppy/mcp_/error_isolation.py +407 -0
- code_puppy/mcp_/examples/retry_example.py +226 -0
- code_puppy/mcp_/health_monitor.py +589 -0
- code_puppy/mcp_/managed_server.py +428 -0
- code_puppy/mcp_/manager.py +807 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +451 -0
- code_puppy/mcp_/retry_manager.py +337 -0
- code_puppy/mcp_/server_registry_catalog.py +1126 -0
- code_puppy/mcp_/status_tracker.py +355 -0
- code_puppy/mcp_/system_tools.py +209 -0
- code_puppy/mcp_prompts/__init__.py +1 -0
- code_puppy/mcp_prompts/hook_creator.py +103 -0
- code_puppy/messaging/__init__.py +255 -0
- code_puppy/messaging/bus.py +613 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +361 -0
- code_puppy/messaging/messages.py +569 -0
- code_puppy/messaging/queue_console.py +271 -0
- code_puppy/messaging/renderers.py +311 -0
- code_puppy/messaging/rich_renderer.py +1158 -0
- code_puppy/messaging/spinner/__init__.py +83 -0
- code_puppy/messaging/spinner/console_spinner.py +240 -0
- code_puppy/messaging/spinner/spinner_base.py +95 -0
- code_puppy/messaging/subagent_console.py +460 -0
- code_puppy/model_factory.py +848 -0
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +168 -0
- code_puppy/models.json +174 -0
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +186 -0
- code_puppy/plugins/agent_skills/__init__.py +22 -0
- code_puppy/plugins/agent_skills/config.py +175 -0
- code_puppy/plugins/agent_skills/discovery.py +136 -0
- code_puppy/plugins/agent_skills/downloader.py +392 -0
- code_puppy/plugins/agent_skills/installer.py +22 -0
- code_puppy/plugins/agent_skills/metadata.py +219 -0
- code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
- code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
- code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
- code_puppy/plugins/agent_skills/skills_menu.py +781 -0
- 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 +706 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +133 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
- code_puppy/plugins/antigravity_oauth/storage.py +288 -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 +863 -0
- code_puppy/plugins/antigravity_oauth/utils.py +168 -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 +329 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +137 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -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 +25 -0
- code_puppy/plugins/claude_code_oauth/config.py +52 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
- code_puppy/plugins/claude_code_oauth/utils.py +640 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +152 -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 +470 -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/hook_creator/__init__.py +1 -0
- code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy/plugins/hook_manager/__init__.py +1 -0
- code_puppy/plugins/hook_manager/config.py +290 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/scheduler/__init__.py +1 -0
- code_puppy/plugins/scheduler/register_callbacks.py +88 -0
- code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
- code_puppy/plugins/scheduler/scheduler_wizard.py +341 -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/plugins/synthetic_status/__init__.py +1 -0
- code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
- code_puppy/plugins/synthetic_status/status_api.py +147 -0
- code_puppy/plugins/universal_constructor/__init__.py +13 -0
- code_puppy/plugins/universal_constructor/models.py +138 -0
- code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
- code_puppy/plugins/universal_constructor/registry.py +302 -0
- code_puppy/plugins/universal_constructor/sandbox.py +584 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/pydantic_patches.py +356 -0
- code_puppy/reopenable_async_client.py +232 -0
- code_puppy/round_robin_model.py +150 -0
- code_puppy/scheduler/__init__.py +41 -0
- code_puppy/scheduler/__main__.py +9 -0
- code_puppy/scheduler/cli.py +118 -0
- code_puppy/scheduler/config.py +126 -0
- code_puppy/scheduler/daemon.py +280 -0
- code_puppy/scheduler/executor.py +155 -0
- code_puppy/scheduler/platform.py +19 -0
- code_puppy/scheduler/platform_unix.py +22 -0
- code_puppy/scheduler/platform_win.py +32 -0
- code_puppy/session_storage.py +338 -0
- code_puppy/status_display.py +257 -0
- code_puppy/summarization_agent.py +176 -0
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +501 -0
- code_puppy/tools/agent_tools.py +603 -0
- code_puppy/tools/ask_user_question/__init__.py +26 -0
- code_puppy/tools/ask_user_question/constants.py +73 -0
- code_puppy/tools/ask_user_question/demo_tui.py +55 -0
- code_puppy/tools/ask_user_question/handler.py +232 -0
- code_puppy/tools/ask_user_question/models.py +304 -0
- code_puppy/tools/ask_user_question/registration.py +26 -0
- code_puppy/tools/ask_user_question/renderers.py +309 -0
- code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
- code_puppy/tools/ask_user_question/theme.py +155 -0
- code_puppy/tools/ask_user_question/tui_loop.py +423 -0
- 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 +378 -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 +534 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +1346 -0
- code_puppy/tools/common.py +1409 -0
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +886 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +244 -0
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/tools/tools_content.py +51 -0
- code_puppy/tools/universal_constructor.py +889 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +82 -0
- codepp-0.0.437.dist-info/METADATA +766 -0
- codepp-0.0.437.dist-info/RECORD +288 -0
- codepp-0.0.437.dist-info/WHEEL +4 -0
- codepp-0.0.437.dist-info/entry_points.txt +3 -0
- codepp-0.0.437.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from code_puppy.callbacks import register_callback
|
|
5
|
+
from code_puppy.messaging import emit_error, emit_info
|
|
6
|
+
|
|
7
|
+
# Global cache for loaded commands
|
|
8
|
+
_custom_commands: Dict[str, str] = {}
|
|
9
|
+
_command_descriptions: Dict[str, str] = {}
|
|
10
|
+
_commands_loaded: bool = False # Sentinel to track if commands have been loaded
|
|
11
|
+
|
|
12
|
+
# Directories to scan for commands (in priority order - later directories override earlier)
|
|
13
|
+
_COMMAND_DIRECTORIES = [
|
|
14
|
+
"~/.code-puppy/commands", # Global commands (all projects)
|
|
15
|
+
".claude/commands",
|
|
16
|
+
".github/prompts",
|
|
17
|
+
".agents/commands",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MarkdownCommandResult:
|
|
22
|
+
"""Special marker for markdown command results that should be processed as input."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, content: str):
|
|
25
|
+
self.content = content
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
return self.content
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"MarkdownCommandResult({len(self.content)} chars)"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _load_markdown_commands() -> None:
|
|
35
|
+
"""Load markdown command files from the configured directories.
|
|
36
|
+
|
|
37
|
+
Scans for *.md files in the configured directories and loads them
|
|
38
|
+
as custom commands. Later directories override earlier ones with the
|
|
39
|
+
same command name (project commands override global).
|
|
40
|
+
"""
|
|
41
|
+
global _custom_commands, _command_descriptions, _commands_loaded
|
|
42
|
+
|
|
43
|
+
_custom_commands.clear()
|
|
44
|
+
_command_descriptions.clear()
|
|
45
|
+
_commands_loaded = True # Mark as loaded even if directories are empty
|
|
46
|
+
|
|
47
|
+
# Process directories in order - later directories override earlier ones
|
|
48
|
+
for directory in _COMMAND_DIRECTORIES:
|
|
49
|
+
dir_path = Path(directory).expanduser()
|
|
50
|
+
if not dir_path.exists():
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# Look for markdown files
|
|
54
|
+
pattern = "*.md" if directory != ".github/prompts" else "*.prompt.md"
|
|
55
|
+
# Sort within directory for consistent ordering
|
|
56
|
+
md_files = sorted(dir_path.glob(pattern))
|
|
57
|
+
|
|
58
|
+
for md_file in md_files:
|
|
59
|
+
try:
|
|
60
|
+
# Extract command name from filename
|
|
61
|
+
if md_file.name.endswith(".prompt.md"):
|
|
62
|
+
base_name = md_file.name[: -len(".prompt.md")]
|
|
63
|
+
else:
|
|
64
|
+
base_name = md_file.stem
|
|
65
|
+
|
|
66
|
+
# Read file content
|
|
67
|
+
content = md_file.read_text(encoding="utf-8").strip()
|
|
68
|
+
if not content:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
# Extract first line as description (or use filename)
|
|
72
|
+
lines = content.split("\n")
|
|
73
|
+
description = base_name.replace("_", " ").replace("-", " ").title()
|
|
74
|
+
|
|
75
|
+
# Try to get description from first non-empty line that's not a heading
|
|
76
|
+
for line in lines:
|
|
77
|
+
stripped = line.strip()
|
|
78
|
+
if stripped and not stripped.startswith("#"):
|
|
79
|
+
# Truncate long descriptions
|
|
80
|
+
description = stripped[:50] + (
|
|
81
|
+
"..." if len(stripped) > 50 else ""
|
|
82
|
+
)
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
# Later directories override earlier ones (project > global)
|
|
86
|
+
_custom_commands[base_name] = content
|
|
87
|
+
_command_descriptions[base_name] = description
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
emit_error(f"Failed to load command from {md_file}: {e}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
94
|
+
"""Return help entries for loaded markdown commands."""
|
|
95
|
+
# Reload commands to pick up any changes
|
|
96
|
+
_load_markdown_commands()
|
|
97
|
+
|
|
98
|
+
help_entries = []
|
|
99
|
+
for name, description in sorted(_command_descriptions.items()):
|
|
100
|
+
help_entries.append((name, f"Execute markdown command: {description}"))
|
|
101
|
+
|
|
102
|
+
return help_entries
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _handle_custom_command(command: str, name: str) -> Optional[Any]:
|
|
106
|
+
"""Handle a markdown-based custom command.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
command: The full command string
|
|
110
|
+
name: The command name without leading slash
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
MarkdownCommandResult with content to be processed as input,
|
|
114
|
+
or None if not found
|
|
115
|
+
"""
|
|
116
|
+
if not name:
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
# Ensure commands are loaded (use sentinel, not dict emptiness)
|
|
120
|
+
if not _commands_loaded:
|
|
121
|
+
_load_markdown_commands()
|
|
122
|
+
|
|
123
|
+
# Look up the command
|
|
124
|
+
content = _custom_commands.get(name)
|
|
125
|
+
if content is None:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# Extract any additional arguments from the command
|
|
129
|
+
parts = command.split(maxsplit=1)
|
|
130
|
+
args = parts[1] if len(parts) > 1 else ""
|
|
131
|
+
|
|
132
|
+
# If there are arguments, append them to the prompt
|
|
133
|
+
if args:
|
|
134
|
+
prompt = f"{content}\n\nAdditional context: {args}"
|
|
135
|
+
else:
|
|
136
|
+
prompt = content
|
|
137
|
+
|
|
138
|
+
# Emit info message and return the special marker
|
|
139
|
+
emit_info(f"📝 Executing markdown command: {name}")
|
|
140
|
+
return MarkdownCommandResult(prompt)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Register callbacks
|
|
144
|
+
register_callback("custom_command_help", _custom_help)
|
|
145
|
+
register_callback("custom_command", _handle_custom_command)
|
|
146
|
+
|
|
147
|
+
# Make the result class available for the command handler
|
|
148
|
+
# Import this in command_handler.py to check for this type
|
|
149
|
+
__all__ = ["MarkdownCommandResult"]
|
|
150
|
+
|
|
151
|
+
# Load commands at import time
|
|
152
|
+
_load_markdown_commands()
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Example Custom Command Plugin
|
|
2
|
+
|
|
3
|
+
> **Note**: This example demonstrates **custom commands** via the callback system.
|
|
4
|
+
> For **built-in commands**, see the built-in command files in `code_puppy/command_line/`.
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This plugin demonstrates how to create custom commands using Code Puppy's callback system.
|
|
9
|
+
|
|
10
|
+
**Important**: Custom commands use `register_callback()`, NOT `@register_command`.
|
|
11
|
+
|
|
12
|
+
## Command Types in Code Puppy
|
|
13
|
+
|
|
14
|
+
### 1. Built-in Commands (Core Functionality)
|
|
15
|
+
- Use `@register_command` decorator
|
|
16
|
+
- Located in `code_puppy/command_line/core_commands.py`, `session_commands.py`, `config_commands.py`
|
|
17
|
+
- Examples: `/help`, `/cd`, `/set`, `/agent`
|
|
18
|
+
- Check those files for implementation examples
|
|
19
|
+
|
|
20
|
+
### 2. Custom Commands (Plugins) ← **This Example**
|
|
21
|
+
- Use `register_callback()` function
|
|
22
|
+
- Located in plugin directories like this one
|
|
23
|
+
- Examples: `/woof`, `/echo` (from this plugin)
|
|
24
|
+
- Designed for plugin-specific functionality
|
|
25
|
+
|
|
26
|
+
## How This Plugin Works
|
|
27
|
+
|
|
28
|
+
### File Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
code_puppy/plugins/example_custom_command/
|
|
32
|
+
├── register_callbacks.py # Plugin implementation
|
|
33
|
+
└── README.md # This file
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Implementation
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from code_puppy.callbacks import register_callback
|
|
40
|
+
from code_puppy.messaging import emit_info
|
|
41
|
+
|
|
42
|
+
# 1. Define help entries for your commands
|
|
43
|
+
def _custom_help():
|
|
44
|
+
return [
|
|
45
|
+
("woof", "Emit a playful woof message (no model)"),
|
|
46
|
+
("echo", "Echo back your text (display only)"),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# 2. Define command handler
|
|
50
|
+
def _handle_custom_command(command: str, name: str):
|
|
51
|
+
"""Handle custom commands.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
command: Full command string (e.g., "/woof something")
|
|
55
|
+
name: Command name without slash (e.g., "woof")
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
- None: Command not handled by this plugin
|
|
59
|
+
- True: Command handled successfully
|
|
60
|
+
- str: Text to process as user input to the model
|
|
61
|
+
"""
|
|
62
|
+
if name == "woof":
|
|
63
|
+
emit_info("🐶 Woof!")
|
|
64
|
+
return True # Handled, don't invoke model
|
|
65
|
+
|
|
66
|
+
if name == "echo":
|
|
67
|
+
# Extract text after command name
|
|
68
|
+
parts = command.split(maxsplit=1)
|
|
69
|
+
if len(parts) == 2:
|
|
70
|
+
return parts[1] # Return as prompt to model
|
|
71
|
+
return "" # Empty prompt
|
|
72
|
+
|
|
73
|
+
return None # Not our command
|
|
74
|
+
|
|
75
|
+
# 3. Register callbacks
|
|
76
|
+
register_callback("custom_command_help", _custom_help)
|
|
77
|
+
register_callback("custom_command", _handle_custom_command)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Commands Provided
|
|
81
|
+
|
|
82
|
+
### `/woof [text]`
|
|
83
|
+
|
|
84
|
+
**Description**: Playful command that sends a prompt to the model.
|
|
85
|
+
|
|
86
|
+
**Behavior**:
|
|
87
|
+
- Without text: Sends "Tell me a dog fact" to the model
|
|
88
|
+
- With text: Sends your text as the prompt
|
|
89
|
+
|
|
90
|
+
**Examples**:
|
|
91
|
+
```bash
|
|
92
|
+
/woof
|
|
93
|
+
# → Sends prompt: "Tell me a dog fact"
|
|
94
|
+
|
|
95
|
+
/woof What's the best breed?
|
|
96
|
+
# → Sends prompt: "What's the best breed?"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `/echo <text>`
|
|
100
|
+
|
|
101
|
+
**Description**: Display-only command that shows your text.
|
|
102
|
+
|
|
103
|
+
**Behavior**:
|
|
104
|
+
- Shows the text you provide
|
|
105
|
+
- Returns it as input to the model
|
|
106
|
+
|
|
107
|
+
**Examples**:
|
|
108
|
+
```bash
|
|
109
|
+
/echo Hello world
|
|
110
|
+
# → Displays: "example plugin echo -> Hello world"
|
|
111
|
+
# → Sends to model: "Hello world"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Creating Your Own Plugin
|
|
115
|
+
|
|
116
|
+
### Step 1: Create Plugin Directory
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
mkdir -p code_puppy/plugins/my_plugin
|
|
120
|
+
touch code_puppy/plugins/my_plugin/__init__.py
|
|
121
|
+
touch code_puppy/plugins/my_plugin/register_callbacks.py
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 2: Implement Callbacks
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# code_puppy/plugins/my_plugin/register_callbacks.py
|
|
128
|
+
|
|
129
|
+
from code_puppy.callbacks import register_callback
|
|
130
|
+
from code_puppy.messaging import emit_info, emit_success
|
|
131
|
+
|
|
132
|
+
def _custom_help():
|
|
133
|
+
"""Provide help text for /help display."""
|
|
134
|
+
return [
|
|
135
|
+
("mycommand", "Description of my command"),
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
def _handle_custom_command(command: str, name: str):
|
|
139
|
+
"""Handle your custom commands."""
|
|
140
|
+
if name == "mycommand":
|
|
141
|
+
# Your command logic here
|
|
142
|
+
emit_success("My command executed!")
|
|
143
|
+
return True # Command handled
|
|
144
|
+
|
|
145
|
+
return None # Not our command
|
|
146
|
+
|
|
147
|
+
# Register the callbacks
|
|
148
|
+
register_callback("custom_command_help", _custom_help)
|
|
149
|
+
register_callback("custom_command", _handle_custom_command)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Step 3: Test Your Plugin
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Restart Code Puppy to load the plugin
|
|
156
|
+
code-puppy
|
|
157
|
+
|
|
158
|
+
# Try your command
|
|
159
|
+
/mycommand
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Return Value Behaviors
|
|
163
|
+
|
|
164
|
+
Your `_handle_custom_command` function can return:
|
|
165
|
+
|
|
166
|
+
| Return Value | Behavior |
|
|
167
|
+
|-------------|----------|
|
|
168
|
+
| `None` | Command not recognized, try next plugin |
|
|
169
|
+
| `True` | Command handled successfully, no model invocation |
|
|
170
|
+
| `str` | String processed as user input to the model |
|
|
171
|
+
| `MarkdownCommandResult(content)` | Special case for markdown commands |
|
|
172
|
+
|
|
173
|
+
## Best Practices
|
|
174
|
+
|
|
175
|
+
### ✅ DO:
|
|
176
|
+
|
|
177
|
+
- **Use for plugin-specific features**: OAuth flows, integrations, utilities
|
|
178
|
+
- **Return `True` for display-only commands**: Avoid unnecessary model calls
|
|
179
|
+
- **Return strings to invoke the model**: Let users interact naturally
|
|
180
|
+
- **Provide clear help text**: Users see this in `/help`
|
|
181
|
+
- **Handle errors gracefully**: Use try/except and emit_error
|
|
182
|
+
- **Keep commands simple**: Complex logic → separate module
|
|
183
|
+
|
|
184
|
+
### ❌ DON'T:
|
|
185
|
+
|
|
186
|
+
- **Don't use `@register_command`**: That's for built-in commands only
|
|
187
|
+
- **Don't modify global state**: Use Code Puppy's config system
|
|
188
|
+
- **Don't make blocking calls**: Keep commands fast and responsive
|
|
189
|
+
- **Don't invoke the model directly**: Return strings instead
|
|
190
|
+
- **Don't duplicate built-in commands**: Check existing commands first
|
|
191
|
+
|
|
192
|
+
## Command Execution Order
|
|
193
|
+
|
|
194
|
+
1. **Built-in commands** checked first (via registry)
|
|
195
|
+
2. **Legacy fallback** checked (for backward compatibility)
|
|
196
|
+
3. **Custom commands** checked (via callbacks) ← Your plugin runs here
|
|
197
|
+
4. If no match, show "Unknown command" warning
|
|
198
|
+
|
|
199
|
+
## Available Messaging Functions
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from code_puppy.messaging import (
|
|
203
|
+
emit_info, # Blue info message
|
|
204
|
+
emit_success, # Green success message
|
|
205
|
+
emit_warning, # Yellow warning message
|
|
206
|
+
emit_error, # Red error message
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Examples
|
|
210
|
+
emit_info("Processing...")
|
|
211
|
+
emit_success("Done!")
|
|
212
|
+
emit_warning("This might take a while")
|
|
213
|
+
emit_error("Something went wrong")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Testing Your Plugin
|
|
217
|
+
|
|
218
|
+
### Manual Testing
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Start Code Puppy
|
|
222
|
+
code-puppy
|
|
223
|
+
|
|
224
|
+
# Test your commands
|
|
225
|
+
/mycommand
|
|
226
|
+
/help # Verify your command appears
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Unit Testing
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
# tests/test_my_plugin.py
|
|
233
|
+
|
|
234
|
+
from code_puppy.plugins.my_plugin.register_callbacks import _handle_custom_command
|
|
235
|
+
|
|
236
|
+
def test_my_command():
|
|
237
|
+
result = _handle_custom_command("/mycommand", "mycommand")
|
|
238
|
+
assert result is True
|
|
239
|
+
|
|
240
|
+
def test_unknown_command():
|
|
241
|
+
result = _handle_custom_command("/unknown", "unknown")
|
|
242
|
+
assert result is None
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Difference from Built-in Commands
|
|
246
|
+
|
|
247
|
+
| Feature | Built-in Commands | Custom Commands (Plugins) |
|
|
248
|
+
|---------|------------------|---------------------------|
|
|
249
|
+
| **Decorator/Function** | `@register_command` | `register_callback()` |
|
|
250
|
+
| **Location** | `core_commands.py`, etc. | Plugin directory |
|
|
251
|
+
| **Purpose** | Core functionality | Plugin features |
|
|
252
|
+
| **Auto-discovery** | Via imports | Via plugin loader |
|
|
253
|
+
| **Priority** | Checked first | Checked last |
|
|
254
|
+
| **Help display** | Automatic | Manual via callback |
|
|
255
|
+
|
|
256
|
+
## Example Plugins in This Repo
|
|
257
|
+
|
|
258
|
+
- **`example_custom_command/`** (this plugin) - Basic command examples
|
|
259
|
+
- **`customizable_commands/`** - Markdown file commands
|
|
260
|
+
- **`claude_code_oauth/`** - OAuth integration example
|
|
261
|
+
- **`chatgpt_oauth/`** - Another OAuth example
|
|
262
|
+
- **`file_permission_handler/`** - File system integration
|
|
263
|
+
|
|
264
|
+
## Further Reading
|
|
265
|
+
|
|
266
|
+
- `code_puppy/callbacks.py` - Callback system implementation
|
|
267
|
+
- `code_puppy/command_line/command_handler.py` - Command dispatcher
|
|
268
|
+
- `code_puppy/command_line/core_commands.py` - Example built-in commands
|
|
269
|
+
- `code_puppy/command_line/command_registry.py` - Registry system
|
|
270
|
+
|
|
271
|
+
## Questions?
|
|
272
|
+
|
|
273
|
+
If you're unsure whether to create a custom command or a built-in command:
|
|
274
|
+
|
|
275
|
+
- **Is it core Code Puppy functionality?** → Use `@register_command` (built-in)
|
|
276
|
+
- Add to appropriate category file: `core_commands.py`, `session_commands.py`, or `config_commands.py`
|
|
277
|
+
- **Is it plugin-specific?** → Use `register_callback()` (custom)
|
|
278
|
+
- Create a plugin directory and use the callback system (like this example)
|
|
279
|
+
- **Is it a prompt template?** → Use markdown file in `.claude/commands/`
|
|
280
|
+
- The `customizable_commands` plugin will auto-load `.md` files
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from code_puppy.callbacks import register_callback
|
|
2
|
+
from code_puppy.messaging import emit_info
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _custom_help():
|
|
6
|
+
return [
|
|
7
|
+
("woof", "Emit a playful woof message (no model)"),
|
|
8
|
+
("echo", "Echo back your text (display only)"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _handle_custom_command(command: str, name: str):
|
|
13
|
+
"""Handle a demo custom command.
|
|
14
|
+
|
|
15
|
+
Policy: custom commands must NOT invoke the model. They should emit
|
|
16
|
+
messages or return True to indicate handling. Returning a string is
|
|
17
|
+
treated as a display-only message by the command handler.
|
|
18
|
+
|
|
19
|
+
Supports:
|
|
20
|
+
- /woof → emits a fun message and returns True
|
|
21
|
+
- /echo <text> → emits the text (display-only)
|
|
22
|
+
"""
|
|
23
|
+
if not name:
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
if name == "woof":
|
|
27
|
+
# If extra text is provided, pass it as a prompt; otherwise, send a fun default
|
|
28
|
+
parts = command.split(maxsplit=1)
|
|
29
|
+
if len(parts) == 2:
|
|
30
|
+
text = parts[1]
|
|
31
|
+
emit_info(f"🐶 Woof! sending prompt: {text}")
|
|
32
|
+
return text
|
|
33
|
+
emit_info("🐶 Woof! sending prompt: Tell me a dog fact")
|
|
34
|
+
return "Tell me a dog fact"
|
|
35
|
+
|
|
36
|
+
if name == "echo":
|
|
37
|
+
# Return the rest of the command (after the name) to be treated as input
|
|
38
|
+
# Example: "/echo Hello" → returns "Hello"
|
|
39
|
+
rest = command.split(maxsplit=1)
|
|
40
|
+
if len(rest) == 2:
|
|
41
|
+
text = rest[1]
|
|
42
|
+
emit_info(f"example plugin echo -> {text}")
|
|
43
|
+
return text
|
|
44
|
+
emit_info("example plugin echo (empty)")
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
register_callback("custom_command_help", _custom_help)
|
|
51
|
+
register_callback("custom_command", _handle_custom_command)
|