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

@@ -130,7 +130,7 @@ def loop(
130
130
  memory = None
131
131
  if resume:
132
132
  try:
133
- _, memory = load_memory(
133
+ _, memory, _ = load_memory(
134
134
  resume,
135
135
  8000,
136
136
  lambda x: default_enc.encode(x).ids,
@@ -166,6 +166,7 @@ def loop(
166
166
  - The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
167
167
  - Run long running commands in background using screen instead of "&".
168
168
  - Use longer wait_for_seconds if the command is expected to run for a long time.
169
+ - Do not use 'cat' to read files, use ReadFiles tool instead.
169
170
  """,
170
171
  ),
171
172
  ToolParam(
@@ -281,22 +282,13 @@ Saves provided description and file contents of all the relevant file paths or g
281
282
  ),
282
283
  ]
283
284
 
284
- initial_info = initialize(
285
- os.getcwd(), [], resume if (memory and resume) else "", 8000
285
+ system = initialize(
286
+ os.getcwd(),
287
+ [],
288
+ resume if (memory and resume) else "",
289
+ max_tokens=8000,
290
+ mode="wcgw",
286
291
  )
287
- system = f"""
288
- You're an expert software engineer with shell and code knowledge.
289
-
290
- Instructions:
291
-
292
- - You should use the provided bash execution, reading and writing file tools to complete objective.
293
- - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
294
- - Always read relevant files before editing.
295
- - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
296
- - Do not install new tools/packages before ensuring no such tools/package or an alternative already exists.
297
-
298
- {initial_info}
299
- """
300
292
 
301
293
  with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f:
302
294
  system += f.read()
@@ -28,6 +28,7 @@ from ...types_ import (
28
28
  )
29
29
  from .. import tools
30
30
  from ..computer_use import SLEEP_TIME_MAX_S
31
+ from ..modes import get_kt_prompt
31
32
  from ..tools import DoneFlag, default_enc, get_tool_output, which_tool_name
32
33
 
33
34
  COMPUTER_USE_ON_DOCKER_ENABLED = False
@@ -45,48 +46,35 @@ async def handle_read_resource(uri: AnyUrl) -> str:
45
46
  raise ValueError("No resources available")
46
47
 
47
48
 
48
- @server.list_prompts() # type: ignore
49
- async def handle_list_prompts() -> list[types.Prompt]:
50
- return [
49
+ PROMPTS = {
50
+ "KnowledgeTransfer": (
51
51
  types.Prompt(
52
52
  name="KnowledgeTransfer",
53
53
  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.",
54
- )
55
- ]
54
+ ),
55
+ get_kt_prompt,
56
+ )
57
+ }
58
+
59
+
60
+ @server.list_prompts() # type: ignore
61
+ async def handle_list_prompts() -> list[types.Prompt]:
62
+ return [x[0] for x in PROMPTS.values()]
56
63
 
57
64
 
58
65
  @server.get_prompt() # type: ignore
59
66
  async def handle_get_prompt(
60
67
  name: str, arguments: dict[str, str] | None
61
68
  ) -> types.GetPromptResult:
62
- messages = []
63
- if name == "KnowledgeTransfer":
64
- messages = [
65
- types.PromptMessage(
66
- role="user",
67
- content=types.TextContent(
68
- type="text",
69
- text="""Use `ContextSave` tool to do a knowledge transfer of the task in hand.
70
- Write detailed description in order to do a KT.
71
- Save all information necessary for a person to understand the task and the problems.
72
-
73
- Format the `description` field using Markdown with the following sections.
74
- - "# Objective" section containing project and task objective.
75
- - "# All user instructions" section should be provided containing all instructions user shared in the conversation.
76
- - "# Current status of the task" should be provided containing only what is already achieved, not what's remaining.
77
- - "# All issues with snippets" section containing snippets of error, traceback, file snippets, commands, etc. But no comments or solutions.
78
- - Be very verbose in the all issues with snippets section providing as much error context as possible.
79
- - "# Build and development instructions" section containing instructions to build or run project or run tests, or envrionment related information. Only include what's known. Leave empty if unknown.
80
- - After the tool completes succesfully, tell me the task id and the file path the tool generated (important!)
81
- - This tool marks end of your conversation, do not run any further tools after calling this.
82
-
83
- Provide all relevant file paths in order to understand and solve the the task. Err towards providing more file paths than fewer.
84
-
85
- (Note to self: this conversation can then be resumed later asking "Resume `<generated id>`" which should call Initialize tool)
86
- """,
87
- ),
88
- )
89
- ]
69
+ messages = [
70
+ types.PromptMessage(
71
+ role="user",
72
+ content=types.TextContent(
73
+ type="text",
74
+ text=PROMPTS[name][1](),
75
+ ),
76
+ )
77
+ ]
90
78
  return types.GetPromptResult(messages=messages)
91
79
 
92
80
 
@@ -117,6 +105,9 @@ async def handle_list_tools() -> list[types.Tool]:
117
105
  - If user has mentioned any files use `initial_files_to_read` to read, use absolute paths only.
118
106
  - If `any_workspace_path` is provided, a tree structure of the workspace will be shown.
119
107
  - Leave `any_workspace_path` as empty if no file or folder is mentioned.
108
+ - By default use mode "wcgw"
109
+ - In "code-writer" mode, set the commands and globs which user asked to set, otherwise use 'all'.
110
+ - 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.
120
111
  """,
121
112
  ),
122
113
  ToolParam(
@@ -263,6 +254,8 @@ async def handle_call_tool(
263
254
  except ValidationError:
264
255
 
265
256
  def try_json(x: str) -> Any:
257
+ if not isinstance(x, str):
258
+ return x
266
259
  try:
267
260
  return json.loads(x)
268
261
  except json.JSONDecodeError:
@@ -284,24 +277,9 @@ async def handle_call_tool(
284
277
  if isinstance(output_or_done, str):
285
278
  if issubclass(tool_type, Initialize):
286
279
  output_or_done += """
287
- ---
288
- You're an expert software engineer with shell and code knowledge.
289
-
290
- Instructions:
291
-
292
- - You should use the provided bash execution, reading and writing file tools to complete objective.
293
- - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
294
- - Always read relevant files before editing.
295
- - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
296
- - Do not install new tools/packages before ensuring no such tools/package or an alternative already exists.
297
- - 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 shell tools.
298
- - Do not use Ctrl-c or Ctrl-z or interrupt commands without asking the user, because often the program don't show any update but they still are running.
299
- - Do not use echo to write multi-line files, always use FileEdit tool to update a code.
300
-
301
- Additional instructions:
302
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost, or to get absolute cwd.
303
-
304
- Always write production ready, syntactically correct code.
280
+ - Additional important note: as soon as you encounter "The user has chosen to disallow the tool call.", immediately stop doing everything and ask user for the reason.
281
+
282
+ Initialize call done.
305
283
  """
306
284
 
307
285
  content.append(types.TextContent(type="text", text=output_or_done))
wcgw/client/memory.py CHANGED
@@ -1,7 +1,8 @@
1
+ import json
1
2
  import os
2
3
  import re
3
4
  import shlex
4
- from typing import Callable, Optional
5
+ from typing import Any, Callable, Optional
5
6
 
6
7
  from ..types_ import ContextSave
7
8
 
@@ -30,7 +31,11 @@ def format_memory(task_memory: ContextSave, relevant_files: str) -> str:
30
31
  return memory_data
31
32
 
32
33
 
33
- def save_memory(task_memory: ContextSave, relevant_files: str) -> str:
34
+ def save_memory(
35
+ task_memory: ContextSave,
36
+ relevant_files: str,
37
+ bash_state_dict: Optional[dict[str, Any]] = None,
38
+ ) -> str:
34
39
  app_dir = get_app_dir_xdg()
35
40
  memory_dir = os.path.join(app_dir, "memory")
36
41
  os.makedirs(memory_dir, exist_ok=True)
@@ -45,6 +50,12 @@ def save_memory(task_memory: ContextSave, relevant_files: str) -> str:
45
50
  with open(memory_file_full, "w") as f:
46
51
  f.write(memory_data)
47
52
 
53
+ # Save bash state if provided
54
+ if bash_state_dict is not None:
55
+ state_file = os.path.join(memory_dir, f"{task_id}_bash_state.json")
56
+ with open(state_file, "w") as f:
57
+ json.dump(bash_state_dict, f, indent=2)
58
+
48
59
  return memory_file_full
49
60
 
50
61
 
@@ -53,7 +64,7 @@ def load_memory[T](
53
64
  max_tokens: Optional[int],
54
65
  encoder: Callable[[str], list[T]],
55
66
  decoder: Callable[[list[T]], str],
56
- ) -> tuple[str, str]:
67
+ ) -> tuple[str, str, Optional[dict[str, Any]]]:
57
68
  app_dir = get_app_dir_xdg()
58
69
  memory_dir = os.path.join(app_dir, "memory")
59
70
  memory_file = os.path.join(memory_dir, f"{task_id}.txt")
@@ -75,4 +86,12 @@ def load_memory[T](
75
86
  parsed_ = shlex.split(matched_path)
76
87
  if parsed_ and len(parsed_) == 1:
77
88
  project_root_path = parsed_[0]
78
- return project_root_path, data
89
+
90
+ # Try to load bash state if exists
91
+ state_file = os.path.join(memory_dir, f"{task_id}_bash_state.json")
92
+ bash_state: Optional[dict[str, Any]] = None
93
+ if os.path.exists(state_file):
94
+ with open(state_file) as f:
95
+ bash_state = json.load(f)
96
+
97
+ return project_root_path, data, bash_state
wcgw/client/modes.py ADDED
@@ -0,0 +1,242 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Literal, NamedTuple
3
+
4
+ from ..types_ import Modes, ModesConfig
5
+
6
+
7
+ class BashCommandMode(NamedTuple):
8
+ bash_mode: Literal["normal_mode", "restricted_mode"]
9
+ allowed_commands: Literal["all", "none"]
10
+
11
+ def serialize(self) -> dict[str, Any]:
12
+ return {"bash_mode": self.bash_mode, "allowed_commands": self.allowed_commands}
13
+
14
+ @classmethod
15
+ def deserialize(cls, data: dict[str, Any]) -> "BashCommandMode":
16
+ return cls(data["bash_mode"], data["allowed_commands"])
17
+
18
+
19
+ class FileEditMode(NamedTuple):
20
+ allowed_globs: Literal["all"] | list[str]
21
+
22
+ def serialize(self) -> dict[str, Any]:
23
+ return {"allowed_globs": self.allowed_globs}
24
+
25
+ @classmethod
26
+ def deserialize(cls, data: dict[str, Any]) -> "FileEditMode":
27
+ return cls(data["allowed_globs"])
28
+
29
+
30
+ class WriteIfEmptyMode(NamedTuple):
31
+ allowed_globs: Literal["all"] | list[str]
32
+
33
+ def serialize(self) -> dict[str, Any]:
34
+ return {"allowed_globs": self.allowed_globs}
35
+
36
+ @classmethod
37
+ def deserialize(cls, data: dict[str, Any]) -> "WriteIfEmptyMode":
38
+ return cls(data["allowed_globs"])
39
+
40
+
41
+ @dataclass
42
+ class ModeImpl:
43
+ bash_command_mode: BashCommandMode
44
+ file_edit_mode: FileEditMode
45
+ write_if_empty_mode: WriteIfEmptyMode
46
+
47
+
48
+ def code_writer_prompt(
49
+ allowed_file_edit_globs: Literal["all"] | list[str],
50
+ all_write_new_globs: Literal["all"] | list[str],
51
+ allowed_commands: Literal["all"] | list[str],
52
+ ) -> str:
53
+ base = """
54
+ You have to run in "code_writer" mode.
55
+ """
56
+
57
+ path_prompt = """
58
+ - You are allowed to run FileEdit in the provided repository only.
59
+ """
60
+
61
+ if allowed_file_edit_globs != "all":
62
+ if allowed_file_edit_globs:
63
+ path_prompt = f"""
64
+ - You are allowed to run FileEdit for files matching only the following globs: {', '.join(allowed_file_edit_globs)}
65
+ """
66
+ else:
67
+ path_prompt = """
68
+ - You are not allowed to run FileEdit.
69
+ """
70
+ base += path_prompt
71
+
72
+ path_prompt = """
73
+ - You are allowed to run WriteIfEmpty in the provided repository only.
74
+ """
75
+
76
+ if all_write_new_globs != "all":
77
+ if all_write_new_globs:
78
+ path_prompt = f"""
79
+ - You are allowed to run WriteIfEmpty files matching only the following globs: {', '.join(allowed_file_edit_globs)}
80
+ """
81
+ else:
82
+ path_prompt = """
83
+ - You are not allowed to run WriteIfEmpty.
84
+ """
85
+ base += path_prompt
86
+
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.
89
+ - Do not use echo to write multi-line files, always use FileEdit tool to update a code.
90
+ - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
91
+ - You should use the provided bash execution, reading and writing file tools to complete objective.
92
+ - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
93
+ - 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.
94
+ """
95
+
96
+ command_prompt = f"""
97
+ - You are only allowed to run commands for project setup, code writing, editing, updating, testing, running and debugging related to the project.
98
+ - Do not run anything that adds or removes packages, changes system configuration or environment.
99
+ {run_command_common}
100
+ """
101
+ if allowed_commands != "all":
102
+ if allowed_commands:
103
+ command_prompt = f"""
104
+ - You are only allowed to run the following commands: {', '.join(allowed_commands)}
105
+ {run_command_common}
106
+ """
107
+ else:
108
+ command_prompt = """
109
+ - You are not allowed to run any commands.
110
+ """
111
+
112
+ base += command_prompt
113
+ return base
114
+
115
+
116
+ WCGW_PROMPT = """
117
+ ---
118
+ You're an expert software engineer with shell and code knowledge.
119
+
120
+ Instructions:
121
+
122
+ - You should use the provided bash execution, reading and writing file tools to complete objective.
123
+ - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
124
+ - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
125
+ - Do not install new tools/packages before ensuring no such tools/package or an alternative already exists.
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.
128
+ - Do not use echo to write multi-line files, always use FileEdit tool to update a code.
129
+
130
+ Additional instructions:
131
+ Always run `pwd` if you get any file or directory not found error to make sure you're not lost, or to get absolute cwd.
132
+
133
+ Always write production ready, syntactically correct code.
134
+
135
+
136
+ """
137
+ ARCHITECT_PROMPT = """You have to run in "architect" mode. This means
138
+ - You are not allowed to edit or update any file. You are not allowed to create any file.
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
+ - 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.
142
+ - You are not allowed to change directory (bash will run in -r mode)
143
+
144
+ Your response should be in self-critique and brainstorm style.
145
+ - Read as many relevant files as possible.
146
+ - Be comprehensive in your understanding and search of relevant files.
147
+ - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
148
+ """
149
+
150
+
151
+ DEFAULT_MODES: dict[Modes, ModeImpl] = {
152
+ Modes.wcgw: ModeImpl(
153
+ bash_command_mode=BashCommandMode("normal_mode", "all"),
154
+ write_if_empty_mode=WriteIfEmptyMode("all"),
155
+ file_edit_mode=FileEditMode("all"),
156
+ ),
157
+ Modes.architect: ModeImpl(
158
+ bash_command_mode=BashCommandMode("restricted_mode", "all"),
159
+ write_if_empty_mode=WriteIfEmptyMode([]),
160
+ file_edit_mode=FileEditMode([]),
161
+ ),
162
+ Modes.code_writer: ModeImpl(
163
+ bash_command_mode=BashCommandMode("normal_mode", "all"),
164
+ write_if_empty_mode=WriteIfEmptyMode("all"),
165
+ file_edit_mode=FileEditMode("all"),
166
+ ),
167
+ }
168
+
169
+
170
+ def modes_to_state(
171
+ mode: ModesConfig,
172
+ ) -> tuple[BashCommandMode, FileEditMode, WriteIfEmptyMode, Modes]:
173
+ # First get default mode config
174
+ if isinstance(mode, str):
175
+ mode_impl = DEFAULT_MODES[Modes[mode]] # converts str to Modes enum
176
+ mode_name = Modes[mode]
177
+ else:
178
+ # For CodeWriterMode, use code_writer as base and override
179
+ mode_impl = DEFAULT_MODES[Modes.code_writer]
180
+ # Override with custom settings from CodeWriterMode
181
+ mode_impl = ModeImpl(
182
+ bash_command_mode=BashCommandMode(
183
+ mode_impl.bash_command_mode.bash_mode,
184
+ "all" if mode.allowed_commands else "none",
185
+ ),
186
+ file_edit_mode=FileEditMode(mode.allowed_globs),
187
+ write_if_empty_mode=WriteIfEmptyMode(mode.allowed_globs),
188
+ )
189
+ mode_name = Modes.code_writer
190
+ return (
191
+ mode_impl.bash_command_mode,
192
+ mode_impl.file_edit_mode,
193
+ mode_impl.write_if_empty_mode,
194
+ mode_name,
195
+ )
196
+
197
+
198
+ WCGW_KT = """Use `ContextSave` tool to do a knowledge transfer of the task in hand.
199
+ Write detailed description in order to do a KT.
200
+ Save all information necessary for a person to understand the task and the problems.
201
+
202
+ Format the `description` field using Markdown with the following sections.
203
+ - "# Objective" section containing project and task objective.
204
+ - "# All user instructions" section should be provided containing all instructions user shared in the conversation.
205
+ - "# Current status of the task" should be provided containing only what is already achieved, not what's remaining.
206
+ - "# All issues with snippets" section containing snippets of error, traceback, file snippets, commands, etc. But no comments or solutions.
207
+ - Be very verbose in the all issues with snippets section providing as much error context as possible.
208
+ - "# Build and development instructions" section containing instructions to build or run project or run tests, or envrionment related information. Only include what's known. Leave empty if unknown.
209
+ - Any other relevant sections following the above.
210
+ - After the tool completes succesfully, tell me the task id and the file path the tool generated (important!)
211
+ - This tool marks end of your conversation, do not run any further tools after calling this.
212
+
213
+ Provide all relevant file paths in order to understand and solve the the task. Err towards providing more file paths than fewer.
214
+
215
+ (Note to self: this conversation can then be resumed later asking "Resume wcgw task `<generated id>`" which should call Initialize tool)
216
+ """
217
+
218
+
219
+ ARCHITECT_KT = """Use `ContextSave` tool to do a knowledge transfer of the task in hand.
220
+ Write detailed description in order to do a KT.
221
+ Save all information necessary for a person to understand the task and the problems.
222
+
223
+ Format the `description` field using Markdown with the following sections.
224
+ - "# Objective" section containing project and task objective.
225
+ - "# All user instructions" section should be provided containing all instructions user shared in the conversation.
226
+ - "# Designed plan" should be provided containing the designed plan as discussed.
227
+ - Any other relevant sections following the above.
228
+ - After the tool completes succesfully, tell me the task id and the file path the tool generated (important!)
229
+ - This tool marks end of your conversation, do not run any further tools after calling this.
230
+
231
+ Provide all relevant file paths in order to understand and solve the the task. Err towards providing more file paths than fewer.
232
+
233
+ (Note to self: this conversation can then be resumed later asking "Resume wcgw task `<generated id>`" which should call Initialize tool)
234
+ """
235
+
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]
@@ -125,7 +125,7 @@ def loop(
125
125
  memory = None
126
126
  if resume:
127
127
  try:
128
- _, memory = load_memory(
128
+ _, memory, _ = load_memory(
129
129
  resume,
130
130
  8000,
131
131
  lambda x: default_enc.encode(x).ids,
@@ -176,6 +176,7 @@ def loop(
176
176
  - Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
177
177
  - The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
178
178
  - Run long running commands in background using screen instead of "&".
179
+ - Do not use 'cat' to read files, use ReadFiles tool instead.
179
180
  """,
180
181
  ),
181
182
  openai.pydantic_function_tool(
@@ -226,23 +227,13 @@ Saves provided description and file contents of all the relevant file paths or g
226
227
  ),
227
228
  ]
228
229
 
229
- initial_info = initialize(
230
- os.getcwd(), [], resume if (memory and resume) else "", 8000
230
+ system = initialize(
231
+ os.getcwd(),
232
+ [],
233
+ resume if (memory and resume) else "",
234
+ max_tokens=8000,
235
+ mode="wcgw",
231
236
  )
232
- system = f"""
233
- You're an expert software engineer with shell and code knowledge.
234
-
235
- Instructions:
236
-
237
- - You should use the provided bash execution, reading and writing file tools to complete objective.
238
- - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
239
- - Always read relevant files before editing.
240
- - Do not provide code snippets unless asked by the user, instead directly add/edit the code.
241
- - Do not install new tools/packages before ensuring no such tools/package or an alternative already exists.
242
-
243
- {initial_info}
244
-
245
- """
246
237
 
247
238
  with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f:
248
239
  system += f.read()
wcgw/client/tools.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import base64
2
2
  import datetime
3
+ import fnmatch
3
4
  import glob
4
5
  import importlib.metadata
5
6
  import json
@@ -14,6 +15,7 @@ from os.path import expanduser
14
15
  from pathlib import Path
15
16
  from tempfile import NamedTemporaryFile, TemporaryDirectory
16
17
  from typing import (
18
+ Any,
17
19
  Callable,
18
20
  Literal,
19
21
  Optional,
@@ -39,12 +41,15 @@ from websockets.sync.client import connect as syncconnect
39
41
  from ..types_ import (
40
42
  BashCommand,
41
43
  BashInteraction,
44
+ CodeWriterMode,
42
45
  ContextSave,
43
46
  FileEdit,
44
47
  FileEditFindReplace,
45
48
  GetScreenInfo,
46
49
  Initialize,
47
50
  Keyboard,
51
+ Modes,
52
+ ModesConfig,
48
53
  Mouse,
49
54
  ReadFiles,
50
55
  ReadImage,
@@ -55,6 +60,15 @@ from ..types_ import (
55
60
  from .computer_use import run_computer_tool
56
61
  from .file_ops.search_replace import search_replace_edit
57
62
  from .memory import load_memory, save_memory
63
+ from .modes import (
64
+ ARCHITECT_PROMPT,
65
+ WCGW_PROMPT,
66
+ BashCommandMode,
67
+ FileEditMode,
68
+ WriteIfEmptyMode,
69
+ code_writer_prompt,
70
+ modes_to_state,
71
+ )
58
72
  from .repo_ops.repo_context import get_repo_context
59
73
  from .sys_utils import command_run
60
74
 
@@ -123,14 +137,19 @@ PROMPT_CONST = "#@wcgw@#"
123
137
  PROMPT = PROMPT_CONST
124
138
 
125
139
 
126
- def start_shell() -> pexpect.spawn: # type: ignore
140
+ def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: # type: ignore
127
141
  try:
142
+ cmd = "/bin/bash"
143
+ if is_restricted_mode:
144
+ cmd += " -r"
145
+
128
146
  shell = pexpect.spawn(
129
- "/bin/bash",
147
+ cmd,
130
148
  env={**os.environ, **{"PS1": PROMPT}}, # type: ignore[arg-type]
131
149
  echo=False,
132
150
  encoding="utf-8",
133
151
  timeout=TIMEOUT,
152
+ cwd=initial_dir,
134
153
  )
135
154
  shell.sendline(f"export PS1={PROMPT}")
136
155
  except Exception as e:
@@ -203,15 +222,54 @@ BASH_CLF_OUTPUT = Literal["repl", "pending"]
203
222
 
204
223
 
205
224
  class BashState:
206
- def __init__(self) -> None:
207
- self._init()
225
+ def __init__(
226
+ self,
227
+ working_dir: str,
228
+ bash_command_mode: Optional[BashCommandMode],
229
+ file_edit_mode: Optional[FileEditMode],
230
+ write_if_empty_mode: Optional[WriteIfEmptyMode],
231
+ mode: Optional[Modes],
232
+ whitelist_for_overwrite: Optional[set[str]] = None,
233
+ ) -> None:
234
+ self._cwd = working_dir or os.getcwd()
235
+ self._bash_command_mode: BashCommandMode = bash_command_mode or BashCommandMode(
236
+ "normal_mode", "all"
237
+ )
238
+ self._file_edit_mode: FileEditMode = file_edit_mode or FileEditMode("all")
239
+ self._write_if_empty_mode: WriteIfEmptyMode = (
240
+ write_if_empty_mode or WriteIfEmptyMode("all")
241
+ )
242
+ self._mode = mode or Modes.wcgw
243
+ self._whitelist_for_overwrite: set[str] = whitelist_for_overwrite or set()
244
+
245
+ self._init_shell()
246
+
247
+ @property
248
+ def mode(self) -> Modes:
249
+ return self._mode
208
250
 
209
- def _init(self) -> None:
251
+ @property
252
+ def bash_command_mode(self) -> BashCommandMode:
253
+ return self._bash_command_mode
254
+
255
+ @property
256
+ def file_edit_mode(self) -> FileEditMode:
257
+ return self._file_edit_mode
258
+
259
+ @property
260
+ def write_if_empty_mode(self) -> WriteIfEmptyMode:
261
+ return self._write_if_empty_mode
262
+
263
+ def _init_shell(self) -> None:
210
264
  self._state: Literal["repl"] | datetime.datetime = "repl"
211
265
  self._is_in_docker: Optional[str] = ""
212
- self._cwd: str = os.getcwd()
213
- self._shell = start_shell()
214
- self._whitelist_for_overwrite: set[str] = set()
266
+ # Ensure self._cwd exists
267
+ os.makedirs(self._cwd, exist_ok=True)
268
+ self._shell = start_shell(
269
+ self._bash_command_mode.bash_mode == "restricted_mode",
270
+ self._cwd,
271
+ )
272
+
215
273
  self._pending_output = ""
216
274
 
217
275
  # Get exit info to ensure shell is ready
@@ -258,9 +316,49 @@ class BashState:
258
316
  self._cwd = current_dir
259
317
  return current_dir
260
318
 
261
- def reset(self) -> None:
319
+ def reset_shell(self) -> None:
262
320
  self.shell.close(True)
263
- self._init()
321
+ self._init_shell()
322
+
323
+ def serialize(self) -> dict[str, Any]:
324
+ """Serialize BashState to a dictionary for saving"""
325
+ return {
326
+ "bash_command_mode": self._bash_command_mode.serialize(),
327
+ "file_edit_mode": self._file_edit_mode.serialize(),
328
+ "write_if_empty_mode": self._write_if_empty_mode.serialize(),
329
+ "whitelist_for_overwrite": list(self._whitelist_for_overwrite),
330
+ "mode": self._mode,
331
+ }
332
+
333
+ @staticmethod
334
+ def parse_state(
335
+ state: dict[str, Any],
336
+ ) -> tuple[BashCommandMode, FileEditMode, WriteIfEmptyMode, Modes, list[str]]:
337
+ return (
338
+ BashCommandMode.deserialize(state["bash_command_mode"]),
339
+ FileEditMode.deserialize(state["file_edit_mode"]),
340
+ WriteIfEmptyMode.deserialize(state["write_if_empty_mode"]),
341
+ Modes[str(state["mode"])],
342
+ state["whitelist_for_overwrite"],
343
+ )
344
+
345
+ def load_state(
346
+ self,
347
+ bash_command_mode: BashCommandMode,
348
+ file_edit_mode: FileEditMode,
349
+ write_if_empty_mode: WriteIfEmptyMode,
350
+ mode: Modes,
351
+ whitelist_for_overwrite: list[str],
352
+ cwd: str,
353
+ ) -> None:
354
+ """Create a new BashState instance from a serialized state dictionary"""
355
+ self._bash_command_mode = bash_command_mode
356
+ self._cwd = cwd or self._cwd
357
+ self._file_edit_mode = file_edit_mode
358
+ self._write_if_empty_mode = write_if_empty_mode
359
+ self._whitelist_for_overwrite = set(whitelist_for_overwrite)
360
+ self._mode = mode
361
+ self.reset_shell()
264
362
 
265
363
  def get_pending_for(self) -> str:
266
364
  if isinstance(self._state, datetime.datetime):
@@ -290,7 +388,8 @@ class BashState:
290
388
  return self._pending_output
291
389
 
292
390
 
293
- BASH_STATE = BashState()
391
+ BASH_STATE = BashState(os.getcwd(), None, None, None, None)
392
+ INITIALIZED = False
294
393
 
295
394
 
296
395
  def initialize(
@@ -298,57 +397,122 @@ def initialize(
298
397
  read_files_: list[str],
299
398
  task_id_to_resume: str,
300
399
  max_tokens: Optional[int],
400
+ mode: ModesConfig,
301
401
  ) -> str:
302
- reset_shell()
402
+ global BASH_STATE
303
403
 
304
404
  # Expand the workspace path
305
- any_workspace_path = expand_user(any_workspace_path, BASH_STATE.is_in_docker)
405
+ any_workspace_path = expand_user(any_workspace_path, None)
306
406
  repo_context = ""
307
407
 
308
408
  memory = ""
409
+ bash_state = None
309
410
  if task_id_to_resume:
310
411
  try:
311
- project_root_path, task_mem = load_memory(
412
+ project_root_path, task_mem, bash_state = load_memory(
312
413
  task_id_to_resume,
313
414
  max_tokens,
314
415
  lambda x: default_enc.encode(x).ids,
315
416
  lambda x: default_enc.decode(x),
316
417
  )
317
418
  memory = "Following is the retrieved task:\n" + task_mem
318
- if (
319
- not any_workspace_path or not os.path.exists(any_workspace_path)
320
- ) and os.path.exists(project_root_path):
419
+ if os.path.exists(project_root_path):
321
420
  any_workspace_path = project_root_path
421
+
322
422
  except Exception:
323
423
  memory = f'Error: Unable to load task with ID "{task_id_to_resume}" '
324
424
 
425
+ folder_to_start = None
325
426
  if any_workspace_path:
326
427
  if os.path.exists(any_workspace_path):
327
428
  repo_context, folder_to_start = get_repo_context(any_workspace_path, 200)
328
429
 
329
- BASH_STATE.shell.sendline(f"cd {shlex.quote(str(folder_to_start))}")
330
- BASH_STATE.shell.expect(PROMPT, timeout=0.2)
331
- BASH_STATE.update_cwd()
332
-
333
430
  repo_context = f"---\n# Workspace structure\n{repo_context}\n---\n"
431
+
432
+ # update modes if they're relative
433
+ if isinstance(mode, CodeWriterMode):
434
+ mode.update_relative_globs(any_workspace_path)
435
+ else:
436
+ assert isinstance(mode, str)
334
437
  else:
335
- repo_context = (
336
- f"\nInfo: Workspace path {any_workspace_path} does not exist\n"
337
- )
438
+ if os.path.abspath(any_workspace_path):
439
+ os.makedirs(any_workspace_path, exist_ok=True)
440
+ repo_context = f"\nInfo: Workspace path {any_workspace_path} did not exist. I've created it for you.\n"
441
+ else:
442
+ repo_context = (
443
+ f"\nInfo: Workspace path {any_workspace_path} does not exist."
444
+ )
445
+ # Restore bash state if available
446
+ if bash_state is not None:
447
+ try:
448
+ parsed_state = BashState.parse_state(bash_state)
449
+ if mode == "wcgw":
450
+ BASH_STATE.load_state(
451
+ parsed_state[0],
452
+ parsed_state[1],
453
+ parsed_state[2],
454
+ parsed_state[3],
455
+ parsed_state[4] + list(BASH_STATE.whitelist_for_overwrite),
456
+ str(folder_to_start) if folder_to_start else "",
457
+ )
458
+ else:
459
+ state = modes_to_state(mode)
460
+ BASH_STATE.load_state(
461
+ state[0],
462
+ state[1],
463
+ state[2],
464
+ state[3],
465
+ parsed_state[4] + list(BASH_STATE.whitelist_for_overwrite),
466
+ str(folder_to_start) if folder_to_start else "",
467
+ )
468
+ except ValueError:
469
+ console.print(traceback.format_exc())
470
+ console.print("Error: couldn't load bash state")
471
+ pass
472
+ else:
473
+ state = modes_to_state(mode)
474
+ BASH_STATE.load_state(
475
+ state[0],
476
+ state[1],
477
+ state[2],
478
+ state[3],
479
+ list(BASH_STATE.whitelist_for_overwrite),
480
+ str(folder_to_start) if folder_to_start else "",
481
+ )
482
+ del mode
338
483
 
339
484
  initial_files_context = ""
340
485
  if read_files_:
486
+ if folder_to_start:
487
+ read_files_ = [
488
+ os.path.join(folder_to_start, f) if not os.path.isabs(f) else f
489
+ for f in read_files_
490
+ ]
341
491
  initial_files = read_files(read_files_, max_tokens)
342
492
  initial_files_context = f"---\n# Requested files\n{initial_files}\n---\n"
343
493
 
344
494
  uname_sysname = os.uname().sysname
345
495
  uname_machine = os.uname().machine
346
496
 
497
+ mode_prompt = ""
498
+ if BASH_STATE.mode == Modes.code_writer:
499
+ mode_prompt = code_writer_prompt(
500
+ BASH_STATE.file_edit_mode.allowed_globs,
501
+ BASH_STATE.write_if_empty_mode.allowed_globs,
502
+ "all" if BASH_STATE.bash_command_mode.allowed_commands else [],
503
+ )
504
+ elif BASH_STATE.mode == Modes.architect:
505
+ mode_prompt = ARCHITECT_PROMPT
506
+ else:
507
+ mode_prompt = WCGW_PROMPT
508
+
347
509
  output = f"""
510
+ {mode_prompt}
511
+
348
512
  # Environment
349
513
  System: {uname_sysname}
350
514
  Machine: {uname_machine}
351
- Current working directory: {BASH_STATE.cwd}
515
+ Initialized in directory (also cwd): {BASH_STATE.cwd}
352
516
 
353
517
  {repo_context}
354
518
 
@@ -359,11 +523,14 @@ Current working directory: {BASH_STATE.cwd}
359
523
  {memory}
360
524
  """
361
525
 
526
+ global INITIALIZED
527
+ INITIALIZED = True
528
+
362
529
  return output
363
530
 
364
531
 
365
532
  def reset_shell() -> str:
366
- BASH_STATE.reset()
533
+ BASH_STATE.reset_shell()
367
534
  return "Reset successful" + get_status()
368
535
 
369
536
 
@@ -472,6 +639,8 @@ def execute_bash(
472
639
  try:
473
640
  is_interrupt = False
474
641
  if isinstance(bash_arg, BashCommand):
642
+ if BASH_STATE.bash_command_mode.allowed_commands == "none":
643
+ return "Error: BashCommand not allowed in current mode", 0.0
475
644
  updated_repl_mode = update_repl_prompt(bash_arg.command)
476
645
  if updated_repl_mode:
477
646
  BASH_STATE.set_repl()
@@ -647,31 +816,11 @@ def execute_bash(
647
816
  console.print(traceback.format_exc())
648
817
  console.print("Malformed output, restarting shell", style="red")
649
818
  # Malformed output, restart shell
650
- BASH_STATE.reset()
819
+ BASH_STATE.reset_shell()
651
820
  output = "(exit shell has restarted)"
652
821
  return output, 0
653
822
 
654
823
 
655
- def serve_image_in_bg(file_path: str, client_uuid: str, name: str) -> None:
656
- if not client_uuid:
657
- client_uuid = str(uuid.uuid4())
658
-
659
- server_url = "wss://wcgw.arcfu.com/register_serve_image"
660
-
661
- with open(file_path, "rb") as image_file:
662
- image_bytes = image_file.read()
663
- media_type = mimetypes.guess_type(file_path)[0]
664
- image_b64 = base64.b64encode(image_bytes).decode("utf-8")
665
- uu = {"name": name, "image_b64": image_b64, "media_type": media_type}
666
-
667
- with syncconnect(f"{server_url}/{client_uuid}") as websocket:
668
- try:
669
- websocket.send(json.dumps(uu))
670
- except websockets.ConnectionClosed:
671
- console.print(f"Connection closed for UUID: {client_uuid}, retrying")
672
- serve_image_in_bg(file_path, client_uuid, name)
673
-
674
-
675
824
  MEDIA_TYPES = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
676
825
 
677
826
 
@@ -766,6 +915,13 @@ def write_file(
766
915
  path_ = expand_user(writefile.file_path, BASH_STATE.is_in_docker)
767
916
 
768
917
  error_on_exist_ = error_on_exist and path_ not in BASH_STATE.whitelist_for_overwrite
918
+
919
+ # Validate using write_if_empty_mode after checking whitelist
920
+ allowed_globs = BASH_STATE.write_if_empty_mode.allowed_globs
921
+ if allowed_globs != "all" and not any(
922
+ fnmatch.fnmatch(path_, pattern) for pattern in allowed_globs
923
+ ):
924
+ return f"Error: updating file {path_} not allowed in current mode. Doesn't match allowed globs: {allowed_globs}"
769
925
  add_overwrite_warning = ""
770
926
  if not BASH_STATE.is_in_docker:
771
927
  if (error_on_exist or error_on_exist_) and os.path.exists(path_):
@@ -897,6 +1053,15 @@ def _do_diff_edit(fedit: FileEdit, max_tokens: Optional[int]) -> str:
897
1053
  else:
898
1054
  path_ = expand_user(fedit.file_path, BASH_STATE.is_in_docker)
899
1055
 
1056
+ # Validate using file_edit_mode
1057
+ allowed_globs = BASH_STATE.file_edit_mode.allowed_globs
1058
+ if allowed_globs != "all" and not any(
1059
+ fnmatch.fnmatch(path_, pattern) for pattern in allowed_globs
1060
+ ):
1061
+ raise Exception(
1062
+ f"Error: updating file {path_} not allowed in current mode. Doesn't match allowed globs: {allowed_globs}"
1063
+ )
1064
+
900
1065
  # The LLM is now aware that the file exists
901
1066
  BASH_STATE.add_to_whitelist_for_overwrite(path_)
902
1067
 
@@ -1063,7 +1228,7 @@ def get_tool_output(
1063
1228
  loop_call: Callable[[str, float], tuple[str, float]],
1064
1229
  max_tokens: Optional[int],
1065
1230
  ) -> tuple[list[str | ImageData | DoneFlag], float]:
1066
- global IS_IN_DOCKER, TOOL_CALLS
1231
+ global IS_IN_DOCKER, TOOL_CALLS, INITIALIZED
1067
1232
  if isinstance(args, dict):
1068
1233
  adapter = TypeAdapter[TOOLS](TOOLS, config={"extra": "forbid"})
1069
1234
  arg = adapter.validate_python(args)
@@ -1071,17 +1236,27 @@ def get_tool_output(
1071
1236
  arg = args
1072
1237
  output: tuple[str | DoneFlag | ImageData, float]
1073
1238
  TOOL_CALLS.append(arg)
1239
+
1074
1240
  if isinstance(arg, Confirmation):
1075
1241
  console.print("Calling ask confirmation tool")
1076
1242
  output = ask_confirmation(arg), 0.0
1077
1243
  elif isinstance(arg, (BashCommand | BashInteraction)):
1078
1244
  console.print("Calling execute bash tool")
1245
+ if not INITIALIZED:
1246
+ raise Exception("Initialize tool not called yet.")
1247
+
1079
1248
  output = execute_bash(enc, arg, max_tokens, arg.wait_for_seconds)
1080
1249
  elif isinstance(arg, WriteIfEmpty):
1081
1250
  console.print("Calling write file tool")
1251
+ if not INITIALIZED:
1252
+ raise Exception("Initialize tool not called yet.")
1253
+
1082
1254
  output = write_file(arg, True, max_tokens), 0
1083
1255
  elif isinstance(arg, FileEdit):
1084
1256
  console.print("Calling full file edit tool")
1257
+ if not INITIALIZED:
1258
+ raise Exception("Initialize tool not called yet.")
1259
+
1085
1260
  output = do_diff_edit(arg, max_tokens), 0.0
1086
1261
  elif isinstance(arg, DoneFlag):
1087
1262
  console.print("Calling mark finish tool")
@@ -1106,6 +1281,7 @@ def get_tool_output(
1106
1281
  arg.initial_files_to_read,
1107
1282
  arg.task_id_to_resume,
1108
1283
  max_tokens,
1284
+ arg.mode,
1109
1285
  ),
1110
1286
  0.0,
1111
1287
  )
@@ -1153,12 +1329,12 @@ def get_tool_output(
1153
1329
  fglob = expand_user(fglob, None)
1154
1330
  if not os.path.isabs(fglob) and arg.project_root_path:
1155
1331
  fglob = os.path.join(arg.project_root_path, fglob)
1156
- globs = glob.glob(fglob)
1332
+ globs = glob.glob(fglob, recursive=True)
1157
1333
  relevant_files.extend(globs[:1000])
1158
1334
  if not globs:
1159
1335
  warnings += f"Warning: No files found for the glob: {fglob}\n"
1160
1336
  relevant_files_data = read_files(relevant_files[:10_000], None)
1161
- output_ = save_memory(arg, relevant_files_data)
1337
+ output_ = save_memory(arg, relevant_files_data, BASH_STATE.serialize())
1162
1338
  if not relevant_files and arg.relevant_file_globs:
1163
1339
  output_ = f'Error: No files found for the given globs. Context file successfully saved at "{output_}", but please fix the error.'
1164
1340
  elif warnings:
wcgw/types_.py CHANGED
@@ -1,4 +1,6 @@
1
- from typing import Literal, Optional, Sequence
1
+ import os
2
+ from enum import Enum
3
+ from typing import Any, Literal, Optional, Sequence, Union
2
4
 
3
5
  from pydantic import BaseModel as PydanticBaseModel
4
6
 
@@ -11,6 +13,54 @@ class NoExtraArgs(PydanticBaseModel):
11
13
  BaseModel = NoExtraArgs
12
14
 
13
15
 
16
+ class Modes(str, Enum):
17
+ wcgw = "wcgw"
18
+ architect = "architect"
19
+ code_writer = "code_writer"
20
+
21
+
22
+ class CodeWriterMode(BaseModel):
23
+ allowed_globs: Literal["all"] | list[str]
24
+ allowed_commands: Literal["all"] | list[str]
25
+
26
+ def update_relative_globs(self, workspace_root: str) -> None:
27
+ """Update globs if they're relative paths"""
28
+ if self.allowed_globs != "all":
29
+ self.allowed_globs = [
30
+ glob if os.path.isabs(glob) else os.path.join(workspace_root, glob)
31
+ for glob in self.allowed_globs
32
+ ]
33
+
34
+
35
+ ModesConfig = Union[Literal["wcgw", "architect"], CodeWriterMode]
36
+
37
+
38
+ class Initialize(BaseModel):
39
+ any_workspace_path: str
40
+ initial_files_to_read: list[str]
41
+ task_id_to_resume: str
42
+ mode_name: Literal["wcgw", "architect", "code_writer"]
43
+ code_writer_config: Optional[CodeWriterMode] = None
44
+
45
+ def model_post_init(self, __context: Any) -> None:
46
+ if self.mode_name == "code_writer":
47
+ assert (
48
+ self.code_writer_config is not None
49
+ ), "code_writer_config can't be null when the mode is code_writer"
50
+ return super().model_post_init(__context)
51
+
52
+ @property
53
+ def mode(self) -> ModesConfig:
54
+ if self.mode_name == "wcgw":
55
+ return "wcgw"
56
+ if self.mode_name == "architect":
57
+ return "architect"
58
+ assert (
59
+ self.code_writer_config is not None
60
+ ), "code_writer_config can't be null when the mode is code_writer"
61
+ return self.code_writer_config
62
+
63
+
14
64
  class BashCommand(BaseModel):
15
65
  command: str
16
66
  wait_for_seconds: Optional[int] = None
@@ -56,12 +106,6 @@ class FileEdit(BaseModel):
56
106
  file_edit_using_search_replace_blocks: str
57
107
 
58
108
 
59
- class Initialize(BaseModel):
60
- any_workspace_path: str
61
- initial_files_to_read: list[str]
62
- task_id_to_resume: str
63
-
64
-
65
109
  class GetScreenInfo(BaseModel):
66
110
  docker_image_id: str
67
111
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 2.7.1
3
+ Version: 2.8.0
4
4
  Summary: Shell and coding agent on claude and chatgpt
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
@@ -41,6 +41,8 @@ Description-Content-Type: text/markdown
41
41
  [![codecov](https://codecov.io/gh/rusiaaman/wcgw/graph/badge.svg)](https://codecov.io/gh/rusiaaman/wcgw)
42
42
 
43
43
  ## Updates
44
+ - [8 Jan 2025] Context saving tool for saving relevant file paths along with a description in a single file. Can be used as a task checkpoint or for knowledge transfer.
45
+
44
46
  - [29 Dec 2024] Syntax checking on file writing and edits is now stable. Made `initialize` tool call useful; sending smart repo structure to claude if any repo is referenced. Large file handling is also now improved.
45
47
 
46
48
  - [9 Dec 2024] [Vscode extension to paste context on Claude app](https://marketplace.visualstudio.com/items?itemName=AmanRusia.wcgw)
@@ -66,7 +68,7 @@ Description-Content-Type: text/markdown
66
68
  - Only one command is allowed to be run at a time, simplifying management and avoiding rogue processes. There's only single shell instance at any point of time.
67
69
  - Current working directory is always returned after any shell command to prevent AI from getting lost.
68
70
  - Command polling exits after a quick timeout to avoid slow feedback. However, status checking has wait tolerance based on fresh output streaming from a command. Both of these approach combined provides a good shell interaction experience.
69
-
71
+ - ⚡ **Saving repo context in a single file**: Task checkpointing using "ContextSave" tool saves detailed context in a single file. Tasks can later be resumed in a new chat asking "Resume `task id`". The saved file can be used to do other kinds of knowledge transfer, such as taking help from another AI.
70
72
 
71
73
  ## Top use cases examples
72
74
 
@@ -140,6 +142,11 @@ over here
140
142
 
141
143
  Then ask claude to execute shell commands, read files, edit files, run your code, etc.
142
144
 
145
+ #### Task checkpoint or knowledge transfer
146
+ - You can do a task checkpoint or a knowledge transfer by attaching "KnowledgeTransfer" prompt using "Attach from MCP" button.
147
+ - On running "KnowledgeTransfer" prompt, the "ContextSave" tool will be called saving the task description and all file content together in a single file. An id for the task will be generated.
148
+ - You can in a new chat say "Resume '<task id>'", the AI should then call "Initialize" with the task id and load the context from there.
149
+ - Or you can directly open the file generated and share it with another AI for help.
143
150
 
144
151
  ### [Optional] Vs code extension
145
152
  https://marketplace.visualstudio.com/items?itemName=AmanRusia.wcgw
@@ -1,22 +1,23 @@
1
1
  wcgw/__init__.py,sha256=9K2QW7QuSLhMTVbKbBYd9UUp-ZyrfBrxcjuD_xk458k,118
2
- wcgw/types_.py,sha256=6gqdwC0F_rAQ80uPQpTWnw7uUByAD9qPa6-hKFgxqUU,1946
2
+ wcgw/types_.py,sha256=D3518a2azSKeW3D-ACYWJwsaqo7Oj-8BRRR2IhCUtNU,3414
3
3
  wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  wcgw/client/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
5
- wcgw/client/anthropic_client.py,sha256=p-QsqjTl5LJxCFL5GfPNlQCRa4Bh4XM6MWfkaYJj4qc,21439
5
+ wcgw/client/anthropic_client.py,sha256=TLPQCc96cxLGGMHg1sYqX3_IxTm_-WQ1e-XPQD6i6xU,20958
6
6
  wcgw/client/cli.py,sha256=-z0kpDAW3mzfQrQeZfaVJhBCAQY3HXnt9GdgQ8s-u0Y,1003
7
7
  wcgw/client/common.py,sha256=OCH7Tx64jojz3M3iONUrGMadE07W21DiZs5sOxWX1Qc,1456
8
8
  wcgw/client/computer_use.py,sha256=35NKAlMrxwD0TBlMMRnbCwz4g8TBRGOlcy-cmS-yJ_A,15247
9
9
  wcgw/client/diff-instructions.txt,sha256=tmJ9Fu9XdO_72lYXQQNY9RZyx91bjxrXJf9d_KBz57k,1611
10
- wcgw/client/memory.py,sha256=x7AsZo2GwYyEmnxtTvmQJBlUxD_26LuENQbCbNhTGyY,2272
11
- wcgw/client/openai_client.py,sha256=UCiamF27iVhYh5fI5Kpa1IIMMXI6R5uPtRDjdKyHhOQ,18340
10
+ wcgw/client/memory.py,sha256=8LdYsOhvCOoC1kfvDr85kNy07WnhPMvE6B2FRM2w85Y,2902
11
+ wcgw/client/modes.py,sha256=ZhCY-Orbk2JtbxbrPz-YNqLO6PA_zNvE1uNCD3JmnYA,10463
12
+ wcgw/client/openai_client.py,sha256=WvCnTij5QOPhe0VCdsk5Qo1Ob6yBdxFSvsCmnF0Jozw,17858
12
13
  wcgw/client/openai_utils.py,sha256=KfMB1-p2zDiA7pPWwAVarochf7-qeL1UMgtlDV9DtKA,2662
13
14
  wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
14
- wcgw/client/tools.py,sha256=Cs3VnwlhYpYvLrq5Q0bYAvgRObne-UU3P6VcfgqK4_0,44914
15
+ wcgw/client/tools.py,sha256=9_pbB1K83teY9kiYs1i_bUsKIYSv7kGhi2-VxLZOFOI,51034
15
16
  wcgw/client/file_ops/diff_edit.py,sha256=o0ucArVwn5p6QTDgYsjLfMy4TJXxUG3qcppFBNF3bbQ,16751
16
17
  wcgw/client/file_ops/search_replace.py,sha256=89ieDC9fTsIKPDx7CJwnwpX32dRdSlMKoBtKVXc7VWI,3971
17
18
  wcgw/client/mcp_server/Readme.md,sha256=I8N4dHkTUVGNQ63BQkBMBhCCBTgqGOSF_pUR6iOEiUk,2495
18
19
  wcgw/client/mcp_server/__init__.py,sha256=hyPPwO9cabAJsOMWhKyat9yl7OlSmIobaoAZKHu3DMc,381
19
- wcgw/client/mcp_server/server.py,sha256=UWUHBlu7UllR4q3xgyHQLeoUSLKmtkGc9nNrZaoKYe4,14905
20
+ wcgw/client/mcp_server/server.py,sha256=C-wqaoS7sGWhQQW0pKS3HLjsUwjo2zzDJZm8d6znozU,12812
20
21
  wcgw/client/repo_ops/display_tree.py,sha256=5FD4hfMkM2cIZnXlu7WfJswJLthj0SkuHlkGH6dpWQU,4632
21
22
  wcgw/client/repo_ops/path_prob.py,sha256=SWf0CDn37rtlsYRQ51ufSxay-heaQoVIhr1alB9tZ4M,2144
22
23
  wcgw/client/repo_ops/paths_model.vocab,sha256=M1pXycYDQehMXtpp-qAgU7rtzeBbCOiJo4qcYFY0kqk,315087
@@ -46,8 +47,8 @@ mcp_wcgw/shared/memory.py,sha256=dBsOghxHz8-tycdSVo9kSujbsC8xb_tYsGmuJobuZnw,281
46
47
  mcp_wcgw/shared/progress.py,sha256=ymxOsb8XO5Mhlop7fRfdbmvPodANj7oq6O4dD0iUcnw,1048
47
48
  mcp_wcgw/shared/session.py,sha256=e44a0LQOW8gwdLs9_DE9oDsxqW2U8mXG3d5KT95bn5o,10393
48
49
  mcp_wcgw/shared/version.py,sha256=d2LZii-mgsPIxpshjkXnOTUmk98i0DT4ff8VpA_kAvE,111
49
- wcgw-2.7.1.dist-info/METADATA,sha256=XoEYy3PMfwJzhaSz8TcR2-yI_R7FgM141XVzaBVsHPU,8351
50
- wcgw-2.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
51
- wcgw-2.7.1.dist-info/entry_points.txt,sha256=eKo1omwbAggWlQ0l7GKoR7uV1-j16nk9tK0BhC2Oz_E,120
52
- wcgw-2.7.1.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
53
- wcgw-2.7.1.dist-info/RECORD,,
50
+ wcgw-2.8.0.dist-info/METADATA,sha256=3Mr_eVjHoBtbPcswgwoQ8PCrsHJWhr4WptUxrhhU27A,9408
51
+ wcgw-2.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
52
+ wcgw-2.8.0.dist-info/entry_points.txt,sha256=eKo1omwbAggWlQ0l7GKoR7uV1-j16nk9tK0BhC2Oz_E,120
53
+ wcgw-2.8.0.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
54
+ wcgw-2.8.0.dist-info/RECORD,,
File without changes