wcgw 5.1.3__py3-none-any.whl → 5.3.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.
- wcgw/client/diff-instructions.txt +10 -10
- wcgw/client/file_ops/search_replace.py +1 -1
- wcgw/client/tool_prompts.py +0 -1
- wcgw/client/tools.py +28 -16
- wcgw/types_.py +8 -1
- {wcgw-5.1.3.dist-info → wcgw-5.3.0.dist-info}/METADATA +27 -2
- {wcgw-5.1.3.dist-info → wcgw-5.3.0.dist-info}/RECORD +10 -10
- {wcgw-5.1.3.dist-info → wcgw-5.3.0.dist-info}/WHEEL +0 -0
- {wcgw-5.1.3.dist-info → wcgw-5.3.0.dist-info}/entry_points.txt +0 -0
- {wcgw-5.1.3.dist-info → wcgw-5.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -59,14 +59,14 @@ def call_hello_renamed():
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
# *SEARCH/REPLACE block* Rules:
|
|
62
|
-
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, whitespaces, etc.
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
- Every "SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, whitespaces, etc.
|
|
64
|
+
- In a single call the edit blocks should be constructed from top to bottom of the file in a sequence.
|
|
65
|
+
- An edit block can't edit over a previous edit in the same tool call.
|
|
66
|
+
- Including multiple unique *SEARCH/REPLACE* blocks if needed.
|
|
67
|
+
- Include enough and only enough lines in each SEARCH section to uniquely match each set of lines that need to change.
|
|
68
|
+
- Keep *SEARCH/REPLACE* blocks concise.
|
|
69
|
+
- Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
|
|
70
|
+
- Include just the changing lines, and a few surrounding lines (0-3 lines) if needed for uniqueness.
|
|
71
|
+
- Other than for uniqueness, avoid including those lines which do not change in search (and replace) blocks. Target 0-3 non trivial extra lines per block.
|
|
72
|
+
- Preserve leading spaces and indentations in both SEARCH and REPLACE blocks.
|
|
@@ -4,7 +4,7 @@ from typing import Callable, Optional
|
|
|
4
4
|
from .diff_edit import FileEditInput, FileEditOutput, SearchReplaceMatchError
|
|
5
5
|
|
|
6
6
|
# Global regex patterns
|
|
7
|
-
SEARCH_MARKER = re.compile(r"^<<<<<<+\s*SEARCH
|
|
7
|
+
SEARCH_MARKER = re.compile(r"^<<<<<<+\s*SEARCH>?\s*$")
|
|
8
8
|
DIVIDER_MARKER = re.compile(r"^======*\s*$")
|
|
9
9
|
REPLACE_MARKER = re.compile(r"^>>>>>>+\s*REPLACE\s*$")
|
|
10
10
|
|
wcgw/client/tool_prompts.py
CHANGED
|
@@ -57,7 +57,6 @@ TOOL_PROMPTS = [
|
|
|
57
57
|
- Read full file content of one or more files.
|
|
58
58
|
- Provide absolute paths only (~ allowed)
|
|
59
59
|
- Only if the task requires line numbers understanding:
|
|
60
|
-
- You may populate "show_line_numbers_reason" with your reason, by default null/empty means no line numbers are shown.
|
|
61
60
|
- You may extract a range of lines. E.g., `/path/to/file:1-10` for lines 1-10. You can drop start or end like `/path/to/file:1-` or `/path/to/file:-10`
|
|
62
61
|
""",
|
|
63
62
|
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
|
wcgw/client/tools.py
CHANGED
|
@@ -26,7 +26,8 @@ from openai.types.chat import (
|
|
|
26
26
|
ChatCompletionMessageParam,
|
|
27
27
|
)
|
|
28
28
|
from pydantic import BaseModel, TypeAdapter, ValidationError
|
|
29
|
-
from syntax_checker import
|
|
29
|
+
from syntax_checker import Output as SCOutput
|
|
30
|
+
from syntax_checker import check_syntax as raw_check_syntax
|
|
30
31
|
|
|
31
32
|
from ..client.bash_state.bash_state import (
|
|
32
33
|
BashState,
|
|
@@ -78,6 +79,13 @@ class Context:
|
|
|
78
79
|
console: Console
|
|
79
80
|
|
|
80
81
|
|
|
82
|
+
def check_syntax(ext: str, content: str) -> SCOutput:
|
|
83
|
+
if ext == "html":
|
|
84
|
+
# Ignore due to prevelance of templating, causing issues
|
|
85
|
+
return raw_check_syntax("html", "")
|
|
86
|
+
return raw_check_syntax(ext, content)
|
|
87
|
+
|
|
88
|
+
|
|
81
89
|
def get_mode_prompt(context: Context) -> str:
|
|
82
90
|
mode_prompt = ""
|
|
83
91
|
if context.bash_state.mode == "code_writer":
|
|
@@ -255,8 +263,21 @@ def initialize(
|
|
|
255
263
|
)
|
|
256
264
|
initial_files_context = f"---\n# Requested files\n{initial_files}\n---\n"
|
|
257
265
|
|
|
258
|
-
# Check for CLAUDE.md
|
|
266
|
+
# Check for global CLAUDE.md and workspace CLAUDE.md
|
|
259
267
|
alignment_context = ""
|
|
268
|
+
|
|
269
|
+
# First check for global CLAUDE.md in ~/.wcgw/CLAUDE.md
|
|
270
|
+
global_alignment_file_path = os.path.join(expanduser("~"), ".wcgw", "CLAUDE.md")
|
|
271
|
+
if os.path.exists(global_alignment_file_path):
|
|
272
|
+
try:
|
|
273
|
+
with open(global_alignment_file_path, "r") as f:
|
|
274
|
+
global_alignment_content = f.read()
|
|
275
|
+
alignment_context += f"---\n# Important guidelines from the user\n```\n{global_alignment_content}\n```\n---\n\n"
|
|
276
|
+
except Exception:
|
|
277
|
+
# Handle any errors when reading the global file
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
# Then check for workspace-specific CLAUDE.md
|
|
260
281
|
if folder_to_start:
|
|
261
282
|
alignment_file_path = os.path.join(folder_to_start, "CLAUDE.md")
|
|
262
283
|
if os.path.exists(alignment_file_path):
|
|
@@ -264,10 +285,10 @@ def initialize(
|
|
|
264
285
|
# Read the CLAUDE.md file content
|
|
265
286
|
with open(alignment_file_path, "r") as f:
|
|
266
287
|
alignment_content = f.read()
|
|
267
|
-
alignment_context
|
|
288
|
+
alignment_context += f"---\n# CLAUDE.md - user shared project guidelines to follow\n```\n{alignment_content}\n```\n---\n\n"
|
|
268
289
|
except Exception:
|
|
269
290
|
# Handle any errors when reading the file
|
|
270
|
-
|
|
291
|
+
pass
|
|
271
292
|
|
|
272
293
|
uname_sysname = os.uname().sysname
|
|
273
294
|
uname_machine = os.uname().machine
|
|
@@ -509,7 +530,7 @@ def write_file(
|
|
|
509
530
|
error_on_exist_ = (
|
|
510
531
|
error_on_exist and path_ not in context.bash_state.whitelist_for_overwrite
|
|
511
532
|
)
|
|
512
|
-
|
|
533
|
+
curr_hash = ""
|
|
513
534
|
if error_on_exist and path_ in context.bash_state.whitelist_for_overwrite:
|
|
514
535
|
# Ensure hash has not changed
|
|
515
536
|
if os.path.exists(path_):
|
|
@@ -596,15 +617,12 @@ def write_file(
|
|
|
596
617
|
paths_: list[str] = []
|
|
597
618
|
for start, end in unread_ranges:
|
|
598
619
|
paths_.append(path_ + ":" + f"{start}-{end}")
|
|
599
|
-
paths_readfiles = ReadFiles(
|
|
600
|
-
file_paths=paths_, show_line_numbers_reason=""
|
|
601
|
-
)
|
|
620
|
+
paths_readfiles = ReadFiles(file_paths=paths_)
|
|
602
621
|
readfiles, file_ranges_dict, truncated = read_files(
|
|
603
622
|
paths_readfiles.file_paths,
|
|
604
623
|
coding_max_tokens,
|
|
605
624
|
noncoding_max_tokens,
|
|
606
625
|
context,
|
|
607
|
-
show_line_numbers=False,
|
|
608
626
|
start_line_nums=paths_readfiles.start_line_nums,
|
|
609
627
|
end_line_nums=paths_readfiles.end_line_nums,
|
|
610
628
|
)
|
|
@@ -993,7 +1011,6 @@ def get_tool_output(
|
|
|
993
1011
|
coding_max_tokens,
|
|
994
1012
|
noncoding_max_tokens,
|
|
995
1013
|
context,
|
|
996
|
-
bool(arg.show_line_numbers_reason),
|
|
997
1014
|
arg.start_line_nums,
|
|
998
1015
|
arg.end_line_nums,
|
|
999
1016
|
)
|
|
@@ -1115,7 +1132,6 @@ def read_files(
|
|
|
1115
1132
|
coding_max_tokens: Optional[int],
|
|
1116
1133
|
noncoding_max_tokens: Optional[int],
|
|
1117
1134
|
context: Context,
|
|
1118
|
-
show_line_numbers: bool = False,
|
|
1119
1135
|
start_line_nums: Optional[list[Optional[int]]] = None,
|
|
1120
1136
|
end_line_nums: Optional[list[Optional[int]]] = None,
|
|
1121
1137
|
) -> tuple[
|
|
@@ -1152,7 +1168,6 @@ def read_files(
|
|
|
1152
1168
|
coding_max_tokens,
|
|
1153
1169
|
noncoding_max_tokens,
|
|
1154
1170
|
context,
|
|
1155
|
-
show_line_numbers,
|
|
1156
1171
|
start_line_num,
|
|
1157
1172
|
end_line_num,
|
|
1158
1173
|
)
|
|
@@ -1196,12 +1211,11 @@ def read_file(
|
|
|
1196
1211
|
coding_max_tokens: Optional[int],
|
|
1197
1212
|
noncoding_max_tokens: Optional[int],
|
|
1198
1213
|
context: Context,
|
|
1199
|
-
show_line_numbers: bool = False,
|
|
1200
1214
|
start_line_num: Optional[int] = None,
|
|
1201
1215
|
end_line_num: Optional[int] = None,
|
|
1202
1216
|
) -> tuple[str, bool, int, str, tuple[int, int]]:
|
|
1203
1217
|
context.console.print(f"Reading file: {file_path}")
|
|
1204
|
-
|
|
1218
|
+
show_line_numbers = True
|
|
1205
1219
|
# Line numbers are now passed as parameters, no need to parse from path
|
|
1206
1220
|
|
|
1207
1221
|
# Expand the path before checking if it's absolute
|
|
@@ -1222,7 +1236,6 @@ def read_file(
|
|
|
1222
1236
|
|
|
1223
1237
|
if all_lines and all_lines[-1].endswith("\n"):
|
|
1224
1238
|
# Special handling of line counts because readlines doesn't consider last empty line as a separate line
|
|
1225
|
-
all_lines[-1] = all_lines[-1][:-1]
|
|
1226
1239
|
all_lines.append("")
|
|
1227
1240
|
|
|
1228
1241
|
total_lines = len(all_lines)
|
|
@@ -1347,7 +1360,6 @@ if __name__ == "__main__":
|
|
|
1347
1360
|
Context(BASH_STATE, BASH_STATE.console),
|
|
1348
1361
|
ReadFiles(
|
|
1349
1362
|
file_paths=["/Users/arusia/repos/wcgw/src/wcgw/client/tools.py"],
|
|
1350
|
-
show_line_numbers_reason="true",
|
|
1351
1363
|
),
|
|
1352
1364
|
default_enc,
|
|
1353
1365
|
0,
|
wcgw/types_.py
CHANGED
|
@@ -63,6 +63,10 @@ class Initialize(BaseModel):
|
|
|
63
63
|
assert self.code_writer_config is not None, (
|
|
64
64
|
"code_writer_config can't be null when the mode is code_writer"
|
|
65
65
|
)
|
|
66
|
+
if self.type != "first_call" and not self.thread_id:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
"Thread id should be provided if type != 'first_call', including when resetting"
|
|
69
|
+
)
|
|
66
70
|
return super().model_post_init(__context)
|
|
67
71
|
|
|
68
72
|
@property
|
|
@@ -119,10 +123,13 @@ class WriteIfEmpty(BaseModel):
|
|
|
119
123
|
|
|
120
124
|
class ReadFiles(BaseModel):
|
|
121
125
|
file_paths: list[str]
|
|
122
|
-
show_line_numbers_reason: Optional[str] = None
|
|
123
126
|
_start_line_nums: List[Optional[int]] = PrivateAttr(default_factory=lambda: [])
|
|
124
127
|
_end_line_nums: List[Optional[int]] = PrivateAttr(default_factory=lambda: [])
|
|
125
128
|
|
|
129
|
+
@property
|
|
130
|
+
def show_line_numbers_reason(self) -> str:
|
|
131
|
+
return "True"
|
|
132
|
+
|
|
126
133
|
@property
|
|
127
134
|
def start_line_nums(self) -> List[Optional[int]]:
|
|
128
135
|
"""Get the start line numbers."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wcgw
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.3.0
|
|
4
4
|
Summary: Shell and coding agent for Claude and other mcp clients
|
|
5
5
|
Project-URL: Homepage, https://github.com/rusiaaman/wcgw
|
|
6
6
|
Author-email: Aman Rusia <gapypi@arcfu.com>
|
|
@@ -19,7 +19,7 @@ Requires-Dist: pyte>=0.8.2
|
|
|
19
19
|
Requires-Dist: python-dotenv>=1.0.1
|
|
20
20
|
Requires-Dist: rich>=13.8.1
|
|
21
21
|
Requires-Dist: semantic-version>=2.10.0
|
|
22
|
-
Requires-Dist: syntax-checker
|
|
22
|
+
Requires-Dist: syntax-checker==0.4.0b4
|
|
23
23
|
Requires-Dist: tokenizers>=0.21.0
|
|
24
24
|
Requires-Dist: toml>=0.10.2
|
|
25
25
|
Requires-Dist: tree-sitter-bash>=0.23.3
|
|
@@ -152,6 +152,31 @@ Then add or update the claude config file `%APPDATA%\Claude\claude_desktop_confi
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
```
|
|
155
|
+
When you encounter an error, execute the command wsl uv --python 3.12 wcgw@latest in command prompt. If you get the `error /bin/bash: line 1: uv: command not found`, it means uv was not installed globally and you need to point to the correct path of uv.
|
|
156
|
+
1. Find where uv is installed:
|
|
157
|
+
```bash
|
|
158
|
+
whereis uv
|
|
159
|
+
```
|
|
160
|
+
Example output:
|
|
161
|
+
```uv: /home/mywsl/.local/bin/uv```
|
|
162
|
+
|
|
163
|
+
2. Test the full path works:
|
|
164
|
+
```
|
|
165
|
+
wsl /home/mywsl/.local/bin/uv tool run --python 3.12 wcgw@latest
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
3. Update the config with the full path:
|
|
169
|
+
```
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"wcgw": {
|
|
173
|
+
"command": "wsl.exe",
|
|
174
|
+
"args": ["/home/mywsl/.local/bin/uv", "tool", "run", "--python", "3.12", "wcgw@latest"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
Replace `/home/mywsl/.local/bin/uv` with your actual uv path from step 1.
|
|
155
180
|
|
|
156
181
|
### Usage
|
|
157
182
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
wcgw/__init__.py,sha256=JgAY25VsA208v8E7QTIU0E50nsk-TCJ4FWTEHmnssYU,127
|
|
2
2
|
wcgw/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
wcgw/types_.py,sha256=
|
|
3
|
+
wcgw/types_.py,sha256=9cK7ooTbGK3QaBNJCFZLnrezfpFUSNkA8S02JCHCl6c,8174
|
|
4
4
|
wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
wcgw/client/common.py,sha256=OCH7Tx64jojz3M3iONUrGMadE07W21DiZs5sOxWX1Qc,1456
|
|
6
|
-
wcgw/client/diff-instructions.txt,sha256=
|
|
6
|
+
wcgw/client/diff-instructions.txt,sha256=a-UeK9VEHJlwffAzZ-I1OiUkeMOuGU8zj3By-_STy28,1866
|
|
7
7
|
wcgw/client/memory.py,sha256=U2Nw2si3Zg7n_RhNAuaYcmrrDtZ_Mooi-kfAOKflT-I,3079
|
|
8
8
|
wcgw/client/modes.py,sha256=roH6SPBokJMr5IzAlccdI-vJyvyS5vqSMMyth7TE86A,10315
|
|
9
|
-
wcgw/client/tool_prompts.py,sha256=
|
|
10
|
-
wcgw/client/tools.py,sha256=
|
|
9
|
+
wcgw/client/tool_prompts.py,sha256=7tq9ijSo4CznVlMFcWpauELfCT_QAqwi8Chsl3SRfBY,4539
|
|
10
|
+
wcgw/client/tools.py,sha256=YNxXmNj2-rhCPZ12wmfMBsAXn0_nQnCPs1OTudEh5wM,49026
|
|
11
11
|
wcgw/client/bash_state/bash_state.py,sha256=b-Zo6pO3psfxu9TA0uw1ZZAa6bLmDPxbl7277fZfmrM,41862
|
|
12
12
|
wcgw/client/bash_state/parser/__init__.py,sha256=AnlNSmoQTSoqqlLOLX4P1uXfzc5VGeCGJsGgtisq2zE,207
|
|
13
13
|
wcgw/client/bash_state/parser/bash_statement_parser.py,sha256=9a8vPO1r3_tXmaAcubTQ5UY-NseWlalgm8LZA17LXuY,6058
|
|
14
14
|
wcgw/client/encoder/__init__.py,sha256=Y-8f43I6gMssUCWpX5rLYiAFv3D-JPRs4uNEejPlke8,1514
|
|
15
15
|
wcgw/client/file_ops/diff_edit.py,sha256=AwLq6-pY7czv1y-JA5O2Q4rgbvn82YmSL9jD8XB3Vo4,19019
|
|
16
16
|
wcgw/client/file_ops/extensions.py,sha256=CmfD7ON6SY24Prh2tRZdV9KbhuOrWqqk8qL1VtshzB8,3608
|
|
17
|
-
wcgw/client/file_ops/search_replace.py,sha256=
|
|
17
|
+
wcgw/client/file_ops/search_replace.py,sha256=5LFg-_U_ijnNrkYei4SWCPGKPGgDzJs49EDsIBzLmuY,6822
|
|
18
18
|
wcgw/client/mcp_server/Readme.md,sha256=2Z88jj1mf9daYGW1CWaldcJ0moy8owDumhR2glBY3A8,109
|
|
19
19
|
wcgw/client/mcp_server/__init__.py,sha256=mm7xhBIPwJpRT3u-Qsj4cKVMpVyucJoKRlbMP_gRRB0,343
|
|
20
20
|
wcgw/client/mcp_server/server.py,sha256=jjwrmZZ8X0tXD0rsPZ9fKjEpdXpXCfdhEsN3Ho_tC8I,4989
|
|
@@ -30,8 +30,8 @@ wcgw_cli/anthropic_client.py,sha256=8bjDY59-aioyTJgpB-NBHZNhZaq6rqcTJcOf81kzCyA,
|
|
|
30
30
|
wcgw_cli/cli.py,sha256=-7FBe_lahKyUOhf65iurTA1M1gXXXAiT0OVKQVcZKKo,948
|
|
31
31
|
wcgw_cli/openai_client.py,sha256=GOqoSFazTV-cFjpdZGPM0DIwec8Up2TEcKUbsN40AGY,15990
|
|
32
32
|
wcgw_cli/openai_utils.py,sha256=xGOb3W5ALrIozV7oszfGYztpj0FnXdD7jAxm5lEIVKY,2439
|
|
33
|
-
wcgw-5.
|
|
34
|
-
wcgw-5.
|
|
35
|
-
wcgw-5.
|
|
36
|
-
wcgw-5.
|
|
37
|
-
wcgw-5.
|
|
33
|
+
wcgw-5.3.0.dist-info/METADATA,sha256=9s9b9DmREZoUB09FKMrXr3h-EnvtGRyPYmvrmGMXM6U,15512
|
|
34
|
+
wcgw-5.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
wcgw-5.3.0.dist-info/entry_points.txt,sha256=UnjK-MAH4Qssh0tGJDMeij1oi-oRKokItkknP_BwShE,94
|
|
36
|
+
wcgw-5.3.0.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
|
|
37
|
+
wcgw-5.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|