claude-mpm 5.6.10__py3-none-any.whl → 5.6.33__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/commander.py +174 -4
- claude_mpm/cli/parsers/commander_parser.py +43 -10
- claude_mpm/cli/startup.py +140 -20
- claude_mpm/cli/startup_display.py +2 -1
- claude_mpm/commander/__init__.py +6 -0
- claude_mpm/commander/adapters/__init__.py +32 -3
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +98 -1
- claude_mpm/commander/adapters/claude_code.py +32 -1
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/app.py +32 -16
- claude_mpm/commander/api/routes/messages.py +11 -11
- claude_mpm/commander/api/routes/projects.py +20 -20
- claude_mpm/commander/api/routes/sessions.py +19 -21
- claude_mpm/commander/api/routes/work.py +86 -50
- claude_mpm/commander/api/schemas.py +4 -0
- claude_mpm/commander/chat/cli.py +42 -3
- claude_mpm/commander/config.py +5 -3
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +215 -10
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/frameworks/base.py +4 -1
- claude_mpm/commander/instance_manager.py +124 -11
- claude_mpm/commander/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/registry.py +10 -4
- claude_mpm/commander/runtime/monitor.py +32 -2
- claude_mpm/commander/work/executor.py +38 -20
- claude_mpm/commander/workflow/event_handler.py +25 -3
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +3 -3
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +37 -7
- claude_mpm/core/socketio_pool.py +13 -5
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +284 -89
- claude_mpm/hooks/claude_hooks/hook_handler.py +81 -32
- claude_mpm/hooks/claude_hooks/installer.py +90 -28
- claude_mpm/hooks/claude_hooks/memory_integration.py +1 -1
- claude_mpm/hooks/claude_hooks/response_tracking.py +1 -1
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +2 -2
- claude_mpm/hooks/claude_hooks/services/container.py +310 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +2 -2
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +6 -6
- claude_mpm/scripts/claude-hook-handler.sh +3 -3
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/pm_skills_deployer.py +3 -2
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/registry.py +295 -90
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/METADATA +5 -3
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/RECORD +91 -94
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/WHEEL +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/top_level.txt +0 -0
|
@@ -4,25 +4,39 @@ This module implements REST endpoints for managing work items
|
|
|
4
4
|
in project work queues.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import List, Optional
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
8
|
|
|
9
|
-
from fastapi import APIRouter, HTTPException, Query
|
|
9
|
+
from fastapi import APIRouter, HTTPException, Query, Request
|
|
10
10
|
|
|
11
11
|
from ...models.work import WorkPriority, WorkState
|
|
12
12
|
from ...work import WorkQueue
|
|
13
|
-
from ..errors import ProjectNotFoundError
|
|
14
13
|
from ..schemas import CreateWorkRequest, WorkItemResponse
|
|
15
14
|
|
|
16
15
|
router = APIRouter()
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
def _get_registry():
|
|
20
|
-
"""Get registry instance from app
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if registry is None:
|
|
18
|
+
def _get_registry(request: Request):
|
|
19
|
+
"""Get registry instance from app.state."""
|
|
20
|
+
if not hasattr(request.app.state, "registry") or request.app.state.registry is None:
|
|
24
21
|
raise RuntimeError("Registry not initialized")
|
|
25
|
-
return registry
|
|
22
|
+
return request.app.state.registry
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_work_queues(request: Request) -> Dict:
|
|
26
|
+
"""Get work queues dict from app.state (shared with daemon)."""
|
|
27
|
+
if (
|
|
28
|
+
not hasattr(request.app.state, "work_queues")
|
|
29
|
+
or request.app.state.work_queues is None
|
|
30
|
+
):
|
|
31
|
+
raise RuntimeError("Work queues not initialized")
|
|
32
|
+
return request.app.state.work_queues
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_daemon(request: Request):
|
|
36
|
+
"""Get daemon instance from app.state."""
|
|
37
|
+
if not hasattr(request.app.state, "daemon_instance"):
|
|
38
|
+
return None
|
|
39
|
+
return request.app.state.daemon_instance
|
|
26
40
|
|
|
27
41
|
|
|
28
42
|
def _work_item_to_response(work_item) -> WorkItemResponse:
|
|
@@ -51,10 +65,13 @@ def _work_item_to_response(work_item) -> WorkItemResponse:
|
|
|
51
65
|
|
|
52
66
|
|
|
53
67
|
@router.post("/projects/{project_id}/work", response_model=WorkItemResponse)
|
|
54
|
-
async def add_work(
|
|
68
|
+
async def add_work(
|
|
69
|
+
request: Request, project_id: str, work: CreateWorkRequest
|
|
70
|
+
) -> WorkItemResponse:
|
|
55
71
|
"""Add work item to project queue.
|
|
56
72
|
|
|
57
73
|
Args:
|
|
74
|
+
request: FastAPI request (for accessing app.state)
|
|
58
75
|
project_id: Project identifier
|
|
59
76
|
work: Work item creation request
|
|
60
77
|
|
|
@@ -80,22 +97,29 @@ async def add_work(project_id: str, work: CreateWorkRequest) -> WorkItemResponse
|
|
|
80
97
|
...
|
|
81
98
|
}
|
|
82
99
|
"""
|
|
83
|
-
registry = _get_registry()
|
|
100
|
+
registry = _get_registry(request)
|
|
101
|
+
work_queues = _get_work_queues(request)
|
|
102
|
+
daemon = _get_daemon(request)
|
|
84
103
|
|
|
85
104
|
# Get project
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
raise HTTPException(status_code=404, detail=str(e)) from e
|
|
105
|
+
project = registry.get(project_id)
|
|
106
|
+
if project is None:
|
|
107
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
90
108
|
|
|
91
|
-
# Get or create work queue
|
|
92
|
-
|
|
93
|
-
# For now, we'll need to integrate with project's work queue
|
|
94
|
-
# Access or create work queue
|
|
95
|
-
if not hasattr(project, "_work_queue"):
|
|
96
|
-
project._work_queue = WorkQueue(project_id)
|
|
109
|
+
# Get or create work queue (shared with daemon)
|
|
110
|
+
import logging
|
|
97
111
|
|
|
98
|
-
|
|
112
|
+
logger = logging.getLogger(__name__)
|
|
113
|
+
logger.info(
|
|
114
|
+
f"work_queues dict id: {id(work_queues)}, keys: {list(work_queues.keys())}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if project_id not in work_queues:
|
|
118
|
+
logger.info(f"Creating new work queue for {project_id}")
|
|
119
|
+
work_queues[project_id] = WorkQueue(project_id)
|
|
120
|
+
logger.info(f"After creation, work_queues keys: {list(work_queues.keys())}")
|
|
121
|
+
|
|
122
|
+
queue = work_queues[project_id]
|
|
99
123
|
|
|
100
124
|
# Convert priority int to enum
|
|
101
125
|
priority = WorkPriority(work.priority)
|
|
@@ -105,16 +129,25 @@ async def add_work(project_id: str, work: CreateWorkRequest) -> WorkItemResponse
|
|
|
105
129
|
content=work.content, priority=priority, depends_on=work.depends_on
|
|
106
130
|
)
|
|
107
131
|
|
|
132
|
+
# Ensure daemon has a session for this project (creates if needed)
|
|
133
|
+
if daemon and not daemon.sessions.get(project_id):
|
|
134
|
+
# Session creation will be handled by daemon's main loop
|
|
135
|
+
# when it detects work in the queue
|
|
136
|
+
pass
|
|
137
|
+
|
|
108
138
|
return _work_item_to_response(work_item)
|
|
109
139
|
|
|
110
140
|
|
|
111
141
|
@router.get("/projects/{project_id}/work", response_model=List[WorkItemResponse])
|
|
112
142
|
async def list_work(
|
|
113
|
-
|
|
143
|
+
request: Request,
|
|
144
|
+
project_id: str,
|
|
145
|
+
state: Optional[str] = Query(None, description="Filter by state"),
|
|
114
146
|
) -> List[WorkItemResponse]:
|
|
115
147
|
"""List work items for project.
|
|
116
148
|
|
|
117
149
|
Args:
|
|
150
|
+
request: FastAPI request (for accessing app.state)
|
|
118
151
|
project_id: Project identifier
|
|
119
152
|
state: Optional state filter (pending, queued, in_progress, etc.)
|
|
120
153
|
|
|
@@ -136,19 +169,20 @@ async def list_work(
|
|
|
136
169
|
{"id": "work-1", "state": "queued", ...}
|
|
137
170
|
]
|
|
138
171
|
"""
|
|
139
|
-
registry = _get_registry()
|
|
172
|
+
registry = _get_registry(request)
|
|
173
|
+
work_queues = _get_work_queues(request)
|
|
140
174
|
|
|
141
175
|
# Get project
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
raise HTTPException(status_code=404, detail=str(e)) from e
|
|
176
|
+
project = registry.get(project_id)
|
|
177
|
+
if project is None:
|
|
178
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
146
179
|
|
|
147
|
-
# Get work queue
|
|
148
|
-
if not
|
|
149
|
-
|
|
180
|
+
# Get work queue (shared with daemon)
|
|
181
|
+
if project_id not in work_queues:
|
|
182
|
+
# Return empty list if no work queue exists yet
|
|
183
|
+
return []
|
|
150
184
|
|
|
151
|
-
queue =
|
|
185
|
+
queue = work_queues[project_id]
|
|
152
186
|
|
|
153
187
|
# Parse state filter
|
|
154
188
|
state_filter = None
|
|
@@ -169,10 +203,11 @@ async def list_work(
|
|
|
169
203
|
|
|
170
204
|
|
|
171
205
|
@router.get("/projects/{project_id}/work/{work_id}", response_model=WorkItemResponse)
|
|
172
|
-
async def get_work(project_id: str, work_id: str) -> WorkItemResponse:
|
|
206
|
+
async def get_work(request: Request, project_id: str, work_id: str) -> WorkItemResponse:
|
|
173
207
|
"""Get work item details.
|
|
174
208
|
|
|
175
209
|
Args:
|
|
210
|
+
request: FastAPI request (for accessing app.state)
|
|
176
211
|
project_id: Project identifier
|
|
177
212
|
work_id: Work item identifier
|
|
178
213
|
|
|
@@ -191,19 +226,19 @@ async def get_work(project_id: str, work_id: str) -> WorkItemResponse:
|
|
|
191
226
|
...
|
|
192
227
|
}
|
|
193
228
|
"""
|
|
194
|
-
registry = _get_registry()
|
|
229
|
+
registry = _get_registry(request)
|
|
230
|
+
work_queues = _get_work_queues(request)
|
|
195
231
|
|
|
196
232
|
# Get project
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
raise HTTPException(status_code=404, detail=str(e)) from e
|
|
233
|
+
project = registry.get(project_id)
|
|
234
|
+
if project is None:
|
|
235
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
201
236
|
|
|
202
|
-
# Get work queue
|
|
203
|
-
if not
|
|
237
|
+
# Get work queue (shared with daemon)
|
|
238
|
+
if project_id not in work_queues:
|
|
204
239
|
raise HTTPException(status_code=404, detail="Work queue not found")
|
|
205
240
|
|
|
206
|
-
queue =
|
|
241
|
+
queue = work_queues[project_id]
|
|
207
242
|
|
|
208
243
|
# Get work item
|
|
209
244
|
work_item = queue.get(work_id)
|
|
@@ -214,10 +249,11 @@ async def get_work(project_id: str, work_id: str) -> WorkItemResponse:
|
|
|
214
249
|
|
|
215
250
|
|
|
216
251
|
@router.post("/projects/{project_id}/work/{work_id}/cancel")
|
|
217
|
-
async def cancel_work(project_id: str, work_id: str) -> dict:
|
|
252
|
+
async def cancel_work(request: Request, project_id: str, work_id: str) -> dict:
|
|
218
253
|
"""Cancel pending work item.
|
|
219
254
|
|
|
220
255
|
Args:
|
|
256
|
+
request: FastAPI request (for accessing app.state)
|
|
221
257
|
project_id: Project identifier
|
|
222
258
|
work_id: Work item identifier
|
|
223
259
|
|
|
@@ -231,19 +267,19 @@ async def cancel_work(project_id: str, work_id: str) -> dict:
|
|
|
231
267
|
POST /api/projects/proj-123/work/work-xyz/cancel
|
|
232
268
|
Response: {"status": "cancelled", "id": "work-xyz"}
|
|
233
269
|
"""
|
|
234
|
-
registry = _get_registry()
|
|
270
|
+
registry = _get_registry(request)
|
|
271
|
+
work_queues = _get_work_queues(request)
|
|
235
272
|
|
|
236
273
|
# Get project
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
raise HTTPException(status_code=404, detail=str(e)) from e
|
|
274
|
+
project = registry.get(project_id)
|
|
275
|
+
if project is None:
|
|
276
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
241
277
|
|
|
242
|
-
# Get work queue
|
|
243
|
-
if not
|
|
278
|
+
# Get work queue (shared with daemon)
|
|
279
|
+
if project_id not in work_queues:
|
|
244
280
|
raise HTTPException(status_code=404, detail="Work queue not found")
|
|
245
281
|
|
|
246
|
-
queue =
|
|
282
|
+
queue = work_queues[project_id]
|
|
247
283
|
|
|
248
284
|
# Cancel work item
|
|
249
285
|
if not queue.cancel(work_id):
|
|
@@ -17,10 +17,14 @@ class RegisterProjectRequest(BaseModel):
|
|
|
17
17
|
|
|
18
18
|
Attributes:
|
|
19
19
|
path: Filesystem path to project directory
|
|
20
|
+
project_id: Optional project identifier (UUID generated if omitted)
|
|
20
21
|
name: Optional display name (derived from path if omitted)
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
path: str = Field(..., description="Filesystem path to project")
|
|
25
|
+
project_id: Optional[str] = Field(
|
|
26
|
+
None, description="Project identifier (UUID generated if omitted)"
|
|
27
|
+
)
|
|
24
28
|
name: Optional[str] = Field(
|
|
25
29
|
None, description="Display name (derived from path if omitted)"
|
|
26
30
|
)
|
claude_mpm/commander/chat/cli.py
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
9
|
+
from claude_mpm.commander.env_loader import load_env
|
|
8
10
|
from claude_mpm.commander.instance_manager import InstanceManager
|
|
9
11
|
from claude_mpm.commander.llm.openrouter_client import (
|
|
10
12
|
OpenRouterClient,
|
|
@@ -19,23 +21,53 @@ from claude_mpm.commander.tmux_orchestrator import TmuxOrchestrator
|
|
|
19
21
|
|
|
20
22
|
from .repl import CommanderREPL
|
|
21
23
|
|
|
24
|
+
# Load environment variables at module import
|
|
25
|
+
load_env()
|
|
26
|
+
|
|
22
27
|
logger = logging.getLogger(__name__)
|
|
23
28
|
|
|
24
29
|
|
|
30
|
+
@dataclass
|
|
31
|
+
class CommanderCLIConfig:
|
|
32
|
+
"""Configuration for Commander CLI mode.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
summarize_responses: Whether to use LLM to summarize instance responses
|
|
36
|
+
port: Port for internal services (reserved for future use)
|
|
37
|
+
state_dir: Directory for state persistence (optional)
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> config = CommanderCLIConfig(summarize_responses=False)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
summarize_responses: bool = True
|
|
44
|
+
port: int = 8765
|
|
45
|
+
state_dir: Optional[Path] = None
|
|
46
|
+
|
|
47
|
+
|
|
25
48
|
async def run_commander(
|
|
26
49
|
port: int = 8765,
|
|
27
50
|
state_dir: Optional[Path] = None,
|
|
51
|
+
config: Optional[CommanderCLIConfig] = None,
|
|
28
52
|
) -> None:
|
|
29
53
|
"""Run Commander in interactive mode.
|
|
30
54
|
|
|
31
55
|
Args:
|
|
32
56
|
port: Port for internal services (unused currently).
|
|
33
57
|
state_dir: Directory for state persistence (optional).
|
|
58
|
+
config: Commander CLI configuration (optional, uses defaults if None).
|
|
34
59
|
|
|
35
60
|
Example:
|
|
36
61
|
>>> asyncio.run(run_commander())
|
|
37
62
|
# Starts interactive Commander REPL
|
|
63
|
+
>>> config = CommanderCLIConfig(summarize_responses=False)
|
|
64
|
+
>>> asyncio.run(run_commander(config=config))
|
|
65
|
+
# Starts Commander without response summarization
|
|
38
66
|
"""
|
|
67
|
+
# Use default config if not provided
|
|
68
|
+
if config is None:
|
|
69
|
+
config = CommanderCLIConfig(port=port, state_dir=state_dir)
|
|
70
|
+
|
|
39
71
|
# Setup logging
|
|
40
72
|
logging.basicConfig(
|
|
41
73
|
level=logging.INFO,
|
|
@@ -57,8 +89,8 @@ async def run_commander(
|
|
|
57
89
|
# Try to initialize LLM client (optional)
|
|
58
90
|
llm_client: Optional[OpenRouterClient] = None
|
|
59
91
|
try:
|
|
60
|
-
|
|
61
|
-
llm_client = OpenRouterClient(
|
|
92
|
+
llm_config = OpenRouterConfig()
|
|
93
|
+
llm_client = OpenRouterClient(llm_config)
|
|
62
94
|
logger.info("LLM client initialized")
|
|
63
95
|
except ValueError as e:
|
|
64
96
|
logger.warning(f"LLM client not available: {e}")
|
|
@@ -68,7 +100,14 @@ async def run_commander(
|
|
|
68
100
|
output_relay: Optional[OutputRelay] = None
|
|
69
101
|
if llm_client:
|
|
70
102
|
try:
|
|
71
|
-
summarizer
|
|
103
|
+
# Only create summarizer if summarize_responses is enabled
|
|
104
|
+
summarizer = None
|
|
105
|
+
if config.summarize_responses:
|
|
106
|
+
summarizer = OutputSummarizer(llm_client)
|
|
107
|
+
logger.info("Response summarization enabled")
|
|
108
|
+
else:
|
|
109
|
+
logger.info("Response summarization disabled")
|
|
110
|
+
|
|
72
111
|
handler = OutputHandler(orchestrator, summarizer)
|
|
73
112
|
formatter = OutputFormatter()
|
|
74
113
|
output_relay = OutputRelay(handler, formatter)
|
claude_mpm/commander/config.py
CHANGED
|
@@ -14,28 +14,30 @@ class DaemonConfig:
|
|
|
14
14
|
|
|
15
15
|
Attributes:
|
|
16
16
|
host: API server bind address
|
|
17
|
-
port: API server port
|
|
17
|
+
port: API server port (default: 8766 from NetworkPorts.COMMANDER_DEFAULT)
|
|
18
18
|
log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
|
|
19
19
|
state_dir: Directory for state persistence
|
|
20
20
|
max_projects: Maximum concurrent projects
|
|
21
21
|
healthcheck_interval: Healthcheck interval in seconds
|
|
22
22
|
save_interval: State persistence interval in seconds
|
|
23
23
|
poll_interval: Event polling interval in seconds
|
|
24
|
+
summarize_responses: Whether to use LLM to summarize instance responses
|
|
24
25
|
|
|
25
26
|
Example:
|
|
26
|
-
>>> config = DaemonConfig(port=
|
|
27
|
+
>>> config = DaemonConfig(port=8766, log_level="DEBUG")
|
|
27
28
|
>>> config.state_dir
|
|
28
29
|
PosixPath('/Users/user/.claude-mpm/commander')
|
|
29
30
|
"""
|
|
30
31
|
|
|
31
32
|
host: str = "127.0.0.1"
|
|
32
|
-
port: int =
|
|
33
|
+
port: int = 8766 # Default commander port (from network_config.NetworkPorts.COMMANDER_DEFAULT)
|
|
33
34
|
log_level: str = "INFO"
|
|
34
35
|
state_dir: Path = Path.home() / ".claude-mpm" / "commander"
|
|
35
36
|
max_projects: int = 10
|
|
36
37
|
healthcheck_interval: int = 30
|
|
37
38
|
save_interval: int = 30
|
|
38
39
|
poll_interval: float = 2.0
|
|
40
|
+
summarize_responses: bool = True
|
|
39
41
|
|
|
40
42
|
def __post_init__(self):
|
|
41
43
|
"""Ensure state_dir is a Path object and create if needed."""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Core coordination components for MPM Commander.
|
|
2
|
+
|
|
3
|
+
This module provides core components that coordinate between different
|
|
4
|
+
subsystems like events, work execution, and session management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .block_manager import BlockManager
|
|
8
|
+
from .response_manager import ResponseManager, ResponseRoute
|
|
9
|
+
|
|
10
|
+
__all__ = ["BlockManager", "ResponseManager", "ResponseRoute"]
|