claude-task-master 0.1.3__py3-none-any.whl → 0.1.5__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.
Files changed (31) hide show
  1. claude_task_master/__init__.py +1 -1
  2. claude_task_master/api/models.py +309 -0
  3. claude_task_master/api/routes.py +229 -0
  4. claude_task_master/api/routes_repo.py +317 -0
  5. claude_task_master/bin/claudetm +1 -1
  6. claude_task_master/cli.py +3 -1
  7. claude_task_master/cli_commands/mailbox.py +295 -0
  8. claude_task_master/cli_commands/workflow.py +37 -0
  9. claude_task_master/core/__init__.py +5 -0
  10. claude_task_master/core/agent_phases.py +1 -1
  11. claude_task_master/core/orchestrator.py +432 -9
  12. claude_task_master/core/parallel.py +4 -4
  13. claude_task_master/core/plan_updater.py +199 -0
  14. claude_task_master/core/pr_context.py +179 -64
  15. claude_task_master/core/prompts.py +4 -0
  16. claude_task_master/core/prompts_plan_update.py +148 -0
  17. claude_task_master/core/state.py +5 -1
  18. claude_task_master/core/workflow_stages.py +229 -22
  19. claude_task_master/github/client_pr.py +86 -20
  20. claude_task_master/mailbox/__init__.py +23 -0
  21. claude_task_master/mailbox/merger.py +163 -0
  22. claude_task_master/mailbox/models.py +95 -0
  23. claude_task_master/mailbox/storage.py +209 -0
  24. claude_task_master/mcp/server.py +183 -0
  25. claude_task_master/mcp/tools.py +921 -0
  26. claude_task_master/webhooks/events.py +356 -2
  27. {claude_task_master-0.1.3.dist-info → claude_task_master-0.1.5.dist-info}/METADATA +223 -4
  28. {claude_task_master-0.1.3.dist-info → claude_task_master-0.1.5.dist-info}/RECORD +31 -23
  29. {claude_task_master-0.1.3.dist-info → claude_task_master-0.1.5.dist-info}/WHEEL +1 -1
  30. {claude_task_master-0.1.3.dist-info → claude_task_master-0.1.5.dist-info}/entry_points.txt +0 -0
  31. {claude_task_master-0.1.3.dist-info → claude_task_master-0.1.5.dist-info}/top_level.txt +0 -0
@@ -3,5 +3,5 @@
3
3
  Uses Claude Agent SDK to keep Claude working until a goal is achieved.
4
4
  """
5
5
 
6
- __version__ = "0.1.3"
6
+ __version__ = "0.1.5"
7
7
  __all__ = ["__version__"]
@@ -9,6 +9,9 @@ Request Models:
9
9
  - ResumeRequest: Resume a paused/blocked task
10
10
  - ConfigUpdateRequest: Update task configuration
11
11
  - TaskInitRequest: Initialize a new task
12
+ - CloneRepoRequest: Clone a git repository to the workspace
13
+ - SetupRepoRequest: Set up a cloned repository for development
14
+ - PlanRepoRequest: Create a plan for a repository (read-only)
12
15
 
13
16
  Response Models:
14
17
  - TaskStatusResponse: Full task status information
@@ -19,17 +22,26 @@ Response Models:
19
22
  - ContextResponse: Accumulated context/learnings
20
23
  - HealthResponse: Server health status
21
24
  - ErrorResponse: Standard error response
25
+ - CloneRepoResponse: Result of cloning a repository
26
+ - SetupRepoResponse: Result of setting up a repository
27
+ - PlanRepoResponse: Result of planning for a repository
22
28
 
23
29
  Usage:
24
30
  from claude_task_master.api.models import (
25
31
  PauseRequest,
26
32
  TaskStatusResponse,
27
33
  ErrorResponse,
34
+ CloneRepoRequest,
35
+ CloneRepoResponse,
28
36
  )
29
37
 
30
38
  @app.post("/control/pause", response_model=ControlResponse)
31
39
  async def pause_task(request: PauseRequest):
32
40
  ...
41
+
42
+ @app.post("/repo/clone", response_model=CloneRepoResponse)
43
+ async def clone_repo(request: CloneRepoRequest):
44
+ ...
33
45
  """
34
46
 
35
47
  from __future__ import annotations
@@ -246,6 +258,120 @@ class TaskInitRequest(BaseModel):
246
258
  )
247
259
 
248
260
 
261
+ # =============================================================================
262
+ # Repo Setup Request Models
263
+ # =============================================================================
264
+
265
+
266
+ class CloneRepoRequest(BaseModel):
267
+ """Request model for cloning a git repository.
268
+
269
+ Clones a repository to the workspace for AI developer environments.
270
+ Default target is ~/workspace/claude-task-master/{repo-name}.
271
+
272
+ Attributes:
273
+ url: Git repository URL (HTTPS or SSH format).
274
+ target_dir: Optional custom target directory path.
275
+ If not provided, defaults to ~/workspace/claude-task-master/{repo-name}.
276
+ branch: Optional branch to checkout after cloning.
277
+ """
278
+
279
+ url: str = Field(
280
+ ...,
281
+ min_length=1,
282
+ max_length=2048,
283
+ description="Git repository URL (HTTPS or SSH format)",
284
+ examples=[
285
+ "https://github.com/user/repo.git",
286
+ "git@github.com:user/repo.git",
287
+ ],
288
+ )
289
+ target_dir: str | None = Field(
290
+ default=None,
291
+ max_length=4096,
292
+ description="Optional custom target directory path. "
293
+ "Defaults to ~/workspace/claude-task-master/{repo-name}",
294
+ examples=[
295
+ "~/workspace/claude-task-master/my-project",
296
+ "/home/user/projects/my-app",
297
+ ],
298
+ )
299
+ branch: str | None = Field(
300
+ default=None,
301
+ max_length=256,
302
+ description="Optional branch to checkout after cloning",
303
+ examples=["main", "develop", "feature/new-feature"],
304
+ )
305
+
306
+
307
+ class SetupRepoRequest(BaseModel):
308
+ """Request model for setting up a cloned repository for development.
309
+
310
+ Detects the project type and performs appropriate setup:
311
+ - Creates virtual environment (for Python projects)
312
+ - Installs dependencies (pip, npm, pnpm, yarn, bun)
313
+ - Runs setup scripts (setup-hooks.sh, setup.sh, etc.)
314
+
315
+ Attributes:
316
+ work_dir: Path to the cloned repository directory.
317
+ """
318
+
319
+ work_dir: str = Field(
320
+ ...,
321
+ min_length=1,
322
+ max_length=4096,
323
+ description="Path to the cloned repository directory to set up",
324
+ examples=[
325
+ "~/workspace/claude-task-master/my-project",
326
+ "/home/user/projects/my-app",
327
+ ],
328
+ )
329
+
330
+
331
+ class PlanRepoRequest(BaseModel):
332
+ """Request model for creating a plan for a repository.
333
+
334
+ Creates a plan without executing any work. Uses read-only tools
335
+ (Read, Glob, Grep) to analyze the codebase and outputs a structured
336
+ plan with tasks and success criteria.
337
+
338
+ Use this after cloning and setting up a repo to plan work before
339
+ execution, or to get a plan for a new goal in an existing repository.
340
+
341
+ Attributes:
342
+ work_dir: Path to the repository directory to plan for.
343
+ goal: The goal/task description to plan for.
344
+ model: Model to use for planning (default: opus for best quality).
345
+ """
346
+
347
+ work_dir: str = Field(
348
+ ...,
349
+ min_length=1,
350
+ max_length=4096,
351
+ description="Path to the repository directory to plan for",
352
+ examples=[
353
+ "~/workspace/claude-task-master/my-project",
354
+ "/home/user/projects/my-app",
355
+ ],
356
+ )
357
+ goal: str = Field(
358
+ ...,
359
+ min_length=1,
360
+ max_length=10000,
361
+ description="The goal/task description to plan for",
362
+ examples=[
363
+ "Implement user authentication with JWT",
364
+ "Add dark mode support to the UI",
365
+ "Fix the database connection pooling issue",
366
+ ],
367
+ )
368
+ model: str = Field(
369
+ default="opus",
370
+ pattern="^(opus|sonnet|haiku)$",
371
+ description="Model to use for planning (opus, sonnet, haiku)",
372
+ )
373
+
374
+
249
375
  # =============================================================================
250
376
  # Response Models - Nested Components
251
377
  # =============================================================================
@@ -551,3 +677,186 @@ class APIInfo(BaseModel):
551
677
  version: str
552
678
  description: str = "REST API for Claude Task Master task orchestration"
553
679
  docs_url: str | None = "/docs"
680
+
681
+
682
+ # =============================================================================
683
+ # Mailbox Request/Response Models
684
+ # =============================================================================
685
+
686
+
687
+ class SendMailboxMessageRequest(BaseModel):
688
+ """Request model for sending a message to the mailbox.
689
+
690
+ Attributes:
691
+ content: The message content describing the change request.
692
+ sender: Identifier of the sender (default: "anonymous").
693
+ priority: Message priority (0=low, 1=normal, 2=high, 3=urgent).
694
+ metadata: Optional additional metadata.
695
+ """
696
+
697
+ content: str = Field(
698
+ ...,
699
+ min_length=1,
700
+ max_length=100000,
701
+ description="The message content describing the change request",
702
+ examples=["Please also add tests for the new feature", "Prioritize the bug fix"],
703
+ )
704
+ sender: str = Field(
705
+ default="anonymous",
706
+ max_length=256,
707
+ description="Identifier of the sender",
708
+ examples=["supervisor-agent", "user@example.com", "monitoring-system"],
709
+ )
710
+ priority: int = Field(
711
+ default=1,
712
+ ge=0,
713
+ le=3,
714
+ description="Message priority (0=low, 1=normal, 2=high, 3=urgent)",
715
+ )
716
+ metadata: dict[str, Any] | None = Field(
717
+ default=None,
718
+ description="Optional additional metadata",
719
+ )
720
+
721
+
722
+ class SendMailboxMessageResponse(BaseModel):
723
+ """Response model for sending a message to the mailbox.
724
+
725
+ Attributes:
726
+ success: Whether the message was sent successfully.
727
+ message_id: The ID of the created message.
728
+ message: Human-readable result message.
729
+ error: Error message if request failed.
730
+ """
731
+
732
+ success: bool
733
+ message_id: str | None = None
734
+ message: str | None = None
735
+ error: str | None = None
736
+
737
+
738
+ class MailboxMessagePreview(BaseModel):
739
+ """Preview of a mailbox message for status responses.
740
+
741
+ Attributes:
742
+ id: Message ID.
743
+ sender: Message sender.
744
+ content_preview: Truncated message content.
745
+ priority: Message priority level.
746
+ timestamp: When the message was created.
747
+ """
748
+
749
+ id: str
750
+ sender: str
751
+ content_preview: str
752
+ priority: int
753
+ timestamp: datetime
754
+
755
+
756
+ class MailboxStatusResponse(BaseModel):
757
+ """Response model for mailbox status check.
758
+
759
+ Attributes:
760
+ success: Whether the request succeeded.
761
+ count: Number of pending messages.
762
+ messages: List of message previews.
763
+ last_checked: When the mailbox was last checked.
764
+ total_messages_received: Total count of messages ever received.
765
+ error: Error message if request failed.
766
+ """
767
+
768
+ success: bool
769
+ count: int = 0
770
+ messages: list[MailboxMessagePreview] = []
771
+ last_checked: datetime | None = None
772
+ total_messages_received: int = 0
773
+ error: str | None = None
774
+
775
+
776
+ class ClearMailboxResponse(BaseModel):
777
+ """Response model for clearing the mailbox.
778
+
779
+ Attributes:
780
+ success: Whether the operation succeeded.
781
+ messages_cleared: Number of messages that were cleared.
782
+ message: Human-readable result message.
783
+ error: Error message if operation failed.
784
+ """
785
+
786
+ success: bool
787
+ messages_cleared: int = 0
788
+ message: str | None = None
789
+ error: str | None = None
790
+
791
+
792
+ # =============================================================================
793
+ # Repo Setup Response Models
794
+ # =============================================================================
795
+
796
+
797
+ class CloneRepoResponse(BaseModel):
798
+ """Response model for cloning a git repository.
799
+
800
+ Attributes:
801
+ success: Whether the clone operation succeeded.
802
+ message: Human-readable result message.
803
+ repo_url: The repository URL that was cloned.
804
+ target_dir: The directory where the repo was cloned to.
805
+ branch: The branch that was checked out (if specified).
806
+ error: Error message if clone failed.
807
+ """
808
+
809
+ success: bool
810
+ message: str
811
+ repo_url: str | None = None
812
+ target_dir: str | None = None
813
+ branch: str | None = None
814
+ error: str | None = None
815
+
816
+
817
+ class SetupRepoResponse(BaseModel):
818
+ """Response model for setting up a repository for development.
819
+
820
+ Attributes:
821
+ success: Whether the setup operation succeeded.
822
+ message: Human-readable result message.
823
+ work_dir: The directory that was set up.
824
+ steps_completed: List of setup steps that were completed.
825
+ venv_path: Path to the virtual environment (if created).
826
+ dependencies_installed: Whether dependencies were successfully installed.
827
+ setup_scripts_run: List of setup scripts that were executed.
828
+ error: Error message if setup failed.
829
+ """
830
+
831
+ success: bool
832
+ message: str
833
+ work_dir: str | None = None
834
+ steps_completed: list[str] = []
835
+ venv_path: str | None = None
836
+ dependencies_installed: bool = False
837
+ setup_scripts_run: list[str] = []
838
+ error: str | None = None
839
+
840
+
841
+ class PlanRepoResponse(BaseModel):
842
+ """Response model for creating a plan for a repository.
843
+
844
+ Attributes:
845
+ success: Whether the planning operation succeeded.
846
+ message: Human-readable result message.
847
+ work_dir: The repository directory that was analyzed.
848
+ goal: The goal that was planned for.
849
+ plan: The generated plan (markdown with task checkboxes).
850
+ criteria: The success criteria for the plan.
851
+ run_id: The run ID for the created task state.
852
+ error: Error message if planning failed.
853
+ """
854
+
855
+ success: bool
856
+ message: str
857
+ work_dir: str | None = None
858
+ goal: str | None = None
859
+ plan: str | None = None
860
+ criteria: str | None = None
861
+ run_id: str | None = None
862
+ error: str | None = None
@@ -15,6 +15,9 @@ Endpoints:
15
15
  - POST /control/stop: Stop a running task with optional cleanup
16
16
  - POST /control/resume: Resume a paused or blocked task
17
17
  - PATCH /config: Update runtime configuration options
18
+ - POST /repo/clone: Clone a git repository to workspace
19
+ - POST /repo/setup: Set up a cloned repository for development
20
+ - POST /repo/plan: Create a plan for a repository (read-only)
18
21
 
19
22
  Usage:
20
23
  from claude_task_master.api.routes import (
@@ -44,15 +47,20 @@ from typing import TYPE_CHECKING, Any
44
47
 
45
48
  from claude_task_master import __version__
46
49
  from claude_task_master.api.models import (
50
+ ClearMailboxResponse,
47
51
  ConfigUpdateRequest,
48
52
  ContextResponse,
49
53
  ControlResponse,
50
54
  ErrorResponse,
51
55
  HealthResponse,
52
56
  LogsResponse,
57
+ MailboxMessagePreview,
58
+ MailboxStatusResponse,
53
59
  PlanResponse,
54
60
  ProgressResponse,
55
61
  ResumeRequest,
62
+ SendMailboxMessageRequest,
63
+ SendMailboxMessageResponse,
56
64
  StopRequest,
57
65
  TaskDeleteResponse,
58
66
  TaskInitRequest,
@@ -64,6 +72,7 @@ from claude_task_master.api.models import (
64
72
  WebhookStatusInfo,
65
73
  WorkflowStage,
66
74
  )
75
+ from claude_task_master.api.routes_repo import create_repo_router
67
76
  from claude_task_master.api.routes_webhooks import create_webhooks_router
68
77
  from claude_task_master.core.agent import ModelType
69
78
  from claude_task_master.core.control import ControlManager
@@ -1099,6 +1108,216 @@ def create_task_router() -> APIRouter:
1099
1108
  return router
1100
1109
 
1101
1110
 
1111
+ # =============================================================================
1112
+ # Mailbox Router (Send, Status, Clear)
1113
+ # =============================================================================
1114
+
1115
+
1116
+ def create_mailbox_router() -> APIRouter:
1117
+ """Create router for mailbox endpoints.
1118
+
1119
+ These endpoints allow external systems to send messages to the mailbox
1120
+ which will be processed after the current task completes.
1121
+
1122
+ Returns:
1123
+ APIRouter configured with mailbox endpoints.
1124
+
1125
+ Raises:
1126
+ ImportError: If FastAPI is not installed.
1127
+ """
1128
+ if not FASTAPI_AVAILABLE:
1129
+ raise ImportError(
1130
+ "FastAPI not installed. Install with: pip install claude-task-master[api]"
1131
+ )
1132
+
1133
+ router = APIRouter(tags=["Mailbox"])
1134
+
1135
+ @router.post(
1136
+ "/send",
1137
+ response_model=SendMailboxMessageResponse,
1138
+ responses={
1139
+ 400: {"model": ErrorResponse, "description": "Invalid request"},
1140
+ 500: {"model": ErrorResponse, "description": "Internal server error"},
1141
+ },
1142
+ summary="Send Message to Mailbox",
1143
+ description="Send a message to the claudetm mailbox for processing after the current task.",
1144
+ )
1145
+ async def send_message(
1146
+ request: Request, message_request: SendMailboxMessageRequest
1147
+ ) -> SendMailboxMessageResponse | JSONResponse:
1148
+ """Send a message to the mailbox.
1149
+
1150
+ Messages in the mailbox will be processed after the current task completes.
1151
+ Multiple messages are merged into a single change request that updates
1152
+ the plan before continuing work.
1153
+
1154
+ Args:
1155
+ message_request: The message to send with content, sender, and priority.
1156
+
1157
+ Returns:
1158
+ SendMailboxMessageResponse with message_id on success.
1159
+
1160
+ Raises:
1161
+ 400: If the request is invalid.
1162
+ 500: If an error occurs sending the message.
1163
+ """
1164
+ from claude_task_master.mailbox import MailboxStorage
1165
+
1166
+ working_dir: Path = getattr(request.app.state, "working_dir", Path.cwd())
1167
+ state_dir = working_dir / ".claude-task-master"
1168
+
1169
+ # Validate content is not empty/whitespace only
1170
+ if not message_request.content.strip():
1171
+ return JSONResponse(
1172
+ status_code=400,
1173
+ content=ErrorResponse(
1174
+ error="invalid_request",
1175
+ message="Message content cannot be empty or whitespace only",
1176
+ suggestion="Provide a non-empty message content",
1177
+ ).model_dump(),
1178
+ )
1179
+
1180
+ try:
1181
+ mailbox = MailboxStorage(state_dir=state_dir)
1182
+ message_id = mailbox.add_message(
1183
+ content=message_request.content.strip(),
1184
+ sender=message_request.sender,
1185
+ priority=message_request.priority,
1186
+ metadata=message_request.metadata or {},
1187
+ )
1188
+
1189
+ return SendMailboxMessageResponse(
1190
+ success=True,
1191
+ message_id=message_id,
1192
+ message=f"Message sent successfully (id: {message_id})",
1193
+ )
1194
+
1195
+ except Exception as e:
1196
+ logger.exception("Error sending message to mailbox")
1197
+ return JSONResponse(
1198
+ status_code=500,
1199
+ content=ErrorResponse(
1200
+ error="internal_error",
1201
+ message="Failed to send message to mailbox",
1202
+ detail=str(e),
1203
+ ).model_dump(),
1204
+ )
1205
+
1206
+ @router.get(
1207
+ "",
1208
+ response_model=MailboxStatusResponse,
1209
+ responses={
1210
+ 500: {"model": ErrorResponse, "description": "Internal server error"},
1211
+ },
1212
+ summary="Get Mailbox Status",
1213
+ description="Check the status of the mailbox including message count and previews.",
1214
+ )
1215
+ async def get_mailbox_status(request: Request) -> MailboxStatusResponse | JSONResponse:
1216
+ """Get mailbox status.
1217
+
1218
+ Returns the number of pending messages and previews of each.
1219
+
1220
+ Returns:
1221
+ MailboxStatusResponse with message count and previews.
1222
+
1223
+ Raises:
1224
+ 500: If an error occurs checking the mailbox.
1225
+ """
1226
+ from datetime import datetime as dt
1227
+
1228
+ from claude_task_master.mailbox import MailboxStorage
1229
+
1230
+ working_dir: Path = getattr(request.app.state, "working_dir", Path.cwd())
1231
+ state_dir = working_dir / ".claude-task-master"
1232
+
1233
+ try:
1234
+ mailbox = MailboxStorage(state_dir=state_dir)
1235
+ status = mailbox.get_status()
1236
+
1237
+ # Convert preview dicts to MailboxMessagePreview models
1238
+ previews = []
1239
+ for preview_data in status["previews"]:
1240
+ previews.append(
1241
+ MailboxMessagePreview(
1242
+ id=preview_data["id"],
1243
+ sender=preview_data["sender"],
1244
+ content_preview=preview_data["content_preview"],
1245
+ priority=preview_data["priority"],
1246
+ timestamp=dt.fromisoformat(preview_data["timestamp"]),
1247
+ )
1248
+ )
1249
+
1250
+ # Parse last_checked if present
1251
+ last_checked = None
1252
+ if status["last_checked"]:
1253
+ last_checked = dt.fromisoformat(status["last_checked"])
1254
+
1255
+ return MailboxStatusResponse(
1256
+ success=True,
1257
+ count=status["count"],
1258
+ messages=previews,
1259
+ last_checked=last_checked,
1260
+ total_messages_received=status["total_messages_received"],
1261
+ )
1262
+
1263
+ except Exception as e:
1264
+ logger.exception("Error checking mailbox status")
1265
+ return JSONResponse(
1266
+ status_code=500,
1267
+ content=ErrorResponse(
1268
+ error="internal_error",
1269
+ message="Failed to check mailbox status",
1270
+ detail=str(e),
1271
+ ).model_dump(),
1272
+ )
1273
+
1274
+ @router.delete(
1275
+ "",
1276
+ response_model=ClearMailboxResponse,
1277
+ responses={
1278
+ 500: {"model": ErrorResponse, "description": "Internal server error"},
1279
+ },
1280
+ summary="Clear Mailbox",
1281
+ description="Clear all messages from the mailbox.",
1282
+ )
1283
+ async def clear_mailbox(request: Request) -> ClearMailboxResponse | JSONResponse:
1284
+ """Clear all messages from the mailbox.
1285
+
1286
+ Returns:
1287
+ ClearMailboxResponse with number of messages cleared.
1288
+
1289
+ Raises:
1290
+ 500: If an error occurs clearing the mailbox.
1291
+ """
1292
+ from claude_task_master.mailbox import MailboxStorage
1293
+
1294
+ working_dir: Path = getattr(request.app.state, "working_dir", Path.cwd())
1295
+ state_dir = working_dir / ".claude-task-master"
1296
+
1297
+ try:
1298
+ mailbox = MailboxStorage(state_dir=state_dir)
1299
+ count = mailbox.clear()
1300
+
1301
+ return ClearMailboxResponse(
1302
+ success=True,
1303
+ messages_cleared=count,
1304
+ message=f"Cleared {count} message(s) from mailbox",
1305
+ )
1306
+
1307
+ except Exception as e:
1308
+ logger.exception("Error clearing mailbox")
1309
+ return JSONResponse(
1310
+ status_code=500,
1311
+ content=ErrorResponse(
1312
+ error="internal_error",
1313
+ message="Failed to clear mailbox",
1314
+ detail=str(e),
1315
+ ).model_dump(),
1316
+ )
1317
+
1318
+ return router
1319
+
1320
+
1102
1321
  # =============================================================================
1103
1322
  # Router Registration
1104
1323
  # =============================================================================
@@ -1129,7 +1348,17 @@ def register_routes(app: FastAPI) -> None:
1129
1348
  webhooks_router = create_webhooks_router()
1130
1349
  app.include_router(webhooks_router, prefix="/webhooks")
1131
1350
 
1351
+ # Create and register mailbox router
1352
+ mailbox_router = create_mailbox_router()
1353
+ app.include_router(mailbox_router, prefix="/mailbox")
1354
+
1355
+ # Create and register repo setup router
1356
+ repo_router = create_repo_router()
1357
+ app.include_router(repo_router, prefix="/repo")
1358
+
1132
1359
  logger.debug("Registered info routes: /status, /plan, /logs, /progress, /context, /health")
1133
1360
  logger.debug("Registered control routes: /control/stop, /control/resume, /config")
1134
1361
  logger.debug("Registered task routes: /task/init, /task")
1135
1362
  logger.debug("Registered webhook routes: /webhooks, /webhooks/{id}, /webhooks/test")
1363
+ logger.debug("Registered mailbox routes: /mailbox/send, /mailbox")
1364
+ logger.debug("Registered repo routes: /repo/clone, /repo/setup, /repo/plan")