mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-27 04:20:09 +00:00
Rename hanzo-repl → hanzo-dev to match @hanzo/dev (Rust/TS). New packages: - hanzo-lsp: async LSP client (diagnostics, go-to-def, references, context enrichment) - hanzo-hooks: pre/post tool use hooks (shell scripts, exit-code deny) - hanzo-sandbox: container detection, filesystem isolation, Linux unshare hanzo-dev TUI: - Vim keybindings (Normal/Insert/Visual/Command modes, 37 tests) - Git slash commands (/branch /commit /worktree /diff /stash, 13 tests) - All slash commands at parity: /model /permissions /clear /resume /memory /init /export /session /plan /solve /code /auto /fast /remote-control - Multi-provider auth: Anthropic OAuth, OpenAI device code, Hanzo PKCE, auto-detect Auth: - login_with_anthropic() — PKCE via console.hanzo.ai - login_with_openai() — device code flow via auth.openai.com - login_auto() — env var detection + saved credential fallback - OAuthCredentialStore supports provider keys (hanzo/anthropic/openai) Parity test suite (38 tests): SSE, MCP normalization, permissions, compaction, token usage, PKCE RFC vector, config, backoff, session roundtrip, hooks. 348 tests total, 0 failures across Python + Rust + Universe E2E.
114 lines
3.4 KiB
Python
114 lines
3.4 KiB
Python
"""Mock LSP server for integration tests. Speaks Content-Length framed JSON-RPC."""
|
|
|
|
import json
|
|
import sys
|
|
|
|
|
|
def read_message():
|
|
headers = {}
|
|
while True:
|
|
line = sys.stdin.buffer.readline()
|
|
if not line:
|
|
return None
|
|
if line == b"\r\n":
|
|
break
|
|
key, value = line.decode("utf-8").split(":", 1)
|
|
headers[key.strip().lower()] = value.strip()
|
|
length = int(headers["content-length"])
|
|
body = sys.stdin.buffer.read(length)
|
|
return json.loads(body)
|
|
|
|
|
|
def write_message(payload):
|
|
raw = json.dumps(payload).encode("utf-8")
|
|
sys.stdout.buffer.write(f"Content-Length: {len(raw)}\r\n\r\n".encode("utf-8"))
|
|
sys.stdout.buffer.write(raw)
|
|
sys.stdout.buffer.flush()
|
|
|
|
|
|
while True:
|
|
message = read_message()
|
|
if message is None:
|
|
break
|
|
|
|
method = message.get("method")
|
|
if method == "initialize":
|
|
write_message({
|
|
"jsonrpc": "2.0",
|
|
"id": message["id"],
|
|
"result": {
|
|
"capabilities": {
|
|
"definitionProvider": True,
|
|
"referencesProvider": True,
|
|
"textDocumentSync": 1,
|
|
}
|
|
},
|
|
})
|
|
elif method == "initialized":
|
|
continue
|
|
elif method == "textDocument/didOpen":
|
|
document = message["params"]["textDocument"]
|
|
write_message({
|
|
"jsonrpc": "2.0",
|
|
"method": "textDocument/publishDiagnostics",
|
|
"params": {
|
|
"uri": document["uri"],
|
|
"diagnostics": [
|
|
{
|
|
"range": {
|
|
"start": {"line": 0, "character": 0},
|
|
"end": {"line": 0, "character": 3},
|
|
},
|
|
"severity": 1,
|
|
"source": "mock-server",
|
|
"message": "mock error",
|
|
}
|
|
],
|
|
},
|
|
})
|
|
elif method == "textDocument/didChange":
|
|
continue
|
|
elif method == "textDocument/didSave":
|
|
continue
|
|
elif method == "textDocument/definition":
|
|
uri = message["params"]["textDocument"]["uri"]
|
|
write_message({
|
|
"jsonrpc": "2.0",
|
|
"id": message["id"],
|
|
"result": [
|
|
{
|
|
"uri": uri,
|
|
"range": {
|
|
"start": {"line": 0, "character": 0},
|
|
"end": {"line": 0, "character": 3},
|
|
},
|
|
}
|
|
],
|
|
})
|
|
elif method == "textDocument/references":
|
|
uri = message["params"]["textDocument"]["uri"]
|
|
write_message({
|
|
"jsonrpc": "2.0",
|
|
"id": message["id"],
|
|
"result": [
|
|
{
|
|
"uri": uri,
|
|
"range": {
|
|
"start": {"line": 0, "character": 0},
|
|
"end": {"line": 0, "character": 3},
|
|
},
|
|
},
|
|
{
|
|
"uri": uri,
|
|
"range": {
|
|
"start": {"line": 1, "character": 4},
|
|
"end": {"line": 1, "character": 7},
|
|
},
|
|
},
|
|
],
|
|
})
|
|
elif method == "shutdown":
|
|
write_message({"jsonrpc": "2.0", "id": message["id"], "result": None})
|
|
elif method == "exit":
|
|
break
|