wcgw 5.0.2__tar.gz → 5.1.1__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 (128) hide show
  1. {wcgw-5.0.2 → wcgw-5.1.1}/.github/workflows/python-tests.yml +1 -1
  2. {wcgw-5.0.2 → wcgw-5.1.1}/.github/workflows/python-types.yml +1 -1
  3. {wcgw-5.0.2 → wcgw-5.1.1}/Dockerfile +4 -12
  4. {wcgw-5.0.2 → wcgw-5.1.1}/PKG-INFO +6 -18
  5. {wcgw-5.0.2 → wcgw-5.1.1}/README.md +4 -17
  6. {wcgw-5.0.2 → wcgw-5.1.1}/pyproject.toml +3 -3
  7. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/bash_state/bash_state.py +2 -2
  8. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/file_ops/diff_edit.py +14 -2
  9. wcgw-5.1.1/src/wcgw/client/file_ops/extensions.py +137 -0
  10. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/file_ops/search_replace.py +1 -2
  11. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/mcp_server/server.py +10 -18
  12. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/memory.py +4 -1
  13. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/display_tree.py +4 -4
  14. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/tool_prompts.py +16 -15
  15. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/tools.py +95 -38
  16. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/anthropic_client.py +8 -4
  17. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/openai_client.py +7 -3
  18. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_edit.py +15 -15
  19. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_file_range_tracking.py +3 -2
  20. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_mcp_server.py +4 -4
  21. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_tools.py +82 -78
  22. wcgw-5.1.1/uv.lock +1503 -0
  23. wcgw-5.0.2/.gitmodules +0 -3
  24. wcgw-5.0.2/src/mcp_wcgw_fork/.git +0 -1
  25. wcgw-5.0.2/src/mcp_wcgw_fork/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  26. wcgw-5.0.2/src/mcp_wcgw_fork/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  27. wcgw-5.0.2/src/mcp_wcgw_fork/.github/workflows/main-checks.yml +0 -11
  28. wcgw-5.0.2/src/mcp_wcgw_fork/.github/workflows/publish-pypi.yml +0 -55
  29. wcgw-5.0.2/src/mcp_wcgw_fork/.github/workflows/pull-request-checks.yml +0 -8
  30. wcgw-5.0.2/src/mcp_wcgw_fork/.github/workflows/shared.yml +0 -69
  31. wcgw-5.0.2/src/mcp_wcgw_fork/.gitignore +0 -162
  32. wcgw-5.0.2/src/mcp_wcgw_fork/.python-version +0 -1
  33. wcgw-5.0.2/src/mcp_wcgw_fork/CODE_OF_CONDUCT.md +0 -128
  34. wcgw-5.0.2/src/mcp_wcgw_fork/CONTRIBUTING.md +0 -56
  35. wcgw-5.0.2/src/mcp_wcgw_fork/LICENSE +0 -21
  36. wcgw-5.0.2/src/mcp_wcgw_fork/README.md +0 -342
  37. wcgw-5.0.2/src/mcp_wcgw_fork/RELEASE.md +0 -15
  38. wcgw-5.0.2/src/mcp_wcgw_fork/SECURITY.md +0 -14
  39. wcgw-5.0.2/src/mcp_wcgw_fork/examples/README.md +0 -5
  40. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/.python-version +0 -1
  41. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/README.md +0 -55
  42. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/__init__.py +0 -1
  43. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/__main__.py +0 -5
  44. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/server.py +0 -130
  45. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-prompt/pyproject.toml +0 -47
  46. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/.python-version +0 -1
  47. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/README.md +0 -48
  48. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/__init__.py +0 -1
  49. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/__main__.py +0 -5
  50. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/server.py +0 -86
  51. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-resource/pyproject.toml +0 -47
  52. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/.python-version +0 -1
  53. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/README.md +0 -48
  54. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/__init__.py +0 -1
  55. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/__main__.py +0 -5
  56. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/server.py +0 -100
  57. wcgw-5.0.2/src/mcp_wcgw_fork/examples/servers/simple-tool/pyproject.toml +0 -47
  58. wcgw-5.0.2/src/mcp_wcgw_fork/pyproject.toml +0 -75
  59. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/__init__.py +0 -114
  60. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/client/__main__.py +0 -79
  61. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/client/session.py +0 -234
  62. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/client/sse.py +0 -142
  63. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/client/stdio.py +0 -128
  64. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/__init__.py +0 -514
  65. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/__main__.py +0 -50
  66. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/models.py +0 -16
  67. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/session.py +0 -288
  68. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/sse.py +0 -178
  69. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/stdio.py +0 -83
  70. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/server/websocket.py +0 -61
  71. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/context.py +0 -14
  72. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/exceptions.py +0 -9
  73. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/memory.py +0 -87
  74. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/progress.py +0 -40
  75. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/session.py +0 -288
  76. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared/version.py +0 -3
  77. wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/types.py +0 -1060
  78. wcgw-5.0.2/src/mcp_wcgw_fork/tests/__init__.py +0 -0
  79. wcgw-5.0.2/src/mcp_wcgw_fork/tests/client/__init__.py +0 -0
  80. wcgw-5.0.2/src/mcp_wcgw_fork/tests/client/test_session.py +0 -98
  81. wcgw-5.0.2/src/mcp_wcgw_fork/tests/client/test_stdio.py +0 -38
  82. wcgw-5.0.2/src/mcp_wcgw_fork/tests/conftest.py +0 -29
  83. wcgw-5.0.2/src/mcp_wcgw_fork/tests/server/__init__.py +0 -0
  84. wcgw-5.0.2/src/mcp_wcgw_fork/tests/server/test_session.py +0 -100
  85. wcgw-5.0.2/src/mcp_wcgw_fork/tests/server/test_stdio.py +0 -68
  86. wcgw-5.0.2/src/mcp_wcgw_fork/tests/shared/test_memory.py +0 -28
  87. wcgw-5.0.2/src/mcp_wcgw_fork/tests/test_types.py +0 -29
  88. wcgw-5.0.2/src/mcp_wcgw_fork/uv.lock +0 -621
  89. wcgw-5.0.2/src/wcgw/client/__init__.py +0 -0
  90. wcgw-5.0.2/src/wcgw/py.typed +0 -0
  91. wcgw-5.0.2/uv.lock +0 -1379
  92. {wcgw-5.0.2 → wcgw-5.1.1}/.github/workflows/python-publish.yml +0 -0
  93. {wcgw-5.0.2 → wcgw-5.1.1}/.gitignore +0 -0
  94. /wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/client/__init__.py → /wcgw-5.1.1/.gitmodules +0 -0
  95. {wcgw-5.0.2 → wcgw-5.1.1}/.python-version +0 -0
  96. {wcgw-5.0.2 → wcgw-5.1.1}/.vscode/settings.json +0 -0
  97. {wcgw-5.0.2 → wcgw-5.1.1}/CLAUDE.md +0 -0
  98. {wcgw-5.0.2 → wcgw-5.1.1}/LICENSE +0 -0
  99. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/__init__.py +0 -0
  100. {wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw/shared → wcgw-5.1.1/src/wcgw/client}/__init__.py +0 -0
  101. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/bash_state/parser/__init__.py +0 -0
  102. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/bash_state/parser/bash_statement_parser.py +0 -0
  103. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/common.py +0 -0
  104. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/diff-instructions.txt +0 -0
  105. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/encoder/__init__.py +0 -0
  106. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/mcp_server/Readme.md +0 -0
  107. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/mcp_server/__init__.py +0 -0
  108. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/modes.py +0 -0
  109. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/file_stats.py +0 -0
  110. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/path_prob.py +0 -0
  111. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/paths_model.vocab +0 -0
  112. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/paths_tokens.model +0 -0
  113. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/client/repo_ops/repo_context.py +0 -0
  114. {wcgw-5.0.2/src/mcp_wcgw_fork/src/mcp_wcgw → wcgw-5.1.1/src/wcgw}/py.typed +0 -0
  115. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw/types_.py +0 -0
  116. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/__init__.py +0 -0
  117. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/__main__.py +0 -0
  118. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/cli.py +0 -0
  119. {wcgw-5.0.2 → wcgw-5.1.1}/src/wcgw_cli/openai_utils.py +0 -0
  120. {wcgw-5.0.2 → wcgw-5.1.1}/static/claude-ss.jpg +0 -0
  121. {wcgw-5.0.2 → wcgw-5.1.1}/static/computer-use.jpg +0 -0
  122. {wcgw-5.0.2 → wcgw-5.1.1}/static/example.jpg +0 -0
  123. {wcgw-5.0.2 → wcgw-5.1.1}/static/rocket-icon.png +0 -0
  124. {wcgw-5.0.2 → wcgw-5.1.1}/static/ss1.png +0 -0
  125. {wcgw-5.0.2 → wcgw-5.1.1}/static/workflow-demo.gif +0 -0
  126. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_bash_parser.py +0 -0
  127. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_bash_parser_complex.py +0 -0
  128. {wcgw-5.0.2 → wcgw-5.1.1}/tests/test_readfiles.py +0 -0
@@ -27,7 +27,7 @@ jobs:
27
27
  pip install uv
28
28
  - name: Run tests with coverage
29
29
  run: |
30
- uv run --group tests --python "${{ matrix.python-version }}" pytest --cov=wcgw --cov-report=xml --cov-report=term-missing
30
+ uv run --frozen --group tests --python "${{ matrix.python-version }}" pytest --cov=wcgw --cov-report=xml --cov-report=term-missing
31
31
  - name: Upload coverage reports to Codecov
32
32
  uses: codecov/codecov-action@v4
33
33
  if: success()
@@ -27,4 +27,4 @@ jobs:
27
27
  pip install uv
28
28
  - name: Run type checks
29
29
  run: |
30
- uv run --group types --python "${{ matrix.python-version }}" mypy --strict src/wcgw
30
+ uv run --frozen --group types --python "${{ matrix.python-version }}" mypy --strict src/wcgw
@@ -14,8 +14,8 @@ COPY README.md /app/
14
14
  ENV UV_COMPILE_BYTECODE=1
15
15
  ENV UV_LINK_MODE=copy
16
16
 
17
- # Install dependencies including git
18
- RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
17
+ # No need for git as we don't need to clone submodules anymore
18
+ RUN apt-get update && rm -rf /var/lib/apt/lists/*
19
19
 
20
20
  # Install dependencies
21
21
  RUN --mount=type=cache,target=/root/.cache/uv \
@@ -24,14 +24,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
24
24
  # Copy the entire project into the container
25
25
  COPY src /app/src
26
26
 
27
- # Check if src/mcp_wcgw_fork is empty and clone the repository if needed
28
- RUN if [ ! -d "/app/src/mcp_wcgw_fork" ] || [ -z "$(ls -A /app/src/mcp_wcgw_fork)" ]; then \
29
- mkdir -p /app/src/mcp_wcgw_fork && \
30
- git clone https://github.com/rusiaaman/python-sdk.git /app/src/mcp_wcgw_fork && \
31
- echo "Repository cloned successfully"; \
32
- else \
33
- echo "src/mcp_wcgw_fork already exists and is not empty"; \
34
- fi
27
+ # No need to clone the submodule as it has been removed
35
28
 
36
29
  # Install the project
37
30
  RUN --mount=type=cache,target=/root/.cache/uv \
@@ -47,8 +40,7 @@ WORKDIR /workspace
47
40
 
48
41
  # Copy the installed application from the previous stage
49
42
  COPY --from=uv --chown=app:app /app/.venv /app/.venv
50
- # Copy the cloned repository if it exists
51
- COPY --from=uv --chown=app:app /app/src/mcp_wcgw_fork /app/src/mcp_wcgw_fork
43
+ # No need to copy the submodule as it has been removed
52
44
 
53
45
  # Add the virtual environment to the PATH
54
46
  ENV PATH="/app/.venv/bin:$PATH"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 5.0.2
3
+ Version: 5.1.1
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>
@@ -8,6 +8,7 @@ License-File: LICENSE
8
8
  Requires-Python: >=3.11
9
9
  Requires-Dist: anthropic>=0.39.0
10
10
  Requires-Dist: fastapi>=0.115.0
11
+ Requires-Dist: mcp>=1.7.0
11
12
  Requires-Dist: openai>=1.46.0
12
13
  Requires-Dist: petname>=2.6
13
14
  Requires-Dist: pexpect>=4.9.0
@@ -117,13 +118,7 @@ Then create or update `claude_desktop_config.json` (~/Library/Application Suppor
117
118
  "mcpServers": {
118
119
  "wcgw": {
119
120
  "command": "uv",
120
- "args": [
121
- "tool",
122
- "run",
123
- "--python",
124
- "3.12",
125
- "wcgw@latest"
126
- ]
121
+ "args": ["tool", "run", "--python", "3.12", "wcgw@latest"]
127
122
  }
128
123
  }
129
124
  }
@@ -152,14 +147,7 @@ Then add or update the claude config file `%APPDATA%\Claude\claude_desktop_confi
152
147
  "mcpServers": {
153
148
  "wcgw": {
154
149
  "command": "wsl.exe",
155
- "args": [
156
- "uv",
157
- "tool",
158
- "run",
159
- "--python",
160
- "3.12",
161
- "wcgw@latest"
162
- ]
150
+ "args": ["uv", "tool", "run", "--python", "3.12", "wcgw@latest"]
163
151
  }
164
152
  }
165
153
  }
@@ -231,7 +219,7 @@ Then you can update `/Users/username/Library/Application Support/Claude/claude_d
231
219
  ```
232
220
  {
233
221
  "mcpServers": {
234
- "filesystem": {
222
+ "wcgw": {
235
223
  "command": "docker",
236
224
  "args": [
237
225
  "run",
@@ -239,7 +227,7 @@ Then you can update `/Users/username/Library/Application Support/Claude/claude_d
239
227
  "--rm",
240
228
  "--mount",
241
229
  "type=bind,src=/Users/username/Desktop,dst=/workspace/Desktop",
242
- "wcgw",
230
+ "wcgw"
243
231
  ]
244
232
  }
245
233
  }
@@ -87,13 +87,7 @@ Then create or update `claude_desktop_config.json` (~/Library/Application Suppor
87
87
  "mcpServers": {
88
88
  "wcgw": {
89
89
  "command": "uv",
90
- "args": [
91
- "tool",
92
- "run",
93
- "--python",
94
- "3.12",
95
- "wcgw@latest"
96
- ]
90
+ "args": ["tool", "run", "--python", "3.12", "wcgw@latest"]
97
91
  }
98
92
  }
99
93
  }
@@ -122,14 +116,7 @@ Then add or update the claude config file `%APPDATA%\Claude\claude_desktop_confi
122
116
  "mcpServers": {
123
117
  "wcgw": {
124
118
  "command": "wsl.exe",
125
- "args": [
126
- "uv",
127
- "tool",
128
- "run",
129
- "--python",
130
- "3.12",
131
- "wcgw@latest"
132
- ]
119
+ "args": ["uv", "tool", "run", "--python", "3.12", "wcgw@latest"]
133
120
  }
134
121
  }
135
122
  }
@@ -201,7 +188,7 @@ Then you can update `/Users/username/Library/Application Support/Claude/claude_d
201
188
  ```
202
189
  {
203
190
  "mcpServers": {
204
- "filesystem": {
191
+ "wcgw": {
205
192
  "command": "docker",
206
193
  "args": [
207
194
  "run",
@@ -209,7 +196,7 @@ Then you can update `/Users/username/Library/Application Support/Claude/claude_d
209
196
  "--rm",
210
197
  "--mount",
211
198
  "type=bind,src=/Users/username/Desktop,dst=/workspace/Desktop",
212
- "wcgw",
199
+ "wcgw"
213
200
  ]
214
201
  }
215
202
  }
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  authors = [{ name = "Aman Rusia", email = "gapypi@arcfu.com" }]
3
3
  name = "wcgw"
4
- version = "5.0.2"
4
+ version = "5.1.1"
5
5
  description = "Shell and coding agent for Claude and other mcp clients"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11"
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "psutil>=7.0.0",
27
27
  "tree-sitter>=0.24.0",
28
28
  "tree-sitter-bash>=0.23.3",
29
+ "mcp>=1.7.0",
29
30
  ]
30
31
 
31
32
  [project.urls]
@@ -36,10 +37,9 @@ requires = ["hatchling"]
36
37
  build-backend = "hatchling.build"
37
38
 
38
39
  [tool.hatch.build.targets.wheel]
39
- packages = ["src/wcgw", "src/mcp_wcgw_fork/src/mcp_wcgw", "src/wcgw_cli"]
40
+ packages = ["src/wcgw", "src/wcgw_cli"]
40
41
 
41
42
  [tool.hatch.build.targets.wheel.sources]
42
- "src/mcp_wcgw_fork/src/mcp_wcgw" = "mcp_wcgw"
43
43
  "src/wcgw" = "wcgw"
44
44
  "src/wcgw_cli" = "wcgw_cli"
45
45
 
@@ -996,7 +996,7 @@ def execute_bash(
996
996
  bash_state: BashState,
997
997
  enc: EncoderDecoder[int],
998
998
  bash_arg: BashCommand,
999
- max_tokens: Optional[int],
999
+ max_tokens: Optional[int], # This will be noncoding_max_tokens
1000
1000
  timeout_s: Optional[float],
1001
1001
  ) -> tuple[str, float]:
1002
1002
  try:
@@ -1026,7 +1026,7 @@ def _execute_bash(
1026
1026
  bash_state: BashState,
1027
1027
  enc: EncoderDecoder[int],
1028
1028
  bash_arg: BashCommand,
1029
- max_tokens: Optional[int],
1029
+ max_tokens: Optional[int], # This will be noncoding_max_tokens
1030
1030
  timeout_s: Optional[float],
1031
1031
  ) -> tuple[str, float]:
1032
1032
  try:
@@ -46,10 +46,13 @@ class FileEditOutput:
46
46
  last_idx = 0
47
47
  errors = []
48
48
  warnings = set[str]()
49
+ info = set[str]()
50
+ score = 0.0
49
51
  for (span, tolerances, replace_with), search_ in zip(
50
52
  self.edited_with_tolerances, self.orig_search_blocks
51
53
  ):
52
54
  for tol in tolerances:
55
+ score += tol.count * tol.score_multiplier
53
56
  if tol.count > 0:
54
57
  if tol.severity_cat == "WARNING":
55
58
  warnings.add(tol.error_name)
@@ -66,6 +69,8 @@ Error:
66
69
  {tol.error_name}
67
70
  ---
68
71
  """)
72
+ else:
73
+ info.add(tol.error_name)
69
74
  if len(errors) >= max_errors:
70
75
  raise SearchReplaceMatchError("\n".join(errors))
71
76
  if last_idx < span.start:
@@ -80,12 +85,19 @@ Error:
80
85
  if errors:
81
86
  raise SearchReplaceMatchError("\n".join(errors))
82
87
 
88
+ if score > 1000:
89
+ display = (list(warnings) + list(info))[:max_errors]
90
+ raise SearchReplaceMatchError(
91
+ "Too many warnings generated, not apply the edits\n"
92
+ + "\n".join(display)
93
+ )
94
+
83
95
  return new_lines, set(warnings)
84
96
 
85
97
  @staticmethod
86
98
  def get_best_match(
87
99
  outputs: list["FileEditOutput"],
88
- ) -> tuple[list["FileEditOutput"], bool]:
100
+ ) -> list["FileEditOutput"]:
89
101
  best_hits: list[FileEditOutput] = []
90
102
  best_score = float("-inf")
91
103
  assert outputs
@@ -103,7 +115,7 @@ Error:
103
115
  best_score = hit_score
104
116
  elif abs(hit_score - best_score) < 1e-3:
105
117
  best_hits.append(output)
106
- return best_hits, best_score > 1000
118
+ return best_hits
107
119
 
108
120
 
109
121
  def line_process_max_space_tolerance(line: str) -> str:
@@ -0,0 +1,137 @@
1
+ """
2
+ File with definitions of known source code file extensions.
3
+ Used to determine the appropriate context length for files.
4
+ Supports selecting between coding_max_tokens and noncoding_max_tokens
5
+ based on file extensions.
6
+ """
7
+ from typing import Dict, Optional, Set
8
+
9
+ # Set of file extensions considered to be source code
10
+ # Each extension should be listed without the dot (e.g., 'py' not '.py')
11
+ SOURCE_CODE_EXTENSIONS: Set[str] = {
12
+ # Python
13
+ 'py', 'pyx', 'pyi', 'pyw',
14
+
15
+ # JavaScript and TypeScript
16
+ 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs',
17
+
18
+ # Web
19
+ 'html', 'htm', 'xhtml', 'css', 'scss', 'sass', 'less',
20
+
21
+ # C and C++
22
+ 'c', 'h', 'cpp', 'cxx', 'cc', 'hpp', 'hxx', 'hh', 'inl',
23
+
24
+ # C#
25
+ 'cs', 'csx',
26
+
27
+ # Java
28
+ 'java', 'scala', 'kt', 'kts', 'groovy',
29
+
30
+ # Go
31
+ 'go', 'mod',
32
+
33
+ # Rust
34
+ 'rs', 'rlib',
35
+
36
+ # Swift
37
+ 'swift',
38
+
39
+ # Ruby
40
+ 'rb', 'rake', 'gemspec',
41
+
42
+ # PHP
43
+ 'php', 'phtml', 'phar', 'phps',
44
+
45
+ # Shell
46
+ 'sh', 'bash', 'zsh', 'fish',
47
+
48
+ # PowerShell
49
+ 'ps1', 'psm1', 'psd1',
50
+
51
+ # SQL
52
+ 'sql', 'ddl', 'dml',
53
+
54
+ # Markup and config
55
+ 'xml', 'json', 'yaml', 'yml', 'toml', 'ini', 'cfg', 'conf',
56
+
57
+ # Documentation
58
+ 'md', 'markdown', 'rst', 'adoc', 'tex',
59
+
60
+ # Build and dependency files
61
+ 'Makefile', 'Dockerfile', 'Jenkinsfile',
62
+
63
+ # Haskell
64
+ 'hs', 'lhs',
65
+
66
+ # Lisp family
67
+ 'lisp', 'cl', 'el', 'clj', 'cljs', 'edn', 'scm',
68
+
69
+ # Erlang and Elixir
70
+ 'erl', 'hrl', 'ex', 'exs',
71
+
72
+ # Dart and Flutter
73
+ 'dart',
74
+
75
+ # Objective-C
76
+ 'm', 'mm',
77
+ }
78
+
79
+ # Context length limits based on file type (in tokens)
80
+ CONTEXT_LENGTH_LIMITS: Dict[str, int] = {
81
+ 'source_code': 24000, # For known source code files
82
+ 'default': 8000, # For all other files
83
+ }
84
+
85
+ def is_source_code_file(filename: str) -> bool:
86
+ """
87
+ Determine if a file is a source code file based on its extension.
88
+
89
+ Args:
90
+ filename: The name of the file to check
91
+
92
+ Returns:
93
+ True if the file has a recognized source code extension, False otherwise
94
+ """
95
+ # Extract extension (without the dot)
96
+ parts = filename.split('.')
97
+ if len(parts) > 1:
98
+ ext = parts[-1].lower()
99
+ return ext in SOURCE_CODE_EXTENSIONS
100
+
101
+ # Files without extensions (like 'Makefile', 'Dockerfile')
102
+ # Case-insensitive match for files without extensions
103
+ return filename.lower() in {ext.lower() for ext in SOURCE_CODE_EXTENSIONS}
104
+
105
+ def get_context_length_for_file(filename: str) -> int:
106
+ """
107
+ Get the appropriate context length limit for a file based on its extension.
108
+
109
+ Args:
110
+ filename: The name of the file to check
111
+
112
+ Returns:
113
+ The context length limit in tokens
114
+ """
115
+ if is_source_code_file(filename):
116
+ return CONTEXT_LENGTH_LIMITS['source_code']
117
+ return CONTEXT_LENGTH_LIMITS['default']
118
+
119
+
120
+ def select_max_tokens(filename: str, coding_max_tokens: Optional[int], noncoding_max_tokens: Optional[int]) -> Optional[int]:
121
+ """
122
+ Select the appropriate max_tokens limit based on file type.
123
+
124
+ Args:
125
+ filename: The name of the file to check
126
+ coding_max_tokens: Maximum tokens for source code files
127
+ noncoding_max_tokens: Maximum tokens for non-source code files
128
+
129
+ Returns:
130
+ The appropriate max_tokens limit for the file
131
+ """
132
+ if coding_max_tokens is None and noncoding_max_tokens is None:
133
+ return None
134
+
135
+ if is_source_code_file(filename):
136
+ return coding_max_tokens
137
+ return noncoding_max_tokens
@@ -155,7 +155,7 @@ def edit_with_individual_fallback(
155
155
  original_lines: list[str], search_replace_blocks: list[tuple[list[str], list[str]]]
156
156
  ) -> tuple[list[str], set[str]]:
157
157
  outputs = FileEditInput(original_lines, 0, search_replace_blocks, 0).edit_file()
158
- best_matches, is_error = FileEditOutput.get_best_match(outputs)
158
+ best_matches = FileEditOutput.get_best_match(outputs)
159
159
 
160
160
  try:
161
161
  edited_content, comments_ = best_matches[0].replace_or_throw(3)
@@ -171,7 +171,6 @@ def edit_with_individual_fallback(
171
171
  all_comments |= comments_
172
172
  return running_lines, all_comments
173
173
  raise
174
- assert not is_error
175
174
 
176
175
  if len(best_matches) > 1:
177
176
  # Find the first block that differs across matches
@@ -1,13 +1,12 @@
1
1
  import importlib
2
2
  import logging
3
3
  import os
4
- from typing import Any
4
+ from typing import Any, Optional
5
5
 
6
- import mcp_wcgw.server.stdio
7
- import mcp_wcgw.types as types
8
- from mcp_wcgw.server import NotificationOptions, Server
9
- from mcp_wcgw.server.models import InitializationOptions
10
- from mcp_wcgw.types import Tool as ToolParam
6
+ import mcp.server.stdio
7
+ import mcp.types as types
8
+ from mcp.server import NotificationOptions, Server
9
+ from mcp.server.models import InitializationOptions
11
10
  from pydantic import AnyUrl
12
11
 
13
12
  from wcgw.client.modes import KTS
@@ -25,7 +24,7 @@ from ..tools import (
25
24
  which_tool_name,
26
25
  )
27
26
 
28
- server = Server("wcgw")
27
+ server: Server[Any] = Server("wcgw")
29
28
 
30
29
  # Log only time stamp
31
30
  logging.basicConfig(level=logging.INFO, format="%(asctime)s: %(message)s")
@@ -89,15 +88,7 @@ async def handle_list_tools() -> list[types.Tool]:
89
88
  Each tool specifies its arguments using JSON Schema validation.
90
89
  """
91
90
 
92
- tools_ = [
93
- ToolParam(
94
- inputSchema=tool.inputSchema,
95
- name=tool.name,
96
- description=tool.description,
97
- )
98
- for tool in TOOL_PROMPTS
99
- ]
100
- return tools_
91
+ return TOOL_PROMPTS
101
92
 
102
93
 
103
94
  @server.call_tool() # type: ignore
@@ -119,7 +110,8 @@ async def handle_call_tool(
119
110
  default_enc,
120
111
  0.0,
121
112
  lambda x, y: ("", 0),
122
- 8000,
113
+ 24000, # coding_max_tokens
114
+ 8000, # noncoding_max_tokens
123
115
  )
124
116
 
125
117
  except Exception as e:
@@ -165,7 +157,7 @@ async def main() -> None:
165
157
  ) as BASH_STATE:
166
158
  BASH_STATE.console.log("wcgw version: " + version)
167
159
  # Run the server using stdin/stdout streams
168
- async with mcp_wcgw.server.stdio.stdio_server() as (read_stream, write_stream):
160
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
169
161
  await server.run(
170
162
  read_stream,
171
163
  write_stream,
@@ -64,7 +64,8 @@ T = TypeVar("T")
64
64
 
65
65
  def load_memory(
66
66
  task_id: str,
67
- max_tokens: Optional[int],
67
+ coding_max_tokens: Optional[int],
68
+ noncoding_max_tokens: Optional[int],
68
69
  encoder: Callable[[str], list[T]],
69
70
  decoder: Callable[[list[T]], str],
70
71
  ) -> tuple[str, str, Optional[dict[str, Any]]]:
@@ -75,6 +76,8 @@ def load_memory(
75
76
  with open(memory_file, "r") as f:
76
77
  data = f.read()
77
78
 
79
+ # Memory files are considered non-code files for token limits
80
+ max_tokens = noncoding_max_tokens
78
81
  if max_tokens:
79
82
  toks = encoder(data)
80
83
  if len(toks) > max_tokens:
@@ -15,7 +15,7 @@ class DirectoryTree:
15
15
  self.root = root
16
16
  self.max_files = max_files
17
17
  self.expanded_files: Set[Path] = set()
18
- self.expanded_dirs = set[Path]()
18
+ self.expanded_dirs: Set[Path] = set()
19
19
 
20
20
  if not self.root.exists():
21
21
  raise ValueError(f"Root path {root} does not exist")
@@ -77,11 +77,11 @@ class DirectoryTree:
77
77
  def _display_recursive(
78
78
  current_path: Path, indent: int = 0, depth: int = 0
79
79
  ) -> None:
80
- # Print current directory name
80
+ # Print current directory name with a trailing slash for directories
81
81
  if current_path == self.root:
82
- writer.write(f"{current_path}\n")
82
+ writer.write(f"{current_path}/\n")
83
83
  else:
84
- writer.write(f"{' ' * indent}{current_path.name}\n")
84
+ writer.write(f"{' ' * indent}{current_path.name}/\n")
85
85
 
86
86
  # Don't recurse beyond depth 1 unless path contains expanded files
87
87
  if depth > 0 and current_path not in self.expanded_dirs:
@@ -1,6 +1,6 @@
1
1
  import os
2
- from dataclasses import dataclass
3
- from typing import Any
2
+
3
+ from mcp.types import Tool, ToolAnnotations
4
4
 
5
5
  from ..types_ import (
6
6
  BashCommand,
@@ -15,15 +15,8 @@ with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f
15
15
  diffinstructions = f.read()
16
16
 
17
17
 
18
- @dataclass
19
- class Prompts:
20
- inputSchema: dict[str, Any]
21
- name: str
22
- description: str
23
-
24
-
25
18
  TOOL_PROMPTS = [
26
- Prompts(
19
+ Tool(
27
20
  inputSchema=Initialize.model_json_schema(),
28
21
  name="Initialize",
29
22
  description="""
@@ -38,8 +31,9 @@ TOOL_PROMPTS = [
38
31
  - Use type="reset_shell" if in a conversation shell is not working after multiple tries.
39
32
  - Use type="user_asked_change_workspace" if in a conversation user asked to change workspace
40
33
  """,
34
+ annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
41
35
  ),
42
- Prompts(
36
+ Tool(
43
37
  inputSchema=BashCommand.model_json_schema(),
44
38
  name="BashCommand",
45
39
  description="""
@@ -54,8 +48,9 @@ TOOL_PROMPTS = [
54
48
  - Programs don't hang easily, so most likely explanation for no output is usually that the program is still running, and you need to check status again.
55
49
  - Do not send Ctrl-c before checking for status till 10 minutes or whatever is appropriate for the program to finish.
56
50
  """,
51
+ annotations=ToolAnnotations(destructiveHint=True, openWorldHint=True),
57
52
  ),
58
- Prompts(
53
+ Tool(
59
54
  inputSchema=ReadFiles.model_json_schema(),
60
55
  name="ReadFiles",
61
56
  description="""
@@ -65,13 +60,15 @@ TOOL_PROMPTS = [
65
60
  - You may populate "show_line_numbers_reason" with your reason, by default null/empty means no line numbers are shown.
66
61
  - 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`
67
62
  """,
63
+ annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
68
64
  ),
69
- Prompts(
65
+ Tool(
70
66
  inputSchema=ReadImage.model_json_schema(),
71
67
  name="ReadImage",
72
68
  description="Read an image from the shell.",
69
+ annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
73
70
  ),
74
- Prompts(
71
+ Tool(
75
72
  inputSchema=FileWriteOrEdit.model_json_schema(),
76
73
  name="FileWriteOrEdit",
77
74
  description="""
@@ -85,13 +82,17 @@ TOOL_PROMPTS = [
85
82
 
86
83
  """
87
84
  + diffinstructions,
85
+ annotations=ToolAnnotations(
86
+ destructiveHint=True, idempotentHint=True, openWorldHint=False
87
+ ),
88
88
  ),
89
- Prompts(
89
+ Tool(
90
90
  inputSchema=ContextSave.model_json_schema(),
91
91
  name="ContextSave",
92
92
  description="""
93
93
  Saves provided description and file contents of all the relevant file paths or globs in a single text file.
94
94
  - Provide random 3 word unqiue id or whatever user provided.
95
95
  - Leave project path as empty string if no project path""",
96
+ annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=False),
96
97
  ),
97
98
  ]