monoco-toolkit 0.1.5__py3-none-any.whl → 0.1.6__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 (43) hide show
  1. monoco/core/config.py +60 -8
  2. monoco/core/feature.py +58 -0
  3. monoco/core/injection.py +196 -0
  4. monoco/core/integrations.py +181 -0
  5. monoco/core/registry.py +36 -0
  6. monoco/core/resources/en/AGENTS.md +8 -0
  7. monoco/core/resources/en/SKILL.md +66 -0
  8. monoco/core/resources/zh/AGENTS.md +8 -0
  9. monoco/core/resources/zh/SKILL.md +66 -0
  10. monoco/core/setup.py +40 -24
  11. monoco/core/skills.py +444 -0
  12. monoco/core/sync.py +224 -0
  13. monoco/core/workspace.py +2 -6
  14. monoco/daemon/services.py +1 -1
  15. monoco/features/config/commands.py +104 -44
  16. monoco/features/i18n/adapter.py +29 -0
  17. monoco/features/i18n/core.py +1 -11
  18. monoco/features/i18n/resources/en/AGENTS.md +8 -0
  19. monoco/features/i18n/resources/en/SKILL.md +94 -0
  20. monoco/features/i18n/resources/zh/AGENTS.md +8 -0
  21. monoco/features/i18n/resources/zh/SKILL.md +94 -0
  22. monoco/features/issue/adapter.py +34 -0
  23. monoco/features/issue/commands.py +8 -8
  24. monoco/features/issue/core.py +5 -16
  25. monoco/features/issue/migration.py +134 -0
  26. monoco/features/issue/models.py +5 -3
  27. monoco/features/issue/resources/en/AGENTS.md +9 -0
  28. monoco/features/issue/resources/en/SKILL.md +51 -0
  29. monoco/features/issue/resources/zh/AGENTS.md +9 -0
  30. monoco/features/issue/resources/zh/SKILL.md +85 -0
  31. monoco/features/spike/adapter.py +30 -0
  32. monoco/features/spike/core.py +3 -20
  33. monoco/features/spike/resources/en/AGENTS.md +7 -0
  34. monoco/features/spike/resources/en/SKILL.md +74 -0
  35. monoco/features/spike/resources/zh/AGENTS.md +7 -0
  36. monoco/features/spike/resources/zh/SKILL.md +74 -0
  37. monoco/main.py +4 -0
  38. {monoco_toolkit-0.1.5.dist-info → monoco_toolkit-0.1.6.dist-info}/METADATA +1 -1
  39. monoco_toolkit-0.1.6.dist-info/RECORD +62 -0
  40. monoco_toolkit-0.1.5.dist-info/RECORD +0 -36
  41. {monoco_toolkit-0.1.5.dist-info → monoco_toolkit-0.1.6.dist-info}/WHEEL +0 -0
  42. {monoco_toolkit-0.1.5.dist-info → monoco_toolkit-0.1.6.dist-info}/entry_points.txt +0 -0
  43. {monoco_toolkit-0.1.5.dist-info → monoco_toolkit-0.1.6.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: monoco-i18n
3
+ description: 文档国际化质量控制。确保多语言文档保持同步。
4
+ ---
5
+
6
+ # 文档国际化
7
+
8
+ 管理 Monoco 项目文档的国际化。
9
+
10
+ ## 概述
11
+
12
+ I18n 功能提供:
13
+
14
+ - **自动扫描**缺失的翻译
15
+ - **标准化结构**用于多语言文档
16
+ - **质量控制**以维护文档一致性
17
+
18
+ ## 核心命令
19
+
20
+ ### 扫描缺失的翻译
21
+
22
+ ```bash
23
+ monoco i18n scan
24
+ ```
25
+
26
+ 扫描项目中的 markdown 文件并报告缺失的翻译。
27
+
28
+ **输出**:
29
+
30
+ - 列出没有对应翻译的源文件
31
+ - 显示缺少哪些目标语言
32
+ - 遵循 `.gitignore` 和默认排除规则
33
+
34
+ ## 配置
35
+
36
+ I18n 设置在 `.monoco/config.yaml` 中配置:
37
+
38
+ ```yaml
39
+ i18n:
40
+ source_lang: en # 源语言代码
41
+ target_langs: # 目标语言代码
42
+ - zh
43
+ - ja
44
+ ```
45
+
46
+ ## 文档结构
47
+
48
+ ### 根文件(后缀模式)
49
+
50
+ 对于项目根目录中的文件:
51
+
52
+ - 源文件: `README.md`
53
+ - 中文: `README_ZH.md`
54
+ - 日文: `README_JA.md`
55
+
56
+ ### 子目录文件(目录模式)
57
+
58
+ 对于 `docs/` 或其他目录中的文件:
59
+
60
+ ```
61
+ docs/
62
+ ├── en/
63
+ │ ├── guide.md
64
+ │ └── api.md
65
+ ├── zh/
66
+ │ ├── guide.md
67
+ │ └── api.md
68
+ └── ja/
69
+ ├── guide.md
70
+ └── api.md
71
+ ```
72
+
73
+ ## 排除规则
74
+
75
+ 以下内容会自动从 i18n 扫描中排除:
76
+
77
+ - `.gitignore` 模式(自动遵循)
78
+ - `.references/` 目录
79
+ - 构建产物(`dist/`, `build/`, `node_modules/`)
80
+ - `Issues/` 目录
81
+
82
+ ## 最佳实践
83
+
84
+ 1. **先创建英文版**: 首先用源语言编写文档
85
+ 2. **遵循命名约定**: 使用适当的模式(后缀或目录)
86
+ 3. **定期运行扫描**: 使用 `monoco i18n scan` 验证覆盖率
87
+ 4. **提交所有语言**: 将翻译保存在版本控制中
88
+
89
+ ## 工作流程
90
+
91
+ 1. 用源语言(如英语)编写文档
92
+ 2. 按照命名约定创建翻译文件
93
+ 3. 运行 `monoco i18n scan` 验证所有翻译是否存在
94
+ 4. 修复扫描报告的任何缺失翻译
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+ from typing import Dict
3
+ from monoco.core.feature import MonocoFeature, IntegrationData
4
+ from monoco.features.issue import core
5
+
6
+ class IssueFeature(MonocoFeature):
7
+ @property
8
+ def name(self) -> str:
9
+ return "issue"
10
+
11
+ def initialize(self, root: Path, config: Dict) -> None:
12
+ issues_path = root / config.get("paths", {}).get("issues", "Issues")
13
+ core.init(issues_path)
14
+
15
+ def integrate(self, root: Path, config: Dict) -> IntegrationData:
16
+ # Determine language from config, default to 'en'
17
+ lang = config.get("i18n", {}).get("source_lang", "en")
18
+
19
+ # Current file is in monoco/features/issue/adapter.py
20
+ # Resource path: monoco/features/issue/resources/{lang}/AGENTS.md
21
+ base_dir = Path(__file__).parent / "resources"
22
+
23
+ # Try specific language, fallback to 'en'
24
+ prompt_file = base_dir / lang / "AGENTS.md"
25
+ if not prompt_file.exists():
26
+ prompt_file = base_dir / "en" / "AGENTS.md"
27
+
28
+ content = ""
29
+ if prompt_file.exists():
30
+ content = prompt_file.read_text(encoding="utf-8").strip()
31
+
32
+ return IntegrationData(
33
+ system_prompts={"Issue Management": content}
34
+ )
@@ -23,7 +23,7 @@ def create(
23
23
  title: str = typer.Option(..., "--title", "-t", help="Issue title"),
24
24
  parent: Optional[str] = typer.Option(None, "--parent", "-p", help="Parent Issue ID"),
25
25
  is_backlog: bool = typer.Option(False, "--backlog", help="Create as backlog item"),
26
- stage: Optional[IssueStage] = typer.Option(None, "--stage", help="Issue stage (todo, doing, review)"),
26
+ stage: Optional[IssueStage] = typer.Option(None, "--stage", help="Issue stage (draft, doing, review)"),
27
27
  dependencies: List[str] = typer.Option([], "--dependency", "-d", help="Issue dependency ID(s)"),
28
28
  related: List[str] = typer.Option([], "--related", "-r", help="Related Issue ID(s)"),
29
29
  subdir: Optional[str] = typer.Option(None, "--subdir", "-s", help="Subdirectory for organization (e.g. 'Backend/Auth')"),
@@ -73,13 +73,13 @@ def move_open(
73
73
  issue_id: str = typer.Argument(..., help="Issue ID to open"),
74
74
  root: Optional[str] = typer.Option(None, "--root", help="Override issues root directory"),
75
75
  ):
76
- """Move issue to open status and set stage to Todo."""
76
+ """Move issue to open status and set stage to Draft."""
77
77
  config = get_config()
78
78
  issues_root = _resolve_issues_root(config, root)
79
79
  try:
80
80
  # Pull operation: Force stage to TODO
81
- core.update_issue(issues_root, issue_id, status=IssueStatus.OPEN, stage=IssueStage.TODO)
82
- console.print(f"[green]▶[/green] Issue [bold]{issue_id}[/bold] moved to open/todo.")
81
+ core.update_issue(issues_root, issue_id, status=IssueStatus.OPEN, stage=IssueStage.DRAFT)
82
+ console.print(f"[green]▶[/green] Issue [bold]{issue_id}[/bold] moved to open/draft.")
83
83
  except Exception as e:
84
84
  console.print(f"[red]✘ Error:[/red] {str(e)}")
85
85
  raise typer.Exit(code=1)
@@ -210,11 +210,11 @@ def pull(
210
210
  issue_id: str = typer.Argument(..., help="Issue ID to pull from backlog"),
211
211
  root: Optional[str] = typer.Option(None, "--root", help="Override issues root directory"),
212
212
  ):
213
- """Pull issue from backlog (Open & Todo)."""
213
+ """Pull issue from backlog (Open & Draft)."""
214
214
  config = get_config()
215
215
  issues_root = _resolve_issues_root(config, root)
216
216
  try:
217
- core.update_issue(issues_root, issue_id, status=IssueStatus.OPEN, stage=IssueStage.TODO)
217
+ core.update_issue(issues_root, issue_id, status=IssueStatus.OPEN, stage=IssueStage.DRAFT)
218
218
  console.print(f"[green]🔥[/green] Issue [bold]{issue_id}[/bold] pulled from backlog.")
219
219
  except Exception as e:
220
220
  console.print(f"[red]✘ Error:[/red] {str(e)}")
@@ -319,7 +319,7 @@ def board(
319
319
  columns: List[RenderableType] = []
320
320
 
321
321
  stage_titles = {
322
- "todo": "[bold white]TODO[/bold white]",
322
+ "draft": "[bold white]DRAFT[/bold white]",
323
323
  "doing": "[bold yellow]DOING[/bold yellow]",
324
324
  "review": "[bold cyan]REVIEW[/bold cyan]",
325
325
  "done": "[bold green]DONE[/bold green]"
@@ -547,7 +547,7 @@ def _resolve_issues_root(config, cli_root: Optional[str]) -> Path:
547
547
  # We need to detect if we are in a Workspace Root with multiple projects
548
548
  cwd = Path.cwd()
549
549
 
550
- # If CWD is NOT a project root (no monoco.yaml/Issues), scan for subprojects
550
+ # If CWD is NOT a project root (no .monoco/), scan for subprojects
551
551
  if not is_project_root(cwd):
552
552
  subprojects = find_projects(cwd)
553
553
  if len(subprojects) > 1:
@@ -583,15 +583,6 @@ description: Monoco Issue System 的官方技能定义。将 Issue 视为通用
583
583
  5. **Modification**: `monoco issue start/submit/delete <id>`
584
584
  """
585
585
 
586
- PROMPT_CONTENT = """
587
- ### Issue Management
588
- System for managing tasks using `monoco issue`.
589
- - **Create**: `monoco issue create <type> -t "Title"` (types: epic, feature, chore, fix)
590
- - **Status**: `monoco issue open|close|backlog <id>`
591
- - **Check**: `monoco issue lint` (Must run after manual edits)
592
- - **Lifecycle**: `monoco issue start|submit|delete <id>`
593
- - **Structure**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (e.g. `Issues/Features/open/`). Do not deviate.
594
- """
595
586
 
596
587
  def init(issues_root: Path):
597
588
  """Initialize the Issues directory structure."""
@@ -612,9 +603,7 @@ def get_resources() -> Dict[str, Any]:
612
603
  "skills": {
613
604
  "issues-management": SKILL_CONTENT
614
605
  },
615
- "prompts": {
616
- "issues-management": PROMPT_CONTENT
617
- }
606
+ "prompts": {} # Handled by adapter via resource files
618
607
  }
619
608
 
620
609
 
@@ -662,7 +651,7 @@ def get_board_data(issues_root: Path) -> Dict[str, List[IssueMetadata]]:
662
651
  Get open issues grouped by their stage for Kanban view.
663
652
  """
664
653
  board = {
665
- IssueStage.TODO.value: [],
654
+ IssueStage.DRAFT.value: [],
666
655
  IssueStage.DOING.value: [],
667
656
  IssueStage.REVIEW.value: [],
668
657
  IssueStage.DONE.value: []
@@ -1042,11 +1031,11 @@ def recalculate_parent(issues_root: Path, parent_id: str):
1042
1031
  # FEAT-0003 Req: "If first child starts doing, auto-start Parent?"
1043
1032
  # If parent is OPEN/TODO and child is DOING/REVIEW/DONE, set parent to DOING?
1044
1033
  current_status = data.get("status", "open").lower()
1045
- current_stage = data.get("stage", "todo").lower()
1034
+ current_stage = data.get("stage", "draft").lower()
1046
1035
 
1047
- if current_status == "open" and current_stage == "todo":
1036
+ if current_status == "open" and current_stage == "draft":
1048
1037
  # Check if any child is active
1049
- active_children = [c for c in children if c.status == IssueStatus.OPEN and c.stage != IssueStage.TODO]
1038
+ active_children = [c for c in children if c.status == IssueStatus.OPEN and c.stage != IssueStage.DRAFT]
1050
1039
  closed_children = [c for c in children if c.status == IssueStatus.CLOSED]
1051
1040
 
1052
1041
  if active_children or closed_children:
@@ -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)
@@ -61,7 +61,7 @@ class IssueStatus(str, Enum):
61
61
  BACKLOG = "backlog"
62
62
 
63
63
  class IssueStage(str, Enum):
64
- TODO = "todo"
64
+ DRAFT = "draft"
65
65
  DOING = "doing"
66
66
  REVIEW = "review"
67
67
  DONE = "done"
@@ -125,6 +125,8 @@ class IssueMetadata(BaseModel):
125
125
  # Stage normalization
126
126
  if "stage" in v and isinstance(v["stage"], str):
127
127
  v["stage"] = v["stage"].lower()
128
+ if v["stage"] == "todo":
129
+ v["stage"] = "draft"
128
130
  return v
129
131
 
130
132
  @model_validator(mode='after')
@@ -132,7 +134,7 @@ class IssueMetadata(BaseModel):
132
134
  # Logic Definition:
133
135
  # status: backlog -> stage: null
134
136
  # status: closed -> stage: done
135
- # status: open -> stage: todo | doing | review (default todo)
137
+ # status: open -> stage: draft | doing | review (default draft)
136
138
 
137
139
  if self.status == IssueStatus.BACKLOG:
138
140
  self.stage = IssueStage.FREEZED
@@ -148,7 +150,7 @@ class IssueMetadata(BaseModel):
148
150
  elif self.status == IssueStatus.OPEN:
149
151
  # Ensure valid stage for open status
150
152
  if self.stage is None or self.stage == IssueStage.DONE:
151
- self.stage = IssueStage.TODO
153
+ self.stage = IssueStage.DRAFT
152
154
 
153
155
  return self
154
156
 
@@ -0,0 +1,9 @@
1
+ ### Issue Management
2
+
3
+ System for managing tasks using `monoco issue`.
4
+
5
+ - **Create**: `monoco issue create <type> -t "Title"` (types: epic, feature, chore, fix)
6
+ - **Status**: `monoco issue open|close|backlog <id>`
7
+ - **Check**: `monoco issue lint` (Must run after manual edits)
8
+ - **Lifecycle**: `monoco issue start|submit|delete <id>`
9
+ - **Structure**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (e.g. `Issues/Features/open/`). Do not deviate.
@@ -0,0 +1,51 @@
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
+ ## Guidelines
27
+
28
+ ### Directory Structure
29
+
30
+ `Issues/{CapitalizedPluralType}/{lowercase_status}/`
31
+
32
+ - `{TYPE}`: `Epics`, `Features`, `Chores`, `Fixes`
33
+ - `{STATUS}`: `open`, `backlog`, `closed`
34
+
35
+ ### Path Transitions
36
+
37
+ Use `monoco issue`:
38
+
39
+ 1. **Create**: `monoco issue create <type> --title "..."`
40
+
41
+ - Params: `--parent <id>`, `--dependency <id>`, `--related <id>`, `--sprint <id>`, `--tags <tag>`
42
+
43
+ 2. **Transition**: `monoco issue open/close/backlog <id>`
44
+
45
+ 3. **View**: `monoco issue scope`
46
+
47
+ 4. **Validation**: `monoco issue lint`
48
+
49
+ 5. **Modification**: `monoco issue start/submit/delete <id>`
50
+
51
+ 6. **Commit**: `monoco issue commit` (Atomic commit for issue files)
@@ -0,0 +1,9 @@
1
+ ### Issue 管理
2
+
3
+ 使用 `monoco issue` 管理任务的系统。
4
+
5
+ - **创建**: `monoco issue create <type> -t "标题"` (类型: epic, feature, chore, fix)
6
+ - **状态**: `monoco issue open|close|backlog <id>`
7
+ - **检查**: `monoco issue lint` (手动编辑后必须运行)
8
+ - **生命周期**: `monoco issue start|submit|delete <id>`
9
+ - **结构**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (例如 `Issues/Features/open/`)。不要偏离此结构。
@@ -0,0 +1,85 @@
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
+ ## 准则 (Guidelines)
61
+
62
+ ### 目录结构
63
+
64
+ `Issues/{CapitalizedPluralType}/{lowercase_status}/`
65
+
66
+ - `{TYPE}`: `Epics`, `Features`, `Chores`, `Fixes`
67
+ - `{STATUS}`: `open`, `backlog`, `closed`
68
+
69
+ ### 路径流转
70
+
71
+ 使用 `monoco issue`:
72
+
73
+ 1. **Create**: `monoco issue create <type> --title "..."`
74
+
75
+ - Params: `--parent <id>`, `--dependency <id>`, `--related <id>`, `--sprint <id>`, `--tags <tag>`
76
+
77
+ 2. **Transition**: `monoco issue open/close/backlog <id>`
78
+
79
+ 3. **View**: `monoco issue scope`
80
+
81
+ 4. **Validation**: `monoco issue lint`
82
+
83
+ 5. **Modification**: `monoco issue start/submit/delete <id>`
84
+
85
+ 6. **Commit**: `monoco issue commit` (Atomic commit for issue files)
@@ -0,0 +1,30 @@
1
+ from pathlib import Path
2
+ from typing import Dict
3
+ from monoco.core.feature import MonocoFeature, IntegrationData
4
+ from monoco.features.spike import core
5
+
6
+ class SpikeFeature(MonocoFeature):
7
+ @property
8
+ def name(self) -> str:
9
+ return "spike"
10
+
11
+ def initialize(self, root: Path, config: Dict) -> None:
12
+ spikes_name = config.get("paths", {}).get("spikes", ".references")
13
+ core.init(root, spikes_name)
14
+
15
+ def integrate(self, root: Path, config: Dict) -> IntegrationData:
16
+ # Determine language from config, default to 'en'
17
+ lang = config.get("i18n", {}).get("source_lang", "en")
18
+ base_dir = Path(__file__).parent / "resources"
19
+
20
+ prompt_file = base_dir / lang / "AGENTS.md"
21
+ if not prompt_file.exists():
22
+ prompt_file = base_dir / "en" / "AGENTS.md"
23
+
24
+ content = ""
25
+ if prompt_file.exists():
26
+ content = prompt_file.read_text(encoding="utf-8").strip()
27
+
28
+ return IntegrationData(
29
+ system_prompts={"Spike (Research)": content}
30
+ )
@@ -31,17 +31,10 @@ def run_git_command(cmd: List[str], cwd: Path) -> bool:
31
31
 
32
32
  def get_config_file_path(root: Path) -> Path:
33
33
  """Determine the config file to update."""
34
- # Priority 1: .monoco/config.yaml
34
+ # Standard: .monoco/config.yaml
35
35
  hidden = root / ".monoco" / "config.yaml"
36
- if hidden.exists():
37
- return hidden
38
36
 
39
- # Priority 2: monoco.yaml
40
- visible = root / "monoco.yaml"
41
- if visible.exists():
42
- return visible
43
-
44
- # Default to .monoco/config.yaml for new files
37
+ # Ensure parent exists
45
38
  hidden.parent.mkdir(exist_ok=True)
46
39
  return hidden
47
40
 
@@ -131,24 +124,14 @@ This skill normalizes how we introduce external code repositories.
131
124
  3. **Remove**: `monoco spike remove <name>`
132
125
  """
133
126
 
134
- PROMPT_CONTENT = """### Spike (Research)
135
- Manage external reference repositories.
136
- - **Add Repo**: `monoco spike add <url>` (Available in `.reference/<name>` for reading)
137
- - **Sync**: `monoco spike sync` (Run to download content)
138
- - **Constraint**: Never edit files in `.reference/`. Treat them as read-only external knowledge."""
139
-
140
127
  def init(root: Path, spikes_dir_name: str):
141
128
  """Initialize Spike environment."""
142
129
  ensure_gitignore(root, spikes_dir_name)
143
130
  (root / spikes_dir_name).mkdir(exist_ok=True)
144
131
 
145
- def get_resources() -> Dict[str, Any]:
146
132
  return {
147
133
  "skills": {
148
134
  "git-repo-spike": SKILL_CONTENT
149
135
  },
150
- "prompts": {
151
- "spike": PROMPT_CONTENT
152
- }
136
+ "prompts": {} # Handled by adapter via resource files
153
137
  }
154
-
@@ -0,0 +1,7 @@
1
+ ### Spike (Research)
2
+
3
+ Manage external reference repositories.
4
+
5
+ - **Add Repo**: `monoco spike add <url>` (Available in `.reference/<name>` for reading)
6
+ - **Sync**: `monoco spike sync` (Run to download content)
7
+ - **Constraint**: Never edit files in `.reference/`. Treat them as read-only external knowledge.