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,719 @@
1
+ """Command handlers for Code Puppy - CONFIG commands.
2
+
3
+ This module contains @register_command decorated handlers that are automatically
4
+ discovered by the command registry system.
5
+ """
6
+
7
+ import json
8
+
9
+ from code_puppy.command_line.command_registry import register_command
10
+ from code_puppy.config import get_config_keys
11
+
12
+
13
+ # Import get_commands_help from command_handler to avoid circular imports
14
+ # This will be defined in command_handler.py
15
+ def get_commands_help():
16
+ """Lazy import to avoid circular dependency."""
17
+ from code_puppy.command_line.command_handler import get_commands_help as _gch
18
+
19
+ return _gch()
20
+
21
+
22
+ @register_command(
23
+ name="show",
24
+ description="Show puppy config key-values",
25
+ usage="/show",
26
+ category="config",
27
+ )
28
+ def handle_show_command(command: str) -> bool:
29
+ """Show current puppy configuration."""
30
+ from rich.text import Text
31
+
32
+ from code_puppy.agents import get_current_agent
33
+ from code_puppy.command_line.model_picker_completion import get_active_model
34
+ from code_puppy.config import (
35
+ get_auto_save_session,
36
+ get_compaction_strategy,
37
+ get_compaction_threshold,
38
+ get_default_agent,
39
+ get_effective_temperature,
40
+ get_openai_reasoning_effort,
41
+ get_openai_verbosity,
42
+ get_owner_name,
43
+ get_protected_token_count,
44
+ get_puppy_name,
45
+ get_resume_message_count,
46
+ get_temperature,
47
+ get_use_dbos,
48
+ get_yolo_mode,
49
+ )
50
+ from code_puppy.keymap import (
51
+ get_cancel_agent_display_name,
52
+ )
53
+ from code_puppy.messaging import emit_info
54
+
55
+ puppy_name = get_puppy_name()
56
+ owner_name = get_owner_name()
57
+ model = get_active_model()
58
+ yolo_mode = get_yolo_mode()
59
+ auto_save = get_auto_save_session()
60
+ protected_tokens = get_protected_token_count()
61
+ compaction_threshold = get_compaction_threshold()
62
+ compaction_strategy = get_compaction_strategy()
63
+ global_temperature = get_temperature()
64
+ effective_temperature = get_effective_temperature(model)
65
+
66
+ # Get current agent info
67
+ current_agent = get_current_agent()
68
+ default_agent = get_default_agent()
69
+
70
+ status_msg = f"""[bold magenta]🐶 Puppy Status[/bold magenta]
71
+
72
+ [bold]puppy_name:[/bold] [cyan]{puppy_name}[/cyan]
73
+ [bold]owner_name:[/bold] [cyan]{owner_name}[/cyan]
74
+ [bold]current_agent:[/bold] [magenta]{current_agent.display_name}[/magenta]
75
+ [bold]default_agent:[/bold] [cyan]{default_agent}[/cyan]
76
+ [bold]model:[/bold] [green]{model}[/green]
77
+ [bold]YOLO_MODE:[/bold] {"[red]ON[/red]" if yolo_mode else "[yellow]off[/yellow]"}
78
+ [bold]DBOS:[/bold] {"[green]enabled[/green]" if get_use_dbos() else "[yellow]disabled[/yellow]"} (toggle: /set enable_dbos true|false)
79
+ [bold]auto_save_session:[/bold] {"[green]enabled[/green]" if auto_save else "[yellow]disabled[/yellow]"}
80
+ [bold]protected_tokens:[/bold] [cyan]{protected_tokens:,}[/cyan] recent tokens preserved
81
+ [bold]compaction_threshold:[/bold] [cyan]{compaction_threshold:.1%}[/cyan] context usage triggers compaction
82
+ [bold]compaction_strategy:[/bold] [cyan]{compaction_strategy}[/cyan] (summarization or truncation)
83
+ [bold]resume_message_count:[/bold] [cyan]{get_resume_message_count()}[/cyan] messages shown on /resume
84
+ [bold]reasoning_effort:[/bold] [cyan]{get_openai_reasoning_effort()}[/cyan]
85
+ [bold]verbosity:[/bold] [cyan]{get_openai_verbosity()}[/cyan]
86
+ [bold]temperature:[/bold] [cyan]{effective_temperature if effective_temperature is not None else "(model default)"}[/cyan]{" (per-model)" if effective_temperature != global_temperature and effective_temperature is not None else ""}
87
+ [bold]cancel_agent_key:[/bold] [cyan]{get_cancel_agent_display_name()}[/cyan] (options: ctrl+c, ctrl+k, ctrl+q)
88
+
89
+ """
90
+ emit_info(Text.from_markup(status_msg))
91
+ return True
92
+
93
+
94
+ @register_command(
95
+ name="reasoning",
96
+ description="Set OpenAI reasoning effort for GPT-5 models (e.g., /reasoning high)",
97
+ usage="/reasoning <minimal|low|medium|high|xhigh>",
98
+ category="config",
99
+ )
100
+ def handle_reasoning_command(command: str) -> bool:
101
+ """Set OpenAI reasoning effort level."""
102
+ from code_puppy.messaging import emit_error, emit_success, emit_warning
103
+
104
+ tokens = command.split()
105
+ if len(tokens) != 2:
106
+ emit_warning("Usage: /reasoning <minimal|low|medium|high|xhigh>")
107
+ return True
108
+
109
+ effort = tokens[1]
110
+ try:
111
+ from code_puppy.config import set_openai_reasoning_effort
112
+
113
+ set_openai_reasoning_effort(effort)
114
+ except ValueError as exc:
115
+ emit_error(str(exc))
116
+ return True
117
+
118
+ from code_puppy.config import get_openai_reasoning_effort
119
+
120
+ normalized_effort = get_openai_reasoning_effort()
121
+
122
+ from code_puppy.agents.agent_manager import get_current_agent
123
+
124
+ agent = get_current_agent()
125
+ agent.reload_code_generation_agent()
126
+ emit_success(
127
+ f"Reasoning effort set to '{normalized_effort}' and active agent reloaded"
128
+ )
129
+ return True
130
+
131
+
132
+ @register_command(
133
+ name="verbosity",
134
+ description="Set OpenAI verbosity for GPT-5 models (e.g., /verbosity high)",
135
+ usage="/verbosity <low|medium|high>",
136
+ category="config",
137
+ )
138
+ def handle_verbosity_command(command: str) -> bool:
139
+ """Set OpenAI verbosity level.
140
+
141
+ Controls how concise vs. verbose the model's responses are:
142
+ - low: more concise responses
143
+ - medium: balanced (default)
144
+ - high: more verbose responses
145
+ """
146
+ from code_puppy.messaging import emit_error, emit_success, emit_warning
147
+
148
+ tokens = command.split()
149
+ if len(tokens) != 2:
150
+ emit_warning("Usage: /verbosity <low|medium|high>")
151
+ return True
152
+
153
+ verbosity = tokens[1]
154
+ try:
155
+ from code_puppy.config import set_openai_verbosity
156
+
157
+ set_openai_verbosity(verbosity)
158
+ except ValueError as exc:
159
+ emit_error(str(exc))
160
+ return True
161
+
162
+ from code_puppy.config import get_openai_verbosity
163
+
164
+ normalized_verbosity = get_openai_verbosity()
165
+
166
+ from code_puppy.agents.agent_manager import get_current_agent
167
+
168
+ agent = get_current_agent()
169
+ agent.reload_code_generation_agent()
170
+ emit_success(f"Verbosity set to '{normalized_verbosity}' and active agent reloaded")
171
+ return True
172
+
173
+
174
+ @register_command(
175
+ name="set",
176
+ description="Set puppy config (e.g., /set yolo_mode true)",
177
+ usage="/set <key> <value>",
178
+ category="config",
179
+ )
180
+ def handle_set_command(command: str) -> bool:
181
+ """Set configuration values."""
182
+ from rich.text import Text
183
+
184
+ from code_puppy.config import set_config_value
185
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
186
+
187
+ tokens = command.split(None, 2)
188
+ argstr = command[len("/set") :].strip()
189
+ key = None
190
+ value = None
191
+ if "=" in argstr:
192
+ key, value = argstr.split("=", 1)
193
+ key = key.strip()
194
+ value = value.strip()
195
+ elif len(tokens) >= 3:
196
+ key = tokens[1]
197
+ value = tokens[2]
198
+ elif len(tokens) == 2:
199
+ key = tokens[1]
200
+ value = ""
201
+ else:
202
+ config_keys = get_config_keys()
203
+ if "compaction_strategy" not in config_keys:
204
+ config_keys.append("compaction_strategy")
205
+ session_help = (
206
+ "\n[yellow]Session Management[/yellow]"
207
+ "\n [cyan]auto_save_session[/cyan] Auto-save chat after every response (true/false)"
208
+ )
209
+ keymap_help = (
210
+ "\n[yellow]Keyboard Shortcuts[/yellow]"
211
+ "\n [cyan]cancel_agent_key[/cyan] Key to cancel agent tasks (ctrl+c, ctrl+k, or ctrl+q)"
212
+ )
213
+ emit_warning(
214
+ Text.from_markup(
215
+ f"Usage: /set KEY=VALUE or /set KEY VALUE\nConfig keys: {', '.join(config_keys)}\n[dim]Note: compaction_strategy can be 'summarization' or 'truncation'[/dim]{session_help}{keymap_help}"
216
+ )
217
+ )
218
+ return True
219
+ if key:
220
+ # Check if we're toggling DBOS enablement
221
+ if key == "enable_dbos":
222
+ emit_info(
223
+ Text.from_markup(
224
+ "[yellow]āš ļø DBOS configuration changed. Please restart Code Puppy for this change to take effect.[/yellow]"
225
+ )
226
+ )
227
+
228
+ # Validate cancel_agent_key before setting
229
+ if key == "cancel_agent_key":
230
+ from code_puppy.keymap import VALID_CANCEL_KEYS
231
+
232
+ normalized_value = value.strip().lower()
233
+ if normalized_value not in VALID_CANCEL_KEYS:
234
+ emit_error(
235
+ f"Invalid cancel_agent_key '{value}'. Valid options: {', '.join(sorted(VALID_CANCEL_KEYS))}"
236
+ )
237
+ return True
238
+ value = normalized_value # Use normalized value
239
+ emit_info(
240
+ Text.from_markup(
241
+ "[yellow]āš ļø cancel_agent_key changed. Please restart Code Puppy for this change to take effect.[/yellow]"
242
+ )
243
+ )
244
+
245
+ set_config_value(key, value)
246
+ emit_success(f'Set {key} = "{value}" in puppy.cfg!')
247
+
248
+ # Reload the current agent to pick up the new config
249
+ from code_puppy.agents import get_current_agent
250
+
251
+ try:
252
+ current_agent = get_current_agent()
253
+ current_agent.reload_code_generation_agent()
254
+ emit_info("Agent reloaded with updated config")
255
+ except Exception as reload_error:
256
+ emit_warning(f"Config saved but agent reload failed: {reload_error}")
257
+ else:
258
+ emit_error("You must supply a key.")
259
+ return True
260
+
261
+
262
+ def _get_json_agents_pinned_to_model(model_name: str) -> list:
263
+ """Get JSON agents that have this model pinned in their JSON file."""
264
+ from code_puppy.agents.json_agent import discover_json_agents
265
+
266
+ pinned = []
267
+ json_agents = discover_json_agents()
268
+ for agent_name, agent_path in json_agents.items():
269
+ try:
270
+ with open(agent_path, "r") as f:
271
+ agent_data = json.load(f)
272
+ if agent_data.get("model") == model_name:
273
+ pinned.append(agent_name)
274
+ except Exception:
275
+ continue
276
+ return pinned
277
+
278
+
279
+ @register_command(
280
+ name="pin_model",
281
+ description="Pin a specific model to an agent",
282
+ usage="/pin_model <agent> <model>",
283
+ category="config",
284
+ )
285
+ def handle_pin_model_command(command: str) -> bool:
286
+ """Pin a specific model to an agent."""
287
+ from code_puppy.agents.json_agent import discover_json_agents
288
+ from code_puppy.command_line.model_picker_completion import load_model_names
289
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
290
+
291
+ tokens = command.split()
292
+
293
+ if len(tokens) != 3:
294
+ emit_warning("Usage: /pin_model <agent-name> <model-name>")
295
+
296
+ # Show available models and agents
297
+ available_models = load_model_names()
298
+ json_agents = discover_json_agents()
299
+
300
+ # Get built-in agents
301
+ from code_puppy.agents.agent_manager import get_agent_descriptions
302
+
303
+ builtin_agents = get_agent_descriptions()
304
+
305
+ emit_info("Available models:")
306
+ for model in available_models:
307
+ emit_info(f" {model}")
308
+
309
+ if builtin_agents:
310
+ emit_info("\nAvailable built-in agents:")
311
+ for agent_name, description in builtin_agents.items():
312
+ emit_info(f" {agent_name} - {description}")
313
+
314
+ if json_agents:
315
+ emit_info("\nAvailable JSON agents:")
316
+ for agent_name, agent_path in json_agents.items():
317
+ emit_info(f" {agent_name} ({agent_path})")
318
+ return True
319
+
320
+ agent_name = tokens[1].lower()
321
+ model_name = tokens[2]
322
+
323
+ # Handle special case: (unpin) option (case-insensitive)
324
+ if model_name.lower() == "(unpin)":
325
+ # Delegate to unpin command
326
+ return handle_unpin_command(f"/unpin {agent_name}")
327
+
328
+ # Check if model exists
329
+ available_models = load_model_names()
330
+ if model_name not in available_models:
331
+ emit_error(f"Model '{model_name}' not found")
332
+ emit_warning(f"Available models: {', '.join(available_models)}")
333
+ return True
334
+
335
+ # Check if this is a JSON agent or a built-in Python agent
336
+ json_agents = discover_json_agents()
337
+
338
+ # Get list of available built-in agents
339
+ from code_puppy.agents.agent_manager import get_agent_descriptions
340
+
341
+ builtin_agents = get_agent_descriptions()
342
+
343
+ is_json_agent = agent_name in json_agents
344
+ is_builtin_agent = agent_name in builtin_agents
345
+
346
+ if not is_json_agent and not is_builtin_agent:
347
+ emit_error(f"Agent '{agent_name}' not found")
348
+
349
+ # Show available agents
350
+ if builtin_agents:
351
+ emit_info("Available built-in agents:")
352
+ for name, desc in builtin_agents.items():
353
+ emit_info(f" {name} - {desc}")
354
+
355
+ if json_agents:
356
+ emit_info("\nAvailable JSON agents:")
357
+ for name, path in json_agents.items():
358
+ emit_info(f" {name} ({path})")
359
+ return True
360
+
361
+ # Handle different agent types
362
+ try:
363
+ if is_json_agent:
364
+ # Handle JSON agent - modify the JSON file
365
+ agent_file_path = json_agents[agent_name]
366
+
367
+ with open(agent_file_path, "r", encoding="utf-8") as f:
368
+ agent_config = json.load(f)
369
+
370
+ # Set the model
371
+ agent_config["model"] = model_name
372
+
373
+ # Save the updated configuration
374
+ with open(agent_file_path, "w", encoding="utf-8") as f:
375
+ json.dump(agent_config, f, indent=2, ensure_ascii=False)
376
+
377
+ else:
378
+ # Handle built-in Python agent - store in config
379
+ from code_puppy.config import set_agent_pinned_model
380
+
381
+ set_agent_pinned_model(agent_name, model_name)
382
+
383
+ emit_success(f"Model '{model_name}' pinned to agent '{agent_name}'")
384
+
385
+ # If this is the current agent, refresh it so the prompt updates immediately
386
+ from code_puppy.agents import get_current_agent
387
+
388
+ current_agent = get_current_agent()
389
+ if current_agent.name == agent_name:
390
+ try:
391
+ if is_json_agent and hasattr(current_agent, "refresh_config"):
392
+ current_agent.refresh_config()
393
+ current_agent.reload_code_generation_agent()
394
+ emit_info(f"Active agent reloaded with pinned model '{model_name}'")
395
+ except Exception as reload_error:
396
+ emit_warning(f"Pinned model applied but reload failed: {reload_error}")
397
+
398
+ return True
399
+
400
+ except Exception as e:
401
+ emit_error(f"Failed to pin model to agent '{agent_name}': {e}")
402
+ return True
403
+
404
+
405
+ @register_command(
406
+ name="unpin",
407
+ description="Unpin a model from an agent (resets to default)",
408
+ usage="/unpin <agent>",
409
+ category="config",
410
+ )
411
+ def handle_unpin_command(command: str) -> bool:
412
+ """Unpin a model from an agent (resets to default)."""
413
+ from code_puppy.agents.json_agent import discover_json_agents
414
+ from code_puppy.config import get_agent_pinned_model
415
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
416
+
417
+ tokens = command.split()
418
+
419
+ if len(tokens) != 2:
420
+ emit_warning("Usage: /unpin <agent-name>")
421
+
422
+ # Show available agents
423
+ json_agents = discover_json_agents()
424
+
425
+ # Get built-in agents
426
+ from code_puppy.agents.agent_manager import get_agent_descriptions
427
+
428
+ builtin_agents = get_agent_descriptions()
429
+
430
+ if builtin_agents:
431
+ emit_info("Available built-in agents:")
432
+ for agent_name, description in builtin_agents.items():
433
+ pinned_model = get_agent_pinned_model(agent_name)
434
+ if pinned_model:
435
+ emit_info(f" {agent_name} - {description} [→ {pinned_model}]")
436
+ else:
437
+ emit_info(f" {agent_name} - {description}")
438
+
439
+ if json_agents:
440
+ emit_info("\nAvailable JSON agents:")
441
+ for agent_name, agent_path in json_agents.items():
442
+ # Read the JSON file to check for pinned model
443
+ try:
444
+ with open(agent_path, "r") as f:
445
+ agent_config = json.load(f)
446
+ pinned_model = agent_config.get("model")
447
+ if pinned_model:
448
+ emit_info(f" {agent_name} ({agent_path}) [→ {pinned_model}]")
449
+ else:
450
+ emit_info(f" {agent_name} ({agent_path})")
451
+ except Exception:
452
+ emit_info(f" {agent_name} ({agent_path})")
453
+ return True
454
+
455
+ agent_name_input = tokens[1].lower()
456
+
457
+ # Check if this is a JSON agent or a built-in Python agent
458
+ json_agents = discover_json_agents()
459
+
460
+ # Get list of available built-in agents
461
+ from code_puppy.agents.agent_manager import get_agent_descriptions
462
+
463
+ builtin_agents = get_agent_descriptions()
464
+
465
+ # Find matching agent (case-insensitive)
466
+ agent_name = None
467
+ is_json_agent = False
468
+ is_builtin_agent = False
469
+
470
+ # Check JSON agents (case-insensitive)
471
+ for json_agent_name in json_agents:
472
+ if json_agent_name.lower() == agent_name_input:
473
+ agent_name = json_agent_name
474
+ is_json_agent = True
475
+ break
476
+
477
+ # Check built-in agents (case-insensitive)
478
+ if not is_json_agent:
479
+ for builtin_agent_name in builtin_agents:
480
+ if builtin_agent_name.lower() == agent_name_input:
481
+ agent_name = builtin_agent_name
482
+ is_builtin_agent = True
483
+ break
484
+
485
+ if not is_json_agent and not is_builtin_agent:
486
+ emit_error(f"Agent '{agent_name_input}' not found")
487
+
488
+ # Show available agents
489
+ if builtin_agents:
490
+ emit_info("Available built-in agents:")
491
+ for name, desc in builtin_agents.items():
492
+ emit_info(f" {name} - {desc}")
493
+
494
+ if json_agents:
495
+ emit_info("\nAvailable JSON agents:")
496
+ for name, path in json_agents.items():
497
+ emit_info(f" {name} ({path})")
498
+ return True
499
+
500
+ try:
501
+ if is_json_agent:
502
+ # Handle JSON agent - remove the model from JSON file
503
+ agent_file_path = json_agents[agent_name]
504
+
505
+ with open(agent_file_path, "r", encoding="utf-8") as f:
506
+ agent_config = json.load(f)
507
+
508
+ # Remove the model key if it exists
509
+ if "model" in agent_config:
510
+ del agent_config["model"]
511
+
512
+ # Save the updated configuration
513
+ with open(agent_file_path, "w", encoding="utf-8") as f:
514
+ json.dump(agent_config, f, indent=2, ensure_ascii=False)
515
+
516
+ else:
517
+ # Handle built-in Python agent - clear from config
518
+ from code_puppy.config import clear_agent_pinned_model
519
+
520
+ clear_agent_pinned_model(agent_name)
521
+
522
+ emit_success(f"Model unpinned from agent '{agent_name}' (reset to default)")
523
+
524
+ # If this is the current agent, refresh it so the prompt updates immediately
525
+ from code_puppy.agents import get_current_agent
526
+
527
+ current_agent = get_current_agent()
528
+ if current_agent.name == agent_name:
529
+ try:
530
+ if is_json_agent and hasattr(current_agent, "refresh_config"):
531
+ current_agent.refresh_config()
532
+ current_agent.reload_code_generation_agent()
533
+ emit_info("Active agent reloaded with default model")
534
+ except Exception as reload_error:
535
+ emit_warning(f"Model unpinned but reload failed: {reload_error}")
536
+
537
+ return True
538
+
539
+ except Exception as e:
540
+ emit_error(f"Failed to unpin model from agent '{agent_name}': {e}")
541
+ return True
542
+
543
+
544
+ @register_command(
545
+ name="diff",
546
+ description="Configure diff highlighting colors (additions, deletions)",
547
+ usage="/diff",
548
+ category="config",
549
+ )
550
+ def handle_diff_command(command: str) -> bool:
551
+ """Configure diff highlighting colors."""
552
+ import asyncio
553
+ import concurrent.futures
554
+
555
+ from code_puppy.command_line.diff_menu import interactive_diff_picker
556
+ from code_puppy.config import (
557
+ set_diff_addition_color,
558
+ set_diff_deletion_color,
559
+ )
560
+ from code_puppy.messaging import emit_error
561
+
562
+ # Show interactive picker for diff configuration
563
+ with concurrent.futures.ThreadPoolExecutor() as executor:
564
+ future = executor.submit(lambda: asyncio.run(interactive_diff_picker()))
565
+ result = future.result(timeout=300) # 5 min timeout
566
+
567
+ if result:
568
+ # Apply the changes silently (no console output)
569
+ try:
570
+ set_diff_addition_color(result["add_color"])
571
+ set_diff_deletion_color(result["del_color"])
572
+ except Exception as e:
573
+ emit_error(f"Failed to apply diff settings: {e}")
574
+ return True
575
+
576
+
577
+ @register_command(
578
+ name="colors",
579
+ description="Configure banner colors for tool outputs (THINKING, SHELL COMMAND, etc.)",
580
+ usage="/colors",
581
+ category="config",
582
+ )
583
+ def handle_colors_command(command: str) -> bool:
584
+ """Configure banner colors via interactive TUI."""
585
+ import asyncio
586
+ import concurrent.futures
587
+
588
+ from code_puppy.command_line.colors_menu import interactive_colors_picker
589
+ from code_puppy.config import set_banner_color
590
+ from code_puppy.messaging import emit_error, emit_success
591
+
592
+ # Show interactive picker for banner color configuration
593
+ with concurrent.futures.ThreadPoolExecutor() as executor:
594
+ future = executor.submit(lambda: asyncio.run(interactive_colors_picker()))
595
+ result = future.result(timeout=300) # 5 min timeout
596
+
597
+ if result:
598
+ # Apply the changes
599
+ try:
600
+ for banner_name, color in result.items():
601
+ set_banner_color(banner_name, color)
602
+ emit_success("Banner colors saved! šŸŽØ")
603
+ except Exception as e:
604
+ emit_error(f"Failed to apply banner color settings: {e}")
605
+ return True
606
+
607
+
608
+ # ============================================================================
609
+ # UTILITY FUNCTIONS
610
+ # ============================================================================
611
+
612
+
613
+ def _show_color_options(color_type: str):
614
+ # ============================================================================
615
+ # UTILITY FUNCTIONS
616
+ # ============================================================================
617
+
618
+ """Show available Rich color options organized by category."""
619
+ from rich.text import Text
620
+
621
+ from code_puppy.messaging import emit_info
622
+
623
+ # Standard Rich colors organized by category
624
+ color_categories = {
625
+ "Basic Colors": [
626
+ ("black", "⚫"),
627
+ ("red", "šŸ”“"),
628
+ ("green", "🟢"),
629
+ ("yellow", "🟔"),
630
+ ("blue", "šŸ”µ"),
631
+ ("magenta", "🟣"),
632
+ ("cyan", "šŸ”·"),
633
+ ("white", "⚪"),
634
+ ],
635
+ "Bright Colors": [
636
+ ("bright_black", "⚫"),
637
+ ("bright_red", "šŸ”“"),
638
+ ("bright_green", "🟢"),
639
+ ("bright_yellow", "🟔"),
640
+ ("bright_blue", "šŸ”µ"),
641
+ ("bright_magenta", "🟣"),
642
+ ("bright_cyan", "šŸ”·"),
643
+ ("bright_white", "⚪"),
644
+ ],
645
+ "Special Colors": [
646
+ ("orange1", "🟠"),
647
+ ("orange3", "🟠"),
648
+ ("orange4", "🟠"),
649
+ ("deep_sky_blue1", "šŸ”·"),
650
+ ("deep_sky_blue2", "šŸ”·"),
651
+ ("deep_sky_blue3", "šŸ”·"),
652
+ ("deep_sky_blue4", "šŸ”·"),
653
+ ("turquoise2", "šŸ”·"),
654
+ ("turquoise4", "šŸ”·"),
655
+ ("steel_blue1", "šŸ”·"),
656
+ ("steel_blue3", "šŸ”·"),
657
+ ("chartreuse1", "🟢"),
658
+ ("chartreuse2", "🟢"),
659
+ ("chartreuse3", "🟢"),
660
+ ("chartreuse4", "🟢"),
661
+ ("gold1", "🟔"),
662
+ ("gold3", "🟔"),
663
+ ("rosy_brown", "šŸ”“"),
664
+ ("indian_red", "šŸ”“"),
665
+ ],
666
+ }
667
+
668
+ # Suggested colors for each type
669
+ if color_type == "additions":
670
+ suggestions = [
671
+ ("green", "🟢"),
672
+ ("bright_green", "🟢"),
673
+ ("chartreuse1", "🟢"),
674
+ ("green3", "🟢"),
675
+ ("sea_green1", "🟢"),
676
+ ]
677
+ emit_info(
678
+ Text.from_markup(
679
+ "[bold white on green]šŸŽØ Recommended Colors for Additions:[/bold white on green]"
680
+ )
681
+ )
682
+ for color, emoji in suggestions:
683
+ emit_info(
684
+ Text.from_markup(
685
+ f" [cyan]{color:<16}[/cyan] [white on {color}]ā– ā– ā– ā– ā– ā– ā– ā– ā– ā– [/white on {color}] {emoji}"
686
+ )
687
+ )
688
+ elif color_type == "deletions":
689
+ suggestions = [
690
+ ("orange1", "🟠"),
691
+ ("red", "šŸ”“"),
692
+ ("bright_red", "šŸ”“"),
693
+ ("indian_red", "šŸ”“"),
694
+ ("dark_red", "šŸ”“"),
695
+ ]
696
+ emit_info(
697
+ Text.from_markup(
698
+ "[bold white on orange1]šŸŽØ Recommended Colors for Deletions:[/bold white on orange1]"
699
+ )
700
+ )
701
+ for color, emoji in suggestions:
702
+ emit_info(
703
+ Text.from_markup(
704
+ f" [cyan]{color:<16}[/cyan] [white on {color}]ā– ā– ā– ā– ā– ā– ā– ā– ā– ā– [/white on {color}] {emoji}"
705
+ )
706
+ )
707
+
708
+ emit_info("\nšŸŽØ All Available Rich Colors:")
709
+ for category, colors in color_categories.items():
710
+ emit_info(f"\n{category}:")
711
+ # Display in columns for better readability
712
+ for i in range(0, len(colors), 4):
713
+ row = colors[i : i + 4]
714
+ row_text = " ".join([f"[{color}]ā– [/{color}] {color}" for color, _ in row])
715
+ emit_info(Text.from_markup(f" {row_text}"))
716
+
717
+ emit_info("\nUsage: /diff {color_type} <color_name>")
718
+ emit_info("All diffs use white text on your chosen background colors")
719
+ emit_info("You can also use hex colors like #ff0000 or rgb(255,0,0)")