wcgw 2.8.10__py3-none-any.whl → 3.0.1rc1__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.
Potentially problematic release.
This version of wcgw might be problematic. Click here for more details.
- wcgw/__init__.py +1 -1
- wcgw/client/bash_state/bash_state.py +768 -0
- wcgw/client/encoder/__init__.py +47 -0
- wcgw/client/mcp_server/Readme.md +2 -88
- wcgw/client/mcp_server/__init__.py +2 -2
- wcgw/client/mcp_server/server.py +46 -198
- wcgw/client/modes.py +3 -9
- wcgw/client/repo_ops/display_tree.py +1 -12
- wcgw/client/tool_prompts.py +105 -0
- wcgw/client/tools.py +230 -1084
- wcgw/py.typed +0 -0
- wcgw/relay/client.py +95 -0
- wcgw/relay/serve.py +11 -45
- wcgw/types_.py +51 -61
- {wcgw-2.8.10.dist-info → wcgw-3.0.1rc1.dist-info}/METADATA +2 -3
- {wcgw-2.8.10.dist-info → wcgw-3.0.1rc1.dist-info}/RECORD +22 -19
- wcgw_cli/anthropic_client.py +253 -365
- wcgw_cli/cli.py +0 -2
- wcgw_cli/openai_client.py +223 -280
- wcgw/client/computer_use.py +0 -435
- wcgw/client/sys_utils.py +0 -41
- {wcgw-2.8.10.dist-info → wcgw-3.0.1rc1.dist-info}/WHEEL +0 -0
- {wcgw-2.8.10.dist-info → wcgw-3.0.1rc1.dist-info}/entry_points.txt +0 -0
- {wcgw-2.8.10.dist-info → wcgw-3.0.1rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from typing import Callable, Protocol, TypeVar, cast
|
|
3
|
+
|
|
4
|
+
import tokenizers # type: ignore[import-untyped]
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EncoderDecoder(Protocol[T]):
|
|
10
|
+
def encoder(self, text: str) -> list[T]: ...
|
|
11
|
+
|
|
12
|
+
def decoder(self, tokens: list[T]) -> str: ...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LazyEncoder:
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
self._tokenizer: tokenizers.Tokenizer | None = None
|
|
18
|
+
self._init_lock = threading.Lock()
|
|
19
|
+
self._init_thread = threading.Thread(target=self._initialize, daemon=True)
|
|
20
|
+
self._init_thread.start()
|
|
21
|
+
|
|
22
|
+
def _initialize(self) -> None:
|
|
23
|
+
with self._init_lock:
|
|
24
|
+
if self._tokenizer is None:
|
|
25
|
+
self._tokenizer = tokenizers.Tokenizer.from_pretrained(
|
|
26
|
+
"Xenova/claude-tokenizer"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def _ensure_initialized(self) -> None:
|
|
30
|
+
if self._tokenizer is None:
|
|
31
|
+
with self._init_lock:
|
|
32
|
+
if self._tokenizer is None:
|
|
33
|
+
self._init_thread.join()
|
|
34
|
+
|
|
35
|
+
def encoder(self, text: str) -> list[int]:
|
|
36
|
+
self._ensure_initialized()
|
|
37
|
+
assert self._tokenizer is not None, "Couldn't initialize tokenizer"
|
|
38
|
+
return cast(list[int], self._tokenizer.encode(text).ids)
|
|
39
|
+
|
|
40
|
+
def decoder(self, tokens: list[int]) -> str:
|
|
41
|
+
self._ensure_initialized()
|
|
42
|
+
assert self._tokenizer is not None, "Couldn't initialize tokenizer"
|
|
43
|
+
return cast(str, self._tokenizer.decode(tokens))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_default_encoder() -> EncoderDecoder[int]:
|
|
47
|
+
return LazyEncoder()
|
wcgw/client/mcp_server/Readme.md
CHANGED
|
@@ -1,89 +1,3 @@
|
|
|
1
|
-
#
|
|
1
|
+
# The doc has moved to main Readme.md
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
It also has a computer use feature to connect to linux running on docker. Claude can fully control it including mouse and keyboard.
|
|
6
|
-
|
|
7
|
-
## Setup
|
|
8
|
-
|
|
9
|
-
Update `claude_desktop_config.json` (~/Library/Application Support/Claude/claude_desktop_config.json)
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"mcpServers": {
|
|
14
|
-
"wcgw": {
|
|
15
|
-
"command": "uv",
|
|
16
|
-
"args": [
|
|
17
|
-
"tool",
|
|
18
|
-
"run",
|
|
19
|
-
"--from",
|
|
20
|
-
"wcgw@latest",
|
|
21
|
-
"--python",
|
|
22
|
-
"3.12",
|
|
23
|
-
"wcgw_mcp"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Then restart claude app.
|
|
31
|
-
|
|
32
|
-
### [Optional] Computer use support using desktop on docker
|
|
33
|
-
|
|
34
|
-
Computer use is disabled by default. Add `--computer-use` to enable it. This will add necessary tools to Claude including ScreenShot, Mouse and Keyboard control.
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{
|
|
38
|
-
"mcpServers": {
|
|
39
|
-
"wcgw": {
|
|
40
|
-
"command": "uv",
|
|
41
|
-
"args": [
|
|
42
|
-
"tool",
|
|
43
|
-
"run",
|
|
44
|
-
"--from",
|
|
45
|
-
"wcgw@latest",
|
|
46
|
-
"--python",
|
|
47
|
-
"3.12",
|
|
48
|
-
"wcgw_mcp",
|
|
49
|
-
"--computer-use"
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Claude will be able to connect to any docker container with linux environment. Native system control isn't supported outside docker.
|
|
57
|
-
|
|
58
|
-
You'll need to run a docker image with desktop and optional VNC connection. Here's a demo image:
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
docker run -p 6080:6080 ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Then ask claude desktop app to control the docker os. It'll connect to the docker container and control it.
|
|
65
|
-
|
|
66
|
-
Connect to `http://localhost:6080/vnc.html` for desktop view (VNC) of the system running in the docker.
|
|
67
|
-
|
|
68
|
-
## Usage
|
|
69
|
-
|
|
70
|
-
Wait for a few seconds. You should be able to see this icon if everything goes right.
|
|
71
|
-
|
|
72
|
-

|
|
73
|
-
over here
|
|
74
|
-
|
|
75
|
-

|
|
76
|
-
|
|
77
|
-
Then ask claude to execute shell commands, read files, edit files, run your code, etc.
|
|
78
|
-
|
|
79
|
-
If you've run the docker for LLM to access, you can ask it to control the "docker os". If you don't provide the docker container id to it, it'll try to search for available docker using `docker ps` command.
|
|
80
|
-
|
|
81
|
-
## Example
|
|
82
|
-
|
|
83
|
-
### Computer use example
|
|
84
|
-
|
|
85
|
-

|
|
86
|
-
|
|
87
|
-
### Shell example
|
|
88
|
-
|
|
89
|
-

|
|
3
|
+

|
|
@@ -7,9 +7,9 @@ main = Typer()
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@main.command()
|
|
10
|
-
def app(
|
|
10
|
+
def app() -> None:
|
|
11
11
|
"""Main entry point for the package."""
|
|
12
|
-
asyncio.run(server.main(
|
|
12
|
+
asyncio.run(server.main())
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Optionally expose other important items at package level
|
wcgw/client/mcp_server/server.py
CHANGED
|
@@ -11,27 +11,14 @@ from mcp_wcgw.server.models import InitializationOptions
|
|
|
11
11
|
from mcp_wcgw.types import Tool as ToolParam
|
|
12
12
|
from pydantic import AnyUrl, ValidationError
|
|
13
13
|
|
|
14
|
+
from wcgw.client.modes import KTS
|
|
15
|
+
from wcgw.client.tool_prompts import TOOL_PROMPTS
|
|
16
|
+
|
|
14
17
|
from ...types_ import (
|
|
15
|
-
BashCommand,
|
|
16
|
-
BashInteraction,
|
|
17
|
-
ContextSave,
|
|
18
|
-
FileEdit,
|
|
19
|
-
GetScreenInfo,
|
|
20
18
|
Initialize,
|
|
21
|
-
Keyboard,
|
|
22
|
-
Mouse,
|
|
23
|
-
ReadFiles,
|
|
24
|
-
ReadImage,
|
|
25
|
-
ResetShell,
|
|
26
|
-
ScreenShot,
|
|
27
|
-
WriteIfEmpty,
|
|
28
19
|
)
|
|
29
|
-
from .. import
|
|
30
|
-
from ..
|
|
31
|
-
from ..modes import get_kt_prompt
|
|
32
|
-
from ..tools import DoneFlag, default_enc, get_tool_output, which_tool_name
|
|
33
|
-
|
|
34
|
-
COMPUTER_USE_ON_DOCKER_ENABLED = False
|
|
20
|
+
from ..bash_state.bash_state import CONFIG, BashState
|
|
21
|
+
from ..tools import Context, default_enc, get_tool_output, which_tool_name
|
|
35
22
|
|
|
36
23
|
server = Server("wcgw")
|
|
37
24
|
|
|
@@ -64,7 +51,7 @@ PROMPTS = {
|
|
|
64
51
|
name="KnowledgeTransfer",
|
|
65
52
|
description="Prompt for invoking ContextSave tool in order to do a comprehensive knowledge transfer of a coding task. Prompts to save detailed error log and instructions.",
|
|
66
53
|
),
|
|
67
|
-
|
|
54
|
+
KTS,
|
|
68
55
|
)
|
|
69
56
|
}
|
|
70
57
|
|
|
@@ -78,12 +65,12 @@ async def handle_list_prompts() -> list[types.Prompt]:
|
|
|
78
65
|
async def handle_get_prompt(
|
|
79
66
|
name: str, arguments: dict[str, str] | None
|
|
80
67
|
) -> types.GetPromptResult:
|
|
68
|
+
assert BASH_STATE
|
|
81
69
|
messages = [
|
|
82
70
|
types.PromptMessage(
|
|
83
71
|
role="user",
|
|
84
72
|
content=types.TextContent(
|
|
85
|
-
type="text",
|
|
86
|
-
text=PROMPTS[name][1](),
|
|
73
|
+
type="text", text=PROMPTS[name][1][BASH_STATE.mode]
|
|
87
74
|
),
|
|
88
75
|
)
|
|
89
76
|
]
|
|
@@ -97,165 +84,22 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
97
84
|
Each tool specifies its arguments using JSON Schema validation.
|
|
98
85
|
"""
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
os.path.join(
|
|
102
|
-
os.path.dirname(os.path.dirname(__file__)), "diff-instructions.txt"
|
|
103
|
-
)
|
|
104
|
-
) as f:
|
|
105
|
-
diffinstructions = f.read()
|
|
106
|
-
|
|
107
|
-
tools = [
|
|
108
|
-
ToolParam(
|
|
109
|
-
inputSchema=Initialize.model_json_schema(),
|
|
110
|
-
name="Initialize",
|
|
111
|
-
description="""
|
|
112
|
-
- Always call this at the start of the conversation before using any of the shell tools from wcgw.
|
|
113
|
-
- This will reset the shell.
|
|
114
|
-
- Use `any_workspace_path` to initialize the shell in the appropriate project directory.
|
|
115
|
-
- If the user has mentioned a workspace or project root, use it to set `any_workspace_path`.
|
|
116
|
-
- If the user has mentioned a folder or file with unclear project root, use the file or folder as `any_workspace_path`.
|
|
117
|
-
- If user has mentioned any files use `initial_files_to_read` to read, use absolute paths only.
|
|
118
|
-
- If `any_workspace_path` is provided, a tree structure of the workspace will be shown.
|
|
119
|
-
- Leave `any_workspace_path` as empty if no file or folder is mentioned.
|
|
120
|
-
- By default use mode "wcgw"
|
|
121
|
-
- In "code-writer" mode, set the commands and globs which user asked to set, otherwise use 'all'.
|
|
122
|
-
- In order to change the mode later, call this tool again but be sure to not provide any other argument like task_id_to_resume unnecessarily.
|
|
123
|
-
""",
|
|
124
|
-
),
|
|
125
|
-
ToolParam(
|
|
126
|
-
inputSchema=BashCommand.model_json_schema(),
|
|
127
|
-
name="BashCommand",
|
|
128
|
-
description=f"""
|
|
129
|
-
- Execute a bash command. This is stateful (beware with subsequent calls).
|
|
130
|
-
- Do not use interactive commands like nano. Prefer writing simpler commands.
|
|
131
|
-
- Status of the command and the current working directory will always be returned at the end.
|
|
132
|
-
- Optionally `exit shell has restarted` is the output, in which case environment resets, you can run fresh commands.
|
|
133
|
-
- The first or the last line might be `(...truncated)` if the output is too long.
|
|
134
|
-
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
|
|
135
|
-
- The control will return to you in {SLEEP_TIME_MAX_S} seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
|
|
136
|
-
- Run long running commands in background using screen instead of "&".
|
|
137
|
-
- Use longer wait_for_seconds if the command is expected to run for a long time.
|
|
138
|
-
- Do not use 'cat' to read files, use ReadFiles tool instead.
|
|
139
|
-
""",
|
|
140
|
-
),
|
|
141
|
-
ToolParam(
|
|
142
|
-
inputSchema=BashInteraction.model_json_schema(),
|
|
143
|
-
name="BashInteraction",
|
|
144
|
-
description=f"""
|
|
145
|
-
- Interact with running program using this tool
|
|
146
|
-
- Special keys like arrows, interrupts, enter, etc.
|
|
147
|
-
- Send text input to the running program.
|
|
148
|
-
- Send send_specials=["Enter"] to recheck status of a running program.
|
|
149
|
-
- Only one of send_text, send_specials, send_ascii should be provided.
|
|
150
|
-
- This returns within {SLEEP_TIME_MAX_S} seconds, for heavy programs keep checking status for upto 10 turns before asking user to continue checking again.
|
|
151
|
-
- Programs don't hang easily, so most likely explanation for no output is usually that the program is still running, and you need to check status again using ["Enter"].
|
|
152
|
-
- Do not send Ctrl-c before checking for status till 10 minutes or whatever is appropriate for the program to finish.
|
|
153
|
-
- Set longer wait_for_seconds when program is expected to run for a long time.
|
|
154
|
-
""",
|
|
155
|
-
),
|
|
156
|
-
ToolParam(
|
|
157
|
-
inputSchema=ReadFiles.model_json_schema(),
|
|
158
|
-
name="ReadFiles",
|
|
159
|
-
description="""
|
|
160
|
-
- Read full file content of one or more files.
|
|
161
|
-
- Provide absolute file paths only
|
|
162
|
-
""",
|
|
163
|
-
),
|
|
164
|
-
ToolParam(
|
|
165
|
-
inputSchema=WriteIfEmpty.model_json_schema(),
|
|
166
|
-
name="WriteIfEmpty",
|
|
167
|
-
description="""
|
|
168
|
-
- Write content to an empty or non-existent file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
169
|
-
- Provide absolute file path only.
|
|
170
|
-
- For editing existing files, use FileEdit instead of this tool.
|
|
171
|
-
""",
|
|
172
|
-
),
|
|
173
|
-
ToolParam(
|
|
174
|
-
inputSchema=ReadImage.model_json_schema(),
|
|
175
|
-
name="ReadImage",
|
|
176
|
-
description="Read an image from the shell.",
|
|
177
|
-
),
|
|
87
|
+
tools_ = [
|
|
178
88
|
ToolParam(
|
|
179
|
-
inputSchema=
|
|
180
|
-
name=
|
|
181
|
-
description=
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
inputSchema=FileEdit.model_json_schema(),
|
|
185
|
-
name="FileEdit",
|
|
186
|
-
description="""
|
|
187
|
-
- Use absolute file path only.
|
|
188
|
-
- Use SEARCH/REPLACE blocks to edit the file.
|
|
189
|
-
- If the edit fails due to block not matching, please retry with correct block till it matches. Re-read the file to ensure you've all the lines correct.
|
|
190
|
-
"""
|
|
191
|
-
+ diffinstructions,
|
|
192
|
-
),
|
|
193
|
-
ToolParam(
|
|
194
|
-
inputSchema=ContextSave.model_json_schema(),
|
|
195
|
-
name="ContextSave",
|
|
196
|
-
description="""
|
|
197
|
-
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
|
|
198
|
-
- Provide random unqiue id or whatever user provided.
|
|
199
|
-
- Leave project path as empty string if no project path""",
|
|
200
|
-
),
|
|
89
|
+
inputSchema=tool.inputSchema,
|
|
90
|
+
name=tool.name,
|
|
91
|
+
description=tool.description,
|
|
92
|
+
)
|
|
93
|
+
for tool in TOOL_PROMPTS
|
|
201
94
|
]
|
|
202
|
-
|
|
203
|
-
tools += [
|
|
204
|
-
ToolParam(
|
|
205
|
-
inputSchema=GetScreenInfo.model_json_schema(),
|
|
206
|
-
name="GetScreenInfo",
|
|
207
|
-
description="""
|
|
208
|
-
- Important: call this first in the conversation before ScreenShot, Mouse, and Keyboard tools.
|
|
209
|
-
- Get display information of a linux os running on docker using image "ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest"
|
|
210
|
-
- If user hasn't provided docker image id, check using `docker ps` and provide the id.
|
|
211
|
-
- If the docker is not running, run using `docker run -d -p 6080:6080 ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest`
|
|
212
|
-
- Connects shell to the docker environment.
|
|
213
|
-
- Note: once this is called, the shell enters the docker environment. All bash commands will run over there.
|
|
214
|
-
""",
|
|
215
|
-
),
|
|
216
|
-
ToolParam(
|
|
217
|
-
inputSchema=ScreenShot.model_json_schema(),
|
|
218
|
-
name="ScreenShot",
|
|
219
|
-
description="""
|
|
220
|
-
- Capture screenshot of the linux os on docker.
|
|
221
|
-
- All actions on UI using mouse and keyboard return within 0.5 seconds.
|
|
222
|
-
* So if you're doing something that takes longer for UI to update like heavy page loading, keep checking UI for update using ScreenShot upto 10 turns.
|
|
223
|
-
* Notice for smallest of the loading icons to check if your action worked.
|
|
224
|
-
* After 10 turns of no change, ask user for permission to keep checking.
|
|
225
|
-
* If you don't notice even slightest of the change, it's likely you clicked on the wrong place.
|
|
226
|
-
""",
|
|
227
|
-
),
|
|
228
|
-
ToolParam(
|
|
229
|
-
inputSchema=Mouse.model_json_schema(),
|
|
230
|
-
name="Mouse",
|
|
231
|
-
description="""
|
|
232
|
-
- Interact with the linux os on docker using mouse.
|
|
233
|
-
- Uses xdotool
|
|
234
|
-
- About left_click_drag: the current mouse position will be used as the starting point, click and drag to the given x, y coordinates. Useful in things like sliders, moving things around, etc.
|
|
235
|
-
- The output of this command has the screenshot after doing this action. Use this to verify if the action was successful.
|
|
236
|
-
""",
|
|
237
|
-
),
|
|
238
|
-
ToolParam(
|
|
239
|
-
inputSchema=Keyboard.model_json_schema(),
|
|
240
|
-
name="Keyboard",
|
|
241
|
-
description="""
|
|
242
|
-
- Interact with the linux os on docker using keyboard.
|
|
243
|
-
- Emulate keyboard input to the screen
|
|
244
|
-
- Uses xdootool to send keyboard input, keys like Return, BackSpace, Escape, Page_Up, etc. can be used.
|
|
245
|
-
- Do not use it to interact with Bash tool.
|
|
246
|
-
- Make sure you've selected a text area or an editable element before sending text.
|
|
247
|
-
- The output of this command has the screenshot after doing this action. Use this to verify if the action was successful.
|
|
248
|
-
""",
|
|
249
|
-
),
|
|
250
|
-
]
|
|
251
|
-
|
|
252
|
-
return tools
|
|
95
|
+
return tools_
|
|
253
96
|
|
|
254
97
|
|
|
255
98
|
@server.call_tool() # type: ignore
|
|
256
99
|
async def handle_call_tool(
|
|
257
100
|
name: str, arguments: dict[str, Any] | None
|
|
258
101
|
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
102
|
+
global BASH_STATE
|
|
259
103
|
if not arguments:
|
|
260
104
|
raise ValueError("Missing arguments")
|
|
261
105
|
|
|
@@ -276,8 +120,14 @@ async def handle_call_tool(
|
|
|
276
120
|
tool_call = tool_type(**{k: try_json(v) for k, v in arguments.items()})
|
|
277
121
|
|
|
278
122
|
try:
|
|
123
|
+
assert BASH_STATE
|
|
279
124
|
output_or_dones, _ = get_tool_output(
|
|
280
|
-
|
|
125
|
+
Context(BASH_STATE, BASH_STATE.console),
|
|
126
|
+
tool_call,
|
|
127
|
+
default_enc,
|
|
128
|
+
0.0,
|
|
129
|
+
lambda x, y: ("", 0),
|
|
130
|
+
8000,
|
|
281
131
|
)
|
|
282
132
|
|
|
283
133
|
except Exception as e:
|
|
@@ -285,7 +135,6 @@ async def handle_call_tool(
|
|
|
285
135
|
|
|
286
136
|
content: list[types.TextContent | types.ImageContent | types.EmbeddedResource] = []
|
|
287
137
|
for output_or_done in output_or_dones:
|
|
288
|
-
assert not isinstance(output_or_done, DoneFlag)
|
|
289
138
|
if isinstance(output_or_done, str):
|
|
290
139
|
if issubclass(tool_type, Initialize):
|
|
291
140
|
output_or_done += """
|
|
@@ -307,31 +156,30 @@ Initialize call done.
|
|
|
307
156
|
return content
|
|
308
157
|
|
|
309
158
|
|
|
310
|
-
|
|
311
|
-
global COMPUTER_USE_ON_DOCKER_ENABLED
|
|
312
|
-
|
|
313
|
-
tools.TIMEOUT = SLEEP_TIME_MAX_S
|
|
314
|
-
tools.TIMEOUT_WHILE_OUTPUT = 55
|
|
315
|
-
tools.OUTPUT_WAIT_PATIENCE = 5
|
|
316
|
-
tools.console = Console()
|
|
159
|
+
BASH_STATE = None
|
|
317
160
|
|
|
318
|
-
if computer_use:
|
|
319
|
-
COMPUTER_USE_ON_DOCKER_ENABLED = True
|
|
320
161
|
|
|
162
|
+
async def main() -> None:
|
|
163
|
+
global BASH_STATE
|
|
164
|
+
CONFIG.update(3, 55, 5)
|
|
321
165
|
version = str(importlib.metadata.version("wcgw"))
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
166
|
+
home_dir = os.path.expanduser("~")
|
|
167
|
+
with BashState(
|
|
168
|
+
Console(), home_dir, None, None, None, None, False, None
|
|
169
|
+
) as BASH_STATE:
|
|
170
|
+
BASH_STATE.console.log("wcgw version: " + version)
|
|
171
|
+
# Run the server using stdin/stdout streams
|
|
172
|
+
async with mcp_wcgw.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
173
|
+
await server.run(
|
|
174
|
+
read_stream,
|
|
175
|
+
write_stream,
|
|
176
|
+
InitializationOptions(
|
|
177
|
+
server_name="wcgw",
|
|
178
|
+
server_version=version,
|
|
179
|
+
capabilities=server.get_capabilities(
|
|
180
|
+
notification_options=NotificationOptions(),
|
|
181
|
+
experimental_capabilities={},
|
|
182
|
+
),
|
|
334
183
|
),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
)
|
|
184
|
+
raise_exceptions=False,
|
|
185
|
+
)
|
wcgw/client/modes.py
CHANGED
|
@@ -61,7 +61,7 @@ You are now running in "code_writer" mode.
|
|
|
61
61
|
if allowed_file_edit_globs != "all":
|
|
62
62
|
if allowed_file_edit_globs:
|
|
63
63
|
path_prompt = f"""
|
|
64
|
-
- You are allowed to run FileEdit for files matching only the following globs: {
|
|
64
|
+
- You are allowed to run FileEdit for files matching only the following globs: {", ".join(allowed_file_edit_globs)}
|
|
65
65
|
"""
|
|
66
66
|
else:
|
|
67
67
|
path_prompt = """
|
|
@@ -76,7 +76,7 @@ You are now running in "code_writer" mode.
|
|
|
76
76
|
if all_write_new_globs != "all":
|
|
77
77
|
if all_write_new_globs:
|
|
78
78
|
path_prompt = f"""
|
|
79
|
-
- You are allowed to run WriteIfEmpty files matching only the following globs: {
|
|
79
|
+
- You are allowed to run WriteIfEmpty files matching only the following globs: {", ".join(allowed_file_edit_globs)}
|
|
80
80
|
"""
|
|
81
81
|
else:
|
|
82
82
|
path_prompt = """
|
|
@@ -101,7 +101,7 @@ You are now running in "code_writer" mode.
|
|
|
101
101
|
if allowed_commands != "all":
|
|
102
102
|
if allowed_commands:
|
|
103
103
|
command_prompt = f"""
|
|
104
|
-
- You are only allowed to run the following commands: {
|
|
104
|
+
- You are only allowed to run the following commands: {", ".join(allowed_commands)}
|
|
105
105
|
{run_command_common}
|
|
106
106
|
"""
|
|
107
107
|
else:
|
|
@@ -234,9 +234,3 @@ Provide all relevant file paths in order to understand and solve the the task. E
|
|
|
234
234
|
"""
|
|
235
235
|
|
|
236
236
|
KTS = {Modes.wcgw: WCGW_KT, Modes.architect: ARCHITECT_KT, Modes.code_writer: WCGW_KT}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def get_kt_prompt() -> str:
|
|
240
|
-
from .tools import BASH_STATE
|
|
241
|
-
|
|
242
|
-
return KTS[BASH_STATE.mode]
|
|
@@ -109,18 +109,7 @@ class DirectoryTree:
|
|
|
109
109
|
current_path, shown_items
|
|
110
110
|
)
|
|
111
111
|
if hidden_files > 0 or hidden_dirs > 0:
|
|
112
|
-
|
|
113
|
-
if hidden_dirs > 0:
|
|
114
|
-
hidden_msg.append(
|
|
115
|
-
f"{hidden_dirs} director{'ies' if hidden_dirs != 1 else 'y'}"
|
|
116
|
-
)
|
|
117
|
-
if hidden_files > 0:
|
|
118
|
-
hidden_msg.append(
|
|
119
|
-
f"{hidden_files} file{'s' if hidden_files != 1 else ''}"
|
|
120
|
-
)
|
|
121
|
-
writer.write(
|
|
122
|
-
f"{' ' * (indent + 2)}... {' and '.join(hidden_msg)} hidden\n"
|
|
123
|
-
)
|
|
112
|
+
writer.write(f"{' ' * (indent + 2)}...\n")
|
|
124
113
|
|
|
125
114
|
_display_recursive(self.root, depth=0)
|
|
126
115
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..types_ import (
|
|
6
|
+
BashCommand,
|
|
7
|
+
ContextSave,
|
|
8
|
+
FileEdit,
|
|
9
|
+
Initialize,
|
|
10
|
+
ReadFiles,
|
|
11
|
+
ReadImage,
|
|
12
|
+
ResetWcgw,
|
|
13
|
+
WriteIfEmpty,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f:
|
|
17
|
+
diffinstructions = f.read()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Prompts:
|
|
22
|
+
inputSchema: dict[str, Any]
|
|
23
|
+
name: str
|
|
24
|
+
description: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
TOOL_PROMPTS = [
|
|
28
|
+
Prompts(
|
|
29
|
+
inputSchema=Initialize.model_json_schema(),
|
|
30
|
+
name="Initialize",
|
|
31
|
+
description="""
|
|
32
|
+
- Always call this at the start of the conversation before using any of the shell tools from wcgw.
|
|
33
|
+
- This will reset the shell.
|
|
34
|
+
- Use `any_workspace_path` to initialize the shell in the appropriate project directory.
|
|
35
|
+
- If the user has mentioned a workspace or project root, use it to set `any_workspace_path`.
|
|
36
|
+
- If the user has mentioned a folder or file with unclear project root, use the file or folder as `any_workspace_path`.
|
|
37
|
+
- If user has mentioned any files use `initial_files_to_read` to read, use absolute paths only.
|
|
38
|
+
- Leave `any_workspace_path` as empty if no file or folder is mentioned.
|
|
39
|
+
- By default use mode "wcgw"
|
|
40
|
+
- In "code-writer" mode, set the commands and globs which user asked to set, otherwise use 'all'.
|
|
41
|
+
- Call `ResetWcgw` if you want to change the mode later.
|
|
42
|
+
""",
|
|
43
|
+
),
|
|
44
|
+
Prompts(
|
|
45
|
+
inputSchema=BashCommand.model_json_schema(),
|
|
46
|
+
name="BashCommand",
|
|
47
|
+
description="""
|
|
48
|
+
- Execute a bash command. This is stateful (beware with subsequent calls).
|
|
49
|
+
- Status of the command and the current working directory will always be returned at the end.
|
|
50
|
+
- The first or the last line might be `(...truncated)` if the output is too long.
|
|
51
|
+
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
|
|
52
|
+
- Run long running commands in background using screen instead of "&".
|
|
53
|
+
- Do not use 'cat' to read files, use ReadFiles tool instead
|
|
54
|
+
- In order to check status of previous command, use `status_check` with empty command argument.
|
|
55
|
+
- Only command is allowed to run at a time. You need to wait for any previous command to finish before running a new one.
|
|
56
|
+
- Programs don't hang easily, so most likely explanation for no output is usually that the program is still running, and you need to check status again.
|
|
57
|
+
- Do not send Ctrl-c before checking for status till 10 minutes or whatever is appropriate for the program to finish.
|
|
58
|
+
""",
|
|
59
|
+
),
|
|
60
|
+
Prompts(
|
|
61
|
+
inputSchema=ReadFiles.model_json_schema(),
|
|
62
|
+
name="ReadFiles",
|
|
63
|
+
description="""
|
|
64
|
+
- Read full file content of one or more files.
|
|
65
|
+
- Provide absolute file paths only
|
|
66
|
+
""",
|
|
67
|
+
),
|
|
68
|
+
Prompts(
|
|
69
|
+
inputSchema=WriteIfEmpty.model_json_schema(),
|
|
70
|
+
name="WriteIfEmpty",
|
|
71
|
+
description="""
|
|
72
|
+
- Write content to an empty or non-existent file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
73
|
+
- Provide absolute file path only.
|
|
74
|
+
- For editing existing files, use FileEdit instead of this tool.
|
|
75
|
+
""",
|
|
76
|
+
),
|
|
77
|
+
Prompts(
|
|
78
|
+
inputSchema=ReadImage.model_json_schema(),
|
|
79
|
+
name="ReadImage",
|
|
80
|
+
description="Read an image from the shell.",
|
|
81
|
+
),
|
|
82
|
+
Prompts(
|
|
83
|
+
inputSchema=ResetWcgw.model_json_schema(),
|
|
84
|
+
name="ResetWcgw",
|
|
85
|
+
description="Resets the shell. Use either when changing mode, or when all interrupts and prompt reset attempts have failed repeatedly.",
|
|
86
|
+
),
|
|
87
|
+
Prompts(
|
|
88
|
+
inputSchema=FileEdit.model_json_schema(),
|
|
89
|
+
name="FileEdit",
|
|
90
|
+
description="""
|
|
91
|
+
- Use absolute file path only.
|
|
92
|
+
- Use SEARCH/REPLACE blocks to edit the file.
|
|
93
|
+
- If the edit fails due to block not matching, please retry with correct block till it matches. Re-read the file to ensure you've all the lines correct.
|
|
94
|
+
"""
|
|
95
|
+
+ diffinstructions,
|
|
96
|
+
),
|
|
97
|
+
Prompts(
|
|
98
|
+
inputSchema=ContextSave.model_json_schema(),
|
|
99
|
+
name="ContextSave",
|
|
100
|
+
description="""
|
|
101
|
+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
|
|
102
|
+
- Provide random unqiue id or whatever user provided.
|
|
103
|
+
- Leave project path as empty string if no project path""",
|
|
104
|
+
),
|
|
105
|
+
]
|