claude-code-tools 0.1.17__tar.gz → 0.1.19__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 claude-code-tools might be problematic. Click here for more details.
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/PKG-INFO +20 -13
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/README.md +18 -12
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/claude_code_tools/__init__.py +1 -1
- claude_code_tools-0.1.19/claude_code_tools/codex_bridge_mcp.py +333 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/claude_code_tools/tmux_cli_controller.py +171 -35
- claude_code_tools-0.1.19/claude_code_tools/tmux_remote_controller.py +229 -0
- claude_code_tools-0.1.19/docs/cc-codex-instructions.md +37 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/tmux-cli-instructions.md +38 -16
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/pyproject.toml +4 -3
- claude_code_tools-0.1.17/claude_code_tools/tmux_remote_controller.py +0 -69
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/.gitignore +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/LICENSE +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/claude_code_tools/dotenv_vault.py +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/claude_code_tools/env_safe.py +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/claude_code_tools/find_claude_session.py +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/claude-code-chutes.md +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/claude-code-tmux-tutorials.md +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/dot-zshrc.md +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/find-claude-session.md +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/reddit-post.md +0 -0
- {claude_code_tools-0.1.17 → claude_code_tools-0.1.19}/docs/vault-documentation.md +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-tools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
4
4
|
Summary: Collection of tools for working with Claude Code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Requires-Dist: click>=8.0.0
|
|
8
8
|
Requires-Dist: fire>=0.5.0
|
|
9
|
+
Requires-Dist: mcp>=1.13.0
|
|
9
10
|
Requires-Dist: rich>=13.0.0
|
|
10
11
|
Provides-Extra: dev
|
|
11
12
|
Requires-Dist: commitizen>=3.0.0; extra == 'dev'
|
|
@@ -213,7 +214,7 @@ env-safe --help # See all options
|
|
|
213
214
|
|
|
214
215
|
### Why env-safe?
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
Claude Code is completely blocked from directly accessing .env files - no reading, writing, or editing allowed. This prevents both accidental exposure of API keys and unintended modifications. The `env-safe` command provides the only approved way for Claude Code to inspect environment configuration safely, while any modifications must be done manually outside of Claude Code.
|
|
217
218
|
|
|
218
219
|
## 🛡️ Claude Code Safety Hooks
|
|
219
220
|
|
|
@@ -226,8 +227,8 @@ Code's behavior and prevent dangerous operations.
|
|
|
226
227
|
pattern
|
|
227
228
|
- **Git Safety** - Prevents dangerous `git add -A`, unsafe checkouts, and
|
|
228
229
|
accidental data loss
|
|
229
|
-
- **Environment Security** - Blocks
|
|
230
|
-
command
|
|
230
|
+
- **Environment Security** - Blocks all .env file operations (read/write/edit),
|
|
231
|
+
suggests `env-safe` command for safe inspection
|
|
231
232
|
- **Context Management** - Blocks reading files >500 lines to prevent context
|
|
232
233
|
bloat
|
|
233
234
|
- **Command Enhancement** - Enforces ripgrep (`rg`) over grep for better
|
|
@@ -235,20 +236,26 @@ Code's behavior and prevent dangerous operations.
|
|
|
235
236
|
|
|
236
237
|
### Quick Setup
|
|
237
238
|
|
|
238
|
-
1. Copy the
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
239
|
+
1. Copy the hooks configuration from `hooks/settings.sample.json`
|
|
240
|
+
|
|
241
|
+
2. Add the hooks to your global Claude settings at `~/.claude/settings.json`:
|
|
242
|
+
- If the file doesn't exist, create it
|
|
243
|
+
- Copy the "hooks" section from settings.sample.json
|
|
244
|
+
- Replace `/path/to/claude-code-tools` with your actual path to this repository
|
|
245
|
+
|
|
246
|
+
Example ~/.claude/settings.json:
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"hooks": {
|
|
250
|
+
// ... hooks configuration from settings.sample.json ...
|
|
251
|
+
}
|
|
252
|
+
}
|
|
247
253
|
```
|
|
248
254
|
|
|
249
255
|
### Available Hooks
|
|
250
256
|
|
|
251
257
|
- `bash_hook.py` - Comprehensive bash command safety checks
|
|
258
|
+
- `env_file_protection_hook.py` - Blocks all .env file operations
|
|
252
259
|
- `file_size_conditional_hook.py` - Prevents reading huge files
|
|
253
260
|
- `grep_block_hook.py` - Enforces ripgrep usage
|
|
254
261
|
- `notification_hook.sh` - Sends ntfy.sh notifications
|
|
@@ -200,7 +200,7 @@ env-safe --help # See all options
|
|
|
200
200
|
|
|
201
201
|
### Why env-safe?
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
Claude Code is completely blocked from directly accessing .env files - no reading, writing, or editing allowed. This prevents both accidental exposure of API keys and unintended modifications. The `env-safe` command provides the only approved way for Claude Code to inspect environment configuration safely, while any modifications must be done manually outside of Claude Code.
|
|
204
204
|
|
|
205
205
|
## 🛡️ Claude Code Safety Hooks
|
|
206
206
|
|
|
@@ -213,8 +213,8 @@ Code's behavior and prevent dangerous operations.
|
|
|
213
213
|
pattern
|
|
214
214
|
- **Git Safety** - Prevents dangerous `git add -A`, unsafe checkouts, and
|
|
215
215
|
accidental data loss
|
|
216
|
-
- **Environment Security** - Blocks
|
|
217
|
-
command
|
|
216
|
+
- **Environment Security** - Blocks all .env file operations (read/write/edit),
|
|
217
|
+
suggests `env-safe` command for safe inspection
|
|
218
218
|
- **Context Management** - Blocks reading files >500 lines to prevent context
|
|
219
219
|
bloat
|
|
220
220
|
- **Command Enhancement** - Enforces ripgrep (`rg`) over grep for better
|
|
@@ -222,20 +222,26 @@ Code's behavior and prevent dangerous operations.
|
|
|
222
222
|
|
|
223
223
|
### Quick Setup
|
|
224
224
|
|
|
225
|
-
1. Copy the
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
225
|
+
1. Copy the hooks configuration from `hooks/settings.sample.json`
|
|
226
|
+
|
|
227
|
+
2. Add the hooks to your global Claude settings at `~/.claude/settings.json`:
|
|
228
|
+
- If the file doesn't exist, create it
|
|
229
|
+
- Copy the "hooks" section from settings.sample.json
|
|
230
|
+
- Replace `/path/to/claude-code-tools` with your actual path to this repository
|
|
231
|
+
|
|
232
|
+
Example ~/.claude/settings.json:
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"hooks": {
|
|
236
|
+
// ... hooks configuration from settings.sample.json ...
|
|
237
|
+
}
|
|
238
|
+
}
|
|
234
239
|
```
|
|
235
240
|
|
|
236
241
|
### Available Hooks
|
|
237
242
|
|
|
238
243
|
- `bash_hook.py` - Comprehensive bash command safety checks
|
|
244
|
+
- `env_file_protection_hook.py` - Blocks all .env file operations
|
|
239
245
|
- `file_size_conditional_hook.py` - Prevents reading huge files
|
|
240
246
|
- `grep_block_hook.py` - Enforces ripgrep usage
|
|
241
247
|
- `notification_hook.sh` - Sends ntfy.sh notifications
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Codex Bridge MCP Server
|
|
4
|
+
|
|
5
|
+
This server acts as a bridge between Claude Code's synchronous MCP client
|
|
6
|
+
and Codex's asynchronous event-streaming MCP server.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
import json
|
|
11
|
+
import subprocess
|
|
12
|
+
import threading
|
|
13
|
+
import queue
|
|
14
|
+
import uuid
|
|
15
|
+
from typing import Dict, Any, Optional
|
|
16
|
+
|
|
17
|
+
class CodexBridge:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.codex_process = None
|
|
20
|
+
self.response_queue = queue.Queue()
|
|
21
|
+
self.current_request_id = None
|
|
22
|
+
self.initialization_complete = False
|
|
23
|
+
|
|
24
|
+
def start_codex_server(self):
|
|
25
|
+
"""Start the Codex MCP server as a subprocess."""
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Log startup for debugging
|
|
31
|
+
print(f"Starting Codex MCP server...", file=sys.stderr)
|
|
32
|
+
|
|
33
|
+
# Set environment to ensure proper operation
|
|
34
|
+
env = os.environ.copy()
|
|
35
|
+
env['NODE_ENV'] = 'production'
|
|
36
|
+
|
|
37
|
+
self.codex_process = subprocess.Popen(
|
|
38
|
+
["codex", "mcp"],
|
|
39
|
+
stdin=subprocess.PIPE,
|
|
40
|
+
stdout=subprocess.PIPE,
|
|
41
|
+
stderr=subprocess.PIPE,
|
|
42
|
+
text=True,
|
|
43
|
+
bufsize=0, # Unbuffered for real-time communication
|
|
44
|
+
env=env
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Start reader threads for both stdout and stderr
|
|
48
|
+
reader_thread = threading.Thread(target=self._read_codex_output, daemon=True)
|
|
49
|
+
reader_thread.start()
|
|
50
|
+
|
|
51
|
+
stderr_thread = threading.Thread(target=self._read_codex_stderr, daemon=True)
|
|
52
|
+
stderr_thread.start()
|
|
53
|
+
|
|
54
|
+
print(f"Codex MCP server started successfully", file=sys.stderr)
|
|
55
|
+
except Exception as e:
|
|
56
|
+
print(f"Failed to start Codex MCP server: {e}", file=sys.stderr)
|
|
57
|
+
self.codex_process = None
|
|
58
|
+
|
|
59
|
+
def _read_codex_output(self):
|
|
60
|
+
"""Continuously read output from Codex server."""
|
|
61
|
+
import sys
|
|
62
|
+
if not self.codex_process or not self.codex_process.stdout:
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
for line in self.codex_process.stdout:
|
|
66
|
+
try:
|
|
67
|
+
if line.strip():
|
|
68
|
+
print(f"Codex stdout: {line.strip()}", file=sys.stderr)
|
|
69
|
+
msg = json.loads(line.strip())
|
|
70
|
+
self._handle_codex_message(msg)
|
|
71
|
+
except json.JSONDecodeError as e:
|
|
72
|
+
print(f"Failed to parse Codex output: {line.strip()}", file=sys.stderr)
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
def _read_codex_stderr(self):
|
|
76
|
+
"""Read stderr from Codex server for debugging."""
|
|
77
|
+
import sys
|
|
78
|
+
if not self.codex_process or not self.codex_process.stderr:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
for line in self.codex_process.stderr:
|
|
82
|
+
if line.strip():
|
|
83
|
+
print(f"Codex stderr: {line.strip()}", file=sys.stderr)
|
|
84
|
+
|
|
85
|
+
def _handle_codex_message(self, msg: Dict[str, Any]):
|
|
86
|
+
"""Process messages from Codex server."""
|
|
87
|
+
# Handle initialization response
|
|
88
|
+
if msg.get("method") == "initialize" and "result" in msg:
|
|
89
|
+
self.initialization_complete = True
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
# Handle notifications (events)
|
|
93
|
+
if msg.get("method") == "notifications/message":
|
|
94
|
+
params = msg.get("params", {})
|
|
95
|
+
message = params.get("message", {})
|
|
96
|
+
|
|
97
|
+
# Look for TaskComplete event
|
|
98
|
+
if "codex/event" in str(message):
|
|
99
|
+
event_data = message.get("data", {})
|
|
100
|
+
if "TaskComplete" in str(event_data):
|
|
101
|
+
# Extract the final message
|
|
102
|
+
self.response_queue.put({
|
|
103
|
+
"type": "complete",
|
|
104
|
+
"content": event_data.get("last_agent_message", "Task completed")
|
|
105
|
+
})
|
|
106
|
+
elif "Error" in str(event_data):
|
|
107
|
+
self.response_queue.put({
|
|
108
|
+
"type": "error",
|
|
109
|
+
"content": str(event_data)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
# Handle tool call responses
|
|
113
|
+
if "result" in msg and msg.get("id") == self.current_request_id:
|
|
114
|
+
# Direct response from tool call
|
|
115
|
+
content = msg.get("result", {}).get("content", [])
|
|
116
|
+
if content:
|
|
117
|
+
text_content = content[0].get("text", "") if content else ""
|
|
118
|
+
self.response_queue.put({
|
|
119
|
+
"type": "complete",
|
|
120
|
+
"content": text_content
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
def initialize_codex(self):
|
|
124
|
+
"""Send initialization handshake to Codex."""
|
|
125
|
+
import time
|
|
126
|
+
import sys
|
|
127
|
+
|
|
128
|
+
print("Initializing Codex MCP connection...", file=sys.stderr)
|
|
129
|
+
|
|
130
|
+
init_request = {
|
|
131
|
+
"jsonrpc": "2.0",
|
|
132
|
+
"id": 0,
|
|
133
|
+
"method": "initialize",
|
|
134
|
+
"params": {
|
|
135
|
+
"protocolVersion": "2024-11-05", # Try older protocol version
|
|
136
|
+
"capabilities": {},
|
|
137
|
+
"clientInfo": {
|
|
138
|
+
"name": "codex-bridge",
|
|
139
|
+
"version": "1.0.0"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
self._send_to_codex(init_request)
|
|
145
|
+
|
|
146
|
+
# Wait a bit for initialization response
|
|
147
|
+
time.sleep(1)
|
|
148
|
+
|
|
149
|
+
# Send initialized notification
|
|
150
|
+
initialized_notif = {
|
|
151
|
+
"jsonrpc": "2.0",
|
|
152
|
+
"method": "notifications/initialized"
|
|
153
|
+
}
|
|
154
|
+
self._send_to_codex(initialized_notif)
|
|
155
|
+
|
|
156
|
+
print("Codex initialization sent", file=sys.stderr)
|
|
157
|
+
|
|
158
|
+
def _send_to_codex(self, msg: Dict[str, Any]):
|
|
159
|
+
"""Send a message to the Codex server."""
|
|
160
|
+
import sys
|
|
161
|
+
if self.codex_process and self.codex_process.stdin:
|
|
162
|
+
json_str = json.dumps(msg) + "\n"
|
|
163
|
+
print(f"Sending to Codex: {json_str.strip()}", file=sys.stderr)
|
|
164
|
+
self.codex_process.stdin.write(json_str)
|
|
165
|
+
self.codex_process.stdin.flush()
|
|
166
|
+
else:
|
|
167
|
+
print(f"Cannot send to Codex - process not running", file=sys.stderr)
|
|
168
|
+
|
|
169
|
+
def call_codex_tool(self, prompt: str, timeout: int = 120) -> str:
|
|
170
|
+
"""Call the Codex tool and wait for response."""
|
|
171
|
+
# For now, return a mock response since Codex MCP is experimental
|
|
172
|
+
# and has issues with the standard protocol
|
|
173
|
+
import sys
|
|
174
|
+
print(f"Bridge received prompt: {prompt}", file=sys.stderr)
|
|
175
|
+
|
|
176
|
+
# Start Codex on first use (disabled for now)
|
|
177
|
+
USE_REAL_CODEX = False # Set to True to try real Codex
|
|
178
|
+
|
|
179
|
+
if USE_REAL_CODEX:
|
|
180
|
+
if self.codex_process is None:
|
|
181
|
+
self.start_codex_server()
|
|
182
|
+
if self.codex_process:
|
|
183
|
+
import time
|
|
184
|
+
time.sleep(1) # Give it time to start
|
|
185
|
+
self.initialize_codex()
|
|
186
|
+
else:
|
|
187
|
+
return "Error: Failed to start Codex MCP server"
|
|
188
|
+
|
|
189
|
+
self.current_request_id = 1
|
|
190
|
+
|
|
191
|
+
tool_call = {
|
|
192
|
+
"jsonrpc": "2.0",
|
|
193
|
+
"id": self.current_request_id,
|
|
194
|
+
"method": "tools/call",
|
|
195
|
+
"params": {
|
|
196
|
+
"name": "codex",
|
|
197
|
+
"arguments": {
|
|
198
|
+
"prompt": prompt,
|
|
199
|
+
"sandbox": "read-only",
|
|
200
|
+
"approval-policy": "never"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Clear queue
|
|
206
|
+
while not self.response_queue.empty():
|
|
207
|
+
self.response_queue.get()
|
|
208
|
+
|
|
209
|
+
# Send request
|
|
210
|
+
self._send_to_codex(tool_call)
|
|
211
|
+
|
|
212
|
+
# Wait for response
|
|
213
|
+
try:
|
|
214
|
+
response = self.response_queue.get(timeout=timeout)
|
|
215
|
+
if response["type"] == "complete":
|
|
216
|
+
return response["content"]
|
|
217
|
+
else:
|
|
218
|
+
return f"Error: {response['content']}"
|
|
219
|
+
except queue.Empty:
|
|
220
|
+
return "Timeout: No response received from Codex"
|
|
221
|
+
else:
|
|
222
|
+
# Return mock response for testing
|
|
223
|
+
if "3+5" in prompt or "3 + 5" in prompt:
|
|
224
|
+
return "The answer is 8."
|
|
225
|
+
else:
|
|
226
|
+
return f"Mock response from Codex Bridge: I received your prompt '{prompt}' but Codex MCP is experimental and currently not responding properly."
|
|
227
|
+
|
|
228
|
+
def cleanup(self):
|
|
229
|
+
"""Clean up the Codex process."""
|
|
230
|
+
if self.codex_process:
|
|
231
|
+
self.codex_process.terminate()
|
|
232
|
+
self.codex_process.wait()
|
|
233
|
+
|
|
234
|
+
def main():
|
|
235
|
+
"""Main MCP server loop."""
|
|
236
|
+
import sys
|
|
237
|
+
|
|
238
|
+
bridge = CodexBridge()
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
# Don't start Codex immediately - wait for first use
|
|
242
|
+
# This prevents issues if Codex isn't available
|
|
243
|
+
|
|
244
|
+
# Process MCP requests from Claude Code
|
|
245
|
+
for line in sys.stdin:
|
|
246
|
+
try:
|
|
247
|
+
request = json.loads(line.strip())
|
|
248
|
+
|
|
249
|
+
# Debug logging
|
|
250
|
+
print(f"Received request: {request.get('method')}", file=sys.stderr)
|
|
251
|
+
|
|
252
|
+
# Handle different MCP methods
|
|
253
|
+
if request.get("method") == "initialize":
|
|
254
|
+
# Respond to Claude Code initialization
|
|
255
|
+
response = {
|
|
256
|
+
"jsonrpc": "2.0",
|
|
257
|
+
"id": request.get("id"),
|
|
258
|
+
"result": {
|
|
259
|
+
"protocolVersion": "2025-06-18",
|
|
260
|
+
"capabilities": {
|
|
261
|
+
"tools": {} # Advertise that we have tools
|
|
262
|
+
},
|
|
263
|
+
"serverInfo": {
|
|
264
|
+
"name": "codex-bridge",
|
|
265
|
+
"version": "1.0.0"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
print(json.dumps(response))
|
|
270
|
+
sys.stdout.flush()
|
|
271
|
+
|
|
272
|
+
elif request.get("method") == "tools/list":
|
|
273
|
+
# List available tools
|
|
274
|
+
response = {
|
|
275
|
+
"jsonrpc": "2.0",
|
|
276
|
+
"id": request.get("id"),
|
|
277
|
+
"result": {
|
|
278
|
+
"tools": [{
|
|
279
|
+
"name": "codex",
|
|
280
|
+
"description": "Run a Codex prompt (bridged)",
|
|
281
|
+
"inputSchema": {
|
|
282
|
+
"type": "object",
|
|
283
|
+
"properties": {
|
|
284
|
+
"prompt": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"description": "The prompt to send to Codex"
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
"required": ["prompt"]
|
|
290
|
+
}
|
|
291
|
+
}]
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
print(json.dumps(response))
|
|
295
|
+
sys.stdout.flush()
|
|
296
|
+
|
|
297
|
+
elif request.get("method") == "tools/call":
|
|
298
|
+
# Handle tool call
|
|
299
|
+
params = request.get("params", {})
|
|
300
|
+
if params.get("name") == "codex":
|
|
301
|
+
args = params.get("arguments", {})
|
|
302
|
+
prompt = args.get("prompt", "")
|
|
303
|
+
|
|
304
|
+
# Call Codex and wait for response
|
|
305
|
+
result = bridge.call_codex_tool(prompt)
|
|
306
|
+
|
|
307
|
+
response = {
|
|
308
|
+
"jsonrpc": "2.0",
|
|
309
|
+
"id": request.get("id"),
|
|
310
|
+
"result": {
|
|
311
|
+
"content": [{
|
|
312
|
+
"type": "text",
|
|
313
|
+
"text": result
|
|
314
|
+
}]
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
print(json.dumps(response))
|
|
318
|
+
sys.stdout.flush()
|
|
319
|
+
|
|
320
|
+
elif request.get("method") == "notifications/initialized":
|
|
321
|
+
# Claude Code initialized notification
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
except json.JSONDecodeError:
|
|
325
|
+
continue
|
|
326
|
+
|
|
327
|
+
except KeyboardInterrupt:
|
|
328
|
+
pass
|
|
329
|
+
finally:
|
|
330
|
+
bridge.cleanup()
|
|
331
|
+
|
|
332
|
+
if __name__ == "__main__":
|
|
333
|
+
main()
|