Skip to content

Commit 6648ccf

Browse files
committed
feat(plan): sync plan state from agent to renderer
Integrate PlanStateService observation to update renderer with current plan. Add logging for plan updates and summary rendering.
1 parent da39b5b commit 6648ccf

File tree

6 files changed

+78
-20
lines changed

6 files changed

+78
-20
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ class CodingAgent(
8585
private val policyEngine = DefaultPolicyEngine()
8686
private val toolOrchestrator = ToolOrchestrator(toolRegistry, policyEngine, renderer, mcpConfigService = mcpToolConfigService)
8787

88+
/**
89+
* Get the PlanStateService for observing plan state changes.
90+
* Returns null if no plan tool is registered.
91+
*/
92+
fun getPlanStateService(): cc.unitmesh.agent.plan.PlanStateService? {
93+
return toolOrchestrator.getPlanStateService()
94+
}
95+
8896
private val errorRecoveryAgent = ErrorRecoveryAgent(projectPath, llmService)
8997
private val analysisAgent = AnalysisAgent(llmService, contentThreshold = 15000)
9098
private val mcpToolsInitializer = McpToolsInitializer()

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ import cc.unitmesh.agent.render.ToolCallInfo
1414
import cc.unitmesh.agent.tool.ToolType
1515
import cc.unitmesh.agent.tool.toToolType
1616
import cc.unitmesh.llm.compression.TokenInfo
17+
import com.intellij.openapi.diagnostic.Logger
1718
import kotlinx.coroutines.flow.MutableStateFlow
1819
import kotlinx.coroutines.flow.StateFlow
1920
import kotlinx.coroutines.flow.asStateFlow
2021
import kotlinx.coroutines.flow.update
2122

23+
private val jewelRendererLogger = Logger.getInstance("JewelRenderer")
24+
2225
/**
2326
* Jewel-compatible Renderer for IntelliJ IDEA plugin.
2427
*
@@ -84,6 +87,15 @@ class JewelRenderer : BaseRenderer() {
8487
private val _currentPlan = MutableStateFlow<AgentPlan?>(null)
8588
val currentPlan: StateFlow<AgentPlan?> = _currentPlan.asStateFlow()
8689

90+
/**
91+
* Set the current plan directly.
92+
* Used to sync with PlanStateService from CodingAgent.
93+
*/
94+
fun setPlan(plan: AgentPlan?) {
95+
jewelRendererLogger.info("setPlan: plan=${plan != null}, tasks=${plan?.tasks?.size ?: 0}")
96+
_currentPlan.value = plan
97+
}
98+
8799
// BaseRenderer implementation
88100

89101
override fun renderIterationHeader(current: Int, max: Int) {
@@ -137,17 +149,22 @@ class JewelRenderer : BaseRenderer() {
137149
}
138150

139151
override fun renderToolCall(toolName: String, paramsStr: String) {
152+
jewelRendererLogger.info("renderToolCall: toolName=$toolName, paramsStr length=${paramsStr.length}")
153+
140154
val toolInfo = formatToolCallDisplay(toolName, paramsStr)
141155
val params = parseParamsString(paramsStr)
142156
val toolType = toolName.toToolType()
143157

158+
jewelRendererLogger.info("renderToolCall: parsed params keys=${params.keys}")
159+
144160
// Handle task-boundary tool - update task list
145161
if (toolName == "task-boundary") {
146162
updateTaskFromToolCall(params)
147163
}
148164

149165
// Handle plan management tool - update plan state
150166
if (toolName == "plan") {
167+
jewelRendererLogger.info("renderToolCall: detected plan tool, calling updatePlanFromToolCall")
151168
updatePlanFromToolCall(params)
152169
// Skip rendering plan tool to timeline - it's shown in PlanSummaryBar
153170
return
@@ -302,10 +319,13 @@ class JewelRenderer : BaseRenderer() {
302319
* Internal method to update plan state
303320
*/
304321
private fun updatePlanState(action: String, planMarkdown: String, taskIndex: Int?, stepIndex: Int?) {
322+
jewelRendererLogger.info("updatePlanState: action=$action, planMarkdown length=${planMarkdown.length}")
305323
when (action) {
306324
"CREATE", "UPDATE" -> {
307325
if (planMarkdown.isNotBlank()) {
308-
_currentPlan.value = MarkdownPlanParser.parseToPlan(planMarkdown)
326+
val plan = MarkdownPlanParser.parseToPlan(planMarkdown)
327+
jewelRendererLogger.info("Parsed plan: ${plan.tasks.size} tasks")
328+
_currentPlan.value = plan
309329
}
310330
}
311331
"COMPLETE_STEP" -> {

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaTerminalRenderer.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,10 @@ fun IdeaTerminalRenderer(
7676

7777
// Command display
7878
CommandDisplay(command = command, isDangerous = isDangerous, dangerReason = dangerReason)
79-
8079
// Output (if available)
81-
if (showOutput && executionResult != null) {
82-
OutputDisplay(result = executionResult!!)
83-
}
80+
// if (showOutput && executionResult != null) {
81+
// OutputDisplay(result = executionResult!!)
82+
// }
8483
}
8584
}
8685

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentViewModel.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,32 @@ class IdeaAgentViewModel(
262262
enableLLMStreaming = true
263263
)
264264
agentInitialized = true
265+
266+
// Start observing PlanStateService and sync to renderer
267+
startPlanStateObserver(codingAgent!!)
265268
}
266269
return codingAgent!!
267270
}
268271

272+
// Job for observing PlanStateService
273+
private var planStateObserverJob: Job? = null
274+
275+
/**
276+
* Start observing PlanStateService and sync plan state to renderer.
277+
*/
278+
private fun startPlanStateObserver(agent: CodingAgent) {
279+
// Cancel any existing observer
280+
planStateObserverJob?.cancel()
281+
282+
val planStateService = agent.getPlanStateService() ?: return
283+
284+
planStateObserverJob = coroutineScope.launch {
285+
planStateService.currentPlan.collect { plan ->
286+
renderer.setPlan(plan)
287+
}
288+
}
289+
}
290+
269291
/**
270292
* Check if LLM service is configured.
271293
*/

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/changes/IdeaFileChangeSummary.kt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,25 @@ fun IdeaFileChangeSummary(
5252
return
5353
}
5454

55-
// Show diff dialog if a file is selected
56-
selectedChange?.let { change ->
57-
IdeaFileChangeDiffDialog(
58-
project = project,
59-
change = change,
60-
onDismiss = { selectedChange = null },
61-
onUndo = {
62-
undoChange(project, change)
63-
FileChangeTracker.removeChange(change)
64-
selectedChange = null
65-
},
66-
onKeep = {
67-
FileChangeTracker.removeChange(change)
68-
selectedChange = null
55+
// Handle diff dialog display when a file is selected
56+
LaunchedEffect(selectedChange) {
57+
selectedChange?.let { change ->
58+
ApplicationManager.getApplication().invokeLater {
59+
IdeaFileChangeDiffDialogWrapper.show(
60+
project = project,
61+
change = change,
62+
onUndo = {
63+
undoChange(project, change)
64+
FileChangeTracker.removeChange(change)
65+
},
66+
onKeep = {
67+
FileChangeTracker.removeChange(change)
68+
},
69+
onDismiss = {}
70+
)
6971
}
70-
)
72+
selectedChange = null
73+
}
7174
}
7275

7376
Column(

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/plan/IdeaPlanSummaryBar.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,21 @@ import org.jetbrains.jewel.ui.icons.AllIconsKeys
4343
* Displays a collapsible summary of the current plan above the input box.
4444
* Uses Jewel theming and components for native IntelliJ look and feel.
4545
*/
46+
private val planSummaryLogger = com.intellij.openapi.diagnostic.Logger.getInstance("IdeaPlanSummaryBar")
47+
4648
@Composable
4749
fun IdeaPlanSummaryBar(
4850
plan: AgentPlan?,
4951
modifier: Modifier = Modifier,
5052
onViewDetails: (() -> Unit)? = null,
5153
onDismiss: (() -> Unit)? = null
5254
) {
55+
// Log for debugging
56+
planSummaryLogger.info("IdeaPlanSummaryBar called: plan=${plan != null}, tasks=${plan?.tasks?.size ?: 0}")
57+
5358
// Don't render if no plan
5459
if (plan == null || plan.tasks.isEmpty()) {
60+
planSummaryLogger.info("IdeaPlanSummaryBar: not rendering (plan is null or empty)")
5561
return
5662
}
5763

0 commit comments

Comments
 (0)