minion-code 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.1.dist-info/METADATA +475 -0
- minion_code-0.1.1.dist-info/RECORD +111 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/WHEEL +1 -1
- minion_code-0.1.1.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.1.dist-info}/licenses/LICENSE +0 -0
- {minion_code-0.1.0.dist-info → minion_code-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -4,47 +4,49 @@ import os
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def get_todo_file_path(
|
|
7
|
+
def get_todo_file_path(
|
|
8
|
+
agent_id: Optional[str] = None, storage_dir: str = ".minion"
|
|
9
|
+
) -> str:
|
|
8
10
|
"""
|
|
9
11
|
Get the file path for todo storage for a specific agent.
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
Args:
|
|
12
14
|
agent_id: Agent identifier. If None, uses default.
|
|
13
15
|
storage_dir: Directory where todo files are stored.
|
|
14
|
-
|
|
16
|
+
|
|
15
17
|
Returns:
|
|
16
18
|
Full path to the todo file for the agent.
|
|
17
19
|
"""
|
|
18
20
|
# Ensure storage directory exists
|
|
19
21
|
os.makedirs(storage_dir, exist_ok=True)
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
# Generate filename based on agent_id
|
|
22
24
|
if agent_id:
|
|
23
25
|
filename = f"todos_{agent_id}.json"
|
|
24
26
|
else:
|
|
25
27
|
filename = "todos_default.json"
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
return os.path.join(storage_dir, filename)
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def get_default_storage_dir() -> str:
|
|
31
33
|
"""Get the default storage directory for todo files."""
|
|
32
|
-
return ".
|
|
34
|
+
return ".minion"
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def ensure_storage_dir_exists(storage_dir: Optional[str] = None) -> str:
|
|
36
38
|
"""
|
|
37
39
|
Ensure the storage directory exists and return its path.
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
Args:
|
|
40
42
|
storage_dir: Directory path. If None, uses default.
|
|
41
|
-
|
|
43
|
+
|
|
42
44
|
Returns:
|
|
43
45
|
The storage directory path.
|
|
44
46
|
"""
|
|
45
47
|
if storage_dir is None:
|
|
46
48
|
storage_dir = get_default_storage_dir()
|
|
47
|
-
|
|
49
|
+
|
|
48
50
|
os.makedirs(storage_dir, exist_ok=True)
|
|
49
51
|
return storage_dir
|
|
50
52
|
|
|
@@ -52,59 +54,61 @@ def ensure_storage_dir_exists(storage_dir: Optional[str] = None) -> str:
|
|
|
52
54
|
def list_todo_files(storage_dir: Optional[str] = None) -> list[str]:
|
|
53
55
|
"""
|
|
54
56
|
List all todo files in the storage directory.
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
Args:
|
|
57
59
|
storage_dir: Directory to search. If None, uses default.
|
|
58
|
-
|
|
60
|
+
|
|
59
61
|
Returns:
|
|
60
62
|
List of todo file paths.
|
|
61
63
|
"""
|
|
62
64
|
if storage_dir is None:
|
|
63
65
|
storage_dir = get_default_storage_dir()
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
if not os.path.exists(storage_dir):
|
|
66
68
|
return []
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
todo_files = []
|
|
69
71
|
for filename in os.listdir(storage_dir):
|
|
70
72
|
if filename.startswith("todos_") and filename.endswith(".json"):
|
|
71
73
|
todo_files.append(os.path.join(storage_dir, filename))
|
|
72
|
-
|
|
74
|
+
|
|
73
75
|
return todo_files
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
def extract_agent_id_from_todo_file(file_path: str) -> Optional[str]:
|
|
77
79
|
"""
|
|
78
80
|
Extract agent ID from a todo file path.
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
Args:
|
|
81
83
|
file_path: Path to the todo file.
|
|
82
|
-
|
|
84
|
+
|
|
83
85
|
Returns:
|
|
84
86
|
Agent ID if found, None if it's the default file.
|
|
85
87
|
"""
|
|
86
88
|
filename = os.path.basename(file_path)
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
if filename == "todos_default.json":
|
|
89
91
|
return None
|
|
90
|
-
|
|
92
|
+
|
|
91
93
|
if filename.startswith("todos_") and filename.endswith(".json"):
|
|
92
94
|
# Extract agent_id from "todos_{agent_id}.json"
|
|
93
95
|
agent_id = filename[6:-5] # Remove "todos_" prefix and ".json" suffix
|
|
94
96
|
return agent_id if agent_id else None
|
|
95
|
-
|
|
97
|
+
|
|
96
98
|
return None
|
|
97
99
|
|
|
98
100
|
|
|
99
101
|
def is_todo_file(file_path: str) -> bool:
|
|
100
102
|
"""
|
|
101
103
|
Check if a file path is a todo file.
|
|
102
|
-
|
|
104
|
+
|
|
103
105
|
Args:
|
|
104
106
|
file_path: Path to check.
|
|
105
|
-
|
|
107
|
+
|
|
106
108
|
Returns:
|
|
107
109
|
True if it's a todo file, False otherwise.
|
|
108
110
|
"""
|
|
109
111
|
filename = os.path.basename(file_path)
|
|
110
|
-
return (
|
|
112
|
+
return (
|
|
113
|
+
filename.startswith("todos_") and filename.endswith(".json")
|
|
114
|
+
) or filename == "todos_default.json"
|
|
@@ -27,22 +27,22 @@ class TodoItem:
|
|
|
27
27
|
content: str
|
|
28
28
|
status: TodoStatus
|
|
29
29
|
priority: TodoPriority
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
def to_dict(self) -> Dict[str, Any]:
|
|
32
32
|
return {
|
|
33
33
|
"id": self.id,
|
|
34
34
|
"content": self.content,
|
|
35
35
|
"status": self.status.value,
|
|
36
|
-
"priority": self.priority.value
|
|
36
|
+
"priority": self.priority.value,
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
@classmethod
|
|
40
|
-
def from_dict(cls, data: Dict[str, Any]) ->
|
|
40
|
+
def from_dict(cls, data: Dict[str, Any]) -> "TodoItem":
|
|
41
41
|
return cls(
|
|
42
42
|
id=data["id"],
|
|
43
43
|
content=data["content"],
|
|
44
44
|
status=TodoStatus(data["status"]),
|
|
45
|
-
priority=TodoPriority(data["priority"])
|
|
45
|
+
priority=TodoPriority(data["priority"]),
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
@@ -52,70 +52,72 @@ class TodoStorage:
|
|
|
52
52
|
storage_dir = get_default_storage_dir()
|
|
53
53
|
self.storage_dir = storage_dir
|
|
54
54
|
os.makedirs(storage_dir, exist_ok=True)
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
def _get_file_path(self, agent_id: Optional[str] = None) -> str:
|
|
57
57
|
return get_todo_file_path(agent_id, self.storage_dir)
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
def get_todos(self, agent_id: Optional[str] = None) -> List[TodoItem]:
|
|
60
60
|
"""Get all todos for a specific agent or default."""
|
|
61
61
|
file_path = self._get_file_path(agent_id)
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
if not os.path.exists(file_path):
|
|
64
64
|
return []
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
try:
|
|
67
|
-
with open(file_path,
|
|
67
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
68
68
|
data = json.load(f)
|
|
69
69
|
return [TodoItem.from_dict(item) for item in data]
|
|
70
70
|
except (json.JSONDecodeError, KeyError, ValueError):
|
|
71
71
|
return []
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
def set_todos(self, todos: List[TodoItem], agent_id: Optional[str] = None) -> None:
|
|
74
74
|
"""Set todos for a specific agent or default."""
|
|
75
75
|
file_path = self._get_file_path(agent_id)
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
data = [todo.to_dict() for todo in todos]
|
|
78
|
-
|
|
79
|
-
with open(file_path,
|
|
78
|
+
|
|
79
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
80
80
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
def add_todo(self, todo: TodoItem, agent_id: Optional[str] = None) -> None:
|
|
83
83
|
"""Add a new todo."""
|
|
84
84
|
todos = self.get_todos(agent_id)
|
|
85
85
|
todos.append(todo)
|
|
86
86
|
self.set_todos(todos, agent_id)
|
|
87
|
-
|
|
88
|
-
def update_todo(
|
|
87
|
+
|
|
88
|
+
def update_todo(
|
|
89
|
+
self, todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None
|
|
90
|
+
) -> bool:
|
|
89
91
|
"""Update a specific todo. Returns True if found and updated."""
|
|
90
92
|
todos = self.get_todos(agent_id)
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
for todo in todos:
|
|
93
95
|
if todo.id == todo_id:
|
|
94
|
-
if
|
|
95
|
-
todo.content = updates[
|
|
96
|
-
if
|
|
97
|
-
todo.status = TodoStatus(updates[
|
|
98
|
-
if
|
|
99
|
-
todo.priority = TodoPriority(updates[
|
|
100
|
-
|
|
96
|
+
if "content" in updates:
|
|
97
|
+
todo.content = updates["content"]
|
|
98
|
+
if "status" in updates:
|
|
99
|
+
todo.status = TodoStatus(updates["status"])
|
|
100
|
+
if "priority" in updates:
|
|
101
|
+
todo.priority = TodoPriority(updates["priority"])
|
|
102
|
+
|
|
101
103
|
self.set_todos(todos, agent_id)
|
|
102
104
|
return True
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
return False
|
|
105
|
-
|
|
107
|
+
|
|
106
108
|
def remove_todo(self, todo_id: str, agent_id: Optional[str] = None) -> bool:
|
|
107
109
|
"""Remove a todo by ID. Returns True if found and removed."""
|
|
108
110
|
todos = self.get_todos(agent_id)
|
|
109
111
|
original_length = len(todos)
|
|
110
|
-
|
|
112
|
+
|
|
111
113
|
todos = [todo for todo in todos if todo.id != todo_id]
|
|
112
|
-
|
|
114
|
+
|
|
113
115
|
if len(todos) < original_length:
|
|
114
116
|
self.set_todos(todos, agent_id)
|
|
115
117
|
return True
|
|
116
|
-
|
|
118
|
+
|
|
117
119
|
return False
|
|
118
|
-
|
|
120
|
+
|
|
119
121
|
def clear_todos(self, agent_id: Optional[str] = None) -> None:
|
|
120
122
|
"""Clear all todos for a specific agent."""
|
|
121
123
|
self.set_todos([], agent_id)
|
|
@@ -124,26 +126,34 @@ class TodoStorage:
|
|
|
124
126
|
# Global storage instance
|
|
125
127
|
_storage = TodoStorage()
|
|
126
128
|
|
|
129
|
+
|
|
127
130
|
def get_todos(agent_id: Optional[str] = None) -> List[TodoItem]:
|
|
128
131
|
"""Get todos from global storage."""
|
|
129
132
|
return _storage.get_todos(agent_id)
|
|
130
133
|
|
|
134
|
+
|
|
131
135
|
def set_todos(todos: List[TodoItem], agent_id: Optional[str] = None) -> None:
|
|
132
136
|
"""Set todos in global storage."""
|
|
133
137
|
_storage.set_todos(todos, agent_id)
|
|
134
138
|
|
|
139
|
+
|
|
135
140
|
def add_todo(todo: TodoItem, agent_id: Optional[str] = None) -> None:
|
|
136
141
|
"""Add todo to global storage."""
|
|
137
142
|
_storage.add_todo(todo, agent_id)
|
|
138
143
|
|
|
139
|
-
|
|
144
|
+
|
|
145
|
+
def update_todo(
|
|
146
|
+
todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None
|
|
147
|
+
) -> bool:
|
|
140
148
|
"""Update todo in global storage."""
|
|
141
149
|
return _storage.update_todo(todo_id, updates, agent_id)
|
|
142
150
|
|
|
151
|
+
|
|
143
152
|
def remove_todo(todo_id: str, agent_id: Optional[str] = None) -> bool:
|
|
144
153
|
"""Remove todo from global storage."""
|
|
145
154
|
return _storage.remove_todo(todo_id, agent_id)
|
|
146
155
|
|
|
156
|
+
|
|
147
157
|
def clear_todos(agent_id: Optional[str] = None) -> None:
|
|
148
158
|
"""Clear all todos in global storage."""
|
|
149
|
-
_storage.clear_todos(agent_id)
|
|
159
|
+
_storage.clear_todos(agent_id)
|