This repository was archived by the owner on Jun 5, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 91
Expand file tree
/
Copy pathcli.py
More file actions
183 lines (145 loc) · 6.38 KB
/
cli.py
File metadata and controls
183 lines (145 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import shlex
from typing import Any, Optional
import regex as re
from codegate.clients.clients import ClientType
from codegate.pipeline.base import (
PipelineContext,
PipelineResponse,
PipelineResult,
PipelineStep,
)
from codegate.pipeline.cli.commands import CustomInstructions, Version, Workspace
codegate_regex = re.compile(r"^codegate(?:\s+(.*))?", re.IGNORECASE)
HELP_TEXT = """
## CodeGate CLI\n
**Usage**: `codegate [-h] <command> [args]`\n
Check the help of each command by running `codegate <command> -h`\n
Available commands:
- `version`: Show the version of CodeGate
- `workspace`: Perform different operations on workspaces
- `custom-instructions`: Set custom instructions for the workspace
"""
NOT_FOUND_TEXT = "Command not found. Use `codegate -h` to see available commands."
async def codegate_cli(command):
"""
Process the 'codegate' command.
"""
if len(command) == 0:
return HELP_TEXT
available_commands = {
"version": Version().exec,
"workspace": Workspace().exec,
"custom-instructions": CustomInstructions().exec,
}
out_func = available_commands.get(command[0])
if out_func is None:
if command[0] == "-h":
return HELP_TEXT
return NOT_FOUND_TEXT
return await out_func(command[1:])
def _get_cli_from_cline(
codegate_regex: re.Pattern[str], last_user_message_str: str
) -> Optional[re.Match[str]]:
# Check if there are <task> or <feedback> tags
tag_match = re.search(r"<(task|feedback)>(.*?)</\1>", last_user_message_str, re.DOTALL)
if tag_match:
# Extract the content between the tags
stripped_message = tag_match.group(2).strip()
else:
# If no <task> or <feedback> tags, use the entire message
stripped_message = last_user_message_str.strip()
# Remove all other XML tags and trim whitespace
stripped_message = re.sub(r"<[^>]+>", "", stripped_message).strip()
# Check if "codegate" is the first word
match = codegate_regex.match(stripped_message)
return match
def _get_cli_from_open_interpreter(last_user_message_str: str) -> Optional[re.Match[str]]:
# Extract the last "### User:" block
user_blocks = re.findall(r"### User:\s*(.*?)(?=\n###|\Z)", last_user_message_str, re.DOTALL)
last_user_block = user_blocks[-1].strip() if user_blocks else last_user_message_str.strip()
# Match "codegate" only in the last user block or entire input
return re.match(r"^codegate\s*(.*?)\s*$", last_user_block, re.IGNORECASE)
def _get_cli_from_continue(last_user_message_str: str) -> Optional[re.Match[str]]:
"""
Continue sends a differently formatted message to the CLI if DeepSeek is used
"""
deepseek_match = re.search(
r"utilizing the DeepSeek Coder model.*?### Instruction:\s*codegate\s+(.*?)\s*### Response:",
last_user_message_str,
re.DOTALL | re.IGNORECASE,
)
if deepseek_match:
command = deepseek_match.group(1).strip()
return re.match(r"^(.*?)$", command) # This creates a match object with the command
return codegate_regex.match(last_user_message_str)
def _get_cli_from_copilot(last_user_message_str: str) -> Optional[re.Match[str]]:
"""
Process Copilot-specific CLI command format.
Copilot sends messages in the format:
<attachment>file contents</attachment>codegate command
Args:
last_user_message_str (str): The message string from Copilot
Returns:
Optional[re.Match[str]]: A regex match object if command is found, None otherwise
"""
cleaned_text = re.sub(
r"<attachment>.*</attachment>", "", last_user_message_str, flags=re.DOTALL
)
return codegate_regex.match(cleaned_text.strip())
class CodegateCli(PipelineStep):
"""Pipeline step that handles codegate cli."""
@property
def name(self) -> str:
"""
Returns the name of this pipeline step.
Returns:
str: The identifier 'codegate-cli'
"""
return "codegate-cli"
async def process(self, request: Any, context: PipelineContext) -> PipelineResult:
"""
Checks if the last user message contains "codegate" and process the command.
This short-circuits the pipeline if the message is found.
Args:
request (Any): The chat completion request to process
context (PipelineContext): The current pipeline context
Returns:
PipelineResult: Contains the response if triggered, otherwise continues
pipeline
"""
last_user_message = self.get_last_user_message(request)
if last_user_message is not None:
last_user_message_str, _ = last_user_message
last_user_message_str = last_user_message_str.strip()
# Check client-specific matchers first
if context.client in [ClientType.CLINE, ClientType.KODU]:
match = _get_cli_from_cline(codegate_regex, last_user_message_str)
elif context.client in [ClientType.OPEN_INTERPRETER]:
match = _get_cli_from_open_interpreter(last_user_message_str)
elif context.client in [ClientType.CONTINUE]:
match = _get_cli_from_continue(last_user_message_str)
elif context.client in [ClientType.COPILOT]:
match = _get_cli_from_copilot(last_user_message_str)
else:
# Check if "codegate" is the first word in the message
match = codegate_regex.match(last_user_message_str)
if match:
command = match.group(1) or ""
command = command.strip()
# Process the command
args = shlex.split(f"codegate {command}")
if args:
context.shortcut_response = True
cmd_out = await codegate_cli(args[1:])
if context.client in [ClientType.CLINE, ClientType.KODU]:
cmd_out = (
f"<attempt_completion><result>{cmd_out}</result></attempt_completion>\n"
)
return PipelineResult(
response=PipelineResponse(
step_name=self.name, content=cmd_out, model=request.get_model()
),
context=context,
)
# Fall through
return PipelineResult(request=request, context=context)