monoco-toolkit 0.3.12__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 (120) hide show
  1. monoco/core/automation/__init__.py +0 -11
  2. monoco/core/automation/handlers.py +108 -26
  3. monoco/core/config.py +28 -10
  4. monoco/core/daemon/__init__.py +5 -0
  5. monoco/core/daemon/pid.py +290 -0
  6. monoco/core/injection.py +86 -8
  7. monoco/core/integrations.py +0 -24
  8. monoco/core/router/__init__.py +1 -39
  9. monoco/core/router/action.py +3 -142
  10. monoco/core/scheduler/events.py +28 -2
  11. monoco/core/setup.py +9 -0
  12. monoco/core/sync.py +199 -4
  13. monoco/core/watcher/__init__.py +6 -0
  14. monoco/core/watcher/base.py +18 -1
  15. monoco/core/watcher/im.py +460 -0
  16. monoco/core/watcher/memo.py +40 -48
  17. monoco/daemon/app.py +3 -60
  18. monoco/daemon/commands.py +459 -25
  19. monoco/daemon/scheduler.py +1 -16
  20. monoco/daemon/services.py +15 -0
  21. monoco/features/agent/resources/en/AGENTS.md +14 -14
  22. monoco/features/agent/resources/en/skills/monoco_role_engineer/SKILL.md +101 -0
  23. monoco/features/agent/resources/en/skills/monoco_role_manager/SKILL.md +95 -0
  24. monoco/features/agent/resources/en/skills/monoco_role_planner/SKILL.md +177 -0
  25. monoco/features/agent/resources/en/skills/monoco_role_reviewer/SKILL.md +139 -0
  26. monoco/features/agent/resources/zh/skills/monoco_role_engineer/SKILL.md +101 -0
  27. monoco/features/agent/resources/zh/skills/monoco_role_manager/SKILL.md +95 -0
  28. monoco/features/agent/resources/zh/skills/monoco_role_planner/SKILL.md +177 -0
  29. monoco/features/agent/resources/zh/skills/monoco_role_reviewer/SKILL.md +139 -0
  30. monoco/features/hooks/__init__.py +61 -6
  31. monoco/features/hooks/commands.py +281 -271
  32. monoco/features/hooks/dispatchers/__init__.py +23 -0
  33. monoco/features/hooks/dispatchers/agent_dispatcher.py +486 -0
  34. monoco/features/hooks/dispatchers/git_dispatcher.py +478 -0
  35. monoco/features/hooks/manager.py +357 -0
  36. monoco/features/hooks/models.py +262 -0
  37. monoco/features/hooks/parser.py +322 -0
  38. monoco/features/hooks/universal_interceptor.py +503 -0
  39. monoco/features/im/__init__.py +67 -0
  40. monoco/features/im/core.py +782 -0
  41. monoco/features/im/models.py +311 -0
  42. monoco/features/issue/commands.py +65 -50
  43. monoco/features/issue/core.py +199 -99
  44. monoco/features/issue/domain_commands.py +0 -19
  45. monoco/features/issue/resources/en/AGENTS.md +17 -122
  46. monoco/features/issue/resources/hooks/agent/before-tool.sh +102 -0
  47. monoco/features/issue/resources/hooks/agent/session-start.sh +88 -0
  48. monoco/features/issue/resources/hooks/{post-checkout.sh → git/git-post-checkout.sh} +10 -9
  49. monoco/features/issue/resources/hooks/git/git-pre-commit.sh +31 -0
  50. monoco/features/issue/resources/hooks/{pre-push.sh → git/git-pre-push.sh} +7 -13
  51. monoco/features/issue/resources/zh/AGENTS.md +18 -123
  52. monoco/features/memo/cli.py +15 -64
  53. monoco/features/memo/core.py +6 -34
  54. monoco/features/memo/models.py +24 -15
  55. monoco/features/memo/resources/en/AGENTS.md +31 -0
  56. monoco/features/memo/resources/zh/AGENTS.md +28 -5
  57. monoco/main.py +5 -3
  58. {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/METADATA +1 -1
  59. monoco_toolkit-0.4.0.dist-info/RECORD +170 -0
  60. monoco/core/automation/config.py +0 -338
  61. monoco/core/execution.py +0 -67
  62. monoco/core/executor/__init__.py +0 -38
  63. monoco/core/executor/agent_action.py +0 -254
  64. monoco/core/executor/git_action.py +0 -303
  65. monoco/core/executor/im_action.py +0 -309
  66. monoco/core/executor/pytest_action.py +0 -218
  67. monoco/core/router/router.py +0 -392
  68. monoco/features/agent/resources/atoms/atom-code-dev.yaml +0 -61
  69. monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +0 -73
  70. monoco/features/agent/resources/atoms/atom-knowledge.yaml +0 -55
  71. monoco/features/agent/resources/atoms/atom-review.yaml +0 -60
  72. monoco/features/agent/resources/en/skills/monoco_atom_core/SKILL.md +0 -99
  73. monoco/features/agent/resources/en/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
  74. monoco/features/agent/resources/en/skills/monoco_workflow_agent_manager/SKILL.md +0 -93
  75. monoco/features/agent/resources/en/skills/monoco_workflow_agent_planner/SKILL.md +0 -85
  76. monoco/features/agent/resources/en/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -114
  77. monoco/features/agent/resources/workflows/workflow-dev.yaml +0 -83
  78. monoco/features/agent/resources/workflows/workflow-issue-create.yaml +0 -72
  79. monoco/features/agent/resources/workflows/workflow-review.yaml +0 -94
  80. monoco/features/agent/resources/zh/roles/monoco_role_engineer.yaml +0 -49
  81. monoco/features/agent/resources/zh/roles/monoco_role_manager.yaml +0 -46
  82. monoco/features/agent/resources/zh/roles/monoco_role_planner.yaml +0 -46
  83. monoco/features/agent/resources/zh/roles/monoco_role_reviewer.yaml +0 -47
  84. monoco/features/agent/resources/zh/skills/monoco_atom_core/SKILL.md +0 -99
  85. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
  86. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_manager/SKILL.md +0 -88
  87. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_planner/SKILL.md +0 -259
  88. monoco/features/agent/resources/zh/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -137
  89. monoco/features/artifact/resources/zh/skills/monoco_atom_artifact/SKILL.md +0 -278
  90. monoco/features/glossary/resources/en/skills/monoco_atom_glossary/SKILL.md +0 -35
  91. monoco/features/glossary/resources/zh/skills/monoco_atom_glossary/SKILL.md +0 -35
  92. monoco/features/hooks/adapter.py +0 -67
  93. monoco/features/hooks/core.py +0 -441
  94. monoco/features/i18n/resources/en/skills/monoco_atom_i18n/SKILL.md +0 -96
  95. monoco/features/i18n/resources/en/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
  96. monoco/features/i18n/resources/zh/skills/monoco_atom_i18n/SKILL.md +0 -96
  97. monoco/features/i18n/resources/zh/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
  98. monoco/features/issue/resources/en/skills/monoco_atom_issue/SKILL.md +0 -165
  99. monoco/features/issue/resources/en/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
  100. monoco/features/issue/resources/en/skills/monoco_workflow_issue_development/SKILL.md +0 -224
  101. monoco/features/issue/resources/en/skills/monoco_workflow_issue_management/SKILL.md +0 -159
  102. monoco/features/issue/resources/en/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
  103. monoco/features/issue/resources/hooks/pre-commit.sh +0 -41
  104. monoco/features/issue/resources/zh/skills/monoco_atom_issue_lifecycle/SKILL.md +0 -190
  105. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
  106. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_development/SKILL.md +0 -224
  107. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_management/SKILL.md +0 -159
  108. monoco/features/issue/resources/zh/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
  109. monoco/features/memo/resources/en/skills/monoco_atom_memo/SKILL.md +0 -77
  110. monoco/features/memo/resources/en/skills/monoco_workflow_note_processing/SKILL.md +0 -140
  111. monoco/features/memo/resources/zh/skills/monoco_atom_memo/SKILL.md +0 -77
  112. monoco/features/memo/resources/zh/skills/monoco_workflow_note_processing/SKILL.md +0 -140
  113. monoco/features/spike/resources/en/skills/monoco_atom_spike/SKILL.md +0 -76
  114. monoco/features/spike/resources/en/skills/monoco_workflow_research/SKILL.md +0 -121
  115. monoco/features/spike/resources/zh/skills/monoco_atom_spike/SKILL.md +0 -76
  116. monoco/features/spike/resources/zh/skills/monoco_workflow_research/SKILL.md +0 -121
  117. monoco_toolkit-0.3.12.dist-info/RECORD +0 -202
  118. {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/WHEEL +0 -0
  119. {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/entry_points.txt +0 -0
  120. {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/licenses/LICENSE +0 -0
monoco/core/sync.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import typer
2
+ import subprocess
2
3
  from pathlib import Path
3
4
  from typing import Optional, List
4
5
  from monoco.core.registry import FeatureRegistry
@@ -21,8 +22,10 @@ def _get_targets(root: Path, config, cli_target: Optional[Path]) -> List[Path]:
21
22
  return targets
22
23
 
23
24
  # 2. Registry Defaults (Dynamic Detection)
25
+ # We now default to ALL integrations instead of auto-detecting
26
+ # because we want to enable all agents by default.
24
27
  integrations = get_active_integrations(
25
- root, config_overrides=None, auto_detect=True
28
+ root, config_overrides=None, auto_detect=False
26
29
  )
27
30
 
28
31
  if integrations:
@@ -133,8 +136,9 @@ def sync_command(
133
136
  skill_manager = SkillManager(root, active_features)
134
137
 
135
138
  # Get active integrations
139
+ # Disable auto-detect to distribute to all supported frameworks
136
140
  integrations = get_active_integrations(
137
- root, config_overrides=None, auto_detect=True
141
+ root, config_overrides=None, auto_detect=False
138
142
  )
139
143
 
140
144
  if integrations:
@@ -165,7 +169,7 @@ def sync_command(
165
169
  # 5. Distribute Workflows (if --workflows flag is set)
166
170
  if workflows:
167
171
  console.print("[bold blue]Distributing Flow Skills as Workflows...[/bold blue]")
168
-
172
+
169
173
  try:
170
174
  workflow_results = skill_manager.distribute_workflows(force=False, lang=skill_lang)
171
175
  success_count = sum(1 for v in workflow_results.values() if v)
@@ -182,6 +186,103 @@ def sync_command(
182
186
  f"[red] Failed to distribute workflows: {e}[/red]"
183
187
  )
184
188
 
189
+ # 6. Sync Universal Hooks (Git & Agent)
190
+ console.print("[bold blue]Synchronizing Universal Hooks...[/bold blue]")
191
+
192
+ try:
193
+ from monoco.features.hooks import UniversalHookManager, HookType
194
+ from monoco.features.hooks.dispatchers import (
195
+ GitHookDispatcher,
196
+ ClaudeCodeDispatcher,
197
+ GeminiDispatcher,
198
+ )
199
+
200
+ hooks_manager = UniversalHookManager()
201
+
202
+ # Register Dispatchers
203
+ git_dispatcher = GitHookDispatcher()
204
+ hooks_manager.register_dispatcher(HookType.GIT, git_dispatcher)
205
+
206
+ # Register Agent Dispatchers for active platforms
207
+ # NOTE: Only Claude Code and Gemini CLI are officially supported
208
+ agent_dispatchers = {
209
+ "claude-code": ClaudeCodeDispatcher(),
210
+ "gemini-cli": GeminiDispatcher(),
211
+ }
212
+ for dispatcher in agent_dispatchers.values():
213
+ hooks_manager.register_dispatcher(HookType.AGENT, dispatcher)
214
+
215
+ # 6.1 Scan for hooks
216
+ all_hooks = []
217
+
218
+ # 6.1.1 Scan builtin hooks from hooks feature
219
+ try:
220
+ from monoco.features import hooks as hooks_module
221
+ hooks_feature_dir = Path(hooks_module.__file__).parent
222
+ builtin_hooks_dir = hooks_feature_dir / "resources" / "hooks"
223
+ if builtin_hooks_dir.exists():
224
+ groups = hooks_manager.scan(builtin_hooks_dir)
225
+ for group in groups.values():
226
+ all_hooks.extend(group.hooks)
227
+ except Exception as e:
228
+ console.print(f"[dim] No builtin hooks found: {e}[/dim]")
229
+
230
+ # 6.1.2 Scan for hooks in all active features
231
+ for feature in active_features:
232
+ if feature.name == "hooks":
233
+ continue # Already scanned
234
+
235
+ import importlib
236
+ try:
237
+ # Use the module where the feature class is defined (usually adapter.py)
238
+ module_name = feature.__class__.__module__
239
+ module = importlib.import_module(module_name)
240
+
241
+ if hasattr(module, "__file__") and module.__file__:
242
+ # feature_dir is the directory containing adapter.py
243
+ feature_dir = Path(module.__file__).parent
244
+ hooks_resource_dir = feature_dir / "resources" / "hooks"
245
+
246
+ if hooks_resource_dir.exists():
247
+ groups = hooks_manager.scan(hooks_resource_dir)
248
+ for group in groups.values():
249
+ all_hooks.extend(group.hooks)
250
+ except Exception:
251
+ continue
252
+
253
+ # 6.2 Sync Git Hooks
254
+ git_hooks = [h for h in all_hooks if h.metadata.type == HookType.GIT]
255
+
256
+ if not (root / ".git").exists():
257
+ console.print("[dim] Git repository not found. Initializing...[/dim]")
258
+ # Set global default branch to main
259
+ subprocess.run(["git", "config", "--global", "init.defaultBranch", "main"], check=False)
260
+ subprocess.run(["git", "init"], cwd=root, check=False)
261
+
262
+ git_results = git_dispatcher.sync(git_hooks, root)
263
+
264
+ git_installed = sum(1 for v in git_results.values() if v)
265
+ if git_installed > 0:
266
+ console.print(f"[green] ✓ Synchronized {git_installed} Git hooks[/green]")
267
+ elif git_hooks:
268
+ console.print("[yellow] No Git hooks were successfully synchronized[/yellow]")
269
+
270
+ # 6.3 Sync Agent Hooks using the new ACL-based dispatchers
271
+ agent_hooks = [h for h in all_hooks if h.metadata.type == HookType.AGENT]
272
+
273
+ for provider, dispatcher in agent_dispatchers.items():
274
+ provider_hooks = [h for h in agent_hooks if h.metadata.provider == provider]
275
+ if provider_hooks:
276
+ results = dispatcher.sync(provider_hooks, root)
277
+ success_count = sum(1 for v in results.values() if v)
278
+ if success_count > 0:
279
+ console.print(f"[green] ✓ Synchronized {success_count} agent hooks to {provider}[/green]")
280
+
281
+ except Exception as e:
282
+ console.print(f"[red] Failed to synchronize Universal Hooks: {e}[/red]")
283
+ import traceback
284
+ console.print(f"[dim]{traceback.format_exc()}[/dim]")
285
+
185
286
  # 4. Determine Targets
186
287
  targets = _get_targets(root, config, target)
187
288
 
@@ -299,7 +400,7 @@ def uninstall_command(
299
400
 
300
401
  # 3. Clean up Workflows
301
402
  console.print("[bold blue]Cleaning up distributed workflows...[/bold blue]")
302
-
403
+
303
404
  try:
304
405
  removed_count = skill_manager.cleanup_workflows()
305
406
  if removed_count > 0:
@@ -310,3 +411,97 @@ def uninstall_command(
310
411
  console.print(
311
412
  f"[red] Failed to clean workflows: {e}[/red]"
312
413
  )
414
+
415
+ # 4. Clean up Git Hooks
416
+ console.print("[bold blue]Cleaning up Git Hooks...[/bold blue]")
417
+
418
+ try:
419
+ from monoco.features.hooks.dispatchers import GitHookDispatcher
420
+
421
+ git_dispatcher = GitHookDispatcher()
422
+ installed = git_dispatcher.list_installed(root)
423
+
424
+ uninstalled = 0
425
+ for hook_info in installed:
426
+ if git_dispatcher.uninstall(hook_info["event"], root):
427
+ uninstalled += 1
428
+
429
+ if uninstalled > 0:
430
+ console.print(
431
+ f"[green] ✓ Removed {uninstalled} Git hooks[/green]"
432
+ )
433
+ else:
434
+ console.print("[dim] No Monoco Git hooks to clean up[/dim]")
435
+ except Exception as e:
436
+ console.print(f"[red] Failed to clean Git hooks: {e}[/red]")
437
+
438
+ # 5. Clean up Agent Hooks
439
+ console.print("[bold blue]Cleaning up Agent Hooks...[/bold blue]")
440
+
441
+ try:
442
+ from monoco.features.hooks.dispatchers import (
443
+ ClaudeCodeDispatcher,
444
+ GeminiDispatcher,
445
+ )
446
+
447
+ # Clean up Claude Code hooks
448
+ claude_dispatcher = ClaudeCodeDispatcher()
449
+ claude_settings = claude_dispatcher.get_settings_path(root)
450
+ if claude_settings and claude_settings.exists():
451
+ try:
452
+ import json
453
+ with open(claude_settings, "r", encoding="utf-8") as f:
454
+ settings = json.load(f)
455
+
456
+ if "hooks" in settings:
457
+ original_count = len(settings["hooks"])
458
+ # Remove Monoco-managed hooks
459
+ for event in list(settings["hooks"].keys()):
460
+ configs = settings["hooks"][event]
461
+ if isinstance(configs, list):
462
+ settings["hooks"][event] = [
463
+ c for c in configs if not c.get("_monoco_managed")
464
+ ]
465
+ # Clean up empty events
466
+ settings["hooks"] = {
467
+ k: v for k, v in settings["hooks"].items() if v
468
+ }
469
+
470
+ with open(claude_settings, "w", encoding="utf-8") as f:
471
+ json.dump(settings, f, indent=2, ensure_ascii=False)
472
+
473
+ console.print("[green] ✓ Cleaned up Claude Code hooks[/green]")
474
+ except Exception as e:
475
+ console.print(f"[red] Failed to clean Claude Code hooks: {e}[/red]")
476
+
477
+ # Clean up Gemini CLI hooks
478
+ gemini_dispatcher = GeminiDispatcher()
479
+ gemini_settings = gemini_dispatcher.get_settings_path(root)
480
+ if gemini_settings and gemini_settings.exists():
481
+ try:
482
+ import json
483
+ with open(gemini_settings, "r", encoding="utf-8") as f:
484
+ settings = json.load(f)
485
+
486
+ if "hooks" in settings:
487
+ # Remove Monoco-managed hooks
488
+ for event in list(settings["hooks"].keys()):
489
+ configs = settings["hooks"][event]
490
+ if isinstance(configs, list):
491
+ settings["hooks"][event] = [
492
+ c for c in configs if not c.get("_monoco_managed")
493
+ ]
494
+ # Clean up empty events
495
+ settings["hooks"] = {
496
+ k: v for k, v in settings["hooks"].items() if v
497
+ }
498
+
499
+ with open(gemini_settings, "w", encoding="utf-8") as f:
500
+ json.dump(settings, f, indent=2, ensure_ascii=False)
501
+
502
+ console.print("[green] ✓ Cleaned up Gemini CLI hooks[/green]")
503
+ except Exception as e:
504
+ console.print(f"[red] Failed to clean Gemini CLI hooks: {e}[/red]")
505
+
506
+ except Exception as e:
507
+ console.print(f"[red] Failed to clean Agent hooks: {e}[/red]")
@@ -35,6 +35,7 @@ from .issue import IssueWatcher, IssueFileEvent
35
35
  from .memo import MemoWatcher, MemoFileEvent
36
36
  from .task import TaskWatcher, TaskFileEvent
37
37
  from .dropzone import DropzoneWatcher, DropzoneFileEvent
38
+ from .im import IMWatcher, IMFileEvent, IMInboundWatcher, IMWebhookWatcher
38
39
 
39
40
  __all__ = [
40
41
  # Base classes
@@ -54,4 +55,9 @@ __all__ = [
54
55
  "TaskFileEvent",
55
56
  "DropzoneWatcher",
56
57
  "DropzoneFileEvent",
58
+ # IM watchers (FEAT-0167)
59
+ "IMWatcher",
60
+ "IMFileEvent",
61
+ "IMInboundWatcher",
62
+ "IMWebhookWatcher",
57
63
  ]
@@ -180,6 +180,23 @@ class FilesystemWatcher(ABC):
180
180
  if callback in self._callbacks:
181
181
  self._callbacks.remove(callback)
182
182
 
183
+ def _is_async_callable(self, callback: Callable) -> bool:
184
+ """
185
+ Check if a callable is async (coroutine function or has async __call__).
186
+
187
+ This handles both:
188
+ - Regular async functions: async def func(): ...
189
+ - Callable objects with async __call__: class Handler: async def __call__(self, ...): ...
190
+ """
191
+ # Direct check for coroutine function
192
+ if inspect.iscoroutinefunction(callback):
193
+ return True
194
+ # Check for callable object with async __call__ method
195
+ if hasattr(callback, "__call__") and not inspect.ismethod(callback):
196
+ if inspect.iscoroutinefunction(callback.__call__):
197
+ return True
198
+ return False
199
+
183
200
  async def emit(self, event: FileEvent) -> None:
184
201
  """
185
202
  Emit a file event to all registered callbacks and EventBus.
@@ -190,7 +207,7 @@ class FilesystemWatcher(ABC):
190
207
  # Call local callbacks
191
208
  for callback in self._callbacks:
192
209
  try:
193
- if inspect.iscoroutinefunction(callback):
210
+ if self._is_async_callable(callback):
194
211
  await callback(event)
195
212
  else:
196
213
  callback(event)