wcgw 5.0.2__py3-none-any.whl → 5.1.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/bash_state/bash_state.py +2 -2
- wcgw/client/file_ops/diff_edit.py +14 -2
- wcgw/client/file_ops/extensions.py +137 -0
- wcgw/client/file_ops/search_replace.py +1 -2
- wcgw/client/mcp_server/server.py +10 -18
- wcgw/client/memory.py +4 -1
- wcgw/client/tool_prompts.py +16 -15
- wcgw/client/tools.py +95 -38
- {wcgw-5.0.2.dist-info → wcgw-5.1.0.dist-info}/METADATA +2 -1
- wcgw-5.1.0.dist-info/RECORD +37 -0
- wcgw_cli/anthropic_client.py +8 -4
- wcgw_cli/openai_client.py +7 -3
- mcp_wcgw/__init__.py +0 -114
- mcp_wcgw/client/__init__.py +0 -0
- mcp_wcgw/client/__main__.py +0 -79
- mcp_wcgw/client/session.py +0 -234
- mcp_wcgw/client/sse.py +0 -142
- mcp_wcgw/client/stdio.py +0 -128
- mcp_wcgw/py.typed +0 -0
- mcp_wcgw/server/__init__.py +0 -514
- mcp_wcgw/server/__main__.py +0 -50
- mcp_wcgw/server/models.py +0 -16
- mcp_wcgw/server/session.py +0 -288
- mcp_wcgw/server/sse.py +0 -178
- mcp_wcgw/server/stdio.py +0 -83
- mcp_wcgw/server/websocket.py +0 -61
- mcp_wcgw/shared/__init__.py +0 -0
- mcp_wcgw/shared/context.py +0 -14
- mcp_wcgw/shared/exceptions.py +0 -9
- mcp_wcgw/shared/memory.py +0 -87
- mcp_wcgw/shared/progress.py +0 -40
- mcp_wcgw/shared/session.py +0 -288
- mcp_wcgw/shared/version.py +0 -3
- mcp_wcgw/types.py +0 -1060
- wcgw-5.0.2.dist-info/RECORD +0 -58
- {wcgw-5.0.2.dist-info → wcgw-5.1.0.dist-info}/WHEEL +0 -0
- {wcgw-5.0.2.dist-info → wcgw-5.1.0.dist-info}/entry_points.txt +0 -0
- {wcgw-5.0.2.dist-info → wcgw-5.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
-
) ->
|
|
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
|
|
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
|
|
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
|
wcgw/client/mcp_server/server.py
CHANGED
|
@@ -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
|
|
7
|
-
import
|
|
8
|
-
from
|
|
9
|
-
from
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
wcgw/client/memory.py
CHANGED
|
@@ -64,7 +64,8 @@ T = TypeVar("T")
|
|
|
64
64
|
|
|
65
65
|
def load_memory(
|
|
66
66
|
task_id: str,
|
|
67
|
-
|
|
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:
|
wcgw/client/tool_prompts.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
-
|
|
3
|
-
from
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
]
|