wcgw 2.8.10__py3-none-any.whl → 3.0.1__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.

@@ -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()
@@ -102,6 +102,8 @@ def line_process_max_space_tolerance(line: str) -> str:
102
102
  return re.sub(r"\s", "", line)
103
103
 
104
104
 
105
+ REMOVE_INDENTATION = "Warning: matching after removing all spaces in lines."
106
+
105
107
  DEFAULT_TOLERANCES = [
106
108
  Tolerance(
107
109
  line_process=str.rstrip,
@@ -119,11 +121,50 @@ DEFAULT_TOLERANCES = [
119
121
  line_process=line_process_max_space_tolerance,
120
122
  severity_cat="WARNING",
121
123
  score_multiplier=50,
122
- error_name="Warning: matching after removing all spaces in lines.",
124
+ error_name=REMOVE_INDENTATION,
123
125
  ),
124
126
  ]
125
127
 
126
128
 
129
+ def fix_indentation(
130
+ matched_lines: list[str], searched_lines: list[str], replaced_lines: list[str]
131
+ ) -> list[str]:
132
+ if not matched_lines or not searched_lines or not replaced_lines:
133
+ return replaced_lines
134
+
135
+ def get_indentation(line: str) -> str:
136
+ match = re.match(r"^(\s*)", line)
137
+ assert match
138
+ return match.group(0)
139
+
140
+ matched_indents = [get_indentation(line) for line in matched_lines if line.strip()]
141
+ searched_indents = [
142
+ get_indentation(line) for line in searched_lines if line.strip()
143
+ ]
144
+ if len(matched_indents) != len(searched_indents):
145
+ return replaced_lines
146
+ diffs: list[int] = [
147
+ len(searched) - len(matched)
148
+ for matched, searched in zip(matched_indents, searched_indents)
149
+ ]
150
+ if not all(diff == diffs[0] for diff in diffs):
151
+ return replaced_lines
152
+ if diffs[0] == 0:
153
+ return replaced_lines
154
+
155
+ # At this point we have same number of non-empty lines and the same indentation difference
156
+ # We can now adjust the indentation of the replaced lines
157
+ def adjust_indentation(line: str, diff: int) -> str:
158
+ if diff < 0:
159
+ return matched_indents[0][:-diff] + line
160
+ return line[diff:]
161
+
162
+ if diffs[0] > 0:
163
+ if not (all(not line[: diffs[0]].strip() for line in replaced_lines)):
164
+ return replaced_lines
165
+ return [adjust_indentation(line, diffs[0]) for line in replaced_lines]
166
+
167
+
127
168
  def remove_leading_trailing_empty_lines(lines: list[str]) -> list[str]:
128
169
  start = 0
129
170
  end = len(lines) - 1
@@ -247,6 +288,16 @@ class FileEditInput:
247
288
  ]
248
289
 
249
290
  for match, tolerances in matches_with_tolerances:
291
+ if any(
292
+ tolerance.error_name == REMOVE_INDENTATION
293
+ for tolerance in tolerances
294
+ ):
295
+ replace_by = fix_indentation(
296
+ self.file_lines[match.start : match.stop],
297
+ first_block[0],
298
+ replace_by,
299
+ )
300
+
250
301
  file_edit_input = FileEditInput(
251
302
  self.file_lines,
252
303
  match.stop,
@@ -1,89 +1,3 @@
1
- # Claude desktop support
1
+ # The doc has moved to main Readme.md
2
2
 
3
- `wcgw` enables Claude desktop app on Mac to access shell and file system in order to automate tasks, run code, etc.
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
- ![mcp icon](https://github.com/rusiaaman/wcgw/blob/main/static/rocket-icon.png?raw=true)
73
- over here
74
-
75
- ![mcp icon](https://github.com/rusiaaman/wcgw/blob/main/static/claude-ss.jpg?raw=true)
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
- ![computer-use](https://github.com/rusiaaman/wcgw/blob/main/static/computer-use.jpg?raw=true)
86
-
87
- ### Shell example
88
-
89
- ![example](https://github.com/rusiaaman/wcgw/blob/main/static/example.jpg?raw=true)
3
+ ![main readme](https://github.com/rusiaaman/wcgw/blob/main/README.md)
@@ -7,9 +7,9 @@ main = Typer()
7
7
 
8
8
 
9
9
  @main.command()
10
- def app(computer_use: bool = False) -> None:
10
+ def app() -> None:
11
11
  """Main entry point for the package."""
12
- asyncio.run(server.main(computer_use))
12
+ asyncio.run(server.main())
13
13
 
14
14
 
15
15
  # Optionally expose other important items at package level
@@ -1,5 +1,4 @@
1
1
  import importlib
2
- import json
3
2
  import logging
4
3
  import os
5
4
  from typing import Any
@@ -9,29 +8,22 @@ import mcp_wcgw.types as types
9
8
  from mcp_wcgw.server import NotificationOptions, Server
10
9
  from mcp_wcgw.server.models import InitializationOptions
11
10
  from mcp_wcgw.types import Tool as ToolParam
12
- from pydantic import AnyUrl, ValidationError
11
+ from pydantic import AnyUrl
12
+
13
+ from wcgw.client.modes import KTS
14
+ from wcgw.client.tool_prompts import TOOL_PROMPTS
13
15
 
14
16
  from ...types_ import (
15
- BashCommand,
16
- BashInteraction,
17
- ContextSave,
18
- FileEdit,
19
- GetScreenInfo,
20
17
  Initialize,
21
- Keyboard,
22
- Mouse,
23
- ReadFiles,
24
- ReadImage,
25
- ResetShell,
26
- ScreenShot,
27
- WriteIfEmpty,
28
18
  )
29
- from .. import tools
30
- from ..computer_use import SLEEP_TIME_MAX_S
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
19
+ from ..bash_state.bash_state import CONFIG, BashState
20
+ from ..tools import (
21
+ Context,
22
+ default_enc,
23
+ get_tool_output,
24
+ parse_tool_by_name,
25
+ which_tool_name,
26
+ )
35
27
 
36
28
  server = Server("wcgw")
37
29
 
@@ -64,7 +56,7 @@ PROMPTS = {
64
56
  name="KnowledgeTransfer",
65
57
  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
58
  ),
67
- get_kt_prompt,
59
+ KTS,
68
60
  )
69
61
  }
70
62
 
@@ -78,12 +70,12 @@ async def handle_list_prompts() -> list[types.Prompt]:
78
70
  async def handle_get_prompt(
79
71
  name: str, arguments: dict[str, str] | None
80
72
  ) -> types.GetPromptResult:
73
+ assert BASH_STATE
81
74
  messages = [
82
75
  types.PromptMessage(
83
76
  role="user",
84
77
  content=types.TextContent(
85
- type="text",
86
- text=PROMPTS[name][1](),
78
+ type="text", text=PROMPTS[name][1][BASH_STATE.mode]
87
79
  ),
88
80
  )
89
81
  ]
@@ -97,187 +89,37 @@ async def handle_list_tools() -> list[types.Tool]:
97
89
  Each tool specifies its arguments using JSON Schema validation.
98
90
  """
99
91
 
100
- with open(
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
- ),
178
- ToolParam(
179
- inputSchema=ResetShell.model_json_schema(),
180
- name="ResetShell",
181
- description="Resets the shell. Use only if all interrupts and prompt reset attempts have failed repeatedly.\nAlso exits the docker environment.\nYou need to call GetScreenInfo again.",
182
- ),
183
- ToolParam(
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
- ),
92
+ tools_ = [
193
93
  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
- ),
94
+ inputSchema=tool.inputSchema,
95
+ name=tool.name,
96
+ description=tool.description,
97
+ )
98
+ for tool in TOOL_PROMPTS
201
99
  ]
202
- if COMPUTER_USE_ON_DOCKER_ENABLED:
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
100
+ return tools_
253
101
 
254
102
 
255
103
  @server.call_tool() # type: ignore
256
104
  async def handle_call_tool(
257
105
  name: str, arguments: dict[str, Any] | None
258
106
  ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
107
+ global BASH_STATE
259
108
  if not arguments:
260
109
  raise ValueError("Missing arguments")
261
110
 
262
111
  tool_type = which_tool_name(name)
112
+ tool_call = parse_tool_by_name(name, arguments)
263
113
 
264
114
  try:
265
- tool_call = tool_type(**arguments)
266
- except ValidationError:
267
-
268
- def try_json(x: str) -> Any:
269
- if not isinstance(x, str):
270
- return x
271
- try:
272
- return json.loads(x)
273
- except json.JSONDecodeError:
274
- return x
275
-
276
- tool_call = tool_type(**{k: try_json(v) for k, v in arguments.items()})
277
-
278
- try:
115
+ assert BASH_STATE
279
116
  output_or_dones, _ = get_tool_output(
280
- tool_call, default_enc, 0.0, lambda x, y: ("", 0), 8000
117
+ Context(BASH_STATE, BASH_STATE.console),
118
+ tool_call,
119
+ default_enc,
120
+ 0.0,
121
+ lambda x, y: ("", 0),
122
+ 8000,
281
123
  )
282
124
 
283
125
  except Exception as e:
@@ -285,7 +127,6 @@ async def handle_call_tool(
285
127
 
286
128
  content: list[types.TextContent | types.ImageContent | types.EmbeddedResource] = []
287
129
  for output_or_done in output_or_dones:
288
- assert not isinstance(output_or_done, DoneFlag)
289
130
  if isinstance(output_or_done, str):
290
131
  if issubclass(tool_type, Initialize):
291
132
  output_or_done += """
@@ -307,31 +148,30 @@ Initialize call done.
307
148
  return content
308
149
 
309
150
 
310
- async def main(computer_use: bool) -> None:
311
- global COMPUTER_USE_ON_DOCKER_ENABLED
151
+ BASH_STATE = None
312
152
 
313
- tools.TIMEOUT = SLEEP_TIME_MAX_S
314
- tools.TIMEOUT_WHILE_OUTPUT = 55
315
- tools.OUTPUT_WAIT_PATIENCE = 5
316
- tools.console = Console()
317
-
318
- if computer_use:
319
- COMPUTER_USE_ON_DOCKER_ENABLED = True
320
153
 
154
+ async def main() -> None:
155
+ global BASH_STATE
156
+ CONFIG.update(3, 55, 5)
321
157
  version = str(importlib.metadata.version("wcgw"))
322
- tools.console.log("wcgw version: " + version)
323
- # Run the server using stdin/stdout streams
324
- async with mcp_wcgw.server.stdio.stdio_server() as (read_stream, write_stream):
325
- await server.run(
326
- read_stream,
327
- write_stream,
328
- InitializationOptions(
329
- server_name="wcgw",
330
- server_version=version,
331
- capabilities=server.get_capabilities(
332
- notification_options=NotificationOptions(),
333
- experimental_capabilities={},
158
+ home_dir = os.path.expanduser("~")
159
+ with BashState(
160
+ Console(), home_dir, None, None, None, None, True, None
161
+ ) as BASH_STATE:
162
+ BASH_STATE.console.log("wcgw version: " + version)
163
+ # Run the server using stdin/stdout streams
164
+ async with mcp_wcgw.server.stdio.stdio_server() as (read_stream, write_stream):
165
+ await server.run(
166
+ read_stream,
167
+ write_stream,
168
+ InitializationOptions(
169
+ server_name="wcgw",
170
+ server_version=version,
171
+ capabilities=server.get_capabilities(
172
+ notification_options=NotificationOptions(),
173
+ experimental_capabilities={},
174
+ ),
334
175
  ),
335
- ),
336
- raise_exceptions=False,
337
- )
176
+ raise_exceptions=False,
177
+ )
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: {', '.join(allowed_file_edit_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: {', '.join(allowed_file_edit_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 = """
@@ -85,7 +85,7 @@ You are now running in "code_writer" mode.
85
85
  base += path_prompt
86
86
 
87
87
  run_command_common = """
88
- - Do not use Ctrl-c or Ctrl-z or interrupt commands without asking the user, because often the programs don't show any update but they still are running.
88
+ - Do not use Ctrl-c interrupt commands without asking the user, because often the programs don't show any update but they still are running.
89
89
  - Do not use echo to write multi-line files, always use FileEdit tool to update a code.
90
90
  - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
91
91
  - You should use the provided bash execution, reading and writing file tools to complete objective.
@@ -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: {', '.join(allowed_commands)}
104
+ - You are only allowed to run the following commands: {", ".join(allowed_commands)}
105
105
  {run_command_common}
106
106
  """
107
107
  else:
@@ -124,7 +124,7 @@ Instructions:
124
124
  - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
125
125
  - Do not install new tools/packages before ensuring no such tools/package or an alternative already exists.
126
126
  - Do not use artifacts if you have access to the repository and not asked by the user to provide artifacts/snippets. Directly create/update using wcgw tools
127
- - Do not use Ctrl-c or Ctrl-z or interrupt commands without asking the user, because often the programs don't show any update but they still are running.
127
+ - Do not use Ctrl-c or interrupt commands without asking the user, because often the programs don't show any update but they still are running.
128
128
  - Do not use echo to write multi-line files, always use FileEdit tool to update a code.
129
129
 
130
130
  Additional instructions:
@@ -138,10 +138,11 @@ ARCHITECT_PROMPT = """You are now running in "architect" mode. This means
138
138
  - You are not allowed to edit or update any file. You are not allowed to create any file.
139
139
  - You are not allowed to run any commands that may change disk, system configuration, packages or environment. Only read-only commands are allowed.
140
140
  - Only run commands that allows you to explore the repository, understand the system or read anything of relevance.
141
- - Do not use Ctrl-c or Ctrl-z or interrupt commands without asking the user, because often the programs don't show any update but they still are running.
141
+ - Do not use Ctrl-c or interrupt commands without asking the user, because often the programs don't show any update but they still are running.
142
142
  - You are not allowed to change directory (bash will run in -r mode)
143
+ - Share only snippets when any implementation is requested.
143
144
 
144
- Your response should be in self-critique and brainstorm style.
145
+ Respond only after doing the following:
145
146
  - Read as many relevant files as possible.
146
147
  - Be comprehensive in your understanding and search of relevant files.
147
148
  - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
@@ -149,17 +150,17 @@ Your response should be in self-critique and brainstorm style.
149
150
 
150
151
 
151
152
  DEFAULT_MODES: dict[Modes, ModeImpl] = {
152
- Modes.wcgw: ModeImpl(
153
+ "wcgw": ModeImpl(
153
154
  bash_command_mode=BashCommandMode("normal_mode", "all"),
154
155
  write_if_empty_mode=WriteIfEmptyMode("all"),
155
156
  file_edit_mode=FileEditMode("all"),
156
157
  ),
157
- Modes.architect: ModeImpl(
158
+ "architect": ModeImpl(
158
159
  bash_command_mode=BashCommandMode("restricted_mode", "all"),
159
160
  write_if_empty_mode=WriteIfEmptyMode([]),
160
161
  file_edit_mode=FileEditMode([]),
161
162
  ),
162
- Modes.code_writer: ModeImpl(
163
+ "code_writer": ModeImpl(
163
164
  bash_command_mode=BashCommandMode("normal_mode", "all"),
164
165
  write_if_empty_mode=WriteIfEmptyMode("all"),
165
166
  file_edit_mode=FileEditMode("all"),
@@ -172,11 +173,11 @@ def modes_to_state(
172
173
  ) -> tuple[BashCommandMode, FileEditMode, WriteIfEmptyMode, Modes]:
173
174
  # First get default mode config
174
175
  if isinstance(mode, str):
175
- mode_impl = DEFAULT_MODES[Modes[mode]] # converts str to Modes enum
176
- mode_name = Modes[mode]
176
+ mode_impl = DEFAULT_MODES[mode] # converts str to Modes enum
177
+ mode_name: Modes = mode
177
178
  else:
178
179
  # For CodeWriterMode, use code_writer as base and override
179
- mode_impl = DEFAULT_MODES[Modes.code_writer]
180
+ mode_impl = DEFAULT_MODES["code_writer"]
180
181
  # Override with custom settings from CodeWriterMode
181
182
  mode_impl = ModeImpl(
182
183
  bash_command_mode=BashCommandMode(
@@ -186,7 +187,7 @@ def modes_to_state(
186
187
  file_edit_mode=FileEditMode(mode.allowed_globs),
187
188
  write_if_empty_mode=WriteIfEmptyMode(mode.allowed_globs),
188
189
  )
189
- mode_name = Modes.code_writer
190
+ mode_name = "code_writer"
190
191
  return (
191
192
  mode_impl.bash_command_mode,
192
193
  mode_impl.file_edit_mode,
@@ -233,10 +234,4 @@ Provide all relevant file paths in order to understand and solve the the task. E
233
234
  (Note to self: this conversation can then be resumed later asking "Resume wcgw task `<generated id>`" which should call Initialize tool)
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]
237
+ KTS = {"wcgw": WCGW_KT, "architect": ARCHITECT_KT, "code_writer": WCGW_KT}