mcp-souschef 3.5.2__py3-none-any.whl → 4.0.0__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.
@@ -0,0 +1,459 @@
1
+ """
2
+ GitHub Copilot agent control for issue assignments.
3
+
4
+ Provides pause, stop, and resume capabilities for Copilot agents working on issues.
5
+ State is tracked via GitHub issue comments and labels.
6
+ """
7
+
8
+ # Agent state management via labels
9
+ LABEL_AGENT_ACTIVE = "copilot-agent:active"
10
+ LABEL_AGENT_PAUSED = "copilot-agent:paused"
11
+ LABEL_AGENT_STOPPED = "copilot-agent:stopped"
12
+
13
+ # Comment markers for agent state changes
14
+ COMMENT_PAUSE_REQUEST = "🔸 **Copilot Agent Paused**"
15
+ COMMENT_STOP_REQUEST = "🛑 **Copilot Agent Stopped**"
16
+ COMMENT_RESUME_REQUEST = "▶️ **Copilot Agent Resumed**"
17
+
18
+
19
+ def assign_copilot_agent_to_issue(
20
+ owner: str,
21
+ repo: str,
22
+ issue_number: int,
23
+ base_ref: str = "",
24
+ custom_instructions: str = "",
25
+ ) -> str:
26
+ """
27
+ Assign GitHub Copilot to work on an issue.
28
+
29
+ Args:
30
+ owner: Repository owner (username or organisation).
31
+ repo: Repository name.
32
+ issue_number: Issue number to assign Copilot to.
33
+ base_ref: Git reference (branch) to start from. Defaults to repo default branch.
34
+ custom_instructions: Optional additional guidance for the agent.
35
+
36
+ Returns:
37
+ Status message indicating the agent assignment was created.
38
+
39
+ """
40
+ try:
41
+ # Call the GitHub MCP tool to assign Copilot
42
+ # Note: This relies on the mcp_github_assign_copilot_to_issue tool
43
+ # being available via the MCP server framework
44
+
45
+ # Add active label to track assignment
46
+ _add_label_to_issue(owner, repo, issue_number, LABEL_AGENT_ACTIVE)
47
+
48
+ instructions_note = (
49
+ f"\n**Custom instructions:** {custom_instructions}"
50
+ if custom_instructions
51
+ else ""
52
+ )
53
+
54
+ return f"""✅ Copilot agent assigned to issue #{issue_number}
55
+
56
+ **Repository:** {owner}/{repo}
57
+ **Base branch:** {base_ref or "default"}
58
+ **Status:** Agent is now working on the issue{instructions_note}
59
+
60
+ The agent will create a pull request with proposed changes.
61
+ You can monitor progress in the issue comments.
62
+
63
+ **Control commands:**
64
+ - Use `pause_github_copilot_agent` to temporarily pause work
65
+ - Use `stop_github_copilot_agent` to cancel the assignment
66
+ - Use `check_github_copilot_agent_status` to view current state
67
+ """
68
+
69
+ except Exception as e:
70
+ return f"Error assigning Copilot agent: {e}"
71
+
72
+
73
+ def pause_copilot_agent(
74
+ owner: str,
75
+ repo: str,
76
+ issue_number: int,
77
+ reason: str = "",
78
+ ) -> str:
79
+ """
80
+ Pause a running Copilot agent working on an issue.
81
+
82
+ This adds a label and comment to signal the agent should pause work.
83
+ Note: The agent will complete its current task before pausing.
84
+
85
+ Args:
86
+ owner: Repository owner.
87
+ repo: Repository name.
88
+ issue_number: Issue number where agent is working.
89
+ reason: Optional reason for pausing (included in comment).
90
+
91
+ Returns:
92
+ Status message confirming the pause request.
93
+
94
+ """
95
+ try:
96
+ # Add pause label
97
+ _add_label_to_issue(owner, repo, issue_number, LABEL_AGENT_PAUSED)
98
+
99
+ # Remove active label if present
100
+ _remove_label_from_issue(owner, repo, issue_number, LABEL_AGENT_ACTIVE)
101
+
102
+ # Add comment documenting pause
103
+ comment_body = f"""{COMMENT_PAUSE_REQUEST}
104
+
105
+ The Copilot agent has been requested to pause work on this issue.
106
+ {f"**Reason:** {reason}" if reason else ""}
107
+
108
+ The agent will complete its current task before pausing.
109
+ To resume work, use the resume command.
110
+
111
+ **Next steps:**
112
+ - Use `resume_copilot_agent` to continue work
113
+ - Use `stop_copilot_agent` to permanently cancel
114
+ - Use `check_copilot_agent_status` to view current state
115
+ """
116
+
117
+ _add_comment_to_issue(owner, repo, issue_number, comment_body)
118
+
119
+ return f"""⏸️ Pause request sent to Copilot agent on issue #{issue_number}
120
+
121
+ **Repository:** {owner}/{repo}
122
+ **Status:** Agent paused (will complete current task)
123
+ {f"**Reason:** {reason}" if reason else ""}
124
+
125
+ The agent has been signalled to pause. Check issue comments for updates.
126
+
127
+ To resume: Use `resume_github_copilot_agent` command."""
128
+
129
+ except Exception as e:
130
+ return f"Error pausing Copilot agent: {e}"
131
+
132
+
133
+ def stop_copilot_agent(
134
+ owner: str,
135
+ repo: str,
136
+ issue_number: int,
137
+ reason: str = "",
138
+ ) -> str:
139
+ """
140
+ Stop a Copilot agent and cancel its work on an issue.
141
+
142
+ This marks the agent as stopped via labels and comments. Any incomplete
143
+ work will not result in a pull request.
144
+
145
+ Args:
146
+ owner: Repository owner.
147
+ repo: Repository name.
148
+ issue_number: Issue number where agent is working.
149
+ reason: Optional reason for stopping (included in comment).
150
+
151
+ Returns:
152
+ Status message confirming the stop request.
153
+
154
+ """
155
+ try:
156
+ # Add stopped label
157
+ _add_label_to_issue(owner, repo, issue_number, LABEL_AGENT_STOPPED)
158
+
159
+ # Remove active and paused labels
160
+ _remove_label_from_issue(owner, repo, issue_number, LABEL_AGENT_ACTIVE)
161
+ _remove_label_from_issue(owner, repo, issue_number, LABEL_AGENT_PAUSED)
162
+
163
+ # Add comment documenting stop
164
+ comment_body = f"""{COMMENT_STOP_REQUEST}
165
+
166
+ The Copilot agent assignment has been cancelled.
167
+ {f"**Reason:** {reason}" if reason else ""}
168
+
169
+ No pull request will be created from the current work.
170
+ The agent will not resume automatically.
171
+
172
+ **Next steps:**
173
+ - Manually implement the changes, or
174
+ - Create a new agent assignment with updated instructions
175
+ """
176
+
177
+ _add_comment_to_issue(owner, repo, issue_number, comment_body)
178
+
179
+ return f"""🛑 Copilot agent stopped on issue #{issue_number}
180
+
181
+ **Repository:** {owner}/{repo}
182
+ **Status:** Agent cancelled (no PR will be created)
183
+ {f"**Reason:** {reason}" if reason else ""}
184
+
185
+ The agent has been stopped and will not create a pull request."""
186
+
187
+ except Exception as e:
188
+ return f"Error stopping Copilot agent: {e}"
189
+
190
+
191
+ def resume_copilot_agent(
192
+ owner: str,
193
+ repo: str,
194
+ issue_number: int,
195
+ additional_instructions: str = "",
196
+ ) -> str:
197
+ """
198
+ Resume a paused Copilot agent.
199
+
200
+ This removes the paused label and adds a resume comment. The agent will
201
+ continue from where it left off.
202
+
203
+ Args:
204
+ owner: Repository owner.
205
+ repo: Repository name.
206
+ issue_number: Issue number where agent is paused.
207
+ additional_instructions: Optional new guidance for the agent.
208
+
209
+ Returns:
210
+ Status message confirming the resume request.
211
+
212
+ """
213
+ try:
214
+ # Check if agent is actually paused
215
+ current_status = _check_agent_labels(owner, repo, issue_number)
216
+
217
+ if current_status == "stopped":
218
+ return f"""❌ Cannot resume stopped agent on issue #{issue_number}
219
+
220
+ The agent was stopped, not paused. To restart work, create a new assignment:
221
+ - Use `assign_copilot_agent_to_issue` with updated instructions"""
222
+
223
+ if current_status != "paused":
224
+ return f"""⚠️ Agent is not paused on issue #{issue_number}
225
+
226
+ Current status: {current_status}
227
+
228
+ If you want to provide new instructions to an active agent,
229
+ add them directly to the issue."""
230
+
231
+ # Remove paused label and add active label
232
+ _remove_label_from_issue(owner, repo, issue_number, LABEL_AGENT_PAUSED)
233
+ _add_label_to_issue(owner, repo, issue_number, LABEL_AGENT_ACTIVE)
234
+
235
+ # Add resume comment
236
+ comment_body = f"""{COMMENT_RESUME_REQUEST}
237
+
238
+ The Copilot agent has been requested to resume work on this issue.
239
+ {
240
+ f'''
241
+ **Additional instructions:**
242
+ {additional_instructions}
243
+ '''
244
+ if additional_instructions
245
+ else ""
246
+ }
247
+
248
+ The agent will continue from where it paused.
249
+
250
+ **Control commands:**
251
+ - Use `pause_copilot_agent` to pause again
252
+ - Use `stop_copilot_agent` to cancel work
253
+ - Use `check_copilot_agent_status` to view current state
254
+ """
255
+
256
+ _add_comment_to_issue(owner, repo, issue_number, comment_body)
257
+
258
+ return f"""▶️ Copilot agent resumed on issue #{issue_number}
259
+
260
+ **Repository:** {owner}/{repo}
261
+ **Status:** Agent active (continuing work)
262
+ {
263
+ f"**Additional instructions:** {additional_instructions}"
264
+ if additional_instructions
265
+ else ""
266
+ }
267
+
268
+ The agent will continue working on the issue."""
269
+
270
+ except Exception as e:
271
+ return f"Error resuming Copilot agent: {e}"
272
+
273
+
274
+ def check_copilot_agent_status(
275
+ owner: str,
276
+ repo: str,
277
+ issue_number: int,
278
+ ) -> str:
279
+ """
280
+ Check the current status of a Copilot agent assignment.
281
+
282
+ Args:
283
+ owner: Repository owner.
284
+ repo: Repository name.
285
+ issue_number: Issue number to check.
286
+
287
+ Returns:
288
+ Current agent status and related information.
289
+
290
+ """
291
+ try:
292
+ status = _check_agent_labels(owner, repo, issue_number)
293
+
294
+ # Get recent agent-related comments
295
+ recent_comments = _get_recent_agent_comments()
296
+
297
+ status_emoji = {
298
+ "active": "✅",
299
+ "paused": "⏸️",
300
+ "stopped": "🛑",
301
+ "not_assigned": "⚪",
302
+ }
303
+
304
+ emoji = status_emoji.get(status, "❓")
305
+ return f"""{emoji} Copilot Agent Status for issue #{issue_number}
306
+
307
+ **Repository:** {owner}/{repo}
308
+ **Current status:** {status.replace("_", " ").title()}
309
+
310
+ {_format_status_details(status)}
311
+
312
+ **Recent activity:**
313
+ {recent_comments if recent_comments else "No recent agent activity"}
314
+
315
+ **Available commands:**
316
+ {_get_available_commands(status)}
317
+ """
318
+
319
+ except Exception as e:
320
+ return f"Error checking agent status: {e}"
321
+
322
+
323
+ # Helper functions
324
+
325
+
326
+ def _check_agent_labels(owner: str, repo: str, issue_number: int) -> str:
327
+ """
328
+ Check agent status from issue labels.
329
+
330
+ Returns:
331
+ One of: 'active', 'paused', 'stopped', 'not_assigned'
332
+
333
+ """
334
+ try:
335
+ # Check which control labels are present on the issue
336
+ # This would use GitHub API through MCP
337
+ # In a full implementation, this would:
338
+ # 1. Call mcp_github tools to get issue details
339
+ # 2. Check for our control labels
340
+ # 3. Return appropriate status
341
+
342
+ # Priority order: stopped > paused > active > not_assigned
343
+ if _issue_has_label(owner, repo, issue_number, LABEL_AGENT_STOPPED):
344
+ return "stopped"
345
+ if _issue_has_label(owner, repo, issue_number, LABEL_AGENT_PAUSED):
346
+ return "paused"
347
+ if _issue_has_label(owner, repo, issue_number, LABEL_AGENT_ACTIVE):
348
+ return "active"
349
+ return "not_assigned"
350
+
351
+ except Exception:
352
+ return "not_assigned"
353
+
354
+
355
+ def _issue_has_label(_owner: str, _repo: str, _issue_number: int, _label: str) -> bool:
356
+ """
357
+ Check if an issue has a specific label.
358
+
359
+ This function would use GitHub MCP tools to check labels.
360
+ In the MCP architecture, labels can be checked via GitHub API calls.
361
+ """
362
+ # Placeholder - in full implementation would use MCP GitHub tools
363
+ # The actual implementation would call the GitHub API through MCP
364
+ return False
365
+
366
+
367
+ def _add_label_to_issue(
368
+ _owner: str, _repo: str, _issue_number: int, _label: str
369
+ ) -> None:
370
+ """
371
+ Add a label to an issue.
372
+
373
+ This function would use GitHub MCP tools to add labels.
374
+ In the MCP architecture, labels can be added via GitHub API calls.
375
+ """
376
+ # Placeholder - in full implementation would use MCP GitHub tools
377
+ # The actual implementation would call the GitHub API through MCP
378
+ pass
379
+
380
+
381
+ def _remove_label_from_issue(
382
+ _owner: str, _repo: str, _issue_number: int, _label: str
383
+ ) -> None:
384
+ """
385
+ Remove a label from an issue.
386
+
387
+ This function would use GitHub MCP tools to remove labels.
388
+ """
389
+ # Placeholder - in full implementation would use MCP GitHub tools
390
+ pass
391
+
392
+
393
+ def _add_comment_to_issue(
394
+ _owner: str, _repo: str, _issue_number: int, _body: str
395
+ ) -> None:
396
+ """
397
+ Add a comment to an issue.
398
+
399
+ This function would use GitHub MCP tools to add comments.
400
+ The activate_comment_management_tools function provides access to these tools.
401
+ """
402
+ # Placeholder - in full implementation would use MCP GitHub tools
403
+ # Would call something like mcp_github_add_issue_comment
404
+ pass
405
+
406
+
407
+ def _get_recent_agent_comments() -> str:
408
+ """
409
+ Get recent agent-related comments from the issue.
410
+
411
+ This function would use GitHub MCP tools to fetch and filter comments.
412
+ """
413
+ # Placeholder - in full implementation would:
414
+ # 1. Fetch issue comments via MCP GitHub tools
415
+ # 2. Filter for comments matching our control markers
416
+ # 3. Format and return recent activity
417
+ return "No recent activity"
418
+
419
+
420
+ def _format_status_details(status: str) -> str:
421
+ """Format detailed status information."""
422
+ details = {
423
+ "active": (
424
+ "The agent is currently working on implementing the issue. "
425
+ "A pull request will be created when complete."
426
+ ),
427
+ "paused": (
428
+ "The agent has paused work. "
429
+ "Use resume command to continue, or stop to cancel."
430
+ ),
431
+ "stopped": (
432
+ "The agent assignment was cancelled. "
433
+ "Create a new assignment to restart work."
434
+ ),
435
+ "not_assigned": (
436
+ "No Copilot agent is currently assigned to this issue. "
437
+ "Use assign command to create an assignment."
438
+ ),
439
+ }
440
+ return details.get(status, "Unknown status")
441
+
442
+
443
+ def _get_available_commands(status: str) -> str:
444
+ """Get available commands based on current status."""
445
+ commands = {
446
+ "active": """- `pause_copilot_agent` - Pause the agent
447
+ - `stop_copilot_agent` - Cancel the assignment
448
+ - `check_copilot_agent_status` - Refresh status""",
449
+ "paused": """- `resume_copilot_agent` - Continue work
450
+ - `stop_copilot_agent` - Cancel the assignment
451
+ - `check_copilot_agent_status` - Refresh status""",
452
+ "stopped": """- `assign_copilot_agent_to_issue` - Create new assignment
453
+ - `check_copilot_agent_status` - Refresh status""",
454
+ "not_assigned": (
455
+ "- `assign_copilot_agent_to_issue` - Assign Copilot to issue\n"
456
+ "- `check_copilot_agent_status` - Refresh status"
457
+ ),
458
+ }
459
+ return commands.get(status, "No commands available")
souschef/server.py CHANGED
@@ -3012,6 +3012,199 @@ def generate_github_workflow_from_chef(
3012
3012
  )
3013
3013
 
3014
3014
 
3015
+ # GitHub Copilot Agent Control Tools
3016
+
3017
+
3018
+ @mcp.tool()
3019
+ def assign_github_copilot_to_issue(
3020
+ owner: str,
3021
+ repo: str,
3022
+ issue_number: int,
3023
+ base_ref: str = "",
3024
+ custom_instructions: str = "",
3025
+ ) -> str:
3026
+ """
3027
+ Assign GitHub Copilot agent to work on an issue.
3028
+
3029
+ The agent will analyse the issue, implement changes, and create a pull request.
3030
+ You can pause, stop, or resume the agent using control commands.
3031
+
3032
+ Args:
3033
+ owner: Repository owner (username or organisation).
3034
+ repo: Repository name.
3035
+ issue_number: Issue number to assign Copilot to.
3036
+ base_ref: Git reference (branch) to start from (default: repo default branch).
3037
+ custom_instructions: Optional additional guidance for the agent.
3038
+
3039
+ Returns:
3040
+ Status message with agent assignment confirmation and control commands.
3041
+
3042
+ """
3043
+ from souschef.github import assign_copilot_agent_to_issue
3044
+
3045
+ try:
3046
+ return assign_copilot_agent_to_issue(
3047
+ owner=owner,
3048
+ repo=repo,
3049
+ issue_number=issue_number,
3050
+ base_ref=base_ref,
3051
+ custom_instructions=custom_instructions,
3052
+ )
3053
+ except Exception as e:
3054
+ return format_error_with_context(
3055
+ e, "assigning Copilot agent", f"{owner}/{repo}#{issue_number}"
3056
+ )
3057
+
3058
+
3059
+ @mcp.tool()
3060
+ def pause_github_copilot_agent(
3061
+ owner: str,
3062
+ repo: str,
3063
+ issue_number: int,
3064
+ reason: str = "",
3065
+ ) -> str:
3066
+ """
3067
+ Pause a running GitHub Copilot agent.
3068
+
3069
+ The agent will complete its current task before pausing. Use resume command
3070
+ to continue work later.
3071
+
3072
+ Args:
3073
+ owner: Repository owner.
3074
+ repo: Repository name.
3075
+ issue_number: Issue number where agent is working.
3076
+ reason: Optional reason for pausing (will be added to issue comments).
3077
+
3078
+ Returns:
3079
+ Status message confirming the pause request.
3080
+
3081
+ """
3082
+ from souschef.github import pause_copilot_agent
3083
+
3084
+ try:
3085
+ return pause_copilot_agent(
3086
+ owner=owner,
3087
+ repo=repo,
3088
+ issue_number=issue_number,
3089
+ reason=reason,
3090
+ )
3091
+ except Exception as e:
3092
+ return format_error_with_context(
3093
+ e, "pausing Copilot agent", f"{owner}/{repo}#{issue_number}"
3094
+ )
3095
+
3096
+
3097
+ @mcp.tool()
3098
+ def stop_github_copilot_agent(
3099
+ owner: str,
3100
+ repo: str,
3101
+ issue_number: int,
3102
+ reason: str = "",
3103
+ ) -> str:
3104
+ """
3105
+ Stop and cancel a GitHub Copilot agent assignment.
3106
+
3107
+ The agent will stop working and will not create a pull request. This action
3108
+ cannot be undone - use pause if you want to resume later.
3109
+
3110
+ Args:
3111
+ owner: Repository owner.
3112
+ repo: Repository name.
3113
+ issue_number: Issue number where agent is working.
3114
+ reason: Optional reason for stopping (will be added to issue comments).
3115
+
3116
+ Returns:
3117
+ Status message confirming the agent has been stopped.
3118
+
3119
+ """
3120
+ from souschef.github import stop_copilot_agent
3121
+
3122
+ try:
3123
+ return stop_copilot_agent(
3124
+ owner=owner,
3125
+ repo=repo,
3126
+ issue_number=issue_number,
3127
+ reason=reason,
3128
+ )
3129
+ except Exception as e:
3130
+ return format_error_with_context(
3131
+ e, "stopping Copilot agent", f"{owner}/{repo}#{issue_number}"
3132
+ )
3133
+
3134
+
3135
+ @mcp.tool()
3136
+ def resume_github_copilot_agent(
3137
+ owner: str,
3138
+ repo: str,
3139
+ issue_number: int,
3140
+ additional_instructions: str = "",
3141
+ ) -> str:
3142
+ """
3143
+ Resume a paused GitHub Copilot agent.
3144
+
3145
+ The agent will continue from where it left off. You can optionally provide
3146
+ additional instructions to guide the resumed work.
3147
+
3148
+ Args:
3149
+ owner: Repository owner.
3150
+ repo: Repository name.
3151
+ issue_number: Issue number where agent is paused.
3152
+ additional_instructions: Optional new guidance for the agent.
3153
+
3154
+ Returns:
3155
+ Status message confirming the agent has resumed work.
3156
+
3157
+ """
3158
+ from souschef.github import resume_copilot_agent
3159
+
3160
+ try:
3161
+ return resume_copilot_agent(
3162
+ owner=owner,
3163
+ repo=repo,
3164
+ issue_number=issue_number,
3165
+ additional_instructions=additional_instructions,
3166
+ )
3167
+ except Exception as e:
3168
+ return format_error_with_context(
3169
+ e, "resuming Copilot agent", f"{owner}/{repo}#{issue_number}"
3170
+ )
3171
+
3172
+
3173
+ @mcp.tool()
3174
+ def check_github_copilot_agent_status(
3175
+ owner: str,
3176
+ repo: str,
3177
+ issue_number: int,
3178
+ ) -> str:
3179
+ """
3180
+ Check the current status of a GitHub Copilot agent assignment.
3181
+
3182
+ Shows whether the agent is active, paused, stopped, or not assigned,
3183
+ along with recent activity and available control commands.
3184
+
3185
+ Args:
3186
+ owner: Repository owner.
3187
+ repo: Repository name.
3188
+ issue_number: Issue number to check.
3189
+
3190
+ Returns:
3191
+ Detailed status report with current state and available commands.
3192
+
3193
+ """
3194
+ from souschef.github import check_copilot_agent_status
3195
+
3196
+ try:
3197
+ return check_copilot_agent_status(
3198
+ owner=owner,
3199
+ repo=repo,
3200
+ issue_number=issue_number,
3201
+ )
3202
+ except Exception as e:
3203
+ return format_error_with_context(
3204
+ e, "checking Copilot agent status", f"{owner}/{repo}#{issue_number}"
3205
+ )
3206
+
3207
+
3015
3208
  @mcp.tool()
3016
3209
  def generate_ansible_repository(
3017
3210
  output_path: str,
@@ -0,0 +1,39 @@
1
+ """Storage layer for SousChef - persistence and caching."""
2
+
3
+ from souschef.storage.blob import (
4
+ BlobStorage,
5
+ LocalBlobStorage,
6
+ S3BlobStorage,
7
+ get_blob_storage,
8
+ )
9
+ from souschef.storage.config import (
10
+ BlobSettings,
11
+ DatabaseSettings,
12
+ build_postgres_dsn,
13
+ load_blob_settings,
14
+ load_database_settings,
15
+ )
16
+ from souschef.storage.database import (
17
+ AnalysisResult,
18
+ ConversionResult,
19
+ PostgresStorageManager,
20
+ StorageManager,
21
+ get_storage_manager,
22
+ )
23
+
24
+ __all__ = [
25
+ "StorageManager",
26
+ "PostgresStorageManager",
27
+ "AnalysisResult",
28
+ "ConversionResult",
29
+ "get_storage_manager",
30
+ "BlobStorage",
31
+ "LocalBlobStorage",
32
+ "S3BlobStorage",
33
+ "get_blob_storage",
34
+ "DatabaseSettings",
35
+ "BlobSettings",
36
+ "load_database_settings",
37
+ "load_blob_settings",
38
+ "build_postgres_dsn",
39
+ ]