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.
- monoco/core/automation/__init__.py +40 -0
- monoco/core/automation/field_watcher.py +296 -0
- monoco/core/automation/handlers.py +805 -0
- monoco/core/config.py +29 -11
- monoco/core/daemon/__init__.py +5 -0
- monoco/core/daemon/pid.py +290 -0
- monoco/core/git.py +15 -0
- monoco/core/hooks/context.py +74 -13
- monoco/core/injection.py +86 -8
- monoco/core/integrations.py +0 -24
- monoco/core/router/__init__.py +17 -0
- monoco/core/router/action.py +202 -0
- monoco/core/scheduler/__init__.py +63 -0
- monoco/core/scheduler/base.py +152 -0
- monoco/core/scheduler/engines.py +175 -0
- monoco/core/scheduler/events.py +197 -0
- monoco/core/scheduler/local.py +377 -0
- monoco/core/setup.py +9 -0
- monoco/core/sync.py +199 -4
- monoco/core/watcher/__init__.py +63 -0
- monoco/core/watcher/base.py +382 -0
- monoco/core/watcher/dropzone.py +152 -0
- monoco/core/watcher/im.py +460 -0
- monoco/core/watcher/issue.py +303 -0
- monoco/core/watcher/memo.py +192 -0
- monoco/core/watcher/task.py +238 -0
- monoco/daemon/app.py +3 -60
- monoco/daemon/commands.py +459 -25
- monoco/daemon/events.py +34 -0
- monoco/daemon/scheduler.py +157 -201
- monoco/daemon/services.py +42 -243
- monoco/features/agent/__init__.py +25 -7
- monoco/features/agent/cli.py +91 -57
- monoco/features/agent/engines.py +31 -170
- monoco/features/agent/resources/en/AGENTS.md +14 -14
- monoco/features/agent/resources/en/skills/monoco_role_engineer/SKILL.md +101 -0
- monoco/features/agent/resources/en/skills/monoco_role_manager/SKILL.md +95 -0
- monoco/features/agent/resources/en/skills/monoco_role_planner/SKILL.md +177 -0
- monoco/features/agent/resources/en/skills/monoco_role_reviewer/SKILL.md +139 -0
- monoco/features/agent/resources/zh/skills/monoco_role_engineer/SKILL.md +101 -0
- monoco/features/agent/resources/zh/skills/monoco_role_manager/SKILL.md +95 -0
- monoco/features/agent/resources/zh/skills/monoco_role_planner/SKILL.md +177 -0
- monoco/features/agent/resources/zh/skills/monoco_role_reviewer/SKILL.md +139 -0
- monoco/features/agent/worker.py +1 -1
- monoco/features/hooks/__init__.py +61 -6
- monoco/features/hooks/commands.py +281 -271
- monoco/features/hooks/dispatchers/__init__.py +23 -0
- monoco/features/hooks/dispatchers/agent_dispatcher.py +486 -0
- monoco/features/hooks/dispatchers/git_dispatcher.py +478 -0
- monoco/features/hooks/manager.py +357 -0
- monoco/features/hooks/models.py +262 -0
- monoco/features/hooks/parser.py +322 -0
- monoco/features/hooks/universal_interceptor.py +503 -0
- monoco/features/im/__init__.py +67 -0
- monoco/features/im/core.py +782 -0
- monoco/features/im/models.py +311 -0
- monoco/features/issue/commands.py +133 -60
- monoco/features/issue/core.py +385 -40
- monoco/features/issue/domain_commands.py +0 -19
- monoco/features/issue/resources/en/AGENTS.md +17 -122
- monoco/features/issue/resources/hooks/agent/before-tool.sh +102 -0
- monoco/features/issue/resources/hooks/agent/session-start.sh +88 -0
- monoco/features/issue/resources/hooks/{post-checkout.sh → git/git-post-checkout.sh} +10 -9
- monoco/features/issue/resources/hooks/git/git-pre-commit.sh +31 -0
- monoco/features/issue/resources/hooks/{pre-push.sh → git/git-pre-push.sh} +7 -13
- monoco/features/issue/resources/zh/AGENTS.md +18 -123
- monoco/features/memo/cli.py +15 -64
- monoco/features/memo/core.py +6 -34
- monoco/features/memo/models.py +24 -15
- monoco/features/memo/resources/en/AGENTS.md +31 -0
- monoco/features/memo/resources/zh/AGENTS.md +28 -5
- monoco/features/spike/commands.py +5 -3
- monoco/main.py +5 -3
- {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/METADATA +1 -1
- monoco_toolkit-0.4.0.dist-info/RECORD +170 -0
- monoco/core/execution.py +0 -67
- monoco/features/agent/apoptosis.py +0 -44
- monoco/features/agent/manager.py +0 -127
- monoco/features/agent/resources/atoms/atom-code-dev.yaml +0 -61
- monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +0 -73
- monoco/features/agent/resources/atoms/atom-knowledge.yaml +0 -55
- monoco/features/agent/resources/atoms/atom-review.yaml +0 -60
- monoco/features/agent/resources/en/skills/monoco_atom_core/SKILL.md +0 -99
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_manager/SKILL.md +0 -93
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_planner/SKILL.md +0 -85
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -114
- monoco/features/agent/resources/workflows/workflow-dev.yaml +0 -83
- monoco/features/agent/resources/workflows/workflow-issue-create.yaml +0 -72
- monoco/features/agent/resources/workflows/workflow-review.yaml +0 -94
- monoco/features/agent/resources/zh/roles/monoco_role_engineer.yaml +0 -49
- monoco/features/agent/resources/zh/roles/monoco_role_manager.yaml +0 -46
- monoco/features/agent/resources/zh/roles/monoco_role_planner.yaml +0 -46
- monoco/features/agent/resources/zh/roles/monoco_role_reviewer.yaml +0 -47
- monoco/features/agent/resources/zh/skills/monoco_atom_core/SKILL.md +0 -99
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_manager/SKILL.md +0 -88
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_planner/SKILL.md +0 -259
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -137
- monoco/features/agent/session.py +0 -169
- monoco/features/artifact/resources/zh/skills/monoco_atom_artifact/SKILL.md +0 -278
- monoco/features/glossary/resources/en/skills/monoco_atom_glossary/SKILL.md +0 -35
- monoco/features/glossary/resources/zh/skills/monoco_atom_glossary/SKILL.md +0 -35
- monoco/features/hooks/adapter.py +0 -67
- monoco/features/hooks/core.py +0 -441
- monoco/features/i18n/resources/en/skills/monoco_atom_i18n/SKILL.md +0 -96
- monoco/features/i18n/resources/en/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
- monoco/features/i18n/resources/zh/skills/monoco_atom_i18n/SKILL.md +0 -96
- monoco/features/i18n/resources/zh/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
- monoco/features/issue/resources/en/skills/monoco_atom_issue/SKILL.md +0 -165
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_development/SKILL.md +0 -224
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_management/SKILL.md +0 -159
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
- monoco/features/issue/resources/hooks/pre-commit.sh +0 -41
- monoco/features/issue/resources/zh/skills/monoco_atom_issue_lifecycle/SKILL.md +0 -190
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_development/SKILL.md +0 -224
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_management/SKILL.md +0 -159
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
- monoco/features/memo/resources/en/skills/monoco_atom_memo/SKILL.md +0 -77
- monoco/features/memo/resources/en/skills/monoco_workflow_note_processing/SKILL.md +0 -140
- monoco/features/memo/resources/zh/skills/monoco_atom_memo/SKILL.md +0 -77
- monoco/features/memo/resources/zh/skills/monoco_workflow_note_processing/SKILL.md +0 -140
- monoco/features/spike/resources/en/skills/monoco_atom_spike/SKILL.md +0 -76
- monoco/features/spike/resources/en/skills/monoco_workflow_research/SKILL.md +0 -121
- monoco/features/spike/resources/zh/skills/monoco_atom_spike/SKILL.md +0 -76
- monoco/features/spike/resources/zh/skills/monoco_workflow_research/SKILL.md +0 -121
- monoco_toolkit-0.3.11.dist-info/RECORD +0 -181
- {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.11.dist-info → monoco_toolkit-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Automation Module - Event-driven automation framework.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Field change detection
|
|
6
|
+
- Independent Event Handlers for Agent collaboration (FEAT-0162)
|
|
7
|
+
|
|
8
|
+
Architecture: No Workflow or Orchestration. Each handler is an independent,
|
|
9
|
+
stateless microservice that responds to specific events. Workflow emerges
|
|
10
|
+
from the natural interaction of handlers.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .field_watcher import (
|
|
14
|
+
YAMLFrontMatterExtractor,
|
|
15
|
+
FieldWatcher,
|
|
16
|
+
FieldCondition,
|
|
17
|
+
)
|
|
18
|
+
from .handlers import (
|
|
19
|
+
TaskFileHandler,
|
|
20
|
+
IssueStageHandler,
|
|
21
|
+
MemoThresholdHandler,
|
|
22
|
+
PRCreatedHandler,
|
|
23
|
+
start_all_handlers,
|
|
24
|
+
stop_all_handlers,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# Field watching
|
|
29
|
+
"YAMLFrontMatterExtractor",
|
|
30
|
+
"FieldWatcher",
|
|
31
|
+
"FieldCondition",
|
|
32
|
+
# Independent Event Handlers (FEAT-0162)
|
|
33
|
+
"TaskFileHandler",
|
|
34
|
+
"IssueStageHandler",
|
|
35
|
+
"MemoThresholdHandler",
|
|
36
|
+
"PRCreatedHandler",
|
|
37
|
+
# Convenience functions
|
|
38
|
+
"start_all_handlers",
|
|
39
|
+
"stop_all_handlers",
|
|
40
|
+
]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Field Watcher - YAML Front Matter field change detection.
|
|
3
|
+
|
|
4
|
+
Part of the Event Automation Framework.
|
|
5
|
+
Provides field-level change detection for Markdown files with YAML Front Matter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
15
|
+
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
from monoco.core.watcher.base import FieldChange, ChangeType
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class FieldCondition:
|
|
25
|
+
"""
|
|
26
|
+
Condition for field value matching.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
field: Field name to check
|
|
30
|
+
operator: Comparison operator (eq, ne, gt, lt, gte, lte, in, contains)
|
|
31
|
+
value: Expected value
|
|
32
|
+
"""
|
|
33
|
+
field: str
|
|
34
|
+
operator: str # eq, ne, gt, lt, gte, lte, in, contains
|
|
35
|
+
value: Any
|
|
36
|
+
|
|
37
|
+
OPERATORS = {
|
|
38
|
+
"eq": lambda a, b: a == b,
|
|
39
|
+
"ne": lambda a, b: a != b,
|
|
40
|
+
"gt": lambda a, b: a is not None and b is not None and a > b,
|
|
41
|
+
"lt": lambda a, b: a is not None and b is not None and a < b,
|
|
42
|
+
"gte": lambda a, b: a is not None and b is not None and a >= b,
|
|
43
|
+
"lte": lambda a, b: a is not None and b is not None and a <= b,
|
|
44
|
+
"in": lambda a, b: a in b if b is not None else False,
|
|
45
|
+
"contains": lambda a, b: b in a if a is not None else False,
|
|
46
|
+
"exists": lambda a, b: a is not None,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def evaluate(self, data: Dict[str, Any]) -> bool:
|
|
50
|
+
"""Evaluate condition against data."""
|
|
51
|
+
actual_value = data.get(self.field)
|
|
52
|
+
|
|
53
|
+
op_func = self.OPERATORS.get(self.operator)
|
|
54
|
+
if not op_func:
|
|
55
|
+
logger.warning(f"Unknown operator: {self.operator}")
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
return op_func(actual_value, self.value)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.debug(f"Condition evaluation failed: {e}")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class YAMLFrontMatterExtractor:
|
|
66
|
+
"""
|
|
67
|
+
Extracts YAML Front Matter from Markdown files.
|
|
68
|
+
|
|
69
|
+
Provides methods to:
|
|
70
|
+
- Parse YAML Front Matter from content
|
|
71
|
+
- Extract specific fields
|
|
72
|
+
- Detect field changes between versions
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
FRONTMATTER_PATTERN = re.compile(
|
|
76
|
+
r'^---\s*\n(.*?)\n---\s*\n',
|
|
77
|
+
re.MULTILINE | re.DOTALL,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def extract(cls, content: str) -> Optional[Dict[str, Any]]:
|
|
82
|
+
"""
|
|
83
|
+
Extract YAML Front Matter from markdown content.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
content: Markdown file content
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dict of front matter fields, or None if not found
|
|
90
|
+
"""
|
|
91
|
+
match = cls.FRONTMATTER_PATTERN.match(content)
|
|
92
|
+
if not match:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
yaml_content = match.group(1)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
return yaml.safe_load(yaml_content) or {}
|
|
99
|
+
except yaml.YAMLError as e:
|
|
100
|
+
logger.warning(f"Failed to parse YAML front matter: {e}")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def extract_from_file(cls, file_path: Path) -> Optional[Dict[str, Any]]:
|
|
105
|
+
"""Extract YAML Front Matter from a file."""
|
|
106
|
+
try:
|
|
107
|
+
content = file_path.read_text(encoding="utf-8")
|
|
108
|
+
return cls.extract(content)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.debug(f"Could not read {file_path}: {e}")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def get_field(cls, content: str, field_name: str) -> Any:
|
|
115
|
+
"""Get a specific field from front matter."""
|
|
116
|
+
frontmatter = cls.extract(content)
|
|
117
|
+
if frontmatter is None:
|
|
118
|
+
return None
|
|
119
|
+
return frontmatter.get(field_name)
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def detect_changes(
|
|
123
|
+
cls,
|
|
124
|
+
old_content: str,
|
|
125
|
+
new_content: str,
|
|
126
|
+
tracked_fields: Optional[List[str]] = None,
|
|
127
|
+
) -> List[FieldChange]:
|
|
128
|
+
"""
|
|
129
|
+
Detect changes in front matter fields.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
old_content: Previous file content
|
|
133
|
+
new_content: Current file content
|
|
134
|
+
tracked_fields: List of fields to track (None = all)
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
List of FieldChange objects
|
|
138
|
+
"""
|
|
139
|
+
old_fm = cls.extract(old_content) or {}
|
|
140
|
+
new_fm = cls.extract(new_content) or {}
|
|
141
|
+
|
|
142
|
+
changes = []
|
|
143
|
+
|
|
144
|
+
# Determine which fields to check
|
|
145
|
+
if tracked_fields:
|
|
146
|
+
fields_to_check = tracked_fields
|
|
147
|
+
else:
|
|
148
|
+
fields_to_check = list(set(old_fm.keys()) | set(new_fm.keys()))
|
|
149
|
+
|
|
150
|
+
for field_name in fields_to_check:
|
|
151
|
+
old_value = old_fm.get(field_name)
|
|
152
|
+
new_value = new_fm.get(field_name)
|
|
153
|
+
|
|
154
|
+
if old_value != new_value:
|
|
155
|
+
# Determine change type
|
|
156
|
+
if old_value is None and new_value is not None:
|
|
157
|
+
change_type = ChangeType.CREATED
|
|
158
|
+
elif old_value is not None and new_value is None:
|
|
159
|
+
change_type = ChangeType.DELETED
|
|
160
|
+
else:
|
|
161
|
+
change_type = ChangeType.MODIFIED
|
|
162
|
+
|
|
163
|
+
changes.append(FieldChange(
|
|
164
|
+
field_name=field_name,
|
|
165
|
+
old_value=old_value,
|
|
166
|
+
new_value=new_value,
|
|
167
|
+
change_type=change_type,
|
|
168
|
+
))
|
|
169
|
+
|
|
170
|
+
return changes
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class FieldWatcher:
|
|
174
|
+
"""
|
|
175
|
+
Watches specific fields for changes and triggers conditions.
|
|
176
|
+
|
|
177
|
+
Maintains a cache of field values and emits events when:
|
|
178
|
+
- Fields change
|
|
179
|
+
- Conditions are met
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> watcher = FieldWatcher(
|
|
183
|
+
... tracked_fields=["status", "stage"],
|
|
184
|
+
... )
|
|
185
|
+
>>>
|
|
186
|
+
>>> # Add a condition
|
|
187
|
+
>>> watcher.add_condition(FieldCondition(
|
|
188
|
+
... field="stage",
|
|
189
|
+
... operator="eq",
|
|
190
|
+
... value="doing",
|
|
191
|
+
... ))
|
|
192
|
+
>>>
|
|
193
|
+
>>> # Check file
|
|
194
|
+
>>> changes = watcher.check_file(path, content)
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(
|
|
198
|
+
self,
|
|
199
|
+
tracked_fields: Optional[List[str]] = None,
|
|
200
|
+
):
|
|
201
|
+
self.tracked_fields = tracked_fields
|
|
202
|
+
self._field_cache: Dict[str, Dict[str, Any]] = {} # file_path -> field_values
|
|
203
|
+
self._conditions: List[FieldCondition] = []
|
|
204
|
+
self._condition_callbacks: List[Callable[[str, FieldCondition, Dict[str, Any]], None]] = []
|
|
205
|
+
|
|
206
|
+
def add_condition(self, condition: FieldCondition) -> None:
|
|
207
|
+
"""Add a condition to watch for."""
|
|
208
|
+
self._conditions.append(condition)
|
|
209
|
+
|
|
210
|
+
def add_callback(
|
|
211
|
+
self,
|
|
212
|
+
callback: Callable[[str, FieldCondition, Dict[str, Any]], None],
|
|
213
|
+
) -> None:
|
|
214
|
+
"""Add a callback for when conditions are met."""
|
|
215
|
+
self._condition_callbacks.append(callback)
|
|
216
|
+
|
|
217
|
+
def check_file(
|
|
218
|
+
self,
|
|
219
|
+
file_path: Union[str, Path],
|
|
220
|
+
content: str,
|
|
221
|
+
) -> List[FieldChange]:
|
|
222
|
+
"""
|
|
223
|
+
Check a file for field changes.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
file_path: Path to the file
|
|
227
|
+
content: Current file content
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
List of field changes
|
|
231
|
+
"""
|
|
232
|
+
path_key = str(file_path)
|
|
233
|
+
|
|
234
|
+
# Extract current fields
|
|
235
|
+
current_fm = YAMLFrontMatterExtractor.extract(content) or {}
|
|
236
|
+
|
|
237
|
+
if self.tracked_fields:
|
|
238
|
+
current_fields = {
|
|
239
|
+
f: current_fm.get(f)
|
|
240
|
+
for f in self.tracked_fields
|
|
241
|
+
}
|
|
242
|
+
else:
|
|
243
|
+
current_fields = current_fm
|
|
244
|
+
|
|
245
|
+
# Get cached fields
|
|
246
|
+
cached_fields = self._field_cache.get(path_key, {})
|
|
247
|
+
|
|
248
|
+
# Detect changes
|
|
249
|
+
changes = []
|
|
250
|
+
for field_name, new_value in current_fields.items():
|
|
251
|
+
old_value = cached_fields.get(field_name)
|
|
252
|
+
if old_value != new_value:
|
|
253
|
+
changes.append(FieldChange(
|
|
254
|
+
field_name=field_name,
|
|
255
|
+
old_value=old_value,
|
|
256
|
+
new_value=new_value,
|
|
257
|
+
change_type=ChangeType.MODIFIED if old_value is not None else ChangeType.CREATED,
|
|
258
|
+
))
|
|
259
|
+
|
|
260
|
+
# Update cache
|
|
261
|
+
self._field_cache[path_key] = current_fields
|
|
262
|
+
|
|
263
|
+
# Check conditions
|
|
264
|
+
if changes:
|
|
265
|
+
self._check_conditions(path_key, current_fields)
|
|
266
|
+
|
|
267
|
+
return changes
|
|
268
|
+
|
|
269
|
+
def _check_conditions(self, file_path: str, fields: Dict[str, Any]) -> None:
|
|
270
|
+
"""Check if any conditions are met."""
|
|
271
|
+
for condition in self._conditions:
|
|
272
|
+
if condition.evaluate(fields):
|
|
273
|
+
for callback in self._condition_callbacks:
|
|
274
|
+
try:
|
|
275
|
+
callback(file_path, condition, fields)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logger.error(f"Condition callback error: {e}")
|
|
278
|
+
|
|
279
|
+
def get_cached_fields(self, file_path: Union[str, Path]) -> Optional[Dict[str, Any]]:
|
|
280
|
+
"""Get cached fields for a file."""
|
|
281
|
+
return self._field_cache.get(str(file_path))
|
|
282
|
+
|
|
283
|
+
def clear_cache(self, file_path: Optional[Union[str, Path]] = None) -> None:
|
|
284
|
+
"""Clear the field cache."""
|
|
285
|
+
if file_path:
|
|
286
|
+
self._field_cache.pop(str(file_path), None)
|
|
287
|
+
else:
|
|
288
|
+
self._field_cache.clear()
|
|
289
|
+
|
|
290
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
291
|
+
"""Get watcher statistics."""
|
|
292
|
+
return {
|
|
293
|
+
"tracked_files": len(self._field_cache),
|
|
294
|
+
"tracked_fields": self.tracked_fields,
|
|
295
|
+
"conditions": len(self._conditions),
|
|
296
|
+
}
|