monoco-toolkit 0.3.11__py3-none-any.whl → 0.4.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 (132) hide show
  1. monoco/core/automation/__init__.py +40 -0
  2. monoco/core/automation/field_watcher.py +296 -0
  3. monoco/core/automation/handlers.py +805 -0
  4. monoco/core/config.py +29 -11
  5. monoco/core/daemon/__init__.py +5 -0
  6. monoco/core/daemon/pid.py +290 -0
  7. monoco/core/git.py +15 -0
  8. monoco/core/hooks/context.py +74 -13
  9. monoco/core/injection.py +86 -8
  10. monoco/core/integrations.py +0 -24
  11. monoco/core/router/__init__.py +17 -0
  12. monoco/core/router/action.py +202 -0
  13. monoco/core/scheduler/__init__.py +63 -0
  14. monoco/core/scheduler/base.py +152 -0
  15. monoco/core/scheduler/engines.py +175 -0
  16. monoco/core/scheduler/events.py +197 -0
  17. monoco/core/scheduler/local.py +377 -0
  18. monoco/core/setup.py +9 -0
  19. monoco/core/sync.py +199 -4
  20. monoco/core/watcher/__init__.py +63 -0
  21. monoco/core/watcher/base.py +382 -0
  22. monoco/core/watcher/dropzone.py +152 -0
  23. monoco/core/watcher/im.py +460 -0
  24. monoco/core/watcher/issue.py +303 -0
  25. monoco/core/watcher/memo.py +192 -0
  26. monoco/core/watcher/task.py +238 -0
  27. monoco/daemon/app.py +3 -60
  28. monoco/daemon/commands.py +459 -25
  29. monoco/daemon/events.py +34 -0
  30. monoco/daemon/scheduler.py +157 -201
  31. monoco/daemon/services.py +42 -243
  32. monoco/features/agent/__init__.py +25 -7
  33. monoco/features/agent/cli.py +91 -57
  34. monoco/features/agent/engines.py +31 -170
  35. monoco/features/agent/resources/en/AGENTS.md +14 -14
  36. monoco/features/agent/resources/en/skills/monoco_role_engineer/SKILL.md +101 -0
  37. monoco/features/agent/resources/en/skills/monoco_role_manager/SKILL.md +95 -0
  38. monoco/features/agent/resources/en/skills/monoco_role_planner/SKILL.md +177 -0
  39. monoco/features/agent/resources/en/skills/monoco_role_reviewer/SKILL.md +139 -0
  40. monoco/features/agent/resources/zh/skills/monoco_role_engineer/SKILL.md +101 -0
  41. monoco/features/agent/resources/zh/skills/monoco_role_manager/SKILL.md +95 -0
  42. monoco/features/agent/resources/zh/skills/monoco_role_planner/SKILL.md +177 -0
  43. monoco/features/agent/resources/zh/skills/monoco_role_reviewer/SKILL.md +139 -0
  44. monoco/features/agent/worker.py +1 -1
  45. monoco/features/hooks/__init__.py +61 -6
  46. monoco/features/hooks/commands.py +281 -271
  47. monoco/features/hooks/dispatchers/__init__.py +23 -0
  48. monoco/features/hooks/dispatchers/agent_dispatcher.py +486 -0
  49. monoco/features/hooks/dispatchers/git_dispatcher.py +478 -0
  50. monoco/features/hooks/manager.py +357 -0
  51. monoco/features/hooks/models.py +262 -0
  52. monoco/features/hooks/parser.py +322 -0
  53. monoco/features/hooks/universal_interceptor.py +503 -0
  54. monoco/features/im/__init__.py +67 -0
  55. monoco/features/im/core.py +782 -0
  56. monoco/features/im/models.py +311 -0
  57. monoco/features/issue/commands.py +133 -60
  58. monoco/features/issue/core.py +385 -40
  59. monoco/features/issue/domain_commands.py +0 -19
  60. monoco/features/issue/resources/en/AGENTS.md +17 -122
  61. monoco/features/issue/resources/hooks/agent/before-tool.sh +102 -0
  62. monoco/features/issue/resources/hooks/agent/session-start.sh +88 -0
  63. monoco/features/issue/resources/hooks/{post-checkout.sh → git/git-post-checkout.sh} +10 -9
  64. monoco/features/issue/resources/hooks/git/git-pre-commit.sh +31 -0
  65. monoco/features/issue/resources/hooks/{pre-push.sh → git/git-pre-push.sh} +7 -13
  66. monoco/features/issue/resources/zh/AGENTS.md +18 -123
  67. monoco/features/memo/cli.py +15 -64
  68. monoco/features/memo/core.py +6 -34
  69. monoco/features/memo/models.py +24 -15
  70. monoco/features/memo/resources/en/AGENTS.md +31 -0
  71. monoco/features/memo/resources/zh/AGENTS.md +28 -5
  72. monoco/features/spike/commands.py +5 -3
  73. monoco/main.py +5 -3
  74. {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/METADATA +1 -1
  75. monoco_toolkit-0.4.0.dist-info/RECORD +170 -0
  76. monoco/core/execution.py +0 -67
  77. monoco/features/agent/apoptosis.py +0 -44
  78. monoco/features/agent/manager.py +0 -127
  79. monoco/features/agent/resources/atoms/atom-code-dev.yaml +0 -61
  80. monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +0 -73
  81. monoco/features/agent/resources/atoms/atom-knowledge.yaml +0 -55
  82. monoco/features/agent/resources/atoms/atom-review.yaml +0 -60
  83. monoco/features/agent/resources/en/skills/monoco_atom_core/SKILL.md +0 -99
  84. monoco/features/agent/resources/en/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
  85. monoco/features/agent/resources/en/skills/monoco_workflow_agent_manager/SKILL.md +0 -93
  86. monoco/features/agent/resources/en/skills/monoco_workflow_agent_planner/SKILL.md +0 -85
  87. monoco/features/agent/resources/en/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -114
  88. monoco/features/agent/resources/workflows/workflow-dev.yaml +0 -83
  89. monoco/features/agent/resources/workflows/workflow-issue-create.yaml +0 -72
  90. monoco/features/agent/resources/workflows/workflow-review.yaml +0 -94
  91. monoco/features/agent/resources/zh/roles/monoco_role_engineer.yaml +0 -49
  92. monoco/features/agent/resources/zh/roles/monoco_role_manager.yaml +0 -46
  93. monoco/features/agent/resources/zh/roles/monoco_role_planner.yaml +0 -46
  94. monoco/features/agent/resources/zh/roles/monoco_role_reviewer.yaml +0 -47
  95. monoco/features/agent/resources/zh/skills/monoco_atom_core/SKILL.md +0 -99
  96. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
  97. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_manager/SKILL.md +0 -88
  98. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_planner/SKILL.md +0 -259
  99. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -137
  100. monoco/features/agent/session.py +0 -169
  101. monoco/features/artifact/resources/zh/skills/monoco_atom_artifact/SKILL.md +0 -278
  102. monoco/features/glossary/resources/en/skills/monoco_atom_glossary/SKILL.md +0 -35
  103. monoco/features/glossary/resources/zh/skills/monoco_atom_glossary/SKILL.md +0 -35
  104. monoco/features/hooks/adapter.py +0 -67
  105. monoco/features/hooks/core.py +0 -441
  106. monoco/features/i18n/resources/en/skills/monoco_atom_i18n/SKILL.md +0 -96
  107. monoco/features/i18n/resources/en/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
  108. monoco/features/i18n/resources/zh/skills/monoco_atom_i18n/SKILL.md +0 -96
  109. monoco/features/i18n/resources/zh/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
  110. monoco/features/issue/resources/en/skills/monoco_atom_issue/SKILL.md +0 -165
  111. monoco/features/issue/resources/en/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
  112. monoco/features/issue/resources/en/skills/monoco_workflow_issue_development/SKILL.md +0 -224
  113. monoco/features/issue/resources/en/skills/monoco_workflow_issue_management/SKILL.md +0 -159
  114. monoco/features/issue/resources/en/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
  115. monoco/features/issue/resources/hooks/pre-commit.sh +0 -41
  116. monoco/features/issue/resources/zh/skills/monoco_atom_issue_lifecycle/SKILL.md +0 -190
  117. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
  118. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_development/SKILL.md +0 -224
  119. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_management/SKILL.md +0 -159
  120. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
  121. monoco/features/memo/resources/en/skills/monoco_atom_memo/SKILL.md +0 -77
  122. monoco/features/memo/resources/en/skills/monoco_workflow_note_processing/SKILL.md +0 -140
  123. monoco/features/memo/resources/zh/skills/monoco_atom_memo/SKILL.md +0 -77
  124. monoco/features/memo/resources/zh/skills/monoco_workflow_note_processing/SKILL.md +0 -140
  125. monoco/features/spike/resources/en/skills/monoco_atom_spike/SKILL.md +0 -76
  126. monoco/features/spike/resources/en/skills/monoco_workflow_research/SKILL.md +0 -121
  127. monoco/features/spike/resources/zh/skills/monoco_atom_spike/SKILL.md +0 -76
  128. monoco/features/spike/resources/zh/skills/monoco_workflow_research/SKILL.md +0 -121
  129. monoco_toolkit-0.3.11.dist-info/RECORD +0 -181
  130. {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/WHEEL +0 -0
  131. {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/entry_points.txt +0 -0
  132. {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,17 @@
1
+ """
2
+ Router Module - Layer 2 of the Event Automation Framework.
3
+
4
+ This module provides Action ABC and ActionResult for handlers.
5
+ """
6
+
7
+ from .action import (
8
+ Action,
9
+ ActionResult,
10
+ ActionStatus,
11
+ )
12
+
13
+ __all__ = [
14
+ "Action",
15
+ "ActionResult",
16
+ "ActionStatus",
17
+ ]
@@ -0,0 +1,202 @@
1
+ """
2
+ Action Abstractions - Layer 2 & 3 of the Event Automation Framework.
3
+
4
+ This module defines the Action ABC and ActionResult for handler return types.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from abc import ABC, abstractmethod
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ from enum import Enum
14
+ from typing import Any, Dict, Optional
15
+
16
+ from monoco.core.scheduler import AgentEvent, AgentEventType
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ActionStatus(Enum):
22
+ """Status of an Action execution."""
23
+ PENDING = "pending"
24
+ RUNNING = "running"
25
+ SUCCESS = "success"
26
+ FAILED = "failed"
27
+ SKIPPED = "skipped"
28
+ CANCELLED = "cancelled"
29
+
30
+
31
+ @dataclass
32
+ class ActionResult:
33
+ """
34
+ Result of an Action execution.
35
+
36
+ Attributes:
37
+ success: Whether the action succeeded
38
+ status: Detailed status
39
+ output: Output data from the action
40
+ error: Error message if failed
41
+ metadata: Additional metadata
42
+ started_at: Execution start time
43
+ completed_at: Execution completion time
44
+ """
45
+ success: bool
46
+ status: ActionStatus
47
+ output: Any = None
48
+ error: Optional[str] = None
49
+ metadata: Dict[str, Any] = field(default_factory=dict)
50
+ started_at: Optional[datetime] = None
51
+ completed_at: Optional[datetime] = None
52
+
53
+ @classmethod
54
+ def success_result(
55
+ cls,
56
+ output: Any = None,
57
+ metadata: Optional[Dict[str, Any]] = None,
58
+ ) -> "ActionResult":
59
+ """Create a success result."""
60
+ return cls(
61
+ success=True,
62
+ status=ActionStatus.SUCCESS,
63
+ output=output,
64
+ metadata=metadata or {},
65
+ completed_at=datetime.now(),
66
+ )
67
+
68
+ @classmethod
69
+ def failure_result(
70
+ cls,
71
+ error: str,
72
+ metadata: Optional[Dict[str, Any]] = None,
73
+ ) -> "ActionResult":
74
+ """Create a failure result."""
75
+ return cls(
76
+ success=False,
77
+ status=ActionStatus.FAILED,
78
+ error=error,
79
+ metadata=metadata or {},
80
+ completed_at=datetime.now(),
81
+ )
82
+
83
+ @classmethod
84
+ def skipped_result(
85
+ cls,
86
+ reason: str = "",
87
+ metadata: Optional[Dict[str, Any]] = None,
88
+ ) -> "ActionResult":
89
+ """Create a skipped result."""
90
+ return cls(
91
+ success=True,
92
+ status=ActionStatus.SKIPPED,
93
+ metadata={"reason": reason, **(metadata or {})},
94
+ completed_at=datetime.now(),
95
+ )
96
+
97
+
98
+ class Action(ABC):
99
+ """
100
+ Abstract base class for Actions (Layer 3).
101
+
102
+ Actions are the units of work that respond to events.
103
+ They are executed by handlers when events match their conditions.
104
+
105
+ Responsibilities:
106
+ - Define execution conditions (can_execute)
107
+ - Implement execution logic (execute)
108
+ - Return execution results
109
+
110
+ Example:
111
+ >>> class MyAction(Action):
112
+ ... @property
113
+ ... def name(self) -> str:
114
+ ... return "MyAction"
115
+ ...
116
+ ... async def can_execute(self, event: AgentEvent) -> bool:
117
+ ... return event.type == AgentEventType.ISSUE_CREATED
118
+ ...
119
+ ... async def execute(self, event: AgentEvent) -> ActionResult:
120
+ ... # Do something
121
+ ... return ActionResult.success_result()
122
+ """
123
+
124
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
125
+ self.config = config or {}
126
+ self._execution_count = 0
127
+ self._last_execution: Optional[datetime] = None
128
+
129
+ @property
130
+ @abstractmethod
131
+ def name(self) -> str:
132
+ """Return the unique name of this action."""
133
+ pass
134
+
135
+ @abstractmethod
136
+ async def can_execute(self, event: AgentEvent) -> bool:
137
+ """
138
+ Check if this action should execute for the given event.
139
+
140
+ Args:
141
+ event: The event to check
142
+
143
+ Returns:
144
+ True if the action should execute, False otherwise
145
+ """
146
+ pass
147
+
148
+ @abstractmethod
149
+ async def execute(self, event: AgentEvent) -> ActionResult:
150
+ """
151
+ Execute the action.
152
+
153
+ Args:
154
+ event: The event that triggered this action
155
+
156
+ Returns:
157
+ ActionResult indicating success/failure
158
+ """
159
+ pass
160
+
161
+ async def __call__(self, event: AgentEvent) -> ActionResult:
162
+ """
163
+ Make action callable - checks conditions then executes.
164
+
165
+ Args:
166
+ event: The event to process
167
+
168
+ Returns:
169
+ ActionResult
170
+ """
171
+ self._last_execution = datetime.now()
172
+
173
+ try:
174
+ if not await self.can_execute(event):
175
+ return ActionResult.skipped_result(
176
+ reason="Conditions not met",
177
+ metadata={"action": self.name, "event_type": event.type.value},
178
+ )
179
+
180
+ self._execution_count += 1
181
+ result = await self.execute(event)
182
+
183
+ # Ensure result has timestamps
184
+ if result.started_at is None:
185
+ result.started_at = self._last_execution
186
+
187
+ return result
188
+
189
+ except Exception as e:
190
+ logger.error(f"Action {self.name} failed: {e}", exc_info=True)
191
+ return ActionResult.failure_result(
192
+ error=str(e),
193
+ metadata={"action": self.name, "event_type": event.type.value},
194
+ )
195
+
196
+ def get_stats(self) -> Dict[str, Any]:
197
+ """Get action statistics."""
198
+ return {
199
+ "name": self.name,
200
+ "execution_count": self._execution_count,
201
+ "last_execution": self._last_execution.isoformat() if self._last_execution else None,
202
+ }
@@ -0,0 +1,63 @@
1
+ """
2
+ AgentScheduler - Core scheduling abstraction layer for Monoco.
3
+
4
+ This module provides the high-level AgentScheduler abstraction that decouples
5
+ scheduling policies from specific Agent Provider implementations.
6
+
7
+ Architecture:
8
+ - AgentScheduler: Abstract base class for all schedulers
9
+ - AgentTask: Data class representing a task to be scheduled
10
+ - AgentStatus: Enum for task lifecycle states
11
+ - EngineAdapter: Abstract base for agent engine adapters
12
+ - EngineFactory: Factory for creating engine adapters
13
+ - EventBus: Central event system for agent scheduling
14
+ - AgentEventType: Event types for agent lifecycle
15
+
16
+ Implementations:
17
+ - LocalProcessScheduler: Local process-based scheduler (default)
18
+ - Future: DockerScheduler, RemoteScheduler, etc.
19
+ """
20
+
21
+ from .base import (
22
+ AgentStatus,
23
+ AgentTask,
24
+ AgentScheduler,
25
+ )
26
+ from .engines import (
27
+ EngineAdapter,
28
+ EngineFactory,
29
+ GeminiAdapter,
30
+ ClaudeAdapter,
31
+ QwenAdapter,
32
+ KimiAdapter,
33
+ )
34
+ from .events import (
35
+ AgentEventType,
36
+ AgentEvent,
37
+ EventBus,
38
+ EventHandler,
39
+ event_bus,
40
+ )
41
+ from .local import LocalProcessScheduler
42
+
43
+ __all__ = [
44
+ # Base abstractions
45
+ "AgentStatus",
46
+ "AgentTask",
47
+ "AgentScheduler",
48
+ # Engine adapters
49
+ "EngineAdapter",
50
+ "EngineFactory",
51
+ "GeminiAdapter",
52
+ "ClaudeAdapter",
53
+ "QwenAdapter",
54
+ "KimiAdapter",
55
+ # Events
56
+ "AgentEventType",
57
+ "AgentEvent",
58
+ "EventBus",
59
+ "EventHandler",
60
+ "event_bus",
61
+ # Implementations
62
+ "LocalProcessScheduler",
63
+ ]
@@ -0,0 +1,152 @@
1
+ """
2
+ Base abstractions for AgentScheduler.
3
+
4
+ Defines the core AgentScheduler ABC, AgentTask dataclass, and AgentStatus enum.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime
10
+ from enum import Enum
11
+ from typing import Dict, Any, Optional
12
+
13
+
14
+ class AgentStatus(Enum):
15
+ """
16
+ Lifecycle states for an agent task.
17
+
18
+ States:
19
+ PENDING: Task is queued, waiting for resources
20
+ RUNNING: Task is actively executing
21
+ COMPLETED: Task finished successfully
22
+ FAILED: Task failed with an error
23
+ TERMINATED: Task was manually terminated
24
+ TIMEOUT: Task exceeded its time limit
25
+ """
26
+ PENDING = "pending"
27
+ RUNNING = "running"
28
+ COMPLETED = "completed"
29
+ FAILED = "failed"
30
+ TERMINATED = "terminated"
31
+ TIMEOUT = "timeout"
32
+
33
+
34
+ @dataclass
35
+ class AgentTask:
36
+ """
37
+ Data class representing a task to be scheduled.
38
+
39
+ Attributes:
40
+ task_id: Unique identifier for the task
41
+ role_name: Name of the agent role (e.g., "Engineer", "Architect")
42
+ issue_id: Associated issue ID
43
+ prompt: The instruction/context to send to the agent
44
+ engine: Agent engine to use (e.g., "gemini", "claude")
45
+ timeout: Maximum execution time in seconds
46
+ metadata: Additional task metadata
47
+ created_at: Task creation timestamp
48
+ """
49
+ task_id: str
50
+ role_name: str
51
+ issue_id: str
52
+ prompt: str
53
+ engine: str = "gemini"
54
+ timeout: int = 900
55
+ metadata: Dict[str, Any] = field(default_factory=dict)
56
+ created_at: datetime = field(default_factory=datetime.now)
57
+
58
+ def __post_init__(self):
59
+ """Ensure created_at is set."""
60
+ if self.created_at is None:
61
+ self.created_at = datetime.now()
62
+
63
+
64
+ class AgentScheduler(ABC):
65
+ """
66
+ High-level scheduling abstraction that decouples scheduling policies
67
+ from specific Agent Provider implementations.
68
+
69
+ Responsibilities:
70
+ - Task scheduling and lifecycle management
71
+ - Resource quota control (concurrency limits)
72
+ - Status monitoring and event publishing
73
+
74
+ Implementations:
75
+ - LocalProcessScheduler: Local process mode (current)
76
+ - DockerScheduler: Container mode (future)
77
+ - RemoteScheduler: Remote service mode (future)
78
+
79
+ Example:
80
+ >>> scheduler = LocalProcessScheduler(max_concurrent=5)
81
+ >>> task = AgentTask(
82
+ ... task_id="uuid-123",
83
+ ... role_name="Engineer",
84
+ ... issue_id="FEAT-123",
85
+ ... prompt="Implement feature X",
86
+ ... engine="gemini"
87
+ ... )
88
+ >>> session_id = await scheduler.schedule(task)
89
+ >>> status = scheduler.get_status(session_id)
90
+ """
91
+
92
+ @abstractmethod
93
+ async def schedule(self, task: AgentTask) -> str:
94
+ """
95
+ Schedule a task for execution.
96
+
97
+ Args:
98
+ task: The task to schedule
99
+
100
+ Returns:
101
+ session_id: Unique identifier for the scheduled session
102
+
103
+ Raises:
104
+ RuntimeError: If scheduling fails
105
+ """
106
+ pass
107
+
108
+ @abstractmethod
109
+ async def terminate(self, session_id: str) -> bool:
110
+ """
111
+ Terminate a running or pending task.
112
+
113
+ Args:
114
+ session_id: The session ID to terminate
115
+
116
+ Returns:
117
+ True if termination was successful, False otherwise
118
+ """
119
+ pass
120
+
121
+ @abstractmethod
122
+ def get_status(self, session_id: str) -> Optional[AgentStatus]:
123
+ """
124
+ Get the current status of a task.
125
+
126
+ Args:
127
+ session_id: The session ID to query
128
+
129
+ Returns:
130
+ The current AgentStatus, or None if session not found
131
+ """
132
+ pass
133
+
134
+ @abstractmethod
135
+ def list_active(self) -> Dict[str, AgentStatus]:
136
+ """
137
+ List all active (pending or running) tasks.
138
+
139
+ Returns:
140
+ Dictionary mapping session_id to AgentStatus
141
+ """
142
+ pass
143
+
144
+ @abstractmethod
145
+ def get_stats(self) -> Dict[str, Any]:
146
+ """
147
+ Get scheduler statistics.
148
+
149
+ Returns:
150
+ Dictionary containing scheduler metrics
151
+ """
152
+ pass
@@ -0,0 +1,175 @@
1
+ """
2
+ Agent Engine Adapters for Monoco Scheduler.
3
+
4
+ This module provides a unified interface for different AI agent execution engines,
5
+ allowing the Worker to seamlessly switch between Gemini, Claude, and future engines.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import List
10
+
11
+
12
+ class EngineAdapter(ABC):
13
+ """
14
+ Abstract base class for agent engine adapters.
15
+
16
+ Each adapter is responsible for:
17
+ 1. Constructing the correct CLI command for its engine
18
+ 2. Handling engine-specific error scenarios
19
+ 3. Providing metadata about the engine's capabilities
20
+ """
21
+
22
+ @abstractmethod
23
+ def build_command(self, prompt: str) -> List[str]:
24
+ """
25
+ Build the CLI command to execute the agent with the given prompt.
26
+
27
+ Args:
28
+ prompt: The instruction/context to send to the agent
29
+
30
+ Returns:
31
+ List of command arguments (e.g., ["gemini", "-y", "prompt text"])
32
+ """
33
+ pass
34
+
35
+ @property
36
+ @abstractmethod
37
+ def name(self) -> str:
38
+ """Return the canonical name of this engine."""
39
+ pass
40
+
41
+ @property
42
+ def supports_yolo_mode(self) -> bool:
43
+ """Whether this engine supports auto-approval mode."""
44
+ return False
45
+
46
+
47
+ class GeminiAdapter(EngineAdapter):
48
+ """
49
+ Adapter for Google Gemini CLI.
50
+
51
+ Command format: gemini -p <prompt> -y
52
+ The -y flag enables "YOLO mode" (auto-approval of actions).
53
+ """
54
+
55
+ def build_command(self, prompt: str) -> List[str]:
56
+ # Based on Gemini CLI help: -p <prompt> for non-interactive
57
+ return ["gemini", "-p", prompt, "-y"]
58
+
59
+ @property
60
+ def name(self) -> str:
61
+ return "gemini"
62
+
63
+ @property
64
+ def supports_yolo_mode(self) -> bool:
65
+ return True
66
+
67
+
68
+ class ClaudeAdapter(EngineAdapter):
69
+ """
70
+ Adapter for Anthropic Claude CLI.
71
+
72
+ Command format: claude -p <prompt>
73
+ The -p/--print flag enables non-interactive mode.
74
+ """
75
+
76
+ def build_command(self, prompt: str) -> List[str]:
77
+ # Based on Claude CLI help: -p <prompt> is NOT standard, usually -p means print/non-interactive.
78
+ # But for one-shot execution, we do passing prompt as argument with -p flag.
79
+ return ["claude", "-p", prompt]
80
+
81
+ @property
82
+ def name(self) -> str:
83
+ return "claude"
84
+
85
+ @property
86
+ def supports_yolo_mode(self) -> bool:
87
+ # Claude uses -p for non-interactive mode, similar concept
88
+ return True
89
+
90
+
91
+ class QwenAdapter(EngineAdapter):
92
+ """
93
+ Adapter for Qwen Code CLI.
94
+
95
+ Command format: qwen -p <prompt> -y
96
+ """
97
+
98
+ def build_command(self, prompt: str) -> List[str]:
99
+ # Assuming Qwen follows similar patterns (based on user feedback)
100
+ return ["qwen", "-p", prompt, "-y"]
101
+
102
+ @property
103
+ def name(self) -> str:
104
+ return "qwen"
105
+
106
+ @property
107
+ def supports_yolo_mode(self) -> bool:
108
+ return True
109
+
110
+
111
+ class KimiAdapter(EngineAdapter):
112
+ """
113
+ Adapter for Kimi CLI (Moonshot AI).
114
+
115
+ Command format: kimi -p <prompt> --print
116
+ Note: --print implicitly adds --yolo.
117
+ """
118
+
119
+ def build_command(self, prompt: str) -> List[str]:
120
+ # Based on Kimi CLI help: -p, --prompt TEXT.
121
+ # Also using --print for non-interactive mode (which enables yolo).
122
+ return ["kimi", "-p", prompt, "--print"]
123
+
124
+ @property
125
+ def name(self) -> str:
126
+ return "kimi"
127
+
128
+ @property
129
+ def supports_yolo_mode(self) -> bool:
130
+ return True
131
+
132
+
133
+ class EngineFactory:
134
+ """
135
+ Factory for creating engine adapter instances.
136
+
137
+ Usage:
138
+ adapter = EngineFactory.create("gemini")
139
+ command = adapter.build_command("Write a test")
140
+ """
141
+
142
+ _adapters = {
143
+ "gemini": GeminiAdapter,
144
+ "claude": ClaudeAdapter,
145
+ "qwen": QwenAdapter,
146
+ "kimi": KimiAdapter,
147
+ }
148
+
149
+ @classmethod
150
+ def create(cls, engine_name: str) -> EngineAdapter:
151
+ """
152
+ Create an adapter instance for the specified engine.
153
+
154
+ Args:
155
+ engine_name: Name of the engine (e.g., "gemini", "claude")
156
+
157
+ Returns:
158
+ An instance of the appropriate EngineAdapter
159
+
160
+ Raises:
161
+ ValueError: If the engine is not supported
162
+ """
163
+ adapter_class = cls._adapters.get(engine_name.lower())
164
+ if not adapter_class:
165
+ supported = ", ".join(cls._adapters.keys())
166
+ raise ValueError(
167
+ f"Unsupported engine: '{engine_name}'. "
168
+ f"Supported engines: {supported}"
169
+ )
170
+ return adapter_class()
171
+
172
+ @classmethod
173
+ def supported_engines(cls) -> List[str]:
174
+ """Return a list of all supported engine names."""
175
+ return list(cls._adapters.keys())