monoco-toolkit 0.1.1__py3-none-any.whl → 0.2.8__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/__init__.py +0 -0
- monoco/cli/project.py +87 -0
- monoco/cli/workspace.py +46 -0
- monoco/core/agent/__init__.py +5 -0
- monoco/core/agent/action.py +144 -0
- monoco/core/agent/adapters.py +129 -0
- monoco/core/agent/protocol.py +31 -0
- monoco/core/agent/state.py +106 -0
- monoco/core/config.py +212 -17
- monoco/core/execution.py +62 -0
- monoco/core/feature.py +58 -0
- monoco/core/git.py +51 -2
- monoco/core/injection.py +196 -0
- monoco/core/integrations.py +242 -0
- monoco/core/lsp.py +68 -0
- monoco/core/output.py +21 -3
- monoco/core/registry.py +36 -0
- monoco/core/resources/en/AGENTS.md +8 -0
- monoco/core/resources/en/SKILL.md +66 -0
- monoco/core/resources/zh/AGENTS.md +8 -0
- monoco/core/resources/zh/SKILL.md +65 -0
- monoco/core/setup.py +96 -110
- monoco/core/skills.py +444 -0
- monoco/core/state.py +53 -0
- monoco/core/sync.py +224 -0
- monoco/core/telemetry.py +4 -1
- monoco/core/workspace.py +85 -20
- monoco/daemon/app.py +127 -58
- monoco/daemon/models.py +4 -0
- monoco/daemon/services.py +56 -155
- monoco/features/config/commands.py +125 -44
- monoco/features/i18n/adapter.py +29 -0
- monoco/features/i18n/commands.py +89 -10
- monoco/features/i18n/core.py +113 -27
- monoco/features/i18n/resources/en/AGENTS.md +8 -0
- monoco/features/i18n/resources/en/SKILL.md +94 -0
- monoco/features/i18n/resources/zh/AGENTS.md +8 -0
- monoco/features/i18n/resources/zh/SKILL.md +94 -0
- monoco/features/issue/adapter.py +34 -0
- monoco/features/issue/commands.py +343 -101
- monoco/features/issue/core.py +384 -150
- monoco/features/issue/domain/__init__.py +0 -0
- monoco/features/issue/domain/lifecycle.py +126 -0
- monoco/features/issue/domain/models.py +170 -0
- monoco/features/issue/domain/parser.py +223 -0
- monoco/features/issue/domain/workspace.py +104 -0
- monoco/features/issue/engine/__init__.py +22 -0
- monoco/features/issue/engine/config.py +172 -0
- monoco/features/issue/engine/machine.py +185 -0
- monoco/features/issue/engine/models.py +18 -0
- monoco/features/issue/linter.py +325 -120
- monoco/features/issue/lsp/__init__.py +3 -0
- monoco/features/issue/lsp/definition.py +72 -0
- monoco/features/issue/migration.py +134 -0
- monoco/features/issue/models.py +46 -24
- monoco/features/issue/monitor.py +94 -0
- monoco/features/issue/resources/en/AGENTS.md +20 -0
- monoco/features/issue/resources/en/SKILL.md +111 -0
- monoco/features/issue/resources/zh/AGENTS.md +20 -0
- monoco/features/issue/resources/zh/SKILL.md +138 -0
- monoco/features/issue/validator.py +455 -0
- monoco/features/spike/adapter.py +30 -0
- monoco/features/spike/commands.py +45 -24
- monoco/features/spike/core.py +6 -40
- monoco/features/spike/resources/en/AGENTS.md +7 -0
- monoco/features/spike/resources/en/SKILL.md +74 -0
- monoco/features/spike/resources/zh/AGENTS.md +7 -0
- monoco/features/spike/resources/zh/SKILL.md +74 -0
- monoco/main.py +91 -2
- monoco_toolkit-0.2.8.dist-info/METADATA +136 -0
- monoco_toolkit-0.2.8.dist-info/RECORD +83 -0
- monoco_toolkit-0.1.1.dist-info/METADATA +0 -93
- monoco_toolkit-0.1.1.dist-info/RECORD +0 -33
- {monoco_toolkit-0.1.1.dist-info → monoco_toolkit-0.2.8.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.1.1.dist-info → monoco_toolkit-0.2.8.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.1.1.dist-info → monoco_toolkit-0.2.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import yaml
|
|
4
|
+
import hashlib
|
|
5
|
+
import secrets
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from .models import generate_uid
|
|
10
|
+
|
|
11
|
+
# Migration Mappings
|
|
12
|
+
DIR_MAP = {
|
|
13
|
+
"STORIES": "Features",
|
|
14
|
+
"Stories": "Features",
|
|
15
|
+
"TASKS": "Chores",
|
|
16
|
+
"Tasks": "Chores",
|
|
17
|
+
"BUGS": "Fixes",
|
|
18
|
+
"Bugs": "Fixes",
|
|
19
|
+
"EPICS": "Epics",
|
|
20
|
+
"Epics": "Epics",
|
|
21
|
+
"features": "Features",
|
|
22
|
+
"chores": "Chores",
|
|
23
|
+
"fixes": "Fixes",
|
|
24
|
+
"epics": "Epics"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
TYPE_MAP = {
|
|
28
|
+
"story": "feature",
|
|
29
|
+
"task": "chore",
|
|
30
|
+
"bug": "fix"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ID_PREFIX_MAP = {
|
|
34
|
+
"STORY": "FEAT",
|
|
35
|
+
"TASK": "CHORE",
|
|
36
|
+
"BUG": "FIX"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def migrate_issues_directory(issues_dir: Path):
|
|
40
|
+
"""
|
|
41
|
+
Core migration logic to upgrade an Issues directory to the latest Monoco standard.
|
|
42
|
+
"""
|
|
43
|
+
if not issues_dir.exists():
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# 1. Rename Directories
|
|
47
|
+
for old_name, new_name in DIR_MAP.items():
|
|
48
|
+
old_path = issues_dir / old_name
|
|
49
|
+
if old_path.exists():
|
|
50
|
+
new_path = issues_dir / new_name
|
|
51
|
+
|
|
52
|
+
# Case sensitivity check for some filesystems
|
|
53
|
+
same_inode = False
|
|
54
|
+
try:
|
|
55
|
+
if new_path.exists() and os.path.samefile(old_path, new_path):
|
|
56
|
+
same_inode = True
|
|
57
|
+
except OSError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
if same_inode:
|
|
61
|
+
if old_path.name != new_path.name:
|
|
62
|
+
old_path.rename(new_path)
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
if new_path.exists():
|
|
66
|
+
import shutil
|
|
67
|
+
for item in old_path.iterdir():
|
|
68
|
+
dest = new_path / item.name
|
|
69
|
+
if dest.exists() and item.is_dir():
|
|
70
|
+
for subitem in item.iterdir():
|
|
71
|
+
shutil.move(str(subitem), str(dest / subitem.name))
|
|
72
|
+
shutil.rmtree(item)
|
|
73
|
+
else:
|
|
74
|
+
shutil.move(str(item), str(dest))
|
|
75
|
+
shutil.rmtree(old_path)
|
|
76
|
+
else:
|
|
77
|
+
old_path.rename(new_path)
|
|
78
|
+
|
|
79
|
+
# 2. Rename Files and Update Content
|
|
80
|
+
for subdir_name in ["Features", "Chores", "Fixes", "Epics"]:
|
|
81
|
+
subdir = issues_dir / subdir_name
|
|
82
|
+
if not subdir.exists():
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
for file_path in subdir.rglob("*.md"):
|
|
86
|
+
content = file_path.read_text(encoding="utf-8")
|
|
87
|
+
new_content = content
|
|
88
|
+
|
|
89
|
+
# Replace Type in Frontmatter
|
|
90
|
+
for old_type, new_type in TYPE_MAP.items():
|
|
91
|
+
new_content = re.sub(rf"^type:\s*{old_type}", f"type: {new_type}", new_content, flags=re.IGNORECASE | re.MULTILINE)
|
|
92
|
+
|
|
93
|
+
# Replace ID Prefixes
|
|
94
|
+
for old_prefix, new_prefix in ID_PREFIX_MAP.items():
|
|
95
|
+
new_content = new_content.replace(f"[[{old_prefix}-", f"[[{new_prefix}-")
|
|
96
|
+
new_content = re.sub(rf"^id: {old_prefix}-", f"id: {new_prefix}-", new_content, flags=re.MULTILINE)
|
|
97
|
+
new_content = re.sub(rf"^parent: {old_prefix}-", f"parent: {new_prefix}-", new_content, flags=re.MULTILINE)
|
|
98
|
+
new_content = new_content.replace(f"{old_prefix}-", f"{new_prefix}-")
|
|
99
|
+
|
|
100
|
+
# Structural Updates (UID, Stage)
|
|
101
|
+
match = re.search(r"^---(.*?)---", new_content, re.DOTALL | re.MULTILINE)
|
|
102
|
+
if match:
|
|
103
|
+
yaml_str = match.group(1)
|
|
104
|
+
try:
|
|
105
|
+
data = yaml.safe_load(yaml_str) or {}
|
|
106
|
+
changed = False
|
|
107
|
+
|
|
108
|
+
if 'uid' not in data:
|
|
109
|
+
data['uid'] = generate_uid()
|
|
110
|
+
changed = True
|
|
111
|
+
|
|
112
|
+
if 'stage' in data and data['stage'] == 'todo':
|
|
113
|
+
data['stage'] = 'draft'
|
|
114
|
+
changed = True
|
|
115
|
+
|
|
116
|
+
if changed:
|
|
117
|
+
new_yaml = yaml.dump(data, sort_keys=False, allow_unicode=True)
|
|
118
|
+
new_content = new_content.replace(match.group(1), "\n" + new_yaml)
|
|
119
|
+
except yaml.YAMLError:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
if new_content != content:
|
|
123
|
+
file_path.write_text(new_content, encoding="utf-8")
|
|
124
|
+
|
|
125
|
+
# Rename File
|
|
126
|
+
filename = file_path.name
|
|
127
|
+
new_filename = filename
|
|
128
|
+
for old_prefix, new_prefix in ID_PREFIX_MAP.items():
|
|
129
|
+
if filename.startswith(f"{old_prefix}-"):
|
|
130
|
+
new_filename = filename.replace(f"{old_prefix}-", f"{new_prefix}-", 1)
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
if new_filename != filename:
|
|
134
|
+
file_path.rename(file_path.parent / new_filename)
|
monoco/features/issue/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import List, Optional, Any
|
|
2
|
+
from typing import List, Optional, Any, Dict
|
|
3
3
|
from pydantic import BaseModel, Field, model_validator
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
import hashlib
|
|
@@ -61,7 +61,7 @@ class IssueStatus(str, Enum):
|
|
|
61
61
|
BACKLOG = "backlog"
|
|
62
62
|
|
|
63
63
|
class IssueStage(str, Enum):
|
|
64
|
-
|
|
64
|
+
DRAFT = "draft"
|
|
65
65
|
DOING = "doing"
|
|
66
66
|
REVIEW = "review"
|
|
67
67
|
DONE = "done"
|
|
@@ -78,19 +78,30 @@ class IsolationType(str, Enum):
|
|
|
78
78
|
WORKTREE = "worktree"
|
|
79
79
|
|
|
80
80
|
class IssueIsolation(BaseModel):
|
|
81
|
-
type:
|
|
81
|
+
type: str
|
|
82
82
|
ref: str # Git branch name
|
|
83
83
|
path: Optional[str] = None # Worktree path (relative to repo root or absolute)
|
|
84
84
|
created_at: datetime = Field(default_factory=current_time)
|
|
85
85
|
|
|
86
|
+
class IssueAction(BaseModel):
|
|
87
|
+
label: str
|
|
88
|
+
target_status: Optional[str] = None
|
|
89
|
+
target_stage: Optional[str] = None
|
|
90
|
+
target_solution: Optional[str] = None
|
|
91
|
+
icon: Optional[str] = None
|
|
92
|
+
|
|
93
|
+
# Generic execution extensions
|
|
94
|
+
command: Optional[str] = None
|
|
95
|
+
params: Dict[str, Any] = {}
|
|
96
|
+
|
|
86
97
|
class IssueMetadata(BaseModel):
|
|
87
98
|
model_config = {"extra": "allow"}
|
|
88
99
|
|
|
89
100
|
id: str
|
|
90
101
|
uid: Optional[str] = None # Global unique identifier for cross-project identity
|
|
91
|
-
type:
|
|
92
|
-
status:
|
|
93
|
-
stage: Optional[
|
|
102
|
+
type: str
|
|
103
|
+
status: str = "open"
|
|
104
|
+
stage: Optional[str] = None
|
|
94
105
|
title: str
|
|
95
106
|
|
|
96
107
|
# Time Anchors
|
|
@@ -101,17 +112,39 @@ class IssueMetadata(BaseModel):
|
|
|
101
112
|
|
|
102
113
|
parent: Optional[str] = None
|
|
103
114
|
sprint: Optional[str] = None
|
|
104
|
-
solution: Optional[
|
|
115
|
+
solution: Optional[str] = None
|
|
105
116
|
isolation: Optional[IssueIsolation] = None
|
|
106
117
|
dependencies: List[str] = []
|
|
107
118
|
related: List[str] = []
|
|
108
119
|
tags: List[str] = []
|
|
120
|
+
files: List[str] = []
|
|
121
|
+
path: Optional[str] = None # Absolute path to the issue file
|
|
122
|
+
|
|
123
|
+
# Proxy UI Actions (Excluded from file persistence)
|
|
124
|
+
# Modified: Remove exclude=True to allow API/CLI inspection. Must be manually excluded during YAML Dump.
|
|
125
|
+
actions: List[IssueAction] = Field(default=[])
|
|
109
126
|
|
|
110
127
|
|
|
111
128
|
@model_validator(mode='before')
|
|
112
129
|
@classmethod
|
|
113
130
|
def normalize_fields(cls, v: Any) -> Any:
|
|
114
131
|
if isinstance(v, dict):
|
|
132
|
+
# Handle common capitalization variations for robustness
|
|
133
|
+
field_map = {
|
|
134
|
+
"ID": "id",
|
|
135
|
+
"Type": "type",
|
|
136
|
+
"Status": "status",
|
|
137
|
+
"Stage": "stage",
|
|
138
|
+
"Title": "title",
|
|
139
|
+
"Parent": "parent",
|
|
140
|
+
"Solution": "solution",
|
|
141
|
+
"Sprint": "sprint",
|
|
142
|
+
}
|
|
143
|
+
for old_k, new_k in field_map.items():
|
|
144
|
+
if old_k in v and new_k not in v:
|
|
145
|
+
v[new_k] = v[old_k] # Don't pop yet to avoid mutation issues if used elsewhere, or pop if safe.
|
|
146
|
+
# Pydantic v2 mode='before' is usually a copy if we want to be safe, but let's just add it.
|
|
147
|
+
|
|
115
148
|
# Normalize type and status to lowercase for compatibility
|
|
116
149
|
if "type" in v and isinstance(v["type"], str):
|
|
117
150
|
v["type"] = v["type"].lower()
|
|
@@ -122,30 +155,19 @@ class IssueMetadata(BaseModel):
|
|
|
122
155
|
# Stage normalization
|
|
123
156
|
if "stage" in v and isinstance(v["stage"], str):
|
|
124
157
|
v["stage"] = v["stage"].lower()
|
|
158
|
+
if v["stage"] == "todo":
|
|
159
|
+
v["stage"] = "draft"
|
|
125
160
|
return v
|
|
126
161
|
|
|
127
162
|
@model_validator(mode='after')
|
|
128
163
|
def validate_lifecycle(self) -> 'IssueMetadata':
|
|
129
164
|
# Logic Definition:
|
|
130
|
-
# status: backlog -> stage:
|
|
165
|
+
# status: backlog -> stage: freezed
|
|
131
166
|
# status: closed -> stage: done
|
|
132
|
-
# status: open -> stage:
|
|
133
|
-
|
|
134
|
-
if self.status == IssueStatus.BACKLOG:
|
|
135
|
-
self.stage = IssueStage.FREEZED
|
|
136
|
-
|
|
137
|
-
elif self.status == IssueStatus.CLOSED:
|
|
138
|
-
# Enforce stage=done for closed issues
|
|
139
|
-
if self.stage != IssueStage.DONE:
|
|
140
|
-
self.stage = IssueStage.DONE
|
|
141
|
-
# Auto-fill closed_at if missing
|
|
142
|
-
if not self.closed_at:
|
|
143
|
-
self.closed_at = current_time()
|
|
167
|
+
# status: open -> stage: draft | doing | review | done (default draft)
|
|
144
168
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if self.stage is None or self.stage == IssueStage.DONE:
|
|
148
|
-
self.stage = IssueStage.TODO
|
|
169
|
+
# NOTE: We do NOT auto-correct state here anymore to allow Linter to detect inconsistencies.
|
|
170
|
+
# Auto-correction should be applied explicitly by 'create' or 'update' commands via core logic.
|
|
149
171
|
|
|
150
172
|
return self
|
|
151
173
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Callable, Awaitable, Any, Optional
|
|
6
|
+
|
|
7
|
+
from watchdog.observers import Observer
|
|
8
|
+
from watchdog.events import FileSystemEventHandler
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("monoco.features.issue.monitor")
|
|
11
|
+
|
|
12
|
+
class IssueEventHandler(FileSystemEventHandler):
|
|
13
|
+
def __init__(self, loop, on_upsert: Callable[[dict], Awaitable[None]], on_delete: Callable[[dict], Awaitable[None]]):
|
|
14
|
+
self.loop = loop
|
|
15
|
+
self.on_upsert = on_upsert
|
|
16
|
+
self.on_delete = on_delete
|
|
17
|
+
|
|
18
|
+
def _process_upsert(self, path_str: str):
|
|
19
|
+
if not path_str.endswith(".md"):
|
|
20
|
+
return
|
|
21
|
+
asyncio.run_coroutine_threadsafe(self._handle_upsert(path_str), self.loop)
|
|
22
|
+
|
|
23
|
+
async def _handle_upsert(self, path_str: str):
|
|
24
|
+
try:
|
|
25
|
+
from monoco.features.issue.core import parse_issue
|
|
26
|
+
path = Path(path_str)
|
|
27
|
+
if not path.exists():
|
|
28
|
+
return
|
|
29
|
+
issue = parse_issue(path)
|
|
30
|
+
if issue:
|
|
31
|
+
await self.on_upsert(issue.model_dump(mode='json'))
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logger.error(f"Error handling upsert for {path_str}: {e}")
|
|
34
|
+
|
|
35
|
+
def _process_delete(self, path_str: str):
|
|
36
|
+
if not path_str.endswith(".md"):
|
|
37
|
+
return
|
|
38
|
+
asyncio.run_coroutine_threadsafe(self._handle_delete(path_str), self.loop)
|
|
39
|
+
|
|
40
|
+
async def _handle_delete(self, path_str: str):
|
|
41
|
+
try:
|
|
42
|
+
filename = Path(path_str).name
|
|
43
|
+
match = re.match(r"([A-Z]+-\d{4})", filename)
|
|
44
|
+
if match:
|
|
45
|
+
issue_id = match.group(1)
|
|
46
|
+
await self.on_delete({"id": issue_id})
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.error(f"Error handling delete for {path_str}: {e}")
|
|
49
|
+
|
|
50
|
+
def on_created(self, event):
|
|
51
|
+
if not event.is_directory:
|
|
52
|
+
self._process_upsert(event.src_path)
|
|
53
|
+
|
|
54
|
+
def on_modified(self, event):
|
|
55
|
+
if not event.is_directory:
|
|
56
|
+
self._process_upsert(event.src_path)
|
|
57
|
+
|
|
58
|
+
def on_deleted(self, event):
|
|
59
|
+
if not event.is_directory:
|
|
60
|
+
self._process_delete(event.src_path)
|
|
61
|
+
|
|
62
|
+
def on_moved(self, event):
|
|
63
|
+
if not event.is_directory:
|
|
64
|
+
self._process_delete(event.src_path)
|
|
65
|
+
self._process_upsert(event.dest_path)
|
|
66
|
+
|
|
67
|
+
class IssueMonitor:
|
|
68
|
+
"""
|
|
69
|
+
Monitor the Issues directory for changes using Watchdog and trigger callbacks.
|
|
70
|
+
"""
|
|
71
|
+
def __init__(self, issues_root: Path, on_upsert: Callable[[dict], Awaitable[None]], on_delete: Callable[[dict], Awaitable[None]]):
|
|
72
|
+
self.issues_root = issues_root
|
|
73
|
+
self.on_upsert = on_upsert
|
|
74
|
+
self.on_delete = on_delete
|
|
75
|
+
self.observer = Observer()
|
|
76
|
+
self.loop = None
|
|
77
|
+
|
|
78
|
+
async def start(self):
|
|
79
|
+
self.loop = asyncio.get_running_loop()
|
|
80
|
+
event_handler = IssueEventHandler(self.loop, self.on_upsert, self.on_delete)
|
|
81
|
+
|
|
82
|
+
if not self.issues_root.exists():
|
|
83
|
+
logger.warning(f"Issues root {self.issues_root} does not exist. creating...")
|
|
84
|
+
self.issues_root.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
self.observer.schedule(event_handler, str(self.issues_root), recursive=True)
|
|
87
|
+
self.observer.start()
|
|
88
|
+
logger.info(f"Issue Monitor started (Watchdog). Watching {self.issues_root}")
|
|
89
|
+
|
|
90
|
+
def stop(self):
|
|
91
|
+
if self.observer.is_alive():
|
|
92
|
+
self.observer.stop()
|
|
93
|
+
self.observer.join()
|
|
94
|
+
logger.info(f"Issue Monitor stopped for {self.issues_root}")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Issue Management (Agent Guidance)
|
|
2
|
+
|
|
3
|
+
## Issue Management
|
|
4
|
+
|
|
5
|
+
System for managing tasks using `monoco issue`.
|
|
6
|
+
|
|
7
|
+
- **Create**: `monoco issue create <type> -t "Title"` (types: epic, feature, chore, fix)
|
|
8
|
+
- **Status**: `monoco issue open|close|backlog <id>`
|
|
9
|
+
- **Check**: `monoco issue lint` (Must run after manual edits)
|
|
10
|
+
- **Lifecycle**: `monoco issue start|submit|delete <id>`
|
|
11
|
+
- **Sync Context**: `monoco issue sync-files [id]` (Update file tracking)
|
|
12
|
+
- **Structure**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (e.g. `Issues/Features/open/`). Do not deviate.
|
|
13
|
+
- **Rules**:
|
|
14
|
+
1. **Heading**: Must have `## {ID}: {Title}` (matches metadata).
|
|
15
|
+
2. **Checkboxes**: Min 2 using `- [ ]`, `- [x]`, `- [-]`, `- [/]`.
|
|
16
|
+
3. **Review**: `## Review Comments` section required for Review/Done stages.
|
|
17
|
+
4. **Environment Policies**:
|
|
18
|
+
- Must use `monoco issue start --branch`.
|
|
19
|
+
- 🛑 **NO** direct coding on `main`/`master` (Linter will fail).
|
|
20
|
+
- Must update `files` field after coding (via `sync-files` or manual).
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monoco-issue
|
|
3
|
+
description: Official skill for Monoco Issue System. Treats Issues as Universal Atoms, managing the lifecycle of Epic/Feature/Chore/Fix.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Issue Management
|
|
7
|
+
|
|
8
|
+
Use this skill to create and manage **Issues** (Universal Atoms) in Monoco projects.
|
|
9
|
+
|
|
10
|
+
## Core Ontology
|
|
11
|
+
|
|
12
|
+
### 1. Strategy Layer
|
|
13
|
+
|
|
14
|
+
- **🏆 EPIC**: Grand goals, vision containers. Mindset: Architect.
|
|
15
|
+
|
|
16
|
+
### 2. Value Layer
|
|
17
|
+
|
|
18
|
+
- **✨ FEATURE**: Value increments from user perspective. Mindset: Product Owner.
|
|
19
|
+
- **Atomicity Principle**: Feature = Design + Dev + Test + Doc + i18n. They are one.
|
|
20
|
+
|
|
21
|
+
### 3. Execution Layer
|
|
22
|
+
|
|
23
|
+
- **🧹 CHORE**: Engineering maintenance, no direct user value. Mindset: Builder.
|
|
24
|
+
- **🐞 FIX**: Correcting deviations. Mindset: Debugger.
|
|
25
|
+
|
|
26
|
+
## Workflow Policies
|
|
27
|
+
|
|
28
|
+
### 1. Strict Git Workflow
|
|
29
|
+
|
|
30
|
+
Monoco enforces a **Feature Branch** model.
|
|
31
|
+
|
|
32
|
+
- **Start**: Must use `monoco issue start <ID> --branch` to start working. This creates a `feat/<ID>-<slug>` branch.
|
|
33
|
+
- **Protected Main**: **NO** direct modification on `main`, `master`, or `production` branches. Linter will block this.
|
|
34
|
+
- **Submit**: Run `monoco issue submit <ID>` before PR to clean up and validate.
|
|
35
|
+
|
|
36
|
+
### 2. File Tracking
|
|
37
|
+
|
|
38
|
+
Agents must track modified files to maintain Self-Contained Context.
|
|
39
|
+
|
|
40
|
+
- **Mechanism**: Issue Ticket Front Matter contains a `files: []` field.
|
|
41
|
+
- **Automated (Recommended)**: Run `monoco issue sync-files` inside the Feature Branch. It diffs against the base branch.
|
|
42
|
+
- **Manual (Fallback)**: If working without branches, Agent MUST **actively** append modified paths to the `files` list.
|
|
43
|
+
|
|
44
|
+
## Guidelines
|
|
45
|
+
|
|
46
|
+
### Directory Structure & Naming
|
|
47
|
+
|
|
48
|
+
`Issues/{CapitalizedPluralType}/{lowercase_status}/`
|
|
49
|
+
|
|
50
|
+
- **Types**: `Epics`, `Features`, `Chores`, `Fixes`
|
|
51
|
+
- **Statuses**: `open`, `backlog`, `closed`
|
|
52
|
+
|
|
53
|
+
### Structural Integrity
|
|
54
|
+
|
|
55
|
+
Issues are validated via `monoco issue lint`. key constraints:
|
|
56
|
+
|
|
57
|
+
1. **Mandatory Heading**: `## {ID}: {Title}` must match front matter.
|
|
58
|
+
2. **Min Checkboxes**: At least 2 checkboxes (AC/Tasks).
|
|
59
|
+
3. **Review Protocol**: `## Review Comments` required for `review` or `done` stages.
|
|
60
|
+
|
|
61
|
+
### Path Transitions
|
|
62
|
+
|
|
63
|
+
Use `monoco issue`:
|
|
64
|
+
|
|
65
|
+
1. **Create**: `monoco issue create <type> --title "..."`
|
|
66
|
+
- Params: `--parent <id>`, `--dependency <id>`, `--related <id>`, `--sprint <id>`, `--tags <tag>`
|
|
67
|
+
|
|
68
|
+
2. **Transition**: `monoco issue open/close/backlog <id>`
|
|
69
|
+
|
|
70
|
+
3. **View**: `monoco issue scope`
|
|
71
|
+
|
|
72
|
+
4. **Validation**: `monoco issue lint`
|
|
73
|
+
|
|
74
|
+
5. **Modification**: `monoco issue start/submit/delete <id>`
|
|
75
|
+
|
|
76
|
+
6. **Sync**: `monoco issue sync-files [id]` (Sync code changes to Issue file)
|
|
77
|
+
|
|
78
|
+
7. **Validation**: `monoco issue lint` (Enforces compliance)
|
|
79
|
+
|
|
80
|
+
## Validation Rules (FEAT-0082)
|
|
81
|
+
|
|
82
|
+
To ensure data integrity, all Issue tickets must follow these strict rules:
|
|
83
|
+
|
|
84
|
+
### 1. Structural Consistency
|
|
85
|
+
|
|
86
|
+
- Must contain a Level 2 Heading matching exactly: `## {ID}: {Title}`.
|
|
87
|
+
- Example: `## FEAT-0082: Issue Ticket Validator`
|
|
88
|
+
|
|
89
|
+
### 2. Content Completeness
|
|
90
|
+
|
|
91
|
+
- **Checkboxes**: Minimum of 2 checkboxes required (one for AC, one for Tasks).
|
|
92
|
+
- **Review Comments**: If `stage` is `review` or `done`, a `## Review Comments` section is mandatory and must not be empty.
|
|
93
|
+
|
|
94
|
+
### 3. Checkbox Syntax & Hierarchy
|
|
95
|
+
|
|
96
|
+
- Use only `- [ ]`, `- [x]`, `- [-]`, or `- [/]`.
|
|
97
|
+
- **Inheritance**: If nested checkboxes exist, the parent state must reflect child states (e.g., if any child is `[/]`, parent must be `[/]`; if all children are `[x]`, parent must be `[x]`).
|
|
98
|
+
|
|
99
|
+
### 4. State Matrix
|
|
100
|
+
|
|
101
|
+
The `status` (folder) and `stage` (front matter) must be compatible:
|
|
102
|
+
|
|
103
|
+
- **open**: Draft, Doing, Review, Done
|
|
104
|
+
- **backlog**: Draft, Doing, Review
|
|
105
|
+
- **closed**: Done
|
|
106
|
+
|
|
107
|
+
### 5. Environment Policy
|
|
108
|
+
|
|
109
|
+
Linter includes environment-aware guardrails:
|
|
110
|
+
|
|
111
|
+
- 🛑 **Dirty Main Protection**: Fails if uncommitted changes are detected on protected branches (`main`/`master`).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Issue 管理 (Agent 指引)
|
|
2
|
+
|
|
3
|
+
## Issue 管理
|
|
4
|
+
|
|
5
|
+
使用 `monoco issue` 管理任务的系统。
|
|
6
|
+
|
|
7
|
+
- **创建**: `monoco issue create <type> -t "标题"` (类型: epic, feature, chore, fix)
|
|
8
|
+
- **状态**: `monoco issue open|close|backlog <id>`
|
|
9
|
+
- **检查**: `monoco issue lint` (手动编辑后必须运行)
|
|
10
|
+
- **生命周期**: `monoco issue start|submit|delete <id>`
|
|
11
|
+
- **上下文同步**: `monoco issue sync-files [id]` (更新文件追踪)
|
|
12
|
+
- **结构**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (如 `Issues/Features/open/`)。
|
|
13
|
+
- **强制规则**:
|
|
14
|
+
1. **标题**: 必须包含 `## {ID}: {Title}` 标题(与 Front Matter 一致)。
|
|
15
|
+
2. **内容**: 至少 2 个 Checkbox,使用 `- [ ]`, `- [x]`, `- [-]`, `- [/]`。
|
|
16
|
+
3. **评审**: `review`/`done` 阶段必须包含 `## Review Comments` 章节且内容不为空。
|
|
17
|
+
4. **环境策略**:
|
|
18
|
+
- 必须使用 `monoco issue start --branch` 创建 Feature 分支。
|
|
19
|
+
- 🛑 **禁止**直接在 `main`/`master` 分支修改代码 (Linter 会报错)。
|
|
20
|
+
- 修改代码后**必须**更新 `files` 字段(通过 `sync-files` 或手动)。
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monoco-issue
|
|
3
|
+
description: Monoco Issue System 的官方技能定义。将 Issue 视为通用原子 (Universal Atom),管理 Epic/Feature/Chore/Fix 的生命周期。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 自我管理 (Monoco Issue System)
|
|
7
|
+
|
|
8
|
+
使用此技能在 Monoco 项目中创建和管理 **Issue** (通用原子)。该系统参考 Jira 表达体系,同时保持 "建设者 (Builder)" 和 "调试者 (Debugger)" 思维模式的隔离。
|
|
9
|
+
|
|
10
|
+
## 核心本体论 (Core Ontology)
|
|
11
|
+
|
|
12
|
+
Monoco 不仅仅复刻 Jira,而是基于 **"思维模式 (Mindset)"** 重新定义工作单元。
|
|
13
|
+
|
|
14
|
+
### 1. 战略层 (Strategy)
|
|
15
|
+
|
|
16
|
+
#### 🏆 EPIC (史诗)
|
|
17
|
+
|
|
18
|
+
- **Mindset**: _Architect_ (架构师)
|
|
19
|
+
- **定义**: 跨越多个周期的宏大目标。它不是单纯的"大任务",而是"愿景的容器"。
|
|
20
|
+
- **产出**: 定义了系统的边界和核心价值。
|
|
21
|
+
|
|
22
|
+
### 2. 价值层 (Value)
|
|
23
|
+
|
|
24
|
+
#### ✨ FEATURE (特性)
|
|
25
|
+
|
|
26
|
+
- **Mindset**: _Product Owner_ (产品负责人)
|
|
27
|
+
- **定义**: 用户视角的价值增量。必须是可独立交付 (Shippable) 的垂直切片。
|
|
28
|
+
- **Focus**: "Why" & "What" (用户想要什么?)。
|
|
29
|
+
- **Prefix**: `FEAT-`
|
|
30
|
+
|
|
31
|
+
### 3. 执行层 (Execution)
|
|
32
|
+
|
|
33
|
+
#### 🧹 CHORE (杂务)
|
|
34
|
+
|
|
35
|
+
- **Mindset**: _Builder_ (建设者)
|
|
36
|
+
- **定义**: **不产生**直接用户价值的工程性事务。
|
|
37
|
+
- **场景**: 架构升级、写构建脚本、修复 CI/CD 流水线。
|
|
38
|
+
- **Focus**: "How" (为了支撑系统运转,必须做什么)。
|
|
39
|
+
- **Prefix**: `CHORE-`
|
|
40
|
+
|
|
41
|
+
> 注: 取代了传统的 Task 概念。
|
|
42
|
+
|
|
43
|
+
#### 🐞 FIX (修复)
|
|
44
|
+
|
|
45
|
+
- **Mindset**: _Debugger_ (调试者)
|
|
46
|
+
- **定义**: 预期与现实的偏差。它是负价值的修正。
|
|
47
|
+
- **Focus**: "Fix" (恢复原状)。
|
|
48
|
+
- **Prefix**: `FIX-`
|
|
49
|
+
|
|
50
|
+
> 注: 取代了传统的 Bug 概念。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
**关系链**:
|
|
55
|
+
|
|
56
|
+
- **主要**: `EPIC` (愿景) -> `FEATURE` (价值交付单元)
|
|
57
|
+
- **次要**: `CHORE` (工程维护/支撑) - 通常独立存在。
|
|
58
|
+
- **原子性原则**: Feature = Design + Dev + Test + Doc + i18n。它们是一体的。
|
|
59
|
+
|
|
60
|
+
## 工作流策略 (Workflow Policies)
|
|
61
|
+
|
|
62
|
+
### 1. 严格 Git 工作流 (Strict Git Workflow)
|
|
63
|
+
|
|
64
|
+
Monoco 强制采用 **Feature Branch** 模式。
|
|
65
|
+
|
|
66
|
+
- **Start**: 必须使用 `monoco issue start <ID> --branch` 启动任务。这会自动创建 `feat/<ID>-<slug>` 分支。
|
|
67
|
+
- **禁止主干开发**: **严禁** 直接在 `main`, `master`, `production` 分支上修改代码。Linter 会拦截此类行为。
|
|
68
|
+
- **Submit**: 在提交 PR 前,运行 `monoco issue submit <ID>` 进行清理和预发布检查。
|
|
69
|
+
|
|
70
|
+
### 2. 文件追踪 (File Tracking)
|
|
71
|
+
|
|
72
|
+
为了保证上下文的自包含性 (Self-Contained Context),Agent 必须记录修改过的文件。
|
|
73
|
+
|
|
74
|
+
- **机制**: Issue Ticket 的 Front Matter 包含 `files: []` 字段。
|
|
75
|
+
- **自动化 (推荐)**: 在 Feature Branch 中运行 `monoco issue sync-files`。它会自动对比当前分支与 Base 分支的差异并更新列表。
|
|
76
|
+
- **手动 (备选)**: 如果进行非分支开发,Agent 必须**主动**将修改的文件路径写入 `files` 列表。
|
|
77
|
+
|
|
78
|
+
## 准则 (Guidelines)
|
|
79
|
+
|
|
80
|
+
### 目录结构
|
|
81
|
+
|
|
82
|
+
`Issues/{CapitalizedPluralType}/{lowercase_status}/`
|
|
83
|
+
|
|
84
|
+
- `{TYPE}`: `Epics`, `Features`, `Chores`, `Fixes`
|
|
85
|
+
- `{STATUS}`: `open`, `backlog`, `closed`
|
|
86
|
+
|
|
87
|
+
### 路径流转
|
|
88
|
+
|
|
89
|
+
使用 `monoco issue`:
|
|
90
|
+
|
|
91
|
+
1. **Create**: `monoco issue create <type> --title "..."`
|
|
92
|
+
- Params: `--parent <id>`, `--dependency <id>`, `--related <id>`, `--sprint <id>`, `--tags <tag>`
|
|
93
|
+
|
|
94
|
+
2. **Transition**: `monoco issue open/close/backlog <id>`
|
|
95
|
+
|
|
96
|
+
3. **View**: `monoco issue scope`
|
|
97
|
+
|
|
98
|
+
4. **Validation**: `monoco issue lint`
|
|
99
|
+
|
|
100
|
+
5. **Modification**: `monoco issue start/submit/delete <id>`
|
|
101
|
+
|
|
102
|
+
6. **Sync**: `monoco issue sync-files [id]` (同步代码变更到 Issue 文件)
|
|
103
|
+
|
|
104
|
+
7. **Validation**: `monoco issue lint` (强制执行合规性检查)
|
|
105
|
+
|
|
106
|
+
## 合规与结构校验 (Validation Rules)
|
|
107
|
+
|
|
108
|
+
为了确保数据严谨性,所有 Issue Ticket 必须遵循以下强制规则:
|
|
109
|
+
|
|
110
|
+
### 1. 结构一致性 (Structural Consistency)
|
|
111
|
+
|
|
112
|
+
- 必须包含一个二级标题 (`##`),内容必须与 Front Matter 中的 ID 和 Title 严格匹配。
|
|
113
|
+
- 格式: `## {ID}: {Title}`
|
|
114
|
+
- 示例: `## FEAT-0082: Issue Ticket Validator`
|
|
115
|
+
|
|
116
|
+
### 2. 内容完整性 (Content Completeness)
|
|
117
|
+
|
|
118
|
+
- **Checkbox 数量**: 每个 Ticket 必须包含至少 2 个 Checkbox(通常代表 AC 和 Tasks)。
|
|
119
|
+
- **评审记录**: 当 `stage` 为 `review` 或 `done` 时,必须包含 `## Review Comments` 标题且内容不能为空。
|
|
120
|
+
|
|
121
|
+
### 3. Checkbox 语法与层级 (Checkbox Matrix)
|
|
122
|
+
|
|
123
|
+
- 仅限使用: `- [ ]`, `- [x]`, `- [-]`, `- [/]`。
|
|
124
|
+
- **层级继承**: 若存在嵌套 Checkbox,父项状态必须正确反映子项的聚合结果(例如: 任一子项为 `[/]` 则父项必为 `[/]`;子项全选则父项为 `[x]`)。
|
|
125
|
+
|
|
126
|
+
### 4. 状态矩阵 (State Matrix)
|
|
127
|
+
|
|
128
|
+
`status` (物理存放目录) 与 `stage` (Front Matter 字段) 必须兼容:
|
|
129
|
+
|
|
130
|
+
- **open**: Draft, Doing, Review, Done
|
|
131
|
+
- **backlog**: Draft, Doing, Review
|
|
132
|
+
- **closed**: Done
|
|
133
|
+
|
|
134
|
+
### 5. 环境策略 (Environment Policy)
|
|
135
|
+
|
|
136
|
+
Linter 包含环境感知防护:
|
|
137
|
+
|
|
138
|
+
- 🛑 **Dirty Main Protection**: 当检测到处于受保护分支 (`main`/`master`) 且存在未提交变更时,Lint 将失败并阻止操作。
|