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.
Files changed (115) hide show
  1. examples/cli_entrypoint.py +60 -0
  2. examples/{agent_with_todos.py → components/agent_with_todos.py} +58 -47
  3. examples/{message_response_children_demo.py → components/message_response_children_demo.py} +61 -55
  4. examples/components/messages_component.py +199 -0
  5. examples/file_freshness_example.py +22 -22
  6. examples/file_watching_example.py +32 -26
  7. examples/interruptible_tui.py +921 -3
  8. examples/repl_tui.py +129 -0
  9. examples/skills/example_usage.py +57 -0
  10. examples/start.py +173 -0
  11. minion_code/__init__.py +1 -1
  12. minion_code/acp_server/__init__.py +34 -0
  13. minion_code/acp_server/agent.py +539 -0
  14. minion_code/acp_server/hooks.py +354 -0
  15. minion_code/acp_server/main.py +194 -0
  16. minion_code/acp_server/permissions.py +142 -0
  17. minion_code/acp_server/test_client.py +104 -0
  18. minion_code/adapters/__init__.py +22 -0
  19. minion_code/adapters/output_adapter.py +207 -0
  20. minion_code/adapters/rich_adapter.py +169 -0
  21. minion_code/adapters/textual_adapter.py +254 -0
  22. minion_code/agents/__init__.py +2 -2
  23. minion_code/agents/code_agent.py +517 -104
  24. minion_code/agents/hooks.py +378 -0
  25. minion_code/cli.py +538 -429
  26. minion_code/cli_simple.py +665 -0
  27. minion_code/commands/__init__.py +136 -29
  28. minion_code/commands/clear_command.py +19 -46
  29. minion_code/commands/help_command.py +33 -49
  30. minion_code/commands/history_command.py +37 -55
  31. minion_code/commands/model_command.py +194 -0
  32. minion_code/commands/quit_command.py +9 -12
  33. minion_code/commands/resume_command.py +181 -0
  34. minion_code/commands/skill_command.py +89 -0
  35. minion_code/commands/status_command.py +48 -73
  36. minion_code/commands/tools_command.py +54 -52
  37. minion_code/commands/version_command.py +34 -69
  38. minion_code/components/ConfirmDialog.py +430 -0
  39. minion_code/components/Message.py +318 -97
  40. minion_code/components/MessageResponse.py +30 -29
  41. minion_code/components/Messages.py +351 -0
  42. minion_code/components/PromptInput.py +499 -245
  43. minion_code/components/__init__.py +24 -17
  44. minion_code/const.py +7 -0
  45. minion_code/screens/REPL.py +1453 -469
  46. minion_code/screens/__init__.py +1 -1
  47. minion_code/services/__init__.py +20 -20
  48. minion_code/services/event_system.py +19 -14
  49. minion_code/services/file_freshness_service.py +223 -170
  50. minion_code/skills/__init__.py +25 -0
  51. minion_code/skills/skill.py +128 -0
  52. minion_code/skills/skill_loader.py +198 -0
  53. minion_code/skills/skill_registry.py +177 -0
  54. minion_code/subagents/__init__.py +31 -0
  55. minion_code/subagents/builtin/__init__.py +30 -0
  56. minion_code/subagents/builtin/claude_code_guide.py +32 -0
  57. minion_code/subagents/builtin/explore.py +36 -0
  58. minion_code/subagents/builtin/general_purpose.py +19 -0
  59. minion_code/subagents/builtin/plan.py +61 -0
  60. minion_code/subagents/subagent.py +116 -0
  61. minion_code/subagents/subagent_loader.py +147 -0
  62. minion_code/subagents/subagent_registry.py +151 -0
  63. minion_code/tools/__init__.py +8 -2
  64. minion_code/tools/bash_tool.py +16 -3
  65. minion_code/tools/file_edit_tool.py +201 -104
  66. minion_code/tools/file_read_tool.py +183 -26
  67. minion_code/tools/file_write_tool.py +17 -3
  68. minion_code/tools/glob_tool.py +23 -2
  69. minion_code/tools/grep_tool.py +229 -21
  70. minion_code/tools/ls_tool.py +28 -3
  71. minion_code/tools/multi_edit_tool.py +89 -84
  72. minion_code/tools/python_interpreter_tool.py +9 -1
  73. minion_code/tools/skill_tool.py +210 -0
  74. minion_code/tools/task_tool.py +287 -0
  75. minion_code/tools/todo_read_tool.py +28 -24
  76. minion_code/tools/todo_write_tool.py +82 -65
  77. minion_code/{types.py → type_defs.py} +15 -2
  78. minion_code/utils/__init__.py +45 -17
  79. minion_code/utils/config.py +610 -0
  80. minion_code/utils/history.py +114 -0
  81. minion_code/utils/logs.py +53 -0
  82. minion_code/utils/mcp_loader.py +153 -55
  83. minion_code/utils/output_truncator.py +233 -0
  84. minion_code/utils/session_storage.py +369 -0
  85. minion_code/utils/todo_file_utils.py +26 -22
  86. minion_code/utils/todo_storage.py +43 -33
  87. minion_code/web/__init__.py +9 -0
  88. minion_code/web/adapters/__init__.py +5 -0
  89. minion_code/web/adapters/web_adapter.py +524 -0
  90. minion_code/web/api/__init__.py +7 -0
  91. minion_code/web/api/chat.py +277 -0
  92. minion_code/web/api/interactions.py +136 -0
  93. minion_code/web/api/sessions.py +135 -0
  94. minion_code/web/server.py +149 -0
  95. minion_code/web/services/__init__.py +5 -0
  96. minion_code/web/services/session_manager.py +420 -0
  97. minion_code-0.1.1.dist-info/METADATA +475 -0
  98. minion_code-0.1.1.dist-info/RECORD +111 -0
  99. {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/WHEEL +1 -1
  100. minion_code-0.1.1.dist-info/entry_points.txt +6 -0
  101. tests/test_adapter.py +67 -0
  102. tests/test_adapter_simple.py +79 -0
  103. tests/test_file_read_tool.py +144 -0
  104. tests/test_readonly_tools.py +0 -2
  105. tests/test_skills.py +441 -0
  106. examples/advance_tui.py +0 -508
  107. examples/rich_example.py +0 -4
  108. examples/simple_file_watching.py +0 -57
  109. examples/simple_tui.py +0 -267
  110. examples/simple_usage.py +0 -69
  111. minion_code-0.1.0.dist-info/METADATA +0 -350
  112. minion_code-0.1.0.dist-info/RECORD +0 -59
  113. minion_code-0.1.0.dist-info/entry_points.txt +0 -4
  114. {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/licenses/LICENSE +0 -0
  115. {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)
@@ -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"]