monoco-toolkit 0.3.6__py3-none-any.whl → 0.3.9__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 (58) hide show
  1. monoco/cli/workspace.py +1 -1
  2. monoco/core/config.py +51 -0
  3. monoco/core/hooks/__init__.py +19 -0
  4. monoco/core/hooks/base.py +104 -0
  5. monoco/core/hooks/builtin/__init__.py +11 -0
  6. monoco/core/hooks/builtin/git_cleanup.py +266 -0
  7. monoco/core/hooks/builtin/logging_hook.py +78 -0
  8. monoco/core/hooks/context.py +131 -0
  9. monoco/core/hooks/registry.py +222 -0
  10. monoco/core/integrations.py +6 -0
  11. monoco/core/registry.py +2 -0
  12. monoco/core/setup.py +1 -1
  13. monoco/core/skills.py +226 -42
  14. monoco/features/{scheduler → agent}/__init__.py +4 -2
  15. monoco/features/{scheduler → agent}/cli.py +134 -80
  16. monoco/features/{scheduler → agent}/config.py +17 -3
  17. monoco/features/agent/defaults.py +55 -0
  18. monoco/features/agent/flow_skills.py +281 -0
  19. monoco/features/{scheduler → agent}/manager.py +39 -2
  20. monoco/features/{scheduler → agent}/models.py +6 -3
  21. monoco/features/{scheduler → agent}/reliability.py +1 -1
  22. monoco/features/agent/resources/skills/flow_engineer/SKILL.md +94 -0
  23. monoco/features/agent/resources/skills/flow_manager/SKILL.md +88 -0
  24. monoco/features/agent/resources/skills/flow_reviewer/SKILL.md +114 -0
  25. monoco/features/{scheduler → agent}/session.py +36 -1
  26. monoco/features/{scheduler → agent}/worker.py +2 -2
  27. monoco/features/i18n/resources/skills/i18n_scan_workflow/SKILL.md +105 -0
  28. monoco/features/issue/commands.py +427 -21
  29. monoco/features/issue/core.py +100 -0
  30. monoco/features/issue/criticality.py +553 -0
  31. monoco/features/issue/domain/models.py +28 -2
  32. monoco/features/issue/engine/machine.py +70 -13
  33. monoco/features/issue/git_service.py +185 -0
  34. monoco/features/issue/linter.py +291 -62
  35. monoco/features/issue/models.py +49 -2
  36. monoco/features/issue/resources/en/SKILL.md +48 -0
  37. monoco/features/issue/resources/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  38. monoco/features/issue/resources/zh/SKILL.md +50 -0
  39. monoco/features/issue/validator.py +185 -65
  40. monoco/features/memo/__init__.py +2 -1
  41. monoco/features/memo/adapter.py +32 -0
  42. monoco/features/memo/cli.py +36 -14
  43. monoco/features/memo/core.py +59 -0
  44. monoco/features/memo/resources/skills/note_processing_workflow/SKILL.md +140 -0
  45. monoco/features/memo/resources/zh/AGENTS.md +8 -0
  46. monoco/features/memo/resources/zh/SKILL.md +75 -0
  47. monoco/features/spike/resources/skills/research_workflow/SKILL.md +121 -0
  48. monoco/main.py +2 -3
  49. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
  50. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +55 -37
  51. monoco/features/scheduler/defaults.py +0 -54
  52. monoco/features/skills/__init__.py +0 -0
  53. monoco/features/skills/core.py +0 -102
  54. /monoco/core/{hooks.py → githooks.py} +0 -0
  55. /monoco/features/{scheduler → agent}/engines.py +0 -0
  56. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
  57. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
  58. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
- from .models import RoleTemplate, SchedulerConfig
1
+ from .models import RoleTemplate, AgentConfig, SchedulerConfig
2
2
  from .worker import Worker
3
- from .config import load_scheduler_config
3
+ from .config import load_scheduler_config, load_agent_config
4
4
  from .defaults import DEFAULT_ROLES
5
5
  from .session import Session, RuntimeSession
6
6
  from .manager import SessionManager
@@ -8,7 +8,9 @@ from .reliability import ApoptosisManager
8
8
 
9
9
  __all__ = [
10
10
  "RoleTemplate",
11
+ "AgentConfig",
11
12
  "SchedulerConfig",
13
+ "load_agent_config",
12
14
  "Worker",
13
15
  "load_scheduler_config",
14
16
  "DEFAULT_ROLES",
@@ -4,10 +4,16 @@ from pathlib import Path
4
4
  from typing import Optional
5
5
  from monoco.core.output import print_output
6
6
  from monoco.core.config import get_config
7
- from monoco.features.scheduler import SessionManager, load_scheduler_config
7
+ from monoco.features.agent import SessionManager, load_scheduler_config
8
8
 
9
- app = typer.Typer(name="agent", help="Manage agent sessions")
10
- role_app = typer.Typer(name="role", help="Manage agent roles")
9
+ app = typer.Typer(name="agent", help="Manage agent sessions and roles")
10
+ session_app = typer.Typer(name="session", help="Manage active agent sessions")
11
+ role_app = typer.Typer(name="role", help="Manage agent roles (CRUD)")
12
+ provider_app = typer.Typer(name="provider", help="Manage agent providers (Engines)")
13
+
14
+ app.add_typer(session_app, name="session")
15
+ app.add_typer(role_app, name="role")
16
+ app.add_typer(provider_app, name="provider")
11
17
 
12
18
 
13
19
  @role_app.command(name="list")
@@ -15,7 +21,7 @@ def list_roles():
15
21
  """
16
22
  List available agent roles and their sources.
17
23
  """
18
- from monoco.features.scheduler.config import RoleLoader
24
+ from monoco.features.agent.config import RoleLoader
19
25
 
20
26
  settings = get_config()
21
27
  project_root = Path(settings.paths.root).resolve()
@@ -37,70 +43,67 @@ def list_roles():
37
43
  print_output(output, title="Agent Roles")
38
44
 
39
45
 
40
- @app.command()
41
- def draft(
42
- desc: str = typer.Option(..., "--desc", "-d", help="Description of the task"),
43
- type: str = typer.Option(
44
- "feature", "--type", "-t", help="Issue type (feature/chore/fix)"
45
- ),
46
- ):
46
+ @provider_app.command(name="list")
47
+ def list_providers():
47
48
  """
48
- Draft a new issue based on a natural language description.
49
- This creates a temporary 'drafter' agent session.
49
+ List available agent providers and their status.
50
50
  """
51
- from monoco.core.output import print_error
51
+ from monoco.core.integrations import get_all_integrations
52
52
 
53
53
  settings = get_config()
54
- project_root = Path(settings.paths.root).resolve()
55
-
56
- # Load Roles
57
- roles = load_scheduler_config(project_root)
58
- # Use 'crafter' as the role for drafting (it handles new tasks)
59
- role_name = "crafter"
60
- selected_role = roles.get(role_name)
61
-
62
- if not selected_role:
63
- print_error(f"Role '{role_name}' not found.")
64
- raise typer.Exit(code=1)
54
+ # Ideally we'd pass project-specific integrations here if they existed in config objects
55
+ integrations = get_all_integrations(enabled_only=False)
65
56
 
66
- print_output(
67
- f"Drafting {type} from description: '{desc}'",
68
- title="Agent Drafter",
69
- )
57
+ output = []
58
+ for key, integration in integrations.items():
59
+ output.append(
60
+ {
61
+ "key": key,
62
+ "name": integration.name,
63
+ "binary": integration.bin_name or "-",
64
+ "enabled": integration.enabled,
65
+ "rules": integration.system_prompt_file,
66
+ }
67
+ )
70
68
 
71
- manager = SessionManager()
72
- # We use a placeholder ID as we don't know the ID yet.
73
- # The agent is expected to create the file, so the ID will be generated then.
74
- session = manager.create_session("NEW_TASK", selected_role)
69
+ print_output(output, title="Agent Providers")
75
70
 
76
- context = {"description": desc, "type": type}
77
71
 
78
- try:
79
- session.start(context=context)
72
+ @provider_app.command(name="check")
73
+ def check_providers():
74
+ """
75
+ Run health checks on available providers.
76
+ """
77
+ from monoco.core.integrations import get_all_integrations
80
78
 
81
- # Monitoring Loop
82
- while session.refresh_status() == "running":
83
- time.sleep(1)
79
+ integrations = get_all_integrations(enabled_only=True)
84
80
 
85
- if session.model.status == "failed":
86
- print_error("Drafting failed.")
87
- else:
88
- print_output("Drafting completed.", title="Agent Drafter")
81
+ output = []
82
+ for key, integration in integrations.items():
83
+ health = integration.check_health()
84
+ output.append(
85
+ {
86
+ "provider": integration.name,
87
+ "available": "✅" if health.available else "❌",
88
+ "latency": f"{health.latency_ms}ms" if health.latency_ms else "-",
89
+ "error": health.error or "-",
90
+ }
91
+ )
89
92
 
90
- except KeyboardInterrupt:
91
- print("\nStopping...")
92
- session.terminate()
93
- print_output("Drafting cancelled.")
93
+ print_output(output, title="Provider Health Check")
94
94
 
95
95
 
96
96
  @app.command()
97
97
  def run(
98
- target: str = typer.Argument(
99
- ..., help="Issue ID (e.g. FEAT-101) or a Task Description in quotes."
98
+ target: Optional[str] = typer.Argument(
99
+ None, help="Issue ID (e.g. FEAT-101) or a Task Description in quotes."
100
100
  ),
101
101
  role: Optional[str] = typer.Option(
102
102
  None,
103
- help="Specific role to use (crafter/builder/auditor). Default: intelligent selection.",
103
+ help="Specific role to use (Planner/Builder/Reviewer). Default: intelligent selection.",
104
+ ),
105
+ type: str = typer.Option(
106
+ "feature", "--type", "-t", help="Issue type for new tasks (feature/chore/fix)."
104
107
  ),
105
108
  detach: bool = typer.Option(
106
109
  False, "--detach", "-d", help="Run in background (Daemon)"
@@ -111,11 +114,19 @@ def run(
111
114
  ):
112
115
  """
113
116
  Start an agent session.
114
- - If TARGET is an Issue ID: Work on that issue.
115
- - If TARGET is a text description: Create a new issue (Crafter).
117
+
118
+ If TARGET is an Issue ID, it starts a session for that issue.
119
+ If TARGET is a description, it starts a 'Planner' session to draft a new issue.
116
120
  """
117
- settings = get_config()
118
- project_root = Path(settings.paths.root).resolve()
121
+ from monoco.core.output import print_error
122
+
123
+ # Normal run mode - target is required
124
+ if not target:
125
+ from monoco.core.output import print_error
126
+
127
+ print_error("TARGET (Issue ID or Task Description) is required.")
128
+ raise typer.Exit(code=1)
129
+ raise typer.Exit(code=1)
119
130
 
120
131
  # 1. Smart Intent Recognition
121
132
  import re
@@ -124,12 +135,12 @@ def run(
124
135
 
125
136
  if is_id:
126
137
  issue_id = target.upper()
127
- role_name = role or "builder"
138
+ role_name = role or "Builder"
128
139
  description = None
129
140
  else:
130
- # Implicit Draft Mode via run command
141
+ # Implicit Draft Mode via run command (target is description)
131
142
  issue_id = "NEW_TASK"
132
- role_name = role or "crafter"
143
+ role_name = role or "Planner"
133
144
  description = target
134
145
 
135
146
  # 2. Load Roles
@@ -137,8 +148,6 @@ def run(
137
148
  selected_role = roles.get(role_name)
138
149
 
139
150
  if not selected_role:
140
- from monoco.core.output import print_error
141
-
142
151
  print_error(f"Role '{role_name}' not found. Available: {list(roles.keys())}")
143
152
  raise typer.Exit(code=1)
144
153
 
@@ -173,10 +182,8 @@ def run(
173
182
  time.sleep(1)
174
183
 
175
184
  if session.model.status == "failed":
176
- from monoco.core.output import print_error
177
-
178
185
  print_error(
179
- f"Session {session.model.id} FAILED. Use 'monoco agent autopsy {session.model.id}' for analysis."
186
+ f"Session {session.model.id} FAILED. Use 'monoco agent session autopsy {session.model.id}' for analysis."
180
187
  )
181
188
  else:
182
189
  print_output(
@@ -190,10 +197,10 @@ def run(
190
197
  print_output("Session terminated.")
191
198
 
192
199
 
193
- @app.command()
194
- def kill(session_id: str):
200
+ @session_app.command(name="kill")
201
+ def kill_session(session_id: str):
195
202
  """
196
- Terminate a session.
203
+ Terminate a specific session.
197
204
  """
198
205
  manager = SessionManager()
199
206
  session = manager.get_session(session_id)
@@ -204,14 +211,16 @@ def kill(session_id: str):
204
211
  print_output(f"Session {session_id} not found.", style="red")
205
212
 
206
213
 
207
- @app.command()
208
- def autopsy(
209
- target: str = typer.Argument(..., help="Session ID or Issue ID to analyze."),
210
- ):
211
- """
212
- Execute Post-Mortem analysis on a failed session or target Issue.
213
- """
214
+ @session_app.command(name="autopsy")
215
+ def autopsy_command(target: str):
216
+ """Execute Post-Mortem analysis on a failed session or target Issue."""
217
+ _run_autopsy(target)
218
+
219
+
220
+ def _run_autopsy(target: str):
221
+ """Execute Post-Mortem analysis on a failed session or target Issue."""
214
222
  from .reliability import ApoptosisManager
223
+ from monoco.core.output import print_error
215
224
 
216
225
  manager = SessionManager()
217
226
 
@@ -229,25 +238,70 @@ def autopsy(
229
238
  settings = get_config()
230
239
  project_root = Path(settings.paths.root).resolve()
231
240
  roles = load_scheduler_config(project_root)
232
- builder_role = roles.get("builder")
241
+ builder_role = roles.get("Builder")
233
242
 
234
243
  if not builder_role:
235
- print_output("Builder role not found.", style="red")
244
+ print_error("Builder role not found.")
236
245
  raise typer.Exit(code=1)
237
246
 
238
247
  session = manager.create_session(target.upper(), builder_role)
239
248
  session.model.status = "failed"
240
249
  else:
241
- print_output(
242
- f"Could not find session or valid Issue ID for '{target}'", style="red"
243
- )
250
+ print_error(f"Could not find session or valid Issue ID for '{target}'")
244
251
  raise typer.Exit(code=1)
245
252
 
246
253
  apoptosis = ApoptosisManager(manager)
247
254
  apoptosis.trigger_apoptosis(session.model.id)
248
255
 
249
256
 
250
- @app.command(name="list")
257
+ def _run_draft(desc: str, type: str, detach: bool):
258
+ """Draft a new issue based on a natural language description."""
259
+ from monoco.core.output import print_error
260
+
261
+ settings = get_config()
262
+ project_root = Path(settings.paths.root).resolve()
263
+
264
+ # Load Roles
265
+ roles = load_scheduler_config(project_root)
266
+ # Use 'Planner' as the role for drafting (it handles new tasks)
267
+ role_name = "Planner"
268
+ selected_role = roles.get(role_name)
269
+
270
+ if not selected_role:
271
+ print_error(f"Role '{role_name}' not found.")
272
+ raise typer.Exit(code=1)
273
+
274
+ print_output(
275
+ f"Drafting {type} from description: '{desc}'",
276
+ title="Agent Drafter",
277
+ )
278
+
279
+ manager = SessionManager()
280
+ # We use a placeholder ID as we don't know the ID yet.
281
+ # The agent is expected to create the file, so the ID will be generated then.
282
+ session = manager.create_session("NEW_TASK", selected_role)
283
+
284
+ context = {"description": desc, "type": type}
285
+
286
+ try:
287
+ session.start(context=context)
288
+
289
+ # Monitoring Loop
290
+ while session.refresh_status() == "running":
291
+ time.sleep(1)
292
+
293
+ if session.model.status == "failed":
294
+ print_error("Drafting failed.")
295
+ else:
296
+ print_output("Drafting completed.", title="Agent Drafter")
297
+
298
+ except KeyboardInterrupt:
299
+ print("\nStopping...")
300
+ session.terminate()
301
+ print_output("Drafting cancelled.")
302
+
303
+
304
+ @session_app.command(name="list")
251
305
  def list_sessions():
252
306
  """
253
307
  List active agent sessions.
@@ -274,8 +328,8 @@ def list_sessions():
274
328
  )
275
329
 
276
330
 
277
- @app.command()
278
- def logs(session_id: str):
331
+ @session_app.command(name="logs")
332
+ def session_logs(session_id: str):
279
333
  """
280
334
  Stream logs for a session.
281
335
  """
@@ -1,7 +1,7 @@
1
1
  from typing import Dict, Optional
2
2
  import yaml
3
3
  from pathlib import Path
4
- from .models import RoleTemplate, SchedulerConfig
4
+ from .models import RoleTemplate, AgentConfig
5
5
  from .defaults import DEFAULT_ROLES
6
6
 
7
7
 
@@ -45,8 +45,8 @@ class RoleLoader:
45
45
  data = yaml.safe_load(f) or {}
46
46
 
47
47
  if "roles" in data:
48
- # Validate using SchedulerConfig
49
- config = SchedulerConfig(roles=data["roles"])
48
+ # Validate using AgentConfig
49
+ config = AgentConfig(roles=data["roles"])
50
50
  for role in config.roles:
51
51
  # Level 3 > Level 2 > Level 1 (名字相同的 Role 进行覆盖/Merge)
52
52
  # Currently we do total replacement for same-named roles
@@ -66,3 +66,17 @@ def load_scheduler_config(project_root: Path) -> Dict[str, RoleTemplate]:
66
66
  """
67
67
  loader = RoleLoader(project_root)
68
68
  return loader.load_all()
69
+
70
+
71
+ def load_agent_config(project_root: Path) -> Dict[str, RoleTemplate]:
72
+ """
73
+ Load agent configuration from tiered sources.
74
+
75
+ Args:
76
+ project_root: Path to the project root directory
77
+
78
+ Returns:
79
+ Dictionary mapping role names to RoleTemplate objects
80
+ """
81
+ loader = RoleLoader(project_root)
82
+ return loader.load_all()
@@ -0,0 +1,55 @@
1
+ from .models import RoleTemplate
2
+
3
+ DEFAULT_ROLES = [
4
+ RoleTemplate(
5
+ name="Planner",
6
+ description="Responsible for requirement analysis, design documents, and issue drafting.",
7
+ trigger="task.received",
8
+ goal="Produce a structured design document or issue ticket.",
9
+ system_prompt=(
10
+ "You are a Planner agent. Your goal is to transform requirements into structured plans.\n"
11
+ "Analyze the workspace context and produce detailed issue tickets or design documents."
12
+ ),
13
+ engine="gemini",
14
+ ),
15
+ RoleTemplate(
16
+ name="Builder",
17
+ description="Responsible for implementing code and tests based on the design.",
18
+ trigger="design.approved",
19
+ goal="Implement functional code and passing tests.",
20
+ system_prompt="You are a Builder agent. Your job is to implement the solution as specified in the issue.",
21
+ engine="gemini",
22
+ ),
23
+ RoleTemplate(
24
+ name="Reviewer",
25
+ description="Responsible for code quality, architectural consistency, and linting.",
26
+ trigger="implementation.submitted",
27
+ goal="Provide critical feedback and approve or reject submissions.",
28
+ system_prompt="You are a Reviewer agent. Analyze code changes for bugs, style, and correctness.",
29
+ engine="gemini",
30
+ ),
31
+ RoleTemplate(
32
+ name="Merger",
33
+ description="Responsible for branch management and merging verified changes into trunk.",
34
+ trigger="review.passed",
35
+ goal="Safely merge feature branches and prune resources.",
36
+ system_prompt="You are a Merger agent. Handle the final integration of features and clean up environment.",
37
+ engine="gemini",
38
+ ),
39
+ RoleTemplate(
40
+ name="Coroner",
41
+ description="Responsible for post-mortem analysis and root cause identification on failure.",
42
+ trigger="session.crashed",
43
+ goal="Generate a detailed autopsy report.",
44
+ system_prompt="You are a Coroner agent. Analyze the failure state and generate a ## Post-mortem report.",
45
+ engine="gemini",
46
+ ),
47
+ RoleTemplate(
48
+ name="Manager",
49
+ description="The central orchestrator that processes memos and schedules other roles.",
50
+ trigger="daily.check / memo.added",
51
+ goal="Orchestrate the development workflow and manage team priorities.",
52
+ system_prompt="You are the Manager agent. You act as the scheduler and coordinator for the entire agentic system.",
53
+ engine="gemini",
54
+ ),
55
+ ]