wcgw 5.4.0__tar.gz → 5.4.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 (60) hide show
  1. {wcgw-5.4.0 → wcgw-5.4.2}/Dockerfile +3 -0
  2. {wcgw-5.4.0 → wcgw-5.4.2}/PKG-INFO +1 -1
  3. {wcgw-5.4.0 → wcgw-5.4.2}/pyproject.toml +1 -1
  4. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/diff-instructions.txt +1 -2
  5. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/mcp_server/__init__.py +15 -2
  6. wcgw-5.4.2/src/wcgw/client/schema_generator.py +63 -0
  7. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/tool_prompts.py +7 -6
  8. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/tools.py +8 -0
  9. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/types_.py +4 -2
  10. {wcgw-5.4.0 → wcgw-5.4.2}/uv.lock +1 -1
  11. {wcgw-5.4.0 → wcgw-5.4.2}/.github/workflows/python-publish.yml +0 -0
  12. {wcgw-5.4.0 → wcgw-5.4.2}/.github/workflows/python-tests.yml +0 -0
  13. {wcgw-5.4.0 → wcgw-5.4.2}/.github/workflows/python-types.yml +0 -0
  14. {wcgw-5.4.0 → wcgw-5.4.2}/.gitignore +0 -0
  15. {wcgw-5.4.0 → wcgw-5.4.2}/.gitmodules +0 -0
  16. {wcgw-5.4.0 → wcgw-5.4.2}/.python-version +0 -0
  17. {wcgw-5.4.0 → wcgw-5.4.2}/.vscode/settings.json +0 -0
  18. {wcgw-5.4.0 → wcgw-5.4.2}/CLAUDE.md +0 -0
  19. {wcgw-5.4.0 → wcgw-5.4.2}/LICENSE +0 -0
  20. {wcgw-5.4.0 → wcgw-5.4.2}/README.md +0 -0
  21. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/__init__.py +0 -0
  22. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/__init__.py +0 -0
  23. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/bash_state/bash_state.py +0 -0
  24. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/bash_state/parser/__init__.py +0 -0
  25. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/bash_state/parser/bash_statement_parser.py +0 -0
  26. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/common.py +0 -0
  27. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/encoder/__init__.py +0 -0
  28. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/file_ops/diff_edit.py +0 -0
  29. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/file_ops/extensions.py +0 -0
  30. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/file_ops/search_replace.py +0 -0
  31. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/mcp_server/Readme.md +0 -0
  32. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/mcp_server/server.py +0 -0
  33. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/memory.py +0 -0
  34. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/modes.py +0 -0
  35. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/display_tree.py +0 -0
  36. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/file_stats.py +0 -0
  37. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/path_prob.py +0 -0
  38. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/paths_model.vocab +0 -0
  39. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/paths_tokens.model +0 -0
  40. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/client/repo_ops/repo_context.py +0 -0
  41. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw/py.typed +0 -0
  42. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/__init__.py +0 -0
  43. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/__main__.py +0 -0
  44. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/anthropic_client.py +0 -0
  45. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/cli.py +0 -0
  46. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/openai_client.py +0 -0
  47. {wcgw-5.4.0 → wcgw-5.4.2}/src/wcgw_cli/openai_utils.py +0 -0
  48. {wcgw-5.4.0 → wcgw-5.4.2}/static/claude-ss.jpg +0 -0
  49. {wcgw-5.4.0 → wcgw-5.4.2}/static/computer-use.jpg +0 -0
  50. {wcgw-5.4.0 → wcgw-5.4.2}/static/example.jpg +0 -0
  51. {wcgw-5.4.0 → wcgw-5.4.2}/static/rocket-icon.png +0 -0
  52. {wcgw-5.4.0 → wcgw-5.4.2}/static/ss1.png +0 -0
  53. {wcgw-5.4.0 → wcgw-5.4.2}/static/workflow-demo.gif +0 -0
  54. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_bash_parser.py +0 -0
  55. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_bash_parser_complex.py +0 -0
  56. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_edit.py +0 -0
  57. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_file_range_tracking.py +0 -0
  58. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_mcp_server.py +0 -0
  59. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_readfiles.py +0 -0
  60. {wcgw-5.4.0 → wcgw-5.4.2}/tests/test_tools.py +0 -0
@@ -35,6 +35,9 @@ FROM python:3.12-slim-bookworm
35
35
 
36
36
  RUN apt-get update && apt-get install -y screen && rm -rf /var/lib/apt/lists/*
37
37
 
38
+ # Create app user and group
39
+ RUN groupadd -r app && useradd -r -g app app
40
+
38
41
  # Set the working directory in the container
39
42
  WORKDIR /workspace
40
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 5.4.0
3
+ Version: 5.4.2
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>
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  authors = [{ name = "Aman Rusia", email = "gapypi@arcfu.com" }]
3
3
  name = "wcgw"
4
- version = "5.4.0"
4
+ version = "5.4.2"
5
5
  description = "Shell and coding agent for Claude and other mcp clients"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11"
@@ -61,8 +61,7 @@ def call_hello_renamed():
61
61
  # *SEARCH/REPLACE block* Rules:
62
62
 
63
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.
64
+ - Use multiple search/replace blocks in a single FileWriteOrEdit tool call to edit in a single file in multiple places from top to bottom (separate calls are slower).
66
65
  - Including multiple unique *SEARCH/REPLACE* blocks if needed.
67
66
  - Include enough and only enough lines in each SEARCH section to uniquely match each set of lines that need to change.
68
67
  - Keep *SEARCH/REPLACE* blocks concise.
@@ -1,14 +1,27 @@
1
1
  # mypy: disable-error-code="import-untyped"
2
- from wcgw.client.mcp_server import server
3
2
  import asyncio
3
+ import importlib
4
+
5
+ import typer
4
6
  from typer import Typer
5
7
 
8
+ from wcgw.client.mcp_server import server
9
+
6
10
  main = Typer()
7
11
 
8
12
 
9
13
  @main.command()
10
- def app() -> None:
14
+ def app(
15
+ version: bool = typer.Option(
16
+ False, "--version", "-v", help="Show version and exit"
17
+ ),
18
+ ) -> None:
11
19
  """Main entry point for the package."""
20
+ if version:
21
+ version_ = importlib.metadata.version("wcgw")
22
+ print(f"wcgw version: {version_}")
23
+ raise typer.Exit()
24
+
12
25
  asyncio.run(server.main())
13
26
 
14
27
 
@@ -0,0 +1,63 @@
1
+ """
2
+ Custom JSON schema generator to remove title fields from Pydantic models.
3
+
4
+ This module provides utilities to remove auto-generated title fields from JSON schemas,
5
+ making them more suitable for tool schemas where titles are not needed.
6
+ """
7
+
8
+ import copy
9
+ from typing import Any, Dict
10
+
11
+
12
+ def recursive_purge_dict_key(d: Dict[str, Any], k: str) -> None:
13
+ """
14
+ Remove a key from a dictionary recursively, but only from JSON schema metadata.
15
+
16
+ This function removes the specified key from dictionaries that appear to be
17
+ JSON schema objects (have "type" or "$ref" or are property definitions).
18
+ This prevents removing legitimate data fields that happen to have the same name.
19
+
20
+ Args:
21
+ d: The dictionary to clean
22
+ k: The key to remove (typically "title")
23
+ """
24
+ if isinstance(d, dict):
25
+ # Only remove the key if this looks like a JSON schema object
26
+ # This includes objects with "type", "$ref", or if we're in a "properties" context
27
+ is_schema_object = (
28
+ "type" in d or
29
+ "$ref" in d or
30
+ any(schema_key in d for schema_key in ["properties", "items", "additionalProperties", "enum", "const", "anyOf", "allOf", "oneOf"])
31
+ )
32
+
33
+ if is_schema_object and k in d:
34
+ del d[k]
35
+
36
+ # Recursively process all values, regardless of key names
37
+ # This ensures we catch all nested structures
38
+ for key, value in d.items():
39
+ if isinstance(value, dict):
40
+ recursive_purge_dict_key(value, k)
41
+ elif isinstance(value, list):
42
+ for item in value:
43
+ if isinstance(item, dict):
44
+ recursive_purge_dict_key(item, k)
45
+
46
+
47
+ def remove_titles_from_schema(schema: Dict[str, Any]) -> Dict[str, Any]:
48
+ """
49
+ Remove all 'title' keys from a JSON schema dictionary.
50
+
51
+ This function creates a copy of the schema and removes all title keys
52
+ recursively, making it suitable for use with APIs that don't need titles.
53
+
54
+ Args:
55
+ schema: The JSON schema dictionary to clean
56
+
57
+ Returns:
58
+ A new dictionary with all title keys removed
59
+ """
60
+
61
+ schema_copy = copy.deepcopy(schema)
62
+ recursive_purge_dict_key(schema_copy, "title")
63
+ return schema_copy
@@ -10,6 +10,7 @@ from ..types_ import (
10
10
  ReadFiles,
11
11
  ReadImage,
12
12
  )
13
+ from .schema_generator import remove_titles_from_schema
13
14
 
14
15
  with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f:
15
16
  diffinstructions = f.read()
@@ -17,7 +18,7 @@ with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f
17
18
 
18
19
  TOOL_PROMPTS = [
19
20
  Tool(
20
- inputSchema=Initialize.model_json_schema(),
21
+ inputSchema=remove_titles_from_schema(Initialize.model_json_schema()),
21
22
  name="Initialize",
22
23
  description="""
23
24
  - Always call this at the start of the conversation before using any of the shell tools from wcgw.
@@ -34,7 +35,7 @@ TOOL_PROMPTS = [
34
35
  annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
35
36
  ),
36
37
  Tool(
37
- inputSchema=BashCommand.model_json_schema(),
38
+ inputSchema=remove_titles_from_schema(BashCommand.model_json_schema()),
38
39
  name="BashCommand",
39
40
  description="""
40
41
  - Execute a bash command. This is stateful (beware with subsequent calls).
@@ -51,7 +52,7 @@ TOOL_PROMPTS = [
51
52
  annotations=ToolAnnotations(destructiveHint=True, openWorldHint=True),
52
53
  ),
53
54
  Tool(
54
- inputSchema=ReadFiles.model_json_schema(),
55
+ inputSchema=remove_titles_from_schema(ReadFiles.model_json_schema()),
55
56
  name="ReadFiles",
56
57
  description="""
57
58
  - Read full file content of one or more files.
@@ -62,13 +63,13 @@ TOOL_PROMPTS = [
62
63
  annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
63
64
  ),
64
65
  Tool(
65
- inputSchema=ReadImage.model_json_schema(),
66
+ inputSchema=remove_titles_from_schema(ReadImage.model_json_schema()),
66
67
  name="ReadImage",
67
68
  description="Read an image from the shell.",
68
69
  annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
69
70
  ),
70
71
  Tool(
71
- inputSchema=FileWriteOrEdit.model_json_schema(),
72
+ inputSchema=remove_titles_from_schema(FileWriteOrEdit.model_json_schema()),
72
73
  name="FileWriteOrEdit",
73
74
  description="""
74
75
  - Writes or edits a file based on the percentage of changes.
@@ -84,7 +85,7 @@ TOOL_PROMPTS = [
84
85
  ),
85
86
  ),
86
87
  Tool(
87
- inputSchema=ContextSave.model_json_schema(),
88
+ inputSchema=remove_titles_from_schema(ContextSave.model_json_schema()),
88
89
  name="ContextSave",
89
90
  description="""
90
91
  Saves provided description and file contents of all the relevant file paths or globs in a single text file.
@@ -6,6 +6,7 @@ import mimetypes
6
6
  import os
7
7
  import subprocess
8
8
  import traceback
9
+ import uuid
9
10
  from dataclasses import dataclass
10
11
  from hashlib import sha256
11
12
  from os.path import expanduser
@@ -34,6 +35,7 @@ from ..client.bash_state.bash_state import (
34
35
  execute_bash,
35
36
  generate_thread_id,
36
37
  get_status,
38
+ get_tmpdir,
37
39
  )
38
40
  from ..client.repo_ops.file_stats import (
39
41
  FileStats,
@@ -153,6 +155,12 @@ def initialize(
153
155
  )
154
156
 
155
157
  folder_to_start = None
158
+ if type == "first_call" and not any_workspace_path:
159
+ tmp_dir = get_tmpdir()
160
+ any_workspace_path = os.path.join(
161
+ tmp_dir, "claude-playground-" + uuid.uuid4().hex[:4]
162
+ )
163
+
156
164
  if any_workspace_path:
157
165
  if os.path.exists(any_workspace_path):
158
166
  if os.path.isfile(any_workspace_path):
@@ -49,7 +49,9 @@ class Initialize(BaseModel):
49
49
  "reset_shell",
50
50
  "user_asked_change_workspace",
51
51
  ]
52
- any_workspace_path: str
52
+ any_workspace_path: str = Field(
53
+ description="Workspce to initialise in. Don't use ~ by default, instead use empty string"
54
+ )
53
55
  initial_files_to_read: list[str]
54
56
  task_id_to_resume: str
55
57
  mode_name: Literal["wcgw", "architect", "code_writer"]
@@ -87,7 +89,7 @@ class Command(BaseModel):
87
89
 
88
90
 
89
91
  class StatusCheck(BaseModel):
90
- status_check: Literal[True]
92
+ status_check: Literal[True] = True
91
93
  type: Literal["status_check"] = "status_check"
92
94
 
93
95
 
@@ -1398,7 +1398,7 @@ wheels = [
1398
1398
 
1399
1399
  [[package]]
1400
1400
  name = "wcgw"
1401
- version = "5.4.0"
1401
+ version = "5.4.2"
1402
1402
  source = { editable = "." }
1403
1403
  dependencies = [
1404
1404
  { name = "anthropic" },
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
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