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.
Files changed (115) hide show
  1. examples/cli_entrypoint.py +60 -0
  2. examples/{agent_with_todos.py → components/agent_with_todos.py} +58 -47
  3. examples/{message_response_children_demo.py → components/message_response_children_demo.py} +61 -55
  4. examples/components/messages_component.py +199 -0
  5. examples/file_freshness_example.py +22 -22
  6. examples/file_watching_example.py +32 -26
  7. examples/interruptible_tui.py +921 -3
  8. examples/repl_tui.py +129 -0
  9. examples/skills/example_usage.py +57 -0
  10. examples/start.py +173 -0
  11. minion_code/__init__.py +1 -1
  12. minion_code/acp_server/__init__.py +34 -0
  13. minion_code/acp_server/agent.py +539 -0
  14. minion_code/acp_server/hooks.py +354 -0
  15. minion_code/acp_server/main.py +194 -0
  16. minion_code/acp_server/permissions.py +142 -0
  17. minion_code/acp_server/test_client.py +104 -0
  18. minion_code/adapters/__init__.py +22 -0
  19. minion_code/adapters/output_adapter.py +207 -0
  20. minion_code/adapters/rich_adapter.py +169 -0
  21. minion_code/adapters/textual_adapter.py +254 -0
  22. minion_code/agents/__init__.py +2 -2
  23. minion_code/agents/code_agent.py +517 -104
  24. minion_code/agents/hooks.py +378 -0
  25. minion_code/cli.py +538 -429
  26. minion_code/cli_simple.py +665 -0
  27. minion_code/commands/__init__.py +136 -29
  28. minion_code/commands/clear_command.py +19 -46
  29. minion_code/commands/help_command.py +33 -49
  30. minion_code/commands/history_command.py +37 -55
  31. minion_code/commands/model_command.py +194 -0
  32. minion_code/commands/quit_command.py +9 -12
  33. minion_code/commands/resume_command.py +181 -0
  34. minion_code/commands/skill_command.py +89 -0
  35. minion_code/commands/status_command.py +48 -73
  36. minion_code/commands/tools_command.py +54 -52
  37. minion_code/commands/version_command.py +34 -69
  38. minion_code/components/ConfirmDialog.py +430 -0
  39. minion_code/components/Message.py +318 -97
  40. minion_code/components/MessageResponse.py +30 -29
  41. minion_code/components/Messages.py +351 -0
  42. minion_code/components/PromptInput.py +499 -245
  43. minion_code/components/__init__.py +24 -17
  44. minion_code/const.py +7 -0
  45. minion_code/screens/REPL.py +1453 -469
  46. minion_code/screens/__init__.py +1 -1
  47. minion_code/services/__init__.py +20 -20
  48. minion_code/services/event_system.py +19 -14
  49. minion_code/services/file_freshness_service.py +223 -170
  50. minion_code/skills/__init__.py +25 -0
  51. minion_code/skills/skill.py +128 -0
  52. minion_code/skills/skill_loader.py +198 -0
  53. minion_code/skills/skill_registry.py +177 -0
  54. minion_code/subagents/__init__.py +31 -0
  55. minion_code/subagents/builtin/__init__.py +30 -0
  56. minion_code/subagents/builtin/claude_code_guide.py +32 -0
  57. minion_code/subagents/builtin/explore.py +36 -0
  58. minion_code/subagents/builtin/general_purpose.py +19 -0
  59. minion_code/subagents/builtin/plan.py +61 -0
  60. minion_code/subagents/subagent.py +116 -0
  61. minion_code/subagents/subagent_loader.py +147 -0
  62. minion_code/subagents/subagent_registry.py +151 -0
  63. minion_code/tools/__init__.py +8 -2
  64. minion_code/tools/bash_tool.py +16 -3
  65. minion_code/tools/file_edit_tool.py +201 -104
  66. minion_code/tools/file_read_tool.py +183 -26
  67. minion_code/tools/file_write_tool.py +17 -3
  68. minion_code/tools/glob_tool.py +23 -2
  69. minion_code/tools/grep_tool.py +229 -21
  70. minion_code/tools/ls_tool.py +28 -3
  71. minion_code/tools/multi_edit_tool.py +89 -84
  72. minion_code/tools/python_interpreter_tool.py +9 -1
  73. minion_code/tools/skill_tool.py +210 -0
  74. minion_code/tools/task_tool.py +287 -0
  75. minion_code/tools/todo_read_tool.py +28 -24
  76. minion_code/tools/todo_write_tool.py +82 -65
  77. minion_code/{types.py → type_defs.py} +15 -2
  78. minion_code/utils/__init__.py +45 -17
  79. minion_code/utils/config.py +610 -0
  80. minion_code/utils/history.py +114 -0
  81. minion_code/utils/logs.py +53 -0
  82. minion_code/utils/mcp_loader.py +153 -55
  83. minion_code/utils/output_truncator.py +233 -0
  84. minion_code/utils/session_storage.py +369 -0
  85. minion_code/utils/todo_file_utils.py +26 -22
  86. minion_code/utils/todo_storage.py +43 -33
  87. minion_code/web/__init__.py +9 -0
  88. minion_code/web/adapters/__init__.py +5 -0
  89. minion_code/web/adapters/web_adapter.py +524 -0
  90. minion_code/web/api/__init__.py +7 -0
  91. minion_code/web/api/chat.py +277 -0
  92. minion_code/web/api/interactions.py +136 -0
  93. minion_code/web/api/sessions.py +135 -0
  94. minion_code/web/server.py +149 -0
  95. minion_code/web/services/__init__.py +5 -0
  96. minion_code/web/services/session_manager.py +420 -0
  97. minion_code-0.1.2.dist-info/METADATA +476 -0
  98. minion_code-0.1.2.dist-info/RECORD +111 -0
  99. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/WHEEL +1 -1
  100. minion_code-0.1.2.dist-info/entry_points.txt +6 -0
  101. tests/test_adapter.py +67 -0
  102. tests/test_adapter_simple.py +79 -0
  103. tests/test_file_read_tool.py +144 -0
  104. tests/test_readonly_tools.py +0 -2
  105. tests/test_skills.py +441 -0
  106. examples/advance_tui.py +0 -508
  107. examples/rich_example.py +0 -4
  108. examples/simple_file_watching.py +0 -57
  109. examples/simple_tui.py +0 -267
  110. examples/simple_usage.py +0 -69
  111. minion_code-0.1.0.dist-info/METADATA +0 -350
  112. minion_code-0.1.0.dist-info/RECORD +0 -59
  113. minion_code-0.1.0.dist-info/entry_points.txt +0 -4
  114. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/licenses/LICENSE +0 -0
  115. {minion_code-0.1.0.dist-info → minion_code-0.1.2.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(agent_id: Optional[str] = None, storage_dir: str = ".minion_workspace") -> str:
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 ".minion_workspace"
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 (filename.startswith("todos_") and filename.endswith(".json")) or filename == "todos_default.json"
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]) -> 'TodoItem':
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, 'r', encoding='utf-8') as f:
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, 'w', encoding='utf-8') as f:
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(self, todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None) -> bool:
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 '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
-
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
- def update_todo(todo_id: str, updates: Dict[str, Any], agent_id: Optional[str] = None) -> bool:
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)
@@ -0,0 +1,9 @@
1
+ """
2
+ Web API module for minion-code.
3
+
4
+ Provides FastAPI-based HTTP/SSE server for cross-process frontend communication.
5
+ """
6
+
7
+ from .server import create_app, run_server
8
+
9
+ __all__ = ["create_app", "run_server"]
@@ -0,0 +1,5 @@
1
+ """Web output adapters."""
2
+
3
+ from .web_adapter import WebOutputAdapter
4
+
5
+ __all__ = ["WebOutputAdapter"]