Skip to content

Commit 3d9ec96

Browse files
authored
Merge pull request #480 from phodal/master
feat: add mpp-vscode support
2 parents 6e071b5 + 1f09174 commit 3d9ec96

File tree

122 files changed

+14884
-387
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+14884
-387
lines changed

mpp-core/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ kotlin {
135135
// Kotlin Logging for multiplatform logging
136136
implementation("io.github.oshai:kotlin-logging:7.0.13")
137137

138-
runtimeOnly("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat")
138+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat")
139139
// Koog AI Framework - JVM only for now
140140
implementation("ai.koog:koog-agents:0.5.2")
141141
implementation("ai.koog:agents-mcp:0.5.2")

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/CodingAgentTemplate.kt

Lines changed: 139 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,75 @@ All tools use the DevIns format with JSON parameters:
3636
```
3737
</devin>
3838
39-
# Task Completion Strategy
39+
# Task Execution Strategy: Explore First, Then Plan
4040
41-
**IMPORTANT: Focus on completing the task efficiently.**
41+
**CRITICAL: Always explore the codebase BEFORE creating a plan.**
4242
43-
1. **Understand the Task**: Read the user's request carefully
44-
2. **Gather Minimum Required Information**: Only collect information directly needed for the task
45-
3. **Execute the Task**: Make the necessary changes or provide the answer
46-
4. **Verify if Needed**: For code changes, compile/test to verify
47-
5. **Provide Summary**: Always end with a clear summary of what was done
43+
## Phase 1: Exploration (REQUIRED before planning)
44+
Before creating any plan, you MUST gather context:
4845
49-
**Avoid over-exploration**: Don't spend iterations exploring unrelated code. Stay focused on the task.
46+
1. **Understand the request**: What exactly does the user want?
47+
2. **Locate relevant files**: Use `/glob` to find files related to the task
48+
3. **Read key files**: Use `/read-file` to understand existing code structure, patterns, and conventions
49+
4. **Search for references**: Use `/grep` to find related code, usages, or patterns
50+
51+
**Minimum exploration before planning:**
52+
- For code modifications: Read the target file(s) and understand the structure
53+
- For new features: Find similar existing implementations to follow patterns
54+
- For bug fixes: Locate the bug and understand the context
55+
56+
## Phase 2: Plan Creation (after exploration)
57+
Only create a plan AFTER you have sufficient context:
58+
59+
```markdown
60+
1. Task Title
61+
- [ ] Specific step with file path (e.g., "Add field to src/Entity.java")
62+
- [ ] Another specific step
63+
64+
2. Another Task
65+
- [ ] Step with clear action
66+
```
67+
68+
## Plan Actions
69+
- `CREATE`: Create a new plan (only after exploration)
70+
- `COMPLETE_STEP`: Mark a step done (taskIndex=1, stepIndex=1 for first step)
71+
- `VIEW`: View current plan status
72+
73+
## When to Use Planning
74+
- Tasks requiring multiple files to be modified
75+
- Complex features with dependencies between steps
76+
- Skip planning for simple single-file edits
77+
78+
## Plan Update Rules
79+
- Mark ONE step at a time after completing actual work
80+
- Do NOT batch multiple COMPLETE_STEP calls
81+
- Update after work is done, not before
82+
83+
Example workflow:
84+
1. User: "Add validation to UserController"
85+
2. Agent: Use /glob to find UserController
86+
3. Agent: Use /read-file to read UserController
87+
4. Agent: Create plan with specific steps based on what was learned
88+
5. Agent: Execute each step, marking complete as done
89+
90+
<devin>
91+
/plan
92+
```json
93+
{"action": "CREATE", "planMarkdown": "1. Add Validation\n - [ ] Add @Valid annotation to createUser method in src/main/java/UserController.java\n - [ ] Create UserValidator class in src/main/java/validators/"}
94+
```
95+
</devin>
96+
97+
## Avoiding Common Mistakes
98+
99+
**DON'T:**
100+
- Create a plan immediately without reading any files
101+
- Make assumptions about file locations or code structure
102+
- Create vague steps like "implement feature" without specifics
103+
104+
**DO:**
105+
- Read relevant files first to understand the codebase
106+
- Create specific steps with actual file paths
107+
- Base your plan on what you learned during exploration
50108
51109
# Information-Gathering Strategy
52110
@@ -95,10 +153,15 @@ When a tool fails:
95153
96154
# IMPORTANT: One Tool Per Response
97155
98-
**Execute ONLY ONE tool per response.**
156+
**Execute ONLY ONE tool per response. This is critical for proper execution.**
99157
100158
- ✅ CORRECT: One <devin> block with ONE tool call
101-
- ❌ WRONG: Multiple <devin> blocks
159+
- ❌ WRONG: Multiple <devin> blocks or multiple tool calls
160+
161+
**Special note for /plan tool:**
162+
- Do NOT call multiple COMPLETE_STEP in one response
163+
- Complete one step, wait for confirmation, then proceed to next step
164+
- Each plan update requires a separate response cycle
102165
103166
# Response Format
104167
@@ -161,17 +224,75 @@ ${'$'}{toolList}
161224
```
162225
</devin>
163226
164-
# 任务完成策略
227+
# 任务执行策略:先探索,后计划
228+
229+
**关键原则:在创建计划之前,必须先探索代码库。**
230+
231+
## 第一阶段:探索(创建计划前必须完成)
232+
在创建任何计划之前,你必须收集上下文:
233+
234+
1. **理解请求**:用户到底想要什么?
235+
2. **定位相关文件**:使用 `/glob` 查找与任务相关的文件
236+
3. **阅读关键文件**:使用 `/read-file` 了解现有代码结构、模式和约定
237+
4. **搜索引用**:使用 `/grep` 查找相关代码、用法或模式
238+
239+
**创建计划前的最少探索:**
240+
- 对于代码修改:读取目标文件,理解其结构
241+
- 对于新功能:找到类似的现有实现以遵循模式
242+
- 对于 bug 修复:定位 bug 并理解上下文
243+
244+
## 第二阶段:创建计划(在探索之后)
245+
只有在获得足够上下文后才创建计划:
246+
247+
```markdown
248+
1. 任务标题
249+
- [ ] 具体步骤带文件路径(如:"在 src/Entity.java 中添加字段")
250+
- [ ] 另一个具体步骤
251+
252+
2. 另一个任务
253+
- [ ] 有明确操作的步骤
254+
```
255+
256+
## 计划操作
257+
- `CREATE`: 创建新计划(仅在探索之后)
258+
- `COMPLETE_STEP`: 标记步骤完成 (taskIndex=1, stepIndex=1 表示第一个任务的第一个步骤)
259+
- `VIEW`: 查看当前计划状态
260+
261+
## 何时使用计划
262+
- 需要修改多个文件的任务
263+
- 步骤之间有依赖关系的复杂功能
264+
- 简单的单文件编辑跳过计划
265+
266+
## 计划更新规则
267+
- 完成实际工作后一次只标记一个步骤
268+
- 不要在一次响应中批量调用 COMPLETE_STEP
269+
- 工作完成后更新,而不是之前
270+
271+
示例工作流:
272+
1. 用户:"给 UserController 添加验证"
273+
2. Agent:使用 /glob 查找 UserController
274+
3. Agent:使用 /read-file 读取 UserController
275+
4. Agent:根据学到的内容创建具体步骤的计划
276+
5. Agent:执行每个步骤,完成后标记
277+
278+
<devin>
279+
/plan
280+
```json
281+
{"action": "CREATE", "planMarkdown": "1. 添加验证\n - [ ] 在 src/main/java/UserController.java 的 createUser 方法添加 @Valid 注解\n - [ ] 在 src/main/java/validators/ 创建 UserValidator 类"}
282+
```
283+
</devin>
165284
166-
**重要:专注于高效完成任务。**
285+
## 避免常见错误
167286
168-
1. **理解任务**:仔细阅读用户的请求
169-
2. **收集最少必要信息**:只收集任务直接需要的信息
170-
3. **执行任务**:进行必要的更改或提供答案
171-
4. **必要时验证**:对于代码更改,编译/测试以验证
172-
5. **提供总结**:始终以清晰的总结结束
287+
**不要:**
288+
- 在没有读取任何文件的情况下立即创建计划
289+
- 对文件位置或代码结构做出假设
290+
- 创建模糊的步骤如"实现功能"而没有具体内容
173291
174-
**避免过度探索**:不要花费迭代次数探索无关代码。保持专注于任务。
292+
**要:**
293+
- 先读取相关文件以了解代码库
294+
- 创建带有实际文件路径的具体步骤
295+
- 基于探索阶段学到的内容制定计划
175296
176297
# 信息收集策略
177298

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/executor/CodingAgentExecutor.kt

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package cc.unitmesh.agent.executor
33
import cc.unitmesh.agent.*
44
import cc.unitmesh.agent.conversation.ConversationManager
55
import cc.unitmesh.agent.core.SubAgentManager
6+
import cc.unitmesh.agent.logging.getLogger
67
import cc.unitmesh.agent.tool.schema.ToolResultFormatter
78
import cc.unitmesh.agent.orchestrator.ToolExecutionResult
89
import cc.unitmesh.agent.orchestrator.ToolOrchestrator
910
import cc.unitmesh.agent.render.CodingAgentRenderer
1011
import cc.unitmesh.agent.state.ToolCall
1112
import cc.unitmesh.agent.state.ToolExecutionState
13+
import cc.unitmesh.agent.plan.PlanSummaryData
1214
import cc.unitmesh.agent.tool.ToolResult
1315
import cc.unitmesh.agent.tool.ToolType
1416
import cc.unitmesh.agent.tool.toToolType
@@ -24,8 +26,8 @@ import cc.unitmesh.agent.orchestrator.ToolExecutionContext as OrchestratorContex
2426
data class AsyncShellConfig(
2527
/** Initial wait timeout in milliseconds before notifying AI that process is still running */
2628
val initialWaitTimeoutMs: Long = 60_000L, // 1 minute
27-
/** Maximum total wait time in milliseconds */
28-
val maxWaitTimeoutMs: Long = 300_000L, // 5 minutes
29+
/** Maximum total wait time in milliseconds (2 minutes, similar to Cursor/Claude Code) */
30+
val maxWaitTimeoutMs: Long = 120_000L, // 2 minutes
2931
/** Interval for checking process status after initial timeout */
3032
val checkIntervalMs: Long = 30_000L // 30 seconds
3133
)
@@ -38,7 +40,13 @@ class CodingAgentExecutor(
3840
maxIterations: Int = 100,
3941
private val subAgentManager: SubAgentManager? = null,
4042
enableLLMStreaming: Boolean = true,
41-
private val asyncShellConfig: AsyncShellConfig = AsyncShellConfig()
43+
private val asyncShellConfig: AsyncShellConfig = AsyncShellConfig(),
44+
/**
45+
* When true, only execute the first tool call per LLM response.
46+
* This enforces the "one tool per response" rule even when LLM returns multiple tool calls.
47+
* Default is true to prevent LLM from executing multiple tools in one iteration.
48+
*/
49+
private val singleToolPerIteration: Boolean = true
4250
) : BaseAgentExecutor(
4351
projectPath = projectPath,
4452
llmService = llmService,
@@ -47,6 +55,7 @@ class CodingAgentExecutor(
4755
maxIterations = maxIterations,
4856
enableLLMStreaming = enableLLMStreaming
4957
) {
58+
private val logger = getLogger("CodingAgentExecutor")
5059
private val steps = mutableListOf<AgentStep>()
5160
private val edits = mutableListOf<AgentEdit>()
5261

@@ -92,12 +101,22 @@ class CodingAgentExecutor(
92101
break
93102
}
94103

95-
val toolCalls = toolCallParser.parseToolCalls(llmResponse.toString())
96-
if (toolCalls.isEmpty()) {
104+
val allToolCalls = toolCallParser.parseToolCalls(llmResponse.toString())
105+
if (allToolCalls.isEmpty()) {
97106
renderer.renderTaskComplete()
98107
break
99108
}
100109

110+
// When singleToolPerIteration is enabled, only execute the first tool call
111+
// This enforces the "one tool per response" rule even when LLM returns multiple tool calls
112+
val toolCalls = if (singleToolPerIteration && allToolCalls.size > 1) {
113+
logger.warn { "LLM returned ${allToolCalls.size} tool calls, but singleToolPerIteration is enabled. Only executing the first one: ${allToolCalls.first().toolName}" }
114+
renderer.renderError("Warning: LLM returned ${allToolCalls.size} tool calls, only executing the first one")
115+
listOf(allToolCalls.first())
116+
} else {
117+
allToolCalls
118+
}
119+
101120
val toolResults = executeToolCalls(toolCalls)
102121
val toolResultsText = ToolResultFormatter.formatMultipleToolResults(toolResults)
103122
conversationManager!!.addToolResults(toolResultsText)
@@ -204,15 +223,15 @@ class CodingAgentExecutor(
204223
for (toolCall in toolsToExecute) {
205224
val toolName = toolCall.toolName
206225
val params = toolCall.params.mapValues { it.value as Any }
207-
val paramsStr = params.entries.joinToString(" ") { (key, value) ->
208-
"$key=\"$value\""
209-
}
210226

211-
renderer.renderToolCall(toolName, paramsStr)
227+
// Use renderToolCallWithParams to pass parsed params directly
228+
// This avoids string parsing issues with complex values like planMarkdown
229+
renderer.renderToolCallWithParams(toolName, params)
212230

213231
val executionContext = OrchestratorContext(
214232
workingDirectory = projectPath,
215-
environment = emptyMap()
233+
environment = emptyMap(),
234+
timeout = asyncShellConfig.maxWaitTimeoutMs // Use max timeout for shell commands
216235
)
217236

218237
var executionResult = toolOrchestrator.executeToolCall(
@@ -273,6 +292,11 @@ class CodingAgentExecutor(
273292
executionResult.metadata
274293
)
275294

295+
// Render plan summary bar after plan tool execution
296+
if (toolName == "plan" && executionResult.isSuccess) {
297+
renderPlanSummaryIfAvailable()
298+
}
299+
276300
val currentToolType = toolName.toToolType()
277301
if ((currentToolType == ToolType.WriteFile) && executionResult.isSuccess) {
278302
recordFileEdit(params)
@@ -511,4 +535,14 @@ class CodingAgentExecutor(
511535
fun getConversationHistory(): List<cc.unitmesh.devins.llm.Message> {
512536
return conversationManager?.getHistory() ?: emptyList()
513537
}
538+
539+
/**
540+
* Render plan summary bar if a plan is available
541+
*/
542+
private fun renderPlanSummaryIfAvailable() {
543+
val planStateService = toolOrchestrator.getPlanStateService() ?: return
544+
val currentPlan = planStateService.currentPlan.value ?: return
545+
val summary = PlanSummaryData.from(currentPlan)
546+
renderer.renderPlanSummary(summary)
547+
}
514548
}

0 commit comments

Comments
 (0)