monoco-toolkit 0.3.10__py3-none-any.whl → 0.3.12__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 (130) hide show
  1. monoco/__main__.py +8 -0
  2. monoco/core/artifacts/__init__.py +16 -0
  3. monoco/core/artifacts/manager.py +575 -0
  4. monoco/core/artifacts/models.py +161 -0
  5. monoco/core/automation/__init__.py +51 -0
  6. monoco/core/automation/config.py +338 -0
  7. monoco/core/automation/field_watcher.py +296 -0
  8. monoco/core/automation/handlers.py +723 -0
  9. monoco/core/config.py +31 -4
  10. monoco/core/executor/__init__.py +38 -0
  11. monoco/core/executor/agent_action.py +254 -0
  12. monoco/core/executor/git_action.py +303 -0
  13. monoco/core/executor/im_action.py +309 -0
  14. monoco/core/executor/pytest_action.py +218 -0
  15. monoco/core/git.py +38 -0
  16. monoco/core/hooks/context.py +74 -13
  17. monoco/core/ingestion/__init__.py +20 -0
  18. monoco/core/ingestion/discovery.py +248 -0
  19. monoco/core/ingestion/watcher.py +343 -0
  20. monoco/core/ingestion/worker.py +436 -0
  21. monoco/core/loader.py +633 -0
  22. monoco/core/registry.py +34 -25
  23. monoco/core/router/__init__.py +55 -0
  24. monoco/core/router/action.py +341 -0
  25. monoco/core/router/router.py +392 -0
  26. monoco/core/scheduler/__init__.py +63 -0
  27. monoco/core/scheduler/base.py +152 -0
  28. monoco/core/scheduler/engines.py +175 -0
  29. monoco/core/scheduler/events.py +171 -0
  30. monoco/core/scheduler/local.py +377 -0
  31. monoco/core/skills.py +119 -80
  32. monoco/core/watcher/__init__.py +57 -0
  33. monoco/core/watcher/base.py +365 -0
  34. monoco/core/watcher/dropzone.py +152 -0
  35. monoco/core/watcher/issue.py +303 -0
  36. monoco/core/watcher/memo.py +200 -0
  37. monoco/core/watcher/task.py +238 -0
  38. monoco/daemon/app.py +77 -1
  39. monoco/daemon/commands.py +10 -0
  40. monoco/daemon/events.py +34 -0
  41. monoco/daemon/mailroom_service.py +196 -0
  42. monoco/daemon/models.py +1 -0
  43. monoco/daemon/scheduler.py +207 -0
  44. monoco/daemon/services.py +27 -58
  45. monoco/daemon/triggers.py +55 -0
  46. monoco/features/agent/__init__.py +25 -7
  47. monoco/features/agent/adapter.py +17 -7
  48. monoco/features/agent/cli.py +91 -57
  49. monoco/features/agent/engines.py +31 -170
  50. monoco/{core/resources/en/skills/monoco_core → features/agent/resources/en/skills/monoco_atom_core}/SKILL.md +2 -2
  51. monoco/features/agent/resources/en/skills/{flow_engineer → monoco_workflow_agent_engineer}/SKILL.md +2 -2
  52. monoco/features/agent/resources/en/skills/{flow_manager → monoco_workflow_agent_manager}/SKILL.md +2 -2
  53. monoco/features/agent/resources/en/skills/{flow_planner → monoco_workflow_agent_planner}/SKILL.md +2 -2
  54. monoco/features/agent/resources/en/skills/{flow_reviewer → monoco_workflow_agent_reviewer}/SKILL.md +2 -2
  55. monoco/features/agent/resources/{roles/role-engineer.yaml → zh/roles/monoco_role_engineer.yaml} +3 -3
  56. monoco/features/agent/resources/{roles/role-manager.yaml → zh/roles/monoco_role_manager.yaml} +8 -8
  57. monoco/features/agent/resources/{roles/role-planner.yaml → zh/roles/monoco_role_planner.yaml} +8 -8
  58. monoco/features/agent/resources/{roles/role-reviewer.yaml → zh/roles/monoco_role_reviewer.yaml} +8 -8
  59. monoco/{core/resources/zh/skills/monoco_core → features/agent/resources/zh/skills/monoco_atom_core}/SKILL.md +2 -2
  60. monoco/features/agent/resources/zh/skills/{flow_engineer → monoco_workflow_agent_engineer}/SKILL.md +2 -2
  61. monoco/features/agent/resources/zh/skills/{flow_manager → monoco_workflow_agent_manager}/SKILL.md +2 -2
  62. monoco/features/agent/resources/zh/skills/{flow_planner → monoco_workflow_agent_planner}/SKILL.md +2 -2
  63. monoco/features/agent/resources/zh/skills/{flow_reviewer → monoco_workflow_agent_reviewer}/SKILL.md +2 -2
  64. monoco/features/agent/worker.py +1 -1
  65. monoco/features/artifact/__init__.py +0 -0
  66. monoco/features/artifact/adapter.py +33 -0
  67. monoco/features/artifact/resources/zh/AGENTS.md +14 -0
  68. monoco/features/artifact/resources/zh/skills/monoco_atom_artifact/SKILL.md +278 -0
  69. monoco/features/glossary/adapter.py +18 -7
  70. monoco/features/glossary/resources/en/skills/{monoco_glossary → monoco_atom_glossary}/SKILL.md +2 -2
  71. monoco/features/glossary/resources/zh/skills/{monoco_glossary → monoco_atom_glossary}/SKILL.md +2 -2
  72. monoco/features/hooks/__init__.py +11 -0
  73. monoco/features/hooks/adapter.py +67 -0
  74. monoco/features/hooks/commands.py +309 -0
  75. monoco/features/hooks/core.py +441 -0
  76. monoco/features/hooks/resources/ADDING_HOOKS.md +234 -0
  77. monoco/features/i18n/adapter.py +18 -5
  78. monoco/features/i18n/core.py +482 -17
  79. monoco/features/i18n/resources/en/skills/{monoco_i18n → monoco_atom_i18n}/SKILL.md +2 -2
  80. monoco/features/i18n/resources/en/skills/{i18n_scan_workflow → monoco_workflow_i18n_scan}/SKILL.md +2 -2
  81. monoco/features/i18n/resources/zh/skills/{monoco_i18n → monoco_atom_i18n}/SKILL.md +2 -2
  82. monoco/features/i18n/resources/zh/skills/{i18n_scan_workflow → monoco_workflow_i18n_scan}/SKILL.md +2 -2
  83. monoco/features/issue/adapter.py +19 -6
  84. monoco/features/issue/commands.py +352 -20
  85. monoco/features/issue/core.py +475 -16
  86. monoco/features/issue/engine/machine.py +114 -4
  87. monoco/features/issue/linter.py +60 -5
  88. monoco/features/issue/models.py +2 -2
  89. monoco/features/issue/resources/en/AGENTS.md +109 -0
  90. monoco/features/issue/resources/en/skills/{monoco_issue → monoco_atom_issue}/SKILL.md +2 -2
  91. monoco/features/issue/resources/en/skills/{issue_create_workflow → monoco_workflow_issue_creation}/SKILL.md +2 -2
  92. monoco/features/issue/resources/en/skills/{issue_develop_workflow → monoco_workflow_issue_development}/SKILL.md +2 -2
  93. monoco/features/issue/resources/en/skills/{issue_lifecycle_workflow → monoco_workflow_issue_management}/SKILL.md +2 -2
  94. monoco/features/issue/resources/en/skills/{issue_refine_workflow → monoco_workflow_issue_refinement}/SKILL.md +2 -2
  95. monoco/features/issue/resources/hooks/post-checkout.sh +39 -0
  96. monoco/features/issue/resources/hooks/pre-commit.sh +41 -0
  97. monoco/features/issue/resources/hooks/pre-push.sh +35 -0
  98. monoco/features/issue/resources/zh/AGENTS.md +109 -0
  99. monoco/features/issue/resources/zh/skills/{monoco_issue → monoco_atom_issue_lifecycle}/SKILL.md +2 -2
  100. monoco/features/issue/resources/zh/skills/{issue_create_workflow → monoco_workflow_issue_creation}/SKILL.md +2 -2
  101. monoco/features/issue/resources/zh/skills/{issue_develop_workflow → monoco_workflow_issue_development}/SKILL.md +2 -2
  102. monoco/features/issue/resources/zh/skills/{issue_lifecycle_workflow → monoco_workflow_issue_management}/SKILL.md +2 -2
  103. monoco/features/issue/resources/zh/skills/{issue_refine_workflow → monoco_workflow_issue_refinement}/SKILL.md +2 -2
  104. monoco/features/issue/validator.py +101 -1
  105. monoco/features/memo/adapter.py +21 -8
  106. monoco/features/memo/cli.py +103 -10
  107. monoco/features/memo/core.py +178 -92
  108. monoco/features/memo/models.py +53 -0
  109. monoco/features/memo/resources/en/skills/{monoco_memo → monoco_atom_memo}/SKILL.md +2 -2
  110. monoco/features/memo/resources/en/skills/{note_processing_workflow → monoco_workflow_note_processing}/SKILL.md +2 -2
  111. monoco/features/memo/resources/zh/skills/{monoco_memo → monoco_atom_memo}/SKILL.md +2 -2
  112. monoco/features/memo/resources/zh/skills/{note_processing_workflow → monoco_workflow_note_processing}/SKILL.md +2 -2
  113. monoco/features/spike/adapter.py +18 -5
  114. monoco/features/spike/commands.py +5 -3
  115. monoco/features/spike/resources/en/skills/{monoco_spike → monoco_atom_spike}/SKILL.md +2 -2
  116. monoco/features/spike/resources/en/skills/{research_workflow → monoco_workflow_research}/SKILL.md +2 -2
  117. monoco/features/spike/resources/zh/skills/{monoco_spike → monoco_atom_spike}/SKILL.md +2 -2
  118. monoco/features/spike/resources/zh/skills/{research_workflow → monoco_workflow_research}/SKILL.md +2 -2
  119. monoco/main.py +38 -1
  120. {monoco_toolkit-0.3.10.dist-info → monoco_toolkit-0.3.12.dist-info}/METADATA +7 -1
  121. monoco_toolkit-0.3.12.dist-info/RECORD +202 -0
  122. monoco/features/agent/apoptosis.py +0 -44
  123. monoco/features/agent/manager.py +0 -91
  124. monoco/features/agent/session.py +0 -121
  125. monoco_toolkit-0.3.10.dist-info/RECORD +0 -156
  126. /monoco/{core → features/agent}/resources/en/AGENTS.md +0 -0
  127. /monoco/{core → features/agent}/resources/zh/AGENTS.md +0 -0
  128. {monoco_toolkit-0.3.10.dist-info → monoco_toolkit-0.3.12.dist-info}/WHEEL +0 -0
  129. {monoco_toolkit-0.3.10.dist-info → monoco_toolkit-0.3.12.dist-info}/entry_points.txt +0 -0
  130. {monoco_toolkit-0.3.10.dist-info → monoco_toolkit-0.3.12.dist-info}/licenses/LICENSE +0 -0
@@ -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())
@@ -0,0 +1,171 @@
1
+ """
2
+ EventBus - Central event system for Agent scheduling (FEAT-0155).
3
+
4
+ Provides async event publishing/subscription mechanism for decoupled
5
+ Agent lifecycle management.
6
+ """
7
+
8
+ import asyncio
9
+ import inspect
10
+ import logging
11
+ from enum import Enum, auto
12
+ from typing import Dict, List, Callable, Any, Optional
13
+ from dataclasses import dataclass, field
14
+ from datetime import datetime
15
+
16
+ logger = logging.getLogger("monoco.core.scheduler.events")
17
+
18
+
19
+ class AgentEventType(Enum):
20
+ """Event types for Agent lifecycle and triggers."""
21
+ # Memo events
22
+ MEMO_CREATED = "memo.created"
23
+ MEMO_THRESHOLD = "memo.threshold"
24
+
25
+ # Issue events
26
+ ISSUE_CREATED = "issue.created"
27
+ ISSUE_UPDATED = "issue.updated"
28
+ ISSUE_STAGE_CHANGED = "issue.stage_changed"
29
+ ISSUE_STATUS_CHANGED = "issue.status_changed"
30
+
31
+ # Session events
32
+ SESSION_STARTED = "session.started"
33
+ SESSION_COMPLETED = "session.completed"
34
+ SESSION_FAILED = "session.failed"
35
+ SESSION_CRASHED = "session.crashed"
36
+ SESSION_TERMINATED = "session.terminated"
37
+
38
+ # PR events (for Reviewer trigger)
39
+ PR_CREATED = "pr.created"
40
+ PR_UPDATED = "pr.updated"
41
+
42
+
43
+ @dataclass
44
+ class AgentEvent:
45
+ """Event data structure."""
46
+ type: AgentEventType
47
+ payload: Dict[str, Any]
48
+ timestamp: datetime = None
49
+ source: str = None
50
+
51
+ def __post_init__(self):
52
+ if self.timestamp is None:
53
+ self.timestamp = datetime.now()
54
+
55
+
56
+ EventHandler = Callable[[AgentEvent], Any]
57
+
58
+
59
+ class EventBus:
60
+ """
61
+ Central async event bus for Agent scheduling.
62
+
63
+ Supports:
64
+ - Subscribe/unsubscribe handlers for specific event types
65
+ - Publish events to all subscribed handlers
66
+ - Async handler execution
67
+ """
68
+
69
+ def __init__(self):
70
+ self._handlers: Dict[AgentEventType, List[EventHandler]] = {
71
+ event_type: [] for event_type in AgentEventType
72
+ }
73
+ self._lock = asyncio.Lock()
74
+ self._event_queue: asyncio.Queue = asyncio.Queue()
75
+ self._dispatch_task: Optional[asyncio.Task] = None
76
+ self._running = False
77
+
78
+ async def start(self):
79
+ """Start the event dispatch loop."""
80
+ if self._running:
81
+ return
82
+ self._running = True
83
+ self._dispatch_task = asyncio.create_task(self._dispatch_loop())
84
+ logger.info("EventBus started")
85
+
86
+ async def stop(self):
87
+ """Stop the event dispatch loop."""
88
+ if not self._running:
89
+ return
90
+ self._running = False
91
+ if self._dispatch_task:
92
+ self._dispatch_task.cancel()
93
+ try:
94
+ await self._dispatch_task
95
+ except asyncio.CancelledError:
96
+ pass
97
+ logger.info("EventBus stopped")
98
+
99
+ async def _dispatch_loop(self):
100
+ """Background loop to dispatch events."""
101
+ while self._running:
102
+ try:
103
+ event = await asyncio.wait_for(self._event_queue.get(), timeout=1.0)
104
+ await self._dispatch_event(event)
105
+ except asyncio.TimeoutError:
106
+ continue
107
+ except asyncio.CancelledError:
108
+ break
109
+ except Exception as e:
110
+ logger.error(f"Error dispatching event: {e}")
111
+
112
+ async def _dispatch_event(self, event: AgentEvent):
113
+ """Dispatch event to all subscribed handlers."""
114
+ handlers = self._handlers.get(event.type, [])
115
+ if not handlers:
116
+ logger.debug(f"No handlers for event {event.type.value}")
117
+ return
118
+
119
+ logger.debug(f"Dispatching {event.type.value} to {len(handlers)} handlers")
120
+
121
+ # Execute handlers concurrently
122
+ tasks = []
123
+ for handler in handlers:
124
+ try:
125
+ if inspect.iscoroutinefunction(handler):
126
+ tasks.append(asyncio.create_task(handler(event)))
127
+ else:
128
+ handler(event)
129
+ except Exception as e:
130
+ logger.error(f"Handler error for {event.type.value}: {e}")
131
+
132
+ if tasks:
133
+ await asyncio.gather(*tasks, return_exceptions=True)
134
+
135
+ def subscribe(self, event_type: AgentEventType, handler: EventHandler):
136
+ """Subscribe a handler to an event type."""
137
+ if handler not in self._handlers[event_type]:
138
+ self._handlers[event_type].append(handler)
139
+ logger.debug(f"Handler subscribed to {event_type.value}")
140
+
141
+ def unsubscribe(self, event_type: AgentEventType, handler: EventHandler):
142
+ """Unsubscribe a handler from an event type."""
143
+ if handler in self._handlers[event_type]:
144
+ self._handlers[event_type].remove(handler)
145
+ logger.debug(f"Handler unsubscribed from {event_type.value}")
146
+
147
+ async def publish(self, event_type: AgentEventType, payload: Dict[str, Any], source: str = None):
148
+ """Publish an event to the bus."""
149
+ event = AgentEvent(type=event_type, payload=payload, source=source)
150
+ await self._event_queue.put(event)
151
+ logger.debug(f"Published event {event_type.value}")
152
+
153
+ def get_subscriber_count(self, event_type: AgentEventType) -> int:
154
+ """Get number of subscribers for an event type."""
155
+ return len(self._handlers.get(event_type, []))
156
+
157
+ def get_stats(self) -> Dict[str, Any]:
158
+ """Get event bus statistics."""
159
+ return {
160
+ "running": self._running,
161
+ "queue_size": self._event_queue.qsize(),
162
+ "subscribers": {
163
+ event_type.value: len(handlers)
164
+ for event_type, handlers in self._handlers.items()
165
+ if handlers
166
+ }
167
+ }
168
+
169
+
170
+ # Global event bus instance
171
+ event_bus = EventBus()