minion-code 0.1.0__py3-none-any.whl → 0.1.2__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.2.dist-info/METADATA +476 -0
- minion_code-0.1.2.dist-info/RECORD +111 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/WHEEL +1 -1
- minion_code-0.1.2.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.2.dist-info}/licenses/LICENSE +0 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
CLI Entry Point
|
|
5
|
+
|
|
6
|
+
This is the main entry point that provides access to all CLI interfaces:
|
|
7
|
+
- Modern TUI REPL (default)
|
|
8
|
+
- Traditional console CLI
|
|
9
|
+
- Direct command execution
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Add project root to path
|
|
16
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run():
|
|
20
|
+
"""Main CLI entry point"""
|
|
21
|
+
try:
|
|
22
|
+
from minion_code.cli import app
|
|
23
|
+
|
|
24
|
+
app()
|
|
25
|
+
except Exception as e:
|
|
26
|
+
print(f"❌ Error starting CLI: {e}")
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_repl():
|
|
31
|
+
"""Direct REPL entry point"""
|
|
32
|
+
try:
|
|
33
|
+
from minion_code.screens.REPL import run as run_repl_func
|
|
34
|
+
|
|
35
|
+
run_repl_func()
|
|
36
|
+
except ImportError as e:
|
|
37
|
+
print(f"❌ TUI dependencies not available: {e}")
|
|
38
|
+
print("💡 Install with: pip install textual rich")
|
|
39
|
+
# Fallback to console
|
|
40
|
+
from minion_code.cli_simple import app
|
|
41
|
+
|
|
42
|
+
app()
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"❌ Error starting REPL: {e}")
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run_console():
|
|
49
|
+
"""Direct console CLI entry point"""
|
|
50
|
+
try:
|
|
51
|
+
from minion_code.cli_simple import app
|
|
52
|
+
|
|
53
|
+
app()
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"❌ Error starting console CLI: {e}")
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
run()
|
|
@@ -8,7 +8,7 @@ import uuid
|
|
|
8
8
|
import json
|
|
9
9
|
|
|
10
10
|
# Add the parent directory to the path so we can import minion_code
|
|
11
|
-
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
12
12
|
|
|
13
13
|
from minion_code.agents.code_agent import MinionCodeAgent
|
|
14
14
|
|
|
@@ -16,84 +16,91 @@ from minion_code.agents.code_agent import MinionCodeAgent
|
|
|
16
16
|
async def demonstrate_todo_workflow():
|
|
17
17
|
"""Demonstrate using Todo tools with MinionCodeAgent."""
|
|
18
18
|
print("=== MinionCodeAgent Todo Workflow Demo ===\n")
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
# Create agent
|
|
21
21
|
print("1. Creating MinionCodeAgent...")
|
|
22
|
-
agent = await MinionCodeAgent.create(
|
|
23
|
-
name="Todo Demo Agent",
|
|
24
|
-
llm="gpt-4o-mini"
|
|
25
|
-
)
|
|
22
|
+
agent = await MinionCodeAgent.create(name="Todo Demo Agent", llm="gpt-4o-mini")
|
|
26
23
|
print("✓ Agent created successfully\n")
|
|
27
|
-
|
|
24
|
+
|
|
28
25
|
# Show available tools
|
|
29
26
|
print("2. Available tools:")
|
|
30
27
|
tools_info = agent.get_tools_info()
|
|
31
|
-
todo_tools = [tool for tool in tools_info if
|
|
28
|
+
todo_tools = [tool for tool in tools_info if "todo" in tool["name"].lower()]
|
|
32
29
|
for tool in todo_tools:
|
|
33
|
-
readonly_icon = "🔒" if tool[
|
|
30
|
+
readonly_icon = "🔒" if tool["readonly"] else "✏️"
|
|
34
31
|
print(f" {readonly_icon} {tool['name']}: {tool['description']}")
|
|
35
32
|
print()
|
|
36
|
-
|
|
33
|
+
|
|
37
34
|
# Define project tasks
|
|
38
35
|
tasks = [
|
|
39
36
|
"Analyze project requirements",
|
|
40
|
-
"Design system architecture",
|
|
37
|
+
"Design system architecture",
|
|
41
38
|
"Implement core functionality",
|
|
42
39
|
"Write unit tests",
|
|
43
40
|
"Create documentation",
|
|
44
|
-
"Deploy to production"
|
|
41
|
+
"Deploy to production",
|
|
45
42
|
]
|
|
46
|
-
|
|
43
|
+
|
|
47
44
|
# Create initial todos using agent
|
|
48
45
|
print("3. Creating initial todos...")
|
|
49
46
|
todos_data = []
|
|
50
47
|
for i, task in enumerate(tasks):
|
|
51
|
-
todos_data.append(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
todos_data.append(
|
|
49
|
+
{
|
|
50
|
+
"id": str(uuid.uuid4()),
|
|
51
|
+
"content": task,
|
|
52
|
+
"status": (
|
|
53
|
+
"pending" if i > 0 else "in_progress"
|
|
54
|
+
), # First task in progress
|
|
55
|
+
"priority": "high" if i < 2 else "medium",
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
|
|
58
59
|
todos_json = json.dumps(todos_data)
|
|
59
|
-
|
|
60
|
+
|
|
60
61
|
# Use agent to create todos
|
|
61
62
|
create_message = f"""Please use the todo_write tool to create these todos:
|
|
62
63
|
{todos_json}"""
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
response = await agent.run_async(create_message)
|
|
65
|
-
print(
|
|
66
|
-
|
|
66
|
+
print(
|
|
67
|
+
f"✓ Agent response: {response.answer if hasattr(response, 'answer') else str(response)}\n"
|
|
68
|
+
)
|
|
69
|
+
|
|
67
70
|
# Read current todos
|
|
68
71
|
print("4. Reading current todos...")
|
|
69
72
|
read_message = """Please use the todo_read tool to show current todos."""
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
response = await agent.run_async(read_message)
|
|
72
|
-
print(
|
|
73
|
-
|
|
75
|
+
print(
|
|
76
|
+
f"✓ Current todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n"
|
|
77
|
+
)
|
|
78
|
+
|
|
74
79
|
# Simulate completing a few tasks
|
|
75
80
|
print("5. Completing tasks...")
|
|
76
|
-
|
|
81
|
+
|
|
77
82
|
# Complete first task and start next
|
|
78
83
|
for i in range(3): # Complete 3 tasks
|
|
79
84
|
print(f" Step {i+1}: Completing current task and starting next...")
|
|
80
|
-
|
|
85
|
+
|
|
81
86
|
# Get current todos from storage to update them
|
|
82
87
|
from minion_code.utils.todo_storage import get_todos, TodoStatus
|
|
88
|
+
|
|
83
89
|
# Use the same agent ID logic as the tool
|
|
84
90
|
import hashlib
|
|
91
|
+
|
|
85
92
|
agent_id = "demo_agent" # We'll use a consistent ID for this demo
|
|
86
93
|
current_todos = get_todos(agent_id)
|
|
87
|
-
|
|
94
|
+
|
|
88
95
|
if not current_todos:
|
|
89
96
|
print(" No todos found")
|
|
90
97
|
break
|
|
91
|
-
|
|
98
|
+
|
|
92
99
|
# Update todos: complete in_progress, start next pending
|
|
93
100
|
updated_todos = []
|
|
94
101
|
found_in_progress = False
|
|
95
102
|
started_next = False
|
|
96
|
-
|
|
103
|
+
|
|
97
104
|
for todo in current_todos:
|
|
98
105
|
if todo.status == TodoStatus.IN_PROGRESS:
|
|
99
106
|
# Mark as completed
|
|
@@ -101,7 +108,7 @@ async def demonstrate_todo_workflow():
|
|
|
101
108
|
"id": todo.id,
|
|
102
109
|
"content": todo.content,
|
|
103
110
|
"status": "completed",
|
|
104
|
-
"priority": todo.priority.value
|
|
111
|
+
"priority": todo.priority.value,
|
|
105
112
|
}
|
|
106
113
|
found_in_progress = True
|
|
107
114
|
elif todo.status == TodoStatus.PENDING and not started_next:
|
|
@@ -110,7 +117,7 @@ async def demonstrate_todo_workflow():
|
|
|
110
117
|
"id": todo.id,
|
|
111
118
|
"content": todo.content,
|
|
112
119
|
"status": "in_progress",
|
|
113
|
-
"priority": todo.priority.value
|
|
120
|
+
"priority": todo.priority.value,
|
|
114
121
|
}
|
|
115
122
|
started_next = True
|
|
116
123
|
else:
|
|
@@ -119,32 +126,36 @@ async def demonstrate_todo_workflow():
|
|
|
119
126
|
"id": todo.id,
|
|
120
127
|
"content": todo.content,
|
|
121
128
|
"status": todo.status.value,
|
|
122
|
-
"priority": todo.priority.value
|
|
129
|
+
"priority": todo.priority.value,
|
|
123
130
|
}
|
|
124
|
-
|
|
131
|
+
|
|
125
132
|
updated_todos.append(todo_data)
|
|
126
|
-
|
|
133
|
+
|
|
127
134
|
if not found_in_progress:
|
|
128
135
|
print(" No in_progress task found")
|
|
129
136
|
break
|
|
130
|
-
|
|
137
|
+
|
|
131
138
|
# Update todos using agent
|
|
132
139
|
updated_json = json.dumps(updated_todos)
|
|
133
140
|
update_message = f"""Please use the todo_write tool to update todos:
|
|
134
141
|
{updated_json}"""
|
|
135
|
-
|
|
142
|
+
|
|
136
143
|
response = await agent.run_async(update_message)
|
|
137
|
-
print(
|
|
138
|
-
|
|
144
|
+
print(
|
|
145
|
+
f" ✓ Updated: {response.answer if hasattr(response, 'answer') else str(response)}"
|
|
146
|
+
)
|
|
147
|
+
|
|
139
148
|
print()
|
|
140
|
-
|
|
149
|
+
|
|
141
150
|
# Final status
|
|
142
151
|
print("6. Final todo status...")
|
|
143
152
|
final_message = """Please use the todo_read tool to show final todos."""
|
|
144
|
-
|
|
153
|
+
|
|
145
154
|
response = await agent.run_async(final_message)
|
|
146
|
-
print(
|
|
147
|
-
|
|
155
|
+
print(
|
|
156
|
+
f"✓ Final todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n"
|
|
157
|
+
)
|
|
158
|
+
|
|
148
159
|
# Show conversation history
|
|
149
160
|
print("7. Conversation summary:")
|
|
150
161
|
history = agent.get_conversation_history()
|
|
@@ -152,7 +163,7 @@ async def demonstrate_todo_workflow():
|
|
|
152
163
|
for i, interaction in enumerate(history, 1):
|
|
153
164
|
print(f" {i}. User: {interaction['user_message'][:50]}...")
|
|
154
165
|
print(f" Agent: {str(interaction['agent_response'])[:50]}...")
|
|
155
|
-
|
|
166
|
+
|
|
156
167
|
print("\n=== Demo Complete ===")
|
|
157
168
|
|
|
158
169
|
|
|
@@ -162,4 +173,4 @@ def main():
|
|
|
162
173
|
|
|
163
174
|
|
|
164
175
|
if __name__ == "__main__":
|
|
165
|
-
main()
|
|
176
|
+
main()
|
|
@@ -13,15 +13,20 @@ from textual.widgets import Header, Footer, Button, Static
|
|
|
13
13
|
# Import the components
|
|
14
14
|
import sys
|
|
15
15
|
import os
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
17
18
|
|
|
18
19
|
from minion_code.components import Message, MessageResponse
|
|
19
|
-
from minion_code.
|
|
20
|
+
from minion_code.type_defs import (
|
|
21
|
+
Message as MessageType,
|
|
22
|
+
MessageContent,
|
|
23
|
+
MessageType as MsgType,
|
|
24
|
+
)
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
class MessageResponseChildrenDemo(App):
|
|
23
28
|
"""Demo showing MessageResponse children usage like React"""
|
|
24
|
-
|
|
29
|
+
|
|
25
30
|
CSS = """
|
|
26
31
|
Screen {
|
|
27
32
|
background: $surface;
|
|
@@ -46,79 +51,82 @@ class MessageResponseChildrenDemo(App):
|
|
|
46
51
|
background: $surface-lighten-1;
|
|
47
52
|
}
|
|
48
53
|
"""
|
|
49
|
-
|
|
54
|
+
|
|
50
55
|
def compose(self) -> ComposeResult:
|
|
51
56
|
"""Compose the demo interface"""
|
|
52
57
|
yield Header()
|
|
53
|
-
|
|
58
|
+
|
|
54
59
|
with Container(classes="demo-container"):
|
|
55
60
|
with ScrollableContainer(classes="messages-container", id="messages"):
|
|
56
61
|
# Show examples of different usage patterns
|
|
57
62
|
yield from self._create_usage_examples()
|
|
58
|
-
|
|
63
|
+
|
|
59
64
|
# with Container(classes="controls"):
|
|
60
65
|
# yield Button("Add Tool Progress", id="add_tool_progress", variant="primary")
|
|
61
66
|
# yield Button("Add Nested Response", id="add_nested", variant="success")
|
|
62
67
|
# yield Button("Add Multiple Children", id="add_multiple", variant="warning")
|
|
63
68
|
# yield Button("Add Dynamic Mount", id="add_dynamic", variant="default")
|
|
64
69
|
# yield Button("Clear", id="clear", variant="error")
|
|
65
|
-
|
|
70
|
+
|
|
66
71
|
yield Footer()
|
|
67
72
|
self.log(self.tree)
|
|
68
|
-
|
|
73
|
+
|
|
69
74
|
def _create_usage_examples(self):
|
|
70
75
|
"""Create examples showing different MessageResponse usage patterns"""
|
|
71
76
|
child_msg = MessageType(
|
|
72
77
|
type=MsgType.ASSISTANT,
|
|
73
78
|
message=MessageContent("This message is wrapped in MessageResponse"),
|
|
74
|
-
timestamp=time.time()
|
|
79
|
+
timestamp=time.time(),
|
|
75
80
|
)
|
|
76
81
|
yield Message(child_msg, classes="example-header")
|
|
77
|
-
yield Static(
|
|
82
|
+
yield Static(
|
|
83
|
+
"\n2. Single child message (React-like):", classes="example-header"
|
|
84
|
+
)
|
|
78
85
|
|
|
79
86
|
# This is equivalent to: <MessageResponse><Message /></MessageResponse>
|
|
80
87
|
|
|
81
88
|
yield MessageResponse(children=[Message(child_msg, classes="example-header")])
|
|
82
|
-
|
|
89
|
+
|
|
83
90
|
# Example 3: Tool execution progress
|
|
84
91
|
yield Static("\n3. Tool execution progress:", classes="example-header")
|
|
85
92
|
tool_msg = MessageType(
|
|
86
93
|
type=MsgType.TOOL_USE,
|
|
87
|
-
message=MessageContent(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
message=MessageContent(
|
|
95
|
+
[
|
|
96
|
+
{
|
|
97
|
+
"type": "tool_use",
|
|
98
|
+
"id": "tool_demo",
|
|
99
|
+
"name": "file_editor",
|
|
100
|
+
"input": {"path": "demo.py", "content": "print('Hello')"},
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
),
|
|
104
|
+
timestamp=time.time(),
|
|
94
105
|
)
|
|
95
106
|
yield MessageResponse(children=[Message(tool_msg, verbose=True)])
|
|
96
|
-
|
|
107
|
+
|
|
97
108
|
# Example 4: Multiple children (using initialization)
|
|
98
109
|
yield Static("\n4. Multiple children:", classes="example-header")
|
|
99
|
-
|
|
110
|
+
|
|
100
111
|
# Add multiple children to demonstrate flexibility
|
|
101
112
|
status_msg = MessageType(
|
|
102
113
|
type=MsgType.ASSISTANT,
|
|
103
114
|
message=MessageContent("Step 1: Analyzing code..."),
|
|
104
|
-
timestamp=time.time()
|
|
115
|
+
timestamp=time.time(),
|
|
105
116
|
)
|
|
106
117
|
progress_msg = MessageType(
|
|
107
118
|
type=MsgType.ASSISTANT,
|
|
108
119
|
message=MessageContent("Step 2: Applying changes..."),
|
|
109
|
-
timestamp=time.time()
|
|
120
|
+
timestamp=time.time(),
|
|
110
121
|
)
|
|
111
|
-
|
|
122
|
+
|
|
112
123
|
# Create with children during initialization
|
|
113
|
-
yield MessageResponse(children=[
|
|
114
|
-
|
|
115
|
-
Message(progress_msg)
|
|
116
|
-
])
|
|
117
|
-
|
|
124
|
+
yield MessageResponse(children=[Message(status_msg), Message(progress_msg)])
|
|
125
|
+
|
|
118
126
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
119
127
|
"""Handle button presses"""
|
|
120
128
|
messages_container = self.query_one("#messages", ScrollableContainer)
|
|
121
|
-
|
|
129
|
+
|
|
122
130
|
if event.button.id == "add_tool_progress":
|
|
123
131
|
self._add_tool_progress(messages_container)
|
|
124
132
|
elif event.button.id == "add_nested":
|
|
@@ -129,48 +137,45 @@ class MessageResponseChildrenDemo(App):
|
|
|
129
137
|
self._add_dynamic_mount(messages_container)
|
|
130
138
|
elif event.button.id == "clear":
|
|
131
139
|
self._clear_messages(messages_container)
|
|
132
|
-
|
|
140
|
+
|
|
133
141
|
def _add_tool_progress(self, container):
|
|
134
142
|
"""Add tool execution progress using MessageResponse + Message"""
|
|
135
143
|
# Simulate tool execution progress
|
|
136
144
|
tool_msg = MessageType(
|
|
137
145
|
type=MsgType.ASSISTANT,
|
|
138
146
|
message=MessageContent("🔧 Executing file operation..."),
|
|
139
|
-
timestamp=time.time()
|
|
147
|
+
timestamp=time.time(),
|
|
140
148
|
)
|
|
141
|
-
|
|
149
|
+
|
|
142
150
|
# Create MessageResponse with child during initialization
|
|
143
151
|
response = MessageResponse(children=[Message(tool_msg)])
|
|
144
|
-
|
|
152
|
+
|
|
145
153
|
container.mount(response)
|
|
146
154
|
container.scroll_end()
|
|
147
|
-
|
|
155
|
+
|
|
148
156
|
def _add_nested_response(self, container):
|
|
149
157
|
"""Add nested MessageResponse (response within response)"""
|
|
150
158
|
# Create outer message
|
|
151
159
|
outer_msg = MessageType(
|
|
152
160
|
type=MsgType.ASSISTANT,
|
|
153
161
|
message=MessageContent("Starting complex operation..."),
|
|
154
|
-
timestamp=time.time()
|
|
162
|
+
timestamp=time.time(),
|
|
155
163
|
)
|
|
156
|
-
|
|
164
|
+
|
|
157
165
|
# Create inner message for sub-operation
|
|
158
166
|
inner_msg = MessageType(
|
|
159
167
|
type=MsgType.ASSISTANT,
|
|
160
168
|
message=MessageContent(" └─ Sub-operation in progress..."),
|
|
161
|
-
timestamp=time.time()
|
|
169
|
+
timestamp=time.time(),
|
|
162
170
|
)
|
|
163
|
-
|
|
171
|
+
|
|
164
172
|
# Create nested structure with initialization
|
|
165
173
|
inner_response = MessageResponse(children=[Message(inner_msg)])
|
|
166
|
-
outer_response = MessageResponse(children=[
|
|
167
|
-
|
|
168
|
-
inner_response
|
|
169
|
-
])
|
|
170
|
-
|
|
174
|
+
outer_response = MessageResponse(children=[Message(outer_msg), inner_response])
|
|
175
|
+
|
|
171
176
|
container.mount(outer_response)
|
|
172
177
|
container.scroll_end()
|
|
173
|
-
|
|
178
|
+
|
|
174
179
|
def _add_multiple_children(self, container):
|
|
175
180
|
"""Add MessageResponse with multiple child messages"""
|
|
176
181
|
# Create multiple related messages
|
|
@@ -178,38 +183,39 @@ class MessageResponseChildrenDemo(App):
|
|
|
178
183
|
MessageType(
|
|
179
184
|
type=MsgType.ASSISTANT,
|
|
180
185
|
message=MessageContent(f"Processing step {i+1}/3..."),
|
|
181
|
-
timestamp=time.time()
|
|
182
|
-
)
|
|
186
|
+
timestamp=time.time(),
|
|
187
|
+
)
|
|
188
|
+
for i in range(3)
|
|
183
189
|
]
|
|
184
|
-
|
|
190
|
+
|
|
185
191
|
# Create MessageResponse with multiple children during initialization
|
|
186
192
|
message_widgets = [Message(msg) for msg in messages]
|
|
187
193
|
response = MessageResponse(children=message_widgets)
|
|
188
|
-
|
|
194
|
+
|
|
189
195
|
container.mount(response)
|
|
190
196
|
container.scroll_end()
|
|
191
|
-
|
|
197
|
+
|
|
192
198
|
def _add_dynamic_mount(self, container):
|
|
193
199
|
"""Add MessageResponse and then dynamically mount children after it's mounted"""
|
|
194
200
|
# Create empty MessageResponse first
|
|
195
201
|
response = MessageResponse(content="Dynamic mounting example:")
|
|
196
|
-
|
|
202
|
+
|
|
197
203
|
# Mount it to the container
|
|
198
204
|
container.mount(response)
|
|
199
|
-
|
|
205
|
+
|
|
200
206
|
# Use call_after_refresh to mount children after the component is fully mounted
|
|
201
207
|
def mount_children_after():
|
|
202
208
|
msg = MessageType(
|
|
203
209
|
type=MsgType.ASSISTANT,
|
|
204
210
|
message=MessageContent("This was added dynamically after mounting!"),
|
|
205
|
-
timestamp=time.time()
|
|
211
|
+
timestamp=time.time(),
|
|
206
212
|
)
|
|
207
213
|
response.mount_child(Message(msg))
|
|
208
|
-
|
|
214
|
+
|
|
209
215
|
# Schedule the dynamic mounting
|
|
210
216
|
self.call_after_refresh(mount_children_after)
|
|
211
217
|
container.scroll_end()
|
|
212
|
-
|
|
218
|
+
|
|
213
219
|
def _clear_messages(self, container):
|
|
214
220
|
"""Clear all messages except headers"""
|
|
215
221
|
for child in list(container.children)[2:]: # Keep title and subtitle
|
|
@@ -223,4 +229,4 @@ def main():
|
|
|
223
229
|
|
|
224
230
|
|
|
225
231
|
if __name__ == "__main__":
|
|
226
|
-
main()
|
|
232
|
+
main()
|