wcgw 1.2.0__tar.gz → 1.2.2__tar.gz

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.

Files changed (28) hide show
  1. {wcgw-1.2.0 → wcgw-1.2.2}/PKG-INFO +1 -1
  2. {wcgw-1.2.0 → wcgw-1.2.2}/gpt_action_json_schema.json +12 -2
  3. {wcgw-1.2.0 → wcgw-1.2.2}/gpt_instructions.txt +25 -7
  4. {wcgw-1.2.0 → wcgw-1.2.2}/pyproject.toml +1 -1
  5. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/anthropic_client.py +4 -3
  6. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/diff-instructions.txt +24 -4
  7. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/openai_client.py +6 -4
  8. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/tools.py +82 -51
  9. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/types_.py +2 -24
  10. {wcgw-1.2.0 → wcgw-1.2.2}/.github/workflows/python-publish.yml +0 -0
  11. {wcgw-1.2.0 → wcgw-1.2.2}/.github/workflows/python-tests.yml +0 -0
  12. {wcgw-1.2.0 → wcgw-1.2.2}/.gitignore +0 -0
  13. {wcgw-1.2.0 → wcgw-1.2.2}/.python-version +0 -0
  14. {wcgw-1.2.0 → wcgw-1.2.2}/.vscode/settings.json +0 -0
  15. {wcgw-1.2.0 → wcgw-1.2.2}/README.md +0 -0
  16. {wcgw-1.2.0 → wcgw-1.2.2}/src/__init__.py +0 -0
  17. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/__init__.py +0 -0
  18. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/__init__.py +0 -0
  19. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/__main__.py +0 -0
  20. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/cli.py +0 -0
  21. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/common.py +0 -0
  22. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/client/openai_utils.py +0 -0
  23. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/relay/serve.py +0 -0
  24. {wcgw-1.2.0 → wcgw-1.2.2}/src/wcgw/relay/static/privacy.txt +0 -0
  25. {wcgw-1.2.0 → wcgw-1.2.2}/static/ss1.png +0 -0
  26. {wcgw-1.2.0 → wcgw-1.2.2}/tests/test_basic.py +0 -0
  27. {wcgw-1.2.0 → wcgw-1.2.2}/tests/test_tools.py +0 -0
  28. {wcgw-1.2.0 → wcgw-1.2.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wcgw
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: What could go wrong giving full shell access to chatgpt?
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
@@ -255,6 +255,14 @@
255
255
  "schemas": {
256
256
  "BashInteractionWithUUID": {
257
257
  "properties": {
258
+ "type": {
259
+ "type": "string",
260
+ "enum": [
261
+ "BashInteraction"
262
+ ],
263
+ "const": "BashInteraction",
264
+ "title": "Type"
265
+ },
258
266
  "send_text": {
259
267
  "anyOf": [
260
268
  {
@@ -312,7 +320,8 @@
312
320
  },
313
321
  "type": "object",
314
322
  "required": [
315
- "user_id"
323
+ "user_id",
324
+ "type"
316
325
  ],
317
326
  "title": "BashInteractionWithUUID"
318
327
  },
@@ -443,7 +452,8 @@
443
452
  },
444
453
  "type": "object",
445
454
  "required": [
446
- "user_id"
455
+ "user_id",
456
+ "should_reset"
447
457
  ],
448
458
  "title": "ResetShellWithUUID"
449
459
  },
@@ -29,6 +29,7 @@ Instructions for `BashInteraction`
29
29
  - Interact with running program using this tool
30
30
  - Special keys like arrows, interrupts, enter, etc.
31
31
  - Send text input to the running program.
32
+ - Send send_specials=["Enter"] to recheck status of a running program.
32
33
  - Only one of send_text, send_specials, send_ascii should be provided.
33
34
 
34
35
  Instructions for `ResetShell`
@@ -38,16 +39,35 @@ Instructions for `FileEdit`:
38
39
  - Use absolute file path only.
39
40
  - Use SEARCH/REPLACE blocks to edit the file.
40
41
  Only edit the files using the following SEARCH/REPLACE blocks.
42
+
41
43
  ```
42
44
  <<<<<<< SEARCH
43
- // Original code
45
+ def hello():
46
+ "print a greeting"
47
+
48
+ print("hello")
49
+ =======
50
+ from hello import hello as hello_renamed
51
+ >>>>>>> REPLACE
52
+ <<<<<<< SEARCH
53
+ def call_hello():
54
+ "call hello"
55
+
56
+ hello()
44
57
  =======
45
- // New code
58
+ def call_hello_renamed():
59
+ "call hello renamed"
60
+
61
+ hello_renamed()
46
62
  >>>>>>> REPLACE
47
63
  <<<<<<< SEARCH
48
- // Original code further down in the file
64
+ impl1()
65
+ hello()
66
+ impl2()
49
67
  =======
50
- // New code to replace
68
+ impl1()
69
+ hello_renamed()
70
+ impl2()
51
71
  >>>>>>> REPLACE
52
72
  ```
53
73
 
@@ -71,11 +91,9 @@ Instructions for `FileEdit`:
71
91
  Include just the changing lines, and a few surrounding lines if needed for uniqueness.
72
92
  Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
73
93
 
74
- Include context lines before and after the code to edit for better matching in "<<<<<<< SEARCH" and ">>>>>>> REPLACE". Recommended to add 3 lines on each side.
75
-
76
94
  ---
77
95
  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.
78
96
 
79
97
  Always write production ready, syntactically correct code.
80
98
  ---
81
- Ask the user for the user_id `UUID` if they haven't provided in the first message.
99
+ Ask the user for the user_id `UUID` if they haven't provided in the first message.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  authors = [{ name = "Aman Rusia", email = "gapypi@arcfu.com" }]
3
3
  name = "wcgw"
4
- version = "1.2.0"
4
+ version = "1.2.2"
5
5
  description = "What could go wrong giving full shell access to chatgpt?"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.10, <3.13"
@@ -223,9 +223,10 @@ You're a cli assistant.
223
223
 
224
224
  Instructions:
225
225
 
226
- - You should use the provided bash execution tool to run script to complete objective.
227
- - First understand about the project by understanding the folder structure (ignoring node_modules or venv, etc.)
228
- - Always read relevant files before making any changes.
226
+ - You should use the provided bash execution, reading and writing file tools to complete objective.
227
+ - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
228
+ - Always read relevant files before editing.
229
+ - Do not provide code snippets unless asked by the user, instead directly edit the code.
229
230
 
230
231
  System information:
231
232
  - System: {uname_sysname}
@@ -1,5 +1,7 @@
1
1
 
2
- Instructions for
2
+ Instructions for editing files.
3
+
4
+
3
5
  Only edit the files using the following SEARCH/REPLACE blocks.
4
6
  ```
5
7
  <<<<<<< SEARCH
@@ -8,7 +10,27 @@ def hello():
8
10
 
9
11
  print("hello")
10
12
  =======
11
- from hello import hello
13
+ from hello import hello as hello_renamed
14
+ >>>>>>> REPLACE
15
+ <<<<<<< SEARCH
16
+ def call_hello():
17
+ "call hello"
18
+
19
+ hello()
20
+ =======
21
+ def call_hello_renamed():
22
+ "call hello renamed"
23
+
24
+ hello_renamed()
25
+ >>>>>>> REPLACE
26
+ <<<<<<< SEARCH
27
+ impl1()
28
+ hello()
29
+ impl2()
30
+ =======
31
+ impl1()
32
+ hello_renamed()
33
+ impl2()
12
34
  >>>>>>> REPLACE
13
35
  ```
14
36
 
@@ -31,5 +53,3 @@ Keep *SEARCH/REPLACE* blocks concise.
31
53
  Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
32
54
  Include just the changing lines, and a few surrounding lines if needed for uniqueness.
33
55
  Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
34
-
35
- Include context lines before and after the code to edit for better matching in "<<<<<<< SEARCH". Recommended to add 3 lines on each side.
@@ -185,6 +185,7 @@ def loop(
185
185
  - Interact with running program using this tool
186
186
  - Special keys like arrows, interrupts, enter, etc.
187
187
  - Send text input to the running program.
188
+ - Send send_specials=["Enter"] to recheck status of a running program.
188
189
  - Only one of send_text, send_specials, send_ascii should be provided.""",
189
190
  ),
190
191
  openai.pydantic_function_tool(
@@ -226,10 +227,11 @@ You're a cli assistant.
226
227
 
227
228
  Instructions:
228
229
 
229
- - You should use the provided bash execution tool to run script to complete objective.
230
- - First understand about the project by understanding the folder structure (ignoring node_modules or venv, etc.)
231
- - Always read relevant files before making any changes.
232
-
230
+ - You should use the provided bash execution, reading and writing file tools to complete objective.
231
+ - First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
232
+ - Always read relevant files before editing.
233
+ - Do not provide code snippets unless asked by the user, instead directly edit the code.
234
+
233
235
  System information:
234
236
  - System: {uname_sysname}
235
237
  - Machine: {uname_machine}
@@ -251,55 +251,71 @@ def execute_bash(
251
251
  )
252
252
 
253
253
  SHELL.sendline(command)
254
- elif bash_arg.send_specials:
255
- console.print(f"Sending special sequence: {bash_arg.send_specials}")
256
- for char in bash_arg.send_specials:
257
- if char == "Key-up":
258
- SHELL.send("\033[A")
259
- elif char == "Key-down":
260
- SHELL.send("\033[B")
261
- elif char == "Key-left":
262
- SHELL.send("\033[D")
263
- elif char == "Key-right":
264
- SHELL.send("\033[C")
265
- elif char == "Enter":
266
- SHELL.send("\n")
267
- elif char == "Ctrl-c":
268
- SHELL.sendintr()
269
- is_interrupt = True
270
- elif char == "Ctrl-d":
271
- SHELL.sendintr()
272
- is_interrupt = True
273
- elif char == "Ctrl-z":
274
- SHELL.send("\x1a")
275
- else:
276
- raise Exception(f"Unknown special character: {char}")
277
- elif bash_arg.send_ascii:
278
- console.print(f"Sending ASCII sequence: {bash_arg.send_ascii}")
279
- for ascii_char in bash_arg.send_ascii:
280
- SHELL.send(chr(ascii_char))
281
- if ascii_char == 3:
282
- is_interrupt = True
254
+
283
255
  else:
284
- if bash_arg.send_text is None:
256
+ if (
257
+ sum(
258
+ [
259
+ int(bool(bash_arg.send_text)),
260
+ int(bool(bash_arg.send_specials)),
261
+ int(bool(bash_arg.send_ascii)),
262
+ ]
263
+ )
264
+ != 1
265
+ ):
285
266
  return (
286
- "Failure: at least one of send_text, send_specials or send_ascii should be provided",
267
+ "Failure: exactly one of send_text, send_specials or send_ascii should be provided",
287
268
  0.0,
288
269
  )
270
+ if bash_arg.send_specials:
271
+ console.print(f"Sending special sequence: {bash_arg.send_specials}")
272
+ for char in bash_arg.send_specials:
273
+ if char == "Key-up":
274
+ SHELL.send("\033[A")
275
+ elif char == "Key-down":
276
+ SHELL.send("\033[B")
277
+ elif char == "Key-left":
278
+ SHELL.send("\033[D")
279
+ elif char == "Key-right":
280
+ SHELL.send("\033[C")
281
+ elif char == "Enter":
282
+ SHELL.send("\n")
283
+ elif char == "Ctrl-c":
284
+ SHELL.sendintr()
285
+ is_interrupt = True
286
+ elif char == "Ctrl-d":
287
+ SHELL.sendintr()
288
+ is_interrupt = True
289
+ elif char == "Ctrl-z":
290
+ SHELL.send("\x1a")
291
+ else:
292
+ raise Exception(f"Unknown special character: {char}")
293
+ elif bash_arg.send_ascii:
294
+ console.print(f"Sending ASCII sequence: {bash_arg.send_ascii}")
295
+ for ascii_char in bash_arg.send_ascii:
296
+ SHELL.send(chr(ascii_char))
297
+ if ascii_char == 3:
298
+ is_interrupt = True
299
+ else:
300
+ if bash_arg.send_text is None:
301
+ return (
302
+ "Failure: at least one of send_text, send_specials or send_ascii should be provided",
303
+ 0.0,
304
+ )
289
305
 
290
- updated_repl_mode = update_repl_prompt(bash_arg.send_text)
291
- if updated_repl_mode:
292
- BASH_STATE = "repl"
293
- response = (
294
- "Prompt updated, you can execute REPL lines using BashCommand now"
295
- )
296
- console.print(response)
297
- return (
298
- response,
299
- 0,
300
- )
301
- console.print(f"Interact text: {bash_arg.send_text}")
302
- SHELL.sendline(bash_arg.send_text)
306
+ updated_repl_mode = update_repl_prompt(bash_arg.send_text)
307
+ if updated_repl_mode:
308
+ BASH_STATE = "repl"
309
+ response = (
310
+ "Prompt updated, you can execute REPL lines using BashCommand now"
311
+ )
312
+ console.print(response)
313
+ return (
314
+ response,
315
+ 0,
316
+ )
317
+ console.print(f"Interact text: {bash_arg.send_text}")
318
+ SHELL.sendline(bash_arg.send_text)
303
319
 
304
320
  BASH_STATE = "repl"
305
321
 
@@ -453,6 +469,14 @@ def find_least_edit_distance_substring(
453
469
  content_lines = [
454
470
  line.strip() for line in orig_content_lines
455
471
  ] # Remove trailing and leading space for calculating edit distance
472
+ new_to_original_indices = {}
473
+ new_content_lines = []
474
+ for i in range(len(content_lines)):
475
+ if not content_lines[i]:
476
+ continue
477
+ new_content_lines.append(content_lines[i])
478
+ new_to_original_indices[len(new_content_lines) - 1] = i
479
+ content_lines = new_content_lines
456
480
  find_lines = find_str.split("\n")
457
481
  find_lines = [
458
482
  line.strip() for line in find_lines
@@ -470,8 +494,9 @@ def find_least_edit_distance_substring(
470
494
  edit_distance_sum += len(find_lines[j])
471
495
  if edit_distance_sum < min_edit_distance:
472
496
  min_edit_distance = edit_distance_sum
473
- min_edit_distance_lines = orig_content_lines[i: i + len(
474
- find_lines)]
497
+ orig_start_index = new_to_original_indices[i]
498
+ orig_end_index = new_to_original_indices.get(i + len(find_lines) - 1, len(orig_content_lines) - 1) + 1
499
+ min_edit_distance_lines = orig_content_lines[orig_start_index:orig_end_index]
475
500
  return "\n".join(min_edit_distance_lines), min_edit_distance
476
501
 
477
502
 
@@ -481,12 +506,12 @@ def edit_content(content: str, find_lines: str, replace_with_lines: str) -> str:
481
506
  closest_match, min_edit_distance = find_least_edit_distance_substring(
482
507
  content, find_lines
483
508
  )
484
- print(
485
- f"Exact match not found, found with whitespace removed edit distance: {min_edit_distance}"
486
- )
487
- if min_edit_distance / len(find_lines) < 1 / 100:
488
- print("Editing file with closest match")
509
+ if min_edit_distance == 0:
489
510
  return edit_content(content, closest_match, replace_with_lines)
511
+ else:
512
+ print(
513
+ f"Exact match not found, found with whitespace removed edit distance: {min_edit_distance}"
514
+ )
490
515
  raise Exception(
491
516
  f"Error: no match found for the provided `find_lines` in the file. Closest match:\n---\n{closest_match}\n---\nFile not edited"
492
517
  )
@@ -511,6 +536,12 @@ def do_diff_edit(fedit: FileEdit) -> str:
511
536
  apply_diff_to = f.read()
512
537
 
513
538
  lines = fedit.file_edit_using_search_replace_blocks.split("\n")
539
+
540
+ if not lines or not re.match(r"^<<<<<<+\s*SEARCH\s*$", lines[0]):
541
+ raise Exception(
542
+ "Error: first line should be `<<<<<< SEARCH` to start a search-replace block"
543
+ )
544
+
514
545
  n_lines = len(lines)
515
546
  i = 0
516
547
  replacement_count = 0
@@ -13,26 +13,11 @@ Specials = Literal[
13
13
 
14
14
 
15
15
  class BashInteraction(BaseModel):
16
+ type: Literal["BashInteraction"]
16
17
  send_text: Optional[str] = None
17
18
  send_specials: Optional[Sequence[Specials]] = None
18
19
  send_ascii: Optional[Sequence[int]] = None
19
20
 
20
- def model_post_init(self, __context: object) -> None:
21
- # Ensure only one of the fields is set
22
- if (
23
- sum(
24
- [
25
- int(bool(self.send_text)),
26
- int(bool(self.send_specials)),
27
- int(bool(self.send_ascii)),
28
- ]
29
- )
30
- != 1
31
- ):
32
- raise ValueError(
33
- "Exactly one of 'send_text', 'send_specials', or 'send_ascii' must be set"
34
- )
35
-
36
21
 
37
22
  class ReadImage(BaseModel):
38
23
  file_path: str
@@ -61,16 +46,9 @@ class FileEditFindReplace(BaseModel):
61
46
 
62
47
 
63
48
  class ResetShell(BaseModel):
64
- should_reset: Literal[True] = True
49
+ should_reset: Literal[True]
65
50
 
66
51
 
67
52
  class FileEdit(BaseModel):
68
53
  file_path: str
69
54
  file_edit_using_search_replace_blocks: str
70
-
71
- def model_post_init(self, __context: object) -> None:
72
- # Ensure first line is "<<<<<<< SEARCH"
73
-
74
- if not re.match(r"^<<<<<<+\s*SEARCH\s*$", self.file_edit_using_search_replace_blocks.split("\n")[0]):
75
-
76
- raise ValueError("First line of file_edit_using_search_replace_blocks must be '<<<<<<< SEARCH'")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes