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,322 @@
1
+ """
2
+ Universal Hooks: Front Matter Parser
3
+
4
+ Parses YAML Front Matter from hook scripts with support for multiple
5
+ comment styles and languages.
6
+ """
7
+
8
+ import re
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ import yaml
14
+
15
+ from .models import HookMetadata, ParsedHook
16
+
17
+
18
+ @dataclass
19
+ class ParseError:
20
+ """Error information from parsing a hook script."""
21
+
22
+ path: Path
23
+ line_number: int
24
+ message: str
25
+ raw_content: Optional[str] = None
26
+
27
+
28
+ class HookParser:
29
+ """
30
+ Parser for extracting YAML Front Matter from hook scripts.
31
+
32
+ Supports multiple comment styles:
33
+ - Shell/Python/Ruby: `# ---` / `# ---`
34
+ - JavaScript/TypeScript/C/C++/Java/Rust/Go: `// ---` / `// ---`
35
+ - Lua/SQL/Haskell: `-- ---` / `-- ---`
36
+ - HTML/XML: `<!-- ---` / `--- -->`
37
+
38
+ The parser detects the comment style automatically based on file extension
39
+ or the first line of the script.
40
+ """
41
+
42
+ # Comment style definitions: (prefix, suffix, file_extensions)
43
+ COMMENT_STYLES = {
44
+ "shell": ("#", "", {"sh", "bash", "zsh", "fish", "py", "rb", "pl", "r", "ps1", "Makefile", "Dockerfile"}),
45
+ "c_style": ("//", "", {"js", "ts", "jsx", "tsx", "c", "cpp", "cc", "cxx", "h", "hpp", "java", "cs", "go", "rs", "swift", "kt", "scala", "php", "dart"}),
46
+ "double_dash": ("--", "", {"lua", "sql", "hs", "lhs", "elm", "sql", "mysql", "pgsql"}),
47
+ "html": ("<!--", "-->", {"html", "htm", "xml", "svg", "vue", "svelte"}),
48
+ }
49
+
50
+ # Regex patterns for front matter detection
51
+ FRONT_MATTER_DELIMITER = "---"
52
+
53
+ def __init__(self):
54
+ """Initialize the parser."""
55
+ self.errors: list[ParseError] = []
56
+
57
+ def _detect_comment_style(self, path: Path, first_line: str) -> tuple[str, str]:
58
+ """
59
+ Detect the comment style for a file.
60
+
61
+ Args:
62
+ path: Path to the script file
63
+ first_line: First line of the file content
64
+
65
+ Returns:
66
+ Tuple of (prefix, suffix) for the comment style
67
+ """
68
+ # First, try to detect from file extension
69
+ ext = path.suffix.lstrip(".").lower()
70
+ name = path.name.lower()
71
+
72
+ for style_name, (prefix, suffix, extensions) in self.COMMENT_STYLES.items():
73
+ if ext in extensions or name in extensions:
74
+ return (prefix, suffix)
75
+
76
+ # If no extension match, try to detect from shebang or first line
77
+ if first_line.startswith("#!/"):
78
+ # Detect from shebang
79
+ shebang = first_line.lower()
80
+ if any(shell in shebang for shell in ["bash", "sh", "zsh", "python", "ruby", "perl"]):
81
+ return ("#", "")
82
+ if "node" in shebang or "deno" in shebang or "bun" in shebang:
83
+ return ("//", "")
84
+ if "lua" in shebang:
85
+ return ("--", "")
86
+
87
+ # Default to shell style if uncertain
88
+ return ("#", "")
89
+
90
+ def _strip_comment_prefix(self, line: str, prefix: str, suffix: str) -> str:
91
+ """
92
+ Remove comment prefix and suffix from a line.
93
+
94
+ Args:
95
+ line: The line to process
96
+ prefix: The comment prefix (e.g., "#", "//", "--")
97
+ suffix: The comment suffix (e.g., "-->")
98
+
99
+ Returns:
100
+ The line with comment markers stripped
101
+ """
102
+ # Remove leading/trailing whitespace first
103
+ stripped = line.strip()
104
+
105
+ # Remove prefix
106
+ if prefix and stripped.startswith(prefix):
107
+ stripped = stripped[len(prefix):]
108
+
109
+ # Remove suffix
110
+ if suffix and stripped.endswith(suffix):
111
+ stripped = stripped[:-len(suffix)]
112
+
113
+ return stripped.strip()
114
+
115
+ def _extract_front_matter_lines(
116
+ self,
117
+ lines: list[str],
118
+ prefix: str,
119
+ suffix: str,
120
+ ) -> Optional[tuple[list[str], int, int]]:
121
+ """
122
+ Extract front matter lines from script content.
123
+
124
+ Args:
125
+ lines: All lines of the script
126
+ prefix: Comment prefix
127
+ suffix: Comment suffix
128
+
129
+ Returns:
130
+ Tuple of (front_matter_lines, start_line, end_line) or None if no front matter
131
+ """
132
+ if not lines:
133
+ return None
134
+
135
+ start_idx = 0
136
+
137
+ # Skip shebang line if present
138
+ if lines[0].startswith("#!/"):
139
+ start_idx = 1
140
+
141
+ # Look for opening delimiter
142
+ opening_line_idx = None
143
+ for i in range(start_idx, len(lines)):
144
+ stripped = self._strip_comment_prefix(lines[i], prefix, suffix)
145
+ if stripped == self.FRONT_MATTER_DELIMITER:
146
+ opening_line_idx = i
147
+ break
148
+
149
+ if opening_line_idx is None:
150
+ return None
151
+
152
+ # Look for closing delimiter
153
+ closing_line_idx = None
154
+ for i in range(opening_line_idx + 1, len(lines)):
155
+ stripped = self._strip_comment_prefix(lines[i], prefix, suffix)
156
+ if stripped == self.FRONT_MATTER_DELIMITER:
157
+ closing_line_idx = i
158
+ break
159
+
160
+ if closing_line_idx is None:
161
+ return None
162
+
163
+ # Extract front matter lines (between delimiters)
164
+ front_matter_lines = []
165
+ for i in range(opening_line_idx + 1, closing_line_idx):
166
+ stripped = self._strip_comment_prefix(lines[i], prefix, suffix)
167
+ front_matter_lines.append(stripped)
168
+
169
+ # Line numbers are 1-based for error reporting
170
+ return (
171
+ front_matter_lines,
172
+ opening_line_idx + 1,
173
+ closing_line_idx + 1,
174
+ )
175
+
176
+ def parse_file(self, path: Path) -> Optional[ParsedHook]:
177
+ """
178
+ Parse a hook script file and extract its metadata.
179
+
180
+ Args:
181
+ path: Path to the hook script
182
+
183
+ Returns:
184
+ ParsedHook if successful, None if parsing fails
185
+ """
186
+ try:
187
+ content = path.read_text(encoding="utf-8")
188
+ except Exception as e:
189
+ self.errors.append(ParseError(
190
+ path=path,
191
+ line_number=0,
192
+ message=f"Failed to read file: {e}",
193
+ ))
194
+ return None
195
+
196
+ return self.parse_content(path, content)
197
+
198
+ def parse_content(self, path: Path, content: str) -> Optional[ParsedHook]:
199
+ """
200
+ Parse hook content and extract metadata.
201
+
202
+ Args:
203
+ path: Path to the hook script (for error reporting and style detection)
204
+ content: The script content
205
+
206
+ Returns:
207
+ ParsedHook if successful, None if no valid front matter or parsing fails
208
+ """
209
+ lines = content.splitlines()
210
+
211
+ if not lines:
212
+ self.errors.append(ParseError(
213
+ path=path,
214
+ line_number=0,
215
+ message="Empty file",
216
+ ))
217
+ return None
218
+
219
+ # Detect comment style
220
+ first_line = lines[0] if lines else ""
221
+ prefix, suffix = self._detect_comment_style(path, first_line)
222
+
223
+ # Extract front matter
224
+ result = self._extract_front_matter_lines(lines, prefix, suffix)
225
+ if result is None:
226
+ # No front matter found - this is OK, just return None
227
+ return None
228
+
229
+ front_matter_lines, start_line, end_line = result
230
+ yaml_content = "\n".join(front_matter_lines)
231
+
232
+ # Parse YAML
233
+ try:
234
+ data = yaml.safe_load(yaml_content)
235
+ except yaml.YAMLError as e:
236
+ # Try to extract line number from error
237
+ line_no = getattr(e, 'problem_mark', None)
238
+ if line_no:
239
+ actual_line = start_line + line_no.line + 1
240
+ else:
241
+ actual_line = start_line
242
+
243
+ self.errors.append(ParseError(
244
+ path=path,
245
+ line_number=actual_line,
246
+ message=f"YAML parsing error: {e}",
247
+ raw_content=yaml_content,
248
+ ))
249
+ return None
250
+
251
+ if not isinstance(data, dict):
252
+ self.errors.append(ParseError(
253
+ path=path,
254
+ line_number=start_line,
255
+ message="Front matter must be a YAML mapping (key: value)",
256
+ raw_content=yaml_content,
257
+ ))
258
+ return None
259
+
260
+ # Parse metadata using Pydantic
261
+ try:
262
+ metadata = HookMetadata.model_validate(data)
263
+ except Exception as e:
264
+ self.errors.append(ParseError(
265
+ path=path,
266
+ line_number=start_line,
267
+ message=f"Metadata validation error: {e}",
268
+ raw_content=yaml_content,
269
+ ))
270
+ return None
271
+
272
+ return ParsedHook(
273
+ metadata=metadata,
274
+ script_path=path,
275
+ content=content,
276
+ front_matter_start_line=start_line,
277
+ front_matter_end_line=end_line,
278
+ )
279
+
280
+ def parse_directory(
281
+ self,
282
+ directory: Path,
283
+ pattern: str = "*",
284
+ ) -> list[ParsedHook]:
285
+ """
286
+ Parse all hook scripts in a directory.
287
+
288
+ Args:
289
+ directory: Directory to scan
290
+ pattern: Glob pattern for matching files (default: "*")
291
+
292
+ Returns:
293
+ List of successfully parsed hooks
294
+ """
295
+ parsed_hooks: list[ParsedHook] = []
296
+
297
+ if not directory.exists():
298
+ return parsed_hooks
299
+
300
+ for file_path in directory.rglob(pattern):
301
+ if not file_path.is_file():
302
+ continue
303
+
304
+ # Skip hidden files and common non-script files
305
+ if file_path.name.startswith("."):
306
+ continue
307
+ if file_path.suffix.lower() in {".md",".txt",".json",".lock"}:
308
+ continue
309
+
310
+ hook = self.parse_file(file_path)
311
+ if hook is not None:
312
+ parsed_hooks.append(hook)
313
+
314
+ return parsed_hooks
315
+
316
+ def get_errors(self) -> list[ParseError]:
317
+ """Get all parsing errors encountered."""
318
+ return self.errors.copy()
319
+
320
+ def clear_errors(self) -> None:
321
+ """Clear the error list."""
322
+ self.errors.clear()