claude-mpm 4.2.7__py3-none-any.whl → 4.2.11__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/dashboard.py +62 -120
- claude_mpm/cli/commands/monitor.py +71 -212
- claude_mpm/cli/commands/run.py +33 -33
- claude_mpm/cli/parser.py +79 -2
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/dashboard/static/css/code-tree.css +16 -4
- claude_mpm/dashboard/static/css/dashboard.css +15 -1
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/code-tree.js +775 -142
- claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
- claude_mpm/dashboard/static/js/dashboard.js +108 -91
- claude_mpm/dashboard/static/js/socket-client.js +9 -7
- claude_mpm/dashboard/templates/index.html +5 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
- claude_mpm/services/agents/deployment/agent_format_converter.py +3 -3
- claude_mpm/services/agents/deployment/agent_template_builder.py +3 -5
- claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +256 -0
- claude_mpm/services/monitor/event_emitter.py +279 -0
- claude_mpm/services/monitor/handlers/__init__.py +20 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
- claude_mpm/services/monitor/handlers/dashboard.py +298 -0
- claude_mpm/services/monitor/handlers/hooks.py +491 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +298 -0
- claude_mpm/services/monitor/server.py +442 -0
- claude_mpm/services/socketio/client_proxy.py +20 -12
- claude_mpm/services/socketio/dashboard_server.py +4 -4
- claude_mpm/services/socketio/monitor_client.py +4 -6
- claude_mpm/tools/code_tree_analyzer.py +33 -17
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +48 -43
- claude_mpm/cli/commands/socketio_monitor.py +0 -233
- claude_mpm/scripts/socketio_daemon.py +0 -571
- claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
- claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
- claude_mpm/scripts/socketio_server_manager.py +0 -349
- claude_mpm/services/cli/dashboard_launcher.py +0 -423
- claude_mpm/services/cli/socketio_manager.py +0 -595
- claude_mpm/services/dashboard/stable_server.py +0 -962
- claude_mpm/services/socketio/monitor_server.py +0 -505
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
"""Connection management service for Claude hook handler.
|
|
2
2
|
|
|
3
|
+
This service implements the SINGLE-PATH EVENT EMISSION ARCHITECTURE
|
|
4
|
+
to eliminate duplicate events and improve performance.
|
|
5
|
+
|
|
6
|
+
ARCHITECTURE: Hook → ConnectionManager → Direct Socket.IO → Dashboard
|
|
7
|
+
↓ (fallback only)
|
|
8
|
+
HTTP POST → Monitor → Dashboard
|
|
9
|
+
|
|
10
|
+
CRITICAL: This service must maintain the single emission path principle.
|
|
11
|
+
See docs/developer/EVENT_EMISSION_ARCHITECTURE.md for full documentation.
|
|
12
|
+
|
|
3
13
|
This service manages:
|
|
4
14
|
- SocketIO connection pool initialization
|
|
5
|
-
-
|
|
6
|
-
- Event emission through both channels
|
|
15
|
+
- Direct event emission with HTTP fallback
|
|
7
16
|
- Connection cleanup
|
|
8
17
|
"""
|
|
9
18
|
|
|
@@ -61,14 +70,8 @@ except ImportError:
|
|
|
61
70
|
)
|
|
62
71
|
|
|
63
72
|
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
from claude_mpm.services.event_bus import EventBus
|
|
67
|
-
|
|
68
|
-
EVENTBUS_AVAILABLE = True
|
|
69
|
-
except ImportError:
|
|
70
|
-
EVENTBUS_AVAILABLE = False
|
|
71
|
-
EventBus = None
|
|
73
|
+
# EventBus removed - using direct Socket.IO calls with HTTP fallback
|
|
74
|
+
# This eliminates duplicate events and improves performance
|
|
72
75
|
|
|
73
76
|
|
|
74
77
|
class ConnectionManagerService:
|
|
@@ -84,9 +87,7 @@ class ConnectionManagerService:
|
|
|
84
87
|
self.connection_pool = None
|
|
85
88
|
self._initialize_socketio_pool()
|
|
86
89
|
|
|
87
|
-
#
|
|
88
|
-
self.event_bus = None
|
|
89
|
-
self._initialize_eventbus()
|
|
90
|
+
# EventBus removed - using direct Socket.IO with HTTP fallback only
|
|
90
91
|
|
|
91
92
|
def _initialize_socketio_pool(self):
|
|
92
93
|
"""Initialize the SocketIO connection pool."""
|
|
@@ -102,25 +103,17 @@ class ConnectionManagerService:
|
|
|
102
103
|
)
|
|
103
104
|
self.connection_pool = None
|
|
104
105
|
|
|
105
|
-
def _initialize_eventbus(self):
|
|
106
|
-
"""Initialize the EventBus for in-process distribution."""
|
|
107
|
-
if EVENTBUS_AVAILABLE:
|
|
108
|
-
try:
|
|
109
|
-
self.event_bus = EventBus.get_instance()
|
|
110
|
-
if DEBUG:
|
|
111
|
-
print("✅ EventBus initialized for hook handler", file=sys.stderr)
|
|
112
|
-
except Exception as e:
|
|
113
|
-
if DEBUG:
|
|
114
|
-
print(f"⚠️ Failed to initialize EventBus: {e}", file=sys.stderr)
|
|
115
|
-
self.event_bus = None
|
|
116
|
-
|
|
117
106
|
def emit_event(self, namespace: str, event: str, data: dict):
|
|
118
|
-
"""Emit event through
|
|
107
|
+
"""Emit event through direct Socket.IO connection with HTTP fallback.
|
|
108
|
+
|
|
109
|
+
🚨 CRITICAL: This method implements the SINGLE-PATH EMISSION ARCHITECTURE.
|
|
110
|
+
DO NOT add additional emission paths (EventBus, etc.) as this creates duplicates.
|
|
111
|
+
See docs/developer/EVENT_EMISSION_ARCHITECTURE.md for details.
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
113
|
+
High-performance single-path approach:
|
|
114
|
+
- Primary: Direct Socket.IO connection for ultra-low latency
|
|
115
|
+
- Fallback: HTTP POST for reliability when direct connection fails
|
|
116
|
+
- Eliminates duplicate events from multiple emission paths
|
|
124
117
|
"""
|
|
125
118
|
# Create event data for normalization
|
|
126
119
|
raw_event = {
|
|
@@ -152,49 +145,51 @@ class ConnectionManagerService:
|
|
|
152
145
|
file=sys.stderr,
|
|
153
146
|
)
|
|
154
147
|
|
|
155
|
-
#
|
|
156
|
-
# This
|
|
148
|
+
# Emit through direct Socket.IO connection pool (primary path)
|
|
149
|
+
# This provides ultra-low latency direct async communication
|
|
157
150
|
if self.connection_pool:
|
|
158
151
|
try:
|
|
159
152
|
# Emit to Socket.IO server directly
|
|
160
153
|
self.connection_pool.emit("claude_event", claude_event_data)
|
|
161
154
|
if DEBUG:
|
|
162
155
|
print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
|
|
156
|
+
return # Success - no need for fallback
|
|
163
157
|
except Exception as e:
|
|
164
158
|
if DEBUG:
|
|
165
159
|
print(f"⚠️ Failed to emit via connection pool: {e}", file=sys.stderr)
|
|
166
160
|
|
|
167
|
-
#
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# Publish to EventBus with topic format: hook.{event}
|
|
171
|
-
topic = f"hook.{event}"
|
|
172
|
-
self.event_bus.publish(topic, claude_event_data)
|
|
161
|
+
# HTTP fallback for cross-process communication (when direct calls fail)
|
|
162
|
+
# This replaces EventBus for reliability without the complexity
|
|
163
|
+
self._try_http_fallback(claude_event_data)
|
|
173
164
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
189
|
-
except Exception as e:
|
|
165
|
+
def _try_http_fallback(self, claude_event_data: dict):
|
|
166
|
+
"""HTTP fallback when direct Socket.IO connection fails."""
|
|
167
|
+
try:
|
|
168
|
+
import requests
|
|
169
|
+
|
|
170
|
+
# Send to monitor server HTTP API
|
|
171
|
+
response = requests.post(
|
|
172
|
+
"http://localhost:8765/api/events",
|
|
173
|
+
json=claude_event_data,
|
|
174
|
+
timeout=2.0,
|
|
175
|
+
headers={"Content-Type": "application/json"},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if response.status_code in [200, 204]:
|
|
190
179
|
if DEBUG:
|
|
191
|
-
print(
|
|
192
|
-
|
|
180
|
+
print("✅ HTTP fallback successful", file=sys.stderr)
|
|
181
|
+
elif DEBUG:
|
|
182
|
+
print(
|
|
183
|
+
f"⚠️ HTTP fallback failed: {response.status_code}",
|
|
184
|
+
file=sys.stderr,
|
|
185
|
+
)
|
|
193
186
|
|
|
194
|
-
|
|
187
|
+
except Exception as e:
|
|
188
|
+
if DEBUG:
|
|
189
|
+
print(f"⚠️ HTTP fallback error: {e}", file=sys.stderr)
|
|
195
190
|
|
|
196
|
-
# Warn if
|
|
197
|
-
if not self.connection_pool and
|
|
191
|
+
# Warn if no emission method is available
|
|
192
|
+
if not self.connection_pool and DEBUG:
|
|
198
193
|
print(f"⚠️ No event emission method available for: {event}", file=sys.stderr)
|
|
199
194
|
|
|
200
195
|
def cleanup(self):
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
This service manages:
|
|
4
4
|
- HTTP POST event emission for ephemeral hook processes
|
|
5
|
-
- EventBus
|
|
6
|
-
- Event emission through both channels
|
|
5
|
+
- Direct event emission without EventBus complexity
|
|
7
6
|
|
|
8
7
|
DESIGN DECISION: Use stateless HTTP POST instead of persistent SocketIO
|
|
9
8
|
connections because hook handlers are ephemeral processes (< 1 second lifetime).
|
|
10
9
|
This eliminates disconnection issues and matches the process lifecycle.
|
|
11
10
|
"""
|
|
12
11
|
|
|
12
|
+
import asyncio
|
|
13
13
|
import os
|
|
14
14
|
import sys
|
|
15
15
|
from datetime import datetime
|
|
@@ -26,6 +26,15 @@ except ImportError:
|
|
|
26
26
|
REQUESTS_AVAILABLE = False
|
|
27
27
|
requests = None
|
|
28
28
|
|
|
29
|
+
# Import high-performance event emitter
|
|
30
|
+
try:
|
|
31
|
+
from claude_mpm.services.monitor.event_emitter import get_event_emitter
|
|
32
|
+
|
|
33
|
+
EVENT_EMITTER_AVAILABLE = True
|
|
34
|
+
except ImportError:
|
|
35
|
+
EVENT_EMITTER_AVAILABLE = False
|
|
36
|
+
get_event_emitter = None
|
|
37
|
+
|
|
29
38
|
# Import EventNormalizer for consistent event formatting
|
|
30
39
|
try:
|
|
31
40
|
from claude_mpm.services.socketio.event_normalizer import EventNormalizer
|
|
@@ -51,14 +60,8 @@ except ImportError:
|
|
|
51
60
|
)
|
|
52
61
|
|
|
53
62
|
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
from claude_mpm.services.event_bus import EventBus
|
|
57
|
-
|
|
58
|
-
EVENTBUS_AVAILABLE = True
|
|
59
|
-
except ImportError:
|
|
60
|
-
EVENTBUS_AVAILABLE = False
|
|
61
|
-
EventBus = None
|
|
63
|
+
# EventBus removed - using direct HTTP POST only
|
|
64
|
+
# This eliminates duplicate events and simplifies the architecture
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
class ConnectionManagerService:
|
|
@@ -74,9 +77,7 @@ class ConnectionManagerService:
|
|
|
74
77
|
self.server_port = int(os.environ.get("CLAUDE_MPM_SERVER_PORT", "8765"))
|
|
75
78
|
self.http_endpoint = f"http://{self.server_host}:{self.server_port}/api/events"
|
|
76
79
|
|
|
77
|
-
#
|
|
78
|
-
self.event_bus = None
|
|
79
|
-
self._initialize_eventbus()
|
|
80
|
+
# EventBus removed - using direct HTTP POST only
|
|
80
81
|
|
|
81
82
|
# For backward compatibility with tests
|
|
82
83
|
self.connection_pool = None # No longer used
|
|
@@ -87,26 +88,14 @@ class ConnectionManagerService:
|
|
|
87
88
|
file=sys.stderr,
|
|
88
89
|
)
|
|
89
90
|
|
|
90
|
-
def _initialize_eventbus(self):
|
|
91
|
-
"""Initialize the EventBus for in-process distribution."""
|
|
92
|
-
if EVENTBUS_AVAILABLE:
|
|
93
|
-
try:
|
|
94
|
-
self.event_bus = EventBus.get_instance()
|
|
95
|
-
if DEBUG:
|
|
96
|
-
print("✅ EventBus initialized for hook handler", file=sys.stderr)
|
|
97
|
-
except Exception as e:
|
|
98
|
-
if DEBUG:
|
|
99
|
-
print(f"⚠️ Failed to initialize EventBus: {e}", file=sys.stderr)
|
|
100
|
-
self.event_bus = None
|
|
101
|
-
|
|
102
91
|
def emit_event(self, namespace: str, event: str, data: dict):
|
|
103
|
-
"""Emit event using
|
|
92
|
+
"""Emit event using high-performance async emitter with HTTP fallback.
|
|
104
93
|
|
|
105
|
-
WHY
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
94
|
+
WHY Hybrid approach:
|
|
95
|
+
- Direct async calls for ultra-low latency in-process events
|
|
96
|
+
- HTTP POST fallback for cross-process communication
|
|
97
|
+
- Connection pooling for memory protection
|
|
98
|
+
- Automatic routing based on availability
|
|
110
99
|
"""
|
|
111
100
|
# Create event data for normalization
|
|
112
101
|
raw_event = {
|
|
@@ -138,52 +127,103 @@ class ConnectionManagerService:
|
|
|
138
127
|
file=sys.stderr,
|
|
139
128
|
)
|
|
140
129
|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
if
|
|
130
|
+
# Try high-performance async emitter first (direct calls)
|
|
131
|
+
success = self._try_async_emit(namespace, event, claude_event_data)
|
|
132
|
+
if success:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
# Fallback to HTTP POST for cross-process communication
|
|
136
|
+
self._try_http_emit(namespace, event, claude_event_data)
|
|
137
|
+
|
|
138
|
+
def _try_async_emit(self, namespace: str, event: str, data: dict) -> bool:
|
|
139
|
+
"""Try to emit event using high-performance async emitter."""
|
|
140
|
+
if not EVENT_EMITTER_AVAILABLE:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
# Run async emission in the current event loop or create one
|
|
145
|
+
loop = None
|
|
144
146
|
try:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
elif DEBUG and response.status_code != 204:
|
|
155
|
-
print(
|
|
156
|
-
f"⚠️ HTTP POST returned status {response.status_code} for: {event}",
|
|
157
|
-
file=sys.stderr,
|
|
158
|
-
)
|
|
159
|
-
except requests.exceptions.Timeout:
|
|
160
|
-
# Timeout is expected for fire-and-forget pattern
|
|
161
|
-
if DEBUG:
|
|
162
|
-
print(f"✅ HTTP POST sent (timeout OK): {event}", file=sys.stderr)
|
|
163
|
-
except requests.exceptions.ConnectionError:
|
|
164
|
-
# Server might not be running - this is OK
|
|
147
|
+
loop = asyncio.get_running_loop()
|
|
148
|
+
except RuntimeError:
|
|
149
|
+
# No running loop, create a new one
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
if loop:
|
|
153
|
+
# We're in an async context, create a task
|
|
154
|
+
task = loop.create_task(self._async_emit(namespace, event, data))
|
|
155
|
+
# Don't wait for completion to maintain low latency
|
|
165
156
|
if DEBUG:
|
|
166
|
-
print(f"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
157
|
+
print(f"✅ Async emit scheduled: {event}", file=sys.stderr)
|
|
158
|
+
return True
|
|
159
|
+
# No event loop, run synchronously
|
|
160
|
+
success = asyncio.run(self._async_emit(namespace, event, data))
|
|
161
|
+
if DEBUG and success:
|
|
162
|
+
print(f"✅ Async emit successful: {event}", file=sys.stderr)
|
|
163
|
+
return success
|
|
164
|
+
|
|
165
|
+
except Exception as e:
|
|
166
|
+
if DEBUG:
|
|
167
|
+
print(f"⚠️ Async emit failed: {e}", file=sys.stderr)
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
async def _async_emit(self, namespace: str, event: str, data: dict) -> bool:
|
|
171
|
+
"""Async helper for event emission."""
|
|
172
|
+
try:
|
|
173
|
+
emitter = await get_event_emitter()
|
|
174
|
+
return await emitter.emit_event(namespace, "claude_event", data)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
if DEBUG:
|
|
177
|
+
print(f"⚠️ Async emitter error: {e}", file=sys.stderr)
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
def _try_http_emit(self, namespace: str, event: str, data: dict):
|
|
181
|
+
"""Try to emit event using HTTP POST fallback."""
|
|
182
|
+
if not REQUESTS_AVAILABLE:
|
|
183
|
+
if DEBUG:
|
|
184
|
+
print(
|
|
185
|
+
"⚠️ requests module not available - cannot emit via HTTP",
|
|
186
|
+
file=sys.stderr,
|
|
187
|
+
)
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
# Create payload for HTTP API
|
|
192
|
+
payload = {
|
|
193
|
+
"namespace": namespace,
|
|
194
|
+
"event": "claude_event", # Standard event name for dashboard
|
|
195
|
+
"data": data,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# Send HTTP POST with reasonable timeout
|
|
199
|
+
response = requests.post(
|
|
200
|
+
self.http_endpoint,
|
|
201
|
+
json=payload,
|
|
202
|
+
timeout=2.0, # 2 second timeout
|
|
203
|
+
headers={"Content-Type": "application/json"},
|
|
174
204
|
)
|
|
175
205
|
|
|
176
|
-
|
|
177
|
-
if self.event_bus and EVENTBUS_AVAILABLE:
|
|
178
|
-
try:
|
|
179
|
-
# Publish to EventBus with topic format: hook.{event}
|
|
180
|
-
topic = f"hook.{event}"
|
|
181
|
-
self.event_bus.publish(topic, claude_event_data)
|
|
182
|
-
if DEBUG:
|
|
183
|
-
print(f"✅ Published to EventBus: {topic}", file=sys.stderr)
|
|
184
|
-
except Exception as e:
|
|
206
|
+
if response.status_code in [200, 204]:
|
|
185
207
|
if DEBUG:
|
|
186
|
-
print(f"
|
|
208
|
+
print(f"✅ HTTP POST successful: {event}", file=sys.stderr)
|
|
209
|
+
elif DEBUG:
|
|
210
|
+
print(
|
|
211
|
+
f"⚠️ HTTP POST failed with status {response.status_code}: {event}",
|
|
212
|
+
file=sys.stderr,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
except requests.exceptions.Timeout:
|
|
216
|
+
if DEBUG:
|
|
217
|
+
print(f"⚠️ HTTP POST timeout for: {event}", file=sys.stderr)
|
|
218
|
+
except requests.exceptions.ConnectionError:
|
|
219
|
+
if DEBUG:
|
|
220
|
+
print(
|
|
221
|
+
f"⚠️ HTTP POST connection failed for: {event} (server not running?)",
|
|
222
|
+
file=sys.stderr,
|
|
223
|
+
)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
if DEBUG:
|
|
226
|
+
print(f"⚠️ HTTP POST error for {event}: {e}", file=sys.stderr)
|
|
187
227
|
|
|
188
228
|
def cleanup(self):
|
|
189
229
|
"""Cleanup connections on service destruction."""
|
|
@@ -129,13 +129,13 @@ class AgentFormatConverter:
|
|
|
129
129
|
if isinstance(tools_line, str):
|
|
130
130
|
if tools_line.startswith("[") and tools_line.endswith("]"):
|
|
131
131
|
# Already in list format
|
|
132
|
-
|
|
132
|
+
pass
|
|
133
133
|
else:
|
|
134
134
|
# Convert comma-separated to list
|
|
135
135
|
tools = [tool.strip() for tool in tools_line.split(",")]
|
|
136
|
-
|
|
136
|
+
str(tools).replace("'", '"')
|
|
137
137
|
else:
|
|
138
|
-
|
|
138
|
+
pass
|
|
139
139
|
|
|
140
140
|
# Extract additional fields
|
|
141
141
|
model = self.extract_yaml_field(yaml_content, "model") or "sonnet"
|
|
@@ -265,7 +265,7 @@ class AgentTemplateBuilder:
|
|
|
265
265
|
has_core_tools = len(agent_tools.intersection(core_tools)) >= 5
|
|
266
266
|
|
|
267
267
|
# Include tools field only if agent is clearly restricted (missing core tools or very few tools)
|
|
268
|
-
|
|
268
|
+
not has_core_tools or len(agent_tools) < 6
|
|
269
269
|
|
|
270
270
|
# Build YAML frontmatter using Claude Code's compatible format
|
|
271
271
|
# ONLY include fields that Claude Code recognizes
|
|
@@ -587,7 +587,7 @@ tools:
|
|
|
587
587
|
|
|
588
588
|
# Combine enhanced description with examples
|
|
589
589
|
if examples:
|
|
590
|
-
description_parts = [enhanced_description, ""
|
|
590
|
+
description_parts = [enhanced_description, "", *examples]
|
|
591
591
|
else:
|
|
592
592
|
description_parts = [enhanced_description]
|
|
593
593
|
|
|
@@ -611,9 +611,7 @@ tools:
|
|
|
611
611
|
|
|
612
612
|
# Remove redundant spaces around punctuation
|
|
613
613
|
single_line = re.sub(r"\s+([,.!?;:])", r"\1", single_line)
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
return single_line
|
|
614
|
+
return re.sub(r"([,.!?;:])\s+", r"\1 ", single_line)
|
|
617
615
|
|
|
618
616
|
def _create_enhanced_description(
|
|
619
617
|
self,
|