wcgw 2.7.0__py3-none-any.whl → 2.7.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.
- wcgw/client/anthropic_client.py +12 -17
- wcgw/client/mcp_server/server.py +44 -20
- wcgw/client/memory.py +43 -17
- wcgw/client/openai_client.py +13 -17
- wcgw/client/tools.py +34 -26
- wcgw/relay/serve.py +7 -7
- wcgw/types_.py +3 -7
- {wcgw-2.7.0.dist-info → wcgw-2.7.1.dist-info}/METADATA +14 -3
- {wcgw-2.7.0.dist-info → wcgw-2.7.1.dist-info}/RECORD +12 -12
- {wcgw-2.7.0.dist-info → wcgw-2.7.1.dist-info}/WHEEL +0 -0
- {wcgw-2.7.0.dist-info → wcgw-2.7.1.dist-info}/entry_points.txt +0 -0
- {wcgw-2.7.0.dist-info → wcgw-2.7.1.dist-info}/licenses/LICENSE +0 -0
wcgw/client/anthropic_client.py
CHANGED
|
@@ -25,10 +25,10 @@ from typer import Typer
|
|
|
25
25
|
from ..types_ import (
|
|
26
26
|
BashCommand,
|
|
27
27
|
BashInteraction,
|
|
28
|
+
ContextSave,
|
|
28
29
|
FileEdit,
|
|
29
30
|
GetScreenInfo,
|
|
30
31
|
Keyboard,
|
|
31
|
-
KnowledgeTransfer,
|
|
32
32
|
Mouse,
|
|
33
33
|
ReadFiles,
|
|
34
34
|
ReadImage,
|
|
@@ -130,7 +130,12 @@ def loop(
|
|
|
130
130
|
memory = None
|
|
131
131
|
if resume:
|
|
132
132
|
try:
|
|
133
|
-
memory = load_memory(
|
|
133
|
+
_, memory = load_memory(
|
|
134
|
+
resume,
|
|
135
|
+
8000,
|
|
136
|
+
lambda x: default_enc.encode(x).ids,
|
|
137
|
+
lambda x: default_enc.decode(x),
|
|
138
|
+
)
|
|
134
139
|
except OSError:
|
|
135
140
|
if resume == "latest":
|
|
136
141
|
resume_path = sorted(Path(".wcgw").iterdir(), key=os.path.getmtime)[-1]
|
|
@@ -215,22 +220,12 @@ def loop(
|
|
|
215
220
|
""",
|
|
216
221
|
),
|
|
217
222
|
ToolParam(
|
|
218
|
-
input_schema=
|
|
219
|
-
name="
|
|
223
|
+
input_schema=ContextSave.model_json_schema(),
|
|
224
|
+
name="ContextSave",
|
|
220
225
|
description="""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
- `all_user_instructions` should contain all instructions user shared in the conversation.
|
|
225
|
-
- `current_status_of_the_task` should contain only what is already achieved, not what's remaining.
|
|
226
|
-
- `all_issues_snippets` should only contain snippets of error, traceback, file snippets, commands, etc., no comments or solutions (important!).
|
|
227
|
-
- Be very verbose in `all_issues_snippets` providing as much error context as possible.
|
|
228
|
-
- Provide an id if the user hasn't provided one.
|
|
229
|
-
- This tool will return a text file path where the information is saved.
|
|
230
|
-
- After the tool completes succesfully, tell the user the task id and the generate file path. (important!)
|
|
231
|
-
- Leave arguments as empty string if they aren't relevant.
|
|
232
|
-
- This tool marks end of your conversation, do not run any further tools after calling this.
|
|
233
|
-
- Provide absolute file paths only in `relevant_file_paths` containing all relevant files.
|
|
226
|
+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
|
|
227
|
+
- Provide random unqiue id or whatever user provided.
|
|
228
|
+
- Leave project path as empty string if no project path
|
|
234
229
|
""",
|
|
235
230
|
),
|
|
236
231
|
]
|
wcgw/client/mcp_server/server.py
CHANGED
|
@@ -14,6 +14,7 @@ from mcp_wcgw.types import Tool as ToolParam
|
|
|
14
14
|
from ...types_ import (
|
|
15
15
|
BashCommand,
|
|
16
16
|
BashInteraction,
|
|
17
|
+
ContextSave,
|
|
17
18
|
FileEdit,
|
|
18
19
|
GetScreenInfo,
|
|
19
20
|
Initialize,
|
|
@@ -46,14 +47,47 @@ async def handle_read_resource(uri: AnyUrl) -> str:
|
|
|
46
47
|
|
|
47
48
|
@server.list_prompts() # type: ignore
|
|
48
49
|
async def handle_list_prompts() -> list[types.Prompt]:
|
|
49
|
-
return [
|
|
50
|
+
return [
|
|
51
|
+
types.Prompt(
|
|
52
|
+
name="KnowledgeTransfer",
|
|
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
|
+
]
|
|
50
56
|
|
|
51
57
|
|
|
52
58
|
@server.get_prompt() # type: ignore
|
|
53
59
|
async def handle_get_prompt(
|
|
54
60
|
name: str, arguments: dict[str, str] | None
|
|
55
61
|
) -> types.GetPromptResult:
|
|
56
|
-
|
|
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
|
+
]
|
|
90
|
+
return types.GetPromptResult(messages=messages)
|
|
57
91
|
|
|
58
92
|
|
|
59
93
|
@server.list_tools() # type: ignore
|
|
@@ -153,24 +187,14 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
153
187
|
"""
|
|
154
188
|
+ diffinstructions,
|
|
155
189
|
),
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
# - `all_issues_snippets` should only contain snippets of error, traceback, file snippets, commands, etc., no comments or solutions (important!).
|
|
165
|
-
# - Be very verbose in `all_issues_snippets` providing as much error context as possible.
|
|
166
|
-
# - Provide an id if the user hasn't provided one.
|
|
167
|
-
# - This tool will return a text file path where the information is saved.
|
|
168
|
-
# - After the tool completes succesfully, tell the user the task id and the generate file path. (important!)
|
|
169
|
-
# - Leave arguments as empty string if they aren't relevant.
|
|
170
|
-
# - This tool marks end of your conversation, do not run any further tools after calling this.
|
|
171
|
-
# - Provide absolute file paths only in `relevant_file_paths` containing all relevant files.
|
|
172
|
-
# """,
|
|
173
|
-
# ),
|
|
190
|
+
ToolParam(
|
|
191
|
+
inputSchema=ContextSave.model_json_schema(),
|
|
192
|
+
name="ContextSave",
|
|
193
|
+
description="""
|
|
194
|
+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
|
|
195
|
+
- Provide random unqiue id or whatever user provided.
|
|
196
|
+
- Leave project path as empty string if no project path""",
|
|
197
|
+
),
|
|
174
198
|
]
|
|
175
199
|
if COMPUTER_USE_ON_DOCKER_ENABLED:
|
|
176
200
|
tools += [
|
wcgw/client/memory.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import re
|
|
3
|
+
import shlex
|
|
4
|
+
from typing import Callable, Optional
|
|
2
5
|
|
|
3
|
-
from ..types_ import
|
|
6
|
+
from ..types_ import ContextSave
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
def get_app_dir_xdg() -> str:
|
|
@@ -8,19 +11,26 @@ def get_app_dir_xdg() -> str:
|
|
|
8
11
|
return os.path.join(xdg_data_dir, "wcgw")
|
|
9
12
|
|
|
10
13
|
|
|
11
|
-
def format_memory(task_memory:
|
|
12
|
-
memory_data =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
|
|
14
|
+
def format_memory(task_memory: ContextSave, relevant_files: str) -> str:
|
|
15
|
+
memory_data = ""
|
|
16
|
+
if task_memory.project_root_path:
|
|
17
|
+
memory_data += (
|
|
18
|
+
f"# PROJECT ROOT = {shlex.quote(task_memory.project_root_path)}\n"
|
|
19
|
+
)
|
|
20
|
+
memory_data += task_memory.description
|
|
17
21
|
|
|
18
|
-
memory_data +=
|
|
22
|
+
memory_data += (
|
|
23
|
+
"\n\n"
|
|
24
|
+
+ "# Relevant file paths\n"
|
|
25
|
+
+ ", ".join(map(shlex.quote, task_memory.relevant_file_globs))
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
memory_data += "\n\n# Relevant Files:\n" + relevant_files
|
|
19
29
|
|
|
20
30
|
return memory_data
|
|
21
31
|
|
|
22
32
|
|
|
23
|
-
def save_memory(task_memory:
|
|
33
|
+
def save_memory(task_memory: ContextSave, relevant_files: str) -> str:
|
|
24
34
|
app_dir = get_app_dir_xdg()
|
|
25
35
|
memory_dir = os.path.join(app_dir, "memory")
|
|
26
36
|
os.makedirs(memory_dir, exist_ok=True)
|
|
@@ -30,23 +40,39 @@ def save_memory(task_memory: KnowledgeTransfer, relevant_files: str) -> str:
|
|
|
30
40
|
raise Exception("Task id can not be empty")
|
|
31
41
|
memory_data = format_memory(task_memory, relevant_files)
|
|
32
42
|
|
|
33
|
-
memory_file = os.path.join(memory_dir, f"{task_id}.json")
|
|
34
43
|
memory_file_full = os.path.join(memory_dir, f"{task_id}.txt")
|
|
35
44
|
|
|
36
45
|
with open(memory_file_full, "w") as f:
|
|
37
46
|
f.write(memory_data)
|
|
38
47
|
|
|
39
|
-
with open(memory_file, "w") as f:
|
|
40
|
-
f.write(task_memory.model_dump_json())
|
|
41
|
-
|
|
42
48
|
return memory_file_full
|
|
43
49
|
|
|
44
50
|
|
|
45
|
-
def load_memory(
|
|
51
|
+
def load_memory[T](
|
|
52
|
+
task_id: str,
|
|
53
|
+
max_tokens: Optional[int],
|
|
54
|
+
encoder: Callable[[str], list[T]],
|
|
55
|
+
decoder: Callable[[list[T]], str],
|
|
56
|
+
) -> tuple[str, str]:
|
|
46
57
|
app_dir = get_app_dir_xdg()
|
|
47
58
|
memory_dir = os.path.join(app_dir, "memory")
|
|
48
|
-
memory_file = os.path.join(memory_dir, f"{task_id}.
|
|
59
|
+
memory_file = os.path.join(memory_dir, f"{task_id}.txt")
|
|
49
60
|
|
|
50
61
|
with open(memory_file, "r") as f:
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
data = f.read()
|
|
63
|
+
|
|
64
|
+
if max_tokens:
|
|
65
|
+
toks = encoder(data)
|
|
66
|
+
if len(toks) > max_tokens:
|
|
67
|
+
toks = toks[: max(0, max_tokens - 10)]
|
|
68
|
+
data = decoder(toks)
|
|
69
|
+
data += "\n(... truncated)"
|
|
70
|
+
|
|
71
|
+
project_root_match = re.search(r"# PROJECT ROOT = \s*(.*?)\s*$", data, re.MULTILINE)
|
|
72
|
+
project_root_path = ""
|
|
73
|
+
if project_root_match:
|
|
74
|
+
matched_path = project_root_match.group(1)
|
|
75
|
+
parsed_ = shlex.split(matched_path)
|
|
76
|
+
if parsed_ and len(parsed_) == 1:
|
|
77
|
+
project_root_path = parsed_[0]
|
|
78
|
+
return project_root_path, data
|
wcgw/client/openai_client.py
CHANGED
|
@@ -26,8 +26,8 @@ from typer import Typer
|
|
|
26
26
|
from ..types_ import (
|
|
27
27
|
BashCommand,
|
|
28
28
|
BashInteraction,
|
|
29
|
+
ContextSave,
|
|
29
30
|
FileEdit,
|
|
30
|
-
KnowledgeTransfer,
|
|
31
31
|
ReadFiles,
|
|
32
32
|
ReadImage,
|
|
33
33
|
ResetShell,
|
|
@@ -39,6 +39,7 @@ from .openai_utils import get_input_cost, get_output_cost
|
|
|
39
39
|
from .tools import (
|
|
40
40
|
DoneFlag,
|
|
41
41
|
ImageData,
|
|
42
|
+
default_enc,
|
|
42
43
|
get_tool_output,
|
|
43
44
|
initialize,
|
|
44
45
|
which_tool,
|
|
@@ -124,7 +125,12 @@ def loop(
|
|
|
124
125
|
memory = None
|
|
125
126
|
if resume:
|
|
126
127
|
try:
|
|
127
|
-
memory = load_memory(
|
|
128
|
+
_, memory = load_memory(
|
|
129
|
+
resume,
|
|
130
|
+
8000,
|
|
131
|
+
lambda x: default_enc.encode(x).ids,
|
|
132
|
+
lambda x: default_enc.decode(x),
|
|
133
|
+
)
|
|
128
134
|
except OSError:
|
|
129
135
|
if resume == "latest":
|
|
130
136
|
resume_path = sorted(Path(".wcgw").iterdir(), key=os.path.getmtime)[-1]
|
|
@@ -211,22 +217,12 @@ def loop(
|
|
|
211
217
|
description="Resets the shell. Use only if all interrupts and prompt reset attempts have failed repeatedly.",
|
|
212
218
|
),
|
|
213
219
|
openai.pydantic_function_tool(
|
|
214
|
-
|
|
220
|
+
ContextSave,
|
|
215
221
|
description="""
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
-
|
|
220
|
-
- `current_status_of_the_task` should contain only what is already achieved, not what's remaining.
|
|
221
|
-
- `all_issues_snippets` should only contain snippets of error, traceback, file snippets, commands, etc., no comments or solutions (important!).
|
|
222
|
-
- Be very verbose in `all_issues_snippets` providing as much error context as possible.
|
|
223
|
-
- Provide an id if the user hasn't provided one.
|
|
224
|
-
- This tool will return a text file path where the information is saved.
|
|
225
|
-
- After the tool completes succesfully, tell the user the task id and the generate file path. (important!)
|
|
226
|
-
- Leave arguments as empty string if they aren't relevant.
|
|
227
|
-
- This tool marks end of your conversation, do not run any further tools after calling this.
|
|
228
|
-
- Provide absolute file paths only in `relevant_file_paths` containing all relevant files.
|
|
229
|
-
""",
|
|
222
|
+
|
|
223
|
+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
|
|
224
|
+
- Provide random unqiue id or whatever user provided.
|
|
225
|
+
- Leave project path as empty string if no project path""",
|
|
230
226
|
),
|
|
231
227
|
]
|
|
232
228
|
|
wcgw/client/tools.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import datetime
|
|
3
|
+
import glob
|
|
3
4
|
import importlib.metadata
|
|
4
5
|
import json
|
|
5
6
|
import mimetypes
|
|
@@ -38,12 +39,12 @@ from websockets.sync.client import connect as syncconnect
|
|
|
38
39
|
from ..types_ import (
|
|
39
40
|
BashCommand,
|
|
40
41
|
BashInteraction,
|
|
42
|
+
ContextSave,
|
|
41
43
|
FileEdit,
|
|
42
44
|
FileEditFindReplace,
|
|
43
45
|
GetScreenInfo,
|
|
44
46
|
Initialize,
|
|
45
47
|
Keyboard,
|
|
46
|
-
KnowledgeTransfer,
|
|
47
48
|
Mouse,
|
|
48
49
|
ReadFiles,
|
|
49
50
|
ReadImage,
|
|
@@ -53,7 +54,7 @@ from ..types_ import (
|
|
|
53
54
|
)
|
|
54
55
|
from .computer_use import run_computer_tool
|
|
55
56
|
from .file_ops.search_replace import search_replace_edit
|
|
56
|
-
from .memory import
|
|
57
|
+
from .memory import load_memory, save_memory
|
|
57
58
|
from .repo_ops.repo_context import get_repo_context
|
|
58
59
|
from .sys_utils import command_run
|
|
59
60
|
|
|
@@ -307,16 +308,17 @@ def initialize(
|
|
|
307
308
|
memory = ""
|
|
308
309
|
if task_id_to_resume:
|
|
309
310
|
try:
|
|
310
|
-
task_mem = load_memory(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
311
|
+
project_root_path, task_mem = load_memory(
|
|
312
|
+
task_id_to_resume,
|
|
313
|
+
max_tokens,
|
|
314
|
+
lambda x: default_enc.encode(x).ids,
|
|
315
|
+
lambda x: default_enc.decode(x),
|
|
315
316
|
)
|
|
317
|
+
memory = "Following is the retrieved task:\n" + task_mem
|
|
316
318
|
if (
|
|
317
319
|
not any_workspace_path or not os.path.exists(any_workspace_path)
|
|
318
|
-
) and os.path.exists(
|
|
319
|
-
any_workspace_path =
|
|
320
|
+
) and os.path.exists(project_root_path):
|
|
321
|
+
any_workspace_path = project_root_path
|
|
320
322
|
except Exception:
|
|
321
323
|
memory = f'Error: Unable to load task with ID "{task_id_to_resume}" '
|
|
322
324
|
|
|
@@ -1003,7 +1005,7 @@ TOOLS = (
|
|
|
1003
1005
|
| Keyboard
|
|
1004
1006
|
| ScreenShot
|
|
1005
1007
|
| GetScreenInfo
|
|
1006
|
-
|
|
|
1008
|
+
| ContextSave
|
|
1007
1009
|
)
|
|
1008
1010
|
|
|
1009
1011
|
|
|
@@ -1045,8 +1047,8 @@ def which_tool_name(name: str) -> Type[TOOLS]:
|
|
|
1045
1047
|
return ScreenShot
|
|
1046
1048
|
elif name == "GetScreenInfo":
|
|
1047
1049
|
return GetScreenInfo
|
|
1048
|
-
elif name == "
|
|
1049
|
-
return
|
|
1050
|
+
elif name == "ContextSave":
|
|
1051
|
+
return ContextSave
|
|
1050
1052
|
else:
|
|
1051
1053
|
raise ValueError(f"Unknown tool name: {name}")
|
|
1052
1054
|
|
|
@@ -1142,20 +1144,26 @@ def get_tool_output(
|
|
|
1142
1144
|
)
|
|
1143
1145
|
BASH_STATE.set_in_docker(arg.docker_image_id)
|
|
1144
1146
|
return outputs, outputs_cost[1]
|
|
1145
|
-
elif isinstance(arg,
|
|
1147
|
+
elif isinstance(arg, ContextSave):
|
|
1146
1148
|
console.print("Calling task memory tool")
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1149
|
+
assert not BASH_STATE.is_in_docker, "KT not supported in docker"
|
|
1150
|
+
relevant_files = []
|
|
1151
|
+
warnings = ""
|
|
1152
|
+
for fglob in arg.relevant_file_globs:
|
|
1153
|
+
fglob = expand_user(fglob, None)
|
|
1154
|
+
if not os.path.isabs(fglob) and arg.project_root_path:
|
|
1155
|
+
fglob = os.path.join(arg.project_root_path, fglob)
|
|
1156
|
+
globs = glob.glob(fglob)
|
|
1157
|
+
relevant_files.extend(globs[:1000])
|
|
1158
|
+
if not globs:
|
|
1159
|
+
warnings += f"Warning: No files found for the glob: {fglob}\n"
|
|
1160
|
+
relevant_files_data = read_files(relevant_files[:10_000], None)
|
|
1161
|
+
output_ = save_memory(arg, relevant_files_data)
|
|
1162
|
+
if not relevant_files and arg.relevant_file_globs:
|
|
1163
|
+
output_ = f'Error: No files found for the given globs. Context file successfully saved at "{output_}", but please fix the error.'
|
|
1164
|
+
elif warnings:
|
|
1165
|
+
output_ = warnings + "\nContext file successfully saved at " + output_
|
|
1166
|
+
output = output_, 0.0
|
|
1159
1167
|
else:
|
|
1160
1168
|
raise ValueError(f"Unknown tool: {arg}")
|
|
1161
1169
|
if isinstance(output[0], str):
|
|
@@ -1184,7 +1192,7 @@ class Mdata(BaseModel):
|
|
|
1184
1192
|
| str
|
|
1185
1193
|
| ReadFiles
|
|
1186
1194
|
| Initialize
|
|
1187
|
-
|
|
|
1195
|
+
| ContextSave
|
|
1188
1196
|
)
|
|
1189
1197
|
|
|
1190
1198
|
|
wcgw/relay/serve.py
CHANGED
|
@@ -16,10 +16,10 @@ from pydantic import BaseModel
|
|
|
16
16
|
from ..types_ import (
|
|
17
17
|
BashCommand,
|
|
18
18
|
BashInteraction,
|
|
19
|
+
ContextSave,
|
|
19
20
|
FileEdit,
|
|
20
21
|
FileEditFindReplace,
|
|
21
22
|
Initialize,
|
|
22
|
-
KnowledgeTransfer,
|
|
23
23
|
ReadFiles,
|
|
24
24
|
ResetShell,
|
|
25
25
|
WriteIfEmpty,
|
|
@@ -36,7 +36,7 @@ class Mdata(BaseModel):
|
|
|
36
36
|
| FileEdit
|
|
37
37
|
| ReadFiles
|
|
38
38
|
| Initialize
|
|
39
|
-
|
|
|
39
|
+
| ContextSave
|
|
40
40
|
| str
|
|
41
41
|
)
|
|
42
42
|
user_id: UUID
|
|
@@ -317,13 +317,13 @@ async def initialize(initialize_data: InitializeWithUUID) -> str:
|
|
|
317
317
|
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
318
318
|
|
|
319
319
|
|
|
320
|
-
class
|
|
320
|
+
class ContextSaveWithUUID(ContextSave):
|
|
321
321
|
user_id: UUID
|
|
322
322
|
|
|
323
323
|
|
|
324
|
-
@app.post("/v1/
|
|
325
|
-
async def
|
|
326
|
-
user_id =
|
|
324
|
+
@app.post("/v1/context_save")
|
|
325
|
+
async def context_save(context_save_data: ContextSaveWithUUID) -> str:
|
|
326
|
+
user_id = context_save_data.user_id
|
|
327
327
|
if user_id not in clients:
|
|
328
328
|
return "Failure: id not found, ask the user to check it."
|
|
329
329
|
|
|
@@ -335,7 +335,7 @@ async def knowledge_transfer(knowledge_transfer_data: KTWithUUID) -> str:
|
|
|
335
335
|
|
|
336
336
|
gpts[user_id] = put_results
|
|
337
337
|
|
|
338
|
-
await clients[user_id](Mdata(data=
|
|
338
|
+
await clients[user_id](Mdata(data=context_save_data, user_id=user_id))
|
|
339
339
|
|
|
340
340
|
start_time = time.time()
|
|
341
341
|
while time.time() - start_time < 30:
|
wcgw/types_.py
CHANGED
|
@@ -101,12 +101,8 @@ class Keyboard(BaseModel):
|
|
|
101
101
|
text: str
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
class
|
|
104
|
+
class ContextSave(BaseModel):
|
|
105
105
|
id: str
|
|
106
106
|
project_root_path: str
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
current_status_of_the_task: str
|
|
110
|
-
all_issues_snippets: str
|
|
111
|
-
relevant_file_paths: list[str]
|
|
112
|
-
build_and_development_instructions: str
|
|
107
|
+
description: str
|
|
108
|
+
relevant_file_globs: list[str]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wcgw
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.1
|
|
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>
|
|
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
- Chatgpt - Allows custom gpt to talk to your shell via a relay server. (linux or mac)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
⚠️ Warning: do not
|
|
36
|
+
⚠️ Warning: do not allow BashCommand tool without reviewing the command, it may result in data loss.
|
|
37
37
|
|
|
38
38
|
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-tests.yml)
|
|
39
39
|
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-types.yml)
|
|
@@ -55,7 +55,18 @@ Description-Content-Type: text/markdown
|
|
|
55
55
|
- ⚡ **Large file edit**: Supports large file incremental edits to avoid token limit issues. Faster than full file write.
|
|
56
56
|
- ⚡ **Syntax checking on edits**: Reports feedback to the LLM if its edits have any syntax errors, so that it can redo it.
|
|
57
57
|
- ⚡ **Interactive Command Handling**: Supports interactive commands using arrow keys, interrupt, and ansi escape sequences.
|
|
58
|
-
- ⚡ **
|
|
58
|
+
- ⚡ **File protections**:
|
|
59
|
+
- The AI needs to read a file at least once before it's allowed to edit or rewrite it. This avoids accidental overwrites.
|
|
60
|
+
- Avoids context filling up while reading very large files. Files get chunked based on token length.
|
|
61
|
+
- On initialisation the provided workspace's directory structure is returned after selecting important files (based on .gitignore as well as a statistical approach)
|
|
62
|
+
- File edit based on search-replace tries to find correct search block if it has multiple matches based on previous search blocks. Fails otherwise (for correctness).
|
|
63
|
+
- File edit has spacing tolerant matching, with warning on issues like indentation mismatch. If there's no match, the closest match is returned to the AI to fix its mistakes.
|
|
64
|
+
- Using Aider-like search and replace, which has better performance than tool call based search and replace.
|
|
65
|
+
- ⚡ **Shell optimisations**:
|
|
66
|
+
- 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
|
+
- Current working directory is always returned after any shell command to prevent AI from getting lost.
|
|
68
|
+
- 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
|
+
|
|
59
70
|
|
|
60
71
|
## Top use cases examples
|
|
61
72
|
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
wcgw/__init__.py,sha256=9K2QW7QuSLhMTVbKbBYd9UUp-ZyrfBrxcjuD_xk458k,118
|
|
2
|
-
wcgw/types_.py,sha256=
|
|
2
|
+
wcgw/types_.py,sha256=6gqdwC0F_rAQ80uPQpTWnw7uUByAD9qPa6-hKFgxqUU,1946
|
|
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=
|
|
5
|
+
wcgw/client/anthropic_client.py,sha256=p-QsqjTl5LJxCFL5GfPNlQCRa4Bh4XM6MWfkaYJj4qc,21439
|
|
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=
|
|
11
|
-
wcgw/client/openai_client.py,sha256=
|
|
10
|
+
wcgw/client/memory.py,sha256=x7AsZo2GwYyEmnxtTvmQJBlUxD_26LuENQbCbNhTGyY,2272
|
|
11
|
+
wcgw/client/openai_client.py,sha256=UCiamF27iVhYh5fI5Kpa1IIMMXI6R5uPtRDjdKyHhOQ,18340
|
|
12
12
|
wcgw/client/openai_utils.py,sha256=KfMB1-p2zDiA7pPWwAVarochf7-qeL1UMgtlDV9DtKA,2662
|
|
13
13
|
wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
|
|
14
|
-
wcgw/client/tools.py,sha256=
|
|
14
|
+
wcgw/client/tools.py,sha256=Cs3VnwlhYpYvLrq5Q0bYAvgRObne-UU3P6VcfgqK4_0,44914
|
|
15
15
|
wcgw/client/file_ops/diff_edit.py,sha256=o0ucArVwn5p6QTDgYsjLfMy4TJXxUG3qcppFBNF3bbQ,16751
|
|
16
16
|
wcgw/client/file_ops/search_replace.py,sha256=89ieDC9fTsIKPDx7CJwnwpX32dRdSlMKoBtKVXc7VWI,3971
|
|
17
17
|
wcgw/client/mcp_server/Readme.md,sha256=I8N4dHkTUVGNQ63BQkBMBhCCBTgqGOSF_pUR6iOEiUk,2495
|
|
18
18
|
wcgw/client/mcp_server/__init__.py,sha256=hyPPwO9cabAJsOMWhKyat9yl7OlSmIobaoAZKHu3DMc,381
|
|
19
|
-
wcgw/client/mcp_server/server.py,sha256=
|
|
19
|
+
wcgw/client/mcp_server/server.py,sha256=UWUHBlu7UllR4q3xgyHQLeoUSLKmtkGc9nNrZaoKYe4,14905
|
|
20
20
|
wcgw/client/repo_ops/display_tree.py,sha256=5FD4hfMkM2cIZnXlu7WfJswJLthj0SkuHlkGH6dpWQU,4632
|
|
21
21
|
wcgw/client/repo_ops/path_prob.py,sha256=SWf0CDn37rtlsYRQ51ufSxay-heaQoVIhr1alB9tZ4M,2144
|
|
22
22
|
wcgw/client/repo_ops/paths_model.vocab,sha256=M1pXycYDQehMXtpp-qAgU7rtzeBbCOiJo4qcYFY0kqk,315087
|
|
23
23
|
wcgw/client/repo_ops/paths_tokens.model,sha256=jiwwE4ae8ADKuTZISutXuM5Wfyc_FBmN5rxTjoNnCos,1569052
|
|
24
24
|
wcgw/client/repo_ops/repo_context.py,sha256=5NqRxBY0K-SBFXJ0Ybt7llzYOBD8pRkTpruMMJHWxv4,4336
|
|
25
|
-
wcgw/relay/serve.py,sha256=
|
|
25
|
+
wcgw/relay/serve.py,sha256=Z5EwtaCAtKFBSnUw4mPYw0sze3Coc4Fa8gObRRG_bT0,9525
|
|
26
26
|
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
27
27
|
mcp_wcgw/__init__.py,sha256=fKCgOdN7cn7gR3YGFaGyV5Goe8A2sEyllLcsRkN0i-g,2601
|
|
28
28
|
mcp_wcgw/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -46,8 +46,8 @@ mcp_wcgw/shared/memory.py,sha256=dBsOghxHz8-tycdSVo9kSujbsC8xb_tYsGmuJobuZnw,281
|
|
|
46
46
|
mcp_wcgw/shared/progress.py,sha256=ymxOsb8XO5Mhlop7fRfdbmvPodANj7oq6O4dD0iUcnw,1048
|
|
47
47
|
mcp_wcgw/shared/session.py,sha256=e44a0LQOW8gwdLs9_DE9oDsxqW2U8mXG3d5KT95bn5o,10393
|
|
48
48
|
mcp_wcgw/shared/version.py,sha256=d2LZii-mgsPIxpshjkXnOTUmk98i0DT4ff8VpA_kAvE,111
|
|
49
|
-
wcgw-2.7.
|
|
50
|
-
wcgw-2.7.
|
|
51
|
-
wcgw-2.7.
|
|
52
|
-
wcgw-2.7.
|
|
53
|
-
wcgw-2.7.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|