minion-code 0.1.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.
- examples/advance_tui.py +508 -0
- examples/agent_with_todos.py +165 -0
- examples/file_freshness_example.py +97 -0
- examples/file_watching_example.py +110 -0
- examples/interruptible_tui.py +5 -0
- examples/message_response_children_demo.py +226 -0
- examples/rich_example.py +4 -0
- examples/simple_file_watching.py +57 -0
- examples/simple_tui.py +267 -0
- examples/simple_usage.py +69 -0
- minion_code/__init__.py +16 -0
- minion_code/agents/__init__.py +11 -0
- minion_code/agents/code_agent.py +320 -0
- minion_code/cli.py +502 -0
- minion_code/commands/__init__.py +90 -0
- minion_code/commands/clear_command.py +70 -0
- minion_code/commands/help_command.py +90 -0
- minion_code/commands/history_command.py +104 -0
- minion_code/commands/quit_command.py +32 -0
- minion_code/commands/status_command.py +115 -0
- minion_code/commands/tools_command.py +86 -0
- minion_code/commands/version_command.py +104 -0
- minion_code/components/Message.py +304 -0
- minion_code/components/MessageResponse.py +188 -0
- minion_code/components/PromptInput.py +534 -0
- minion_code/components/__init__.py +29 -0
- minion_code/screens/REPL.py +925 -0
- minion_code/screens/__init__.py +4 -0
- minion_code/services/__init__.py +50 -0
- minion_code/services/event_system.py +108 -0
- minion_code/services/file_freshness_service.py +582 -0
- minion_code/tools/__init__.py +69 -0
- minion_code/tools/bash_tool.py +58 -0
- minion_code/tools/file_edit_tool.py +238 -0
- minion_code/tools/file_read_tool.py +73 -0
- minion_code/tools/file_write_tool.py +36 -0
- minion_code/tools/glob_tool.py +58 -0
- minion_code/tools/grep_tool.py +105 -0
- minion_code/tools/ls_tool.py +65 -0
- minion_code/tools/multi_edit_tool.py +271 -0
- minion_code/tools/python_interpreter_tool.py +105 -0
- minion_code/tools/todo_read_tool.py +100 -0
- minion_code/tools/todo_write_tool.py +234 -0
- minion_code/tools/user_input_tool.py +53 -0
- minion_code/types.py +88 -0
- minion_code/utils/__init__.py +44 -0
- minion_code/utils/mcp_loader.py +211 -0
- minion_code/utils/todo_file_utils.py +110 -0
- minion_code/utils/todo_storage.py +149 -0
- minion_code-0.1.0.dist-info/METADATA +350 -0
- minion_code-0.1.0.dist-info/RECORD +59 -0
- minion_code-0.1.0.dist-info/WHEEL +5 -0
- minion_code-0.1.0.dist-info/entry_points.txt +4 -0
- minion_code-0.1.0.dist-info/licenses/LICENSE +661 -0
- minion_code-0.1.0.dist-info/top_level.txt +3 -0
- tests/__init__.py +1 -0
- tests/test_basic.py +20 -0
- tests/test_readonly_tools.py +102 -0
- tests/test_tools.py +83 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example usage of the FileFreshnessService with event system."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
# Add parent directory to path for imports
|
|
9
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
10
|
+
|
|
11
|
+
from minion_code.services import (
|
|
12
|
+
file_freshness_service,
|
|
13
|
+
add_event_listener,
|
|
14
|
+
emit_event,
|
|
15
|
+
record_file_read,
|
|
16
|
+
record_file_edit,
|
|
17
|
+
check_file_freshness,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
"""Demonstrate FileFreshnessService usage."""
|
|
23
|
+
|
|
24
|
+
# Create a test file
|
|
25
|
+
test_file = "test_file.txt"
|
|
26
|
+
with open(test_file, "w") as f:
|
|
27
|
+
f.write("Initial content")
|
|
28
|
+
|
|
29
|
+
print("=== File Freshness Service Demo ===\n")
|
|
30
|
+
|
|
31
|
+
# Set up event listeners to monitor what's happening
|
|
32
|
+
def on_file_read(context):
|
|
33
|
+
print(f"๐ File read event: {context.data['file_path']}")
|
|
34
|
+
|
|
35
|
+
def on_file_edited(context):
|
|
36
|
+
print(f"โ๏ธ File edited event: {context.data['file_path']}")
|
|
37
|
+
|
|
38
|
+
def on_file_conflict(context):
|
|
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
|
+
|
|
45
|
+
# 1. Record initial file read
|
|
46
|
+
print("1. Recording initial file read...")
|
|
47
|
+
record_file_read(test_file)
|
|
48
|
+
|
|
49
|
+
# 2. Check freshness (should be fresh)
|
|
50
|
+
print("\n2. Checking file freshness...")
|
|
51
|
+
result = check_file_freshness(test_file)
|
|
52
|
+
print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
|
|
53
|
+
|
|
54
|
+
# 3. Simulate external modification
|
|
55
|
+
print("\n3. Simulating external file modification...")
|
|
56
|
+
time.sleep(0.1) # Small delay to ensure different timestamp
|
|
57
|
+
with open(test_file, "w") as f:
|
|
58
|
+
f.write("Modified content externally")
|
|
59
|
+
|
|
60
|
+
# 4. Check freshness again (should detect conflict)
|
|
61
|
+
print("\n4. Checking freshness after external modification...")
|
|
62
|
+
result = check_file_freshness(test_file)
|
|
63
|
+
print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
|
|
64
|
+
|
|
65
|
+
# 5. Record agent edit (should clear conflict)
|
|
66
|
+
print("\n5. Recording agent edit...")
|
|
67
|
+
record_file_edit(test_file, "Agent modified content")
|
|
68
|
+
|
|
69
|
+
# 6. Check freshness after agent edit
|
|
70
|
+
print("\n6. Checking freshness after agent edit...")
|
|
71
|
+
result = check_file_freshness(test_file)
|
|
72
|
+
print(f" Is fresh: {result.is_fresh}, Conflict: {result.conflict}")
|
|
73
|
+
|
|
74
|
+
# 7. Show session files and conflicts
|
|
75
|
+
print("\n7. Session summary:")
|
|
76
|
+
session_files = file_freshness_service.get_session_files()
|
|
77
|
+
conflicted_files = file_freshness_service.get_conflicted_files()
|
|
78
|
+
important_files = file_freshness_service.get_important_files()
|
|
79
|
+
|
|
80
|
+
print(f" Session files: {session_files}")
|
|
81
|
+
print(f" Conflicted files: {conflicted_files}")
|
|
82
|
+
print(f" Important files: {important_files}")
|
|
83
|
+
|
|
84
|
+
# 8. Test session reset
|
|
85
|
+
print("\n8. Resetting session...")
|
|
86
|
+
emit_event('session:startup', {'context': {}})
|
|
87
|
+
|
|
88
|
+
session_files_after = file_freshness_service.get_session_files()
|
|
89
|
+
print(f" Session files after reset: {session_files_after}")
|
|
90
|
+
|
|
91
|
+
# Cleanup
|
|
92
|
+
os.remove(test_file)
|
|
93
|
+
print("\nโ
Demo completed successfully!")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example usage of file watching functionality."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import threading
|
|
8
|
+
|
|
9
|
+
# Add parent directory to path for imports
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
11
|
+
|
|
12
|
+
from minion_code.services import (
|
|
13
|
+
start_watching_todo_file,
|
|
14
|
+
stop_watching_todo_file,
|
|
15
|
+
add_event_listener,
|
|
16
|
+
record_file_read,
|
|
17
|
+
file_freshness_service,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
"""Demonstrate file watching functionality."""
|
|
23
|
+
|
|
24
|
+
print("=== File Watching Demo ===\n")
|
|
25
|
+
|
|
26
|
+
# Set up event listeners
|
|
27
|
+
def on_todo_file_changed(context):
|
|
28
|
+
data = context.data
|
|
29
|
+
print(f"๐ Todo file changed for agent {data['agent_id']}")
|
|
30
|
+
print(f" File: {data['file_path']}")
|
|
31
|
+
print(f" Reminder: {data['reminder']}")
|
|
32
|
+
print()
|
|
33
|
+
|
|
34
|
+
add_event_listener('todo:file_changed', on_todo_file_changed)
|
|
35
|
+
|
|
36
|
+
# Create test directory and file
|
|
37
|
+
test_dir = "test_todos"
|
|
38
|
+
os.makedirs(test_dir, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
test_file = os.path.join(test_dir, "agent1.json")
|
|
41
|
+
agent_id = "agent1"
|
|
42
|
+
|
|
43
|
+
# Create initial todo file
|
|
44
|
+
with open(test_file, "w") as f:
|
|
45
|
+
f.write('{"todos": [{"id": 1, "content": "Initial task", "status": "pending"}]}')
|
|
46
|
+
|
|
47
|
+
print(f"1. Created test todo file: {test_file}")
|
|
48
|
+
|
|
49
|
+
# Start watching the file
|
|
50
|
+
print(f"2. Starting to watch todo file for agent: {agent_id}")
|
|
51
|
+
start_watching_todo_file(agent_id, test_file)
|
|
52
|
+
|
|
53
|
+
# Record initial read
|
|
54
|
+
record_file_read(test_file)
|
|
55
|
+
print("3. Recorded initial file read")
|
|
56
|
+
|
|
57
|
+
# Check if we're watching
|
|
58
|
+
watched_files = file_freshness_service.get_watched_files()
|
|
59
|
+
print(f"4. Currently watching files: {watched_files}")
|
|
60
|
+
|
|
61
|
+
# Wait a bit for watcher to initialize
|
|
62
|
+
time.sleep(1)
|
|
63
|
+
|
|
64
|
+
# Simulate external modification
|
|
65
|
+
print("\n5. Simulating external file modification...")
|
|
66
|
+
|
|
67
|
+
def modify_file():
|
|
68
|
+
time.sleep(0.5) # Small delay
|
|
69
|
+
with open(test_file, "w") as f:
|
|
70
|
+
f.write('{"todos": [{"id": 1, "content": "Modified task", "status": "completed"}]}')
|
|
71
|
+
print(" โ๏ธ File modified externally")
|
|
72
|
+
|
|
73
|
+
# Run modification in separate thread to avoid blocking
|
|
74
|
+
modifier_thread = threading.Thread(target=modify_file)
|
|
75
|
+
modifier_thread.start()
|
|
76
|
+
|
|
77
|
+
# Wait for modification and watcher to detect it
|
|
78
|
+
modifier_thread.join()
|
|
79
|
+
time.sleep(2) # Give watcher time to detect change
|
|
80
|
+
|
|
81
|
+
# Modify file again
|
|
82
|
+
print("6. Another external modification...")
|
|
83
|
+
time.sleep(0.5)
|
|
84
|
+
with open(test_file, "w") as f:
|
|
85
|
+
f.write('{"todos": [{"id": 2, "content": "Another task", "status": "pending"}]}')
|
|
86
|
+
|
|
87
|
+
# Wait for detection
|
|
88
|
+
time.sleep(2)
|
|
89
|
+
|
|
90
|
+
# Stop watching
|
|
91
|
+
print("7. Stopping file watcher...")
|
|
92
|
+
stop_watching_todo_file(agent_id)
|
|
93
|
+
|
|
94
|
+
# Verify we're no longer watching
|
|
95
|
+
watched_files_after = file_freshness_service.get_watched_files()
|
|
96
|
+
print(f"8. Files being watched after stop: {watched_files_after}")
|
|
97
|
+
|
|
98
|
+
# Cleanup
|
|
99
|
+
try:
|
|
100
|
+
os.remove(test_file)
|
|
101
|
+
os.rmdir(test_dir)
|
|
102
|
+
print("\n๐งน Cleaned up test files")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"โ ๏ธ Cleanup warning: {e}")
|
|
105
|
+
|
|
106
|
+
print("\nโ
File watching demo completed!")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Demo showing MessageResponse with children usage
|
|
4
|
+
Equivalent to React's <MessageResponse><Message /></MessageResponse> pattern
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import time
|
|
9
|
+
from textual.app import App, ComposeResult
|
|
10
|
+
from textual.containers import Container, Vertical, ScrollableContainer
|
|
11
|
+
from textual.widgets import Header, Footer, Button, Static
|
|
12
|
+
|
|
13
|
+
# Import the components
|
|
14
|
+
import sys
|
|
15
|
+
import os
|
|
16
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
17
|
+
|
|
18
|
+
from minion_code.components import Message, MessageResponse
|
|
19
|
+
from minion_code.types import Message as MessageType, MessageContent, MessageType as MsgType
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MessageResponseChildrenDemo(App):
|
|
23
|
+
"""Demo showing MessageResponse children usage like React"""
|
|
24
|
+
|
|
25
|
+
CSS = """
|
|
26
|
+
Screen {
|
|
27
|
+
background: $surface;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.demo-container {
|
|
31
|
+
padding: 1;
|
|
32
|
+
height: 100%;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.messages-container {
|
|
36
|
+
height: 1fr;
|
|
37
|
+
border: solid $primary;
|
|
38
|
+
padding: 1;
|
|
39
|
+
margin-bottom: 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.controls {
|
|
43
|
+
height: auto;
|
|
44
|
+
dock: bottom;
|
|
45
|
+
padding: 1;
|
|
46
|
+
background: $surface-lighten-1;
|
|
47
|
+
}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def compose(self) -> ComposeResult:
|
|
51
|
+
"""Compose the demo interface"""
|
|
52
|
+
yield Header()
|
|
53
|
+
|
|
54
|
+
with Container(classes="demo-container"):
|
|
55
|
+
with ScrollableContainer(classes="messages-container", id="messages"):
|
|
56
|
+
# Show examples of different usage patterns
|
|
57
|
+
yield from self._create_usage_examples()
|
|
58
|
+
|
|
59
|
+
# with Container(classes="controls"):
|
|
60
|
+
# yield Button("Add Tool Progress", id="add_tool_progress", variant="primary")
|
|
61
|
+
# yield Button("Add Nested Response", id="add_nested", variant="success")
|
|
62
|
+
# yield Button("Add Multiple Children", id="add_multiple", variant="warning")
|
|
63
|
+
# yield Button("Add Dynamic Mount", id="add_dynamic", variant="default")
|
|
64
|
+
# yield Button("Clear", id="clear", variant="error")
|
|
65
|
+
|
|
66
|
+
yield Footer()
|
|
67
|
+
self.log(self.tree)
|
|
68
|
+
|
|
69
|
+
def _create_usage_examples(self):
|
|
70
|
+
"""Create examples showing different MessageResponse usage patterns"""
|
|
71
|
+
child_msg = MessageType(
|
|
72
|
+
type=MsgType.ASSISTANT,
|
|
73
|
+
message=MessageContent("This message is wrapped in MessageResponse"),
|
|
74
|
+
timestamp=time.time()
|
|
75
|
+
)
|
|
76
|
+
yield Message(child_msg, classes="example-header")
|
|
77
|
+
yield Static("\n2. Single child message (React-like):", classes="example-header")
|
|
78
|
+
|
|
79
|
+
# This is equivalent to: <MessageResponse><Message /></MessageResponse>
|
|
80
|
+
|
|
81
|
+
yield MessageResponse(children=[Message(child_msg, classes="example-header")])
|
|
82
|
+
|
|
83
|
+
# Example 3: Tool execution progress
|
|
84
|
+
yield Static("\n3. Tool execution progress:", classes="example-header")
|
|
85
|
+
tool_msg = MessageType(
|
|
86
|
+
type=MsgType.TOOL_USE,
|
|
87
|
+
message=MessageContent([{
|
|
88
|
+
"type": "tool_use",
|
|
89
|
+
"id": "tool_demo",
|
|
90
|
+
"name": "file_editor",
|
|
91
|
+
"input": {"path": "demo.py", "content": "print('Hello')"}
|
|
92
|
+
}]),
|
|
93
|
+
timestamp=time.time()
|
|
94
|
+
)
|
|
95
|
+
yield MessageResponse(children=[Message(tool_msg, verbose=True)])
|
|
96
|
+
|
|
97
|
+
# Example 4: Multiple children (using initialization)
|
|
98
|
+
yield Static("\n4. Multiple children:", classes="example-header")
|
|
99
|
+
|
|
100
|
+
# Add multiple children to demonstrate flexibility
|
|
101
|
+
status_msg = MessageType(
|
|
102
|
+
type=MsgType.ASSISTANT,
|
|
103
|
+
message=MessageContent("Step 1: Analyzing code..."),
|
|
104
|
+
timestamp=time.time()
|
|
105
|
+
)
|
|
106
|
+
progress_msg = MessageType(
|
|
107
|
+
type=MsgType.ASSISTANT,
|
|
108
|
+
message=MessageContent("Step 2: Applying changes..."),
|
|
109
|
+
timestamp=time.time()
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Create with children during initialization
|
|
113
|
+
yield MessageResponse(children=[
|
|
114
|
+
Message(status_msg),
|
|
115
|
+
Message(progress_msg)
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
119
|
+
"""Handle button presses"""
|
|
120
|
+
messages_container = self.query_one("#messages", ScrollableContainer)
|
|
121
|
+
|
|
122
|
+
if event.button.id == "add_tool_progress":
|
|
123
|
+
self._add_tool_progress(messages_container)
|
|
124
|
+
elif event.button.id == "add_nested":
|
|
125
|
+
self._add_nested_response(messages_container)
|
|
126
|
+
elif event.button.id == "add_multiple":
|
|
127
|
+
self._add_multiple_children(messages_container)
|
|
128
|
+
elif event.button.id == "add_dynamic":
|
|
129
|
+
self._add_dynamic_mount(messages_container)
|
|
130
|
+
elif event.button.id == "clear":
|
|
131
|
+
self._clear_messages(messages_container)
|
|
132
|
+
|
|
133
|
+
def _add_tool_progress(self, container):
|
|
134
|
+
"""Add tool execution progress using MessageResponse + Message"""
|
|
135
|
+
# Simulate tool execution progress
|
|
136
|
+
tool_msg = MessageType(
|
|
137
|
+
type=MsgType.ASSISTANT,
|
|
138
|
+
message=MessageContent("๐ง Executing file operation..."),
|
|
139
|
+
timestamp=time.time()
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Create MessageResponse with child during initialization
|
|
143
|
+
response = MessageResponse(children=[Message(tool_msg)])
|
|
144
|
+
|
|
145
|
+
container.mount(response)
|
|
146
|
+
container.scroll_end()
|
|
147
|
+
|
|
148
|
+
def _add_nested_response(self, container):
|
|
149
|
+
"""Add nested MessageResponse (response within response)"""
|
|
150
|
+
# Create outer message
|
|
151
|
+
outer_msg = MessageType(
|
|
152
|
+
type=MsgType.ASSISTANT,
|
|
153
|
+
message=MessageContent("Starting complex operation..."),
|
|
154
|
+
timestamp=time.time()
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Create inner message for sub-operation
|
|
158
|
+
inner_msg = MessageType(
|
|
159
|
+
type=MsgType.ASSISTANT,
|
|
160
|
+
message=MessageContent(" โโ Sub-operation in progress..."),
|
|
161
|
+
timestamp=time.time()
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Create nested structure with initialization
|
|
165
|
+
inner_response = MessageResponse(children=[Message(inner_msg)])
|
|
166
|
+
outer_response = MessageResponse(children=[
|
|
167
|
+
Message(outer_msg),
|
|
168
|
+
inner_response
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
container.mount(outer_response)
|
|
172
|
+
container.scroll_end()
|
|
173
|
+
|
|
174
|
+
def _add_multiple_children(self, container):
|
|
175
|
+
"""Add MessageResponse with multiple child messages"""
|
|
176
|
+
# Create multiple related messages
|
|
177
|
+
messages = [
|
|
178
|
+
MessageType(
|
|
179
|
+
type=MsgType.ASSISTANT,
|
|
180
|
+
message=MessageContent(f"Processing step {i+1}/3..."),
|
|
181
|
+
timestamp=time.time()
|
|
182
|
+
) for i in range(3)
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Create MessageResponse with multiple children during initialization
|
|
186
|
+
message_widgets = [Message(msg) for msg in messages]
|
|
187
|
+
response = MessageResponse(children=message_widgets)
|
|
188
|
+
|
|
189
|
+
container.mount(response)
|
|
190
|
+
container.scroll_end()
|
|
191
|
+
|
|
192
|
+
def _add_dynamic_mount(self, container):
|
|
193
|
+
"""Add MessageResponse and then dynamically mount children after it's mounted"""
|
|
194
|
+
# Create empty MessageResponse first
|
|
195
|
+
response = MessageResponse(content="Dynamic mounting example:")
|
|
196
|
+
|
|
197
|
+
# Mount it to the container
|
|
198
|
+
container.mount(response)
|
|
199
|
+
|
|
200
|
+
# Use call_after_refresh to mount children after the component is fully mounted
|
|
201
|
+
def mount_children_after():
|
|
202
|
+
msg = MessageType(
|
|
203
|
+
type=MsgType.ASSISTANT,
|
|
204
|
+
message=MessageContent("This was added dynamically after mounting!"),
|
|
205
|
+
timestamp=time.time()
|
|
206
|
+
)
|
|
207
|
+
response.mount_child(Message(msg))
|
|
208
|
+
|
|
209
|
+
# Schedule the dynamic mounting
|
|
210
|
+
self.call_after_refresh(mount_children_after)
|
|
211
|
+
container.scroll_end()
|
|
212
|
+
|
|
213
|
+
def _clear_messages(self, container):
|
|
214
|
+
"""Clear all messages except headers"""
|
|
215
|
+
for child in list(container.children)[2:]: # Keep title and subtitle
|
|
216
|
+
child.remove()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def main():
|
|
220
|
+
"""Run the demo application"""
|
|
221
|
+
app = MessageResponseChildrenDemo()
|
|
222
|
+
app.run()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
main()
|
examples/rich_example.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Simple file watching example."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
# Add parent directory to path for imports
|
|
9
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
10
|
+
|
|
11
|
+
from minion_code.services import (
|
|
12
|
+
start_watching_todo_file,
|
|
13
|
+
stop_watching_todo_file,
|
|
14
|
+
add_event_listener,
|
|
15
|
+
record_file_read,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
"""Simple file watching demonstration."""
|
|
21
|
+
|
|
22
|
+
# Set up event listener
|
|
23
|
+
def on_file_changed(context):
|
|
24
|
+
print(f"๐ File changed: {context.data['file_path']}")
|
|
25
|
+
print(f"๐ก {context.data['reminder']}")
|
|
26
|
+
|
|
27
|
+
add_event_listener('todo:file_changed', on_file_changed)
|
|
28
|
+
|
|
29
|
+
# Create test file
|
|
30
|
+
test_file = "simple_todo.json"
|
|
31
|
+
with open(test_file, "w") as f:
|
|
32
|
+
f.write('{"task": "initial"}')
|
|
33
|
+
|
|
34
|
+
print(f"โ
Created {test_file}")
|
|
35
|
+
|
|
36
|
+
# Start watching
|
|
37
|
+
start_watching_todo_file("test_agent", test_file)
|
|
38
|
+
record_file_read(test_file)
|
|
39
|
+
print("๐ Started watching file")
|
|
40
|
+
|
|
41
|
+
# Wait and modify
|
|
42
|
+
time.sleep(1)
|
|
43
|
+
print("โ๏ธ Modifying file...")
|
|
44
|
+
with open(test_file, "w") as f:
|
|
45
|
+
f.write('{"task": "modified"}')
|
|
46
|
+
|
|
47
|
+
# Wait for detection
|
|
48
|
+
time.sleep(2)
|
|
49
|
+
|
|
50
|
+
# Stop watching and cleanup
|
|
51
|
+
stop_watching_todo_file("test_agent")
|
|
52
|
+
os.remove(test_file)
|
|
53
|
+
print("๐งน Cleaned up")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|