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,110 @@
|
|
|
1
|
+
"""Utilities for todo file path management."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_todo_file_path(agent_id: Optional[str] = None, storage_dir: str = ".minion_workspace") -> str:
|
|
8
|
+
"""
|
|
9
|
+
Get the file path for todo storage for a specific agent.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
agent_id: Agent identifier. If None, uses default.
|
|
13
|
+
storage_dir: Directory where todo files are stored.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Full path to the todo file for the agent.
|
|
17
|
+
"""
|
|
18
|
+
# Ensure storage directory exists
|
|
19
|
+
os.makedirs(storage_dir, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
# Generate filename based on agent_id
|
|
22
|
+
if agent_id:
|
|
23
|
+
filename = f"todos_{agent_id}.json"
|
|
24
|
+
else:
|
|
25
|
+
filename = "todos_default.json"
|
|
26
|
+
|
|
27
|
+
return os.path.join(storage_dir, filename)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_default_storage_dir() -> str:
|
|
31
|
+
"""Get the default storage directory for todo files."""
|
|
32
|
+
return ".minion_workspace"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def ensure_storage_dir_exists(storage_dir: Optional[str] = None) -> str:
|
|
36
|
+
"""
|
|
37
|
+
Ensure the storage directory exists and return its path.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
storage_dir: Directory path. If None, uses default.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
The storage directory path.
|
|
44
|
+
"""
|
|
45
|
+
if storage_dir is None:
|
|
46
|
+
storage_dir = get_default_storage_dir()
|
|
47
|
+
|
|
48
|
+
os.makedirs(storage_dir, exist_ok=True)
|
|
49
|
+
return storage_dir
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def list_todo_files(storage_dir: Optional[str] = None) -> list[str]:
|
|
53
|
+
"""
|
|
54
|
+
List all todo files in the storage directory.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
storage_dir: Directory to search. If None, uses default.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List of todo file paths.
|
|
61
|
+
"""
|
|
62
|
+
if storage_dir is None:
|
|
63
|
+
storage_dir = get_default_storage_dir()
|
|
64
|
+
|
|
65
|
+
if not os.path.exists(storage_dir):
|
|
66
|
+
return []
|
|
67
|
+
|
|
68
|
+
todo_files = []
|
|
69
|
+
for filename in os.listdir(storage_dir):
|
|
70
|
+
if filename.startswith("todos_") and filename.endswith(".json"):
|
|
71
|
+
todo_files.append(os.path.join(storage_dir, filename))
|
|
72
|
+
|
|
73
|
+
return todo_files
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def extract_agent_id_from_todo_file(file_path: str) -> Optional[str]:
|
|
77
|
+
"""
|
|
78
|
+
Extract agent ID from a todo file path.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
file_path: Path to the todo file.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Agent ID if found, None if it's the default file.
|
|
85
|
+
"""
|
|
86
|
+
filename = os.path.basename(file_path)
|
|
87
|
+
|
|
88
|
+
if filename == "todos_default.json":
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
if filename.startswith("todos_") and filename.endswith(".json"):
|
|
92
|
+
# Extract agent_id from "todos_{agent_id}.json"
|
|
93
|
+
agent_id = filename[6:-5] # Remove "todos_" prefix and ".json" suffix
|
|
94
|
+
return agent_id if agent_id else None
|
|
95
|
+
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_todo_file(file_path: str) -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Check if a file path is a todo file.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
file_path: Path to check.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True if it's a todo file, False otherwise.
|
|
108
|
+
"""
|
|
109
|
+
filename = os.path.basename(file_path)
|
|
110
|
+
return (filename.startswith("todos_") and filename.endswith(".json")) or filename == "todos_default.json"
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Todo storage utilities for managing todo items."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
from dataclasses import dataclass, asdict
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
from .todo_file_utils import get_todo_file_path, get_default_storage_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TodoStatus(Enum):
|
|
13
|
+
PENDING = "pending"
|
|
14
|
+
IN_PROGRESS = "in_progress"
|
|
15
|
+
COMPLETED = "completed"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TodoPriority(Enum):
|
|
19
|
+
HIGH = "high"
|
|
20
|
+
MEDIUM = "medium"
|
|
21
|
+
LOW = "low"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class TodoItem:
|
|
26
|
+
id: str
|
|
27
|
+
content: str
|
|
28
|
+
status: TodoStatus
|
|
29
|
+
priority: TodoPriority
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
32
|
+
return {
|
|
33
|
+
"id": self.id,
|
|
34
|
+
"content": self.content,
|
|
35
|
+
"status": self.status.value,
|
|
36
|
+
"priority": self.priority.value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'TodoItem':
|
|
41
|
+
return cls(
|
|
42
|
+
id=data["id"],
|
|
43
|
+
content=data["content"],
|
|
44
|
+
status=TodoStatus(data["status"]),
|
|
45
|
+
priority=TodoPriority(data["priority"])
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TodoStorage:
|
|
50
|
+
def __init__(self, storage_dir: Optional[str] = None):
|
|
51
|
+
if storage_dir is None:
|
|
52
|
+
storage_dir = get_default_storage_dir()
|
|
53
|
+
self.storage_dir = storage_dir
|
|
54
|
+
os.makedirs(storage_dir, exist_ok=True)
|
|
55
|
+
|
|
56
|
+
def _get_file_path(self, agent_id: Optional[str] = None) -> str:
|
|
57
|
+
return get_todo_file_path(agent_id, self.storage_dir)
|
|
58
|
+
|
|
59
|
+
def get_todos(self, agent_id: Optional[str] = None) -> List[TodoItem]:
|
|
60
|
+
"""Get all todos for a specific agent or default."""
|
|
61
|
+
file_path = self._get_file_path(agent_id)
|
|
62
|
+
|
|
63
|
+
if not os.path.exists(file_path):
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
68
|
+
data = json.load(f)
|
|
69
|
+
return [TodoItem.from_dict(item) for item in data]
|
|
70
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
def set_todos(self, todos: List[TodoItem], agent_id: Optional[str] = None) -> None:
|
|
74
|
+
"""Set todos for a specific agent or default."""
|
|
75
|
+
file_path = self._get_file_path(agent_id)
|
|
76
|
+
|
|
77
|
+
data = [todo.to_dict() for todo in todos]
|
|
78
|
+
|
|
79
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
80
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
81
|
+
|
|
82
|
+
def add_todo(self, todo: TodoItem, agent_id: Optional[str] = None) -> None:
|
|
83
|
+
"""Add a new todo."""
|
|
84
|
+
todos = self.get_todos(agent_id)
|
|
85
|
+
todos.append(todo)
|
|
86
|
+
self.set_todos(todos, agent_id)
|
|
87
|
+
|
|
88
|
+
def update_todo(self, todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None) -> bool:
|
|
89
|
+
"""Update a specific todo. Returns True if found and updated."""
|
|
90
|
+
todos = self.get_todos(agent_id)
|
|
91
|
+
|
|
92
|
+
for todo in todos:
|
|
93
|
+
if todo.id == todo_id:
|
|
94
|
+
if 'content' in updates:
|
|
95
|
+
todo.content = updates['content']
|
|
96
|
+
if 'status' in updates:
|
|
97
|
+
todo.status = TodoStatus(updates['status'])
|
|
98
|
+
if 'priority' in updates:
|
|
99
|
+
todo.priority = TodoPriority(updates['priority'])
|
|
100
|
+
|
|
101
|
+
self.set_todos(todos, agent_id)
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def remove_todo(self, todo_id: str, agent_id: Optional[str] = None) -> bool:
|
|
107
|
+
"""Remove a todo by ID. Returns True if found and removed."""
|
|
108
|
+
todos = self.get_todos(agent_id)
|
|
109
|
+
original_length = len(todos)
|
|
110
|
+
|
|
111
|
+
todos = [todo for todo in todos if todo.id != todo_id]
|
|
112
|
+
|
|
113
|
+
if len(todos) < original_length:
|
|
114
|
+
self.set_todos(todos, agent_id)
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def clear_todos(self, agent_id: Optional[str] = None) -> None:
|
|
120
|
+
"""Clear all todos for a specific agent."""
|
|
121
|
+
self.set_todos([], agent_id)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# Global storage instance
|
|
125
|
+
_storage = TodoStorage()
|
|
126
|
+
|
|
127
|
+
def get_todos(agent_id: Optional[str] = None) -> List[TodoItem]:
|
|
128
|
+
"""Get todos from global storage."""
|
|
129
|
+
return _storage.get_todos(agent_id)
|
|
130
|
+
|
|
131
|
+
def set_todos(todos: List[TodoItem], agent_id: Optional[str] = None) -> None:
|
|
132
|
+
"""Set todos in global storage."""
|
|
133
|
+
_storage.set_todos(todos, agent_id)
|
|
134
|
+
|
|
135
|
+
def add_todo(todo: TodoItem, agent_id: Optional[str] = None) -> None:
|
|
136
|
+
"""Add todo to global storage."""
|
|
137
|
+
_storage.add_todo(todo, agent_id)
|
|
138
|
+
|
|
139
|
+
def update_todo(todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None) -> bool:
|
|
140
|
+
"""Update todo in global storage."""
|
|
141
|
+
return _storage.update_todo(todo_id, updates, agent_id)
|
|
142
|
+
|
|
143
|
+
def remove_todo(todo_id: str, agent_id: Optional[str] = None) -> bool:
|
|
144
|
+
"""Remove todo from global storage."""
|
|
145
|
+
return _storage.remove_todo(todo_id, agent_id)
|
|
146
|
+
|
|
147
|
+
def clear_todos(agent_id: Optional[str] = None) -> None:
|
|
148
|
+
"""Clear all todos in global storage."""
|
|
149
|
+
_storage.clear_todos(agent_id)
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minion-code
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python project depending on minion
|
|
5
|
+
Author-email: User <user@example.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: minionx
|
|
10
|
+
Requires-Dist: typer>=0.9.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest; extra == "dev"
|
|
13
|
+
Requires-Dist: black; extra == "dev"
|
|
14
|
+
Requires-Dist: flake8; extra == "dev"
|
|
15
|
+
Requires-Dist: mypy; extra == "dev"
|
|
16
|
+
Provides-Extra: tui
|
|
17
|
+
Requires-Dist: textual>=0.40.0; extra == "tui"
|
|
18
|
+
Requires-Dist: rich>=13.0.0; extra == "tui"
|
|
19
|
+
Provides-Extra: agent
|
|
20
|
+
Requires-Dist: openai>=1.0.0; extra == "agent"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# MinionCodeAgent
|
|
24
|
+
|
|
25
|
+
一个增强的AI代码助手,基于Minion框架构建,预配置了丰富的开发工具,专为代码开发任务优化。
|
|
26
|
+
|
|
27
|
+
## 特性
|
|
28
|
+
|
|
29
|
+
- 🤖 **智能代码助手**:预配置的AI agent,专为编程任务设计
|
|
30
|
+
- 🔧 **丰富的工具集**:自动包含文件操作、命令执行、网络搜索等12+个工具
|
|
31
|
+
- ⚡ **即开即用**:一行代码创建,无需复杂配置
|
|
32
|
+
- 📝 **对话历史**:内置对话历史跟踪和管理
|
|
33
|
+
- 🎯 **优化提示**:专为代码开发任务优化的系统提示
|
|
34
|
+
- 🛡️ **安全设计**:内置安全检查,防止危险操作
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 克隆仓库
|
|
40
|
+
git clone <repository-url>
|
|
41
|
+
cd minion-code
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 快速开始
|
|
46
|
+
|
|
47
|
+
### CLI使用
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 基本使用
|
|
51
|
+
mcode
|
|
52
|
+
|
|
53
|
+
# 指定工作目录
|
|
54
|
+
mcode --dir /path/to/project
|
|
55
|
+
|
|
56
|
+
# 启用详细输出
|
|
57
|
+
mcode --verbose
|
|
58
|
+
|
|
59
|
+
# 使用MCP配置文件加载额外工具
|
|
60
|
+
mcode --config mcp.json
|
|
61
|
+
|
|
62
|
+
# 组合使用
|
|
63
|
+
mcode --dir /path/to/project --config mcp.json --verbose
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 编程接口
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import asyncio
|
|
70
|
+
from minion_code import MinionCodeAgent
|
|
71
|
+
|
|
72
|
+
async def main():
|
|
73
|
+
# 创建AI代码助手,自动配置所有工具
|
|
74
|
+
agent = await MinionCodeAgent.create(
|
|
75
|
+
name="My Code Assistant",
|
|
76
|
+
llm="gpt-4.1"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# 与AI助手对话
|
|
80
|
+
response = await agent.run_async("List files in current directory")
|
|
81
|
+
print(response.answer)
|
|
82
|
+
|
|
83
|
+
response = await agent.run_async("Read the README.md file")
|
|
84
|
+
print(response.answer)
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 自定义配置
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# 自定义系统提示和工作目录
|
|
93
|
+
agent = await MinionCodeAgent.create(
|
|
94
|
+
name="Python Expert",
|
|
95
|
+
llm="gpt-4.1",
|
|
96
|
+
system_prompt="You are a specialized Python developer assistant.",
|
|
97
|
+
workdir="/path/to/project",
|
|
98
|
+
additional_tools=[MyCustomTool()]
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 查看可用工具
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# 打印工具摘要
|
|
106
|
+
agent.print_tools_summary()
|
|
107
|
+
|
|
108
|
+
# 获取工具信息
|
|
109
|
+
tools_info = agent.get_tools_info()
|
|
110
|
+
for tool in tools_info:
|
|
111
|
+
print(f"{tool['name']}: {tool['description']}")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 内置工具
|
|
115
|
+
|
|
116
|
+
MinionCodeAgent自动包含以下工具类别:
|
|
117
|
+
|
|
118
|
+
### 📁 文件和目录工具
|
|
119
|
+
- **FileReadTool**: 读取文件内容
|
|
120
|
+
- **FileWriteTool**: 写入文件
|
|
121
|
+
- **GrepTool**: 在文件中搜索文本
|
|
122
|
+
- **GlobTool**: 文件模式匹配
|
|
123
|
+
- **LsTool**: 列出目录内容
|
|
124
|
+
|
|
125
|
+
### 💻 系统和执行工具
|
|
126
|
+
- **BashTool**: 执行shell命令
|
|
127
|
+
- **PythonInterpreterTool**: 执行Python代码
|
|
128
|
+
|
|
129
|
+
### 🌐 网络和搜索工具
|
|
130
|
+
- **WebSearchTool**: 网络搜索
|
|
131
|
+
- **WikipediaSearchTool**: Wikipedia搜索
|
|
132
|
+
- **VisitWebpageTool**: 访问网页
|
|
133
|
+
|
|
134
|
+
### 🔧 其他工具
|
|
135
|
+
- **UserInputTool**: 用户输入
|
|
136
|
+
- **TodoWriteTool**: 任务管理写入
|
|
137
|
+
- **TodoReadTool**: 任务管理读取
|
|
138
|
+
|
|
139
|
+
## MCP工具集成
|
|
140
|
+
|
|
141
|
+
MinionCodeAgent支持通过MCP (Model Context Protocol) 配置文件加载额外的工具。
|
|
142
|
+
|
|
143
|
+
### MCP配置文件格式
|
|
144
|
+
|
|
145
|
+
创建一个JSON配置文件(如`mcp.json`):
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"mcpServers": {
|
|
150
|
+
"chrome-devtools": {
|
|
151
|
+
"command": "npx",
|
|
152
|
+
"args": ["-y", "chrome-devtools-mcp@latest"],
|
|
153
|
+
"env": {
|
|
154
|
+
"FASTMCP_LOG_LEVEL": "ERROR"
|
|
155
|
+
},
|
|
156
|
+
"disabled": false,
|
|
157
|
+
"autoApprove": []
|
|
158
|
+
},
|
|
159
|
+
"filesystem": {
|
|
160
|
+
"command": "uvx",
|
|
161
|
+
"args": ["mcp-server-filesystem", "/tmp"],
|
|
162
|
+
"disabled": true,
|
|
163
|
+
"autoApprove": ["read_file", "list_directory"]
|
|
164
|
+
},
|
|
165
|
+
"git": {
|
|
166
|
+
"command": "uvx",
|
|
167
|
+
"args": ["mcp-server-git"],
|
|
168
|
+
"disabled": false,
|
|
169
|
+
"autoApprove": ["git_status", "git_log"]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 配置选项说明
|
|
176
|
+
|
|
177
|
+
- `command`: 启动MCP服务器的命令
|
|
178
|
+
- `args`: 命令参数列表
|
|
179
|
+
- `env`: 环境变量(可选)
|
|
180
|
+
- `disabled`: 是否禁用此服务器(默认false)
|
|
181
|
+
- `autoApprove`: 自动批准的工具名称列表(可选)
|
|
182
|
+
|
|
183
|
+
### 使用MCP配置
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# 使用MCP配置文件
|
|
187
|
+
minion-code --config examples/mcp_config.json
|
|
188
|
+
|
|
189
|
+
# 查看加载的工具(包括MCP工具)
|
|
190
|
+
# 在CLI中输入: tools
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 编程接口中使用MCP工具
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from minion_code.utils.mcp_loader import load_mcp_tools
|
|
197
|
+
from pathlib import Path
|
|
198
|
+
|
|
199
|
+
async def main():
|
|
200
|
+
# 加载MCP工具
|
|
201
|
+
mcp_tools = await load_mcp_tools(Path("mcp.json"))
|
|
202
|
+
|
|
203
|
+
# 创建包含MCP工具的agent
|
|
204
|
+
agent = await MinionCodeAgent.create(
|
|
205
|
+
name="Enhanced Assistant",
|
|
206
|
+
llm="gpt-4o-mini",
|
|
207
|
+
additional_tools=mcp_tools
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## 对话历史管理
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
# 获取对话历史
|
|
215
|
+
history = agent.get_conversation_history()
|
|
216
|
+
for entry in history:
|
|
217
|
+
print(f"User: {entry['user_message']}")
|
|
218
|
+
print(f"Agent: {entry['agent_response']}")
|
|
219
|
+
|
|
220
|
+
# 清除历史
|
|
221
|
+
agent.clear_conversation_history()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## 与原始实现的对比
|
|
225
|
+
|
|
226
|
+
### 之前 (复杂的手动配置)
|
|
227
|
+
```python
|
|
228
|
+
# 需要手动导入和配置所有工具
|
|
229
|
+
from minion_code.tools import (
|
|
230
|
+
FileReadTool, FileWriteTool, BashTool,
|
|
231
|
+
GrepTool, GlobTool, LsTool,
|
|
232
|
+
PythonInterpreterTool, WebSearchTool,
|
|
233
|
+
# ... 更多工具
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# 手动创建工具实例
|
|
237
|
+
custom_tools = [
|
|
238
|
+
FileReadTool(),
|
|
239
|
+
FileWriteTool(),
|
|
240
|
+
BashTool(),
|
|
241
|
+
# ... 更多工具配置
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
# 手动设置系统提示
|
|
245
|
+
SYSTEM_PROMPT = "You are a coding agent..."
|
|
246
|
+
|
|
247
|
+
# 创建agent (约50行代码)
|
|
248
|
+
agent = await CodeAgent.create(
|
|
249
|
+
name="Minion Code Assistant",
|
|
250
|
+
llm="gpt-4o-mini",
|
|
251
|
+
system_prompt=SYSTEM_PROMPT,
|
|
252
|
+
tools=custom_tools,
|
|
253
|
+
)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 现在 (使用MinionCodeAgent)
|
|
257
|
+
```python
|
|
258
|
+
# 一行代码完成所有设置
|
|
259
|
+
agent = await MinionCodeAgent.create(
|
|
260
|
+
name="Minion Code Assistant",
|
|
261
|
+
llm="gpt-4o-mini"
|
|
262
|
+
)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## API参考
|
|
266
|
+
|
|
267
|
+
### MinionCodeAgent.create()
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
async def create(
|
|
271
|
+
name: str = "Minion Code Assistant",
|
|
272
|
+
llm: str = "gpt-4o-mini",
|
|
273
|
+
system_prompt: Optional[str] = None,
|
|
274
|
+
workdir: Optional[Union[str, Path]] = None,
|
|
275
|
+
additional_tools: Optional[List[Any]] = None,
|
|
276
|
+
**kwargs
|
|
277
|
+
) -> MinionCodeAgent
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**参数:**
|
|
281
|
+
- `name`: Agent名称
|
|
282
|
+
- `llm`: 使用的LLM模型
|
|
283
|
+
- `system_prompt`: 自定义系统提示(可选)
|
|
284
|
+
- `workdir`: 工作目录(可选,默认当前目录)
|
|
285
|
+
- `additional_tools`: 额外工具列表(可选)
|
|
286
|
+
- `**kwargs`: 传递给CodeAgent.create()的其他参数
|
|
287
|
+
|
|
288
|
+
### 实例方法
|
|
289
|
+
|
|
290
|
+
- `run_async(message: str)`: 异步运行agent
|
|
291
|
+
- `run(message: str)`: 同步运行agent
|
|
292
|
+
- `get_conversation_history()`: 获取对话历史
|
|
293
|
+
- `clear_conversation_history()`: 清除对话历史
|
|
294
|
+
- `get_tools_info()`: 获取工具信息
|
|
295
|
+
- `print_tools_summary()`: 打印工具摘要
|
|
296
|
+
|
|
297
|
+
### 属性
|
|
298
|
+
|
|
299
|
+
- `agent`: 访问底层CodeAgent实例
|
|
300
|
+
- `tools`: 获取可用工具列表
|
|
301
|
+
- `name`: 获取agent名称
|
|
302
|
+
|
|
303
|
+
## 安全特性
|
|
304
|
+
|
|
305
|
+
- **命令执行安全**:BashTool禁止执行危险命令(如`rm -rf`、`sudo`等)
|
|
306
|
+
- **Python执行限制**:PythonInterpreterTool在受限环境中执行,只允许安全的内置函数和指定模块
|
|
307
|
+
- **文件访问控制**:所有文件操作都有路径验证和错误处理
|
|
308
|
+
|
|
309
|
+
## 示例
|
|
310
|
+
|
|
311
|
+
查看 `examples/` 目录中的完整示例:
|
|
312
|
+
|
|
313
|
+
- `simple_code_agent.py`: 基本MinionCodeAgent使用示例
|
|
314
|
+
- `simple_tui.py`: 简化的TUI实现
|
|
315
|
+
- `advanced_textual_tui.py`: 高级TUI界面(使用Textual库)
|
|
316
|
+
- `minion_agent_tui.py`: 原始复杂实现(对比参考)
|
|
317
|
+
- `mcp_config.json`: MCP配置文件示例
|
|
318
|
+
- `test_mcp_config.py`: MCP配置加载测试
|
|
319
|
+
- `demo_mcp_cli.py`: MCP CLI功能演示
|
|
320
|
+
|
|
321
|
+
运行示例:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# 基本使用示例
|
|
325
|
+
python examples/simple_code_agent.py
|
|
326
|
+
|
|
327
|
+
# 简单TUI
|
|
328
|
+
python examples/simple_tui.py
|
|
329
|
+
|
|
330
|
+
# 高级TUI (需要安装 textual: pip install textual rich)
|
|
331
|
+
python examples/advanced_textual_tui.py
|
|
332
|
+
|
|
333
|
+
# 测试MCP配置加载
|
|
334
|
+
python examples/test_mcp_config.py
|
|
335
|
+
|
|
336
|
+
# MCP CLI功能演示
|
|
337
|
+
python examples/demo_mcp_cli.py
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## 文档
|
|
341
|
+
|
|
342
|
+
- [MCP工具集成指南](docs/MCP_GUIDE.md) - 详细的MCP配置和使用指南
|
|
343
|
+
|
|
344
|
+
## 贡献
|
|
345
|
+
|
|
346
|
+
欢迎提交Issue和Pull Request来改进这个项目!
|
|
347
|
+
|
|
348
|
+
## 许可证
|
|
349
|
+
|
|
350
|
+
MIT License
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
examples/advance_tui.py,sha256=Cz3c1KOfe8kEUIw4BKODrOufBDGkHDKLux1tCt8Z7Z8,16484
|
|
2
|
+
examples/agent_with_todos.py,sha256=6PfKYtY0KLb2J9S_-23HMoFcxN6-Ax787piUYn0VcZg,5641
|
|
3
|
+
examples/file_freshness_example.py,sha256=SAHoy5e4FcVrphCcNEcq7T8yrPTk-dpOYe784fyaYsA,3277
|
|
4
|
+
examples/file_watching_example.py,sha256=pGbgy67pvIeMYxNK7-qyK7dLoEgy3C_sUnos9E1X9oo,3290
|
|
5
|
+
examples/interruptible_tui.py,sha256=pzdtPHPznlDJ-t-YI0EdVEmiTa06V2CdWJ7DDNrtOpg,89
|
|
6
|
+
examples/message_response_children_demo.py,sha256=HR0RKU244kn90ie2rQWNVPEDL_bPtkRFJq-S-AQaLMo,8098
|
|
7
|
+
examples/rich_example.py,sha256=jyRTsuDolecb1z2SY1mi65EdEqX48-wF8xwC-avlnJQ,119
|
|
8
|
+
examples/simple_file_watching.py,sha256=L3K4KSbm0VLY4L_jzav8VfwYvLfz4v3MtS6SSmSxGdU,1381
|
|
9
|
+
examples/simple_tui.py,sha256=uvAO3uIDBimsHEUE73RsOAdfvnon_ZRFEmD0nimegsM,9730
|
|
10
|
+
examples/simple_usage.py,sha256=nwL9QfV9-ksDUpjFnxarCoEQjp68cyC5HyjixVXXetg,2009
|
|
11
|
+
minion_code/__init__.py,sha256=j3jQs-zfANxNTrT50Iq1u1uSb_ZiSd5aoZZ7IS45ZZw,488
|
|
12
|
+
minion_code/cli.py,sha256=EctJ08i7oATE5LzT74k_zpb7d5n4wt0yxH7T44l0S3Q,19531
|
|
13
|
+
minion_code/types.py,sha256=lFdEZhtHuooa-Mw3FcV9Fz29qOoa4tpmF_zVFS6XQvs,2159
|
|
14
|
+
minion_code/agents/__init__.py,sha256=R7stiTi15WZjaqN4By5oGldmVjGxWltrKQdH0C13gOM,279
|
|
15
|
+
minion_code/agents/code_agent.py,sha256=bmc11N4amjyQ4unJwS03jo_SGOKeMs77fLRW9u0c1RY,10683
|
|
16
|
+
minion_code/commands/__init__.py,sha256=Wfklafo1m1WevlnMd8Af8xldUsla_rcAUDscOCX9MYk,3072
|
|
17
|
+
minion_code/commands/clear_command.py,sha256=P6DBxKn5vyI8sL0D_cowIv_Jy8JOC242nFgxtw2qZ18,2474
|
|
18
|
+
minion_code/commands/help_command.py,sha256=_NXwWoBDbMcJyFq4DKJ44Is38DpmrzkyZRM3g17B0xw,3141
|
|
19
|
+
minion_code/commands/history_command.py,sha256=R6nyXWOsBcz9ZmCp6GUG3G4V3khfRAJBLXC_bkA8hAY,3648
|
|
20
|
+
minion_code/commands/quit_command.py,sha256=_2Ge1U2MAwudtWbO9NIPHbev_hLfYmIILWFBDdJfEf8,937
|
|
21
|
+
minion_code/commands/status_command.py,sha256=MuRzaROVHvye_ztb1jpXJ24gZWaJ_iR1nlwSp5tmUWw,3699
|
|
22
|
+
minion_code/commands/tools_command.py,sha256=u_1Qo1DKeYNO7d5cQPcxTfUF4dQUg_DG3ont4KPmiWM,3119
|
|
23
|
+
minion_code/commands/version_command.py,sha256=W14BkA6cSjFk1TFYiea6VIPQYauPGDeRexcg53v_kls,2997
|
|
24
|
+
minion_code/components/Message.py,sha256=7i1Mwr22dcrROsQsgeB1GAhZqhJ7JkR77bTmy4Q7Fqs,10847
|
|
25
|
+
minion_code/components/MessageResponse.py,sha256=rutuOrOWrSmCFUlDX0p3fLT4eXvApgdQ8CjDex_2HC8,5483
|
|
26
|
+
minion_code/components/PromptInput.py,sha256=x2ulLqtBxB6AzN-_YYVFd6iOsN2_LAuY1sxYIUjtkDI,19979
|
|
27
|
+
minion_code/components/__init__.py,sha256=gWhfIVQJ9Pn8KR8ldGDBK2sRDiFjF_g02aFibMkFtR8,688
|
|
28
|
+
minion_code/screens/REPL.py,sha256=mRHAtQt_5P_GGAnwxldWB1zI4JFdxVTtvE7n-4dhRO4,33270
|
|
29
|
+
minion_code/screens/__init__.py,sha256=AW3jj2Yg0fI5hy4WEHEn3clutRUWEyqMj_2L_0Yz0Xg,82
|
|
30
|
+
minion_code/services/__init__.py,sha256=lIJKlfWcvhW7m5C_F9OuT1GIneCX_ajtI2t8kff7kXE,1146
|
|
31
|
+
minion_code/services/event_system.py,sha256=9dRwhVWKLI2Rx1eSkAMnhRYqT5tsnOfL0mJ9YAZjeQw,3677
|
|
32
|
+
minion_code/services/file_freshness_service.py,sha256=DU9irzvgb6I-NS5yub_gUTDbI1At3dTNJ-psed5HW8s,22865
|
|
33
|
+
minion_code/tools/__init__.py,sha256=sc7e7OfNJYBlCdDWquLJ0_s7dTT3oZC7-seNqzKuE6I,1546
|
|
34
|
+
minion_code/tools/bash_tool.py,sha256=wjXmNXxqkhkvHLXuhHY-irViITB7lI_HSp6ZmrhXOOg,1796
|
|
35
|
+
minion_code/tools/file_edit_tool.py,sha256=hjL8SZ8aZ4Wx0E6Dl52CpdDlJQV2RfqVa4edJ_CrbAQ,8967
|
|
36
|
+
minion_code/tools/file_read_tool.py,sha256=BYQR-W2BPMYTqbv4wic8KKiecJDcgnWHsHRFe-rq2RY,2264
|
|
37
|
+
minion_code/tools/file_write_tool.py,sha256=fBL2j4B-Uav-5Wb0XZT9UnxQTvXIOrPXJr2zh7l1238,1045
|
|
38
|
+
minion_code/tools/glob_tool.py,sha256=_FK8ldPuKERya5tLQDNDkLdsK6_foTRNdLmVq_Zrzuc,1848
|
|
39
|
+
minion_code/tools/grep_tool.py,sha256=pbibSdqkDluv2VlZeGVxlsgc2aTJMkh37ESos5kfyj0,3532
|
|
40
|
+
minion_code/tools/ls_tool.py,sha256=zb116DRJNh1zdfjnqvWQ7QiTh90pYSro9kS_P4eKvuA,2190
|
|
41
|
+
minion_code/tools/multi_edit_tool.py,sha256=Jwoajtxj2M2od5KzrqA-tH_EA--skMj-LbtPXsas-4w,10358
|
|
42
|
+
minion_code/tools/python_interpreter_tool.py,sha256=x7zIvcCzkS7YvXsx6G7MjS4q_Lz1WxiAVb7BQRxHyx4,3185
|
|
43
|
+
minion_code/tools/todo_read_tool.py,sha256=7stXl3j-gq8cvh-atJgHMjw--YTvrcb5RfsVAKqy6g0,3557
|
|
44
|
+
minion_code/tools/todo_write_tool.py,sha256=fwbQ2r6d9zpnH1x2oyFEheUmH4wfhUkCqZABUPnDjDs,8768
|
|
45
|
+
minion_code/tools/user_input_tool.py,sha256=Ta-eOzDhNYo-PiZr3JX3JxiV0qQxQdpu-ObANOFTJfY,1633
|
|
46
|
+
minion_code/utils/__init__.py,sha256=WN9iP_dGXxhR2aC9sjsTlUm8dm3RhaQ5JPdvGXcqrl8,812
|
|
47
|
+
minion_code/utils/mcp_loader.py,sha256=N3EivkUJskZIb1DqwLnPxXQ6Y4dwq2WfgJErRn9Mwls,6887
|
|
48
|
+
minion_code/utils/todo_file_utils.py,sha256=tBJvkOMSdyaVDQ9s1Tm8_ZZcBBA2eWVRzAP314g-UsQ,3007
|
|
49
|
+
minion_code/utils/todo_storage.py,sha256=a4ExojWozWz-0swsjBiByFZhrgzxDK1Dh7NtucknCl4,4941
|
|
50
|
+
minion_code-0.1.0.dist-info/licenses/LICENSE,sha256=ILBn-G3jdarm2w8oOrLmXeJNU3czuJvVhDLBASWdhM8,34522
|
|
51
|
+
tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
|
|
52
|
+
tests/test_basic.py,sha256=7naqzwTF7zGSNPYObTfhD0GNCSBZ0E0vTSxoHtae4Ow,427
|
|
53
|
+
tests/test_readonly_tools.py,sha256=8IPq0DnG0phQPk21PunOiPuetezXz-7jBaL_FJIUbAM,2446
|
|
54
|
+
tests/test_tools.py,sha256=37QOOVQ9KHRQpv6v2m0rBJ39KX0sB5QomhrSPiNxczs,2207
|
|
55
|
+
minion_code-0.1.0.dist-info/METADATA,sha256=U8JtTa14rEAAdPnAOq7S-X9AgyI2J1oBoRYdcsRi1v8,8383
|
|
56
|
+
minion_code-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
minion_code-0.1.0.dist-info/entry_points.txt,sha256=uyHHqCwYjm4muWOY0O8BGMvo_ysEJQYhKtUJ8YtFNek,138
|
|
58
|
+
minion_code-0.1.0.dist-info/top_level.txt,sha256=L9ER44uG1buFZPVkazo5KUfNFewvi-raU-0kE_-EG6I,27
|
|
59
|
+
minion_code-0.1.0.dist-info/RECORD,,
|