minion-code 0.1.0__py3-none-any.whl → 0.1.1__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.
- examples/cli_entrypoint.py +60 -0
- examples/{agent_with_todos.py → components/agent_with_todos.py} +58 -47
- examples/{message_response_children_demo.py → components/message_response_children_demo.py} +61 -55
- examples/components/messages_component.py +199 -0
- examples/file_freshness_example.py +22 -22
- examples/file_watching_example.py +32 -26
- examples/interruptible_tui.py +921 -3
- examples/repl_tui.py +129 -0
- examples/skills/example_usage.py +57 -0
- examples/start.py +173 -0
- minion_code/__init__.py +1 -1
- minion_code/acp_server/__init__.py +34 -0
- minion_code/acp_server/agent.py +539 -0
- minion_code/acp_server/hooks.py +354 -0
- minion_code/acp_server/main.py +194 -0
- minion_code/acp_server/permissions.py +142 -0
- minion_code/acp_server/test_client.py +104 -0
- minion_code/adapters/__init__.py +22 -0
- minion_code/adapters/output_adapter.py +207 -0
- minion_code/adapters/rich_adapter.py +169 -0
- minion_code/adapters/textual_adapter.py +254 -0
- minion_code/agents/__init__.py +2 -2
- minion_code/agents/code_agent.py +517 -104
- minion_code/agents/hooks.py +378 -0
- minion_code/cli.py +538 -429
- minion_code/cli_simple.py +665 -0
- minion_code/commands/__init__.py +136 -29
- minion_code/commands/clear_command.py +19 -46
- minion_code/commands/help_command.py +33 -49
- minion_code/commands/history_command.py +37 -55
- minion_code/commands/model_command.py +194 -0
- minion_code/commands/quit_command.py +9 -12
- minion_code/commands/resume_command.py +181 -0
- minion_code/commands/skill_command.py +89 -0
- minion_code/commands/status_command.py +48 -73
- minion_code/commands/tools_command.py +54 -52
- minion_code/commands/version_command.py +34 -69
- minion_code/components/ConfirmDialog.py +430 -0
- minion_code/components/Message.py +318 -97
- minion_code/components/MessageResponse.py +30 -29
- minion_code/components/Messages.py +351 -0
- minion_code/components/PromptInput.py +499 -245
- minion_code/components/__init__.py +24 -17
- minion_code/const.py +7 -0
- minion_code/screens/REPL.py +1453 -469
- minion_code/screens/__init__.py +1 -1
- minion_code/services/__init__.py +20 -20
- minion_code/services/event_system.py +19 -14
- minion_code/services/file_freshness_service.py +223 -170
- minion_code/skills/__init__.py +25 -0
- minion_code/skills/skill.py +128 -0
- minion_code/skills/skill_loader.py +198 -0
- minion_code/skills/skill_registry.py +177 -0
- minion_code/subagents/__init__.py +31 -0
- minion_code/subagents/builtin/__init__.py +30 -0
- minion_code/subagents/builtin/claude_code_guide.py +32 -0
- minion_code/subagents/builtin/explore.py +36 -0
- minion_code/subagents/builtin/general_purpose.py +19 -0
- minion_code/subagents/builtin/plan.py +61 -0
- minion_code/subagents/subagent.py +116 -0
- minion_code/subagents/subagent_loader.py +147 -0
- minion_code/subagents/subagent_registry.py +151 -0
- minion_code/tools/__init__.py +8 -2
- minion_code/tools/bash_tool.py +16 -3
- minion_code/tools/file_edit_tool.py +201 -104
- minion_code/tools/file_read_tool.py +183 -26
- minion_code/tools/file_write_tool.py +17 -3
- minion_code/tools/glob_tool.py +23 -2
- minion_code/tools/grep_tool.py +229 -21
- minion_code/tools/ls_tool.py +28 -3
- minion_code/tools/multi_edit_tool.py +89 -84
- minion_code/tools/python_interpreter_tool.py +9 -1
- minion_code/tools/skill_tool.py +210 -0
- minion_code/tools/task_tool.py +287 -0
- minion_code/tools/todo_read_tool.py +28 -24
- minion_code/tools/todo_write_tool.py +82 -65
- minion_code/{types.py → type_defs.py} +15 -2
- minion_code/utils/__init__.py +45 -17
- minion_code/utils/config.py +610 -0
- minion_code/utils/history.py +114 -0
- minion_code/utils/logs.py +53 -0
- minion_code/utils/mcp_loader.py +153 -55
- minion_code/utils/output_truncator.py +233 -0
- minion_code/utils/session_storage.py +369 -0
- minion_code/utils/todo_file_utils.py +26 -22
- minion_code/utils/todo_storage.py +43 -33
- minion_code/web/__init__.py +9 -0
- minion_code/web/adapters/__init__.py +5 -0
- minion_code/web/adapters/web_adapter.py +524 -0
- minion_code/web/api/__init__.py +7 -0
- minion_code/web/api/chat.py +277 -0
- minion_code/web/api/interactions.py +136 -0
- minion_code/web/api/sessions.py +135 -0
- minion_code/web/server.py +149 -0
- minion_code/web/services/__init__.py +5 -0
- minion_code/web/services/session_manager.py +420 -0
- minion_code-0.1.1.dist-info/METADATA +475 -0
- minion_code-0.1.1.dist-info/RECORD +111 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/WHEEL +1 -1
- minion_code-0.1.1.dist-info/entry_points.txt +6 -0
- tests/test_adapter.py +67 -0
- tests/test_adapter_simple.py +79 -0
- tests/test_file_read_tool.py +144 -0
- tests/test_readonly_tools.py +0 -2
- tests/test_skills.py +441 -0
- examples/advance_tui.py +0 -508
- examples/rich_example.py +0 -4
- examples/simple_file_watching.py +0 -57
- examples/simple_tui.py +0 -267
- examples/simple_usage.py +0 -69
- minion_code-0.1.0.dist-info/METADATA +0 -350
- minion_code-0.1.0.dist-info/RECORD +0 -59
- minion_code-0.1.0.dist-info/entry_points.txt +0 -4
- {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Textual Output Adapter - TUI implementation
|
|
5
|
+
|
|
6
|
+
This adapter integrates with Textual TUI through a callback system.
|
|
7
|
+
It uses asyncio.Future to implement non-blocking user interactions,
|
|
8
|
+
allowing the TUI to display dialogs and return results asynchronously.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import List, Optional, Callable, Dict, Any
|
|
12
|
+
import asyncio
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
from .output_adapter import OutputAdapter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class PendingInteraction:
|
|
20
|
+
"""Represents a user interaction waiting for response"""
|
|
21
|
+
|
|
22
|
+
type: str # "confirm", "choice", "input"
|
|
23
|
+
data: Dict[str, Any]
|
|
24
|
+
future: asyncio.Future
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TextualOutputAdapter(OutputAdapter):
|
|
28
|
+
"""
|
|
29
|
+
Textual TUI adapter for non-blocking user interaction.
|
|
30
|
+
|
|
31
|
+
This adapter communicates with the TUI through a callback function.
|
|
32
|
+
For user interactions (confirm, choice, input), it creates asyncio.Future
|
|
33
|
+
objects and waits for the TUI to resolve them via resolve_interaction().
|
|
34
|
+
|
|
35
|
+
Workflow:
|
|
36
|
+
1. Command calls adapter.confirm()
|
|
37
|
+
2. Adapter creates Future and calls on_output callback
|
|
38
|
+
3. TUI displays dialog and waits for user action
|
|
39
|
+
4. User clicks button → TUI calls adapter.resolve_interaction()
|
|
40
|
+
5. Future completes, command continues
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, on_output: Optional[Callable[[str, dict], None]] = None):
|
|
44
|
+
"""
|
|
45
|
+
Initialize Textual adapter.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
on_output: Callback function to send output to TUI.
|
|
49
|
+
Called with (output_type: str, data: dict)
|
|
50
|
+
Example: on_output("confirm", {"message": "...", ...})
|
|
51
|
+
"""
|
|
52
|
+
self.on_output = on_output or self._default_output
|
|
53
|
+
self._pending_interactions: Dict[str, PendingInteraction] = {}
|
|
54
|
+
self._interaction_counter = 0
|
|
55
|
+
|
|
56
|
+
def _default_output(self, output_type: str, data: dict):
|
|
57
|
+
"""Default output handler (prints to console for debugging)."""
|
|
58
|
+
print(f"[TextualAdapter] {output_type}: {data}")
|
|
59
|
+
|
|
60
|
+
def _generate_interaction_id(self) -> str:
|
|
61
|
+
"""Generate unique interaction ID."""
|
|
62
|
+
self._interaction_counter += 1
|
|
63
|
+
return f"interaction_{self._interaction_counter}"
|
|
64
|
+
|
|
65
|
+
def panel(self, content: str, title: str = "", border_style: str = "blue") -> None:
|
|
66
|
+
"""Send panel output to TUI."""
|
|
67
|
+
self.on_output(
|
|
68
|
+
"panel", {"content": content, "title": title, "border_style": border_style}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def table(self, headers: List[str], rows: List[List[str]], title: str = "") -> None:
|
|
72
|
+
"""Send table output to TUI."""
|
|
73
|
+
self.on_output("table", {"headers": headers, "rows": rows, "title": title})
|
|
74
|
+
|
|
75
|
+
def text(self, content: str, style: str = "") -> None:
|
|
76
|
+
"""Send text output to TUI."""
|
|
77
|
+
self.on_output("text", {"content": content, "style": style})
|
|
78
|
+
|
|
79
|
+
async def confirm(
|
|
80
|
+
self,
|
|
81
|
+
message: str,
|
|
82
|
+
title: str = "Confirm",
|
|
83
|
+
default: bool = False,
|
|
84
|
+
ok_text: str = "Yes",
|
|
85
|
+
cancel_text: str = "No",
|
|
86
|
+
) -> bool:
|
|
87
|
+
"""
|
|
88
|
+
Request user confirmation (non-blocking).
|
|
89
|
+
|
|
90
|
+
Creates a Future, sends notification to TUI, and waits for
|
|
91
|
+
the TUI to call resolve_interaction() with the result.
|
|
92
|
+
"""
|
|
93
|
+
interaction_id = self._generate_interaction_id()
|
|
94
|
+
future = asyncio.Future()
|
|
95
|
+
|
|
96
|
+
# Store pending interaction
|
|
97
|
+
self._pending_interactions[interaction_id] = PendingInteraction(
|
|
98
|
+
type="confirm",
|
|
99
|
+
data={
|
|
100
|
+
"message": message,
|
|
101
|
+
"title": title,
|
|
102
|
+
"default": default,
|
|
103
|
+
"ok_text": ok_text,
|
|
104
|
+
"cancel_text": cancel_text,
|
|
105
|
+
},
|
|
106
|
+
future=future,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Notify TUI to display confirmation dialog
|
|
110
|
+
self.on_output(
|
|
111
|
+
"confirm",
|
|
112
|
+
{
|
|
113
|
+
"interaction_id": interaction_id,
|
|
114
|
+
"message": message,
|
|
115
|
+
"title": title,
|
|
116
|
+
"default": default,
|
|
117
|
+
"ok_text": ok_text,
|
|
118
|
+
"cancel_text": cancel_text,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Wait for TUI to resolve this interaction
|
|
123
|
+
try:
|
|
124
|
+
result = await future
|
|
125
|
+
return result
|
|
126
|
+
finally:
|
|
127
|
+
# Clean up
|
|
128
|
+
self._pending_interactions.pop(interaction_id, None)
|
|
129
|
+
|
|
130
|
+
async def choice(
|
|
131
|
+
self,
|
|
132
|
+
message: str,
|
|
133
|
+
choices: List[str],
|
|
134
|
+
title: str = "Select",
|
|
135
|
+
default_index: int = 0,
|
|
136
|
+
) -> int:
|
|
137
|
+
"""
|
|
138
|
+
Request user choice selection (non-blocking).
|
|
139
|
+
|
|
140
|
+
Returns the selected index (0-based), or -1 if cancelled.
|
|
141
|
+
"""
|
|
142
|
+
interaction_id = self._generate_interaction_id()
|
|
143
|
+
future = asyncio.Future()
|
|
144
|
+
|
|
145
|
+
self._pending_interactions[interaction_id] = PendingInteraction(
|
|
146
|
+
type="choice",
|
|
147
|
+
data={
|
|
148
|
+
"message": message,
|
|
149
|
+
"choices": choices,
|
|
150
|
+
"title": title,
|
|
151
|
+
"default_index": default_index,
|
|
152
|
+
},
|
|
153
|
+
future=future,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.on_output(
|
|
157
|
+
"choice",
|
|
158
|
+
{
|
|
159
|
+
"interaction_id": interaction_id,
|
|
160
|
+
"message": message,
|
|
161
|
+
"choices": choices,
|
|
162
|
+
"title": title,
|
|
163
|
+
"default_index": default_index,
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
result = await future
|
|
169
|
+
return result
|
|
170
|
+
finally:
|
|
171
|
+
self._pending_interactions.pop(interaction_id, None)
|
|
172
|
+
|
|
173
|
+
async def input(
|
|
174
|
+
self,
|
|
175
|
+
message: str,
|
|
176
|
+
title: str = "Input",
|
|
177
|
+
default: str = "",
|
|
178
|
+
placeholder: str = "",
|
|
179
|
+
) -> Optional[str]:
|
|
180
|
+
"""
|
|
181
|
+
Request user text input (non-blocking).
|
|
182
|
+
|
|
183
|
+
Returns the input string, or None if cancelled.
|
|
184
|
+
"""
|
|
185
|
+
interaction_id = self._generate_interaction_id()
|
|
186
|
+
future = asyncio.Future()
|
|
187
|
+
|
|
188
|
+
self._pending_interactions[interaction_id] = PendingInteraction(
|
|
189
|
+
type="input",
|
|
190
|
+
data={
|
|
191
|
+
"message": message,
|
|
192
|
+
"title": title,
|
|
193
|
+
"default": default,
|
|
194
|
+
"placeholder": placeholder,
|
|
195
|
+
},
|
|
196
|
+
future=future,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
self.on_output(
|
|
200
|
+
"input",
|
|
201
|
+
{
|
|
202
|
+
"interaction_id": interaction_id,
|
|
203
|
+
"message": message,
|
|
204
|
+
"title": title,
|
|
205
|
+
"default": default,
|
|
206
|
+
"placeholder": placeholder,
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
result = await future
|
|
212
|
+
return result
|
|
213
|
+
finally:
|
|
214
|
+
self._pending_interactions.pop(interaction_id, None)
|
|
215
|
+
|
|
216
|
+
def resolve_interaction(self, interaction_id: str, result: Any):
|
|
217
|
+
"""
|
|
218
|
+
Resolve a pending interaction with a result.
|
|
219
|
+
|
|
220
|
+
This method should be called by the TUI when the user completes
|
|
221
|
+
an interaction (clicks a button, enters text, etc.).
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
interaction_id: The interaction ID from the original request
|
|
225
|
+
result: The user's response (bool for confirm, int for choice, str for input)
|
|
226
|
+
"""
|
|
227
|
+
interaction = self._pending_interactions.get(interaction_id)
|
|
228
|
+
if interaction and not interaction.future.done():
|
|
229
|
+
interaction.future.set_result(result)
|
|
230
|
+
|
|
231
|
+
def cancel_interaction(self, interaction_id: str):
|
|
232
|
+
"""
|
|
233
|
+
Cancel a pending interaction.
|
|
234
|
+
|
|
235
|
+
This should be called if the user cancels the dialog or it's
|
|
236
|
+
dismissed without a response.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
interaction_id: The interaction ID to cancel
|
|
240
|
+
"""
|
|
241
|
+
interaction = self._pending_interactions.get(interaction_id)
|
|
242
|
+
if interaction and not interaction.future.done():
|
|
243
|
+
# Set appropriate default value based on interaction type
|
|
244
|
+
if interaction.type == "confirm":
|
|
245
|
+
interaction.future.set_result(False)
|
|
246
|
+
elif interaction.type == "choice":
|
|
247
|
+
interaction.future.set_result(-1)
|
|
248
|
+
elif interaction.type == "input":
|
|
249
|
+
interaction.future.set_result(None)
|
|
250
|
+
|
|
251
|
+
def print(self, *args, **kwargs) -> None:
|
|
252
|
+
"""Generic print - converts to text output."""
|
|
253
|
+
content = " ".join(str(arg) for arg in args)
|
|
254
|
+
self.text(content)
|
minion_code/agents/__init__.py
CHANGED
|
@@ -6,6 +6,6 @@ Minion Code Agents Package
|
|
|
6
6
|
Enhanced agents with specialized capabilities for code development tasks.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from .code_agent import MinionCodeAgent,create_minion_code_agent
|
|
9
|
+
from .code_agent import MinionCodeAgent, create_minion_code_agent
|
|
10
10
|
|
|
11
|
-
__all__ = ["MinionCodeAgent","create_minion_code_agent"]
|
|
11
|
+
__all__ = ["MinionCodeAgent", "create_minion_code_agent"]
|