Skip to content

Commit c7db2d4

Browse files
committed
feat(plan-parser): enhance markdown section status parsing #331
- Add support for section status markers at the start of the line. - Improve regex to handle both start and end status markers. - Update status mapping logic to use predefined sets for clarity. - Add test case for error status in section titles. - Refactor task status assignment for better readability.
1 parent a106524 commit c7db2d4

File tree

3 files changed

+75
-49
lines changed

3 files changed

+75
-49
lines changed

core/src/main/kotlin/cc/unitmesh/devti/observer/plan/AgentPlan.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,22 @@ data class AgentPlan(
2323
) {
2424
fun updateCompletionStatus() {
2525
if (tasks.isEmpty()) return
26-
26+
27+
if (this.status == TaskStatus.COMPLETED) {
28+
completed = true
29+
return
30+
}
31+
2732
completed = tasks.all { it.status == TaskStatus.COMPLETED }
28-
33+
2934
status = when {
3035
tasks.all { it.status == TaskStatus.COMPLETED } -> TaskStatus.COMPLETED
3136
tasks.any { it.status == TaskStatus.FAILED } -> TaskStatus.FAILED
3237
tasks.any { it.status == TaskStatus.IN_PROGRESS } -> TaskStatus.IN_PROGRESS
3338
else -> TaskStatus.TODO
3439
}
3540
}
36-
41+
3742
/**
3843
* 推进PDCA循环到下一阶段
3944
* @return 当前阶段
@@ -47,46 +52,49 @@ data class AgentPlan(
4752
}
4853
return phase
4954
}
50-
55+
5156
/**
5257
* 设置PDCA循环的特定阶段
5358
* @param newPhase 要设置的新阶段
5459
*/
5560
fun setPdcaPhase(newPhase: PlanPhase) {
5661
phase = newPhase
5762
}
58-
63+
5964
/**
6065
* 获取当前PDCA阶段
6166
* @return 当前PDCA阶段
6267
*/
6368
fun getPdcaPhase(): PlanPhase = phase
64-
69+
6570
/**
6671
* 根据PDCA阶段更新计划和任务状态
6772
*/
6873
fun processPdcaPhase() {
6974
when (phase) {
7075
PlanPhase.PLAN -> {
7176
// 计划阶段:任务准备就绪但尚未开始
72-
tasks.forEach {
77+
tasks.forEach {
7378
if (it.status == TaskStatus.TODO) {
7479
// 保持任务为TODO状态
7580
}
7681
}
7782
}
83+
7884
PlanPhase.DO -> {
7985
// 执行阶段:将任务状态更新为进行中
80-
tasks.forEach {
86+
tasks.forEach {
8187
if (it.status == TaskStatus.TODO) {
8288
it.updateStatus(TaskStatus.IN_PROGRESS)
8389
}
8490
}
8591
}
92+
8693
PlanPhase.CHECK -> {
8794
// 检查阶段:评估任务执行情况
8895
updateCompletionStatus()
8996
}
97+
9098
PlanPhase.ACT -> {
9199
// 行动阶段:基于检查结果采取行动
92100
// 失败的任务可以在这里重置为TODO以便重试

core/src/main/kotlin/cc/unitmesh/devti/observer/plan/MarkdownPlanParser.kt

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ object MarkdownPlanParser {
112112

113113
// Check for section status marker like "1. Section Title [✓]"
114114
val sectionStatusMatch =
115-
"^(\\d+)\\.\\s*(.+?)(?:\\s*\\[(.)\\])?$".toRegex().find(listItemFirstLine)
115+
"^(\\d+)\\.\\s*(?:\\[([xX!*✓]?)\\]\\s*)?(.+?)(?:\\s*\\[([xX!*✓]?)\\])?$".toRegex().find(listItemFirstLine)
116116

117117
if (sectionStatusMatch != null) {
118118
// Save previous section if exists
@@ -133,16 +133,18 @@ object MarkdownPlanParser {
133133
}
134134

135135
// Extract the title without any status marker
136-
currentSectionTitle = sectionStatusMatch.groupValues[2].trim()
137-
138-
// Check for section status marker
139-
val statusMarker = sectionStatusMatch.groupValues.getOrNull(3)
140-
currentSectionCompleted =
141-
statusMarker == "" || statusMarker == "x" || statusMarker == "X"
136+
currentSectionTitle = sectionStatusMatch.groupValues[3].trim()
137+
138+
// Check for section status marker (either at start or end)
139+
val startStatusMarker = sectionStatusMatch.groupValues[2]
140+
val endStatusMarker = sectionStatusMatch.groupValues[4]
141+
val statusMarker = if (startStatusMarker.isNotEmpty()) startStatusMarker else endStatusMarker
142+
143+
currentSectionCompleted = statusMarker in GITHUB_TODO_COMPLETED
142144
currentSectionStatus = when (statusMarker) {
143-
"", "x", "X" -> TaskStatus.COMPLETED
144-
"!" -> TaskStatus.FAILED
145-
"*" -> TaskStatus.IN_PROGRESS
145+
in GITHUB_TODO_COMPLETED -> TaskStatus.COMPLETED
146+
in GITHUB_TODO_FAILED -> TaskStatus.FAILED
147+
in GITHUB_TODO_IN_PROGRESS -> TaskStatus.IN_PROGRESS
146148
else -> TaskStatus.TODO
147149
}
148150

@@ -226,10 +228,10 @@ object MarkdownPlanParser {
226228
val todoText = githubTodoMatch.groupValues[2].trim()
227229

228230
// Determine task status based on marker
229-
val task = when {
230-
checkState in GITHUB_TODO_COMPLETED -> PlanTask(todoText, true, TaskStatus.COMPLETED)
231-
checkState in GITHUB_TODO_FAILED -> PlanTask(todoText, false, TaskStatus.FAILED)
232-
checkState in GITHUB_TODO_IN_PROGRESS -> PlanTask(todoText, false, TaskStatus.IN_PROGRESS)
231+
val task = when (checkState) {
232+
in GITHUB_TODO_COMPLETED -> PlanTask(todoText, true, TaskStatus.COMPLETED)
233+
in GITHUB_TODO_FAILED -> PlanTask(todoText, false, TaskStatus.FAILED)
234+
in GITHUB_TODO_IN_PROGRESS -> PlanTask(todoText, false, TaskStatus.IN_PROGRESS)
233235
else -> PlanTask(todoText, false, TaskStatus.TODO)
234236
}
235237

core/src/test/kotlin/cc/unitmesh/devti/observer/plan/MarkdownPlanParserTest.kt

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package cc.unitmesh.devti.observer.plan
22

3+
import junit.framework.TestCase.assertEquals
34
import org.assertj.core.api.Assertions
45
import org.junit.Test
56

67
class MarkdownPlanParserTest {
7-
88
@Test
99
fun should_parse_markdown_with_single_section_and_tasks() {
1010
// Given
@@ -74,7 +74,7 @@ class MarkdownPlanParserTest {
7474
// 添加调试信息以查看更多细节
7575
println("解析后的计划项数量: ${planItems.size}")
7676
planItems.forEachIndexed { index, item ->
77-
println("${index+1}项: $item")
77+
println("${index + 1}项: $item")
7878
}
7979

8080
// 进行更宽松的测试断言,先确保基本功能可用
@@ -234,26 +234,26 @@ class MarkdownPlanParserTest {
234234
fun should_return_correct_items() {
235235
// Given
236236
val markdownContent = """
237-
1. **分析现有代码结构**:
238-
- BlogService 中的 `deleteBlog` 方法目前只支持按 ID 删除
239-
- BlogPost 实体类中的 author 字段类型为 String,但 DTO 中的 author 是 Author 对象类型,存在映射不一致
240-
- Repository 层使用 CrudRepository 需要扩展自定义删除方法
241-
242-
2. **数据库字段确认**:
243-
- 需要确认 BlogPost 表实际存储的 author 字段类型(当前代码显示为 String 类型)
244-
245-
3. **功能实现步骤**:
246-
- [ ] 在 BlogRepository 添加按作者删除的方法
247-
- [ ] 扩展 BlogService 添加 deleteByAuthor 方法
248-
- [ ] 在 BlogController 添加新的 DELETE 端点
249-
- [ ] 修复 DTO 与实体类的 author 字段类型一致性
250-
- [ ] 添加 Swagger 接口文档注解
251-
- [ ] 补充单元测试
252-
253-
4. **异常处理**:
254-
- 处理不存在的作者删除请求
255-
- 添加事务管理注解
256-
- 统一返回结果格式
237+
1. **分析现有代码结构**:
238+
- BlogService 中的 `deleteBlog` 方法目前只支持按 ID 删除
239+
- BlogPost 实体类中的 author 字段类型为 String,但 DTO 中的 author 是 Author 对象类型,存在映射不一致
240+
- Repository 层使用 CrudRepository 需要扩展自定义删除方法
241+
242+
2. **数据库字段确认**:
243+
- 需要确认 BlogPost 表实际存储的 author 字段类型(当前代码显示为 String 类型)
244+
245+
3. **功能实现步骤**:
246+
- [ ] 在 BlogRepository 添加按作者删除的方法
247+
- [ ] 扩展 BlogService 添加 deleteByAuthor 方法
248+
- [ ] 在 BlogController 添加新的 DELETE 端点
249+
- [ ] 修复 DTO 与实体类的 author 字段类型一致性
250+
- [ ] 添加 Swagger 接口文档注解
251+
- [ ] 补充单元测试
252+
253+
4. **异常处理**:
254+
- 处理不存在的作者删除请求
255+
- 添加事务管理注解
256+
- 统一返回结果格式
257257
""".trimIndent()
258258

259259
// When
@@ -320,10 +320,26 @@ class MarkdownPlanParserTest {
320320
Assertions.assertThat(secondTask.status).isEqualTo(TaskStatus.IN_PROGRESS)
321321

322322
// 验证嵌套任务被正确解析
323-
val nestedTasks = planItems[0].tasks.filter { it.step.contains("BlogController")
324-
|| it.step.contains("BlogService")
325-
|| it.step.contains("BlogRepository")
326-
|| it.step.contains("BlogPost") }
323+
val nestedTasks = planItems[0].tasks.filter {
324+
it.step.contains("BlogController")
325+
|| it.step.contains("BlogService")
326+
|| it.step.contains("BlogRepository")
327+
|| it.step.contains("BlogPost")
328+
}
327329
Assertions.assertThat(nestedTasks).isNotEmpty()
328330
}
329-
}
331+
332+
@Test
333+
fun should_support_error_status_in_section_title() {
334+
val content = """
335+
1. [✓] 数据库表结构确认
336+
- `blog_post` 表已存在 `category` 字段
337+
2. [*] 更新领域对象
338+
""".trimIndent()
339+
val plans = MarkdownPlanParser.parse(content)
340+
assertEquals(2, plans.size)
341+
assertEquals(TaskStatus.COMPLETED, plans[0].status)
342+
assertEquals("数据库表结构确认", plans[0].title)
343+
assertEquals(TaskStatus.IN_PROGRESS, plans[1].status)
344+
}
345+
}

0 commit comments

Comments
 (0)