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.
Files changed (288) hide show
  1. code_puppy/__init__.py +10 -0
  2. code_puppy/__main__.py +10 -0
  3. code_puppy/agents/__init__.py +31 -0
  4. code_puppy/agents/agent_c_reviewer.py +155 -0
  5. code_puppy/agents/agent_code_puppy.py +117 -0
  6. code_puppy/agents/agent_code_reviewer.py +90 -0
  7. code_puppy/agents/agent_cpp_reviewer.py +132 -0
  8. code_puppy/agents/agent_creator_agent.py +638 -0
  9. code_puppy/agents/agent_golang_reviewer.py +151 -0
  10. code_puppy/agents/agent_helios.py +124 -0
  11. code_puppy/agents/agent_javascript_reviewer.py +160 -0
  12. code_puppy/agents/agent_manager.py +742 -0
  13. code_puppy/agents/agent_pack_leader.py +385 -0
  14. code_puppy/agents/agent_planning.py +165 -0
  15. code_puppy/agents/agent_python_programmer.py +169 -0
  16. code_puppy/agents/agent_python_reviewer.py +90 -0
  17. code_puppy/agents/agent_qa_expert.py +163 -0
  18. code_puppy/agents/agent_qa_kitten.py +208 -0
  19. code_puppy/agents/agent_scheduler.py +121 -0
  20. code_puppy/agents/agent_security_auditor.py +181 -0
  21. code_puppy/agents/agent_terminal_qa.py +323 -0
  22. code_puppy/agents/agent_typescript_reviewer.py +166 -0
  23. code_puppy/agents/base_agent.py +2156 -0
  24. code_puppy/agents/event_stream_handler.py +348 -0
  25. code_puppy/agents/json_agent.py +202 -0
  26. code_puppy/agents/pack/__init__.py +34 -0
  27. code_puppy/agents/pack/bloodhound.py +304 -0
  28. code_puppy/agents/pack/husky.py +327 -0
  29. code_puppy/agents/pack/retriever.py +393 -0
  30. code_puppy/agents/pack/shepherd.py +348 -0
  31. code_puppy/agents/pack/terrier.py +287 -0
  32. code_puppy/agents/pack/watchdog.py +367 -0
  33. code_puppy/agents/prompt_reviewer.py +145 -0
  34. code_puppy/agents/subagent_stream_handler.py +276 -0
  35. code_puppy/api/__init__.py +13 -0
  36. code_puppy/api/app.py +169 -0
  37. code_puppy/api/main.py +21 -0
  38. code_puppy/api/pty_manager.py +453 -0
  39. code_puppy/api/routers/__init__.py +12 -0
  40. code_puppy/api/routers/agents.py +36 -0
  41. code_puppy/api/routers/commands.py +217 -0
  42. code_puppy/api/routers/config.py +75 -0
  43. code_puppy/api/routers/sessions.py +234 -0
  44. code_puppy/api/templates/terminal.html +361 -0
  45. code_puppy/api/websocket.py +154 -0
  46. code_puppy/callbacks.py +692 -0
  47. code_puppy/chatgpt_codex_client.py +338 -0
  48. code_puppy/claude_cache_client.py +672 -0
  49. code_puppy/cli_runner.py +1073 -0
  50. code_puppy/command_line/__init__.py +1 -0
  51. code_puppy/command_line/add_model_menu.py +1092 -0
  52. code_puppy/command_line/agent_menu.py +662 -0
  53. code_puppy/command_line/attachments.py +395 -0
  54. code_puppy/command_line/autosave_menu.py +704 -0
  55. code_puppy/command_line/clipboard.py +527 -0
  56. code_puppy/command_line/colors_menu.py +532 -0
  57. code_puppy/command_line/command_handler.py +293 -0
  58. code_puppy/command_line/command_registry.py +150 -0
  59. code_puppy/command_line/config_commands.py +719 -0
  60. code_puppy/command_line/core_commands.py +867 -0
  61. code_puppy/command_line/diff_menu.py +865 -0
  62. code_puppy/command_line/file_path_completion.py +73 -0
  63. code_puppy/command_line/load_context_completion.py +52 -0
  64. code_puppy/command_line/mcp/__init__.py +10 -0
  65. code_puppy/command_line/mcp/base.py +32 -0
  66. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  67. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  68. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  69. code_puppy/command_line/mcp/edit_command.py +148 -0
  70. code_puppy/command_line/mcp/handler.py +138 -0
  71. code_puppy/command_line/mcp/help_command.py +147 -0
  72. code_puppy/command_line/mcp/install_command.py +214 -0
  73. code_puppy/command_line/mcp/install_menu.py +705 -0
  74. code_puppy/command_line/mcp/list_command.py +94 -0
  75. code_puppy/command_line/mcp/logs_command.py +235 -0
  76. code_puppy/command_line/mcp/remove_command.py +82 -0
  77. code_puppy/command_line/mcp/restart_command.py +100 -0
  78. code_puppy/command_line/mcp/search_command.py +123 -0
  79. code_puppy/command_line/mcp/start_all_command.py +135 -0
  80. code_puppy/command_line/mcp/start_command.py +117 -0
  81. code_puppy/command_line/mcp/status_command.py +184 -0
  82. code_puppy/command_line/mcp/stop_all_command.py +112 -0
  83. code_puppy/command_line/mcp/stop_command.py +80 -0
  84. code_puppy/command_line/mcp/test_command.py +107 -0
  85. code_puppy/command_line/mcp/utils.py +129 -0
  86. code_puppy/command_line/mcp/wizard_utils.py +334 -0
  87. code_puppy/command_line/mcp_completion.py +174 -0
  88. code_puppy/command_line/model_picker_completion.py +197 -0
  89. code_puppy/command_line/model_settings_menu.py +932 -0
  90. code_puppy/command_line/motd.py +96 -0
  91. code_puppy/command_line/onboarding_slides.py +179 -0
  92. code_puppy/command_line/onboarding_wizard.py +342 -0
  93. code_puppy/command_line/pin_command_completion.py +329 -0
  94. code_puppy/command_line/prompt_toolkit_completion.py +846 -0
  95. code_puppy/command_line/session_commands.py +302 -0
  96. code_puppy/command_line/shell_passthrough.py +145 -0
  97. code_puppy/command_line/skills_completion.py +160 -0
  98. code_puppy/command_line/uc_menu.py +893 -0
  99. code_puppy/command_line/utils.py +93 -0
  100. code_puppy/command_line/wiggum_state.py +78 -0
  101. code_puppy/config.py +1770 -0
  102. code_puppy/error_logging.py +134 -0
  103. code_puppy/gemini_code_assist.py +385 -0
  104. code_puppy/gemini_model.py +754 -0
  105. code_puppy/hook_engine/README.md +105 -0
  106. code_puppy/hook_engine/__init__.py +21 -0
  107. code_puppy/hook_engine/aliases.py +155 -0
  108. code_puppy/hook_engine/engine.py +221 -0
  109. code_puppy/hook_engine/executor.py +296 -0
  110. code_puppy/hook_engine/matcher.py +156 -0
  111. code_puppy/hook_engine/models.py +240 -0
  112. code_puppy/hook_engine/registry.py +106 -0
  113. code_puppy/hook_engine/validator.py +144 -0
  114. code_puppy/http_utils.py +361 -0
  115. code_puppy/keymap.py +128 -0
  116. code_puppy/main.py +10 -0
  117. code_puppy/mcp_/__init__.py +66 -0
  118. code_puppy/mcp_/async_lifecycle.py +286 -0
  119. code_puppy/mcp_/blocking_startup.py +469 -0
  120. code_puppy/mcp_/captured_stdio_server.py +275 -0
  121. code_puppy/mcp_/circuit_breaker.py +290 -0
  122. code_puppy/mcp_/config_wizard.py +507 -0
  123. code_puppy/mcp_/dashboard.py +308 -0
  124. code_puppy/mcp_/error_isolation.py +407 -0
  125. code_puppy/mcp_/examples/retry_example.py +226 -0
  126. code_puppy/mcp_/health_monitor.py +589 -0
  127. code_puppy/mcp_/managed_server.py +428 -0
  128. code_puppy/mcp_/manager.py +807 -0
  129. code_puppy/mcp_/mcp_logs.py +224 -0
  130. code_puppy/mcp_/registry.py +451 -0
  131. code_puppy/mcp_/retry_manager.py +337 -0
  132. code_puppy/mcp_/server_registry_catalog.py +1126 -0
  133. code_puppy/mcp_/status_tracker.py +355 -0
  134. code_puppy/mcp_/system_tools.py +209 -0
  135. code_puppy/mcp_prompts/__init__.py +1 -0
  136. code_puppy/mcp_prompts/hook_creator.py +103 -0
  137. code_puppy/messaging/__init__.py +255 -0
  138. code_puppy/messaging/bus.py +613 -0
  139. code_puppy/messaging/commands.py +167 -0
  140. code_puppy/messaging/markdown_patches.py +57 -0
  141. code_puppy/messaging/message_queue.py +361 -0
  142. code_puppy/messaging/messages.py +569 -0
  143. code_puppy/messaging/queue_console.py +271 -0
  144. code_puppy/messaging/renderers.py +311 -0
  145. code_puppy/messaging/rich_renderer.py +1158 -0
  146. code_puppy/messaging/spinner/__init__.py +83 -0
  147. code_puppy/messaging/spinner/console_spinner.py +240 -0
  148. code_puppy/messaging/spinner/spinner_base.py +95 -0
  149. code_puppy/messaging/subagent_console.py +460 -0
  150. code_puppy/model_factory.py +848 -0
  151. code_puppy/model_switching.py +63 -0
  152. code_puppy/model_utils.py +168 -0
  153. code_puppy/models.json +174 -0
  154. code_puppy/models_dev_api.json +1 -0
  155. code_puppy/models_dev_parser.py +592 -0
  156. code_puppy/plugins/__init__.py +186 -0
  157. code_puppy/plugins/agent_skills/__init__.py +22 -0
  158. code_puppy/plugins/agent_skills/config.py +175 -0
  159. code_puppy/plugins/agent_skills/discovery.py +136 -0
  160. code_puppy/plugins/agent_skills/downloader.py +392 -0
  161. code_puppy/plugins/agent_skills/installer.py +22 -0
  162. code_puppy/plugins/agent_skills/metadata.py +219 -0
  163. code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
  164. code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
  165. code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
  166. code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
  167. code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
  168. code_puppy/plugins/agent_skills/skills_menu.py +781 -0
  169. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  170. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  171. code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
  172. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  173. code_puppy/plugins/antigravity_oauth/constants.py +133 -0
  174. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  175. code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
  176. code_puppy/plugins/antigravity_oauth/storage.py +288 -0
  177. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  178. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  179. code_puppy/plugins/antigravity_oauth/transport.py +863 -0
  180. code_puppy/plugins/antigravity_oauth/utils.py +168 -0
  181. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  182. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  183. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
  184. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
  185. code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
  186. code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
  187. code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
  188. code_puppy/plugins/claude_code_hooks/config.py +137 -0
  189. code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -0
  190. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  191. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  192. code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
  193. code_puppy/plugins/claude_code_oauth/config.py +52 -0
  194. code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
  195. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  196. code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
  197. code_puppy/plugins/claude_code_oauth/utils.py +640 -0
  198. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  199. code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
  200. code_puppy/plugins/example_custom_command/README.md +280 -0
  201. code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
  202. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  203. code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
  204. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  205. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  206. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  207. code_puppy/plugins/hook_creator/__init__.py +1 -0
  208. code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
  209. code_puppy/plugins/hook_manager/__init__.py +1 -0
  210. code_puppy/plugins/hook_manager/config.py +290 -0
  211. code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
  212. code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
  213. code_puppy/plugins/oauth_puppy_html.py +228 -0
  214. code_puppy/plugins/scheduler/__init__.py +1 -0
  215. code_puppy/plugins/scheduler/register_callbacks.py +88 -0
  216. code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
  217. code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
  218. code_puppy/plugins/shell_safety/__init__.py +6 -0
  219. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  220. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  221. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  222. code_puppy/plugins/synthetic_status/__init__.py +1 -0
  223. code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
  224. code_puppy/plugins/synthetic_status/status_api.py +147 -0
  225. code_puppy/plugins/universal_constructor/__init__.py +13 -0
  226. code_puppy/plugins/universal_constructor/models.py +138 -0
  227. code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
  228. code_puppy/plugins/universal_constructor/registry.py +302 -0
  229. code_puppy/plugins/universal_constructor/sandbox.py +584 -0
  230. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  231. code_puppy/pydantic_patches.py +356 -0
  232. code_puppy/reopenable_async_client.py +232 -0
  233. code_puppy/round_robin_model.py +150 -0
  234. code_puppy/scheduler/__init__.py +41 -0
  235. code_puppy/scheduler/__main__.py +9 -0
  236. code_puppy/scheduler/cli.py +118 -0
  237. code_puppy/scheduler/config.py +126 -0
  238. code_puppy/scheduler/daemon.py +280 -0
  239. code_puppy/scheduler/executor.py +155 -0
  240. code_puppy/scheduler/platform.py +19 -0
  241. code_puppy/scheduler/platform_unix.py +22 -0
  242. code_puppy/scheduler/platform_win.py +32 -0
  243. code_puppy/session_storage.py +338 -0
  244. code_puppy/status_display.py +257 -0
  245. code_puppy/summarization_agent.py +176 -0
  246. code_puppy/terminal_utils.py +418 -0
  247. code_puppy/tools/__init__.py +501 -0
  248. code_puppy/tools/agent_tools.py +603 -0
  249. code_puppy/tools/ask_user_question/__init__.py +26 -0
  250. code_puppy/tools/ask_user_question/constants.py +73 -0
  251. code_puppy/tools/ask_user_question/demo_tui.py +55 -0
  252. code_puppy/tools/ask_user_question/handler.py +232 -0
  253. code_puppy/tools/ask_user_question/models.py +304 -0
  254. code_puppy/tools/ask_user_question/registration.py +26 -0
  255. code_puppy/tools/ask_user_question/renderers.py +309 -0
  256. code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
  257. code_puppy/tools/ask_user_question/theme.py +155 -0
  258. code_puppy/tools/ask_user_question/tui_loop.py +423 -0
  259. code_puppy/tools/browser/__init__.py +37 -0
  260. code_puppy/tools/browser/browser_control.py +289 -0
  261. code_puppy/tools/browser/browser_interactions.py +545 -0
  262. code_puppy/tools/browser/browser_locators.py +640 -0
  263. code_puppy/tools/browser/browser_manager.py +378 -0
  264. code_puppy/tools/browser/browser_navigation.py +251 -0
  265. code_puppy/tools/browser/browser_screenshot.py +179 -0
  266. code_puppy/tools/browser/browser_scripts.py +462 -0
  267. code_puppy/tools/browser/browser_workflows.py +221 -0
  268. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  269. code_puppy/tools/browser/terminal_command_tools.py +534 -0
  270. code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
  271. code_puppy/tools/browser/terminal_tools.py +525 -0
  272. code_puppy/tools/command_runner.py +1346 -0
  273. code_puppy/tools/common.py +1409 -0
  274. code_puppy/tools/display.py +84 -0
  275. code_puppy/tools/file_modifications.py +886 -0
  276. code_puppy/tools/file_operations.py +802 -0
  277. code_puppy/tools/scheduler_tools.py +412 -0
  278. code_puppy/tools/skills_tools.py +244 -0
  279. code_puppy/tools/subagent_context.py +158 -0
  280. code_puppy/tools/tools_content.py +51 -0
  281. code_puppy/tools/universal_constructor.py +889 -0
  282. code_puppy/uvx_detection.py +242 -0
  283. code_puppy/version_checker.py +82 -0
  284. codepp-0.0.437.dist-info/METADATA +766 -0
  285. codepp-0.0.437.dist-info/RECORD +288 -0
  286. codepp-0.0.437.dist-info/WHEEL +4 -0
  287. codepp-0.0.437.dist-info/entry_points.txt +3 -0
  288. 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)
@@ -0,0 +1,4 @@
1
+ """File Permission Handler Plugin Package."""
2
+
3
+ __version__ = "1.0.0"
4
+ __description__ = "Unified file permission handling system for code-puppy"