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.
Files changed (59) hide show
  1. examples/advance_tui.py +508 -0
  2. examples/agent_with_todos.py +165 -0
  3. examples/file_freshness_example.py +97 -0
  4. examples/file_watching_example.py +110 -0
  5. examples/interruptible_tui.py +5 -0
  6. examples/message_response_children_demo.py +226 -0
  7. examples/rich_example.py +4 -0
  8. examples/simple_file_watching.py +57 -0
  9. examples/simple_tui.py +267 -0
  10. examples/simple_usage.py +69 -0
  11. minion_code/__init__.py +16 -0
  12. minion_code/agents/__init__.py +11 -0
  13. minion_code/agents/code_agent.py +320 -0
  14. minion_code/cli.py +502 -0
  15. minion_code/commands/__init__.py +90 -0
  16. minion_code/commands/clear_command.py +70 -0
  17. minion_code/commands/help_command.py +90 -0
  18. minion_code/commands/history_command.py +104 -0
  19. minion_code/commands/quit_command.py +32 -0
  20. minion_code/commands/status_command.py +115 -0
  21. minion_code/commands/tools_command.py +86 -0
  22. minion_code/commands/version_command.py +104 -0
  23. minion_code/components/Message.py +304 -0
  24. minion_code/components/MessageResponse.py +188 -0
  25. minion_code/components/PromptInput.py +534 -0
  26. minion_code/components/__init__.py +29 -0
  27. minion_code/screens/REPL.py +925 -0
  28. minion_code/screens/__init__.py +4 -0
  29. minion_code/services/__init__.py +50 -0
  30. minion_code/services/event_system.py +108 -0
  31. minion_code/services/file_freshness_service.py +582 -0
  32. minion_code/tools/__init__.py +69 -0
  33. minion_code/tools/bash_tool.py +58 -0
  34. minion_code/tools/file_edit_tool.py +238 -0
  35. minion_code/tools/file_read_tool.py +73 -0
  36. minion_code/tools/file_write_tool.py +36 -0
  37. minion_code/tools/glob_tool.py +58 -0
  38. minion_code/tools/grep_tool.py +105 -0
  39. minion_code/tools/ls_tool.py +65 -0
  40. minion_code/tools/multi_edit_tool.py +271 -0
  41. minion_code/tools/python_interpreter_tool.py +105 -0
  42. minion_code/tools/todo_read_tool.py +100 -0
  43. minion_code/tools/todo_write_tool.py +234 -0
  44. minion_code/tools/user_input_tool.py +53 -0
  45. minion_code/types.py +88 -0
  46. minion_code/utils/__init__.py +44 -0
  47. minion_code/utils/mcp_loader.py +211 -0
  48. minion_code/utils/todo_file_utils.py +110 -0
  49. minion_code/utils/todo_storage.py +149 -0
  50. minion_code-0.1.0.dist-info/METADATA +350 -0
  51. minion_code-0.1.0.dist-info/RECORD +59 -0
  52. minion_code-0.1.0.dist-info/WHEEL +5 -0
  53. minion_code-0.1.0.dist-info/entry_points.txt +4 -0
  54. minion_code-0.1.0.dist-info/licenses/LICENSE +661 -0
  55. minion_code-0.1.0.dist-info/top_level.txt +3 -0
  56. tests/__init__.py +1 -0
  57. tests/test_basic.py +20 -0
  58. tests/test_readonly_tools.py +102 -0
  59. tests/test_tools.py +83 -0
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Help command - Show available commands and their usage
5
+ """
6
+
7
+ from rich.table import Table
8
+ from rich.panel import Panel
9
+ from rich.markdown import Markdown
10
+ from minion_code.commands import BaseCommand
11
+
12
+
13
+ class HelpCommand(BaseCommand):
14
+ """Show help information for commands."""
15
+
16
+ name = "help"
17
+ description = "Show available commands and their usage"
18
+ usage = "/help [command_name]"
19
+ aliases = ["h", "?"]
20
+
21
+ async def execute(self, args: str) -> None:
22
+ """Execute the help command."""
23
+ args = args.strip()
24
+
25
+ if args:
26
+ # Show help for specific command
27
+ await self._show_command_help(args)
28
+ else:
29
+ # Show general help
30
+ await self._show_general_help()
31
+
32
+ async def _show_command_help(self, command_name: str) -> None:
33
+ """Show help for a specific command."""
34
+ from minion_code.commands import command_registry
35
+
36
+ command_class = command_registry.get_command(command_name)
37
+ if not command_class:
38
+ error_panel = Panel(
39
+ f"❌ [bold red]Command '/{command_name}' not found[/bold red]",
40
+ title="[bold red]Error[/bold red]",
41
+ border_style="red"
42
+ )
43
+ self.console.print(error_panel)
44
+ return
45
+
46
+ # Create temporary command instance to get help
47
+ temp_command = command_class(self.console, self.agent)
48
+ help_text = temp_command.get_help()
49
+
50
+ help_panel = Panel(
51
+ Markdown(help_text),
52
+ title=f"[bold blue]Help: /{command_name}[/bold blue]",
53
+ border_style="blue"
54
+ )
55
+ self.console.print(help_panel)
56
+
57
+ async def _show_general_help(self) -> None:
58
+ """Show general help with all commands."""
59
+ from minion_code.commands import command_registry
60
+
61
+ commands = command_registry.list_commands()
62
+
63
+ help_table = Table(
64
+ title="📚 Available Commands",
65
+ show_header=True,
66
+ header_style="bold blue"
67
+ )
68
+ help_table.add_column("Command", style="cyan", no_wrap=True)
69
+ help_table.add_column("Description", style="white")
70
+ help_table.add_column("Aliases", style="yellow")
71
+
72
+ for name, command_class in sorted(commands.items()):
73
+ aliases = ", ".join(f"/{alias}" for alias in command_class.aliases)
74
+ help_table.add_row(
75
+ f"/{name}",
76
+ command_class.description,
77
+ aliases or "-"
78
+ )
79
+
80
+ self.console.print(help_table)
81
+
82
+ # Show usage info
83
+ usage_panel = Panel(
84
+ "💡 [italic]Commands must start with '/' (e.g., /help, /tools)[/italic]\n"
85
+ "💬 [italic]Regular messages are sent to the AI agent[/italic]\n"
86
+ "🔍 [italic]Use '/help <command>' for detailed help on a specific command[/italic]",
87
+ title="[bold green]Usage Tips[/bold green]",
88
+ border_style="green"
89
+ )
90
+ self.console.print(usage_panel)
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ History command - Show conversation history
5
+ """
6
+
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+ from minion_code.commands import BaseCommand
10
+
11
+
12
+ class HistoryCommand(BaseCommand):
13
+ """Show conversation history."""
14
+
15
+ name = "history"
16
+ description = "Show conversation history with the agent"
17
+ usage = "/history [count]"
18
+ aliases = ["hist", "h"]
19
+
20
+ async def execute(self, args: str) -> None:
21
+ """Execute the history command."""
22
+ if not self.agent:
23
+ error_panel = Panel(
24
+ "❌ [bold red]Agent not initialized[/bold red]",
25
+ title="[bold red]Error[/bold red]",
26
+ border_style="red"
27
+ )
28
+ self.console.print(error_panel)
29
+ return
30
+
31
+ # Parse count argument
32
+ count = 5 # default
33
+ if args.strip():
34
+ try:
35
+ count = int(args.strip())
36
+ if count <= 0:
37
+ count = 5
38
+ except ValueError:
39
+ error_panel = Panel(
40
+ f"❌ [bold red]Invalid count: '{args.strip()}'. Using default (5)[/bold red]",
41
+ title="[bold red]Warning[/bold red]",
42
+ border_style="yellow"
43
+ )
44
+ self.console.print(error_panel)
45
+
46
+ history = self.agent.get_conversation_history()
47
+ if not history:
48
+ no_history_panel = Panel(
49
+ "📝 [italic]No conversation history yet.[/italic]",
50
+ title="[bold blue]History[/bold blue]",
51
+ border_style="blue"
52
+ )
53
+ self.console.print(no_history_panel)
54
+ return
55
+
56
+ # Show header
57
+ header_panel = Panel(
58
+ f"📝 [bold blue]Conversation History (showing last {min(count, len(history))} of {len(history)} messages)[/bold blue]",
59
+ border_style="blue"
60
+ )
61
+ self.console.print(header_panel)
62
+
63
+ # Show recent messages
64
+ recent_history = history[-count:] if count < len(history) else history
65
+
66
+ for i, entry in enumerate(recent_history, 1):
67
+ message_num = len(history) - len(recent_history) + i
68
+
69
+ # User message
70
+ user_msg = entry['user_message']
71
+ if len(user_msg) > 150:
72
+ user_msg = user_msg[:150] + "..."
73
+
74
+ user_panel = Panel(
75
+ user_msg,
76
+ title=f"👤 [bold cyan]You (#{message_num})[/bold cyan]",
77
+ border_style="cyan"
78
+ )
79
+ self.console.print(user_panel)
80
+
81
+ # Agent response
82
+ agent_msg = entry['agent_response']
83
+ if len(agent_msg) > 200:
84
+ agent_msg = agent_msg[:200] + "..."
85
+
86
+ agent_panel = Panel(
87
+ agent_msg,
88
+ title="🤖 [bold green]Agent[/bold green]",
89
+ border_style="green"
90
+ )
91
+ self.console.print(agent_panel)
92
+
93
+ if i < len(recent_history): # Don't add spacing after last message
94
+ self.console.print()
95
+
96
+ # Show summary if there are more messages
97
+ if len(history) > count:
98
+ summary_panel = Panel(
99
+ f"💡 [italic]Showing {count} most recent messages. "
100
+ f"Use '/history {len(history)}' to see all {len(history)} messages.[/italic]",
101
+ title="[bold yellow]Note[/bold yellow]",
102
+ border_style="yellow"
103
+ )
104
+ self.console.print(summary_panel)
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Quit command - Exit the application
5
+ """
6
+
7
+ from rich.panel import Panel
8
+ from minion_code.commands import BaseCommand
9
+
10
+
11
+ class QuitCommand(BaseCommand):
12
+ """Exit the application."""
13
+
14
+ name = "quit"
15
+ description = "Exit the application"
16
+ usage = "/quit"
17
+ aliases = ["exit", "q", "bye"]
18
+
19
+ async def execute(self, args: str) -> None:
20
+ """Execute the quit command."""
21
+ goodbye_panel = Panel(
22
+ "👋 [bold yellow]Goodbye! Thanks for using MinionCode![/bold yellow]",
23
+ title="[bold red]Exit[/bold red]",
24
+ border_style="red"
25
+ )
26
+ self.console.print(goodbye_panel)
27
+
28
+ # Set a flag that the TUI can check and cleanup resources
29
+ if hasattr(self, '_tui_instance'):
30
+ self._tui_instance.running = False
31
+ # Cleanup MCP resources
32
+ await self._tui_instance.cleanup()
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Status command - Show system status and information
5
+ """
6
+
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+ from rich.text import Text
10
+ import sys
11
+ import platform
12
+ from datetime import datetime
13
+ from minion_code.commands import BaseCommand
14
+
15
+
16
+ class StatusCommand(BaseCommand):
17
+ """Show system status and information."""
18
+
19
+ name = "status"
20
+ description = "Show system status, agent info, and statistics"
21
+ usage = "/status"
22
+ aliases = ["info", "stat"]
23
+
24
+ async def execute(self, args: str) -> None:
25
+ """Execute the status command."""
26
+ # Create status table
27
+ status_table = Table(
28
+ title="📊 System Status",
29
+ show_header=True,
30
+ header_style="bold blue"
31
+ )
32
+ status_table.add_column("Component", style="cyan", no_wrap=True)
33
+ status_table.add_column("Status", style="white")
34
+ status_table.add_column("Details", style="yellow")
35
+
36
+ # System info
37
+ status_table.add_row(
38
+ "System",
39
+ "✅ Running",
40
+ f"{platform.system()} {platform.release()}"
41
+ )
42
+
43
+ status_table.add_row(
44
+ "Python",
45
+ "✅ Active",
46
+ f"{sys.version.split()[0]}"
47
+ )
48
+
49
+ # Agent info
50
+ if self.agent:
51
+ history = self.agent.get_conversation_history()
52
+ tools_count = len(self.agent.tools) if self.agent.tools else 0
53
+
54
+ status_table.add_row(
55
+ "Agent",
56
+ "✅ Ready",
57
+ f"{tools_count} tools loaded"
58
+ )
59
+
60
+ status_table.add_row(
61
+ "Conversation",
62
+ "📝 Active" if history else "📝 Empty",
63
+ f"{len(history)} messages" if history else "No messages"
64
+ )
65
+ else:
66
+ status_table.add_row(
67
+ "Agent",
68
+ "❌ Not Ready",
69
+ "Not initialized"
70
+ )
71
+
72
+ # Memory info (basic)
73
+ try:
74
+ import psutil
75
+ memory = psutil.virtual_memory()
76
+ status_table.add_row(
77
+ "Memory",
78
+ "📊 Monitored",
79
+ f"{memory.percent}% used ({memory.available // (1024**3)} GB free)"
80
+ )
81
+ except ImportError:
82
+ status_table.add_row(
83
+ "Memory",
84
+ "❌ Not Available",
85
+ "psutil not installed"
86
+ )
87
+
88
+ self.console.print(status_table)
89
+
90
+ # Additional info panel
91
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
92
+
93
+ info_text = Text()
94
+ info_text.append("🕒 Current Time: ", style="bold")
95
+ info_text.append(f"{current_time}\n", style="white")
96
+
97
+ if self.agent:
98
+ info_text.append("🤖 Agent Model: ", style="bold")
99
+ info_text.append(f"{getattr(self.agent, 'llm', 'Unknown')}\n", style="white")
100
+
101
+ info_text.append("🛠️ Available Tools: ", style="bold")
102
+ if self.agent.tools:
103
+ tool_names = [tool.name for tool in self.agent.tools[:5]]
104
+ if len(self.agent.tools) > 5:
105
+ tool_names.append(f"... and {len(self.agent.tools) - 5} more")
106
+ info_text.append(", ".join(tool_names), style="white")
107
+ else:
108
+ info_text.append("None", style="red")
109
+
110
+ info_panel = Panel(
111
+ info_text,
112
+ title="[bold green]Additional Information[/bold green]",
113
+ border_style="green"
114
+ )
115
+ self.console.print(info_panel)
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Tools command - Show available tools
5
+ """
6
+
7
+ from rich.table import Table
8
+ from rich.panel import Panel
9
+ from minion_code.commands import BaseCommand
10
+
11
+
12
+ class ToolsCommand(BaseCommand):
13
+ """Show available tools."""
14
+
15
+ name = "tools"
16
+ description = "List all available tools and their descriptions"
17
+ usage = "/tools [filter]"
18
+ aliases = ["t"]
19
+
20
+ async def execute(self, args: str) -> None:
21
+ """Execute the tools command."""
22
+ if not self.agent or not self.agent.tools:
23
+ error_panel = Panel(
24
+ "❌ [bold red]No tools available or agent not initialized[/bold red]",
25
+ title="[bold red]Error[/bold red]",
26
+ border_style="red"
27
+ )
28
+ self.console.print(error_panel)
29
+ return
30
+
31
+ filter_text = args.strip().lower()
32
+ tools = self.agent.tools
33
+
34
+ # Filter tools if filter text provided
35
+ if filter_text:
36
+ tools = [tool for tool in tools
37
+ if filter_text in tool.name.lower() or
38
+ filter_text in tool.description.lower()]
39
+
40
+ if not tools:
41
+ no_tools_panel = Panel(
42
+ f"❌ [bold yellow]No tools found matching '{filter_text}'[/bold yellow]",
43
+ title="[bold yellow]No Results[/bold yellow]",
44
+ border_style="yellow"
45
+ )
46
+ self.console.print(no_tools_panel)
47
+ return
48
+
49
+ # Create tools table
50
+ tools_table = Table(
51
+ title=f"🛠️ Available Tools{f' (filtered: {filter_text})' if filter_text else ''}",
52
+ show_header=True,
53
+ header_style="bold magenta"
54
+ )
55
+ tools_table.add_column("Tool Name", style="cyan", no_wrap=True)
56
+ tools_table.add_column("Description", style="white")
57
+ tools_table.add_column("Type", style="yellow")
58
+ tools_table.add_column("Inputs", style="green")
59
+
60
+ for tool in tools:
61
+ tool_type = "Read-only" if getattr(tool, 'readonly', False) else "Read-write"
62
+
63
+ # Get input parameters
64
+ inputs = getattr(tool, 'inputs', {})
65
+ input_names = list(inputs.keys()) if inputs else []
66
+ input_str = ", ".join(input_names[:3]) # Show first 3 inputs
67
+ if len(input_names) > 3:
68
+ input_str += f" (+{len(input_names) - 3} more)"
69
+
70
+ tools_table.add_row(
71
+ tool.name,
72
+ tool.description[:50] + "..." if len(tool.description) > 50 else tool.description,
73
+ tool_type,
74
+ input_str or "-"
75
+ )
76
+
77
+ self.console.print(tools_table)
78
+
79
+ # Show summary
80
+ summary_panel = Panel(
81
+ f"📊 [bold blue]Total: {len(tools)} tools[/bold blue]"
82
+ f"{f' (filtered from {len(self.agent.tools)} total)' if filter_text else ''}",
83
+ title="[bold green]Summary[/bold green]",
84
+ border_style="green"
85
+ )
86
+ self.console.print(summary_panel)
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Version command - Show version information
5
+ """
6
+
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+ from minion_code.commands import BaseCommand
10
+
11
+
12
+ class VersionCommand(BaseCommand):
13
+ """Show version information."""
14
+
15
+ name = "version"
16
+ description = "Show version information for MinionCode and dependencies"
17
+ usage = "/version"
18
+ aliases = ["v", "ver"]
19
+
20
+ async def execute(self, args: str) -> None:
21
+ """Execute the version command."""
22
+ # Create version table
23
+ version_table = Table(
24
+ title="📦 Version Information",
25
+ show_header=True,
26
+ header_style="bold blue"
27
+ )
28
+ version_table.add_column("Component", style="cyan", no_wrap=True)
29
+ version_table.add_column("Version", style="white")
30
+ version_table.add_column("Status", style="green")
31
+
32
+ # MinionCode version
33
+ version_table.add_row(
34
+ "MinionCode",
35
+ "0.1.0",
36
+ "✅ Active"
37
+ )
38
+
39
+ # Python version
40
+ import sys
41
+ version_table.add_row(
42
+ "Python",
43
+ f"{sys.version.split()[0]}",
44
+ "✅ Compatible"
45
+ )
46
+
47
+ # Rich version
48
+ try:
49
+ import rich
50
+ version = getattr(rich, '__version__', 'Unknown')
51
+ version_table.add_row(
52
+ "Rich",
53
+ version,
54
+ "✅ Loaded"
55
+ )
56
+ except ImportError:
57
+ version_table.add_row(
58
+ "Rich",
59
+ "Not installed",
60
+ "❌ Missing"
61
+ )
62
+
63
+ # Textual version
64
+ try:
65
+ import textual
66
+ version = getattr(textual, '__version__', 'Unknown')
67
+ version_table.add_row(
68
+ "Textual",
69
+ version,
70
+ "✅ Available"
71
+ )
72
+ except ImportError:
73
+ version_table.add_row(
74
+ "Textual",
75
+ "Not installed",
76
+ "⚠️ Optional"
77
+ )
78
+
79
+ # Minion version
80
+ try:
81
+ import minion
82
+ version_table.add_row(
83
+ "Minion",
84
+ getattr(minion, '__version__', 'Unknown'),
85
+ "✅ Core"
86
+ )
87
+ except ImportError:
88
+ version_table.add_row(
89
+ "Minion",
90
+ "Not found",
91
+ "❌ Required"
92
+ )
93
+
94
+ self.console.print(version_table)
95
+
96
+ # Additional info
97
+ info_panel = Panel(
98
+ "🚀 [bold blue]MinionCode TUI[/bold blue] - Advanced AI-powered development assistant\n"
99
+ "🔗 Built with Rich for beautiful terminal interfaces\n"
100
+ "🤖 Powered by Minion framework for AI agent capabilities",
101
+ title="[bold green]About[/bold green]",
102
+ border_style="green"
103
+ )
104
+ self.console.print(info_panel)