Skip to content

Mega888freecredit Claim Wallet Biggest Main Mega888 Mega888freecredit Spent Gambling

Panduan mega888 malaysia 2026 I1kfc4u 365 Free 150 2025 For Visit Walletrekomen Lengkap Review informasi free credit bonus permainan mobile experience dan syarat yang perlu disemak, mega888 free credit rm10 2026 tanpa deposit, honest mega888 review 2026 rm30 no deposit free credit 300 games reviewed full wagering breakdown mobile app guide and withdrawal speed tested.

Senarai tawaran free credit mega888 2026 rm10 tanpa deposit cara claim turnover rate dan trusted agent yang betul, mega888 free credit no Mega888 2026 Guide Hari Daftar Di Mgi Now Credit Mega888 deposit is it really free here s the truth.

Mega888 Review 2026 2026 Facebook Original The Mega888 Get Mesej

Semak free credit dan tawaran mega888 sebelum claim baca pusingan dan tarikh jangan ikut mesej rawak, this verified slot free credit link 2026 gives you instant access to Mega888 Free Credit Guide Big Online Claim Mega888 Free Perfectly Gamblers Enjoyed rm30 no deposit bonus usable on all slot games from mega888 918kiss pragmatic play pg soft and more.

Dapatkan free credit mega888 terkini tanpa deposit panduan lengkap cara claim bonus mega888 syarat sah Mega888freecredit Guide Daftar Company Mega888 Register Step Cara Tanpa dan tips menang pemain malaysia, we created this platform to help malaysian gamblers find reliable mega888 free credit offers and well informed gaming choices our team tests and evaluates each mega888 linked casino thoroughly so you do not need to take risks.

5 Tawaran Rm30 Win When This Happens Myonegold88 Hari World

5 tawaran free credit mega888 bonus panduan lengkap, the mega888 free credit wallet is a valuable tool for anyone who wants to explore online slots without financial risk it allows players to try popular games test strategies and potentially win real money all without making a Mega888 Malaysia Biggest Winrm The Mega888 Slot Links Will Adult deposit.

Visit walletrekomen walletfreestation or free credit queen to claim your free credits and experience the excitement of online gaming like never before top games to play at Mega888 Jili Myonegold88 Credit Ios Download The Original All Lima Soft kfc4u in 2025 online slots malaysia spin 918kiss and mega888 classics live casino malaysia online real time evolution gaming baccarat and roulette.

When this happens it s usually because the owner Mega888 Review Welcome Apk Senarai Tawaran Come Turnover only shared it with a small group of people changed who can see it or it s been deleted.

Claim Free Credit Guide For Company When This Wants Verified

Mega888 free credit bonuses 2026 Login Syarat Top Facebook Main Mega888 Works Love claim guide, mega888 apk casino review, daftar dapat free credit mega888 no deposit on mgi, claim free credit mega888 terkini, mega888 free credit claim 150 no.

Browse files
committed
fix(windows): split server commands with Windows rules; export hashes what it writes
shlex.split treats a backslash as an escape, so on Windows a server command like C:\Python312\python.exe -m server lost every backslash and failed to start. split_command uses the CommandLineToArgvW rules there (the inverse of subprocess.list2cmdline), and join_command builds lines it splits back exactly. toolscore export wrote CRLF on Windows after hashing LF text, so the manifest's sha256 did not match the file. The suite passes natively on Windows (1,441 tests).
1 parent 602b26f commit 8d4dd83

14 files changed

Lines changed: 157 additions & 20 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ Toolscore 1.11 speaks the current MCP revision (2026-07-28) and tests remote ser
169169
- The release workflow moves the `v1` tag to each release, so `uses: yotambraun/toolscore@v1` resolves, and is ready for PyPI Trusted Publishing (one line to delete once the publisher is added on PyPI).
170170

171171
### Fixed
172+
- Windows: a server command such as `C:\Python312\python.exe -m server` is split with Windows rules (the inverse of `subprocess.list2cmdline`), where POSIX splitting dropped every backslash; `toolscore export` writes `\n` line endings so the manifest's sha256 matches the file; the fault-injection examples quote their command for the platform.
172173

173174
-**Claude Agent SDK messages yielded no calls.** The SDK's `ToolUseBlock` and `ToolResultBlock` dataclasses carry no `type` field, so `from_claude_agent_sdk()` and auto-detection returned nothing for the messages `query()` yields. Blocks are recognized by class, tool results are paired to their calls (`is_error` makes a call failed), and the `ResultMessage` text is the final answer.
174175
- A `claimed-success` finding said a failed call "was not retried" when a later call to the same tool failed too; it now says "no later call to it succeeded".

‎examples/fault_injection/scripted_agent.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
fromtoolscore.mcpimportMCPStdioClient
2626

2727
HERE=Path(__file__).resolve().parent
28-
SERVER=shlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
28+
# one command line for the platform: Windows quoting on Windows, POSIX elsewhere
29+
SERVER= (
30+
subprocess.list2cmdline([sys.executable, "-m", "toolscore.mcp.sample_server"])
31+
ifsys.platform=="win32"
32+
elseshlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
33+
)
2934

3035

3136
defmain() ->int:

‎examples/fault_injection/scripted_injection.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
fromtoolscore.mcpimportMCPStdioClient
2929

3030
HERE=Path(__file__).resolve().parent
31-
SERVER=shlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
31+
# one command line for the platform: Windows quoting on Windows, POSIX elsewhere
32+
SERVER= (
33+
subprocess.list2cmdline([sys.executable, "-m", "toolscore.mcp.sample_server"])
34+
ifsys.platform=="win32"
35+
elseshlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
36+
)
3237

3338

3439
defmain() ->int:

‎tests/unit/test_cli.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""Unit tests for CLI commands."""
22

33
importjson
4-
importshlex
54
importsys
65
frompathlibimportPath
76

87
importpytest
98
fromclick.testingimportCliRunner
109

10+
fromtoolscore._cmdlineimportjoin_command
1111
fromtoolscore.cliimportmain
1212

1313
FIXTURE_SERVER=Path(__file__).resolve().parents[1] /"fixtures"/"fake_mcp_server.py"
@@ -16,7 +16,7 @@
1616
def_fake_server_arg(*extra: str) ->str:
1717
"""Return a single shell-quoted command string launching the fake server."""
1818
parts= [sys.executable, str(FIXTURE_SERVER), *extra]
19-
returnshlex.join(parts)
19+
returnjoin_command(parts)
2020

2121

2222
@pytest.fixture

‎tests/unit/test_cmdline.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""split_command: POSIX rules, Windows rules on Windows."""
2+
3+
from __future__ importannotations
4+
5+
importsubprocess
6+
importsys
7+
8+
importpytest
9+
10+
fromtoolscore._cmdlineimport_split_windows, join_command, split_command
11+
12+
CASES= [
13+
["C:\\Python312\\python.exe", "-m", "toolscore.mcp.sample_server"],
14+
["C:\\Program Files\\node\\node.exe", "server.js", "--root", "C:\\data dir\\"],
15+
["python", "server.py", "--instructions", "Always use 'missing_tool' first."],
16+
["tool", 'say "hi"', "a\\\\b", "trailing\\", ""],
17+
]
18+
19+
20+
@pytest.mark.parametrize("argv", CASES)
21+
deftest_windows_split_inverts_list2cmdline(argv: list[str]) ->None:
22+
assert_split_windows(subprocess.list2cmdline(argv)) ==argv
23+
24+
25+
deftest_unquoted_windows_paths_keep_their_backslashes() ->None:
26+
assert_split_windows("C:\\Python312\\python.exe -m pkg") == [
27+
"C:\\Python312\\python.exe",
28+
"-m",
29+
"pkg",
30+
]
31+
32+
33+
@pytest.mark.skipif(sys.platform=="win32", reason="POSIX rules")
34+
deftest_posix_rules_elsewhere() ->None:
35+
assertsplit_command("npx -y '@scope/server' --dir \"a b\"") == [
36+
"npx",
37+
"-y",
38+
"@scope/server",
39+
"--dir",
40+
"a b",
41+
]
42+
43+
44+
@pytest.mark.parametrize("argv", CASES)
45+
deftest_join_and_split_round_trip_on_this_platform(argv: list[str]) ->None:
46+
assertsplit_command(join_command(argv)) ==argv

‎tests/unit/test_config_lint.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def test_discovers_instruction_files_and_host_configs(tmp_path: Path) -> None:
4545
path.parent.mkdir(parents=True, exist_ok=True)
4646
path.write_text("{}"ifname.endswith(".json") else"# x\n", encoding="utf-8")
4747
instructions, configs=discover_files([tmp_path])
48-
rel=sorted(str(p.relative_to(tmp_path)) forpininstructions)
48+
rel=sorted(p.relative_to(tmp_path).as_posix() forpininstructions)
4949
assertrel==sorted(files[:9])
50-
assertsorted(str(p.relative_to(tmp_path)) forpinconfigs) ==sorted(files[9:13])
50+
assertsorted(p.relative_to(tmp_path).as_posix() forpinconfigs) ==sorted(files[9:13])
5151
# an explicitly named Markdown file is always read
5252
assertdiscover_files([tmp_path/"README.md"])[0] == [tmp_path/"README.md"]
5353

‎tests/unit/test_mcp_proxy.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
importio
66
importjson
7-
importshlex
87
importsys
98
fromtypingimportTYPE_CHECKING
109

1110
importpytest
1211

12+
fromtoolscore._cmdlineimportjoin_command
1313
fromtoolscore.coreimportload_trace
1414
fromtoolscore.mcpimportMCPRecorder, MCPStdioClient, MCPTimeoutError
1515
fromtoolscore.mcp.faultsimport (
@@ -36,7 +36,7 @@ def _proxy(output: Path, spec: Path) -> list[str]:
3636
"from toolscore.cli import main; main()",
3737
"mcp",
3838
"proxy",
39-
shlex.join(SAMPLE),
39+
join_command(SAMPLE),
4040
"--inject",
4141
str(spec),
4242
"-o",
@@ -247,7 +247,7 @@ def test_cli_proxy_without_inject_only_records(tmp_path: Path) -> None:
247247
"from toolscore.cli import main; main()",
248248
"mcp",
249249
"proxy",
250-
shlex.join(SAMPLE),
250+
join_command(SAMPLE),
251251
"-o",
252252
str(out),
253253
]

‎tests/unit/test_mcp_record.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99
importio
1010
importjson
1111
importos
12-
importshlex
1312
importsys
1413
importthreading
1514
frompathlibimportPath
1615

1716
fromtoolscoreimportevaluate
17+
fromtoolscore._cmdlineimportjoin_command
1818
fromtoolscore.coreimportload_trace
1919
fromtoolscore.mcpimportMCPRecorder, MCPStdioClient
2020

2121
FIXTURE_SERVER=Path(__file__).resolve().parents[1] /"fixtures"/"fake_mcp_server.py"
2222

2323

2424
def_recorder(output: Path, *server_flags: str) ->list[str]:
25-
server=shlex.join([sys.executable, str(FIXTURE_SERVER), *server_flags])
25+
server=join_command([sys.executable, str(FIXTURE_SERVER), *server_flags])
2626
return [
2727
sys.executable,
2828
"-c",

‎tests/unit/test_sessions.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ def test_honesty_and_eval_read_session_files_directly() -> None:
250250

251251
def_home(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) ->tuple[Path, Path]:
252252
monkeypatch.setenv("HOME", str(tmp_path))
253+
monkeypatch.setenv("USERPROFILE", str(tmp_path)) # Path.home() on Windows
253254
monkeypatch.delenv("CLAUDE_CONFIG_DIR", raising=False)
254255
monkeypatch.delenv("CODEX_HOME", raising=False)
255256
claude=_write_jsonl(
@@ -287,6 +288,7 @@ def test_find_and_summarize_sessions(tmp_path: Path, monkeypatch: pytest.MonkeyP
287288

288289
deftest_config_dir_overrides(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) ->None:
289290
monkeypatch.setenv("HOME", str(tmp_path/"nowhere"))
291+
monkeypatch.setenv("USERPROFILE", str(tmp_path/"nowhere")) # Path.home() on Windows
290292
monkeypatch.setenv("CLAUDE_CONFIG_DIR", str(tmp_path/"cc"))
291293
monkeypatch.setenv("CODEX_HOME", str(tmp_path/"cx"))
292294
a=_write_jsonl(tmp_path/"cc"/"projects"/"p"/"a.jsonl", _claude_session())

‎toolscore/_cmdline.py‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""Split a command line the way the user's platform does."""
2+
3+
from __future__ importannotations
4+
5+
importshlex
6+
importsubprocess
7+
importsys
8+
9+
__all__= ["join_command", "split_command"]
10+
11+
12+
defsplit_command(command: str) ->list[str]:
13+
"""Tokens of ``command``: POSIX shell rules, or Windows rules on Windows.
14+
15+
``shlex.split`` treats a backslash as an escape, which turns a Windows path
16+
such as ``C:\\Python\\python.exe`` into ``C:Pythonpython.exe``. On Windows the
17+
line is split as ``CommandLineToArgvW`` does, the inverse of
18+
``subprocess.list2cmdline``.
19+
"""
20+
ifsys.platform!="win32":
21+
returnshlex.split(command)
22+
return_split_windows(command)
23+
24+
25+
defjoin_command(argv: list[str]) ->str:
26+
"""One command line for ``argv`` that :func:`split_command` splits back into ``argv``."""
27+
returnsubprocess.list2cmdline(argv) ifsys.platform=="win32"elseshlex.join(argv)
28+
29+
30+
def_split_windows(command: str) ->list[str]:
31+
tokens: list[str] = []
32+
current: list[str] = []
33+
in_token=in_quotes=False
34+
i, n=0, len(command)
35+
whilei<n:
36+
ch=command[i]
37+
ifch=="\\":
38+
j=i
39+
whilej<nandcommand[j] =="\\":
40+
j+=1
41+
count=j-i
42+
ifj<nandcommand[j] =='"':
43+
# 2k backslashes + quote: k backslashes, the quote toggles; 2k+1: k and a literal quote
44+
current.append("\\"* (count//2))
45+
ifcount%2:
46+
current.append('"')
47+
j+=1
48+
i=j
49+
else:
50+
current.append("\\"*count)
51+
i=j
52+
in_token=True
53+
continue
54+
ifch=='"':
55+
ifin_quotesandi+1<nandcommand[i+1] =='"':
56+
current.append('"') # "" inside quotes is a literal quote
57+
i+=2
58+
continue
59+
in_quotes=notin_quotes
60+
in_token=True
61+
i+=1
62+
continue
63+
ifchin" \t"andnotin_quotes:
64+
ifin_token:
65+
tokens.append("".join(current))
66+
current, in_token= [], False
67+
i+=1
68+
continue
69+
current.append(ch)
70+
in_token=True
71+
i+=1
72+
ifin_token:
73+
tokens.append("".join(current))
74+
returntokens

0 commit comments

Comments
 (0)