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.

@@ -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
- Including multiple unique *SEARCH/REPLACE* blocks if needed.
65
- Include enough and only enough lines in each SEARCH section to uniquely match each set of lines that need to change.
66
-
67
- Keep *SEARCH/REPLACE* blocks concise.
68
- Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
69
- Include just the changing lines, and a few surrounding lines (0-3 lines) if needed for uniqueness.
70
- 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.
71
-
72
- Preserve leading spaces and indentations in both SEARCH and REPLACE blocks.
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\s*$")
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
 
@@ -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 check_syntax
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 in the workspace folder on first call
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 = f"---\n# CLAUDE.md - Project alignment guidelines\n```\n{alignment_content}\n```\n---\n\n"
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
- alignment_context = ""
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.1.3
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>=0.3.0
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=ImuOBDtFDcHhqu-EtCuDLXd7ORDW5zwJVLjgrzOEFcA,7939
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=eKRFA86yXWIGwNxIDaegTgTzIrFIBDWWiN1yP8Hf3i4,1685
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=0hfYmCzCo6kh_IC8RGJRfQ6hkB333GZvs_g3o_cvc2w,4662
10
- wcgw/client/tools.py,sha256=bDKyoWN4odI77UDAhqrUm2pD1ioxKIMUtB8IB7yjrOc,48475
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=bB1S-xkDdOX4h7UHHxfCHaQiS9KyYkIdU6UGvIrgwQM,6820
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.1.3.dist-info/METADATA,sha256=h0veH21vbstf5TEtm3UNEgeL5k-UaHi_NZGN0yxSlYA,14755
34
- wcgw-5.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
35
- wcgw-5.1.3.dist-info/entry_points.txt,sha256=UnjK-MAH4Qssh0tGJDMeij1oi-oRKokItkknP_BwShE,94
36
- wcgw-5.1.3.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
37
- wcgw-5.1.3.dist-info/RECORD,,
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