kiwimatecoder 0.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.
- kiwimatecoder/__init__.py +8 -0
- kiwimatecoder/__main__.py +12 -0
- kiwimatecoder/acp/__init__.py +13 -0
- kiwimatecoder/acp/protocol.py +115 -0
- kiwimatecoder/acp/server.py +823 -0
- kiwimatecoder/acp/stdio.py +32 -0
- kiwimatecoder/agent.py +1162 -0
- kiwimatecoder/ai.py +68 -0
- kiwimatecoder/audit.py +65 -0
- kiwimatecoder/browser.py +261 -0
- kiwimatecoder/catalog.py +445 -0
- kiwimatecoder/checkpoints.py +103 -0
- kiwimatecoder/client.py +820 -0
- kiwimatecoder/clipboard.py +99 -0
- kiwimatecoder/commands.py +6757 -0
- kiwimatecoder/config.py +5335 -0
- kiwimatecoder/diagnostics.py +295 -0
- kiwimatecoder/documents.py +299 -0
- kiwimatecoder/evals/__init__.py +39 -0
- kiwimatecoder/evals/cases.py +230 -0
- kiwimatecoder/evals/render.py +46 -0
- kiwimatecoder/evals/runner.py +319 -0
- kiwimatecoder/events.py +95 -0
- kiwimatecoder/forge.py +172 -0
- kiwimatecoder/git.py +164 -0
- kiwimatecoder/headless.py +255 -0
- kiwimatecoder/hooks.py +170 -0
- kiwimatecoder/hunks.py +260 -0
- kiwimatecoder/i18n.py +368 -0
- kiwimatecoder/images.py +195 -0
- kiwimatecoder/index/__init__.py +14 -0
- kiwimatecoder/index/builder.py +376 -0
- kiwimatecoder/index/embeddings.py +151 -0
- kiwimatecoder/index/search.py +247 -0
- kiwimatecoder/index/store.py +251 -0
- kiwimatecoder/instructions.py +83 -0
- kiwimatecoder/jobs.py +594 -0
- kiwimatecoder/lsp/__init__.py +56 -0
- kiwimatecoder/lsp/client.py +548 -0
- kiwimatecoder/lsp/manager.py +353 -0
- kiwimatecoder/lsp/protocol.py +79 -0
- kiwimatecoder/main.py +4258 -0
- kiwimatecoder/mcp/__init__.py +45 -0
- kiwimatecoder/mcp/client.py +385 -0
- kiwimatecoder/mcp/errors.py +7 -0
- kiwimatecoder/mcp/manager.py +253 -0
- kiwimatecoder/mcp/protocol.py +132 -0
- kiwimatecoder/media.py +904 -0
- kiwimatecoder/memory.py +141 -0
- kiwimatecoder/network.py +112 -0
- kiwimatecoder/notify.py +139 -0
- kiwimatecoder/permissions.py +171 -0
- kiwimatecoder/pickers.py +417 -0
- kiwimatecoder/plugins.py +245 -0
- kiwimatecoder/pricing.py +167 -0
- kiwimatecoder/prompts.py +213 -0
- kiwimatecoder/providers.py +342 -0
- kiwimatecoder/redaction.py +66 -0
- kiwimatecoder/remote.py +294 -0
- kiwimatecoder/repl.py +1388 -0
- kiwimatecoder/routing.py +65 -0
- kiwimatecoder/sandbox.py +169 -0
- kiwimatecoder/sdk.py +146 -0
- kiwimatecoder/session.py +574 -0
- kiwimatecoder/share.py +386 -0
- kiwimatecoder/shell.py +660 -0
- kiwimatecoder/skills.py +138 -0
- kiwimatecoder/sync.py +513 -0
- kiwimatecoder/team.py +249 -0
- kiwimatecoder/telemetry.py +392 -0
- kiwimatecoder/templates.py +108 -0
- kiwimatecoder/tools/__init__.py +190 -0
- kiwimatecoder/tools/ask.py +62 -0
- kiwimatecoder/tools/base.py +69 -0
- kiwimatecoder/tools/browser.py +261 -0
- kiwimatecoder/tools/edit_file.py +143 -0
- kiwimatecoder/tools/forge.py +207 -0
- kiwimatecoder/tools/git.py +158 -0
- kiwimatecoder/tools/image.py +300 -0
- kiwimatecoder/tools/io.py +346 -0
- kiwimatecoder/tools/list_dir.py +67 -0
- kiwimatecoder/tools/lsp.py +254 -0
- kiwimatecoder/tools/memory.py +115 -0
- kiwimatecoder/tools/paths.py +234 -0
- kiwimatecoder/tools/read_file.py +113 -0
- kiwimatecoder/tools/registry.py +91 -0
- kiwimatecoder/tools/run_bash.py +189 -0
- kiwimatecoder/tools/search.py +224 -0
- kiwimatecoder/tools/selection.py +108 -0
- kiwimatecoder/tools/shell.py +158 -0
- kiwimatecoder/tools/skill.py +46 -0
- kiwimatecoder/tools/task.py +61 -0
- kiwimatecoder/tools/todo.py +77 -0
- kiwimatecoder/tools/web.py +146 -0
- kiwimatecoder/tools/write_file.py +83 -0
- kiwimatecoder/ui.py +189 -0
- kiwimatecoder/updater.py +502 -0
- kiwimatecoder/web.py +499 -0
- kiwimatecoder-0.1.0.dist-info/METADATA +2016 -0
- kiwimatecoder-0.1.0.dist-info/RECORD +104 -0
- kiwimatecoder-0.1.0.dist-info/WHEEL +5 -0
- kiwimatecoder-0.1.0.dist-info/entry_points.txt +2 -0
- kiwimatecoder-0.1.0.dist-info/licenses/LICENSE +21 -0
- kiwimatecoder-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Minimal JSON-RPC 2.0 and MCP content helpers.
|
|
2
|
+
|
|
3
|
+
Only the subset the client needs is implemented: request/notification
|
|
4
|
+
construction, response validation, SSE framing, and flattening MCP content
|
|
5
|
+
blocks into the plain text the agent consumes. The JSON-RPC method names and
|
|
6
|
+
result shapes follow the Model Context Protocol revision named in
|
|
7
|
+
:data:`MCP_PROTOCOL_VERSION`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from kiwimatecoder import __version__
|
|
16
|
+
from kiwimatecoder.mcp.errors import McpError
|
|
17
|
+
|
|
18
|
+
MCP_PROTOCOL_VERSION = "2025-06-18"
|
|
19
|
+
|
|
20
|
+
CLIENT_INFO = {"name": "kiwimatecoder", "version": __version__}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def request_message(
|
|
24
|
+
request_id: int, method: str, params: dict[str, Any] | None = None
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
"""Build a JSON-RPC request object."""
|
|
27
|
+
message: dict[str, Any] = {
|
|
28
|
+
"jsonrpc": "2.0",
|
|
29
|
+
"id": request_id,
|
|
30
|
+
"method": method,
|
|
31
|
+
"params": params if params is not None else {},
|
|
32
|
+
}
|
|
33
|
+
return message
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def notification_message(
|
|
37
|
+
method: str, params: dict[str, Any] | None = None
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
"""Build a JSON-RPC notification (no ``id``, no response expected)."""
|
|
40
|
+
message: dict[str, Any] = {"jsonrpc": "2.0", "method": method}
|
|
41
|
+
if params is not None:
|
|
42
|
+
message["params"] = params
|
|
43
|
+
return message
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def parse_response(message: dict[str, Any], method: str) -> Any:
|
|
47
|
+
"""Return a JSON-RPC result or raise :class:`McpError` for its error."""
|
|
48
|
+
error = message.get("error")
|
|
49
|
+
if isinstance(error, dict):
|
|
50
|
+
detail = str(error.get("message") or error)
|
|
51
|
+
code = error.get("code")
|
|
52
|
+
suffix = f" (code {code})" if code is not None else ""
|
|
53
|
+
raise McpError(f"{method} failed: {detail}{suffix}")
|
|
54
|
+
if "result" not in message:
|
|
55
|
+
raise McpError(f"{method}: malformed JSON-RPC response")
|
|
56
|
+
return message["result"]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_sse_messages(text: str) -> list[dict[str, Any]]:
|
|
60
|
+
"""Extract JSON-RPC objects from an SSE body's ``data:`` lines."""
|
|
61
|
+
messages: list[dict[str, Any]] = []
|
|
62
|
+
data_lines: list[str] = []
|
|
63
|
+
|
|
64
|
+
def flush() -> None:
|
|
65
|
+
if not data_lines:
|
|
66
|
+
return
|
|
67
|
+
payload = "\n".join(data_lines)
|
|
68
|
+
data_lines.clear()
|
|
69
|
+
try:
|
|
70
|
+
decoded = json.loads(payload)
|
|
71
|
+
except json.JSONDecodeError:
|
|
72
|
+
return
|
|
73
|
+
if isinstance(decoded, dict):
|
|
74
|
+
messages.append(decoded)
|
|
75
|
+
|
|
76
|
+
for line in text.splitlines():
|
|
77
|
+
if line.startswith("data:"):
|
|
78
|
+
data_lines.append(line[5:].lstrip())
|
|
79
|
+
elif not line.strip():
|
|
80
|
+
flush()
|
|
81
|
+
flush()
|
|
82
|
+
return messages
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _flatten_block(block: Any) -> str:
|
|
86
|
+
if not isinstance(block, dict):
|
|
87
|
+
return str(block)
|
|
88
|
+
kind = str(block.get("type") or "")
|
|
89
|
+
if kind == "text":
|
|
90
|
+
return str(block.get("text") or "")
|
|
91
|
+
if kind == "image":
|
|
92
|
+
return "[image content]"
|
|
93
|
+
if kind == "audio":
|
|
94
|
+
return "[audio content]"
|
|
95
|
+
if kind == "resource":
|
|
96
|
+
resource = block.get("resource")
|
|
97
|
+
if isinstance(resource, dict) and resource.get("text") is not None:
|
|
98
|
+
return str(resource["text"])
|
|
99
|
+
return "[resource content]"
|
|
100
|
+
if kind == "resource_link":
|
|
101
|
+
return str(block.get("uri") or "[resource link]")
|
|
102
|
+
return f"[{kind} content]" if kind else "[content]"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def flatten_content(content: Any) -> str:
|
|
106
|
+
"""Render an MCP ``content`` list as plain text for the agent."""
|
|
107
|
+
if content is None:
|
|
108
|
+
return ""
|
|
109
|
+
if isinstance(content, str):
|
|
110
|
+
return content
|
|
111
|
+
if not isinstance(content, list):
|
|
112
|
+
return str(content)
|
|
113
|
+
return "\n".join(part for part in (_flatten_block(item) for item in content) if part)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def flatten_resource_contents(contents: Any) -> str:
|
|
117
|
+
"""Render an MCP ``resources/read`` contents list as plain text."""
|
|
118
|
+
if contents is None:
|
|
119
|
+
return ""
|
|
120
|
+
if not isinstance(contents, list):
|
|
121
|
+
return flatten_content(contents)
|
|
122
|
+
parts: list[str] = []
|
|
123
|
+
for item in contents:
|
|
124
|
+
if not isinstance(item, dict):
|
|
125
|
+
parts.append(str(item))
|
|
126
|
+
elif item.get("text") is not None:
|
|
127
|
+
parts.append(str(item["text"]))
|
|
128
|
+
elif item.get("blob") is not None:
|
|
129
|
+
parts.append("[binary resource]")
|
|
130
|
+
else:
|
|
131
|
+
parts.append("[resource content]")
|
|
132
|
+
return "\n".join(part for part in parts if part)
|