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,199 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script for the Messages component
4
+ Verifies that the Messages component can render a list of messages correctly
5
+ """
6
+
7
+ from textual.app import App, ComposeResult
8
+ from textual.containers import Container, Horizontal
9
+ from textual.widgets import Header, Footer, Button
10
+ from textual import on
11
+
12
+ # Import the components we want to test
13
+ from minion_code.components.Messages import Messages
14
+ from minion_code.type_defs import Message, MessageType, MessageContent
15
+
16
+
17
+ class TestMessagesApp(App):
18
+ """Test application for Messages component"""
19
+
20
+ CSS = """
21
+ TestMessagesApp {
22
+ background: $surface;
23
+ }
24
+
25
+ #test_container {
26
+ height: 1fr;
27
+ margin: 1;
28
+ }
29
+
30
+ #controls {
31
+ dock: bottom;
32
+ height: auto;
33
+ margin: 1;
34
+
35
+ }
36
+
37
+ Button {
38
+ margin: 1;
39
+ min-width: 20;
40
+ }
41
+ """
42
+
43
+ def __init__(self):
44
+ super().__init__()
45
+ self.test_messages = []
46
+ self._create_test_messages()
47
+
48
+ def _create_test_messages(self):
49
+ """Create some test messages"""
50
+ import time
51
+
52
+ # User message
53
+ self.test_messages.append(
54
+ Message(
55
+ type=MessageType.USER,
56
+ message=MessageContent("Hello, can you help me with Python?"),
57
+ timestamp=time.time() - 60,
58
+ options={},
59
+ )
60
+ )
61
+
62
+ # Assistant message
63
+ self.test_messages.append(
64
+ Message(
65
+ type=MessageType.ASSISTANT,
66
+ message=MessageContent(
67
+ "Of course! I'd be happy to help you with Python. What specific topic or problem would you like assistance with?"
68
+ ),
69
+ timestamp=time.time() - 50,
70
+ options={},
71
+ )
72
+ )
73
+
74
+ # User message with code
75
+ self.test_messages.append(
76
+ Message(
77
+ type=MessageType.USER,
78
+ message=MessageContent("How do I create a list comprehension?"),
79
+ timestamp=time.time() - 40,
80
+ options={},
81
+ )
82
+ )
83
+
84
+ # Assistant message with code example
85
+ self.test_messages.append(
86
+ Message(
87
+ type=MessageType.ASSISTANT,
88
+ message=MessageContent(
89
+ """List comprehensions are a concise way to create lists in Python. Here's the basic syntax:
90
+
91
+ ```python
92
+ # Basic syntax: [expression for item in iterable]
93
+ numbers = [1, 2, 3, 4, 5]
94
+ squares = [x**2 for x in numbers]
95
+ print(squares) # [1, 4, 9, 16, 25]
96
+
97
+ # With condition: [expression for item in iterable if condition]
98
+ even_squares = [x**2 for x in numbers if x % 2 == 0]
99
+ print(even_squares) # [4, 16]
100
+ ```
101
+
102
+ List comprehensions are more readable and often faster than traditional for loops for creating lists."""
103
+ ),
104
+ timestamp=time.time() - 30,
105
+ options={},
106
+ )
107
+ )
108
+
109
+ def compose(self) -> ComposeResult:
110
+ """Compose the test application"""
111
+ yield Header(show_clock=True)
112
+
113
+ with Container(id="test_container"):
114
+ yield Messages(
115
+ messages=self.test_messages,
116
+ verbose=True,
117
+ debug=True,
118
+ id="test_messages",
119
+ )
120
+
121
+ with Horizontal(id="controls"):
122
+ yield Button("Add User Message", id="add_user", variant="primary")
123
+ yield Button("Add Assistant Message", id="add_assistant", variant="success")
124
+ yield Button("Clear Messages", id="clear", variant="error")
125
+
126
+ yield Footer()
127
+
128
+ def on_mount(self):
129
+ """Set up the application"""
130
+ self.title = "Messages Component Test"
131
+
132
+ @on(Button.Pressed, "#add_user")
133
+ def add_user_message(self):
134
+ """Add a test user message"""
135
+ import time
136
+
137
+ self.notify("Add User button clicked!") # Debug
138
+
139
+ new_message = Message(
140
+ type=MessageType.USER,
141
+ message=MessageContent(f"Test user message at {time.strftime('%H:%M:%S')}"),
142
+ timestamp=time.time(),
143
+ options={},
144
+ )
145
+
146
+ self.test_messages.append(new_message)
147
+ self.notify(f"Added message, total: {len(self.test_messages)}") # Debug
148
+
149
+ # Update the Messages component using add_message method
150
+ try:
151
+ messages_component = self.query_one("#test_messages", expect_type=Messages)
152
+ self.notify(f"Found messages component: {messages_component}") # Debug
153
+ # Use add_message method which uses mutate_reactive
154
+ messages_component.add_message(new_message)
155
+ self.notify("Messages updated successfully!") # Debug
156
+ except Exception as e:
157
+ self.notify(f"Error updating messages: {e}")
158
+
159
+ @on(Button.Pressed, "#add_assistant")
160
+ def add_assistant_message(self):
161
+ """Add a test assistant message"""
162
+ import time
163
+
164
+ new_message = Message(
165
+ type=MessageType.ASSISTANT,
166
+ message=MessageContent(
167
+ f"Test assistant response at {time.strftime('%H:%M:%S')}. This is a longer message to test how the component handles different message lengths and formatting."
168
+ ),
169
+ timestamp=time.time(),
170
+ options={},
171
+ )
172
+
173
+ self.test_messages.append(new_message)
174
+
175
+ # Update the Messages component using add_message method
176
+ try:
177
+ messages_component = self.query_one("#test_messages", expect_type=Messages)
178
+ # Use add_message method which uses mutate_reactive
179
+ messages_component.add_message(new_message)
180
+ except Exception as e:
181
+ self.notify(f"Error updating messages: {e}")
182
+
183
+ @on(Button.Pressed, "#clear")
184
+ def clear_messages(self):
185
+ """Clear all messages"""
186
+ self.test_messages = []
187
+
188
+ # Update the Messages component using clear_messages method
189
+ try:
190
+ messages_component = self.query_one("#test_messages", expect_type=Messages)
191
+ # Use clear_messages method which uses mutate_reactive
192
+ messages_component.clear_messages()
193
+ except Exception as e:
194
+ self.notify(f"Error clearing messages: {e}")
195
+
196
+
197
+ if __name__ == "__main__":
198
+ app = TestMessagesApp()
199
+ app.run()
@@ -20,78 +20,78 @@ from minion_code.services import (
20
20
 
21
21
  def main():
22
22
  """Demonstrate FileFreshnessService usage."""
23
-
23
+
24
24
  # Create a test file
25
25
  test_file = "test_file.txt"
26
26
  with open(test_file, "w") as f:
27
27
  f.write("Initial content")
28
-
28
+
29
29
  print("=== File Freshness Service Demo ===\n")
30
-
30
+
31
31
  # Set up event listeners to monitor what's happening
32
32
  def on_file_read(context):
33
33
  print(f"📖 File read event: {context.data['file_path']}")
34
-
34
+
35
35
  def on_file_edited(context):
36
36
  print(f"✏️ File edited event: {context.data['file_path']}")
37
-
37
+
38
38
  def on_file_conflict(context):
39
39
  print(f"⚠️ File conflict detected: {context.data['file_path']}")
40
-
41
- add_event_listener('file:read', on_file_read)
42
- add_event_listener('file:edited', on_file_edited)
43
- add_event_listener('file:conflict', on_file_conflict)
44
-
40
+
41
+ add_event_listener("file:read", on_file_read)
42
+ add_event_listener("file:edited", on_file_edited)
43
+ add_event_listener("file:conflict", on_file_conflict)
44
+
45
45
  # 1. Record initial file read
46
46
  print("1. Recording initial file read...")
47
47
  record_file_read(test_file)
48
-
48
+
49
49
  # 2. Check freshness (should be fresh)
50
50
  print("\n2. Checking file freshness...")
51
51
  result = check_file_freshness(test_file)
52
52
  print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
53
-
53
+
54
54
  # 3. Simulate external modification
55
55
  print("\n3. Simulating external file modification...")
56
56
  time.sleep(0.1) # Small delay to ensure different timestamp
57
57
  with open(test_file, "w") as f:
58
58
  f.write("Modified content externally")
59
-
59
+
60
60
  # 4. Check freshness again (should detect conflict)
61
61
  print("\n4. Checking freshness after external modification...")
62
62
  result = check_file_freshness(test_file)
63
63
  print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
64
-
64
+
65
65
  # 5. Record agent edit (should clear conflict)
66
66
  print("\n5. Recording agent edit...")
67
67
  record_file_edit(test_file, "Agent modified content")
68
-
68
+
69
69
  # 6. Check freshness after agent edit
70
70
  print("\n6. Checking freshness after agent edit...")
71
71
  result = check_file_freshness(test_file)
72
72
  print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
73
-
73
+
74
74
  # 7. Show session files and conflicts
75
75
  print("\n7. Session summary:")
76
76
  session_files = file_freshness_service.get_session_files()
77
77
  conflicted_files = file_freshness_service.get_conflicted_files()
78
78
  important_files = file_freshness_service.get_important_files()
79
-
79
+
80
80
  print(f" Session files: {session_files}")
81
81
  print(f" Conflicted files: {conflicted_files}")
82
82
  print(f" Important files: {important_files}")
83
-
83
+
84
84
  # 8. Test session reset
85
85
  print("\n8. Resetting session...")
86
- emit_event('session:startup', {'context': {}})
87
-
86
+ emit_event("session:startup", {"context": {}})
87
+
88
88
  session_files_after = file_freshness_service.get_session_files()
89
89
  print(f" Session files after reset: {session_files_after}")
90
-
90
+
91
91
  # Cleanup
92
92
  os.remove(test_file)
93
93
  print("\n✅ Demo completed successfully!")
94
94
 
95
95
 
96
96
  if __name__ == "__main__":
97
- main()
97
+ main()
@@ -20,9 +20,9 @@ from minion_code.services import (
20
20
 
21
21
  def main():
22
22
  """Demonstrate file watching functionality."""
23
-
23
+
24
24
  print("=== File Watching Demo ===\n")
25
-
25
+
26
26
  # Set up event listeners
27
27
  def on_todo_file_changed(context):
28
28
  data = context.data
@@ -30,71 +30,77 @@ def main():
30
30
  print(f" File: {data['file_path']}")
31
31
  print(f" Reminder: {data['reminder']}")
32
32
  print()
33
-
34
- add_event_listener('todo:file_changed', on_todo_file_changed)
35
-
33
+
34
+ add_event_listener("todo:file_changed", on_todo_file_changed)
35
+
36
36
  # Create test directory and file
37
37
  test_dir = "test_todos"
38
38
  os.makedirs(test_dir, exist_ok=True)
39
-
39
+
40
40
  test_file = os.path.join(test_dir, "agent1.json")
41
41
  agent_id = "agent1"
42
-
42
+
43
43
  # Create initial todo file
44
44
  with open(test_file, "w") as f:
45
- f.write('{"todos": [{"id": 1, "content": "Initial task", "status": "pending"}]}')
46
-
45
+ f.write(
46
+ '{"todos": [{"id": 1, "content": "Initial task", "status": "pending"}]}'
47
+ )
48
+
47
49
  print(f"1. Created test todo file: {test_file}")
48
-
50
+
49
51
  # Start watching the file
50
52
  print(f"2. Starting to watch todo file for agent: {agent_id}")
51
53
  start_watching_todo_file(agent_id, test_file)
52
-
54
+
53
55
  # Record initial read
54
56
  record_file_read(test_file)
55
57
  print("3. Recorded initial file read")
56
-
58
+
57
59
  # Check if we're watching
58
60
  watched_files = file_freshness_service.get_watched_files()
59
61
  print(f"4. Currently watching files: {watched_files}")
60
-
62
+
61
63
  # Wait a bit for watcher to initialize
62
64
  time.sleep(1)
63
-
65
+
64
66
  # Simulate external modification
65
67
  print("\n5. Simulating external file modification...")
66
-
68
+
67
69
  def modify_file():
68
70
  time.sleep(0.5) # Small delay
69
71
  with open(test_file, "w") as f:
70
- f.write('{"todos": [{"id": 1, "content": "Modified task", "status": "completed"}]}')
72
+ f.write(
73
+ '{"todos": [{"id": 1, "content": "Modified task", "status": "completed"}]}'
74
+ )
71
75
  print(" ✏️ File modified externally")
72
-
76
+
73
77
  # Run modification in separate thread to avoid blocking
74
78
  modifier_thread = threading.Thread(target=modify_file)
75
79
  modifier_thread.start()
76
-
80
+
77
81
  # Wait for modification and watcher to detect it
78
82
  modifier_thread.join()
79
83
  time.sleep(2) # Give watcher time to detect change
80
-
84
+
81
85
  # Modify file again
82
86
  print("6. Another external modification...")
83
87
  time.sleep(0.5)
84
88
  with open(test_file, "w") as f:
85
- f.write('{"todos": [{"id": 2, "content": "Another task", "status": "pending"}]}')
86
-
89
+ f.write(
90
+ '{"todos": [{"id": 2, "content": "Another task", "status": "pending"}]}'
91
+ )
92
+
87
93
  # Wait for detection
88
94
  time.sleep(2)
89
-
95
+
90
96
  # Stop watching
91
97
  print("7. Stopping file watcher...")
92
98
  stop_watching_todo_file(agent_id)
93
-
99
+
94
100
  # Verify we're no longer watching
95
101
  watched_files_after = file_freshness_service.get_watched_files()
96
102
  print(f"8. Files being watched after stop: {watched_files_after}")
97
-
103
+
98
104
  # Cleanup
99
105
  try:
100
106
  os.remove(test_file)
@@ -102,9 +108,9 @@ def main():
102
108
  print("\n🧹 Cleaned up test files")
103
109
  except Exception as e:
104
110
  print(f"⚠️ Cleanup warning: {e}")
105
-
111
+
106
112
  print("\n✅ File watching demo completed!")
107
113
 
108
114
 
109
115
  if __name__ == "__main__":
110
- main()
116
+ main()