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.
- monoco/cli/workspace.py +1 -1
- monoco/core/config.py +58 -0
- monoco/core/hooks/__init__.py +19 -0
- monoco/core/hooks/base.py +104 -0
- monoco/core/hooks/builtin/__init__.py +11 -0
- monoco/core/hooks/builtin/git_cleanup.py +266 -0
- monoco/core/hooks/builtin/logging_hook.py +78 -0
- monoco/core/hooks/context.py +131 -0
- monoco/core/hooks/registry.py +222 -0
- monoco/core/injection.py +63 -29
- monoco/core/integrations.py +8 -2
- monoco/core/output.py +5 -5
- monoco/core/registry.py +9 -1
- monoco/core/resource/__init__.py +5 -0
- monoco/core/resource/finder.py +98 -0
- monoco/core/resource/manager.py +91 -0
- monoco/core/resource/models.py +35 -0
- monoco/core/resources/en/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
- monoco/core/resources/zh/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
- monoco/core/setup.py +1 -1
- monoco/core/skill_framework.py +292 -0
- monoco/core/skills.py +538 -254
- monoco/core/sync.py +73 -1
- monoco/core/workflow_converter.py +420 -0
- monoco/features/{scheduler → agent}/__init__.py +5 -3
- monoco/features/agent/adapter.py +31 -0
- monoco/features/agent/apoptosis.py +44 -0
- monoco/features/agent/cli.py +296 -0
- monoco/features/agent/config.py +96 -0
- monoco/features/agent/defaults.py +12 -0
- monoco/features/{scheduler → agent}/engines.py +32 -6
- monoco/features/agent/flow_skills.py +281 -0
- monoco/features/agent/manager.py +91 -0
- monoco/features/{scheduler → agent}/models.py +6 -3
- monoco/features/agent/resources/atoms/atom-code-dev.yaml +61 -0
- monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +73 -0
- monoco/features/agent/resources/atoms/atom-knowledge.yaml +55 -0
- monoco/features/agent/resources/atoms/atom-review.yaml +60 -0
- monoco/features/agent/resources/en/skills/flow_engineer/SKILL.md +94 -0
- monoco/features/agent/resources/en/skills/flow_manager/SKILL.md +93 -0
- monoco/features/agent/resources/en/skills/flow_planner/SKILL.md +85 -0
- monoco/features/agent/resources/en/skills/flow_reviewer/SKILL.md +114 -0
- monoco/features/agent/resources/roles/role-engineer.yaml +49 -0
- monoco/features/agent/resources/roles/role-manager.yaml +46 -0
- monoco/features/agent/resources/roles/role-planner.yaml +46 -0
- monoco/features/agent/resources/roles/role-reviewer.yaml +47 -0
- monoco/features/agent/resources/workflows/workflow-dev.yaml +83 -0
- monoco/features/agent/resources/workflows/workflow-issue-create.yaml +72 -0
- monoco/features/agent/resources/workflows/workflow-review.yaml +94 -0
- monoco/features/agent/resources/zh/skills/flow_engineer/SKILL.md +94 -0
- monoco/features/agent/resources/zh/skills/flow_manager/SKILL.md +88 -0
- monoco/features/agent/resources/zh/skills/flow_planner/SKILL.md +259 -0
- monoco/features/agent/resources/zh/skills/flow_reviewer/SKILL.md +137 -0
- monoco/features/{scheduler → agent}/session.py +36 -1
- monoco/features/{scheduler → agent}/worker.py +40 -4
- monoco/features/glossary/adapter.py +31 -0
- monoco/features/glossary/config.py +5 -0
- monoco/features/glossary/resources/en/AGENTS.md +29 -0
- monoco/features/glossary/resources/en/skills/monoco_glossary/SKILL.md +35 -0
- monoco/features/glossary/resources/zh/AGENTS.md +29 -0
- monoco/features/glossary/resources/zh/skills/monoco_glossary/SKILL.md +35 -0
- monoco/features/i18n/resources/en/skills/i18n_scan_workflow/SKILL.md +105 -0
- monoco/features/i18n/resources/en/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
- monoco/features/i18n/resources/zh/skills/i18n_scan_workflow/SKILL.md +105 -0
- monoco/features/i18n/resources/zh/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
- monoco/features/issue/commands.py +427 -21
- monoco/features/issue/core.py +140 -1
- monoco/features/issue/criticality.py +553 -0
- monoco/features/issue/domain/models.py +28 -2
- monoco/features/issue/engine/machine.py +75 -15
- monoco/features/issue/git_service.py +185 -0
- monoco/features/issue/linter.py +291 -62
- monoco/features/issue/models.py +50 -2
- monoco/features/issue/resources/en/skills/issue_create_workflow/SKILL.md +167 -0
- monoco/features/issue/resources/en/skills/issue_develop_workflow/SKILL.md +224 -0
- monoco/features/issue/resources/en/skills/issue_lifecycle_workflow/SKILL.md +159 -0
- monoco/features/issue/resources/en/skills/issue_refine_workflow/SKILL.md +203 -0
- monoco/features/issue/resources/en/{SKILL.md → skills/monoco_issue/SKILL.md} +50 -0
- monoco/features/issue/resources/zh/skills/issue_create_workflow/SKILL.md +167 -0
- monoco/features/issue/resources/zh/skills/issue_develop_workflow/SKILL.md +224 -0
- monoco/features/issue/resources/zh/skills/issue_lifecycle_workflow/SKILL.md +159 -0
- monoco/features/issue/resources/zh/skills/issue_refine_workflow/SKILL.md +203 -0
- monoco/features/issue/resources/zh/{SKILL.md → skills/monoco_issue/SKILL.md} +52 -0
- monoco/features/issue/validator.py +185 -65
- monoco/features/memo/__init__.py +2 -1
- monoco/features/memo/adapter.py +32 -0
- monoco/features/memo/cli.py +36 -14
- monoco/features/memo/core.py +59 -0
- monoco/features/memo/resources/en/skills/monoco_memo/SKILL.md +77 -0
- monoco/features/memo/resources/en/skills/note_processing_workflow/SKILL.md +140 -0
- monoco/features/memo/resources/zh/AGENTS.md +8 -0
- monoco/features/memo/resources/zh/skills/monoco_memo/SKILL.md +77 -0
- monoco/features/memo/resources/zh/skills/note_processing_workflow/SKILL.md +140 -0
- monoco/features/spike/resources/en/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
- monoco/features/spike/resources/en/skills/research_workflow/SKILL.md +121 -0
- monoco/features/spike/resources/zh/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
- monoco/features/spike/resources/zh/skills/research_workflow/SKILL.md +121 -0
- monoco/main.py +2 -3
- monoco_toolkit-0.3.10.dist-info/METADATA +124 -0
- monoco_toolkit-0.3.10.dist-info/RECORD +156 -0
- monoco/features/scheduler/cli.py +0 -285
- monoco/features/scheduler/config.py +0 -68
- monoco/features/scheduler/defaults.py +0 -54
- monoco/features/scheduler/manager.py +0 -49
- monoco/features/scheduler/reliability.py +0 -106
- monoco/features/skills/core.py +0 -102
- monoco_toolkit-0.3.6.dist-info/METADATA +0 -127
- monoco_toolkit-0.3.6.dist-info/RECORD +0 -97
- /monoco/core/{hooks.py → githooks.py} +0 -0
- /monoco/features/{skills → glossary}/__init__.py +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flow Skills Manager for Monoco Scheduler.
|
|
3
|
+
|
|
4
|
+
This module provides management and injection of standard Agent workflow skills
|
|
5
|
+
(Flow Skills) following the Kimi CLI Flow Skill format.
|
|
6
|
+
|
|
7
|
+
Note: This module is now a thin wrapper around SkillManager for backward compatibility.
|
|
8
|
+
New code should use SkillManager directly for multi-skill architecture support.
|
|
9
|
+
|
|
10
|
+
Key Responsibilities:
|
|
11
|
+
1. Discover flow skills from resources/skills/ directory
|
|
12
|
+
2. Inject flow skills to target agent framework directories
|
|
13
|
+
3. Handle .gitignore updates for injected skills
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import shutil
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import List, Set
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
# Prefix for injected flow skills to avoid conflicts
|
|
24
|
+
FLOW_SKILL_PREFIX = "monoco_flow_"
|
|
25
|
+
|
|
26
|
+
# Gitignore pattern for flow skills
|
|
27
|
+
GITIGNORE_PATTERN = f"{FLOW_SKILL_PREFIX}*/"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def discover_flow_skills(resources_dir: Path) -> List[Path]:
|
|
31
|
+
"""
|
|
32
|
+
Discover all flow skill directories in the resources/skills/ directory.
|
|
33
|
+
|
|
34
|
+
Flow skills are identified by either:
|
|
35
|
+
1. Directory name starting with "flow_" (legacy pattern)
|
|
36
|
+
2. SKILL.md containing "type: flow" in front matter (new pattern)
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
resources_dir: Path to the resources directory (e.g., monoco/features/scheduler/resources/)
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of paths to flow skill directories
|
|
43
|
+
"""
|
|
44
|
+
skills_dir = resources_dir / "skills"
|
|
45
|
+
if not skills_dir.exists():
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
flow_skills = []
|
|
49
|
+
for item in skills_dir.iterdir():
|
|
50
|
+
if not item.is_dir():
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
skill_file = item / "SKILL.md"
|
|
54
|
+
if not skill_file.exists():
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
# Check legacy pattern: directory starts with "flow_"
|
|
58
|
+
if item.name.startswith("flow_"):
|
|
59
|
+
flow_skills.append(item)
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
# Check new pattern: SKILL.md contains "type: flow" in front matter
|
|
63
|
+
try:
|
|
64
|
+
content = skill_file.read_text(encoding="utf-8")
|
|
65
|
+
# Look for type: flow in front matter (between --- markers)
|
|
66
|
+
if content.startswith("---"):
|
|
67
|
+
front_matter_end = content.find("---", 3)
|
|
68
|
+
if front_matter_end != -1:
|
|
69
|
+
front_matter = content[3:front_matter_end]
|
|
70
|
+
if "type: flow" in front_matter:
|
|
71
|
+
flow_skills.append(item)
|
|
72
|
+
except Exception:
|
|
73
|
+
pass # Skip files that can't be read
|
|
74
|
+
|
|
75
|
+
return sorted(flow_skills)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def inject_flow_skill(
|
|
79
|
+
skill_dir: Path, target_dir: Path, prefix: str = FLOW_SKILL_PREFIX
|
|
80
|
+
) -> bool:
|
|
81
|
+
"""
|
|
82
|
+
Inject a single flow skill to the target directory.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
skill_dir: Source flow skill directory (e.g., .../flow_engineer/)
|
|
86
|
+
target_dir: Target directory for skill injection (e.g., .agent/skills/)
|
|
87
|
+
prefix: Prefix to add to the skill directory name
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
True if injection successful, False otherwise
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
# Calculate target skill name with prefix
|
|
94
|
+
# e.g., flow_engineer -> monoco_flow_engineer
|
|
95
|
+
target_skill_name = f"{prefix}{skill_dir.name}"
|
|
96
|
+
target_skill_dir = target_dir / target_skill_name
|
|
97
|
+
|
|
98
|
+
# Remove existing skill directory if present
|
|
99
|
+
if target_skill_dir.exists():
|
|
100
|
+
shutil.rmtree(target_skill_dir)
|
|
101
|
+
|
|
102
|
+
# Copy skill directory
|
|
103
|
+
shutil.copytree(skill_dir, target_skill_dir)
|
|
104
|
+
|
|
105
|
+
console.print(f"[green] ✓ Injected {target_skill_name}/[/green]")
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
console.print(f"[red] ✗ Failed to inject {skill_dir.name}: {e}[/red]")
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def sync_flow_skills(
|
|
114
|
+
resources_dir: Path,
|
|
115
|
+
target_dir: Path,
|
|
116
|
+
prefix: str = FLOW_SKILL_PREFIX,
|
|
117
|
+
force: bool = False,
|
|
118
|
+
) -> dict:
|
|
119
|
+
"""
|
|
120
|
+
Synchronize all flow skills from resources to target directory.
|
|
121
|
+
|
|
122
|
+
This function:
|
|
123
|
+
1. Discovers all flow skills in resources/skills/
|
|
124
|
+
2. Injects them to target_dir with the specified prefix
|
|
125
|
+
3. Optionally removes skills that no longer exist in source
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
resources_dir: Path to the resources directory
|
|
129
|
+
target_dir: Target directory for skill injection (e.g., .agent/skills/)
|
|
130
|
+
prefix: Prefix to add to skill directory names
|
|
131
|
+
force: Force re-injection even if skills already exist
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Dictionary with 'injected', 'failed', 'removed' counts
|
|
135
|
+
"""
|
|
136
|
+
results = {"injected": 0, "failed": 0, "removed": 0}
|
|
137
|
+
|
|
138
|
+
# Ensure target directory exists
|
|
139
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
|
|
141
|
+
# Discover flow skills
|
|
142
|
+
flow_skills = discover_flow_skills(resources_dir)
|
|
143
|
+
|
|
144
|
+
if not flow_skills:
|
|
145
|
+
console.print("[yellow]No flow skills found in resources[/yellow]")
|
|
146
|
+
return results
|
|
147
|
+
|
|
148
|
+
console.print(f"[dim]Found {len(flow_skills)} flow skill(s)[/dim]")
|
|
149
|
+
|
|
150
|
+
# Track expected skill names
|
|
151
|
+
expected_skills: Set[str] = set()
|
|
152
|
+
|
|
153
|
+
# Inject each flow skill
|
|
154
|
+
for skill_dir in flow_skills:
|
|
155
|
+
target_skill_name = f"{prefix}{skill_dir.name}"
|
|
156
|
+
expected_skills.add(target_skill_name)
|
|
157
|
+
|
|
158
|
+
# Check if already exists and not force
|
|
159
|
+
target_skill_dir = target_dir / target_skill_name
|
|
160
|
+
if target_skill_dir.exists() and not force:
|
|
161
|
+
# Check if source is newer
|
|
162
|
+
source_mtime = (skill_dir / "SKILL.md").stat().st_mtime
|
|
163
|
+
target_mtime = (target_skill_dir / "SKILL.md").stat().st_mtime
|
|
164
|
+
|
|
165
|
+
if source_mtime <= target_mtime:
|
|
166
|
+
console.print(f"[dim] = {target_skill_name}/ is up to date[/dim]")
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
if inject_flow_skill(skill_dir, target_dir, prefix):
|
|
170
|
+
results["injected"] += 1
|
|
171
|
+
else:
|
|
172
|
+
results["failed"] += 1
|
|
173
|
+
|
|
174
|
+
# Clean up orphaned skills (optional, when force=True)
|
|
175
|
+
if force:
|
|
176
|
+
for item in target_dir.iterdir():
|
|
177
|
+
if item.is_dir() and item.name.startswith(prefix):
|
|
178
|
+
if item.name not in expected_skills:
|
|
179
|
+
shutil.rmtree(item)
|
|
180
|
+
console.print(f"[dim] - Removed orphaned {item.name}/[/dim]")
|
|
181
|
+
results["removed"] += 1
|
|
182
|
+
|
|
183
|
+
return results
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def update_gitignore(project_root: Path, pattern: str = GITIGNORE_PATTERN) -> bool:
|
|
187
|
+
"""
|
|
188
|
+
Add flow skill pattern to .gitignore if not present.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
project_root: Project root directory
|
|
192
|
+
pattern: Gitignore pattern to add
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
True if .gitignore was updated or already contains pattern
|
|
196
|
+
"""
|
|
197
|
+
gitignore_path = project_root / ".gitignore"
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
# Read existing content
|
|
201
|
+
if gitignore_path.exists():
|
|
202
|
+
content = gitignore_path.read_text(encoding="utf-8")
|
|
203
|
+
lines = content.splitlines()
|
|
204
|
+
else:
|
|
205
|
+
lines = []
|
|
206
|
+
|
|
207
|
+
# Check if pattern already exists
|
|
208
|
+
for line in lines:
|
|
209
|
+
if line.strip() == pattern or line.strip() == f"/{pattern}":
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
# Add pattern with comment
|
|
213
|
+
comment = "# Monoco Flow Skills (auto-generated)"
|
|
214
|
+
new_lines = [comment, pattern, ""]
|
|
215
|
+
|
|
216
|
+
# Append to file
|
|
217
|
+
with open(gitignore_path, "a", encoding="utf-8") as f:
|
|
218
|
+
# Add newline if file doesn't end with one
|
|
219
|
+
if lines and not content.endswith("\n"):
|
|
220
|
+
f.write("\n")
|
|
221
|
+
f.write("\n".join(new_lines))
|
|
222
|
+
|
|
223
|
+
console.print(f"[green] ✓ Updated .gitignore with {pattern}[/green]")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
console.print(f"[red] ✗ Failed to update .gitignore: {e}[/red]")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def remove_flow_skills(target_dir: Path, prefix: str = FLOW_SKILL_PREFIX) -> int:
|
|
232
|
+
"""
|
|
233
|
+
Remove all injected flow skills from target directory.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
target_dir: Target directory (e.g., .agent/skills/)
|
|
237
|
+
prefix: Prefix used for flow skill directories
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Number of skills removed
|
|
241
|
+
"""
|
|
242
|
+
if not target_dir.exists():
|
|
243
|
+
return 0
|
|
244
|
+
|
|
245
|
+
removed_count = 0
|
|
246
|
+
for item in target_dir.iterdir():
|
|
247
|
+
if item.is_dir() and item.name.startswith(prefix):
|
|
248
|
+
shutil.rmtree(item)
|
|
249
|
+
console.print(f"[green] ✓ Removed {item.name}/[/green]")
|
|
250
|
+
removed_count += 1
|
|
251
|
+
|
|
252
|
+
return removed_count
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def get_flow_skill_commands(target_dir: Path, prefix: str = FLOW_SKILL_PREFIX) -> List[str]:
|
|
256
|
+
"""
|
|
257
|
+
Get list of available flow skill commands.
|
|
258
|
+
|
|
259
|
+
In Kimi CLI, flow skills are invoked via /flow:<role> command.
|
|
260
|
+
This function extracts the role names from injected flow skills.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
target_dir: Target directory (e.g., .agent/skills/)
|
|
264
|
+
prefix: Prefix used for flow skill directories
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
List of available /flow:<role> commands
|
|
268
|
+
"""
|
|
269
|
+
if not target_dir.exists():
|
|
270
|
+
return []
|
|
271
|
+
|
|
272
|
+
commands = []
|
|
273
|
+
for item in target_dir.iterdir():
|
|
274
|
+
if item.is_dir() and item.name.startswith(prefix):
|
|
275
|
+
# Extract role from directory name
|
|
276
|
+
# e.g., monoco_flow_engineer -> engineer
|
|
277
|
+
role = item.name[len(prefix) + 5:] # Remove prefix + "flow_"
|
|
278
|
+
if role:
|
|
279
|
+
commands.append(f"/flow:{role}")
|
|
280
|
+
|
|
281
|
+
return sorted(commands)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Dict, List, Optional
|
|
2
|
+
import uuid
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .models import RoleTemplate
|
|
6
|
+
from .worker import Worker
|
|
7
|
+
from .session import Session, RuntimeSession
|
|
8
|
+
from monoco.core.hooks import HookRegistry, get_registry
|
|
9
|
+
from monoco.core.config import find_monoco_root, MonocoConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SessionManager:
|
|
13
|
+
"""
|
|
14
|
+
Manages the lifecycle of sessions.
|
|
15
|
+
Responsible for creating, tracking, and retrieving sessions.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
project_root: Optional[Path] = None,
|
|
21
|
+
hook_registry: Optional[HookRegistry] = None,
|
|
22
|
+
config: Optional[MonocoConfig] = None,
|
|
23
|
+
):
|
|
24
|
+
# In-memory storage for now. In prod, this might be a DB or file-backed.
|
|
25
|
+
self._sessions: Dict[str, RuntimeSession] = {}
|
|
26
|
+
self.project_root = project_root or find_monoco_root()
|
|
27
|
+
self.config = config
|
|
28
|
+
|
|
29
|
+
# Initialize hook registry
|
|
30
|
+
self.hook_registry = hook_registry or get_registry()
|
|
31
|
+
|
|
32
|
+
# Load hooks from config if available
|
|
33
|
+
self._load_hooks_from_config()
|
|
34
|
+
|
|
35
|
+
def _load_hooks_from_config(self) -> None:
|
|
36
|
+
"""Load and register hooks from configuration."""
|
|
37
|
+
if self.config is None:
|
|
38
|
+
try:
|
|
39
|
+
from monoco.core.config import get_config
|
|
40
|
+
self.config = get_config(str(self.project_root))
|
|
41
|
+
except Exception:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
# Load hooks from config
|
|
45
|
+
if self.config and hasattr(self.config, "session_hooks"):
|
|
46
|
+
hooks_config = self.config.session_hooks
|
|
47
|
+
if hooks_config:
|
|
48
|
+
self.hook_registry.load_from_config(hooks_config, self.project_root)
|
|
49
|
+
|
|
50
|
+
def create_session(self, issue_id: str, role: RoleTemplate) -> RuntimeSession:
|
|
51
|
+
session_id = str(uuid.uuid4())
|
|
52
|
+
branch_name = (
|
|
53
|
+
f"agent/{issue_id}/{session_id[:8]}" # Simple branch naming strategy
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
session_model = Session(
|
|
57
|
+
id=session_id,
|
|
58
|
+
issue_id=issue_id,
|
|
59
|
+
role_name=role.name,
|
|
60
|
+
branch_name=branch_name,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Get timeout from config
|
|
64
|
+
timeout = 900
|
|
65
|
+
if self.config and hasattr(self.config, "agent"):
|
|
66
|
+
timeout = self.config.agent.timeout_seconds
|
|
67
|
+
|
|
68
|
+
worker = Worker(role, issue_id, timeout=timeout)
|
|
69
|
+
runtime = RuntimeSession(
|
|
70
|
+
session_model,
|
|
71
|
+
worker,
|
|
72
|
+
hook_registry=self.hook_registry,
|
|
73
|
+
project_root=self.project_root,
|
|
74
|
+
)
|
|
75
|
+
self._sessions[session_id] = runtime
|
|
76
|
+
return runtime
|
|
77
|
+
|
|
78
|
+
def get_session(self, session_id: str) -> Optional[RuntimeSession]:
|
|
79
|
+
return self._sessions.get(session_id)
|
|
80
|
+
|
|
81
|
+
def list_sessions(self, issue_id: Optional[str] = None) -> List[RuntimeSession]:
|
|
82
|
+
if issue_id:
|
|
83
|
+
return [s for s in self._sessions.values() if s.model.issue_id == issue_id]
|
|
84
|
+
return list(self._sessions.values())
|
|
85
|
+
|
|
86
|
+
def terminate_session(self, session_id: str):
|
|
87
|
+
session = self.get_session(session_id)
|
|
88
|
+
if session:
|
|
89
|
+
session.terminate()
|
|
90
|
+
# We might want to keep the record for a while, so don't delete immediately
|
|
91
|
+
# del self._sessions[session_id]
|
|
@@ -4,14 +4,13 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
|
|
5
5
|
class RoleTemplate(BaseModel):
|
|
6
6
|
name: str = Field(
|
|
7
|
-
..., description="Unique identifier for the role (e.g., '
|
|
7
|
+
..., description="Unique identifier for the role (e.g., 'Planner')"
|
|
8
8
|
)
|
|
9
9
|
description: str = Field(..., description="Human-readable description of the role")
|
|
10
10
|
trigger: str = Field(
|
|
11
11
|
..., description="Event that triggers this agent (e.g., 'issue.created')"
|
|
12
12
|
)
|
|
13
13
|
goal: str = Field(..., description="The primary goal/output of this agent")
|
|
14
|
-
tools: List[str] = Field(default_factory=list, description="List of allowed tools")
|
|
15
14
|
system_prompt: str = Field(
|
|
16
15
|
..., description="The system prompt template for this agent"
|
|
17
16
|
)
|
|
@@ -20,5 +19,9 @@ class RoleTemplate(BaseModel):
|
|
|
20
19
|
)
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
class
|
|
22
|
+
class AgentRoleConfig(BaseModel):
|
|
24
23
|
roles: List[RoleTemplate] = Field(default_factory=list)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Backward compatibility alias
|
|
27
|
+
SchedulerConfig = AgentRoleConfig
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atom-code-dev
|
|
3
|
+
type: atom
|
|
4
|
+
domain: code
|
|
5
|
+
description: 代码开发的原子操作 - 调研、实现、测试、文档
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
author: Monoco Toolkit
|
|
8
|
+
|
|
9
|
+
# 系统级合规规则
|
|
10
|
+
compliance_rules:
|
|
11
|
+
- rule: "遵循项目代码规范"
|
|
12
|
+
severity: warning
|
|
13
|
+
|
|
14
|
+
- rule: "优先编写测试"
|
|
15
|
+
severity: warning
|
|
16
|
+
mindset: "TDD"
|
|
17
|
+
|
|
18
|
+
- rule: "保持代码简单直观"
|
|
19
|
+
severity: info
|
|
20
|
+
mindset: "KISS"
|
|
21
|
+
|
|
22
|
+
# 原子操作定义
|
|
23
|
+
operations:
|
|
24
|
+
- name: investigate
|
|
25
|
+
description: 理解需求与上下文,识别相关代码和依赖
|
|
26
|
+
reminder: "理解需求后再开始编码,识别相关文件和依赖 Issue"
|
|
27
|
+
checkpoints:
|
|
28
|
+
- "阅读并理解 Issue 描述"
|
|
29
|
+
- "识别相关代码文件"
|
|
30
|
+
- "检查依赖 Issue 状态"
|
|
31
|
+
- "评估技术可行性"
|
|
32
|
+
output: "技术方案草稿、风险清单"
|
|
33
|
+
|
|
34
|
+
- name: implement
|
|
35
|
+
description: 实现代码变更
|
|
36
|
+
reminder: "遵循项目代码规范,小步提交,处理边界情况"
|
|
37
|
+
checkpoints:
|
|
38
|
+
- "遵循项目代码规范"
|
|
39
|
+
- "编写/更新必要的文档"
|
|
40
|
+
- "处理边界情况"
|
|
41
|
+
- "保持代码可审查性(单次提交 < 400 行)"
|
|
42
|
+
|
|
43
|
+
- name: test
|
|
44
|
+
description: 运行测试确保代码质量
|
|
45
|
+
reminder: "所有测试必须通过,检查测试覆盖率"
|
|
46
|
+
checkpoints:
|
|
47
|
+
- "编写/更新单元测试"
|
|
48
|
+
- "运行测试套件 (pytest, cargo test, etc.)"
|
|
49
|
+
- "修复失败的测试"
|
|
50
|
+
- "检查测试覆盖率"
|
|
51
|
+
compliance_rules:
|
|
52
|
+
- rule: "所有单元测试必须通过后才能提交"
|
|
53
|
+
severity: error
|
|
54
|
+
|
|
55
|
+
- name: document
|
|
56
|
+
description: 更新文档
|
|
57
|
+
reminder: "代码变更必须同步更新文档"
|
|
58
|
+
checkpoints:
|
|
59
|
+
- "更新代码注释"
|
|
60
|
+
- "更新相关文档"
|
|
61
|
+
- "更新 CHANGELOG(如适用)"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atom-issue-lifecycle
|
|
3
|
+
type: atom
|
|
4
|
+
domain: issue
|
|
5
|
+
description: Issue 生命周期管理的原子操作 - 创建、启动、提交、关闭工作单元
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
author: Monoco Toolkit
|
|
8
|
+
|
|
9
|
+
# 系统级合规规则(定义一次,全局生效)
|
|
10
|
+
compliance_rules:
|
|
11
|
+
- rule: "禁止在 main/master 分支直接修改代码"
|
|
12
|
+
severity: error
|
|
13
|
+
check: "git branch --show-current"
|
|
14
|
+
fail_if: "main|master"
|
|
15
|
+
|
|
16
|
+
- rule: "必须使用 feature 分支进行开发"
|
|
17
|
+
severity: error
|
|
18
|
+
command: "monoco issue start <ID> --branch"
|
|
19
|
+
|
|
20
|
+
- rule: "提交前必须通过 lint 检查"
|
|
21
|
+
severity: error
|
|
22
|
+
command: "monoco issue lint"
|
|
23
|
+
|
|
24
|
+
- rule: "每个 Issue 必须至少包含 2 个 Checkbox"
|
|
25
|
+
severity: error
|
|
26
|
+
check: "checkbox_count >= 2"
|
|
27
|
+
|
|
28
|
+
- rule: "Review/Done 阶段必须包含 Review Comments"
|
|
29
|
+
severity: warning
|
|
30
|
+
check: "review_comments_section exists"
|
|
31
|
+
|
|
32
|
+
# 原子操作定义
|
|
33
|
+
operations:
|
|
34
|
+
- name: create
|
|
35
|
+
description: 创建新的 Issue 工作单元
|
|
36
|
+
command: "monoco issue create <type> -t <title>"
|
|
37
|
+
reminder: "选择合适的类型 (epic/feature/chore/fix),编写清晰的描述"
|
|
38
|
+
checkpoints:
|
|
39
|
+
- "必须包含至少 2 个 Checkbox"
|
|
40
|
+
- "标题必须与 Front Matter 一致"
|
|
41
|
+
|
|
42
|
+
- name: start
|
|
43
|
+
description: 启动开发,创建功能分支
|
|
44
|
+
command: "monoco issue start <ID> --branch"
|
|
45
|
+
reminder: "确保使用 --branch 创建功能分支,禁止在 main/master 上开发"
|
|
46
|
+
checkpoints:
|
|
47
|
+
- "禁止在 main/master 分支直接修改代码"
|
|
48
|
+
- "必须创建 feature 分支"
|
|
49
|
+
|
|
50
|
+
- name: sync
|
|
51
|
+
description: 同步文件追踪,记录修改的文件
|
|
52
|
+
command: "monoco issue sync-files"
|
|
53
|
+
reminder: "定期同步文件追踪,保持 Issue 与代码变更同步"
|
|
54
|
+
|
|
55
|
+
- name: lint
|
|
56
|
+
description: 检查 Issue 合规性
|
|
57
|
+
command: "monoco issue lint"
|
|
58
|
+
reminder: "提交前必须运行 lint 检查"
|
|
59
|
+
checkpoints:
|
|
60
|
+
- "必须通过所有合规检查"
|
|
61
|
+
|
|
62
|
+
- name: submit
|
|
63
|
+
description: 提交代码进行评审
|
|
64
|
+
command: "monoco issue submit <ID>"
|
|
65
|
+
reminder: "确保所有测试通过,lint 无错误后再提交"
|
|
66
|
+
checkpoints:
|
|
67
|
+
- "所有单元测试必须通过"
|
|
68
|
+
- "必须通过 lint 检查"
|
|
69
|
+
|
|
70
|
+
- name: close
|
|
71
|
+
description: 关闭 Issue,清理环境
|
|
72
|
+
command: "monoco issue close <ID> --solution completed --prune"
|
|
73
|
+
reminder: "代码合并后及时关闭 Issue,清理 feature 分支"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atom-knowledge
|
|
3
|
+
type: atom
|
|
4
|
+
domain: knowledge
|
|
5
|
+
description: 知识管理的原子操作 - 捕获、处理、转化、归档
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
author: Monoco Toolkit
|
|
8
|
+
|
|
9
|
+
# 系统级合规规则
|
|
10
|
+
compliance_rules:
|
|
11
|
+
- rule: "Memo 是临时的,不应无限堆积"
|
|
12
|
+
severity: warning
|
|
13
|
+
|
|
14
|
+
- rule: "可执行的想法必须转为 Issue 追踪"
|
|
15
|
+
severity: error
|
|
16
|
+
|
|
17
|
+
# 原子操作定义
|
|
18
|
+
operations:
|
|
19
|
+
- name: capture
|
|
20
|
+
description: 快速捕获 fleeting ideas
|
|
21
|
+
command: "monoco memo add <content>"
|
|
22
|
+
reminder: "保持简洁,不中断当前工作流,添加上下文"
|
|
23
|
+
checkpoints:
|
|
24
|
+
- "使用简洁的描述"
|
|
25
|
+
- "添加上下文(-c file:line 如适用)"
|
|
26
|
+
- "不中断当前任务流"
|
|
27
|
+
|
|
28
|
+
- name: process
|
|
29
|
+
description: 定期处理 Memo,评估价值
|
|
30
|
+
command: "monoco memo list"
|
|
31
|
+
reminder: "定期回顾和分类 Memo(建议每周)"
|
|
32
|
+
checkpoints:
|
|
33
|
+
- "运行 monoco memo list 查看所有 Memo"
|
|
34
|
+
- "评估每个 Memo 的价值"
|
|
35
|
+
- "分类:可执行 / 纯参考 / 无价值"
|
|
36
|
+
|
|
37
|
+
- name: convert
|
|
38
|
+
description: 将可执行的 Memo 转化为 Issue
|
|
39
|
+
command: "monoco issue create <type> -t <title>"
|
|
40
|
+
reminder: "有价值的想法尽快转为 Issue"
|
|
41
|
+
checkpoints:
|
|
42
|
+
- "判断 Issue 类型 (feature/chore/fix)"
|
|
43
|
+
- "编写清晰的描述和验收标准"
|
|
44
|
+
- "关联原始 Memo"
|
|
45
|
+
compliance_rules:
|
|
46
|
+
- rule: "可执行的想法必须转为 Issue 追踪"
|
|
47
|
+
severity: error
|
|
48
|
+
|
|
49
|
+
- name: archive
|
|
50
|
+
description: 归档纯参考资料
|
|
51
|
+
reminder: "纯参考资料归档保存,无价值的直接删除"
|
|
52
|
+
checkpoints:
|
|
53
|
+
- "确认 Memo 内容为纯参考资料"
|
|
54
|
+
- "移动到知识库或文档"
|
|
55
|
+
- "从 Memo 列表中移除"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atom-review
|
|
3
|
+
type: atom
|
|
4
|
+
domain: review
|
|
5
|
+
description: 代码评审的原子操作 - 检出、验证、对抗、反馈
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
author: Monoco Toolkit
|
|
8
|
+
|
|
9
|
+
# 系统级合规规则
|
|
10
|
+
compliance_rules:
|
|
11
|
+
- rule: "必须先通过 Engineer 的测试 (Verify),再进行对抗测试 (Challenge)"
|
|
12
|
+
severity: error
|
|
13
|
+
|
|
14
|
+
- rule: "必须尝试编写至少一个边界测试用例"
|
|
15
|
+
severity: warning
|
|
16
|
+
|
|
17
|
+
- rule: "禁止未经测试直接 Approve"
|
|
18
|
+
severity: error
|
|
19
|
+
|
|
20
|
+
# 原子操作定义
|
|
21
|
+
operations:
|
|
22
|
+
- name: checkout
|
|
23
|
+
description: 检出待评审的代码
|
|
24
|
+
reminder: "获取待评审的 PR/Branch,确认与 Base 分支的差异"
|
|
25
|
+
checkpoints:
|
|
26
|
+
- "检出 PR/Branch"
|
|
27
|
+
- "确认与 Base 分支的差异"
|
|
28
|
+
- "检查环境配置"
|
|
29
|
+
|
|
30
|
+
- name: verify
|
|
31
|
+
description: 验证 Engineer 提交的测试 (White-box)
|
|
32
|
+
reminder: "先运行 Engineer 编写的测试,验证功能正确性"
|
|
33
|
+
checkpoints:
|
|
34
|
+
- "运行 Engineer 编写的单元测试"
|
|
35
|
+
- "运行集成测试(如适用)"
|
|
36
|
+
- "检查测试覆盖率报告"
|
|
37
|
+
compliance_rules:
|
|
38
|
+
- rule: "如果现有测试失败,直接进入 Reject 流程"
|
|
39
|
+
severity: error
|
|
40
|
+
|
|
41
|
+
- name: challenge
|
|
42
|
+
description: 对抗测试,尝试破坏代码 (Black-box / Edge Cases)
|
|
43
|
+
reminder: "Try to break it - 寻找边界情况和安全漏洞"
|
|
44
|
+
checkpoints:
|
|
45
|
+
- "分析代码逻辑,寻找盲区(并发、大/小数值、注入攻击等)"
|
|
46
|
+
- "编写新的 Challenge Test Cases"
|
|
47
|
+
- "运行这些新测试"
|
|
48
|
+
compliance_rules:
|
|
49
|
+
- rule: "必须尝试编写至少一个边界测试用例"
|
|
50
|
+
severity: warning
|
|
51
|
+
|
|
52
|
+
- name: feedback
|
|
53
|
+
description: 提供评审反馈
|
|
54
|
+
reminder: "明确批准、拒绝或请求修改,提供具体反馈"
|
|
55
|
+
checkpoints:
|
|
56
|
+
- "功能是否正确实现"
|
|
57
|
+
- "代码是否符合设计规范"
|
|
58
|
+
- "测试是否充分"
|
|
59
|
+
- "文档是否同步更新"
|
|
60
|
+
- "是否遵循项目规范"
|