monoco-toolkit 0.3.6__py3-none-any.whl → 0.3.9__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 +51 -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/integrations.py +6 -0
- monoco/core/registry.py +2 -0
- monoco/core/setup.py +1 -1
- monoco/core/skills.py +226 -42
- monoco/features/{scheduler → agent}/__init__.py +4 -2
- monoco/features/{scheduler → agent}/cli.py +134 -80
- monoco/features/{scheduler → agent}/config.py +17 -3
- monoco/features/agent/defaults.py +55 -0
- monoco/features/agent/flow_skills.py +281 -0
- monoco/features/{scheduler → agent}/manager.py +39 -2
- monoco/features/{scheduler → agent}/models.py +6 -3
- monoco/features/{scheduler → agent}/reliability.py +1 -1
- monoco/features/agent/resources/skills/flow_engineer/SKILL.md +94 -0
- monoco/features/agent/resources/skills/flow_manager/SKILL.md +88 -0
- monoco/features/agent/resources/skills/flow_reviewer/SKILL.md +114 -0
- monoco/features/{scheduler → agent}/session.py +36 -1
- monoco/features/{scheduler → agent}/worker.py +2 -2
- monoco/features/i18n/resources/skills/i18n_scan_workflow/SKILL.md +105 -0
- monoco/features/issue/commands.py +427 -21
- monoco/features/issue/core.py +100 -0
- monoco/features/issue/criticality.py +553 -0
- monoco/features/issue/domain/models.py +28 -2
- monoco/features/issue/engine/machine.py +70 -13
- monoco/features/issue/git_service.py +185 -0
- monoco/features/issue/linter.py +291 -62
- monoco/features/issue/models.py +49 -2
- monoco/features/issue/resources/en/SKILL.md +48 -0
- monoco/features/issue/resources/skills/issue_lifecycle_workflow/SKILL.md +159 -0
- monoco/features/issue/resources/zh/SKILL.md +50 -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/skills/note_processing_workflow/SKILL.md +140 -0
- monoco/features/memo/resources/zh/AGENTS.md +8 -0
- monoco/features/memo/resources/zh/SKILL.md +75 -0
- monoco/features/spike/resources/skills/research_workflow/SKILL.md +121 -0
- monoco/main.py +2 -3
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +55 -37
- monoco/features/scheduler/defaults.py +0 -54
- monoco/features/skills/__init__.py +0 -0
- monoco/features/skills/core.py +0 -102
- /monoco/core/{hooks.py → githooks.py} +0 -0
- /monoco/features/{scheduler → agent}/engines.py +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.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)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
import uuid
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
3
5
|
from .models import RoleTemplate
|
|
4
6
|
from .worker import Worker
|
|
5
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
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class SessionManager:
|
|
@@ -11,9 +15,37 @@ class SessionManager:
|
|
|
11
15
|
Responsible for creating, tracking, and retrieving sessions.
|
|
12
16
|
"""
|
|
13
17
|
|
|
14
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
project_root: Optional[Path] = None,
|
|
21
|
+
hook_registry: Optional[HookRegistry] = None,
|
|
22
|
+
config: Optional[MonocoConfig] = None,
|
|
23
|
+
):
|
|
15
24
|
# In-memory storage for now. In prod, this might be a DB or file-backed.
|
|
16
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)
|
|
17
49
|
|
|
18
50
|
def create_session(self, issue_id: str, role: RoleTemplate) -> RuntimeSession:
|
|
19
51
|
session_id = str(uuid.uuid4())
|
|
@@ -29,7 +61,12 @@ class SessionManager:
|
|
|
29
61
|
)
|
|
30
62
|
|
|
31
63
|
worker = Worker(role, issue_id)
|
|
32
|
-
runtime = RuntimeSession(
|
|
64
|
+
runtime = RuntimeSession(
|
|
65
|
+
session_model,
|
|
66
|
+
worker,
|
|
67
|
+
hook_registry=self.hook_registry,
|
|
68
|
+
project_root=self.project_root,
|
|
69
|
+
)
|
|
33
70
|
self._sessions[session_id] = runtime
|
|
34
71
|
return runtime
|
|
35
72
|
|
|
@@ -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 AgentConfig(BaseModel):
|
|
24
23
|
roles: List[RoleTemplate] = Field(default_factory=list)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Backward compatibility alias
|
|
27
|
+
SchedulerConfig = AgentConfig
|
|
@@ -20,7 +20,7 @@ class ApoptosisManager:
|
|
|
20
20
|
roles = load_scheduler_config(project_root)
|
|
21
21
|
|
|
22
22
|
# Find coroner role
|
|
23
|
-
self.coroner_role = roles.get("
|
|
23
|
+
self.coroner_role = roles.get("Coroner")
|
|
24
24
|
|
|
25
25
|
if not self.coroner_role:
|
|
26
26
|
raise ValueError("Coroner role not defined!")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-engineer
|
|
3
|
+
description: Engineer 角色的标准化工作流 (Flow Skill)。定义从需求调研到代码提交的标准操作流程,确保测试覆盖和代码质量。
|
|
4
|
+
type: flow
|
|
5
|
+
role: engineer
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Engineer Flow
|
|
10
|
+
|
|
11
|
+
Engineer 角色的标准化工作流,确保代码开发遵循 "Investigate → Code → Test → Report → Submit" 流程。
|
|
12
|
+
|
|
13
|
+
## 工作流状态机
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
stateDiagram-v2
|
|
17
|
+
[*] --> Investigate: 接收任务
|
|
18
|
+
|
|
19
|
+
Investigate --> Code: 需求清晰
|
|
20
|
+
Investigate --> Investigate: 需求模糊<br/>(请求澄清)
|
|
21
|
+
|
|
22
|
+
Code --> Test: 编码完成
|
|
23
|
+
|
|
24
|
+
Test --> Test: 测试失败<br/>(修复代码)
|
|
25
|
+
Test --> Report: 测试通过
|
|
26
|
+
|
|
27
|
+
Report --> Submit: 报告完成
|
|
28
|
+
|
|
29
|
+
Submit --> [*]: 提交成功
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 执行步骤
|
|
33
|
+
|
|
34
|
+
### 1. Investigate (调研)
|
|
35
|
+
|
|
36
|
+
- **目标**: 充分理解需求,识别技术风险和依赖
|
|
37
|
+
- **输入**: Issue 描述、相关代码、依赖 Issue
|
|
38
|
+
- **输出**: 技术方案草稿、风险清单
|
|
39
|
+
- **检查点**:
|
|
40
|
+
- [ ] 阅读并理解 Issue 描述
|
|
41
|
+
- [ ] 识别相关代码文件
|
|
42
|
+
- [ ] 检查依赖 Issue 状态
|
|
43
|
+
- [ ] 评估技术可行性
|
|
44
|
+
|
|
45
|
+
### 2. Code (编码)
|
|
46
|
+
|
|
47
|
+
- **目标**: 实现功能或修复缺陷
|
|
48
|
+
- **前置条件**: 需求已清晰,分支已创建 (`monoco issue start <ID> --branch`)
|
|
49
|
+
- **检查点**:
|
|
50
|
+
- [ ] 遵循项目代码规范
|
|
51
|
+
- [ ] 编写/更新必要的文档
|
|
52
|
+
- [ ] 处理边界情况
|
|
53
|
+
|
|
54
|
+
### 3. Test (测试)
|
|
55
|
+
|
|
56
|
+
- **目标**: 确保代码质量和功能正确性
|
|
57
|
+
- **策略**: 循环测试直到通过
|
|
58
|
+
- **检查点**:
|
|
59
|
+
- [ ] 编写/更新单元测试
|
|
60
|
+
- [ ] 运行测试套件 (`pytest`, `cargo test`, etc.)
|
|
61
|
+
- [ ] 修复失败的测试
|
|
62
|
+
- [ ] 检查测试覆盖率
|
|
63
|
+
|
|
64
|
+
### 4. Report (报告)
|
|
65
|
+
|
|
66
|
+
- **目标**: 记录变更内容,更新 Issue 状态
|
|
67
|
+
- **检查点**:
|
|
68
|
+
- [ ] 更新 Issue 文件追踪 (`monoco issue sync-files`)
|
|
69
|
+
- [ ] 编写变更摘要
|
|
70
|
+
- [ ] 更新任务清单 (Checkboxes)
|
|
71
|
+
|
|
72
|
+
### 5. Submit (提交)
|
|
73
|
+
|
|
74
|
+
- **目标**: 完成代码提交,进入评审流程
|
|
75
|
+
- **检查点**:
|
|
76
|
+
- [ ] 运行 `monoco issue lint` 检查合规性
|
|
77
|
+
- [ ] 运行 `monoco issue submit <ID>`
|
|
78
|
+
- [ ] 等待评审结果
|
|
79
|
+
|
|
80
|
+
## 决策分支
|
|
81
|
+
|
|
82
|
+
| 条件 | 动作 |
|
|
83
|
+
|------|------|
|
|
84
|
+
| 需求不清晰 | 返回 Investigate,请求澄清 |
|
|
85
|
+
| 测试失败 | 返回 Code,修复问题 |
|
|
86
|
+
| Lint 失败 | 修复合规性问题,重新 Submit |
|
|
87
|
+
| 评审拒绝 | 返回 Code,按反馈修改 |
|
|
88
|
+
|
|
89
|
+
## 合规要求
|
|
90
|
+
|
|
91
|
+
- **禁止**: 跳过测试直接提交
|
|
92
|
+
- **禁止**: 在 main/master 分支直接修改代码
|
|
93
|
+
- **必须**: 使用 `monoco issue start --branch` 创建功能分支
|
|
94
|
+
- **必须**: 所有单元测试通过后才能 Submit
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-manager
|
|
3
|
+
description: Manager 角色的标准化工作流 (Flow Skill)。定义从 Inbox 整理到任务指派的标准操作流程,确保需求清晰和任务合理拆解。
|
|
4
|
+
type: flow
|
|
5
|
+
role: manager
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Manager Flow
|
|
10
|
+
|
|
11
|
+
Manager 角色的标准化工作流,确保 "Inbox → Clarify → Decompose → Assign" 流程。
|
|
12
|
+
|
|
13
|
+
## 工作流状态机
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
stateDiagram-v2
|
|
17
|
+
[*] --> Inbox: 新需求/想法
|
|
18
|
+
|
|
19
|
+
Inbox --> Clarify: 开始处理
|
|
20
|
+
Inbox --> Inbox: 暂存<br/>(低优先级)
|
|
21
|
+
|
|
22
|
+
Clarify --> Decompose: 需求清晰
|
|
23
|
+
Clarify --> Clarify: 需更多信息<br/>(等待反馈)
|
|
24
|
+
|
|
25
|
+
Decompose --> Assign: 拆解完成
|
|
26
|
+
Decompose --> Decompose: 过于复杂<br/>(拆分为 Epic)
|
|
27
|
+
|
|
28
|
+
Assign --> [*]: 指派完成
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 执行步骤
|
|
32
|
+
|
|
33
|
+
### 1. Inbox (收件箱)
|
|
34
|
+
|
|
35
|
+
- **目标**: 收集和暂存所有 incoming 需求、想法和任务
|
|
36
|
+
- **输入**: Memo、用户反馈、系统告警、技术债务
|
|
37
|
+
- **检查点**:
|
|
38
|
+
- [ ] 记录需求来源和背景
|
|
39
|
+
- [ ] 初步分类 (Feature/Chore/Fix)
|
|
40
|
+
- [ ] 评估紧急程度
|
|
41
|
+
|
|
42
|
+
### 2. Clarify (需求澄清)
|
|
43
|
+
|
|
44
|
+
- **目标**: 将模糊的需求转化为清晰的描述
|
|
45
|
+
- **策略**: 5W2H 分析法
|
|
46
|
+
- **检查点**:
|
|
47
|
+
- [ ] **What**: 要解决什么问题?
|
|
48
|
+
- [ ] **Why**: 为什么重要?
|
|
49
|
+
- [ ] **Who**: 谁是利益相关者?
|
|
50
|
+
- [ ] **When**: 期望的完成时间?
|
|
51
|
+
- [ ] **Where**: 影响范围?
|
|
52
|
+
- [ ] **How**: 建议的实现方式?
|
|
53
|
+
- [ ] **How Much**: 工作量预估?
|
|
54
|
+
|
|
55
|
+
### 3. Decompose (任务拆解)
|
|
56
|
+
|
|
57
|
+
- **目标**: 将大任务拆分为可独立交付的子任务
|
|
58
|
+
- **策略**: 垂直切片 (Vertical Slicing)
|
|
59
|
+
- **检查点**:
|
|
60
|
+
- [ ] 识别核心价值和依赖关系
|
|
61
|
+
- [ ] 拆分为可独立交付的 Feature/Chore/Fix
|
|
62
|
+
- [ ] 设置合理的优先级
|
|
63
|
+
- [ ] 为复杂任务创建 Epic
|
|
64
|
+
|
|
65
|
+
### 4. Assign (任务指派)
|
|
66
|
+
|
|
67
|
+
- **目标**: 将任务分配给合适的执行者
|
|
68
|
+
- **检查点**:
|
|
69
|
+
- [ ] 评估团队能力和负载
|
|
70
|
+
- [ ] 明确验收标准 (Acceptance Criteria)
|
|
71
|
+
- [ ] 设置合理的截止日期
|
|
72
|
+
- [ ] 通知相关成员
|
|
73
|
+
|
|
74
|
+
## 决策分支
|
|
75
|
+
|
|
76
|
+
| 条件 | 动作 |
|
|
77
|
+
|------|------|
|
|
78
|
+
| 需求过于模糊 | 返回 Inbox,等待更多信息 |
|
|
79
|
+
| 任务过于复杂 | 创建 Epic,拆分为多个 Feature |
|
|
80
|
+
| 依赖其他任务 | 设置依赖关系,调整优先级 |
|
|
81
|
+
| 资源不足 | 调整范围或延期 |
|
|
82
|
+
|
|
83
|
+
## 合规要求
|
|
84
|
+
|
|
85
|
+
- **必须**: 每个任务都有清晰的验收标准
|
|
86
|
+
- **必须**: 复杂任务必须拆分为 Epic + Features
|
|
87
|
+
- **禁止**: 指派没有澄清需求给 Engineer
|
|
88
|
+
- **建议**: 使用 `monoco memo` 管理临时想法
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-reviewer
|
|
3
|
+
description: Reviewer 角色的标准化工作流 (Flow Skill)。定义从代码检出到评审完成的标准操作流程,确保代码质量和流程合规。
|
|
4
|
+
type: flow
|
|
5
|
+
role: reviewer
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Reviewer Flow
|
|
10
|
+
|
|
11
|
+
Reviewer 角色的标准化工作流,确保 "Checkout → Test → Review → Decide → Cleanup" 流程。
|
|
12
|
+
|
|
13
|
+
## 工作流状态机
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
stateDiagram-v2
|
|
17
|
+
[*] --> Checkout: 收到评审请求
|
|
18
|
+
|
|
19
|
+
Checkout --> Test: 检出完成
|
|
20
|
+
Checkout --> Checkout: 检出失败<br/>(环境检查)
|
|
21
|
+
|
|
22
|
+
Test --> Review: 测试通过
|
|
23
|
+
Test --> Test: 测试失败<br/>(记录问题)
|
|
24
|
+
|
|
25
|
+
Review --> Approve: 代码 OK
|
|
26
|
+
Review --> Reject: 发现问题
|
|
27
|
+
|
|
28
|
+
Reject --> Checkout: 修复后重审
|
|
29
|
+
|
|
30
|
+
Approve --> Cleanup: 批准
|
|
31
|
+
|
|
32
|
+
Cleanup --> [*]: 清理完成
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 执行步骤
|
|
36
|
+
|
|
37
|
+
### 1. Checkout (检出)
|
|
38
|
+
|
|
39
|
+
- **目标**: 获取待评审的代码
|
|
40
|
+
- **检查点**:
|
|
41
|
+
- [ ] 检出 PR/Branch
|
|
42
|
+
- [ ] 确认与 Base 分支的差异
|
|
43
|
+
- [ ] 检查环境配置
|
|
44
|
+
|
|
45
|
+
### 2. Test (测试)
|
|
46
|
+
|
|
47
|
+
- **目标**: 验证功能正确性和测试覆盖
|
|
48
|
+
- **检查点**:
|
|
49
|
+
- [ ] 运行单元测试
|
|
50
|
+
- [ ] 运行集成测试 (如适用)
|
|
51
|
+
- [ ] 验证手动测试场景
|
|
52
|
+
- [ ] 检查测试覆盖率报告
|
|
53
|
+
|
|
54
|
+
### 3. Review (代码审查)
|
|
55
|
+
|
|
56
|
+
- **目标**: 检查代码质量和设计合理性
|
|
57
|
+
- **检查清单**:
|
|
58
|
+
- [ ] **功能**: 代码是否实现了需求?
|
|
59
|
+
- [ ] **设计**: 架构是否合理?
|
|
60
|
+
- [ ] **可读性**: 代码是否易于理解?
|
|
61
|
+
- [ ] **测试**: 测试是否充分?
|
|
62
|
+
- [ ] **文档**: 文档是否同步更新?
|
|
63
|
+
- [ ] **合规**: 是否遵循项目规范?
|
|
64
|
+
|
|
65
|
+
### 4. Decide (决策)
|
|
66
|
+
|
|
67
|
+
- **目标**: 做出批准或拒绝的决定
|
|
68
|
+
- **选项**:
|
|
69
|
+
- **Approve**: 代码可以合并
|
|
70
|
+
- **Reject**: 需要修改,提供具体反馈
|
|
71
|
+
- **Request Changes**: 小问题,可快速修复
|
|
72
|
+
|
|
73
|
+
### 5. Cleanup (清理)
|
|
74
|
+
|
|
75
|
+
- **目标**: 完成评审后的环境清理
|
|
76
|
+
- **检查点**:
|
|
77
|
+
- [ ] 删除本地分支 (如适用)
|
|
78
|
+
- [ ] 更新 Issue 状态
|
|
79
|
+
- [ ] 记录评审意见到 Review Comments
|
|
80
|
+
|
|
81
|
+
## 决策分支
|
|
82
|
+
|
|
83
|
+
| 条件 | 动作 |
|
|
84
|
+
|------|------|
|
|
85
|
+
| 测试失败 | Reject,要求修复测试 |
|
|
86
|
+
| 代码风格问题 | Request Changes 或提供建议 |
|
|
87
|
+
| 设计问题 | Reject,要求重新设计 |
|
|
88
|
+
| 文档缺失 | Request Changes,要求补充 |
|
|
89
|
+
| 修复后重审 | 返回 Checkout,重新执行流程 |
|
|
90
|
+
|
|
91
|
+
## 评审意见模板
|
|
92
|
+
|
|
93
|
+
```markdown
|
|
94
|
+
## Review Comments
|
|
95
|
+
|
|
96
|
+
### ✅ 优点
|
|
97
|
+
-
|
|
98
|
+
|
|
99
|
+
### ⚠️ 建议
|
|
100
|
+
-
|
|
101
|
+
|
|
102
|
+
### ❌ 必须修改
|
|
103
|
+
-
|
|
104
|
+
|
|
105
|
+
### 📝 其他
|
|
106
|
+
-
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 合规要求
|
|
110
|
+
|
|
111
|
+
- **必须**: 所有测试通过才能 Approve
|
|
112
|
+
- **必须**: 拒绝时必须提供具体反馈
|
|
113
|
+
- **禁止**: 未经测试直接 Approve
|
|
114
|
+
- **建议**: 使用 `monoco issue close --prune` 合并后清理
|