monoco-toolkit 0.3.5__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 +39 -5
- 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 +104 -0
- monoco/features/issue/criticality.py +553 -0
- monoco/features/issue/domain/models.py +28 -2
- monoco/features/issue/engine/machine.py +65 -37
- monoco/features/issue/git_service.py +185 -0
- monoco/features/issue/linter.py +291 -62
- monoco/features/issue/models.py +91 -14
- 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/test_priority_integration.py +1 -0
- monoco/features/issue/validator.py +185 -65
- monoco/features/memo/__init__.py +4 -0
- monoco/features/memo/adapter.py +32 -0
- monoco/features/memo/cli.py +112 -0
- monoco/features/memo/core.py +146 -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 +6 -3
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +56 -35
- 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.5.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -37,6 +37,48 @@ Monoco enforces a **Feature Branch** model.
|
|
|
37
37
|
- This moves the issue to `Review` stage and generates a Delivery Report.
|
|
38
38
|
- **Note**: This does **not** merge the code. You (or the user) must handle the Merge/PR process.
|
|
39
39
|
|
|
40
|
+
## Standardized Workflow
|
|
41
|
+
|
|
42
|
+
### Workflow Diagram
|
|
43
|
+
|
|
44
|
+
```mermaid
|
|
45
|
+
stateDiagram-v2
|
|
46
|
+
[*] --> Plan
|
|
47
|
+
Plan --> Build: monoco issue start
|
|
48
|
+
Build --> Submit: monoco issue submit
|
|
49
|
+
state "Oracle Loop" as Oracle {
|
|
50
|
+
Submit --> Review: Auto/Manual Review
|
|
51
|
+
Review --> Fix: Reject
|
|
52
|
+
Fix --> Submit: Retry
|
|
53
|
+
}
|
|
54
|
+
Review --> Merge: Approve
|
|
55
|
+
Merge --> [*]: monoco issue close
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Action Steps
|
|
59
|
+
|
|
60
|
+
1. **Plan Phase**:
|
|
61
|
+
- Ensure Issue exists and is in `Open` status.
|
|
62
|
+
- Verify requirements and tasks.
|
|
63
|
+
|
|
64
|
+
2. **Build Phase**:
|
|
65
|
+
- Run `monoco issue start <ID> --branch` (Verification: ensures branch is created).
|
|
66
|
+
- Implement features/fixes.
|
|
67
|
+
- Run `monoco issue sync-files` to track changes.
|
|
68
|
+
|
|
69
|
+
3. **Submit Phase (Oracle Loop)**:
|
|
70
|
+
- Run tests to ensure quality.
|
|
71
|
+
- Run `monoco issue lint`.
|
|
72
|
+
- Run `monoco issue submit <ID>`.
|
|
73
|
+
- **IF** errors/feedback received:
|
|
74
|
+
- Fix issues.
|
|
75
|
+
- Re-run tests.
|
|
76
|
+
- Re-submit.
|
|
77
|
+
|
|
78
|
+
4. **Merge Phase**:
|
|
79
|
+
- Once approved:
|
|
80
|
+
- Run `monoco issue close <ID> --solution completed --prune`.
|
|
81
|
+
|
|
40
82
|
### 2. File Tracking
|
|
41
83
|
|
|
42
84
|
Agents must track modified files to maintain Self-Contained Context.
|
|
@@ -113,3 +155,9 @@ The `status` (folder) and `stage` (front matter) must be compatible:
|
|
|
113
155
|
Linter includes environment-aware guardrails:
|
|
114
156
|
|
|
115
157
|
- 🛑 **Dirty Main Protection**: Fails if uncommitted changes are detected on protected branches (`main`/`master`).
|
|
158
|
+
|
|
159
|
+
### 6. ID Format & Hierarchy
|
|
160
|
+
|
|
161
|
+
- **ID Specification**: Issue IDs must strictly follow the `TYPE-XXXX` format, where `XXXX` are 4 digits (e.g., `FEAT-0001`, `FIX-9999`).
|
|
162
|
+
- **No Suffixes**: IDs with suffixes like `FEAT-0001-1` are strictly prohibited.
|
|
163
|
+
- **Hierarchy Expression**: Sub-features or sub-tasks must be expressed using the `parent` field in the Front Matter. Do NOT use ID naming conventions (like hierarchical suffixes) to express relationships.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: issue-lifecycle-workflow
|
|
3
|
+
description: Issue 生命周期工作流 (Flow Skill)。定义从创建到关闭的完整 Issue 管理流程,确保任务追踪和流程合规。
|
|
4
|
+
type: flow
|
|
5
|
+
domain: issue
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Issue Lifecycle Workflow
|
|
10
|
+
|
|
11
|
+
Issue 生命周期的标准化工作流,确保 "Open → Start → Develop → Submit → Review → Close" 流程。
|
|
12
|
+
|
|
13
|
+
## 工作流状态机
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
stateDiagram-v2
|
|
17
|
+
[*] --> Open: 创建 Issue
|
|
18
|
+
|
|
19
|
+
Open --> Start: 准备开发
|
|
20
|
+
Open --> Open: 需求不清<br/>(等待澄清)
|
|
21
|
+
|
|
22
|
+
Start --> Develop: 分支创建
|
|
23
|
+
Start --> Start: 分支冲突<br/>(解决冲突)
|
|
24
|
+
|
|
25
|
+
Develop --> Submit: 开发完成
|
|
26
|
+
Develop --> Develop: 测试失败<br/>(修复代码)
|
|
27
|
+
|
|
28
|
+
state "Oracle Loop" as ReviewLoop {
|
|
29
|
+
Submit --> Review: 提交评审
|
|
30
|
+
Review --> Fix: 拒绝
|
|
31
|
+
Fix --> Submit: 重新提交
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Review --> Close: 批准合并
|
|
35
|
+
|
|
36
|
+
Close --> [*]: 清理完成
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 执行步骤
|
|
40
|
+
|
|
41
|
+
### 1. Open (创建)
|
|
42
|
+
|
|
43
|
+
- **目标**: 创建清晰、可执行的 Issue
|
|
44
|
+
- **输入**: 需求描述、类型、优先级
|
|
45
|
+
- **输出**: Issue Ticket 文件
|
|
46
|
+
- **检查点**:
|
|
47
|
+
- [ ] 使用 `monoco issue create <type> -t "标题"`
|
|
48
|
+
- [ ] 选择合适的类型(epic/feature/chore/fix)
|
|
49
|
+
- [ ] 编写清晰的描述和验收标准
|
|
50
|
+
- [ ] 设置依赖关系(如需要)
|
|
51
|
+
- [ ] 确保至少 2 个 Checkbox
|
|
52
|
+
|
|
53
|
+
### 2. Start (启动)
|
|
54
|
+
|
|
55
|
+
- **目标**: 准备开发环境,创建功能分支
|
|
56
|
+
- **检查点**:
|
|
57
|
+
- [ ] 运行 `monoco issue start <ID> --branch`
|
|
58
|
+
- [ ] 确认分支已创建并切换
|
|
59
|
+
- [ ] 验证当前不在 main/master 分支
|
|
60
|
+
- [ ] 检查依赖 Issue 是否已完成
|
|
61
|
+
|
|
62
|
+
### 3. Develop (开发)
|
|
63
|
+
|
|
64
|
+
- **目标**: 实现功能或修复缺陷
|
|
65
|
+
- **策略**: 迭代开发,持续测试
|
|
66
|
+
- **检查点**:
|
|
67
|
+
- [ ] 遵循项目代码规范
|
|
68
|
+
- [ ] 编写/更新单元测试
|
|
69
|
+
- [ ] 运行测试套件,确保通过
|
|
70
|
+
- [ ] 定期提交代码(小步提交)
|
|
71
|
+
- [ ] 更新文件追踪(`monoco issue sync-files`)
|
|
72
|
+
|
|
73
|
+
### 4. Submit (提交)
|
|
74
|
+
|
|
75
|
+
- **目标**: 准备代码评审
|
|
76
|
+
- **检查点**:
|
|
77
|
+
- [ ] 运行 `monoco issue lint` 检查合规性
|
|
78
|
+
- [ ] 修复所有 Lint 错误
|
|
79
|
+
- [ ] 更新任务清单状态
|
|
80
|
+
- [ ] 运行 `monoco issue submit <ID>`
|
|
81
|
+
- [ ] 编写变更摘要
|
|
82
|
+
|
|
83
|
+
### 5. Review (评审)
|
|
84
|
+
|
|
85
|
+
- **目标**: 代码质量和流程合规检查
|
|
86
|
+
- **检查点**:
|
|
87
|
+
- [ ] 功能是否正确实现
|
|
88
|
+
- [ ] 代码是否符合设计规范
|
|
89
|
+
- [ ] 测试是否充分
|
|
90
|
+
- [ ] 文档是否同步更新
|
|
91
|
+
- [ ] 是否遵循项目规范
|
|
92
|
+
|
|
93
|
+
### 6. Close (关闭)
|
|
94
|
+
|
|
95
|
+
- **目标**: 完成 Issue,清理环境
|
|
96
|
+
- **检查点**:
|
|
97
|
+
- [ ] 代码已合并到主分支
|
|
98
|
+
- [ ] 运行 `monoco issue close <ID> --solution completed --prune`
|
|
99
|
+
- [ ] 验证分支已清理
|
|
100
|
+
- [ ] 更新 Review Comments(如需要)
|
|
101
|
+
|
|
102
|
+
## 决策分支
|
|
103
|
+
|
|
104
|
+
| 条件 | 动作 |
|
|
105
|
+
|------|------|
|
|
106
|
+
| 需求不清晰 | 返回 Open,请求澄清 |
|
|
107
|
+
| 分支创建失败 | 检查 Git 状态,解决冲突 |
|
|
108
|
+
| 测试失败 | 返回 Develop,修复代码 |
|
|
109
|
+
| Lint 失败 | 修复合规性问题,重新 Submit |
|
|
110
|
+
| 评审拒绝 | 返回 Develop,按反馈修改 |
|
|
111
|
+
| 评审通过 | 进入 Close,合并并清理 |
|
|
112
|
+
|
|
113
|
+
## 合规要求
|
|
114
|
+
|
|
115
|
+
- **禁止**: 在 main/master 分支直接修改代码
|
|
116
|
+
- **必须**: 使用 `monoco issue start --branch` 创建功能分支
|
|
117
|
+
- **必须**: 所有单元测试通过后才能 Submit
|
|
118
|
+
- **必须**: 每个 Issue 至少 2 个 Checkbox
|
|
119
|
+
- **必须**: Review/Done 阶段必须包含 Review Comments
|
|
120
|
+
- **建议**: 小步提交,频繁同步文件追踪
|
|
121
|
+
|
|
122
|
+
## 相关命令
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# 创建 Issue
|
|
126
|
+
monoco issue create feature -t "标题"
|
|
127
|
+
|
|
128
|
+
# 启动开发
|
|
129
|
+
monoco issue start FEAT-0001 --branch
|
|
130
|
+
|
|
131
|
+
# 同步文件追踪
|
|
132
|
+
monoco issue sync-files
|
|
133
|
+
|
|
134
|
+
# 检查合规性
|
|
135
|
+
monoco issue lint
|
|
136
|
+
|
|
137
|
+
# 提交评审
|
|
138
|
+
monoco issue submit FEAT-0001
|
|
139
|
+
|
|
140
|
+
# 关闭 Issue
|
|
141
|
+
monoco issue close FEAT-0001 --solution completed --prune
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Issue 类型指南
|
|
145
|
+
|
|
146
|
+
| 类型 | 用途 | 前缀 | Mindset |
|
|
147
|
+
|------|------|------|---------|
|
|
148
|
+
| Epic | 宏大目标、愿景容器 | EPIC- | Architect |
|
|
149
|
+
| Feature | 用户价值增量 | FEAT- | Product Owner |
|
|
150
|
+
| Chore | 工程性事务 | CHORE- | Builder |
|
|
151
|
+
| Fix | 缺陷修复 | FIX- | Debugger |
|
|
152
|
+
|
|
153
|
+
## 与 flow_engineer 的关系
|
|
154
|
+
|
|
155
|
+
此工作流与 `flow_engineer` 互补:
|
|
156
|
+
- `issue-lifecycle-workflow`: 关注 Issue 管理流程
|
|
157
|
+
- `flow_engineer`: 关注代码实现流程
|
|
158
|
+
|
|
159
|
+
Engineer 在执行 Develop 阶段时,应遵循 `flow_engineer` 的 Investigate → Code → Test → Report → Submit 流程。
|
|
@@ -67,6 +67,50 @@ Monoco 强制采用 **Feature Branch** 模式。
|
|
|
67
67
|
- **禁止主干开发**: **严禁** 直接在 `main`, `master`, `production` 分支上修改代码。Linter 会拦截此类行为。
|
|
68
68
|
- **Submit**: 在提交 PR 前,运行 `monoco issue submit <ID>` 进行清理和预发布检查。
|
|
69
69
|
|
|
70
|
+
## 标准化工作流 (Standardized Workflow)
|
|
71
|
+
|
|
72
|
+
本指南引导 Agent 遵循 Monoco 标准 Issue 工作流。
|
|
73
|
+
|
|
74
|
+
### 工作流图示
|
|
75
|
+
|
|
76
|
+
```mermaid
|
|
77
|
+
stateDiagram-v2
|
|
78
|
+
[*] --> Plan
|
|
79
|
+
Plan --> Build: monoco issue start
|
|
80
|
+
Build --> Submit: monoco issue submit
|
|
81
|
+
state "Oracle Loop" as Oracle {
|
|
82
|
+
Submit --> Review: Auto/Manual Review
|
|
83
|
+
Review --> Fix: Reject
|
|
84
|
+
Fix --> Submit: Retry
|
|
85
|
+
}
|
|
86
|
+
Review --> Merge: Approve
|
|
87
|
+
Merge --> [*]: monoco issue close
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 执行步骤
|
|
91
|
+
|
|
92
|
+
1. **Plan (计划阶段)**:
|
|
93
|
+
- 确保 Issue 已创作且处于 `Open` 状态。
|
|
94
|
+
- 验证需求描述与任务清单 (Acceptance Criteria)。
|
|
95
|
+
|
|
96
|
+
2. **Build (构建阶段)**:
|
|
97
|
+
- 运行 `monoco issue start <ID> --branch` (强制分支隔离)。
|
|
98
|
+
- 实现功能或修复缺陷。
|
|
99
|
+
- 运行 `monoco issue sync-files` 更新修改文件追踪。
|
|
100
|
+
|
|
101
|
+
3. **Submit (提交阶段 - Oracle 循环)**:
|
|
102
|
+
- 运行测试确保质量。
|
|
103
|
+
- 运行 `monoco issue lint` 检查合规性。
|
|
104
|
+
- 运行 `monoco issue submit <ID>` 触发评审。
|
|
105
|
+
- **如果** 收到报错或反馈:
|
|
106
|
+
- 修复问题。
|
|
107
|
+
- 重新运行测试。
|
|
108
|
+
- 重新运行提交。
|
|
109
|
+
|
|
110
|
+
4. **Merge (合并/关闭阶段)**:
|
|
111
|
+
- 一旦获得批准 (人工或自动):
|
|
112
|
+
- 运行 `monoco issue close <ID> --solution completed --prune` 清理环境并下线。
|
|
113
|
+
|
|
70
114
|
### 2. 文件追踪 (File Tracking)
|
|
71
115
|
|
|
72
116
|
为了保证上下文的自包含性 (Self-Contained Context),Agent 必须记录修改过的文件。
|
|
@@ -136,3 +180,9 @@ Monoco 强制采用 **Feature Branch** 模式。
|
|
|
136
180
|
Linter 包含环境感知防护:
|
|
137
181
|
|
|
138
182
|
- 🛑 **Dirty Main Protection**: 当检测到处于受保护分支 (`main`/`master`) 且存在未提交变更时,Lint 将失败并阻止操作。
|
|
183
|
+
|
|
184
|
+
### 6. ID 格式与层级 (ID Format & Hierarchy)
|
|
185
|
+
|
|
186
|
+
- **ID 规范**: Issue ID 必须严格遵循 `TYPE-XXXX` 格式,其中 `XXXX` 必须是 4 位数字(示例: `FEAT-0001`, `FIX-9999`)。
|
|
187
|
+
- **禁止后缀**: 禁止使用类似 `FEAT-0001-1` 这样带后缀的 ID。
|
|
188
|
+
- **层级表达**: 子功能或子任务应通过 `parent` 字段(在 Front Matter 中)来关联父级 Issue,严禁通过 ID 命名约定(如加分级后缀)来表达层级关系。
|
|
@@ -27,6 +27,7 @@ class IssueValidator:
|
|
|
27
27
|
all_issue_ids: Set[str] = set(),
|
|
28
28
|
current_project: Optional[str] = None,
|
|
29
29
|
workspace_root: Optional[str] = None,
|
|
30
|
+
valid_domains: Set[str] = set(),
|
|
30
31
|
) -> List[Diagnostic]:
|
|
31
32
|
"""
|
|
32
33
|
Validate an issue and return diagnostics.
|
|
@@ -77,7 +78,11 @@ class IssueValidator:
|
|
|
77
78
|
diagnostics.extend(self._validate_references(meta, content, all_issue_ids))
|
|
78
79
|
|
|
79
80
|
# 5.5 Domain Integrity
|
|
80
|
-
diagnostics.extend(
|
|
81
|
+
diagnostics.extend(
|
|
82
|
+
self._validate_domains(
|
|
83
|
+
meta, content, all_issue_ids, valid_domains=valid_domains
|
|
84
|
+
)
|
|
85
|
+
)
|
|
81
86
|
|
|
82
87
|
# 6. Time Consistency
|
|
83
88
|
diagnostics.extend(self._validate_time_consistency(meta, content))
|
|
@@ -88,6 +93,9 @@ class IssueValidator:
|
|
|
88
93
|
# 8. Language Consistency
|
|
89
94
|
diagnostics.extend(self._validate_language_consistency(meta, content))
|
|
90
95
|
|
|
96
|
+
# 9. Placeholder Detection
|
|
97
|
+
diagnostics.extend(self._validate_placeholders(meta, content))
|
|
98
|
+
|
|
91
99
|
return diagnostics
|
|
92
100
|
|
|
93
101
|
def _validate_language_consistency(
|
|
@@ -431,7 +439,7 @@ class IssueValidator:
|
|
|
431
439
|
)
|
|
432
440
|
resolver = ReferenceResolver(context)
|
|
433
441
|
|
|
434
|
-
# Malformed ID Check
|
|
442
|
+
# 1. Malformed ID Check (Syntax)
|
|
435
443
|
if meta.parent and meta.parent.startswith("#"):
|
|
436
444
|
line = self._get_field_line(content, "parent")
|
|
437
445
|
diagnostics.append(
|
|
@@ -466,7 +474,63 @@ class IssueValidator:
|
|
|
466
474
|
)
|
|
467
475
|
)
|
|
468
476
|
|
|
469
|
-
|
|
477
|
+
# 2. Body Reference Check (Format and Existence)
|
|
478
|
+
lines = content.split("\n")
|
|
479
|
+
in_fm = False
|
|
480
|
+
fm_end = 0
|
|
481
|
+
for i, line in enumerate(lines):
|
|
482
|
+
if line.strip() == "---":
|
|
483
|
+
if not in_fm:
|
|
484
|
+
in_fm = True
|
|
485
|
+
else:
|
|
486
|
+
fm_end = i
|
|
487
|
+
break
|
|
488
|
+
|
|
489
|
+
for i, line in enumerate(lines):
|
|
490
|
+
if i <= fm_end:
|
|
491
|
+
continue # Skip frontmatter
|
|
492
|
+
|
|
493
|
+
# Find all matches, including those with invalid suffixes to report them properly
|
|
494
|
+
matches = re.finditer(r"\b((?:EPIC|FEAT|CHORE|FIX)-\d{4}(?:-\d+)?)\b", line)
|
|
495
|
+
for match in matches:
|
|
496
|
+
full_raw_id = match.group(1)
|
|
497
|
+
|
|
498
|
+
# Check if it has an invalid suffix (e.g. FEAT-0099-1)
|
|
499
|
+
if len(full_raw_id.split("-")) > 2:
|
|
500
|
+
diagnostics.append(
|
|
501
|
+
self._create_diagnostic(
|
|
502
|
+
f"Invalid ID Format: '{full_raw_id}' has an invalid suffix. Use 'parent' field for hierarchy.",
|
|
503
|
+
DiagnosticSeverity.Warning,
|
|
504
|
+
line=i,
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
continue
|
|
508
|
+
|
|
509
|
+
ref_id = full_raw_id
|
|
510
|
+
|
|
511
|
+
# Knowledge Check (Only if resolver is available)
|
|
512
|
+
if resolver:
|
|
513
|
+
# Check for namespaced ID before this match?
|
|
514
|
+
full_match = re.search(
|
|
515
|
+
r"\b(?:([a-z0-9_-]+)::)?(" + re.escape(ref_id) + r")\b",
|
|
516
|
+
line[max(0, match.start() - 50) : match.end()],
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
check_id = ref_id
|
|
520
|
+
if full_match and full_match.group(1):
|
|
521
|
+
check_id = f"{full_match.group(1)}::{ref_id}"
|
|
522
|
+
|
|
523
|
+
if ref_id != meta.id and not resolver.is_valid_reference(check_id):
|
|
524
|
+
diagnostics.append(
|
|
525
|
+
self._create_diagnostic(
|
|
526
|
+
f"Broken Reference: Issue '{check_id}' not found.",
|
|
527
|
+
DiagnosticSeverity.Warning,
|
|
528
|
+
line=i,
|
|
529
|
+
)
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
# 3. Hierarchy and Graph Integrity (Requires Resolver)
|
|
533
|
+
if not resolver:
|
|
470
534
|
return diagnostics
|
|
471
535
|
|
|
472
536
|
# Logic: Epics must have a parent (unless it is the Sink Root EPIC-0000)
|
|
@@ -506,49 +570,6 @@ class IssueValidator:
|
|
|
506
570
|
)
|
|
507
571
|
)
|
|
508
572
|
|
|
509
|
-
# Body Reference Check
|
|
510
|
-
# Regex for generic issue ID: (EPIC|FEAT|CHORE|FIX)-\d{4}
|
|
511
|
-
# We scan line by line to get line numbers
|
|
512
|
-
lines = content.split("\n")
|
|
513
|
-
# Skip frontmatter for body check to avoid double counting (handled above)
|
|
514
|
-
in_fm = False
|
|
515
|
-
fm_end = 0
|
|
516
|
-
for i, line in enumerate(lines):
|
|
517
|
-
if line.strip() == "---":
|
|
518
|
-
if not in_fm:
|
|
519
|
-
in_fm = True
|
|
520
|
-
else:
|
|
521
|
-
fm_end = i
|
|
522
|
-
break
|
|
523
|
-
|
|
524
|
-
for i, line in enumerate(lines):
|
|
525
|
-
if i <= fm_end:
|
|
526
|
-
continue # Skip frontmatter
|
|
527
|
-
|
|
528
|
-
# Find all matches
|
|
529
|
-
matches = re.finditer(r"\b((?:EPIC|FEAT|CHORE|FIX)-\d{4})\b", line)
|
|
530
|
-
for match in matches:
|
|
531
|
-
ref_id = match.group(1)
|
|
532
|
-
# Check for namespaced ID before this match?
|
|
533
|
-
# The regex above only catches the ID part.
|
|
534
|
-
# Let's adjust regex to optionally catch namespace::
|
|
535
|
-
full_match = re.search(
|
|
536
|
-
r"\b(?:([a-z0-9_-]+)::)?(" + re.escape(ref_id) + r")\b",
|
|
537
|
-
line[max(0, match.start() - 50) : match.end()],
|
|
538
|
-
)
|
|
539
|
-
|
|
540
|
-
check_id = ref_id
|
|
541
|
-
if full_match and full_match.group(1):
|
|
542
|
-
check_id = f"{full_match.group(1)}::{ref_id}"
|
|
543
|
-
|
|
544
|
-
if ref_id != meta.id and not resolver.is_valid_reference(check_id):
|
|
545
|
-
diagnostics.append(
|
|
546
|
-
self._create_diagnostic(
|
|
547
|
-
f"Broken Reference: Issue '{check_id}' not found.",
|
|
548
|
-
DiagnosticSeverity.Warning,
|
|
549
|
-
line=i,
|
|
550
|
-
)
|
|
551
|
-
)
|
|
552
573
|
return diagnostics
|
|
553
574
|
return diagnostics
|
|
554
575
|
|
|
@@ -603,7 +624,11 @@ class IssueValidator:
|
|
|
603
624
|
return diagnostics
|
|
604
625
|
|
|
605
626
|
def _validate_domains(
|
|
606
|
-
self,
|
|
627
|
+
self,
|
|
628
|
+
meta: IssueMetadata,
|
|
629
|
+
content: str,
|
|
630
|
+
all_ids: Set[str] = set(),
|
|
631
|
+
valid_domains: Set[str] = set(),
|
|
607
632
|
) -> List[Diagnostic]:
|
|
608
633
|
diagnostics = []
|
|
609
634
|
# Check if 'domains' field exists in frontmatter text
|
|
@@ -650,38 +675,79 @@ class IssueValidator:
|
|
|
650
675
|
)
|
|
651
676
|
|
|
652
677
|
# Domain Content Validation
|
|
653
|
-
from
|
|
654
|
-
|
|
655
|
-
service = DomainService()
|
|
656
|
-
|
|
678
|
+
# If valid_domains is provided (from file scan), use it as strict source of truth
|
|
657
679
|
if hasattr(meta, "domains") and meta.domains:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
680
|
+
if valid_domains:
|
|
681
|
+
# Use File-based validation
|
|
682
|
+
for domain in meta.domains:
|
|
683
|
+
# 1. Format Check: PascalCase
|
|
684
|
+
is_pascal = re.match(r"^[A-Z][a-zA-Z0-9]+$", domain) is not None
|
|
685
|
+
|
|
686
|
+
if not is_pascal:
|
|
687
|
+
# Suggest conversion
|
|
688
|
+
normalized = "".join(
|
|
689
|
+
word.capitalize()
|
|
690
|
+
for word in re.findall(r"[a-zA-Z0-9]+", domain)
|
|
666
691
|
)
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
692
|
+
if normalized in valid_domains:
|
|
693
|
+
diagnostics.append(
|
|
694
|
+
self._create_diagnostic(
|
|
695
|
+
f"Domain Format Error: '{domain}' must be PascalCase (e.g., '{normalized}').",
|
|
696
|
+
DiagnosticSeverity.Error,
|
|
697
|
+
line=field_line,
|
|
698
|
+
)
|
|
699
|
+
)
|
|
700
|
+
else:
|
|
701
|
+
diagnostics.append(
|
|
702
|
+
self._create_diagnostic(
|
|
703
|
+
f"Domain Format Error: '{domain}' must be PascalCase (no spaces/symbols).",
|
|
704
|
+
DiagnosticSeverity.Error,
|
|
705
|
+
line=field_line,
|
|
706
|
+
)
|
|
707
|
+
)
|
|
708
|
+
continue
|
|
709
|
+
|
|
710
|
+
# 2. Existence Check
|
|
711
|
+
if domain not in valid_domains:
|
|
670
712
|
diagnostics.append(
|
|
671
713
|
self._create_diagnostic(
|
|
672
|
-
f"Unknown Domain: '{domain}'
|
|
714
|
+
f"Unknown Domain: '{domain}' not found. Available: {', '.join(sorted(valid_domains))}",
|
|
673
715
|
DiagnosticSeverity.Error,
|
|
674
716
|
line=field_line,
|
|
675
717
|
)
|
|
676
718
|
)
|
|
677
|
-
|
|
719
|
+
else:
|
|
720
|
+
# Fallback to legacy DomainService (hardcoded list)
|
|
721
|
+
from .domain_service import DomainService
|
|
722
|
+
|
|
723
|
+
service = DomainService()
|
|
724
|
+
for domain in meta.domains:
|
|
725
|
+
if service.is_alias(domain):
|
|
726
|
+
canonical = service.get_canonical(domain)
|
|
678
727
|
diagnostics.append(
|
|
679
728
|
self._create_diagnostic(
|
|
680
|
-
f"
|
|
729
|
+
f"Domain Alias: '{domain}' is an alias for '{canonical}'. Preference: Canonical.",
|
|
681
730
|
DiagnosticSeverity.Warning,
|
|
682
731
|
line=field_line,
|
|
683
732
|
)
|
|
684
733
|
)
|
|
734
|
+
elif not service.is_defined(domain):
|
|
735
|
+
if service.config.strict:
|
|
736
|
+
diagnostics.append(
|
|
737
|
+
self._create_diagnostic(
|
|
738
|
+
f"Unknown Domain: '{domain}' is not defined in domain ontology.",
|
|
739
|
+
DiagnosticSeverity.Error,
|
|
740
|
+
line=field_line,
|
|
741
|
+
)
|
|
742
|
+
)
|
|
743
|
+
else:
|
|
744
|
+
diagnostics.append(
|
|
745
|
+
self._create_diagnostic(
|
|
746
|
+
f"Unknown Domain: '{domain}' is not defined in domain ontology.",
|
|
747
|
+
DiagnosticSeverity.Warning,
|
|
748
|
+
line=field_line,
|
|
749
|
+
)
|
|
750
|
+
)
|
|
685
751
|
|
|
686
752
|
return diagnostics
|
|
687
753
|
|
|
@@ -716,3 +782,57 @@ class IssueValidator:
|
|
|
716
782
|
)
|
|
717
783
|
|
|
718
784
|
return diagnostics
|
|
785
|
+
|
|
786
|
+
def _validate_placeholders(
|
|
787
|
+
self, meta: IssueMetadata, content: str
|
|
788
|
+
) -> List[Diagnostic]:
|
|
789
|
+
"""
|
|
790
|
+
Detect uncleared placeholders in issue content.
|
|
791
|
+
|
|
792
|
+
Placeholders are template hints that should be removed before submission.
|
|
793
|
+
Examples:
|
|
794
|
+
- <!-- Required for Review/Done stage. Record review feedback here. -->
|
|
795
|
+
- <!-- TODO: Add implementation details -->
|
|
796
|
+
- <!-- Placeholder: ... -->
|
|
797
|
+
|
|
798
|
+
Severity depends on stage:
|
|
799
|
+
- review/done: ERROR (must be cleared before submission)
|
|
800
|
+
- draft/open/doing: WARNING (should be cleared)
|
|
801
|
+
"""
|
|
802
|
+
diagnostics = []
|
|
803
|
+
|
|
804
|
+
# Define placeholder patterns
|
|
805
|
+
placeholder_patterns = [
|
|
806
|
+
# HTML comments with common placeholder keywords
|
|
807
|
+
(r"<!--\s*Required for Review/Done stage.*?-->", "Review/Done placeholder"),
|
|
808
|
+
(r"<!--\s*TODO:.*?-->", "TODO placeholder"),
|
|
809
|
+
(r"<!--\s*FIXME:.*?-->", "FIXME placeholder"),
|
|
810
|
+
(r"<!--\s*Placeholder:.*?-->", "Generic placeholder"),
|
|
811
|
+
(r"<!--\s*Template:.*?-->", "Template placeholder"),
|
|
812
|
+
(r"<!--\s*Example:.*?-->", "Example placeholder"),
|
|
813
|
+
# Generic instruction patterns (English and Chinese)
|
|
814
|
+
(r"<!--\s*Record review feedback here.*?-->", "Review placeholder"),
|
|
815
|
+
(r"<!--\s*在此记录评审反馈.*?-->", "Review placeholder (Chinese)"),
|
|
816
|
+
]
|
|
817
|
+
|
|
818
|
+
lines = content.splitlines()
|
|
819
|
+
|
|
820
|
+
# Determine severity based on stage
|
|
821
|
+
if meta.stage in ["review", "done"]:
|
|
822
|
+
severity = DiagnosticSeverity.Error
|
|
823
|
+
else:
|
|
824
|
+
severity = DiagnosticSeverity.Warning
|
|
825
|
+
|
|
826
|
+
for line_idx, line in enumerate(lines):
|
|
827
|
+
for pattern, desc in placeholder_patterns:
|
|
828
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
829
|
+
diagnostics.append(
|
|
830
|
+
self._create_diagnostic(
|
|
831
|
+
f"Uncleared Placeholder: {desc} found. Remove template hints before submission.",
|
|
832
|
+
severity,
|
|
833
|
+
line_idx,
|
|
834
|
+
)
|
|
835
|
+
)
|
|
836
|
+
break # Only report once per line
|
|
837
|
+
|
|
838
|
+
return diagnostics
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict
|
|
3
|
+
from monoco.core.feature import MonocoFeature, IntegrationData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MemoFeature(MonocoFeature):
|
|
7
|
+
@property
|
|
8
|
+
def name(self) -> str:
|
|
9
|
+
return "memo"
|
|
10
|
+
|
|
11
|
+
def initialize(self, root: Path, config: Dict) -> None:
|
|
12
|
+
# Memo feature doesn't require explicit initialization
|
|
13
|
+
# The inbox is created on first use
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def integrate(self, root: Path, config: Dict) -> IntegrationData:
|
|
17
|
+
# Determine language from config, default to 'en'
|
|
18
|
+
lang = config.get("i18n", {}).get("source_lang", "en")
|
|
19
|
+
|
|
20
|
+
# Resource path: monoco/features/memo/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(system_prompts={"Memo (Fleeting Notes)": content})
|