monoco-toolkit 0.3.6__py3-none-any.whl → 0.3.10__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 (113) hide show
  1. monoco/cli/workspace.py +1 -1
  2. monoco/core/config.py +58 -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/injection.py +63 -29
  11. monoco/core/integrations.py +8 -2
  12. monoco/core/output.py +5 -5
  13. monoco/core/registry.py +9 -1
  14. monoco/core/resource/__init__.py +5 -0
  15. monoco/core/resource/finder.py +98 -0
  16. monoco/core/resource/manager.py +91 -0
  17. monoco/core/resource/models.py +35 -0
  18. monoco/core/resources/en/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
  19. monoco/core/resources/zh/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
  20. monoco/core/setup.py +1 -1
  21. monoco/core/skill_framework.py +292 -0
  22. monoco/core/skills.py +538 -254
  23. monoco/core/sync.py +73 -1
  24. monoco/core/workflow_converter.py +420 -0
  25. monoco/features/{scheduler → agent}/__init__.py +5 -3
  26. monoco/features/agent/adapter.py +31 -0
  27. monoco/features/agent/apoptosis.py +44 -0
  28. monoco/features/agent/cli.py +296 -0
  29. monoco/features/agent/config.py +96 -0
  30. monoco/features/agent/defaults.py +12 -0
  31. monoco/features/{scheduler → agent}/engines.py +32 -6
  32. monoco/features/agent/flow_skills.py +281 -0
  33. monoco/features/agent/manager.py +91 -0
  34. monoco/features/{scheduler → agent}/models.py +6 -3
  35. monoco/features/agent/resources/atoms/atom-code-dev.yaml +61 -0
  36. monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +73 -0
  37. monoco/features/agent/resources/atoms/atom-knowledge.yaml +55 -0
  38. monoco/features/agent/resources/atoms/atom-review.yaml +60 -0
  39. monoco/features/agent/resources/en/skills/flow_engineer/SKILL.md +94 -0
  40. monoco/features/agent/resources/en/skills/flow_manager/SKILL.md +93 -0
  41. monoco/features/agent/resources/en/skills/flow_planner/SKILL.md +85 -0
  42. monoco/features/agent/resources/en/skills/flow_reviewer/SKILL.md +114 -0
  43. monoco/features/agent/resources/roles/role-engineer.yaml +49 -0
  44. monoco/features/agent/resources/roles/role-manager.yaml +46 -0
  45. monoco/features/agent/resources/roles/role-planner.yaml +46 -0
  46. monoco/features/agent/resources/roles/role-reviewer.yaml +47 -0
  47. monoco/features/agent/resources/workflows/workflow-dev.yaml +83 -0
  48. monoco/features/agent/resources/workflows/workflow-issue-create.yaml +72 -0
  49. monoco/features/agent/resources/workflows/workflow-review.yaml +94 -0
  50. monoco/features/agent/resources/zh/skills/flow_engineer/SKILL.md +94 -0
  51. monoco/features/agent/resources/zh/skills/flow_manager/SKILL.md +88 -0
  52. monoco/features/agent/resources/zh/skills/flow_planner/SKILL.md +259 -0
  53. monoco/features/agent/resources/zh/skills/flow_reviewer/SKILL.md +137 -0
  54. monoco/features/{scheduler → agent}/session.py +36 -1
  55. monoco/features/{scheduler → agent}/worker.py +40 -4
  56. monoco/features/glossary/adapter.py +31 -0
  57. monoco/features/glossary/config.py +5 -0
  58. monoco/features/glossary/resources/en/AGENTS.md +29 -0
  59. monoco/features/glossary/resources/en/skills/monoco_glossary/SKILL.md +35 -0
  60. monoco/features/glossary/resources/zh/AGENTS.md +29 -0
  61. monoco/features/glossary/resources/zh/skills/monoco_glossary/SKILL.md +35 -0
  62. monoco/features/i18n/resources/en/skills/i18n_scan_workflow/SKILL.md +105 -0
  63. monoco/features/i18n/resources/en/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
  64. monoco/features/i18n/resources/zh/skills/i18n_scan_workflow/SKILL.md +105 -0
  65. monoco/features/i18n/resources/zh/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
  66. monoco/features/issue/commands.py +427 -21
  67. monoco/features/issue/core.py +140 -1
  68. monoco/features/issue/criticality.py +553 -0
  69. monoco/features/issue/domain/models.py +28 -2
  70. monoco/features/issue/engine/machine.py +75 -15
  71. monoco/features/issue/git_service.py +185 -0
  72. monoco/features/issue/linter.py +291 -62
  73. monoco/features/issue/models.py +50 -2
  74. monoco/features/issue/resources/en/skills/issue_create_workflow/SKILL.md +167 -0
  75. monoco/features/issue/resources/en/skills/issue_develop_workflow/SKILL.md +224 -0
  76. monoco/features/issue/resources/en/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  77. monoco/features/issue/resources/en/skills/issue_refine_workflow/SKILL.md +203 -0
  78. monoco/features/issue/resources/en/{SKILL.md → skills/monoco_issue/SKILL.md} +50 -0
  79. monoco/features/issue/resources/zh/skills/issue_create_workflow/SKILL.md +167 -0
  80. monoco/features/issue/resources/zh/skills/issue_develop_workflow/SKILL.md +224 -0
  81. monoco/features/issue/resources/zh/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  82. monoco/features/issue/resources/zh/skills/issue_refine_workflow/SKILL.md +203 -0
  83. monoco/features/issue/resources/zh/{SKILL.md → skills/monoco_issue/SKILL.md} +52 -0
  84. monoco/features/issue/validator.py +185 -65
  85. monoco/features/memo/__init__.py +2 -1
  86. monoco/features/memo/adapter.py +32 -0
  87. monoco/features/memo/cli.py +36 -14
  88. monoco/features/memo/core.py +59 -0
  89. monoco/features/memo/resources/en/skills/monoco_memo/SKILL.md +77 -0
  90. monoco/features/memo/resources/en/skills/note_processing_workflow/SKILL.md +140 -0
  91. monoco/features/memo/resources/zh/AGENTS.md +8 -0
  92. monoco/features/memo/resources/zh/skills/monoco_memo/SKILL.md +77 -0
  93. monoco/features/memo/resources/zh/skills/note_processing_workflow/SKILL.md +140 -0
  94. monoco/features/spike/resources/en/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
  95. monoco/features/spike/resources/en/skills/research_workflow/SKILL.md +121 -0
  96. monoco/features/spike/resources/zh/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
  97. monoco/features/spike/resources/zh/skills/research_workflow/SKILL.md +121 -0
  98. monoco/main.py +2 -3
  99. monoco_toolkit-0.3.10.dist-info/METADATA +124 -0
  100. monoco_toolkit-0.3.10.dist-info/RECORD +156 -0
  101. monoco/features/scheduler/cli.py +0 -285
  102. monoco/features/scheduler/config.py +0 -68
  103. monoco/features/scheduler/defaults.py +0 -54
  104. monoco/features/scheduler/manager.py +0 -49
  105. monoco/features/scheduler/reliability.py +0 -106
  106. monoco/features/skills/core.py +0 -102
  107. monoco_toolkit-0.3.6.dist-info/METADATA +0 -127
  108. monoco_toolkit-0.3.6.dist-info/RECORD +0 -97
  109. /monoco/core/{hooks.py → githooks.py} +0 -0
  110. /monoco/features/{skills → glossary}/__init__.py +0 -0
  111. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/WHEEL +0 -0
  112. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/entry_points.txt +0 -0
  113. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,11 @@
1
1
  from typing import List, Optional, Dict
2
2
  from monoco.core.config import IssueSchemaConfig, TransitionConfig
3
3
  from ..models import IssueMetadata
4
+ from ..criticality import (
5
+ CriticalityLevel,
6
+ PolicyResolver,
7
+ HumanReviewLevel,
8
+ )
4
9
 
5
10
 
6
11
  class StateMachine:
@@ -128,12 +133,15 @@ class StateMachine:
128
133
  to_status: str,
129
134
  to_stage: Optional[str],
130
135
  solution: Optional[str] = None,
131
- ) -> None:
136
+ meta: Optional[IssueMetadata] = None,
137
+ ) -> Optional[TransitionConfig]:
132
138
  """
133
139
  Validate if a transition is allowed. Raises ValueError if not.
140
+ If meta is provided, also validates criticality-based policies.
141
+ Returns the TransitionConfig if a transition occurred, None if no change.
134
142
  """
135
143
  if from_status == to_status and from_stage == to_stage:
136
- return # No change is always allowed (unless we want to enforce specific updates)
144
+ return None # No change is always allowed (unless we want to enforce specific updates)
137
145
 
138
146
  transition = self.find_transition(
139
147
  from_status, from_stage, to_status, to_stage, solution
@@ -150,28 +158,74 @@ class StateMachine:
150
158
  f"Lifecycle Policy: Transition '{transition.label}' requires solution '{transition.required_solution}'."
151
159
  )
152
160
 
153
- def enforce_policy(self, meta: IssueMetadata) -> None:
161
+ # Criticality-based policy checks
162
+ if meta and meta.criticality:
163
+ self._validate_criticality_policy(meta, from_stage, to_stage)
164
+
165
+ return transition
166
+
167
+ def _validate_criticality_policy(
168
+ self,
169
+ meta: IssueMetadata,
170
+ from_stage: Optional[str],
171
+ to_stage: Optional[str],
172
+ ) -> None:
154
173
  """
155
- Apply consistency rules to IssueMetadata.
174
+ Validate transition against criticality-based policies.
175
+ Enforces stricter requirements for high/critical issues.
156
176
  """
157
- from ..models import current_time
177
+ policy = PolicyResolver.resolve(meta.criticality)
178
+
179
+ # Submit to Review: Enforce agent review for medium+
180
+ if to_stage == "review" and from_stage == "doing":
181
+ if meta.criticality >= CriticalityLevel.MEDIUM:
182
+ # For medium+, agent review is mandatory
183
+ # This is enforced by the policy, but we can't check actual review status here
184
+ # The check is informational - actual enforcement happens in submit command
185
+ pass
186
+
187
+ # Close/Accept: Enforce human review for high/critical
188
+ if to_stage == "done":
189
+ if policy.human_review in [
190
+ HumanReviewLevel.REQUIRED,
191
+ HumanReviewLevel.REQUIRED_RECORD,
192
+ ]:
193
+ # For high/critical, human review is mandatory before closing
194
+ # Actual enforcement would check review comments section
195
+ pass
196
+
197
+ def check_policy_compliance(self, meta: IssueMetadata) -> List[str]:
198
+ """
199
+ Check if an issue complies with its criticality policy.
200
+ Returns list of policy violations.
201
+ """
202
+ if not meta.criticality:
203
+ return []
158
204
 
159
- if meta.status == "backlog":
160
- meta.stage = "freezed"
205
+ violations = []
206
+ policy = PolicyResolver.resolve(meta.criticality)
161
207
 
162
- elif meta.status == "closed":
163
- if meta.stage != "done":
164
- meta.stage = "done"
165
- if not meta.closed_at:
166
- meta.closed_at = current_time()
208
+ # Stage-based checks
209
+ if meta.stage == "review":
210
+ # In review stage, check coverage requirement
211
+ # Note: Actual coverage check would require external data
212
+ pass
167
213
 
168
- elif meta.status == "open":
169
- if meta.stage is None:
170
- meta.stage = "draft"
214
+ if meta.stage == "done" or meta.status == "closed":
215
+ # For high/critical, require review comments
216
+ if policy.human_review in [
217
+ HumanReviewLevel.REQUIRED,
218
+ HumanReviewLevel.REQUIRED_RECORD,
219
+ ]:
220
+ # This is a simplified check - full implementation would parse body
221
+ pass
222
+
223
+ return violations
171
224
 
172
225
  def enforce_policy(self, meta: IssueMetadata) -> None:
173
226
  """
174
227
  Apply consistency rules to IssueMetadata.
228
+ Includes criticality-based defaults.
175
229
  """
176
230
  from ..models import current_time
177
231
 
@@ -187,3 +241,9 @@ class StateMachine:
187
241
  elif meta.status == "open":
188
242
  if meta.stage is None:
189
243
  meta.stage = "draft"
244
+
245
+ # Set default criticality if not set
246
+ if meta.criticality is None:
247
+ from ..criticality import CriticalityTypeMapping
248
+
249
+ meta.criticality = CriticalityTypeMapping.get_default(meta.type.value)
@@ -0,0 +1,185 @@
1
+ """
2
+ Git service for Issue operations.
3
+
4
+ Provides atomic commit functionality for issue file changes,
5
+ ensuring task state transitions are properly tracked in git history.
6
+ """
7
+
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import Optional, List, Tuple
11
+ from dataclasses import dataclass
12
+
13
+ from monoco.core import git
14
+
15
+ logger = logging.getLogger("monoco.features.issue.git_service")
16
+
17
+
18
+ @dataclass
19
+ class CommitResult:
20
+ """Result of a commit operation."""
21
+
22
+ success: bool
23
+ commit_hash: Optional[str] = None
24
+ message: Optional[str] = None
25
+ error: Optional[str] = None
26
+
27
+
28
+ class IssueGitService:
29
+ """
30
+ Service for handling git operations related to Issue files.
31
+
32
+ Responsibilities:
33
+ - Detect if current directory is in a git repository
34
+ - Stage specific issue files
35
+ - Generate atomic commit messages for issue transitions
36
+ - Handle graceful degradation when not in a git repo
37
+ """
38
+
39
+ def __init__(self, project_root: Path):
40
+ self.project_root = project_root
41
+ self._is_git_repo: Optional[bool] = None
42
+
43
+ def is_git_repository(self) -> bool:
44
+ """Check if the project root is inside a git repository."""
45
+ if self._is_git_repo is None:
46
+ self._is_git_repo = git.is_git_repo(self.project_root)
47
+ return self._is_git_repo
48
+
49
+ def commit_issue_change(
50
+ self,
51
+ issue_id: str,
52
+ action: str,
53
+ issue_file_path: Path,
54
+ old_file_path: Optional[Path] = None,
55
+ no_commit: bool = False,
56
+ ) -> CommitResult:
57
+ """
58
+ Atomically commit an issue file change.
59
+
60
+ Args:
61
+ issue_id: The issue ID (e.g., "FEAT-0115")
62
+ action: The action being performed (e.g., "close", "start", "open")
63
+ issue_file_path: Current path to the issue file
64
+ old_file_path: Previous path if the file was moved (e.g., status change)
65
+ no_commit: If True, skip the commit operation
66
+
67
+ Returns:
68
+ CommitResult with success status and commit details
69
+ """
70
+ if no_commit:
71
+ return CommitResult(success=True, message="Skipped (no-commit flag)")
72
+
73
+ if not self.is_git_repository():
74
+ logger.info("Not in a git repository, skipping auto-commit")
75
+ return CommitResult(success=True, message="Skipped (not a git repo)")
76
+
77
+ try:
78
+ # Stage the changes
79
+ self._stage_issue_files(issue_file_path, old_file_path)
80
+
81
+ # Generate and execute commit
82
+ commit_message = self._generate_commit_message(issue_id, action)
83
+ commit_hash = git.git_commit(self.project_root, commit_message)
84
+
85
+ return CommitResult(
86
+ success=True,
87
+ commit_hash=commit_hash,
88
+ message=commit_message,
89
+ )
90
+
91
+ except Exception as e:
92
+ error_msg = str(e)
93
+ logger.error(f"Failed to commit issue change: {error_msg}")
94
+ return CommitResult(success=False, error=error_msg)
95
+
96
+ def _stage_issue_files(
97
+ self, current_path: Path, old_path: Optional[Path] = None
98
+ ) -> None:
99
+ """
100
+ Stage issue file changes.
101
+
102
+ If old_path is provided (file was moved), handles the rename properly:
103
+ - Stages deletion of old file
104
+ - Stages addition of new file
105
+ """
106
+ files_to_stage: List[str] = []
107
+
108
+ # Handle the old file path if file was moved (e.g., open -> closed)
109
+ if old_path and old_path.exists():
110
+ # File was moved, need to stage the deletion
111
+ try:
112
+ rel_old_path = old_path.relative_to(self.project_root)
113
+ files_to_stage.append(str(rel_old_path))
114
+ except ValueError:
115
+ # old_path is not relative to project_root, use absolute
116
+ files_to_stage.append(str(old_path))
117
+
118
+ # Handle the current file path
119
+ if current_path.exists():
120
+ try:
121
+ rel_path = current_path.relative_to(self.project_root)
122
+ files_to_stage.append(str(rel_path))
123
+ except ValueError:
124
+ # current_path is not relative to project_root, use absolute
125
+ files_to_stage.append(str(current_path))
126
+
127
+ if files_to_stage:
128
+ git.git_add(self.project_root, files_to_stage)
129
+
130
+ def _generate_commit_message(self, issue_id: str, action: str) -> str:
131
+ """
132
+ Generate a standardized commit message for issue transitions.
133
+
134
+ Format: chore(issue): <action> <issue_id>
135
+
136
+ Examples:
137
+ chore(issue): close FIX-0020
138
+ chore(issue): start FEAT-0115
139
+ chore(issue): open FEAT-0115
140
+ chore(issue): submit FEAT-0115
141
+ """
142
+ return f"chore(issue): {action} {issue_id}"
143
+
144
+ def get_commit_history(
145
+ self, issue_id: str, max_count: int = 10
146
+ ) -> List[Tuple[str, str]]:
147
+ """
148
+ Get commit history for a specific issue.
149
+
150
+ Returns:
151
+ List of (commit_hash, subject) tuples
152
+ """
153
+ if not self.is_git_repository():
154
+ return []
155
+
156
+ try:
157
+ commits = git.search_commits_by_message(self.project_root, issue_id)
158
+ return [(c["hash"], c["subject"]) for c in commits[:max_count]]
159
+ except Exception:
160
+ return []
161
+
162
+
163
+ def should_auto_commit(config) -> bool:
164
+ """
165
+ Check if auto-commit should be enabled based on configuration.
166
+
167
+ Checks for:
168
+ 1. Explicit disable in config: issue.auto_commit = false
169
+ 2. Environment variable: MONOCO_NO_AUTO_COMMIT=1
170
+ """
171
+ import os
172
+
173
+ # Check environment variable first
174
+ if os.environ.get("MONOCO_NO_AUTO_COMMIT", "0") == "1":
175
+ return False
176
+
177
+ # Check config (if issue config has auto_commit setting)
178
+ try:
179
+ if hasattr(config, "issue") and hasattr(config.issue, "auto_commit"):
180
+ return config.issue.auto_commit
181
+ except Exception:
182
+ pass
183
+
184
+ # Default to enabled
185
+ return True