code-puppy 0.0.214__py3-none-any.whl → 0.0.366__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_puppy/__init__.py +7 -1
- code_puppy/agents/__init__.py +2 -0
- code_puppy/agents/agent_c_reviewer.py +59 -6
- code_puppy/agents/agent_code_puppy.py +7 -1
- code_puppy/agents/agent_code_reviewer.py +12 -2
- code_puppy/agents/agent_cpp_reviewer.py +73 -6
- code_puppy/agents/agent_creator_agent.py +45 -4
- code_puppy/agents/agent_golang_reviewer.py +92 -3
- code_puppy/agents/agent_javascript_reviewer.py +101 -8
- code_puppy/agents/agent_manager.py +81 -4
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +28 -6
- code_puppy/agents/agent_qa_expert.py +98 -6
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_security_auditor.py +113 -3
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +106 -7
- code_puppy/agents/base_agent.py +802 -176
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +142 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +10 -5
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +176 -738
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +0 -3
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +15 -26
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +2 -2
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +16 -10
- code_puppy/command_line/mcp/start_all_command.py +18 -6
- code_puppy/command_line/mcp/start_command.py +47 -25
- code_puppy/command_line/mcp/status_command.py +4 -5
- code_puppy/command_line/mcp/stop_all_command.py +7 -1
- code_puppy/command_line/mcp/stop_command.py +8 -4
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/wizard_utils.py +20 -16
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +75 -25
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +463 -63
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +898 -112
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +210 -148
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -698
- code_puppy/mcp_/__init__.py +17 -0
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/blocking_startup.py +70 -43
- code_puppy/mcp_/captured_stdio_server.py +2 -2
- code_puppy/mcp_/config_wizard.py +4 -4
- code_puppy/mcp_/dashboard.py +15 -6
- code_puppy/mcp_/managed_server.py +65 -38
- code_puppy/mcp_/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +6 -6
- code_puppy/mcp_/server_registry_catalog.py +24 -5
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +21 -5
- code_puppy/messaging/spinner/console_spinner.py +86 -51
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +634 -83
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +66 -68
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +9 -12
- code_puppy/session_storage.py +2 -1
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +41 -13
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +536 -52
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +19 -23
- code_puppy/tools/browser/browser_interactions.py +41 -48
- code_puppy/tools/browser/browser_locators.py +36 -38
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +16 -16
- code_puppy/tools/browser/browser_screenshot.py +79 -143
- code_puppy/tools/browser/browser_scripts.py +32 -42
- code_puppy/tools/browser/browser_workflows.py +44 -27
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +930 -147
- code_puppy/tools/common.py +1113 -5
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +226 -154
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/METADATA +149 -75
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/messaging/spinner/textual_spinner.py +0 -106
- code_puppy/tools/browser/camoufox_manager.py +0 -216
- code_puppy/tools/browser/vqa_agent.py +0 -70
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -1105
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -551
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -185
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -17
- code_puppy/tui/screens/autosave_picker.py +0 -175
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -306
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy/tui_state.py +0 -55
- code_puppy-0.0.214.data/data/code_puppy/models.json +0 -112
- code_puppy-0.0.214.dist-info/RECORD +0 -131
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
|
|
4
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
5
|
+
from prompt_toolkit.document import Document
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_json_agents_for_model(model_name: str) -> list:
|
|
9
|
+
"""Get JSON agents that have this model pinned in their JSON file."""
|
|
10
|
+
try:
|
|
11
|
+
from code_puppy.agents.json_agent import discover_json_agents
|
|
12
|
+
|
|
13
|
+
pinned = []
|
|
14
|
+
json_agents = discover_json_agents()
|
|
15
|
+
for agent_name, agent_path in json_agents.items():
|
|
16
|
+
try:
|
|
17
|
+
with open(agent_path, "r") as f:
|
|
18
|
+
agent_data = json.load(f)
|
|
19
|
+
if agent_data.get("model") == model_name:
|
|
20
|
+
pinned.append(agent_name)
|
|
21
|
+
except Exception:
|
|
22
|
+
continue
|
|
23
|
+
return pinned
|
|
24
|
+
except Exception:
|
|
25
|
+
return []
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_pinned_model_for_agent(agent_name: str) -> str | None:
|
|
29
|
+
"""Get the pinned model for an agent (config or JSON)."""
|
|
30
|
+
# Check config first (for built-in agents)
|
|
31
|
+
try:
|
|
32
|
+
from code_puppy.config import get_agent_pinned_model
|
|
33
|
+
|
|
34
|
+
pinned = get_agent_pinned_model(agent_name)
|
|
35
|
+
if pinned:
|
|
36
|
+
return pinned
|
|
37
|
+
except Exception:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Check if it's a JSON agent with a model key
|
|
41
|
+
try:
|
|
42
|
+
from code_puppy.agents.json_agent import discover_json_agents
|
|
43
|
+
|
|
44
|
+
json_agents = discover_json_agents()
|
|
45
|
+
if agent_name in json_agents:
|
|
46
|
+
with open(json_agents[agent_name], "r") as f:
|
|
47
|
+
agent_data = json.load(f)
|
|
48
|
+
return agent_data.get("model")
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_model_display_meta(model_name: str) -> str:
|
|
56
|
+
"""Get display meta for a model showing pinned agents."""
|
|
57
|
+
try:
|
|
58
|
+
from code_puppy.config import get_agents_pinned_to_model
|
|
59
|
+
|
|
60
|
+
pinned_agents = get_agents_pinned_to_model(model_name)
|
|
61
|
+
pinned_agents.extend(_get_json_agents_for_model(model_name))
|
|
62
|
+
pinned_agents = list(set(pinned_agents)) # Deduplicate
|
|
63
|
+
|
|
64
|
+
if pinned_agents:
|
|
65
|
+
agents_str = ", ".join(pinned_agents[:2])
|
|
66
|
+
if len(pinned_agents) > 2:
|
|
67
|
+
agents_str += "..."
|
|
68
|
+
return f"Pinned: [{agents_str}]"
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
return "Model"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_agent_display_meta(agent_name: str) -> str:
|
|
75
|
+
"""Get display meta for an agent showing pinned model."""
|
|
76
|
+
pinned_model = _get_pinned_model_for_agent(agent_name)
|
|
77
|
+
if pinned_model:
|
|
78
|
+
return f"→ {pinned_model}"
|
|
79
|
+
return "default"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_agent_names():
|
|
83
|
+
"""Load all available agent names (both built-in and JSON agents)."""
|
|
84
|
+
agents = set()
|
|
85
|
+
|
|
86
|
+
# Get built-in agents
|
|
87
|
+
try:
|
|
88
|
+
from code_puppy.agents.agent_manager import get_agent_descriptions
|
|
89
|
+
|
|
90
|
+
builtin_agents = get_agent_descriptions()
|
|
91
|
+
agents.update(builtin_agents.keys())
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Get JSON agents
|
|
96
|
+
try:
|
|
97
|
+
from code_puppy.agents.json_agent import discover_json_agents
|
|
98
|
+
|
|
99
|
+
json_agents = discover_json_agents()
|
|
100
|
+
agents.update(json_agents.keys())
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
return sorted(list(agents))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def load_model_names():
|
|
108
|
+
"""Load model names from the config."""
|
|
109
|
+
try:
|
|
110
|
+
from code_puppy.command_line.model_picker_completion import (
|
|
111
|
+
load_model_names as load_models,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return load_models()
|
|
115
|
+
except Exception:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class PinCompleter(Completer):
|
|
120
|
+
"""
|
|
121
|
+
A completer that triggers on '/pin_model' to show available agents
|
|
122
|
+
and models for pinning a model to an agent.
|
|
123
|
+
|
|
124
|
+
Usage: /pin_model <agent-name> <model-name>
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, trigger: str = "/pin_model"):
|
|
128
|
+
self.trigger = trigger
|
|
129
|
+
|
|
130
|
+
def get_completions(
|
|
131
|
+
self, document: Document, complete_event
|
|
132
|
+
) -> Iterable[Completion]:
|
|
133
|
+
text = document.text
|
|
134
|
+
cursor_position = document.cursor_position
|
|
135
|
+
text_before_cursor = text[:cursor_position]
|
|
136
|
+
|
|
137
|
+
# Only trigger if /pin_model is at the very beginning of the line and has a space after it
|
|
138
|
+
stripped_text = text_before_cursor.lstrip()
|
|
139
|
+
if not stripped_text.startswith(self.trigger + " "):
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Find where /pin_model actually starts (after any leading whitespace)
|
|
143
|
+
trigger_pos = text_before_cursor.find(self.trigger)
|
|
144
|
+
|
|
145
|
+
# Get the command part (everything after the trigger and space)
|
|
146
|
+
command_part = text_before_cursor[
|
|
147
|
+
trigger_pos + len(self.trigger) + 1 :
|
|
148
|
+
].lstrip()
|
|
149
|
+
|
|
150
|
+
# Check if we're positioned at the very end (cursor at end of text)
|
|
151
|
+
cursor_at_end = cursor_position == len(text)
|
|
152
|
+
|
|
153
|
+
# Better tokenization: split on spaces, but keep track of cursor position
|
|
154
|
+
tokens = command_part.split() if command_part.strip() else []
|
|
155
|
+
|
|
156
|
+
# Case 1: No arguments yet - complete agent names
|
|
157
|
+
if len(tokens) == 0:
|
|
158
|
+
agent_names = load_agent_names()
|
|
159
|
+
for agent_name in agent_names:
|
|
160
|
+
yield Completion(
|
|
161
|
+
agent_name,
|
|
162
|
+
start_position=-len(command_part),
|
|
163
|
+
display=agent_name,
|
|
164
|
+
display_meta=_get_agent_display_meta(agent_name),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Case 2: Completing first argument (agent name)
|
|
168
|
+
elif len(tokens) == 1:
|
|
169
|
+
# Check cursor position to determine if we're still typing agent or ready for model
|
|
170
|
+
partial_agent = tokens[0]
|
|
171
|
+
|
|
172
|
+
# If we have exactly one token and the cursor is after it (with space),
|
|
173
|
+
# we should show model completions
|
|
174
|
+
if (
|
|
175
|
+
command_part.endswith(" ")
|
|
176
|
+
and cursor_at_end
|
|
177
|
+
and text_before_cursor.endswith(" ")
|
|
178
|
+
):
|
|
179
|
+
# User has typed agent + space, show all models
|
|
180
|
+
model_names = load_model_names()
|
|
181
|
+
# Always show (unpin) option first
|
|
182
|
+
yield Completion(
|
|
183
|
+
"(unpin)",
|
|
184
|
+
start_position=0, # Insert at cursor position
|
|
185
|
+
display="(unpin)",
|
|
186
|
+
display_meta="Reset to default",
|
|
187
|
+
)
|
|
188
|
+
for model_name in model_names:
|
|
189
|
+
yield Completion(
|
|
190
|
+
model_name,
|
|
191
|
+
start_position=0, # Insert at cursor position
|
|
192
|
+
display=model_name,
|
|
193
|
+
display_meta=_get_model_display_meta(model_name),
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
# Still typing agent name, show agent completions
|
|
197
|
+
agent_names = load_agent_names()
|
|
198
|
+
start_pos = -(len(partial_agent))
|
|
199
|
+
|
|
200
|
+
for agent_name in agent_names:
|
|
201
|
+
if agent_name.lower().startswith(partial_agent.lower()):
|
|
202
|
+
yield Completion(
|
|
203
|
+
agent_name,
|
|
204
|
+
start_position=start_pos,
|
|
205
|
+
display=agent_name,
|
|
206
|
+
display_meta=_get_agent_display_meta(agent_name),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Case 3: Completing second argument (model name)
|
|
210
|
+
elif len(tokens) == 2:
|
|
211
|
+
# We're typing the model name
|
|
212
|
+
model_names = load_model_names()
|
|
213
|
+
partial_model = tokens[1]
|
|
214
|
+
|
|
215
|
+
# If partial model is empty (shouldn't happen with split), show all models + (unpin)
|
|
216
|
+
if not partial_model:
|
|
217
|
+
# Always show (unpin) option first
|
|
218
|
+
yield Completion(
|
|
219
|
+
"(unpin)",
|
|
220
|
+
start_position=0,
|
|
221
|
+
display="(unpin)",
|
|
222
|
+
display_meta="Reset to default",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
for model_name in model_names:
|
|
226
|
+
yield Completion(
|
|
227
|
+
model_name,
|
|
228
|
+
start_position=0,
|
|
229
|
+
display=model_name,
|
|
230
|
+
display_meta=_get_model_display_meta(model_name),
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
# Filter based on what the user has typed
|
|
234
|
+
start_pos = -(len(partial_model))
|
|
235
|
+
|
|
236
|
+
# Check if (unpin) matches the partial input (case-insensitive)
|
|
237
|
+
if "(unpin)".lower().startswith(partial_model.lower()):
|
|
238
|
+
yield Completion(
|
|
239
|
+
"(unpin)",
|
|
240
|
+
start_position=start_pos,
|
|
241
|
+
display="(unpin)",
|
|
242
|
+
display_meta="Reset to default",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Filter models based on what the user has typed (case-insensitive)
|
|
246
|
+
for model_name in model_names:
|
|
247
|
+
if model_name.lower().startswith(partial_model.lower()):
|
|
248
|
+
yield Completion(
|
|
249
|
+
model_name,
|
|
250
|
+
start_position=start_pos,
|
|
251
|
+
display=model_name,
|
|
252
|
+
display_meta=_get_model_display_meta(model_name),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Case 4: Handle special case when user selected (unpin)
|
|
256
|
+
elif len(tokens) >= 2 and tokens[1].lower() == "(unpin)".lower():
|
|
257
|
+
# No completion needed, the (unpin) option is complete
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
# Case 5: Have both agent and model - no completion needed
|
|
261
|
+
else:
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Alias for backwards compatibility
|
|
266
|
+
PinModelCompleter = PinCompleter
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class UnpinCompleter(Completer):
|
|
270
|
+
"""
|
|
271
|
+
A completer that triggers on '/unpin' to show available agents
|
|
272
|
+
for unpinning models from agents.
|
|
273
|
+
|
|
274
|
+
Usage: /unpin <agent-name>
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
def __init__(self, trigger: str = "/unpin"):
|
|
278
|
+
self.trigger = trigger
|
|
279
|
+
|
|
280
|
+
def get_completions(
|
|
281
|
+
self, document: Document, complete_event
|
|
282
|
+
) -> Iterable[Completion]:
|
|
283
|
+
text = document.text
|
|
284
|
+
cursor_position = document.cursor_position
|
|
285
|
+
text_before_cursor = text[:cursor_position]
|
|
286
|
+
|
|
287
|
+
# Only trigger if /unpin is at the very beginning of the line and has a space after it
|
|
288
|
+
stripped_text = text_before_cursor.lstrip()
|
|
289
|
+
if not stripped_text.startswith(self.trigger + " "):
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
# Find where /unpin actually starts (after any leading whitespace)
|
|
293
|
+
trigger_pos = text_before_cursor.find(self.trigger)
|
|
294
|
+
|
|
295
|
+
# Get the command part (everything after the trigger and space)
|
|
296
|
+
command_part = text_before_cursor[
|
|
297
|
+
trigger_pos + len(self.trigger) + 1 :
|
|
298
|
+
].lstrip()
|
|
299
|
+
|
|
300
|
+
# Only complete agent names (single argument)
|
|
301
|
+
tokens = command_part.split() if command_part.strip() else []
|
|
302
|
+
|
|
303
|
+
if len(tokens) == 0:
|
|
304
|
+
# Show all available agents
|
|
305
|
+
agent_names = load_agent_names()
|
|
306
|
+
for agent_name in agent_names:
|
|
307
|
+
yield Completion(
|
|
308
|
+
agent_name,
|
|
309
|
+
start_position=-len(command_part),
|
|
310
|
+
display=agent_name,
|
|
311
|
+
display_meta=_get_agent_display_meta(agent_name),
|
|
312
|
+
)
|
|
313
|
+
elif len(tokens) == 1:
|
|
314
|
+
# Filter agent names based on partial input
|
|
315
|
+
agent_names = load_agent_names()
|
|
316
|
+
partial_agent = tokens[0]
|
|
317
|
+
start_pos = -(len(partial_agent))
|
|
318
|
+
|
|
319
|
+
for agent_name in agent_names:
|
|
320
|
+
if agent_name.lower().startswith(partial_agent.lower()):
|
|
321
|
+
yield Completion(
|
|
322
|
+
agent_name,
|
|
323
|
+
start_position=start_pos,
|
|
324
|
+
display=agent_name,
|
|
325
|
+
display_meta=_get_agent_display_meta(agent_name),
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
# No completion for additional arguments
|
|
329
|
+
return
|